@metreeca/keep-sparql 0.10.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 (71) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +106 -0
  3. package/dist/_/_decode.d.ts +20 -0
  4. package/dist/_/_decode.d.ts.map +1 -0
  5. package/dist/_/_decode.js +55 -0
  6. package/dist/_/_decode.js.map +1 -0
  7. package/dist/_/_encode.d.ts +112 -0
  8. package/dist/_/_encode.d.ts.map +1 -0
  9. package/dist/_/_encode.js +262 -0
  10. package/dist/_/_encode.js.map +1 -0
  11. package/dist/_/_flake.d.ts +16 -0
  12. package/dist/_/_flake.d.ts.map +1 -0
  13. package/dist/_/_flake.js +65 -0
  14. package/dist/_/_flake.js.map +1 -0
  15. package/dist/_/_union.d.ts +28 -0
  16. package/dist/_/_union.d.ts.map +1 -0
  17. package/dist/_/_union.js +51 -0
  18. package/dist/_/_union.js.map +1 -0
  19. package/dist/detect/decode.d.ts +25 -0
  20. package/dist/detect/decode.d.ts.map +1 -0
  21. package/dist/detect/decode.js +34 -0
  22. package/dist/detect/decode.js.map +1 -0
  23. package/dist/detect/encode.d.ts +25 -0
  24. package/dist/detect/encode.d.ts.map +1 -0
  25. package/dist/detect/encode.js +30 -0
  26. package/dist/detect/encode.js.map +1 -0
  27. package/dist/detect/index.d.ts +17 -0
  28. package/dist/detect/index.d.ts.map +1 -0
  29. package/dist/detect/index.js +55 -0
  30. package/dist/detect/index.js.map +1 -0
  31. package/dist/index.d.ts +95 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +70 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/lookup/decode.d.ts +33 -0
  36. package/dist/lookup/decode.d.ts.map +1 -0
  37. package/dist/lookup/decode.js +193 -0
  38. package/dist/lookup/decode.js.map +1 -0
  39. package/dist/lookup/encode.d.ts +46 -0
  40. package/dist/lookup/encode.d.ts.map +1 -0
  41. package/dist/lookup/encode.js +111 -0
  42. package/dist/lookup/encode.js.map +1 -0
  43. package/dist/lookup/index.d.ts +21 -0
  44. package/dist/lookup/index.d.ts.map +1 -0
  45. package/dist/lookup/index.js +72 -0
  46. package/dist/lookup/index.js.map +1 -0
  47. package/dist/modify/decode.d.ts +22 -0
  48. package/dist/modify/decode.d.ts.map +1 -0
  49. package/dist/modify/decode.js +26 -0
  50. package/dist/modify/decode.js.map +1 -0
  51. package/dist/modify/encode.d.ts +21 -0
  52. package/dist/modify/encode.d.ts.map +1 -0
  53. package/dist/modify/encode.js +184 -0
  54. package/dist/modify/encode.js.map +1 -0
  55. package/dist/modify/index.d.ts +22 -0
  56. package/dist/modify/index.d.ts.map +1 -0
  57. package/dist/modify/index.js +44 -0
  58. package/dist/modify/index.js.map +1 -0
  59. package/dist/select/decode.d.ts +16 -0
  60. package/dist/select/decode.d.ts.map +1 -0
  61. package/dist/select/decode.js +229 -0
  62. package/dist/select/decode.js.map +1 -0
  63. package/dist/select/encode.d.ts +16 -0
  64. package/dist/select/encode.d.ts.map +1 -0
  65. package/dist/select/encode.js +434 -0
  66. package/dist/select/encode.js.map +1 -0
  67. package/dist/select/index.d.ts +16 -0
  68. package/dist/select/index.d.ts.map +1 -0
  69. package/dist/select/index.js +38 -0
  70. package/dist/select/index.js.map +1 -0
  71. package/package.json +68 -0
@@ -0,0 +1,262 @@
1
+ /*
2
+ * Copyright © 2025-2026 Metreeca srl
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { sh } from "@metreeca/blue";
17
+ import { getShapeClass, getShapeId } from "@metreeca/blue/resource";
18
+ import { getBoundVariant, getStateVariant } from "@metreeca/blue/union";
19
+ import { error, isBoolean, isNumber, isObject, isString, opt } from "@metreeca/core";
20
+ import { some } from "@metreeca/core/arrays";
21
+ import { xsd } from "@metreeca/core/datatype";
22
+ import { isReference } from "@metreeca/qest/resource";
23
+ import { named, rdf, tagged, typed } from "@metreeca/trio";
24
+ import { abs, and, avg, ceil, count, datatype, day, eq, filter, floor, gt, hours, iif, isIRI, isLiteral, isNumeric, lang, lcase, max, min, minutes, month, ne, nil, not, number, pattern, reference, round, seconds, string, strlen, sum, ucase, variable, year } from "@metreeca/wire-sparql/builder";
25
+ /**
26
+ * The RDF {@link Term} of a comparison bound (`<` / `>` / `<=` / `>=`), typed against the {@link RangeShape |
27
+ * shape} variant it resolves to so the comparison resolves in the target's processing type (§5.7.1).
28
+ * {@link getBoundVariant} routes the bound, relaxing the value-domain facets a bound need not satisfy. A
29
+ * string variant carrying the `sh:IRI` datatype (an id / type entry, or a reference-ranged property) renders
30
+ * an IRI node rather than a literal, told apart from an IRI-shaped literal (a `url`) by datatype alone.
31
+ */
32
+ export function boundToTerm(value, shape) {
33
+ return valueToTerm(value, getBoundVariant(value, shape.variants)
34
+ ?? (isString(value) ? shape.variants.find(variant => variant.kind === "dictionary") : undefined)
35
+ ?? error(new RangeError(`unresolved range variant for value <${String(value)}>`)));
36
+ }
37
+ /**
38
+ * Flattens the options of a set-matching or focus constraint to individual match {@link Term | terms}: a
39
+ * localised dictionary set expands to one language-tagged term per language tag (its value, or every element of
40
+ * its value array), a scalar option maps to its term typed by the {@link RangeShape | shape} variant it
41
+ * fits, and an option array maps element-wise. A `null` scalar survives as the absent-value option
42
+ * (§5.7.3).
43
+ */
44
+ export function optionsToTerms(value, shape) {
45
+ if (isObject(value)) {
46
+ // the `und` tag is stored as a plain literal, every other tag as a language-tagged one (§6), so
47
+ // an und option matches by its plain value while a tagged option matches the raw tagged literal
48
+ return Object.entries(value).flatMap(([language, values]) => some(values).map(text => language === "und" ? typed(text) : tagged(text, language)));
49
+ }
50
+ else {
51
+ return some(value).map(option => option === null ? null : valueToTerm(option, getStateVariant(option, shape.variants)
52
+ ?? (isString(option) ? shape.variants.find(variant => variant.kind === "dictionary") : undefined)
53
+ ?? error(new RangeError(`unresolved range variant for value <${String(option)}>`))));
54
+ }
55
+ }
56
+ /**
57
+ * Types an operand against its resolved {@link ValuesShape | shape} (§5.7.1). A string shape carrying the
58
+ * `sh:IRI` datatype (an id / type entry, or a reference-ranged property) renders an IRI node rather than a
59
+ * literal, told apart from an IRI-shaped literal (a `url`) by datatype alone.
60
+ */
61
+ export function valueToTerm(value, shape) {
62
+ switch (shape.kind) {
63
+ case "boolean":
64
+ return typed(String(value), xsd.boolean);
65
+ case "number":
66
+ return typed(String(value), shape.datatype ?? xsd.double);
67
+ case "string":
68
+ return shape.datatype === sh.IRI
69
+ ? named(String(value))
70
+ : typed(String(value), shape.datatype);
71
+ case "dictionary": // a localised dictionary shape is coalesced to a plain xsd:string (§6)
72
+ return typed(String(value));
73
+ case "reference":
74
+ return named(String(value));
75
+ case "resource": // a scalar operand never resolves to an expanded resource shape
76
+ throw new RangeError(`unsupported embedded resource variant for value <${String(value)}>`);
77
+ }
78
+ }
79
+ /**
80
+ * Types a property's write value(s) to the RDF {@link Term | terms} to store, flattening a single value or
81
+ * a value set against the resolved {@link ValuesShape | shape} variant (§5.7.1).
82
+ *
83
+ * Each element is typed by the variant it fits: a boolean, number, or string operand to its datatype-typed
84
+ * literal (a string shape carrying the `sh:IRI` datatype renders an IRI node instead, as in
85
+ * {@link valueToTerm}); a localised dictionary to one plain or language-tagged term per tag (§6); a reference to
86
+ * its IRI as-is; and a nested resource to its declared id when present (a captive resource with its own
87
+ * identity), else a freshly skolemised IRI addressing the embedded sub-resource. Values not fitting the
88
+ * variant are dropped.
89
+ */
90
+ export function valuesToTerms(values, shape) {
91
+ switch (shape.kind) {
92
+ case "boolean":
93
+ return some(values).filter(isBoolean).map(value => typed(value, xsd.boolean));
94
+ case "number":
95
+ return some(values).filter(isNumber).map(value => typed(value, shape.datatype ?? xsd.double));
96
+ case "string":
97
+ return some(values).filter(isString).map(value => shape.datatype === sh.IRI
98
+ ? named(value)
99
+ : typed(value, shape.datatype));
100
+ case "dictionary":
101
+ return some(values).flatMap(value => {
102
+ if (isString(value)) {
103
+ return [typed(value)];
104
+ }
105
+ else if (isObject(value)) {
106
+ return Object.entries(value).flatMap(([tag, texts]) => some(texts).filter(isString).map(text => tag === "und" ? typed(text) : tagged(text, tag)));
107
+ }
108
+ else {
109
+ return [];
110
+ }
111
+ });
112
+ case "reference":
113
+ return some(values).filter(isReference).map(value => named(value));
114
+ case "resource":
115
+ const id = getShapeId(shape);
116
+ return some(values).filter(v => isObject(v)).map(value => {
117
+ const node = id !== undefined ? value[id] : undefined;
118
+ return isReference(node) ? named(node) : named();
119
+ });
120
+ }
121
+ }
122
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
123
+ /**
124
+ * The stored edge connecting `source` to `target` through a {@link Property | property}'s declared
125
+ * predicate. Renders the forward direction when the property declares one and `source` may stand in
126
+ * subject position, else the reverse direction when `target` may; both directions carry the value, so
127
+ * either connects the pair. Yields the empty fragment when neither applies.
128
+ */
129
+ export function link([source, property, target]) {
130
+ return property.forward !== undefined && isAnchor(source) ? pattern([source, named(property.forward), target])
131
+ : property.reverse !== undefined && isAnchor(target) ? pattern([target, named(property.reverse), source])
132
+ : nil();
133
+ function isAnchor(value) {
134
+ return isString(value) || value.kind === "blank" || value.kind === "named";
135
+ }
136
+ }
137
+ /**
138
+ * Generates the SPARQL triple pattern rendering a {@link Property | property}'s forward direction.
139
+ *
140
+ * Emits `subject forward object` when the property declares a `forward` predicate; an undeclared
141
+ * forward direction contributes no pattern, yielding the empty fragment. The reverse-direction
142
+ * counterpart is {@link reverse}.
143
+ *
144
+ * @param edge The edge to render, as a `[subject, property, object]` tuple
145
+ *
146
+ * @returns The generated triple pattern, or the empty fragment when the property declares no forward
147
+ * predicate
148
+ *
149
+ * @see {@link https://www.w3.org/TR/sparql11-query/#QSynTriples SPARQL 1.1 Triple Patterns}
150
+ */
151
+ export function forward([subject, property, object]) {
152
+ return property.forward !== undefined ? pattern([subject, named(property.forward), object]) : nil();
153
+ }
154
+ /**
155
+ * Generates the SPARQL triple pattern rendering a {@link Property | property}'s reverse direction.
156
+ *
157
+ * Emits `subject reverse object`, flipping object and subject, when the property declares a `reverse`
158
+ * predicate; an undeclared reverse direction contributes no pattern, yielding the empty fragment. The
159
+ * subject lands in the triple's object position, so it is constrained to an IRI {@link Reference}. The
160
+ * forward-direction counterpart is {@link forward}.
161
+ *
162
+ * @param edge The edge to render, as a `[object, property, subject]` tuple
163
+ *
164
+ * @returns The generated triple pattern, or the empty fragment when the property declares no reverse
165
+ * predicate
166
+ *
167
+ * @see {@link https://www.w3.org/TR/sparql11-query/#QSynTriples SPARQL 1.1 Triple Patterns}
168
+ */
169
+ export function reverse([object, property, subject]) {
170
+ return property.reverse !== undefined ? pattern([subject, named(property.reverse), object]) : nil();
171
+ }
172
+ /**
173
+ * The gate binding a union shape's discriminator only for members belonging to it (union.md §Model).
174
+ *
175
+ * A classed node shape is gated by its stored `rdf:type` triple; a plain-string literal by a
176
+ * datatype/kind `filter` (boolean datatype, numeric test, or plain string); a localised `dictionary` shape by
177
+ * the language-tagged literal test, disjoint from the plain string; a classless node shape by the bare
178
+ * IRI-kind test. Under the modeller's disjointness guarantee (union.md §State) at most one shape's arm
179
+ * binds a given value.
180
+ *
181
+ * @param anchor The shape's value variable
182
+ * @param shape The shape to gate on
183
+ *
184
+ * @returns The gating triple pattern or `filter`
185
+ */
186
+ export function membership(anchor, shape) {
187
+ const value = variable(anchor);
188
+ switch (shape.kind) {
189
+ case "boolean":
190
+ return filter(and(isLiteral(value), eq(datatype(value), reference(xsd.boolean))));
191
+ case "number":
192
+ return filter(isNumeric(value));
193
+ case "string": // !!! review
194
+ return filter(and(isLiteral(value), eq(lang(value), string("")), ne(datatype(value), reference(xsd.boolean)), not(isNumeric(value))));
195
+ case "dictionary":
196
+ return filter(and(isLiteral(value), ne(lang(value), string(""))));
197
+ case "reference":
198
+ case "resource":
199
+ return opt(getShapeClass(shape), clazz => pattern([anchor, named(rdf.type), named(clazz)]), () => filter(isIRI(value)));
200
+ }
201
+ }
202
+ /**
203
+ * The SPARQL expression applying a transform `pipe` to a value variable.
204
+ *
205
+ * Composes the pipe right-to-left (`reduceRight`), so the pipe's trailing transform applies to `anchor`
206
+ * first and each earlier transform wraps the running expression, leaving the leading transform
207
+ * outermost; an empty pipe yields the bare variable. Each {@link Transform} maps to its SPARQL aggregate
208
+ * (`count`/`sum`/`min`/`max`/`avg`, with the empty-set patch keeping `avg` over no rows unbound rather
209
+ * than erroring, Appendix A.4.3) or scalar function (string, numeric, and date-part).
210
+ *
211
+ * @param anchor The value variable the pipe transforms
212
+ * @param pipe The transforms to compose, leading (outermost) first
213
+ *
214
+ * @returns The SPARQL expression applying `pipe` to `anchor`, or the bare `anchor` variable when `pipe`
215
+ * is empty
216
+ *
217
+ * @see {@link https://www.w3.org/TR/sparql11-query/#aggregates SPARQL 1.1 Aggregates}
218
+ */
219
+ export function expression(anchor, pipe) {
220
+ return pipe.reduceRight((expression, transform) => apply(expression, transform), variable(anchor));
221
+ function apply(expression, transform) {
222
+ switch (transform) {
223
+ case "count":
224
+ return count(expression);
225
+ case "sum":
226
+ return sum(expression);
227
+ case "min":
228
+ return min(expression);
229
+ case "max":
230
+ return max(expression);
231
+ case "avg": // !!! document rationale
232
+ return iif(gt(count(expression), number(0)), avg(expression), `${reference(xsd.integer)}("!")`);
233
+ case "length":
234
+ return strlen(expression);
235
+ case "lower":
236
+ return lcase(expression);
237
+ case "upper":
238
+ return ucase(expression);
239
+ case "abs":
240
+ return abs(expression);
241
+ case "floor":
242
+ return floor(expression);
243
+ case "ceil":
244
+ return ceil(expression);
245
+ case "round":
246
+ return round(expression);
247
+ case "year":
248
+ return year(expression);
249
+ case "month":
250
+ return month(expression);
251
+ case "day":
252
+ return day(expression);
253
+ case "hours":
254
+ return hours(expression);
255
+ case "minutes":
256
+ return minutes(expression);
257
+ case "seconds":
258
+ return seconds(expression);
259
+ }
260
+ }
261
+ }
262
+ //# sourceMappingURL=_encode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_encode.js","sourceRoot":"","sources":["../../src/_/_encode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type Branch, type Flake } from "@metreeca/keep-flake";
2
+ export declare function isXComputed(branch: Flake): boolean;
3
+ export declare function isXScalar(branch: Flake): boolean;
4
+ /**
5
+ * Whether a union node is a crossing intermediate: a path step under a shared predicate (§5.8.1) whose own
6
+ * value is never surfaced (no projection alias, no direct constraint / ordering / focus) and all of whose
7
+ * branches every variant declares. Such a node is traversed like a resource — its shared branches descended
8
+ * once off the anchor — rather than fanned into a membership-gated arm per variant. A surfaced union (a
9
+ * projected or constrained column) or one carrying a variant-specific branch keeps the arm machinery.
10
+ *
11
+ * `isDrainedFlake` / `isConstrainedFlake` are recursive (they reach descendant columns), so this checks the
12
+ * node's own slots directly: a deeper aliased or constrained column is what the crossing navigates toward.
13
+ */
14
+ export declare function crossing(flake: Flake): boolean;
15
+ export declare function references(branch: Branch): boolean;
16
+ //# sourceMappingURL=_flake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_flake.d.ts","sourceRoot":"","sources":["../../src/_/_flake.ts"],"names":[],"mappings":"AAiBA,OAAO,EACN,KAAK,MAAM,EACX,KAAK,KAAK,EAOV,MAAM,sBAAsB,CAAC;AAG9B,wBAAgB,WAAW,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAIlD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAIhD;AAeD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAqB9C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAIlD"}
@@ -0,0 +1,65 @@
1
+ /*
2
+ * Copyright © 2025-2026 Metreeca srl
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { getShapeProperties } from "@metreeca/blue/resource";
17
+ import { getFlakeEntries, getFlakeTransforms, isConstrainedFlake, isDrainedFlake, isPropertyBranch, isScalarFlake } from "@metreeca/keep-flake";
18
+ export function isXComputed(branch) {
19
+ return isDrainedFlake(branch)
20
+ || isConstrainedFlake(branch)
21
+ || bindsComputed(branch);
22
+ }
23
+ export function isXScalar(branch) {
24
+ return isDrainedFlake(branch)
25
+ || isConstrainedFlake(branch)
26
+ || bindsScalar(branch);
27
+ }
28
+ function bindsComputed(flake) {
29
+ return getFlakeTransforms(flake).length > 0 // every transform stage is computed (Flake.pipe non-empty)
30
+ || getFlakeEntries(flake).some(bindsComputed);
31
+ }
32
+ function bindsScalar(flake) {
33
+ return getFlakeTransforms(flake).some(isScalarFlake)
34
+ || getFlakeEntries(flake).some(bindsScalar);
35
+ }
36
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
37
+ /**
38
+ * Whether a union node is a crossing intermediate: a path step under a shared predicate (§5.8.1) whose own
39
+ * value is never surfaced (no projection alias, no direct constraint / ordering / focus) and all of whose
40
+ * branches every variant declares. Such a node is traversed like a resource — its shared branches descended
41
+ * once off the anchor — rather than fanned into a membership-gated arm per variant. A surfaced union (a
42
+ * projected or constrained column) or one carrying a variant-specific branch keeps the arm machinery.
43
+ *
44
+ * `isDrainedFlake` / `isConstrainedFlake` are recursive (they reach descendant columns), so this checks the
45
+ * node's own slots directly: a deeper aliased or constrained column is what the crossing navigates toward.
46
+ */
47
+ export function crossing(flake) {
48
+ return flake.drain?.alias === undefined && !surfaced(flake) && shared(flake);
49
+ function surfaced(flake) {
50
+ return [flake.lt, flake.gt, flake.lte, flake.gte, flake.like, flake.any, flake.all, flake.order, flake.focus]
51
+ .some(slot => slot !== undefined);
52
+ }
53
+ function shared(flake) {
54
+ // a childless union is a leaf column whose own references are retrieved (a bare `media` placeholder),
55
+ // not an intermediate step; it keeps the arm machinery to bind its variant shards
56
+ const branches = getFlakeEntries(flake);
57
+ return branches.length > 0 && branches.every(branch => flake.range.variants.every(variant => getShapeProperties(variant)[branch.path[branch.path.length - 1]] !== undefined));
58
+ }
59
+ }
60
+ export function references(branch) {
61
+ return branch.entry.kind === "id" || branch.entry.kind === "type" ? true
62
+ : isPropertyBranch(branch) ? branch.entry.range.shape.kind === "reference"
63
+ : false;
64
+ }
65
+ //# sourceMappingURL=_flake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_flake.js","sourceRoot":"","sources":["../../src/_/_flake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAGN,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,MAAM,sBAAsB,CAAC;AAG9B,MAAM,UAAU,WAAW,CAAC,MAAa;IACxC,OAAO,cAAc,CAAC,MAAM,CAAC;WACzB,kBAAkB,CAAC,MAAM,CAAC;WAC1B,aAAa,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAa;IACtC,OAAO,cAAc,CAAC,MAAM,CAAC;WACzB,kBAAkB,CAAC,MAAM,CAAC;WAC1B,WAAW,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,aAAa,CAAC,KAAY;IAClC,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,2DAA2D;WACnG,eAAe,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,WAAW,CAAC,KAAY;IAChC,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;WAChD,eAAe,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC9C,CAAC;AAGD,wHAAwH;AAExH;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAY;IAEpC,OAAO,KAAK,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAE7E,SAAS,QAAQ,CAAC,KAAY;QAC7B,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;aAC3G,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IACpC,CAAC;IAED,SAAS,MAAM,CAAC,KAAY;QAE3B,sGAAsG;QACtG,kFAAkF;QAElF,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QAExC,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAC3F,kBAAkB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAC5E,CAAC,CAAC;IACJ,CAAC;AAEF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACxC,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI;QACvE,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW;YACzE,CAAC,CAAC,KAAK,CAAC;AACX,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { ValuesShape } from "@metreeca/blue/value";
2
+ /**
3
+ * Resolves which union variants a retrieval placeholder requests, and the sub-placeholder that projects
4
+ * each (union.md §Model).
5
+ *
6
+ * The model addresses a union in one of two forms:
7
+ *
8
+ * - a **keyed** placeholder (an object whose keys are opaque `${number}` strings) supplies one
9
+ * alternative per entry. Each alternative is matched independently against the variants by kind and
10
+ * structure ({@link @metreeca/blue/union!getModelVariants | getModelVariants}), so one alternative may
11
+ * reach several variants and one variant may be reached by several alternatives.
12
+ * - any **other** placeholder is itself a single alternative, reaching every variant its kind fits.
13
+ *
14
+ * The result pairs each requested variant with the alternative that projects it. When several
15
+ * alternatives reach the same variant, the last in key order wins (plain `Map` semantics); the choice is
16
+ * immaterial, since same-variant alternatives project it identically. The map is the single source of
17
+ * truth shared by the encoder, which emits one arm per requested variant, and the decoder, which reads
18
+ * each requested variant's column and projects it through the paired alternative.
19
+ *
20
+ * @param variants The union variants to resolve against
21
+ * @param placeholder The union property's retrieval placeholder: a keyed alternative map, or a single
22
+ * alternative
23
+ *
24
+ * @returns Each requested variant mapped to its projecting alternative; empty when the placeholder
25
+ * requests no variant
26
+ */
27
+ export declare function getUnionPlaceholders<V extends ValuesShape>(variants: readonly V[], placeholder: unknown): ReadonlyMap<V, unknown>;
28
+ //# sourceMappingURL=_union.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_union.d.ts","sourceRoot":"","sources":["../../src/_/_union.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,WAAW,EACzD,QAAQ,EAAE,SAAS,CAAC,EAAE,EACtB,WAAW,EAAE,OAAO,GAClB,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,CAUzB"}
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright © 2025-2026 Metreeca srl
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { getModelVariants } from "@metreeca/blue/union";
17
+ import { isObject } from "@metreeca/core";
18
+ import { isBranch } from "@metreeca/qest/template";
19
+ /**
20
+ * Resolves which union variants a retrieval placeholder requests, and the sub-placeholder that projects
21
+ * each (union.md §Model).
22
+ *
23
+ * The model addresses a union in one of two forms:
24
+ *
25
+ * - a **keyed** placeholder (an object whose keys are opaque `${number}` strings) supplies one
26
+ * alternative per entry. Each alternative is matched independently against the variants by kind and
27
+ * structure ({@link @metreeca/blue/union!getModelVariants | getModelVariants}), so one alternative may
28
+ * reach several variants and one variant may be reached by several alternatives.
29
+ * - any **other** placeholder is itself a single alternative, reaching every variant its kind fits.
30
+ *
31
+ * The result pairs each requested variant with the alternative that projects it. When several
32
+ * alternatives reach the same variant, the last in key order wins (plain `Map` semantics); the choice is
33
+ * immaterial, since same-variant alternatives project it identically. The map is the single source of
34
+ * truth shared by the encoder, which emits one arm per requested variant, and the decoder, which reads
35
+ * each requested variant's column and projects it through the paired alternative.
36
+ *
37
+ * @param variants The union variants to resolve against
38
+ * @param placeholder The union property's retrieval placeholder: a keyed alternative map, or a single
39
+ * alternative
40
+ *
41
+ * @returns Each requested variant mapped to its projecting alternative; empty when the placeholder
42
+ * requests no variant
43
+ */
44
+ export function getUnionPlaceholders(variants, placeholder) {
45
+ const placeholders = isObject(placeholder, (_, key) => isBranch(key)) && Object.keys(placeholder).length > 0
46
+ ? Object.values(placeholder)
47
+ : [placeholder];
48
+ return new Map(placeholders.flatMap(alternative => (getModelVariants(alternative, variants) ?? []).map(variant => [variant, alternative])));
49
+ }
50
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
51
+ //# sourceMappingURL=_union.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_union.js","sourceRoot":"","sources":["../../src/_/_union.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,oBAAoB,CACnC,QAAsB,EACtB,WAAoB;IAGpB,MAAM,YAAY,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC;QAC3G,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;QAC5B,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAEjB,OAAO,IAAI,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CACjD,CAAC,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CACtF,CAAC,CAAC;AAEJ,CAAC;AAGD,wHAAwH"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Detect-pass result decoder.
3
+ *
4
+ * Settles each queued {@link Detect} against the existence query's solution tuples: the entries
5
+ * that came back are the candidates that are the subject of at least one stored triple. The subject
6
+ * token is recovered from the shared {@link Scope} threaded from the driver, so it matches the
7
+ * encoder's projection, and every request's deferred resolves to whether its entry is present.
8
+ *
9
+ * @module
10
+ */
11
+ import type { Scope } from "@metreeca/core/scope";
12
+ import type { Deferred, Detect } from "@metreeca/keep/batching";
13
+ import type { Tuple, Variable } from "@metreeca/wire-sparql";
14
+ /**
15
+ * Resolves a batch of detect requests against the existence query's present entries.
16
+ *
17
+ * Recovers the entries bound to the subject variable, folds them into a membership set, and settles
18
+ * each request's deferred to whether its `entry` is present.
19
+ *
20
+ * @param scope The shared variable scope, also threaded into the encoder
21
+ * @param batch The queued detect requests to settle, each carrying the `request` whose `entry` is tested
22
+ * @param tuples The solution tuples returned by the batched existence `select`
23
+ */
24
+ export declare function decode(scope: Scope<Variable>, batch: readonly Deferred<Detect>[], tuples: readonly Tuple[]): void;
25
+ //# sourceMappingURL=decode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../../src/detect/decode.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAG7D;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CACrB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,EACtB,KAAK,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,EAAE,EAClC,MAAM,EAAE,SAAS,KAAK,EAAE,GACtB,IAAI,CAaN"}
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright © 2025-2026 Metreeca srl
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Resolves a batch of detect requests against the existence query's present entries.
18
+ *
19
+ * Recovers the entries bound to the subject variable, folds them into a membership set, and settles
20
+ * each request's deferred to whether its `entry` is present.
21
+ *
22
+ * @param scope The shared variable scope, also threaded into the encoder
23
+ * @param batch The queued detect requests to settle, each carrying the `request` whose `entry` is tested
24
+ * @param tuples The solution tuples returned by the batched existence `select`
25
+ */
26
+ export function decode(scope, batch, tuples) {
27
+ const subject = scope.resolve(batch);
28
+ const entries = new Set(tuples.flatMap(tuple => {
29
+ const term = tuple[subject];
30
+ return term?.kind === "named" ? [term.iri] : [];
31
+ }));
32
+ batch.forEach(({ request, resolve }) => resolve(entries.has(request.entry)));
33
+ }
34
+ //# sourceMappingURL=decode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decode.js","sourceRoot":"","sources":["../../src/detect/decode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAkBH;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CACrB,KAAsB,EACtB,KAAkC,EAClC,MAAwB;IAGxB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAErC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,OAAO,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CACtC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACnC,CAAC;AAEH,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Detect-pass query encoder.
3
+ *
4
+ * Folds a batch of candidate entries into one batched `select`: a `values` block binds every
5
+ * entry to the subject variable, a single triple pattern joins it as a subject, and `distinct`
6
+ * collapses the multiple triples each present entry produces. The projection returns exactly the
7
+ * entries that are the subject of at least one stored triple. Variables are allocated through the
8
+ * shared {@link Scope} threaded from the driver, so the decoder recovers the same subject token.
9
+ *
10
+ * @module
11
+ */
12
+ import type { Scope } from "@metreeca/core/scope";
13
+ import type { Deferred, Detect } from "@metreeca/keep/batching";
14
+ import type { SPARQL, Variable } from "@metreeca/wire-sparql";
15
+ /**
16
+ * Encodes the batched existence query for a set of candidate entries.
17
+ *
18
+ * @param scope The shared variable scope, also threaded into the decoder
19
+ * @param batch The candidate items to probe, each carrying the `request` whose `entry` is tested for existence
20
+ *
21
+ * @returns A `select distinct` query projecting the subject variable for every entry that is the
22
+ * subject of at least one stored triple; an entry with no triples yields no row
23
+ */
24
+ export declare function encode(scope: Scope<Variable>, batch: readonly Deferred<Detect>[]): SPARQL;
25
+ //# sourceMappingURL=encode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/detect/encode.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAI9D;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CACrB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,EACtB,KAAK,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,EAAE,GAChC,MAAM,CAeR"}
@@ -0,0 +1,30 @@
1
+ /*
2
+ * Copyright © 2025-2026 Metreeca srl
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { distinct, edge, reference, select, values, variable, where } from "@metreeca/wire-sparql/builder";
17
+ /**
18
+ * Encodes the batched existence query for a set of candidate entries.
19
+ *
20
+ * @param scope The shared variable scope, also threaded into the decoder
21
+ * @param batch The candidate items to probe, each carrying the `request` whose `entry` is tested for existence
22
+ *
23
+ * @returns A `select distinct` query projecting the subject variable for every entry that is the
24
+ * subject of at least one stored triple; an entry with no triples yields no row
25
+ */
26
+ export function encode(scope, batch) {
27
+ const subject = scope.resolve(batch);
28
+ return select(distinct(variable(subject)), where(values([variable(subject)], batch.map(({ request }) => [reference(request.entry)])), edge(variable(subject), variable(scope.resolve()), variable(scope.resolve()))));
29
+ }
30
+ //# sourceMappingURL=encode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encode.js","sourceRoot":"","sources":["../../src/detect/encode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiBH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAG3G;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM,CACrB,KAAsB,EACtB,KAAkC;IAGlC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAErC,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAC/C,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CACrD,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAC1B,CAAC,EACF,IAAI,CACH,QAAQ,CAAC,OAAO,CAAC,EACjB,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EACzB,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CACzB,CACD,CAAC,CAAC;AAEJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Deferred, Detect } from "@metreeca/keep/batching";
2
+ import { type RepositoryClient } from "@metreeca/wire-sparql";
3
+ /**
4
+ * Per-batch body for the detect handler.
5
+ *
6
+ * Two phases run in sequence: **fetch** by folding the batch's candidate entries into one batched
7
+ * existence `select` through {@link encode} and running it; **deliver** by {@link decode | decoding}
8
+ * the solution to the present entries, folding them into a set, and resolving each request to whether
9
+ * its entry is present.
10
+ *
11
+ * @param batch The queued detect requests to resolve
12
+ * @param client The repository the existence query runs against
13
+ *
14
+ * @returns A promise settling once every request in the batch has been resolved
15
+ */
16
+ export declare function detect(batch: readonly Deferred<Detect>[], client: RepositoryClient): Promise<void>;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/detect/index.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,KAAK,gBAAgB,EAAY,MAAM,uBAAuB,CAAC;AAKxE;;;;;;;;;;;;GAYG;AACH,wBAAsB,MAAM,CAC3B,KAAK,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,EAAE,EAClC,MAAM,EAAE,gBAAgB,GACtB,OAAO,CAAC,IAAI,CAAC,CAQf"}