├── .bundlemonrc
├── .eslintrc.js
├── .github
├── dependabot.yml
└── workflows
│ ├── bundlesize.yml
│ ├── download-qt3
│ └── action.yml
│ ├── linter.yml
│ └── main.yml
├── .gitignore
├── .gitmodules
├── .npmrc
├── .nycrc
├── .prettierrc
├── .vscode
├── launch.json
└── settings.json
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── api-extractor.json
├── build.js
├── demo
├── .gitignore
├── index.html
├── main.ts
├── package-lock.json
├── package.json
├── tsconfig.json
└── tslint.json
├── fuzzers
├── corpus_based_fuzzer.ts
├── engine.ts
├── fuzzer.ts
├── index.ts
├── iso_corpus.ts
├── mutators.ts
├── tsconfig.json
├── tslint.json
└── xpath-31-function-catalog-corpus.ts
├── karma.conf.ts
├── package-lock.json
├── package.json
├── performance
├── backend.benchmark.ts
├── compare.benchmark.ts
├── evaluateXPath.benchmark.ts
├── tslint.json
├── union.benchmark.ts
└── utils
│ └── loadFile.ts
├── src
├── documentWriter
│ ├── IDocumentWriter.ts
│ ├── domBackedDocumentWriter.ts
│ └── wrapExternalDocumentWriter.ts
├── domClone
│ ├── Pointer.ts
│ ├── deepCloneNode.ts
│ └── realizeDom.ts
├── domFacade
│ ├── ConcreteNode.ts
│ ├── DomFacade.ts
│ ├── ExternalDomFacade.ts
│ └── IDomFacade.ts
├── evaluateUpdatingExpression.ts
├── evaluateUpdatingExpressionSync.ts
├── evaluateXPath.ts
├── evaluateXPathToArray.ts
├── evaluateXPathToAsyncIterator.ts
├── evaluateXPathToBoolean.ts
├── evaluateXPathToFirstNode.ts
├── evaluateXPathToMap.ts
├── evaluateXPathToNodes.ts
├── evaluateXPathToNumber.ts
├── evaluateXPathToNumbers.ts
├── evaluateXPathToString.ts
├── evaluateXPathToStrings.ts
├── evaluationUtils
│ ├── PositionedError.ts
│ ├── buildEvaluationContext.ts
│ ├── convertUpdateResultToTransferable.ts
│ └── printAndRethrowError.ts
├── executePendingUpdateList.ts
├── expressions
│ ├── Context.ts
│ ├── DynamicContext.ts
│ ├── ExecutionParameters.ts
│ ├── ExecutionSpecificStaticContext.ts
│ ├── Expression.ts
│ ├── FlworExpression.ts
│ ├── ForExpression.ts
│ ├── LetExpression.ts
│ ├── NamedFunctionRef.ts
│ ├── OrderByExpression.ts
│ ├── PossiblyUpdatingExpression.ts
│ ├── Specificity.ts
│ ├── StaticContext.ts
│ ├── UnfocusableDynamicContext.ts
│ ├── UpdatingExpressionResult.ts
│ ├── VarRef.ts
│ ├── WhereExpression.ts
│ ├── XPathErrors.ts
│ ├── adaptJavaScriptValueToXPathValue.ts
│ ├── arrays
│ │ ├── CurlyArrayConstructor.ts
│ │ └── SquareArrayConstructor.ts
│ ├── axes
│ │ ├── AncestorAxis.ts
│ │ ├── AttributeAxis.ts
│ │ ├── ChildAxis.ts
│ │ ├── DescendantAxis.ts
│ │ ├── FollowingAxis.ts
│ │ ├── FollowingSiblingAxis.ts
│ │ ├── ParentAxis.ts
│ │ ├── PrecedingAxis.ts
│ │ ├── PrecedingSiblingAxis.ts
│ │ ├── SelfAxis.ts
│ │ └── validateContextNode.ts
│ ├── conditional
│ │ └── IfExpression.ts
│ ├── dataTypes
│ │ ├── ArrayValue.ts
│ │ ├── AtomicValue.ts
│ │ ├── ETypeNames.ts
│ │ ├── FunctionValue.ts
│ │ ├── ISequence.ts
│ │ ├── MapValue.ts
│ │ ├── Sequences
│ │ │ ├── ArrayBackedSequence.ts
│ │ │ ├── EmptySequence.ts
│ │ │ ├── IteratorBackedSequence.ts
│ │ │ ├── SingletonSequence.ts
│ │ │ └── getEffectiveBooleanValue.ts
│ │ ├── Value.ts
│ │ ├── Variety.ts
│ │ ├── atomize.ts
│ │ ├── builtins
│ │ │ ├── builtinDataTypesByType.ts
│ │ │ ├── builtinModels.ts
│ │ │ └── dataTypeValidatorByType.ts
│ │ ├── canCastToType.ts
│ │ ├── castToType.ts
│ │ ├── casting
│ │ │ ├── AtomicValueDataType.ts
│ │ │ ├── CastResult.ts
│ │ │ ├── castToAnyURI.ts
│ │ │ ├── castToBase64Binary.ts
│ │ │ ├── castToBoolean.ts
│ │ │ ├── castToDate.ts
│ │ │ ├── castToDateTime.ts
│ │ │ ├── castToDayTimeDuration.ts
│ │ │ ├── castToDecimal.ts
│ │ │ ├── castToDouble.ts
│ │ │ ├── castToDuration.ts
│ │ │ ├── castToFloat.ts
│ │ │ ├── castToFloatLikeType.ts
│ │ │ ├── castToGDay.ts
│ │ │ ├── castToGMonth.ts
│ │ │ ├── castToGMonthDay.ts
│ │ │ ├── castToGYear.ts
│ │ │ ├── castToGYearMonth.ts
│ │ │ ├── castToHexBinary.ts
│ │ │ ├── castToInteger.ts
│ │ │ ├── castToNumeric.ts
│ │ │ ├── castToString.ts
│ │ │ ├── castToStringLikeType.ts
│ │ │ ├── castToTime.ts
│ │ │ ├── castToUntypedAtomic.ts
│ │ │ ├── castToYearMonthDuration.ts
│ │ │ └── tryCastToType.ts
│ │ ├── createAtomicValue.ts
│ │ ├── createPointerValue.ts
│ │ ├── documentOrderUtils.ts
│ │ ├── facets
│ │ │ ├── comparators
│ │ │ │ └── decimalComparator.ts
│ │ │ └── facetsByDataType.ts
│ │ ├── isSubtypeOf.ts
│ │ ├── promoteToType.ts
│ │ ├── sequenceFactory.ts
│ │ ├── typeHelpers.ts
│ │ └── valueTypes
│ │ │ ├── AbstractDuration.ts
│ │ │ ├── DateTime.ts
│ │ │ ├── DayTimeDuration.ts
│ │ │ ├── Duration.ts
│ │ │ ├── QName.ts
│ │ │ └── YearMonthDuration.ts
│ ├── debug
│ │ ├── StackTraceEntry.ts
│ │ └── StackTraceGenerator.ts
│ ├── functions
│ │ ├── FunctionCall.ts
│ │ ├── FunctionDefinitionType.ts
│ │ ├── FunctionOperationErrors.ts
│ │ ├── InlineFunction.ts
│ │ ├── argumentHelper.ts
│ │ ├── argumentListToString.ts
│ │ ├── builtInFunctions.ts
│ │ ├── builtInFunctions_arrays.ts
│ │ ├── builtInFunctions_arrays_get.ts
│ │ ├── builtInFunctions_boolean.ts
│ │ ├── builtInFunctions_context.ts
│ │ ├── builtInFunctions_dataTypeConstructors.ts
│ │ ├── builtInFunctions_datetime.ts
│ │ ├── builtInFunctions_debugging.ts
│ │ ├── builtInFunctions_duration.ts
│ │ ├── builtInFunctions_error.ts
│ │ ├── builtInFunctions_fontoxpath.ts
│ │ ├── builtInFunctions_functions.ts
│ │ ├── builtInFunctions_identifiers.ts
│ │ ├── builtInFunctions_json.ts
│ │ ├── builtInFunctions_maps.ts
│ │ ├── builtInFunctions_maps_get.ts
│ │ ├── builtInFunctions_math.ts
│ │ ├── builtInFunctions_node.ts
│ │ ├── builtInFunctions_numeric.ts
│ │ ├── builtInFunctions_operators.ts
│ │ ├── builtInFunctions_qnames.ts
│ │ ├── builtInFunctions_sequences.ts
│ │ ├── builtInFunctions_sequences_deepEqual.ts
│ │ ├── builtInFunctions_string.ts
│ │ ├── convertItemsToCommonType.ts
│ │ ├── functionRegistry.ts
│ │ ├── generateId.ts
│ │ └── isSameMapKey.ts
│ ├── literals
│ │ └── Literal.ts
│ ├── maps
│ │ └── MapConstructor.ts
│ ├── operators
│ │ ├── IntersectExcept.ts
│ │ ├── SequenceOperator.ts
│ │ ├── SimpleMapOperator.ts
│ │ ├── Union.ts
│ │ ├── UniversalExpression.ts
│ │ ├── arithmetic
│ │ │ ├── BinaryEvaluationFunctionMap.ts
│ │ │ ├── BinaryOperator.ts
│ │ │ └── Unary.ts
│ │ ├── boolean
│ │ │ ├── AndOperator.ts
│ │ │ └── OrOperator.ts
│ │ ├── compares
│ │ │ ├── GeneralCompare.ts
│ │ │ ├── NodeCompare.ts
│ │ │ ├── ValueCompare.ts
│ │ │ └── arePointersEqual.ts
│ │ └── types
│ │ │ ├── CastAsOperator.ts
│ │ │ ├── CastableAsOperator.ts
│ │ │ └── InstanceOfOperator.ts
│ ├── path
│ │ ├── AbsolutePathExpression.ts
│ │ ├── ContextItemExpression.ts
│ │ └── PathExpression.ts
│ ├── postfix
│ │ ├── Filter.ts
│ │ ├── Lookup.ts
│ │ ├── UnaryLookup.ts
│ │ └── evaluateLookup.ts
│ ├── quantified
│ │ └── QuantifiedExpression.ts
│ ├── staticallyKnownNamespaces.ts
│ ├── tests
│ │ ├── KindTest.ts
│ │ ├── NameTest.ts
│ │ ├── PITest.ts
│ │ ├── TestAbstractExpression.ts
│ │ └── TypeTest.ts
│ ├── util
│ │ ├── Bucket.ts
│ │ ├── Random.ts
│ │ ├── atomizeSequence.ts
│ │ ├── concatSequences.ts
│ │ ├── createChildGenerator.ts
│ │ ├── createDescendantGenerator.ts
│ │ ├── createDoublyIterableSequence.ts
│ │ ├── createSingleValueIterator.ts
│ │ ├── iterators.ts
│ │ ├── sequenceEvery.ts
│ │ ├── sortedSequenceUtils.ts
│ │ └── zipSingleton.ts
│ ├── xquery-update
│ │ ├── DeleteExpression.ts
│ │ ├── IPendingUpdate.ts
│ │ ├── InsertExpression.ts
│ │ ├── RenameExpression.ts
│ │ ├── ReplaceExpression.ts
│ │ ├── TransformExpression.ts
│ │ ├── UpdatingExpression.ts
│ │ ├── UpdatingFunctionDefinitionType.ts
│ │ ├── UpdatingFunctionValue.ts
│ │ ├── XQueryUpdateFacilityErrors.ts
│ │ ├── applyPulPrimitives.ts
│ │ ├── createPendingUpdateFromTransferable.ts
│ │ ├── pendingUpdates
│ │ │ ├── DeletePendingUpdate.ts
│ │ │ ├── InsertAfterPendingUpdate.ts
│ │ │ ├── InsertAttributesPendingUpdate.ts
│ │ │ ├── InsertBeforePendingUpdate.ts
│ │ │ ├── InsertIntoAsFirstPendingUpdate.ts
│ │ │ ├── InsertIntoAsLastPendingUpdate.ts
│ │ │ ├── InsertIntoPendingUpdate.ts
│ │ │ ├── InsertPendingUpdate.ts
│ │ │ ├── RenamePendingUpdate.ts
│ │ │ ├── ReplaceElementContentPendingUpdate.ts
│ │ │ ├── ReplaceNodePendingUpdate.ts
│ │ │ └── ReplaceValuePendingUpdate.ts
│ │ ├── pulPrimitives.ts
│ │ └── pulRoutines.ts
│ └── xquery
│ │ ├── AttributeConstructor.ts
│ │ ├── CommentConstructor.ts
│ │ ├── ElementConstructor.ts
│ │ ├── ElementConstructorContent.ts
│ │ ├── PIConstructor.ts
│ │ ├── SwitchExpression.ts
│ │ ├── TextConstructor.ts
│ │ ├── TypeSwitchExpression.ts
│ │ ├── XQueryErrors.ts
│ │ └── nameExpression.ts
├── getBuckets.ts
├── index.ts
├── jsCodegen
│ ├── CodeGenContext.ts
│ ├── JavaScriptCompiledXPath.ts
│ ├── compileAstToJavaScript.ts
│ ├── compileXPathToJavaScript.ts
│ ├── emitAxis.ts
│ ├── emitBaseExpr.ts
│ ├── emitCompare.ts
│ ├── emitFunctionCallExpr.ts
│ ├── emitHelpers.ts
│ ├── emitLiterals.ts
│ ├── emitLogicalExpr.ts
│ ├── emitOperand.ts
│ ├── emitPathExpr.ts
│ ├── emitTest.ts
│ ├── escapeJavaScriptString.ts
│ ├── executeJavaScriptCompiledXPath.ts
│ └── runtimeLib.ts
├── nodesFactory
│ ├── DomBackedNodesFactory.ts
│ ├── INodesFactory.ts
│ ├── ISimpleNodesFactory.ts
│ └── wrapExternalNodesFactory.ts
├── parseScript.ts
├── parsing
│ ├── astHelper.ts
│ ├── compileAstToExpression.ts
│ ├── compiledExpressionCache.ts
│ ├── convertXDMReturnValue.ts
│ ├── convertXmlToAst.ts
│ ├── evaluableExpressionToString.ts
│ ├── globalModuleCache.ts
│ ├── literalParser.ts
│ ├── nameParser.ts
│ ├── normalizeEndOfLines.ts
│ ├── parseExpression.ts
│ ├── parsingFunctions.ts
│ ├── parsingUtils.ts
│ ├── processProlog.ts
│ ├── prscParser.ts
│ ├── staticallyCompileXPath.ts
│ ├── tokens.ts
│ ├── typesParser.ts
│ └── whitespaceParser.ts
├── performance.ts
├── precompileXPath.ts
├── registerCustomXPathFunction.ts
├── registerXQueryModule.ts
├── transformXPathItemToJavascriptObject.ts
├── typeInference
│ ├── AnnotationContext.ts
│ ├── README.md
│ ├── annotateAST.ts
│ ├── annotateArrayConstructor.ts
│ ├── annotateArrowExpr.ts
│ ├── annotateBinaryOperator.ts
│ ├── annotateCastOperators.ts
│ ├── annotateCompareOperator.ts
│ ├── annotateContextItemExpr.ts
│ ├── annotateDynamicFunctionInvocationExpr.ts
│ ├── annotateFlworExpression.ts
│ ├── annotateFunctionCall.ts
│ ├── annotateIfThenElseExpr.ts
│ ├── annotateInstanceOfExpr.ts
│ ├── annotateLogicalOperator.ts
│ ├── annotateMapConstructor.ts
│ ├── annotateNamedFunctionRef.ts
│ ├── annotatePathExpr.ts
│ ├── annotateQuantifiedExpr.ts
│ ├── annotateRangeSequenceOperator.ts
│ ├── annotateSequenceOperator.ts
│ ├── annotateSetOperators.ts
│ ├── annotateSimpleMapExpr.ts
│ ├── annotateStringConcatenateOperator.ts
│ ├── annotateTypeSwitchOperator.ts
│ ├── annotateUnaryLookup.ts
│ ├── annotateUnaryOperator.ts
│ └── annotateVarRef.ts
└── types
│ ├── Options.ts
│ ├── Types.ts
│ └── createTypedValueFactory.ts
├── test
├── .eslintrc.js
├── assets
│ ├── failingXQUTSXQueryXTestNames.csv
│ ├── failingXQueryXTestNames.csv
│ ├── jsCodeGenReport.csv
│ ├── overrides
│ │ └── XQUTS
│ │ │ └── Queries
│ │ │ └── XQueryX
│ │ │ ├── RenameExpressions
│ │ │ └── complex-renames-q8-test2.xqx
│ │ │ └── ReplaceExpressions
│ │ │ ├── complex-replacevalues-q1.xqx
│ │ │ ├── complex-replacevalues-q10-test2.xqx
│ │ │ ├── complex-replacevalues-q11-test2.xqx
│ │ │ ├── complex-replacevalues-q13-test2.xqx
│ │ │ ├── complex-replacevalues-q2.xqx
│ │ │ ├── complex-replacevalues-q5-test2.xqx
│ │ │ └── complex-replacevalues-q8-test2.xqx
│ ├── runnableTestSets.csv
│ ├── unrunnableTestCases.csv
│ └── unrunnableXQUTSTestCases.csv
├── browsertests.ts
├── corpusfuzzertests.ts
├── helpers
│ ├── evaluateXPathToAsyncSingleton.ts
│ ├── getPerformanceTests.ts
│ ├── getSkippedTests.ts
│ ├── jsonMlMapper.ts
│ ├── qt3TestsTools.ts
│ └── testFs.ts
├── install-assets.ps1
├── install-assets.sh
├── qt3tests.ts
├── qt3testsBenchmark.ts
├── qt3testsXQueryX.ts
├── specs
│ ├── DomFacade.tests.ts
│ ├── annotation.tests.ts
│ ├── expressions
│ │ ├── Specificity.tests.ts
│ │ ├── adaptJavaScriptValueToXPathValue.tests.ts
│ │ ├── dataTypes
│ │ │ ├── castToType.tests.ts
│ │ │ ├── documentOrderUtils.tests.ts
│ │ │ └── valueTypes
│ │ │ │ ├── DateTime.tests.ts
│ │ │ │ └── Duration.tests.ts
│ │ ├── functions
│ │ │ ├── argumentListToString.tests.ts
│ │ │ └── functionRegistry.tests.ts
│ │ ├── jsCodegen
│ │ │ ├── compare.tests.ts
│ │ │ └── string.tests.ts
│ │ ├── postfix
│ │ │ └── Filter.tests.ts
│ │ └── util
│ │ │ └── concatSequences.tests.ts
│ ├── parsing
│ │ ├── LetExpression.tests.ts
│ │ ├── VarRef.tests.ts
│ │ ├── arrays
│ │ │ ├── ArrayConstructor.tests.ts
│ │ │ ├── arrayFunctions.tests.ts
│ │ │ └── arrayPredicates.tests.ts
│ │ ├── asyncXPath.xq
│ │ ├── axes
│ │ │ ├── AncestorAxis.tests.ts
│ │ │ ├── AttributeAxis.tests.ts
│ │ │ ├── ChildAxis.tests.ts
│ │ │ ├── DescendantAxis.tests.ts
│ │ │ ├── FollowingAxis.tests.ts
│ │ │ ├── FollowingSiblingAxis.tests.ts
│ │ │ ├── ParentAxis.tests.ts
│ │ │ ├── PrecedingAxis.tests.ts
│ │ │ ├── PrecedingSibling.tests.ts
│ │ │ └── SelfAxis.tests.ts
│ │ ├── comments
│ │ │ └── comments.tests.ts
│ │ ├── compareSpecificity.tests.ts
│ │ ├── conditional
│ │ │ └── IfExpression.tests.ts
│ │ ├── createSelectorFromXPath.tests.ts
│ │ ├── createSelectorFromXPathAsync.tests.ts
│ │ ├── debug
│ │ │ └── stackTrace.tests.ts
│ │ ├── deprecatedFeatures.tests.ts
│ │ ├── evaluateXPath.tests.ts
│ │ ├── flwor.tests.ts
│ │ ├── forExpression.tests.ts
│ │ ├── functions
│ │ │ ├── FunctionCall.tests.ts
│ │ │ ├── builtInFunctions.dataTypeConstructors.tests.ts
│ │ │ ├── builtInFunctions.functions.tests.ts
│ │ │ ├── builtInFunctions.math.tests.ts
│ │ │ ├── builtinFunctions.context.tests.ts
│ │ │ ├── builtinFunctions.datetime.tests.ts
│ │ │ ├── builtinFunctions.duration.tests.ts
│ │ │ ├── builtinFunctions.sequences.tests.ts
│ │ │ ├── functionPlaceholder.tests.ts
│ │ │ ├── functions.boolean.tests.ts
│ │ │ ├── functions.debugging.tests.ts
│ │ │ ├── functions.error.tests.ts
│ │ │ ├── functions.fontoxpath.tests.ts
│ │ │ ├── functions.json.tests.ts
│ │ │ ├── functions.node.tests.ts
│ │ │ ├── functions.numeric.tests.ts
│ │ │ ├── functions.qnames.tests.ts
│ │ │ ├── functions.string.tests.ts
│ │ │ ├── functions.tests.ts
│ │ │ └── inlineFunctions.tests.ts
│ │ ├── getBucketForSelector.tests.ts
│ │ ├── getBucketsForNode.tests.ts
│ │ ├── indexOf.tests.ts
│ │ ├── jsCodegen
│ │ │ ├── astRejection.tests.ts
│ │ │ ├── axes.tests.ts
│ │ │ ├── blns.tests.ts
│ │ │ ├── compare.tests.ts
│ │ │ ├── compareAttributes.tests.ts
│ │ │ ├── evaluateXPathWithJsCodegen.ts
│ │ │ ├── functionCall.tests.ts
│ │ │ ├── nodeTests.tests.ts
│ │ │ ├── operators.tests.ts
│ │ │ ├── pathExpr.tests.ts
│ │ │ ├── predicates.tests.ts
│ │ │ ├── returnValues.tests.ts
│ │ │ └── wildcard.tests.ts
│ │ ├── literals
│ │ │ └── Literal.tests.ts
│ │ ├── mainModules.tests.ts
│ │ ├── maps
│ │ │ ├── MapConstructor.tests.ts
│ │ │ └── mapFunctions.tests.ts
│ │ ├── operators
│ │ │ ├── CastableAsOperator.tests.ts
│ │ │ ├── IntersectExcept.tests.ts
│ │ │ ├── SimpleMapOperator.tests.ts
│ │ │ ├── UnionOperator.tests.ts
│ │ │ ├── boolean
│ │ │ │ ├── AndOperator.tests.ts
│ │ │ │ ├── OrOperator.tests.ts
│ │ │ │ └── operatorPrecedence.tests.ts
│ │ │ ├── castOperator.tests.ts
│ │ │ ├── compares
│ │ │ │ └── Compare.tests.ts
│ │ │ ├── numeric
│ │ │ │ ├── BinaryOperator.tests.ts
│ │ │ │ └── Unary.tests.ts
│ │ │ ├── sequenceOperator.tests.ts
│ │ │ ├── stringConcat.tests.ts
│ │ │ └── types
│ │ │ │ └── InstanceOfOperator.tests.ts
│ │ ├── parseScript.tests.ts
│ │ ├── path
│ │ │ ├── AbsolutePathExpression.tests.ts
│ │ │ └── PathExpression.tests.ts
│ │ ├── performance.tests.ts
│ │ ├── postfix
│ │ │ ├── Filter.tests.ts
│ │ │ └── Lookup.tests.ts
│ │ ├── predicates
│ │ │ └── predicates.tests.ts
│ │ ├── quantified
│ │ │ └── QuantifiedExpression.tests.ts
│ │ ├── registerCustomXPathFunction.tests.ts
│ │ ├── registerXQueryModule.tests.ts
│ │ ├── tests
│ │ │ ├── KindTest.tests.ts
│ │ │ ├── NameTest.tests.ts
│ │ │ └── PITest.tests.ts
│ │ ├── usesHints.tests.ts
│ │ ├── xquery-updating
│ │ │ ├── DeleteExpression.tests.ts
│ │ │ ├── InsertExpression.tests.ts
│ │ │ ├── RenameExpression.tests.ts
│ │ │ ├── ReplaceExpression.tests.ts
│ │ │ ├── TransformExpression.tests.ts
│ │ │ ├── assertUpdateList.ts
│ │ │ ├── evaluateUpdatingExpression.tests.ts
│ │ │ ├── evaluateUpdatingExpressionSync.tests.ts
│ │ │ ├── executePendingUpdateList.tests.ts
│ │ │ └── updatingFunctions.tests.ts
│ │ └── xquery
│ │ │ ├── AttributeConstructor.tests.ts
│ │ │ ├── DefaultElementNamespace.tests.ts
│ │ │ ├── DefaultFunctionNamespace.tests.ts
│ │ │ ├── ElementConstructor.tests.ts
│ │ │ ├── PIConstructor.tests.ts
│ │ │ ├── SwitchExpression.tests.ts
│ │ │ ├── TextConstructor.tests.ts
│ │ │ ├── Typeswitch.tests.ts
│ │ │ └── VariableDeclaration.tests.ts
│ └── types
│ │ └── createTypedValueFactory.tests.ts
├── testCodegenFacade.ts
├── testhook.js
├── tsconfig.json
├── tslint.json
├── xQueryXUtils.ts
├── xqutsTests.ts
└── xqutsTestsXQueryX.ts
└── tsconfig.json
/.bundlemonrc:
--------------------------------------------------------------------------------
1 | {
2 | "baseDir": "./dist",
3 | "files": [
4 | {
5 | "path": "fontoxpath.js"
6 | },
7 | {
8 | "path": "fontoxpath.esm.js"
9 | }
10 | ],
11 | "reportOutput": [
12 | "github"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "npm"
4 | directory: "/"
5 | schedule:
6 | interval: "monthly"
7 |
--------------------------------------------------------------------------------
/.github/workflows/bundlesize.yml:
--------------------------------------------------------------------------------
1 | name: Check bundle size
2 | on:
3 | push:
4 | branches: ['master']
5 | pull_request:
6 | types: [synchronize, opened, reopened]
7 |
8 | jobs:
9 | build:
10 | name: Compute bundle size
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-node@v1
15 | with:
16 | node-version: '20'
17 | - name: Install dependencies
18 | run: npm ci
19 | - name: override CI_COMMIT_SHA
20 | if: github.event_name == 'pull_request'
21 | run: echo "CI_COMMIT_SHA=${{ github.event.pull_request.head.sha}}" >> $GITHUB_ENV
22 |
23 | - name: Run BundleMon
24 | run: npx bundlemon
25 |
--------------------------------------------------------------------------------
/.github/workflows/download-qt3/action.yml:
--------------------------------------------------------------------------------
1 | runs:
2 | using: "composite"
3 | steps:
4 | - run: mkdir -p ./test/assets/XQUTS ./test/assets/QT3TS
5 | shell: bash
6 | - run: curl -L https://github.com/LeoWoerteler/QT3TS/archive/master.tar.gz | tar -xz -C ./test/assets/QT3TS --strip-components=1
7 | shell: bash
8 | - run: curl -L https://github.com/LeoWoerteler/XQUTS/archive/master.tar.gz | tar -xz -C ./test/assets/XQUTS --strip-components=1
9 | shell: bash
10 | - run: unzip -q test/assets/QT3TS/xqueryx.zip -d ./test/assets/QT3TS/
11 | shell: bash
12 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | name: Linter
2 | on:
3 | - push
4 | - pull_request
5 | jobs:
6 | test:
7 | name: Linting
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - uses: actions/setup-node@v1
12 | with:
13 | node-version: 16
14 | - name: Install dependencies
15 | run: npm ci;
16 | - name: Linter
17 | run: npm run lint;
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /node_modules
3 | /performance/lib
4 | /test/assets/XQUTS
5 | /test/assets/runnablePerformanceTestNames.csv
6 | /test/built
7 | /built
8 | /demo/built
9 | /dist
10 | /.nyc_output
11 | /tmp
12 | /.tscc_temp
13 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "test/assets/qt3tests"]
2 | path = test/assets/qt3tests
3 | url = git@github.com:w3c/qt3tests.git
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | tag-version-prefix=""
2 |
--------------------------------------------------------------------------------
/.nycrc:
--------------------------------------------------------------------------------
1 | {
2 | "require": "ts-node/register",
3 | "extension": [
4 | ".js",
5 | ".ts"
6 | ],
7 | "reporter": [
8 | "text-summary",
9 | "html",
10 | "lcov"
11 | ],
12 | "instrument": true,
13 | "sourceMap": true,
14 | "include": [
15 | "src/**/*.ts",
16 | "test/**/*.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "singleQuote": true,
4 | "tabWidth": 4,
5 | "useTabs": true
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "liveshare.features": "experimental",
3 | "liveshare.languages.allowGuestCommandControl": true,
4 | "files.watcherExclude": {
5 | "**/built/**": true,
6 | "**/coverage/**": true,
7 | "**/dist/**": true
8 | },
9 | "mocha.enabled": false,
10 | "typescript.preferences.importModuleSpecifier": "relative"
11 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Fonto Group BV, Martin Middel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/api-extractor.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3 | "compiler": {
4 | "tsconfigFilePath": "./tsconfig.json"
5 | },
6 | "apiReport": {
7 | "enabled": false
8 | },
9 | "docModel": {
10 | "enabled": true,
11 | "apiJsonFilePath": "dist/fontoxpath.api.json"
12 |
13 | },
14 | "mainEntryPointFilePath": "built/index.d.ts",
15 | "dtsRollup": {
16 | "enabled": true,
17 | "untrimmedFilePath": "dist/fontoxpath.d.ts"
18 | },
19 | "messages": {
20 | "compilerMessageReporting": {
21 | "default": {
22 | "logLevel": "warning"
23 | }
24 | },
25 | "extractorMessageReporting": {
26 | "default": {
27 | "logLevel": "warning"
28 | }
29 | },
30 | "tsdocMessageReporting": {
31 | "default": {
32 | "logLevel": "warning"
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /web_modules
3 | /built
4 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "start": "vite"
4 | },
5 | "dependencies": {
6 | "typescript": "^4.5.5",
7 | "vite": "^5.4.9"
8 | },
9 | "devDependencies": {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "lib": ["dom"],
5 | "outDir": "./built",
6 | "baseUrl": "..",
7 | "target": "es2017",
8 | "module": "esnext",
9 | "inlineSourceMap": true,
10 | "inlineSources": true,
11 | "moduleResolution": "node",
12 | "plugins": [
13 | {
14 | "transform": "ts-transform-import-path-rewrite",
15 | "import": "transform",
16 | "alias": {
17 | "^(fontoxpath)$": "/src",
18 | "^(xspattern)$": "/web_modules/xspattern.js",
19 | "^(prsc)$": "/web_modules/prsc.js"
20 | },
21 | "after": true,
22 | "afterDeclarations": true,
23 | "type": "config"
24 | },
25 | {
26 | "transform": "@zoltu/typescript-transformer-append-js-extension/output/index.js",
27 | "after": true
28 | }
29 | ]
30 | },
31 | "include": ["../src/**/*", "./main.ts"]
32 | }
33 |
--------------------------------------------------------------------------------
/demo/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "warning",
3 | "extends": ["../test/tslint.json"],
4 | "rules": {
5 | "no-console": false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/fuzzers/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "lib": [
5 | "ES2020"
6 | ],
7 | "paths": {
8 | "fontoxpath": [
9 | "../dist/fontoxpath.esm.js"
10 | ]
11 | },
12 | "strict": false,
13 | "allowJs": true,
14 | "types": [
15 | "mocha",
16 | "node"
17 | ],
18 | "moduleResolution": "node",
19 | "target": "es2020",
20 | "esModuleInterop": true
21 | },
22 | "include": [
23 | "./**/*.ts"
24 | ]
25 | }
--------------------------------------------------------------------------------
/fuzzers/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "warning",
3 | "extends": ["../test/tslint.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/fuzzers/xpath-31-function-catalog-corpus.ts:
--------------------------------------------------------------------------------
1 | import { ICorpusLoader } from 'corpus_based_fuzzer';
2 | import { evaluateXPathToStrings } from 'fontoxpath';
3 | import fs from 'fs';
4 | import { parseXmlDocument } from 'slimdom';
5 |
6 | export default new (class XPath31FunctionCatalog implements ICorpusLoader {
7 | name: string = 'xpath-31-function-catalog';
8 |
9 | get(): string[] {
10 | // Load corpus from the W3C XPath standard
11 | const specDoc = parseXmlDocument(fs.readFileSync('./fuzzers/xpath-31-function-catalog.xml', 'utf8'));
12 | return evaluateXPathToStrings(
13 | '//Q{http://www.w3.org/xpath-functions/spec/namespace}expression/text()',
14 | specDoc
15 | );
16 | }
17 | })();
18 |
--------------------------------------------------------------------------------
/karma.conf.ts:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | config.set({
3 | frameworks: ['mocha', 'chai', 'karma-typescript'],
4 | files: ['src/**/*.ts', 'test/browsertests.ts'],
5 | preprocessors: {
6 | 'src/**/*.ts': 'karma-typescript',
7 | 'test/browsertests.ts': 'karma-typescript',
8 | },
9 | reporters: ['progress'],
10 | port: 9876, // karma web server port
11 | colors: true,
12 | logLevel: config.LOG_INFO,
13 | browsers: ['ChromeHeadless', 'FirefoxHeadless'],
14 | autoWatch: false,
15 | singleRun: true,
16 | concurrency: Infinity,
17 | karmaTypescriptConfig: {
18 | tsconfig: 'test/tsconfig.json',
19 | },
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/performance/evaluateXPath.benchmark.ts:
--------------------------------------------------------------------------------
1 | import benchmarkRunner from '@fontoxml/fonto-benchmark-runner';
2 | import { Document } from 'slimdom';
3 | import { domFacade, evaluateXPath } from '../src/index';
4 |
5 | let document: Document;
6 |
7 | function setup() {
8 | document = new Document();
9 | }
10 |
11 | benchmarkRunner.addBenchmark(
12 | 'evaluateXPath',
13 | () => {
14 | evaluateXPath('true()', document, domFacade);
15 | },
16 | setup
17 | );
18 |
--------------------------------------------------------------------------------
/performance/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "warning",
3 | "extends": ["../test/tslint.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/performance/union.benchmark.ts:
--------------------------------------------------------------------------------
1 | import benchmarkRunner from '@fontoxml/fonto-benchmark-runner';
2 | import { Document, parseXmlDocument } from 'slimdom';
3 | import { domFacade, evaluateXPath } from '../src/index';
4 |
5 | let document: Document;
6 |
7 | function setup() {
8 | document = parseXmlDocument(`
9 |
10 | xpath.playground.fontoxml.com
11 | This is a learning tool for XML, XPath and XQuery.
12 |
13 | ${new Array(1000).fill('with some content')}
14 |
15 | `);
16 | }
17 |
18 | benchmarkRunner.addBenchmark(
19 | 'union',
20 | () => {
21 | evaluateXPath('//tip | reverse(//tip)', document, domFacade);
22 | },
23 | setup
24 | );
25 |
--------------------------------------------------------------------------------
/performance/utils/loadFile.ts:
--------------------------------------------------------------------------------
1 | declare var window: any;
2 |
3 | export default async function loadFile(filename: string): Promise {
4 | if (typeof window !== 'undefined' && window.fetch) {
5 | const request = new window.Request(`${window.location}${filename}`);
6 | const response = await window.fetch(request);
7 | return response.text();
8 | } else {
9 | const fs = await import('fs');
10 | return fs.readFileSync(filename).toString();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/documentWriter/IDocumentWriter.ts:
--------------------------------------------------------------------------------
1 | import { Document, Element, Node } from '../types/Types';
2 |
3 | /**
4 | * @public
5 | */
6 | export default interface IDocumentWriter {
7 | insertBefore(parent: Element | Document, newNode: Node, referenceNode: Node | null): void;
8 | removeAttributeNS(node: Element, namespace: string, name: string): void;
9 | removeChild(parent: Element | Document, child: Node): void;
10 | setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
11 | setData(node: Node, data: string): void;
12 | }
13 |
--------------------------------------------------------------------------------
/src/documentWriter/domBackedDocumentWriter.ts:
--------------------------------------------------------------------------------
1 | import IDocumentWriter from './IDocumentWriter';
2 |
3 | class DomBackedDocumentWriter implements IDocumentWriter {
4 | public insertBefore(parent: Element, newNode: Node, referenceNode: Node) {
5 | return parent['insertBefore'](newNode, referenceNode);
6 | }
7 |
8 | public removeAttributeNS(node: Element, namespace: string, name: string) {
9 | return node['removeAttributeNS'](namespace, name);
10 | }
11 |
12 | public removeChild(parent: Element, child: Node) {
13 | return parent['removeChild'](child);
14 | }
15 |
16 | public setAttributeNS(node: Element, namespace: string, name: string, value: string) {
17 | node['setAttributeNS'](namespace, name, value);
18 | }
19 |
20 | public setData(node: Comment | Text | ProcessingInstruction, data: string) {
21 | node['data'] = data;
22 | }
23 | }
24 |
25 | export default new DomBackedDocumentWriter();
26 |
--------------------------------------------------------------------------------
/src/documentWriter/wrapExternalDocumentWriter.ts:
--------------------------------------------------------------------------------
1 | import { Document, Element, Node } from '../types/Types';
2 | import IDocumentWriter from './IDocumentWriter';
3 | class WrappingDocumentWriter implements IDocumentWriter {
4 | private _externalDocumentWriter: IDocumentWriter;
5 |
6 | constructor(externalDocumentWriter: IDocumentWriter) {
7 | this._externalDocumentWriter = externalDocumentWriter;
8 | }
9 |
10 | public insertBefore(parent: Element | Document, newNode: Node, referenceNode: Node) {
11 | return this._externalDocumentWriter['insertBefore'](parent, newNode, referenceNode);
12 | }
13 |
14 | public removeAttributeNS(node: Element, namespace: string, name: string) {
15 | return this._externalDocumentWriter['removeAttributeNS'](node, namespace, name);
16 | }
17 |
18 | public removeChild(parent: Element | Document, child: Node) {
19 | return this._externalDocumentWriter['removeChild'](parent, child);
20 | }
21 |
22 | public setAttributeNS(node: Element, namespace: string, name: string, value: string) {
23 | this._externalDocumentWriter['setAttributeNS'](node, namespace, name, value);
24 | }
25 |
26 | public setData(node: Node, data: string) {
27 | this._externalDocumentWriter['setData'](node, data);
28 | }
29 | }
30 |
31 | export default function wrapExternalDocumentWriter(externalDocumentWriter: IDocumentWriter) {
32 | return new WrappingDocumentWriter(externalDocumentWriter);
33 | }
34 |
--------------------------------------------------------------------------------
/src/evaluateXPathToArray.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { Options } from './types/Options';
4 |
5 | /**
6 | * Evaluates an XPath on the given contextNode. Returns the result as an array, if the result is an XPath array.
7 | *
8 | * @public
9 | *
10 | * @param selector - The selector to execute. Supports XPath 3.1.
11 | * @param contextItem - The node from which to run the XPath.
12 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
13 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
14 | * @param options - Extra options for evaluating this XPath.
15 | *
16 | * @returns The array result, as a JavaScript array with atomized values.
17 | */
18 | export default function evaluateXPathToArray(
19 | selector: EvaluableExpression,
20 | contextItem?: any | null,
21 | domFacade?: IDomFacade | null,
22 | variables?: { [s: string]: any } | null,
23 | options?: Options | null,
24 | ): any[] {
25 | return evaluateXPath(
26 | selector,
27 | contextItem,
28 | domFacade,
29 | variables,
30 | evaluateXPath.ARRAY_TYPE,
31 | options,
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/evaluateXPathToAsyncIterator.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { Options } from './types/Options';
4 |
5 | /**
6 | * Evaluates an XPath on the given contextNode. Returns the result as an async iterator
7 | *
8 | * @public
9 | *
10 | * @param selector - The selector to execute. Supports XPath 3.1.
11 | * @param contextItem - The node from which to run the XPath.
12 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
13 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
14 | * @param options - Extra options for evaluating this XPath.
15 | *
16 | * @returns An async iterator to the return values.
17 | */
18 | export default function evaluateXPathToAsyncIterator(
19 | selector: EvaluableExpression,
20 | contextItem?: any | null,
21 | domFacade?: IDomFacade | null,
22 | variables?: { [s: string]: any } | null,
23 | options?: Options | null,
24 | ): AsyncIterableIterator {
25 | return evaluateXPath(
26 | selector,
27 | contextItem,
28 | domFacade,
29 | variables,
30 | evaluateXPath.ASYNC_ITERATOR_TYPE,
31 | options,
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/evaluateXPathToBoolean.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { Options } from './types/Options';
4 |
5 | /**
6 | * Evaluates an XPath on the given contextNode.
7 | *
8 | * @public
9 | *
10 | * @param selector - The selector to execute. Supports XPath 3.1.
11 | * @param contextItem - The node from which to run the XPath.
12 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
13 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
14 | * @param options - Extra options for evaluating this XPath.
15 | *
16 | * @returns A boolean result
17 | */
18 | export default function evaluateXPathToBoolean(
19 | selector: EvaluableExpression,
20 | contextItem?: any | null,
21 | domFacade?: IDomFacade | null,
22 | variables?: { [s: string]: any } | null,
23 | options?: Options | null,
24 | ): boolean {
25 | return evaluateXPath(
26 | selector,
27 | contextItem,
28 | domFacade,
29 | variables,
30 | evaluateXPath.BOOLEAN_TYPE,
31 | options,
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/evaluateXPathToFirstNode.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { ReturnType } from './parsing/convertXDMReturnValue';
4 | import { Options } from './types/Options';
5 | import { Node } from './types/Types';
6 |
7 | /**
8 | * Evaluates an XPath on the given contextNode. Returns the first node result.
9 | *
10 | * @public
11 | *
12 | * @param selector - The selector to execute. Supports XPath 3.1.
13 | * @param contextItem - The node from which to run the XPath.
14 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
15 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
16 | * @param options - Extra options for evaluating this XPath.
17 | *
18 | * @returns The first matching node, in the order defined by the XPath.
19 | */
20 | export default function evaluateXPathToFirstNode(
21 | selector: EvaluableExpression,
22 | contextItem?: any | null,
23 | domFacade?: IDomFacade | null,
24 | variables?: { [s: string]: any } | null,
25 | options?: Options | null,
26 | ): T | null {
27 | return evaluateXPath(
28 | selector,
29 | contextItem,
30 | domFacade,
31 | variables,
32 | evaluateXPath.FIRST_NODE_TYPE,
33 | options,
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/evaluateXPathToMap.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { Options } from './types/Options';
4 |
5 | /**
6 | * Evaluates an XPath on the given contextNode. Returns the result as a map, if the result is an XPath map.
7 | *
8 | * @public
9 | *
10 | * @param selector - The selector to execute. Supports XPath 3.1.
11 | * @param contextItem - The node from which to run the XPath.
12 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
13 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
14 | * @param options - Extra options for evaluating this XPath.
15 | *
16 | * @returns The map result, as an object. Because of JavaScript
17 | * constraints, key 1 and '1' are the same. The values in this map are
18 | * the JavaScript simple types. See evaluateXPath for more details in
19 | * mapping types.
20 | */
21 | export default function evaluateXPathToMap(
22 | selector: EvaluableExpression,
23 | contextItem?: any | null,
24 | domFacade?: IDomFacade | null,
25 | variables?: { [s: string]: any } | null,
26 | options?: Options | null,
27 | ): { [s: string]: any } {
28 | return evaluateXPath(
29 | selector,
30 | contextItem,
31 | domFacade,
32 | variables,
33 | evaluateXPath.MAP_TYPE,
34 | options,
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/evaluateXPathToNodes.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { ReturnType } from './parsing/convertXDMReturnValue';
4 | import { Options } from './types/Options';
5 | import { Node } from './types/Types';
6 |
7 | /**
8 | * Evaluates an XPath on the given contextNode. Returns all nodes the XPath resolves to.
9 | * Returns result in the order defined in the XPath. The path operator ('/'), the union operator ('union' and '|') will sort.
10 | * This implies (//A, //B) resolves to all A nodes, followed by all B nodes, both in document order, but not merged.
11 | * However: (//A | //B) resolves to all A and B nodes in document order.
12 | *
13 | * @public
14 | *
15 | * @param selector - The selector to execute. Supports XPath 3.1.
16 | * @param contextItem - The node from which to run the XPath.
17 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
18 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
19 | * @param options - Extra options for evaluating this XPath.
20 | *
21 | * @returns All matching Nodes, in the order defined by the XPath.
22 | */
23 | export default function evaluateXPathToNodes(
24 | selector: EvaluableExpression,
25 | contextItem?: any | null,
26 | domFacade?: IDomFacade | null,
27 | variables?: { [s: string]: any } | null,
28 | options?: Options | null,
29 | ): T[] {
30 | return evaluateXPath(
31 | selector,
32 | contextItem,
33 | domFacade,
34 | variables,
35 | evaluateXPath.NODES_TYPE,
36 | options,
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/src/evaluateXPathToNumber.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { Options } from './types/Options';
4 |
5 | /**
6 | * Evaluates an XPath on the given contextNode. Returns the numeric result.
7 | *
8 | * @public
9 | *
10 | * @param selector - The selector to execute. Supports XPath 3.1.
11 | * @param contextItem - The node from which to run the XPath.
12 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
13 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
14 | * @param options - Extra options for evaluating this XPath.
15 | *
16 | * @returns The numerical result.
17 | */
18 | export default function evaluateXPathToNumber(
19 | selector: EvaluableExpression,
20 | contextItem?: any | null,
21 | domFacade?: IDomFacade | null,
22 | variables?: { [s: string]: any } | null,
23 | options?: Options | null,
24 | ): number {
25 | return evaluateXPath(
26 | selector,
27 | contextItem,
28 | domFacade,
29 | variables,
30 | evaluateXPath.NUMBER_TYPE,
31 | options,
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/evaluateXPathToNumbers.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { Options } from './types/Options';
4 |
5 | /**
6 | * Evaluates an XPath on the given contextNode. Returns the numeric result.
7 | *
8 | * @public
9 | *
10 | * @param selector - The selector to execute. Supports XPath 3.1.
11 | * @param contextItem - The node from which to run the XPath.
12 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
13 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
14 | * @param options - Extra options for evaluating this XPath.
15 | *
16 | * @returns The numerical results.
17 | */
18 | export default function evaluateXPathToNumbers(
19 | selector: EvaluableExpression,
20 | contextItem?: any | null,
21 | domFacade?: IDomFacade | null,
22 | variables?: { [s: string]: any } | null,
23 | options?: Options | null,
24 | ): number[] {
25 | return evaluateXPath(
26 | selector,
27 | contextItem,
28 | domFacade,
29 | variables,
30 | evaluateXPath.NUMBERS_TYPE,
31 | options,
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/evaluateXPathToString.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { Options } from './types/Options';
4 |
5 | /**
6 | * Evaluates an XPath on the given contextNode. Returns the string result as if the XPath is wrapped in string(...).
7 | *
8 | * @public
9 | *
10 | * @param selector - The selector to execute. Supports XPath 3.1.
11 | * @param contextItem - The node from which to run the XPath.
12 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
13 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
14 | * @param options - Extra options for evaluating this XPath.
15 | *
16 | * @returns The string result.
17 | */
18 | export default function evaluateXPathToString(
19 | selector: EvaluableExpression,
20 | contextItem?: any | null,
21 | domFacade?: IDomFacade | null,
22 | variables?: { [s: string]: any } | null,
23 | options?: Options | null,
24 | ): string {
25 | return evaluateXPath(
26 | selector,
27 | contextItem,
28 | domFacade,
29 | variables,
30 | evaluateXPath.STRING_TYPE,
31 | options,
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/evaluateXPathToStrings.ts:
--------------------------------------------------------------------------------
1 | import IDomFacade from './domFacade/IDomFacade';
2 | import evaluateXPath, { EvaluableExpression } from './evaluateXPath';
3 | import { Options } from './types/Options';
4 |
5 | /**
6 | * Evaluates an XPath on the given contextNode. Returns the string result as if the XPath is wrapped in string(...).
7 | *
8 | * @public
9 | *
10 | * @param selector - The selector to execute. Supports XPath 3.1.
11 | * @param contextItem - The node from which to run the XPath.
12 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
13 | * @param variables - Extra variables (name to value). Values can be number, string, boolean, nodes or object literals and arrays.
14 | * @param options - Extra options for evaluating this XPath.
15 | *
16 | * @returns The string result.
17 | */
18 | export default function evaluateXPathToStrings(
19 | selector: EvaluableExpression,
20 | contextItem?: any | null,
21 | domFacade?: IDomFacade | null,
22 | variables?: { [s: string]: any } | null,
23 | options?: Options | null,
24 | ): string[] {
25 | return evaluateXPath(
26 | selector,
27 | contextItem,
28 | domFacade,
29 | variables,
30 | evaluateXPath.STRINGS_TYPE,
31 | options,
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/evaluationUtils/PositionedError.ts:
--------------------------------------------------------------------------------
1 | import { SourceRange } from '../expressions/debug/StackTraceGenerator';
2 |
3 | export class PositionedError extends Error {
4 | public readonly position: SourceRange;
5 |
6 | constructor(message: string, position: SourceRange) {
7 | super(message);
8 | this.position = {
9 | end: {
10 | column: position.end.column,
11 | line: position.end.line,
12 | offset: position.end.offset,
13 | },
14 | start: {
15 | column: position.start.column,
16 | line: position.start.line,
17 | offset: position.start.offset,
18 | },
19 | };
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/evaluationUtils/convertUpdateResultToTransferable.ts:
--------------------------------------------------------------------------------
1 | import { EvaluableExpression } from '..';
2 | import sequenceFactory from '../expressions/dataTypes/sequenceFactory';
3 | import ExecutionParameters from '../expressions/ExecutionParameters';
4 | import UpdatingExpressionResult from '../expressions/UpdatingExpressionResult';
5 | import convertXDMReturnValue, { IReturnTypes } from '../parsing/convertXDMReturnValue';
6 | import { Node } from '../types/Types';
7 |
8 | export default function convertUpdateResultToTransferable<
9 | TNode extends Node,
10 | TReturnType extends keyof IReturnTypes,
11 | >(
12 | result: UpdatingExpressionResult,
13 | script: EvaluableExpression | string,
14 | returnType: TReturnType,
15 | executionParameters: ExecutionParameters,
16 | ): { pendingUpdateList: object[]; xdmValue: IReturnTypes[TReturnType] } {
17 | return {
18 | ['pendingUpdateList']: result.pendingUpdateList.map((update) =>
19 | update.toTransferable(executionParameters),
20 | ),
21 | ['xdmValue']: convertXDMReturnValue(
22 | script,
23 | sequenceFactory.create(result.xdmValue),
24 | returnType,
25 | executionParameters,
26 | ),
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/src/executePendingUpdateList.ts:
--------------------------------------------------------------------------------
1 | import domBackedDocumentWriter from './documentWriter/domBackedDocumentWriter';
2 | import IDocumentWriter from './documentWriter/IDocumentWriter';
3 | import wrapExternalDocumentWriter from './documentWriter/wrapExternalDocumentWriter';
4 | import DomFacade from './domFacade/DomFacade';
5 | import ExternalDomFacade from './domFacade/ExternalDomFacade';
6 | import IDomFacade from './domFacade/IDomFacade';
7 | import createPendingUpdateFromTransferable from './expressions/xquery-update/createPendingUpdateFromTransferable';
8 | import { applyUpdates } from './expressions/xquery-update/pulRoutines';
9 | import INodesFactory from './nodesFactory/INodesFactory';
10 | import wrapExternalNodesFactory from './nodesFactory/wrapExternalNodesFactory';
11 |
12 | /**
13 | * @public
14 | *
15 | * @param pendingUpdateList - The updateScript to execute.
16 | * @param domFacade - The domFacade (or DomFacade like interface) for retrieving relations.
17 | * @param nodesFactory - The nodesFactory for creating nodes.
18 | * @param documentWriter - The documentWriter for writing changes.
19 | */
20 | export default function executePendingUpdateList(
21 | pendingUpdateList: object[],
22 | domFacade?: IDomFacade,
23 | nodesFactory?: INodesFactory,
24 | documentWriter?: IDocumentWriter,
25 | ): void {
26 | const newDomFacade = new DomFacade(domFacade ? domFacade : new ExternalDomFacade());
27 |
28 | documentWriter = documentWriter
29 | ? wrapExternalDocumentWriter(documentWriter)
30 | : domBackedDocumentWriter;
31 |
32 | nodesFactory = nodesFactory ? (nodesFactory = wrapExternalNodesFactory(nodesFactory)) : null;
33 |
34 | const pul = pendingUpdateList.map(createPendingUpdateFromTransferable);
35 | applyUpdates(pul, null, null, newDomFacade, nodesFactory, documentWriter);
36 | }
37 |
--------------------------------------------------------------------------------
/src/expressions/Context.ts:
--------------------------------------------------------------------------------
1 | import { LexicalQualifiedName, ResolvedQualifiedName } from '../types/Options';
2 | import ISequence from './dataTypes/ISequence';
3 | import DynamicContext from './DynamicContext';
4 | import ExecutionParameters from './ExecutionParameters';
5 | import { FunctionProperties } from './functions/functionRegistry';
6 |
7 | export default interface IContext {
8 | registeredDefaultFunctionNamespaceURI: string | null;
9 | registeredVariableBindingByHashKey: { [s: string]: string }[];
10 | registeredVariableDeclarationByHashKey: {
11 | [hash: string]: (
12 | dynamicContext: DynamicContext,
13 | executionParameters: ExecutionParameters,
14 | ) => ISequence;
15 | };
16 | lookupFunction(
17 | namespaceURI: string,
18 | localName: string,
19 | arity: number,
20 | skipExternal?: boolean,
21 | ): FunctionProperties | null;
22 | lookupVariable(namespaceURI: string | null, localName: string): string | null;
23 | resolveFunctionName(lexicalQName: LexicalQualifiedName, arity: number): ResolvedQualifiedName;
24 | resolveNamespace(prefix: string, useExternalResolver?: boolean): string | null;
25 | }
26 |
--------------------------------------------------------------------------------
/src/expressions/ExecutionParameters.ts:
--------------------------------------------------------------------------------
1 | import IDocumentWriter from '../documentWriter/IDocumentWriter';
2 | import { NodePointer, TinyNode } from '../domClone/Pointer';
3 | import { ConcreteNode } from '../domFacade/ConcreteNode';
4 | import DomFacade from '../domFacade/DomFacade';
5 | import INodesFactory from '../nodesFactory/INodesFactory';
6 | import { Logger } from '../types/Options';
7 |
8 | export default class ExecutionParameters {
9 | constructor(
10 | public readonly debug: boolean,
11 | public readonly disableCache: boolean,
12 | public readonly domFacade: DomFacade,
13 | public readonly nodesFactory: INodesFactory,
14 | public readonly documentWriter: IDocumentWriter,
15 | public readonly currentContext: any,
16 | public readonly rootPointerByRootNodeMap: Map,
17 | public readonly logger?: Logger,
18 | public readonly xmlSerializer?: XMLSerializer,
19 | ) {}
20 | }
21 |
--------------------------------------------------------------------------------
/src/expressions/Specificity.ts:
--------------------------------------------------------------------------------
1 | const SPECIFICITY_DIMENSIONS = ['external', 'attribute', 'nodeName', 'nodeType', 'universal'];
2 | const NUMBER_OF_SPECIFICITY_DIMENSIONS = SPECIFICITY_DIMENSIONS.length;
3 |
4 | class Specificity {
5 | public static ATTRIBUTE_KIND = 'attribute';
6 | public static EXTERNAL_KIND = 'external';
7 | public static NODENAME_KIND = 'nodeName';
8 | public static NODETYPE_KIND = 'nodeType';
9 | public static UNIVERSAL_KIND = 'universal';
10 |
11 | private _counts: number[];
12 |
13 | constructor(countsByKind: { [s: string]: number }) {
14 | this._counts = SPECIFICITY_DIMENSIONS.map((specificityKind) => {
15 | return countsByKind[specificityKind] || 0;
16 | });
17 |
18 | if (Object.keys(countsByKind).some((kind) => !SPECIFICITY_DIMENSIONS.includes(kind))) {
19 | throw new Error('Invalid specificity kind passed');
20 | }
21 | }
22 |
23 | /**
24 | * @return new specificity with the combined counts
25 | */
26 | public add(otherSpecificity: Specificity): Specificity {
27 | const sum = SPECIFICITY_DIMENSIONS.reduce((countsByKind, specificityKind, index) => {
28 | countsByKind[specificityKind] = this._counts[index] + otherSpecificity._counts[index];
29 | return countsByKind;
30 | }, Object.create(null));
31 |
32 | return new Specificity(sum);
33 | }
34 |
35 | /**
36 | * @return -1 if specificity is less than otherSpecificity, 1 if it is greater, 0 if equal
37 | */
38 | public compareTo(otherSpecificity: Specificity): -1 | 0 | 1 {
39 | for (let i = 0; i < NUMBER_OF_SPECIFICITY_DIMENSIONS; ++i) {
40 | if (otherSpecificity._counts[i] < this._counts[i]) {
41 | return 1;
42 | }
43 | if (otherSpecificity._counts[i] > this._counts[i]) {
44 | return -1;
45 | }
46 | }
47 | return 0;
48 | }
49 | }
50 | export default Specificity;
51 |
--------------------------------------------------------------------------------
/src/expressions/UnfocusableDynamicContext.ts:
--------------------------------------------------------------------------------
1 | import sequenceFactory from './dataTypes/sequenceFactory';
2 | import DynamicContext from './DynamicContext';
3 |
4 | type VariableBindings = { variableBindings: { [s: string]: any } };
5 |
6 | export default class UnfocusableDynamicContext extends DynamicContext {
7 | constructor(bindings: VariableBindings) {
8 | super({
9 | contextItem: null,
10 | contextItemIndex: -1,
11 | contextSequence: sequenceFactory.empty(),
12 | variableBindings: bindings.variableBindings,
13 | });
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/expressions/UpdatingExpressionResult.ts:
--------------------------------------------------------------------------------
1 | import Value from './dataTypes/Value';
2 | import { IPendingUpdate } from './xquery-update/IPendingUpdate';
3 | export default class UpdatingExpressionResult {
4 | public pendingUpdateList: IPendingUpdate[];
5 | public xdmValue: Value[];
6 | constructor(values: Value[], pendingUpdateList: IPendingUpdate[]) {
7 | this.xdmValue = values;
8 | this.pendingUpdateList = pendingUpdateList;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/expressions/XPathErrors.ts:
--------------------------------------------------------------------------------
1 | import { ValueType, valueTypeToString } from './dataTypes/Value';
2 |
3 | export const errFORG0001 = (value: string, type: ValueType, reason?: string) =>
4 | new Error(
5 | `FORG0001: Cannot cast ${value} to ${valueTypeToString(type)}${
6 | reason ? `, ${reason}` : ''
7 | }`,
8 | );
9 | export const errXPDY0002 = (message: string) => new Error(`XPDY0002: ${message}`);
10 | export const errXPTY0004 = (message: string) => new Error(`XPTY0004: ${message}`);
11 | export const errFOTY0013 = (type: ValueType) =>
12 | new Error(`FOTY0013: Atomization is not supported for ${valueTypeToString(type)}.`);
13 | export const errXPST0081 = (prefix: string) =>
14 | new Error(`XPST0081: The prefix ${prefix} could not be resolved.`);
15 |
--------------------------------------------------------------------------------
/src/expressions/arrays/CurlyArrayConstructor.ts:
--------------------------------------------------------------------------------
1 | import ArrayValue from '../dataTypes/ArrayValue';
2 | import sequenceFactory from '../dataTypes/sequenceFactory';
3 | import { SequenceType } from '../dataTypes/Value';
4 | import DynamicContext from '../DynamicContext';
5 | import ExecutionParameters from '../ExecutionParameters';
6 | import Expression from '../Expression';
7 | import Specificity from '../Specificity';
8 | import createDoublyIterableSequence from '../util/createDoublyIterableSequence';
9 |
10 | class CurlyArrayConstructor extends Expression {
11 | private _members: Expression[];
12 | constructor(members: Expression[], type: SequenceType) {
13 | super(
14 | new Specificity({
15 | [Specificity.EXTERNAL_KIND]: 1,
16 | }),
17 | members,
18 | {
19 | canBeStaticallyEvaluated: members.every(
20 | (member) => member.canBeStaticallyEvaluated,
21 | ),
22 | },
23 | false,
24 | type,
25 | );
26 |
27 | this._members = members;
28 | }
29 |
30 | public evaluate(dynamicContext: DynamicContext, executionParameters: ExecutionParameters) {
31 | if (this._members.length === 0) {
32 | return sequenceFactory.singleton(new ArrayValue([]));
33 | }
34 | return this._members[0]
35 | .evaluateMaybeStatically(dynamicContext, executionParameters)
36 | .mapAll((allValues) =>
37 | sequenceFactory.singleton(
38 | new ArrayValue(
39 | allValues.map((item) =>
40 | createDoublyIterableSequence(sequenceFactory.singleton(item)),
41 | ),
42 | ),
43 | ),
44 | );
45 | }
46 | }
47 | export default CurlyArrayConstructor;
48 |
--------------------------------------------------------------------------------
/src/expressions/arrays/SquareArrayConstructor.ts:
--------------------------------------------------------------------------------
1 | import ArrayValue from '../dataTypes/ArrayValue';
2 | import sequenceFactory from '../dataTypes/sequenceFactory';
3 | import { SequenceType } from '../dataTypes/Value';
4 | import DynamicContext from '../DynamicContext';
5 | import ExecutionParameters from '../ExecutionParameters';
6 | import Expression from '../Expression';
7 | import Specificity from '../Specificity';
8 | import createDoublyIterableSequence from '../util/createDoublyIterableSequence';
9 |
10 | class SquareArrayConstructor extends Expression {
11 | private _members: Expression[];
12 | constructor(members: Expression[], type: SequenceType) {
13 | super(
14 | new Specificity({
15 | [Specificity.EXTERNAL_KIND]: 1,
16 | }),
17 | members,
18 | {
19 | canBeStaticallyEvaluated: members.every(
20 | (member) => member.canBeStaticallyEvaluated,
21 | ),
22 | },
23 | false,
24 | type,
25 | );
26 |
27 | this._members = members;
28 | }
29 |
30 | public evaluate(dynamicContext: DynamicContext, executionParameters: ExecutionParameters) {
31 | return sequenceFactory.singleton(
32 | new ArrayValue(
33 | this._members.map((entry) =>
34 | createDoublyIterableSequence(
35 | entry.evaluateMaybeStatically(dynamicContext, executionParameters),
36 | ),
37 | ),
38 | ),
39 | );
40 | }
41 | }
42 | export default SquareArrayConstructor;
43 |
--------------------------------------------------------------------------------
/src/expressions/axes/SelfAxis.ts:
--------------------------------------------------------------------------------
1 | import ISequence from '../dataTypes/ISequence';
2 | import sequenceFactory from '../dataTypes/sequenceFactory';
3 | import DynamicContext from '../DynamicContext';
4 | import ExecutionParameters from '../ExecutionParameters';
5 | import Expression, { RESULT_ORDERINGS } from '../Expression';
6 | import TestAbstractExpression from '../tests/TestAbstractExpression';
7 | import { Bucket, intersectBuckets } from '../util/Bucket';
8 | import validateContextNode from './validateContextNode';
9 |
10 | class SelfAxis extends Expression {
11 | private readonly _bucket: Bucket;
12 | private readonly _selector: TestAbstractExpression;
13 |
14 | constructor(selector: TestAbstractExpression, filterBucket: Bucket) {
15 | super(selector.specificity, [selector], {
16 | resultOrder: RESULT_ORDERINGS.SORTED,
17 | subtree: true,
18 | peer: true,
19 | canBeStaticallyEvaluated: false,
20 | });
21 |
22 | this._selector = selector;
23 | this._bucket = intersectBuckets(this._selector.getBucket(), filterBucket);
24 | }
25 |
26 | public evaluate(
27 | dynamicContext: DynamicContext,
28 | executionParameters: ExecutionParameters,
29 | ): ISequence {
30 | validateContextNode(dynamicContext.contextItem);
31 |
32 | const isMatch = this._selector.evaluateToBoolean(
33 | dynamicContext,
34 | dynamicContext.contextItem,
35 | executionParameters,
36 | );
37 | return isMatch
38 | ? sequenceFactory.singleton(dynamicContext.contextItem)
39 | : sequenceFactory.empty();
40 | }
41 |
42 | public override getBucket(): Bucket {
43 | return this._bucket;
44 | }
45 | }
46 | export default SelfAxis;
47 |
--------------------------------------------------------------------------------
/src/expressions/axes/validateContextNode.ts:
--------------------------------------------------------------------------------
1 | import { NodePointer } from '../../domClone/Pointer';
2 | import isSubtypeOf from '../dataTypes/isSubtypeOf';
3 | import Value, { ValueType } from '../dataTypes/Value';
4 | import { errXPDY0002 } from '../XPathErrors';
5 |
6 | export default function validateContextNode(value: Value): NodePointer {
7 | if (value === null) {
8 | throw errXPDY0002('context is absent, it needs to be present to use axes.');
9 | }
10 | if (!isSubtypeOf(value.type, ValueType.NODE)) {
11 | throw new Error('XPTY0020: Axes can only be applied to nodes.');
12 | }
13 |
14 | return value.value as NodePointer;
15 | }
16 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/ArrayValue.ts:
--------------------------------------------------------------------------------
1 | import arrayGet from '../functions/builtInFunctions_arrays_get';
2 | import { BUILT_IN_NAMESPACE_URIS } from '../staticallyKnownNamespaces';
3 | import FunctionValue from './FunctionValue';
4 | import ISequence from './ISequence';
5 | import sequenceFactory from './sequenceFactory';
6 | import { SequenceMultiplicity, ValueType } from './Value';
7 |
8 | class ArrayValue extends FunctionValue {
9 | public members: (() => ISequence)[];
10 | constructor(members: (() => ISequence)[]) {
11 | super({
12 | value: (dynamicContext, executionParameters, staticContext, key) =>
13 | arrayGet(
14 | dynamicContext,
15 | executionParameters,
16 | staticContext,
17 | sequenceFactory.singleton(this),
18 | key,
19 | ),
20 | localName: 'get',
21 | namespaceURI: BUILT_IN_NAMESPACE_URIS.ARRAY_NAMESPACE_URI,
22 | // argumentTypes: [{ type: { kind: BaseType.XSINTEGER, seqType: SequenceType.EXACTLY_ONE }, isResArgument: false }],
23 | argumentTypes: [{ type: ValueType.XSINTEGER, mult: SequenceMultiplicity.EXACTLY_ONE }],
24 | arity: 1,
25 | returnType: { type: ValueType.ITEM, mult: SequenceMultiplicity.ZERO_OR_MORE },
26 | });
27 | this.type = ValueType.ARRAY;
28 | this.members = members;
29 | }
30 | }
31 |
32 | export default ArrayValue;
33 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/AtomicValue.ts:
--------------------------------------------------------------------------------
1 | import { ValueType } from './Value';
2 |
3 | type AtomicValue = {
4 | type: ValueType;
5 | value: any;
6 | };
7 |
8 | export default AtomicValue;
9 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/MapValue.ts:
--------------------------------------------------------------------------------
1 | import mapGet from '../functions/builtInFunctions_maps_get';
2 | import { BUILT_IN_NAMESPACE_URIS } from '../staticallyKnownNamespaces';
3 | import FunctionValue from './FunctionValue';
4 | import ISequence from './ISequence';
5 | import sequenceFactory from './sequenceFactory';
6 | import Value, { SequenceMultiplicity, ValueType } from './Value';
7 |
8 | class MapValue extends FunctionValue {
9 | public keyValuePairs: { key: Value; value: () => ISequence }[];
10 | constructor(keyValuePairs: { key: Value; value: () => ISequence }[]) {
11 | super({
12 | // argumentTypes: [{ type: 'item()', isRestArgument: false }],
13 | argumentTypes: [{ type: ValueType.ITEM, mult: SequenceMultiplicity.EXACTLY_ONE }],
14 | arity: 1,
15 | localName: 'get',
16 | namespaceURI: BUILT_IN_NAMESPACE_URIS.MAP_NAMESPACE_URI,
17 | value: (dynamicContext, executionParameters, staticContext, key) =>
18 | mapGet(
19 | dynamicContext,
20 | executionParameters,
21 | staticContext,
22 | sequenceFactory.singleton(this),
23 | key,
24 | ),
25 | returnType: { type: ValueType.ITEM, mult: SequenceMultiplicity.ZERO_OR_MORE },
26 | });
27 | this.type = ValueType.MAP;
28 | this.keyValuePairs = keyValuePairs;
29 | }
30 | }
31 | export default MapValue;
32 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/Sequences/EmptySequence.ts:
--------------------------------------------------------------------------------
1 | import { DONE_TOKEN, IIterator } from '../../util/iterators';
2 | import ISequence, { SwitchCasesCases } from '../ISequence';
3 | import Value from '../Value';
4 |
5 | export default class EmptySequence implements ISequence {
6 | public value: IIterator;
7 |
8 | constructor() {
9 | this.value = {
10 | next: () => DONE_TOKEN,
11 | };
12 | }
13 |
14 | public expandSequence(): ISequence {
15 | return this;
16 | }
17 |
18 | public filter(_callback: (value: Value, i: number, sequence: ISequence) => boolean): ISequence {
19 | return this;
20 | }
21 |
22 | public first(): Value | null {
23 | return null;
24 | }
25 |
26 | public getAllValues(): Value[] {
27 | return [];
28 | }
29 |
30 | public getEffectiveBooleanValue(): boolean {
31 | return false;
32 | }
33 |
34 | public getLength(): number {
35 | return 0;
36 | }
37 |
38 | public isEmpty(): boolean {
39 | return true;
40 | }
41 |
42 | public isSingleton(): boolean {
43 | return false;
44 | }
45 |
46 | public map(_callback: (value: Value, i: number, sequence: ISequence) => Value): ISequence {
47 | return this;
48 | }
49 |
50 | public mapAll(callback: (allValues: Value[]) => ISequence): ISequence {
51 | return callback([]);
52 | }
53 |
54 | public switchCases(cases: SwitchCasesCases): ISequence {
55 | if (cases.empty) {
56 | return cases.empty(this);
57 | }
58 | return cases.default(this);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/Sequences/getEffectiveBooleanValue.ts:
--------------------------------------------------------------------------------
1 | import { errFORG0006 } from '../../functions/FunctionOperationErrors';
2 | import isSubtypeOf from '../isSubtypeOf';
3 | import Value, { ValueType, valueTypeToString } from '../Value';
4 |
5 | export default function getEffectiveBooleanValue(value: Value): boolean {
6 | const jsValue = value.value;
7 |
8 | // If its operand is a sequence whose first item is a node, fn:boolean returns true.
9 | if (isSubtypeOf(value.type, ValueType.NODE)) {
10 | return true;
11 | }
12 |
13 | // If its operand is a singleton value of type xs:boolean or derived from xs:boolean, fn:boolean returns the value of its operand unchanged.
14 | if (isSubtypeOf(value.type, ValueType.XSBOOLEAN)) {
15 | return jsValue as boolean;
16 | }
17 |
18 | // If its operand is a singleton value of type xs:string, xs:anyURI, xs:untypedAtomic, or a type derived from one of these, fn:boolean returns false if the operand value has zero length; otherwise it returns true.
19 | if (
20 | isSubtypeOf(value.type, ValueType.XSSTRING) ||
21 | isSubtypeOf(value.type, ValueType.XSANYURI) ||
22 | isSubtypeOf(value.type, ValueType.XSUNTYPEDATOMIC)
23 | ) {
24 | return (jsValue as string).length !== 0;
25 | }
26 |
27 | // If its operand is a singleton value of any numeric type or derived from a numeric type, fn:boolean returns false if the operand value is NaN or is numerically equal to zero; otherwise it returns true.
28 | if (isSubtypeOf(value.type, ValueType.XSNUMERIC)) {
29 | return !isNaN(jsValue as number) && jsValue !== 0;
30 | }
31 |
32 | // In all other cases, fn:boolean raises a type error [err:FORG0006]FO31.
33 | throw errFORG0006(
34 | `Cannot determine the effective boolean value of a value with the type ${valueTypeToString(
35 | value.type,
36 | )}`,
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/Variety.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * An enumerator for the variety to get rid of the String comparisons.
3 | */
4 |
5 | export enum Variety {
6 | PRIMITIVE,
7 | DERIVED,
8 | LIST,
9 | UNION,
10 | }
11 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/canCastToType.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from './AtomicValue';
2 | import tryCastToType from './casting/tryCastToType';
3 | import { ValueType } from './Value';
4 |
5 | export default function canCastToType(value: AtomicValue, type: ValueType): boolean {
6 | const result = tryCastToType(value, type);
7 | return result.successful;
8 | }
9 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/castToType.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from './AtomicValue';
2 | import tryCastToType from './casting/tryCastToType';
3 | import { ValueType } from './Value';
4 |
5 | export default function castToType(value: AtomicValue, type: ValueType): AtomicValue {
6 | const result = tryCastToType(value, type);
7 | if (result.successful === true) {
8 | return result.value;
9 | }
10 | throw result.error;
11 | }
12 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/AtomicValueDataType.ts:
--------------------------------------------------------------------------------
1 | import DateTime from '../valueTypes/DateTime';
2 | import Duration from '../valueTypes/Duration';
3 |
4 | type AtomicValueDataType = number | string | boolean | DateTime | Duration;
5 |
6 | export default AtomicValueDataType;
7 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/CastResult.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 |
3 | type ErrorResult = {
4 | error: Error;
5 | successful: false;
6 | };
7 | type SuccessResult = {
8 | successful: true;
9 | value: AtomicValue;
10 | };
11 |
12 | type CastResult = ErrorResult | SuccessResult;
13 |
14 | export default CastResult;
15 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToAnyURI.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { SequenceMultiplicity, ValueType } from '../Value';
4 | import CastResult from './CastResult';
5 |
6 | const createAnyURIValue = (value: any): AtomicValue => createAtomicValue(value, ValueType.XSANYURI);
7 |
8 | export default function castToAnyURI(
9 | instanceOf: (typeName: ValueType) => boolean,
10 | ): (value: any) => CastResult {
11 | if (instanceOf(ValueType.XSSTRING) || instanceOf(ValueType.XSUNTYPEDATOMIC)) {
12 | return (value) => ({
13 | successful: true,
14 | value: createAnyURIValue(value),
15 | });
16 | }
17 |
18 | return () => ({
19 | successful: false,
20 | error: new Error(
21 | 'XPTY0004: Casting not supported from given type to xs:anyURI or any of its derived types.',
22 | ),
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToBase64Binary.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { SequenceMultiplicity, ValueType } from '../Value';
4 | import CastResult from './CastResult';
5 |
6 | const createBase64BinaryValue = (value: any): AtomicValue =>
7 | createAtomicValue(value, ValueType.XSBASE64BINARY);
8 |
9 | function hexToString(hex: string) {
10 | let text = '';
11 | for (let i = 0; i < hex.length; i += 2) {
12 | text += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
13 | }
14 | return text;
15 | }
16 |
17 | // This declaration is needed, as we don't depend anymore on lib.dom.
18 | declare let btoa: (s: string) => string;
19 |
20 | export default function castToBase64Binary(
21 | instanceOf: (typeName: ValueType) => boolean,
22 | ): (value: any) => CastResult {
23 | if (instanceOf(ValueType.XSHEXBINARY)) {
24 | return (value) => ({
25 | successful: true,
26 | value: createBase64BinaryValue(btoa(hexToString(value))),
27 | });
28 | }
29 | if (instanceOf(ValueType.XSSTRING) || instanceOf(ValueType.XSUNTYPEDATOMIC)) {
30 | return (value) => ({
31 | successful: true,
32 | value: createBase64BinaryValue(value),
33 | });
34 | }
35 | return () => ({
36 | error: new Error(
37 | 'XPTY0004: Casting not supported from given type to xs:base64Binary or any of its derived types.',
38 | ),
39 | successful: false,
40 | });
41 | }
42 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToBoolean.ts:
--------------------------------------------------------------------------------
1 | import { falseBoolean, trueBoolean } from '../createAtomicValue';
2 | import { ValueType } from '../Value';
3 | import CastResult from './CastResult';
4 |
5 | export default function castToBoolean(
6 | instanceOf: (typeName: ValueType) => boolean,
7 | ): (value: any) => CastResult {
8 | if (instanceOf(ValueType.XSNUMERIC)) {
9 | return (value) => ({
10 | successful: true,
11 | value: value === 0 || isNaN(value) ? falseBoolean : trueBoolean,
12 | });
13 | }
14 | if (instanceOf(ValueType.XSSTRING) || instanceOf(ValueType.XSUNTYPEDATOMIC)) {
15 | return (value) => {
16 | switch (value) {
17 | case 'true':
18 | case '1':
19 | return {
20 | successful: true,
21 | value: trueBoolean,
22 | };
23 | case 'false':
24 | case '0':
25 | return {
26 | successful: true,
27 | value: falseBoolean,
28 | };
29 | default:
30 | return {
31 | successful: false,
32 | error: new Error(
33 | 'XPTY0004: Casting not supported from given type to xs:boolean or any of its derived types.',
34 | ),
35 | };
36 | }
37 | };
38 | }
39 | return () => ({
40 | successful: false,
41 | error: new Error(
42 | 'XPTY0004: Casting not supported from given type to xs:boolean or any of its derived types.',
43 | ),
44 | });
45 | }
46 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToDate.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { ValueType } from '../Value';
4 | import DateTime from '../valueTypes/DateTime';
5 | import CastResult from './CastResult';
6 |
7 | const createDateValue = (value: any): AtomicValue => createAtomicValue(value, ValueType.XSDATE);
8 |
9 | export default function castToDate(
10 | instanceOf: (typeName: ValueType) => boolean,
11 | ): (value: any) => CastResult {
12 | if (instanceOf(ValueType.XSDATETIME)) {
13 | return (value: DateTime) => ({
14 | successful: true,
15 | value: createDateValue(value.convertToType(ValueType.XSDATE)),
16 | });
17 | }
18 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
19 | return (value) => ({
20 | successful: true,
21 | value: createDateValue(DateTime.fromString(value)),
22 | });
23 | }
24 | return () => ({
25 | successful: false,
26 | error: new Error(
27 | 'XPTY0004: Casting not supported from given type to xs:date or any of its derived types.',
28 | ),
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToDateTime.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { ValueType } from '../Value';
4 | import DateTime from '../valueTypes/DateTime';
5 | import CastResult from './CastResult';
6 |
7 | const createDateTimeValue = (value: DateTime): AtomicValue =>
8 | createAtomicValue(value, ValueType.XSDATETIME);
9 |
10 | export default function castToDateTime(
11 | instanceOf: (typeName: ValueType) => boolean,
12 | ): (value: any) => CastResult {
13 | if (instanceOf(ValueType.XSDATE)) {
14 | return (value: DateTime) => ({
15 | successful: true,
16 | value: createDateTimeValue(value.convertToType(ValueType.XSDATETIME)),
17 | });
18 | }
19 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
20 | return (value) => ({
21 | successful: true,
22 | value: createDateTimeValue(DateTime.fromString(value)),
23 | });
24 | }
25 | return () => ({
26 | successful: false,
27 | error: new Error(
28 | 'XPTY0004: Casting not supported from given type to xs:dateTime or any of its derived types.',
29 | ),
30 | });
31 | }
32 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToDayTimeDuration.ts:
--------------------------------------------------------------------------------
1 | import createAtomicValue from '../createAtomicValue';
2 | import { ValueType } from '../Value';
3 | import DayTimeDuration from '../valueTypes/DayTimeDuration';
4 | import CastResult from './CastResult';
5 |
6 | const createDayTimeDurationValue = (value: DayTimeDuration) =>
7 | createAtomicValue(value, ValueType.XSDAYTIMEDURATION);
8 |
9 | export default function castToDayTimeDuration(
10 | instanceOf: (typeName: ValueType) => boolean,
11 | ): (value: any) => CastResult {
12 | if (instanceOf(ValueType.XSDURATION) && !instanceOf(ValueType.XSYEARMONTHDURATION)) {
13 | return (value) => ({
14 | successful: true,
15 | value: createDayTimeDurationValue(value.getDayTimeDuration()),
16 | });
17 | }
18 | if (instanceOf(ValueType.XSYEARMONTHDURATION)) {
19 | return () => ({
20 | successful: true,
21 | value: createDayTimeDurationValue(DayTimeDuration.fromString('PT0.0S')),
22 | });
23 | }
24 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
25 | return (value) => {
26 | const parsedDuration = DayTimeDuration.fromString(value);
27 | if (parsedDuration) {
28 | return {
29 | successful: true,
30 | value: createDayTimeDurationValue(parsedDuration),
31 | };
32 | }
33 | return {
34 | successful: false,
35 | error: new Error(`FORG0001: Can not cast ${value} to xs:dayTimeDuration`),
36 | };
37 | };
38 | }
39 | return () => ({
40 | successful: false,
41 | error: new Error(
42 | 'XPTY0004: Casting not supported from given type to xs:dayTimeDuration or any of its derived types.',
43 | ),
44 | });
45 | }
46 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToDouble.ts:
--------------------------------------------------------------------------------
1 | import createAtomicValue from '../createAtomicValue';
2 | import { ValueType } from '../Value';
3 | import CastResult from './CastResult';
4 | import castToFloatLikeType from './castToFloatLikeType';
5 |
6 | export default function castToDouble(
7 | instanceOf: (typeName: ValueType) => boolean,
8 | ): (value: any) => CastResult {
9 | const caster = castToFloatLikeType(instanceOf, ValueType.XSDOUBLE);
10 | return (value) => {
11 | const castResult = caster(value);
12 | if (!castResult.successful) {
13 | return castResult as CastResult;
14 | }
15 | return {
16 | successful: true,
17 | value: createAtomicValue(castResult.value, ValueType.XSDOUBLE),
18 | };
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToDuration.ts:
--------------------------------------------------------------------------------
1 | import createAtomicValue from '../createAtomicValue';
2 | import { ValueType } from '../Value';
3 | import Duration from '../valueTypes/Duration';
4 | import CastResult from './CastResult';
5 |
6 | const createDurationValue = (value: Duration) => createAtomicValue(value, ValueType.XSDURATION);
7 |
8 | export default function castToDuration(
9 | instanceOf: (typeName: ValueType) => boolean,
10 | ): (value: any) => CastResult {
11 | if (instanceOf(ValueType.XSYEARMONTHDURATION)) {
12 | return (value) => ({
13 | successful: true,
14 | value: createDurationValue(Duration.fromYearMonthDuration(value)),
15 | });
16 | }
17 | if (instanceOf(ValueType.XSDAYTIMEDURATION)) {
18 | return (value) => ({
19 | successful: true,
20 | value: createDurationValue(Duration.fromDayTimeDuration(value)),
21 | });
22 | }
23 | if (instanceOf(ValueType.XSDURATION)) {
24 | return (value) => ({
25 | successful: true,
26 | value: createDurationValue(value),
27 | });
28 | }
29 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
30 | return (value) => {
31 | const parsedDuration = Duration.fromString(value);
32 | if (parsedDuration) {
33 | return {
34 | successful: true,
35 | value: createDurationValue(parsedDuration),
36 | };
37 | }
38 | return {
39 | successful: false,
40 | error: new Error(`FORG0001: Can not cast ${value} to xs:duration`),
41 | };
42 | };
43 | }
44 | return () => ({
45 | successful: false,
46 | error: new Error(
47 | 'XPTY0004: Casting not supported from given type to xs:duration or any of its derived types.',
48 | ),
49 | });
50 | }
51 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToFloat.ts:
--------------------------------------------------------------------------------
1 | import createAtomicValue from '../createAtomicValue';
2 | import { ValueType } from '../Value';
3 | import CastResult from './CastResult';
4 | import castToFloatLikeType from './castToFloatLikeType';
5 |
6 | export default function castToFloat(
7 | instanceOf: (typeName: ValueType) => boolean,
8 | ): (value: any) => CastResult {
9 | const caster = castToFloatLikeType(instanceOf, ValueType.XSFLOAT);
10 | return (value) => {
11 | const castResult = caster(value);
12 | if (!castResult.successful) {
13 | return castResult as CastResult;
14 | }
15 | return {
16 | successful: true,
17 | value: createAtomicValue(castResult.value, ValueType.XSFLOAT),
18 | };
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToFloatLikeType.ts:
--------------------------------------------------------------------------------
1 | import { errFORG0001 } from '../../../expressions/XPathErrors';
2 | import { ValueType } from '../Value';
3 |
4 | export default function castToFloatLikeType(
5 | instanceOf: (typeName: ValueType) => boolean,
6 | to: ValueType,
7 | ): (value: any) => { successful: true; value: number } | { error: Error; successful: false } {
8 | if (instanceOf(ValueType.XSNUMERIC)) {
9 | return (value) => ({
10 | successful: true,
11 | value,
12 | });
13 | }
14 | if (instanceOf(ValueType.XSBOOLEAN)) {
15 | return (value) => ({
16 | successful: true,
17 | value: value ? 1 : 0,
18 | });
19 | }
20 | if (instanceOf(ValueType.XSSTRING) || instanceOf(ValueType.XSUNTYPEDATOMIC)) {
21 | return (value) => {
22 | switch (value) {
23 | case 'NaN':
24 | return {
25 | successful: true,
26 | value: NaN,
27 | };
28 | case 'INF':
29 | case '+INF':
30 | return {
31 | successful: true,
32 | value: Infinity,
33 | };
34 | case '-INF':
35 | return {
36 | successful: true,
37 | value: -Infinity,
38 | };
39 | case '0':
40 | case '+0':
41 | return {
42 | successful: true,
43 | value: 0,
44 | };
45 | case '-0':
46 | return {
47 | successful: true,
48 | value: -0,
49 | };
50 | }
51 | const floatValue = parseFloat(value);
52 | if (!isNaN(floatValue)) {
53 | return {
54 | successful: true,
55 | value: floatValue,
56 | };
57 | }
58 | return {
59 | successful: false,
60 | error: errFORG0001(value, to),
61 | };
62 | };
63 | }
64 | return () => ({
65 | successful: false,
66 | error: new Error(
67 | `XPTY0004: Casting not supported from given type to ${to} or any of its derived types.`,
68 | ),
69 | });
70 | }
71 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToGDay.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { ValueType } from '../Value';
4 | import DateTime from '../valueTypes/DateTime';
5 | import CastResult from './CastResult';
6 |
7 | const createGDayValue = (value: any): AtomicValue => createAtomicValue(value, ValueType.XSGDAY);
8 |
9 | export default function castToGDay(
10 | instanceOf: (typeName: ValueType) => boolean,
11 | ): (value: any) => CastResult {
12 | if (instanceOf(ValueType.XSDATE) || instanceOf(ValueType.XSDATETIME)) {
13 | return (value: DateTime) => ({
14 | successful: true,
15 | value: createGDayValue(value.convertToType(ValueType.XSGDAY)),
16 | });
17 | }
18 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
19 | return (value) => ({
20 | successful: true,
21 | value: createGDayValue(DateTime.fromString(value)),
22 | });
23 | }
24 | return () => ({
25 | successful: false,
26 | error: new Error(
27 | 'XPTY0004: Casting not supported from given type to xs:gDay or any of its derived types.',
28 | ),
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToGMonth.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { ValueType } from '../Value';
4 | import DateTime from '../valueTypes/DateTime';
5 | import CastResult from './CastResult';
6 |
7 | const createGMonthValue = (value: any): AtomicValue => createAtomicValue(value, ValueType.XSGMONTH);
8 |
9 | export default function castToGMonth(
10 | instanceOf: (typeName: ValueType) => boolean,
11 | ): (value: any) => CastResult {
12 | if (instanceOf(ValueType.XSDATE) || instanceOf(ValueType.XSDATETIME)) {
13 | return (value: DateTime) => ({
14 | successful: true,
15 | value: createGMonthValue(value.convertToType(ValueType.XSGMONTH)),
16 | });
17 | }
18 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
19 | return (value) => ({
20 | successful: true,
21 | value: createGMonthValue(DateTime.fromString(value)),
22 | });
23 | }
24 | return () => ({
25 | successful: false,
26 | error: new Error(
27 | 'XPTY0004: Casting not supported from given type to xs:gMonth or any of its derived types.',
28 | ),
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToGMonthDay.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { ValueType } from '../Value';
4 | import DateTime from '../valueTypes/DateTime';
5 | import CastResult from './CastResult';
6 |
7 | const createGMonthDayValue = (value: any): AtomicValue =>
8 | createAtomicValue(value, ValueType.XSGMONTHDAY);
9 |
10 | export default function castToGMonthDay(
11 | instanceOf: (typeName: ValueType) => boolean,
12 | ): (value: any) => CastResult {
13 | if (instanceOf(ValueType.XSDATE) || instanceOf(ValueType.XSDATETIME)) {
14 | return (value) => ({
15 | successful: true,
16 | value: createGMonthDayValue(value.convertToType(ValueType.XSGMONTHDAY)),
17 | });
18 | }
19 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
20 | return (value) => ({
21 | successful: true,
22 | value: createGMonthDayValue(DateTime.fromString(value)),
23 | });
24 | }
25 | return () => ({
26 | successful: false,
27 | error: new Error(
28 | 'XPTY0004: Casting not supported from given type to xs:gMonthDay or any of its derived types.',
29 | ),
30 | });
31 | }
32 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToGYear.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { SequenceMultiplicity, ValueType } from '../Value';
4 | import DateTime from '../valueTypes/DateTime';
5 | import CastResult from './CastResult';
6 |
7 | const createGYearValue = (value: any): AtomicValue => createAtomicValue(value, ValueType.XSGYEAR);
8 |
9 | export default function castToGYear(
10 | instanceOf: (typeName: ValueType) => boolean,
11 | ): (value: any) => CastResult {
12 | if (instanceOf(ValueType.XSDATE) || instanceOf(ValueType.XSDATETIME)) {
13 | return (value) => ({
14 | successful: true,
15 | value: createGYearValue(value.convertToType(ValueType.XSGYEAR)),
16 | });
17 | }
18 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
19 | return (value) => ({
20 | successful: true,
21 | value: createGYearValue(DateTime.fromString(value)),
22 | });
23 | }
24 | return () => ({
25 | successful: false,
26 | error: new Error(
27 | 'XPTY0004: Casting not supported from given type to xs:gYear or any of its derived types.',
28 | ),
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToGYearMonth.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { ValueType } from '../Value';
4 | import DateTime from '../valueTypes/DateTime';
5 | import CastResult from './CastResult';
6 |
7 | const createGYearMonthValue = (value: any): AtomicValue =>
8 | createAtomicValue(value, ValueType.XSGYEARMONTH);
9 |
10 | export default function castToGYearMonth(
11 | instanceOf: (typeName: ValueType) => boolean,
12 | ): (value: any) => CastResult {
13 | if (instanceOf(ValueType.XSDATE) || instanceOf(ValueType.XSDATETIME)) {
14 | return (value) => ({
15 | successful: true,
16 | value: createGYearMonthValue(value.convertToType(ValueType.XSGYEARMONTH)),
17 | });
18 | }
19 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
20 | return (value) => ({
21 | successful: true,
22 | value: createGYearMonthValue(DateTime.fromString(value)),
23 | });
24 | }
25 | return () => ({
26 | successful: false,
27 | error: new Error(
28 | 'XPTY0004: Casting not supported from given type to xs:gYearMonth or any of its derived types.',
29 | ),
30 | });
31 | }
32 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToHexBinary.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { SequenceMultiplicity, ValueType } from '../Value';
4 | import CastResult from './CastResult';
5 |
6 | const createHexBinaryValue = (value: any): AtomicValue =>
7 | createAtomicValue(value, ValueType.XSHEXBINARY);
8 |
9 | function stringToHex(s: string) {
10 | let hex = '';
11 | for (let i = 0, l = s.length; i < l; i++) {
12 | hex += Number(s.charCodeAt(i)).toString(16);
13 | }
14 | return hex.toUpperCase();
15 | }
16 |
17 | // This declaration is needed, as we don't depend anymore on lib.dom.
18 | declare let atob: (s: string) => string;
19 |
20 | export default function castToHexBinary(
21 | instanceOf: (typeName: ValueType) => boolean,
22 | ): (value: any) => CastResult {
23 | if (instanceOf(ValueType.XSBASE64BINARY)) {
24 | return (value) => ({
25 | successful: true,
26 | value: createHexBinaryValue(stringToHex(atob(value))),
27 | });
28 | }
29 | if (instanceOf(ValueType.XSSTRING) || instanceOf(ValueType.XSUNTYPEDATOMIC)) {
30 | return (value) => ({
31 | successful: true,
32 | value: createHexBinaryValue(value),
33 | });
34 | }
35 | return () => ({
36 | successful: false,
37 | error: new Error(
38 | 'XPTY0004: Casting not supported from given type to xs:hexBinary or any of its derived types.',
39 | ),
40 | });
41 | }
42 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToNumeric.ts:
--------------------------------------------------------------------------------
1 | import { ValueType } from '../Value';
2 | import CastResult from './CastResult';
3 |
4 | const numericTypes = [
5 | ValueType.XSDOUBLE,
6 | ValueType.XSFLOAT,
7 | ValueType.XSDECIMAL,
8 | ValueType.XSINTEGER,
9 | ];
10 |
11 | export default function castToNumeric(
12 | inputType: ValueType,
13 | castToPrimitiveType: (
14 | inputType: ValueType,
15 | outputType: ValueType,
16 | ) => (value: any) => CastResult,
17 | ): (value: any) => CastResult {
18 | return (value) => {
19 | for (const outputType of numericTypes) {
20 | const result = castToPrimitiveType(inputType, outputType)(value);
21 | if (result.successful) {
22 | return result;
23 | }
24 | }
25 | // In the case of failure, return the result of the last attempt
26 | return {
27 | successful: false,
28 | error: new Error(
29 | `XPTY0004: Casting not supported from "${value}" given type to xs:numeric or any of its derived types.`,
30 | ),
31 | };
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToString.ts:
--------------------------------------------------------------------------------
1 | import createAtomicValue from '../createAtomicValue';
2 | import { ValueType } from '../Value';
3 | import CastResult from './CastResult';
4 | import castToStringLikeType from './castToStringLikeType';
5 |
6 | export default function castToString(
7 | instanceOf: (typeName: ValueType) => boolean,
8 | ): (value: any) => CastResult {
9 | const caster = castToStringLikeType(instanceOf);
10 | return (value) => {
11 | const castResult = caster(value);
12 | if (!castResult.successful) {
13 | return castResult;
14 | }
15 |
16 | return {
17 | successful: true,
18 | value: createAtomicValue(castResult.value, ValueType.XSSTRING),
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToTime.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../AtomicValue';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { SequenceMultiplicity, ValueType } from '../Value';
4 | import DateTime from '../valueTypes/DateTime';
5 | import CastResult from './CastResult';
6 |
7 | const createTimeValue = (value: any): AtomicValue => createAtomicValue(value, ValueType.XSTIME);
8 |
9 | export default function castToTime(
10 | instanceOf: (typeName: ValueType) => boolean,
11 | ): (value: any) => CastResult {
12 | if (instanceOf(ValueType.XSDATETIME)) {
13 | return (value) => ({
14 | successful: true,
15 | value: createTimeValue(value.convertToType(ValueType.XSTIME)),
16 | });
17 | }
18 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
19 | return (value) => ({
20 | successful: true,
21 | value: createTimeValue(DateTime.fromString(value)),
22 | });
23 | }
24 | return (value) => ({
25 | successful: false,
26 | error: new Error(
27 | 'XPTY0004: Casting not supported from given type to xs:time or any of its derived types.',
28 | ),
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToUntypedAtomic.ts:
--------------------------------------------------------------------------------
1 | import createAtomicValue from '../createAtomicValue';
2 | import { ValueType } from '../Value';
3 | import CastResult from './CastResult';
4 | import castToStringLikeType from './castToStringLikeType';
5 |
6 | export default function castToUntypedAtomic(
7 | instanceOf: (typeName: ValueType) => boolean,
8 | ): (value: any) => CastResult {
9 | const caster = castToStringLikeType(instanceOf);
10 | return (value) => {
11 | const castResult = caster(value);
12 | if (!castResult.successful) {
13 | return castResult;
14 | }
15 |
16 | return {
17 | successful: true,
18 | value: createAtomicValue(castResult.value, ValueType.XSUNTYPEDATOMIC),
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/casting/castToYearMonthDuration.ts:
--------------------------------------------------------------------------------
1 | import { errFORG0001 } from '../../../expressions/XPathErrors';
2 | import createAtomicValue from '../createAtomicValue';
3 | import { ValueType } from '../Value';
4 | import YearMonthDuration from '../valueTypes/YearMonthDuration';
5 | import CastResult from './CastResult';
6 |
7 | const createYearMonthDurationValue = (value: YearMonthDuration) =>
8 | createAtomicValue(value, ValueType.XSYEARMONTHDURATION);
9 |
10 | export default function castToYearMonthDuration(
11 | instanceOf: (typeName: ValueType) => boolean,
12 | ): (value: any) => CastResult {
13 | if (instanceOf(ValueType.XSDURATION) && !instanceOf(ValueType.XSDAYTIMEDURATION)) {
14 | return (value) => ({
15 | successful: true,
16 | value: createYearMonthDurationValue(value.getYearMonthDuration()),
17 | });
18 | }
19 | if (instanceOf(ValueType.XSDAYTIMEDURATION)) {
20 | return (_value) => ({
21 | successful: true,
22 | value: createYearMonthDurationValue(YearMonthDuration.fromString('P0M')),
23 | });
24 | }
25 | if (instanceOf(ValueType.XSUNTYPEDATOMIC) || instanceOf(ValueType.XSSTRING)) {
26 | return (value) => {
27 | const parsedDuration = YearMonthDuration.fromString(value);
28 | if (parsedDuration) {
29 | return {
30 | successful: true,
31 | value: createYearMonthDurationValue(parsedDuration),
32 | };
33 | }
34 | return {
35 | successful: false,
36 | error: errFORG0001(value, ValueType.XSYEARMONTHDURATION),
37 | };
38 | };
39 | }
40 | return () => ({
41 | successful: false,
42 | error: new Error(
43 | 'XPTY0004: Casting not supported from given type to xs:yearMonthDuration or any of its derived types.',
44 | ),
45 | });
46 | }
47 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/createAtomicValue.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from './AtomicValue';
2 | import builtinDataTypesByType from './builtins/builtinDataTypesByType';
3 | import { ValueType } from './Value';
4 |
5 | export default function createAtomicValue(value: any, type: ValueType): AtomicValue {
6 | if (!builtinDataTypesByType[type]) {
7 | throw new Error('Unknown type');
8 | }
9 |
10 | return {
11 | type,
12 | value,
13 | };
14 | }
15 |
16 | export const trueBoolean = createAtomicValue(true, ValueType.XSBOOLEAN);
17 | export const falseBoolean = createAtomicValue(false, ValueType.XSBOOLEAN);
18 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/createPointerValue.ts:
--------------------------------------------------------------------------------
1 | import { NodePointer } from '../../domClone/Pointer';
2 | import DomFacade from '../../domFacade/DomFacade';
3 | import Value, { SequenceMultiplicity, ValueType } from './Value';
4 |
5 | function getNodeSubType(pointer: NodePointer, domFacade: DomFacade): ValueType {
6 | switch (domFacade.getNodeType(pointer)) {
7 | case 2:
8 | return ValueType.ATTRIBUTE;
9 | case 1:
10 | return ValueType.ELEMENT;
11 | case 3:
12 | case 4: // CDATA nodes are text too
13 | return ValueType.TEXT;
14 | case 7:
15 | return ValueType.PROCESSINGINSTRUCTION;
16 | case 8:
17 | return ValueType.COMMENT;
18 | case 9:
19 | return ValueType.DOCUMENTNODE;
20 | default:
21 | return ValueType.NODE;
22 | }
23 | }
24 |
25 | export default function createPointerValue(pointer: NodePointer, domFacade: DomFacade): Value {
26 | const nodeValue: Value = { type: getNodeSubType(pointer, domFacade), value: pointer };
27 | return nodeValue;
28 | }
29 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/facets/comparators/decimalComparator.ts:
--------------------------------------------------------------------------------
1 | function padFraction(fractionString: string, maxLength: number) {
2 | return fractionString.padEnd(maxLength, '0');
3 | }
4 |
5 | export default function decimalComparator(value1: string | number, value2: string | number) {
6 | if ((value1 === '0' || value1 === '-0') && (value2 === '0' || value2 === '-0')) {
7 | return 0;
8 | }
9 |
10 | const regex = /(?:\+|(-))?(\d+)?(?:\.(\d+))?/;
11 | const match1 = regex.exec(value1 + '');
12 | const match2 = regex.exec(value2 + '');
13 |
14 | const positive1 = !match1[1];
15 | const positive2 = !match2[1];
16 | const val1 = (match1[2] || '').replace(/^0*/, '');
17 | const val2 = (match2[2] || '').replace(/^0*/, '');
18 | const fraction1 = match1[3] || '';
19 | const fraction2 = match2[3] || '';
20 |
21 | if (positive1 && !positive2) {
22 | return 1;
23 | }
24 | if (!positive1 && positive2) {
25 | return -1;
26 | }
27 |
28 | const bothPositive = positive1 && positive2;
29 | if (val1.length > val2.length) {
30 | return bothPositive ? 1 : -1;
31 | }
32 | if (val1.length < val2.length) {
33 | return bothPositive ? -1 : 1;
34 | }
35 |
36 | if (val1 > val2) {
37 | return bothPositive ? 1 : -1;
38 | }
39 | if (val1 < val2) {
40 | return bothPositive ? -1 : 1;
41 | }
42 |
43 | const maxLengthFractions = Math.max(fraction1.length, fraction2.length);
44 | const paddedFraction1 = padFraction(fraction1, maxLengthFractions);
45 | const paddedFraction2 = padFraction(fraction2, maxLengthFractions);
46 |
47 | if (paddedFraction1 > paddedFraction2) {
48 | return bothPositive ? 1 : -1;
49 | }
50 | if (paddedFraction1 < paddedFraction2) {
51 | return bothPositive ? -1 : 1;
52 | }
53 |
54 | return 0;
55 | }
56 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/isSubtypeOf.ts:
--------------------------------------------------------------------------------
1 | import builtinDataTypesByType, { TypeModel } from './builtins/builtinDataTypesByType';
2 | import { ValueType } from './Value';
3 | import { Variety } from './Variety';
4 |
5 | function isSubtypeOfType(subType: TypeModel, superType: TypeModel): boolean {
6 | if (superType.variety === Variety.UNION) {
7 | // It is a union type, which can only be the topmost types
8 | return !!superType.memberTypes.find((memberType) => isSubtypeOfType(subType, memberType));
9 | }
10 |
11 | while (subType) {
12 | if (subType.type === superType.type) {
13 | return true;
14 | }
15 | if (subType.variety === Variety.UNION) {
16 | return !!subType.memberTypes.find((memberType) =>
17 | isSubtypeOf(memberType.type, superType.type),
18 | );
19 | }
20 | subType = subType.parent;
21 | }
22 | return false;
23 | }
24 |
25 | /**
26 | * xs:int is a subtype of xs:integer
27 | * xs:decimal is a subtype of xs:numeric
28 | * xs:NMTOKENS is a subtype of xs:NM TOKEN
29 | */
30 | export default function isSubtypeOf(baseSubType: ValueType, baseSuperType: ValueType): boolean {
31 | if (baseSubType === baseSuperType) {
32 | return true;
33 | }
34 |
35 | const superType: TypeModel = builtinDataTypesByType[baseSuperType];
36 | const subType: TypeModel = builtinDataTypesByType[baseSubType];
37 |
38 | return isSubtypeOfType(subType, superType);
39 | }
40 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/promoteToType.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from './AtomicValue';
2 | import createAtomicValue from './createAtomicValue';
3 | import isSubtypeOf from './isSubtypeOf';
4 | import Value, { ValueType } from './Value';
5 |
6 | export default function promoteToType(value: Value, type: ValueType): AtomicValue {
7 | if (isSubtypeOf(value.type, ValueType.XSNUMERIC)) {
8 | if (isSubtypeOf(value.type, ValueType.XSFLOAT)) {
9 | if (type === ValueType.XSDOUBLE) {
10 | return createAtomicValue(value.value, ValueType.XSDOUBLE);
11 | }
12 | return null;
13 | }
14 | if (isSubtypeOf(value.type, ValueType.XSDECIMAL)) {
15 | if (type === ValueType.XSFLOAT) {
16 | return createAtomicValue(value.value, ValueType.XSFLOAT);
17 | }
18 | if (type === ValueType.XSDOUBLE) {
19 | return createAtomicValue(value.value, ValueType.XSDOUBLE);
20 | }
21 | }
22 | return null;
23 | }
24 |
25 | if (isSubtypeOf(value.type, ValueType.XSANYURI)) {
26 | if (type === ValueType.XSSTRING) {
27 | return createAtomicValue(value.value, ValueType.XSSTRING);
28 | }
29 | }
30 | return null;
31 | }
32 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/sequenceFactory.ts:
--------------------------------------------------------------------------------
1 | import { IIterator } from '../util/iterators';
2 | import { falseBoolean, trueBoolean } from './createAtomicValue';
3 | import ISequence from './ISequence';
4 | import ArrayBackedSequence from './Sequences/ArrayBackedSequence';
5 | import EmptySequence from './Sequences/EmptySequence';
6 | import IteratorBackedSequence from './Sequences/IteratorBackedSequence';
7 | import SingletonSequence from './Sequences/SingletonSequence';
8 | import Value from './Value';
9 |
10 | const emptySequence = new EmptySequence();
11 |
12 | function create(
13 | value: Value | Value[] | IIterator | null = null,
14 | predictedLength: number | null | undefined = null,
15 | ): ISequence {
16 | if (value === null) {
17 | return emptySequence;
18 | }
19 | if (Array.isArray(value)) {
20 | switch (value.length) {
21 | case 0:
22 | return emptySequence;
23 | case 1:
24 | return new SingletonSequence(sequenceFactory, value[0]);
25 | default:
26 | return new ArrayBackedSequence(sequenceFactory, value);
27 | }
28 | }
29 |
30 | if ((value as IIterator).next) {
31 | return new IteratorBackedSequence(
32 | sequenceFactory,
33 | value as IIterator,
34 | predictedLength,
35 | );
36 | }
37 | return new SingletonSequence(sequenceFactory, value as Value);
38 | }
39 |
40 | const sequenceFactory = {
41 | create,
42 |
43 | singleton: (value: Value): ISequence => {
44 | return new SingletonSequence(sequenceFactory, value);
45 | },
46 |
47 | empty: () => {
48 | return create();
49 | },
50 |
51 | singletonTrueSequence: () => {
52 | return create(trueBoolean);
53 | },
54 |
55 | singletonFalseSequence: () => {
56 | return create(falseBoolean);
57 | },
58 | };
59 |
60 | export default sequenceFactory;
61 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/valueTypes/AbstractDuration.ts:
--------------------------------------------------------------------------------
1 | abstract class AbstractDuration {
2 | public equals(other: AbstractDuration) {
3 | return (
4 | this.getRawMonths() === other.getRawMonths() &&
5 | this.getRawSeconds() === other.getRawSeconds()
6 | );
7 | }
8 |
9 | public getDays() {
10 | return 0;
11 | }
12 |
13 | public getHours() {
14 | return 0;
15 | }
16 |
17 | public getMinutes() {
18 | return 0;
19 | }
20 |
21 | public getMonths() {
22 | return 0;
23 | }
24 | public getRawMonths() {
25 | return 0;
26 | }
27 |
28 | public getRawSeconds() {
29 | return 0;
30 | }
31 |
32 | public getSeconds() {
33 | return 0;
34 | }
35 |
36 | public getYears() {
37 | return 0;
38 | }
39 |
40 | public isPositive() {
41 | return true;
42 | }
43 | }
44 |
45 | export default AbstractDuration;
46 |
--------------------------------------------------------------------------------
/src/expressions/dataTypes/valueTypes/QName.ts:
--------------------------------------------------------------------------------
1 | class QName {
2 | public localName: string;
3 | public namespaceURI: string;
4 | public prefix: string;
5 |
6 | /**
7 | * @param prefix The prefix of the QName, empty string if absent
8 | * @param namespaceURI The namespaceURI of the QName, empty string if absent
9 | * @param localName The localName of the QName, contains no colons
10 | */
11 | constructor(prefix: string, namespaceURI: string | null, localName: string) {
12 | this.namespaceURI = namespaceURI || null;
13 | this.prefix = prefix || '';
14 | this.localName = localName;
15 | }
16 |
17 | public buildPrefixedName?() {
18 | return this.prefix ? this.prefix + ':' + this.localName : this.localName;
19 | }
20 | }
21 |
22 | export default QName;
23 |
--------------------------------------------------------------------------------
/src/expressions/debug/StackTraceEntry.ts:
--------------------------------------------------------------------------------
1 | import { PositionedError } from '../../evaluationUtils/PositionedError';
2 | import { SourceRange } from './StackTraceGenerator';
3 | export class StackTraceEntry {
4 | public comment: string;
5 | public innerExpressionType: string;
6 | public innerTrace: Error | PositionedError | StackTraceEntry;
7 | public location: SourceRange;
8 |
9 | constructor(
10 | location: SourceRange,
11 | innerExpressionType: string,
12 | comment: string,
13 | innerTrace: Error | StackTraceEntry,
14 | ) {
15 | this.location = location;
16 | this.innerExpressionType = innerExpressionType;
17 | this.comment = comment;
18 | this.innerTrace = innerTrace;
19 | }
20 |
21 | public getErrorLocation(): SourceRange {
22 | return this.innerTrace instanceof Error
23 | ? this.location
24 | : this.innerTrace.getErrorLocation();
25 | }
26 |
27 | public makeStackTrace(): string[] {
28 | let innerStackTrace: string[];
29 | if (this.innerTrace instanceof PositionedError) {
30 | // We are dealing with a nested positioned error
31 | innerStackTrace = ['Inner error:', this.innerTrace.message];
32 | } else if (this.innerTrace instanceof Error) {
33 | innerStackTrace = [this.innerTrace.toString()];
34 | } else {
35 | innerStackTrace = this.innerTrace.makeStackTrace();
36 | }
37 | innerStackTrace.push(
38 | ` at <${this.innerExpressionType}${this.comment ? ` (${this.comment})` : ''}>:${
39 | this.location.start.line
40 | }:${this.location.start.column} - ${this.location.end.line}:${
41 | this.location.end.column
42 | }`,
43 | );
44 | return innerStackTrace;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/expressions/functions/FunctionDefinitionType.ts:
--------------------------------------------------------------------------------
1 | import ISequence from '../dataTypes/ISequence';
2 | import DynamicContext from '../DynamicContext';
3 | import ExecutionParameters from '../ExecutionParameters';
4 | import StaticContext from '../StaticContext';
5 |
6 | type FunctionDefinitionType = (
7 | dynamicContext: DynamicContext,
8 | executionParameters: ExecutionParameters,
9 | staticContext: StaticContext,
10 | ...sequences: ISequence[]
11 | ) => FunctionReturnType;
12 |
13 | export default FunctionDefinitionType;
14 |
--------------------------------------------------------------------------------
/src/expressions/functions/FunctionOperationErrors.ts:
--------------------------------------------------------------------------------
1 | export const errFOAY0001 = () => new Error('FOAY0001: Array index out of bounds');
2 |
3 | export const errFORG0006 = (
4 | message: string = 'A wrong argument type was specified in a function call.',
5 | ) => new Error(`FORG0006: ${message}`);
6 |
--------------------------------------------------------------------------------
/src/expressions/functions/argumentListToString.ts:
--------------------------------------------------------------------------------
1 | import ISequence from '../dataTypes/ISequence';
2 | import { valueTypeToString } from '../dataTypes/Value';
3 |
4 | export default function argumentListToString(argumentList: ISequence[]) {
5 | return argumentList
6 | .map((argument) => {
7 | if (argument === null) {
8 | return 'placeholder';
9 | }
10 | if (argument.isEmpty()) {
11 | return 'item()?';
12 | }
13 |
14 | if (argument.isSingleton()) {
15 | return valueTypeToString(argument.first().type) || 'item()';
16 | }
17 | return valueTypeToString(argument.first().type) + '+';
18 | })
19 | .map((types) => `${types}`)
20 | .join(', ');
21 | }
22 |
--------------------------------------------------------------------------------
/src/expressions/functions/builtInFunctions_arrays_get.ts:
--------------------------------------------------------------------------------
1 | import ArrayValue from '../dataTypes/ArrayValue';
2 | import FunctionDefinitionType from './FunctionDefinitionType';
3 |
4 | const arrayGet: FunctionDefinitionType = (
5 | _dynamicContext,
6 | _executionParameters,
7 | _staticContext,
8 | arraySequence,
9 | positionSequence,
10 | ) => {
11 | return positionSequence.mapAll(([position]) =>
12 | arraySequence.mapAll(([array]) => {
13 | const positionValue = position.value;
14 | if (positionValue <= 0 || positionValue > (array as ArrayValue).members.length) {
15 | throw new Error('FOAY0001: array position out of bounds.');
16 | }
17 | return (array as ArrayValue).members[positionValue - 1]();
18 | }),
19 | );
20 | };
21 |
22 | export default arrayGet;
23 |
--------------------------------------------------------------------------------
/src/expressions/functions/builtInFunctions_maps_get.ts:
--------------------------------------------------------------------------------
1 | import MapValue from '../dataTypes/MapValue';
2 | import sequenceFactory from '../dataTypes/sequenceFactory';
3 | import zipSingleton from '../util/zipSingleton';
4 | import FunctionDefinitionType from './FunctionDefinitionType';
5 | import isSameMapKey from './isSameMapKey';
6 |
7 | const mapGet: FunctionDefinitionType = (
8 | _dynamicContext,
9 | _executionParameters,
10 | _staticContext,
11 | mapSequence,
12 | key,
13 | ) => {
14 | return zipSingleton([mapSequence, key], ([map, keyValue]) => {
15 | const matchingPair = (map as MapValue).keyValuePairs.find((keyValuePair) => {
16 | return isSameMapKey(keyValuePair.key, keyValue);
17 | });
18 |
19 | if (!matchingPair) {
20 | return sequenceFactory.empty();
21 | }
22 | return matchingPair.value();
23 | });
24 | };
25 |
26 | export default mapGet;
27 |
--------------------------------------------------------------------------------
/src/expressions/functions/generateId.ts:
--------------------------------------------------------------------------------
1 | import { ConcreteNode } from '../../domFacade/ConcreteNode';
2 | import { NodePointer, TinyNode } from '../../domClone/Pointer';
3 |
4 | const generateIdMap: WeakMap = new WeakMap();
5 | let generateIdCounter = 0;
6 |
7 | function generateId(ptr: NodePointer): string {
8 | const node = ptr.node;
9 | if (!generateIdMap.has(node)) {
10 | generateIdMap.set(node, `id${++generateIdCounter}`);
11 | }
12 | return generateIdMap.get(node);
13 | }
14 |
15 | export default generateId;
16 |
--------------------------------------------------------------------------------
/src/expressions/literals/Literal.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../dataTypes/AtomicValue';
2 | import createAtomicValue from '../dataTypes/createAtomicValue';
3 | import ISequence from '../dataTypes/ISequence';
4 | import sequenceFactory from '../dataTypes/sequenceFactory';
5 | import { SequenceType, ValueType } from '../dataTypes/Value';
6 | import Expression, { RESULT_ORDERINGS } from '../Expression';
7 | import Specificity from '../Specificity';
8 |
9 | class Literal extends Expression {
10 | private _createValueSequence: () => ISequence;
11 |
12 | constructor(jsValue: string, type: SequenceType) {
13 | super(
14 | new Specificity({}),
15 | [],
16 | {
17 | canBeStaticallyEvaluated: true,
18 | resultOrder: RESULT_ORDERINGS.SORTED,
19 | },
20 | false,
21 | type,
22 | );
23 |
24 | let value: AtomicValue;
25 | switch (type.type) {
26 | case ValueType.XSINTEGER:
27 | value = createAtomicValue(parseInt(jsValue, 10), type.type);
28 | break;
29 | case ValueType.XSSTRING:
30 | value = createAtomicValue(jsValue + '', type.type);
31 | break;
32 | case ValueType.XSDECIMAL:
33 | case ValueType.XSDOUBLE:
34 | value = createAtomicValue(parseFloat(jsValue), type.type);
35 | break;
36 | default:
37 | throw new TypeError('Type ' + type + ' not expected in a literal');
38 | }
39 |
40 | this._createValueSequence = () => sequenceFactory.singleton(value);
41 | }
42 |
43 | public evaluate() {
44 | return this._createValueSequence();
45 | }
46 | }
47 |
48 | export default Literal;
49 |
--------------------------------------------------------------------------------
/src/expressions/operators/SequenceOperator.ts:
--------------------------------------------------------------------------------
1 | import ISequence from '../dataTypes/ISequence';
2 | import sequenceFactory from '../dataTypes/sequenceFactory';
3 | import { SequenceType } from '../dataTypes/Value';
4 | import DynamicContext from '../DynamicContext';
5 | import ExecutionParameters from '../ExecutionParameters';
6 | import Expression, { RESULT_ORDERINGS } from '../Expression';
7 | import PossiblyUpdatingExpression from '../PossiblyUpdatingExpression';
8 | import Specificity from '../Specificity';
9 | import concatSequences from '../util/concatSequences';
10 |
11 | /**
12 | * The Sequence selector evaluates its operands and returns them as a single sequence
13 | */
14 | class SequenceOperator extends PossiblyUpdatingExpression {
15 | constructor(expressions: Expression[], type: SequenceType) {
16 | super(
17 | expressions.reduce((specificity, selector) => {
18 | return specificity.add(selector.specificity);
19 | }, new Specificity({})),
20 | expressions,
21 | {
22 | resultOrder: RESULT_ORDERINGS.UNSORTED,
23 | canBeStaticallyEvaluated: expressions.every(
24 | (selector) => selector.canBeStaticallyEvaluated,
25 | ),
26 | },
27 | type,
28 | );
29 | }
30 |
31 | public performFunctionalEvaluation(
32 | dynamicContext: DynamicContext,
33 | _executionParameters: ExecutionParameters,
34 | sequenceCallbacks: ((innerDynamicContext: DynamicContext) => ISequence)[],
35 | ) {
36 | if (!sequenceCallbacks.length) {
37 | return sequenceFactory.empty();
38 | }
39 | return concatSequences(sequenceCallbacks.map((cb) => cb(dynamicContext)));
40 | }
41 | }
42 |
43 | export default SequenceOperator;
44 |
--------------------------------------------------------------------------------
/src/expressions/operators/UniversalExpression.ts:
--------------------------------------------------------------------------------
1 | import sequenceFactory from '../dataTypes/sequenceFactory';
2 | import Expression from '../Expression';
3 | import Specificity from '../Specificity';
4 |
5 | class UniversalExpression extends Expression {
6 | constructor() {
7 | super(
8 | new Specificity({
9 | [Specificity.UNIVERSAL_KIND]: 1,
10 | }),
11 | [],
12 | {
13 | canBeStaticallyEvaluated: true,
14 | },
15 | );
16 | }
17 |
18 | public evaluate() {
19 | return sequenceFactory.singletonTrueSequence();
20 | }
21 | }
22 | export default UniversalExpression;
23 |
--------------------------------------------------------------------------------
/src/expressions/operators/compares/arePointersEqual.ts:
--------------------------------------------------------------------------------
1 | import { GraftPoint, NodePointer } from '../../../domClone/Pointer';
2 |
3 | function areGraftAncestorsSame(graftAncestor1: GraftPoint, graftAncestor2: GraftPoint): boolean {
4 | if (graftAncestor1 === graftAncestor2) {
5 | return true;
6 | }
7 |
8 | if (
9 | graftAncestor1 &&
10 | graftAncestor2 &&
11 | graftAncestor1.offset === graftAncestor2.offset &&
12 | graftAncestor1.parent === graftAncestor2.parent
13 | ) {
14 | return areGraftAncestorsSame(graftAncestor1.graftAncestor, graftAncestor2.graftAncestor);
15 | }
16 |
17 | return false;
18 | }
19 |
20 | function arePointersEqual(pointer1: NodePointer, pointer2: NodePointer) {
21 | if (
22 | pointer1 === pointer2 ||
23 | (pointer1.node === pointer2.node &&
24 | areGraftAncestorsSame(pointer1.graftAncestor, pointer2.graftAncestor))
25 | ) {
26 | return true;
27 | }
28 |
29 | return false;
30 | }
31 |
32 | export default arePointersEqual;
33 |
--------------------------------------------------------------------------------
/src/expressions/path/ContextItemExpression.ts:
--------------------------------------------------------------------------------
1 | import sequenceFactory from '../dataTypes/sequenceFactory';
2 | import { SequenceType } from '../dataTypes/Value';
3 | import DynamicContext from '../DynamicContext';
4 | import ExecutionParameters from '../ExecutionParameters';
5 | import Expression, { RESULT_ORDERINGS } from '../Expression';
6 | import Specificity from '../Specificity';
7 | import { errXPDY0002 } from '../XPathErrors';
8 |
9 | class ContextItemExpression extends Expression {
10 | constructor(type: SequenceType) {
11 | super(
12 | new Specificity({}),
13 | [],
14 | {
15 | resultOrder: RESULT_ORDERINGS.SORTED,
16 | },
17 | false,
18 | type,
19 | );
20 | }
21 |
22 | public evaluate(dynamicContext: DynamicContext, _executionParameters: ExecutionParameters) {
23 | if (dynamicContext.contextItem === null) {
24 | throw errXPDY0002('context is absent, it needs to be present to use the "." operator');
25 | }
26 | return sequenceFactory.singleton(dynamicContext.contextItem);
27 | }
28 | }
29 | export default ContextItemExpression;
30 |
--------------------------------------------------------------------------------
/src/expressions/postfix/Lookup.ts:
--------------------------------------------------------------------------------
1 | import EmptySequence from '../dataTypes/Sequences/EmptySequence';
2 | import DynamicContext from '../DynamicContext';
3 | import ExecutionParameters from '../ExecutionParameters';
4 | import Expression from '../Expression';
5 | import { Bucket } from '../util/Bucket';
6 | import evaluateLookup from './evaluateLookup';
7 |
8 | class Lookup extends Expression {
9 | private readonly _keySpecifier: '*' | Expression;
10 | private readonly _selector: Expression;
11 |
12 | constructor(selector: Expression, keySpecifier: '*' | Expression) {
13 | super(selector.specificity, [selector].concat(keySpecifier === '*' ? [] : [keySpecifier]), {
14 | canBeStaticallyEvaluated: selector.canBeStaticallyEvaluated,
15 | resultOrder: selector.expectedResultOrder,
16 | subtree: selector.subtree,
17 | });
18 |
19 | this._selector = selector;
20 | this._keySpecifier = keySpecifier;
21 | }
22 |
23 | public evaluate(dynamicContext: DynamicContext, executionParameters: ExecutionParameters) {
24 | const sequence = this._selector.evaluateMaybeStatically(
25 | dynamicContext,
26 | executionParameters,
27 | );
28 | return sequence.mapAll((items) => {
29 | return items.reduce((toReturn, item) => {
30 | return evaluateLookup(
31 | item,
32 | this._keySpecifier,
33 | toReturn,
34 | dynamicContext,
35 | executionParameters,
36 | );
37 | }, new EmptySequence());
38 | });
39 | }
40 |
41 | public override getBucket(): Bucket | null {
42 | return this._selector.getBucket();
43 | }
44 | }
45 |
46 | export default Lookup;
47 |
--------------------------------------------------------------------------------
/src/expressions/postfix/UnaryLookup.ts:
--------------------------------------------------------------------------------
1 | import EmptySequence from '../dataTypes/Sequences/EmptySequence';
2 | import { SequenceType } from '../dataTypes/Value';
3 | import DynamicContext from '../DynamicContext';
4 | import ExecutionParameters from '../ExecutionParameters';
5 | import Expression from '../Expression';
6 | import Specificity from '../Specificity';
7 | import evaluateLookup from './evaluateLookup';
8 |
9 | class UnaryLookup extends Expression {
10 | private readonly _keySpecifier: '*' | Expression;
11 |
12 | constructor(keySpecifier: '*' | Expression, type: SequenceType) {
13 | super(
14 | new Specificity({
15 | [Specificity.EXTERNAL_KIND]: 1,
16 | }),
17 | keySpecifier === '*' ? [] : [keySpecifier],
18 | { canBeStaticallyEvaluated: false },
19 | false,
20 | type,
21 | );
22 |
23 | this._keySpecifier = keySpecifier;
24 | }
25 |
26 | public evaluate(dynamicContext: DynamicContext, executionParameters: ExecutionParameters) {
27 | return evaluateLookup(
28 | dynamicContext.contextItem,
29 | this._keySpecifier,
30 | new EmptySequence(),
31 | dynamicContext,
32 | executionParameters,
33 | );
34 | }
35 | }
36 |
37 | export default UnaryLookup;
38 |
--------------------------------------------------------------------------------
/src/expressions/staticallyKnownNamespaces.ts:
--------------------------------------------------------------------------------
1 | export const enum BUILT_IN_NAMESPACE_URIS {
2 | XMLNS_NAMESPACE_URI = 'http://www.w3.org/2000/xmlns/',
3 | XML_NAMESPACE_URI = 'http://www.w3.org/XML/1998/namespace',
4 | XMLSCHEMA_NAMESPACE_URI = 'http://www.w3.org/2001/XMLSchema',
5 | ARRAY_NAMESPACE_URI = 'http://www.w3.org/2005/xpath-functions/array',
6 | FUNCTIONS_NAMESPACE_URI = 'http://www.w3.org/2005/xpath-functions',
7 | LOCAL_NAMESPACE_URI = 'http://www.w3.org/2005/xquery-local-functions',
8 | MAP_NAMESPACE_URI = 'http://www.w3.org/2005/xpath-functions/map',
9 | MATH_NAMESPACE_URI = 'http://www.w3.org/2005/xpath-functions/math',
10 | FONTOXPATH_NAMESPACE_URI = 'http://fontoxml.com/fontoxpath',
11 | XQUERYX_UPDATING_NAMESPACE_URI = 'http://www.w3.org/2007/xquery-update-10',
12 | XQUERYX_NAMESPACE_URI = 'http://www.w3.org/2005/XQueryX',
13 | }
14 |
15 | export const staticallyKnownNamespaceByPrefix: {
16 | [prefix: string]: BUILT_IN_NAMESPACE_URIS | string;
17 | } = {
18 | ['xml']: BUILT_IN_NAMESPACE_URIS.XML_NAMESPACE_URI,
19 | ['xs']: BUILT_IN_NAMESPACE_URIS.XMLSCHEMA_NAMESPACE_URI,
20 | ['fn']: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
21 | ['map']: BUILT_IN_NAMESPACE_URIS.MAP_NAMESPACE_URI,
22 | ['array']: BUILT_IN_NAMESPACE_URIS.ARRAY_NAMESPACE_URI,
23 | ['math']: BUILT_IN_NAMESPACE_URIS.MATH_NAMESPACE_URI,
24 | ['fontoxpath']: BUILT_IN_NAMESPACE_URIS.FONTOXPATH_NAMESPACE_URI,
25 | ['local']: BUILT_IN_NAMESPACE_URIS.LOCAL_NAMESPACE_URI,
26 | };
27 |
28 | export function registerStaticallyKnownNamespace(prefix: string, namespaceURI: string) {
29 | if (staticallyKnownNamespaceByPrefix[prefix]) {
30 | throw new Error('Prefix already registered: Do not register the same prefix twice.');
31 | }
32 | staticallyKnownNamespaceByPrefix[prefix] = namespaceURI;
33 | }
34 |
--------------------------------------------------------------------------------
/src/expressions/tests/KindTest.ts:
--------------------------------------------------------------------------------
1 | import { NODE_TYPES } from '../../domFacade/ConcreteNode';
2 | import isSubtypeOf from '../dataTypes/isSubtypeOf';
3 | import Value, { ValueType } from '../dataTypes/Value';
4 | import DynamicContext from '../DynamicContext';
5 | import ExecutionParameters from '../ExecutionParameters';
6 | import {} from '../Expression';
7 | import Specificity from '../Specificity';
8 | import { Bucket } from '../util/Bucket';
9 | import TestAbstractExpression from './TestAbstractExpression';
10 |
11 | class KindTest extends TestAbstractExpression {
12 | private _nodeType: NODE_TYPES;
13 | constructor(nodeType: NODE_TYPES) {
14 | super(
15 | new Specificity({
16 | [Specificity.NODETYPE_KIND]: 1,
17 | }),
18 | );
19 |
20 | this._nodeType = nodeType;
21 | }
22 |
23 | public evaluateToBoolean(
24 | _dynamicContext: DynamicContext,
25 | node: Value,
26 | executionParameters: ExecutionParameters,
27 | ) {
28 | if (!isSubtypeOf(node.type, ValueType.NODE)) {
29 | return false;
30 | }
31 | const nodeType = executionParameters.domFacade.getNodeType(node.value);
32 | if (this._nodeType === 3 && nodeType === 4) {
33 | // CDATA_SECTION_NODES should be regarded as text nodes, and CDATA does not exist in the XPath Data Model
34 | return true;
35 | }
36 | return this._nodeType === nodeType;
37 | }
38 | public override getBucket(): Bucket {
39 | return `type-${this._nodeType}`;
40 | }
41 | }
42 | export default KindTest;
43 |
--------------------------------------------------------------------------------
/src/expressions/tests/PITest.ts:
--------------------------------------------------------------------------------
1 | import isSubtypeOf from '../dataTypes/isSubtypeOf';
2 | import Value, { ValueType } from '../dataTypes/Value';
3 | import DynamicContext from '../DynamicContext';
4 | import ExecutionParameters from '../ExecutionParameters';
5 | import {} from '../Expression';
6 | import Specificity from '../Specificity';
7 | import { Bucket } from '../util/Bucket';
8 | import TestAbstractExpression from './TestAbstractExpression';
9 |
10 | class PITest extends TestAbstractExpression {
11 | private _target: string;
12 |
13 | constructor(target: string) {
14 | super(
15 | new Specificity({
16 | [Specificity.NODENAME_KIND]: 1,
17 | }),
18 | );
19 |
20 | this._target = target;
21 | }
22 |
23 | public evaluateToBoolean(
24 | _dynamicContext: DynamicContext,
25 | node: Value,
26 | executionParameters: ExecutionParameters,
27 | ) {
28 | // Assume singleton
29 | const isMatchingProcessingInstruction =
30 | isSubtypeOf(node.type, ValueType.PROCESSINGINSTRUCTION) &&
31 | executionParameters.domFacade.getTarget(node.value) === this._target;
32 | return isMatchingProcessingInstruction;
33 | }
34 |
35 | public override getBucket(): Bucket {
36 | return 'type-7';
37 | }
38 | }
39 |
40 | export default PITest;
41 |
--------------------------------------------------------------------------------
/src/expressions/tests/TestAbstractExpression.ts:
--------------------------------------------------------------------------------
1 | import AtomicValue from '../dataTypes/AtomicValue';
2 | import sequenceFactory from '../dataTypes/sequenceFactory';
3 | import DynamicContext from '../DynamicContext';
4 | import ExecutionParameters from '../ExecutionParameters';
5 | import Expression from '../Expression';
6 | import Specificity from '../Specificity';
7 |
8 | abstract class TestAbstractExpression extends Expression {
9 | constructor(specificity: Specificity) {
10 | super(specificity, [], { canBeStaticallyEvaluated: false });
11 | }
12 |
13 | public evaluate(dynamicContext: DynamicContext, executionParameters: ExecutionParameters) {
14 | return this.evaluateToBoolean(
15 | dynamicContext,
16 | dynamicContext.contextItem,
17 | executionParameters,
18 | )
19 | ? sequenceFactory.singletonTrueSequence()
20 | : sequenceFactory.singletonFalseSequence();
21 | }
22 |
23 | public abstract evaluateToBoolean(
24 | dynamicContext: DynamicContext,
25 | item: AtomicValue,
26 | executionParameters: ExecutionParameters,
27 | ): boolean;
28 | }
29 |
30 | export default TestAbstractExpression;
31 |
--------------------------------------------------------------------------------
/src/expressions/tests/TypeTest.ts:
--------------------------------------------------------------------------------
1 | import isSubtypeOf from '../dataTypes/isSubtypeOf';
2 | import Value, { stringToValueType } from '../dataTypes/Value';
3 | import DynamicContext from '../DynamicContext';
4 | import ExecutionParameters from '../ExecutionParameters';
5 | import Specificity from '../Specificity';
6 | import TestAbstractExpression from './TestAbstractExpression';
7 |
8 | class TypeTest extends TestAbstractExpression {
9 | public _type: { localName: string; namespaceURI: string; prefix: string };
10 |
11 | constructor(type: { localName: string; namespaceURI: string | null; prefix: string }) {
12 | super(new Specificity({}));
13 | this._type = type;
14 | }
15 |
16 | public evaluateToBoolean(
17 | _dynamicContext: DynamicContext,
18 | item: Value,
19 | _executionParameters: ExecutionParameters,
20 | ) {
21 | return isSubtypeOf(
22 | item.type,
23 | stringToValueType(
24 | this._type.prefix
25 | ? this._type.prefix + ':' + this._type.localName
26 | : this._type.localName,
27 | ),
28 | );
29 | }
30 | }
31 | export default TypeTest;
32 |
--------------------------------------------------------------------------------
/src/expressions/util/Random.ts:
--------------------------------------------------------------------------------
1 | // Seed a prng with the chosen seed. This is a linear congruential generator. The constants are
2 | // taken from Steele, GL, Vigna, S. Computationally easy, spectrally good multipliers for
3 | // congruential pseudorandom number generators. Softw Pract Exper. 2022; 52( 2): 443–
4 | // 458. doi:10.1002/spe.3030
5 | export default class Random {
6 | private static readonly _a = 0x72ed;
7 | private static readonly _c = 0;
8 | private static readonly _m = 2 ** 32;
9 |
10 | private readonly _defaultSeed: number;
11 |
12 | constructor(seed: number = Math.floor(Math.random() * Random._m)) {
13 | this._defaultSeed = Math.abs(seed % Random._m);
14 | }
15 |
16 | public getRandomNumber(seed: number | null) {
17 | const current = (Random._a * (seed ?? this._defaultSeed) + Random._c) % Random._m;
18 |
19 | return {
20 | currentInt: Math.floor(current),
21 | currentDecimal: current / Random._m,
22 | };
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/expressions/util/atomizeSequence.ts:
--------------------------------------------------------------------------------
1 | import { atomizeSingleValue } from '../dataTypes/atomize';
2 | import ISequence from '../dataTypes/ISequence';
3 | import sequenceFactory from '../dataTypes/sequenceFactory';
4 | import ExecutionParameters from '../ExecutionParameters';
5 | import { DONE_TOKEN } from './iterators';
6 |
7 | export default function atomizeSequence(
8 | sequence: ISequence,
9 | executionParameters: ExecutionParameters,
10 | ): ISequence {
11 | let currentSequence: ISequence;
12 |
13 | return sequenceFactory.create({
14 | next: (iterationHint) => {
15 | while (true) {
16 | if (!currentSequence) {
17 | const value = sequence.value.next(iterationHint);
18 | if (value.done) {
19 | return DONE_TOKEN;
20 | }
21 |
22 | currentSequence = atomizeSingleValue(value.value, executionParameters);
23 | }
24 | const atomizedValue = currentSequence.value.next(iterationHint);
25 | if (atomizedValue.done) {
26 | currentSequence = null;
27 | continue;
28 | }
29 |
30 | return atomizedValue;
31 | }
32 | },
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/src/expressions/util/concatSequences.ts:
--------------------------------------------------------------------------------
1 | import ISequence from '../dataTypes/ISequence';
2 | import sequenceFactory from '../dataTypes/sequenceFactory';
3 | import Value from '../dataTypes/Value';
4 | import { DONE_TOKEN, IIterator, IterationHint } from './iterators';
5 |
6 | export default function concatSequences(sequences: ISequence[]): ISequence {
7 | let i = 0;
8 | let iterator: IIterator = null;
9 | let isFirst = true;
10 | return sequenceFactory.create({
11 | next: (hint: IterationHint) => {
12 | while (i < sequences.length) {
13 | if (!iterator) {
14 | iterator = sequences[i].value;
15 | isFirst = true;
16 | }
17 | const value = iterator.next(isFirst ? IterationHint.NONE : hint);
18 | isFirst = false;
19 | if (value.done) {
20 | i++;
21 | iterator = null;
22 | continue;
23 | }
24 | return value;
25 | }
26 | return DONE_TOKEN;
27 | },
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/src/expressions/util/createChildGenerator.ts:
--------------------------------------------------------------------------------
1 | import { ChildNodePointer, NodePointer, ParentNodePointer } from '../../domClone/Pointer';
2 | import { NODE_TYPES } from '../../domFacade/ConcreteNode';
3 | import DomFacade from '../../domFacade/DomFacade';
4 | import { Bucket } from './Bucket';
5 | import { DONE_TOKEN, IIterator, ready } from './iterators';
6 |
7 | export default function createChildGenerator(
8 | domFacade: DomFacade,
9 | pointer: NodePointer,
10 | bucket: Bucket | null,
11 | ): IIterator {
12 | const nodeType = domFacade.getNodeType(pointer);
13 | if (nodeType !== NODE_TYPES.ELEMENT_NODE && nodeType !== NODE_TYPES.DOCUMENT_NODE) {
14 | return {
15 | next: () => {
16 | return DONE_TOKEN;
17 | },
18 | };
19 | }
20 |
21 | let childNode = domFacade.getFirstChildPointer(pointer as ParentNodePointer, bucket);
22 | return {
23 | next() {
24 | if (!childNode) {
25 | return DONE_TOKEN;
26 | }
27 | const current = childNode;
28 | childNode = domFacade.getNextSiblingPointer(childNode, bucket);
29 | return ready(current);
30 | },
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/expressions/util/createDoublyIterableSequence.ts:
--------------------------------------------------------------------------------
1 | import ISequence from '../dataTypes/ISequence';
2 | import sequenceFactory from '../dataTypes/sequenceFactory';
3 | import Value from '../dataTypes/Value';
4 | import { IterationHint, IterationResult } from './iterators';
5 |
6 | export default function createDoublyIterableSequence(sequence: ISequence): () => ISequence {
7 | const savedValues: IterationResult[] = [];
8 | const backingIterator = sequence.value;
9 | return () => {
10 | let i = 0;
11 | return sequenceFactory.create({
12 | next: (_hint: IterationHint) => {
13 | if (savedValues[i] !== undefined) {
14 | return savedValues[i++];
15 | }
16 | const val = backingIterator.next(IterationHint.NONE);
17 | if (val.done) {
18 | return val;
19 | }
20 | savedValues[i++] = val;
21 | return val;
22 | },
23 | });
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/src/expressions/util/createSingleValueIterator.ts:
--------------------------------------------------------------------------------
1 | import { DONE_TOKEN, IIterator, ready } from './iterators';
2 |
3 | export default function createSingleValueIterator(onlyValue: T): IIterator {
4 | let hasPassed = false;
5 | return {
6 | next: () => {
7 | if (hasPassed) {
8 | return DONE_TOKEN;
9 | }
10 | hasPassed = true;
11 | return ready(onlyValue);
12 | },
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/src/expressions/util/iterators.ts:
--------------------------------------------------------------------------------
1 | export class IterationResult {
2 | public done: boolean;
3 | public value: T | undefined | null;
4 | constructor(done: boolean, value: T | undefined) {
5 | this.done = done;
6 | this.value = value;
7 | }
8 | }
9 |
10 | export enum IterationHint {
11 | NONE = 0,
12 | SKIP_DESCENDANTS = 1 << 0,
13 | }
14 |
15 | export const DONE_TOKEN = new IterationResult(true, undefined);
16 | export function ready(value: T) {
17 | return new IterationResult(false, value);
18 | }
19 |
20 | export interface IIterator {
21 | next(hint: IterationHint): IterationResult;
22 | }
23 |
--------------------------------------------------------------------------------
/src/expressions/util/sequenceEvery.ts:
--------------------------------------------------------------------------------
1 | import { falseBoolean, trueBoolean } from '../dataTypes/createAtomicValue';
2 | import ISequence from '../dataTypes/ISequence';
3 | import sequenceFactory from '../dataTypes/sequenceFactory';
4 | import Value from '../dataTypes/Value';
5 | import { DONE_TOKEN, IterationHint, ready } from './iterators';
6 |
7 | export default function sequenceEvery(
8 | sequence: ISequence,
9 | typeTest: (value: Value) => ISequence,
10 | ): ISequence {
11 | const iterator = sequence.value;
12 | let typeTestResultIterator: ISequence | null = null;
13 | let done = false;
14 | return sequenceFactory.create({
15 | next: (_hint: IterationHint) => {
16 | while (!done) {
17 | if (!typeTestResultIterator) {
18 | const value = iterator.next(IterationHint.NONE);
19 | if (value.done) {
20 | done = true;
21 | return ready(trueBoolean);
22 | }
23 | typeTestResultIterator = typeTest(value.value);
24 | }
25 | const ebv = typeTestResultIterator.getEffectiveBooleanValue();
26 | typeTestResultIterator = null;
27 | if (ebv === false) {
28 | done = true;
29 | return ready(falseBoolean);
30 | }
31 | }
32 | return DONE_TOKEN;
33 | },
34 | });
35 | }
36 |
--------------------------------------------------------------------------------
/src/expressions/util/zipSingleton.ts:
--------------------------------------------------------------------------------
1 | import ISequence from '../dataTypes/ISequence';
2 | import Value from '../dataTypes/Value';
3 |
4 | type CallbackType = (values: Value[]) => ISequence;
5 |
6 | /**
7 | * Take a bunch of sequences, take their first values and call the callback with those values
8 | */
9 | export default function zipSingleton(sequences: ISequence[], callback: CallbackType): ISequence {
10 | const firstValues = sequences.map((seq) => seq.first());
11 | return callback(firstValues);
12 | }
13 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/IPendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { NodePointer } from '../../domClone/Pointer';
2 | import ExecutionParameters from '../ExecutionParameters';
3 | import { TransferablePendingUpdate } from './createPendingUpdateFromTransferable';
4 |
5 | export type PendingUpdateType =
6 | | 'delete'
7 | | 'insertAfter'
8 | | 'insertAttributes'
9 | | 'insertBefore'
10 | | 'insertInto'
11 | | 'insertIntoAsFirst'
12 | | 'insertIntoAsLast'
13 | | 'put'
14 | | 'rename'
15 | | 'replaceElementContent'
16 | | 'replaceNode'
17 | | 'replaceValue';
18 |
19 | export abstract class IPendingUpdate {
20 | public readonly target: NodePointer;
21 | public readonly type: PendingUpdateType;
22 | constructor(public pendingUpdateType: PendingUpdateType) {
23 | this.type = pendingUpdateType;
24 | }
25 |
26 | public abstract toTransferable(
27 | executionParameters: ExecutionParameters,
28 | ): TransferablePendingUpdate;
29 | }
30 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/UpdatingFunctionDefinitionType.ts:
--------------------------------------------------------------------------------
1 | import FunctionDefinitionType from '../functions/FunctionDefinitionType';
2 | import UpdatingExpressionResult from '../UpdatingExpressionResult';
3 | import { IIterator } from '../util/iterators';
4 |
5 | type UpdatingFunctionDefinitionType = FunctionDefinitionType>;
6 | export default UpdatingFunctionDefinitionType;
7 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/UpdatingFunctionValue.ts:
--------------------------------------------------------------------------------
1 | import FunctionValue, { FunctionSignature } from '../dataTypes/FunctionValue';
2 | import { ParameterType, SequenceType } from '../dataTypes/Value';
3 | import UpdatingExpressionResult from '../UpdatingExpressionResult';
4 | import { IIterator } from '../util/iterators';
5 |
6 | export default class UpdatingFunctionValue extends FunctionValue<
7 | IIterator
8 | > {
9 | constructor(definition: {
10 | argumentTypes: ParameterType[];
11 | arity: number;
12 | isAnonymous?: boolean;
13 | isUpdating: boolean;
14 | localName: string;
15 | namespaceURI: string;
16 | returnType: SequenceType;
17 | value: FunctionSignature>;
18 | }) {
19 | super(definition);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/DeletePendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { AttributeNodePointer, ChildNodePointer } from '../../../domClone/Pointer';
2 | import realizeDom from '../../../domClone/realizeDom';
3 | import ExecutionParameters from '../../../expressions/ExecutionParameters';
4 | import { TransferablePendingUpdate } from '../createPendingUpdateFromTransferable';
5 | import { IPendingUpdate } from '../IPendingUpdate';
6 | export class DeletePendingUpdate extends IPendingUpdate {
7 | public readonly type: 'delete';
8 | constructor(readonly target: AttributeNodePointer | ChildNodePointer) {
9 | super('delete');
10 | }
11 | public toTransferable(executionParameters: ExecutionParameters): TransferablePendingUpdate {
12 | return {
13 | ['type']: this.type,
14 | ['target']: realizeDom(this.target, executionParameters, false),
15 | };
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/InsertAfterPendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { ChildNodePointer } from '../../../domClone/Pointer';
2 | import { InsertPendingUpdate } from './InsertPendingUpdate';
3 | export class InsertAfterPendingUpdate extends InsertPendingUpdate {
4 | public readonly target: ChildNodePointer;
5 | public readonly type: 'insertAfter';
6 | constructor(target: ChildNodePointer, content: ChildNodePointer[]) {
7 | super(target, content, 'insertAfter');
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/InsertAttributesPendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { AttributeNodePointer, ElementNodePointer } from '../../../domClone/Pointer';
2 | import realizeDom from '../../../domClone/realizeDom';
3 | import ExecutionParameters from '../../../expressions/ExecutionParameters';
4 | import { IPendingUpdate } from '../IPendingUpdate';
5 |
6 | export class InsertAttributesPendingUpdate extends IPendingUpdate {
7 | public readonly type: 'insertAttributes';
8 | constructor(
9 | readonly target: ElementNodePointer,
10 | readonly content: AttributeNodePointer[],
11 | ) {
12 | super('insertAttributes');
13 | }
14 | public toTransferable(executionParameters: ExecutionParameters) {
15 | return {
16 | ['type']: this.type,
17 | ['target']: realizeDom(this.target, executionParameters, false),
18 | content: this.content.map((pointer) => realizeDom(pointer, executionParameters, true)),
19 | };
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/InsertBeforePendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { ChildNodePointer, ParentNodePointer } from '../../../domClone/Pointer';
2 | import { InsertPendingUpdate } from './InsertPendingUpdate';
3 | export class InsertBeforePendingUpdate extends InsertPendingUpdate {
4 | public readonly target: ChildNodePointer;
5 | public readonly type: 'insertBefore';
6 | constructor(target: ParentNodePointer, content: ChildNodePointer[]) {
7 | super(target, content, 'insertBefore');
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/InsertIntoAsFirstPendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ChildNodePointer,
3 | DocumentNodePointer,
4 | ElementNodePointer,
5 | } from '../../../domClone/Pointer';
6 | import { InsertPendingUpdate } from './InsertPendingUpdate';
7 | export class InsertIntoAsFirstPendingUpdate extends InsertPendingUpdate {
8 | public readonly target: ElementNodePointer | DocumentNodePointer;
9 | public readonly type: 'insertIntoAsFirst';
10 | constructor(target: ElementNodePointer | DocumentNodePointer, content: ChildNodePointer[]) {
11 | super(target, content, 'insertIntoAsFirst');
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/InsertIntoAsLastPendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ChildNodePointer,
3 | DocumentNodePointer,
4 | ElementNodePointer,
5 | } from '../../../domClone/Pointer';
6 | import { InsertPendingUpdate } from './InsertPendingUpdate';
7 | export class InsertIntoAsLastPendingUpdate extends InsertPendingUpdate {
8 | public readonly target: ElementNodePointer | DocumentNodePointer;
9 | public readonly type: 'insertIntoAsLast';
10 | constructor(target: ElementNodePointer | DocumentNodePointer, content: ChildNodePointer[]) {
11 | super(target, content, 'insertIntoAsLast');
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/InsertIntoPendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AttributeNodePointer,
3 | ChildNodePointer,
4 | DocumentNodePointer,
5 | ElementNodePointer,
6 | } from '../../../domClone/Pointer';
7 | import { InsertPendingUpdate } from './InsertPendingUpdate';
8 | export class InsertIntoPendingUpdate extends InsertPendingUpdate {
9 | public readonly target: ElementNodePointer | DocumentNodePointer;
10 | public readonly type: 'insertInto';
11 | constructor(target: ElementNodePointer, content: (AttributeNodePointer | ChildNodePointer)[]) {
12 | super(target, content, 'insertInto');
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/InsertPendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { AttributeNodePointer, ChildNodePointer, NodePointer } from '../../../domClone/Pointer';
2 | import realizeDom from '../../../domClone/realizeDom';
3 | import ExecutionParameters from '../../../expressions/ExecutionParameters';
4 | import { IPendingUpdate, PendingUpdateType } from '../IPendingUpdate';
5 | export class InsertPendingUpdate extends IPendingUpdate {
6 | constructor(
7 | readonly target: NodePointer,
8 | readonly content: (ChildNodePointer | AttributeNodePointer)[],
9 | type: PendingUpdateType,
10 | ) {
11 | super(type);
12 | }
13 | public toTransferable(executionParameters: ExecutionParameters) {
14 | return {
15 | ['type']: this.type,
16 | ['target']: realizeDom(this.target, executionParameters, false),
17 | ['content']: this.content.map((pointer) =>
18 | realizeDom(pointer, executionParameters, true),
19 | ),
20 | };
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/RenamePendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { ElementNodePointer } from '../../../domClone/Pointer';
2 | import realizeDom from '../../../domClone/realizeDom';
3 | import ExecutionParameters from '../../../expressions/ExecutionParameters';
4 | import QName from '../../dataTypes/valueTypes/QName';
5 | import { IPendingUpdate } from '../IPendingUpdate';
6 | export class RenamePendingUpdate extends IPendingUpdate {
7 | public newName: QName;
8 | public readonly type: 'rename';
9 | constructor(
10 | readonly target: ElementNodePointer,
11 | newName: QName,
12 | ) {
13 | super('rename');
14 | this.newName = newName.buildPrefixedName
15 | ? newName
16 | : new QName(newName.prefix, newName.namespaceURI, newName.localName);
17 | }
18 | public toTransferable(executionParameters: ExecutionParameters) {
19 | return {
20 | ['type']: this.type,
21 | ['target']: realizeDom(this.target, executionParameters, false),
22 | ['newName']: {
23 | ['prefix']: this.newName.prefix,
24 | ['namespaceURI']: this.newName.namespaceURI,
25 | ['localName']: this.newName.localName,
26 | },
27 | };
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/ReplaceElementContentPendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { ElementNodePointer, TextNodePointer } from '../../../domClone/Pointer';
2 | import realizeDom from '../../../domClone/realizeDom';
3 | import ExecutionParameters from '../../../expressions/ExecutionParameters';
4 | import { IPendingUpdate } from '../IPendingUpdate';
5 |
6 | export class ReplaceElementContentPendingUpdate extends IPendingUpdate {
7 | public readonly type: 'replaceElementContent';
8 | constructor(
9 | readonly target: ElementNodePointer,
10 | readonly text: TextNodePointer | null,
11 | ) {
12 | super('replaceElementContent');
13 | }
14 | public toTransferable(executionParameters: ExecutionParameters) {
15 | return {
16 | ['type']: this.type,
17 | ['target']: realizeDom(this.target, executionParameters, false),
18 | ['text']: this.text ? realizeDom(this.text, executionParameters, true) : null,
19 | };
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/ReplaceNodePendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { AttributeNodePointer, ChildNodePointer } from '../../../domClone/Pointer';
2 | import realizeDom from '../../../domClone/realizeDom';
3 | import ExecutionParameters from '../../../expressions/ExecutionParameters';
4 | import { IPendingUpdate } from '../IPendingUpdate';
5 | export class ReplaceNodePendingUpdate extends IPendingUpdate {
6 | constructor(
7 | readonly target: AttributeNodePointer | ChildNodePointer,
8 | readonly replacement: (AttributeNodePointer | ChildNodePointer)[],
9 | ) {
10 | super('replaceNode');
11 | }
12 | public toTransferable(executionParameters: ExecutionParameters) {
13 | return {
14 | ['type']: this.type,
15 | ['target']: realizeDom(this.target, executionParameters, false),
16 | ['replacement']: this.replacement.map((pointer) =>
17 | realizeDom(pointer, executionParameters, true),
18 | ),
19 | };
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/expressions/xquery-update/pendingUpdates/ReplaceValuePendingUpdate.ts:
--------------------------------------------------------------------------------
1 | import { AttributeNodePointer, ElementNodePointer } from '../../../domClone/Pointer';
2 | import realizeDom from '../../../domClone/realizeDom';
3 | import ExecutionParameters from '../../../expressions/ExecutionParameters';
4 | import { IPendingUpdate } from '../IPendingUpdate';
5 | export class ReplaceValuePendingUpdate extends IPendingUpdate {
6 | public readonly type: 'replaceValue';
7 | constructor(
8 | readonly target: ElementNodePointer | AttributeNodePointer,
9 | readonly stringValue: string,
10 | ) {
11 | super('replaceValue');
12 | }
13 | public toTransferable(executionParameters: ExecutionParameters) {
14 | return {
15 | ['type']: this.type,
16 | ['target']: realizeDom(this.target, executionParameters, false),
17 | ['string-value']: this.stringValue,
18 | };
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/jsCodegen/emitLiterals.ts:
--------------------------------------------------------------------------------
1 | import { Bucket } from '../expressions/util/Bucket';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 | import escapeJavaScriptString from './escapeJavaScriptString';
4 | import {
5 | acceptAst,
6 | GeneratedCodeBaseType,
7 | PartialCompilationResult,
8 | } from './JavaScriptCompiledXPath';
9 |
10 | /**
11 | * Create a JavaScript function that returns the string literal.
12 | *
13 | * https://www.w3.org/TR/xpath-31/#doc-xpath31-StringLiteral
14 | *
15 | * @param ast The string literal AST node
16 | * @param identifier The function wrapper identifier
17 | * @returns Wrapped string literal function
18 | */
19 | export function emitStringConstantExpr(ast: IAST): [PartialCompilationResult, Bucket] {
20 | // Note: default the value to the emptyy string. The XQueryX roundtrip may omit them
21 | let text = (astHelper.getFirstChild(ast, 'value')[1] as string) || '';
22 | text = escapeJavaScriptString(text);
23 | return [acceptAst(text, { type: GeneratedCodeBaseType.Value }, []), null];
24 | }
25 |
--------------------------------------------------------------------------------
/src/jsCodegen/emitOperand.ts:
--------------------------------------------------------------------------------
1 | import { Bucket } from '../expressions/util/Bucket';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 | import { CodeGenContext } from './CodeGenContext';
4 | import { PartialCompilationResult, rejectAst } from './JavaScriptCompiledXPath';
5 |
6 | /**
7 | * Retrieves the first or second operand for an operator AST node and wraps
8 | * it in a function.
9 | *
10 | * @param ast Base AST node for which get either the first or second operand.
11 | * @param identifier Function identifier for the emitted code.
12 | * @param operandKind Indicates if it's the first or second operand for an operator.
13 | * @param staticContext Static context parameter to retrieve context-dependent information.
14 | * @returns JavaScript code of the operand.
15 | */
16 | export function emitOperand(
17 | ast: IAST,
18 | operandKind: 'firstOperand' | 'secondOperand',
19 | contextItemExpr: PartialCompilationResult,
20 | context: CodeGenContext,
21 | ): [PartialCompilationResult, Bucket] {
22 | const exprAst = astHelper.followPath(ast, [operandKind, '*']);
23 | if (!exprAst) {
24 | return [rejectAst(`${operandKind} expression not found`), null];
25 | }
26 |
27 | return context.emitBaseExpr(exprAst, contextItemExpr, context);
28 | }
29 |
--------------------------------------------------------------------------------
/src/jsCodegen/escapeJavaScriptString.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Sanitize an untrusted string used in generated JavaScript code as a string
3 | * literal.
4 | *
5 | * Always use this function to escape user-provided strings before including
6 | * them in JavaScript code that will be evaluated. Not doing so can open up
7 | * security vulnerabilities, such as cross-site scripting. JSON.stringify
8 | * ensures the untrusted string is a valid JavaScript string, according to [the
9 | * ECMAScript specification]{@link https://tc39.es/ecma262/#sec-json.stringify}.
10 | *
11 | * String.prototype.replace handles support for characters older JavaScript
12 | * engines [don't support]{@link
13 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#issue_with_plain_json.stringify_for_use_as_javascript},
14 | * which ensures this function returns a valid string.
15 | *
16 | * @param untrustedString - User provided string.
17 | *
18 | * @returns An escaped and valid JavaScript string.
19 | */
20 | function escapeJavaScriptString(untrustedString: string): string {
21 | return JSON.stringify(untrustedString)
22 | .replace(/\u2028/g, '\\u2028') // LINE SEPARATOR
23 | .replace(/\u2029/g, '\\u2029'); // PARAGRAPH SEPARATOR
24 | }
25 |
26 | export default escapeJavaScriptString;
27 |
--------------------------------------------------------------------------------
/src/nodesFactory/INodesFactory.ts:
--------------------------------------------------------------------------------
1 | import { Document } from '../types/Types';
2 | import ISimpleNodesFactory from './ISimpleNodesFactory';
3 |
4 | /**
5 | * Defines the factory methods used in XQuery. Basically equivalent to the Document interface, but
6 | * with the 'createDocument' factory method added.
7 | *
8 | * @public
9 | */
10 | export default interface INodesFactory extends ISimpleNodesFactory {
11 | createDocument(): Document;
12 | }
13 |
--------------------------------------------------------------------------------
/src/nodesFactory/ISimpleNodesFactory.ts:
--------------------------------------------------------------------------------
1 | import { Attr, CDATASection, Comment, Element, ProcessingInstruction, Text } from '../types/Types';
2 |
3 | /**
4 | * Subset of the constructor methods present on Document. Can be used to create textnodes, elements,
5 | * attributes, CDataSecions, comments and processing instructions.
6 | *
7 | * @public
8 | */
9 | export default interface ISimpleNodesFactory {
10 | createAttributeNS(namespaceURI: string, name: string): Attr;
11 |
12 | createCDATASection(contents: string): CDATASection;
13 |
14 | createComment(contents: string): Comment;
15 |
16 | createElementNS(namespaceURI: string, name: string): Element;
17 |
18 | createProcessingInstruction(target: string, data: string): ProcessingInstruction;
19 |
20 | createTextNode(contents: string): Text;
21 | }
22 |
--------------------------------------------------------------------------------
/src/nodesFactory/wrapExternalNodesFactory.ts:
--------------------------------------------------------------------------------
1 | import INodesFactory from './INodesFactory';
2 |
3 | class WrappingNodesFactory implements INodesFactory {
4 | private _externalNodesFactory: INodesFactory;
5 |
6 | constructor(externalNodesFactory: INodesFactory) {
7 | this._externalNodesFactory = externalNodesFactory;
8 | }
9 |
10 | public createAttributeNS(namespaceURI: string, name: string) {
11 | return this._externalNodesFactory['createAttributeNS'](namespaceURI, name);
12 | }
13 |
14 | public createCDATASection(contents: string) {
15 | return this._externalNodesFactory['createCDATASection'](contents);
16 | }
17 |
18 | public createComment(contents: string) {
19 | return this._externalNodesFactory['createComment'](contents);
20 | }
21 |
22 | public createDocument() {
23 | return this._externalNodesFactory['createDocument']();
24 | }
25 |
26 | public createElementNS(namespaceURI: string, name: string) {
27 | return this._externalNodesFactory['createElementNS'](namespaceURI, name);
28 | }
29 |
30 | public createProcessingInstruction(target: string, data: string) {
31 | return this._externalNodesFactory['createProcessingInstruction'](target, data);
32 | }
33 |
34 | public createTextNode(contents: string) {
35 | return this._externalNodesFactory['createTextNode'](contents);
36 | }
37 | }
38 |
39 | export default function wrapExternalNodesFactory(externalNodesFactory: INodesFactory) {
40 | return new WrappingNodesFactory(externalNodesFactory);
41 | }
42 |
--------------------------------------------------------------------------------
/src/parsing/evaluableExpressionToString.ts:
--------------------------------------------------------------------------------
1 | import { ConcreteNode, NODE_TYPES } from '../domFacade/ConcreteNode';
2 | import ExternalDomFacade from '../domFacade/ExternalDomFacade';
3 | import { EvaluableExpression } from '../evaluateXPath';
4 |
5 | export default function evaluableExpressionToString(
6 | evaluableExpression: EvaluableExpression,
7 | ): string {
8 | if (typeof evaluableExpression === 'string') {
9 | return evaluableExpression;
10 | } else {
11 | const domFacade = new ExternalDomFacade();
12 |
13 | const childNodes = domFacade.getChildNodes(evaluableExpression);
14 | const commentNode: any = childNodes.find(
15 | (node: ConcreteNode) => node.nodeType === NODE_TYPES.COMMENT_NODE,
16 | );
17 | return commentNode ? commentNode.data : 'some expression';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/parsing/normalizeEndOfLines.ts:
--------------------------------------------------------------------------------
1 | export default function normalizeEndOfLines(xpathString: string) {
2 | // Replace all character sequences of 0xD followed by 0xA and all 0xD not followed by 0xA with 0xA.
3 | return xpathString.replace(/(\x0D\x0A)|(\x0D(?!\x0A))/g, String.fromCharCode(0xa));
4 | }
5 |
--------------------------------------------------------------------------------
/src/parsing/parseExpression.ts:
--------------------------------------------------------------------------------
1 | import { Location } from '../expressions/debug/StackTraceGenerator';
2 | import { StackTraceEntry } from '../expressions/debug/StackTraceEntry';
3 | import { IAST } from './astHelper';
4 | import { parseUsingPrsc } from './prscParser';
5 |
6 | /**
7 | * Parse an XPath string to a selector.
8 | *
9 | * @param xPathString The string to parse
10 | * @param compilationOptions Whether the compiler should parse XQuery
11 | */
12 | export default function parseExpression(
13 | xPathString: string,
14 | compilationOptions: { allowXQuery?: boolean; debug?: boolean },
15 | ): IAST {
16 | const options = {
17 | xquery: !!compilationOptions.allowXQuery,
18 | outputDebugInfo: !!compilationOptions.debug,
19 | };
20 |
21 | const parseResult = parseUsingPrsc(xPathString, options);
22 | if (parseResult.success === true) {
23 | return parseResult.value;
24 | }
25 | const lines = xPathString.substring(0, parseResult.offset).split('\n');
26 | const line = lines[lines.length - 1];
27 | // The offset is the last known position where the parser was OK, so the error is one over
28 | const column = line.length + 1;
29 |
30 | const positionS: Location = { offset: parseResult.offset, line: lines.length, column };
31 | const positionE: Location = {
32 | offset: parseResult.offset + 1,
33 | line: lines.length,
34 | column: column + 1,
35 | };
36 | throw new StackTraceEntry(
37 | { start: positionS, end: positionE },
38 | '',
39 | '',
40 | new Error(
41 | `XPST0003: Failed to parse script. Expected ${[...new Set(parseResult.expected)]}`,
42 | ),
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/src/parsing/typesParser.ts:
--------------------------------------------------------------------------------
1 | import { map, optional, Parser, then } from 'prsc';
2 | import { ASTAttributes, IAST } from './astHelper';
3 | import { QNameAST } from './literalParser';
4 | import { eqName } from './nameParser';
5 | import { QUESTION_MARK } from './tokens';
6 |
7 | export const typeName: Parser = eqName;
8 |
9 | export const simpleTypeName: Parser = typeName;
10 |
11 | export const singleType: Parser = then(
12 | simpleTypeName,
13 | optional(QUESTION_MARK),
14 | (type, opt) =>
15 | opt !== null
16 | ? ['singleType', ['atomicType', ...type], ['optional']]
17 | : ['singleType', ['atomicType', ...type]],
18 | );
19 |
20 | export const atomicOrUnionType: Parser = map(eqName, (x) => ['atomicType', ...x]);
21 |
--------------------------------------------------------------------------------
/src/parsing/whitespaceParser.ts:
--------------------------------------------------------------------------------
1 | import { delimited, map, not, or, Parser, ParseResult, peek, plus, preceded, star } from 'prsc';
2 | import { cached, regex } from './parsingFunctions';
3 | import * as tokens from './tokens';
4 |
5 | export const whitespaceCache = new Map>();
6 | export const whitespacePlusCache = new Map>();
7 |
8 | export const char: Parser = or([
9 | regex(/[\t\n\r -\uD7FF\uE000\uFFFD]/),
10 | regex(/[\uD800-\uDBFF][\uDC00-\uDFFF]/),
11 | ]);
12 |
13 | export const commentContents: Parser = preceded(
14 | peek(
15 | not(or([tokens.COMMENT_START, tokens.COMMENT_END]), [
16 | 'comment contents cannot contain "(:" or ":)"',
17 | ]),
18 | ),
19 | char,
20 | );
21 |
22 | function commentIndirect(input: string, offset: number) {
23 | return comment(input, offset);
24 | }
25 |
26 | export const comment: Parser = map(
27 | delimited(
28 | tokens.COMMENT_START,
29 | star(or([commentContents, commentIndirect])),
30 | tokens.COMMENT_END,
31 | true,
32 | ),
33 | (x) => x.join(''),
34 | );
35 |
36 | export const whitespaceCharacter: Parser = or([tokens.WHITESPACE, comment]);
37 |
38 | export const explicitWhitespace: Parser = map(plus(tokens.WHITESPACE), (x) => x.join(''));
39 |
40 | export const whitespace: Parser = cached(
41 | map(star(whitespaceCharacter), (x) => x.join('')),
42 | whitespaceCache,
43 | );
44 |
45 | export const whitespacePlus: Parser = cached(
46 | map(plus(whitespaceCharacter), (x) => x.join('')),
47 | whitespacePlusCache,
48 | );
49 |
--------------------------------------------------------------------------------
/src/precompileXPath.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Precompile an XPath selector asynchronously.
3 | *
4 | * @deprecated This code is deprecated. This is a no-op!
5 | *
6 | * @public
7 | *
8 | * @param xPathString - The xPath which should be pre-compiled
9 | *
10 | * @returns A promise which is resolved with the xpath string after compilation.
11 | */
12 | export default function precompileXPath(xPathString: string): Promise {
13 | return Promise.resolve(xPathString);
14 | }
15 |
--------------------------------------------------------------------------------
/src/typeInference/annotateArrayConstructor.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * Inserting the array type of multiplicity exactly one to the ast;
6 | * as the return type of the array constructor is array.
7 | *
8 | * @param ast the AST to be annotated.
9 | * @returns the inferred SequenceType
10 | */
11 | export function annotateArrayConstructor(ast: IAST): SequenceType {
12 | const seqType = {
13 | type: ValueType.ARRAY,
14 | mult: SequenceMultiplicity.EXACTLY_ONE,
15 | };
16 |
17 | astHelper.insertAttribute(ast, 'type', seqType);
18 |
19 | return seqType;
20 | }
21 |
--------------------------------------------------------------------------------
/src/typeInference/annotateCastOperators.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SequenceMultiplicity,
3 | SequenceType,
4 | stringToValueType,
5 | ValueType,
6 | } from '../expressions/dataTypes/Value';
7 | import astHelper, { IAST } from '../parsing/astHelper';
8 |
9 | /**
10 | * Read the target type of the cast operator from the AST and
11 | * inserts it to as new attribute `type` to the AST.
12 | *
13 | * @param ast the AST to be annotated
14 | * @returns the inferred type
15 | */
16 | export function annotateCastOperator(ast: IAST): SequenceType {
17 | const targetTypeString = getTargetTypeFromAST(ast);
18 | const targetValueType = stringToValueType(targetTypeString);
19 | const sequenceType = { type: targetValueType, mult: SequenceMultiplicity.EXACTLY_ONE };
20 |
21 | if (sequenceType.type !== ValueType.ITEM) {
22 | astHelper.insertAttribute(ast, 'type', sequenceType);
23 | }
24 | return sequenceType;
25 | }
26 |
27 | /**
28 | * Inserts a boolean type to the AST, as castable operator returns boolean type.
29 | *
30 | * @param ast the AST to be annotated
31 | * @returns `SequenceType` of type boolean and multiplicity of `Exactly_ONE`
32 | */
33 | export function annotateCastableOperator(ast: IAST): SequenceType {
34 | const sequenceType = { type: ValueType.XSBOOLEAN, mult: SequenceMultiplicity.EXACTLY_ONE };
35 |
36 | astHelper.insertAttribute(ast, 'type', sequenceType);
37 | return sequenceType;
38 | }
39 |
40 | /**
41 | * Helper function that reads the target type of the cast operator from AST.
42 | */
43 | function getTargetTypeFromAST(ast: IAST): string {
44 | const typeInfoNode = astHelper.followPath(ast, ['singleType', 'atomicType']);
45 | const prefix = astHelper.getAttribute(typeInfoNode, 'prefix');
46 | const targetType = typeInfoNode[2];
47 |
48 | return prefix + ':' + targetType;
49 | }
50 |
--------------------------------------------------------------------------------
/src/typeInference/annotateContextItemExpr.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * A context item expression evaluates to the context item. Hence the type that is returned is the one from the current context.
6 | *
7 | * @param ast the AST to be annotated.
8 | * @returns the type of the context item.
9 | */
10 | export function annotateContextItemExpr(ast: IAST): SequenceType {
11 | // TODO: What type should be returned here?
12 | return {
13 | type: ValueType.ITEM,
14 | mult: SequenceMultiplicity.ZERO_OR_MORE,
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/src/typeInference/annotateDynamicFunctionInvocationExpr.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * At this moment there is no way to infer the return type of this function as
6 | * this would require the query to be executed. Therefore, it would return item()*.
7 | *
8 | * @param ast The ast we need to check the type of.
9 | * @returns A SequenceType of item()*.
10 | */
11 | export function annotateDynamicFunctionInvocationExpr(ast: IAST): SequenceType {
12 | return {
13 | type: ValueType.ITEM,
14 | mult: SequenceMultiplicity.ZERO_OR_MORE,
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/src/typeInference/annotateIfThenElseExpr.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * Checks the type and multiplicity of else clause and then clause
6 | * and inserts the type to the ast as an attribute if they match.
7 | *
8 | * @param ast the AST to be annotated.
9 | * @param elseClause the elseClause, uses type data of this param to ast.
10 | * @param thenClause the thenClause.
11 | * @returns the type of the context item.
12 | */
13 | export function annotateIfThenElseExpr(
14 | ast: IAST,
15 | elseClause: SequenceType,
16 | thenClause: SequenceType,
17 | ): SequenceType {
18 | if (!elseClause || !thenClause) {
19 | return {
20 | type: ValueType.ITEM,
21 | mult: SequenceMultiplicity.ZERO_OR_MORE,
22 | };
23 | }
24 | if (elseClause.type === thenClause.type && elseClause.mult === thenClause.mult) {
25 | if (elseClause.type !== ValueType.ITEM) {
26 | astHelper.insertAttribute(ast, 'type', elseClause);
27 | }
28 | return elseClause;
29 | }
30 | return {
31 | type: ValueType.ITEM,
32 | mult: SequenceMultiplicity.ZERO_OR_MORE,
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/src/typeInference/annotateInstanceOfExpr.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * Insert type boolean multiplicity exactly one the type to the ast
6 | * as an attribute, since instance of expression evaluates to a boolean.
7 | *
8 | * @param ast the AST to be annotated.
9 | * @returns the type of the context item.
10 | */
11 | export function annotateInstanceOfExpr(ast: IAST): SequenceType {
12 | const seqType = {
13 | type: ValueType.XSBOOLEAN,
14 | mult: SequenceMultiplicity.EXACTLY_ONE,
15 | };
16 |
17 | astHelper.insertAttribute(ast, 'type', seqType);
18 |
19 | return seqType;
20 | }
21 |
--------------------------------------------------------------------------------
/src/typeInference/annotateLogicalOperator.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * Switch cases that take care of the operators under the logical operator category.
6 | *
7 | * @param ast The AST to annotate.
8 | */
9 | export function annotateLogicalOperator(ast: IAST): SequenceType {
10 | switch (ast[0]) {
11 | case 'orOp':
12 | return annotateOrOperator(ast);
13 | case 'andOp':
14 | return annotateAndOperator(ast);
15 | }
16 | }
17 |
18 | /**
19 | * Inserts a boolean type to the AST, as or operator returns boolean type.
20 | *
21 | * @param ast the AST to be annotated.
22 | * @returns `SequenceType` of type boolean and multiplicity of `Exactly_ONE`.
23 | */
24 | function annotateOrOperator(ast: IAST): SequenceType {
25 | const seqType = {
26 | type: ValueType.XSBOOLEAN,
27 | mult: SequenceMultiplicity.EXACTLY_ONE,
28 | };
29 |
30 | astHelper.insertAttribute(ast, 'type', seqType);
31 |
32 | return seqType;
33 | }
34 |
35 | /**
36 | * Inserts a boolean type to the AST, as and operator returns boolean type.
37 | *
38 | * @param ast the AST to be annotated.
39 | * @returns `SequenceType` of type boolean and multiplicity of `Exactly_ONE`.
40 | */
41 | function annotateAndOperator(ast: IAST): SequenceType {
42 | const seqType = {
43 | type: ValueType.XSBOOLEAN,
44 | mult: SequenceMultiplicity.EXACTLY_ONE,
45 | };
46 |
47 | astHelper.insertAttribute(ast, 'type', seqType);
48 |
49 | return seqType;
50 | }
51 |
--------------------------------------------------------------------------------
/src/typeInference/annotateMapConstructor.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 | import { AnnotationContext } from './AnnotationContext';
4 |
5 | /**
6 | * Inserting the map type of multiplicity exactly one to the ast;
7 | * as the return type of the map constructor is map.
8 | *
9 | * @param ast the AST to be annotated.
10 | * @returns the inferred SequenceType
11 | */
12 | export function annotateMapConstructor(ast: IAST): SequenceType {
13 | const seqType = {
14 | type: ValueType.MAP,
15 | mult: SequenceMultiplicity.EXACTLY_ONE,
16 | };
17 |
18 | astHelper.insertAttribute(ast, 'type', seqType);
19 | return seqType;
20 | }
21 |
--------------------------------------------------------------------------------
/src/typeInference/annotateQuantifiedExpr.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * Annotate the ast by inserting boolean sequence type of exactly one multiplicity.
6 | * Quantified Expression evaluates if a statement satisfies the quantifier;
7 | * therefore its value is a boolean.
8 | *
9 | * @param ast the ast node to be annotated.
10 | * @returns the annotated sequence type.
11 | */
12 | export function annotateQuantifiedExpr(ast: IAST): SequenceType {
13 | const seqType = {
14 | type: ValueType.XSBOOLEAN,
15 | mult: SequenceMultiplicity.EXACTLY_ONE,
16 | };
17 |
18 | astHelper.insertAttribute(ast, 'type', seqType);
19 |
20 | return seqType;
21 | }
22 |
--------------------------------------------------------------------------------
/src/typeInference/annotateRangeSequenceOperator.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * Inserts an integer sequence into the AST for the rangeSequenceExpr node.
6 | *
7 | * @param ast the AST to be annotated.
8 | * @returns an integer sequence.
9 | */
10 | export function annotateRangeSequenceOperator(ast: IAST): SequenceType {
11 | const seqType = {
12 | type: ValueType.XSINTEGER,
13 | mult: SequenceMultiplicity.ONE_OR_MORE,
14 | };
15 |
16 | astHelper.insertAttribute(ast, 'type', seqType);
17 |
18 | return seqType;
19 | }
20 |
--------------------------------------------------------------------------------
/src/typeInference/annotateSequenceOperator.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 | import { filterOnUniqueObjects } from './annotateFlworExpression';
4 |
5 | /**
6 | * If every type of alle the elements are the same we annotate the ast with that type *.
7 | * else inserts an item()* type to the AST, as sequence operator can contain multiple different ITEM types.
8 | * If we have an empty sequence, we return a Node type.
9 | *
10 | * @param ast the AST to be annotated.
11 | * @returns `SequenceType` with multiplicity of `ZERO_OR_MORE`.
12 | */
13 | export function annotateSequenceOperator(
14 | ast: IAST,
15 | length: number,
16 | elems: IAST[],
17 | types: SequenceType[],
18 | ): SequenceType {
19 | let seqType;
20 |
21 | if (length === 0) {
22 | // We have an empty sequence here
23 | seqType = {
24 | type: ValueType.NODE,
25 | mult: SequenceMultiplicity.ZERO_OR_MORE,
26 | };
27 | } else if (length === 1) {
28 | seqType = types[0];
29 | } else {
30 | const contatinsUndefinedOrNull = types.includes(undefined) || types.includes(null);
31 | if (contatinsUndefinedOrNull) {
32 | seqType = {
33 | type: ValueType.ITEM,
34 | mult: SequenceMultiplicity.ZERO_OR_MORE,
35 | };
36 | } else {
37 | const uniqueTypes = filterOnUniqueObjects(types);
38 | seqType =
39 | uniqueTypes.length > 1
40 | ? {
41 | type: ValueType.ITEM,
42 | mult: SequenceMultiplicity.ZERO_OR_MORE,
43 | }
44 | : {
45 | type: uniqueTypes[0].type,
46 | mult: SequenceMultiplicity.ZERO_OR_MORE,
47 | };
48 | }
49 | }
50 |
51 | if (seqType && seqType.type !== ValueType.ITEM) {
52 | astHelper.insertAttribute(ast, 'type', seqType);
53 | }
54 |
55 | return seqType;
56 | }
57 |
--------------------------------------------------------------------------------
/src/typeInference/annotateSetOperators.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * Annotates the union, intersect and except operators with a sequence of nodes.
6 | *
7 | * @param ast the AST to be annotated.
8 | * @returns a SequenceType of type NODE and of multiplicity EXACTLY_ONE
9 | */
10 | export function annotateSetOperator(ast: IAST): SequenceType {
11 | switch (ast[0]) {
12 | case 'unionOp':
13 | return annotateUnionOperator(ast);
14 | case 'intersectOp':
15 | return annotateIntersectOperator(ast);
16 | case 'exceptOp':
17 | return annotateExceptOperator(ast);
18 | }
19 | }
20 |
21 | function annotateUnionOperator(ast: IAST): SequenceType {
22 | const seqType = {
23 | type: ValueType.NODE,
24 | mult: SequenceMultiplicity.ZERO_OR_MORE,
25 | };
26 |
27 | astHelper.insertAttribute(ast, 'type', seqType);
28 | return seqType;
29 | }
30 |
31 | function annotateIntersectOperator(ast: IAST): SequenceType {
32 | const seqType = {
33 | type: ValueType.NODE,
34 | mult: SequenceMultiplicity.ZERO_OR_MORE,
35 | };
36 |
37 | astHelper.insertAttribute(ast, 'type', seqType);
38 | return seqType;
39 | }
40 |
41 | function annotateExceptOperator(ast: IAST): SequenceType {
42 | const seqType = {
43 | type: ValueType.NODE,
44 | mult: SequenceMultiplicity.ZERO_OR_MORE,
45 | };
46 |
47 | astHelper.insertAttribute(ast, 'type', seqType);
48 | return seqType;
49 | }
50 |
--------------------------------------------------------------------------------
/src/typeInference/annotateSimpleMapExpr.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 | import { AnnotationContext } from './AnnotationContext';
4 |
5 | /**
6 | * A simpleMapExpr is a .map() function, so it checks the type of the input,
7 | * and with the scope and the further annotations, then inferres the type.
8 | *
9 | * @param ast the AST to be annotated.
10 | * @returns the inferred SequenceType
11 | */
12 | export function annotateSimpleMapExpr(
13 | ast: IAST,
14 | context: AnnotationContext,
15 | lastType: SequenceType,
16 | ): SequenceType {
17 | if (lastType !== undefined && lastType !== null) {
18 | const sequenceType: SequenceType = {
19 | type: lastType.type,
20 | mult: SequenceMultiplicity.ZERO_OR_MORE,
21 | };
22 | if (sequenceType && sequenceType.type !== ValueType.ITEM) {
23 | astHelper.insertAttribute(ast, 'type', sequenceType);
24 | }
25 | return sequenceType;
26 | } else {
27 | return { type: ValueType.ITEM, mult: SequenceMultiplicity.ZERO_OR_MORE };
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/typeInference/annotateStringConcatenateOperator.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | /**
5 | * Concatenates two strings to each other, hence it always returns a string.
6 | *
7 | * @param ast the AST to be annotated.
8 | * @param context
9 | * @returns a SequenceType with type XSSTRING and multiplicity EXACTLY_ONE.
10 | */
11 | export function annotateStringConcatenateOperator(ast: IAST): SequenceType {
12 | const seqType = {
13 | type: ValueType.XSSTRING,
14 | mult: SequenceMultiplicity.EXACTLY_ONE,
15 | };
16 |
17 | astHelper.insertAttribute(ast, 'type', seqType);
18 | return seqType;
19 | }
20 |
--------------------------------------------------------------------------------
/src/typeInference/annotateUnaryLookup.ts:
--------------------------------------------------------------------------------
1 | import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 |
4 | // TODO: Annotation not yet implemented. How to lookup the NCName and get the type to be returned.
5 | export function annotateUnaryLookup(ast: IAST, ncName: IAST): SequenceType {
6 | return {
7 | type: ValueType.ITEM,
8 | mult: SequenceMultiplicity.ZERO_OR_MORE,
9 | };
10 | }
11 |
--------------------------------------------------------------------------------
/src/typeInference/annotateVarRef.ts:
--------------------------------------------------------------------------------
1 | import { SequenceType, ValueType } from '../expressions/dataTypes/Value';
2 | import astHelper, { IAST } from '../parsing/astHelper';
3 | import { AnnotationContext } from './AnnotationContext';
4 |
5 | /**
6 | * The method to annotate the varRef node
7 | *
8 | * @param ast the ast containing the varRef
9 | * @param annotationContext the context in which its being annotated
10 | * @returns the type of the variable as SequenceType if it is contained in the context
11 | */
12 | export function annotateVarRef(ast: IAST, annotationContext: AnnotationContext): SequenceType {
13 | const varName = astHelper.getQName(astHelper.getFirstChild(ast, 'name'));
14 |
15 | const varType: SequenceType = annotationContext.getVariable(varName.localName);
16 | if (varType && varType.type !== ValueType.ITEM) {
17 | astHelper.insertAttribute(ast, 'type', varType);
18 | }
19 |
20 | if (varName.namespaceURI === null) {
21 | const uri = annotationContext.resolveNamespace(varName.prefix);
22 | if (uri !== undefined) {
23 | astHelper.insertAttribute(ast, 'URI', uri);
24 | }
25 | }
26 |
27 | return varType;
28 | }
29 |
--------------------------------------------------------------------------------
/src/types/Types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @public
3 | */
4 | export type Node = {
5 | nodeType: number;
6 | };
7 |
8 | /**
9 | * @public
10 | */
11 | export type Attr = Node & {
12 | localName: string;
13 | name: string;
14 | namespaceURI: string | null;
15 | nodeName: string;
16 | prefix: string | null;
17 | value: string;
18 | };
19 |
20 | /**
21 | * @public
22 | */
23 | export type CharacterData = Node & { data: string };
24 |
25 | /**
26 | * @public
27 | */
28 | export type CDATASection = CharacterData;
29 |
30 | /**
31 | * @public
32 | */
33 | export type Comment = CharacterData;
34 |
35 | /**
36 | * @public
37 | */
38 | export type Document = Node & {
39 | implementation: {
40 | createDocument(namespaceURI: null, qualifiedNameStr: null, documentType: null): Document;
41 | };
42 | createAttributeNS(namespaceURI: string, name: string): Attr;
43 | createCDATASection(contents: string): CDATASection;
44 | createComment(data: string): Comment;
45 | createElementNS(namespaceURI: string, qualifiedName: string): Element;
46 | createProcessingInstruction(target: string, data: string): ProcessingInstruction;
47 | createTextNode(data: string): Text;
48 | };
49 |
50 | /**
51 | * @public
52 | */
53 | export type Element = Node & {
54 | localName: string;
55 | namespaceURI: string | null;
56 | nodeName: string;
57 | prefix: string | null;
58 | };
59 |
60 | /**
61 | * @public
62 | */
63 | export type ProcessingInstruction = CharacterData & {
64 | nodeName: string;
65 | target: string;
66 | };
67 |
68 | /**
69 | * @public
70 | */
71 | export type Text = CharacterData;
72 |
73 | export type DocumentTypeNode = Node;
74 |
--------------------------------------------------------------------------------
/src/types/createTypedValueFactory.ts:
--------------------------------------------------------------------------------
1 | import DomFacade from '../domFacade/DomFacade';
2 | import ExternalDomFacade from '../domFacade/ExternalDomFacade';
3 | import IDomFacade from '../domFacade/IDomFacade';
4 | import { adaptJavaScriptValueToArrayOfXPathValues } from '../expressions/adaptJavaScriptValueToXPathValue';
5 | import Value, { SequenceType, stringToSequenceType } from '../expressions/dataTypes/Value';
6 |
7 | /**
8 | * Any type is allowed expect: functions, symbols, undefined, and null
9 | *
10 | * @public
11 | */
12 | export type ValidValue = string | number | boolean | object | Date;
13 |
14 | /**
15 | * @public
16 | */
17 | export type UntypedExternalValue = ValidValue | ValidValue[] | null;
18 |
19 | export const IS_XPATH_VALUE_SYMBOL = Symbol('IS_XPATH_VALUE_SYMBOL');
20 |
21 | /**
22 | * A value converted to a specific type. When passed in other usage of fontoxpath calls it will
23 | * be handled as the type.
24 | */
25 | export type TypedExternalValue = {
26 | [IS_XPATH_VALUE_SYMBOL]: true;
27 | convertedValue: Value[];
28 | };
29 |
30 | /**
31 | * Generates a factory to create a @see TypedExternalValue for the type @see typeName.
32 | *
33 | * @param typeName The type into which to convert the values.
34 | */
35 | export default function createTypedValueFactory(sequenceTypeName: string) {
36 | return (value: UntypedExternalValue, domFacade: IDomFacade): TypedExternalValue => {
37 | const wrappedDomFacade: DomFacade = new DomFacade(
38 | domFacade === null ? new ExternalDomFacade() : domFacade,
39 | );
40 |
41 | const convertedValue = adaptJavaScriptValueToArrayOfXPathValues(
42 | wrappedDomFacade,
43 | value,
44 | stringToSequenceType(sequenceTypeName),
45 | );
46 |
47 | return {
48 | [IS_XPATH_VALUE_SYMBOL]: true,
49 | convertedValue,
50 | };
51 | };
52 | }
53 |
--------------------------------------------------------------------------------
/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": ['../.eslintrc.js'],
3 | "parserOptions": {
4 | "project": ["test/tsconfig.json"],
5 | "sourceType": "module"
6 | },
7 | "rules": {
8 | "no-new-func": "off",
9 | "@typescript-eslint/no-empty-function": "off",
10 | "no-console": "off",
11 | "@typescript-eslint/ban-types": "off",
12 | "import/no-extraneous-dependencies": "off"
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/test/browsertests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPathToString, registerCustomXPathFunction } from 'fontoxpath';
3 | import { parseXmlDocument } from 'slimdom';
4 |
5 | describe('Browser tests', function () {
6 | describe('Custom functions union result order', function () {
7 | it('Returns the results in the same order across browsers', function () {
8 | const firstDocument = parseXmlDocument('1st Document');
9 | const secondDocument = parseXmlDocument('2nd Document');
10 | const thirdDocument = parseXmlDocument('3rd Document');
11 |
12 | registerCustomXPathFunction('cf:firstDocument', [], 'item()', () => {
13 | return firstDocument.documentElement;
14 | });
15 |
16 | registerCustomXPathFunction('cf:secondDocument', [], 'item()', () => {
17 | return secondDocument.documentElement;
18 | });
19 |
20 | registerCustomXPathFunction('cf:thirdDocument', [], 'item()', () => {
21 | return thirdDocument.documentElement;
22 | });
23 |
24 | chai.assert.equal(
25 | evaluateXPathToString(
26 | 'cf:firstDocument() | cf:secondDocument() | cf:thirdDocument()',
27 | ),
28 | '1st Document 2nd Document 3rd Document',
29 | );
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/test/helpers/evaluateXPathToAsyncSingleton.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPathToAsyncIterator } from 'fontoxpath';
3 | export default async function evaluateXPathToAsyncSingleton(...args) {
4 | const iterator = evaluateXPathToAsyncIterator.apply(null, args);
5 | const first = await iterator.next();
6 | if (first.done) {
7 | return null;
8 | }
9 | const second = await iterator.next();
10 | chai.assert.isTrue(second.done, 'The XPath should resolve to a singleton, or nothing.');
11 | return first.value;
12 | }
13 |
--------------------------------------------------------------------------------
/test/helpers/getSkippedTests.ts:
--------------------------------------------------------------------------------
1 | import testFs from './testFs';
2 |
3 | export function getSkippedTests(filename) {
4 | const skippedTests = testFs.readFileSync(filename).split(/\r?\n/);
5 | if (process.argv.indexOf('--regenerate') !== -1) {
6 | const skipIndex =
7 | skippedTests.indexOf(
8 | '=====================TESTS ABOVE HAVE BEEN MARKED MANUALLY=====================',
9 | ) + 1;
10 | skippedTests.splice(skipIndex);
11 | }
12 | return skippedTests;
13 | }
14 |
--------------------------------------------------------------------------------
/test/helpers/testFs.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import * as path from 'path';
3 |
4 | if (!fs.promises) {
5 | (fs as any).promises = {
6 | readFile: fs.readFileSync,
7 | };
8 | }
9 |
10 | function createAssetPath(assetPath) {
11 | return path.join('test', 'assets', assetPath);
12 | }
13 |
14 | /**
15 | * @class
16 | * Wrapper for fs for reading test assets
17 | */
18 | export default new (class testFs {
19 | lstatSync(assetPath) {
20 | return fs.lstatSync(createAssetPath(assetPath));
21 | }
22 |
23 | existsSync(assetPath) {
24 | return fs.existsSync(createAssetPath(assetPath));
25 | }
26 |
27 | readdirSync(dirPath) {
28 | return fs.readdirSync(createAssetPath(dirPath));
29 | }
30 |
31 | async readFile(filePath) {
32 | const overridePath = path.join('overrides', filePath);
33 | if (this.existsSync(overridePath)) {
34 | filePath = overridePath;
35 | }
36 | return fs.promises.readFile(createAssetPath(filePath), 'utf-8');
37 | }
38 |
39 | readFileSync(filePath) {
40 | const overridePath = path.join('overrides', filePath);
41 | if (this.existsSync(overridePath)) {
42 | filePath = overridePath;
43 | }
44 | return fs.readFileSync(createAssetPath(filePath), 'utf-8');
45 | }
46 |
47 | writeFileSync(filePath, content) {
48 | return fs.writeFileSync(createAssetPath(filePath), content);
49 | }
50 | })();
51 |
--------------------------------------------------------------------------------
/test/install-assets.ps1:
--------------------------------------------------------------------------------
1 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
2 | Invoke-WebRequest "https://github.com/LeoWoerteler/XQUTS/archive/master.zip" -Out ./test/assets/XQUTS.zip
3 | Expand-Archive ./test/assets/qt3tests/xqueryx.zip -DestinationPath ./test/assets/qt3tests/xqueryx-extracted -Force
4 | Move-Item ./test/assets/qt3tests/xqueryx-extracted/xqueryx ./test/assets/qt3tests/xqueryx
5 | Expand-Archive ./test/assets/XQUTS.zip -DestinationPath ./test/assets/XQUTS-extracted -Force
6 | Move-Item ./test/assets/XQUTS-extracted/XQUTS-master ./test/assets/XQUTS
7 | Remove-Item ./test/assets/XQUTS.zip
8 | Remove-Item ./test/assets/qt3tests/xqueryx-extracted
9 |
--------------------------------------------------------------------------------
/test/install-assets.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | curl -L https://github.com/LeoWoerteler/XQUTS/archive/master.tar.gz | tar -xz -C ./test/assets/XQUTS --strip-components=1
3 | unzip -q test/assets/qt3tests/xqueryx.zip -d ./test/assets/qt3tests/
4 |
--------------------------------------------------------------------------------
/test/specs/expressions/dataTypes/documentOrderUtils.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { mergeSort } from 'fontoxpath/expressions/dataTypes/documentOrderUtils';
3 |
4 | describe('Merge sort', () => {
5 | describe('Default comparison', () => {
6 | it('Orders numbers numerically', () => {
7 | chai.expect(mergeSort([3, 6, 5])).to.have.ordered.members([3, 5, 6]);
8 | });
9 |
10 | it('Orders strings alphabetically', () => {
11 | chai.expect(mergeSort(['c', 'a', 'b'])).to.have.ordered.members(['a', 'b', 'c']);
12 | });
13 | });
14 |
15 | describe('Custom comparison', () => {
16 | it('Orders based on the custom comparator', () => {
17 | const reverseOrderComparator = (value1: number, value2: number) =>
18 | value1 < value2 ? 0 : -1;
19 | chai.expect(mergeSort([3, 6, 5], reverseOrderComparator)).to.have.ordered.members([
20 | 6, 5, 3,
21 | ]);
22 | });
23 | it('Orders based on any value less than 0', () => {
24 | const reverseOrderComparator = (value1: number, value2: number) =>
25 | value1 < value2 ? 0 : -5;
26 | chai.expect(mergeSort([3, 6, 5], reverseOrderComparator)).to.have.ordered.members([
27 | 6, 5, 3,
28 | ]);
29 | });
30 |
31 | it('Reverses the array if the comparer does not return less than 0', () => {
32 | const reverseComparator = () => 0;
33 | chai.expect(mergeSort([3, 6, 5], reverseComparator)).to.have.ordered.members([5, 6, 3]);
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/specs/expressions/functions/functionRegistry.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { SequenceMultiplicity, ValueType } from 'fontoxpath/expressions/dataTypes/Value';
3 | import functionRegistry from 'fontoxpath/expressions/functions/functionRegistry';
4 | import registerCustomXPathFunction from 'fontoxpath/registerCustomXPathFunction';
5 |
6 | describe('functionRegistry.getFunctionByArity', () => {
7 | before(() => {
8 | registerCustomXPathFunction(
9 | 'fontoxpath_test_prefix:functionName',
10 | [],
11 | 'xs:boolean',
12 | function () {},
13 | );
14 |
15 | registerCustomXPathFunction(
16 | 'fontoxpath_test_prefix:functionName',
17 | ['xs:boolean'],
18 | 'xs:boolean',
19 | function () {},
20 | );
21 |
22 | registerCustomXPathFunction(
23 | 'fontoxpath_test_prefix:otherFunctionName',
24 | [],
25 | 'xs:boolean',
26 | function () {},
27 | );
28 | });
29 |
30 | it('return null if a custom function cannot be found', () => {
31 | chai.assert.isNull(
32 | functionRegistry.getFunctionByArity(
33 | 'fontoxpath_test_prefix:bla',
34 | 'functionLocalName',
35 | 0,
36 | ),
37 | );
38 | });
39 |
40 | it('return null if a custom function with a given arity cannot be found', () => {
41 | chai.assert.isNull(
42 | functionRegistry.getFunctionByArity(
43 | 'fontoxpath_test_prefix:functionName',
44 | 'functionLocalName',
45 | 3,
46 | ),
47 | );
48 | });
49 |
50 | it('return null if a built in function cannot be found', () => {
51 | chai.assert.isNull(functionRegistry.getFunctionByArity('bla', 'functionLocalName', 3));
52 | });
53 |
54 | it('return null if a built in function with a given arity cannot be found', () => {
55 | chai.assert.isNull(functionRegistry.getFunctionByArity('true', 'functionLocalName', 3));
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/specs/expressions/jsCodegen/compare.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPathToBoolean, ReturnType } from 'fontoxpath';
3 | import * as slimdom from 'slimdom';
4 | import jsonMlMapper from 'test-helpers/jsonMlMapper';
5 | import evaluateXPathWithJsCodegen from '../../parsing/jsCodegen/evaluateXPathWithJsCodegen';
6 |
7 | describe('compare tests', () => {
8 | let documentNode: slimdom.Document;
9 | beforeEach(() => {
10 | documentNode = new slimdom.Document();
11 | jsonMlMapper.parse(['xml', { attr: 'true' }], documentNode);
12 | });
13 |
14 | it('does not generate compare function for nodes', () => {
15 | const node = documentNode.documentElement;
16 | const query = '@attr << @attr';
17 |
18 | chai.assert.throws(
19 | () => evaluateXPathWithJsCodegen(query, node, null, ReturnType.BOOLEAN),
20 | 'Unsupported compare type',
21 | );
22 |
23 | chai.assert.equal(evaluateXPathToBoolean(query, node), true);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/specs/expressions/postfix/Filter.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import Expression from 'fontoxpath/expressions/Expression';
3 | import Filter from 'fontoxpath/expressions/postfix/Filter';
4 | import Specificity from 'fontoxpath/expressions/Specificity';
5 | import * as sinon from 'sinon';
6 |
7 | describe('Filter', () => {
8 | let equalExpression;
9 |
10 | beforeEach(() => {
11 | equalExpression = {
12 | specificity: new Specificity({}),
13 | equals: sinon.stub().returns(true),
14 | };
15 | });
16 |
17 | describe('Filter.getBucket()', () => {
18 | it('returns the bucket of its selector', () => {
19 | const filter = new Filter(
20 | {
21 | getBucket: () => 'name-just-for-testing',
22 | specificity: new Specificity({}),
23 | } as unknown as Expression,
24 | equalExpression,
25 | );
26 | chai.assert.equal(filter.getBucket(), 'name-just-for-testing');
27 | });
28 | });
29 |
30 | describe('Filter.specificity', () => {
31 | it('returns the specificity of the selector plus the other part', () => {
32 | const filter = new Filter(
33 | { specificity: new Specificity({ external: 1 }) } as Expression,
34 | { specificity: new Specificity({ external: 1 }) } as Expression,
35 | );
36 | chai.assert.equal(
37 | filter.specificity.compareTo(new Specificity({ external: 2 })),
38 | 0,
39 | 'should be of equal specificity',
40 | );
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/specs/expressions/util/concatSequences.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import sequenceFactory from 'fontoxpath/expressions/dataTypes/sequenceFactory';
3 | import Value, { ValueType } from 'fontoxpath/expressions/dataTypes/Value';
4 | import concatSequences from 'fontoxpath/expressions/util/concatSequences';
5 |
6 | function value(val: any) {
7 | return new Value(ValueType.XSINTEGER, val);
8 | }
9 |
10 | describe('concatSequences', () => {
11 | it('concats sequences', () => {
12 | chai.assert.deepEqual(
13 | concatSequences([
14 | sequenceFactory.create([value(1), value(2), value(3)]),
15 | sequenceFactory.create([value(4), value(5), value(6)]),
16 | ]).getAllValues(),
17 | [value(1), value(2), value(3), value(4), value(5), value(6)],
18 | );
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/specs/parsing/arrays/ArrayConstructor.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPathToArray, evaluateXPathToString } from 'fontoxpath';
3 | import * as slimdom from 'slimdom';
4 | import jsonMlMapper from 'test-helpers/jsonMlMapper';
5 |
6 | let documentNode;
7 | beforeEach(() => {
8 | documentNode = new slimdom.Document();
9 | });
10 |
11 | describe('array constructor', () => {
12 | beforeEach(() => {
13 | jsonMlMapper.parse(
14 | ['someElement', { someAttribute: 'someValue' }, 'A piece of text'],
15 | documentNode,
16 | );
17 | });
18 |
19 | describe('curly', () => {
20 | it('can be parsed', () =>
21 | chai.assert.isOk(
22 | evaluateXPathToArray('array {1, 2}', documentNode),
23 | 'It should be able to be parsed',
24 | ));
25 | it('unfolds passed sequences', () =>
26 | chai.assert.deepEqual(evaluateXPathToArray('array {("a", "b"), "c"}', documentNode), [
27 | 'a',
28 | 'b',
29 | 'c',
30 | ]));
31 | });
32 |
33 | describe('square', () => {
34 | it('can be parsed', () =>
35 | chai.assert.isOk(
36 | evaluateXPathToArray('[1, 2]', documentNode),
37 | 'It should be able to be parsed',
38 | ));
39 | it('does not unfold passed sequences', () =>
40 | chai.assert.equal(evaluateXPathToString('[("a", "b"), "c"](1)', documentNode), 'a b'));
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/test/specs/parsing/arrays/arrayPredicates.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPathToNumbers } from 'fontoxpath';
3 |
4 | describe('Predicates and arrays', () => {
5 | it('can filter an array', () =>
6 | chai.assert.deepEqual(
7 | evaluateXPathToNumbers('(array {1,2,3,4,5,6,7})?*[. mod 2 = 1]'),
8 | [1, 3, 5, 7],
9 | ));
10 |
11 | it('can filter an array with a map in it', () =>
12 | chai.assert.deepEqual(
13 | evaluateXPathToNumbers(
14 | '((array {map{"num":1},map{"num":2},map{"num":3},map{"num":4},map{"num":5},map{"num":6},map{"num":7}})?*[?num mod 2 = 1])?*',
15 | ),
16 | [1, 3, 5, 7],
17 | ));
18 |
19 | it('can filter an array with a map in it and applies operators in correct order', () =>
20 | chai.assert.deepEqual(
21 | evaluateXPathToNumbers(
22 | 'array {map{"num":1},map{"num":2},map{"num":3},map{"num":4},map{"num":5},map{"num":6},map{"num":7}}?*[?num mod 2 = 1]?*',
23 | ),
24 | [1, 3, 5, 7],
25 | ));
26 | });
27 |
--------------------------------------------------------------------------------
/test/specs/parsing/asyncXPath.xq:
--------------------------------------------------------------------------------
1 | let $baseUrl := 'https://raw.githubusercontent.com/LeoWoerteler/QT3TS/master/',
2 | $catalog := fontoxpath:fetch($baseUrl || 'catalog.xml'),
3 | $environmentsByName := $catalog/*:catalog/*:environment ! map:entry(@name => string(), .) => map:merge(),
4 | $testSets := $catalog/*:catalog/*:test-set/@file/string()!fontoxpath:fetch($baseUrl || .) => head()
5 | return for $test in $testSets/*:test-set/*:test-case return fontoxpath:evaluate($test/*:test => string(), map{})
6 |
--------------------------------------------------------------------------------
/test/specs/parsing/axes/SelfAxis.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPathToFirstNode, getBucketForSelector, getBucketsForNode } from 'fontoxpath';
5 |
6 | let documentNode;
7 | beforeEach(() => {
8 | documentNode = new slimdom.Document();
9 | });
10 |
11 | describe('self', () => {
12 | it('parses self::', () => {
13 | const element = documentNode.createElement('someElement');
14 | chai.assert.equal(evaluateXPathToFirstNode('self::someElement', element), element);
15 | });
16 |
17 | it('returns the correct bucket', () => {
18 | const element = documentNode.createElement('someElement');
19 | chai.assert.include(
20 | getBucketsForNode(element),
21 | getBucketForSelector('self::someElement'),
22 | 'The self::element selector should have the matching buckets',
23 | );
24 | });
25 |
26 | it('returns the correct intersecting bucket', () => {
27 | const element = documentNode.createElement('someElement');
28 | chai.assert.include(
29 | getBucketsForNode(element),
30 | getBucketForSelector('self::*[self::someElement]'),
31 | 'The self::*[self::someElement] selector should have the matching buckets',
32 | );
33 | });
34 |
35 | it('throws the correct error if context is absent', () => {
36 | chai.assert.throws(() => evaluateXPathToFirstNode('self::*', null), 'XPDY0002');
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/specs/parsing/comments/comments.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPathToBoolean } from 'fontoxpath';
5 |
6 | let documentNode;
7 | beforeEach(() => {
8 | documentNode = new slimdom.Document();
9 | });
10 |
11 | describe('comments', () => {
12 | it('can parse comments', () =>
13 | chai.assert.isTrue(
14 | evaluateXPathToBoolean('true() (: and false() :) or true()', documentNode),
15 | ));
16 |
17 | it('can parse nested comments', () =>
18 | chai.assert.isTrue(
19 | evaluateXPathToBoolean(
20 | 'true() (: and false() (:and true():) :) or false',
21 | documentNode,
22 | ),
23 | ));
24 | });
25 |
--------------------------------------------------------------------------------
/test/specs/parsing/compareSpecificity.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { compareSpecificity, parseScript } from 'fontoxpath';
3 | import { Document } from 'slimdom';
4 |
5 | describe('compareSpecificity', () => {
6 | function assertSpecificity(
7 | selectorExpressionA: string,
8 | selectorExpressionB: string,
9 | expectedResult,
10 | ) {
11 | // Assert selectors as a string
12 | chai.assert.equal(
13 | compareSpecificity(selectorExpressionA, selectorExpressionB),
14 | expectedResult,
15 | );
16 |
17 | // Assert selectors as an AST
18 | chai.assert.equal(
19 | compareSpecificity(
20 | parseScript(selectorExpressionA, {}, new Document()),
21 | parseScript(selectorExpressionB, {}, new Document()),
22 | ),
23 | expectedResult,
24 | );
25 | }
26 |
27 | it('returns 0 for the same xpath', () => assertSpecificity('self::*', 'self::*', 0));
28 | it('nodeType > universal', () => assertSpecificity('self::element()', 'self::node()', 1));
29 | it('name > nodeType', () => assertSpecificity('self::name', 'self::element()', 1));
30 | it('attributes > nodeName', () => assertSpecificity('@id', 'self::name', 1));
31 | it('functions > attributes', () => assertSpecificity('id("123")', '@id', 1));
32 | it('union is the maximum of its operands', () =>
33 | assertSpecificity('self::name | id("123") | self::otherName | id("123")', 'self::name', 1));
34 | });
35 |
--------------------------------------------------------------------------------
/test/specs/parsing/conditional/IfExpression.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPathToBoolean } from 'fontoxpath';
5 |
6 | import evaluateXPathToAsyncSingleton from 'test-helpers/evaluateXPathToAsyncSingleton';
7 |
8 | describe('IfExpression', () => {
9 | let documentNode;
10 | beforeEach(() => {
11 | documentNode = new slimdom.Document();
12 | });
13 |
14 | it('returns the value of the then expression if the test resolves to true', () =>
15 | chai.assert(
16 | evaluateXPathToBoolean(
17 | '(if (true()) then "then expression" else "else expression") eq "then expression"',
18 | documentNode,
19 | ),
20 | ));
21 |
22 | it('returns the value of the else expression if the test resolves to false', () =>
23 | chai.assert(
24 | evaluateXPathToBoolean(
25 | '(if (false()) then "then expression" else "else expression") eq "else expression"',
26 | documentNode,
27 | ),
28 | ));
29 | });
30 |
--------------------------------------------------------------------------------
/test/specs/parsing/createSelectorFromXPath.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 | import jsonMlMapper from 'test-helpers/jsonMlMapper';
4 |
5 | import { evaluateXPathToNodes } from 'fontoxpath';
6 |
7 | describe('createExpressionFromXPath', () => {
8 | let documentNode;
9 | beforeEach(() => {
10 | documentNode = new slimdom.Document();
11 | });
12 |
13 | it('matches hovercrafts full of eels', () => {
14 | jsonMlMapper.parse(['hovercraft', ['eel'], ['eel']], documentNode);
15 | chai.assert.deepEqual(
16 | evaluateXPathToNodes(
17 | 'self::hovercraft[eel and not(*[not(self::eel)])]',
18 | documentNode.documentElement,
19 | ),
20 | [documentNode.documentElement],
21 | );
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/specs/parsing/createSelectorFromXPathAsync.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPathToNumber, precompileXPath } from 'fontoxpath';
3 | import * as slimdom from 'slimdom';
4 |
5 | describe('createExpressionFromXPathAsync', () => {
6 | let documentNode;
7 | beforeEach(() => {
8 | documentNode = new slimdom.Document();
9 | });
10 |
11 | it('can compile a selector asynchronously', () => {
12 | return precompileXPath('1 + 1').then(function (selector) {
13 | // Assume selector to be ok
14 | chai.expect(evaluateXPathToNumber(selector, documentNode)).to.equal(2);
15 | });
16 | }).timeout(10000);
17 |
18 | it('can compile a new, unique selector asynchronously', () => {
19 | const now = Date.now();
20 | return precompileXPath(`1 + ${now}`).then(function (selector) {
21 | // Assume selector to be ok
22 | chai.assert.equal(evaluateXPathToNumber(selector, documentNode), now + 1);
23 | });
24 | }).timeout(10000);
25 | });
26 |
--------------------------------------------------------------------------------
/test/specs/parsing/deprecatedFeatures.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPathToBoolean } from 'fontoxpath';
5 |
6 | describe('Deprecated features', () => {
7 | let documentNode;
8 | beforeEach(() => {
9 | documentNode = new slimdom.Document();
10 | });
11 |
12 | it('Does not accept functions as tests anymore', () =>
13 | chai.assert.throws(
14 | () => evaluateXPathToBoolean('self::false()', documentNode),
15 | 'XPST0003',
16 | ));
17 | });
18 |
--------------------------------------------------------------------------------
/test/specs/parsing/flwor.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPath, evaluateXPathToString, evaluateXPathToStrings } from 'fontoxpath';
5 | import evaluateXPathToAsyncSingleton from 'test-helpers/evaluateXPathToAsyncSingleton';
6 |
7 | let documentNode;
8 | beforeEach(() => {
9 | documentNode = new slimdom.Document();
10 | });
11 |
12 | describe('FLWOR', () => {
13 | it('runs basic flwor expression', () =>
14 | chai.assert.equal(
15 | evaluateXPathToString(
16 | `for $i in (0,1,2)
17 | let $e := 'Hello'
18 | return $e`,
19 | null,
20 | null,
21 | null,
22 | { debug: true, language: evaluateXPath.XQUERY_3_1_LANGUAGE },
23 | ),
24 | 'Hello Hello Hello',
25 | ));
26 |
27 | it('runs flwor with where', () =>
28 | chai.assert.equal(
29 | evaluateXPathToString(
30 | `for $i in (0,1,2)
31 | where $i = 1
32 | let $e := 'Hello'
33 | return $e`,
34 | null,
35 | null,
36 | null,
37 | { debug: true, language: evaluateXPath.XQUERY_3_1_LANGUAGE },
38 | ),
39 | 'Hello',
40 | ));
41 |
42 | it('runs flwor expressions with order by', () => {
43 | chai.assert.deepEqual(
44 | evaluateXPathToStrings(
45 | `for $a in ("B", "A", "C")
46 | order by $a
47 | return $a`,
48 | null,
49 | null,
50 | null,
51 | { debug: true, language: evaluateXPath.XQUERY_3_1_LANGUAGE },
52 | ),
53 | ['A', 'B', 'C'],
54 | );
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/test/specs/parsing/functions/builtinFunctions.context.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPathToBoolean, evaluateXPathToString } from 'fontoxpath';
5 |
6 | let documentNode;
7 | beforeEach(() => {
8 | documentNode = new slimdom.Document();
9 | });
10 |
11 | describe('Context related functions', () => {
12 | describe('fn:current-dateTime', () => {
13 | it('returns the current dateTime', () =>
14 | chai.assert.isOk(evaluateXPathToString('current-dateTime()', documentNode)));
15 | it('returns the same value during the execution of the query', () =>
16 | chai.assert.isTrue(evaluateXPathToBoolean('current-dateTime() eq current-dateTime()')));
17 | it('returns different values for different queries', async () => {
18 | const dateTime = evaluateXPathToString('current-dateTime()');
19 | const dateTimeLater = await new Promise((resolve) =>
20 | setTimeout(() => resolve(evaluateXPathToString('current-dateTime()')), 100),
21 | );
22 |
23 | chai.assert.notEqual(dateTime, dateTimeLater);
24 | });
25 | });
26 | describe('fn:current-date', () => {
27 | it('returns the current date', () =>
28 | chai.assert.isOk(evaluateXPathToString('current-date()', documentNode)));
29 | });
30 | describe('fn:current-time', () => {
31 | it('returns the current time', () =>
32 | chai.assert.isOk(evaluateXPathToString('current-time()', documentNode)));
33 | });
34 | describe('fn:implicit-timezone', () => {
35 | it('returns the implicit timezone', () =>
36 | chai.assert.isOk(evaluateXPathToString('implicit-timezone()', documentNode)));
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/specs/parsing/functions/functions.boolean.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { domFacade, evaluateXPathToBoolean } from 'fontoxpath';
5 |
6 | import evaluateXPathToAsyncSingleton from 'test-helpers/evaluateXPathToAsyncSingleton';
7 |
8 | let documentNode;
9 | beforeEach(() => {
10 | documentNode = new slimdom.Document();
11 | });
12 |
13 | describe('boolean functions', () => {
14 | describe('xs:boolean()', () => {
15 | it('accepts "true"', () => {
16 | chai.assert.equal(
17 | evaluateXPathToBoolean('xs:boolean("true")', documentNode, domFacade),
18 | true,
19 | );
20 | });
21 | });
22 | describe('fn:boolean()', () => {
23 | it('accepts "true"', () => {
24 | chai.assert.equal(
25 | evaluateXPathToBoolean('fn:boolean("true")', documentNode, domFacade),
26 | true,
27 | );
28 | });
29 | });
30 | describe('fn:not()', () => {
31 | it('accepts true', () => {
32 | chai.assert.isFalse(evaluateXPathToBoolean('not(true())', documentNode, domFacade));
33 | });
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/test/specs/parsing/functions/functions.json.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import {
5 | domFacade,
6 | evaluateXPathToArray,
7 | evaluateXPathToBoolean,
8 | evaluateXPathToMap,
9 | } from 'fontoxpath';
10 |
11 | let documentNode;
12 | beforeEach(() => {
13 | documentNode = new slimdom.Document();
14 | });
15 |
16 | describe('functions over json', () => {
17 | it('can parse json objects', () =>
18 | chai.assert.deepEqual(
19 | evaluateXPathToMap('parse-json("{""a"": 1}")', documentNode, domFacade),
20 | { a: 1 },
21 | ));
22 |
23 | it('can parse json arrays', () =>
24 | chai.assert.deepEqual(
25 | evaluateXPathToArray('parse-json("[1,2,3]")', documentNode, domFacade),
26 | [1, 2, 3],
27 | ));
28 |
29 | it('can parse json booleans', () =>
30 | chai.assert.isTrue(
31 | evaluateXPathToBoolean('parse-json("true") eq true()', documentNode, domFacade),
32 | ));
33 |
34 | it('can parse json numbers', () =>
35 | chai.assert.isTrue(
36 | evaluateXPathToBoolean('parse-json("1") eq 1e0', documentNode, domFacade),
37 | ));
38 |
39 | it('can parse json strings', () =>
40 | chai.assert.isTrue(
41 | evaluateXPathToBoolean(
42 | 'parse-json("""A piece of text""") eq "A piece of text"',
43 | documentNode,
44 | domFacade,
45 | ),
46 | ));
47 |
48 | it('can parse json null', () =>
49 | chai.assert.isTrue(
50 | evaluateXPathToBoolean('parse-json("null") => count() eq 0', documentNode, domFacade),
51 | ));
52 |
53 | it('returns an error for invalid json', () =>
54 | chai.assert.throws(
55 | () =>
56 | evaluateXPathToBoolean('parse-json("}{") => count() eq 0', documentNode, domFacade),
57 | 'FOJS0001',
58 | ));
59 | });
60 |
--------------------------------------------------------------------------------
/test/specs/parsing/getBucketsForNode.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { getBucketsForNode } from 'fontoxpath';
3 | import * as slimdom from 'slimdom';
4 | const doc = new slimdom.Document();
5 | describe('getBucketsForNode', () => {
6 | it('returns the correct buckets for elements', () => {
7 | chai.assert.deepEqual(getBucketsForNode(doc.createElement('element')), [
8 | 'type-1-or-type-2',
9 | 'type-1',
10 | 'name-element',
11 | ]);
12 | });
13 | it('returns the correct buckets for text nodes', () => {
14 | chai.assert.deepEqual(getBucketsForNode(doc.createTextNode('A piece of text')), ['type-3']);
15 | chai.assert.deepEqual(getBucketsForNode(doc.createCDATASection('A piece of cdata')), [
16 | 'type-3',
17 | ]);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/specs/parsing/indexOf.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 |
3 | import { evaluateXPathToNumber } from 'fontoxpath';
4 |
5 | describe('indexOf', () => {
6 | it('determine a single index', () =>
7 | chai.assert.equal(evaluateXPathToNumber('index-of(1, 1)'), 1));
8 |
9 | it('determine a single index in two values', () =>
10 | chai.assert.equal(evaluateXPathToNumber('index-of((1, 2), 2)'), 2));
11 |
12 | it('determine a single index in a string set', () =>
13 | chai.assert.equal(evaluateXPathToNumber('index-of(("Hey", "Hola", "Hi"), "Hola")'), 2));
14 | });
15 |
--------------------------------------------------------------------------------
/test/specs/parsing/jsCodegen/blns.tests.ts:
--------------------------------------------------------------------------------
1 | import * as blns from 'blns';
2 | import * as chai from 'chai';
3 | import { evaluateXPath, ReturnType } from 'fontoxpath';
4 | import * as slimdom from 'slimdom';
5 | import jsonMlMapper from 'test-helpers/jsonMlMapper';
6 | import evaluateXPathWithJsCodegen from './evaluateXPathWithJsCodegen';
7 |
8 | describe('Big List of Naughty Strings tests for JSCodegen', () => {
9 | let documentNode: slimdom.Document;
10 | beforeEach(() => {
11 | documentNode = new slimdom.Document();
12 | jsonMlMapper.parse(['xml', 'Hello'], documentNode);
13 | });
14 |
15 | for (const badString of blns) {
16 | it(`BLNS equality: ${badString}`, () => {
17 | const literalQuery = '"' + badString + '"';
18 |
19 | try {
20 | const normalRes = evaluateXPath(
21 | literalQuery,
22 | documentNode,
23 | null,
24 | null,
25 | ReturnType.STRING,
26 | {},
27 | );
28 | const jsCodegenRes = evaluateXPathWithJsCodegen(
29 | literalQuery,
30 | documentNode,
31 | null,
32 | ReturnType.STRING,
33 | {},
34 | );
35 |
36 | chai.assert.equal(jsCodegenRes, normalRes);
37 | } catch (errEval) {
38 | try {
39 | evaluateXPathWithJsCodegen(
40 | literalQuery,
41 | documentNode,
42 | null,
43 | ReturnType.STRING,
44 | {},
45 | );
46 | chai.assert.fail(
47 | `BLNS literal ${literalQuery} errored during evaluation but not during JS Codegen.`,
48 | );
49 | } catch (errJs) {
50 | // Only comparing the string representations, since the stack traces will differ.
51 | chai.assert.equal(errEval.toString(), errJs.toString());
52 | }
53 | }
54 | });
55 | }
56 | });
57 |
--------------------------------------------------------------------------------
/test/specs/parsing/jsCodegen/operators.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import jsonMlMapper from 'test-helpers/jsonMlMapper';
5 |
6 | import { compileXPathToJavaScript, ReturnType } from 'fontoxpath';
7 | import evaluateXPathWithJsCodegen from './evaluateXPathWithJsCodegen';
8 |
9 | describe('operators', () => {
10 | let documentNode: slimdom.Document;
11 |
12 | beforeEach(() => {
13 | documentNode = new slimdom.Document();
14 | jsonMlMapper.parse(['xml', ['title', 'Tips'], ['tips']], documentNode);
15 | });
16 |
17 | it('compiles "or" when used as a base expression', () => {
18 | const elementNode: slimdom.Node = documentNode.firstChild;
19 | chai.assert.isTrue(
20 | evaluateXPathWithJsCodegen(
21 | 'self::p or self::xml',
22 | elementNode,
23 | null,
24 | ReturnType.BOOLEAN,
25 | ),
26 | );
27 | });
28 |
29 | it('compiles "and" when used as a base expression', () => {
30 | const elementNode: slimdom.Node = documentNode.firstChild;
31 | chai.assert.isTrue(
32 | evaluateXPathWithJsCodegen(
33 | 'self::xml and child::element(tips)',
34 | elementNode,
35 | null,
36 | ReturnType.BOOLEAN,
37 | ),
38 | );
39 | });
40 |
41 | it('rejects logical operator when lhs is not compilable', () => {
42 | chai.assert.isFalse(
43 | compileXPathToJavaScript('count((1,2,3)) and self::xml', ReturnType.BOOLEAN, {})
44 | .isAstAccepted,
45 | );
46 | });
47 |
48 | it('rejects logical operator when rhs is not compilable', () => {
49 | chai.assert.isFalse(
50 | compileXPathToJavaScript('self::xml and count((1,2,3))', ReturnType.BOOLEAN, {})
51 | .isAstAccepted,
52 | );
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/specs/parsing/jsCodegen/wildcard.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import jsonMlMapper from 'test-helpers/jsonMlMapper';
5 |
6 | import { ReturnType } from 'fontoxpath';
7 | import evaluateXPathWithJsCodegen from './evaluateXPathWithJsCodegen';
8 |
9 | describe('wildcard', () => {
10 | let documentNode: slimdom.Document;
11 |
12 | beforeEach(() => {
13 | documentNode = new slimdom.Document();
14 | jsonMlMapper.parse(
15 | [
16 | 'xml',
17 | ['title', 'Tips'],
18 | [
19 | 'tips',
20 | ['tip', 'Make it work'],
21 | ['tip', 'Make it right'],
22 | ['tip', 'Make it fast'],
23 | ],
24 | ],
25 | documentNode,
26 | );
27 | });
28 |
29 | it('selects elements (non-specified namespace)', () => {
30 | const results = evaluateXPathWithJsCodegen('/xml/*', documentNode, null, ReturnType.NODES);
31 |
32 | chai.assert.equal(results.length, 2);
33 | });
34 |
35 | it('does not select text elements', () => {
36 | chai.assert.isFalse(
37 | evaluateXPathWithJsCodegen('/xml/tips/tip/*', documentNode, null, ReturnType.BOOLEAN),
38 | );
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/test/specs/parsing/operators/boolean/AndOperator.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPathToBoolean } from 'fontoxpath';
3 | import * as slimdom from 'slimdom';
4 |
5 | import evaluateXPathToAsyncSingleton from 'test-helpers/evaluateXPathToAsyncSingleton';
6 |
7 | describe('and operator', () => {
8 | it('can perform "1 and 1"', () => chai.assert.isTrue(evaluateXPathToBoolean('1 and 1')));
9 |
10 | it('can parse an "and" selector', () => {
11 | chai.assert.isTrue(evaluateXPathToBoolean('true() and true()'));
12 | });
13 |
14 | it('can not compute the ebv of a sequence with 2 items', () => {
15 | chai.assert.throws(
16 | () => evaluateXPathToBoolean('(false(), false()) and (false(), false())'),
17 | 'FORG0006',
18 | );
19 | });
20 |
21 | it('can turns non-empty nodes sequences into "true"', () => {
22 | chai.assert.isTrue(evaluateXPathToBoolean('true() and (.,.)', new slimdom.Document()));
23 | });
24 |
25 | it('can optimize an and expression with buckets', () => {
26 | chai.assert.isFalse(evaluateXPathToBoolean('self::p and true()', new slimdom.Document()));
27 | });
28 |
29 | it('can optimize an and expression with buckets, inside a not()', () => {
30 | chai.assert.isTrue(
31 | evaluateXPathToBoolean('not(self::p and true())', new slimdom.Document()),
32 | );
33 | });
34 |
35 | it('can parse a concatenation of ands', () =>
36 | chai.assert.isFalse(evaluateXPathToBoolean('true() and true() and true() and false()')));
37 | });
38 |
--------------------------------------------------------------------------------
/test/specs/parsing/operators/boolean/OrOperator.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 | import evaluateXPathToAsyncSingleton from 'test-helpers/evaluateXPathToAsyncSingleton';
4 | import jsonMlMapper from 'test-helpers/jsonMlMapper';
5 |
6 | import { evaluateXPathToBoolean } from 'fontoxpath';
7 |
8 | let documentNode;
9 | beforeEach(() => {
10 | documentNode = new slimdom.Document();
11 | });
12 |
13 | describe('or operator', () => {
14 | it('can parse an "or" selector', () => {
15 | chai.assert.isTrue(evaluateXPathToBoolean('false() or true()'));
16 | });
17 | it('can parse an "or" selector returning false', () => {
18 | chai.assert.isFalse(evaluateXPathToBoolean('false() or false()'));
19 | });
20 |
21 | it('can parse an "or" selector with different buckets', () => {
22 | jsonMlMapper.parse(['someParentElement', ['someElement']], documentNode);
23 | chai.assert.isTrue(
24 | evaluateXPathToBoolean(
25 | 'self::someElement or self::processing-instruction()',
26 | documentNode.documentElement.firstChild,
27 | ),
28 | );
29 | });
30 |
31 | it('can parse a concatenation of ors', () =>
32 | chai.assert.isTrue(
33 | evaluateXPathToBoolean(
34 | 'false() or false() or false() or (: Note: the last true() will make te result true:) true()',
35 | ),
36 | ));
37 |
38 | it('allows not in combination with or', () => {
39 | jsonMlMapper.parse(['someOtherParentElement', ['someOtherChildElement']], documentNode);
40 | chai.assert.isTrue(
41 | evaluateXPathToBoolean(
42 | 'someChildElement or not(someOtherChild)',
43 | documentNode.documentElement,
44 | ),
45 | );
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/specs/parsing/operators/boolean/operatorPrecedence.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 | import jsonMlMapper from 'test-helpers/jsonMlMapper';
4 |
5 | import { evaluateXPathToBoolean } from 'fontoxpath';
6 |
7 | let documentNode;
8 | beforeEach(() => {
9 | documentNode = new slimdom.Document();
10 | });
11 |
12 | describe('operators', () => {
13 | it('uses correct operator precedence', () => {
14 | jsonMlMapper.parse(
15 | [
16 | 'someParentElement',
17 | ['someMiddleElement', { someAttribute: 'someValue' }, ['someOtherElement']],
18 | ],
19 | documentNode,
20 | );
21 | chai.assert.isTrue(
22 | evaluateXPathToBoolean(
23 | "(child::someElement and ancestor::someParentElement) or @someAttribute='someValue'",
24 | documentNode.documentElement.firstChild,
25 | ),
26 | );
27 | // The other way around
28 | chai.assert.isTrue(
29 | evaluateXPathToBoolean(
30 | "(child::someOtherElement and ancestor::someParentElement) or @someAttribute='someOtherValue'",
31 | documentNode.documentElement.firstChild,
32 | ),
33 | );
34 | // Changes to testcase A: Operator order changed because of parentheses
35 | chai.assert.isFalse(
36 | evaluateXPathToBoolean(
37 | 'child::someElement and (ancestor::someParentElement or @someAttribute="someValue")',
38 | documentNode.documentElement.firstChild,
39 | ),
40 | );
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/test/specs/parsing/operators/sequenceOperator.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPathToBoolean, evaluateXPathToNumbers } from 'fontoxpath';
5 |
6 | import evaluateXPathToAsyncSingleton from 'test-helpers/evaluateXPathToAsyncSingleton';
7 |
8 | describe('sequence', () => {
9 | it('creates a sequence', () =>
10 | chai.assert.deepEqual(evaluateXPathToNumbers('(1,2,3)'), [1, 2, 3]));
11 |
12 | it('creates an empty sequence', () => chai.assert.deepEqual(evaluateXPathToNumbers('()'), []));
13 |
14 | it('normalizes sequences', () =>
15 | chai.assert.deepEqual(evaluateXPathToNumbers('(1,2,(3,4))'), [1, 2, 3, 4]));
16 | });
17 |
18 | describe('range', () => {
19 | it('creates a sequence', () =>
20 | chai.assert.deepEqual(evaluateXPathToNumbers('1 to 10'), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
21 |
22 | it('creates an empty sequence when passed a > b', () =>
23 | chai.assert.deepEqual(evaluateXPathToNumbers('10 to 1'), []));
24 |
25 | it('creates an empty sequence when passed () to 10', () =>
26 | chai.assert.deepEqual(evaluateXPathToNumbers('() to 10'), []));
27 |
28 | it('creates a sequence of correct length', () =>
29 | chai.assert.isTrue(evaluateXPathToBoolean('(1 to 10) => count() = 10')));
30 |
31 | it('creates an empty sequence when passed 1 to ()', () =>
32 | chai.assert.deepEqual(evaluateXPathToNumbers('1 to ()'), []));
33 | });
34 |
--------------------------------------------------------------------------------
/test/specs/parsing/operators/stringConcat.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPathToString } from 'fontoxpath';
5 |
6 | let documentNode;
7 | beforeEach(() => {
8 | documentNode = new slimdom.Document();
9 | });
10 |
11 | describe('stringConcat', () => {
12 | it('can concatenate strings', () =>
13 | chai.assert.equal(
14 | evaluateXPathToString('"con" || "cat" || "enate"', documentNode),
15 | 'concatenate',
16 | ));
17 |
18 | it('can concatenate empty sequences', () =>
19 | chai.assert.equal(
20 | evaluateXPathToString(
21 | '() || "con" || () || "cat" || () || "enate" || ()',
22 | documentNode,
23 | ),
24 | 'concatenate',
25 | ));
26 | });
27 |
--------------------------------------------------------------------------------
/test/specs/parsing/postfix/Filter.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPathToNumber, evaluateXPathToNumbers } from 'fontoxpath';
3 |
4 | describe('Filter (predicate)', () => {
5 | it('parses', () => chai.assert.equal(evaluateXPathToNumber('(1,2,3)[. = 2]'), 2));
6 | it('allows spaces', () => chai.assert.equal(evaluateXPathToNumber('(1,2,3) [. = 2]'), 2));
7 | it('returns empty sequence when inputted empty sequence', () =>
8 | chai.assert.isEmpty(evaluateXPathToNumbers('(1,2,3)[()]')));
9 | it('returns the sequence when filtering with a string', () =>
10 | chai.assert.deepEqual(evaluateXPathToNumbers('(1,2,3,4)["TAKE ME"]'), [1, 2, 3, 4]));
11 | it('returns the empty sequence when filtering with an empty string', () =>
12 | chai.assert.isEmpty(evaluateXPathToNumbers('(1,2,3,4)[""]')));
13 | });
14 |
--------------------------------------------------------------------------------
/test/specs/parsing/xquery-updating/DeleteExpression.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateUpdatingExpressionSync } from 'fontoxpath';
5 | import assertUpdateList from './assertUpdateList';
6 |
7 | let documentNode: slimdom.Document;
8 | beforeEach(() => {
9 | documentNode = new slimdom.Document();
10 | });
11 |
12 | describe('DeleteExpression', () => {
13 | it('merges pul from target expressions', () => {
14 | const element = documentNode.appendChild(documentNode.createElement('element'));
15 | const a = element.appendChild(documentNode.createElement('a'));
16 |
17 | const result = evaluateUpdatingExpressionSync(
18 | `delete node (/element, delete node /element/a)`,
19 | documentNode,
20 | null,
21 | {},
22 | {},
23 | );
24 |
25 | chai.assert.deepEqual(result.xdmValue, []);
26 | assertUpdateList(result.pendingUpdateList, [
27 | {
28 | type: 'delete',
29 | target: element,
30 | },
31 | {
32 | type: 'delete',
33 | target: a,
34 | },
35 | ]);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/test/specs/parsing/xquery-updating/assertUpdateList.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | export default function assertUpdateList(actual, expected) {
5 | chai.assert.equal(actual.length, expected.length, 'pendingUpdates.length');
6 | for (let i = 0, l = expected.length; i < l; ++i) {
7 | chai.assert.equal(actual[i].type, expected[i].type);
8 | chai.assert.equal(actual[i].target, expected[i].target);
9 |
10 | switch (actual[i].type) {
11 | case 'rename':
12 | chai.assert.equal(actual[i].newName.prefix, expected[i].newName.prefix);
13 | chai.assert.equal(actual[i].newName.namespaceURI, expected[i].newName.namespaceURI);
14 | chai.assert.equal(actual[i].newName.localName, expected[i].newName.localName);
15 | break;
16 | case 'replaceNode':
17 | actual[i].replacement.forEach((replacement, j) =>
18 | chai.assert.equal(
19 | new slimdom.XMLSerializer().serializeToString(replacement),
20 | expected[i].replacementXML[j],
21 | ),
22 | );
23 | break;
24 | case 'replaceElementContent':
25 | chai.assert.equal(actual[i].text.data, expected[i].text);
26 | break;
27 | case 'replaceValue':
28 | chai.assert.equal(actual[i]['string-value'], expected[i].stringValue);
29 | break;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/test/specs/parsing/xquery/AttributeConstructor.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPath, evaluateXPathToFirstNode } from 'fontoxpath';
5 | import evaluateXPathToAsyncSingleton from 'test-helpers/evaluateXPathToAsyncSingleton';
6 |
7 | let documentNode;
8 | beforeEach(() => {
9 | documentNode = new slimdom.Document();
10 | });
11 |
12 | describe('AttributeConstructor', () => {
13 | it('can create an attribute', () => {
14 | const attribute = evaluateXPathToFirstNode(
15 | 'attribute attr {"val"}',
16 | documentNode,
17 | undefined,
18 | {},
19 | { language: evaluateXPath.XQUERY_3_1_LANGUAGE },
20 | );
21 |
22 | chai.assert.equal(attribute.nodeType, 2);
23 | chai.assert.equal(attribute.name, 'attr');
24 | chai.assert.equal(attribute.value, 'val');
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/specs/parsing/xquery/DefaultElementNamespace.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { Document } from 'slimdom';
3 |
4 | import { evaluateXPath, evaluateXPathToBoolean } from 'fontoxpath';
5 |
6 | let documentNode: Document;
7 | beforeEach(() => {
8 | documentNode = new Document();
9 | });
10 |
11 | describe('DefaultElementNamespace', () => {
12 | it('Can create a default element namespace', () => {
13 | chai.assert.isTrue(
14 | evaluateXPathToBoolean(
15 | `declare default element namespace "http://example.com";
16 |
17 | self::p
18 | `,
19 | documentNode.createElementNS('http://example.com', 'p'),
20 | undefined,
21 | {},
22 | { language: evaluateXPath.XQUERY_3_1_LANGUAGE },
23 | ),
24 | );
25 |
26 | chai.assert.isFalse(
27 | evaluateXPathToBoolean(
28 | `declare default element namespace "something-else";
29 |
30 | self::p
31 | `,
32 | documentNode.createElementNS('http://example.com', 'p'),
33 | undefined,
34 | {},
35 | { language: evaluateXPath.XQUERY_3_1_LANGUAGE },
36 | ),
37 | );
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/test/specs/parsing/xquery/PIConstructor.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import * as slimdom from 'slimdom';
3 |
4 | import { evaluateXPath, evaluateXPathToFirstNode } from 'fontoxpath';
5 | import evaluateXPathToAsyncSingleton from 'test-helpers/evaluateXPathToAsyncSingleton';
6 |
7 | let documentNode;
8 | beforeEach(() => {
9 | documentNode = new slimdom.Document();
10 | });
11 |
12 | describe('PIConstructor', () => {
13 | it('can create a PI', () => {
14 | chai.assert.equal(
15 | evaluateXPathToFirstNode(
16 | 'processing-instruction my-pi { "data" }',
17 | documentNode,
18 | undefined,
19 | {},
20 | { language: evaluateXPath.XQUERY_3_1_LANGUAGE },
21 | ).nodeType,
22 | 7,
23 | );
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/specs/parsing/xquery/TextConstructor.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import { evaluateXPath, evaluateXPathToString } from 'fontoxpath';
3 |
4 | import * as slimdom from 'slimdom';
5 |
6 | let documentNode;
7 | beforeEach(() => {
8 | documentNode = new slimdom.Document();
9 | });
10 |
11 | describe('Computed Text Constructor ', () => {
12 | it('create a a computed text constructor', () => {
13 | chai.assert.equal(
14 | evaluateXPathToString(
15 | `text {"Hello"}`,
16 | documentNode,
17 | undefined,
18 | {},
19 | { language: evaluateXPath.XQUERY_3_1_LANGUAGE },
20 | ),
21 | 'Hello',
22 | );
23 | });
24 |
25 | it('create a a computed text constructor expression', () => {
26 | chai.assert.equal(
27 | evaluateXPathToString(
28 | `text {"
"}`,
29 | documentNode,
30 | undefined,
31 | {},
32 | { language: evaluateXPath.XQUERY_3_1_LANGUAGE },
33 | ),
34 | '\n',
35 | );
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/test/specs/parsing/xquery/Typeswitch.tests.ts:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 |
3 | import { evaluateXPath, evaluateXPathToNumber, evaluateXPathToString } from 'fontoxpath';
4 |
5 | describe('Typeswitch', () => {
6 | it('runs typeswitch and returns an integer', () =>
7 | chai.assert.equal(
8 | evaluateXPathToNumber(
9 | `typeswitch((1,2))
10 | case xs:integer return 1
11 | case xs:string+ return 42
12 | case xs:float | xs:string return 27
13 | case xs:integer* return 2828
14 | default return 2`,
15 | null,
16 | null,
17 | null,
18 | { debug: true, language: evaluateXPath.XQUERY_3_1_LANGUAGE },
19 | ),
20 | 2828,
21 | ));
22 |
23 | it('runs typeswitch and returns a string', () =>
24 | chai.assert.equal(
25 | evaluateXPathToString(
26 | `typeswitch(("Hello", "Hi"))
27 | case xs:integer? return "Hey"
28 | case xs:string+ return "Good morning"
29 | case xs:float return "Good afternoon"
30 | case xs:integer* return "Good evening"
31 | default return "Bye"`,
32 | null,
33 | null,
34 | null,
35 | { debug: true, language: evaluateXPath.XQUERY_3_1_LANGUAGE },
36 | ),
37 | 'Good morning',
38 | ));
39 | });
40 |
--------------------------------------------------------------------------------
/test/testhook.js:
--------------------------------------------------------------------------------
1 | const useDist = process.argv.includes('--dist');
2 | const useJsCodegen = process.argv.includes('--jscodegen');
3 |
4 | console.log(
5 | (useDist ? 'Running tests against dist build' : 'Running tests against development bundle') +
6 | (useJsCodegen ? ' using the JSCodegen backend where possible' : '')
7 | );
8 |
9 | const tsConfig = require('./tsconfig.json');
10 |
11 | const tsConfigPaths = require('tsconfig-paths');
12 |
13 | if (useDist) {
14 | tsConfig.compilerOptions.paths.fontoxpath = ['../dist/fontoxpath.js'];
15 | }
16 |
17 | if (useJsCodegen) {
18 | tsConfig.compilerOptions.paths.fontoxpath = ['../test/testCodegenFacade.ts'];
19 | }
20 |
21 | // Make the import of 'xspattern' point to the node_modules version for unit tests
22 | tsConfig.compilerOptions.paths.xspattern = ['node_modules/xspattern/dist/xspattern.ts'];
23 |
24 | tsConfigPaths.register({
25 | baseUrl: './test',
26 | paths: tsConfig.compilerOptions.paths,
27 | allowJs: true,
28 | include: '../dist/*',
29 | });
30 |
31 | require('source-map-support/register');
32 |
--------------------------------------------------------------------------------
/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "fontoxpath": ["../src/index.ts"],
6 | "fontoxpath/*": ["../src/*"],
7 | "test-helpers/*": ["./helpers/*"]
8 | },
9 | "strict": false,
10 | "allowJs": true,
11 | "types": ["mocha", "node"],
12 | "moduleResolution": "node",
13 | "target": "es2020"
14 | },
15 | "include": ["../src/**/*.ts", "./**/*.ts"]
16 | }
17 |
--------------------------------------------------------------------------------
/test/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "warning",
3 | "extends": ["tslint:latest", "tslint-config-prettier"],
4 | "rules": {
5 | "array-type": [true, "array"],
6 | "class-name": true,
7 | "comment-format": [true, "check-space"],
8 | "interface-name": true,
9 | "interface-over-type-literal": false,
10 | "match-default-export-name": true,
11 | "no-implicit-dependencies": [true, "dev"],
12 | "no-bitwise": false,
13 | "no-implicit-dependencies": false,
14 | "no-string-literal": false,
15 | "no-submodule-imports": false,
16 | "no-unnecessary-qualifier": true,
17 | "no-unnecessary-type-assertion": true,
18 | "no-unused-expression": true,
19 | "no-var-keyword": true,
20 | "member-ordering": [
21 | true,
22 | {
23 | "alphabetize": true,
24 | "order": "fields-first"
25 | }
26 | ],
27 | "ordered-imports": true,
28 | "prefer-for-of": false,
29 | "semicolon": [true, "always", "ignore-bound-class-methods"],
30 | "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "declaration": true,
5 | "lib": ["es6", "es2017", "esnext.asynciterable"],
6 | "module": "commonjs",
7 | "noImplicitAny": true,
8 | "outDir": "./built",
9 | "strict": false,
10 | "target": "es2017"
11 | },
12 | "include": ["./src/**/*"],
13 | "exclude": ["./**/*.d.ts"]
14 | }
15 |
--------------------------------------------------------------------------------