@getodk/xpath 0.2.0 → 0.3.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 (120) hide show
  1. package/dist/.vite/manifest.json +4 -4
  2. package/dist/adapter/WHAT/WHATNode.d.ts +30 -0
  3. package/dist/adapter/WHAT/kind.d.ts +11 -0
  4. package/dist/adapter/WHAT/names.d.ts +6 -0
  5. package/dist/adapter/WHAT/optimizations.d.ts +37 -0
  6. package/dist/adapter/WHAT/platform.d.ts +6 -0
  7. package/dist/adapter/WHAT/traversal.d.ts +14 -0
  8. package/dist/adapter/WHAT/values.d.ts +2 -0
  9. package/dist/adapter/WHAT/whatDOMAdapter.d.ts +5 -0
  10. package/dist/adapter/defaults.d.ts +26 -0
  11. package/dist/adapter/interface/XPathCustomUnwrappableNode.d.ts +55 -0
  12. package/dist/adapter/interface/XPathDOMAdapter.d.ts +16 -0
  13. package/dist/adapter/interface/XPathDOMOptimizableOperations.d.ts +13 -0
  14. package/dist/adapter/interface/XPathNameAdapter.d.ts +23 -0
  15. package/dist/adapter/interface/XPathNode.d.ts +164 -0
  16. package/dist/adapter/interface/XPathNodeKindAdapter.d.ts +15 -0
  17. package/dist/adapter/interface/XPathTraversalAdapter.d.ts +38 -0
  18. package/dist/adapter/interface/XPathValueAdapter.d.ts +9 -0
  19. package/dist/adapter/xpathDOMProvider.d.ts +80 -0
  20. package/dist/context/Context.d.ts +12 -10
  21. package/dist/context/EvaluationContext.d.ts +18 -29
  22. package/dist/error/IncompatibleRuntimeEnvironmentError.d.ts +2 -0
  23. package/dist/evaluations/BooleanEvaluation.d.ts +4 -4
  24. package/dist/evaluations/DateTimeLikeEvaluation.d.ts +4 -4
  25. package/dist/evaluations/Evaluation.d.ts +12 -12
  26. package/dist/evaluations/EvaluationType.d.ts +5 -3
  27. package/dist/evaluations/LocationPathEvaluation.d.ts +44 -68
  28. package/dist/evaluations/NodeEvaluation.d.ts +6 -6
  29. package/dist/evaluations/NumberEvaluation.d.ts +4 -4
  30. package/dist/evaluations/StringEvaluation.d.ts +4 -4
  31. package/dist/evaluations/ValueEvaluation.d.ts +11 -11
  32. package/dist/evaluator/DefaultEvaluator.d.ts +16 -0
  33. package/dist/evaluator/Evaluator.d.ts +32 -26
  34. package/dist/evaluator/NamespaceResolver.d.ts +16 -10
  35. package/dist/evaluator/expression/AbsoluteLocationPathExpressionEvaluator.d.ts +0 -1
  36. package/dist/evaluator/expression/BinaryExpressionEvaluator.d.ts +2 -2
  37. package/dist/evaluator/expression/BooleanBinaryExpressionEvaluator.d.ts +5 -5
  38. package/dist/evaluator/expression/ExpressionEvaluator.d.ts +2 -2
  39. package/dist/evaluator/expression/FilterPathExpressionEvaluator.d.ts +2 -2
  40. package/dist/evaluator/expression/FunctionCallExpressionEvaluator.d.ts +2 -2
  41. package/dist/evaluator/expression/LocationPathEvaluator.d.ts +2 -2
  42. package/dist/evaluator/expression/LocationPathExpressionEvaluator.d.ts +3 -3
  43. package/dist/evaluator/expression/NumberExpressionEvaluator.d.ts +3 -3
  44. package/dist/evaluator/expression/NumberLiteralExpressionEvaluator.d.ts +0 -1
  45. package/dist/evaluator/expression/NumericBinaryExpressionEvaluator.d.ts +2 -2
  46. package/dist/evaluator/expression/RelativeLocationPathExpressionEvaluator.d.ts +0 -1
  47. package/dist/evaluator/expression/StringExpressionEvaluator.d.ts +3 -3
  48. package/dist/evaluator/expression/StringLiteralExpressionEvaluator.d.ts +0 -1
  49. package/dist/evaluator/expression/UnaryExpressionEvaluator.d.ts +2 -2
  50. package/dist/evaluator/expression/UnionExpressionEvaluator.d.ts +2 -2
  51. package/dist/evaluator/expression/factory.d.ts +0 -1
  52. package/dist/evaluator/functions/BooleanFunction.d.ts +2 -3
  53. package/dist/evaluator/functions/FunctionAlias.d.ts +2 -3
  54. package/dist/evaluator/functions/FunctionImplementation.d.ts +14 -14
  55. package/dist/evaluator/functions/FunctionLibrary.d.ts +6 -6
  56. package/dist/evaluator/functions/FunctionLibraryCollection.d.ts +3 -3
  57. package/dist/evaluator/functions/NodeSetFunction.d.ts +4 -4
  58. package/dist/evaluator/functions/NumberFunction.d.ts +2 -3
  59. package/dist/evaluator/functions/StringFunction.d.ts +2 -3
  60. package/dist/evaluator/functions/TypedFunctionImplementation.d.ts +5 -4
  61. package/dist/evaluator/result/BaseResult.d.ts +13 -9
  62. package/dist/evaluator/result/BooleanResult.d.ts +5 -6
  63. package/dist/evaluator/result/NodeSetResult.d.ts +28 -33
  64. package/dist/evaluator/result/NumberResult.d.ts +5 -6
  65. package/dist/evaluator/result/PrimitiveResult.d.ts +7 -8
  66. package/dist/evaluator/result/StringResult.d.ts +5 -6
  67. package/dist/evaluator/result/XPathEvaluationResult.d.ts +61 -0
  68. package/dist/evaluator/result/toXPathEvaluationResult.d.ts +5 -0
  69. package/dist/evaluator/step/Step.d.ts +1 -2
  70. package/dist/expressionParser-DpqfmhIO.js +3479 -0
  71. package/dist/expressionParser-DpqfmhIO.js.map +1 -0
  72. package/dist/expressionParser.d.ts +0 -1
  73. package/dist/expressionParser.js +1 -1
  74. package/dist/functions/_shared/number.d.ts +5 -5
  75. package/dist/functions/_shared/string.d.ts +2 -2
  76. package/dist/functions/enketo/index.d.ts +0 -1
  77. package/dist/functions/fn/boolean.d.ts +5 -6
  78. package/dist/functions/fn/index.d.ts +0 -1
  79. package/dist/functions/fn/node-set.d.ts +8 -9
  80. package/dist/functions/fn/number.d.ts +5 -6
  81. package/dist/functions/fn/string.d.ts +10 -11
  82. package/dist/functions/javarosa/index.d.ts +0 -1
  83. package/dist/functions/javarosa/string.d.ts +8 -2
  84. package/dist/functions/xforms/boolean.d.ts +4 -5
  85. package/dist/functions/xforms/datetime.d.ts +7 -8
  86. package/dist/functions/xforms/geo.d.ts +2 -3
  87. package/dist/functions/xforms/index.d.ts +0 -1
  88. package/dist/functions/xforms/node-set.d.ts +16 -7
  89. package/dist/functions/xforms/number.d.ts +21 -22
  90. package/dist/functions/xforms/select.d.ts +3 -4
  91. package/dist/functions/xforms/string.d.ts +8 -9
  92. package/dist/index.d.ts +44 -1
  93. package/dist/index.js +1575 -1151
  94. package/dist/index.js.map +1 -1
  95. package/dist/lib/datetime/coercion.d.ts +0 -1
  96. package/dist/lib/datetime/functions.d.ts +0 -1
  97. package/dist/static/grammar/ExpressionParser.d.ts +0 -1
  98. package/dist/static/grammar/SyntaxLanguage.d.ts +0 -1
  99. package/dist/static/grammar/SyntaxNode.d.ts +0 -1
  100. package/dist/static/grammar/SyntaxTree.d.ts +0 -1
  101. package/dist/static/grammar/TreeSitterXPathParser.d.ts +0 -1
  102. package/dist/static/grammar/type-names.d.ts +0 -1
  103. package/dist/xforms/XFormsElementRepresentation.d.ts +15 -0
  104. package/dist/xforms/XFormsItextTranslations.d.ts +75 -24
  105. package/dist/xforms/XFormsSecondaryInstances.d.ts +4 -0
  106. package/dist/xforms/XFormsXPathEvaluator.d.ts +35 -15
  107. package/package.json +14 -12
  108. package/dist/evaluator/result/ResultType.d.ts +0 -13
  109. package/dist/evaluator/result/index.d.ts +0 -8
  110. package/dist/expressionParser-BRkDdCO9.js +0 -2127
  111. package/dist/expressionParser-BRkDdCO9.js.map +0 -1
  112. package/dist/lib/dom/assertions.d.ts +0 -5
  113. package/dist/lib/dom/predicates.d.ts +0 -12
  114. package/dist/lib/dom/sort.d.ts +0 -1
  115. package/dist/lib/dom/traversal.d.ts +0 -22
  116. package/dist/lib/dom/types.d.ts +0 -21
  117. package/dist/lib/dom/xml.d.ts +0 -1
  118. package/dist/shared/constants.d.ts +0 -13
  119. package/dist/shared/index.d.ts +0 -2
  120. package/dist/shared/interface.d.ts +0 -33
@@ -1,6 +1,6 @@
1
1
  {
2
- "_expressionParser-BRkDdCO9.js": {
3
- "file": "expressionParser-BRkDdCO9.js",
2
+ "_expressionParser-DpqfmhIO.js": {
3
+ "file": "expressionParser-DpqfmhIO.js",
4
4
  "name": "expressionParser"
5
5
  },
6
6
  "src/expressionParser.ts": {
@@ -9,7 +9,7 @@
9
9
  "src": "src/expressionParser.ts",
10
10
  "isEntry": true,
11
11
  "imports": [
12
- "_expressionParser-BRkDdCO9.js"
12
+ "_expressionParser-DpqfmhIO.js"
13
13
  ]
14
14
  },
15
15
  "src/index.ts": {
@@ -18,7 +18,7 @@
18
18
  "src": "src/index.ts",
19
19
  "isEntry": true,
20
20
  "imports": [
21
- "_expressionParser-BRkDdCO9.js"
21
+ "_expressionParser-DpqfmhIO.js"
22
22
  ]
23
23
  }
24
24
  }
@@ -0,0 +1,30 @@
1
+ import { XPathCustomUnwrappableNode } from '../interface/XPathCustomUnwrappableNode.ts';
2
+ import { XPathNodeKindKey } from '../interface/XPathNode.ts';
3
+ export interface WHATDocument extends Document, XPathCustomUnwrappableNode<Document> {
4
+ readonly [XPathNodeKindKey]: 'document';
5
+ }
6
+ export interface WHATElement extends Element, XPathCustomUnwrappableNode<Element> {
7
+ readonly [XPathNodeKindKey]: 'element';
8
+ }
9
+ export interface WHATNamespaceDeclaration extends Attr, XPathCustomUnwrappableNode<Attr> {
10
+ readonly [XPathNodeKindKey]: 'namespace_declaration';
11
+ }
12
+ export interface WHATAttribute extends Attr, XPathCustomUnwrappableNode<Attr> {
13
+ readonly [XPathNodeKindKey]: 'attribute';
14
+ }
15
+ export interface WHATText extends CDATASection, Text, XPathCustomUnwrappableNode<CDATASection | Text> {
16
+ readonly [XPathNodeKindKey]: 'text';
17
+ readonly nodeType: Node['CDATA_SECTION_NODE'] | Node['TEXT_NODE'];
18
+ }
19
+ export interface WHATComment extends Comment, XPathCustomUnwrappableNode<Comment> {
20
+ readonly [XPathNodeKindKey]: 'comment';
21
+ }
22
+ export interface WHATProcessingInstruction extends ProcessingInstruction {
23
+ readonly [XPathNodeKindKey]: 'processing_instruction';
24
+ }
25
+ type UnwrappableWHATNodeUnion<T, U> = T & XPathCustomUnwrappableNode<U>;
26
+ export type WHATNode = UnwrappableWHATNodeUnion<(WHATDocument | WHATElement | WHATNamespaceDeclaration | WHATAttribute | WHATText | WHATComment | WHATProcessingInstruction), Node>;
27
+ export type WHATParentNode = UnwrappableWHATNodeUnion<WHATDocument | WHATElement, ParentNode>;
28
+ export type WHATChildNode = UnwrappableWHATNodeUnion<(WHATElement | WHATText | WHATComment | WHATProcessingInstruction), ChildNode>;
29
+ export type WHATNamedNode = UnwrappableWHATNodeUnion<WHATElement | WHATAttribute, Element | Attr>;
30
+ export {};
@@ -0,0 +1,11 @@
1
+ import { UnspecifiedNonXPathNodeKind, XPathNodeKind } from '../interface/XPathNode.ts';
2
+ import { WHATAttribute, WHATDocument, WHATElement, WHATNamespaceDeclaration, WHATNode, WHATParentNode } from './WHATNode.ts';
3
+ type WHATNodeKind = XPathNodeKind | UnspecifiedNonXPathNodeKind;
4
+ export declare const getWHATNodeKind: (node: WHATNode) => WHATNodeKind;
5
+ export declare const isWHATNode: (value: unknown) => value is WHATNode;
6
+ export declare const isWHATDocument: (node: WHATNode) => node is WHATDocument;
7
+ export declare const isWHATElement: (node: WHATNode) => node is WHATElement;
8
+ export declare const isWHATNamespaceDeclaration: (node: Attr | WHATNode) => node is WHATNamespaceDeclaration;
9
+ export declare const isWHATAttribute: (node: Attr | WHATNode) => node is WHATAttribute;
10
+ export declare const isWHATParentNode: (node: WHATNode) => node is WHATParentNode;
11
+ export {};
@@ -0,0 +1,6 @@
1
+ import { WHATNamedNode, WHATNode, WHATProcessingInstruction } from './WHATNode.ts';
2
+ export declare const getWHATNamespaceURI: (node: WHATNamedNode) => string | null;
3
+ export declare const getWHATQualifiedName: (node: WHATNamedNode) => string;
4
+ export declare const getWHATLocalName: (node: WHATNamedNode) => string;
5
+ export declare const getWHATProcessingInstructionName: (node: WHATProcessingInstruction) => string;
6
+ export declare const resolveWHATNamespaceURI: (node: WHATNode, prefix: string | null) => string | null;
@@ -0,0 +1,37 @@
1
+ import { WHATChildNode, WHATElement, WHATNode, WHATParentNode } from './WHATNode.ts';
2
+ /**
3
+ * @todo optimization, but belongs in values.ts!
4
+ */
5
+ export declare const getQualifiedNamedWHATAttributeValue: (node: WHATElement, namespaceURI: string | null, localName: string) => string | null;
6
+ /**
7
+ * @todo optimization, but belongs in values.ts!
8
+ */
9
+ export declare const getLocalNamedWHATAttributeValue: (node: WHATElement, localName: string) => string | null;
10
+ /**
11
+ * @todo optimization, but belongs in traversal.ts!
12
+ */
13
+ export declare const hasLocalNamedWHATAttribute: (node: WHATElement, localName: string) => boolean;
14
+ /**
15
+ * @todo optimization, but belongs in traversal.ts!
16
+ */
17
+ export declare const getWHATElementByUniqueId: (node: WHATParentNode, id: string) => WHATElement | null;
18
+ /**
19
+ * @todo optimization, but belongs in traversal.ts!
20
+ */
21
+ export declare const getWHATChildrenByLocalName: (node: WHATParentNode, localName: string) => readonly WHATElement[];
22
+ /**
23
+ * @todo optimization, but belongs in traversal.ts!
24
+ */
25
+ export declare const getFirstWHATChildNode: (node: WHATNode) => WHATChildNode | null;
26
+ /**
27
+ * @todo optimization, but belongs in traversal.ts!
28
+ */
29
+ export declare const getLastWHATChildNode: (node: WHATNode) => WHATChildNode | null;
30
+ /**
31
+ * @todo optimization, but belongs in traversal.ts!
32
+ */
33
+ export declare const getFirstChildWHATElement: (node: WHATNode) => WHATElement | null;
34
+ /**
35
+ * @todo optimization, but belongs in traversal.ts!
36
+ */
37
+ export declare const getLastChildWHATElement: (node: WHATNode) => WHATElement | null;
@@ -0,0 +1,6 @@
1
+ interface NodeConstructor {
2
+ readonly prototype: Node;
3
+ new (): Node;
4
+ }
5
+ export declare const getNodeConstructor: () => NodeConstructor;
6
+ export {};
@@ -0,0 +1,14 @@
1
+ import { DocumentOrderComparison } from '../interface/XPathTraversalAdapter.ts';
2
+ import { WHATAttribute, WHATChildNode, WHATDocument, WHATElement, WHATNamespaceDeclaration, WHATNode, WHATParentNode } from './WHATNode.ts';
3
+ export declare const getContainingWHATDocument: (node: WHATNode) => WHATDocument;
4
+ export declare const getWHATNamespaceDeclarations: (node: WHATNode) => readonly WHATNamespaceDeclaration[];
5
+ export declare const getWHATAttributes: (node: WHATNode) => readonly WHATAttribute[];
6
+ export declare const getWHATParentNode: (node: WHATNode) => WHATParentNode | null;
7
+ export declare const getWHATChildNodes: (node: WHATNode) => readonly WHATChildNode[];
8
+ export declare const getChildWHATElements: (node: WHATNode) => readonly WHATElement[];
9
+ export declare const getPreviousSiblingWHATNode: (node: WHATNode) => WHATChildNode | null;
10
+ export declare const getPreviousSiblingWHATElement: (node: WHATNode) => WHATElement | null;
11
+ export declare const getNextSiblingWHATNode: (node: WHATNode) => WHATChildNode | null;
12
+ export declare const getNextSiblingWHATElement: (node: WHATNode) => WHATElement | null;
13
+ export declare const isDescendantWHATNode: (ancestor: WHATNode, other: WHATNode) => boolean;
14
+ export declare const compareWHATDocumentOrder: (a: WHATNode, b: WHATNode) => DocumentOrderComparison;
@@ -0,0 +1,2 @@
1
+ import { WHATNode } from './WHATNode.ts';
2
+ export declare const getWHATNodeValue: (node: WHATNode) => string;
@@ -0,0 +1,5 @@
1
+ import { XPathDOMAdapter } from '../interface/XPathDOMAdapter.ts';
2
+ import { WHATNode } from './WHATNode.ts';
3
+ export interface WHATDOMAdapter extends XPathDOMAdapter<WHATNode> {
4
+ }
5
+ export declare const whatDOMAdapter: WHATDOMAdapter;
@@ -0,0 +1,26 @@
1
+ import { WHATAttribute, WHATChildNode, WHATComment, WHATDocument, WHATElement, WHATNamespaceDeclaration, WHATNode, WHATParentNode, WHATProcessingInstruction, WHATText } from './WHAT/WHATNode.ts';
2
+ import { WHATDOMAdapter } from './WHAT/whatDOMAdapter.ts';
3
+ import { XPathDOMProvider } from './xpathDOMProvider.ts';
4
+ /**
5
+ * Truly silly type hack so that TypeScript doesn't unwrap the names of Default*
6
+ * types to their WHAT* equivalent. This is for clarity at usage and reference
7
+ * sites, internally but especially at the package boundary.
8
+ */
9
+ type AsDefault<T> = T | (T & {
10
+ readonly _?: never;
11
+ });
12
+ type DefaultDOMAdapter = AsDefault<WHATDOMAdapter>;
13
+ type DefaultDOMProvider = AsDefault<XPathDOMProvider<WHATNode>>;
14
+ export declare const DEFAULT_DOM_ADAPTER: DefaultDOMAdapter;
15
+ export declare const DEFAULT_DOM_PROVIDER: DefaultDOMProvider;
16
+ export type DefaultDOMAdapterDocument = AsDefault<WHATDocument>;
17
+ export type DefaultDOMAdapterElement = AsDefault<WHATElement>;
18
+ export type DefaultDOMAdapterNamespaceDeclaration = AsDefault<WHATNamespaceDeclaration>;
19
+ export type DefaultDOMAdapterAttr = AsDefault<WHATAttribute>;
20
+ export type DefaultDOMAdapterText = AsDefault<WHATText>;
21
+ export type DefaultDOMAdapterComment = AsDefault<WHATComment>;
22
+ export type DefaultDOMAdapterProcessingInstruction = AsDefault<WHATProcessingInstruction>;
23
+ export type DefaultDOMAdapterNode = AsDefault<WHATNode>;
24
+ export type DefaultDOMAdapterParentNode = AsDefault<WHATParentNode>;
25
+ export type DefaultDOMAdapterChildNode = AsDefault<WHATChildNode>;
26
+ export {};
@@ -0,0 +1,55 @@
1
+ import { XPathNode, XPathNodeKindKey } from './XPathNode.ts';
2
+ /**
3
+ * @see {@link XPathCustomUnwrappableNode}
4
+ */
5
+ declare const UnwrappedAdapterNode: unique symbol;
6
+ type UnwrappedAdapterNode = typeof UnwrappedAdapterNode;
7
+ /**
8
+ * **!!! HERE BE DRAGONS !!!**
9
+ *
10
+ * Provides type-level support for {@link XPathDOMAdapter} implementations which
11
+ * operate on representations of {@link XPathNode} outside their control
12
+ * (typically as provided by either the runtime platform, or by a third party
13
+ * package). It may not be feasible to assign an {@link XPathNodeKindKey}
14
+ * property **at runtime**. In turn, this creates a conflict with the
15
+ * {@link XPathDOMAdapter} and {@link XPathNode} **types**.
16
+ *
17
+ * This conflict can be addressed **within** an adapter implementation by
18
+ * applying type assertions. Example:
19
+ *
20
+ * ```ts
21
+ * interface AdapterDocument extends PlatformDocument, XPathDocument {}
22
+ *
23
+ * declare const platformDocument: PlatformDocument;
24
+ * const adapterDocument: AdapterDocument = platformDocument satisfies PlatformDocument as AdapterDocument;
25
+ * ```
26
+ *
27
+ * Unfortunately, this **also** creates a type-level conflict for consumers of
28
+ * the adapter, when interacting with other `@getodk/xpath` APIs, such as
29
+ * {@link Evaluator} or any of its subclasses. There, it would be less than
30
+ * ideal to require users to perform the same sort of type assertions: the types
31
+ * are already correct _for their usage_ without such special casting logic.
32
+ *
33
+ * The {@link XPathNodeKindKey} property is a type-level implementation detail
34
+ * **between {@link XPathDOMAdapter} and {@link Evaluator}**, which should not
35
+ * concern end uers.
36
+ *
37
+ * To address this, adapter implementations may also extend their
38
+ * platform-/third party-provided node types with this custom unwrappable type,
39
+ * supplying the original base type which is expected from end users. Example:
40
+ *
41
+ * ```ts
42
+ * interface AdapterDocument extends PlatformDocument,
43
+ * XPathDocument,
44
+ * XPathCustomUnwrappableNode<PlatformDocument> {}
45
+ * ```
46
+ *
47
+ * This directs the end user-facing interfaces to accept the unwrapped type
48
+ * (`PlatformDocument` in the example above) anywhere the adapter's
49
+ * {@link XPathNode} representation would otherwise be accepted.
50
+ */
51
+ export interface XPathCustomUnwrappableNode<T> {
52
+ readonly [UnwrappedAdapterNode]: T;
53
+ }
54
+ export type UnwrapAdapterNode<T extends XPathNode> = T extends XPathCustomUnwrappableNode<infer U> ? U : Omit<T, XPathNodeKindKey>;
55
+ export {};
@@ -0,0 +1,16 @@
1
+ import { XPathDOMOptimizableOperations } from './XPathDOMOptimizableOperations.ts';
2
+ import { XPathNameAdapter } from './XPathNameAdapter.ts';
3
+ import { XPathNode } from './XPathNode.ts';
4
+ import { XPathNodeKindAdapter } from './XPathNodeKindAdapter.ts';
5
+ import { XPathTraversalAdapter } from './XPathTraversalAdapter.ts';
6
+ import { XPathValueAdapter } from './XPathValueAdapter.ts';
7
+ export interface XPathDOMAdapter<T extends XPathNode> extends XPathNodeKindAdapter<T>, XPathNameAdapter<T>, XPathValueAdapter<T>, XPathTraversalAdapter<T>, Partial<XPathDOMOptimizableOperations<T>> {
8
+ }
9
+ export type * from './XPathNode.ts';
10
+ export { XPathNodeKindKey } from './XPathNode.ts';
11
+ export type { XPathCustomUnwrappableNode } from './XPathCustomUnwrappableNode.ts';
12
+ export type * from './XPathDOMOptimizableOperations.ts';
13
+ export type * from './XPathNameAdapter.ts';
14
+ export type * from './XPathNodeKindAdapter.ts';
15
+ export type * from './XPathTraversalAdapter.ts';
16
+ export type * from './XPathValueAdapter.ts';
@@ -0,0 +1,13 @@
1
+ import { XPathNode } from './XPathNode.ts';
2
+ import { AdapterChildNode, AdapterDocument, AdapterElement, AdapterParentNode } from './XPathNodeKindAdapter.ts';
3
+ export interface XPathDOMOptimizableOperations<T extends XPathNode> {
4
+ readonly getElementByUniqueId: (node: AdapterDocument<T>, id: string) => AdapterElement<T> | null;
5
+ readonly getQualifiedNamedAttributeValue: (node: AdapterElement<T>, namespaceURI: string | null, localName: string) => string | null;
6
+ readonly getLocalNamedAttributeValue: (node: AdapterElement<T>, localName: string) => string | null;
7
+ readonly hasLocalNamedAttribute: (node: AdapterElement<T>, localName: string) => boolean;
8
+ readonly getChildrenByLocalName: (node: AdapterParentNode<T>, localName: string) => Iterable<AdapterElement<T>>;
9
+ readonly getFirstChildNode: (node: T) => AdapterChildNode<T> | null;
10
+ readonly getFirstChildElement: (node: T) => AdapterElement<T> | null;
11
+ readonly getLastChildNode: (node: T) => AdapterChildNode<T> | null;
12
+ readonly getLastChildElement: (node: T) => AdapterElement<T> | null;
13
+ }
@@ -0,0 +1,23 @@
1
+ import { XPathNode } from './XPathNode.ts';
2
+ import { AdapterProcessingInstruction, AdapterQualifiedNamedNode } from './XPathNodeKindAdapter.ts';
3
+ export interface XPathNameAdapter<T extends XPathNode> {
4
+ readonly getNamespaceURI: (node: AdapterQualifiedNamedNode<T>) => string | null;
5
+ /**
6
+ * @todo Confirm usage in the following function implementations:
7
+ *
8
+ * - `name` (fn namespace): Should we return the document-defined prefix, or
9
+ * resolve it from the node's namespace URI? The WHAT Working Group
10
+ * (default) adapter will do the former; downstream usage currently punts on
11
+ * this!
12
+ * - `position` (xf namespace): extends XPath 1.0 spec semantics to support
13
+ * lookup of position **among contiguous same-named nodes**. An answer to
14
+ * the above question on `fn:name` may or may not impact whether usage of
15
+ * this API for that purpose is appropriate.
16
+ *
17
+ * If either warrant correction, we'll want to revisit this type as well.
18
+ */
19
+ readonly getQualifiedName: (node: AdapterQualifiedNamedNode<T>) => string;
20
+ readonly getLocalName: (node: AdapterQualifiedNamedNode<T>) => string;
21
+ readonly getProcessingInstructionName: (node: AdapterProcessingInstruction<T>) => string;
22
+ readonly resolveNamespaceURI: (node: T, prefix: string | null) => string | null;
23
+ }
@@ -0,0 +1,164 @@
1
+ export type XPathDocumentKind = 'document';
2
+ export type XPathElementKind = 'element';
3
+ export type XPathNamespaceDeclarationKind = 'namespace_declaration';
4
+ export type XPathAttributeKind = 'attribute';
5
+ export type XPathTextKind = 'text';
6
+ export type XPathCommentKind = 'comment';
7
+ export type XPathProcessingInstructionKind = 'processing_instruction';
8
+ export type XPathNodeKind = XPathDocumentKind | XPathElementKind | XPathNamespaceDeclarationKind | XPathAttributeKind | XPathTextKind | XPathCommentKind | XPathProcessingInstructionKind;
9
+ export declare const XPathNodeKindKey: unique symbol;
10
+ export type XPathNodeKindKey = typeof XPathNodeKindKey;
11
+ /**
12
+ * Any representation of a "document" (or "root") node, having semantics
13
+ * consistent with the
14
+ * {@link https://www.w3.org/TR/1999/REC-xpath-19991116/#data-model | XPath 1.0 data model},
15
+ * and as implemented/provided by an {@link XPathDOMAdapter}.
16
+ *
17
+ * @todo Since we're intentionally using XPath semantics throughout, should we
18
+ * consider renaming this `XPathRoot`? If we're going to use "root node" to
19
+ * refer to this concept, we'll want to (a) be sure we make the change
20
+ * thoroughly throughout `@getodk/xpath` and (b) think of how we make
21
+ * corresponding name changes downstream.
22
+ */
23
+ export interface XPathDocument {
24
+ readonly [XPathNodeKindKey]: XPathDocumentKind;
25
+ }
26
+ /**
27
+ * Any representation of an "element" node, having semantics consistent with the
28
+ * {@link https://www.w3.org/TR/1999/REC-xpath-19991116/#data-model | XPath 1.0 data model},
29
+ * and as implemented/provided by an {@link XPathDOMAdapter}.
30
+ */
31
+ export interface XPathElement {
32
+ readonly [XPathNodeKindKey]: XPathElementKind;
33
+ }
34
+ /**
35
+ * Any representation of a "namespace declaration" node, having semantics
36
+ * consistent with the
37
+ * {@link https://www.w3.org/TR/1999/REC-xpath-19991116/#data-model | XPath 1.0 data model},
38
+ * and as implemented/provided by an {@link XPathDOMAdapter}.
39
+ *
40
+ * Note: in XML syntax, a namespace declaration is defined as a qualified
41
+ * name/URI value pair, defined on an {@link XPathElement | element}, where:
42
+ *
43
+ * 1. The name is in the {@link XMLNS_NAMESPACE_URI | XMLNS namespace}, defined
44
+ * in XML as one of:
45
+ *
46
+ * - `xmlns` (representing the default namespace for that element and for
47
+ * descendants which do not re-declare a default namespace)
48
+ *
49
+ * - a colon-separated name with an `xmlns` prefix and an arbitrary local
50
+ * name suffix (representing a prefix associated with the namespace
51
+ * declaration, for that element and for descendants which do not
52
+ * re-declare a namespace for that prefix)
53
+ *
54
+ * 2. The value is a URI representing the namespace being declared.
55
+ *
56
+ * This syntax is textually similar to that of
57
+ * {@link XPathAttribute | attributes}, but such nodes are **NOT** considered
58
+ * attributes in XPath semantics. The factor distinguishing namespace
59
+ * declarations from attributes is the node's name and/or prefix.
60
+ */
61
+ export interface XPathNamespaceDeclaration {
62
+ readonly [XPathNodeKindKey]: XPathNamespaceDeclarationKind;
63
+ }
64
+ /**
65
+ * Any representation of an "attribute" node, having semantics consistent with
66
+ * the
67
+ * {@link https://www.w3.org/TR/1999/REC-xpath-19991116/#data-model | XPath 1.0 data model},
68
+ * and as implemented/provided by an {@link XPathDOMAdapter}.
69
+ *
70
+ * Note: XPath semantics distinguish attributes from namespace declarations,
71
+ * despite their XML syntax similarity.
72
+ *
73
+ * @see {@link XPathNamespaceDeclaration} for additional clarification of this
74
+ * distinction.
75
+ */
76
+ export interface XPathAttribute {
77
+ readonly [XPathNodeKindKey]: XPathAttributeKind;
78
+ }
79
+ /**
80
+ * Any representation of a "text" node, having semantics
81
+ * consistent with the
82
+ * {@link https://www.w3.org/TR/1999/REC-xpath-19991116/#data-model | XPath 1.0 data model},
83
+ * and as implemented/provided by an {@link XPathDOMAdapter}.
84
+ */
85
+ export interface XPathText {
86
+ readonly [XPathNodeKindKey]: XPathTextKind;
87
+ }
88
+ /**
89
+ * Any representation of a "comment" node, having semantics
90
+ * consistent with the
91
+ * {@link https://www.w3.org/TR/1999/REC-xpath-19991116/#data-model | XPath 1.0 data model},
92
+ * and as implemented/provided by an {@link XPathDOMAdapter}.
93
+ */
94
+ export interface XPathComment {
95
+ readonly [XPathNodeKindKey]: XPathCommentKind;
96
+ }
97
+ /**
98
+ * Any representation of a "processing instruction" node, having semantics
99
+ * consistent with the
100
+ * {@link https://www.w3.org/TR/1999/REC-xpath-19991116/#data-model | XPath 1.0 data model},
101
+ * and as implemented/provided by an {@link XPathDOMAdapter}.
102
+ */
103
+ export interface XPathProcessingInstruction {
104
+ readonly [XPathNodeKindKey]: XPathProcessingInstructionKind;
105
+ }
106
+ /**
107
+ * An {@link XPathNode} is a (mostly) opaque type, used generically throughout
108
+ * `@getodk/xpath` as any {@link XPathDOMAdapter}'s arbitrary node
109
+ * implementation/representation.
110
+ *
111
+ * The `@getodk/xpath` package **DOES NOT** directly access any details of an
112
+ * {@link XPathNode}, except through access points provided by a
113
+ * {@link XPathDOMAdapter}.
114
+ *
115
+ * While each {@link XPathNode} _type_ specifies a property associated with
116
+ * {@link XPathNodeKindKey}, `@getodk/xpath` does not even access this property
117
+ * directly (again, unless that access is implemented by an
118
+ * {@link XPathDOMAdapter}).
119
+ *
120
+ * The key **MAY** be used by an {@link XPathDOMAdapter} in its node
121
+ * representations, distinguishing their XPath semantic kind as defined in the
122
+ * types herein; this is **RECOMMENDED**, where possible for a particular
123
+ * adapter.
124
+ *
125
+ * But it is not strictly necessary for an {@link XPathDOMAdapter}
126
+ * implementation to use the {@link XPathNodeKindKey} for this purpose. The
127
+ * intent of including the key in these opaque node type definitions is to
128
+ * provide a clear type-level representation of "any node" (or "any [kind of]
129
+ * node" for each of its constituent union members), without expressing any
130
+ * further assumptions about those nodes' structures or capabilities.
131
+ *
132
+ * This allows `@getodk/xpath` itself to operate on nodes _in terms of their
133
+ * XPath semantics_, while otherwise deferring the implementation _of those
134
+ * semantics_ to {@link XPathDOMAdapter} implementations.
135
+ */
136
+ export type XPathNode = XPathDocument | XPathElement | XPathNamespaceDeclaration | XPathAttribute | XPathText | XPathComment | XPathProcessingInstruction;
137
+ /**
138
+ * **HERE BE DRAGONS!**
139
+ *
140
+ * In practice, real world XPathEvaluator implementations may produce a
141
+ * {@link https://www.w3.org/TR/xml/#dtd | Document Type Declaration (DTD)} node
142
+ * for certain expressions. (Most trivially: common browser implementations will
143
+ * return a DTD, if present in the context document, for the expression
144
+ * `/node()`!) Importantly, this behavior is **NOT SUPPORTED** by the XPath 1.0
145
+ * specification.
146
+ *
147
+ * We provide this type as an accommodation for {@link XPathDOMAdapter}
148
+ * implementations which opt to support this common-but-unspecified behavior. It
149
+ * is intentionally distinct from the other members of {@link XPathNodeKind}, in
150
+ * order to convey that:
151
+ *
152
+ * 1. If an adapter implementation produces a node associated with this kind, it
153
+ * will not be regarded as any of the node kinds explicitly supported by
154
+ * XPath.
155
+ *
156
+ * 2. Following from that: nodes of this kind cannot be accessed by any
157
+ * (implicit or explicit) XPath syntax which should produce a node type more
158
+ * specific than the `node()`
159
+ * {@link https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-NodeTest | NodeTest}
160
+ * sub-expression.
161
+ *
162
+ * 3. Adapters implementing this node kind should **USE WITH CAUTION**!
163
+ */
164
+ export type UnspecifiedNonXPathNodeKind = 'UNSPECIFIED_NON_XPATH_NODE';
@@ -0,0 +1,15 @@
1
+ import { UnspecifiedNonXPathNodeKind, XPathAttribute, XPathComment, XPathDocument, XPathElement, XPathNamespaceDeclaration, XPathNode, XPathNodeKind, XPathProcessingInstruction, XPathText } from './XPathNode.ts';
2
+ export type AdapterDocument<T extends XPathNode> = Extract<T, XPathDocument>;
3
+ export type AdapterElement<T extends XPathNode> = Extract<T, XPathElement>;
4
+ export type AdapterNamespaceDeclaration<T extends XPathNode> = Extract<T, XPathNamespaceDeclaration>;
5
+ export type AdapterAttribute<T extends XPathNode> = Extract<T, XPathAttribute>;
6
+ export type AdapterText<T extends XPathNode> = Extract<T, XPathText>;
7
+ export type AdapterComment<T extends XPathNode> = Extract<T, XPathComment>;
8
+ export type AdapterProcessingInstruction<T extends XPathNode> = Extract<T, XPathProcessingInstruction>;
9
+ export type AdapterParentNode<T extends XPathNode> = AdapterDocument<T> | AdapterElement<T>;
10
+ export type AdapterChildNode<T extends XPathNode> = AdapterElement<T> | AdapterText<T> | AdapterComment<T> | AdapterProcessingInstruction<T>;
11
+ export type AdapterQualifiedNamedNode<T extends XPathNode> = AdapterElement<T> | AdapterAttribute<T>;
12
+ export interface XPathNodeKindAdapter<T extends XPathNode> {
13
+ readonly isXPathNode: (value: unknown) => value is T;
14
+ readonly getNodeKind: (node: T) => XPathNodeKind | UnspecifiedNonXPathNodeKind;
15
+ }
@@ -0,0 +1,38 @@
1
+ import { XPathNode } from './XPathNode.ts';
2
+ import { AdapterAttribute, AdapterChildNode, AdapterDocument, AdapterElement, AdapterNamespaceDeclaration, AdapterParentNode } from './XPathNodeKindAdapter.ts';
3
+ export type DocumentOrderComparison = -1 | 0 | 1;
4
+ export interface XPathTraversalAdapter<T extends XPathNode> {
5
+ /**
6
+ * Gets the document containing {@link node}.
7
+ *
8
+ * Note: implementations are expected to operate on a fully "attached" tree of
9
+ * nodes, i.e. where any particular descendant node can ultimately be
10
+ * traversed up to the document node produced by this method. In that sense,
11
+ * implementations are also expected to provide this method as an explicit,
12
+ * mandatory optimization of that otherwise implicit expectation.
13
+ */
14
+ readonly getContainingDocument: (node: T) => AdapterDocument<T>;
15
+ readonly getNamespaceDeclarations: (node: T) => Iterable<AdapterNamespaceDeclaration<T>>;
16
+ readonly getAttributes: (node: T) => Iterable<AdapterAttribute<T>>;
17
+ readonly getParentNode: (node: T) => AdapterParentNode<T> | null;
18
+ readonly getChildNodes: (node: T) => Iterable<AdapterChildNode<T>>;
19
+ readonly getChildElements: (node: T) => Iterable<AdapterElement<T>>;
20
+ readonly getPreviousSiblingNode: (node: T) => AdapterChildNode<T> | null;
21
+ readonly getPreviousSiblingElement: (node: T) => AdapterElement<T> | null;
22
+ readonly getNextSiblingNode: (node: T) => AdapterChildNode<T> | null;
23
+ readonly getNextSiblingElement: (node: T) => AdapterElement<T> | null;
24
+ /**
25
+ * @todo Not entirely sure this is a "traversal" API! It may use traversal to
26
+ * produce the result? It doesn't really belong anywhere else, and unlike
27
+ * values it doesn't especially feel like it warrants a separate
28
+ * sub-interface namespace.
29
+ */
30
+ readonly compareDocumentOrder: (a: T, b: T) => DocumentOrderComparison;
31
+ /**
32
+ * Determines if {@link node} is a descendant of {@link ancestor}.
33
+ *
34
+ * @todo The parameter names are doing all of the self-documentation work
35
+ * here. Consider a better name for this!
36
+ */
37
+ readonly isDescendantNode: (ancestor: T, node: T) => boolean;
38
+ }
@@ -0,0 +1,9 @@
1
+ import { XPathNode } from './XPathNode.ts';
2
+ /**
3
+ * @todo Allow adapters to implement (optional) support for non-string types,
4
+ * e.g. when the adapter's node representation already implements the type with
5
+ * the same semantics.
6
+ */
7
+ export interface XPathValueAdapter<T extends XPathNode> {
8
+ readonly getNodeValue: (node: T) => string;
9
+ }
@@ -0,0 +1,80 @@
1
+ import { XPathDOMAdapter } from './interface/XPathDOMAdapter.ts';
2
+ import { XPathDOMOptimizableOperations } from './interface/XPathDOMOptimizableOperations.ts';
3
+ import { XPathNode } from './interface/XPathNode.ts';
4
+ import { AdapterAttribute, AdapterComment, AdapterDocument, AdapterElement, AdapterNamespaceDeclaration, AdapterParentNode, AdapterProcessingInstruction, AdapterQualifiedNamedNode, AdapterText } from './interface/XPathNodeKindAdapter.ts';
5
+ type AssertXPathNode<T extends XPathNode> = <U>(value: U, message?: string) => asserts value is Extract<U, T>;
6
+ type AssertParentNode<T extends XPathNode> = <U>(value: U, message?: string) => asserts value is Extract<U, AdapterParentNode<T>>;
7
+ type NodeKindPredicate<T extends XPathNode, U extends T> = (node: T) => node is U;
8
+ /**
9
+ * Provides frequently used type guards for narrowing an
10
+ * {@link XPathDOMAdapter}'s node representation to either:
11
+ *
12
+ * - A valid context node (as provided to evaluation API call sites)
13
+ * - Any one distinct semantic kind of XPath node
14
+ * - Useful unions of any subset thereof
15
+ */
16
+ interface NodeKindGuards<T extends XPathNode> {
17
+ readonly assertXPathNode: AssertXPathNode<T>;
18
+ readonly assertParentNode: AssertParentNode<T>;
19
+ readonly isDocument: NodeKindPredicate<T, AdapterDocument<T>>;
20
+ readonly isElement: NodeKindPredicate<T, AdapterElement<T>>;
21
+ readonly isNamespaceDeclaration: NodeKindPredicate<T, AdapterNamespaceDeclaration<T>>;
22
+ readonly isAttribute: NodeKindPredicate<T, AdapterAttribute<T>>;
23
+ readonly isText: NodeKindPredicate<T, AdapterText<T>>;
24
+ readonly isComment: NodeKindPredicate<T, AdapterComment<T>>;
25
+ readonly isProcessingInstruction: NodeKindPredicate<T, AdapterProcessingInstruction<T>>;
26
+ readonly isParentNode: NodeKindPredicate<T, AdapterParentNode<T>>;
27
+ readonly isQualifiedNamedNode: NodeKindPredicate<T, AdapterQualifiedNamedNode<T>>;
28
+ }
29
+ type IterableNodeFilter<T extends XPathNode, U extends T> = (nodes: Iterable<T>) => Iterable<U>;
30
+ /**
31
+ * Provides frequently used operations, such as filtering and sorting, on
32
+ * {@link Iterable} sequences of an {@link XPathDOMAdapter}'s node
33
+ * representation.
34
+ */
35
+ interface IterableOperations<T extends XPathNode> {
36
+ readonly filterAttributes: IterableNodeFilter<T, AdapterAttribute<T>>;
37
+ readonly filterQualifiedNamedNodes: IterableNodeFilter<T, AdapterQualifiedNamedNode<T>>;
38
+ readonly filterComments: IterableNodeFilter<T, AdapterComment<T>>;
39
+ readonly filterNamespaceDeclarations: IterableNodeFilter<T, AdapterNamespaceDeclaration<T>>;
40
+ readonly filterProcessingInstructions: IterableNodeFilter<T, AdapterProcessingInstruction<T>>;
41
+ readonly filterTextNodes: IterableNodeFilter<T, AdapterText<T>>;
42
+ readonly sortInDocumentOrder: (nodes: Iterable<T>) => readonly T[];
43
+ }
44
+ /**
45
+ * Omitting XPathDOMOptimizableOperations keys here allows them to be made
46
+ * non-optional, without then repeating the exact same properties and their
47
+ * exact same signatures inline...
48
+ */
49
+ type OmitOptionalOptimizableOperations<T> = Omit<T, keyof XPathDOMOptimizableOperations<XPathNode>>;
50
+ /**
51
+ * An {@link XPathDOMProvider} is a superset of the {@link XPathDOMAdapter}
52
+ * interface. As such, a derived provider is assignable anywhere an adapter (or
53
+ * subset thereof) is expected. We assign this symbol as a means to check
54
+ * whether the adapter passed to {@link xpathDOMProvider} is already a derived
55
+ * provider. Checking for the symbol (via {@link isXPathDOMProvider}) is
56
+ * sufficient, because this symbol is module local and therefore is only ever
57
+ * assigned as part of a first pass through {@link xpathDOMProvider}.
58
+ *
59
+ * While repeated derivation of a DOM provider is not especially likely, and
60
+ * would probably not be particularly expensive even if it did occur, _this is
61
+ * not just an optimization!_ A few other `@getodk/xpath` internals depend on an
62
+ * **identity check** of a given {@link XPathDOMProvider} object as part of
63
+ * broader guard/assertion logic.
64
+ *
65
+ * (An alternate approach to achieve the same thing would involve assigning both
66
+ * the adapter and the provider as keys e.g. in an {@link UpsertableMap} cache,
67
+ * but that would exaggerate the performance consideration, obscuring the intent
68
+ * to preserve object identity.)
69
+ */
70
+ declare const DERIVED_DOM_PROVIDER: unique symbol;
71
+ /**
72
+ * @see {@link DERIVED_DOM_PROVIDER}
73
+ */
74
+ interface DerivedDOMProvider {
75
+ readonly [DERIVED_DOM_PROVIDER]: true;
76
+ }
77
+ export interface XPathDOMProvider<T extends XPathNode> extends OmitOptionalOptimizableOperations<XPathDOMAdapter<T>>, NodeKindGuards<T>, IterableOperations<T>, XPathDOMOptimizableOperations<T>, DerivedDOMProvider {
78
+ }
79
+ export declare const xpathDOMProvider: <T extends XPathNode>(adapter: XPathDOMAdapter<T>) => XPathDOMProvider<T>;
80
+ export {};
@@ -1,14 +1,16 @@
1
1
  import { Temporal } from '@js-temporal/polyfill';
2
+ import { XPathNode } from '../adapter/interface/XPathDOMAdapter.ts';
3
+ import { AdapterDocument, AdapterParentNode } from '../adapter/interface/XPathNodeKindAdapter.ts';
4
+ import { XPathDOMProvider } from '../adapter/xpathDOMProvider.ts';
2
5
  import { Evaluator } from '../evaluator/Evaluator.ts';
3
6
  import { FunctionLibraryCollection } from '../evaluator/functions/FunctionLibraryCollection.ts';
4
- import { ContextDocument, ContextNode, ContextParentNode } from '../lib/dom/types.ts';
5
- import { XPathNamespaceResolverObject } from '../shared/interface.ts';
6
-
7
+ import { NamespaceResolver } from '../evaluator/NamespaceResolver.ts';
7
8
  /**
8
9
  * The context in which any XPath expression *or sub-expression* is evaluated.
9
10
  */
10
- export interface Context {
11
- readonly evaluator: Evaluator;
11
+ export interface Context<T extends XPathNode> {
12
+ readonly evaluator: Evaluator<T>;
13
+ readonly domProvider: XPathDOMProvider<T>;
12
14
  /**
13
15
  * The `contextNode` as specified at the {@link Evaluator.evaluate} call site.
14
16
  * This value is to remain constant for the entire scope of an evaluation.
@@ -16,13 +18,13 @@ export interface Context {
16
18
  * This explicitly supports the `current()` function, but it may also support
17
19
  * a variety of other use cases and optimizations in the future.
18
20
  */
19
- readonly evaluationContextNode: ContextNode;
20
- readonly contextDocument: ContextDocument;
21
- readonly rootNode: ContextParentNode;
22
- readonly contextNodes: Iterable<ContextNode>;
21
+ readonly evaluationContextNode: T;
22
+ readonly contextDocument: AdapterDocument<T>;
23
+ readonly rootNode: AdapterParentNode<T>;
24
+ readonly contextNodes: Iterable<T>;
23
25
  contextPosition(): number;
24
26
  contextSize(): number;
25
27
  readonly functions: FunctionLibraryCollection;
26
- readonly namespaceResolver: XPathNamespaceResolverObject;
28
+ readonly namespaceResolver: NamespaceResolver<T>;
27
29
  readonly timeZone: Temporal.TimeZone;
28
30
  }