graphddb 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdc/index.d.ts +2 -2
- package/dist/{chunk-GFGVDF4W.js → chunk-H34BNRDZ.js} +631 -43
- package/dist/cli.js +186 -11
- package/dist/{from-change-pnURY-cV.d.ts → from-change-CWiXBcgi.d.ts} +1 -1
- package/dist/{index-Eg94ChE1.d.ts → index-CvS9ATKB.d.ts} +289 -104
- package/dist/index.d.ts +85 -7
- package/dist/index.js +253 -1
- package/dist/linter/index.d.ts +4 -4
- package/dist/{maintenance-view-adapter-BP2CJDdz.d.ts → maintenance-view-adapter-NBTZbE8-.d.ts} +57 -4
- package/dist/{registry-Cv9nl_3i.d.ts → registry-BndKbZbg.d.ts} +1 -1
- package/dist/{relation-depth-BR0y7Q1i.d.ts → relation-depth-CCGHLTOv.d.ts} +1 -1
- package/dist/spec/index.d.ts +3 -3
- package/dist/spec/index.js +13 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/transform/index.d.ts +51 -2
- package/dist/transform/index.js +264 -1
- package/docs/cqrs-contract.md +1 -1
- package/docs/design-patterns.md +9 -7
- package/docs/prepared-statements.md +70 -3
- package/docs/python-bridge.md +24 -4
- package/package.json +1 -1
|
@@ -67,7 +67,7 @@ function evaluateKey(segmented, scope, presentFields, nameOf = (f) => f, partial
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// src/spec/types.ts
|
|
70
|
-
var SPEC_VERSION = "1.
|
|
70
|
+
var SPEC_VERSION = "1.1";
|
|
71
71
|
var MARKER_ROW_ENTITY = "__marker__";
|
|
72
72
|
|
|
73
73
|
// src/spec/manifest.ts
|
|
@@ -144,6 +144,9 @@ function buildFields(metadata) {
|
|
|
144
144
|
}
|
|
145
145
|
return sortedRecord(fields);
|
|
146
146
|
}
|
|
147
|
+
function buildManifestEntity(metadata) {
|
|
148
|
+
return buildEntity(metadata);
|
|
149
|
+
}
|
|
147
150
|
function buildEntity(metadata) {
|
|
148
151
|
return {
|
|
149
152
|
table: metadata.tableName,
|
|
@@ -2939,6 +2942,30 @@ function renderTemplateRecord(record, key, params, contextLabel) {
|
|
|
2939
2942
|
}
|
|
2940
2943
|
return out;
|
|
2941
2944
|
}
|
|
2945
|
+
var EFFECT_FIELD_MAPS = /* @__PURE__ */ new WeakMap();
|
|
2946
|
+
function effectFieldMap(modelClass) {
|
|
2947
|
+
let map = EFFECT_FIELD_MAPS.get(modelClass);
|
|
2948
|
+
if (map === void 0) {
|
|
2949
|
+
map = new Map(MetadataRegistry.get(modelClass).fields.map((f) => [f.propertyName, f]));
|
|
2950
|
+
EFFECT_FIELD_MAPS.set(modelClass, map);
|
|
2951
|
+
}
|
|
2952
|
+
return map;
|
|
2953
|
+
}
|
|
2954
|
+
function serializeEffectParams(modelClasses, values) {
|
|
2955
|
+
const maps = modelClasses.map(effectFieldMap);
|
|
2956
|
+
const oldPrefix = `${OLD_VALUE_NAMESPACE}.`;
|
|
2957
|
+
const out = {};
|
|
2958
|
+
for (const [name, value] of Object.entries(values)) {
|
|
2959
|
+
const fieldName = name.startsWith(oldPrefix) ? name.slice(oldPrefix.length) : name;
|
|
2960
|
+
let fieldMeta;
|
|
2961
|
+
for (const map of maps) {
|
|
2962
|
+
fieldMeta = map.get(fieldName);
|
|
2963
|
+
if (fieldMeta !== void 0) break;
|
|
2964
|
+
}
|
|
2965
|
+
out[name] = fieldMeta !== void 0 ? serializeFieldValue(value, fieldMeta) : value instanceof Date ? value.toISOString() : value;
|
|
2966
|
+
}
|
|
2967
|
+
return out;
|
|
2968
|
+
}
|
|
2942
2969
|
function renderEdgeWriteItems(edge, key, params, contextLabel, modelBySignature) {
|
|
2943
2970
|
const adjacency = edge.entity.modelClass;
|
|
2944
2971
|
return edge.items.map((item) => {
|
|
@@ -3284,11 +3311,14 @@ async function executeCommandMethod(contract, methodName, keyOrKeys, params = {}
|
|
|
3284
3311
|
);
|
|
3285
3312
|
if (hasDerivedEffects) {
|
|
3286
3313
|
const modelBySignature = /* @__PURE__ */ new Map();
|
|
3314
|
+
const fragmentClasses = ops.map((opn) => modelClassOf(opn));
|
|
3315
|
+
const effectKey = serializeEffectParams(fragmentClasses, key);
|
|
3316
|
+
const effectParams = serializeEffectParams(fragmentClasses, params);
|
|
3287
3317
|
const edgeItems = edgeWrites2.flatMap(
|
|
3288
3318
|
(edge, i) => renderEdgeWriteItems(
|
|
3289
3319
|
edge,
|
|
3290
|
-
|
|
3291
|
-
|
|
3320
|
+
effectKey,
|
|
3321
|
+
effectParams,
|
|
3292
3322
|
`command method '${methodName}' (edge #${i})`,
|
|
3293
3323
|
modelBySignature
|
|
3294
3324
|
)
|
|
@@ -3296,41 +3326,41 @@ async function executeCommandMethod(contract, methodName, keyOrKeys, params = {}
|
|
|
3296
3326
|
const updateItems = derivedUpdates.map(
|
|
3297
3327
|
(update, i) => renderDerivedUpdate(
|
|
3298
3328
|
update,
|
|
3299
|
-
|
|
3300
|
-
|
|
3329
|
+
effectKey,
|
|
3330
|
+
effectParams,
|
|
3301
3331
|
`command method '${methodName}' (derive #${i})`,
|
|
3302
3332
|
modelBySignature
|
|
3303
3333
|
)
|
|
3304
3334
|
);
|
|
3305
3335
|
const guardItems = uniqueGuards.flatMap(
|
|
3306
|
-
(guard, i) => renderUniqueGuardItems(guard,
|
|
3336
|
+
(guard, i) => renderUniqueGuardItems(guard, effectKey, effectParams, `command method '${methodName}' (unique #${i})`)
|
|
3307
3337
|
);
|
|
3308
3338
|
const outboxItems = outboxEvents.flatMap(
|
|
3309
|
-
(event, i) => renderOutboxEventItems(event,
|
|
3339
|
+
(event, i) => renderOutboxEventItems(event, effectKey, effectParams, `command method '${methodName}' (emit #${i})`)
|
|
3310
3340
|
);
|
|
3311
3341
|
const idempotencyItems = idempotencyGuard !== void 0 ? renderIdempotencyGuardItems(
|
|
3312
3342
|
idempotencyGuard,
|
|
3313
|
-
|
|
3314
|
-
|
|
3343
|
+
effectKey,
|
|
3344
|
+
effectParams,
|
|
3315
3345
|
`command method '${methodName}' (idempotency)`
|
|
3316
3346
|
) : [];
|
|
3317
3347
|
const maintainItems = maintainWrites.map(
|
|
3318
3348
|
(m, i) => renderMaintainWriteItem(
|
|
3319
3349
|
m,
|
|
3320
|
-
|
|
3321
|
-
|
|
3350
|
+
effectKey,
|
|
3351
|
+
effectParams,
|
|
3322
3352
|
`command method '${methodName}' (maintain #${i})`,
|
|
3323
3353
|
modelBySignature
|
|
3324
3354
|
)
|
|
3325
3355
|
);
|
|
3326
3356
|
const maintainOutboxItems = maintainOutbox.flatMap(
|
|
3327
|
-
(mo, i) => renderMaintainOutboxItems(mo,
|
|
3357
|
+
(mo, i) => renderMaintainOutboxItems(mo, effectKey, effectParams, `command method '${methodName}' (maintain-outbox #${i})`)
|
|
3328
3358
|
);
|
|
3329
3359
|
const checks = conditionChecks.map((check, i) => ({
|
|
3330
3360
|
check: renderConditionCheck(
|
|
3331
3361
|
check,
|
|
3332
|
-
|
|
3333
|
-
|
|
3362
|
+
effectKey,
|
|
3363
|
+
effectParams,
|
|
3334
3364
|
`command method '${methodName}' (requires #${i})`
|
|
3335
3365
|
)
|
|
3336
3366
|
}));
|
|
@@ -3366,6 +3396,7 @@ function renderCompiledOp(op, ctx) {
|
|
|
3366
3396
|
const baseOp = op.condition !== void 0 ? withCondition(fragment.op, op.condition) : fragment.op;
|
|
3367
3397
|
const modelBySignature = /* @__PURE__ */ new Map();
|
|
3368
3398
|
const write = renderWrite(baseOp, op.params, op.params, ctx);
|
|
3399
|
+
const effectParams = serializeEffectParams([modelClassOf(fragment.op)], op.params);
|
|
3369
3400
|
const edgeItems = [];
|
|
3370
3401
|
const updateItems = [];
|
|
3371
3402
|
const guardItems = [];
|
|
@@ -3375,34 +3406,34 @@ function renderCompiledOp(op, ctx) {
|
|
|
3375
3406
|
const maintainOutboxItems = [];
|
|
3376
3407
|
const checkItems = [];
|
|
3377
3408
|
for (const edge of fragment.edgeWrites ?? []) {
|
|
3378
|
-
edgeItems.push(...renderEdgeWriteItems(edge,
|
|
3409
|
+
edgeItems.push(...renderEdgeWriteItems(edge, effectParams, effectParams, `${ctx} edge`, modelBySignature));
|
|
3379
3410
|
}
|
|
3380
3411
|
for (const u of fragment.derivedUpdates ?? []) {
|
|
3381
|
-
updateItems.push(renderDerivedUpdate(u,
|
|
3412
|
+
updateItems.push(renderDerivedUpdate(u, effectParams, effectParams, `${ctx} derive`, modelBySignature));
|
|
3382
3413
|
}
|
|
3383
3414
|
for (const g of fragment.uniqueGuards ?? []) {
|
|
3384
|
-
guardItems.push(...renderUniqueGuardItems(g,
|
|
3415
|
+
guardItems.push(...renderUniqueGuardItems(g, effectParams, effectParams, `${ctx} unique`));
|
|
3385
3416
|
}
|
|
3386
3417
|
for (const e of fragment.outboxEvents ?? []) {
|
|
3387
|
-
outboxItems.push(...renderOutboxEventItems(e,
|
|
3418
|
+
outboxItems.push(...renderOutboxEventItems(e, effectParams, effectParams, `${ctx} emit`));
|
|
3388
3419
|
}
|
|
3389
3420
|
if (fragment.idempotencyGuard !== void 0) {
|
|
3390
3421
|
idempotencyItems.push(
|
|
3391
|
-
...renderIdempotencyGuardItems(fragment.idempotencyGuard,
|
|
3422
|
+
...renderIdempotencyGuardItems(fragment.idempotencyGuard, effectParams, effectParams, `${ctx} idempotency`)
|
|
3392
3423
|
);
|
|
3393
3424
|
}
|
|
3394
3425
|
for (const m of fragment.maintainWrites ?? []) {
|
|
3395
3426
|
maintainItems.push(
|
|
3396
|
-
renderMaintainWriteItem(m,
|
|
3427
|
+
renderMaintainWriteItem(m, effectParams, effectParams, `${ctx} maintain`, modelBySignature)
|
|
3397
3428
|
);
|
|
3398
3429
|
}
|
|
3399
3430
|
for (const mo of fragment.maintainOutbox ?? []) {
|
|
3400
3431
|
maintainOutboxItems.push(
|
|
3401
|
-
...renderMaintainOutboxItems(mo,
|
|
3432
|
+
...renderMaintainOutboxItems(mo, effectParams, effectParams, `${ctx} maintain-outbox`)
|
|
3402
3433
|
);
|
|
3403
3434
|
}
|
|
3404
3435
|
for (const c of fragment.conditionChecks ?? []) {
|
|
3405
|
-
checkItems.push({ check: renderConditionCheck(c,
|
|
3436
|
+
checkItems.push({ check: renderConditionCheck(c, effectParams, effectParams, `${ctx} requires`) });
|
|
3406
3437
|
}
|
|
3407
3438
|
const hasDerivedEffects = edgeItems.length > 0 || updateItems.length > 0 || guardItems.length > 0 || outboxItems.length > 0 || idempotencyItems.length > 0 || maintainItems.length > 0 || maintainOutboxItems.length > 0 || checkItems.length > 0;
|
|
3408
3439
|
return {
|
|
@@ -3532,8 +3563,23 @@ async function persistSingleWriteWithHooks(w, runtime, origins, transaction, ret
|
|
|
3532
3563
|
});
|
|
3533
3564
|
}
|
|
3534
3565
|
async function runEnvelopeW1(op, runtime, transaction, writeCtxs) {
|
|
3535
|
-
const
|
|
3536
|
-
|
|
3566
|
+
const r = await runLogicalW1(
|
|
3567
|
+
{
|
|
3568
|
+
kind: op.fragment.op.operation,
|
|
3569
|
+
modelClass: op.fragment.op.entity.modelClass,
|
|
3570
|
+
keyFields: op.fragment.keyFields,
|
|
3571
|
+
params: op.params
|
|
3572
|
+
},
|
|
3573
|
+
runtime,
|
|
3574
|
+
transaction,
|
|
3575
|
+
writeCtxs
|
|
3576
|
+
);
|
|
3577
|
+
if (r.rewritten !== void 0) return { op, rewritten: r.rewritten };
|
|
3578
|
+
return { op: { ...op, params: r.params } };
|
|
3579
|
+
}
|
|
3580
|
+
async function runLogicalW1(op, runtime, transaction, writeCtxs) {
|
|
3581
|
+
const kind = op.kind;
|
|
3582
|
+
const keyFields = new Set(op.keyFields);
|
|
3537
3583
|
const input = {};
|
|
3538
3584
|
if (kind === "put") {
|
|
3539
3585
|
input.item = { ...op.params };
|
|
@@ -3547,15 +3593,14 @@ async function runEnvelopeW1(op, runtime, transaction, writeCtxs) {
|
|
|
3547
3593
|
input.key = key;
|
|
3548
3594
|
input.changes = changes;
|
|
3549
3595
|
}
|
|
3550
|
-
const
|
|
3551
|
-
const ctx = runtime.writeCtx(kind, ctxModelFor(modelClass), input, transaction);
|
|
3596
|
+
const ctx = runtime.writeCtx(kind, ctxModelFor(op.modelClass), input, transaction);
|
|
3552
3597
|
await runtime.runWriteBefore(ctx);
|
|
3553
3598
|
writeCtxs.push(ctx);
|
|
3554
3599
|
if (ctx.kind !== kind) {
|
|
3555
|
-
return { op, rewritten: renderRewrittenWrite(modelClass, ctx) };
|
|
3600
|
+
return { params: op.params, rewritten: renderRewrittenWrite(op.modelClass, ctx) };
|
|
3556
3601
|
}
|
|
3557
3602
|
const merged = ctx.kind === "put" ? { ...ctx.input.item ?? {} } : { ...ctx.input.key ?? {}, ...ctx.input.changes ?? {} };
|
|
3558
|
-
return {
|
|
3603
|
+
return { params: merged };
|
|
3559
3604
|
}
|
|
3560
3605
|
function renderRewrittenWrite(modelClass, ctx) {
|
|
3561
3606
|
const modelStatic = modelClass.asModel();
|
|
@@ -3646,6 +3691,199 @@ async function executeCompiledParallelWrites(ops, options) {
|
|
|
3646
3691
|
]);
|
|
3647
3692
|
return results;
|
|
3648
3693
|
}
|
|
3694
|
+
var LOGICAL_EFFECT_CATEGORIES = [
|
|
3695
|
+
"edge",
|
|
3696
|
+
"derive",
|
|
3697
|
+
"unique",
|
|
3698
|
+
"outbox",
|
|
3699
|
+
"idempotency",
|
|
3700
|
+
"maintain",
|
|
3701
|
+
"maintainOutbox"
|
|
3702
|
+
];
|
|
3703
|
+
function renderLogicalWrite(op, params) {
|
|
3704
|
+
const base = {
|
|
3705
|
+
operation: op.kind,
|
|
3706
|
+
modelClass: op.modelClass,
|
|
3707
|
+
modelStatic: op.modelStatic,
|
|
3708
|
+
key: params,
|
|
3709
|
+
...op.condition !== void 0 ? { condition: op.condition } : {}
|
|
3710
|
+
};
|
|
3711
|
+
if (op.kind === "put") return { ...base, item: params };
|
|
3712
|
+
if (op.kind === "update") {
|
|
3713
|
+
const keyFields = new Set(op.keyFields);
|
|
3714
|
+
const changes = {};
|
|
3715
|
+
for (const [k, v] of Object.entries(params)) {
|
|
3716
|
+
if (!keyFields.has(k)) changes[k] = v;
|
|
3717
|
+
}
|
|
3718
|
+
return { ...base, changes };
|
|
3719
|
+
}
|
|
3720
|
+
return base;
|
|
3721
|
+
}
|
|
3722
|
+
function renderLogicalOp(op, params) {
|
|
3723
|
+
const write = renderLogicalWrite(op, params);
|
|
3724
|
+
const rendered = op.renderEffects(params);
|
|
3725
|
+
const e = rendered.effects;
|
|
3726
|
+
const checkItems = rendered.checks.map((check) => ({ check }));
|
|
3727
|
+
const modelBySignature = new Map(rendered.modelBySignature);
|
|
3728
|
+
const pick = (c) => [...e[c] ?? []];
|
|
3729
|
+
const edgeItems = pick("edge");
|
|
3730
|
+
const updateItems = pick("derive");
|
|
3731
|
+
const guardItems = pick("unique");
|
|
3732
|
+
const outboxItems = pick("outbox");
|
|
3733
|
+
const idempotencyItems = pick("idempotency");
|
|
3734
|
+
const maintainItems = pick("maintain");
|
|
3735
|
+
const maintainOutboxItems = pick("maintainOutbox");
|
|
3736
|
+
return {
|
|
3737
|
+
write,
|
|
3738
|
+
edgeItems,
|
|
3739
|
+
updateItems,
|
|
3740
|
+
guardItems,
|
|
3741
|
+
outboxItems,
|
|
3742
|
+
idempotencyItems,
|
|
3743
|
+
maintainItems,
|
|
3744
|
+
maintainOutboxItems,
|
|
3745
|
+
checkItems,
|
|
3746
|
+
modelBySignature,
|
|
3747
|
+
hasDerivedEffects: edgeItems.length > 0 || updateItems.length > 0 || guardItems.length > 0 || outboxItems.length > 0 || idempotencyItems.length > 0 || maintainItems.length > 0 || maintainOutboxItems.length > 0 || checkItems.length > 0
|
|
3748
|
+
};
|
|
3749
|
+
}
|
|
3750
|
+
function readBackForLogicalOp(op, params) {
|
|
3751
|
+
const sel = op.result?.select;
|
|
3752
|
+
if (sel === void 0 || Object.keys(sel).length === 0) return void 0;
|
|
3753
|
+
const key = {};
|
|
3754
|
+
for (const f of op.keyFields) {
|
|
3755
|
+
if (f in params) key[f] = params[f];
|
|
3756
|
+
}
|
|
3757
|
+
return executeQuery(op.modelClass, key, sel, {
|
|
3758
|
+
consistentRead: op.result?.options?.consistentRead ?? true,
|
|
3759
|
+
maxDepth: op.result?.options?.maxDepth
|
|
3760
|
+
});
|
|
3761
|
+
}
|
|
3762
|
+
async function executeLogicalWriteOps(ops, options) {
|
|
3763
|
+
const label = options.label;
|
|
3764
|
+
const writeMw = buildWriteRuntime(options.context);
|
|
3765
|
+
const writeCtxs = [];
|
|
3766
|
+
const txId = { id: /* @__PURE__ */ Symbol("graphddb.prepared-aot") };
|
|
3767
|
+
const effectiveParams = [];
|
|
3768
|
+
const rewrites = [];
|
|
3769
|
+
if (writeMw.active) {
|
|
3770
|
+
for (const op of ops) {
|
|
3771
|
+
const r = await runLogicalW1(op, writeMw, txId, writeCtxs);
|
|
3772
|
+
effectiveParams.push(r.params);
|
|
3773
|
+
rewrites.push(r.rewritten ?? null);
|
|
3774
|
+
}
|
|
3775
|
+
} else {
|
|
3776
|
+
for (const op of ops) {
|
|
3777
|
+
effectiveParams.push(op.params);
|
|
3778
|
+
rewrites.push(null);
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
const renderedOps = ops.map(
|
|
3782
|
+
(op, i) => rewrites[i] !== null ? rewrittenCompiledOp(rewrites[i]) : renderLogicalOp(op, effectiveParams[i])
|
|
3783
|
+
);
|
|
3784
|
+
const rendered = renderedOps.map((r) => r.write);
|
|
3785
|
+
const modelBySignature = /* @__PURE__ */ new Map();
|
|
3786
|
+
const edgeItems = [];
|
|
3787
|
+
const updateItems = [];
|
|
3788
|
+
const guardItems = [];
|
|
3789
|
+
const outboxItems = [];
|
|
3790
|
+
const idempotencyItems = [];
|
|
3791
|
+
const maintainItems = [];
|
|
3792
|
+
const maintainOutboxItems = [];
|
|
3793
|
+
const checkItems = [];
|
|
3794
|
+
for (const r of renderedOps) {
|
|
3795
|
+
for (const [sig, name] of r.modelBySignature) modelBySignature.set(sig, name);
|
|
3796
|
+
edgeItems.push(...r.edgeItems);
|
|
3797
|
+
updateItems.push(...r.updateItems);
|
|
3798
|
+
guardItems.push(...r.guardItems);
|
|
3799
|
+
outboxItems.push(...r.outboxItems);
|
|
3800
|
+
idempotencyItems.push(...r.idempotencyItems);
|
|
3801
|
+
maintainItems.push(...r.maintainItems);
|
|
3802
|
+
maintainOutboxItems.push(...r.maintainOutboxItems);
|
|
3803
|
+
checkItems.push(...r.checkItems);
|
|
3804
|
+
}
|
|
3805
|
+
const hasDerivedEffects = renderedOps.some((r) => r.hasDerivedEffects);
|
|
3806
|
+
const origins = writeMw.active ? rendered.map((w) => ({ model: ctxModelFor(w.modelClass), kind: w.operation })) : [];
|
|
3807
|
+
if (rendered.length === 1 && !hasDerivedEffects) {
|
|
3808
|
+
if (writeMw.active) {
|
|
3809
|
+
await persistSingleWriteWithHooks(rendered[0], writeMw, origins, txId, options.retry);
|
|
3810
|
+
} else {
|
|
3811
|
+
await executeSingleWrite(rendered[0], options.retry);
|
|
3812
|
+
}
|
|
3813
|
+
} else {
|
|
3814
|
+
await executeReferentialTransaction(
|
|
3815
|
+
rendered,
|
|
3816
|
+
edgeItems,
|
|
3817
|
+
updateItems,
|
|
3818
|
+
guardItems,
|
|
3819
|
+
outboxItems,
|
|
3820
|
+
idempotencyItems,
|
|
3821
|
+
maintainItems,
|
|
3822
|
+
maintainOutboxItems,
|
|
3823
|
+
checkItems,
|
|
3824
|
+
label,
|
|
3825
|
+
modelBySignature,
|
|
3826
|
+
options.retry,
|
|
3827
|
+
writeMw.active ? { runtime: writeMw, origins, transaction: txId } : void 0
|
|
3828
|
+
);
|
|
3829
|
+
}
|
|
3830
|
+
if (writeMw.active) {
|
|
3831
|
+
for (let i = writeCtxs.length - 1; i >= 0; i--) {
|
|
3832
|
+
await writeMw.runWriteAfter(writeCtxs[i], {});
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
const readBacks = await Promise.all(
|
|
3836
|
+
ops.map((op, i) => readBackForLogicalOp(op, effectiveParams[i]))
|
|
3837
|
+
);
|
|
3838
|
+
return { readBacks };
|
|
3839
|
+
}
|
|
3840
|
+
async function executeLogicalParallelWrites(ops, options) {
|
|
3841
|
+
const label = options.label;
|
|
3842
|
+
const results = new Array(ops.length);
|
|
3843
|
+
const renderedOps = ops.map((op) => renderLogicalOp(op, op.params));
|
|
3844
|
+
const coalescibleIdx = [];
|
|
3845
|
+
const individualIdx = [];
|
|
3846
|
+
const hooksActive = buildWriteRuntime(options.context).active;
|
|
3847
|
+
renderedOps.forEach((r, i) => {
|
|
3848
|
+
const w = r.write;
|
|
3849
|
+
if (!hooksActive && !r.hasDerivedEffects && w.condition === void 0 && (w.operation === "put" || w.operation === "delete")) {
|
|
3850
|
+
coalescibleIdx.push(i);
|
|
3851
|
+
} else {
|
|
3852
|
+
individualIdx.push(i);
|
|
3853
|
+
}
|
|
3854
|
+
});
|
|
3855
|
+
await Promise.all([
|
|
3856
|
+
(async () => {
|
|
3857
|
+
if (coalescibleIdx.length === 0) return;
|
|
3858
|
+
const writes = coalescibleIdx.map((i) => renderedOps[i].write);
|
|
3859
|
+
try {
|
|
3860
|
+
await executeParallelWrites(writes);
|
|
3861
|
+
await Promise.all(
|
|
3862
|
+
coalescibleIdx.map(async (i) => {
|
|
3863
|
+
const value = await readBackForLogicalOp(ops[i], ops[i].params);
|
|
3864
|
+
results[i] = { ok: true, value };
|
|
3865
|
+
})
|
|
3866
|
+
);
|
|
3867
|
+
} catch (e) {
|
|
3868
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
3869
|
+
for (const i of coalescibleIdx) results[i] = { ok: false, error };
|
|
3870
|
+
}
|
|
3871
|
+
})(),
|
|
3872
|
+
...individualIdx.map(async (i) => {
|
|
3873
|
+
try {
|
|
3874
|
+
const { readBacks } = await executeLogicalWriteOps([ops[i]], {
|
|
3875
|
+
label: `${label} (op #${i})`,
|
|
3876
|
+
...options.retry !== void 0 ? { retry: options.retry } : {},
|
|
3877
|
+
...options.context !== void 0 ? { context: options.context } : {}
|
|
3878
|
+
});
|
|
3879
|
+
results[i] = { ok: true, value: readBacks[0] };
|
|
3880
|
+
} catch (e) {
|
|
3881
|
+
results[i] = { ok: false, error: e instanceof Error ? e : new Error(String(e)) };
|
|
3882
|
+
}
|
|
3883
|
+
})
|
|
3884
|
+
]);
|
|
3885
|
+
return results;
|
|
3886
|
+
}
|
|
3649
3887
|
function withCondition(op, condition) {
|
|
3650
3888
|
if (op.condition === void 0) return { ...op, condition };
|
|
3651
3889
|
const base = op.condition;
|
|
@@ -4052,7 +4290,7 @@ function classify(routes) {
|
|
|
4052
4290
|
}
|
|
4053
4291
|
return kind;
|
|
4054
4292
|
}
|
|
4055
|
-
function
|
|
4293
|
+
function analyzeWriteRoute(alias, route) {
|
|
4056
4294
|
const present = INTENT_KEYS.filter((k) => route[k] !== void 0);
|
|
4057
4295
|
if (present.length !== 1) {
|
|
4058
4296
|
throw new Error(
|
|
@@ -4073,23 +4311,37 @@ function compileWriteRoute(alias, route) {
|
|
|
4073
4311
|
);
|
|
4074
4312
|
const conditionRec = route.condition !== void 0 ? recordSlots(route.condition, `${alias} condition`) : void 0;
|
|
4075
4313
|
if (route.result !== void 0) assertStaticTemplate(route.result, `${alias} result`);
|
|
4076
|
-
const fragment = compileWriteFragment({
|
|
4077
|
-
alias,
|
|
4078
|
-
intent,
|
|
4079
|
-
model,
|
|
4080
|
-
keyFieldNames: keyRec.fields,
|
|
4081
|
-
inputFieldNames: inputRec.fields
|
|
4082
|
-
});
|
|
4083
4314
|
return {
|
|
4084
4315
|
alias,
|
|
4085
4316
|
intent,
|
|
4086
|
-
|
|
4317
|
+
model,
|
|
4318
|
+
keyFields: keyRec.fields,
|
|
4319
|
+
inputFields: inputRec.fields,
|
|
4087
4320
|
keySlots: keyRec.slots,
|
|
4088
4321
|
inputSlots: inputRec.slots,
|
|
4089
4322
|
...conditionRec !== void 0 ? { conditionSlots: conditionRec.slots } : {},
|
|
4090
4323
|
...route.result !== void 0 ? { result: route.result } : {}
|
|
4091
4324
|
};
|
|
4092
4325
|
}
|
|
4326
|
+
function compileWriteRoute(alias, route) {
|
|
4327
|
+
const analyzed = analyzeWriteRoute(alias, route);
|
|
4328
|
+
const fragment = compileWriteFragment({
|
|
4329
|
+
alias,
|
|
4330
|
+
intent: analyzed.intent,
|
|
4331
|
+
model: analyzed.model,
|
|
4332
|
+
keyFieldNames: analyzed.keyFields,
|
|
4333
|
+
inputFieldNames: analyzed.inputFields
|
|
4334
|
+
});
|
|
4335
|
+
return {
|
|
4336
|
+
alias,
|
|
4337
|
+
intent: analyzed.intent,
|
|
4338
|
+
fragment,
|
|
4339
|
+
keySlots: analyzed.keySlots,
|
|
4340
|
+
inputSlots: analyzed.inputSlots,
|
|
4341
|
+
...analyzed.conditionSlots !== void 0 ? { conditionSlots: analyzed.conditionSlots } : {},
|
|
4342
|
+
...analyzed.result !== void 0 ? { result: analyzed.result } : {}
|
|
4343
|
+
};
|
|
4344
|
+
}
|
|
4093
4345
|
function compileReadRoute(alias, route) {
|
|
4094
4346
|
const isQuery = route.query !== void 0;
|
|
4095
4347
|
const isList = route.list !== void 0;
|
|
@@ -4256,6 +4508,25 @@ function stableJson(value) {
|
|
|
4256
4508
|
return v;
|
|
4257
4509
|
});
|
|
4258
4510
|
}
|
|
4511
|
+
function evaluatePreparedRoutes(body) {
|
|
4512
|
+
if (typeof body !== "function") {
|
|
4513
|
+
throw new Error(
|
|
4514
|
+
`${PREPARE_LABEL}: expected a body function \`($) => ({ alias: { ... } })\`.`
|
|
4515
|
+
);
|
|
4516
|
+
}
|
|
4517
|
+
const $ = makePreparedInputProxy();
|
|
4518
|
+
const map = body($);
|
|
4519
|
+
if (map === null || typeof map !== "object") {
|
|
4520
|
+
throw new Error(
|
|
4521
|
+
`${PREPARE_LABEL}: the body must return a descriptor map \`{ alias: { ... } }\`.`
|
|
4522
|
+
);
|
|
4523
|
+
}
|
|
4524
|
+
const routes = Object.keys(map).map((alias) => ({
|
|
4525
|
+
alias,
|
|
4526
|
+
route: map[alias]
|
|
4527
|
+
}));
|
|
4528
|
+
return { kind: classify(routes), routes };
|
|
4529
|
+
}
|
|
4259
4530
|
function prepare(body) {
|
|
4260
4531
|
if (typeof body !== "function") {
|
|
4261
4532
|
throw new Error(
|
|
@@ -6839,12 +7110,42 @@ function synthesizeMutationTransaction(contractName, methodName, ops, checks, ed
|
|
|
6839
7110
|
...maintainOutboxItems,
|
|
6840
7111
|
...checkItems
|
|
6841
7112
|
];
|
|
7113
|
+
const categories = [
|
|
7114
|
+
...base.items.map(() => "base"),
|
|
7115
|
+
...edgeItems.map(() => "edge"),
|
|
7116
|
+
...updateItems.map(() => "derive"),
|
|
7117
|
+
...guardItems.map(() => "unique"),
|
|
7118
|
+
...outboxItems.map(() => "outbox"),
|
|
7119
|
+
...idempotencyItems.map(() => "idempotency"),
|
|
7120
|
+
...maintainItems.map(() => "maintain"),
|
|
7121
|
+
...maintainOutboxItems.map(() => "maintainOutbox"),
|
|
7122
|
+
...checkItems.map(() => "check")
|
|
7123
|
+
];
|
|
6842
7124
|
if (items.length > MAX_TRANSACT_ITEMS) {
|
|
6843
7125
|
throw new Error(
|
|
6844
7126
|
`Contract '${contractName}.${methodName}': a mutation composes ${items.length} items (writes + edges + derived updates + uniqueness guards + outbox events + idempotency guard + ConditionChecks) into one TransactWriteItems, exceeding the DynamoDB limit of ${MAX_TRANSACT_ITEMS}. An atomic transaction cannot be split; reduce the fragment / effect count.`
|
|
6845
7127
|
);
|
|
6846
7128
|
}
|
|
6847
|
-
return {
|
|
7129
|
+
return {
|
|
7130
|
+
spec: { params: sortedParams(params), items, maxItems: items.length },
|
|
7131
|
+
categories
|
|
7132
|
+
};
|
|
7133
|
+
}
|
|
7134
|
+
function serializePreparedWriteFragment(label, fragment) {
|
|
7135
|
+
const { spec, categories } = synthesizeMutationTransaction(
|
|
7136
|
+
label,
|
|
7137
|
+
"plan",
|
|
7138
|
+
[fragment.op],
|
|
7139
|
+
fragment.conditionChecks ?? [],
|
|
7140
|
+
fragment.edgeWrites ?? [],
|
|
7141
|
+
fragment.derivedUpdates ?? [],
|
|
7142
|
+
fragment.uniqueGuards ?? [],
|
|
7143
|
+
fragment.outboxEvents ?? [],
|
|
7144
|
+
fragment.idempotencyGuard,
|
|
7145
|
+
fragment.maintainWrites ?? [],
|
|
7146
|
+
fragment.maintainOutbox ?? []
|
|
7147
|
+
);
|
|
7148
|
+
return { transaction: spec, categories };
|
|
6848
7149
|
}
|
|
6849
7150
|
function modeTargetFor(mode, op, commandSpec, contractName, methodName, opName, transactionSpecs) {
|
|
6850
7151
|
if (mode === "parallel") {
|
|
@@ -6967,7 +7268,7 @@ function serializeCommandContract(contractName, contract, commandSpecs, transact
|
|
|
6967
7268
|
idempotencyGuard,
|
|
6968
7269
|
maintainWrites,
|
|
6969
7270
|
maintainOutbox
|
|
6970
|
-
)
|
|
7271
|
+
).spec
|
|
6971
7272
|
) : (
|
|
6972
7273
|
// #90: writes only.
|
|
6973
7274
|
composeFragmentTransaction(contractName, methodName, ops)
|
|
@@ -7263,12 +7564,12 @@ function buildReadOperations(def) {
|
|
|
7263
7564
|
};
|
|
7264
7565
|
const ops = [root];
|
|
7265
7566
|
ops.push(...buildRelationOperations(metadata, select, "$"));
|
|
7266
|
-
return ops;
|
|
7567
|
+
return injectRefsSourceProjections(ops);
|
|
7267
7568
|
}
|
|
7268
7569
|
function projectionFields(select) {
|
|
7269
7570
|
const out = [];
|
|
7270
7571
|
for (const [field, value] of Object.entries(select)) {
|
|
7271
|
-
if (value === true) out.push(field);
|
|
7572
|
+
if (value === true || isInlineSnapshotSpec(value)) out.push(field);
|
|
7272
7573
|
}
|
|
7273
7574
|
return out.sort();
|
|
7274
7575
|
}
|
|
@@ -7284,9 +7585,16 @@ function buildRelationOperations(metadata, select, parentPath) {
|
|
|
7284
7585
|
const targetMeta = MetadataRegistry.get(targetClass);
|
|
7285
7586
|
const childPath = `${parentPath}.${rel.propertyName}`;
|
|
7286
7587
|
if (rel.type === "refs") {
|
|
7287
|
-
|
|
7288
|
-
|
|
7588
|
+
if (spec.filter !== void 0) {
|
|
7589
|
+
throw new Error(
|
|
7590
|
+
`Relation '${rel.propertyName}' (refs) declares a \`filter\`, which the static operations spec cannot apply (a BatchGetItem has no server-side FilterExpression and the spec runtimes run no client-side filter stage). Remove the filter from selects compiled to operations.json / generated Python, or filter in the consumer.`
|
|
7591
|
+
);
|
|
7592
|
+
}
|
|
7593
|
+
ops.push(
|
|
7594
|
+
buildRefsOperation(rel, targetMeta, childSelect, `${childPath}.items`)
|
|
7289
7595
|
);
|
|
7596
|
+
ops.push(...buildRelationOperations(targetMeta, childSelect, `${childPath}.items`));
|
|
7597
|
+
continue;
|
|
7290
7598
|
}
|
|
7291
7599
|
if (rel.type === "hasMany") {
|
|
7292
7600
|
const limit = spec.limit ?? rel.options?.limit?.default ?? 20;
|
|
@@ -7397,6 +7705,65 @@ function buildBatchGetOperation(rel, targetMeta, select, resultPath) {
|
|
|
7397
7705
|
sourceField
|
|
7398
7706
|
};
|
|
7399
7707
|
}
|
|
7708
|
+
function buildRefsOperation(rel, targetMeta, select, resultPath) {
|
|
7709
|
+
const refs = rel.refs;
|
|
7710
|
+
if (!refs) {
|
|
7711
|
+
throw new Error(
|
|
7712
|
+
`Relation '${rel.propertyName}' is type 'refs' but carries no refs binding.`
|
|
7713
|
+
);
|
|
7714
|
+
}
|
|
7715
|
+
const { targetField, sourceField } = singleSourceField(rel);
|
|
7716
|
+
const resolved = resolveKey([targetField], targetMeta);
|
|
7717
|
+
if (resolved.type !== "pk" || resolved.partial) {
|
|
7718
|
+
throw new Error(
|
|
7719
|
+
`Relation '${rel.propertyName}' (refs) must resolve to a full primary key (BatchGetItem); got ${resolved.type}${resolved.partial ? " (partial)" : ""}.`
|
|
7720
|
+
);
|
|
7721
|
+
}
|
|
7722
|
+
if (!targetMeta.primaryKey) throw new Error("Primary key not defined");
|
|
7723
|
+
const nameOf = (f) => f === targetField ? sourceField : f;
|
|
7724
|
+
const { pk, sk } = evaluateKey(
|
|
7725
|
+
targetMeta.primaryKey.segmented,
|
|
7726
|
+
"result",
|
|
7727
|
+
/* @__PURE__ */ new Set([targetField]),
|
|
7728
|
+
nameOf
|
|
7729
|
+
);
|
|
7730
|
+
const keyCondition = { PK: pk };
|
|
7731
|
+
if (sk !== void 0) keyCondition.SK = sk;
|
|
7732
|
+
return {
|
|
7733
|
+
type: "BatchGetItem",
|
|
7734
|
+
tableName: TableMapping.resolve(targetMeta.tableName),
|
|
7735
|
+
keyCondition,
|
|
7736
|
+
projection: projectionFields(select),
|
|
7737
|
+
resultPath,
|
|
7738
|
+
sourceField,
|
|
7739
|
+
sourceList: { from: refs.from, key: refs.key }
|
|
7740
|
+
};
|
|
7741
|
+
}
|
|
7742
|
+
function injectRefsSourceProjections(ops) {
|
|
7743
|
+
for (let i = 0; i < ops.length; i++) {
|
|
7744
|
+
const op = ops[i];
|
|
7745
|
+
if (op.sourceList === void 0) continue;
|
|
7746
|
+
const tokens = op.resultPath.split(".");
|
|
7747
|
+
const parentPath = tokens.slice(0, -2).join(".") || "$";
|
|
7748
|
+
const parentIndex = ops.findIndex((p) => p.resultPath === parentPath);
|
|
7749
|
+
if (parentIndex === -1) {
|
|
7750
|
+
throw new Error(
|
|
7751
|
+
`refs fan-out at '${op.resultPath}': no parent operation at '${parentPath}'.`
|
|
7752
|
+
);
|
|
7753
|
+
}
|
|
7754
|
+
const parent = ops[parentIndex];
|
|
7755
|
+
const from2 = op.sourceList.from;
|
|
7756
|
+
if (parent.projection.includes(from2)) {
|
|
7757
|
+
continue;
|
|
7758
|
+
}
|
|
7759
|
+
ops[parentIndex] = {
|
|
7760
|
+
...parent,
|
|
7761
|
+
projection: [...parent.projection, from2].sort()
|
|
7762
|
+
};
|
|
7763
|
+
ops[i] = { ...op, sourceList: { ...op.sourceList, implicit: true } };
|
|
7764
|
+
}
|
|
7765
|
+
return ops;
|
|
7766
|
+
}
|
|
7400
7767
|
function filterSpec(filter) {
|
|
7401
7768
|
return { declarative: filter };
|
|
7402
7769
|
}
|
|
@@ -7551,6 +7918,216 @@ function buildOperations(queries = {}, commands = {}, transactions = {}, contrac
|
|
|
7551
7918
|
};
|
|
7552
7919
|
}
|
|
7553
7920
|
|
|
7921
|
+
// src/spec/prepared.ts
|
|
7922
|
+
import { createHash } from "crypto";
|
|
7923
|
+
var PREPARED_FORMAT_VERSION = "1";
|
|
7924
|
+
function canonicalJson(value) {
|
|
7925
|
+
return JSON.stringify(value, (_k, v) => {
|
|
7926
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) {
|
|
7927
|
+
const sorted = {};
|
|
7928
|
+
for (const key of Object.keys(v).sort()) {
|
|
7929
|
+
sorted[key] = v[key];
|
|
7930
|
+
}
|
|
7931
|
+
return sorted;
|
|
7932
|
+
}
|
|
7933
|
+
return v;
|
|
7934
|
+
});
|
|
7935
|
+
}
|
|
7936
|
+
function entityFingerprint(metadata) {
|
|
7937
|
+
return createHash("sha256").update(canonicalJson(buildManifestEntity(metadata))).digest("hex");
|
|
7938
|
+
}
|
|
7939
|
+
function planFingerprint(plan2) {
|
|
7940
|
+
return createHash("sha256").update(canonicalJson(plan2)).digest("hex");
|
|
7941
|
+
}
|
|
7942
|
+
function bindOfSlot(slot, where) {
|
|
7943
|
+
if (slot.kind === "param") return { param: slot.name };
|
|
7944
|
+
const v = slot.value;
|
|
7945
|
+
if (v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
7946
|
+
return { literal: v };
|
|
7947
|
+
}
|
|
7948
|
+
if (v instanceof Date) return { literalDate: v.toISOString() };
|
|
7949
|
+
if (typeof v === "bigint") return { literalBigInt: v.toString() };
|
|
7950
|
+
throw new Error(
|
|
7951
|
+
`prepared AOT: ${where} carries a non-serializable literal (${typeof v}).`
|
|
7952
|
+
);
|
|
7953
|
+
}
|
|
7954
|
+
function bindMapOf(slots, where) {
|
|
7955
|
+
const out = {};
|
|
7956
|
+
for (const [field, slot] of Object.entries(slots)) {
|
|
7957
|
+
out[field] = bindOfSlot(slot, `${where} field '${field}'`);
|
|
7958
|
+
}
|
|
7959
|
+
return out;
|
|
7960
|
+
}
|
|
7961
|
+
function paramKindForField(metadata, field) {
|
|
7962
|
+
const f = metadata.fields.find((x) => x.propertyName === field);
|
|
7963
|
+
return f?.dynamoType === "N" ? "number" : "string";
|
|
7964
|
+
}
|
|
7965
|
+
function addParam(params, name, kind) {
|
|
7966
|
+
if (!(name in params)) params[name] = { type: kind, required: true };
|
|
7967
|
+
}
|
|
7968
|
+
function readRouteQuerySpec(route, metadata) {
|
|
7969
|
+
const params = {};
|
|
7970
|
+
const key = {};
|
|
7971
|
+
for (const field of Object.keys(route.keySlots)) {
|
|
7972
|
+
const kind = paramKindForField(metadata, field);
|
|
7973
|
+
params[field] = { kind, required: true };
|
|
7974
|
+
key[field] = kind === "number" ? param.number() : param.string();
|
|
7975
|
+
}
|
|
7976
|
+
const def = {
|
|
7977
|
+
__isOperationDefinition: true,
|
|
7978
|
+
entity: { name: route.modelClass.name, modelClass: route.modelClass },
|
|
7979
|
+
operation: route.kind === "query" ? "query" : "list",
|
|
7980
|
+
key,
|
|
7981
|
+
select: route.select,
|
|
7982
|
+
params
|
|
7983
|
+
};
|
|
7984
|
+
return buildQuerySpec(def);
|
|
7985
|
+
}
|
|
7986
|
+
function compileReadPlan(routes) {
|
|
7987
|
+
const specs = [];
|
|
7988
|
+
const params = {};
|
|
7989
|
+
const entities = {};
|
|
7990
|
+
for (const { alias, route } of routes) {
|
|
7991
|
+
const compiled = compileReadRoute(alias, route);
|
|
7992
|
+
const metadata = MetadataRegistry.get(compiled.modelClass);
|
|
7993
|
+
const entityName = compiled.modelClass.name;
|
|
7994
|
+
entities[entityName] = entityFingerprint(metadata);
|
|
7995
|
+
for (const [field, slot] of Object.entries(compiled.keySlots)) {
|
|
7996
|
+
if (slot.kind === "param") {
|
|
7997
|
+
addParam(params, slot.name, paramKindForField(metadata, field));
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
const dynamicParam = (slot, kind) => {
|
|
8001
|
+
if (slot?.kind === "param") addParam(params, slot.name, kind);
|
|
8002
|
+
};
|
|
8003
|
+
dynamicParam(compiled.limitSlot, "number");
|
|
8004
|
+
dynamicParam(compiled.afterSlot, "string");
|
|
8005
|
+
dynamicParam(compiled.consistentReadSlot, "string");
|
|
8006
|
+
specs.push({
|
|
8007
|
+
alias,
|
|
8008
|
+
op: compiled.kind,
|
|
8009
|
+
entity: entityName,
|
|
8010
|
+
select: compiled.select,
|
|
8011
|
+
key: bindMapOf(compiled.keySlots, `${alias} key`),
|
|
8012
|
+
...compiled.maxDepth !== void 0 ? { maxDepth: compiled.maxDepth } : {},
|
|
8013
|
+
...compiled.order !== void 0 ? { order: compiled.order } : {},
|
|
8014
|
+
...compiled.filter !== void 0 ? { filter: compiled.filter } : {},
|
|
8015
|
+
...compiled.consistentReadSlot !== void 0 ? { consistentRead: bindOfSlot(compiled.consistentReadSlot, `${alias} consistentRead`) } : {},
|
|
8016
|
+
...compiled.limitSlot !== void 0 ? { limit: bindOfSlot(compiled.limitSlot, `${alias} limit`) } : {},
|
|
8017
|
+
...compiled.afterSlot !== void 0 ? { after: bindOfSlot(compiled.afterSlot, `${alias} after`) } : {},
|
|
8018
|
+
query: readRouteQuerySpec(compiled, metadata)
|
|
8019
|
+
});
|
|
8020
|
+
}
|
|
8021
|
+
return { kind: "read", params, entities, reads: specs };
|
|
8022
|
+
}
|
|
8023
|
+
function compileWritePlan(routes, planLabel) {
|
|
8024
|
+
const specs = [];
|
|
8025
|
+
const params = {};
|
|
8026
|
+
const entities = {};
|
|
8027
|
+
for (const { alias, route } of routes) {
|
|
8028
|
+
const analyzed = analyzeWriteRoute(alias, route);
|
|
8029
|
+
if (analyzed.intent === "create" && analyzed.conditionSlots !== void 0) {
|
|
8030
|
+
throw new Error(
|
|
8031
|
+
`prepared AOT (${planLabel} '${alias}'): a \`condition\` on a 'create' is not supported \u2014 a create already guards with attribute_not_exists(PK). Use an 'update' with a condition, or drop the condition.`
|
|
8032
|
+
);
|
|
8033
|
+
}
|
|
8034
|
+
const modelClass = resolveModelClass(analyzed.model);
|
|
8035
|
+
const metadata = MetadataRegistry.get(modelClass);
|
|
8036
|
+
const entityName = modelClass.name;
|
|
8037
|
+
entities[entityName] = entityFingerprint(metadata);
|
|
8038
|
+
const fragment = compileWriteFragment({
|
|
8039
|
+
alias,
|
|
8040
|
+
intent: analyzed.intent,
|
|
8041
|
+
model: analyzed.model,
|
|
8042
|
+
keyFieldNames: analyzed.keyFields,
|
|
8043
|
+
inputFieldNames: analyzed.inputFields
|
|
8044
|
+
});
|
|
8045
|
+
const { transaction, categories } = serializePreparedWriteFragment(
|
|
8046
|
+
`${planLabel}.${alias}`,
|
|
8047
|
+
fragment
|
|
8048
|
+
);
|
|
8049
|
+
for (const item of transaction.items) {
|
|
8050
|
+
if (item.literalKey === true || item.entity === MARKER_ROW_ENTITY) continue;
|
|
8051
|
+
if (item.entity in entities) continue;
|
|
8052
|
+
const cls = resolveRegisteredModel(item.entity, `${planLabel}.${alias}`);
|
|
8053
|
+
entities[item.entity] = entityFingerprint(MetadataRegistry.get(cls));
|
|
8054
|
+
}
|
|
8055
|
+
for (const [field, slot] of Object.entries({
|
|
8056
|
+
...analyzed.inputSlots,
|
|
8057
|
+
...analyzed.keySlots,
|
|
8058
|
+
...analyzed.conditionSlots ?? {}
|
|
8059
|
+
})) {
|
|
8060
|
+
if (slot.kind === "param") {
|
|
8061
|
+
addParam(params, slot.name, paramKindForField(metadata, field));
|
|
8062
|
+
}
|
|
8063
|
+
}
|
|
8064
|
+
let readback;
|
|
8065
|
+
const resultSelect = analyzed.result?.select;
|
|
8066
|
+
if (resultSelect !== void 0 && Object.keys(resultSelect).length > 0) {
|
|
8067
|
+
const kf = {};
|
|
8068
|
+
const kp = {};
|
|
8069
|
+
for (const field of fragment.keyFields) {
|
|
8070
|
+
const kind = paramKindForField(metadata, field);
|
|
8071
|
+
kp[field] = { kind, required: true };
|
|
8072
|
+
kf[field] = kind === "number" ? param.number() : param.string();
|
|
8073
|
+
}
|
|
8074
|
+
readback = buildQuerySpec({
|
|
8075
|
+
__isOperationDefinition: true,
|
|
8076
|
+
entity: { name: entityName, modelClass },
|
|
8077
|
+
operation: "query",
|
|
8078
|
+
key: kf,
|
|
8079
|
+
select: resultSelect,
|
|
8080
|
+
params: kp
|
|
8081
|
+
});
|
|
8082
|
+
}
|
|
8083
|
+
specs.push({
|
|
8084
|
+
alias,
|
|
8085
|
+
entity: entityName,
|
|
8086
|
+
intent: analyzed.intent,
|
|
8087
|
+
keyFields: fragment.keyFields,
|
|
8088
|
+
key: bindMapOf(analyzed.keySlots, `${alias} key`),
|
|
8089
|
+
input: bindMapOf(analyzed.inputSlots, `${alias} input`),
|
|
8090
|
+
...analyzed.conditionSlots !== void 0 ? { condition: bindMapOf(analyzed.conditionSlots, `${alias} condition`) } : {},
|
|
8091
|
+
...analyzed.result !== void 0 ? { result: analyzed.result } : {},
|
|
8092
|
+
...readback !== void 0 ? { readback } : {},
|
|
8093
|
+
transaction,
|
|
8094
|
+
categories
|
|
8095
|
+
});
|
|
8096
|
+
}
|
|
8097
|
+
return { kind: "write", params, entities, writes: specs };
|
|
8098
|
+
}
|
|
8099
|
+
function resolveRegisteredModel(name, label) {
|
|
8100
|
+
for (const cls of MetadataRegistry.getAll().keys()) {
|
|
8101
|
+
if (cls.name === name) {
|
|
8102
|
+
return cls;
|
|
8103
|
+
}
|
|
8104
|
+
}
|
|
8105
|
+
throw new Error(
|
|
8106
|
+
`prepared AOT (${label}): a derived effect targets entity '${name}', which is not a registered model.`
|
|
8107
|
+
);
|
|
8108
|
+
}
|
|
8109
|
+
function compilePreparedPlan(body, planLabel = "prepared") {
|
|
8110
|
+
const { kind, routes } = evaluatePreparedRoutes(body);
|
|
8111
|
+
if (kind === "read") {
|
|
8112
|
+
return compileReadPlan(
|
|
8113
|
+
routes
|
|
8114
|
+
);
|
|
8115
|
+
}
|
|
8116
|
+
return compileWritePlan(
|
|
8117
|
+
routes,
|
|
8118
|
+
planLabel
|
|
8119
|
+
);
|
|
8120
|
+
}
|
|
8121
|
+
function buildPreparedPlanDocument(plans) {
|
|
8122
|
+
const sorted = {};
|
|
8123
|
+
for (const id of Object.keys(plans).sort()) sorted[id] = plans[id];
|
|
8124
|
+
return {
|
|
8125
|
+
formatVersion: PREPARED_FORMAT_VERSION,
|
|
8126
|
+
specVersion: SPEC_VERSION,
|
|
8127
|
+
plans: sorted
|
|
8128
|
+
};
|
|
8129
|
+
}
|
|
8130
|
+
|
|
7554
8131
|
// src/spec/index.ts
|
|
7555
8132
|
function buildBridgeBundle(queries = {}, commands = {}, registry = MetadataRegistry, transactions = {}, contractInputs = {}) {
|
|
7556
8133
|
const bundle = {
|
|
@@ -7564,6 +8141,7 @@ function buildBridgeBundle(queries = {}, commands = {}, registry = MetadataRegis
|
|
|
7564
8141
|
export {
|
|
7565
8142
|
SPEC_VERSION,
|
|
7566
8143
|
MARKER_ROW_ENTITY,
|
|
8144
|
+
buildManifestEntity,
|
|
7567
8145
|
buildManifest,
|
|
7568
8146
|
buildProjection,
|
|
7569
8147
|
compileFilterExpression,
|
|
@@ -7600,7 +8178,11 @@ export {
|
|
|
7600
8178
|
compileSingleFragmentPlan,
|
|
7601
8179
|
MAX_TRANSACT_COMPOSE_ITEMS,
|
|
7602
8180
|
compileMutationPlan,
|
|
8181
|
+
serializeEffectParams,
|
|
7603
8182
|
executeCommandMethod,
|
|
8183
|
+
LOGICAL_EFFECT_CATEGORIES,
|
|
8184
|
+
executeLogicalWriteOps,
|
|
8185
|
+
executeLogicalParallelWrites,
|
|
7604
8186
|
isPreparedParamRef,
|
|
7605
8187
|
PreparedWriteStatement,
|
|
7606
8188
|
PreparedReadStatement,
|
|
@@ -7646,5 +8228,11 @@ export {
|
|
|
7646
8228
|
assertContractBoundaries,
|
|
7647
8229
|
buildQuerySpec,
|
|
7648
8230
|
buildOperations,
|
|
8231
|
+
PREPARED_FORMAT_VERSION,
|
|
8232
|
+
canonicalJson,
|
|
8233
|
+
entityFingerprint,
|
|
8234
|
+
planFingerprint,
|
|
8235
|
+
compilePreparedPlan,
|
|
8236
|
+
buildPreparedPlanDocument,
|
|
7649
8237
|
buildBridgeBundle
|
|
7650
8238
|
};
|