graphddb 0.7.7 → 0.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdc/index.d.ts +2 -2
- package/dist/cdc/index.js +2 -2
- package/dist/{chunk-WPABX7WG.js → chunk-NYM7K2ST.js} +983 -396
- package/dist/{chunk-F2DI3GTI.js → chunk-PFFPLD4B.js} +5 -1
- package/dist/{chunk-5NXNEW43.js → chunk-ZPNRLOKA.js} +1 -1
- package/dist/cli.js +387 -29
- package/dist/{from-change-D2zFApzb.d.ts → from-change-DanwjE5b.d.ts} +1 -1
- package/dist/{index-BnTZQ_IG.d.ts → index-CtPJSMrc.d.ts} +10 -5
- package/dist/index.d.ts +146 -117
- package/dist/index.js +83 -23
- package/dist/linter/index.d.ts +4 -4
- package/dist/{maintenance-view-adapter-C1HaBX4y.d.ts → maintenance-view-adapter-BATUh_I8.d.ts} +298 -81
- package/dist/{registry-DWhq5wiA.d.ts → registry-CXhP4TaE.d.ts} +1 -1
- package/dist/{relation-depth-BnqfKkZB.d.ts → relation-depth-Dg3yhl7S.d.ts} +1 -1
- package/dist/spec/index.d.ts +3 -3
- package/dist/spec/index.js +2 -2
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +2 -2
- package/package.json +2 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BATCH_GET_MAX_KEYS,
|
|
3
|
+
CONDITION_OPERATOR_KEYS,
|
|
3
4
|
ChangeCaptureRegistry,
|
|
4
5
|
ClientManager,
|
|
5
6
|
MAX_TRANSACT_ITEMS,
|
|
6
7
|
MetadataRegistry,
|
|
7
8
|
NO_MIDDLEWARE,
|
|
9
|
+
SELECT_SPEC,
|
|
8
10
|
attachHiddenKey,
|
|
9
11
|
attachModelClass,
|
|
10
12
|
buildConditionExpression,
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
chunkArray,
|
|
22
24
|
commitTransaction,
|
|
23
25
|
compileRawCondition,
|
|
26
|
+
condParamName,
|
|
24
27
|
ctxModelFor,
|
|
25
28
|
detectRelationFields,
|
|
26
29
|
execItemKeySignature,
|
|
@@ -49,7 +52,7 @@ import {
|
|
|
49
52
|
serializeRawCondition,
|
|
50
53
|
skTemplate,
|
|
51
54
|
validateInlineSnapshotSelect
|
|
52
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-PFFPLD4B.js";
|
|
53
56
|
import {
|
|
54
57
|
TableMapping,
|
|
55
58
|
createColumnMap,
|
|
@@ -201,6 +204,246 @@ function buildManifest(registry = MetadataRegistry) {
|
|
|
201
204
|
};
|
|
202
205
|
}
|
|
203
206
|
|
|
207
|
+
// src/spec/guard.ts
|
|
208
|
+
function conditionInputToSpec(condition, context, renderLeaf2, renderTreeLeaf, renderRawSlot) {
|
|
209
|
+
if (condition === void 0 || condition === null) return void 0;
|
|
210
|
+
if (isRawCondition(condition)) {
|
|
211
|
+
const { expression, names, values } = serializeRawCondition(
|
|
212
|
+
condition,
|
|
213
|
+
renderRawSlot
|
|
214
|
+
);
|
|
215
|
+
return { kind: "raw", expression, names, values };
|
|
216
|
+
}
|
|
217
|
+
if (typeof condition !== "object") {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`${context}: write condition must be an object (\`{ notExists: true }\`, \`{ attributeExists: '<field>' }\`, \`{ attributeNotExists: '<field>' }\`, or a declarative field condition).`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
const obj = condition;
|
|
223
|
+
if (obj.notExists === true) return { kind: "notExists" };
|
|
224
|
+
if ("attributeExists" in obj && typeof obj.attributeExists === "string") {
|
|
225
|
+
if (obj.attributeExists.length === 0) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`${context}: \`attributeExists\` must name a non-empty attribute field.`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
return { kind: "attributeExists", field: obj.attributeExists };
|
|
231
|
+
}
|
|
232
|
+
if ("attributeNotExists" in obj) {
|
|
233
|
+
if (typeof obj.attributeNotExists !== "string" || obj.attributeNotExists.length === 0) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`${context}: \`attributeNotExists\` must name a non-empty attribute field.`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return { kind: "attributeNotExists", field: obj.attributeNotExists };
|
|
239
|
+
}
|
|
240
|
+
if (usesOperatorTree(obj)) {
|
|
241
|
+
const leaf = renderTreeLeaf ?? ((value, name) => defaultTreeLeaf(value, name, context));
|
|
242
|
+
return { kind: "expr", declarative: buildTree(obj, context, leaf) };
|
|
243
|
+
}
|
|
244
|
+
const fields = {};
|
|
245
|
+
for (const key of Object.keys(obj).sort()) {
|
|
246
|
+
fields[key] = renderLeaf2(key, obj[key]);
|
|
247
|
+
}
|
|
248
|
+
return { kind: "equals", fields };
|
|
249
|
+
}
|
|
250
|
+
function assertNotNestedRaw(sub, context) {
|
|
251
|
+
if (isRawCondition(sub)) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`${context}: a raw \`cond\` write condition may be used as a whole condition, but not nested inside an \`and\` / \`or\` / \`not\` group of a serializable (public-contract / Python-bridge) command. Move the \`cond\` to the top level, or express the group declaratively.`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
var LOGICAL_KEYS = /* @__PURE__ */ new Set(["and", "or", "not"]);
|
|
258
|
+
var OPERATOR_KEYS = /* @__PURE__ */ new Set([
|
|
259
|
+
"eq",
|
|
260
|
+
"ne",
|
|
261
|
+
"gt",
|
|
262
|
+
"ge",
|
|
263
|
+
"lt",
|
|
264
|
+
"le",
|
|
265
|
+
"between",
|
|
266
|
+
"in",
|
|
267
|
+
"beginsWith",
|
|
268
|
+
"contains",
|
|
269
|
+
"notContains",
|
|
270
|
+
"attributeExists",
|
|
271
|
+
"attributeType",
|
|
272
|
+
"size"
|
|
273
|
+
]);
|
|
274
|
+
function isOperatorObject(value) {
|
|
275
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
const keys = Object.keys(value);
|
|
279
|
+
if (keys.length === 0) return false;
|
|
280
|
+
return keys.every((k) => OPERATOR_KEYS.has(k));
|
|
281
|
+
}
|
|
282
|
+
function usesOperatorTree(obj) {
|
|
283
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
284
|
+
if (LOGICAL_KEYS.has(key)) return true;
|
|
285
|
+
if (isOperatorObject(value)) return true;
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
function defaultTreeLeaf(value, name, context) {
|
|
290
|
+
void name;
|
|
291
|
+
if (value instanceof Date) return value.toISOString();
|
|
292
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
293
|
+
return value;
|
|
294
|
+
}
|
|
295
|
+
throw new Error(
|
|
296
|
+
`${context}: condition operand must be a concrete literal or a param; got ${typeof value}.`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
function conditionParamName(field, op, index) {
|
|
300
|
+
if (op === "between") return `${field}_${index === 0 ? "lo" : "hi"}`;
|
|
301
|
+
if (op === "in") return `${field}_${index ?? 0}`;
|
|
302
|
+
if (op === "size") return `${field}_size`;
|
|
303
|
+
return field;
|
|
304
|
+
}
|
|
305
|
+
function buildTree(obj, context, renderTreeLeaf) {
|
|
306
|
+
const out = {};
|
|
307
|
+
for (const key of Object.keys(obj).sort()) {
|
|
308
|
+
const value = obj[key];
|
|
309
|
+
if (value === void 0) continue;
|
|
310
|
+
if (key === "and" || key === "or") {
|
|
311
|
+
out[key] = value.map((sub) => {
|
|
312
|
+
assertNotNestedRaw(sub, context);
|
|
313
|
+
return buildTree(sub, context, renderTreeLeaf);
|
|
314
|
+
});
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (key === "not") {
|
|
318
|
+
assertNotNestedRaw(value, context);
|
|
319
|
+
out[key] = buildTree(
|
|
320
|
+
value,
|
|
321
|
+
context,
|
|
322
|
+
renderTreeLeaf
|
|
323
|
+
);
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (!isOperatorObject(value)) {
|
|
327
|
+
out[key] = { eq: renderTreeLeaf(value, conditionParamName(key, "eq")) };
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const ops = value;
|
|
331
|
+
const rendered = {};
|
|
332
|
+
for (const op of Object.keys(ops).sort()) {
|
|
333
|
+
const opVal = ops[op];
|
|
334
|
+
if (op === "between") {
|
|
335
|
+
const [lo, hi] = opVal;
|
|
336
|
+
rendered[op] = [
|
|
337
|
+
renderTreeLeaf(lo, conditionParamName(key, op, 0)),
|
|
338
|
+
renderTreeLeaf(hi, conditionParamName(key, op, 1))
|
|
339
|
+
];
|
|
340
|
+
} else if (op === "in") {
|
|
341
|
+
rendered[op] = opVal.map(
|
|
342
|
+
(v, i) => renderTreeLeaf(v, conditionParamName(key, op, i))
|
|
343
|
+
);
|
|
344
|
+
} else if (op === "attributeExists") {
|
|
345
|
+
rendered[op] = opVal;
|
|
346
|
+
} else if (op === "attributeType") {
|
|
347
|
+
rendered[op] = String(opVal);
|
|
348
|
+
} else {
|
|
349
|
+
rendered[op] = renderTreeLeaf(opVal, conditionParamName(key, op));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
out[key] = rendered;
|
|
353
|
+
}
|
|
354
|
+
return out;
|
|
355
|
+
}
|
|
356
|
+
function assertSupportedCondition(commandName, condition) {
|
|
357
|
+
if (condition.kind === "notExists") return;
|
|
358
|
+
if (condition.kind === "attributeExists" || condition.kind === "attributeNotExists") {
|
|
359
|
+
if (typeof condition.field !== "string" || condition.field.length === 0) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`Command '${commandName}' has an ${condition.kind} write condition with no attribute field. The Python bridge requires a non-empty field name.`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (condition.kind === "equals") {
|
|
367
|
+
const entries = Object.entries(condition.fields);
|
|
368
|
+
if (entries.length === 0) {
|
|
369
|
+
throw new Error(
|
|
370
|
+
`Command '${commandName}' has an empty equality write condition. The Python bridge supports '{ notExists }' or non-empty field equality conditions only.`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
for (const [field, value] of entries) {
|
|
374
|
+
if (typeof value !== "string") {
|
|
375
|
+
throw new Error(
|
|
376
|
+
`Command '${commandName}' write condition on '${field}' must be a template string value; the Python bridge supports equality on param/literal values only.`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (condition.kind === "raw") {
|
|
383
|
+
if (typeof condition.expression !== "string" || condition.expression.length === 0) {
|
|
384
|
+
throw new Error(
|
|
385
|
+
`Command '${commandName}' has an empty raw \`cond\` write condition.`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (condition.kind === "expr") {
|
|
391
|
+
const tree = condition.declarative;
|
|
392
|
+
if (!tree || Object.keys(tree).length === 0) {
|
|
393
|
+
throw new Error(
|
|
394
|
+
`Command '${commandName}' has an empty declarative write condition.`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
throw new Error(
|
|
400
|
+
`Command '${commandName}' has an unsupported write condition.`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
function assertJsonSerializable(value, path = "$") {
|
|
404
|
+
if (value === null) return;
|
|
405
|
+
switch (typeof value) {
|
|
406
|
+
case "string":
|
|
407
|
+
case "boolean":
|
|
408
|
+
return;
|
|
409
|
+
case "number":
|
|
410
|
+
if (!Number.isFinite(value)) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
`Bridge spec is not JSON-serializable: non-finite number at ${path}.`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
case "object":
|
|
417
|
+
break;
|
|
418
|
+
default:
|
|
419
|
+
throw new Error(
|
|
420
|
+
`Bridge spec is not JSON-serializable: '${typeof value}' at ${path}.`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
if (Array.isArray(value)) {
|
|
424
|
+
value.forEach((v, i) => assertJsonSerializable(v, `${path}[${i}]`));
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const proto = Object.getPrototypeOf(value);
|
|
428
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
`Bridge spec is not JSON-serializable: non-plain object (${value.constructor?.name ?? "unknown"}) at ${path}.`
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
for (const [k, v] of Object.entries(value)) {
|
|
434
|
+
if (v === void 0) {
|
|
435
|
+
throw new Error(
|
|
436
|
+
`Bridge spec is not JSON-serializable: 'undefined' value at ${path}.${k}.`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
assertJsonSerializable(v, `${path}.${k}`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function assertBundleSerializable(bundle) {
|
|
443
|
+
assertJsonSerializable(bundle.manifest, "$.manifest");
|
|
444
|
+
assertJsonSerializable(bundle.operations, "$.operations");
|
|
445
|
+
}
|
|
446
|
+
|
|
204
447
|
// src/planner/projection.ts
|
|
205
448
|
function buildProjection(select, additionalFields) {
|
|
206
449
|
const paths = [];
|
|
@@ -244,8 +487,8 @@ function buildProjection(select, additionalFields) {
|
|
|
244
487
|
}
|
|
245
488
|
|
|
246
489
|
// src/expression/filter-expression.ts
|
|
247
|
-
var
|
|
248
|
-
var
|
|
490
|
+
var LOGICAL_KEYS2 = /* @__PURE__ */ new Set(["and", "or", "not"]);
|
|
491
|
+
var OPERATOR_KEYS2 = /* @__PURE__ */ new Set([
|
|
249
492
|
"eq",
|
|
250
493
|
"ne",
|
|
251
494
|
"gt",
|
|
@@ -277,7 +520,7 @@ function valueAlias(ctx, field, raw) {
|
|
|
277
520
|
}
|
|
278
521
|
function compileField(ctx, field, condition) {
|
|
279
522
|
const nAlias = nameAlias(ctx, field);
|
|
280
|
-
if (!
|
|
523
|
+
if (!isOperatorObject2(condition)) {
|
|
281
524
|
const vAlias = valueAlias(ctx, field, condition);
|
|
282
525
|
return `${nAlias} = ${vAlias}`;
|
|
283
526
|
}
|
|
@@ -346,13 +589,13 @@ function compileField(ctx, field, condition) {
|
|
|
346
589
|
}
|
|
347
590
|
return joinAnd(clauses);
|
|
348
591
|
}
|
|
349
|
-
function
|
|
592
|
+
function isOperatorObject2(value) {
|
|
350
593
|
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date || value instanceof Uint8Array || value instanceof Set) {
|
|
351
594
|
return false;
|
|
352
595
|
}
|
|
353
596
|
const keys = Object.keys(value);
|
|
354
597
|
if (keys.length === 0) return false;
|
|
355
|
-
return keys.every((k) =>
|
|
598
|
+
return keys.every((k) => OPERATOR_KEYS2.has(k));
|
|
356
599
|
}
|
|
357
600
|
function joinAnd(clauses) {
|
|
358
601
|
if (clauses.length === 1) return clauses[0];
|
|
@@ -395,7 +638,7 @@ function compileNode(ctx, node) {
|
|
|
395
638
|
const clauses = [];
|
|
396
639
|
for (const [key, value] of Object.entries(node)) {
|
|
397
640
|
if (value === void 0) continue;
|
|
398
|
-
if (
|
|
641
|
+
if (LOGICAL_KEYS2.has(key)) {
|
|
399
642
|
if (key === "and" || key === "or") {
|
|
400
643
|
const parts = value.map((sub) => compileNode(ctx, sub)).filter((s) => s.length > 0);
|
|
401
644
|
if (parts.length === 0) continue;
|
|
@@ -476,7 +719,7 @@ function eq(a, b) {
|
|
|
476
719
|
return a === b;
|
|
477
720
|
}
|
|
478
721
|
function evaluateFieldCondition(actual, item, field, condition) {
|
|
479
|
-
if (!
|
|
722
|
+
if (!isOperatorObject2(condition)) {
|
|
480
723
|
return eq(actual, condition);
|
|
481
724
|
}
|
|
482
725
|
const ops = condition;
|
|
@@ -1512,17 +1755,165 @@ async function executeQueryInner(modelClass, key, selectSpec, options, mw) {
|
|
|
1512
1755
|
return hydratedItem;
|
|
1513
1756
|
}
|
|
1514
1757
|
|
|
1515
|
-
// src/
|
|
1516
|
-
function
|
|
1517
|
-
const
|
|
1518
|
-
const
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1758
|
+
// src/runtime/read-plan.ts
|
|
1759
|
+
function bindSlots(slots, params) {
|
|
1760
|
+
const out = {};
|
|
1761
|
+
for (const [field, slot] of Object.entries(slots)) {
|
|
1762
|
+
out[field] = slot.kind === "param" ? params[slot.name] : slot.value;
|
|
1763
|
+
}
|
|
1764
|
+
return out;
|
|
1765
|
+
}
|
|
1766
|
+
function bindOptionalSlot(slot, params) {
|
|
1767
|
+
if (slot === void 0) return void 0;
|
|
1768
|
+
return slot.kind === "param" ? params[slot.name] : slot.value;
|
|
1769
|
+
}
|
|
1770
|
+
function executeCompiledReadRoute(route, params, options) {
|
|
1771
|
+
const key = bindSlots(route.keySlots, params);
|
|
1772
|
+
const opts = { ...options ?? {} };
|
|
1773
|
+
if (route.kind === "query") {
|
|
1774
|
+
if (route.consistentReadSlot !== void 0) {
|
|
1775
|
+
opts.consistentRead = bindOptionalSlot(route.consistentReadSlot, params);
|
|
1776
|
+
}
|
|
1777
|
+
if (route.maxDepth !== void 0) opts.maxDepth = route.maxDepth;
|
|
1778
|
+
return executeQuery(route.modelClass, key, route.select, opts);
|
|
1779
|
+
}
|
|
1780
|
+
if (route.limitSlot !== void 0) {
|
|
1781
|
+
opts.limit = bindOptionalSlot(route.limitSlot, params);
|
|
1782
|
+
}
|
|
1783
|
+
if (route.afterSlot !== void 0) {
|
|
1784
|
+
opts.after = bindOptionalSlot(route.afterSlot, params);
|
|
1785
|
+
}
|
|
1786
|
+
if (route.order !== void 0) opts.order = route.order;
|
|
1787
|
+
if (route.filter !== void 0) opts.filter = route.filter;
|
|
1788
|
+
return executeList(route.modelClass, key, route.select, opts);
|
|
1789
|
+
}
|
|
1790
|
+
var MODEL_CLASS_IDS = /* @__PURE__ */ new WeakMap();
|
|
1791
|
+
var nextModelClassId = 1;
|
|
1792
|
+
function modelClassIdentity(cls) {
|
|
1793
|
+
let id = MODEL_CLASS_IDS.get(cls);
|
|
1794
|
+
if (id === void 0) {
|
|
1795
|
+
id = nextModelClassId++;
|
|
1796
|
+
MODEL_CLASS_IDS.set(cls, id);
|
|
1797
|
+
}
|
|
1798
|
+
return `model#${id}`;
|
|
1799
|
+
}
|
|
1800
|
+
var BoundedLru = class {
|
|
1801
|
+
constructor(max) {
|
|
1802
|
+
this.max = max;
|
|
1803
|
+
}
|
|
1804
|
+
max;
|
|
1805
|
+
map = /* @__PURE__ */ new Map();
|
|
1806
|
+
get(key) {
|
|
1807
|
+
const v = this.map.get(key);
|
|
1808
|
+
if (v !== void 0) {
|
|
1809
|
+
this.map.delete(key);
|
|
1810
|
+
this.map.set(key, v);
|
|
1811
|
+
}
|
|
1812
|
+
return v;
|
|
1813
|
+
}
|
|
1814
|
+
set(key, value) {
|
|
1815
|
+
if (this.map.has(key)) this.map.delete(key);
|
|
1816
|
+
this.map.set(key, value);
|
|
1817
|
+
if (this.map.size > this.max) {
|
|
1818
|
+
const oldest = this.map.keys().next().value;
|
|
1819
|
+
if (oldest !== void 0) this.map.delete(oldest);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
get size() {
|
|
1823
|
+
return this.map.size;
|
|
1824
|
+
}
|
|
1825
|
+
clear() {
|
|
1826
|
+
this.map.clear();
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
var ADHOC_READ_PLAN_CACHE_MAX = 256;
|
|
1830
|
+
var ADHOC_READ_PLAN_CACHE = new BoundedLru(ADHOC_READ_PLAN_CACHE_MAX);
|
|
1831
|
+
var adHocCompileCount = 0;
|
|
1832
|
+
function shapeToken(value) {
|
|
1833
|
+
if (value === null) return "null";
|
|
1834
|
+
const t = typeof value;
|
|
1835
|
+
if (t === "string") return JSON.stringify(value);
|
|
1836
|
+
if (t === "number" || t === "boolean") return String(value);
|
|
1837
|
+
if (t === "undefined") return "u";
|
|
1838
|
+
if (t === "bigint") return `n${String(value)}`;
|
|
1839
|
+
if (t === "function" || t === "symbol") return null;
|
|
1840
|
+
if (value instanceof Date) return `D${value.getTime()}`;
|
|
1841
|
+
if (Array.isArray(value)) {
|
|
1842
|
+
const parts2 = [];
|
|
1843
|
+
for (const v of value) {
|
|
1844
|
+
const p = shapeToken(v);
|
|
1845
|
+
if (p === null) return null;
|
|
1846
|
+
parts2.push(p);
|
|
1847
|
+
}
|
|
1848
|
+
return `[${parts2.join(",")}]`;
|
|
1849
|
+
}
|
|
1850
|
+
if (isSelectBuilder(value)) {
|
|
1851
|
+
const spec = value[SELECT_SPEC];
|
|
1852
|
+
const inner = shapeToken({
|
|
1853
|
+
select: spec.select,
|
|
1854
|
+
filter: spec.filter,
|
|
1855
|
+
limit: spec.limit,
|
|
1856
|
+
after: spec.after,
|
|
1857
|
+
order: spec.order
|
|
1858
|
+
});
|
|
1859
|
+
return inner === null ? null : `B${inner}`;
|
|
1860
|
+
}
|
|
1861
|
+
const proto = Object.getPrototypeOf(value);
|
|
1862
|
+
if (proto !== Object.prototype && proto !== null) return null;
|
|
1863
|
+
const rec = value;
|
|
1864
|
+
const parts = [];
|
|
1865
|
+
for (const k of Object.keys(rec).sort()) {
|
|
1866
|
+
const p = shapeToken(rec[k]);
|
|
1867
|
+
if (p === null) return null;
|
|
1868
|
+
parts.push(`${JSON.stringify(k)}:${p}`);
|
|
1869
|
+
}
|
|
1870
|
+
return `{${parts.join(",")}}`;
|
|
1871
|
+
}
|
|
1872
|
+
function adHocStructureKey(kind, modelClass, key, select) {
|
|
1873
|
+
const selectToken = shapeToken(select);
|
|
1874
|
+
if (selectToken === null) return null;
|
|
1875
|
+
const fields = Object.keys(key).map((f) => JSON.stringify(f)).join(",");
|
|
1876
|
+
return `${kind}|${modelClassIdentity(modelClass)}|${fields}|${selectToken}`;
|
|
1877
|
+
}
|
|
1878
|
+
function lowerAdHocReadRoute(kind, modelClass, key, select) {
|
|
1879
|
+
const cacheKey = adHocStructureKey(kind, modelClass, key, select);
|
|
1880
|
+
if (cacheKey !== null) {
|
|
1881
|
+
const cached = ADHOC_READ_PLAN_CACHE.get(cacheKey);
|
|
1882
|
+
if (cached !== void 0) return cached;
|
|
1883
|
+
}
|
|
1884
|
+
adHocCompileCount++;
|
|
1885
|
+
const keySlots = {};
|
|
1886
|
+
for (const field of Object.keys(key)) {
|
|
1887
|
+
keySlots[field] = { kind: "param", name: field };
|
|
1888
|
+
}
|
|
1889
|
+
const route = { alias: "$adhoc", kind, modelClass, select, keySlots };
|
|
1890
|
+
if (cacheKey !== null) ADHOC_READ_PLAN_CACHE.set(cacheKey, route);
|
|
1891
|
+
return route;
|
|
1892
|
+
}
|
|
1893
|
+
function runAdHoc(route, select, key, options) {
|
|
1894
|
+
const effective = route.select === select ? route : { ...route, select };
|
|
1895
|
+
return executeCompiledReadRoute(effective, key, options);
|
|
1896
|
+
}
|
|
1897
|
+
function executeAdHocQuery(modelClass, key, selectSpec, options) {
|
|
1898
|
+
const route = lowerAdHocReadRoute("query", modelClass, key, selectSpec);
|
|
1899
|
+
return runAdHoc(route, selectSpec, key, options);
|
|
1900
|
+
}
|
|
1901
|
+
function executeAdHocList(modelClass, key, selectSpec, options) {
|
|
1902
|
+
const route = lowerAdHocReadRoute("list", modelClass, key, selectSpec);
|
|
1903
|
+
return runAdHoc(route, selectSpec, key, options);
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
// src/operations/explain.ts
|
|
1907
|
+
function executeExplain(modelClass, key, options) {
|
|
1908
|
+
const metadata = MetadataRegistry.get(modelClass);
|
|
1909
|
+
const exclusiveStartKey = options?.after ? decodeCursor(options.after) : void 0;
|
|
1910
|
+
const normalized = normalizeTopLevelSelect(options?.select);
|
|
1911
|
+
const select = normalized.select ?? {};
|
|
1912
|
+
const filter = options?.filter ?? normalized.filter;
|
|
1913
|
+
const mainPlan = plan(metadata, {
|
|
1914
|
+
key,
|
|
1915
|
+
select,
|
|
1916
|
+
limit: options?.limit,
|
|
1526
1917
|
after: exclusiveStartKey,
|
|
1527
1918
|
order: options?.order,
|
|
1528
1919
|
consistentRead: options?.consistentRead,
|
|
@@ -2597,6 +2988,53 @@ function renderInputLeaf(leaf, isKeyFieldInPut, consumerIndex, consumerField, re
|
|
|
2597
2988
|
}
|
|
2598
2989
|
return leaf;
|
|
2599
2990
|
}
|
|
2991
|
+
function isConditionOperatorObject(value) {
|
|
2992
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date || isMutationInputRef(value)) {
|
|
2993
|
+
return false;
|
|
2994
|
+
}
|
|
2995
|
+
const keys = Object.keys(value);
|
|
2996
|
+
if (keys.length === 0) return false;
|
|
2997
|
+
return keys.every((k) => CONDITION_OPERATOR_KEYS.has(k));
|
|
2998
|
+
}
|
|
2999
|
+
function lowerConditionLeaf(leaf) {
|
|
3000
|
+
if (isMutationInputRef(leaf)) return mintContractParamRef(leaf.field);
|
|
3001
|
+
return leaf;
|
|
3002
|
+
}
|
|
3003
|
+
function lowerFragmentCondition(condition) {
|
|
3004
|
+
const obj = condition;
|
|
3005
|
+
const out = {};
|
|
3006
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
3007
|
+
if (value === void 0) continue;
|
|
3008
|
+
if (key === "and" || key === "or") {
|
|
3009
|
+
out[key] = value.map((sub) => lowerFragmentCondition(sub));
|
|
3010
|
+
continue;
|
|
3011
|
+
}
|
|
3012
|
+
if (key === "not") {
|
|
3013
|
+
out[key] = lowerFragmentCondition(value);
|
|
3014
|
+
continue;
|
|
3015
|
+
}
|
|
3016
|
+
if (!isConditionOperatorObject(value)) {
|
|
3017
|
+
out[key] = lowerConditionLeaf(value);
|
|
3018
|
+
continue;
|
|
3019
|
+
}
|
|
3020
|
+
const ops = value;
|
|
3021
|
+
const rendered = {};
|
|
3022
|
+
for (const [op, opVal] of Object.entries(ops)) {
|
|
3023
|
+
if (op === "between") {
|
|
3024
|
+
const [lo, hi] = opVal;
|
|
3025
|
+
rendered[op] = [lowerConditionLeaf(lo), lowerConditionLeaf(hi)];
|
|
3026
|
+
} else if (op === "in") {
|
|
3027
|
+
rendered[op] = opVal.map((v) => lowerConditionLeaf(v));
|
|
3028
|
+
} else if (op === "attributeExists" || op === "attributeType") {
|
|
3029
|
+
rendered[op] = opVal;
|
|
3030
|
+
} else {
|
|
3031
|
+
rendered[op] = lowerConditionLeaf(opVal);
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
out[key] = rendered;
|
|
3035
|
+
}
|
|
3036
|
+
return out;
|
|
3037
|
+
}
|
|
2600
3038
|
function compileFragment(fragment, index = 0, resolveEntityRef = null, maintenanceGraph) {
|
|
2601
3039
|
const keyFields = primaryKeyFields(fragment);
|
|
2602
3040
|
const keyFieldSet = new Set(keyFields);
|
|
@@ -2609,6 +3047,7 @@ function compileFragment(fragment, index = 0, resolveEntityRef = null, maintenan
|
|
|
2609
3047
|
);
|
|
2610
3048
|
}
|
|
2611
3049
|
}
|
|
3050
|
+
const userCondition = fragment.condition !== void 0 ? lowerFragmentCondition(fragment.condition) : void 0;
|
|
2612
3051
|
let op;
|
|
2613
3052
|
if (fragment.intent === "create") {
|
|
2614
3053
|
const item = {};
|
|
@@ -2621,6 +3060,7 @@ function compileFragment(fragment, index = 0, resolveEntityRef = null, maintenan
|
|
|
2621
3060
|
resolveEntityRef
|
|
2622
3061
|
);
|
|
2623
3062
|
}
|
|
3063
|
+
const condition = userCondition !== void 0 ? { and: [{ PK: { attributeExists: false } }, userCondition] } : { notExists: true };
|
|
2624
3064
|
op = {
|
|
2625
3065
|
__isContractMethodOp: true,
|
|
2626
3066
|
entity: fragment.entity,
|
|
@@ -2628,9 +3068,7 @@ function compileFragment(fragment, index = 0, resolveEntityRef = null, maintenan
|
|
|
2628
3068
|
keys: wholeKeysSentinel(),
|
|
2629
3069
|
// a `put` derives identity from the item.
|
|
2630
3070
|
item,
|
|
2631
|
-
|
|
2632
|
-
// the proposal specifies (create → Put `attribute_not_exists(PK)`).
|
|
2633
|
-
condition: { notExists: true }
|
|
3071
|
+
condition
|
|
2634
3072
|
};
|
|
2635
3073
|
} else {
|
|
2636
3074
|
const changes = {};
|
|
@@ -2644,7 +3082,11 @@ function compileFragment(fragment, index = 0, resolveEntityRef = null, maintenan
|
|
|
2644
3082
|
operation,
|
|
2645
3083
|
keys: wholeKeysSentinel(),
|
|
2646
3084
|
keyFields,
|
|
2647
|
-
...fragment.intent === "update" ? { changes } : {}
|
|
3085
|
+
...fragment.intent === "update" ? { changes } : {},
|
|
3086
|
+
// #242: a conditioned update / remove — the write is a CAS-style gate (the
|
|
3087
|
+
// row must satisfy the condition or the write is rejected, not silently
|
|
3088
|
+
// committed). Absent → an unconditional update / remove (unchanged).
|
|
3089
|
+
...userCondition !== void 0 ? { condition: userCondition } : {}
|
|
2648
3090
|
};
|
|
2649
3091
|
}
|
|
2650
3092
|
const conditionChecks = lifecycle?.effects.requires !== void 0 && lifecycle.effects.requires.length > 0 ? deriveConditionChecks(fragment, lifecycle.effects.requires) : void 0;
|
|
@@ -2832,13 +3274,62 @@ function renderCondition(condition, key, params, context) {
|
|
|
2832
3274
|
return { attributeNotExists: obj.attributeNotExists };
|
|
2833
3275
|
}
|
|
2834
3276
|
}
|
|
2835
|
-
return
|
|
3277
|
+
return renderConditionTree(
|
|
2836
3278
|
condition,
|
|
2837
3279
|
key,
|
|
2838
3280
|
params,
|
|
2839
3281
|
`${context} condition`
|
|
2840
3282
|
);
|
|
2841
3283
|
}
|
|
3284
|
+
function isConditionOperatorObject2(value) {
|
|
3285
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date || isContractParamRef(value) || isContractKeyFieldRef(value)) {
|
|
3286
|
+
return false;
|
|
3287
|
+
}
|
|
3288
|
+
const keys = Object.keys(value);
|
|
3289
|
+
if (keys.length === 0) return false;
|
|
3290
|
+
return keys.every((k) => CONDITION_OPERATOR_KEYS.has(k));
|
|
3291
|
+
}
|
|
3292
|
+
function renderConditionTree(node, key, params, context) {
|
|
3293
|
+
const out = {};
|
|
3294
|
+
for (const [k, value] of Object.entries(node)) {
|
|
3295
|
+
if (value === void 0) continue;
|
|
3296
|
+
if (k === "and" || k === "or") {
|
|
3297
|
+
out[k] = value.map(
|
|
3298
|
+
(sub) => renderConditionTree(sub, key, params, `${context} ${k}`)
|
|
3299
|
+
);
|
|
3300
|
+
continue;
|
|
3301
|
+
}
|
|
3302
|
+
if (k === "not") {
|
|
3303
|
+
out[k] = renderConditionTree(value, key, params, `${context} not`);
|
|
3304
|
+
continue;
|
|
3305
|
+
}
|
|
3306
|
+
if (!isConditionOperatorObject2(value)) {
|
|
3307
|
+
out[k] = renderLeaf(value, key, params, `${context} field '${k}'`);
|
|
3308
|
+
continue;
|
|
3309
|
+
}
|
|
3310
|
+
const ops = value;
|
|
3311
|
+
const rendered = {};
|
|
3312
|
+
for (const [op, opVal] of Object.entries(ops)) {
|
|
3313
|
+
if (op === "between") {
|
|
3314
|
+
const [lo, hi] = opVal;
|
|
3315
|
+
rendered[op] = [
|
|
3316
|
+
renderLeaf(lo, key, params, `${context} field '${k}' between[0]`),
|
|
3317
|
+
renderLeaf(hi, key, params, `${context} field '${k}' between[1]`)
|
|
3318
|
+
];
|
|
3319
|
+
} else if (op === "in") {
|
|
3320
|
+
rendered[op] = opVal.map(
|
|
3321
|
+
(v, i) => renderLeaf(v, key, params, `${context} field '${k}' in[${i}]`)
|
|
3322
|
+
);
|
|
3323
|
+
} else if (op === "attributeExists" || op === "attributeType") {
|
|
3324
|
+
rendered[op] = opVal;
|
|
3325
|
+
} else {
|
|
3326
|
+
rendered[op] = renderLeaf(opVal, key, params, `${context} field '${k}' ${op}`);
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
out[k] = rendered;
|
|
3330
|
+
}
|
|
3331
|
+
return out;
|
|
3332
|
+
}
|
|
2842
3333
|
function renderWrite(op, key, params, contextLabel) {
|
|
2843
3334
|
if (!isContractKeyRef(op.keys) && op.operation !== "put") {
|
|
2844
3335
|
throw new Error(
|
|
@@ -2924,6 +3415,7 @@ function renderConditionCheck(check, key, params, contextLabel) {
|
|
|
2924
3415
|
}
|
|
2925
3416
|
var RUNTIME_TOKEN_RE = /\{([^{}]+)\}/g;
|
|
2926
3417
|
function renderTemplate(template, key, params, contextLabel) {
|
|
3418
|
+
if (typeof template !== "string") return template;
|
|
2927
3419
|
const resolveToken = (token) => {
|
|
2928
3420
|
if (token in params) return params[token];
|
|
2929
3421
|
if (token in key) return key[token];
|
|
@@ -3930,13 +4422,13 @@ async function executeReadRoute(alias, route) {
|
|
|
3930
4422
|
);
|
|
3931
4423
|
const opt = route.options ?? {};
|
|
3932
4424
|
if (isQuery) {
|
|
3933
|
-
return
|
|
4425
|
+
return executeAdHocQuery(modelClass, route.key, route.select, {
|
|
3934
4426
|
consistentRead: opt.consistentRead,
|
|
3935
4427
|
maxDepth: opt.maxDepth,
|
|
3936
4428
|
context: opt.context
|
|
3937
4429
|
});
|
|
3938
4430
|
}
|
|
3939
|
-
return
|
|
4431
|
+
return executeAdHocList(modelClass, route.key, route.select, {
|
|
3940
4432
|
limit: opt.limit,
|
|
3941
4433
|
after: opt.after,
|
|
3942
4434
|
order: opt.order,
|
|
@@ -4171,17 +4663,6 @@ function recordSlots(binding, where) {
|
|
|
4171
4663
|
}
|
|
4172
4664
|
return { fields, slots };
|
|
4173
4665
|
}
|
|
4174
|
-
function bindSlots(slots, params) {
|
|
4175
|
-
const out = {};
|
|
4176
|
-
for (const [field, slot] of Object.entries(slots)) {
|
|
4177
|
-
out[field] = slot.kind === "param" ? params[slot.name] : slot.value;
|
|
4178
|
-
}
|
|
4179
|
-
return out;
|
|
4180
|
-
}
|
|
4181
|
-
function bindOptionalSlot(slot, params) {
|
|
4182
|
-
if (slot === void 0) return void 0;
|
|
4183
|
-
return slot.kind === "param" ? params[slot.name] : slot.value;
|
|
4184
|
-
}
|
|
4185
4666
|
function recordOptionSlot(value, where) {
|
|
4186
4667
|
if (value === void 0) return void 0;
|
|
4187
4668
|
if (isPreparedParamRef(value)) return { kind: "param", name: value.name };
|
|
@@ -4241,23 +4722,7 @@ var PreparedReadStatement = class {
|
|
|
4241
4722
|
return out;
|
|
4242
4723
|
}
|
|
4243
4724
|
runRoute(route, params, options) {
|
|
4244
|
-
|
|
4245
|
-
const consistentRead = bindOptionalSlot(route.consistentReadSlot, params);
|
|
4246
|
-
if (route.kind === "query") {
|
|
4247
|
-
return executeQuery(route.modelClass, key, route.select, {
|
|
4248
|
-
consistentRead,
|
|
4249
|
-
maxDepth: route.maxDepth,
|
|
4250
|
-
...options.retry !== void 0 ? { retry: options.retry } : {},
|
|
4251
|
-
...options.context !== void 0 ? { context: options.context } : {}
|
|
4252
|
-
});
|
|
4253
|
-
}
|
|
4254
|
-
const limit = bindOptionalSlot(route.limitSlot, params);
|
|
4255
|
-
const after = bindOptionalSlot(route.afterSlot, params);
|
|
4256
|
-
return executeList(route.modelClass, key, route.select, {
|
|
4257
|
-
limit,
|
|
4258
|
-
after,
|
|
4259
|
-
order: route.order,
|
|
4260
|
-
filter: route.filter,
|
|
4725
|
+
return executeCompiledReadRoute(route, params, {
|
|
4261
4726
|
...options.retry !== void 0 ? { retry: options.retry } : {},
|
|
4262
4727
|
...options.context !== void 0 ? { context: options.context } : {}
|
|
4263
4728
|
});
|
|
@@ -4421,35 +4886,6 @@ function assertStaticTemplate(value, where) {
|
|
|
4421
4886
|
}
|
|
4422
4887
|
}
|
|
4423
4888
|
var PREPARE_CACHE_MAX = 256;
|
|
4424
|
-
var BoundedLru = class {
|
|
4425
|
-
constructor(max) {
|
|
4426
|
-
this.max = max;
|
|
4427
|
-
}
|
|
4428
|
-
max;
|
|
4429
|
-
map = /* @__PURE__ */ new Map();
|
|
4430
|
-
get(key) {
|
|
4431
|
-
const v = this.map.get(key);
|
|
4432
|
-
if (v !== void 0) {
|
|
4433
|
-
this.map.delete(key);
|
|
4434
|
-
this.map.set(key, v);
|
|
4435
|
-
}
|
|
4436
|
-
return v;
|
|
4437
|
-
}
|
|
4438
|
-
set(key, value) {
|
|
4439
|
-
if (this.map.has(key)) this.map.delete(key);
|
|
4440
|
-
this.map.set(key, value);
|
|
4441
|
-
if (this.map.size > this.max) {
|
|
4442
|
-
const oldest = this.map.keys().next().value;
|
|
4443
|
-
if (oldest !== void 0) this.map.delete(oldest);
|
|
4444
|
-
}
|
|
4445
|
-
}
|
|
4446
|
-
get size() {
|
|
4447
|
-
return this.map.size;
|
|
4448
|
-
}
|
|
4449
|
-
clear() {
|
|
4450
|
-
this.map.clear();
|
|
4451
|
-
}
|
|
4452
|
-
};
|
|
4453
4889
|
var PREPARE_CACHE = new BoundedLru(PREPARE_CACHE_MAX);
|
|
4454
4890
|
function structureKey(routes) {
|
|
4455
4891
|
const parts = routes.map(({ alias, route }) => {
|
|
@@ -4465,18 +4901,11 @@ function structureKey(routes) {
|
|
|
4465
4901
|
});
|
|
4466
4902
|
return parts.join("||");
|
|
4467
4903
|
}
|
|
4468
|
-
var MODEL_IDENTITY_IDS = /* @__PURE__ */ new WeakMap();
|
|
4469
|
-
var nextModelIdentityId = 1;
|
|
4470
4904
|
function modelIdentity(ref, alias) {
|
|
4471
4905
|
try {
|
|
4472
4906
|
const model = resolveModelStatic(ref, alias);
|
|
4473
4907
|
const cls = resolveModelClass(model);
|
|
4474
|
-
|
|
4475
|
-
if (id === void 0) {
|
|
4476
|
-
id = nextModelIdentityId++;
|
|
4477
|
-
MODEL_IDENTITY_IDS.set(cls, id);
|
|
4478
|
-
}
|
|
4479
|
-
return `model#${id}`;
|
|
4908
|
+
return modelClassIdentity(cls);
|
|
4480
4909
|
} catch {
|
|
4481
4910
|
return "unresolved";
|
|
4482
4911
|
}
|
|
@@ -4776,10 +5205,10 @@ var DDBModel = class {
|
|
|
4776
5205
|
// method type — the public overloads (the only caller-visible surface)
|
|
4777
5206
|
// are untouched.
|
|
4778
5207
|
query: ((key, select, options) => {
|
|
4779
|
-
return
|
|
5208
|
+
return executeAdHocQuery(modelClass, key, select, options);
|
|
4780
5209
|
}),
|
|
4781
5210
|
list: ((key, select, options) => {
|
|
4782
|
-
return
|
|
5211
|
+
return executeAdHocList(modelClass, key, select, options);
|
|
4783
5212
|
}),
|
|
4784
5213
|
explain(key, options) {
|
|
4785
5214
|
return executeExplain(
|
|
@@ -4945,23 +5374,107 @@ function assertFaithfulInput(input, intent, entityName) {
|
|
|
4945
5374
|
}
|
|
4946
5375
|
return input;
|
|
4947
5376
|
}
|
|
5377
|
+
function isConditionOperatorObject3(value) {
|
|
5378
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date || isMutationInputRef(value)) {
|
|
5379
|
+
return false;
|
|
5380
|
+
}
|
|
5381
|
+
const keys = Object.keys(value);
|
|
5382
|
+
if (keys.length === 0) return false;
|
|
5383
|
+
return keys.every((k) => CONDITION_OPERATOR_KEYS.has(k));
|
|
5384
|
+
}
|
|
5385
|
+
function assertFaithfulConditionLeaf(leaf, intent, entityName, where) {
|
|
5386
|
+
if (isMutationInputRef(leaf) || isConcreteLiteral(leaf)) return;
|
|
5387
|
+
if (isAliasFieldRef(leaf)) {
|
|
5388
|
+
throw new Error(
|
|
5389
|
+
`mutation: the '${intent}' fragment on '${entityName}' uses a \`$.alias.field\` cross-fragment reference in its \`condition\` (${where}). A write condition gates a single row against the command INPUT (or the row's own persisted attributes) \u2014 a \`TransactWriteItems\` cannot read another item's value mid-transaction to gate this write. Use a \`$.field\` input reference or a literal.`
|
|
5390
|
+
);
|
|
5391
|
+
}
|
|
5392
|
+
throw new Error(
|
|
5393
|
+
`mutation: the '${intent}' fragment on '${entityName}' binds a \`condition\` operand (${where}) to a value that is neither a \`$.field\` input reference nor a concrete literal. A write condition is declarative \u2014 only \`$.field\` references or literals are allowed, never a computed value.`
|
|
5394
|
+
);
|
|
5395
|
+
}
|
|
5396
|
+
function assertFaithfulCondition(condition, intent, entityName) {
|
|
5397
|
+
if (condition === null || typeof condition !== "object" || Array.isArray(condition)) {
|
|
5398
|
+
throw new Error(
|
|
5399
|
+
`mutation: the '${intent}' fragment on '${entityName}' has a non-object \`condition\`. A write condition is a declarative field-clause / logical-group object (e.g. \`{ status: 'PENDING', version: { in: [$.v] } }\` or \`{ and: [ \u2026 ] }\`).`
|
|
5400
|
+
);
|
|
5401
|
+
}
|
|
5402
|
+
const obj = condition;
|
|
5403
|
+
if (Object.keys(obj).length === 0) {
|
|
5404
|
+
throw new Error(
|
|
5405
|
+
`mutation: the '${intent}' fragment on '${entityName}' declared an EMPTY \`condition\` object. Omit \`condition\` entirely for an unconditional write, or declare a real gate.`
|
|
5406
|
+
);
|
|
5407
|
+
}
|
|
5408
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
5409
|
+
if (value === void 0) continue;
|
|
5410
|
+
if (key === "and" || key === "or") {
|
|
5411
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
5412
|
+
throw new Error(
|
|
5413
|
+
`mutation: the '${intent}' fragment on '${entityName}' \`condition\` \`${key}\` must be a non-empty array of sub-conditions.`
|
|
5414
|
+
);
|
|
5415
|
+
}
|
|
5416
|
+
for (const sub of value) assertFaithfulCondition(sub, intent, entityName);
|
|
5417
|
+
continue;
|
|
5418
|
+
}
|
|
5419
|
+
if (key === "not") {
|
|
5420
|
+
assertFaithfulCondition(value, intent, entityName);
|
|
5421
|
+
continue;
|
|
5422
|
+
}
|
|
5423
|
+
if (!isConditionOperatorObject3(value)) {
|
|
5424
|
+
assertFaithfulConditionLeaf(value, intent, entityName, `field '${key}'`);
|
|
5425
|
+
continue;
|
|
5426
|
+
}
|
|
5427
|
+
const ops = value;
|
|
5428
|
+
for (const [op, opVal] of Object.entries(ops)) {
|
|
5429
|
+
if (op === "between") {
|
|
5430
|
+
if (!Array.isArray(opVal) || opVal.length !== 2) {
|
|
5431
|
+
throw new Error(
|
|
5432
|
+
`mutation: the '${intent}' fragment on '${entityName}' \`condition\` field '${key}' \`between\` takes a \`[lo, hi]\` pair.`
|
|
5433
|
+
);
|
|
5434
|
+
}
|
|
5435
|
+
assertFaithfulConditionLeaf(opVal[0], intent, entityName, `field '${key}' between[0]`);
|
|
5436
|
+
assertFaithfulConditionLeaf(opVal[1], intent, entityName, `field '${key}' between[1]`);
|
|
5437
|
+
} else if (op === "in") {
|
|
5438
|
+
if (!Array.isArray(opVal) || opVal.length === 0) {
|
|
5439
|
+
throw new Error(
|
|
5440
|
+
`mutation: the '${intent}' fragment on '${entityName}' \`condition\` field '${key}' \`in\` takes a non-empty array of operands.`
|
|
5441
|
+
);
|
|
5442
|
+
}
|
|
5443
|
+
opVal.forEach(
|
|
5444
|
+
(v, i) => assertFaithfulConditionLeaf(v, intent, entityName, `field '${key}' in[${i}]`)
|
|
5445
|
+
);
|
|
5446
|
+
} else if (op === "attributeExists" || op === "attributeType") {
|
|
5447
|
+
if (isMutationInputRef(opVal)) {
|
|
5448
|
+
throw new Error(
|
|
5449
|
+
`mutation: the '${intent}' fragment on '${entityName}' \`condition\` field '${key}' \`${op}\` was given a \`$.field\` reference. \`${op}\` takes a ${op === "attributeExists" ? "concrete boolean" : "concrete DynamoDB type-code string"} (a structural operand, not a value-bound param). Use a literal.`
|
|
5450
|
+
);
|
|
5451
|
+
}
|
|
5452
|
+
} else {
|
|
5453
|
+
assertFaithfulConditionLeaf(opVal, intent, entityName, `field '${key}' ${op}`);
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
return condition;
|
|
5458
|
+
}
|
|
4948
5459
|
function isConcreteLiteral(value) {
|
|
4949
5460
|
const t = typeof value;
|
|
4950
5461
|
return value === null || value instanceof Date || t === "string" || t === "number" || t === "boolean";
|
|
4951
5462
|
}
|
|
4952
|
-
function makeFragment(intent, entity, input, use) {
|
|
5463
|
+
function makeFragment(intent, entity, input, use, condition) {
|
|
4953
5464
|
const checked = assertFaithfulInput(input ?? {}, intent, entity.name);
|
|
4954
5465
|
if (use !== void 0 && !isEntityWritesDefinition(use)) {
|
|
4955
5466
|
throw new Error(
|
|
4956
5467
|
`mutation: the '${intent}' fragment on '${entity.name}' was given a \`use:\` that is not an \`entityWrites(...)\` save contract. \`use:\` takes the WHOLE \`writes\` set (an \`entityWrites(...)\` value), never a single lifecycle (e.g. \`Model.writes.create\`) \u2014 the fragment's intent already selects the lifecycle.`
|
|
4957
5468
|
);
|
|
4958
5469
|
}
|
|
5470
|
+
const checkedCondition = condition !== void 0 ? assertFaithfulCondition(condition, intent, entity.name) : void 0;
|
|
4959
5471
|
return {
|
|
4960
5472
|
[FRAGMENT_BRAND]: true,
|
|
4961
5473
|
intent,
|
|
4962
5474
|
entity,
|
|
4963
5475
|
input: checked,
|
|
4964
|
-
...use !== void 0 ? { use } : {}
|
|
5476
|
+
...use !== void 0 ? { use } : {},
|
|
5477
|
+
...checkedCondition !== void 0 ? { condition: checkedCondition } : {}
|
|
4965
5478
|
};
|
|
4966
5479
|
}
|
|
4967
5480
|
var COMMAND_PLAN_BRAND = /* @__PURE__ */ Symbol("graphddb:commandPlan");
|
|
@@ -5055,7 +5568,7 @@ function mutation(nameOrBody, maybeBody) {
|
|
|
5055
5568
|
const { intent, entity } = resolveDescriptorTarget(descriptor, alias);
|
|
5056
5569
|
const merged = mergeBindings(descriptor, intent, alias);
|
|
5057
5570
|
const resolved = resolveAliasRefs(merged, aliasToIndex, alias);
|
|
5058
|
-
return makeFragment(intent, entity, resolved, descriptor.use);
|
|
5571
|
+
return makeFragment(intent, entity, resolved, descriptor.use, descriptor.condition);
|
|
5059
5572
|
});
|
|
5060
5573
|
return { [COMMAND_PLAN_BRAND]: true, name, fragments };
|
|
5061
5574
|
}
|
|
@@ -5375,320 +5888,148 @@ function descriptorKeyRecord(record, name, what) {
|
|
|
5375
5888
|
}
|
|
5376
5889
|
return out;
|
|
5377
5890
|
}
|
|
5378
|
-
function
|
|
5379
|
-
|
|
5380
|
-
for (const [field, leaf] of Object.entries(record)) {
|
|
5381
|
-
if (isParam(leaf)) {
|
|
5382
|
-
out[field] = mintContractParamRef(field);
|
|
5383
|
-
} else {
|
|
5384
|
-
out[field] = leaf;
|
|
5385
|
-
}
|
|
5386
|
-
}
|
|
5387
|
-
return out;
|
|
5388
|
-
}
|
|
5389
|
-
function readDescriptorToOp(name, d) {
|
|
5390
|
-
const isQuery = d.query !== void 0;
|
|
5391
|
-
const isList = d.list !== void 0;
|
|
5392
|
-
if (isQuery === isList) {
|
|
5393
|
-
throw new Error(
|
|
5394
|
-
`publicQueryModel: method '${name}' must carry exactly one of \`query\` or \`list\` (the target model); it declared ${isQuery && isList ? "both" : "neither"}.`
|
|
5395
|
-
);
|
|
5396
|
-
}
|
|
5397
|
-
if (d.key === void 0 || d.select === void 0) {
|
|
5398
|
-
throw new Error(
|
|
5399
|
-
`publicQueryModel: the '${isQuery ? "query" : "list"}' descriptor for method '${name}' must declare both \`key\` and \`select\`.`
|
|
5400
|
-
);
|
|
5401
|
-
}
|
|
5402
|
-
const model = resolveDescriptorModel(isQuery ? d.query : d.list, name);
|
|
5403
|
-
const entity = entityRef(model);
|
|
5404
|
-
const operation = isQuery ? "query" : "list";
|
|
5405
|
-
const keyFieldNames2 = Object.keys(d.key);
|
|
5406
|
-
const keyRecord = descriptorKeyRecord(d.key, name, `${operation} key`);
|
|
5407
|
-
const { select, compose } = extractCompose({ ...d.select }, operation, entity.name);
|
|
5408
|
-
const keys = isQuery ? wholeKeysSentinel() : keyRecord;
|
|
5409
|
-
return {
|
|
5410
|
-
__isContractMethodOp: true,
|
|
5411
|
-
entity,
|
|
5412
|
-
operation,
|
|
5413
|
-
keys,
|
|
5414
|
-
...isQuery ? { keyFields: keyFieldNames2 } : {},
|
|
5415
|
-
...select !== void 0 ? { select } : {},
|
|
5416
|
-
...compose.length > 0 ? { compose } : {},
|
|
5417
|
-
...d.description !== void 0 ? { description: d.description } : {}
|
|
5418
|
-
};
|
|
5419
|
-
}
|
|
5420
|
-
function writeDescriptorToPlan(name, d) {
|
|
5421
|
-
const present = WRITE_INTENT_KEYS.filter((k) => d[k] !== void 0);
|
|
5422
|
-
if (present.length !== 1) {
|
|
5423
|
-
throw new Error(
|
|
5424
|
-
`publicCommandModel: method '${name}' must carry exactly one intent key (\`create\` / \`update\` / \`remove\`), but declared ${present.length === 0 ? "none" : present.join(", ")}.`
|
|
5425
|
-
);
|
|
5426
|
-
}
|
|
5427
|
-
const intent = present[0];
|
|
5428
|
-
if (d.key === void 0 || d.key === null || typeof d.key !== "object") {
|
|
5429
|
-
throw new Error(
|
|
5430
|
-
`publicCommandModel: the '${intent}' descriptor for method '${name}' must declare a \`key\` object binding the target's primary-key fields.`
|
|
5431
|
-
);
|
|
5432
|
-
}
|
|
5433
|
-
const model = resolveDescriptorModel(d[intent], name);
|
|
5434
|
-
const keyFieldNames2 = Object.keys(d.key);
|
|
5435
|
-
const inputFieldNames = d.input !== void 0 ? Object.keys(d.input) : [];
|
|
5436
|
-
const plan2 = mutation(name, ($) => {
|
|
5437
|
-
const refMap = (fields) => {
|
|
5438
|
-
const out = {};
|
|
5439
|
-
for (const f of fields) out[f] = $[f];
|
|
5440
|
-
return out;
|
|
5441
|
-
};
|
|
5442
|
-
const descriptor = {
|
|
5443
|
-
[intent]: (() => model),
|
|
5444
|
-
key: refMap(keyFieldNames2),
|
|
5445
|
-
...inputFieldNames.length > 0 ? { input: refMap(inputFieldNames) } : {}
|
|
5446
|
-
};
|
|
5447
|
-
return { [name]: descriptor };
|
|
5448
|
-
});
|
|
5449
|
-
const select = d.result !== void 0 && d.result.select !== void 0 ? d.result.select : void 0;
|
|
5450
|
-
const condition = d.condition !== void 0 ? descriptorConditionRecord(d.condition, name) : void 0;
|
|
5451
|
-
return { plan: plan2, select, condition, mode: d.mode ?? "transaction", description: d.description };
|
|
5452
|
-
}
|
|
5453
|
-
|
|
5454
|
-
// src/spec/guard.ts
|
|
5455
|
-
function conditionInputToSpec(condition, context, renderLeaf2, renderTreeLeaf, renderRawSlot) {
|
|
5456
|
-
if (condition === void 0 || condition === null) return void 0;
|
|
5457
|
-
if (isRawCondition(condition)) {
|
|
5458
|
-
const { expression, names, values } = serializeRawCondition(
|
|
5459
|
-
condition,
|
|
5460
|
-
renderRawSlot
|
|
5461
|
-
);
|
|
5462
|
-
return { kind: "raw", expression, names, values };
|
|
5463
|
-
}
|
|
5464
|
-
if (typeof condition !== "object") {
|
|
5465
|
-
throw new Error(
|
|
5466
|
-
`${context}: write condition must be an object (\`{ notExists: true }\`, \`{ attributeExists: '<field>' }\`, \`{ attributeNotExists: '<field>' }\`, or a declarative field condition).`
|
|
5467
|
-
);
|
|
5468
|
-
}
|
|
5469
|
-
const obj = condition;
|
|
5470
|
-
if (obj.notExists === true) return { kind: "notExists" };
|
|
5471
|
-
if ("attributeExists" in obj && typeof obj.attributeExists === "string") {
|
|
5472
|
-
if (obj.attributeExists.length === 0) {
|
|
5473
|
-
throw new Error(
|
|
5474
|
-
`${context}: \`attributeExists\` must name a non-empty attribute field.`
|
|
5475
|
-
);
|
|
5476
|
-
}
|
|
5477
|
-
return { kind: "attributeExists", field: obj.attributeExists };
|
|
5478
|
-
}
|
|
5479
|
-
if ("attributeNotExists" in obj) {
|
|
5480
|
-
if (typeof obj.attributeNotExists !== "string" || obj.attributeNotExists.length === 0) {
|
|
5481
|
-
throw new Error(
|
|
5482
|
-
`${context}: \`attributeNotExists\` must name a non-empty attribute field.`
|
|
5483
|
-
);
|
|
5484
|
-
}
|
|
5485
|
-
return { kind: "attributeNotExists", field: obj.attributeNotExists };
|
|
5486
|
-
}
|
|
5487
|
-
if (usesOperatorTree(obj)) {
|
|
5488
|
-
const leaf = renderTreeLeaf ?? ((value, name) => defaultTreeLeaf(value, name, context));
|
|
5489
|
-
return { kind: "expr", declarative: buildTree(obj, context, leaf) };
|
|
5490
|
-
}
|
|
5491
|
-
const fields = {};
|
|
5492
|
-
for (const key of Object.keys(obj).sort()) {
|
|
5493
|
-
fields[key] = renderLeaf2(key, obj[key]);
|
|
5494
|
-
}
|
|
5495
|
-
return { kind: "equals", fields };
|
|
5496
|
-
}
|
|
5497
|
-
function assertNotNestedRaw(sub, context) {
|
|
5498
|
-
if (isRawCondition(sub)) {
|
|
5499
|
-
throw new Error(
|
|
5500
|
-
`${context}: a raw \`cond\` write condition may be used as a whole condition, but not nested inside an \`and\` / \`or\` / \`not\` group of a serializable (public-contract / Python-bridge) command. Move the \`cond\` to the top level, or express the group declaratively.`
|
|
5501
|
-
);
|
|
5502
|
-
}
|
|
5503
|
-
}
|
|
5504
|
-
var LOGICAL_KEYS2 = /* @__PURE__ */ new Set(["and", "or", "not"]);
|
|
5505
|
-
var OPERATOR_KEYS2 = /* @__PURE__ */ new Set([
|
|
5506
|
-
"eq",
|
|
5507
|
-
"ne",
|
|
5508
|
-
"gt",
|
|
5509
|
-
"ge",
|
|
5510
|
-
"lt",
|
|
5511
|
-
"le",
|
|
5512
|
-
"between",
|
|
5513
|
-
"in",
|
|
5514
|
-
"beginsWith",
|
|
5515
|
-
"contains",
|
|
5516
|
-
"notContains",
|
|
5517
|
-
"attributeExists",
|
|
5518
|
-
"attributeType",
|
|
5519
|
-
"size"
|
|
5520
|
-
]);
|
|
5521
|
-
function isOperatorObject2(value) {
|
|
5522
|
-
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date) {
|
|
5891
|
+
function isConditionOperatorObject4(value) {
|
|
5892
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date || isParam(value) || isContractParamRef(value) || isContractKeyFieldRef(value)) {
|
|
5523
5893
|
return false;
|
|
5524
5894
|
}
|
|
5525
5895
|
const keys = Object.keys(value);
|
|
5526
5896
|
if (keys.length === 0) return false;
|
|
5527
|
-
return keys.every((k) =>
|
|
5897
|
+
return keys.every((k) => CONDITION_OPERATOR_KEYS.has(k));
|
|
5528
5898
|
}
|
|
5529
|
-
function
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
if (isOperatorObject2(value)) return true;
|
|
5533
|
-
}
|
|
5534
|
-
return false;
|
|
5899
|
+
function mintConditionOperand(leaf, field, op, index) {
|
|
5900
|
+
if (isParam(leaf)) return mintContractParamRef(conditionParamName(field, op, index));
|
|
5901
|
+
return leaf;
|
|
5535
5902
|
}
|
|
5536
|
-
function
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
return
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
);
|
|
5903
|
+
function mintRawConditionParams(raw) {
|
|
5904
|
+
let paramIndex = 0;
|
|
5905
|
+
const parts = raw.parts.map((part) => {
|
|
5906
|
+
if (isColumn(part)) return part;
|
|
5907
|
+
if (isParam(part)) return mintContractParamRef(condParamName(paramIndex++));
|
|
5908
|
+
return part;
|
|
5909
|
+
});
|
|
5910
|
+
return { ...raw, parts };
|
|
5545
5911
|
}
|
|
5546
|
-
function
|
|
5547
|
-
if (
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5912
|
+
function descriptorConditionRecord(record, name) {
|
|
5913
|
+
if (isRawCondition(record)) {
|
|
5914
|
+
return mintRawConditionParams(record);
|
|
5915
|
+
}
|
|
5916
|
+
const top = record;
|
|
5917
|
+
if (top.notExists === true || typeof top.attributeExists === "string" || typeof top.attributeNotExists === "string") {
|
|
5918
|
+
return { ...top };
|
|
5919
|
+
}
|
|
5920
|
+
return descriptorConditionTree(top, name, `publicCommandModel: method '${name}' condition`);
|
|
5551
5921
|
}
|
|
5552
|
-
function
|
|
5922
|
+
function descriptorConditionTree(obj, name, context) {
|
|
5553
5923
|
const out = {};
|
|
5554
|
-
for (const key of Object.
|
|
5555
|
-
const value = obj[key];
|
|
5924
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
5556
5925
|
if (value === void 0) continue;
|
|
5557
5926
|
if (key === "and" || key === "or") {
|
|
5558
5927
|
out[key] = value.map((sub) => {
|
|
5559
|
-
|
|
5560
|
-
return
|
|
5928
|
+
assertNoNestedRawCond(sub, name);
|
|
5929
|
+
return descriptorConditionTree(sub, name, `${context} ${key}`);
|
|
5561
5930
|
});
|
|
5562
5931
|
continue;
|
|
5563
5932
|
}
|
|
5564
5933
|
if (key === "not") {
|
|
5565
|
-
|
|
5566
|
-
out[key] =
|
|
5567
|
-
value,
|
|
5568
|
-
context,
|
|
5569
|
-
renderTreeLeaf
|
|
5570
|
-
);
|
|
5934
|
+
assertNoNestedRawCond(value, name);
|
|
5935
|
+
out[key] = descriptorConditionTree(value, name, `${context} not`);
|
|
5571
5936
|
continue;
|
|
5572
5937
|
}
|
|
5573
|
-
if (!
|
|
5574
|
-
out[key] =
|
|
5938
|
+
if (!isConditionOperatorObject4(value)) {
|
|
5939
|
+
out[key] = mintConditionOperand(value, key, "eq");
|
|
5575
5940
|
continue;
|
|
5576
5941
|
}
|
|
5577
5942
|
const ops = value;
|
|
5578
5943
|
const rendered = {};
|
|
5579
|
-
for (const op of Object.
|
|
5580
|
-
const opVal = ops[op];
|
|
5944
|
+
for (const [op, opVal] of Object.entries(ops)) {
|
|
5581
5945
|
if (op === "between") {
|
|
5582
5946
|
const [lo, hi] = opVal;
|
|
5583
5947
|
rendered[op] = [
|
|
5584
|
-
|
|
5585
|
-
|
|
5948
|
+
mintConditionOperand(lo, key, op, 0),
|
|
5949
|
+
mintConditionOperand(hi, key, op, 1)
|
|
5586
5950
|
];
|
|
5587
5951
|
} else if (op === "in") {
|
|
5588
|
-
rendered[op] = opVal.map(
|
|
5589
|
-
|
|
5590
|
-
);
|
|
5591
|
-
} else if (op === "attributeExists") {
|
|
5952
|
+
rendered[op] = opVal.map((v, i) => mintConditionOperand(v, key, op, i));
|
|
5953
|
+
} else if (op === "attributeExists" || op === "attributeType") {
|
|
5592
5954
|
rendered[op] = opVal;
|
|
5593
|
-
} else
|
|
5594
|
-
rendered[op] =
|
|
5595
|
-
} else {
|
|
5596
|
-
rendered[op] = renderTreeLeaf(opVal, conditionParamName(key, op));
|
|
5597
|
-
}
|
|
5598
|
-
}
|
|
5599
|
-
out[key] = rendered;
|
|
5600
|
-
}
|
|
5601
|
-
return out;
|
|
5602
|
-
}
|
|
5603
|
-
function assertSupportedCondition(commandName, condition) {
|
|
5604
|
-
if (condition.kind === "notExists") return;
|
|
5605
|
-
if (condition.kind === "attributeExists" || condition.kind === "attributeNotExists") {
|
|
5606
|
-
if (typeof condition.field !== "string" || condition.field.length === 0) {
|
|
5607
|
-
throw new Error(
|
|
5608
|
-
`Command '${commandName}' has an ${condition.kind} write condition with no attribute field. The Python bridge requires a non-empty field name.`
|
|
5609
|
-
);
|
|
5610
|
-
}
|
|
5611
|
-
return;
|
|
5612
|
-
}
|
|
5613
|
-
if (condition.kind === "equals") {
|
|
5614
|
-
const entries = Object.entries(condition.fields);
|
|
5615
|
-
if (entries.length === 0) {
|
|
5616
|
-
throw new Error(
|
|
5617
|
-
`Command '${commandName}' has an empty equality write condition. The Python bridge supports '{ notExists }' or non-empty field equality conditions only.`
|
|
5618
|
-
);
|
|
5619
|
-
}
|
|
5620
|
-
for (const [field, value] of entries) {
|
|
5621
|
-
if (typeof value !== "string") {
|
|
5622
|
-
throw new Error(
|
|
5623
|
-
`Command '${commandName}' write condition on '${field}' must be a template string value; the Python bridge supports equality on param/literal values only.`
|
|
5624
|
-
);
|
|
5955
|
+
} else {
|
|
5956
|
+
rendered[op] = mintConditionOperand(opVal, key, op);
|
|
5625
5957
|
}
|
|
5626
5958
|
}
|
|
5627
|
-
|
|
5628
|
-
}
|
|
5629
|
-
if (condition.kind === "raw") {
|
|
5630
|
-
if (typeof condition.expression !== "string" || condition.expression.length === 0) {
|
|
5631
|
-
throw new Error(
|
|
5632
|
-
`Command '${commandName}' has an empty raw \`cond\` write condition.`
|
|
5633
|
-
);
|
|
5634
|
-
}
|
|
5635
|
-
return;
|
|
5959
|
+
out[key] = rendered;
|
|
5636
5960
|
}
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
return;
|
|
5961
|
+
return out;
|
|
5962
|
+
}
|
|
5963
|
+
function assertNoNestedRawCond(sub, name) {
|
|
5964
|
+
if (isRawCondition(sub)) {
|
|
5965
|
+
throw new Error(
|
|
5966
|
+
`publicCommandModel: method '${name}' condition nests a raw \`cond\` fragment inside an \`and\` / \`or\` / \`not\` group, which is not serializable. Use a \`cond\` as the whole condition, or express the group with declarative operators.`
|
|
5967
|
+
);
|
|
5645
5968
|
}
|
|
5646
|
-
throw new Error(
|
|
5647
|
-
`Command '${commandName}' has an unsupported write condition.`
|
|
5648
|
-
);
|
|
5649
5969
|
}
|
|
5650
|
-
function
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
if (!Number.isFinite(value)) {
|
|
5658
|
-
throw new Error(
|
|
5659
|
-
`Bridge spec is not JSON-serializable: non-finite number at ${path}.`
|
|
5660
|
-
);
|
|
5661
|
-
}
|
|
5662
|
-
return;
|
|
5663
|
-
case "object":
|
|
5664
|
-
break;
|
|
5665
|
-
default:
|
|
5666
|
-
throw new Error(
|
|
5667
|
-
`Bridge spec is not JSON-serializable: '${typeof value}' at ${path}.`
|
|
5668
|
-
);
|
|
5970
|
+
function readDescriptorToOp(name, d) {
|
|
5971
|
+
const isQuery = d.query !== void 0;
|
|
5972
|
+
const isList = d.list !== void 0;
|
|
5973
|
+
if (isQuery === isList) {
|
|
5974
|
+
throw new Error(
|
|
5975
|
+
`publicQueryModel: method '${name}' must carry exactly one of \`query\` or \`list\` (the target model); it declared ${isQuery && isList ? "both" : "neither"}.`
|
|
5976
|
+
);
|
|
5669
5977
|
}
|
|
5670
|
-
if (
|
|
5671
|
-
|
|
5672
|
-
|
|
5978
|
+
if (d.key === void 0 || d.select === void 0) {
|
|
5979
|
+
throw new Error(
|
|
5980
|
+
`publicQueryModel: the '${isQuery ? "query" : "list"}' descriptor for method '${name}' must declare both \`key\` and \`select\`.`
|
|
5981
|
+
);
|
|
5673
5982
|
}
|
|
5674
|
-
const
|
|
5675
|
-
|
|
5983
|
+
const model = resolveDescriptorModel(isQuery ? d.query : d.list, name);
|
|
5984
|
+
const entity = entityRef(model);
|
|
5985
|
+
const operation = isQuery ? "query" : "list";
|
|
5986
|
+
const keyFieldNames2 = Object.keys(d.key);
|
|
5987
|
+
const keyRecord = descriptorKeyRecord(d.key, name, `${operation} key`);
|
|
5988
|
+
const { select, compose } = extractCompose({ ...d.select }, operation, entity.name);
|
|
5989
|
+
const keys = isQuery ? wholeKeysSentinel() : keyRecord;
|
|
5990
|
+
return {
|
|
5991
|
+
__isContractMethodOp: true,
|
|
5992
|
+
entity,
|
|
5993
|
+
operation,
|
|
5994
|
+
keys,
|
|
5995
|
+
...isQuery ? { keyFields: keyFieldNames2 } : {},
|
|
5996
|
+
...select !== void 0 ? { select } : {},
|
|
5997
|
+
...compose.length > 0 ? { compose } : {},
|
|
5998
|
+
...d.description !== void 0 ? { description: d.description } : {}
|
|
5999
|
+
};
|
|
6000
|
+
}
|
|
6001
|
+
function writeDescriptorToPlan(name, d) {
|
|
6002
|
+
const present = WRITE_INTENT_KEYS.filter((k) => d[k] !== void 0);
|
|
6003
|
+
if (present.length !== 1) {
|
|
5676
6004
|
throw new Error(
|
|
5677
|
-
`
|
|
6005
|
+
`publicCommandModel: method '${name}' must carry exactly one intent key (\`create\` / \`update\` / \`remove\`), but declared ${present.length === 0 ? "none" : present.join(", ")}.`
|
|
5678
6006
|
);
|
|
5679
6007
|
}
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
}
|
|
5686
|
-
assertJsonSerializable(v, `${path}.${k}`);
|
|
6008
|
+
const intent = present[0];
|
|
6009
|
+
if (d.key === void 0 || d.key === null || typeof d.key !== "object") {
|
|
6010
|
+
throw new Error(
|
|
6011
|
+
`publicCommandModel: the '${intent}' descriptor for method '${name}' must declare a \`key\` object binding the target's primary-key fields.`
|
|
6012
|
+
);
|
|
5687
6013
|
}
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
6014
|
+
const model = resolveDescriptorModel(d[intent], name);
|
|
6015
|
+
const keyFieldNames2 = Object.keys(d.key);
|
|
6016
|
+
const inputFieldNames = d.input !== void 0 ? Object.keys(d.input) : [];
|
|
6017
|
+
const plan2 = mutation(name, ($) => {
|
|
6018
|
+
const refMap = (fields) => {
|
|
6019
|
+
const out = {};
|
|
6020
|
+
for (const f of fields) out[f] = $[f];
|
|
6021
|
+
return out;
|
|
6022
|
+
};
|
|
6023
|
+
const descriptor = {
|
|
6024
|
+
[intent]: (() => model),
|
|
6025
|
+
key: refMap(keyFieldNames2),
|
|
6026
|
+
...inputFieldNames.length > 0 ? { input: refMap(inputFieldNames) } : {}
|
|
6027
|
+
};
|
|
6028
|
+
return { [name]: descriptor };
|
|
6029
|
+
});
|
|
6030
|
+
const select = d.result !== void 0 && d.result.select !== void 0 ? d.result.select : void 0;
|
|
6031
|
+
const condition = d.condition !== void 0 ? descriptorConditionRecord(d.condition, name) : void 0;
|
|
6032
|
+
return { plan: plan2, select, condition, mode: d.mode ?? "transaction", description: d.description };
|
|
5692
6033
|
}
|
|
5693
6034
|
|
|
5694
6035
|
// src/define/transaction.ts
|
|
@@ -6060,7 +6401,7 @@ function verifyWrite(instrs, surfaced) {
|
|
|
6060
6401
|
verifyRecord(instrs.map((x) => x.item), surfaced, `${op} item`);
|
|
6061
6402
|
verifyRecord(instrs.map((x) => x.key), surfaced, `${op} key`);
|
|
6062
6403
|
verifyRecord(instrs.map((x) => x.changes), surfaced, `${op} changes`);
|
|
6063
|
-
|
|
6404
|
+
verifyCondition(
|
|
6064
6405
|
instrs.map((x) => x.condition),
|
|
6065
6406
|
surfaced,
|
|
6066
6407
|
`${op} condition`
|
|
@@ -6092,6 +6433,148 @@ function verifyRecord(records, surfaced, context) {
|
|
|
6092
6433
|
);
|
|
6093
6434
|
}
|
|
6094
6435
|
}
|
|
6436
|
+
function isConditionOperatorObject5(value) {
|
|
6437
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date || isTransactionRef(value)) {
|
|
6438
|
+
return false;
|
|
6439
|
+
}
|
|
6440
|
+
const keys = Object.keys(value);
|
|
6441
|
+
if (keys.length === 0) return false;
|
|
6442
|
+
return keys.every((k) => CONDITION_OPERATOR_KEYS.has(k));
|
|
6443
|
+
}
|
|
6444
|
+
function verifyCondition(conditions, surfaced, context) {
|
|
6445
|
+
const present = conditions.filter(
|
|
6446
|
+
(c) => c !== void 0 && c !== null
|
|
6447
|
+
);
|
|
6448
|
+
if (present.length === 0) return;
|
|
6449
|
+
if (present.length !== conditions.length) {
|
|
6450
|
+
throw new Error(
|
|
6451
|
+
`defineTransaction: ${context} present in some evaluation passes but not others; the callback must be a pure, declarative description.`
|
|
6452
|
+
);
|
|
6453
|
+
}
|
|
6454
|
+
if (present.some((c) => isRawCondition(c))) return;
|
|
6455
|
+
if (present.some((c) => typeof c !== "object" || Array.isArray(c))) {
|
|
6456
|
+
throw new Error(
|
|
6457
|
+
`defineTransaction: ${context} must be a declarative condition object (field clause / operator object / \`and\` / \`or\` / \`not\` group).`
|
|
6458
|
+
);
|
|
6459
|
+
}
|
|
6460
|
+
verifyConditionTree(
|
|
6461
|
+
present.map((c) => c),
|
|
6462
|
+
surfaced,
|
|
6463
|
+
context
|
|
6464
|
+
);
|
|
6465
|
+
}
|
|
6466
|
+
function verifyConditionTree(nodes, surfaced, context) {
|
|
6467
|
+
const keys = Object.keys(nodes[0]).sort();
|
|
6468
|
+
for (const n of nodes) {
|
|
6469
|
+
const k = Object.keys(n).sort();
|
|
6470
|
+
if (k.length !== keys.length || k.some((x, i) => x !== keys[i])) {
|
|
6471
|
+
throw new Error(
|
|
6472
|
+
`defineTransaction: ${context} key set diverged between evaluation passes; the callback must be a pure, declarative description.`
|
|
6473
|
+
);
|
|
6474
|
+
}
|
|
6475
|
+
}
|
|
6476
|
+
for (const key of keys) {
|
|
6477
|
+
const values = nodes.map((n) => n[key]);
|
|
6478
|
+
if (key === "and" || key === "or") {
|
|
6479
|
+
const arrays = values.map((v) => {
|
|
6480
|
+
if (!Array.isArray(v) || v.length === 0) {
|
|
6481
|
+
throw new Error(
|
|
6482
|
+
`defineTransaction: ${context} \`${key}\` must be a non-empty array of sub-conditions.`
|
|
6483
|
+
);
|
|
6484
|
+
}
|
|
6485
|
+
return v;
|
|
6486
|
+
});
|
|
6487
|
+
const len = arrays[0].length;
|
|
6488
|
+
if (arrays.some((a) => a.length !== len)) {
|
|
6489
|
+
throw new Error(
|
|
6490
|
+
`defineTransaction: ${context} \`${key}\` length diverged between evaluation passes; the callback must be declarative.`
|
|
6491
|
+
);
|
|
6492
|
+
}
|
|
6493
|
+
for (let i = 0; i < len; i++) {
|
|
6494
|
+
verifyCondition(
|
|
6495
|
+
arrays.map((a) => a[i]),
|
|
6496
|
+
surfaced,
|
|
6497
|
+
`${context} ${key}[${i}]`
|
|
6498
|
+
);
|
|
6499
|
+
}
|
|
6500
|
+
continue;
|
|
6501
|
+
}
|
|
6502
|
+
if (key === "not") {
|
|
6503
|
+
verifyCondition(
|
|
6504
|
+
values,
|
|
6505
|
+
surfaced,
|
|
6506
|
+
`${context} not`
|
|
6507
|
+
);
|
|
6508
|
+
continue;
|
|
6509
|
+
}
|
|
6510
|
+
if (!values.every((v) => isConditionOperatorObject5(v))) {
|
|
6511
|
+
if (values.some((v) => isConditionOperatorObject5(v))) {
|
|
6512
|
+
throw new Error(
|
|
6513
|
+
`defineTransaction: ${context} field '${key}' is an operator object in some evaluation passes but a bare value in others; the callback must be declarative.`
|
|
6514
|
+
);
|
|
6515
|
+
}
|
|
6516
|
+
verifyLeaf(values, surfaced, `${context} field '${key}'`);
|
|
6517
|
+
continue;
|
|
6518
|
+
}
|
|
6519
|
+
verifyOperatorObject(
|
|
6520
|
+
values.map((v) => v),
|
|
6521
|
+
surfaced,
|
|
6522
|
+
`${context} field '${key}'`
|
|
6523
|
+
);
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
function verifyOperatorObject(objs, surfaced, context) {
|
|
6527
|
+
const ops = Object.keys(objs[0]).sort();
|
|
6528
|
+
for (const o of objs) {
|
|
6529
|
+
const k = Object.keys(o).sort();
|
|
6530
|
+
if (k.length !== ops.length || k.some((x, i) => x !== ops[i])) {
|
|
6531
|
+
throw new Error(
|
|
6532
|
+
`defineTransaction: ${context} operator set diverged between evaluation passes; the callback must be a pure, declarative description.`
|
|
6533
|
+
);
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
for (const op of ops) {
|
|
6537
|
+
const opVals = objs.map((o) => o[op]);
|
|
6538
|
+
if (op === "between") {
|
|
6539
|
+
const pairs = opVals.map((v) => {
|
|
6540
|
+
if (!Array.isArray(v) || v.length !== 2) {
|
|
6541
|
+
throw new Error(
|
|
6542
|
+
`defineTransaction: ${context} \`between\` takes a \`[lo, hi]\` pair.`
|
|
6543
|
+
);
|
|
6544
|
+
}
|
|
6545
|
+
return v;
|
|
6546
|
+
});
|
|
6547
|
+
verifyLeaf(pairs.map((p) => p[0]), surfaced, `${context} between[0]`);
|
|
6548
|
+
verifyLeaf(pairs.map((p) => p[1]), surfaced, `${context} between[1]`);
|
|
6549
|
+
} else if (op === "in") {
|
|
6550
|
+
const arrays = opVals.map((v) => {
|
|
6551
|
+
if (!Array.isArray(v) || v.length === 0) {
|
|
6552
|
+
throw new Error(
|
|
6553
|
+
`defineTransaction: ${context} \`in\` takes a non-empty array of operands.`
|
|
6554
|
+
);
|
|
6555
|
+
}
|
|
6556
|
+
return v;
|
|
6557
|
+
});
|
|
6558
|
+
const len = arrays[0].length;
|
|
6559
|
+
if (arrays.some((a) => a.length !== len)) {
|
|
6560
|
+
throw new Error(
|
|
6561
|
+
`defineTransaction: ${context} \`in\` length diverged between evaluation passes; the callback must be declarative.`
|
|
6562
|
+
);
|
|
6563
|
+
}
|
|
6564
|
+
for (let i = 0; i < len; i++) {
|
|
6565
|
+
verifyLeaf(arrays.map((a) => a[i]), surfaced, `${context} in[${i}]`);
|
|
6566
|
+
}
|
|
6567
|
+
} else if (op === "attributeExists" || op === "attributeType") {
|
|
6568
|
+
if (opVals.some((v) => isTransactionRef(v))) {
|
|
6569
|
+
throw new Error(
|
|
6570
|
+
`defineTransaction: ${context} \`${op}\` was given a field reference. \`${op}\` takes a ${op === "attributeExists" ? "concrete boolean" : "concrete DynamoDB type-code string"} (a structural operand, not a value-bound param); use a literal.`
|
|
6571
|
+
);
|
|
6572
|
+
}
|
|
6573
|
+
} else {
|
|
6574
|
+
verifyLeaf(opVals, surfaced, `${context} ${op}`);
|
|
6575
|
+
}
|
|
6576
|
+
}
|
|
6577
|
+
}
|
|
6095
6578
|
function verifyWhen(whens, surfaced, context) {
|
|
6096
6579
|
const present = whens.filter((w) => w !== void 0);
|
|
6097
6580
|
if (present.length === 0) return;
|
|
@@ -6204,10 +6687,19 @@ function conditionTreeLeaf(value, name, context) {
|
|
|
6204
6687
|
`defineTransaction: ${context} condition operand must be a field reference (from \`p\` or a forEach element) or a concrete literal; got ${typeof value}.`
|
|
6205
6688
|
);
|
|
6206
6689
|
}
|
|
6207
|
-
function
|
|
6690
|
+
function valueLeaf(value, context) {
|
|
6691
|
+
if (isTransactionRef(value)) return value.token;
|
|
6692
|
+
if (value instanceof Date) return value.toISOString();
|
|
6693
|
+
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
6694
|
+
if (typeof value === "string") return value;
|
|
6695
|
+
throw new Error(
|
|
6696
|
+
`defineTransaction: ${context} must be a field reference (from \`p\` or a forEach element) or a concrete literal; got ${typeof value}.`
|
|
6697
|
+
);
|
|
6698
|
+
}
|
|
6699
|
+
function valueRecord(structure, context) {
|
|
6208
6700
|
const out = {};
|
|
6209
6701
|
for (const field of Object.keys(structure).sort()) {
|
|
6210
|
-
out[field] =
|
|
6702
|
+
out[field] = valueLeaf(structure[field], `${context} field '${field}'`);
|
|
6211
6703
|
}
|
|
6212
6704
|
return out;
|
|
6213
6705
|
}
|
|
@@ -6280,7 +6772,7 @@ function buildWriteItem(instr, forEachSource, txName) {
|
|
|
6280
6772
|
if (instr.operation === "put") {
|
|
6281
6773
|
return {
|
|
6282
6774
|
...base,
|
|
6283
|
-
item:
|
|
6775
|
+
item: valueRecord(instr.item ?? {}, `${txName} put`)
|
|
6284
6776
|
};
|
|
6285
6777
|
}
|
|
6286
6778
|
if (instr.operation === "delete") {
|
|
@@ -6304,7 +6796,7 @@ function buildWriteItem(instr, forEachSource, txName) {
|
|
|
6304
6796
|
)
|
|
6305
6797
|
};
|
|
6306
6798
|
}
|
|
6307
|
-
const changes =
|
|
6799
|
+
const changes = valueRecord(instr.changes ?? {}, `${txName} update changes`);
|
|
6308
6800
|
injectTransactionGsiRederive(
|
|
6309
6801
|
metadata,
|
|
6310
6802
|
instr.key ?? {},
|
|
@@ -6493,8 +6985,8 @@ function recoverKind(metadata, fieldName) {
|
|
|
6493
6985
|
function opToDefinition(contractName, methodName, op) {
|
|
6494
6986
|
const metadata = MetadataRegistry.get(op.entity.modelClass);
|
|
6495
6987
|
const params = {};
|
|
6496
|
-
const place = (paramName, bindField) => {
|
|
6497
|
-
const recovered = recoverKind(metadata, bindField);
|
|
6988
|
+
const place = (paramName, bindField, kindOverride) => {
|
|
6989
|
+
const recovered = kindOverride !== void 0 ? { kind: kindOverride } : recoverKind(metadata, bindField);
|
|
6498
6990
|
params[paramName] = recovered.literals === void 0 ? { kind: recovered.kind, required: true } : { kind: recovered.kind, literals: recovered.literals, required: true };
|
|
6499
6991
|
return recovered;
|
|
6500
6992
|
};
|
|
@@ -6568,13 +7060,13 @@ function convertStructure(structure, place, context) {
|
|
|
6568
7060
|
}
|
|
6569
7061
|
return out;
|
|
6570
7062
|
}
|
|
6571
|
-
function convertLeaf(leaf, bindField, place, context) {
|
|
7063
|
+
function convertLeaf(leaf, bindField, place, context, kindOverride) {
|
|
6572
7064
|
if (isContractParamRef(leaf)) {
|
|
6573
|
-
place(leaf.field, bindField);
|
|
7065
|
+
place(leaf.field, bindField, kindOverride);
|
|
6574
7066
|
return leaf;
|
|
6575
7067
|
}
|
|
6576
7068
|
if (isContractKeyFieldRef(leaf)) {
|
|
6577
|
-
place(leaf.field, bindField);
|
|
7069
|
+
place(leaf.field, bindField, kindOverride);
|
|
6578
7070
|
return leaf;
|
|
6579
7071
|
}
|
|
6580
7072
|
if (isConcreteLiteral3(leaf)) return leaf;
|
|
@@ -6582,24 +7074,118 @@ function convertLeaf(leaf, bindField, place, context) {
|
|
|
6582
7074
|
`${context}: value is neither a param/key reference nor a concrete literal \u2014 it cannot be serialized as a declarative contract operation.`
|
|
6583
7075
|
);
|
|
6584
7076
|
}
|
|
7077
|
+
function nearestColumnFields(condition) {
|
|
7078
|
+
const parts = condition.parts;
|
|
7079
|
+
const columnAt = (i) => isColumn(parts[i]) ? parts[i].name : void 0;
|
|
7080
|
+
const out = [];
|
|
7081
|
+
for (let i = 0; i < parts.length; i++) {
|
|
7082
|
+
const part = parts[i];
|
|
7083
|
+
if (!isContractParamRef(part) && !isContractKeyFieldRef(part)) continue;
|
|
7084
|
+
let found;
|
|
7085
|
+
for (let d = 1; d < parts.length && found === void 0; d++) {
|
|
7086
|
+
found = columnAt(i - d) ?? columnAt(i + d);
|
|
7087
|
+
}
|
|
7088
|
+
out.push(found);
|
|
7089
|
+
}
|
|
7090
|
+
return out;
|
|
7091
|
+
}
|
|
6585
7092
|
function convertCondition(condition, place, context) {
|
|
6586
|
-
if (isRawCondition(condition))
|
|
7093
|
+
if (isRawCondition(condition)) {
|
|
7094
|
+
const compareFieldForSlot = nearestColumnFields(condition);
|
|
7095
|
+
let slotIndex = 0;
|
|
7096
|
+
for (const part of condition.parts) {
|
|
7097
|
+
if (isContractParamRef(part) || isContractKeyFieldRef(part)) {
|
|
7098
|
+
const compareField = compareFieldForSlot[slotIndex];
|
|
7099
|
+
if (compareField === void 0) {
|
|
7100
|
+
throw new Error(
|
|
7101
|
+
`${context}: a raw \`cond\` value slot ('${part.field}') has no adjacent column, so its param type cannot be inferred. A \`cond\` param slot must be compared against a \`Model.col.<field>\` reference (e.g. \`cond\`\${Model.col.x} < \${param.number()}\`\`) so the parameter inherits that column's type; refusing to emit an un-typed (silently 'string') param.`
|
|
7102
|
+
);
|
|
7103
|
+
}
|
|
7104
|
+
place(part.field, compareField);
|
|
7105
|
+
slotIndex++;
|
|
7106
|
+
}
|
|
7107
|
+
}
|
|
7108
|
+
return condition;
|
|
7109
|
+
}
|
|
6587
7110
|
if (typeof condition === "object" && condition !== null) {
|
|
6588
|
-
const
|
|
6589
|
-
if (
|
|
6590
|
-
if (typeof
|
|
6591
|
-
return { attributeExists:
|
|
7111
|
+
const obj2 = condition;
|
|
7112
|
+
if (obj2.notExists === true) return { notExists: true };
|
|
7113
|
+
if (typeof obj2.attributeExists === "string") {
|
|
7114
|
+
return { attributeExists: obj2.attributeExists };
|
|
6592
7115
|
}
|
|
6593
|
-
if (typeof
|
|
6594
|
-
return { attributeNotExists:
|
|
7116
|
+
if (typeof obj2.attributeNotExists === "string") {
|
|
7117
|
+
return { attributeNotExists: obj2.attributeNotExists };
|
|
6595
7118
|
}
|
|
6596
7119
|
}
|
|
7120
|
+
const obj = condition;
|
|
7121
|
+
if (usesConditionTree(obj)) {
|
|
7122
|
+
return convertConditionTree(obj, place, context);
|
|
7123
|
+
}
|
|
6597
7124
|
const out = {};
|
|
6598
|
-
for (const [field, leaf] of Object.entries(
|
|
7125
|
+
for (const [field, leaf] of Object.entries(obj)) {
|
|
6599
7126
|
out[field] = convertLeaf(leaf, field, place, `${context} field '${field}'`);
|
|
6600
7127
|
}
|
|
6601
7128
|
return out;
|
|
6602
7129
|
}
|
|
7130
|
+
var CONDITION_LOGICAL_KEYS = /* @__PURE__ */ new Set(["and", "or", "not"]);
|
|
7131
|
+
function isConditionOperatorObject6(value) {
|
|
7132
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date || isContractParamRef(value) || isContractKeyFieldRef(value)) {
|
|
7133
|
+
return false;
|
|
7134
|
+
}
|
|
7135
|
+
const keys = Object.keys(value);
|
|
7136
|
+
if (keys.length === 0) return false;
|
|
7137
|
+
return keys.every((k) => CONDITION_OPERATOR_KEYS.has(k));
|
|
7138
|
+
}
|
|
7139
|
+
function usesConditionTree(obj) {
|
|
7140
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
7141
|
+
if (CONDITION_LOGICAL_KEYS.has(key)) return true;
|
|
7142
|
+
if (isConditionOperatorObject6(value)) return true;
|
|
7143
|
+
}
|
|
7144
|
+
return false;
|
|
7145
|
+
}
|
|
7146
|
+
function convertConditionTree(obj, place, context) {
|
|
7147
|
+
const out = {};
|
|
7148
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
7149
|
+
if (value === void 0) continue;
|
|
7150
|
+
if (key === "and" || key === "or") {
|
|
7151
|
+
out[key] = value.map(
|
|
7152
|
+
(sub) => convertConditionTree(sub, place, `${context} ${key}`)
|
|
7153
|
+
);
|
|
7154
|
+
continue;
|
|
7155
|
+
}
|
|
7156
|
+
if (key === "not") {
|
|
7157
|
+
out[key] = convertConditionTree(value, place, `${context} not`);
|
|
7158
|
+
continue;
|
|
7159
|
+
}
|
|
7160
|
+
if (!isConditionOperatorObject6(value)) {
|
|
7161
|
+
out[key] = convertLeaf(value, key, place, `${context} field '${key}'`);
|
|
7162
|
+
continue;
|
|
7163
|
+
}
|
|
7164
|
+
const ops = value;
|
|
7165
|
+
const rendered = {};
|
|
7166
|
+
for (const [op, opVal] of Object.entries(ops)) {
|
|
7167
|
+
if (op === "between") {
|
|
7168
|
+
const [lo, hi] = opVal;
|
|
7169
|
+
rendered[op] = [
|
|
7170
|
+
convertLeaf(lo, key, place, `${context} field '${key}' between[0]`),
|
|
7171
|
+
convertLeaf(hi, key, place, `${context} field '${key}' between[1]`)
|
|
7172
|
+
];
|
|
7173
|
+
} else if (op === "in") {
|
|
7174
|
+
rendered[op] = opVal.map(
|
|
7175
|
+
(v, i) => convertLeaf(v, key, place, `${context} field '${key}' in[${i}]`)
|
|
7176
|
+
);
|
|
7177
|
+
} else if (op === "attributeExists" || op === "attributeType") {
|
|
7178
|
+
rendered[op] = opVal;
|
|
7179
|
+
} else if (op === "size") {
|
|
7180
|
+
rendered[op] = convertLeaf(opVal, key, place, `${context} field '${key}' ${op}`, "number");
|
|
7181
|
+
} else {
|
|
7182
|
+
rendered[op] = convertLeaf(opVal, key, place, `${context} field '${key}' ${op}`);
|
|
7183
|
+
}
|
|
7184
|
+
}
|
|
7185
|
+
out[key] = rendered;
|
|
7186
|
+
}
|
|
7187
|
+
return out;
|
|
7188
|
+
}
|
|
6603
7189
|
function assertBooleanProjection(select, context) {
|
|
6604
7190
|
const out = {};
|
|
6605
7191
|
for (const [field, leaf] of Object.entries(select)) {
|
|
@@ -6910,6 +7496,7 @@ var TEMPLATE_TOKEN_RE = /\{([^{}]+)\}/g;
|
|
|
6910
7496
|
function collectTemplateParams(record, metadata, out) {
|
|
6911
7497
|
if (record === void 0) return;
|
|
6912
7498
|
for (const template of Object.values(record)) {
|
|
7499
|
+
if (typeof template !== "string") continue;
|
|
6913
7500
|
for (const match of template.matchAll(TEMPLATE_TOKEN_RE)) {
|
|
6914
7501
|
const token = match[1];
|
|
6915
7502
|
const field = token.startsWith("old.") ? token.slice("old.".length) : token;
|
|
@@ -7788,7 +8375,7 @@ function buildCommandSpec(def) {
|
|
|
7788
8375
|
const condition = extractCondition(def);
|
|
7789
8376
|
const description = def.description !== void 0 ? { description: def.description } : {};
|
|
7790
8377
|
if (def.operation === "put") {
|
|
7791
|
-
const item =
|
|
8378
|
+
const item = templateRecord2(def.key);
|
|
7792
8379
|
return {
|
|
7793
8380
|
type: "PutItem",
|
|
7794
8381
|
tableName,
|
|
@@ -7816,7 +8403,7 @@ function buildCommandSpec(def) {
|
|
|
7816
8403
|
entity,
|
|
7817
8404
|
params,
|
|
7818
8405
|
keyCondition: writeKeyCondition(metadata, def.key, entity),
|
|
7819
|
-
changes:
|
|
8406
|
+
changes: templateRecord2(def.changes),
|
|
7820
8407
|
...condition ? { condition } : {},
|
|
7821
8408
|
...description
|
|
7822
8409
|
};
|
|
@@ -7840,7 +8427,7 @@ function writeKeyCondition(metadata, key, entity) {
|
|
|
7840
8427
|
if (sk !== void 0) out.SK = sk;
|
|
7841
8428
|
return out;
|
|
7842
8429
|
}
|
|
7843
|
-
function
|
|
8430
|
+
function templateRecord2(structure) {
|
|
7844
8431
|
const out = {};
|
|
7845
8432
|
for (const key of Object.keys(structure).sort()) {
|
|
7846
8433
|
out[key] = templateLeaf(key, structure[key]);
|
|
@@ -8171,6 +8758,10 @@ export {
|
|
|
8171
8758
|
MARKER_ROW_ENTITY,
|
|
8172
8759
|
buildManifestEntity,
|
|
8173
8760
|
buildManifest,
|
|
8761
|
+
conditionParamName,
|
|
8762
|
+
assertSupportedCondition,
|
|
8763
|
+
assertJsonSerializable,
|
|
8764
|
+
assertBundleSerializable,
|
|
8174
8765
|
buildProjection,
|
|
8175
8766
|
compileFilterExpression,
|
|
8176
8767
|
evaluateFilter,
|
|
@@ -8238,10 +8829,6 @@ export {
|
|
|
8238
8829
|
publicQueryModel,
|
|
8239
8830
|
publicCommandModel,
|
|
8240
8831
|
isPlannedCommandMethod,
|
|
8241
|
-
conditionParamName,
|
|
8242
|
-
assertSupportedCondition,
|
|
8243
|
-
assertJsonSerializable,
|
|
8244
|
-
assertBundleSerializable,
|
|
8245
8832
|
isTransactionRef,
|
|
8246
8833
|
when,
|
|
8247
8834
|
defineTransaction,
|