@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
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Document order of nodes (XDM 3.1 section 2.4), for any DOM.
3
+ *
4
+ * The nodes of a tree are numbered once, in one firstChild/nextSibling
5
+ * walk, the first time a node of that tree is compared. Sorting then
6
+ * compares numbers, which is fast whatever the DOM; walking ancestors per
7
+ * comparison (compareDocumentPosition in jsdom or xmldom) is not. Numbers
8
+ * grow across trees, so trees are ordered among themselves by when they
9
+ * were (last) numbered.
10
+ *
11
+ * Attributes and namespace nodes are not numbered: they sort after their
12
+ * element and before its first child (element p, its namespace nodes in
13
+ * (p, p + 0.5), its attributes at p + 0.75). Two attributes of the same
14
+ * element are ordered by their index in its attribute list, looked up only
15
+ * for such a tie: walking every attribute list up front would dominate
16
+ * sorting `//item/@price` (and NamedNodeMap is slow in jsdom).
17
+ *
18
+ * The numbering assumes the trees do not change while an instance is in
19
+ * use. One instance serves one evaluation (so a tree changed between two
20
+ * evaluations is numbered again) unless the caller shares one (dynamic
21
+ * option `documentOrder`) over trees it does not change. A node missing
22
+ * from its tree's numbering (added later) makes the tree be numbered
23
+ * again; a node moved within a numbered tree keeps its stale number.
24
+ *
25
+ * @module @tradik/xslt3/xpath/eval/documentOrder
26
+ */
27
+
28
+ import { NODE_TYPES, parentOf, rootOf } from "./domNodes.js";
29
+ import { namespaceNodesOf } from "./namespaceNodes.js";
30
+
31
+ /** Offset of the attributes of an element from the element's number. */
32
+ const ATTRIBUTE_OFFSET = 0.75;
33
+
34
+ /** Positions of the nodes of trees, built lazily. */
35
+ export class DocumentOrder {
36
+ constructor() {
37
+ /** @type {Map<Node, number>} */
38
+ this.positions = new Map();
39
+ /** @type {Map<Attr, number>} index in the attribute list */
40
+ this.attributeIndexes = new Map();
41
+ this.next = 0;
42
+ }
43
+
44
+ /**
45
+ * Numbers the nodes of the tree containing a node, attributes aside.
46
+ * @param {Node} node
47
+ */
48
+ index(node) {
49
+ const root = rootOf(node);
50
+ const positions = this.positions;
51
+ positions.set(root, this.next++);
52
+ let current = root.firstChild;
53
+ while (current) {
54
+ positions.set(current, this.next++);
55
+ const first = current.firstChild;
56
+ if (first) {
57
+ current = first;
58
+ continue;
59
+ }
60
+ while (!current.nextSibling) {
61
+ current = current.parentNode;
62
+ if (current === root || !current) return;
63
+ }
64
+ current = current.nextSibling;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * @param {Node} node
70
+ * @returns {number} the position of a node: larger for later nodes
71
+ */
72
+ position(node) {
73
+ const type = node.nodeType;
74
+ if (type === NODE_TYPES.namespace) {
75
+ const element = parentOf(node);
76
+ const siblings = namespaceNodesOf(element);
77
+ const rank = siblings.indexOf(node) + 1;
78
+ return this.position(element) + rank / (2 * (siblings.length + 1));
79
+ }
80
+ if (type === NODE_TYPES.attribute && node.ownerElement) {
81
+ return this.position(node.ownerElement) + ATTRIBUTE_OFFSET;
82
+ }
83
+ let position = this.positions.get(node);
84
+ if (position === undefined) {
85
+ this.index(node);
86
+ position = this.positions.get(node);
87
+ }
88
+ return position;
89
+ }
90
+
91
+ /**
92
+ * @param {Attr} attribute - An attribute with an owner element
93
+ * @returns {number} its index in the attribute list of its element
94
+ */
95
+ attributeIndex(attribute) {
96
+ let index = this.attributeIndexes.get(attribute);
97
+ if (index === undefined) {
98
+ const attributes = attribute.ownerElement.attributes;
99
+ for (let i = 0, n = attributes.length; i < n; i++) {
100
+ this.attributeIndexes.set(attributes[i], i);
101
+ }
102
+ index = this.attributeIndexes.get(attribute) ?? 0;
103
+ }
104
+ return index;
105
+ }
106
+
107
+ /**
108
+ * Order of two nodes at the same position: the attributes of one
109
+ * element (or the same node twice).
110
+ * @returns {number}
111
+ */
112
+ tie(a, b) {
113
+ return a === b ? 0 : this.attributeIndex(a) - this.attributeIndex(b);
114
+ }
115
+
116
+ /**
117
+ * @param {Node} a
118
+ * @param {Node} b
119
+ * @returns {number} negative when a comes first, positive when b does,
120
+ * 0 for the same node
121
+ */
122
+ compare(a, b) {
123
+ if (a === b) return 0;
124
+ return this.position(a) - this.position(b) || this.tie(a, b);
125
+ }
126
+
127
+ /**
128
+ * @param {Node[]} nodes
129
+ * @returns {Node[]} the nodes in document order without duplicates (a
130
+ * new array)
131
+ */
132
+ sort(nodes) {
133
+ if (nodes.length < 2) return nodes.slice();
134
+ const keyed = nodes.map((node) => ({ node, key: this.position(node) }));
135
+ keyed.sort((x, y) => x.key - y.key || this.tie(x.node, y.node));
136
+ const result = [keyed[0].node];
137
+ for (let i = 1; i < keyed.length; i++) {
138
+ if (keyed[i].node !== keyed[i - 1].node) result.push(keyed[i].node);
139
+ }
140
+ return result;
141
+ }
142
+ }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * The XDM view of DOM nodes, for any DOM implementation (browsers, jsdom,
3
+ * @xmldom/xmldom): node kinds, names, parents, children and attributes.
4
+ *
5
+ * - Document fragments are document nodes; CDATA sections are text nodes
6
+ * (adjacent text nodes are not merged); document type nodes are skipped.
7
+ * - Namespace declarations (`xmlns` attributes) are namespace nodes, never
8
+ * attributes.
9
+ * - Namespace nodes are the objects of namespaceNodes.js (nodeType 13).
10
+ *
11
+ * @module @tradik/xslt3/xpath/eval/domNodes
12
+ */
13
+
14
+ /** DOM nodeType values. */
15
+ export const NODE_TYPES = Object.freeze({
16
+ element: 1,
17
+ attribute: 2,
18
+ text: 3,
19
+ cdata: 4,
20
+ processingInstruction: 7,
21
+ comment: 8,
22
+ document: 9,
23
+ documentType: 10,
24
+ fragment: 11,
25
+ namespace: 13,
26
+ });
27
+
28
+ /** Namespace of `xmlns` attributes. */
29
+ export const XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
30
+
31
+ /** XDM node kinds by DOM nodeType. */
32
+ const KINDS = {
33
+ 1: "element",
34
+ 2: "attribute",
35
+ 3: "text",
36
+ 4: "text",
37
+ 7: "processing-instruction",
38
+ 8: "comment",
39
+ 9: "document",
40
+ 11: "document",
41
+ 13: "namespace",
42
+ };
43
+
44
+ /** @param {Node} node @returns {string|undefined} the XDM node kind */
45
+ export const nodeKind = (node) => KINDS[node.nodeType];
46
+
47
+ /**
48
+ * @param {Node} attribute
49
+ * @returns {boolean} whether an attribute is a namespace declaration
50
+ */
51
+ export function isNamespaceDeclaration(attribute) {
52
+ if (attribute.namespaceURI === XMLNS_NAMESPACE) return true;
53
+ const name = attribute.name ?? attribute.nodeName;
54
+ return (
55
+ !attribute.namespaceURI && (name === "xmlns" || name.startsWith("xmlns:"))
56
+ );
57
+ }
58
+
59
+ /**
60
+ * @param {Node} node
61
+ * @returns {Node|null} the parent in XDM terms (the element of an
62
+ * attribute or namespace node)
63
+ */
64
+ export function parentOf(node) {
65
+ const type = node.nodeType;
66
+ if (type === 2 || type === 13) return node.ownerElement ?? null;
67
+ return node.parentNode ?? null;
68
+ }
69
+
70
+ /**
71
+ * @param {Node} node
72
+ * @returns {boolean} whether the node is an XDM node (not a document type)
73
+ */
74
+ export const isXdmNode = (node) => KINDS[node.nodeType] !== undefined;
75
+
76
+ /**
77
+ * @param {Node} node
78
+ * @returns {boolean} whether the node can have children (element,
79
+ * document, document fragment)
80
+ */
81
+ export const isContainer = (node) => {
82
+ const type = node.nodeType;
83
+ return type === 1 || type === 9 || type === 11;
84
+ };
85
+
86
+ /**
87
+ * @param {Node} node
88
+ * @returns {Node[]} the children in XDM terms
89
+ */
90
+ export function childrenOf(node) {
91
+ if (!isContainer(node)) return [];
92
+ const result = [];
93
+ // firstChild/nextSibling: childNodes is a live list behind a Proxy in
94
+ // jsdom, several times slower to iterate
95
+ for (let child = node.firstChild; child; child = child.nextSibling) {
96
+ if (isXdmNode(child)) result.push(child);
97
+ }
98
+ return result;
99
+ }
100
+
101
+ /**
102
+ * @param {Node} node
103
+ * @returns {Attr[]} the attributes of an element, without namespace
104
+ * declarations; none for other nodes
105
+ */
106
+ export function attributesOf(node) {
107
+ if (node.nodeType !== 1) return [];
108
+ const attributes = node.attributes;
109
+ const result = [];
110
+ for (let i = 0, n = attributes.length; i < n; i++) {
111
+ const attribute = attributes[i];
112
+ if (!isNamespaceDeclaration(attribute)) result.push(attribute);
113
+ }
114
+ return result;
115
+ }
116
+
117
+ /**
118
+ * The attribute of an element with a name, without walking its attribute
119
+ * list (slow in jsdom).
120
+ * @param {Node} node
121
+ * @param {string} uri - Namespace URI, "" for none
122
+ * @param {string} local - Local name
123
+ * @returns {Attr|null} the attribute, null when the element has none of
124
+ * that name (or the node is not an element); undefined when the DOM
125
+ * cannot look attributes up by name
126
+ */
127
+ export function namedAttributeOf(node, uri, local) {
128
+ if (node.nodeType !== 1) return null;
129
+ if (typeof node.getAttributeNodeNS !== "function") return undefined;
130
+ const attribute = node.getAttributeNodeNS(uri === "" ? null : uri, local);
131
+ return attribute && !isNamespaceDeclaration(attribute) ? attribute : null;
132
+ }
133
+
134
+ /** @param {Node} node @returns {string} namespace URI of the name, "" for none */
135
+ export const nodeNamespace = (node) => node.namespaceURI ?? "";
136
+
137
+ /**
138
+ * @param {Node} node
139
+ * @returns {string} local part of the node name: the target of a
140
+ * processing instruction, the prefix of a namespace node, "" for unnamed
141
+ * nodes
142
+ */
143
+ export function nodeLocalName(node) {
144
+ switch (node.nodeType) {
145
+ case 1:
146
+ case 2:
147
+ return node.localName ?? node.nodeName;
148
+ case 7:
149
+ return node.target ?? node.nodeName;
150
+ case 13:
151
+ return node.localName;
152
+ default:
153
+ return "";
154
+ }
155
+ }
156
+
157
+ /**
158
+ * @param {Node} node
159
+ * @returns {string} prefix of an element or attribute name, "" for none
160
+ */
161
+ export const nodePrefix = (node) =>
162
+ node.nodeType === 1 || node.nodeType === 2 ? (node.prefix ?? "") : "";
163
+
164
+ /**
165
+ * @param {Node} node
166
+ * @returns {Node} the root of the tree containing the node
167
+ */
168
+ export function rootOf(node) {
169
+ let current = node;
170
+ for (let parent = parentOf(current); parent; parent = parentOf(current)) {
171
+ current = parent;
172
+ }
173
+ return current;
174
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The dynamic context of dynamic function calls and inline function
3
+ * bodies, which XSLT 3.0 distinguishes (fn:current-output-uri is absent
4
+ * there).
5
+ *
6
+ * @module @tradik/xslt3/xpath/eval/dynamicCall
7
+ */
8
+
9
+ /** @type {WeakMap<object, object>} */
10
+ const dynamicCallContexts = new WeakMap();
11
+
12
+ /**
13
+ * The dynamic context of a dynamic function call or an inline function
14
+ * body: the caller's, marked `dynamicCall` (XSLT 3.0 clears the current
15
+ * output URI there, see fn:current-output-uri).
16
+ * @param {object} dyn - Dynamic context the function item was made in
17
+ * @returns {object} a context inheriting from it, one per context
18
+ */
19
+ export function dynamicCallContext(dyn) {
20
+ let derived = dynamicCallContexts.get(dyn);
21
+ if (!derived) {
22
+ derived = Object.create(dyn);
23
+ derived.dynamicCall = true;
24
+ dynamicCallContexts.set(dyn, derived);
25
+ }
26
+ return derived;
27
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * The dynamic context of one evaluation (XPath 3.1 section 2.1.2), shared
3
+ * by every evaluator of the expression and passed to function
4
+ * implementations: implicit timezone, current date and time (stable during
5
+ * the evaluation), available documents, document order, collation.
6
+ *
7
+ * @module @tradik/xslt3/xpath/eval/dynamicContext
8
+ */
9
+
10
+ import { XPathError } from "../../errors.js";
11
+ import { DateTimeValue } from "../../xdm/datetime.js";
12
+ import { Decimal } from "../../xdm/decimal.js";
13
+ import { DocumentOrder } from "./documentOrder.js";
14
+ import { deepEqualItem } from "../../functions/mapArrayEqual.js";
15
+ import { compareCodepoints } from "../../xdm/strings.js";
16
+ import { functionItemOf, resolveFunction } from "./functionItems.js";
17
+ import {
18
+ createCollections,
19
+ createTextLoader,
20
+ createXmlParser,
21
+ } from "./resources.js";
22
+ import { resolveUri } from "./uris.js";
23
+
24
+ const XSL_NAMESPACE = "http://www.w3.org/1999/XSL/Transform";
25
+
26
+ /**
27
+ * The date and time of a JS Date in a timezone.
28
+ * @param {Date} date
29
+ * @param {number} timezone - Offset in minutes
30
+ * @returns {DateTimeValue}
31
+ */
32
+ export function dateTimeOf(date, timezone) {
33
+ const shifted = new Date(date.getTime() + timezone * 60000);
34
+ const millis = shifted.getUTCMilliseconds();
35
+ return new DateTimeValue({
36
+ year: shifted.getUTCFullYear(),
37
+ month: shifted.getUTCMonth() + 1,
38
+ day: shifted.getUTCDate(),
39
+ hour: shifted.getUTCHours(),
40
+ minute: shifted.getUTCMinutes(),
41
+ second: Decimal.of(BigInt(shifted.getUTCSeconds() * 1000 + millis), 3),
42
+ timezone,
43
+ });
44
+ }
45
+
46
+ /**
47
+ * A document loader with the stability of fn:doc (the same URI gives the
48
+ * same document during an evaluation). Raises FODC0005 for an invalid URI
49
+ * (a colon not preceded by a valid scheme) and FODC0002 when the loader
50
+ * fails or finds nothing.
51
+ * @param {((uri: string, base?: string) => Node)|undefined} loader - Gets
52
+ * the absolute URI and the base URI it was resolved against
53
+ * @param {string|undefined} baseUri
54
+ * @returns {(uri: string, base?: string) => Node}
55
+ */
56
+ function documentLoader(loader, baseUri) {
57
+ const documents = new Map();
58
+ return (uri, base = baseUri) => {
59
+ const scheme = /^([^/?#]*):/.exec(uri);
60
+ if (scheme && !/^[A-Za-z][A-Za-z0-9+.-]*$/.test(scheme[1])) {
61
+ throw new XPathError("FODC0005", `Invalid URI ${uri}`);
62
+ }
63
+ const absolute = resolveUri(uri, base);
64
+ if (documents.has(absolute)) return documents.get(absolute);
65
+ let document;
66
+ try {
67
+ document = loader?.(absolute, base);
68
+ } catch (error) {
69
+ throw new XPathError("FODC0002", `Cannot load ${absolute}`, {
70
+ cause: error,
71
+ });
72
+ }
73
+ if (!document) {
74
+ throw new XPathError("FODC0002", `No document at ${absolute}`);
75
+ }
76
+ documents.set(absolute, document);
77
+ return document;
78
+ };
79
+ }
80
+
81
+ /**
82
+ * A factory of empty documents for functions that build nodes (such as
83
+ * fn:analyze-string): the DOM implementation of the context node, else of
84
+ * the global document.
85
+ * @param {*} contextNode
86
+ * @returns {() => Document}
87
+ */
88
+ function documentFactory(contextNode) {
89
+ return () => {
90
+ const owner = contextNode?.ownerDocument ?? contextNode;
91
+ const implementation =
92
+ owner?.implementation ?? globalThis.document?.implementation;
93
+ if (!implementation) {
94
+ throw new XPathError(
95
+ "XPDY0130",
96
+ "No DOM implementation to build nodes with: pass createDocument",
97
+ );
98
+ }
99
+ return implementation.createDocument(null, null, null);
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Creates the dynamic context of an evaluation.
105
+ * @param {import("./staticContext.js").StaticContext} sc
106
+ * @param {object} options - Dynamic options (see xpath/index.js)
107
+ * @param {*} [contextNode] - Context item, whose DOM builds new nodes when
108
+ * `options.createDocument` is absent
109
+ * @returns {object} the dynamic context
110
+ */
111
+ export function createDynamicContext(sc, options, contextNode) {
112
+ const implicitTimezone = options.implicitTimezone ?? 0;
113
+ const now = options.currentDateTime;
114
+ const currentDateTime =
115
+ now instanceof DateTimeValue
116
+ ? now
117
+ : dateTimeOf(now ?? new Date(), implicitTimezone);
118
+ const compareOptions = { implicitTimezone };
119
+ return {
120
+ implicitTimezone,
121
+ currentDateTime,
122
+ defaultCollation: sc.defaultCollation,
123
+ staticBaseUri: sc.baseUri,
124
+ defaultLanguage: "en",
125
+ contextItem: undefined,
126
+ position: 0,
127
+ size: 0,
128
+ loadDocument: documentLoader(options.documentLoader, sc.baseUri),
129
+ loadText: createTextLoader(options.textLoader, sc.baseUri),
130
+ parseXml: createXmlParser(options.xmlParser),
131
+ collection: createCollections(options.collections, sc.baseUri),
132
+ trace: options.trace ?? (() => {}),
133
+ decimalFormats: sc.decimalFormats,
134
+ createDocument: options.createDocument ?? documentFactory(contextNode),
135
+ deepEqualItem: function hook(a, b, itemOptions) {
136
+ return deepEqualItem(a, b, {
137
+ ...compareOptions,
138
+ collation: compareCodepoints,
139
+ deepEqualItem: hook,
140
+ ...itemOptions,
141
+ });
142
+ },
143
+ order: options.documentOrder ?? new DocumentOrder(),
144
+ // Set by the owner of an evaluation (see descendantMemo.js)
145
+ descendantMemo: null,
146
+ compareOptions,
147
+ compatibleCompareOptions: { ...compareOptions, backwardsCompatible: true },
148
+ lookupFunction(uri, local, arity, ctx) {
149
+ // xsl:original is only reachable by a static call (XSLT 3.0 3.5.3)
150
+ if (uri === XSL_NAMESPACE) return null;
151
+ try {
152
+ // the static context of the calling expression, when one is set
153
+ const callSc = this.sc ?? sc;
154
+ return functionItemOf(resolveFunction(uri, local, arity, callSc), ctx);
155
+ } catch (error) {
156
+ if (error.code === "XPST0017") return null;
157
+ throw error;
158
+ }
159
+ },
160
+ };
161
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Compilers of the control-flow expressions: for, let, some/every, if and
3
+ * the simple map operator `!` (XPath 3.1 sections 3.12 to 3.14, 3.17).
4
+ *
5
+ * @module @tradik/xslt3/xpath/eval/flow
6
+ */
7
+
8
+ import { effectiveBooleanValue } from "../../xdm/nodes.js";
9
+ import { booleanItem } from "./atomics.js";
10
+ import { bindVariable, withFocus, withVariable } from "./scope.js";
11
+ import { clark, namespaceOf } from "./staticContext.js";
12
+
13
+ /**
14
+ * @param {import("../syntax/ast.js").Binding} binding
15
+ * @param {import("./scope.js").Scope} scope
16
+ * @returns {string} Clark name of the bound variable
17
+ */
18
+ const bindingKey = (binding, scope) =>
19
+ clark(namespaceOf(binding.name, scope.sc, ""), binding.name.local);
20
+
21
+ /**
22
+ * Compiles nested bindings, innermost last: `wrap` builds the evaluator of
23
+ * one binding from its input and the evaluator of what it encloses.
24
+ * @param {import("../syntax/ast.js").Binding[]} bindings
25
+ * @param {import("./scope.js").Scope} scope
26
+ * @param {Function} compile
27
+ * @param {(scope: object) => Function} body - Compiles the innermost part
28
+ * @param {(input: Function, inner: Function) => Function} wrap
29
+ * @returns {import("./scope.js").Evaluator}
30
+ */
31
+ function nest(bindings, scope, compile, body, wrap) {
32
+ const level = (index, current) => {
33
+ if (index === bindings.length) return body(current);
34
+ const binding = bindings[index];
35
+ const input = compile(binding.expr, current);
36
+ const inner = level(
37
+ index + 1,
38
+ bindVariable(current, bindingKey(binding, current)),
39
+ );
40
+ return wrap(input, inner);
41
+ };
42
+ return level(0, scope);
43
+ }
44
+
45
+ /** Compilers by node type. */
46
+ export const flowCompilers = {
47
+ ForExpr(node, scope, compile) {
48
+ return nest(
49
+ node.bindings,
50
+ scope,
51
+ compile,
52
+ (inner) => compile(node.returnExpr, inner),
53
+ (input, inner) => (ctx) => {
54
+ const result = [];
55
+ for (const item of input(ctx)) {
56
+ for (const out of inner(withVariable(ctx, [item]))) result.push(out);
57
+ }
58
+ return result;
59
+ },
60
+ );
61
+ },
62
+
63
+ LetExpr(node, scope, compile) {
64
+ return nest(
65
+ node.bindings,
66
+ scope,
67
+ compile,
68
+ (inner) => compile(node.returnExpr, inner),
69
+ (input, inner) => (ctx) => inner(withVariable(ctx, input(ctx))),
70
+ );
71
+ },
72
+
73
+ QuantifiedExpr(node, scope, compile) {
74
+ const some = node.quantifier === "some";
75
+ const test = nest(
76
+ node.bindings,
77
+ scope,
78
+ compile,
79
+ (inner) => {
80
+ const satisfies = compile(node.satisfies, inner);
81
+ return (ctx) => effectiveBooleanValue(satisfies(ctx));
82
+ },
83
+ (input, inner) => (ctx) => {
84
+ for (const item of input(ctx)) {
85
+ if (inner(withVariable(ctx, [item])) === some) return some;
86
+ }
87
+ return !some;
88
+ },
89
+ );
90
+ return (ctx) => [booleanItem(test(ctx))];
91
+ },
92
+
93
+ IfExpr(node, scope, compile) {
94
+ const condition = compile(node.condition, scope);
95
+ const thenExpr = compile(node.thenExpr, scope);
96
+ const elseExpr = compile(node.elseExpr, scope);
97
+ return (ctx) =>
98
+ effectiveBooleanValue(condition(ctx)) ? thenExpr(ctx) : elseExpr(ctx);
99
+ },
100
+
101
+ SimpleMapExpr(node, scope, compile) {
102
+ const left = compile(node.left, scope);
103
+ const right = compile(node.right, scope);
104
+ return (ctx) => {
105
+ const input = left(ctx);
106
+ const result = [];
107
+ const size = input.length;
108
+ for (let i = 0; i < size; i++) {
109
+ const out = right(withFocus(ctx, input[i], i + 1, size));
110
+ for (const item of out) result.push(item);
111
+ }
112
+ return result;
113
+ };
114
+ },
115
+ };