@routier/core 0.7.0 → 0.8.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.
- package/dist/codegen/blocks.d.ts +17 -1
- package/dist/codegen/handlers/types.d.ts +13 -5
- package/dist/codegen/index.cjs +28 -0
- package/dist/codegen/index.cjs.map +1 -1
- package/dist/codegen/index.js +28 -0
- package/dist/codegen/index.js.map +1 -1
- package/dist/collections/MemoryDataCollection.d.ts +2 -4
- package/dist/collections/index.cjs +127 -15
- package/dist/collections/index.cjs.map +1 -1
- package/dist/collections/index.js +127 -15
- package/dist/collections/index.js.map +1 -1
- package/dist/expressions/index.cjs +226 -4
- package/dist/expressions/index.cjs.map +1 -1
- package/dist/expressions/index.js +228 -5
- package/dist/expressions/index.js.map +1 -1
- package/dist/expressions/parser.d.ts +42 -1
- package/dist/index.cjs +753 -345
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +756 -345
- package/dist/index.js.map +1 -1
- package/dist/plugins/EphemeralDataPlugin.d.ts +8 -0
- package/dist/plugins/index.cjs +566 -49
- package/dist/plugins/index.cjs.map +1 -1
- package/dist/plugins/index.js +566 -48
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/query/QueryOptionsCollection.d.ts +15 -5
- package/dist/plugins/query/index.d.ts +1 -0
- package/dist/plugins/query/renames.d.ts +27 -0
- package/dist/plugins/query/types.d.ts +15 -1
- package/dist/plugins/translators/SqlTranslator.d.ts +15 -0
- package/dist/schema/SchemaDefinition.d.ts +8 -0
- package/dist/schema/changeTracker.d.ts +10 -0
- package/dist/schema/index.cjs +291 -274
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +294 -276
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/types.d.ts +8 -7
- package/dist/schema/utils/storageDates.d.ts +25 -0
- package/dist/transfer/index.cjs.map +1 -1
- package/dist/transfer/index.js.map +1 -1
- package/dist/utilities/index.cjs +74 -29
- package/dist/utilities/index.cjs.map +1 -1
- package/dist/utilities/index.js +74 -29
- package/dist/utilities/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/codegen/utils.d.ts +0 -22
package/dist/plugins/index.js
CHANGED
|
@@ -1019,6 +1019,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
1019
1019
|
|
|
1020
1020
|
|
|
1021
1021
|
|
|
1022
|
+
|
|
1022
1023
|
// Error message constants
|
|
1023
1024
|
const ERROR_MESSAGES = {
|
|
1024
1025
|
PROPERTY_NOT_FOUND: (path)=>`Error parsing query, could not find PropertyInfo for path: ${path}`,
|
|
@@ -1626,6 +1627,9 @@ const COALESCE_OPERATORS = sourceKeyed({
|
|
|
1626
1627
|
// A comparison always names a schema property, so the condition alone settles it
|
|
1627
1628
|
return true;
|
|
1628
1629
|
}
|
|
1630
|
+
if (operand.kind === "opaque") {
|
|
1631
|
+
return operand.reads.some(containsProperty);
|
|
1632
|
+
}
|
|
1629
1633
|
return operand.kind === "arithmetic" && (containsProperty(operand.left) || containsProperty(operand.right) || operand.extra != null && containsProperty(operand.extra));
|
|
1630
1634
|
};
|
|
1631
1635
|
const DECLARATION_KEYWORDS = new Set([
|
|
@@ -1803,13 +1807,18 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1803
1807
|
scope;
|
|
1804
1808
|
paramsName;
|
|
1805
1809
|
params;
|
|
1810
|
+
/**
|
|
1811
|
+
* Whether this parses a value selector rather than a filter, and so reads a call it has no node for
|
|
1812
|
+
* as an `OpaqueOperand` instead of refusing it.
|
|
1813
|
+
*/ readsValues;
|
|
1806
1814
|
/** Set when a param value shaped the tree itself (e.g. x[p.name]) — such templates cannot be cached. */ structurallyDependsOnParams = false;
|
|
1807
|
-
constructor(schema, stream, scope, paramsName, params){
|
|
1815
|
+
constructor(schema, stream, scope, paramsName, params, readsValues = false){
|
|
1808
1816
|
this.schema = schema;
|
|
1809
1817
|
this.stream = stream;
|
|
1810
1818
|
this.scope = scope;
|
|
1811
1819
|
this.paramsName = paramsName;
|
|
1812
1820
|
this.params = params;
|
|
1821
|
+
this.readsValues = readsValues;
|
|
1813
1822
|
}
|
|
1814
1823
|
parse() {
|
|
1815
1824
|
const expression = this.parseOr();
|
|
@@ -1831,6 +1840,71 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1831
1840
|
}
|
|
1832
1841
|
return answer;
|
|
1833
1842
|
}
|
|
1843
|
+
/**
|
|
1844
|
+
* What a value selector returns: one value, or the fields of an object literal.
|
|
1845
|
+
*
|
|
1846
|
+
* A block body is read only when it does nothing but return, which is what a transpiler makes of an
|
|
1847
|
+
* arrow function. Anything more is refused, and the caller falls back to running the function.
|
|
1848
|
+
*/ parseSelector() {
|
|
1849
|
+
const block = this.stream.matchPunctuation("{");
|
|
1850
|
+
if (block) {
|
|
1851
|
+
const keyword = this.stream.next();
|
|
1852
|
+
if (keyword.kind !== "identifier" || keyword.value !== "return") {
|
|
1853
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a selector block that does more than return"));
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
const selected = this.stream.isPunctuation("{") ? this.parseObjectLiteral() : this.stream.isPunctuation("(") && this.stream.isPunctuation("{", 1) ? this.parseBracketedObjectLiteral() : this.parseInterpolation();
|
|
1857
|
+
if (block) {
|
|
1858
|
+
this.stream.matchPunctuation(";");
|
|
1859
|
+
this.stream.expectPunctuation("}");
|
|
1860
|
+
}
|
|
1861
|
+
if (!this.stream.isAtEnd) {
|
|
1862
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`unexpected token '${this.stream.peek()?.value}'`));
|
|
1863
|
+
}
|
|
1864
|
+
return selected;
|
|
1865
|
+
}
|
|
1866
|
+
/** `({ … })`, the arrow body that returns an object. */ parseBracketedObjectLiteral() {
|
|
1867
|
+
this.stream.expectPunctuation("(");
|
|
1868
|
+
const fields = this.parseObjectLiteral();
|
|
1869
|
+
this.stream.expectPunctuation(")");
|
|
1870
|
+
return fields;
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* The fields of an object literal, each one value.
|
|
1874
|
+
*
|
|
1875
|
+
* A field's value is read without a top-level conditional, which needs brackets here: the look-ahead
|
|
1876
|
+
* for a `?` does not stop at the comma that ends the field.
|
|
1877
|
+
*/ parseObjectLiteral() {
|
|
1878
|
+
this.stream.expectPunctuation("{");
|
|
1879
|
+
const fields = [];
|
|
1880
|
+
while(!this.stream.matchPunctuation("}")){
|
|
1881
|
+
const key = this.stream.next();
|
|
1882
|
+
if (key.kind !== "identifier" && key.kind !== "string") {
|
|
1883
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`the object key '${key.value}'`));
|
|
1884
|
+
}
|
|
1885
|
+
fields.push({
|
|
1886
|
+
name: key.value,
|
|
1887
|
+
operand: this.stream.matchPunctuation(":") ? this.parseValue() : this.parseShorthand(key)
|
|
1888
|
+
});
|
|
1889
|
+
if (!this.stream.matchPunctuation(",")) {
|
|
1890
|
+
this.stream.expectPunctuation("}");
|
|
1891
|
+
break;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
return fields;
|
|
1895
|
+
}
|
|
1896
|
+
/** `{ name }`, which reads what the parameter list bound `name` to. */ parseShorthand(key) {
|
|
1897
|
+
const binding = key.kind === "identifier" ? this.scope.get(key.value) : undefined;
|
|
1898
|
+
if (binding == null || binding.kind === "inlined") {
|
|
1899
|
+
throw new Error(ERROR_MESSAGES.VARIABLE_VALUE(key.value));
|
|
1900
|
+
}
|
|
1901
|
+
return this.parseChain({
|
|
1902
|
+
kind: binding.kind,
|
|
1903
|
+
path: [
|
|
1904
|
+
...binding.path
|
|
1905
|
+
]
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1834
1908
|
/** The expression a `{ … }` block answers with. */ parseBlock() {
|
|
1835
1909
|
this.stream.expectPunctuation("{");
|
|
1836
1910
|
const answer = this.parseStatements();
|
|
@@ -2094,7 +2168,7 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
2094
2168
|
* A structural dependence found inside propagates outward: the template it belongs to cannot be
|
|
2095
2169
|
* cached either.
|
|
2096
2170
|
*/ parseNested(source) {
|
|
2097
|
-
const nested = new ExpressionParser(this.schema, new TokenStream(tokenize(source)), this.scope, this.paramsName, this.params);
|
|
2171
|
+
const nested = new ExpressionParser(this.schema, new TokenStream(tokenize(source)), this.scope, this.paramsName, this.params, this.readsValues);
|
|
2098
2172
|
const operand = nested.parseInterpolation();
|
|
2099
2173
|
// Leftover tokens mean the interpolation held something this reads only part of. Silently
|
|
2100
2174
|
// keeping the part it understood is the worst outcome available: `${x.age > 5 ? "a" : "b"}`
|
|
@@ -2386,7 +2460,7 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
2386
2460
|
if (argument.kind === "method-call") {
|
|
2387
2461
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("nested method call inside .includes()"));
|
|
2388
2462
|
}
|
|
2389
|
-
if (argument.kind === "arithmetic" || argument.kind === "conditional") {
|
|
2463
|
+
if (argument.kind === "arithmetic" || argument.kind === "conditional" || argument.kind === "opaque") {
|
|
2390
2464
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("arithmetic inside .includes()"));
|
|
2391
2465
|
}
|
|
2392
2466
|
return {
|
|
@@ -2480,7 +2554,7 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
2480
2554
|
if (argument.kind === "method-call") {
|
|
2481
2555
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`nested method call inside .${method}()`));
|
|
2482
2556
|
}
|
|
2483
|
-
if (argument.kind === "arithmetic" || argument.kind === "conditional") {
|
|
2557
|
+
if (argument.kind === "arithmetic" || argument.kind === "conditional" || argument.kind === "opaque") {
|
|
2484
2558
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`arithmetic inside .${method}()`));
|
|
2485
2559
|
}
|
|
2486
2560
|
return {
|
|
@@ -2490,9 +2564,20 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
2490
2564
|
argument
|
|
2491
2565
|
};
|
|
2492
2566
|
}
|
|
2567
|
+
if (this.readsValues) {
|
|
2568
|
+
return this.withGroupCall(this.opaqueCall(this.resolveChain(options.kind, path, transformer, locale)));
|
|
2569
|
+
}
|
|
2493
2570
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`method '.${method}()'`));
|
|
2494
2571
|
}
|
|
2495
2572
|
if (transformer != null) {
|
|
2573
|
+
if (this.readsValues) {
|
|
2574
|
+
return this.withGroupCall({
|
|
2575
|
+
kind: "opaque",
|
|
2576
|
+
reads: [
|
|
2577
|
+
this.resolveChain(options.kind, path, transformer, locale)
|
|
2578
|
+
]
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2496
2581
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("property access after a transform method"));
|
|
2497
2582
|
}
|
|
2498
2583
|
path.push(segment.value);
|
|
@@ -2637,10 +2722,39 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
2637
2722
|
argument
|
|
2638
2723
|
};
|
|
2639
2724
|
}
|
|
2725
|
+
// Any other member or call of a value, which a selector reads through
|
|
2726
|
+
if (this.readsValues) {
|
|
2727
|
+
this.stream.next();
|
|
2728
|
+
this.stream.next();
|
|
2729
|
+
receiver = this.stream.isPunctuation("(") ? this.opaqueCall(receiver) : {
|
|
2730
|
+
kind: "opaque",
|
|
2731
|
+
reads: [
|
|
2732
|
+
receiver
|
|
2733
|
+
]
|
|
2734
|
+
};
|
|
2735
|
+
continue;
|
|
2736
|
+
}
|
|
2640
2737
|
break;
|
|
2641
2738
|
}
|
|
2642
2739
|
return receiver;
|
|
2643
2740
|
}
|
|
2741
|
+
/** A call with no node of its own, from its `(`, kept for what its receiver and arguments read. */ opaqueCall(receiver) {
|
|
2742
|
+
const reads = [
|
|
2743
|
+
receiver
|
|
2744
|
+
];
|
|
2745
|
+
this.stream.expectPunctuation("(");
|
|
2746
|
+
while(!this.stream.matchPunctuation(")")){
|
|
2747
|
+
reads.push(this.parseValue());
|
|
2748
|
+
if (!this.stream.matchPunctuation(",")) {
|
|
2749
|
+
this.stream.expectPunctuation(")");
|
|
2750
|
+
break;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
return {
|
|
2754
|
+
kind: "opaque",
|
|
2755
|
+
reads
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2644
2758
|
withValueTransformer(operand) {
|
|
2645
2759
|
if (this.stream.isPunctuation(".")) {
|
|
2646
2760
|
const method = this.stream.peek(1);
|
|
@@ -2674,6 +2788,10 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
2674
2788
|
if (right.kind === "method-call") {
|
|
2675
2789
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("method call on the right side of a comparison"));
|
|
2676
2790
|
}
|
|
2791
|
+
// A comparison is a tree a backend renders, and this operand has no node in one
|
|
2792
|
+
if (left.kind === "opaque" || right.kind === "opaque") {
|
|
2793
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a call with no expression form inside a comparison"));
|
|
2794
|
+
}
|
|
2677
2795
|
if (needsBrackets(left) || needsBrackets(right)) {
|
|
2678
2796
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a bitwise or nullish operator compared without brackets, which JavaScript reads the other way round"));
|
|
2679
2797
|
}
|
|
@@ -2887,6 +3005,9 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
2887
3005
|
if (operand.kind === "method-call") {
|
|
2888
3006
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a method call inside arithmetic"));
|
|
2889
3007
|
}
|
|
3008
|
+
if (operand.kind === "opaque") {
|
|
3009
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a call with no expression form inside arithmetic"));
|
|
3010
|
+
}
|
|
2890
3011
|
return this.createValueExpression(operand, null, /* applyConverter */ false);
|
|
2891
3012
|
}
|
|
2892
3013
|
createPropertyExpression(operand) {
|
|
@@ -3222,6 +3343,104 @@ const toExpression = (schema, fn, params)=>{
|
|
|
3222
3343
|
return _types__rspack_import_1/* .Expression.notParsable */.r4.notParsable(refusalOf(error));
|
|
3223
3344
|
}
|
|
3224
3345
|
};
|
|
3346
|
+
const collectReads = (operand, into)=>{
|
|
3347
|
+
switch(operand.kind){
|
|
3348
|
+
case "property":
|
|
3349
|
+
into.add(operand.property);
|
|
3350
|
+
return;
|
|
3351
|
+
case "method-call":
|
|
3352
|
+
collectReads(operand.target, into);
|
|
3353
|
+
collectReads(operand.argument, into);
|
|
3354
|
+
return;
|
|
3355
|
+
case "arithmetic":
|
|
3356
|
+
collectReads(operand.left, into);
|
|
3357
|
+
collectReads(operand.right, into);
|
|
3358
|
+
if (operand.extra != null) {
|
|
3359
|
+
collectReads(operand.extra, into);
|
|
3360
|
+
}
|
|
3361
|
+
return;
|
|
3362
|
+
case "conditional":
|
|
3363
|
+
for (const property of getProperties(operand.condition)){
|
|
3364
|
+
into.add(property);
|
|
3365
|
+
}
|
|
3366
|
+
collectReads(operand.whenTrue, into);
|
|
3367
|
+
collectReads(operand.whenFalse, into);
|
|
3368
|
+
return;
|
|
3369
|
+
case "opaque":
|
|
3370
|
+
for (const read of operand.reads){
|
|
3371
|
+
collectReads(read, into);
|
|
3372
|
+
}
|
|
3373
|
+
return;
|
|
3374
|
+
}
|
|
3375
|
+
};
|
|
3376
|
+
const selectedValue = (operand)=>{
|
|
3377
|
+
const found = new Set();
|
|
3378
|
+
collectReads(operand, found);
|
|
3379
|
+
const reads = [
|
|
3380
|
+
...found
|
|
3381
|
+
];
|
|
3382
|
+
return {
|
|
3383
|
+
property: reads.length === 1 ? reads[0] : null,
|
|
3384
|
+
reads,
|
|
3385
|
+
isDirectProperty: operand.kind === "property" && operand.transformer == null
|
|
3386
|
+
};
|
|
3387
|
+
};
|
|
3388
|
+
// Keyed like the template cache. A selector takes no params, so every result is cacheable
|
|
3389
|
+
const selectorCache = new WeakMap();
|
|
3390
|
+
/**
|
|
3391
|
+
* Reads a sort, map, group or `nearest` selector with the grammar filters use, for what its value is
|
|
3392
|
+
* read from.
|
|
3393
|
+
*
|
|
3394
|
+
* Not for evaluating it: the caller's function stays the value, and nothing here renders one. A plugin
|
|
3395
|
+
* decides from the result whether it can run the option. One that orders or projects by column cannot
|
|
3396
|
+
* run a value that is not the property itself, and one that runs the function over stored rows cannot
|
|
3397
|
+
* run it over a renamed property.
|
|
3398
|
+
*
|
|
3399
|
+
* So the grammar is wider here than for a filter. A call it has no node for, such as `getFullYear()`,
|
|
3400
|
+
* is kept for the operands it reads rather than refused, since the function it came from still runs.
|
|
3401
|
+
*
|
|
3402
|
+
* `not-parsable` is not logged. The option runs as it did before the selector was parsed.
|
|
3403
|
+
*
|
|
3404
|
+
* Cached by function source per schema, like `toExpression`. The result is shared, so it is read-only.
|
|
3405
|
+
*/ const parseSelector = (schema, selector)=>{
|
|
3406
|
+
const source = selector.toString();
|
|
3407
|
+
let bySource = selectorCache.get(schema);
|
|
3408
|
+
const cached = bySource?.get(source);
|
|
3409
|
+
if (cached != null) {
|
|
3410
|
+
return cached;
|
|
3411
|
+
}
|
|
3412
|
+
let parsed;
|
|
3413
|
+
try {
|
|
3414
|
+
const shape = resolveFunctionShape(source, false);
|
|
3415
|
+
const parser = new ExpressionParser(schema, new TokenStream(tokenize(shape.body)), shape.scope, null, undefined, true);
|
|
3416
|
+
const selected = parser.parseSelector();
|
|
3417
|
+
parsed = Array.isArray(selected) ? {
|
|
3418
|
+
kind: "object",
|
|
3419
|
+
fields: selected.map((field)=>({
|
|
3420
|
+
name: field.name,
|
|
3421
|
+
...selectedValue(field.operand)
|
|
3422
|
+
}))
|
|
3423
|
+
} : {
|
|
3424
|
+
kind: "value",
|
|
3425
|
+
value: selectedValue(selected)
|
|
3426
|
+
};
|
|
3427
|
+
} catch (error) {
|
|
3428
|
+
parsed = {
|
|
3429
|
+
kind: "not-parsable",
|
|
3430
|
+
reason: refusalOf(error)
|
|
3431
|
+
};
|
|
3432
|
+
}
|
|
3433
|
+
if (bySource == null) {
|
|
3434
|
+
bySource = new Map();
|
|
3435
|
+
selectorCache.set(schema, bySource);
|
|
3436
|
+
}
|
|
3437
|
+
// Stryker disable next-line all: the same resource bound as the template cache's
|
|
3438
|
+
if (bySource.size >= MAX_CACHED_TEMPLATES_PER_SCHEMA) {
|
|
3439
|
+
bySource.clear();
|
|
3440
|
+
}
|
|
3441
|
+
bySource.set(source, parsed);
|
|
3442
|
+
return parsed;
|
|
3443
|
+
}; // #endregion
|
|
3225
3444
|
|
|
3226
3445
|
|
|
3227
3446
|
},
|
|
@@ -3929,6 +4148,30 @@ const mismatchWarning = (expression)=>{
|
|
|
3929
4148
|
const outcome = expression.negated ? "every row matches" : "no row matches";
|
|
3930
4149
|
return `Routier: '${side.property.property.getAssignmentPath()}' is a ${side.expected}, and this filter ` + `compares it against ${describeLiteral(side.value.value)}, which is a ${typeof side.value.value}. ` + `A strict comparison between them is the same answer for every row, so ${outcome} and the filter ` + `runs in memory. https://routier.dev/guides/strict-comparison-types`;
|
|
3931
4150
|
};
|
|
4151
|
+
/**
|
|
4152
|
+
* An item as one dispatch receives it: a new object, so a report written on it stays with that dispatch.
|
|
4153
|
+
*
|
|
4154
|
+
* A database option starts `executed` again, because a report is only an answer from the plugin that
|
|
4155
|
+
* made it. A memory option keeps the reason core planned it with. A join's inner options are copied the
|
|
4156
|
+
* same way, since a plugin can report on them too.
|
|
4157
|
+
*/ const toDispatchItem = (item)=>{
|
|
4158
|
+
const option = item.option;
|
|
4159
|
+
const value = option.name === "join" ? {
|
|
4160
|
+
...option.value,
|
|
4161
|
+
innerOptions: option.value.innerOptions.forDispatch()
|
|
4162
|
+
} : option.value;
|
|
4163
|
+
return {
|
|
4164
|
+
index: item.index,
|
|
4165
|
+
option: option.target === "database" ? {
|
|
4166
|
+
...option,
|
|
4167
|
+
value,
|
|
4168
|
+
reason: "executed"
|
|
4169
|
+
} : {
|
|
4170
|
+
...option,
|
|
4171
|
+
value
|
|
4172
|
+
}
|
|
4173
|
+
};
|
|
4174
|
+
};
|
|
3932
4175
|
class QueryOptionsCollection {
|
|
3933
4176
|
options = new Map();
|
|
3934
4177
|
nextExecutionTarget = "database";
|
|
@@ -3967,7 +4210,7 @@ class QueryOptionsCollection {
|
|
|
3967
4210
|
}
|
|
3968
4211
|
}
|
|
3969
4212
|
if (name === "filter") {
|
|
3970
|
-
// Need to check for unmapped
|
|
4213
|
+
// Need to check for unmapped properties
|
|
3971
4214
|
const filterValue = value;
|
|
3972
4215
|
// A tautology (`x => true`) filters nothing — skip it entirely so
|
|
3973
4216
|
// plugins never see it
|
|
@@ -3984,14 +4227,10 @@ class QueryOptionsCollection {
|
|
|
3984
4227
|
this.cutOverToMemory("unmapped-property");
|
|
3985
4228
|
return false;
|
|
3986
4229
|
}
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
// where the in-memory names exist
|
|
3992
|
-
this.cutOverToMemory("renamed-property");
|
|
3993
|
-
return false;
|
|
3994
|
-
}
|
|
4230
|
+
// A renamed property stays with the database. Whether the backend can read a
|
|
4231
|
+
// `from` name is the plugin's to know, not this collection's: the property
|
|
4232
|
+
// travels with the option, and a plugin that cannot resolve it reports it
|
|
4233
|
+
// back — see `reportRenamedProperties`
|
|
3995
4234
|
if (comparesTypesThatCannotMatch(expression)) {
|
|
3996
4235
|
_utilities__rspack_import_3/* .logger.warn */.vF.warn(mismatchWarning(expression));
|
|
3997
4236
|
this.cutOverToMemory("predicate-error");
|
|
@@ -4003,26 +4242,19 @@ class QueryOptionsCollection {
|
|
|
4003
4242
|
}
|
|
4004
4243
|
if (name === "sort") {
|
|
4005
4244
|
const sortValue = value;
|
|
4006
|
-
// Same rule as filters:
|
|
4007
|
-
//
|
|
4245
|
+
// Same rule as filters: an unmapped property only exists after deserialization. A
|
|
4246
|
+
// renamed one stays with the database, for the plugin to resolve or report
|
|
4008
4247
|
if (sortValue.property != null && sortValue.property.isUnmapped) {
|
|
4009
4248
|
this.cutOverToMemory("unmapped-property");
|
|
4010
|
-
} else if (sortValue.property != null && sortValue.property.hasRenamedSegments) {
|
|
4011
|
-
this.cutOverToMemory("renamed-property");
|
|
4012
4249
|
}
|
|
4013
4250
|
}
|
|
4014
4251
|
if (name === "nearest") {
|
|
4015
4252
|
const nearestValue = value;
|
|
4016
|
-
// Same rule as sort, and for the same reason:
|
|
4017
|
-
//
|
|
4018
|
-
//
|
|
4019
|
-
//
|
|
4020
|
-
// This is also what lets every translator's in-memory fallback read the column by
|
|
4021
|
-
// its resolved name — anything whose storage name differs never reaches them.
|
|
4253
|
+
// Same rule as sort, and for the same reason: an unmapped property is not stored at
|
|
4254
|
+
// all, so it is only readable after deserialization, which is where memory execution
|
|
4255
|
+
// runs. A vector stored under a `from` name is the plugin's to resolve or report.
|
|
4022
4256
|
if (nearestValue.property != null && nearestValue.property.isUnmapped) {
|
|
4023
4257
|
this.cutOverToMemory("unmapped-property");
|
|
4024
|
-
} else if (nearestValue.property != null && nearestValue.property.hasRenamedSegments) {
|
|
4025
|
-
this.cutOverToMemory("renamed-property");
|
|
4026
4258
|
}
|
|
4027
4259
|
}
|
|
4028
4260
|
if ((name === "filter" || name === "sort") && (this.options.has("skip") || this.options.has("take"))) {
|
|
@@ -4129,6 +4361,9 @@ class QueryOptionsCollection {
|
|
|
4129
4361
|
* the shared collection before executing. Without restoring, a re-executed terminal —
|
|
4130
4362
|
* the whole point of a subscribed queryable — stacks its option a second time and
|
|
4131
4363
|
* runs it over the first execution's scalar result.
|
|
4364
|
+
*
|
|
4365
|
+
* The item objects are shared with the snapshot. Nothing reports on them, because every
|
|
4366
|
+
* dispatch sends a `forDispatch` copy, so a restore brings back no reports.
|
|
4132
4367
|
*/ snapshot() {
|
|
4133
4368
|
const options = new Map([
|
|
4134
4369
|
...this.options.entries()
|
|
@@ -4206,19 +4441,48 @@ class QueryOptionsCollection {
|
|
|
4206
4441
|
}
|
|
4207
4442
|
}
|
|
4208
4443
|
/**
|
|
4209
|
-
*
|
|
4444
|
+
* A copy of the collection for one dispatch to a plugin, with nothing reported on it.
|
|
4210
4445
|
*
|
|
4211
4446
|
* Capability is answered per dispatch, so a report is only an answer for the execution that
|
|
4212
|
-
* produced it.
|
|
4213
|
-
*
|
|
4214
|
-
*
|
|
4215
|
-
|
|
4447
|
+
* produced it. Reports are written onto items, and the items of a queryable's collection
|
|
4448
|
+
* outlive any one execution: a snapshot shares them, and a subscription dispatches the same
|
|
4449
|
+
* query on every change. A report left on them replays options the plugin did run on the
|
|
4450
|
+
* next execution, such as a `skip` applied twice over rows already windowed, or hands a
|
|
4451
|
+
* renamed filter to memory that the engine could have run.
|
|
4452
|
+
*
|
|
4453
|
+
* Each item keeps its index, name, value and target. A half from `split`/`splitAt` is copied
|
|
4454
|
+
* with a copy of its origin, and its items are that copy's items, so a report on the half still
|
|
4455
|
+
* cascades over the whole dispatch without reaching the collection it was copied from.
|
|
4456
|
+
*/ forDispatch() {
|
|
4457
|
+
if (this.origin == null) {
|
|
4458
|
+
return this.copyForDispatch().copy;
|
|
4459
|
+
}
|
|
4460
|
+
const { copy: root, copies } = this.origin.copyForDispatch();
|
|
4461
|
+
const half = new QueryOptionsCollection();
|
|
4216
4462
|
this.resolveEnumeration();
|
|
4217
4463
|
for (const item of this.enumeratedItems){
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
}
|
|
4464
|
+
// An item added to the half after it was split has no counterpart in the origin
|
|
4465
|
+
half.adopt(copies.get(item) ?? toDispatchItem(item));
|
|
4221
4466
|
}
|
|
4467
|
+
half.origin = root;
|
|
4468
|
+
return half;
|
|
4469
|
+
}
|
|
4470
|
+
copyForDispatch() {
|
|
4471
|
+
const copy = new QueryOptionsCollection();
|
|
4472
|
+
const copies = new Map();
|
|
4473
|
+
this.resolveEnumeration();
|
|
4474
|
+
for (const item of this.enumeratedItems){
|
|
4475
|
+
const copied = toDispatchItem(item);
|
|
4476
|
+
copies.set(item, copied);
|
|
4477
|
+
copy.adopt(copied);
|
|
4478
|
+
}
|
|
4479
|
+
copy.nextExecutionTarget = this.nextExecutionTarget;
|
|
4480
|
+
copy.nextExecutionReason = this.nextExecutionReason;
|
|
4481
|
+
copy.nextIndex = this.nextIndex;
|
|
4482
|
+
return {
|
|
4483
|
+
copy,
|
|
4484
|
+
copies
|
|
4485
|
+
};
|
|
4222
4486
|
}
|
|
4223
4487
|
/** The options the database did not run, in the order they were written. */ notExecuted() {
|
|
4224
4488
|
this.resolveEnumeration();
|
|
@@ -4418,6 +4682,128 @@ var HashType = /*#__PURE__*/ (/* unused pure expression or super */ null && (fun
|
|
|
4418
4682
|
}({})));
|
|
4419
4683
|
|
|
4420
4684
|
|
|
4685
|
+
},
|
|
4686
|
+
575(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
4687
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
4688
|
+
l: () => (isArrayValued)
|
|
4689
|
+
});
|
|
4690
|
+
/* import */ var _types__rspack_import_0 = __webpack_require__(537);
|
|
4691
|
+
|
|
4692
|
+
/**
|
|
4693
|
+
* Types whose runtime value is a JS array.
|
|
4694
|
+
*
|
|
4695
|
+
* `Array` and `Vector` are the same thing to every layer that copies, compares, hashes or
|
|
4696
|
+
* freezes a value — a vector is a list of numbers and nothing more. They differ only where a
|
|
4697
|
+
* backend chooses storage, which is the one place that should ask for `SchemaTypes.Vector` by
|
|
4698
|
+
* name.
|
|
4699
|
+
*
|
|
4700
|
+
* This exists so adding a third array-shaped type is one edit rather than a hunt through
|
|
4701
|
+
* twelve handlers. Missing one of those is silent in the worst way: a vector that clones by
|
|
4702
|
+
* reference is shared with the change tracker's copy, so overwriting an embedding produces no
|
|
4703
|
+
* diff and the save reports nothing to do.
|
|
4704
|
+
*/ const ARRAY_VALUED_TYPES = new Set([
|
|
4705
|
+
_types__rspack_import_0/* .SchemaTypes.Array */.L.Array,
|
|
4706
|
+
_types__rspack_import_0/* .SchemaTypes.Vector */.L.Vector
|
|
4707
|
+
]);
|
|
4708
|
+
/** True when the property's value is a JS array and needs value rather than reference semantics. */ const isArrayValued = (type)=>ARRAY_VALUED_TYPES.has(type);
|
|
4709
|
+
/**
|
|
4710
|
+
* True when the property's elements are primitives, so a spread is a sufficient copy.
|
|
4711
|
+
*
|
|
4712
|
+
* A vector is always numbers, so it never needs the per-element deep copy an array of objects
|
|
4713
|
+
* or dates does.
|
|
4714
|
+
*/ const PRIMITIVE_ELEMENT_TYPES = new Set([
|
|
4715
|
+
_types__rspack_import_0/* .SchemaTypes.String */.L.String,
|
|
4716
|
+
_types__rspack_import_0/* .SchemaTypes.Number */.L.Number,
|
|
4717
|
+
_types__rspack_import_0/* .SchemaTypes.Boolean */.L.Boolean
|
|
4718
|
+
]);
|
|
4719
|
+
const hasPrimitiveElements = (type, elementType)=>type === SchemaTypes.Vector || PRIMITIVE_ELEMENT_TYPES.has(elementType);
|
|
4720
|
+
|
|
4721
|
+
|
|
4722
|
+
},
|
|
4723
|
+
894(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
4724
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
4725
|
+
T: () => (getStorageDateReviver)
|
|
4726
|
+
});
|
|
4727
|
+
/* import */ var _types__rspack_import_0 = __webpack_require__(537);
|
|
4728
|
+
/* import */ var _propertyKind__rspack_import_1 = __webpack_require__(575);
|
|
4729
|
+
|
|
4730
|
+
|
|
4731
|
+
const collectDatePaths = (properties, paths)=>{
|
|
4732
|
+
for (const property of properties){
|
|
4733
|
+
// The stored value belongs to whoever wrote it: a custom serializer, deserializer or
|
|
4734
|
+
// transform reads it back, and would be handed a Date it did not expect. Unmapped
|
|
4735
|
+
// properties are never stored.
|
|
4736
|
+
if (property.valueSerializer != null || property.valueDeserializer != null || property.transform != null || property.isUnmapped) {
|
|
4737
|
+
continue;
|
|
4738
|
+
}
|
|
4739
|
+
if (property.type === _types__rspack_import_0/* .SchemaTypes.Object */.L.Object) {
|
|
4740
|
+
collectDatePaths(property.children, paths);
|
|
4741
|
+
continue;
|
|
4742
|
+
}
|
|
4743
|
+
const isArray = (0,_propertyKind__rspack_import_1/* .isArrayValued */.l)(property.type) && property.innerSchema?.type === _types__rspack_import_0/* .SchemaTypes.Date */.L.Date;
|
|
4744
|
+
if (property.type !== _types__rspack_import_0/* .SchemaTypes.Date */.L.Date && isArray === false) {
|
|
4745
|
+
continue;
|
|
4746
|
+
}
|
|
4747
|
+
paths.push({
|
|
4748
|
+
segments: [
|
|
4749
|
+
...property.getParentPathArray({
|
|
4750
|
+
useFromPropertyName: true
|
|
4751
|
+
}),
|
|
4752
|
+
property.getResolvedName()
|
|
4753
|
+
],
|
|
4754
|
+
isArray
|
|
4755
|
+
});
|
|
4756
|
+
}
|
|
4757
|
+
};
|
|
4758
|
+
const reviveAt = (record, path)=>{
|
|
4759
|
+
const { segments } = path;
|
|
4760
|
+
let parent = record;
|
|
4761
|
+
for(let i = 0, length = segments.length - 1; i < length; i++){
|
|
4762
|
+
parent = parent[segments[i]];
|
|
4763
|
+
// An absent or null parent holds no date
|
|
4764
|
+
if (parent == null || typeof parent !== "object") {
|
|
4765
|
+
return;
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4768
|
+
const key = segments[segments.length - 1];
|
|
4769
|
+
const value = parent[key];
|
|
4770
|
+
if (path.isArray === false) {
|
|
4771
|
+
if (typeof value === "string") {
|
|
4772
|
+
parent[key] = new Date(value);
|
|
4773
|
+
}
|
|
4774
|
+
return;
|
|
4775
|
+
}
|
|
4776
|
+
if (Array.isArray(value)) {
|
|
4777
|
+
for(let i = 0, length = value.length; i < length; i++){
|
|
4778
|
+
if (typeof value[i] === "string") {
|
|
4779
|
+
value[i] = new Date(value[i]);
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
};
|
|
4784
|
+
/** Per schema: `null` for a schema with no dates, so a caller can skip the pass. */ const revivers = new WeakMap();
|
|
4785
|
+
/**
|
|
4786
|
+
* The reviver for `schema`'s records, or `null` when it declares no dates.
|
|
4787
|
+
*
|
|
4788
|
+
* Built once per compiled schema. A read revives every row it returns, so the paths are resolved
|
|
4789
|
+
* here rather than per row.
|
|
4790
|
+
*/ const getStorageDateReviver = (schema)=>{
|
|
4791
|
+
const cached = revivers.get(schema);
|
|
4792
|
+
if (cached !== undefined) {
|
|
4793
|
+
return cached;
|
|
4794
|
+
}
|
|
4795
|
+
const paths = [];
|
|
4796
|
+
collectDatePaths(schema.properties, paths);
|
|
4797
|
+
const reviver = paths.length === 0 ? null : (record)=>{
|
|
4798
|
+
for(let i = 0, length = paths.length; i < length; i++){
|
|
4799
|
+
reviveAt(record, paths[i]);
|
|
4800
|
+
}
|
|
4801
|
+
};
|
|
4802
|
+
revivers.set(schema, reviver);
|
|
4803
|
+
return reviver;
|
|
4804
|
+
};
|
|
4805
|
+
|
|
4806
|
+
|
|
4421
4807
|
},
|
|
4422
4808
|
76(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
4423
4809
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -4692,9 +5078,10 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
4692
5078
|
QB: () => (/* reexport */ RetryDbPlugin),
|
|
4693
5079
|
m6: () => (/* reexport */ executeJoin),
|
|
4694
5080
|
i1: () => (/* reexport */ parameteriseDocument),
|
|
4695
|
-
|
|
5081
|
+
wk: () => (/* reexport */ reportRenamedProperties),
|
|
4696
5082
|
VW: () => (/* reexport */ applyInnerOptions),
|
|
4697
5083
|
B2: () => (/* reexport */ describeUnparsableFilter),
|
|
5084
|
+
__: () => (/* reexport */ toEntityShape),
|
|
4698
5085
|
Jd: () => (/* reexport */ EphemeralDataPlugin),
|
|
4699
5086
|
Pl: () => (/* reexport */ deserializePersistResult),
|
|
4700
5087
|
JF: () => (/* reexport */ DataTranslator),
|
|
@@ -5685,9 +6072,12 @@ class JsonTranslator extends DataTranslator {
|
|
|
5685
6072
|
}
|
|
5686
6073
|
}
|
|
5687
6074
|
|
|
6075
|
+
// EXTERNAL MODULE: ./src/schema/utils/storageDates.ts
|
|
6076
|
+
var storageDates = __webpack_require__(894);
|
|
5688
6077
|
;// CONCATENATED MODULE: ./src/plugins/translators/SqlTranslator.ts
|
|
5689
6078
|
|
|
5690
6079
|
|
|
6080
|
+
|
|
5691
6081
|
/**
|
|
5692
6082
|
* A stored vector as a list of numbers, whatever the driver handed back.
|
|
5693
6083
|
*
|
|
@@ -5716,6 +6106,31 @@ class SqlTranslator extends DataTranslator {
|
|
|
5716
6106
|
super(query);
|
|
5717
6107
|
this.pushedDown = pushedDown;
|
|
5718
6108
|
}
|
|
6109
|
+
/**
|
|
6110
|
+
* Dates back as Dates, before the caller's selectors run over the rows.
|
|
6111
|
+
*
|
|
6112
|
+
* A `group` key and a `map` are the caller's lambdas, run here over rows as the engine returned them.
|
|
6113
|
+
* SQLite, D1 and libSQL hand a date back as the TEXT it was stored as, which has no `getFullYear()`
|
|
6114
|
+
* and groups apart from the Date the entity holds. Revived at storage paths and in place, which the
|
|
6115
|
+
* datastore's deserialization still reads, and after `decodeJsonColumns`, so a date inside a JSON
|
|
6116
|
+
* column is revived too. Only a string is converted, so an engine that returns a Date (PostgreSQL,
|
|
6117
|
+
* PGlite, MySQL) is left alone, and so is a row already revived.
|
|
6118
|
+
*
|
|
6119
|
+
* Not a joined statement's rows, which are tuples, each half already deserialized. Nor rows whose
|
|
6120
|
+
* `group` or `map` was handed back, which the datastore runs after deserializing them.
|
|
6121
|
+
*/ translate(data) {
|
|
6122
|
+
const runsHere = (name)=>this.query.options.get(name).some((item)=>item.option.target === "database" && item.option.reason === "executed");
|
|
6123
|
+
if (this.pushedDown.join !== true && this.query.schema != null && Array.isArray(data) && (runsHere("group") || runsHere("map"))) {
|
|
6124
|
+
const reviveDates = (0,storageDates/* .getStorageDateReviver */.T)(this.query.schema);
|
|
6125
|
+
for(let i = 0, length = reviveDates == null ? 0 : data.length; i < length; i++){
|
|
6126
|
+
const row = data[i];
|
|
6127
|
+
if (row != null && typeof row === "object") {
|
|
6128
|
+
reviveDates(row);
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
return super.translate(data);
|
|
6133
|
+
}
|
|
5719
6134
|
count(data, _) {
|
|
5720
6135
|
if (Array.isArray(data) && data.length > 0) {
|
|
5721
6136
|
// Count is returned as the property alias on the query.
|
|
@@ -6167,7 +6582,6 @@ const isParameter = (value)=>typeof value === "object" && value !== null && PARA
|
|
|
6167
6582
|
*/ const MEMORY_EXECUTION_EXPLANATIONS = {
|
|
6168
6583
|
"not-parsable": "A filter could not be parsed into an expression tree, so it and every option after it run in memory.",
|
|
6169
6584
|
"unmapped-property": "The property is not stored in the database, so it can only be read after deserialization.",
|
|
6170
|
-
"renamed-property": "The property is stored under a different name, and selectors use the in-memory name, so it can only be read after deserialization.",
|
|
6171
6585
|
"map-rename": "A map renames or drops properties, so every option after it refers to names the database does not have.",
|
|
6172
6586
|
"after-nearest": "A similarity search orders and limits rows, and the plugin cannot report whether it performed the search, so every option after it runs in memory.",
|
|
6173
6587
|
"after-join": "A join produces [outer, inner] tuples rather than entities, and the plugin cannot report how it joined, so every option after it runs in memory.",
|
|
@@ -6669,6 +7083,80 @@ const formatStep = (step, lines)=>{
|
|
|
6669
7083
|
return lines.join("\n");
|
|
6670
7084
|
};
|
|
6671
7085
|
|
|
7086
|
+
// EXTERNAL MODULE: ./src/expressions/utils.ts
|
|
7087
|
+
var utils = __webpack_require__(63);
|
|
7088
|
+
;// CONCATENATED MODULE: ./src/plugins/query/renames.ts
|
|
7089
|
+
|
|
7090
|
+
|
|
7091
|
+
const PROPERTY_READING_OPTIONS = [
|
|
7092
|
+
"filter",
|
|
7093
|
+
"sort",
|
|
7094
|
+
"nearest",
|
|
7095
|
+
"map",
|
|
7096
|
+
"group"
|
|
7097
|
+
];
|
|
7098
|
+
const namesRenamedProperty = (expression)=>{
|
|
7099
|
+
let found = false;
|
|
7100
|
+
if (expression == null) {
|
|
7101
|
+
return found;
|
|
7102
|
+
}
|
|
7103
|
+
(0,utils/* .forEach */.jJ)(expression, (node)=>{
|
|
7104
|
+
if ((0,assertions/* .isPropertyExpression */.e3)(node) && node.property.hasRenamedSegments) {
|
|
7105
|
+
found = true;
|
|
7106
|
+
return false;
|
|
7107
|
+
}
|
|
7108
|
+
return true;
|
|
7109
|
+
});
|
|
7110
|
+
return found;
|
|
7111
|
+
};
|
|
7112
|
+
const isRenamed = (property)=>property != null && property.hasRenamedSegments;
|
|
7113
|
+
/**
|
|
7114
|
+
* Whether a selector's value is read from a renamed property, whether it is that property or computed
|
|
7115
|
+
* from it: `r => r.dueDate.getTime()` reads `dueDate` all the same. Every property it reads when the
|
|
7116
|
+
* selector was parsed, and otherwise the property recorded for it.
|
|
7117
|
+
*/ const readsRenamedValue = (value)=>value != null && (value.reads != null ? value.reads.some(isRenamed) : isRenamed(value.property));
|
|
7118
|
+
const readsRenamedField = (fields)=>fields != null && fields.some(readsRenamedValue);
|
|
7119
|
+
const readsRenamedProperty = (name, value)=>{
|
|
7120
|
+
switch(name){
|
|
7121
|
+
case "filter":
|
|
7122
|
+
return namesRenamedProperty(value.expression);
|
|
7123
|
+
case "map":
|
|
7124
|
+
// A projection reads each field it selects
|
|
7125
|
+
return readsRenamedField(value.fields);
|
|
7126
|
+
case "group":
|
|
7127
|
+
// A group reads its key, then copies every field of the row into its members: every schema
|
|
7128
|
+
// property, or what a `map` before it selected
|
|
7129
|
+
return readsRenamedValue(value.key) || readsRenamedField(value.fields);
|
|
7130
|
+
default:
|
|
7131
|
+
return readsRenamedValue(value);
|
|
7132
|
+
}
|
|
7133
|
+
};
|
|
7134
|
+
/**
|
|
7135
|
+
* Hands back every option over a property stored under a `.from()` name, for the datastore to run
|
|
7136
|
+
* in memory.
|
|
7137
|
+
*
|
|
7138
|
+
* Core keeps such an option with the database, because only the plugin knows whether its backend
|
|
7139
|
+
* reads storage names. One that translates the option — SQL renders the column from
|
|
7140
|
+
* `getResolvedName()` — needs nothing from here. One that runs the caller's lambda over rows as it
|
|
7141
|
+
* stores them reads a key the row does not have, and answers wrongly without an error: that plugin
|
|
7142
|
+
* calls this before it reads anything, and the datastore finishes the query after deserialization,
|
|
7143
|
+
* where the in-memory names exist.
|
|
7144
|
+
*
|
|
7145
|
+
* Reported as `missing-capability`: the backend cannot express the option as written, and like
|
|
7146
|
+
* every capability, that is only knowable by the plugin.
|
|
7147
|
+
*
|
|
7148
|
+
* @param names Which options to check, for a plugin that resolves some of them itself — Mongo renders
|
|
7149
|
+
* filters and sorts through the stored path, and runs `nearest`, `map` and `group` in JavaScript.
|
|
7150
|
+
*/ const reportRenamedProperties = (options, names = PROPERTY_READING_OPTIONS)=>{
|
|
7151
|
+
for (const name of names){
|
|
7152
|
+
for (const item of options.get(name)){
|
|
7153
|
+
if (readsRenamedProperty(name, item.option.value)) {
|
|
7154
|
+
options.reportMissingCapability(item);
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
7157
|
+
}
|
|
7158
|
+
};
|
|
7159
|
+
|
|
6672
7160
|
;// CONCATENATED MODULE: ./src/plugins/query/types.ts
|
|
6673
7161
|
var types_QueryOrdering = /*#__PURE__*/ function(QueryOrdering) {
|
|
6674
7162
|
QueryOrdering["Descending"] = "desc";
|
|
@@ -6686,6 +7174,7 @@ var types_QueryOrdering = /*#__PURE__*/ function(QueryOrdering) {
|
|
|
6686
7174
|
|
|
6687
7175
|
|
|
6688
7176
|
|
|
7177
|
+
|
|
6689
7178
|
// EXTERNAL MODULE: ./src/expressions/evaluate.ts
|
|
6690
7179
|
var evaluate = __webpack_require__(379);
|
|
6691
7180
|
// EXTERNAL MODULE: ./src/expressions/fold.ts
|
|
@@ -6699,6 +7188,9 @@ var fold = __webpack_require__(43);
|
|
|
6699
7188
|
* Everything except `map` and `group`. Those two are defined BY a closure — the projection is the
|
|
6700
7189
|
* option — and no data form of them exists to send. Nothing else needs its closure: a sort is a
|
|
6701
7190
|
* property and a direction, a filter is an expression tree, `nearest` is a vector and a count.
|
|
7191
|
+
*
|
|
7192
|
+
* Except a sort or `nearest` whose selector computes its value, such as `x => x.name.length`. It is sent
|
|
7193
|
+
* as the property it reads, and the receiver would order by that instead. See `isSendable`.
|
|
6702
7194
|
*/ const SENDABLE = new Set([
|
|
6703
7195
|
"skip",
|
|
6704
7196
|
"take",
|
|
@@ -6712,6 +7204,7 @@ var fold = __webpack_require__(43);
|
|
|
6712
7204
|
"sum",
|
|
6713
7205
|
"distinct"
|
|
6714
7206
|
]);
|
|
7207
|
+
const isSendable = (name, value)=>SENDABLE.has(name) && (name !== "sort" && name !== "nearest" || value.isDirectProperty !== false);
|
|
6715
7208
|
/**
|
|
6716
7209
|
* Splits options into the PREFIX that can be sent and the remainder that cannot.
|
|
6717
7210
|
*
|
|
@@ -6727,7 +7220,12 @@ var fold = __webpack_require__(43);
|
|
|
6727
7220
|
const local = new QueryOptionsCollection/* .QueryOptionsCollection */.H();
|
|
6728
7221
|
let stopped = false;
|
|
6729
7222
|
options.forEach((option)=>{
|
|
6730
|
-
|
|
7223
|
+
// Reported by the plugin, so it belongs to the datastore, and so does everything after it —
|
|
7224
|
+
// a report cascades to the end of the database phase, which keeps what is left a prefix
|
|
7225
|
+
if (option.target === "database" && option.reason !== "executed") {
|
|
7226
|
+
return;
|
|
7227
|
+
}
|
|
7228
|
+
if (stopped === false && isSendable(option.name, option.value) === false) {
|
|
6731
7229
|
stopped = true;
|
|
6732
7230
|
}
|
|
6733
7231
|
(stopped ? local : sendable).add(option.name, option.value);
|
|
@@ -7594,6 +8092,15 @@ class EphemeralDataPlugin {
|
|
|
7594
8092
|
*/ get databaseName() {
|
|
7595
8093
|
return this._databaseName;
|
|
7596
8094
|
}
|
|
8095
|
+
/**
|
|
8096
|
+
* Whether the records this plugin holds are in storage shape, keyed by `from` names.
|
|
8097
|
+
*
|
|
8098
|
+
* True for every store of what the datastore serialized, which is why a renamed property is
|
|
8099
|
+
* reported and records are cloned and keyed by their storage names. The datastore's change probe
|
|
8100
|
+
* holds rows the broadcast has already deserialized, so it reads them by in-memory names instead.
|
|
8101
|
+
*/ get holdsStorageShape() {
|
|
8102
|
+
return true;
|
|
8103
|
+
}
|
|
7597
8104
|
/**
|
|
7598
8105
|
* All-or-nothing across every collection in the save.
|
|
7599
8106
|
*
|
|
@@ -7801,7 +8308,9 @@ class EphemeralDataPlugin {
|
|
|
7801
8308
|
* join discards the surplus. Same pairs either way.
|
|
7802
8309
|
*/ resolveJoinInnerSide(event, outerKeys, done) {
|
|
7803
8310
|
const joinOption = event.operation.options.getLast("join");
|
|
7804
|
-
|
|
8311
|
+
// Not reached when an option before it was reported: the datastore's own join branch pairs
|
|
8312
|
+
// the rows this read returns.
|
|
8313
|
+
if (joinOption == null || joinOption.reason !== "executed") {
|
|
7805
8314
|
done({
|
|
7806
8315
|
ok: "success"
|
|
7807
8316
|
});
|
|
@@ -7828,7 +8337,7 @@ class EphemeralDataPlugin {
|
|
|
7828
8337
|
const innerRows = [];
|
|
7829
8338
|
// Records are held in STORAGE shape, so the key is read by its resolved column name.
|
|
7830
8339
|
const innerKey = joinOption.value.innerKey;
|
|
7831
|
-
const keyColumn = innerKey.property?.getResolvedName() ?? innerKey.propertyName;
|
|
8340
|
+
const keyColumn = this.holdsStorageShape ? innerKey.property?.getResolvedName() ?? innerKey.propertyName : innerKey.propertyName;
|
|
7832
8341
|
for (const record of innerCollection.values()){
|
|
7833
8342
|
if (outerKeys != null && outerKeys.has(record[keyColumn]) === false) {
|
|
7834
8343
|
continue;
|
|
@@ -7857,7 +8366,7 @@ class EphemeralDataPlugin {
|
|
|
7857
8366
|
* to fall back to `structuredClone`, which is roughly an order of magnitude slower and was paid
|
|
7858
8367
|
* on EVERY read of EVERY schema that renames a property.
|
|
7859
8368
|
*/ recordCloner(schema) {
|
|
7860
|
-
const hasRenamedProperties = schema.properties.some((property)=>property.from != null);
|
|
8369
|
+
const hasRenamedProperties = this.holdsStorageShape && schema.properties.some((property)=>property.from != null);
|
|
7861
8370
|
return hasRenamedProperties ? schema.cloneStorage : schema.clone;
|
|
7862
8371
|
}
|
|
7863
8372
|
query(event, done) {
|
|
@@ -7869,6 +8378,12 @@ class EphemeralDataPlugin {
|
|
|
7869
8378
|
const schema = operation.schema;
|
|
7870
8379
|
const collection = this.resolveCollection(schema);
|
|
7871
8380
|
const cloneRecord = this.recordCloner(schema);
|
|
8381
|
+
// Records are held in storage shape and every option below runs the caller's lambda
|
|
8382
|
+
// over them, so a `from` property is read by a name the record does not have. Handed
|
|
8383
|
+
// back, and the datastore runs it after deserialization.
|
|
8384
|
+
if (this.holdsStorageShape) {
|
|
8385
|
+
reportRenamedProperties(operation.options);
|
|
8386
|
+
}
|
|
7872
8387
|
collection.load((r)=>{
|
|
7873
8388
|
if (r.ok === Result/* .Result.ERROR */.Q.ERROR) {
|
|
7874
8389
|
done(Result/* .PluginEventResult.error */.D.error(event.id, r.error));
|
|
@@ -7877,7 +8392,9 @@ class EphemeralDataPlugin {
|
|
|
7877
8392
|
const orderedOptions = [];
|
|
7878
8393
|
operation.options.forEach((o)=>orderedOptions.push(o));
|
|
7879
8394
|
let leadingFilterCount = 0;
|
|
7880
|
-
|
|
8395
|
+
// Stops at a reported filter too: the database phase ends there, and the datastore
|
|
8396
|
+
// runs it and everything after it.
|
|
8397
|
+
while(leadingFilterCount < orderedOptions.length && orderedOptions[leadingFilterCount].name === "filter" && orderedOptions[leadingFilterCount].reason === "executed"){
|
|
7881
8398
|
leadingFilterCount++;
|
|
7882
8399
|
}
|
|
7883
8400
|
// Key-equality fast path: when a leading filter's parsed expression pins
|
|
@@ -7955,14 +8472,14 @@ class EphemeralDataPlugin {
|
|
|
7955
8472
|
* that was never applied.
|
|
7956
8473
|
*
|
|
7957
8474
|
* Before the inner side, to match execution order.
|
|
7958
|
-
*/ const described = describeFilters(operation.options.get("filter").map((entry)=>entry.option.value));
|
|
8475
|
+
*/ const described = describeFilters(operation.options.get("filter").filter((entry)=>entry.option.reason === "executed").map((entry)=>entry.option.value));
|
|
7959
8476
|
event.executedQueries.push({
|
|
7960
8477
|
text: `${operation.schema.collectionName}: scanned ${cloned.length} in-memory ` + `${cloned.length === 1 ? "record" : "records"}, filter ${described.text}`,
|
|
7961
8478
|
parameters: described.parameters.length > 0 ? described.parameters : undefined
|
|
7962
8479
|
});
|
|
7963
8480
|
const joinOption = operation.options.getLast("join");
|
|
7964
|
-
const outerKeys = joinOption == null ? null : distinctJoinKeys(cloned, joinOption.value.outerKey, joinOption.value.semiJoinKeyThreshold, {
|
|
7965
|
-
storageShape:
|
|
8481
|
+
const outerKeys = joinOption == null || joinOption.reason !== "executed" ? null : distinctJoinKeys(cloned, joinOption.value.outerKey, joinOption.value.semiJoinKeyThreshold, {
|
|
8482
|
+
storageShape: this.holdsStorageShape
|
|
7966
8483
|
});
|
|
7967
8484
|
this.resolveJoinInnerSide(event, outerKeys, (joinResult)=>{
|
|
7968
8485
|
if (joinResult.ok === "error") {
|
|
@@ -8138,7 +8655,7 @@ class TelemetryDbPlugin {
|
|
|
8138
8655
|
*
|
|
8139
8656
|
* The clone is a real date and fails `instanceof Date`, which is what a caller checks. Mutated in
|
|
8140
8657
|
* place because the clone is already private to this call.
|
|
8141
|
-
*/ const
|
|
8658
|
+
*/ const CacheDbPlugin_reviveDates = (value)=>{
|
|
8142
8659
|
if (value == null || typeof value !== "object") {
|
|
8143
8660
|
return value;
|
|
8144
8661
|
}
|
|
@@ -8147,12 +8664,12 @@ class TelemetryDbPlugin {
|
|
|
8147
8664
|
}
|
|
8148
8665
|
if (Array.isArray(value)) {
|
|
8149
8666
|
for(let i = 0, length = value.length; i < length; i++){
|
|
8150
|
-
value[i] =
|
|
8667
|
+
value[i] = CacheDbPlugin_reviveDates(value[i]);
|
|
8151
8668
|
}
|
|
8152
8669
|
return value;
|
|
8153
8670
|
}
|
|
8154
8671
|
for (const key of Object.keys(value)){
|
|
8155
|
-
value[key] =
|
|
8672
|
+
value[key] = CacheDbPlugin_reviveDates(value[key]);
|
|
8156
8673
|
}
|
|
8157
8674
|
return value;
|
|
8158
8675
|
};
|
|
@@ -8194,7 +8711,7 @@ class CacheDbPlugin {
|
|
|
8194
8711
|
* the next update would be written UNCHECKED with no error anywhere.
|
|
8195
8712
|
* Pinned by `datastore/src/collections/wrapperStacking.test.ts`.
|
|
8196
8713
|
*/ rebuild(entry) {
|
|
8197
|
-
return new entry.construct(
|
|
8714
|
+
return new entry.construct(CacheDbPlugin_reviveDates(structuredClone(entry.value)), entry.isTransformed);
|
|
8198
8715
|
}
|
|
8199
8716
|
query(event, done) {
|
|
8200
8717
|
const key = this.keyFor(event);
|
|
@@ -8650,6 +9167,7 @@ var __webpack_exports__nearestBy = __webpack_exports__.iG;
|
|
|
8650
9167
|
var __webpack_exports__parameter = __webpack_exports__.Wi;
|
|
8651
9168
|
var __webpack_exports__parameteriseDocument = __webpack_exports__.i1;
|
|
8652
9169
|
var __webpack_exports__readJoinKey = __webpack_exports__.qy;
|
|
9170
|
+
var __webpack_exports__reportRenamedProperties = __webpack_exports__.wk;
|
|
8653
9171
|
var __webpack_exports__semiJoinFilter = __webpack_exports__.lA;
|
|
8654
9172
|
var __webpack_exports__serializeBulkPersist = __webpack_exports__.n;
|
|
8655
9173
|
var __webpack_exports__serializePersistResult = __webpack_exports__.yR;
|
|
@@ -8658,6 +9176,6 @@ var __webpack_exports__splitSendableOptions = __webpack_exports__.PP;
|
|
|
8658
9176
|
var __webpack_exports__toEntityShape = __webpack_exports__.__;
|
|
8659
9177
|
var __webpack_exports__withExecutedQueries = __webpack_exports__.Kg;
|
|
8660
9178
|
var __webpack_exports__withInnerSide = __webpack_exports__.oJ;
|
|
8661
|
-
export { __webpack_exports__BatchingDbPlugin as BatchingDbPlugin, __webpack_exports__CacheDbPlugin as CacheDbPlugin, __webpack_exports__ConcurrencyDbPlugin as ConcurrencyDbPlugin, __webpack_exports__DATABASE_EXECUTION_EXPLANATIONS as DATABASE_EXECUTION_EXPLANATIONS, __webpack_exports__DEFAULT_SEMI_JOIN_KEY_THRESHOLD as DEFAULT_SEMI_JOIN_KEY_THRESHOLD, __webpack_exports__DataTranslator as DataTranslator, __webpack_exports__EXECUTED_QUERIES_UNSUPPORTED as EXECUTED_QUERIES_UNSUPPORTED, __webpack_exports__EphemeralDataPlugin as EphemeralDataPlugin, __webpack_exports__JsonTranslator as JsonTranslator, __webpack_exports__MEMORY_EXECUTION_EXPLANATIONS as MEMORY_EXECUTION_EXPLANATIONS, __webpack_exports__Query as Query, __webpack_exports__QueryOptionsCollection as QueryOptionsCollection, __webpack_exports__QueryOrdering as QueryOrdering, __webpack_exports__RetryDbPlugin as RetryDbPlugin, __webpack_exports__SqlTranslator as SqlTranslator, __webpack_exports__TelemetryDbPlugin as TelemetryDbPlugin, __webpack_exports__TranslatedArrayValue as TranslatedArrayValue, __webpack_exports__TranslatedGroupValue as TranslatedGroupValue, __webpack_exports__TranslatedSingleValue as TranslatedSingleValue, __webpack_exports__TupleTranslator as TupleTranslator, __webpack_exports__applyInnerOptions as applyInnerOptions, __webpack_exports__collectingSink as collectingSink, __webpack_exports__cosineDistance as cosineDistance, __webpack_exports__createRequestHandler as createRequestHandler, __webpack_exports__describeFilterAsJs as describeFilterAsJs, __webpack_exports__describeFilters as describeFilters, __webpack_exports__describeUnparsableFilter as describeUnparsableFilter, __webpack_exports__deserializeBulkPersist as deserializeBulkPersist, __webpack_exports__deserializePersistResult as deserializePersistResult, __webpack_exports__deserializeQueryOptions as deserializeQueryOptions, __webpack_exports__distinctJoinKeys as distinctJoinKeys, __webpack_exports__executeJoin as executeJoin, __webpack_exports__executedQueriesOf as executedQueriesOf, __webpack_exports__explainQuery as explainQuery, __webpack_exports__formatExplanation as formatExplanation, __webpack_exports__hashJoin as hashJoin, __webpack_exports__isDatabaseStep as isDatabaseStep, __webpack_exports__joinInPlugin as joinInPlugin, __webpack_exports__loadJoinInnerSide as loadJoinInnerSide, __webpack_exports__loggerSink as loggerSink, __webpack_exports__mappedResultColumns as mappedResultColumns, __webpack_exports__nearestBy as nearestBy, __webpack_exports__parameter as parameter, __webpack_exports__parameteriseDocument as parameteriseDocument, __webpack_exports__readJoinKey as readJoinKey, __webpack_exports__semiJoinFilter as semiJoinFilter, __webpack_exports__serializeBulkPersist as serializeBulkPersist, __webpack_exports__serializePersistResult as serializePersistResult, __webpack_exports__serializeQueryOptions as serializeQueryOptions, __webpack_exports__splitSendableOptions as splitSendableOptions, __webpack_exports__toEntityShape as toEntityShape, __webpack_exports__withExecutedQueries as withExecutedQueries, __webpack_exports__withInnerSide as withInnerSide };
|
|
9179
|
+
export { __webpack_exports__BatchingDbPlugin as BatchingDbPlugin, __webpack_exports__CacheDbPlugin as CacheDbPlugin, __webpack_exports__ConcurrencyDbPlugin as ConcurrencyDbPlugin, __webpack_exports__DATABASE_EXECUTION_EXPLANATIONS as DATABASE_EXECUTION_EXPLANATIONS, __webpack_exports__DEFAULT_SEMI_JOIN_KEY_THRESHOLD as DEFAULT_SEMI_JOIN_KEY_THRESHOLD, __webpack_exports__DataTranslator as DataTranslator, __webpack_exports__EXECUTED_QUERIES_UNSUPPORTED as EXECUTED_QUERIES_UNSUPPORTED, __webpack_exports__EphemeralDataPlugin as EphemeralDataPlugin, __webpack_exports__JsonTranslator as JsonTranslator, __webpack_exports__MEMORY_EXECUTION_EXPLANATIONS as MEMORY_EXECUTION_EXPLANATIONS, __webpack_exports__Query as Query, __webpack_exports__QueryOptionsCollection as QueryOptionsCollection, __webpack_exports__QueryOrdering as QueryOrdering, __webpack_exports__RetryDbPlugin as RetryDbPlugin, __webpack_exports__SqlTranslator as SqlTranslator, __webpack_exports__TelemetryDbPlugin as TelemetryDbPlugin, __webpack_exports__TranslatedArrayValue as TranslatedArrayValue, __webpack_exports__TranslatedGroupValue as TranslatedGroupValue, __webpack_exports__TranslatedSingleValue as TranslatedSingleValue, __webpack_exports__TupleTranslator as TupleTranslator, __webpack_exports__applyInnerOptions as applyInnerOptions, __webpack_exports__collectingSink as collectingSink, __webpack_exports__cosineDistance as cosineDistance, __webpack_exports__createRequestHandler as createRequestHandler, __webpack_exports__describeFilterAsJs as describeFilterAsJs, __webpack_exports__describeFilters as describeFilters, __webpack_exports__describeUnparsableFilter as describeUnparsableFilter, __webpack_exports__deserializeBulkPersist as deserializeBulkPersist, __webpack_exports__deserializePersistResult as deserializePersistResult, __webpack_exports__deserializeQueryOptions as deserializeQueryOptions, __webpack_exports__distinctJoinKeys as distinctJoinKeys, __webpack_exports__executeJoin as executeJoin, __webpack_exports__executedQueriesOf as executedQueriesOf, __webpack_exports__explainQuery as explainQuery, __webpack_exports__formatExplanation as formatExplanation, __webpack_exports__hashJoin as hashJoin, __webpack_exports__isDatabaseStep as isDatabaseStep, __webpack_exports__joinInPlugin as joinInPlugin, __webpack_exports__loadJoinInnerSide as loadJoinInnerSide, __webpack_exports__loggerSink as loggerSink, __webpack_exports__mappedResultColumns as mappedResultColumns, __webpack_exports__nearestBy as nearestBy, __webpack_exports__parameter as parameter, __webpack_exports__parameteriseDocument as parameteriseDocument, __webpack_exports__readJoinKey as readJoinKey, __webpack_exports__reportRenamedProperties as reportRenamedProperties, __webpack_exports__semiJoinFilter as semiJoinFilter, __webpack_exports__serializeBulkPersist as serializeBulkPersist, __webpack_exports__serializePersistResult as serializePersistResult, __webpack_exports__serializeQueryOptions as serializeQueryOptions, __webpack_exports__splitSendableOptions as splitSendableOptions, __webpack_exports__toEntityShape as toEntityShape, __webpack_exports__withExecutedQueries as withExecutedQueries, __webpack_exports__withInnerSide as withInnerSide };
|
|
8662
9180
|
|
|
8663
9181
|
//# sourceMappingURL=index.js.map
|