@tradik/xslt3 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/LICENSE.md +28 -0
  2. package/README.md +98 -2
  3. package/package.json +43 -4
  4. package/src/errors.js +20 -0
  5. package/src/functions/aggregates.js +144 -0
  6. package/src/functions/arraySort.js +33 -0
  7. package/src/functions/arrays.js +181 -0
  8. package/src/functions/attributeTypes.js +58 -0
  9. package/src/functions/baseUris.js +82 -0
  10. package/src/functions/collations.js +127 -0
  11. package/src/functions/collections.js +84 -0
  12. package/src/functions/context.js +83 -0
  13. package/src/functions/core.js +167 -0
  14. package/src/functions/datetime.js +203 -0
  15. package/src/functions/deepEqual.js +132 -0
  16. package/src/functions/diagnostics.js +88 -0
  17. package/src/functions/focus.js +34 -0
  18. package/src/functions/format/dateComponents.js +176 -0
  19. package/src/functions/format/datePicture.js +115 -0
  20. package/src/functions/format/decimalFormat.js +65 -0
  21. package/src/functions/format/digitPattern.js +145 -0
  22. package/src/functions/format/formatDateTime.js +204 -0
  23. package/src/functions/format/formatInteger.js +109 -0
  24. package/src/functions/format/formatNumber.js +165 -0
  25. package/src/functions/format/fractionFormat.js +80 -0
  26. package/src/functions/format/germanWords.js +89 -0
  27. package/src/functions/format/numberLanguages.js +41 -0
  28. package/src/functions/format/numberPicture.js +202 -0
  29. package/src/functions/format/numberWords.js +155 -0
  30. package/src/functions/format/place.js +74 -0
  31. package/src/functions/format/timezoneFormat.js +89 -0
  32. package/src/functions/higherOrder.js +106 -0
  33. package/src/functions/ids.js +127 -0
  34. package/src/functions/json/index.js +17 -0
  35. package/src/functions/json/jsonChars.js +110 -0
  36. package/src/functions/json/jsonParser.js +203 -0
  37. package/src/functions/json/jsonStrings.js +116 -0
  38. package/src/functions/json/jsonToXml.js +116 -0
  39. package/src/functions/json/parseJson.js +119 -0
  40. package/src/functions/json/xmlRepresentation.js +103 -0
  41. package/src/functions/json/xmlToJson.js +147 -0
  42. package/src/functions/library.js +47 -0
  43. package/src/functions/mapArrayEqual.js +41 -0
  44. package/src/functions/maps.js +152 -0
  45. package/src/functions/math.js +73 -0
  46. package/src/functions/nodePaths.js +181 -0
  47. package/src/functions/nodes.js +199 -0
  48. package/src/functions/numeric.js +148 -0
  49. package/src/functions/options.js +58 -0
  50. package/src/functions/parseIetfDate.js +104 -0
  51. package/src/functions/parseXml.js +77 -0
  52. package/src/functions/qnames.js +68 -0
  53. package/src/functions/random.js +128 -0
  54. package/src/functions/regex/analyzeString.js +144 -0
  55. package/src/functions/regex/charClass.js +112 -0
  56. package/src/functions/regex/compile.js +81 -0
  57. package/src/functions/regex/escapes.js +109 -0
  58. package/src/functions/regex/parser.js +169 -0
  59. package/src/functions/regex/reader.js +37 -0
  60. package/src/functions/regex/regexFunctions.js +140 -0
  61. package/src/functions/regex/translate.js +102 -0
  62. package/src/functions/regex/unicodeBlocks.js +194 -0
  63. package/src/functions/registry.js +140 -0
  64. package/src/functions/sequences.js +173 -0
  65. package/src/functions/serializeFunction.js +53 -0
  66. package/src/functions/signatures.js +105 -0
  67. package/src/functions/sort.js +102 -0
  68. package/src/functions/stringMatch.js +45 -0
  69. package/src/functions/strings.js +194 -0
  70. package/src/functions/stringsCore.js +66 -0
  71. package/src/functions/support.js +106 -0
  72. package/src/functions/textDecoding.js +117 -0
  73. package/src/functions/ucaCollation.js +127 -0
  74. package/src/functions/unparsedText.js +71 -0
  75. package/src/functions/uri.js +166 -0
  76. package/src/functions/xqueryModule.js +24 -0
  77. package/src/index.js +38 -0
  78. package/src/items/array.js +84 -0
  79. package/src/items/function.js +45 -0
  80. package/src/items/map.js +120 -0
  81. package/src/items/mapKey.js +82 -0
  82. package/src/serialize/index.js +129 -0
  83. package/src/serialize/markup/content.js +74 -0
  84. package/src/serialize/markup/dialect.js +178 -0
  85. package/src/serialize/markup/htmlData.js +70 -0
  86. package/src/serialize/markup/namespaces.js +145 -0
  87. package/src/serialize/markup/prolog.js +91 -0
  88. package/src/serialize/markup/writer.js +183 -0
  89. package/src/serialize/methods/adaptive.js +131 -0
  90. package/src/serialize/methods/json.js +158 -0
  91. package/src/serialize/methods/text.js +43 -0
  92. package/src/serialize/output/buffer.js +48 -0
  93. package/src/serialize/output/encoding.js +146 -0
  94. package/src/serialize/output/expander.js +204 -0
  95. package/src/serialize/params/fromElement.js +141 -0
  96. package/src/serialize/params/fromMap.js +128 -0
  97. package/src/serialize/params/names.js +69 -0
  98. package/src/serialize/params/settings.js +153 -0
  99. package/src/serialize/params/values.js +163 -0
  100. package/src/serialize/sequence.js +111 -0
  101. package/src/xdm/arithmetic.js +155 -0
  102. package/src/xdm/atomic.js +142 -0
  103. package/src/xdm/binary.js +92 -0
  104. package/src/xdm/calendar.js +71 -0
  105. package/src/xdm/cast.js +176 -0
  106. package/src/xdm/compare.js +192 -0
  107. package/src/xdm/datetime.js +206 -0
  108. package/src/xdm/decimal.js +200 -0
  109. package/src/xdm/duration.js +128 -0
  110. package/src/xdm/generalCompare.js +152 -0
  111. package/src/xdm/index.js +47 -0
  112. package/src/xdm/lexical.js +126 -0
  113. package/src/xdm/nodes.js +170 -0
  114. package/src/xdm/numeric.js +122 -0
  115. package/src/xdm/qname.js +65 -0
  116. package/src/xdm/strings.js +52 -0
  117. package/src/xdm/temporalArithmetic.js +180 -0
  118. package/src/xdm/timeline.js +68 -0
  119. package/src/xdm/types.js +178 -0
  120. package/src/xpath/eval/atomics.js +45 -0
  121. package/src/xpath/eval/axes.js +212 -0
  122. package/src/xpath/eval/calls.js +168 -0
  123. package/src/xpath/eval/coercion.js +146 -0
  124. package/src/xpath/eval/compiler.js +71 -0
  125. package/src/xpath/eval/constructors.js +129 -0
  126. package/src/xpath/eval/decimalFormats.js +93 -0
  127. package/src/xpath/eval/descendantMemo.js +77 -0
  128. package/src/xpath/eval/documentOrder.js +142 -0
  129. package/src/xpath/eval/domNodes.js +174 -0
  130. package/src/xpath/eval/dynamicCall.js +27 -0
  131. package/src/xpath/eval/dynamicContext.js +161 -0
  132. package/src/xpath/eval/flow.js +115 -0
  133. package/src/xpath/eval/functionItems.js +194 -0
  134. package/src/xpath/eval/groupedChildren.js +88 -0
  135. package/src/xpath/eval/inline.js +71 -0
  136. package/src/xpath/eval/namespaceNodes.js +90 -0
  137. package/src/xpath/eval/nodeTests.js +164 -0
  138. package/src/xpath/eval/operators.js +190 -0
  139. package/src/xpath/eval/paths.js +163 -0
  140. package/src/xpath/eval/positional.js +81 -0
  141. package/src/xpath/eval/predicates.js +56 -0
  142. package/src/xpath/eval/primary.js +99 -0
  143. package/src/xpath/eval/resources.js +149 -0
  144. package/src/xpath/eval/scope.js +108 -0
  145. package/src/xpath/eval/sequenceType.js +193 -0
  146. package/src/xpath/eval/staticContext.js +142 -0
  147. package/src/xpath/eval/steps.js +98 -0
  148. package/src/xpath/eval/subtype.js +154 -0
  149. package/src/xpath/eval/typeExprs.js +147 -0
  150. package/src/xpath/eval/typeNames.js +101 -0
  151. package/src/xpath/eval/uris.js +21 -0
  152. package/src/xpath/eval/values.js +64 -0
  153. package/src/xpath/index.js +217 -0
  154. package/src/xpath/syntax/ast.js +130 -0
  155. package/src/xpath/syntax/chars.js +58 -0
  156. package/src/xpath/syntax/constructors.js +144 -0
  157. package/src/xpath/syntax/expressions.js +107 -0
  158. package/src/xpath/syntax/index.js +44 -0
  159. package/src/xpath/syntax/kindTests.js +134 -0
  160. package/src/xpath/syntax/lexer.js +220 -0
  161. package/src/xpath/syntax/operators.js +111 -0
  162. package/src/xpath/syntax/paths.js +102 -0
  163. package/src/xpath/syntax/postfix.js +91 -0
  164. package/src/xpath/syntax/primaries.js +149 -0
  165. package/src/xpath/syntax/steps.js +146 -0
  166. package/src/xpath/syntax/syntaxError.js +31 -0
  167. package/src/xpath/syntax/tokenStream.js +163 -0
  168. package/src/xpath/syntax/typeAst.js +76 -0
  169. package/src/xpath/syntax/typeOperators.js +127 -0
  170. package/src/xpath/syntax/types.js +158 -0
  171. package/src/xslt/api.js +153 -0
  172. package/src/xslt/compiler/acceptance.js +178 -0
  173. package/src/xslt/compiler/accumulatorDecl.js +146 -0
  174. package/src/xslt/compiler/attributeValues.js +174 -0
  175. package/src/xslt/compiler/attributes.js +211 -0
  176. package/src/xslt/compiler/avt.js +103 -0
  177. package/src/xslt/compiler/body.js +154 -0
  178. package/src/xslt/compiler/children.js +105 -0
  179. package/src/xslt/compiler/compatibility.js +130 -0
  180. package/src/xslt/compiler/components.js +179 -0
  181. package/src/xslt/compiler/contextItem.js +95 -0
  182. package/src/xslt/compiler/decimalFormats.js +132 -0
  183. package/src/xslt/compiler/declarationKinds.js +82 -0
  184. package/src/xslt/compiler/elementInfo.js +246 -0
  185. package/src/xslt/compiler/expressions.js +175 -0
  186. package/src/xslt/compiler/globalContextItem.js +92 -0
  187. package/src/xslt/compiler/globals.js +209 -0
  188. package/src/xslt/compiler/linker.js +134 -0
  189. package/src/xslt/compiler/miscDecl.js +171 -0
  190. package/src/xslt/compiler/modeDecl.js +88 -0
  191. package/src/xslt/compiler/moduleLoading.js +111 -0
  192. package/src/xslt/compiler/modules.js +158 -0
  193. package/src/xslt/compiler/outputDecl.js +209 -0
  194. package/src/xslt/compiler/overrideLinks.js +141 -0
  195. package/src/xslt/compiler/packageChecks.js +84 -0
  196. package/src/xslt/compiler/packageRegistry.js +95 -0
  197. package/src/xslt/compiler/packageTable.js +181 -0
  198. package/src/xslt/compiler/packageVersions.js +143 -0
  199. package/src/xslt/compiler/packages.js +138 -0
  200. package/src/xslt/compiler/registry.js +234 -0
  201. package/src/xslt/compiler/staticStage.js +197 -0
  202. package/src/xslt/compiler/staticVariables.js +93 -0
  203. package/src/xslt/compiler/stylesheet.js +205 -0
  204. package/src/xslt/compiler/tailPosition.js +74 -0
  205. package/src/xslt/compiler/templatesDecl.js +180 -0
  206. package/src/xslt/compiler/useAccumulators.js +76 -0
  207. package/src/xslt/compiler/useWhen.js +94 -0
  208. package/src/xslt/compiler/visibility.js +160 -0
  209. package/src/xslt/index.js +11 -0
  210. package/src/xslt/instructions/analyzeString.js +137 -0
  211. package/src/xslt/instructions/assert.js +72 -0
  212. package/src/xslt/instructions/attributeSets.js +115 -0
  213. package/src/xslt/instructions/conditional.js +99 -0
  214. package/src/xslt/instructions/control.js +123 -0
  215. package/src/xslt/instructions/copy.js +151 -0
  216. package/src/xslt/instructions/elements.js +161 -0
  217. package/src/xslt/instructions/evaluate.js +189 -0
  218. package/src/xslt/instructions/fork.js +35 -0
  219. package/src/xslt/instructions/grouping.js +106 -0
  220. package/src/xslt/instructions/index.js +94 -0
  221. package/src/xslt/instructions/iterate.js +182 -0
  222. package/src/xslt/instructions/literal.js +142 -0
  223. package/src/xslt/instructions/maps.js +72 -0
  224. package/src/xslt/instructions/merge.js +190 -0
  225. package/src/xslt/instructions/number.js +205 -0
  226. package/src/xslt/instructions/output.js +102 -0
  227. package/src/xslt/instructions/params.js +110 -0
  228. package/src/xslt/instructions/resultDocument.js +143 -0
  229. package/src/xslt/instructions/sort.js +114 -0
  230. package/src/xslt/instructions/sourceDocument.js +73 -0
  231. package/src/xslt/instructions/templates.js +153 -0
  232. package/src/xslt/instructions/text.js +151 -0
  233. package/src/xslt/instructions/try.js +186 -0
  234. package/src/xslt/instructions/variables.js +119 -0
  235. package/src/xslt/names.js +204 -0
  236. package/src/xslt/patterns/axisOrigins.js +121 -0
  237. package/src/xslt/patterns/compile.js +151 -0
  238. package/src/xslt/patterns/general.js +132 -0
  239. package/src/xslt/patterns/grammar.js +190 -0
  240. package/src/xslt/patterns/modes.js +153 -0
  241. package/src/xslt/patterns/paths.js +130 -0
  242. package/src/xslt/patterns/patternChecks.js +29 -0
  243. package/src/xslt/patterns/steps.js +162 -0
  244. package/src/xslt/processor.js +178 -0
  245. package/src/xslt/runtime/accumulatorFunctions.js +48 -0
  246. package/src/xslt/runtime/accumulators.js +174 -0
  247. package/src/xslt/runtime/apply.js +153 -0
  248. package/src/xslt/runtime/atomicKeys.js +36 -0
  249. package/src/xslt/runtime/availability.js +162 -0
  250. package/src/xslt/runtime/baseUri.js +64 -0
  251. package/src/xslt/runtime/compilerHook.js +27 -0
  252. package/src/xslt/runtime/conditional.js +110 -0
  253. package/src/xslt/runtime/context.js +106 -0
  254. package/src/xslt/runtime/copy.js +123 -0
  255. package/src/xslt/runtime/copyFunctions.js +71 -0
  256. package/src/xslt/runtime/copyOrigins.js +60 -0
  257. package/src/xslt/runtime/defaultMethod.js +42 -0
  258. package/src/xslt/runtime/documentContent.js +93 -0
  259. package/src/xslt/runtime/dynamicXPath.js +90 -0
  260. package/src/xslt/runtime/functionCache.js +65 -0
  261. package/src/xslt/runtime/functionCall.js +57 -0
  262. package/src/xslt/runtime/functions.js +191 -0
  263. package/src/xslt/runtime/groups.js +108 -0
  264. package/src/xslt/runtime/invocation.js +156 -0
  265. package/src/xslt/runtime/iteration.js +107 -0
  266. package/src/xslt/runtime/keys.js +141 -0
  267. package/src/xslt/runtime/lazy.js +97 -0
  268. package/src/xslt/runtime/locations.js +86 -0
  269. package/src/xslt/runtime/machine.js +137 -0
  270. package/src/xslt/runtime/merging.js +167 -0
  271. package/src/xslt/runtime/namespaceScope.js +141 -0
  272. package/src/xslt/runtime/numberFormat.js +89 -0
  273. package/src/xslt/runtime/numbering.js +168 -0
  274. package/src/xslt/runtime/params.js +59 -0
  275. package/src/xslt/runtime/properties.js +103 -0
  276. package/src/xslt/runtime/rawResults.js +82 -0
  277. package/src/xslt/runtime/sequenceReceiver.js +102 -0
  278. package/src/xslt/runtime/snapshot.js +66 -0
  279. package/src/xslt/runtime/sortKeys.js +119 -0
  280. package/src/xslt/runtime/streamAvailable.js +60 -0
  281. package/src/xslt/runtime/strip.js +102 -0
  282. package/src/xslt/runtime/transformFunction.js +169 -0
  283. package/src/xslt/runtime/transformation.js +168 -0
  284. package/src/xslt/runtime/transformationContext.js +85 -0
  285. package/src/xslt/runtime/treeReceiver.js +194 -0
  286. package/src/xslt/runtime/values.js +92 -0
package/LICENSE.md ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, spagu (tradik) and the XSLT-Processor contributors (see CONTRIBUTORS.md)
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -1,3 +1,99 @@
1
- # Temporary Holding Version
1
+ # @tradik/xslt3
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/@tradik/xslt3.svg)](https://www.npmjs.com/package/@tradik/xslt3)
4
+ [![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
5
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D22.8-brightgreen.svg)](https://nodejs.org/)
6
+
7
+ XSLT 3.0 and XPath 3.1 in JavaScript, with no runtime dependencies. It also
8
+ runs XSLT 2.0 stylesheets, and 1.0 ones in backwards-compatible mode, in
9
+ Node.js and in browsers, on any DOM (the browser's, jsdom, @xmldom/xmldom).
10
+
11
+ | W3C test suite | Applicable tests | Passing |
12
+ |---|---:|---:|
13
+ | qt3tests (XPath 3.1, functions and operators) | 21,787 | 99.9% |
14
+ | xslt30-test (XSLT 3.0 and 2.0) | 7,914 | 98.2% |
15
+
16
+ Schema awareness and streaming are not supported (stylesheets that ask for
17
+ streaming run without it).
18
+
19
+ ## Install
20
+
21
+ ```sh
22
+ npm install @tradik/xslt3
23
+ ```
24
+
25
+ ## XSLT 3.0
26
+
27
+ ```js
28
+ import { compileStylesheet, serialize } from "@tradik/xslt3";
29
+
30
+ const parseXml = (text) =>
31
+ new DOMParser().parseFromString(text, "application/xml");
32
+
33
+ const stylesheet = compileStylesheet(
34
+ `<xsl:stylesheet version="3.0" expand-text="yes"
35
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
36
+ <xsl:output method="html" html-version="5"/>
37
+ <xsl:template match="orders">
38
+ <ul>
39
+ <xsl:for-each-group select="order" group-by="@city">
40
+ <xsl:sort select="current-grouping-key()"/>
41
+ <li>{current-grouping-key()}: {sum(current-group()/@total)}</li>
42
+ </xsl:for-each-group>
43
+ </ul>
44
+ </xsl:template>
45
+ </xsl:stylesheet>`,
46
+ { parseXml },
47
+ );
48
+
49
+ const result = stylesheet.transform({ source: parseXml(ordersXml) });
50
+ serialize([result.principal].flat(), result.output);
51
+ // <ul><li>Gdańsk: 80</li><li>Kraków: 162.75</li></ul>
52
+ ```
53
+
54
+ `transform()` returns the principal result, the `xsl:result-document`
55
+ outputs (a `Map` by URI), the `xsl:message` output and the `xsl:output`
56
+ parameters. An `XSLTProcessor` class with the browser's method names is also
57
+ exported.
58
+
59
+ ## XPath 3.1
60
+
61
+ ```js
62
+ import { compileXPath, evaluateXPath } from "@tradik/xslt3";
63
+
64
+ evaluateXPath("sum((1, 2, 3)) * 2", null); // [xs:integer 12]
65
+
66
+ const query = compileXPath("//item[@price > $min]/@id ! string()", {
67
+ variables: ["min"],
68
+ });
69
+ query.evaluate(document, { variables: { min: 10 } });
70
+ ```
71
+
72
+ ## Through the XSLT 1.0 package
73
+
74
+ [@tradik/xslt-processor](https://www.npmjs.com/package/@tradik/xslt-processor),
75
+ the browser `XSLTProcessor` with Chrome's XSLT 1.0 behaviour, hands
76
+ stylesheets that declare version 2.0 or 3.0 to this package when you opt in:
77
+
78
+ ```js
79
+ const processor = new XSLTProcessor({ xsltVersion: "auto" });
80
+ await processor.importStylesheetAsync(stylesheet);
81
+ ```
82
+
83
+ ## Security
84
+
85
+ Expressions and stylesheets read nothing outside the data you pass unless you
86
+ allow it: `doc()` needs a `documentLoader`, `unparsed-text()` and `json-doc()`
87
+ a `textLoader`, `collection()` a `collections` option. `xsl:evaluate` can be
88
+ switched off for untrusted input with `transform({ dynamicEvaluation: false })`.
89
+
90
+ ## Documentation
91
+
92
+ - [Design, options and results](https://github.com/spagu/XSLT-Processor/blob/main/docs/XSLT3.md)
93
+ - [Benchmarks](https://github.com/spagu/XSLT-Processor/blob/main/docs/BENCHMARKS.md)
94
+ - [Playground](https://xslt-processor.tradik.com/playground/?mode=xslt3) (XSLT 3.0 and XPath 3.1 in the browser)
95
+ - [Changelog](https://github.com/spagu/XSLT-Processor/blob/main/CHANGELOG.md)
96
+
97
+ ## License
98
+
99
+ BSD-3-Clause, see [LICENSE.md](LICENSE.md).
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "@tradik/xslt3",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "XSLT 3.0 and XPath 3.1 in JavaScript: also runs XSLT 2.0 stylesheets and 1.0 ones in backwards-compatible mode, with no runtime dependencies",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.js"
8
+ },
9
+ "files": [
10
+ "src",
11
+ "!src/**/*.test.js",
12
+ "README.md",
13
+ "LICENSE.md"
14
+ ],
15
+ "scripts": {
16
+ "test": "node --test --experimental-test-coverage --test-coverage-lines=100 --test-coverage-functions=100 --test-coverage-branches=96 --test-coverage-exclude=\"**/*.test.js\" \"src/**/*.test.js\""
17
+ },
18
+ "engines": {
19
+ "node": ">=22.8.0"
20
+ },
21
+ "license": "BSD-3-Clause",
22
+ "author": "tradik",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/spagu/XSLT-Processor.git",
26
+ "directory": "packages/xslt3"
27
+ },
28
+ "homepage": "https://xslt-processor.tradik.com/",
29
+ "bugs": {
30
+ "url": "https://github.com/spagu/XSLT-Processor/issues"
31
+ },
32
+ "keywords": [
33
+ "xslt",
34
+ "xslt3",
35
+ "xslt2",
36
+ "xpath",
37
+ "xpath3",
38
+ "xml",
39
+ "json",
40
+ "transform"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public"
44
+ }
45
+ }
package/src/errors.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Errors of the XPath, XSLT and F&O specifications, identified by their
3
+ * error code (a local name in the err namespace, e.g. "XPST0003").
4
+ *
5
+ * @module @tradik/xslt3/errors
6
+ */
7
+
8
+ /** An error with a W3C error code. */
9
+ export class XPathError extends Error {
10
+ /**
11
+ * @param {string} code - Error code, e.g. "XPST0003"
12
+ * @param {string} message - Description
13
+ * @param {ErrorOptions} [options] - Standard error options (cause)
14
+ */
15
+ constructor(code, message, options) {
16
+ super(`${code}: ${message}`, options);
17
+ this.name = "XPathError";
18
+ this.code = code;
19
+ }
20
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Aggregate functions (F&O 3.1 section 14.4): avg, max, min and sum.
3
+ * xs:untypedAtomic values are cast to xs:double; numbers are promoted to a
4
+ * common type; durations must all be xs:yearMonthDuration or all
5
+ * xs:dayTimeDuration for sum and avg.
6
+ *
7
+ * @module @tradik/xslt3/functions/aggregates
8
+ */
9
+
10
+ import { XPathError } from "../errors.js";
11
+ import { arithmetic } from "../xdm/arithmetic.js";
12
+ import { cast } from "../xdm/cast.js";
13
+ import { compareAtomic } from "../xdm/compare.js";
14
+ import { derivesFrom, isNumericType, types } from "../xdm/types.js";
15
+ import { collationArg } from "./collations.js";
16
+ import { define, integerItem } from "./support.js";
17
+
18
+ /** @param {string} message @returns {never} */
19
+ const invalid = (message) => {
20
+ throw new XPathError("FORG0006", message);
21
+ };
22
+
23
+ const numericLevels = ["decimal", "float", "double"];
24
+
25
+ /**
26
+ * Casts xs:untypedAtomic to xs:double, numbers to their common type and
27
+ * xs:anyURI to xs:string when mixed with strings.
28
+ * @param {Array<import("../xdm/atomic.js").AtomicValue>} items
29
+ * @returns {Array<import("../xdm/atomic.js").AtomicValue>}
30
+ */
31
+ export function promoteAll(items) {
32
+ const values = items.map((item) =>
33
+ item.type === types.untypedAtomic ? cast(item, types.double) : item,
34
+ );
35
+ const primitives = new Set(values.map((v) => v.type.primitive.localName));
36
+ if (values.every((v) => isNumericType(v.type))) {
37
+ const level = Math.max(
38
+ ...[...primitives].map((p) => numericLevels.indexOf(p)),
39
+ );
40
+ if (level === 0) return values;
41
+ const target = types[numericLevels[level]];
42
+ return values.map((v) => cast(v, target));
43
+ }
44
+ if (primitives.has("anyURI") && primitives.has("string")) {
45
+ // only the URIs are promoted: an xs:token stays an xs:token
46
+ return values.map((v) =>
47
+ v.type.primitive === types.anyURI ? cast(v, types.string) : v,
48
+ );
49
+ }
50
+ return values;
51
+ }
52
+
53
+ /**
54
+ * The kind of total a sum adds up, checking that all items share it.
55
+ * @param {Array<import("../xdm/atomic.js").AtomicValue>} values
56
+ * @throws {XPathError} FORG0006 for mixed or non-additive types
57
+ */
58
+ function checkAdditive(values) {
59
+ const kinds = new Set(
60
+ values.map(({ type }) => {
61
+ if (isNumericType(type)) return "numeric";
62
+ if (derivesFrom(type, types.yearMonthDuration)) return "yearMonth";
63
+ if (derivesFrom(type, types.dayTimeDuration)) return "dayTime";
64
+ return invalid(`Cannot sum or average ${type.prefixedName} values`);
65
+ }),
66
+ );
67
+ if (kinds.size > 1) invalid("Cannot sum or average values of mixed types");
68
+ }
69
+
70
+ /**
71
+ * The sum of a non-empty sequence.
72
+ * @param {Array<import("../xdm/atomic.js").AtomicValue>} items
73
+ * @returns {import("../xdm/atomic.js").AtomicValue}
74
+ */
75
+ function total(items) {
76
+ const values = promoteAll(items);
77
+ checkAdditive(values);
78
+ return values
79
+ .slice(1)
80
+ .reduce((sum, value) => arithmetic(sum, "+", value), values[0]);
81
+ }
82
+
83
+ /**
84
+ * fn:max or fn:min.
85
+ * @param {Array<*>[]} args - [$arg, $collation?]
86
+ * @param {object} context
87
+ * @param {number} direction - 1 for max, -1 for min
88
+ * @returns {Array<*>}
89
+ */
90
+ function extreme([items, collation], context, direction) {
91
+ const options = {
92
+ collation: collationArg(collation, context).compare,
93
+ implicitTimezone: context.implicitTimezone ?? 0,
94
+ };
95
+ if (items.length === 0) return [];
96
+ const values = promoteAll(items);
97
+ const nan = values.find(
98
+ (v) => typeof v.value === "number" && Number.isNaN(v.value),
99
+ );
100
+ let best = values[0];
101
+ for (const value of values) {
102
+ let result;
103
+ try {
104
+ result = compareAtomic(value, best, options);
105
+ } catch {
106
+ invalid("max and min need mutually comparable values");
107
+ }
108
+ if (!result.ordered) invalid(`${value.type.prefixedName} is not ordered`);
109
+ if (result.order * direction > 0) best = value;
110
+ }
111
+ return [nan ?? best];
112
+ }
113
+
114
+ /**
115
+ * fn:sum.
116
+ * @param {Array<*>[]} args - [$arg, $zero?]
117
+ * @returns {Array<*>}
118
+ */
119
+ function sum([items, zero]) {
120
+ if (items.length === 0) return zero ?? [integerItem(0)];
121
+ return [total(items)];
122
+ }
123
+
124
+ const ATOMS = "xs:anyAtomicType*";
125
+ const ATOM_OPT = "xs:anyAtomicType?";
126
+
127
+ /** @type {import("./support.js").FunctionDefinition[]} */
128
+ export const aggregateFunctions = [
129
+ define("avg", [ATOMS], ATOM_OPT, ([items]) =>
130
+ items.length === 0
131
+ ? []
132
+ : [arithmetic(total(items), "div", integerItem(items.length))],
133
+ ),
134
+ define("max", [ATOMS], ATOM_OPT, (args, c) => extreme(args, c, 1)),
135
+ define("max", [ATOMS, "xs:string"], ATOM_OPT, (args, c) =>
136
+ extreme(args, c, 1),
137
+ ),
138
+ define("min", [ATOMS], ATOM_OPT, (args, c) => extreme(args, c, -1)),
139
+ define("min", [ATOMS, "xs:string"], ATOM_OPT, (args, c) =>
140
+ extreme(args, c, -1),
141
+ ),
142
+ define("sum", [ATOMS], "xs:anyAtomicType", sum),
143
+ define("sum", [ATOMS, ATOM_OPT], ATOM_OPT, sum),
144
+ ];
@@ -0,0 +1,33 @@
1
+ /**
2
+ * array:sort (F&O 3.1 section 17.3.19): the members of an array sorted
3
+ * like fn:sort sorts items.
4
+ *
5
+ * @module @tradik/xslt3/functions/arraySort
6
+ */
7
+
8
+ import { XdmArray } from "../items/array.js";
9
+ import { NS } from "./signatures.js";
10
+ import { sortByKeys } from "./sort.js";
11
+
12
+ /**
13
+ * array:sort.
14
+ * @returns {Array}
15
+ */
16
+ const sort = ([[array], collation, key], context) => [
17
+ new XdmArray(
18
+ sortByKeys(array.members, collation, key?.[0], context, (member) => member),
19
+ ),
20
+ ];
21
+
22
+ /** Function definitions. */
23
+ export const arraySortFunctions = [
24
+ ["array(*)"],
25
+ ["array(*)", "xs:string?"],
26
+ ["array(*)", "xs:string?", "function(item()*) as xs:anyAtomicType*"],
27
+ ].map((params) => ({
28
+ namespace: NS.array,
29
+ local: "sort",
30
+ params,
31
+ returns: "array(*)",
32
+ impl: sort,
33
+ }));
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Functions on arrays (F&O 3.1 section 17.3), in the namespace
3
+ * http://www.w3.org/2005/xpath-functions/array.
4
+ *
5
+ * @module @tradik/xslt3/functions/arrays
6
+ */
7
+
8
+ import { XPathError } from "../errors.js";
9
+ import { XdmArray } from "../items/array.js";
10
+ import { isArray } from "../xdm/atomic.js";
11
+ import { integerItem } from "../xpath/eval/atomics.js";
12
+ import { arraySortFunctions } from "./arraySort.js";
13
+ import { NS } from "./signatures.js";
14
+
15
+ /**
16
+ * @param {string} local
17
+ * @param {string[]} params
18
+ * @param {string} returns
19
+ * @param {Function} impl
20
+ * @returns {object} a definition in the array namespace
21
+ */
22
+ const arrayFunction = (local, params, returns, impl) => ({
23
+ namespace: NS.array,
24
+ local,
25
+ params,
26
+ returns,
27
+ impl,
28
+ });
29
+
30
+ /**
31
+ * Checks a position for insertion (1 to size + 1).
32
+ * @param {XdmArray} array
33
+ * @param {*} position - xs:integer
34
+ * @returns {number} 0-based index
35
+ */
36
+ function insertionIndex(array, position) {
37
+ const index = Number(position.value) - 1;
38
+ if (!(index >= 0 && index <= array.size)) {
39
+ throw new XPathError(
40
+ "FOAY0001",
41
+ `Position ${position.value} is out of bounds`,
42
+ );
43
+ }
44
+ return index;
45
+ }
46
+
47
+ /**
48
+ * Flattens arrays in a sequence, recursively.
49
+ * @param {Array} sequence
50
+ * @returns {Array}
51
+ */
52
+ const flatten = (sequence) =>
53
+ sequence.flatMap((item) =>
54
+ isArray(item) ? flatten(item.members.flat()) : [item],
55
+ );
56
+
57
+ /**
58
+ * array:subsequence.
59
+ * @returns {Array}
60
+ */
61
+ function subarray([[array], [start], length]) {
62
+ const from = Number(start.value) - 1;
63
+ if (from < 0 || from > array.size) {
64
+ throw new XPathError("FOAY0001", "Subarray start out of bounds");
65
+ }
66
+ const count = length ? Number(length[0].value) : array.size - from;
67
+ if (count < 0) throw new XPathError("FOAY0002", "Negative length");
68
+ if (from + count > array.size) {
69
+ throw new XPathError("FOAY0001", "Subarray out of bounds");
70
+ }
71
+ return [new XdmArray(array.members.slice(from, from + count))];
72
+ }
73
+
74
+ /** Function definitions. */
75
+ export const arrayFunctions = [
76
+ arrayFunction("size", ["array(*)"], "xs:integer", ([[a]]) => [
77
+ integerItem(a.size),
78
+ ]),
79
+ arrayFunction("get", ["array(*)", "xs:integer"], "item()*", ([[a], [i]]) =>
80
+ a.get(i.value),
81
+ ),
82
+ arrayFunction(
83
+ "put",
84
+ ["array(*)", "xs:integer", "item()*"],
85
+ "array(*)",
86
+ ([[a], [i], v]) => {
87
+ a.get(i.value);
88
+ const members = a.members.slice();
89
+ members[Number(i.value) - 1] = v;
90
+ return [new XdmArray(members)];
91
+ },
92
+ ),
93
+ arrayFunction("append", ["array(*)", "item()*"], "array(*)", ([[a], v]) => [
94
+ new XdmArray([...a.members, v]),
95
+ ]),
96
+ ...[2, 3].map((arity) =>
97
+ arrayFunction(
98
+ "subarray",
99
+ ["array(*)", "xs:integer", "xs:integer"].slice(0, arity),
100
+ "array(*)",
101
+ subarray,
102
+ ),
103
+ ),
104
+ arrayFunction(
105
+ "remove",
106
+ ["array(*)", "xs:integer*"],
107
+ "array(*)",
108
+ ([[a], positions]) => {
109
+ const removed = new Set(
110
+ positions.map((p) => (a.get(p.value), Number(p.value) - 1)),
111
+ );
112
+ return [new XdmArray(a.members.filter((_, i) => !removed.has(i)))];
113
+ },
114
+ ),
115
+ arrayFunction(
116
+ "insert-before",
117
+ ["array(*)", "xs:integer", "item()*"],
118
+ "array(*)",
119
+ ([[a], [p], v]) => {
120
+ const members = a.members.slice();
121
+ members.splice(insertionIndex(a, p), 0, v);
122
+ return [new XdmArray(members)];
123
+ },
124
+ ),
125
+ arrayFunction("head", ["array(*)"], "item()*", ([[a]]) => a.get(1)),
126
+ arrayFunction("tail", ["array(*)"], "array(*)", ([[a]]) => {
127
+ a.get(1);
128
+ return [new XdmArray(a.members.slice(1))];
129
+ }),
130
+ arrayFunction("reverse", ["array(*)"], "array(*)", ([[a]]) => [
131
+ new XdmArray(a.members.slice().reverse()),
132
+ ]),
133
+ arrayFunction("join", ["array(*)*"], "array(*)", ([arrays]) => [
134
+ new XdmArray(arrays.flatMap((a) => a.members)),
135
+ ]),
136
+ arrayFunction("flatten", ["item()*"], "item()*", ([sequence]) =>
137
+ flatten(sequence),
138
+ ),
139
+ arrayFunction(
140
+ "for-each",
141
+ ["array(*)", "function(item()*) as item()*"],
142
+ "array(*)",
143
+ ([[a], [f]]) => [new XdmArray(a.members.map((m) => f.invoke([m])))],
144
+ ),
145
+ arrayFunction(
146
+ "filter",
147
+ ["array(*)", "function(item()*) as xs:boolean"],
148
+ "array(*)",
149
+ ([[a], [f]]) => [
150
+ new XdmArray(a.members.filter((m) => f.invoke([m])[0].value)),
151
+ ],
152
+ ),
153
+ arrayFunction(
154
+ "fold-left",
155
+ ["array(*)", "item()*", "function(item()*, item()*) as item()*"],
156
+ "item()*",
157
+ ([[a], zero, [f]]) =>
158
+ a.members.reduce((acc, m) => f.invoke([acc, m]), zero),
159
+ ),
160
+ arrayFunction(
161
+ "fold-right",
162
+ ["array(*)", "item()*", "function(item()*, item()*) as item()*"],
163
+ "item()*",
164
+ ([[a], zero, [f]]) =>
165
+ a.members.reduceRight((acc, m) => f.invoke([m, acc]), zero),
166
+ ),
167
+ arrayFunction(
168
+ "for-each-pair",
169
+ ["array(*)", "array(*)", "function(item()*, item()*) as item()*"],
170
+ "array(*)",
171
+ ([[a], [b], [f]]) => {
172
+ const length = Math.min(a.size, b.size);
173
+ const members = [];
174
+ for (let i = 0; i < length; i++) {
175
+ members.push(f.invoke([a.members[i], b.members[i]]));
176
+ }
177
+ return [new XdmArray(members)];
178
+ },
179
+ ),
180
+ ...arraySortFunctions,
181
+ ];
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Attribute types declared in the internal DTD subset of a document
3
+ * (`<!ATTLIST>` declarations), which make attributes IDs, IDREFs or
4
+ * IDREFS without a schema (XDM 3.1: the is-id and is-idrefs properties).
5
+ * DOM implementations that expose `doctype.internalSubset` (such as
6
+ * @xmldom/xmldom) are supported; external DTD subsets are not read.
7
+ * `xml:id` attributes are always IDs.
8
+ *
9
+ * @module @tradik/xslt3/functions/attributeTypes
10
+ */
11
+
12
+ import { XML_NAMESPACE } from "../xpath/eval/namespaceNodes.js";
13
+
14
+ const ATTLIST = /<!ATTLIST\s+([^\s>]+)((?:[^>"']|"[^"]*"|'[^']*')*)>/g;
15
+ const ATTRIBUTE_DEF =
16
+ /([^\s()"']+)\s+(\([^)]*\)|NOTATION\s*\([^)]*\)|[A-Z]+)\s+(?:#REQUIRED|#IMPLIED|(?:#FIXED\s+)?(?:"[^"]*"|'[^']*'))/g;
17
+
18
+ /** @type {WeakMap<object, Map<string, string>>} */
19
+ const cache = new WeakMap();
20
+
21
+ /**
22
+ * Declared types of the attributes of a document, by "element attribute".
23
+ * @param {Document} document
24
+ * @returns {Map<string, string>} e.g. "chapter id" to "ID"
25
+ */
26
+ function declaredTypes(document) {
27
+ let types = cache.get(document);
28
+ if (!types) {
29
+ types = new Map();
30
+ const subset = document.doctype?.internalSubset ?? "";
31
+ for (const [, element, body] of subset.matchAll(ATTLIST)) {
32
+ for (const [, attribute, type] of body.matchAll(ATTRIBUTE_DEF)) {
33
+ const key = `${element} ${attribute}`;
34
+ if (!types.has(key)) types.set(key, type);
35
+ }
36
+ }
37
+ cache.set(document, types);
38
+ }
39
+ return types;
40
+ }
41
+
42
+ /**
43
+ * The type of an attribute: "ID" for xml:id, else its DTD declaration.
44
+ * @param {Attr} attribute
45
+ * @param {Element} element - Its parent
46
+ * @returns {string|undefined} "ID", "IDREF", "IDREFS", "CDATA"...
47
+ */
48
+ export function attributeType(attribute, element) {
49
+ if (
50
+ attribute.namespaceURI === XML_NAMESPACE &&
51
+ attribute.localName === "id"
52
+ ) {
53
+ return "ID";
54
+ }
55
+ return declaredTypes(element.ownerDocument).get(
56
+ `${element.nodeName} ${attribute.name}`,
57
+ );
58
+ }