@jarenjs/json 0.9.2 → 0.34.2
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.
- package/ARCHITECTURE.md +86 -13
- package/README.md +248 -23
- package/dist/types/canonical.d.ts +37 -0
- package/dist/types/cow.d.ts +28 -0
- package/dist/types/errors.d.ts +45 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/jslt/errors.d.ts +15 -8
- package/dist/types/jslt/index.d.ts +22 -0
- package/dist/types/jslt/packs/finance.d.ts +119 -0
- package/dist/types/jslt/packs/index.d.ts +310 -0
- package/dist/types/jslt/packs/math.d.ts +159 -0
- package/dist/types/jslt/packs/stats.d.ts +48 -0
- package/dist/types/jslt/registry.d.ts +65 -0
- package/dist/types/jtlt/errors.d.ts +3 -6
- package/dist/types/option-variants.d.ts +29 -0
- package/dist/types/patch.d.ts +214 -0
- package/dist/types/path.d.ts +139 -9
- package/dist/types/pointer.d.ts +100 -9
- package/dist/types/query/compile.d.ts +12 -0
- package/dist/types/query/errors.d.ts +72 -8
- package/dist/types/query/index.d.ts +317 -25
- package/dist/types/query/normalize.d.ts +24 -0
- package/dist/types/query/operators.d.ts +241 -1
- package/dist/types/query/runtime.d.ts +5 -8
- package/dist/types/query/types.d.ts +34 -0
- package/dist/types/segments.d.ts +31 -0
- package/dist/types/write.d.ts +204 -0
- package/dist/types/xquery/parse.d.ts +2 -3
- package/docs/JSLT-FORMAT.md +74 -3
- package/docs/JSLT-PRELUDE.md +1 -1
- package/docs/QUERY-FORMAT.md +695 -33
- package/package.json +18 -4
- package/schemas/geojson.draft-07.schema.json +323 -0
- package/schemas/geojson.jaren.schema.json +863 -0
- package/schemas/geojson.schema.json +172 -0
- package/schemas/jaren-jslt.authoring.schema.json +142 -0
- package/schemas/jaren-jslt.draft-07.schema.json +152 -11
- package/schemas/jaren-jslt.llm-profile.schema.json +782 -0
- package/schemas/jaren-jslt.schema.json +152 -11
- package/schemas/jaren-query.draft-07.schema.json +152 -11
- package/schemas/jaren-query.llm-profile.schema.json +619 -0
- package/schemas/jaren-query.schema.json +82 -15
- package/src/basic.js +1 -1
- package/src/canonical.js +170 -0
- package/src/cow.js +106 -0
- package/src/errors.js +68 -0
- package/src/index.js +3 -0
- package/src/jslt/dispatch.js +178 -28
- package/src/jslt/errors.js +19 -14
- package/src/jslt/index.js +37 -29
- package/src/jslt/packs/finance.js +49 -0
- package/src/jslt/packs/index.js +18 -0
- package/src/jslt/packs/math.js +46 -0
- package/src/jslt/packs/stats.js +65 -0
- package/src/jslt/registry.js +200 -0
- package/src/jslt/stylesheet.js +14 -23
- package/src/jtlt/desugar.js +2 -3
- package/src/jtlt/errors.js +6 -12
- package/src/jtlt/index.js +12 -29
- package/src/jtlt/template.js +9 -18
- package/src/option-variants.js +54 -0
- package/src/patch.js +1052 -0
- package/src/path.js +319 -52
- package/src/pointer.js +225 -44
- package/src/query/compile.js +790 -75
- package/src/query/errors.js +72 -12
- package/src/query/index.js +274 -42
- package/src/query/normalize.js +489 -78
- package/src/query/operators.js +620 -23
- package/src/query/runtime.js +5 -19
- package/src/query/types.js +213 -0
- package/src/segments.js +409 -64
- package/src/write.js +660 -0
- package/src/xquery/parse.js +37 -53
package/src/query/runtime.js
CHANGED
|
@@ -64,23 +64,6 @@ export function appendItem(list, v) {
|
|
|
64
64
|
list.push(v);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/**
|
|
68
|
-
* Invoke `fn(item)` for each item of a sequence value, in order.
|
|
69
|
-
* @param {any} v - a sequence value (EMPTY, item, or Seq)
|
|
70
|
-
* @param {(item: any) => void} fn
|
|
71
|
-
*/
|
|
72
|
-
export function forEachItem(v, fn) {
|
|
73
|
-
if (v === EMPTY)
|
|
74
|
-
return;
|
|
75
|
-
if (v instanceof Seq) {
|
|
76
|
-
const items = v.items;
|
|
77
|
-
for (let i = 0; i < items.length; i++)
|
|
78
|
-
fn(items[i]);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
fn(v);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
67
|
/**
|
|
85
68
|
* Number of items in a sequence value.
|
|
86
69
|
* @param {any} v - a sequence value (EMPTY, item, or Seq)
|
|
@@ -155,8 +138,11 @@ export function describeItem(v) {
|
|
|
155
138
|
/**
|
|
156
139
|
* Deterministic serialization of one JSON item, for `$groupby` keys
|
|
157
140
|
* (QUERY-FORMAT.md section 6.5) — **engine-internal**, not an interchange
|
|
158
|
-
* format
|
|
159
|
-
*
|
|
141
|
+
* format. For interchange (hashing, signing) use `canonicalizeJson`
|
|
142
|
+
* (canonical.js, RFC 8785), which is deliberately NOT this function: JCS
|
|
143
|
+
* rejects `NaN`/`Infinity` outright, where grouping needs them to be
|
|
144
|
+
* representable so that `NaN` groups with `NaN`. This exists solely so
|
|
145
|
+
* that deep-equal items (D2) map to the same string:
|
|
160
146
|
*
|
|
161
147
|
* - object members serialize sorted by key (code-unit order), so key
|
|
162
148
|
* order never matters;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Optional type annotation over the published normalized form
|
|
4
|
+
* (QUERY-FORMAT.md Appendix C.8). The AST carries cardinality but no
|
|
5
|
+
* value types; a consumer that owns a type source — a store's JSON
|
|
6
|
+
* Schema, a model document — supplies `typeOf(pathNode)` and gets back
|
|
7
|
+
* a NEW frozen tree mirroring the input with a `type` tag on every
|
|
8
|
+
* node. The pass never mutates its input and never runs during
|
|
9
|
+
* compilation.
|
|
10
|
+
*
|
|
11
|
+
* The lattice is small and closed: `unknown` is the top and always a
|
|
12
|
+
* safe answer; a wrong tag is a defect. Operators propagate through the
|
|
13
|
+
* registry's declared `resultType` families (comparison, arithmetic,
|
|
14
|
+
* string, aggregate); everything undeclared yields `unknown` rather
|
|
15
|
+
* than a guess.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { OPERATORS } from './operators.js';
|
|
19
|
+
import { CARD_ONE } from './normalize.js';
|
|
20
|
+
|
|
21
|
+
/** The closed set of type-tag names, sorted. */
|
|
22
|
+
export const TYPE_TAGS = Object.freeze([
|
|
23
|
+
'array', 'boolean', 'integer', 'null', 'number', 'object', 'string', 'unknown',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
// Interned tag objects: one frozen { type, optional } per combination,
|
|
27
|
+
// so annotated trees share tags by identity and comparisons are cheap.
|
|
28
|
+
const TAGS = (() => {
|
|
29
|
+
/** @type {Record<string, Readonly<{type: string, optional: boolean}>>} */
|
|
30
|
+
const table = {};
|
|
31
|
+
for (const type of TYPE_TAGS) {
|
|
32
|
+
table[`${type}:0`] = Object.freeze({ type, optional: false });
|
|
33
|
+
table[`${type}:1`] = Object.freeze({ type, optional: true });
|
|
34
|
+
}
|
|
35
|
+
return Object.freeze(table);
|
|
36
|
+
})();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The interned tag for a type name and optionality.
|
|
40
|
+
* @param {string} type - one of {@link TYPE_TAGS}
|
|
41
|
+
* @param {boolean} optional
|
|
42
|
+
*/
|
|
43
|
+
function tagOf(type, optional) {
|
|
44
|
+
return TAGS[`${type}:${optional ? 1 : 0}`];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Join two tag names: equal names join to themselves, `integer` widens
|
|
49
|
+
* into `number`, anything else joins to `unknown` (the top).
|
|
50
|
+
* @param {string} a
|
|
51
|
+
* @param {string} b
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
function joinTagName(a, b) {
|
|
55
|
+
if (a === b) return a;
|
|
56
|
+
if ((a === 'integer' && b === 'number') || (a === 'number' && b === 'integer'))
|
|
57
|
+
return 'number';
|
|
58
|
+
return 'unknown';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The tag name of a literal JSON value. @param {any} value */
|
|
62
|
+
function literalTagName(value) {
|
|
63
|
+
if (value === null) return 'null';
|
|
64
|
+
switch (typeof value) {
|
|
65
|
+
case 'boolean': return 'boolean';
|
|
66
|
+
case 'number': return Number.isInteger(value) ? 'integer' : 'number';
|
|
67
|
+
case 'string': return 'string';
|
|
68
|
+
default: return Array.isArray(value) ? 'array' : 'object';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Normalize whatever the caller's `typeOf` returned into an interned
|
|
74
|
+
* tag: a bare tag name (optionality derived from the node's own
|
|
75
|
+
* cardinality), a `{ type, optional }` object, or `null`/`undefined`
|
|
76
|
+
* for "unknown". Anything outside the lattice is `unknown` — a safe
|
|
77
|
+
* answer, never a throw, because a sloppy hook must not fail analysis.
|
|
78
|
+
* @param {any} answer
|
|
79
|
+
* @param {any} node
|
|
80
|
+
*/
|
|
81
|
+
function normalizeHookAnswer(answer, node) {
|
|
82
|
+
const fallback = node.card !== CARD_ONE;
|
|
83
|
+
if (answer == null) return tagOf('unknown', fallback);
|
|
84
|
+
if (typeof answer === 'string')
|
|
85
|
+
return TYPE_TAGS.includes(answer) ? tagOf(answer, fallback) : tagOf('unknown', fallback);
|
|
86
|
+
const type = TYPE_TAGS.includes(answer.type) ? answer.type : 'unknown';
|
|
87
|
+
return tagOf(type, answer.optional === true);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Annotate a normalized analysis with type tags (Appendix C.8): a NEW
|
|
92
|
+
* frozen analysis whose tree mirrors the input with `type` on every
|
|
93
|
+
* node. The input analysis and its tree are never mutated.
|
|
94
|
+
* @param {{ astVersion: number, root: any }} analysis - an
|
|
95
|
+
* `analyzeQuery` result (or any record carrying the frozen `root`)
|
|
96
|
+
* @param {{ typeOf?: (pathNode: any) => any }} [hooks] - `typeOf`
|
|
97
|
+
* answers for `path` nodes: a tag name, a `{ type, optional }`
|
|
98
|
+
* object, or `null` for unknown
|
|
99
|
+
* @returns {any} the annotated analysis
|
|
100
|
+
*/
|
|
101
|
+
export function annotateTypes(analysis, hooks = {}) {
|
|
102
|
+
const typeOf = typeof hooks.typeOf === 'function' ? hooks.typeOf : null;
|
|
103
|
+
|
|
104
|
+
/** @param {any} node @returns {any} */
|
|
105
|
+
function annotate(node) {
|
|
106
|
+
switch (node.kind) {
|
|
107
|
+
case 'literal':
|
|
108
|
+
return Object.freeze({ ...node, type: tagOf(literalTagName(node.value), false) });
|
|
109
|
+
case 'var':
|
|
110
|
+
return Object.freeze({ ...node, type: tagOf('unknown', node.card !== CARD_ONE) });
|
|
111
|
+
case 'path': {
|
|
112
|
+
const answer = typeOf === null ? null : typeOf(node);
|
|
113
|
+
return Object.freeze({ ...node, type: normalizeHookAnswer(answer, node) });
|
|
114
|
+
}
|
|
115
|
+
case 'object':
|
|
116
|
+
return Object.freeze({
|
|
117
|
+
...node,
|
|
118
|
+
entries: Object.freeze(node.entries.map(
|
|
119
|
+
(e) => Object.freeze({ ...e, expr: annotate(e.expr) }))),
|
|
120
|
+
type: tagOf('object', false),
|
|
121
|
+
});
|
|
122
|
+
case 'map':
|
|
123
|
+
return Object.freeze({
|
|
124
|
+
...node,
|
|
125
|
+
pairs: Object.freeze(node.pairs.map(
|
|
126
|
+
(p) => Object.freeze({ ...p, key: annotate(p.key), value: annotate(p.value) }))),
|
|
127
|
+
type: tagOf('object', false),
|
|
128
|
+
});
|
|
129
|
+
case 'array':
|
|
130
|
+
return Object.freeze({
|
|
131
|
+
...node,
|
|
132
|
+
elements: Object.freeze(node.elements.map(annotate)),
|
|
133
|
+
type: tagOf('array', false),
|
|
134
|
+
});
|
|
135
|
+
case 'raw':
|
|
136
|
+
return Object.freeze({ ...node, type: tagOf('unknown', false) });
|
|
137
|
+
case 'op': {
|
|
138
|
+
const args = Object.freeze(node.args.map(annotate));
|
|
139
|
+
// a registered operator (options.extensions / the JSLT registry)
|
|
140
|
+
// carries its entry on the node — fall back to it so a pack op's
|
|
141
|
+
// declared resultType flows through the analyzer, exactly as
|
|
142
|
+
// compileOp resolves `OPERATORS[name] ?? node.entry`
|
|
143
|
+
const entry = OPERATORS[node.name] ?? node.entry;
|
|
144
|
+
const resultType = entry !== undefined && typeof entry.resultType === 'function'
|
|
145
|
+
? entry.resultType(args.map((a) => a.type))
|
|
146
|
+
: null;
|
|
147
|
+
const name = typeof resultType === 'string' && TYPE_TAGS.includes(resultType)
|
|
148
|
+
? resultType
|
|
149
|
+
: 'unknown';
|
|
150
|
+
return Object.freeze({ ...node, args, type: tagOf(name, node.card !== CARD_ONE) });
|
|
151
|
+
}
|
|
152
|
+
case 'call':
|
|
153
|
+
return Object.freeze({
|
|
154
|
+
...node,
|
|
155
|
+
args: Object.freeze(node.args.map(annotate)),
|
|
156
|
+
type: tagOf('unknown', true),
|
|
157
|
+
});
|
|
158
|
+
case 'let': {
|
|
159
|
+
const bindings = Object.freeze(node.bindings.map(
|
|
160
|
+
(b) => Object.freeze({ ...b, expr: annotate(b.expr) })));
|
|
161
|
+
const ret = annotate(node.ret);
|
|
162
|
+
return Object.freeze({
|
|
163
|
+
...node, bindings, ret,
|
|
164
|
+
type: tagOf(ret.type.type, node.card !== CARD_ONE),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
case 'quant':
|
|
168
|
+
return Object.freeze({
|
|
169
|
+
...node,
|
|
170
|
+
bindings: Object.freeze(node.bindings.map(
|
|
171
|
+
(b) => Object.freeze({ ...b, expr: annotate(b.expr) }))),
|
|
172
|
+
satisfies: annotate(node.satisfies),
|
|
173
|
+
type: tagOf('boolean', false),
|
|
174
|
+
});
|
|
175
|
+
default: { // 'flwor'
|
|
176
|
+
const fold = node.fold === null
|
|
177
|
+
? null
|
|
178
|
+
: Object.freeze({ ...node.fold, expr: annotate(node.fold.expr) });
|
|
179
|
+
const forBindings = Object.freeze(node.forBindings.map(
|
|
180
|
+
(b) => Object.freeze({ ...b, expr: annotate(b.expr) })));
|
|
181
|
+
const letBindings = Object.freeze(node.letBindings.map(
|
|
182
|
+
(b) => Object.freeze({ ...b, expr: annotate(b.expr) })));
|
|
183
|
+
const where = node.where === null ? null : annotate(node.where);
|
|
184
|
+
const groupby = node.groupby === null
|
|
185
|
+
? null
|
|
186
|
+
: Object.freeze({
|
|
187
|
+
...node.groupby,
|
|
188
|
+
keys: Object.freeze(node.groupby.keys.map(
|
|
189
|
+
(k) => Object.freeze({ ...k, expr: annotate(k.expr) }))),
|
|
190
|
+
});
|
|
191
|
+
const orderby = node.orderby === null
|
|
192
|
+
? null
|
|
193
|
+
: Object.freeze({
|
|
194
|
+
...node.orderby,
|
|
195
|
+
specs: Object.freeze(node.orderby.specs.map(
|
|
196
|
+
(s) => Object.freeze({ ...s, key: annotate(s.key) }))),
|
|
197
|
+
});
|
|
198
|
+
const ret = annotate(node.ret);
|
|
199
|
+
// the phrase's ITEM type: the $return item's tag (joined with the
|
|
200
|
+
// accumulator's for $fold — either can be the final value)
|
|
201
|
+
const itemTag = fold === null
|
|
202
|
+
? ret.type.type
|
|
203
|
+
: joinTagName(fold.expr.type.type, ret.type.type);
|
|
204
|
+
return Object.freeze({
|
|
205
|
+
...node, fold, forBindings, letBindings, where, groupby, orderby, ret,
|
|
206
|
+
type: tagOf(itemTag, node.card !== CARD_ONE),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return Object.freeze({ ...analysis, root: annotate(analysis.root) });
|
|
213
|
+
}
|