graphddb 0.1.1 → 0.2.1
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/README.md +130 -11
- package/dist/{chunk-6LEHSX45.js → chunk-4B4MUPUJ.js} +3037 -1143
- package/dist/{chunk-347U24SB.js → chunk-PWV7JDMR.js} +353 -37
- package/dist/{chunk-UNRQ5YJT.js → chunk-SBNP62H7.js} +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +61 -3334
- package/dist/index.js +187 -1881
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/types-DPJ4tPjX.d.ts +4607 -0
- package/package.json +1 -1
- package/dist/types-CDrWiPxp.d.ts +0 -1203
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BatchGetResult,
|
|
3
|
+
DDBModel,
|
|
2
4
|
EDGE_WRITES_MARKER,
|
|
3
5
|
ENTITY_WRITES_MARKER,
|
|
4
6
|
LIFECYCLE_CONTRACT_MARKER,
|
|
@@ -16,19 +18,19 @@ import {
|
|
|
16
18
|
buildContracts,
|
|
17
19
|
buildManifest,
|
|
18
20
|
buildOperations,
|
|
19
|
-
|
|
21
|
+
buildProjection,
|
|
20
22
|
buildQuerySpec,
|
|
21
|
-
buildRelation,
|
|
22
|
-
buildRelationExecutionPlan,
|
|
23
23
|
buildTransactionSpec,
|
|
24
24
|
buildTransactions,
|
|
25
25
|
collectContractBoundaryViolations,
|
|
26
26
|
collectContractN1Violations,
|
|
27
|
-
|
|
27
|
+
compileFilterExpression,
|
|
28
28
|
compileFragment,
|
|
29
29
|
compileMutationPlan,
|
|
30
30
|
compileSingleFragmentPlan,
|
|
31
|
+
conditionParamName,
|
|
31
32
|
contractOfMethodSpec,
|
|
33
|
+
decodeCursor,
|
|
32
34
|
definePlan,
|
|
33
35
|
defineTransaction,
|
|
34
36
|
defineTransactions,
|
|
@@ -38,12 +40,22 @@ import {
|
|
|
38
40
|
deriveModelEdgeWriteItems,
|
|
39
41
|
detectRelationFields,
|
|
40
42
|
edgeWrites,
|
|
43
|
+
encodeCursor,
|
|
41
44
|
entityWrites,
|
|
45
|
+
evaluateFilter,
|
|
46
|
+
execute,
|
|
47
|
+
executeBatchGet,
|
|
48
|
+
executeBatchWrite,
|
|
49
|
+
executeCommandMethod,
|
|
50
|
+
executeExplain,
|
|
51
|
+
executeList,
|
|
52
|
+
executeListInternal,
|
|
53
|
+
executeQuery,
|
|
42
54
|
from,
|
|
43
55
|
getEdgeWrites,
|
|
44
56
|
getEntityWrites,
|
|
45
57
|
getImplicitKeyFields,
|
|
46
|
-
|
|
58
|
+
hydrate,
|
|
47
59
|
isCommandModelContract,
|
|
48
60
|
isCommandPlan,
|
|
49
61
|
isContractComposeNode,
|
|
@@ -56,10 +68,8 @@ import {
|
|
|
56
68
|
isLifecycleContract,
|
|
57
69
|
isMutationFragment,
|
|
58
70
|
isMutationInputRef,
|
|
59
|
-
isParam,
|
|
60
71
|
isPlannedCommandMethod,
|
|
61
72
|
isQueryModelContract,
|
|
62
|
-
isRecordingContractMethod,
|
|
63
73
|
isSelectBuilder,
|
|
64
74
|
isTransactionRef,
|
|
65
75
|
lifecyclePhaseForIntent,
|
|
@@ -67,28 +77,23 @@ import {
|
|
|
67
77
|
mintContractKeyFieldRef,
|
|
68
78
|
mintContractParamRef,
|
|
69
79
|
mutation,
|
|
70
|
-
|
|
71
|
-
normalizeTopLevelSelect,
|
|
72
|
-
param,
|
|
73
|
-
point,
|
|
80
|
+
plan,
|
|
74
81
|
publicCommandModel,
|
|
75
82
|
publicQueryModel,
|
|
76
83
|
query,
|
|
77
|
-
range,
|
|
78
|
-
recordContractOp,
|
|
79
84
|
resolveLifecycle,
|
|
80
|
-
|
|
85
|
+
resolveRelations,
|
|
86
|
+
validateDepth,
|
|
81
87
|
when,
|
|
82
88
|
wholeKeysSentinel
|
|
83
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-4B4MUPUJ.js";
|
|
84
90
|
import {
|
|
85
91
|
CdcEmulator,
|
|
86
92
|
createCdcEmulator
|
|
87
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-SBNP62H7.js";
|
|
88
94
|
import {
|
|
89
95
|
BATCH_GET_MAX_KEYS,
|
|
90
96
|
BATCH_WRITE_MAX_ITEMS,
|
|
91
|
-
ChangeCaptureRegistry,
|
|
92
97
|
ClientManager,
|
|
93
98
|
DynamoExecutor,
|
|
94
99
|
Linter,
|
|
@@ -96,17 +101,16 @@ import {
|
|
|
96
101
|
MetadataRegistry,
|
|
97
102
|
TableMapping,
|
|
98
103
|
TransactionContext,
|
|
99
|
-
attachHiddenKey,
|
|
100
104
|
attachModelClass,
|
|
101
105
|
buildConditionExpression,
|
|
102
106
|
buildDeleteInput,
|
|
103
107
|
buildPutInput,
|
|
104
108
|
buildUpdateExpression,
|
|
105
109
|
buildUpdateInput,
|
|
106
|
-
captureWrite,
|
|
107
110
|
collapseSameKeyItems,
|
|
111
|
+
collectRawConditionParams,
|
|
108
112
|
commitTransaction,
|
|
109
|
-
|
|
113
|
+
cond,
|
|
110
114
|
createDefaultLinter,
|
|
111
115
|
execItemKeySignature,
|
|
112
116
|
executeDelete,
|
|
@@ -117,17 +121,20 @@ import {
|
|
|
117
121
|
gsiAmbiguityRule,
|
|
118
122
|
isColumn,
|
|
119
123
|
isKeySegment,
|
|
124
|
+
isParam,
|
|
125
|
+
isRawCondition,
|
|
120
126
|
k,
|
|
121
127
|
key,
|
|
122
128
|
missingGsiRule,
|
|
123
129
|
noScanRule,
|
|
130
|
+
param,
|
|
124
131
|
relationDepthRule,
|
|
125
132
|
requireLimitRule,
|
|
133
|
+
resolveConditionTree,
|
|
126
134
|
resolveKey,
|
|
127
135
|
resolveModelClass,
|
|
128
|
-
resolveSegmentedKey,
|
|
129
136
|
serializeFieldValue
|
|
130
|
-
} from "./chunk-
|
|
137
|
+
} from "./chunk-PWV7JDMR.js";
|
|
131
138
|
|
|
132
139
|
// src/metadata/prefix.ts
|
|
133
140
|
function derivePrefix(customPrefix, className) {
|
|
@@ -276,1431 +283,64 @@ var numberSet = createFieldDecorator("NS");
|
|
|
276
283
|
var list = createFieldDecorator("L");
|
|
277
284
|
var map = createFieldDecorator("M");
|
|
278
285
|
|
|
279
|
-
// src/decorators/embedded.ts
|
|
280
|
-
function embedded(modelFactory) {
|
|
281
|
-
return function(_value, context) {
|
|
282
|
-
collectEmbedded({
|
|
283
|
-
propertyName: String(context.name),
|
|
284
|
-
modelFactory
|
|
285
|
-
});
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// src/decorators/relation.ts
|
|
290
|
-
var RESERVED_RELATION_PROPERTY_NAMES = /* @__PURE__ */ new Set([
|
|
291
|
-
"items"
|
|
292
|
-
]);
|
|
293
|
-
function assertRelationPropertyNameAllowed(propertyName) {
|
|
294
|
-
if (RESERVED_RELATION_PROPERTY_NAMES.has(propertyName)) {
|
|
295
|
-
throw new Error(
|
|
296
|
-
`Relation property name '${propertyName}' is reserved: it collides with the relation result-path grammar token '${propertyName}', which the planner and runtimes use to mark a hasMany connection (a relation writes to '$.<prop>.items'). A relation named '${propertyName}' would be mis-staged at runtime. Rename the relation property (reserved names: ${[...RESERVED_RELATION_PROPERTY_NAMES].map((n) => `'${n}'`).join(", ")}).`
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
function hasMany(targetFactory, keyBinding, options) {
|
|
301
|
-
return function(_value, context) {
|
|
302
|
-
const propertyName = String(context.name);
|
|
303
|
-
assertRelationPropertyNameAllowed(propertyName);
|
|
304
|
-
collectRelation({
|
|
305
|
-
type: "hasMany",
|
|
306
|
-
propertyName,
|
|
307
|
-
targetFactory,
|
|
308
|
-
keyBinding,
|
|
309
|
-
options
|
|
310
|
-
});
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
function belongsTo(targetFactory, keyBinding) {
|
|
314
|
-
return function(_value, context) {
|
|
315
|
-
const propertyName = String(context.name);
|
|
316
|
-
assertRelationPropertyNameAllowed(propertyName);
|
|
317
|
-
collectRelation({
|
|
318
|
-
type: "belongsTo",
|
|
319
|
-
propertyName,
|
|
320
|
-
targetFactory,
|
|
321
|
-
keyBinding
|
|
322
|
-
});
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
function hasOne(targetFactory, keyBinding) {
|
|
326
|
-
return function(_value, context) {
|
|
327
|
-
const propertyName = String(context.name);
|
|
328
|
-
assertRelationPropertyNameAllowed(propertyName);
|
|
329
|
-
collectRelation({
|
|
330
|
-
type: "hasOne",
|
|
331
|
-
propertyName,
|
|
332
|
-
targetFactory,
|
|
333
|
-
keyBinding
|
|
334
|
-
});
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// src/planner/projection.ts
|
|
339
|
-
function buildProjection(select, additionalFields) {
|
|
340
|
-
const paths = [];
|
|
341
|
-
const names = {};
|
|
342
|
-
let nameCounter = 0;
|
|
343
|
-
function addPath(segments) {
|
|
344
|
-
const aliased = segments.map((seg) => {
|
|
345
|
-
const alias = `#p${nameCounter++}`;
|
|
346
|
-
names[alias] = seg;
|
|
347
|
-
return alias;
|
|
348
|
-
});
|
|
349
|
-
paths.push(aliased.join("."));
|
|
350
|
-
}
|
|
351
|
-
function traverse(selectObj, parentSegments) {
|
|
352
|
-
for (const [fieldName, value] of Object.entries(selectObj)) {
|
|
353
|
-
if (value === true) {
|
|
354
|
-
addPath([...parentSegments, fieldName]);
|
|
355
|
-
} else if (typeof value === "object" && value !== null) {
|
|
356
|
-
if ("select" in value || isSelectBuilder(value)) {
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
traverse(value, [...parentSegments, fieldName]);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
traverse(select, []);
|
|
364
|
-
if (additionalFields) {
|
|
365
|
-
for (const fieldName of additionalFields) {
|
|
366
|
-
addPath([fieldName]);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
if (paths.length === 0) {
|
|
370
|
-
return void 0;
|
|
371
|
-
}
|
|
372
|
-
return {
|
|
373
|
-
projectionExpression: paths.join(", "),
|
|
374
|
-
expressionAttributeNames: names
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// src/select/cond.ts
|
|
379
|
-
var RAW_COND = /* @__PURE__ */ Symbol.for("graphddb.rawCond");
|
|
380
|
-
function isRawCondition(value) {
|
|
381
|
-
return typeof value === "object" && value !== null && value[RAW_COND] === true;
|
|
382
|
-
}
|
|
383
|
-
function cond(template, ...parts) {
|
|
384
|
-
return {
|
|
385
|
-
[RAW_COND]: true,
|
|
386
|
-
template,
|
|
387
|
-
parts
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
function compileRawCondition(raw, allocName, allocValue) {
|
|
391
|
-
let out = raw.template[0];
|
|
392
|
-
for (let i = 0; i < raw.parts.length; i++) {
|
|
393
|
-
const part = raw.parts[i];
|
|
394
|
-
if (isColumn(part)) {
|
|
395
|
-
out += allocName(part.name);
|
|
396
|
-
} else {
|
|
397
|
-
out += allocValue(part);
|
|
398
|
-
}
|
|
399
|
-
out += raw.template[i + 1];
|
|
400
|
-
}
|
|
401
|
-
return out;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// src/expression/filter-expression.ts
|
|
405
|
-
var LOGICAL_KEYS = /* @__PURE__ */ new Set(["and", "or", "not"]);
|
|
406
|
-
var OPERATOR_KEYS = /* @__PURE__ */ new Set([
|
|
407
|
-
"eq",
|
|
408
|
-
"ne",
|
|
409
|
-
"gt",
|
|
410
|
-
"ge",
|
|
411
|
-
"lt",
|
|
412
|
-
"le",
|
|
413
|
-
"between",
|
|
414
|
-
"in",
|
|
415
|
-
"beginsWith",
|
|
416
|
-
"contains",
|
|
417
|
-
"notContains",
|
|
418
|
-
"attributeExists",
|
|
419
|
-
"attributeType",
|
|
420
|
-
"size"
|
|
421
|
-
]);
|
|
422
|
-
function nameAlias(ctx, column) {
|
|
423
|
-
for (const [alias2, col] of Object.entries(ctx.names)) {
|
|
424
|
-
if (col === column) return alias2;
|
|
425
|
-
}
|
|
426
|
-
const alias = `#f${ctx.nameCounter.n++}`;
|
|
427
|
-
ctx.names[alias] = column;
|
|
428
|
-
return alias;
|
|
429
|
-
}
|
|
430
|
-
function valueAlias(ctx, field2, raw) {
|
|
431
|
-
const alias = `:vf${ctx.valueCounter.n++}`;
|
|
432
|
-
const fieldMeta = ctx.fieldMap.get(field2);
|
|
433
|
-
ctx.values[alias] = fieldMeta ? serializeFieldValue(raw, fieldMeta) : raw;
|
|
434
|
-
return alias;
|
|
435
|
-
}
|
|
436
|
-
function compileField(ctx, field2, condition) {
|
|
437
|
-
const nAlias = nameAlias(ctx, field2);
|
|
438
|
-
if (!isOperatorObject(condition)) {
|
|
439
|
-
const vAlias = valueAlias(ctx, field2, condition);
|
|
440
|
-
return `${nAlias} = ${vAlias}`;
|
|
441
|
-
}
|
|
442
|
-
const ops = condition;
|
|
443
|
-
const clauses = [];
|
|
444
|
-
for (const [op, value] of Object.entries(ops)) {
|
|
445
|
-
switch (op) {
|
|
446
|
-
case "eq":
|
|
447
|
-
clauses.push(`${nAlias} = ${valueAlias(ctx, field2, value)}`);
|
|
448
|
-
break;
|
|
449
|
-
case "ne":
|
|
450
|
-
clauses.push(`${nAlias} <> ${valueAlias(ctx, field2, value)}`);
|
|
451
|
-
break;
|
|
452
|
-
case "gt":
|
|
453
|
-
clauses.push(`${nAlias} > ${valueAlias(ctx, field2, value)}`);
|
|
454
|
-
break;
|
|
455
|
-
case "ge":
|
|
456
|
-
clauses.push(`${nAlias} >= ${valueAlias(ctx, field2, value)}`);
|
|
457
|
-
break;
|
|
458
|
-
case "lt":
|
|
459
|
-
clauses.push(`${nAlias} < ${valueAlias(ctx, field2, value)}`);
|
|
460
|
-
break;
|
|
461
|
-
case "le":
|
|
462
|
-
clauses.push(`${nAlias} <= ${valueAlias(ctx, field2, value)}`);
|
|
463
|
-
break;
|
|
464
|
-
case "between": {
|
|
465
|
-
const [lo, hi] = value;
|
|
466
|
-
clauses.push(
|
|
467
|
-
`${nAlias} BETWEEN ${valueAlias(ctx, field2, lo)} AND ${valueAlias(ctx, field2, hi)}`
|
|
468
|
-
);
|
|
469
|
-
break;
|
|
470
|
-
}
|
|
471
|
-
case "in": {
|
|
472
|
-
const arr = value;
|
|
473
|
-
const aliases = arr.map((v) => valueAlias(ctx, field2, v));
|
|
474
|
-
clauses.push(`${nAlias} IN (${aliases.join(", ")})`);
|
|
475
|
-
break;
|
|
476
|
-
}
|
|
477
|
-
case "beginsWith":
|
|
478
|
-
clauses.push(`begins_with(${nAlias}, ${valueAlias(ctx, field2, value)})`);
|
|
479
|
-
break;
|
|
480
|
-
case "contains":
|
|
481
|
-
clauses.push(`contains(${nAlias}, ${valueAlias(ctx, field2, value)})`);
|
|
482
|
-
break;
|
|
483
|
-
case "notContains":
|
|
484
|
-
clauses.push(
|
|
485
|
-
`NOT contains(${nAlias}, ${valueAlias(ctx, field2, value)})`
|
|
486
|
-
);
|
|
487
|
-
break;
|
|
488
|
-
case "attributeExists":
|
|
489
|
-
clauses.push(
|
|
490
|
-
value === false ? `attribute_not_exists(${nAlias})` : `attribute_exists(${nAlias})`
|
|
491
|
-
);
|
|
492
|
-
break;
|
|
493
|
-
case "attributeType":
|
|
494
|
-
clauses.push(
|
|
495
|
-
`attribute_type(${nAlias}, ${valueAlias(ctx, field2, value)})`
|
|
496
|
-
);
|
|
497
|
-
break;
|
|
498
|
-
case "size":
|
|
499
|
-
clauses.push(`size(${nAlias}) = ${valueAlias(ctx, field2, value)}`);
|
|
500
|
-
break;
|
|
501
|
-
default:
|
|
502
|
-
throw new Error(`Unknown filter operator '${op}' on field '${field2}'`);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
return joinAnd(clauses);
|
|
506
|
-
}
|
|
507
|
-
function isOperatorObject(value) {
|
|
508
|
-
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date || value instanceof Uint8Array || value instanceof Set) {
|
|
509
|
-
return false;
|
|
510
|
-
}
|
|
511
|
-
const keys = Object.keys(value);
|
|
512
|
-
if (keys.length === 0) return false;
|
|
513
|
-
return keys.every((k2) => OPERATOR_KEYS.has(k2));
|
|
514
|
-
}
|
|
515
|
-
function joinAnd(clauses) {
|
|
516
|
-
if (clauses.length === 1) return clauses[0];
|
|
517
|
-
return clauses.map((c) => wrap(c)).join(" AND ");
|
|
518
|
-
}
|
|
519
|
-
function wrap(expr) {
|
|
520
|
-
if (isAlreadyWrapped(expr)) return expr;
|
|
521
|
-
if (expr.includes(" AND ") || expr.includes(" OR ")) {
|
|
522
|
-
return `(${expr})`;
|
|
523
|
-
}
|
|
524
|
-
return expr;
|
|
525
|
-
}
|
|
526
|
-
function isAlreadyWrapped(expr) {
|
|
527
|
-
if (!expr.startsWith("(") || !expr.endsWith(")")) return false;
|
|
528
|
-
let depth = 0;
|
|
529
|
-
for (let i = 0; i < expr.length; i++) {
|
|
530
|
-
if (expr[i] === "(") depth++;
|
|
531
|
-
else if (expr[i] === ")") {
|
|
532
|
-
depth--;
|
|
533
|
-
if (depth === 0 && i < expr.length - 1) return false;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
return depth === 0;
|
|
537
|
-
}
|
|
538
|
-
function compileRaw(ctx, raw) {
|
|
539
|
-
return compileRawCondition(
|
|
540
|
-
raw,
|
|
541
|
-
(column) => nameAlias(ctx, column),
|
|
542
|
-
(value) => {
|
|
543
|
-
const alias = `:vf${ctx.valueCounter.n++}`;
|
|
544
|
-
ctx.values[alias] = value;
|
|
545
|
-
return alias;
|
|
546
|
-
}
|
|
547
|
-
);
|
|
548
|
-
}
|
|
549
|
-
function compileNode(ctx, node) {
|
|
550
|
-
if (isRawCondition(node)) {
|
|
551
|
-
return compileRaw(ctx, node);
|
|
552
|
-
}
|
|
553
|
-
const clauses = [];
|
|
554
|
-
for (const [key2, value] of Object.entries(node)) {
|
|
555
|
-
if (value === void 0) continue;
|
|
556
|
-
if (LOGICAL_KEYS.has(key2)) {
|
|
557
|
-
if (key2 === "and" || key2 === "or") {
|
|
558
|
-
const parts = value.map((sub) => compileNode(ctx, sub)).filter((s) => s.length > 0);
|
|
559
|
-
if (parts.length === 0) continue;
|
|
560
|
-
if (parts.length === 1) {
|
|
561
|
-
clauses.push(parts[0]);
|
|
562
|
-
} else {
|
|
563
|
-
const sep = key2 === "and" ? " AND " : " OR ";
|
|
564
|
-
const joined = parts.map((p) => wrap(p)).join(sep);
|
|
565
|
-
clauses.push(`(${joined})`);
|
|
566
|
-
}
|
|
567
|
-
} else {
|
|
568
|
-
const inner = compileNode(ctx, value);
|
|
569
|
-
if (inner.length > 0) clauses.push(`NOT ${wrap(inner)}`);
|
|
570
|
-
}
|
|
571
|
-
continue;
|
|
572
|
-
}
|
|
573
|
-
const clause = compileField(ctx, key2, value);
|
|
574
|
-
if (clause.length > 0) clauses.push(clause);
|
|
575
|
-
}
|
|
576
|
-
return joinAnd(clauses);
|
|
577
|
-
}
|
|
578
|
-
function compileFilterExpression(filter, metadata) {
|
|
579
|
-
if (!filter || Object.keys(filter).length === 0) {
|
|
580
|
-
return void 0;
|
|
581
|
-
}
|
|
582
|
-
const ctx = {
|
|
583
|
-
names: {},
|
|
584
|
-
values: {},
|
|
585
|
-
fieldMap: new Map(metadata.fields.map((f) => [f.propertyName, f])),
|
|
586
|
-
nameCounter: { n: 0 },
|
|
587
|
-
valueCounter: { n: 0 }
|
|
588
|
-
};
|
|
589
|
-
const expression = compileNode(ctx, filter);
|
|
590
|
-
if (expression.length === 0) {
|
|
591
|
-
return void 0;
|
|
592
|
-
}
|
|
593
|
-
return {
|
|
594
|
-
filterExpression: expression,
|
|
595
|
-
expressionAttributeNames: ctx.names,
|
|
596
|
-
expressionAttributeValues: ctx.values
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
function evaluateFilter(item, filter) {
|
|
600
|
-
for (const [key2, value] of Object.entries(filter)) {
|
|
601
|
-
if (value === void 0) continue;
|
|
602
|
-
if (key2 === "and") {
|
|
603
|
-
if (!value.every((s) => evaluateFilter(item, s)))
|
|
604
|
-
return false;
|
|
605
|
-
continue;
|
|
606
|
-
}
|
|
607
|
-
if (key2 === "or") {
|
|
608
|
-
if (!value.some((s) => evaluateFilter(item, s)))
|
|
609
|
-
return false;
|
|
610
|
-
continue;
|
|
611
|
-
}
|
|
612
|
-
if (key2 === "not") {
|
|
613
|
-
if (evaluateFilter(item, value)) return false;
|
|
614
|
-
continue;
|
|
615
|
-
}
|
|
616
|
-
if (!evaluateFieldCondition(item[key2], item, key2, value)) {
|
|
617
|
-
return false;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
return true;
|
|
621
|
-
}
|
|
622
|
-
function cmp(a, b) {
|
|
623
|
-
if (a instanceof Date && b instanceof Date) {
|
|
624
|
-
return a.getTime() - b.getTime();
|
|
625
|
-
}
|
|
626
|
-
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
627
|
-
if (typeof a === "string" && typeof b === "string")
|
|
628
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
629
|
-
if (a === b) return 0;
|
|
630
|
-
return a < b ? -1 : 1;
|
|
631
|
-
}
|
|
632
|
-
function eq(a, b) {
|
|
633
|
-
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
|
634
|
-
return a === b;
|
|
635
|
-
}
|
|
636
|
-
function evaluateFieldCondition(actual, item, field2, condition) {
|
|
637
|
-
if (!isOperatorObject(condition)) {
|
|
638
|
-
return eq(actual, condition);
|
|
639
|
-
}
|
|
640
|
-
const ops = condition;
|
|
641
|
-
for (const [op, value] of Object.entries(ops)) {
|
|
642
|
-
switch (op) {
|
|
643
|
-
case "eq":
|
|
644
|
-
if (!eq(actual, value)) return false;
|
|
645
|
-
break;
|
|
646
|
-
case "ne":
|
|
647
|
-
if (eq(actual, value)) return false;
|
|
648
|
-
break;
|
|
649
|
-
case "gt":
|
|
650
|
-
if (!(cmp(actual, value) > 0)) return false;
|
|
651
|
-
break;
|
|
652
|
-
case "ge":
|
|
653
|
-
if (!(cmp(actual, value) >= 0)) return false;
|
|
654
|
-
break;
|
|
655
|
-
case "lt":
|
|
656
|
-
if (!(cmp(actual, value) < 0)) return false;
|
|
657
|
-
break;
|
|
658
|
-
case "le":
|
|
659
|
-
if (!(cmp(actual, value) <= 0)) return false;
|
|
660
|
-
break;
|
|
661
|
-
case "between": {
|
|
662
|
-
const [lo, hi] = value;
|
|
663
|
-
if (!(cmp(actual, lo) >= 0 && cmp(actual, hi) <= 0)) return false;
|
|
664
|
-
break;
|
|
665
|
-
}
|
|
666
|
-
case "in":
|
|
667
|
-
if (!value.some((v) => eq(actual, v))) return false;
|
|
668
|
-
break;
|
|
669
|
-
case "beginsWith":
|
|
670
|
-
if (typeof actual !== "string" || !actual.startsWith(value))
|
|
671
|
-
return false;
|
|
672
|
-
break;
|
|
673
|
-
case "contains":
|
|
674
|
-
if (typeof actual !== "string" || !actual.includes(value))
|
|
675
|
-
return false;
|
|
676
|
-
break;
|
|
677
|
-
case "notContains":
|
|
678
|
-
if (typeof actual === "string" && actual.includes(value))
|
|
679
|
-
return false;
|
|
680
|
-
break;
|
|
681
|
-
case "attributeExists": {
|
|
682
|
-
const exists = field2 in item && actual !== void 0 && actual !== null;
|
|
683
|
-
if (value === false ? exists : !exists) return false;
|
|
684
|
-
break;
|
|
685
|
-
}
|
|
686
|
-
case "attributeType":
|
|
687
|
-
break;
|
|
688
|
-
case "size": {
|
|
689
|
-
const len = typeof actual === "string" || Array.isArray(actual) ? actual.length : void 0;
|
|
690
|
-
if (len !== value) return false;
|
|
691
|
-
break;
|
|
692
|
-
}
|
|
693
|
-
default:
|
|
694
|
-
throw new Error(`Unknown filter operator '${op}' on field '${field2}'`);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
return true;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// src/planner/planner.ts
|
|
701
|
-
function plan(metadata, input) {
|
|
702
|
-
const queryFields = Object.keys(input.key);
|
|
703
|
-
const resolved = resolveKey(queryFields, metadata);
|
|
704
|
-
if (input.consistentRead && resolved.type === "gsi") {
|
|
705
|
-
throw new Error(
|
|
706
|
-
"consistentRead is not supported for GSI queries. Use a primary key query instead."
|
|
707
|
-
);
|
|
708
|
-
}
|
|
709
|
-
const tableName = TableMapping.resolve(metadata.tableName);
|
|
710
|
-
const built = buildKeyCondition(metadata, resolved, input.key);
|
|
711
|
-
const keyCondition = built.keyCondition;
|
|
712
|
-
const additionalProjectionFields = input.updatable ? Array.from(
|
|
713
|
-
/* @__PURE__ */ new Set([...input.additionalProjectionFields ?? [], "PK", "SK"])
|
|
714
|
-
) : input.additionalProjectionFields;
|
|
715
|
-
const projection = buildProjection(input.select, additionalProjectionFields);
|
|
716
|
-
const compiledFilter = compileFilterExpression(input.filter, metadata);
|
|
717
|
-
const isGetItem = resolved.type === "pk" && !resolved.partial && built.skValue !== void 0 && // GetItem does not support FilterExpression — fall back to Query when a
|
|
718
|
-
// server-side filter is requested so the condition can be applied.
|
|
719
|
-
!compiledFilter;
|
|
720
|
-
const projectionFields = projection ? {
|
|
721
|
-
projectionExpression: projection.projectionExpression,
|
|
722
|
-
expressionAttributeNames: projection.expressionAttributeNames
|
|
723
|
-
} : {};
|
|
724
|
-
const filterFields = compiledFilter ? {
|
|
725
|
-
filterExpression: compiledFilter.filterExpression,
|
|
726
|
-
filterExpressionAttributeNames: compiledFilter.expressionAttributeNames,
|
|
727
|
-
filterExpressionAttributeValues: compiledFilter.expressionAttributeValues
|
|
728
|
-
} : {};
|
|
729
|
-
const consistentRead = input.consistentRead && resolved.type === "pk" ? { consistentRead: true } : {};
|
|
730
|
-
let op;
|
|
731
|
-
if (isGetItem) {
|
|
732
|
-
op = {
|
|
733
|
-
type: "GetItem",
|
|
734
|
-
tableName,
|
|
735
|
-
keyCondition,
|
|
736
|
-
...projectionFields,
|
|
737
|
-
...consistentRead
|
|
738
|
-
};
|
|
739
|
-
} else {
|
|
740
|
-
const queryOp = {
|
|
741
|
-
type: "Query",
|
|
742
|
-
tableName,
|
|
743
|
-
keyCondition,
|
|
744
|
-
scanIndexForward: (input.order ?? "ASC") === "ASC",
|
|
745
|
-
...projectionFields,
|
|
746
|
-
...filterFields,
|
|
747
|
-
...consistentRead
|
|
748
|
-
};
|
|
749
|
-
if (resolved.type === "gsi") {
|
|
750
|
-
queryOp.indexName = resolved.indexName;
|
|
751
|
-
}
|
|
752
|
-
if (built.rangeCondition) {
|
|
753
|
-
queryOp.rangeCondition = built.rangeCondition;
|
|
754
|
-
}
|
|
755
|
-
if (input.limit != null) {
|
|
756
|
-
queryOp.limit = input.limit;
|
|
757
|
-
}
|
|
758
|
-
if (input.after) {
|
|
759
|
-
queryOp.exclusiveStartKey = input.after;
|
|
760
|
-
}
|
|
761
|
-
op = queryOp;
|
|
762
|
-
}
|
|
763
|
-
return { operations: [op] };
|
|
764
|
-
}
|
|
765
|
-
function buildKeyCondition(metadata, resolved, queryKey) {
|
|
766
|
-
if (resolved.type === "pk") {
|
|
767
|
-
if (!metadata.primaryKey) throw new Error("Primary key not defined");
|
|
768
|
-
return buildFromSegments(
|
|
769
|
-
metadata.primaryKey.segmented,
|
|
770
|
-
metadata.primaryKey.inputFieldNames,
|
|
771
|
-
queryKey,
|
|
772
|
-
"PK",
|
|
773
|
-
"SK"
|
|
774
|
-
);
|
|
775
|
-
}
|
|
776
|
-
const gsiDef = metadata.gsiDefinitions.find(
|
|
777
|
-
(g) => g.indexName === resolved.indexName
|
|
778
|
-
);
|
|
779
|
-
if (!gsiDef) throw new Error(`GSI '${resolved.indexName}' not found`);
|
|
780
|
-
return buildFromSegments(
|
|
781
|
-
gsiDef.segmented,
|
|
782
|
-
gsiDef.inputFieldNames,
|
|
783
|
-
queryKey,
|
|
784
|
-
`${gsiDef.indexName}PK`,
|
|
785
|
-
`${gsiDef.indexName}SK`
|
|
786
|
-
);
|
|
787
|
-
}
|
|
788
|
-
function buildFromSegments(segmented, inputFieldNames, queryKey, pkAttr, skAttr) {
|
|
789
|
-
const keyInput = {};
|
|
790
|
-
for (const fieldName of inputFieldNames) {
|
|
791
|
-
keyInput[fieldName] = queryKey[fieldName];
|
|
792
|
-
}
|
|
793
|
-
const { pk, sk, skTruncated } = resolveSegmentedKey(segmented, keyInput);
|
|
794
|
-
const keyCondition = { [pkAttr]: pk };
|
|
795
|
-
if (sk === void 0) {
|
|
796
|
-
return { keyCondition };
|
|
797
|
-
}
|
|
798
|
-
if (skTruncated) {
|
|
799
|
-
return {
|
|
800
|
-
keyCondition,
|
|
801
|
-
rangeCondition: { operator: "begins_with", key: skAttr, value: sk }
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
keyCondition[skAttr] = sk;
|
|
805
|
-
return { keyCondition, skValue: sk };
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// src/executor/executor.ts
|
|
809
|
-
async function execute(operation) {
|
|
810
|
-
return ClientManager.getExecutor().execute(operation);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// src/hydrator/hydrator.ts
|
|
814
|
-
function hydrate(rawItems, select, metadata, updatable = false) {
|
|
815
|
-
return rawItems.map((item) => hydrateItem(item, select, metadata, updatable));
|
|
816
|
-
}
|
|
817
|
-
function hydrateItem(raw, select, metadata, updatable = false) {
|
|
818
|
-
const fieldMap = new Map(
|
|
819
|
-
metadata.fields.map((f) => [f.propertyName, f])
|
|
820
|
-
);
|
|
821
|
-
const embeddedMap = new Map(
|
|
822
|
-
metadata.embeddedFields.map((e) => [e.propertyName, e])
|
|
823
|
-
);
|
|
824
|
-
const result = {};
|
|
825
|
-
for (const [fieldName, selectValue] of Object.entries(select)) {
|
|
826
|
-
if (selectValue === true) {
|
|
827
|
-
const value = raw[fieldName];
|
|
828
|
-
if (value !== void 0) {
|
|
829
|
-
const fieldMeta = fieldMap.get(fieldName);
|
|
830
|
-
result[fieldName] = deserializeValue(value, fieldMeta);
|
|
831
|
-
}
|
|
832
|
-
} else if (typeof selectValue === "object" && selectValue !== null) {
|
|
833
|
-
if ("select" in selectValue || isSelectBuilder(selectValue)) {
|
|
834
|
-
continue;
|
|
835
|
-
}
|
|
836
|
-
const rawEmb = raw[fieldName];
|
|
837
|
-
if (rawEmb && typeof rawEmb === "object") {
|
|
838
|
-
result[fieldName] = hydrateEmbedded(
|
|
839
|
-
rawEmb,
|
|
840
|
-
selectValue,
|
|
841
|
-
metadata,
|
|
842
|
-
fieldName
|
|
843
|
-
);
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
if (updatable) {
|
|
848
|
-
attachHiddenKey(result, raw);
|
|
849
|
-
}
|
|
850
|
-
return result;
|
|
851
|
-
}
|
|
852
|
-
function hydrateEmbedded(raw, select, _metadata, _fieldName) {
|
|
853
|
-
const result = {};
|
|
854
|
-
for (const [key2, selectValue] of Object.entries(select)) {
|
|
855
|
-
if (selectValue === true) {
|
|
856
|
-
if (raw[key2] !== void 0) {
|
|
857
|
-
result[key2] = raw[key2];
|
|
858
|
-
}
|
|
859
|
-
} else if (typeof selectValue === "object" && selectValue !== null && !("select" in selectValue)) {
|
|
860
|
-
const nested = raw[key2];
|
|
861
|
-
if (nested && typeof nested === "object") {
|
|
862
|
-
result[key2] = hydrateEmbedded(
|
|
863
|
-
nested,
|
|
864
|
-
selectValue,
|
|
865
|
-
_metadata,
|
|
866
|
-
key2
|
|
867
|
-
);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
return result;
|
|
872
|
-
}
|
|
873
|
-
function deserializeValue(value, fieldMeta) {
|
|
874
|
-
if (!fieldMeta) return value;
|
|
875
|
-
if (fieldMeta.options?.deserialize) {
|
|
876
|
-
return fieldMeta.options.deserialize(value);
|
|
877
|
-
}
|
|
878
|
-
if (fieldMeta.options?.format === "datetime" && typeof value === "string") {
|
|
879
|
-
return new Date(value);
|
|
880
|
-
}
|
|
881
|
-
if (fieldMeta.options?.format === "date" && typeof value === "string") {
|
|
882
|
-
return /* @__PURE__ */ new Date(value + "T00:00:00.000Z");
|
|
883
|
-
}
|
|
884
|
-
return value;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// src/pagination/cursor.ts
|
|
888
|
-
function encodeCursor(lastEvaluatedKey) {
|
|
889
|
-
const json = JSON.stringify(lastEvaluatedKey);
|
|
890
|
-
const bytes = new TextEncoder().encode(json);
|
|
891
|
-
let binary2 = "";
|
|
892
|
-
for (const b of bytes) {
|
|
893
|
-
binary2 += String.fromCharCode(b);
|
|
894
|
-
}
|
|
895
|
-
return btoa(binary2).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
896
|
-
}
|
|
897
|
-
function decodeCursor(cursor) {
|
|
898
|
-
let base64 = cursor.replace(/-/g, "+").replace(/_/g, "/");
|
|
899
|
-
while (base64.length % 4 !== 0) {
|
|
900
|
-
base64 += "=";
|
|
901
|
-
}
|
|
902
|
-
const binary2 = atob(base64);
|
|
903
|
-
const bytes = new Uint8Array(binary2.length);
|
|
904
|
-
for (let i = 0; i < binary2.length; i++) {
|
|
905
|
-
bytes[i] = binary2.charCodeAt(i);
|
|
906
|
-
}
|
|
907
|
-
const json = new TextDecoder().decode(bytes);
|
|
908
|
-
return JSON.parse(json);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// src/operations/list.ts
|
|
912
|
-
async function executeListInternal(modelClass, key2, options) {
|
|
913
|
-
const metadata = MetadataRegistry.get(modelClass);
|
|
914
|
-
const normalized = normalizeTopLevelSelect(options.select);
|
|
915
|
-
const select = normalized.select;
|
|
916
|
-
const filter = options.filter ?? normalized.filter;
|
|
917
|
-
const after = options.after ?? normalized.after;
|
|
918
|
-
const limit = options.limit ?? normalized.limit;
|
|
919
|
-
const order = options.order ?? normalized.order;
|
|
920
|
-
const exclusiveStartKey = after ? decodeCursor(after) : void 0;
|
|
921
|
-
const implicitKeys = getImplicitKeyFields(select, metadata);
|
|
922
|
-
const executionPlan = plan(metadata, {
|
|
923
|
-
key: key2,
|
|
924
|
-
select,
|
|
925
|
-
limit,
|
|
926
|
-
after: exclusiveStartKey,
|
|
927
|
-
order,
|
|
928
|
-
filter,
|
|
929
|
-
additionalProjectionFields: implicitKeys.length > 0 ? implicitKeys : void 0,
|
|
930
|
-
updatable: options.updatable ?? false
|
|
931
|
-
});
|
|
932
|
-
const operation = executionPlan.operations[0];
|
|
933
|
-
const result = await execute(operation);
|
|
934
|
-
const rawItems = result.items;
|
|
935
|
-
const hydrated = hydrate(rawItems, select, metadata, options.updatable ?? false);
|
|
936
|
-
const cursor = result.lastEvaluatedKey ? encodeCursor(result.lastEvaluatedKey) : null;
|
|
937
|
-
return { items: hydrated, rawItems, cursor };
|
|
938
|
-
}
|
|
939
|
-
async function executeList(modelClass, key2, options) {
|
|
940
|
-
const { items, cursor } = await executeListInternal(modelClass, key2, options);
|
|
941
|
-
return { items, cursor };
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
// src/planner/relation-planner.ts
|
|
945
|
-
function buildRelationQueryKey(rel, rawParentItem) {
|
|
946
|
-
const queryKey = {};
|
|
947
|
-
for (const [targetField, sourceField] of Object.entries(rel.keyBinding)) {
|
|
948
|
-
queryKey[targetField] = rawParentItem[sourceField];
|
|
949
|
-
}
|
|
950
|
-
return queryKey;
|
|
951
|
-
}
|
|
952
|
-
function serializeQueryKey(queryKey) {
|
|
953
|
-
const entries = Object.entries(queryKey).sort(([a], [b]) => a.localeCompare(b));
|
|
954
|
-
return JSON.stringify(entries);
|
|
955
|
-
}
|
|
956
|
-
function planGetItemForQueryKey(metadata, queryKey, select, additionalProjectionFields) {
|
|
957
|
-
const executionPlan = plan(metadata, {
|
|
958
|
-
key: queryKey,
|
|
959
|
-
select,
|
|
960
|
-
additionalProjectionFields
|
|
961
|
-
});
|
|
962
|
-
const operation = executionPlan.operations[0];
|
|
963
|
-
if (operation.type !== "GetItem") {
|
|
964
|
-
throw new Error(
|
|
965
|
-
`Relation target requires GetItem access pattern, got ${operation.type}`
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
return operation;
|
|
969
|
-
}
|
|
970
|
-
function planBatchGetForQueryKeys(metadata, queryKeys, select, additionalProjectionFields) {
|
|
971
|
-
if (queryKeys.length === 0) {
|
|
972
|
-
return [];
|
|
973
|
-
}
|
|
974
|
-
const keyFields = [
|
|
975
|
-
...new Set(queryKeys.flatMap((queryKey) => Object.keys(queryKey)))
|
|
976
|
-
].filter((field2) => !(field2 in select && select[field2] === true));
|
|
977
|
-
const projectionFields = [
|
|
978
|
-
.../* @__PURE__ */ new Set([
|
|
979
|
-
...(additionalProjectionFields ?? []).filter(
|
|
980
|
-
(field2) => !(field2 in select && select[field2] === true)
|
|
981
|
-
),
|
|
982
|
-
...keyFields
|
|
983
|
-
])
|
|
984
|
-
];
|
|
985
|
-
const samplePlan = planGetItemForQueryKey(
|
|
986
|
-
metadata,
|
|
987
|
-
queryKeys[0],
|
|
988
|
-
select,
|
|
989
|
-
projectionFields.length > 0 ? projectionFields : void 0
|
|
990
|
-
);
|
|
991
|
-
const uniqueKeys = dedupeDynamoKeys(
|
|
992
|
-
queryKeys.map(
|
|
993
|
-
(queryKey) => planGetItemForQueryKey(
|
|
994
|
-
metadata,
|
|
995
|
-
queryKey,
|
|
996
|
-
select,
|
|
997
|
-
projectionFields.length > 0 ? projectionFields : void 0
|
|
998
|
-
).keyCondition
|
|
999
|
-
)
|
|
1000
|
-
);
|
|
1001
|
-
const operations = [];
|
|
1002
|
-
for (let i = 0; i < uniqueKeys.length; i += BATCH_GET_MAX_KEYS) {
|
|
1003
|
-
operations.push({
|
|
1004
|
-
type: "BatchGetItem",
|
|
1005
|
-
tableName: samplePlan.tableName,
|
|
1006
|
-
keys: uniqueKeys.slice(i, i + BATCH_GET_MAX_KEYS),
|
|
1007
|
-
...samplePlan.projectionExpression ? { projectionExpression: samplePlan.projectionExpression } : {},
|
|
1008
|
-
...samplePlan.expressionAttributeNames ? { expressionAttributeNames: samplePlan.expressionAttributeNames } : {},
|
|
1009
|
-
...samplePlan.consistentRead ? { consistentRead: true } : {}
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
return operations;
|
|
1013
|
-
}
|
|
1014
|
-
function dedupeDynamoKeys(keys) {
|
|
1015
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1016
|
-
const result = [];
|
|
1017
|
-
for (const key2 of keys) {
|
|
1018
|
-
const serialized = JSON.stringify(
|
|
1019
|
-
Object.entries(key2).sort(([a], [b]) => a.localeCompare(b))
|
|
1020
|
-
);
|
|
1021
|
-
if (seen.has(serialized)) continue;
|
|
1022
|
-
seen.add(serialized);
|
|
1023
|
-
result.push(key2);
|
|
1024
|
-
}
|
|
1025
|
-
return result;
|
|
1026
|
-
}
|
|
1027
|
-
function planRelationOperations(metadata, select, context = { estimatedParentCount: 1 }) {
|
|
1028
|
-
const operations = [];
|
|
1029
|
-
const relations = detectRelationFields(select, metadata);
|
|
1030
|
-
for (const rel of relations) {
|
|
1031
|
-
const selectSpec = normalizeSelectSpec(select[rel.propertyName]);
|
|
1032
|
-
const targetClass = rel.targetFactory();
|
|
1033
|
-
const targetMeta = MetadataRegistry.get(targetClass);
|
|
1034
|
-
if (rel.type === "hasMany") {
|
|
1035
|
-
const limit = selectSpec.limit ?? rel.options?.limit?.default ?? 20;
|
|
1036
|
-
const tableName = TableMapping.resolve(targetMeta.tableName);
|
|
1037
|
-
const listPlan = plan(targetMeta, {
|
|
1038
|
-
key: buildPlaceholderHasManyKey(rel, targetMeta),
|
|
1039
|
-
select: selectSpec.select,
|
|
1040
|
-
limit
|
|
1041
|
-
});
|
|
1042
|
-
operations.push(...listPlan.operations);
|
|
1043
|
-
operations.push(
|
|
1044
|
-
...planRelationOperations(targetMeta, selectSpec.select, {
|
|
1045
|
-
estimatedParentCount: limit
|
|
1046
|
-
})
|
|
1047
|
-
);
|
|
1048
|
-
continue;
|
|
1049
|
-
}
|
|
1050
|
-
const estimatedCount = context.estimatedParentCount ?? 1;
|
|
1051
|
-
operations.push(
|
|
1052
|
-
...planBatchGetForExplain(targetMeta, selectSpec.select, estimatedCount)
|
|
1053
|
-
);
|
|
1054
|
-
operations.push(
|
|
1055
|
-
...planRelationOperations(targetMeta, selectSpec.select, {
|
|
1056
|
-
estimatedParentCount: 1
|
|
1057
|
-
})
|
|
1058
|
-
);
|
|
1059
|
-
}
|
|
1060
|
-
return operations;
|
|
1061
|
-
}
|
|
1062
|
-
function buildPlaceholderHasManyKey(rel, targetMeta) {
|
|
1063
|
-
const queryKey = {};
|
|
1064
|
-
for (const targetField of Object.keys(rel.keyBinding)) {
|
|
1065
|
-
queryKey[targetField] = `__${targetField}__`;
|
|
1066
|
-
}
|
|
1067
|
-
if (targetMeta.primaryKey) {
|
|
1068
|
-
for (const fieldName of targetMeta.primaryKey.inputFieldNames) {
|
|
1069
|
-
if (!(fieldName in queryKey)) {
|
|
1070
|
-
queryKey[fieldName] = `__${fieldName}__`;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
return queryKey;
|
|
1075
|
-
}
|
|
1076
|
-
function planBatchGetForExplain(metadata, select, estimatedKeyCount) {
|
|
1077
|
-
const placeholderKeys = Array.from(
|
|
1078
|
-
{ length: estimatedKeyCount },
|
|
1079
|
-
(_, index) => buildExplainPlaceholderKey(metadata, index)
|
|
1080
|
-
);
|
|
1081
|
-
return planBatchGetForQueryKeys(metadata, placeholderKeys, select);
|
|
1082
|
-
}
|
|
1083
|
-
function buildExplainPlaceholderKey(metadata, index) {
|
|
1084
|
-
const queryKey = {};
|
|
1085
|
-
if (metadata.primaryKey) {
|
|
1086
|
-
for (const fieldName of metadata.primaryKey.inputFieldNames) {
|
|
1087
|
-
queryKey[fieldName] = `__explain_${fieldName}_${index}__`;
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
return queryKey;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
// src/relation/traversal.ts
|
|
1094
|
-
function evaluateFilterClientSide(item, filter, _targetMeta) {
|
|
1095
|
-
return evaluateFilter(item, filter);
|
|
1096
|
-
}
|
|
1097
|
-
function relationSpec(value) {
|
|
1098
|
-
return normalizeSelectSpec(value);
|
|
1099
|
-
}
|
|
1100
|
-
function validateDepth(select, metadata, maxDepth, currentDepth = 1) {
|
|
1101
|
-
for (const [fieldName, value] of Object.entries(select)) {
|
|
1102
|
-
if (typeof value === "object" && value !== null && ("select" in value || isSelectBuilder(value))) {
|
|
1103
|
-
const rel = metadata.relations.find((r) => r.propertyName === fieldName);
|
|
1104
|
-
if (!rel) continue;
|
|
1105
|
-
if (currentDepth > maxDepth) {
|
|
1106
|
-
throw new Error(
|
|
1107
|
-
`Relation traversal depth exceeded: '${fieldName}' at depth ${currentDepth} exceeds maxDepth ${maxDepth}. Set maxDepth: ${currentDepth} to allow this.`
|
|
1108
|
-
);
|
|
1109
|
-
}
|
|
1110
|
-
const targetClass = rel.targetFactory();
|
|
1111
|
-
const targetMeta = MetadataRegistry.get(targetClass);
|
|
1112
|
-
const innerSelect = relationSpec(value).select;
|
|
1113
|
-
validateDepth(innerSelect, targetMeta, maxDepth, currentDepth + 1);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
function relationResultPath(parentPath, rel) {
|
|
1118
|
-
const base = `${parentPath}.${rel.propertyName}`;
|
|
1119
|
-
return rel.type === "hasMany" ? `${base}.items` : base;
|
|
1120
|
-
}
|
|
1121
|
-
function stageRelations(relations, parentPath, plan2) {
|
|
1122
|
-
if (plan2 === void 0 || relations.length === 0) {
|
|
1123
|
-
return relations.length > 0 ? { stages: [[...relations]], concurrency: RELATION_TRAVERSAL_CONCURRENCY } : { stages: [], concurrency: RELATION_TRAVERSAL_CONCURRENCY };
|
|
1124
|
-
}
|
|
1125
|
-
const byStage = /* @__PURE__ */ new Map();
|
|
1126
|
-
const unstaged = [];
|
|
1127
|
-
for (const rel of relations) {
|
|
1128
|
-
const stage = stageOfResultPath(plan2, relationResultPath(parentPath, rel));
|
|
1129
|
-
if (stage === void 0) {
|
|
1130
|
-
unstaged.push(rel);
|
|
1131
|
-
continue;
|
|
1132
|
-
}
|
|
1133
|
-
const bucket = byStage.get(stage);
|
|
1134
|
-
if (bucket) bucket.push(rel);
|
|
1135
|
-
else byStage.set(stage, [rel]);
|
|
1136
|
-
}
|
|
1137
|
-
const stages = [...byStage.keys()].sort((a, b) => a - b).map((s) => byStage.get(s));
|
|
1138
|
-
if (unstaged.length > 0) stages.push(unstaged);
|
|
1139
|
-
return { stages, concurrency: plan2.plan.concurrency };
|
|
1140
|
-
}
|
|
1141
|
-
async function runStaged(relations, parentPath, plan2, worker) {
|
|
1142
|
-
const { stages, concurrency } = stageRelations(relations, parentPath, plan2);
|
|
1143
|
-
for (const stage of stages) {
|
|
1144
|
-
await mapWithConcurrency(stage, concurrency, (rel) => worker(rel));
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
function hasCompleteQueryKey(queryKey) {
|
|
1148
|
-
return Object.values(queryKey).every((value) => value != null);
|
|
1149
|
-
}
|
|
1150
|
-
function matchesQueryKey(item, queryKey) {
|
|
1151
|
-
return Object.entries(queryKey).every(
|
|
1152
|
-
([field2, value]) => item[field2] === value
|
|
1153
|
-
);
|
|
1154
|
-
}
|
|
1155
|
-
async function fetchBelongsToHasOneBatch(rel, rawParentItems, selectSpec, targetMeta, options, currentDepth, parentPath) {
|
|
1156
|
-
const results = rawParentItems.map(
|
|
1157
|
-
() => null
|
|
1158
|
-
);
|
|
1159
|
-
const queryKeys = [];
|
|
1160
|
-
const parentIndexes = [];
|
|
1161
|
-
for (let i = 0; i < rawParentItems.length; i++) {
|
|
1162
|
-
const queryKey = buildRelationQueryKey(rel, rawParentItems[i]);
|
|
1163
|
-
if (!hasCompleteQueryKey(queryKey)) {
|
|
1164
|
-
continue;
|
|
1165
|
-
}
|
|
1166
|
-
queryKeys.push(queryKey);
|
|
1167
|
-
parentIndexes.push(i);
|
|
1168
|
-
}
|
|
1169
|
-
if (queryKeys.length === 0) {
|
|
1170
|
-
return results;
|
|
1171
|
-
}
|
|
1172
|
-
const implicitKeys = getImplicitKeyFields(selectSpec.select, targetMeta);
|
|
1173
|
-
const projectionExtras = options.updatable ? [...implicitKeys, "PK", "SK"] : implicitKeys;
|
|
1174
|
-
const batchOps = planBatchGetForQueryKeys(
|
|
1175
|
-
targetMeta,
|
|
1176
|
-
queryKeys,
|
|
1177
|
-
selectSpec.select,
|
|
1178
|
-
projectionExtras.length > 0 ? projectionExtras : void 0
|
|
1179
|
-
);
|
|
1180
|
-
const executor = ClientManager.getExecutor();
|
|
1181
|
-
const queryKeyToRaw = /* @__PURE__ */ new Map();
|
|
1182
|
-
for (const operation of batchOps) {
|
|
1183
|
-
const batchResult = await executor.batchGet(operation);
|
|
1184
|
-
for (const item of batchResult.items) {
|
|
1185
|
-
for (const queryKey of queryKeys) {
|
|
1186
|
-
if (!matchesQueryKey(item, queryKey)) {
|
|
1187
|
-
continue;
|
|
1188
|
-
}
|
|
1189
|
-
queryKeyToRaw.set(serializeQueryKey(queryKey), item);
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
const nestedRelations = detectRelationFields(selectSpec.select, targetMeta);
|
|
1194
|
-
const ownPath = relationResultPath(parentPath, rel);
|
|
1195
|
-
await mapWithConcurrency(
|
|
1196
|
-
queryKeys,
|
|
1197
|
-
options.plan?.plan.concurrency ?? RELATION_TRAVERSAL_CONCURRENCY,
|
|
1198
|
-
async (queryKey, j) => {
|
|
1199
|
-
const parentIndex = parentIndexes[j];
|
|
1200
|
-
const raw = queryKeyToRaw.get(serializeQueryKey(queryKey));
|
|
1201
|
-
if (!raw) {
|
|
1202
|
-
return;
|
|
1203
|
-
}
|
|
1204
|
-
const updatable = options.updatable ?? false;
|
|
1205
|
-
let resolvedItem = hydrate([raw], selectSpec.select, targetMeta, updatable)[0] ?? null;
|
|
1206
|
-
if (resolvedItem && nestedRelations.length > 0) {
|
|
1207
|
-
resolvedItem = await resolveRelations(
|
|
1208
|
-
resolvedItem,
|
|
1209
|
-
raw,
|
|
1210
|
-
selectSpec.select,
|
|
1211
|
-
targetMeta,
|
|
1212
|
-
options,
|
|
1213
|
-
currentDepth + 1,
|
|
1214
|
-
ownPath
|
|
1215
|
-
);
|
|
1216
|
-
if (resolvedItem && updatable) {
|
|
1217
|
-
attachHiddenKey(resolvedItem, raw);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
if (resolvedItem && !passesPostLoad(resolvedItem, selectSpec, targetMeta)) {
|
|
1221
|
-
results[parentIndex] = null;
|
|
1222
|
-
} else {
|
|
1223
|
-
results[parentIndex] = resolvedItem;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
);
|
|
1227
|
-
return results;
|
|
1228
|
-
}
|
|
1229
|
-
function passesPostLoad(item, spec, targetMeta) {
|
|
1230
|
-
if (spec.filter && !evaluateFilterClientSide(item, spec.filter, targetMeta)) {
|
|
1231
|
-
return false;
|
|
1232
|
-
}
|
|
1233
|
-
return true;
|
|
1234
|
-
}
|
|
1235
|
-
async function resolveSingleValueRelationsBatch(parentItems, rawParentItems, select, metadata, options, currentDepth, parentPath) {
|
|
1236
|
-
const results = parentItems.map((item, i) => {
|
|
1237
|
-
const copy = { ...item };
|
|
1238
|
-
if (options.updatable) {
|
|
1239
|
-
attachHiddenKey(copy, rawParentItems[i]);
|
|
1240
|
-
}
|
|
1241
|
-
return copy;
|
|
1242
|
-
});
|
|
1243
|
-
const relations = detectRelationFields(select, metadata).filter(
|
|
1244
|
-
(rel) => rel.type === "belongsTo" || rel.type === "hasOne"
|
|
1245
|
-
);
|
|
1246
|
-
await runStaged(relations, parentPath, options.plan, async (rel) => {
|
|
1247
|
-
const selectSpec = relationSpec(select[rel.propertyName]);
|
|
1248
|
-
const targetClass = rel.targetFactory();
|
|
1249
|
-
const targetMeta = MetadataRegistry.get(targetClass);
|
|
1250
|
-
const batchResults = await fetchBelongsToHasOneBatch(
|
|
1251
|
-
rel,
|
|
1252
|
-
rawParentItems,
|
|
1253
|
-
selectSpec,
|
|
1254
|
-
targetMeta,
|
|
1255
|
-
options,
|
|
1256
|
-
currentDepth,
|
|
1257
|
-
parentPath
|
|
1258
|
-
);
|
|
1259
|
-
for (let i = 0; i < results.length; i++) {
|
|
1260
|
-
results[i][rel.propertyName] = batchResults[i];
|
|
1261
|
-
}
|
|
1262
|
-
});
|
|
1263
|
-
return results;
|
|
1264
|
-
}
|
|
1265
|
-
async function resolveRelations(parentItem, rawParentItem, select, metadata, options, currentDepth = 1, currentPath = "$") {
|
|
1266
|
-
const result = { ...parentItem };
|
|
1267
|
-
const relations = detectRelationFields(select, metadata);
|
|
1268
|
-
const plan2 = options.plan ?? (currentPath === "$" ? buildRelationExecutionPlan(select, metadata) : void 0);
|
|
1269
|
-
const opts = plan2 === options.plan ? options : { ...options, plan: plan2 };
|
|
1270
|
-
await runStaged(relations, currentPath, opts.plan, async (rel) => {
|
|
1271
|
-
const selectSpec = relationSpec(select[rel.propertyName]);
|
|
1272
|
-
const ownPath = relationResultPath(currentPath, rel);
|
|
1273
|
-
if (rel.type === "hasMany") {
|
|
1274
|
-
const targetClass2 = rel.targetFactory();
|
|
1275
|
-
const queryKey = buildRelationQueryKey(rel, rawParentItem);
|
|
1276
|
-
const limit = selectSpec.limit ?? rel.options?.limit?.default ?? 20;
|
|
1277
|
-
const order = selectSpec.order ?? rel.options?.order ?? "ASC";
|
|
1278
|
-
const listResult = await executeListInternal(targetClass2, queryKey, {
|
|
1279
|
-
select: selectSpec.select,
|
|
1280
|
-
limit,
|
|
1281
|
-
after: selectSpec.after,
|
|
1282
|
-
order,
|
|
1283
|
-
filter: selectSpec.filter,
|
|
1284
|
-
updatable: opts.updatable
|
|
1285
|
-
});
|
|
1286
|
-
let items = listResult.items;
|
|
1287
|
-
const rawItems = listResult.rawItems;
|
|
1288
|
-
const targetMeta2 = MetadataRegistry.get(targetClass2);
|
|
1289
|
-
const nestedRelations = detectRelationFields(selectSpec.select, targetMeta2);
|
|
1290
|
-
if (nestedRelations.length > 0) {
|
|
1291
|
-
items = await resolveSingleValueRelationsBatch(
|
|
1292
|
-
items,
|
|
1293
|
-
rawItems,
|
|
1294
|
-
selectSpec.select,
|
|
1295
|
-
targetMeta2,
|
|
1296
|
-
opts,
|
|
1297
|
-
currentDepth + 1,
|
|
1298
|
-
ownPath
|
|
1299
|
-
);
|
|
1300
|
-
}
|
|
1301
|
-
result[rel.propertyName] = {
|
|
1302
|
-
items,
|
|
1303
|
-
cursor: listResult.cursor
|
|
1304
|
-
};
|
|
1305
|
-
return;
|
|
1306
|
-
}
|
|
1307
|
-
const targetClass = rel.targetFactory();
|
|
1308
|
-
const targetMeta = MetadataRegistry.get(targetClass);
|
|
1309
|
-
const [resolvedItem] = await fetchBelongsToHasOneBatch(
|
|
1310
|
-
rel,
|
|
1311
|
-
[rawParentItem],
|
|
1312
|
-
selectSpec,
|
|
1313
|
-
targetMeta,
|
|
1314
|
-
opts,
|
|
1315
|
-
currentDepth,
|
|
1316
|
-
currentPath
|
|
1317
|
-
);
|
|
1318
|
-
result[rel.propertyName] = resolvedItem;
|
|
1319
|
-
});
|
|
1320
|
-
return result;
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
// src/operations/query.ts
|
|
1324
|
-
async function executeQuery(modelClass, key2, selectSpec, options) {
|
|
1325
|
-
const metadata = MetadataRegistry.get(modelClass);
|
|
1326
|
-
const queryFields = Object.keys(key2);
|
|
1327
|
-
const resolved = resolveKey(queryFields, metadata);
|
|
1328
|
-
if (resolved.type === "gsi" && !resolved.unique) {
|
|
1329
|
-
throw new Error(
|
|
1330
|
-
`Cannot use query() with non-unique GSI '${resolved.indexName}'. Use list() instead.`
|
|
1331
|
-
);
|
|
1332
|
-
}
|
|
1333
|
-
const normalized = normalizeTopLevelSelect(selectSpec);
|
|
1334
|
-
const select = normalized.select;
|
|
1335
|
-
const filter = normalized.filter;
|
|
1336
|
-
const maxDepth = options?.maxDepth ?? 1;
|
|
1337
|
-
const relations = detectRelationFields(select, metadata);
|
|
1338
|
-
if (relations.length > 0) {
|
|
1339
|
-
validateDepth(select, metadata, maxDepth);
|
|
1340
|
-
}
|
|
1341
|
-
const implicitKeys = getImplicitKeyFields(select, metadata);
|
|
1342
|
-
const updatable = options?.updatable ?? false;
|
|
1343
|
-
const executionPlan = plan(metadata, {
|
|
1344
|
-
key: key2,
|
|
1345
|
-
select,
|
|
1346
|
-
filter,
|
|
1347
|
-
consistentRead: options?.consistentRead,
|
|
1348
|
-
additionalProjectionFields: implicitKeys.length > 0 ? implicitKeys : void 0,
|
|
1349
|
-
updatable
|
|
1350
|
-
});
|
|
1351
|
-
const operation = executionPlan.operations[0];
|
|
1352
|
-
const canParallelizeRelations = relations.length > 0 && relations.every(
|
|
1353
|
-
(rel) => Object.values(rel.keyBinding).every(
|
|
1354
|
-
(sourceField) => key2[sourceField] != null
|
|
1355
|
-
)
|
|
1356
|
-
);
|
|
1357
|
-
const parentPromise = execute(operation);
|
|
1358
|
-
const relationPromise = canParallelizeRelations ? resolveRelations({}, key2, select, metadata, { maxDepth, updatable }, 1) : null;
|
|
1359
|
-
let result;
|
|
1360
|
-
try {
|
|
1361
|
-
result = await parentPromise;
|
|
1362
|
-
} catch (err) {
|
|
1363
|
-
if (relationPromise) await relationPromise.catch(() => void 0);
|
|
1364
|
-
throw err;
|
|
1365
|
-
}
|
|
1366
|
-
if (result.items.length === 0) {
|
|
1367
|
-
if (relationPromise) await relationPromise.catch(() => void 0);
|
|
1368
|
-
return null;
|
|
1369
|
-
}
|
|
1370
|
-
const rawItem = result.items[0];
|
|
1371
|
-
const hydrated = hydrate(result.items, select, metadata, updatable);
|
|
1372
|
-
let hydratedItem = hydrated[0] ?? null;
|
|
1373
|
-
if (hydratedItem && relations.length > 0) {
|
|
1374
|
-
if (relationPromise) {
|
|
1375
|
-
const resolvedRelations = await relationPromise;
|
|
1376
|
-
for (const rel of relations) {
|
|
1377
|
-
hydratedItem[rel.propertyName] = resolvedRelations[rel.propertyName];
|
|
1378
|
-
}
|
|
1379
|
-
} else {
|
|
1380
|
-
hydratedItem = await resolveRelations(
|
|
1381
|
-
hydratedItem,
|
|
1382
|
-
rawItem,
|
|
1383
|
-
select,
|
|
1384
|
-
metadata,
|
|
1385
|
-
{ maxDepth, updatable },
|
|
1386
|
-
1
|
|
1387
|
-
);
|
|
1388
|
-
if (hydratedItem && updatable) {
|
|
1389
|
-
attachHiddenKey(hydratedItem, rawItem);
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
if (options?.hydrate) {
|
|
1394
|
-
return options.hydrate(hydratedItem);
|
|
1395
|
-
}
|
|
1396
|
-
return hydratedItem;
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
// src/operations/explain.ts
|
|
1400
|
-
function executeExplain(modelClass, key2, options) {
|
|
1401
|
-
const metadata = MetadataRegistry.get(modelClass);
|
|
1402
|
-
const exclusiveStartKey = options?.after ? decodeCursor(options.after) : void 0;
|
|
1403
|
-
const normalized = normalizeTopLevelSelect(options?.select);
|
|
1404
|
-
const select = normalized.select ?? {};
|
|
1405
|
-
const filter = options?.filter ?? normalized.filter;
|
|
1406
|
-
const mainPlan = plan(metadata, {
|
|
1407
|
-
key: key2,
|
|
1408
|
-
select,
|
|
1409
|
-
limit: options?.limit,
|
|
1410
|
-
after: exclusiveStartKey,
|
|
1411
|
-
order: options?.order,
|
|
1412
|
-
consistentRead: options?.consistentRead,
|
|
1413
|
-
filter
|
|
1414
|
-
});
|
|
1415
|
-
const relationOps = planRelationOperations(metadata, select, {
|
|
1416
|
-
estimatedParentCount: 1
|
|
1417
|
-
});
|
|
1418
|
-
return {
|
|
1419
|
-
operations: [...mainPlan.operations, ...relationOps]
|
|
1420
|
-
};
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
// src/operations/batch.ts
|
|
1424
|
-
var BatchGetResult = class {
|
|
1425
|
-
groups;
|
|
1426
|
-
constructor(groups) {
|
|
1427
|
-
this.groups = groups;
|
|
1428
|
-
}
|
|
1429
|
-
get(model2) {
|
|
1430
|
-
const modelClass = resolveModelClass(model2);
|
|
1431
|
-
return this.groups.get(modelClass) ?? [];
|
|
1432
|
-
}
|
|
1433
|
-
};
|
|
1434
|
-
function buildScalarSelect(metadata) {
|
|
1435
|
-
const select = {};
|
|
1436
|
-
for (const field2 of metadata.fields) {
|
|
1437
|
-
select[field2.propertyName] = true;
|
|
1438
|
-
}
|
|
1439
|
-
return select;
|
|
1440
|
-
}
|
|
1441
|
-
function buildDynamoKey(modelClass, keyObj) {
|
|
1442
|
-
const deleteInput = buildDeleteInput(modelClass, keyObj);
|
|
1443
|
-
return deleteInput.Key;
|
|
1444
|
-
}
|
|
1445
|
-
function hydrateBatchItems(rawItems, metadata) {
|
|
1446
|
-
if (rawItems.length === 0) {
|
|
1447
|
-
return [];
|
|
1448
|
-
}
|
|
1449
|
-
const select = buildScalarSelect(metadata);
|
|
1450
|
-
const hydrated = hydrate(rawItems, select, metadata);
|
|
1451
|
-
if (metadata.embeddedFields.length === 0) {
|
|
1452
|
-
return hydrated;
|
|
1453
|
-
}
|
|
1454
|
-
return hydrated.map((item, index) => {
|
|
1455
|
-
const raw = rawItems[index];
|
|
1456
|
-
const merged = { ...item };
|
|
1457
|
-
for (const emb of metadata.embeddedFields) {
|
|
1458
|
-
const rawEmb = raw?.[emb.propertyName];
|
|
1459
|
-
if (rawEmb !== void 0 && rawEmb !== null) {
|
|
1460
|
-
merged[emb.propertyName] = rawEmb;
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
return merged;
|
|
1464
|
-
});
|
|
1465
|
-
}
|
|
1466
|
-
async function executeBatchGetForTable(tableName, entries) {
|
|
1467
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1468
|
-
if (entries.length === 0) {
|
|
1469
|
-
return groups;
|
|
1470
|
-
}
|
|
1471
|
-
const keys = entries.map((entry) => entry.dynamoKey);
|
|
1472
|
-
const { items: rawItems } = await ClientManager.getExecutor().batchGet({
|
|
1473
|
-
tableName,
|
|
1474
|
-
keys
|
|
1475
|
-
});
|
|
1476
|
-
const entriesByModel = /* @__PURE__ */ new Map();
|
|
1477
|
-
for (const entry of entries) {
|
|
1478
|
-
const existing = entriesByModel.get(entry.modelClass) ?? [];
|
|
1479
|
-
existing.push(entry);
|
|
1480
|
-
entriesByModel.set(entry.modelClass, existing);
|
|
1481
|
-
}
|
|
1482
|
-
for (const [modelClass, modelEntries] of entriesByModel) {
|
|
1483
|
-
const metadata = modelEntries[0].metadata;
|
|
1484
|
-
const expectedKeys = new Set(
|
|
1485
|
-
modelEntries.map(
|
|
1486
|
-
(entry) => `${String(entry.dynamoKey.PK)}::${String(entry.dynamoKey.SK)}`
|
|
1487
|
-
)
|
|
1488
|
-
);
|
|
1489
|
-
const modelRawItems = rawItems.filter(
|
|
1490
|
-
(item) => expectedKeys.has(`${String(item.PK)}::${String(item.SK)}`)
|
|
1491
|
-
);
|
|
1492
|
-
groups.set(modelClass, hydrateBatchItems(modelRawItems, metadata));
|
|
1493
|
-
}
|
|
1494
|
-
return groups;
|
|
1495
|
-
}
|
|
1496
|
-
async function executeBatchGet(requests) {
|
|
1497
|
-
const entriesByTable = /* @__PURE__ */ new Map();
|
|
1498
|
-
for (const request of requests) {
|
|
1499
|
-
const modelClass = resolveModelClass(request.model);
|
|
1500
|
-
const metadata = MetadataRegistry.get(modelClass);
|
|
1501
|
-
const tableName = TableMapping.resolve(metadata.tableName);
|
|
1502
|
-
for (const keyObj of request.keys) {
|
|
1503
|
-
const entry = {
|
|
1504
|
-
modelClass,
|
|
1505
|
-
metadata,
|
|
1506
|
-
dynamoKey: buildDynamoKey(modelClass, keyObj)
|
|
1507
|
-
};
|
|
1508
|
-
const existing = entriesByTable.get(tableName) ?? [];
|
|
1509
|
-
existing.push(entry);
|
|
1510
|
-
entriesByTable.set(tableName, existing);
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
const mergedGroups = /* @__PURE__ */ new Map();
|
|
1514
|
-
for (const [, entries] of entriesByTable) {
|
|
1515
|
-
const tableName = TableMapping.resolve(entries[0].metadata.tableName);
|
|
1516
|
-
const tableGroups = await executeBatchGetForTable(tableName, entries);
|
|
1517
|
-
for (const [modelClass, items] of tableGroups) {
|
|
1518
|
-
const existing = mergedGroups.get(modelClass) ?? [];
|
|
1519
|
-
mergedGroups.set(modelClass, existing.concat(items));
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
return new BatchGetResult(mergedGroups);
|
|
1523
|
-
}
|
|
1524
|
-
function toExecItem(entry) {
|
|
1525
|
-
if (entry.request.type === "put") {
|
|
1526
|
-
return { type: "put", item: entry.request.putInput.Item };
|
|
1527
|
-
}
|
|
1528
|
-
return { type: "delete", key: entry.request.deleteInput.Key };
|
|
1529
|
-
}
|
|
1530
|
-
async function executeBatchWrite(requests) {
|
|
1531
|
-
const entriesByTable = /* @__PURE__ */ new Map();
|
|
1532
|
-
for (const request of requests) {
|
|
1533
|
-
const modelClass = resolveModelClass(request.model);
|
|
1534
|
-
const metadata = MetadataRegistry.get(modelClass);
|
|
1535
|
-
const tableName = TableMapping.resolve(metadata.tableName);
|
|
1536
|
-
if (request.type === "put") {
|
|
1537
|
-
const putInput = buildPutInput(modelClass, request.item, request.options);
|
|
1538
|
-
const entry2 = {
|
|
1539
|
-
tableName,
|
|
1540
|
-
modelName: modelClass.name,
|
|
1541
|
-
request: { type: "put", putInput }
|
|
1542
|
-
};
|
|
1543
|
-
const existing2 = entriesByTable.get(tableName) ?? [];
|
|
1544
|
-
existing2.push(entry2);
|
|
1545
|
-
entriesByTable.set(tableName, existing2);
|
|
1546
|
-
continue;
|
|
1547
|
-
}
|
|
1548
|
-
const deleteInput = buildDeleteInput(modelClass, request.key, request.options);
|
|
1549
|
-
const entry = {
|
|
1550
|
-
tableName,
|
|
1551
|
-
modelName: modelClass.name,
|
|
1552
|
-
request: { type: "delete", deleteInput }
|
|
1553
|
-
};
|
|
1554
|
-
const existing = entriesByTable.get(tableName) ?? [];
|
|
1555
|
-
existing.push(entry);
|
|
1556
|
-
entriesByTable.set(tableName, existing);
|
|
1557
|
-
}
|
|
1558
|
-
const executor = ClientManager.getExecutor();
|
|
1559
|
-
for (const [tableName, entries] of entriesByTable) {
|
|
1560
|
-
await executor.batchWrite(tableName, entries.map(toExecItem));
|
|
1561
|
-
}
|
|
1562
|
-
if (ChangeCaptureRegistry.hasSubscribers()) {
|
|
1563
|
-
for (const [, entries] of entriesByTable) {
|
|
1564
|
-
for (const entry of entries) {
|
|
1565
|
-
if (entry.request.type === "put") {
|
|
1566
|
-
const item = entry.request.putInput.Item;
|
|
1567
|
-
captureWrite({
|
|
1568
|
-
table: entry.tableName,
|
|
1569
|
-
...entry.modelName ? { model: entry.modelName } : {},
|
|
1570
|
-
op: "put",
|
|
1571
|
-
keys: { pk: String(item.PK), sk: String(item.SK ?? "") },
|
|
1572
|
-
newItem: item
|
|
1573
|
-
});
|
|
1574
|
-
} else {
|
|
1575
|
-
const dkey = entry.request.deleteInput.Key;
|
|
1576
|
-
captureWrite({
|
|
1577
|
-
table: entry.tableName,
|
|
1578
|
-
...entry.modelName ? { model: entry.modelName } : {},
|
|
1579
|
-
op: "delete",
|
|
1580
|
-
keys: { pk: String(dkey.PK), sk: String(dkey.SK ?? "") }
|
|
1581
|
-
});
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
286
|
+
// src/decorators/embedded.ts
|
|
287
|
+
function embedded(modelFactory) {
|
|
288
|
+
return function(_value, context) {
|
|
289
|
+
collectEmbedded({
|
|
290
|
+
propertyName: String(context.name),
|
|
291
|
+
modelFactory
|
|
292
|
+
});
|
|
293
|
+
};
|
|
1586
294
|
}
|
|
1587
295
|
|
|
1588
|
-
// src/
|
|
1589
|
-
var
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
static async transaction(fn) {
|
|
1597
|
-
await executeTransaction(fn);
|
|
1598
|
-
}
|
|
1599
|
-
static async batchGet(requests) {
|
|
1600
|
-
return executeBatchGet(requests);
|
|
1601
|
-
}
|
|
1602
|
-
static async batchWrite(requests) {
|
|
1603
|
-
return executeBatchWrite(requests);
|
|
1604
|
-
}
|
|
1605
|
-
static asModel() {
|
|
1606
|
-
const modelClass = this;
|
|
1607
|
-
const modelStatic = {
|
|
1608
|
-
col: createColumnMap(),
|
|
1609
|
-
project(select) {
|
|
1610
|
-
return buildProject(select);
|
|
1611
|
-
},
|
|
1612
|
-
relation(select) {
|
|
1613
|
-
return buildRelation(select);
|
|
1614
|
-
},
|
|
1615
|
-
// The public `query` is overloaded (plain / updatable / hydrate, the last
|
|
1616
|
-
// introducing a fresh return-type parameter `R`, issue #53). An overloaded
|
|
1617
|
-
// method on an object literal cannot contextually infer its single
|
|
1618
|
-
// implementation signature, so the implementation is written with explicit
|
|
1619
|
-
// runtime-facing parameter types and assigned through the interface's
|
|
1620
|
-
// method type — the public overloads (the only caller-visible surface)
|
|
1621
|
-
// are untouched.
|
|
1622
|
-
query: ((key2, select, options) => {
|
|
1623
|
-
if (isRecordingContractMethod()) {
|
|
1624
|
-
recordContractOp("query", modelStatic, {
|
|
1625
|
-
keys: key2,
|
|
1626
|
-
select
|
|
1627
|
-
});
|
|
1628
|
-
return Promise.resolve(null);
|
|
1629
|
-
}
|
|
1630
|
-
return executeQuery(modelClass, key2, select, options);
|
|
1631
|
-
}),
|
|
1632
|
-
async list(key2, options) {
|
|
1633
|
-
if (isRecordingContractMethod()) {
|
|
1634
|
-
recordContractOp("list", modelStatic, {
|
|
1635
|
-
keys: key2,
|
|
1636
|
-
select: options?.select
|
|
1637
|
-
});
|
|
1638
|
-
return { items: [], cursor: null };
|
|
1639
|
-
}
|
|
1640
|
-
const result = await executeList(
|
|
1641
|
-
modelClass,
|
|
1642
|
-
key2,
|
|
1643
|
-
options
|
|
1644
|
-
);
|
|
1645
|
-
return result;
|
|
1646
|
-
},
|
|
1647
|
-
explain(key2, options) {
|
|
1648
|
-
return executeExplain(
|
|
1649
|
-
modelClass,
|
|
1650
|
-
key2,
|
|
1651
|
-
options
|
|
1652
|
-
);
|
|
1653
|
-
},
|
|
1654
|
-
async put(item, options) {
|
|
1655
|
-
if (isRecordingContractMethod()) {
|
|
1656
|
-
recordContractOp("put", modelStatic, {
|
|
1657
|
-
item,
|
|
1658
|
-
condition: options?.condition,
|
|
1659
|
-
batch: options?.batch
|
|
1660
|
-
});
|
|
1661
|
-
return;
|
|
1662
|
-
}
|
|
1663
|
-
await executePut(
|
|
1664
|
-
modelClass,
|
|
1665
|
-
item,
|
|
1666
|
-
options
|
|
1667
|
-
);
|
|
1668
|
-
},
|
|
1669
|
-
async update(entity, changes, options) {
|
|
1670
|
-
if (isRecordingContractMethod()) {
|
|
1671
|
-
recordContractOp("update", modelStatic, {
|
|
1672
|
-
keys: entity,
|
|
1673
|
-
changes,
|
|
1674
|
-
condition: options?.condition,
|
|
1675
|
-
batch: options?.batch
|
|
1676
|
-
});
|
|
1677
|
-
return;
|
|
1678
|
-
}
|
|
1679
|
-
await executeUpdate(
|
|
1680
|
-
modelClass,
|
|
1681
|
-
entity,
|
|
1682
|
-
changes,
|
|
1683
|
-
options
|
|
1684
|
-
);
|
|
1685
|
-
},
|
|
1686
|
-
async delete(key2, options) {
|
|
1687
|
-
if (isRecordingContractMethod()) {
|
|
1688
|
-
recordContractOp("delete", modelStatic, {
|
|
1689
|
-
keys: key2,
|
|
1690
|
-
condition: options?.condition,
|
|
1691
|
-
batch: options?.batch
|
|
1692
|
-
});
|
|
1693
|
-
return;
|
|
1694
|
-
}
|
|
1695
|
-
await executeDelete(modelClass, key2, options);
|
|
1696
|
-
}
|
|
1697
|
-
};
|
|
1698
|
-
return attachModelClass(
|
|
1699
|
-
modelStatic,
|
|
1700
|
-
modelClass
|
|
296
|
+
// src/decorators/relation.ts
|
|
297
|
+
var RESERVED_RELATION_PROPERTY_NAMES = /* @__PURE__ */ new Set([
|
|
298
|
+
"items"
|
|
299
|
+
]);
|
|
300
|
+
function assertRelationPropertyNameAllowed(propertyName) {
|
|
301
|
+
if (RESERVED_RELATION_PROPERTY_NAMES.has(propertyName)) {
|
|
302
|
+
throw new Error(
|
|
303
|
+
`Relation property name '${propertyName}' is reserved: it collides with the relation result-path grammar token '${propertyName}', which the planner and runtimes use to mark a hasMany connection (a relation writes to '$.<prop>.items'). A relation named '${propertyName}' would be mis-staged at runtime. Rename the relation property (reserved names: ${[...RESERVED_RELATION_PROPERTY_NAMES].map((n) => `'${n}'`).join(", ")}).`
|
|
1701
304
|
);
|
|
1702
305
|
}
|
|
1703
|
-
}
|
|
306
|
+
}
|
|
307
|
+
function hasMany(targetFactory, keyBinding, options) {
|
|
308
|
+
return function(_value, context) {
|
|
309
|
+
const propertyName = String(context.name);
|
|
310
|
+
assertRelationPropertyNameAllowed(propertyName);
|
|
311
|
+
collectRelation({
|
|
312
|
+
type: "hasMany",
|
|
313
|
+
propertyName,
|
|
314
|
+
targetFactory,
|
|
315
|
+
keyBinding,
|
|
316
|
+
options
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function belongsTo(targetFactory, keyBinding) {
|
|
321
|
+
return function(_value, context) {
|
|
322
|
+
const propertyName = String(context.name);
|
|
323
|
+
assertRelationPropertyNameAllowed(propertyName);
|
|
324
|
+
collectRelation({
|
|
325
|
+
type: "belongsTo",
|
|
326
|
+
propertyName,
|
|
327
|
+
targetFactory,
|
|
328
|
+
keyBinding
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function hasOne(targetFactory, keyBinding) {
|
|
333
|
+
return function(_value, context) {
|
|
334
|
+
const propertyName = String(context.name);
|
|
335
|
+
assertRelationPropertyNameAllowed(propertyName);
|
|
336
|
+
collectRelation({
|
|
337
|
+
type: "hasOne",
|
|
338
|
+
propertyName,
|
|
339
|
+
targetFactory,
|
|
340
|
+
keyBinding
|
|
341
|
+
});
|
|
342
|
+
};
|
|
343
|
+
}
|
|
1704
344
|
|
|
1705
345
|
// src/operations/declarative-transaction.ts
|
|
1706
346
|
var PLACEHOLDER_RE = /\{[^{}]+\}/g;
|
|
@@ -1777,6 +417,12 @@ function whenHolds(when2, params, element) {
|
|
|
1777
417
|
const right = resolveTemplate(when2.right, params, element);
|
|
1778
418
|
return when2.op === "eq" ? left === right : left !== right;
|
|
1779
419
|
}
|
|
420
|
+
function resolveRawConditionValue(val, params, element) {
|
|
421
|
+
if (val !== null && typeof val === "object" && typeof val.$param === "string") {
|
|
422
|
+
return resolveValue(`{${val.$param}}`, params, element);
|
|
423
|
+
}
|
|
424
|
+
return val;
|
|
425
|
+
}
|
|
1780
426
|
function applyCondition(condition, target, params, element) {
|
|
1781
427
|
if (!condition) return;
|
|
1782
428
|
const names = target.ExpressionAttributeNames ??= {};
|
|
@@ -1792,6 +438,25 @@ function applyCondition(condition, target, params, element) {
|
|
|
1792
438
|
return;
|
|
1793
439
|
}
|
|
1794
440
|
const values = target.ExpressionAttributeValues ??= {};
|
|
441
|
+
if (condition.kind === "raw") {
|
|
442
|
+
Object.assign(names, condition.names);
|
|
443
|
+
for (const [alias, val] of Object.entries(condition.values)) {
|
|
444
|
+
values[alias] = resolveRawConditionValue(val, params, element);
|
|
445
|
+
}
|
|
446
|
+
target.ConditionExpression = condition.expression;
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (condition.kind === "expr") {
|
|
450
|
+
const concrete = resolveConditionTree(
|
|
451
|
+
condition.declarative,
|
|
452
|
+
(name) => resolveValue(`{${name}}`, params, element)
|
|
453
|
+
);
|
|
454
|
+
const compiled = buildConditionExpression(concrete);
|
|
455
|
+
Object.assign(names, compiled.names);
|
|
456
|
+
Object.assign(values, compiled.values);
|
|
457
|
+
target.ConditionExpression = compiled.expression;
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
1795
460
|
const clauses = [];
|
|
1796
461
|
let i = 0;
|
|
1797
462
|
for (const [field2, tmpl] of Object.entries(condition.fields)) {
|
|
@@ -1997,8 +662,7 @@ async function executePointSingle(op, key2, params) {
|
|
|
1997
662
|
}
|
|
1998
663
|
async function executeRangeSingle(op, key2, params) {
|
|
1999
664
|
const innerAfter = params.after !== void 0 ? decodePerKeyCursor(params.after, key2) : void 0;
|
|
2000
|
-
const result = await executeListInternal(modelClassOf(op), key2, {
|
|
2001
|
-
select: selectOf(op),
|
|
665
|
+
const result = await executeListInternal(modelClassOf(op), key2, selectOf(op), {
|
|
2002
666
|
limit: params.limit,
|
|
2003
667
|
after: innerAfter,
|
|
2004
668
|
order: params.order
|
|
@@ -2149,437 +813,6 @@ async function executeRangeFanout(op, keys, params = {}) {
|
|
|
2149
813
|
return out;
|
|
2150
814
|
}
|
|
2151
815
|
|
|
2152
|
-
// src/runtime/command-runtime.ts
|
|
2153
|
-
function resolveCommandMethod(contract, methodName) {
|
|
2154
|
-
const method = contract.methods[methodName];
|
|
2155
|
-
if (method === void 0) {
|
|
2156
|
-
throw new Error(
|
|
2157
|
-
`Contract runtime: command method '${methodName}' does not exist on this contract. Available methods: ${Object.keys(contract.methods).sort().join(", ") || "(none)"}.`
|
|
2158
|
-
);
|
|
2159
|
-
}
|
|
2160
|
-
return method;
|
|
2161
|
-
}
|
|
2162
|
-
function modelClassOf2(op) {
|
|
2163
|
-
return op.entity.modelClass;
|
|
2164
|
-
}
|
|
2165
|
-
function modelStaticOf(op) {
|
|
2166
|
-
return op.entity.modelClass.asModel();
|
|
2167
|
-
}
|
|
2168
|
-
function renderLeaf(leaf, key2, params, context) {
|
|
2169
|
-
if (isContractParamRef(leaf)) {
|
|
2170
|
-
if (!(leaf.field in params)) {
|
|
2171
|
-
throw new Error(
|
|
2172
|
-
`Contract runtime: ${context} references params field '${leaf.field}', which was not supplied. Pass it in the method's params argument.`
|
|
2173
|
-
);
|
|
2174
|
-
}
|
|
2175
|
-
return params[leaf.field];
|
|
2176
|
-
}
|
|
2177
|
-
if (isContractKeyFieldRef(leaf)) {
|
|
2178
|
-
if (leaf.field in key2) return key2[leaf.field];
|
|
2179
|
-
if (leaf.field in params) return params[leaf.field];
|
|
2180
|
-
throw new Error(
|
|
2181
|
-
`Contract runtime: ${context} references key field '${leaf.field}', which was not supplied. Pass it in the method's key or params argument.`
|
|
2182
|
-
);
|
|
2183
|
-
}
|
|
2184
|
-
return leaf;
|
|
2185
|
-
}
|
|
2186
|
-
function renderRecord(structure, key2, params, context) {
|
|
2187
|
-
const out = {};
|
|
2188
|
-
if (structure === void 0) return out;
|
|
2189
|
-
for (const [field2, leaf] of Object.entries(structure)) {
|
|
2190
|
-
out[field2] = renderLeaf(leaf, key2, params, `${context} field '${field2}'`);
|
|
2191
|
-
}
|
|
2192
|
-
return out;
|
|
2193
|
-
}
|
|
2194
|
-
function renderCondition(condition, key2, params, context) {
|
|
2195
|
-
if (condition === void 0) return void 0;
|
|
2196
|
-
if (typeof condition === "object" && condition !== null) {
|
|
2197
|
-
const obj = condition;
|
|
2198
|
-
if (obj.notExists === true) return { notExists: true };
|
|
2199
|
-
if (typeof obj.attributeExists === "string") {
|
|
2200
|
-
return { attributeExists: obj.attributeExists };
|
|
2201
|
-
}
|
|
2202
|
-
if (typeof obj.attributeNotExists === "string") {
|
|
2203
|
-
return { attributeNotExists: obj.attributeNotExists };
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
return renderRecord(
|
|
2207
|
-
condition,
|
|
2208
|
-
key2,
|
|
2209
|
-
params,
|
|
2210
|
-
`${context} condition`
|
|
2211
|
-
);
|
|
2212
|
-
}
|
|
2213
|
-
function renderWrite(op, key2, params, contextLabel) {
|
|
2214
|
-
if (!isContractKeyRef(op.keys) && op.operation !== "put") {
|
|
2215
|
-
throw new Error(
|
|
2216
|
-
`Contract runtime: ${contextLabel} did not pass the contract key directly into its '${op.operation}' key position. A command method must call \`Model.${op.operation}(keys, \u2026)\` with the key whole.`
|
|
2217
|
-
);
|
|
2218
|
-
}
|
|
2219
|
-
const base = {
|
|
2220
|
-
operation: op.operation,
|
|
2221
|
-
modelClass: modelClassOf2(op),
|
|
2222
|
-
modelStatic: modelStaticOf(op),
|
|
2223
|
-
key: key2,
|
|
2224
|
-
condition: renderCondition(op.condition, key2, params, contextLabel)
|
|
2225
|
-
};
|
|
2226
|
-
if (op.operation === "put") {
|
|
2227
|
-
return { ...base, item: renderRecord(op.item, key2, params, `${contextLabel} item`) };
|
|
2228
|
-
}
|
|
2229
|
-
if (op.operation === "update") {
|
|
2230
|
-
return { ...base, changes: renderRecord(op.changes, key2, params, `${contextLabel} changes`) };
|
|
2231
|
-
}
|
|
2232
|
-
return base;
|
|
2233
|
-
}
|
|
2234
|
-
async function executeSingleWrite(w) {
|
|
2235
|
-
const options = w.condition !== void 0 ? { condition: w.condition } : void 0;
|
|
2236
|
-
if (w.operation === "put") {
|
|
2237
|
-
await executePut(w.modelClass, w.item ?? {}, options);
|
|
2238
|
-
return;
|
|
2239
|
-
}
|
|
2240
|
-
if (w.operation === "update") {
|
|
2241
|
-
await executeUpdate(w.modelClass, w.key, w.changes ?? {}, options);
|
|
2242
|
-
return;
|
|
2243
|
-
}
|
|
2244
|
-
await executeDelete(w.modelClass, w.key, options);
|
|
2245
|
-
}
|
|
2246
|
-
async function readBackProjection(write, returnSelection) {
|
|
2247
|
-
const select = {};
|
|
2248
|
-
for (const field2 of Object.keys(returnSelection)) {
|
|
2249
|
-
if (returnSelection[field2] === true) select[field2] = true;
|
|
2250
|
-
}
|
|
2251
|
-
const key2 = readBackKey(write);
|
|
2252
|
-
return executeQuery(write.modelClass, key2, select, { consistentRead: true });
|
|
2253
|
-
}
|
|
2254
|
-
function readBackKey(write) {
|
|
2255
|
-
if (write.operation !== "put") return write.key;
|
|
2256
|
-
const metadata = MetadataRegistry.get(write.modelClass);
|
|
2257
|
-
const item = write.item ?? {};
|
|
2258
|
-
const key2 = {};
|
|
2259
|
-
const fields = metadata.primaryKey ? metadata.primaryKey.inputFieldNames : Object.keys(item);
|
|
2260
|
-
for (const field2 of fields) {
|
|
2261
|
-
if (field2 in item) key2[field2] = item[field2];
|
|
2262
|
-
}
|
|
2263
|
-
return key2;
|
|
2264
|
-
}
|
|
2265
|
-
function renderConditionCheck(check, key2, params, contextLabel) {
|
|
2266
|
-
const modelClass = check.entity.modelClass;
|
|
2267
|
-
const referencedKey = {};
|
|
2268
|
-
for (const [targetField, inputField] of Object.entries(check.keyBinding)) {
|
|
2269
|
-
if (inputField in params) {
|
|
2270
|
-
referencedKey[targetField] = params[inputField];
|
|
2271
|
-
} else if (inputField in key2) {
|
|
2272
|
-
referencedKey[targetField] = key2[inputField];
|
|
2273
|
-
} else {
|
|
2274
|
-
throw new Error(
|
|
2275
|
-
`Contract runtime: ${contextLabel} asserts '${check.entity.name}' exists, keyed by input field '${inputField}', which was not supplied. Pass it in the method's params (or key) argument.`
|
|
2276
|
-
);
|
|
2277
|
-
}
|
|
2278
|
-
}
|
|
2279
|
-
const { TableName, Key } = buildDeleteInput(
|
|
2280
|
-
modelClass,
|
|
2281
|
-
referencedKey
|
|
2282
|
-
);
|
|
2283
|
-
const cond2 = buildConditionExpression({ attributeExists: "PK" });
|
|
2284
|
-
const checkInput = {
|
|
2285
|
-
TableName,
|
|
2286
|
-
Key,
|
|
2287
|
-
ConditionExpression: cond2.expression
|
|
2288
|
-
};
|
|
2289
|
-
if (Object.keys(cond2.names).length > 0) checkInput.ExpressionAttributeNames = cond2.names;
|
|
2290
|
-
if (Object.keys(cond2.values).length > 0) checkInput.ExpressionAttributeValues = cond2.values;
|
|
2291
|
-
return checkInput;
|
|
2292
|
-
}
|
|
2293
|
-
var RUNTIME_TOKEN_RE = /\{([^{}]+)\}/g;
|
|
2294
|
-
function renderTemplate(template, key2, params, contextLabel) {
|
|
2295
|
-
const resolveToken = (token) => {
|
|
2296
|
-
if (token in params) return params[token];
|
|
2297
|
-
if (token in key2) return key2[token];
|
|
2298
|
-
throw new Error(
|
|
2299
|
-
`Contract runtime: ${contextLabel} references input field '${token}', which was not supplied. Pass it in the method's params (or key) argument` + (token.startsWith("old.") ? ` \u2014 an \`old.*\` token is the pre-mutation value of a foreign key on an onUpdateKeyChange (pass it as \`{ '${token}': \u2026 }\`).` : `.`)
|
|
2300
|
-
);
|
|
2301
|
-
};
|
|
2302
|
-
const whole = /^\{([^{}]+)\}$/.exec(template);
|
|
2303
|
-
if (whole !== null) return resolveToken(whole[1]);
|
|
2304
|
-
return template.replace(RUNTIME_TOKEN_RE, (_m, token) => String(resolveToken(token)));
|
|
2305
|
-
}
|
|
2306
|
-
function renderTemplateRecord(record, key2, params, contextLabel) {
|
|
2307
|
-
const out = {};
|
|
2308
|
-
for (const [field2, template] of Object.entries(record ?? {})) {
|
|
2309
|
-
out[field2] = renderTemplate(template, key2, params, contextLabel);
|
|
2310
|
-
}
|
|
2311
|
-
return out;
|
|
2312
|
-
}
|
|
2313
|
-
function renderEdgeWriteItems(edge, key2, params, contextLabel, modelBySignature) {
|
|
2314
|
-
const adjacency = edge.entity.modelClass;
|
|
2315
|
-
return edge.items.map((item) => {
|
|
2316
|
-
if (item.type === "Put") {
|
|
2317
|
-
const rendered = renderTemplateRecord(item.item, key2, params, `${contextLabel} edge Put`);
|
|
2318
|
-
const out2 = { Put: buildPutInput(adjacency, rendered) };
|
|
2319
|
-
modelBySignature?.set(execItemKeySignature(out2), adjacency.name);
|
|
2320
|
-
return out2;
|
|
2321
|
-
}
|
|
2322
|
-
const renderedKey = renderTemplateRecord(
|
|
2323
|
-
item.keyCondition,
|
|
2324
|
-
key2,
|
|
2325
|
-
params,
|
|
2326
|
-
`${contextLabel} edge Delete`
|
|
2327
|
-
);
|
|
2328
|
-
const del = {
|
|
2329
|
-
TableName: TableMapping.resolve(item.tableName),
|
|
2330
|
-
Key: renderedKey
|
|
2331
|
-
};
|
|
2332
|
-
const out = { Delete: del };
|
|
2333
|
-
modelBySignature?.set(execItemKeySignature(out), adjacency.name);
|
|
2334
|
-
return out;
|
|
2335
|
-
});
|
|
2336
|
-
}
|
|
2337
|
-
function renderDerivedUpdate(update, key2, params, contextLabel, modelBySignature) {
|
|
2338
|
-
const metadata = MetadataRegistry.get(update.entity.modelClass);
|
|
2339
|
-
if (!metadata.primaryKey) {
|
|
2340
|
-
throw new Error(
|
|
2341
|
-
`Contract runtime: ${contextLabel} derives a counter update on '${update.entity.name}', which has no primary key.`
|
|
2342
|
-
);
|
|
2343
|
-
}
|
|
2344
|
-
const keyInput = {};
|
|
2345
|
-
for (const [targetField, inputField] of Object.entries(update.keyBinding)) {
|
|
2346
|
-
keyInput[targetField] = renderTemplate(
|
|
2347
|
-
`{${inputField}}`,
|
|
2348
|
-
key2,
|
|
2349
|
-
params,
|
|
2350
|
-
`${contextLabel} derived-update key`
|
|
2351
|
-
);
|
|
2352
|
-
}
|
|
2353
|
-
const { TableName, Key } = buildDeleteInput(
|
|
2354
|
-
update.entity.modelClass,
|
|
2355
|
-
keyInput
|
|
2356
|
-
);
|
|
2357
|
-
const updateInput = {
|
|
2358
|
-
TableName,
|
|
2359
|
-
Key,
|
|
2360
|
-
UpdateExpression: "ADD #a0 :a0",
|
|
2361
|
-
ExpressionAttributeNames: { "#a0": update.attribute },
|
|
2362
|
-
ExpressionAttributeValues: { ":a0": update.amount }
|
|
2363
|
-
};
|
|
2364
|
-
const out = { Update: updateInput };
|
|
2365
|
-
modelBySignature?.set(execItemKeySignature(out), update.entity.modelClass.name);
|
|
2366
|
-
return out;
|
|
2367
|
-
}
|
|
2368
|
-
function renderUniqueGuardItems(guard, key2, params, contextLabel) {
|
|
2369
|
-
return guard.items.map((item) => {
|
|
2370
|
-
if (item.type === "Put") {
|
|
2371
|
-
const Item = renderTemplateRecord(item.item, key2, params, `${contextLabel} guard Put`);
|
|
2372
|
-
const put = { TableName: TableMapping.resolve(item.tableName), Item };
|
|
2373
|
-
const cond2 = buildConditionExpression({ attributeNotExists: "PK" });
|
|
2374
|
-
put.ConditionExpression = cond2.expression;
|
|
2375
|
-
if (Object.keys(cond2.names).length > 0) put.ExpressionAttributeNames = cond2.names;
|
|
2376
|
-
if (Object.keys(cond2.values).length > 0) put.ExpressionAttributeValues = cond2.values;
|
|
2377
|
-
return { Put: put };
|
|
2378
|
-
}
|
|
2379
|
-
const Key = renderTemplateRecord(item.keyCondition, key2, params, `${contextLabel} guard Delete`);
|
|
2380
|
-
const del = { TableName: TableMapping.resolve(item.tableName), Key };
|
|
2381
|
-
return { Delete: del };
|
|
2382
|
-
});
|
|
2383
|
-
}
|
|
2384
|
-
function renderOutboxEventItems(event, key2, params, contextLabel) {
|
|
2385
|
-
return event.items.map((item) => {
|
|
2386
|
-
const Item = renderTemplateRecord(item.item, key2, params, `${contextLabel} outbox Put`);
|
|
2387
|
-
const put = { TableName: TableMapping.resolve(item.tableName), Item };
|
|
2388
|
-
return { Put: put };
|
|
2389
|
-
});
|
|
2390
|
-
}
|
|
2391
|
-
function renderIdempotencyGuardItems(guard, key2, params, contextLabel) {
|
|
2392
|
-
return guard.items.map((item) => {
|
|
2393
|
-
const Item = renderTemplateRecord(item.item, key2, params, `${contextLabel} idempotency Put`);
|
|
2394
|
-
const put = { TableName: TableMapping.resolve(item.tableName), Item };
|
|
2395
|
-
const cond2 = buildConditionExpression({ attributeNotExists: "PK" });
|
|
2396
|
-
put.ConditionExpression = cond2.expression;
|
|
2397
|
-
if (Object.keys(cond2.names).length > 0) put.ExpressionAttributeNames = cond2.names;
|
|
2398
|
-
if (Object.keys(cond2.values).length > 0) put.ExpressionAttributeValues = cond2.values;
|
|
2399
|
-
return { Put: put };
|
|
2400
|
-
});
|
|
2401
|
-
}
|
|
2402
|
-
async function executeReferentialTransaction(writes, edgeItems, updateItems, guardItemsRaw, outboxItems, idempotencyItems, checks, methodName, modelBySignature = /* @__PURE__ */ new Map()) {
|
|
2403
|
-
const writeItems = writes.map((w) => {
|
|
2404
|
-
const options = w.condition !== void 0 ? { condition: w.condition } : void 0;
|
|
2405
|
-
let item;
|
|
2406
|
-
if (w.operation === "put") {
|
|
2407
|
-
item = { Put: buildPutInput(w.modelClass, w.item ?? {}, options) };
|
|
2408
|
-
} else if (w.operation === "update") {
|
|
2409
|
-
item = { Update: buildUpdateInput(w.modelClass, w.key, w.changes ?? {}, options) };
|
|
2410
|
-
} else {
|
|
2411
|
-
item = { Delete: buildDeleteInput(w.modelClass, w.key, options) };
|
|
2412
|
-
}
|
|
2413
|
-
modelBySignature.set(execItemKeySignature(item), w.modelClass.name);
|
|
2414
|
-
return item;
|
|
2415
|
-
});
|
|
2416
|
-
const checkItems = checks.map((c) => ({ ConditionCheck: c.check }));
|
|
2417
|
-
await commitTransaction(
|
|
2418
|
-
[
|
|
2419
|
-
...writeItems,
|
|
2420
|
-
...edgeItems,
|
|
2421
|
-
...updateItems,
|
|
2422
|
-
...guardItemsRaw,
|
|
2423
|
-
...outboxItems,
|
|
2424
|
-
...idempotencyItems,
|
|
2425
|
-
...checkItems
|
|
2426
|
-
],
|
|
2427
|
-
{
|
|
2428
|
-
modelBySignature,
|
|
2429
|
-
limitError: (count) => new Error(
|
|
2430
|
-
`Contract runtime: command method '${methodName}' composes ${count} items (writes + edges + derived updates + uniqueness guards + outbox events + idempotency guard + ConditionChecks) into one TransactWriteItems, but DynamoDB caps a transaction at ${MAX_TRANSACT_ITEMS} items and a transaction is atomic \u2014 it cannot be split. Reduce the fragment / effect count.`
|
|
2431
|
-
)
|
|
2432
|
-
}
|
|
2433
|
-
);
|
|
2434
|
-
}
|
|
2435
|
-
async function executeTransactWrites(writes, methodName) {
|
|
2436
|
-
if (writes.length === 0) return;
|
|
2437
|
-
const modelBySignature = /* @__PURE__ */ new Map();
|
|
2438
|
-
const transactItems = writes.map((w) => {
|
|
2439
|
-
const options = w.condition !== void 0 ? { condition: w.condition } : void 0;
|
|
2440
|
-
let item;
|
|
2441
|
-
if (w.operation === "put") {
|
|
2442
|
-
item = { Put: buildPutInput(w.modelClass, w.item ?? {}, options) };
|
|
2443
|
-
} else if (w.operation === "update") {
|
|
2444
|
-
item = { Update: buildUpdateInput(w.modelClass, w.key, w.changes ?? {}, options) };
|
|
2445
|
-
} else {
|
|
2446
|
-
item = { Delete: buildDeleteInput(w.modelClass, w.key, options) };
|
|
2447
|
-
}
|
|
2448
|
-
modelBySignature.set(execItemKeySignature(item), w.modelClass.name);
|
|
2449
|
-
return item;
|
|
2450
|
-
});
|
|
2451
|
-
await commitTransaction(transactItems, {
|
|
2452
|
-
modelBySignature,
|
|
2453
|
-
limitError: (count) => new Error(
|
|
2454
|
-
`Contract runtime: command method '${methodName}' resolves an array of ${count} keys to a 'transact' (TransactWriteItems) batch, but DynamoDB caps a transaction at ${MAX_TRANSACT_ITEMS} items and a transaction is atomic \u2014 it cannot be split across requests without breaking atomicity. Pass \u2264${MAX_TRANSACT_ITEMS} keys, or declare \`batch: 'batchWrite'\` (a non-atomic BatchWriteItem, chunked automatically) if atomicity is not required.`
|
|
2455
|
-
)
|
|
2456
|
-
});
|
|
2457
|
-
}
|
|
2458
|
-
async function executeBatchWrites(writes, methodName) {
|
|
2459
|
-
if (writes.length === 0) return;
|
|
2460
|
-
const requests = writes.map((w) => {
|
|
2461
|
-
if (w.operation === "put") {
|
|
2462
|
-
return { type: "put", model: w.modelStatic, item: w.item ?? {} };
|
|
2463
|
-
}
|
|
2464
|
-
if (w.operation === "delete") {
|
|
2465
|
-
return { type: "delete", model: w.modelStatic, key: w.key };
|
|
2466
|
-
}
|
|
2467
|
-
throw new Error(
|
|
2468
|
-
`Contract runtime: command method '${methodName}' resolves to an 'update', but \`batch: 'batchWrite'\` only supports put / delete (DynamoDB's BatchWriteItem has no Update request). Declare \`batch: 'transact'\` for a batched update.`
|
|
2469
|
-
);
|
|
2470
|
-
});
|
|
2471
|
-
await executeBatchWrite(requests);
|
|
2472
|
-
}
|
|
2473
|
-
async function executeCommandMethod(contract, methodName, keyOrKeys, params = {}) {
|
|
2474
|
-
const method = resolveCommandMethod(contract, methodName);
|
|
2475
|
-
if (Array.isArray(keyOrKeys)) {
|
|
2476
|
-
const keys = keyOrKeys;
|
|
2477
|
-
if (method.ops !== void 0 && method.ops.length > 1) {
|
|
2478
|
-
throw new Error(
|
|
2479
|
-
`Contract runtime: command method '${methodName}' is a multi-fragment mutation (it composes ${method.ops.length} fragments into one atomic TransactWriteItems) and accepts a single key only. Call it once per target, not with a key array.`
|
|
2480
|
-
);
|
|
2481
|
-
}
|
|
2482
|
-
const hasDerivedEffects2 = method.conditionChecks !== void 0 && method.conditionChecks.length > 0 || method.edgeWrites !== void 0 && method.edgeWrites.length > 0 || method.derivedUpdates !== void 0 && method.derivedUpdates.length > 0 || method.uniqueGuards !== void 0 && method.uniqueGuards.length > 0 || method.outboxEvents !== void 0 && method.outboxEvents.length > 0 || method.idempotencyGuard !== void 0;
|
|
2483
|
-
if (hasDerivedEffects2) {
|
|
2484
|
-
throw new Error(
|
|
2485
|
-
`Contract runtime: command method '${methodName}' carries derived effects (referential integrity / edge writes / derived counters / uniqueness guards / outbox events / idempotency guard \u2014 it composes its write + those items into one atomic TransactWriteItems) and accepts a single key only. Call it once per target, not with a key array.`
|
|
2486
|
-
);
|
|
2487
|
-
}
|
|
2488
|
-
if (method.batch === void 0) {
|
|
2489
|
-
throw new Error(
|
|
2490
|
-
`Contract runtime: command method '${methodName}' was called with an array of keys, but it declares no batched-write form. Declare \`batch: 'transact'\` (atomic TransactWriteItems, \u226425) or \`batch: 'batchWrite'\` (non-atomic BatchWriteItem) on the method body, or call it with a single key.`
|
|
2491
|
-
);
|
|
2492
|
-
}
|
|
2493
|
-
const writes = keys.map(
|
|
2494
|
-
(key3, i) => renderWrite(method.op, key3, params, `command method '${methodName}' (key #${i})`)
|
|
2495
|
-
);
|
|
2496
|
-
if (method.batch === "transact") {
|
|
2497
|
-
await executeTransactWrites(writes, methodName);
|
|
2498
|
-
} else {
|
|
2499
|
-
await executeBatchWrites(writes, methodName);
|
|
2500
|
-
}
|
|
2501
|
-
return;
|
|
2502
|
-
}
|
|
2503
|
-
const key2 = keyOrKeys;
|
|
2504
|
-
const conditionChecks = method.conditionChecks ?? [];
|
|
2505
|
-
const edgeWrites2 = method.edgeWrites ?? [];
|
|
2506
|
-
const derivedUpdates = method.derivedUpdates ?? [];
|
|
2507
|
-
const uniqueGuards = method.uniqueGuards ?? [];
|
|
2508
|
-
const outboxEvents = method.outboxEvents ?? [];
|
|
2509
|
-
const idempotencyGuard = method.idempotencyGuard;
|
|
2510
|
-
const hasDerivedEffects = conditionChecks.length > 0 || edgeWrites2.length > 0 || derivedUpdates.length > 0 || uniqueGuards.length > 0 || outboxEvents.length > 0 || idempotencyGuard !== void 0;
|
|
2511
|
-
const isMultiFragment = method.ops !== void 0 && method.ops.length > 1;
|
|
2512
|
-
if (isMultiFragment || hasDerivedEffects) {
|
|
2513
|
-
const ops = method.ops ?? [method.op];
|
|
2514
|
-
const writes = ops.map(
|
|
2515
|
-
(opn, i) => renderWrite(opn, key2, params, `command method '${methodName}' (fragment #${i})`)
|
|
2516
|
-
);
|
|
2517
|
-
if (hasDerivedEffects) {
|
|
2518
|
-
const modelBySignature = /* @__PURE__ */ new Map();
|
|
2519
|
-
const edgeItems = edgeWrites2.flatMap(
|
|
2520
|
-
(edge, i) => renderEdgeWriteItems(
|
|
2521
|
-
edge,
|
|
2522
|
-
key2,
|
|
2523
|
-
params,
|
|
2524
|
-
`command method '${methodName}' (edge #${i})`,
|
|
2525
|
-
modelBySignature
|
|
2526
|
-
)
|
|
2527
|
-
);
|
|
2528
|
-
const updateItems = derivedUpdates.map(
|
|
2529
|
-
(update, i) => renderDerivedUpdate(
|
|
2530
|
-
update,
|
|
2531
|
-
key2,
|
|
2532
|
-
params,
|
|
2533
|
-
`command method '${methodName}' (derive #${i})`,
|
|
2534
|
-
modelBySignature
|
|
2535
|
-
)
|
|
2536
|
-
);
|
|
2537
|
-
const guardItems = uniqueGuards.flatMap(
|
|
2538
|
-
(guard, i) => renderUniqueGuardItems(guard, key2, params, `command method '${methodName}' (unique #${i})`)
|
|
2539
|
-
);
|
|
2540
|
-
const outboxItems = outboxEvents.flatMap(
|
|
2541
|
-
(event, i) => renderOutboxEventItems(event, key2, params, `command method '${methodName}' (emit #${i})`)
|
|
2542
|
-
);
|
|
2543
|
-
const idempotencyItems = idempotencyGuard !== void 0 ? renderIdempotencyGuardItems(
|
|
2544
|
-
idempotencyGuard,
|
|
2545
|
-
key2,
|
|
2546
|
-
params,
|
|
2547
|
-
`command method '${methodName}' (idempotency)`
|
|
2548
|
-
) : [];
|
|
2549
|
-
const checks = conditionChecks.map((check, i) => ({
|
|
2550
|
-
check: renderConditionCheck(
|
|
2551
|
-
check,
|
|
2552
|
-
key2,
|
|
2553
|
-
params,
|
|
2554
|
-
`command method '${methodName}' (requires #${i})`
|
|
2555
|
-
)
|
|
2556
|
-
}));
|
|
2557
|
-
await executeReferentialTransaction(
|
|
2558
|
-
writes,
|
|
2559
|
-
edgeItems,
|
|
2560
|
-
updateItems,
|
|
2561
|
-
guardItems,
|
|
2562
|
-
outboxItems,
|
|
2563
|
-
idempotencyItems,
|
|
2564
|
-
checks,
|
|
2565
|
-
methodName,
|
|
2566
|
-
modelBySignature
|
|
2567
|
-
);
|
|
2568
|
-
} else {
|
|
2569
|
-
await executeTransactWrites(writes, methodName);
|
|
2570
|
-
}
|
|
2571
|
-
if (method.returnSelection !== void 0 && Object.keys(method.returnSelection).length > 0) {
|
|
2572
|
-
return readBackProjection(writes[0], method.returnSelection);
|
|
2573
|
-
}
|
|
2574
|
-
return;
|
|
2575
|
-
}
|
|
2576
|
-
const write = renderWrite(method.op, key2, params, `command method '${methodName}'`);
|
|
2577
|
-
await executeSingleWrite(write);
|
|
2578
|
-
if (method.returnSelection !== void 0 && Object.keys(method.returnSelection).length > 0) {
|
|
2579
|
-
return readBackProjection(write, method.returnSelection);
|
|
2580
|
-
}
|
|
2581
|
-
}
|
|
2582
|
-
|
|
2583
816
|
// src/linter/rules/query-boundary.ts
|
|
2584
817
|
var queryBoundaryRule = {
|
|
2585
818
|
id: "query-boundary",
|
|
@@ -2628,11 +861,88 @@ function entityRef(model2) {
|
|
|
2628
861
|
}
|
|
2629
862
|
function collectConditionParams(condition, out) {
|
|
2630
863
|
if (condition === void 0) return;
|
|
864
|
+
if (isRawCondition(condition)) {
|
|
865
|
+
for (const { name, param: param2 } of collectRawConditionParams(condition)) {
|
|
866
|
+
out[name] = descriptorFromParam(name, param2, out[name]);
|
|
867
|
+
}
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
2631
870
|
const obj = condition;
|
|
2632
871
|
if (obj.notExists === true) return;
|
|
2633
872
|
if (typeof obj.attributeExists === "string") return;
|
|
2634
873
|
if (typeof obj.attributeNotExists === "string") return;
|
|
2635
|
-
|
|
874
|
+
collectConditionTreeParams(obj, out);
|
|
875
|
+
}
|
|
876
|
+
var SINGLE_OPERAND_OPS = /* @__PURE__ */ new Set([
|
|
877
|
+
"eq",
|
|
878
|
+
"ne",
|
|
879
|
+
"gt",
|
|
880
|
+
"ge",
|
|
881
|
+
"lt",
|
|
882
|
+
"le",
|
|
883
|
+
"beginsWith",
|
|
884
|
+
"contains",
|
|
885
|
+
"notContains",
|
|
886
|
+
"attributeType",
|
|
887
|
+
"size"
|
|
888
|
+
]);
|
|
889
|
+
function collectConditionTreeParams(obj, out) {
|
|
890
|
+
for (const [key2, value] of Object.entries(obj)) {
|
|
891
|
+
if (value === void 0) continue;
|
|
892
|
+
if (key2 === "and" || key2 === "or") {
|
|
893
|
+
for (const sub of value) {
|
|
894
|
+
if (isRawCondition(sub)) {
|
|
895
|
+
for (const { name, param: param2 } of collectRawConditionParams(sub)) {
|
|
896
|
+
out[name] = descriptorFromParam(name, param2, out[name]);
|
|
897
|
+
}
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
collectConditionTreeParams(sub, out);
|
|
901
|
+
}
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
if (key2 === "not") {
|
|
905
|
+
if (isRawCondition(value)) {
|
|
906
|
+
for (const { name, param: param2 } of collectRawConditionParams(value)) {
|
|
907
|
+
out[name] = descriptorFromParam(name, param2, out[name]);
|
|
908
|
+
}
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
collectConditionTreeParams(value, out);
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
if (isParam(value)) {
|
|
915
|
+
out[key2] = descriptorFromParam(key2, value, out[key2]);
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
if (value === null || typeof value !== "object" || value instanceof Date) {
|
|
919
|
+
continue;
|
|
920
|
+
}
|
|
921
|
+
const ops = value;
|
|
922
|
+
for (const [op, opVal] of Object.entries(ops)) {
|
|
923
|
+
if (op === "between") {
|
|
924
|
+
const [lo, hi] = opVal;
|
|
925
|
+
if (isParam(lo)) {
|
|
926
|
+
const n = conditionParamName(key2, op, 0);
|
|
927
|
+
out[n] = descriptorFromParam(n, lo, out[n]);
|
|
928
|
+
}
|
|
929
|
+
if (isParam(hi)) {
|
|
930
|
+
const n = conditionParamName(key2, op, 1);
|
|
931
|
+
out[n] = descriptorFromParam(n, hi, out[n]);
|
|
932
|
+
}
|
|
933
|
+
} else if (op === "in") {
|
|
934
|
+
opVal.forEach((v, i) => {
|
|
935
|
+
if (isParam(v)) {
|
|
936
|
+
const n = conditionParamName(key2, op, i);
|
|
937
|
+
out[n] = descriptorFromParam(n, v, out[n]);
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
} else if (SINGLE_OPERAND_OPS.has(op) && isParam(opVal)) {
|
|
941
|
+
const n = conditionParamName(key2, op);
|
|
942
|
+
out[n] = descriptorFromParam(n, opVal, out[n]);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
2636
946
|
}
|
|
2637
947
|
function makeDefinition(model2, operation, key2, select, changes, condition) {
|
|
2638
948
|
const params = collectParams(key2);
|
|
@@ -2770,7 +1080,6 @@ export {
|
|
|
2770
1080
|
buildUpdateInput,
|
|
2771
1081
|
collectContractBoundaryViolations,
|
|
2772
1082
|
collectContractN1Violations,
|
|
2773
|
-
command,
|
|
2774
1083
|
compileFilterExpression,
|
|
2775
1084
|
compileFragment,
|
|
2776
1085
|
compileMutationPlan,
|
|
@@ -2830,7 +1139,6 @@ export {
|
|
|
2830
1139
|
hasOne,
|
|
2831
1140
|
hydrate,
|
|
2832
1141
|
isColumn,
|
|
2833
|
-
isCommandBatchMode,
|
|
2834
1142
|
isCommandModelContract,
|
|
2835
1143
|
isCommandPlan,
|
|
2836
1144
|
isContractComposeNode,
|
|
@@ -2865,12 +1173,10 @@ export {
|
|
|
2865
1173
|
numberSet,
|
|
2866
1174
|
param,
|
|
2867
1175
|
plan,
|
|
2868
|
-
point,
|
|
2869
1176
|
publicCommandModel,
|
|
2870
1177
|
publicQueryModel,
|
|
2871
1178
|
query,
|
|
2872
1179
|
queryBoundaryRule,
|
|
2873
|
-
range,
|
|
2874
1180
|
relationDepthRule,
|
|
2875
1181
|
requireLimitRule,
|
|
2876
1182
|
resolveKey,
|