graphddb 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/behaviors-DDltNivc.d.ts +217 -0
- package/dist/cdc/index.d.ts +5 -4
- package/dist/cdc/index.js +3 -3
- package/dist/{chunk-HNY2EJPV.js → chunk-KOIJ4SNO.js} +1 -1
- package/dist/{chunk-S2NI4PBW.js → chunk-NWTEUWJD.js} +7 -5
- package/dist/{chunk-LAT64YCZ.js → chunk-SSVG6NQK.js} +657 -18
- package/dist/{chunk-L4QRCHRQ.js → chunk-WOFRHRXY.js} +6 -25
- package/dist/{chunk-T44OB5GU.js → chunk-XZTA4VJH.js} +23 -7
- package/dist/{chunk-GS4C5VGO.js → chunk-YGQ2NO6J.js} +2 -2
- package/dist/{chunk-L2NEDS7U.js → chunk-ZNU7OI5I.js} +241 -8
- package/dist/cli.js +366 -80
- package/dist/index.d.ts +131 -5
- package/dist/index.js +121 -5
- package/dist/internal/index.d.ts +6 -5
- package/dist/internal/index.js +7 -7
- package/dist/{key-DZtjAQDh.d.ts → key-Dne-a20Q.d.ts} +40 -5
- package/dist/linter/index.d.ts +6 -5
- package/dist/{linter-DQY7gUEk.d.ts → linter-Cr1Hnw7n.d.ts} +1 -1
- package/dist/{prepared-artifact-HFealr1q.d.ts → prepared-artifact-BXkARxwO.d.ts} +4 -4
- package/dist/spec/index.d.ts +225 -24
- package/dist/spec/index.js +17 -9
- package/dist/testing/index.d.ts +3 -2
- package/dist/testing/index.js +3 -3
- package/dist/transform/index.d.ts +2 -1
- package/dist/transform/index.js +2 -2
- package/dist/{types-DW__-Icc.d.ts → types-BnFsTZwl.d.ts} +1 -1
- package/dist/{types-2PMXEn5x.d.ts → types-CfxzTEFL.d.ts} +262 -20
- package/docs/python-bridge.md +33 -49
- package/package.json +11 -3
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
PREPARED_FORMAT_VERSION,
|
|
3
3
|
buildManifest,
|
|
4
4
|
entityFingerprint
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NWTEUWJD.js";
|
|
6
6
|
import {
|
|
7
7
|
analyzeWriteRoute,
|
|
8
8
|
assertBundleSerializable,
|
|
@@ -21,33 +21,96 @@ import {
|
|
|
21
21
|
isContractKeyRef,
|
|
22
22
|
isContractParamRef,
|
|
23
23
|
isQueryModelContract
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-XZTA4VJH.js";
|
|
25
25
|
import {
|
|
26
26
|
detectRelationFields,
|
|
27
27
|
isInlineSnapshotSpec,
|
|
28
28
|
normalizeSelectSpec
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-KOIJ4SNO.js";
|
|
30
30
|
import {
|
|
31
|
+
EXPR_VERSION,
|
|
31
32
|
MARKER_ROW_ENTITY,
|
|
32
|
-
|
|
33
|
+
SPEC_VERSION_KEY_EXPR,
|
|
33
34
|
operationsSpecVersion
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-WOFRHRXY.js";
|
|
35
36
|
import {
|
|
36
37
|
BATCH_GET_MAX_KEYS,
|
|
37
38
|
CONDITION_OPERATOR_KEYS,
|
|
39
|
+
GRAPHDDB_CATALOG,
|
|
38
40
|
MAX_TRANSACT_ITEMS,
|
|
39
41
|
MetadataRegistry,
|
|
42
|
+
assertComponentsInCatalog,
|
|
43
|
+
assertPublishSplitMatchesDerived,
|
|
44
|
+
deriveComponentEffects,
|
|
40
45
|
isParam,
|
|
41
46
|
isRawCondition,
|
|
42
47
|
param,
|
|
43
48
|
resolveModelClass
|
|
44
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-ZNU7OI5I.js";
|
|
45
50
|
import {
|
|
46
51
|
TableMapping,
|
|
47
52
|
isColumn,
|
|
48
53
|
resolveKey
|
|
49
54
|
} from "./chunk-XTWXMOHD.js";
|
|
50
55
|
|
|
56
|
+
// src/spec/key-expr.ts
|
|
57
|
+
var PLACEHOLDER = /\{([^{}]+)\}/g;
|
|
58
|
+
var RESULT_PREFIX = "result.";
|
|
59
|
+
var ITEM_PREFIX = "item.";
|
|
60
|
+
function tokenRef(token, template) {
|
|
61
|
+
if (token.startsWith(ITEM_PREFIX)) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`lowerKeyTemplate: read-path key template '${template}' references an '${token}' (item/Map-element) binding, which is a Phase 3 write-path token, not a Phase 2 read-path key wiring. Refusing to lower it.`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (token.startsWith(RESULT_PREFIX)) {
|
|
67
|
+
return { ref: ["result", token.slice(RESULT_PREFIX.length)] };
|
|
68
|
+
}
|
|
69
|
+
return { ref: ["input", token] };
|
|
70
|
+
}
|
|
71
|
+
function lowerKeyTemplate(template) {
|
|
72
|
+
const parts = [];
|
|
73
|
+
let last = 0;
|
|
74
|
+
PLACEHOLDER.lastIndex = 0;
|
|
75
|
+
for (let m = PLACEHOLDER.exec(template); m !== null; m = PLACEHOLDER.exec(template)) {
|
|
76
|
+
const literal = template.slice(last, m.index);
|
|
77
|
+
if (literal !== "") parts.push(literal);
|
|
78
|
+
parts.push(tokenRef(m[1], template));
|
|
79
|
+
last = m.index + m[0].length;
|
|
80
|
+
}
|
|
81
|
+
const tail = template.slice(last);
|
|
82
|
+
if (tail !== "") parts.push(tail);
|
|
83
|
+
if (parts.length === 0) return "";
|
|
84
|
+
if (parts.length === 1) return parts[0];
|
|
85
|
+
return { concat: parts };
|
|
86
|
+
}
|
|
87
|
+
function lowerKeyCondition(keyCondition) {
|
|
88
|
+
const out = {};
|
|
89
|
+
for (const [attr, template] of Object.entries(keyCondition)) {
|
|
90
|
+
out[attr] = lowerKeyTemplate(template);
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
function renderKeyExprToTemplate(node) {
|
|
95
|
+
if (typeof node === "string") return node;
|
|
96
|
+
if (node !== null && typeof node === "object") {
|
|
97
|
+
const obj = node;
|
|
98
|
+
if (Array.isArray(obj.ref)) return refToToken(obj.ref);
|
|
99
|
+
if (Array.isArray(obj.concat)) {
|
|
100
|
+
return obj.concat.map(renderKeyExprToTemplate).join("");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw new Error(
|
|
104
|
+
`renderKeyExprToTemplate: node is not a key-wiring expression (string literal / ref / concat): ${JSON.stringify(node)}`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
function refToToken(path) {
|
|
108
|
+
const [root, ...rest] = path;
|
|
109
|
+
if (root === "result") return `{result.${rest.join(".")}}`;
|
|
110
|
+
if (root === "input") return `{${rest.join(".")}}`;
|
|
111
|
+
throw new Error(`renderKeyExprToTemplate: unexpected ref root '${root}' in key wiring`);
|
|
112
|
+
}
|
|
113
|
+
|
|
51
114
|
// src/spec/contract-n1-check.ts
|
|
52
115
|
function isQueryMethodSpec(spec) {
|
|
53
116
|
return typeof spec.resolution === "string" && spec.resolution !== void 0;
|
|
@@ -133,7 +196,9 @@ ${rest.map((v) => ` - ${v.message}`).join("\n")})` : "";
|
|
|
133
196
|
|
|
134
197
|
// src/spec/contracts.ts
|
|
135
198
|
function kindForDynamoType(dynamoType) {
|
|
136
|
-
|
|
199
|
+
if (dynamoType === "N") return "number";
|
|
200
|
+
if (dynamoType === "M") return "map";
|
|
201
|
+
return "string";
|
|
137
202
|
}
|
|
138
203
|
function recoverKind(metadata, fieldName) {
|
|
139
204
|
const field = metadata.fields.find((f) => f.propertyName === fieldName);
|
|
@@ -185,6 +250,11 @@ function opToDefinition(contractName, methodName, op) {
|
|
|
185
250
|
};
|
|
186
251
|
}
|
|
187
252
|
function paramOfKind(recovered) {
|
|
253
|
+
if (recovered.kind === "map") {
|
|
254
|
+
throw new Error(
|
|
255
|
+
`A \`@map\` field cannot bind a key-position param: a DynamoDB key is composed of scalar segments, but this key field's model storage type is 'M' (issue #295). Bind map values at a value position (\`input\`) instead.`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
188
258
|
if (recovered.kind === "number") return param.number();
|
|
189
259
|
if (recovered.kind === "literal" && recovered.literals !== void 0 && recovered.literals.length > 0) {
|
|
190
260
|
return param.literal(
|
|
@@ -402,6 +472,13 @@ function composeSpecOf(node, contractName, methodName, contracts) {
|
|
|
402
472
|
`Contract '${contractName}.${methodName}': composed child '${c.as}' must declare a \`resolution\` of 'point' or 'range'.`
|
|
403
473
|
);
|
|
404
474
|
}
|
|
475
|
+
const target = contracts[c.contract];
|
|
476
|
+
if (target !== void 0 && isQueryModelContract(target)) {
|
|
477
|
+
const targetMethod = target.methods[c.method];
|
|
478
|
+
if (targetMethod !== void 0) {
|
|
479
|
+
assertComposeTargetNotNested(contractName, methodName, c.as, c.contract, c.method, targetMethod.op);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
405
482
|
const bindIn = c.bind ?? {};
|
|
406
483
|
const bind = {};
|
|
407
484
|
for (const field of Object.keys(bindIn).sort()) {
|
|
@@ -435,6 +512,7 @@ function recordedComposeSpec(node, contractName, methodName, contracts) {
|
|
|
435
512
|
contracts
|
|
436
513
|
);
|
|
437
514
|
const refMethod = node.method;
|
|
515
|
+
assertComposeTargetNotNested(contractName, methodName, node.as, refName, refMethodName, refMethod.op);
|
|
438
516
|
const bind = {};
|
|
439
517
|
for (const field of Object.keys(node.bind).sort()) {
|
|
440
518
|
bind[field] = node.bind[field].path;
|
|
@@ -470,6 +548,12 @@ function composeOf(op) {
|
|
|
470
548
|
const raw = op.compose;
|
|
471
549
|
return Array.isArray(raw) ? raw : [];
|
|
472
550
|
}
|
|
551
|
+
function assertComposeTargetNotNested(contractName, methodName, as, targetContract, targetMethod, targetOp) {
|
|
552
|
+
if (targetOp === void 0 || composeOf(targetOp).length === 0) return;
|
|
553
|
+
throw new Error(
|
|
554
|
+
`Contract '${contractName}.${methodName}': composed child '${as}' references '${targetContract}.${targetMethod}', which itself declares a composition (nested compose). No runtime resolves a composed child's own compose edges, so '${targetContract}.${targetMethod}'s compose field(s) would never be populated through '${contractName}.${methodName}' \u2014 the generated result type would over-promise. Compose the grandchild directly from '${contractName}.${methodName}' instead (issue #256).`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
473
557
|
function compositionPlanOf(compose) {
|
|
474
558
|
if (compose.length === 0) return void 0;
|
|
475
559
|
const { stages, concurrency } = deriveCompositionPlan(compose.length);
|
|
@@ -482,6 +566,9 @@ function compositionPlanOf(compose) {
|
|
|
482
566
|
function batchTxName(contractName, methodName) {
|
|
483
567
|
return `${opRefName(contractName, methodName)}__batch`;
|
|
484
568
|
}
|
|
569
|
+
function batchWriteTxName(contractName, methodName) {
|
|
570
|
+
return `${opRefName(contractName, methodName)}__batchWrite`;
|
|
571
|
+
}
|
|
485
572
|
function keyParamNames(op) {
|
|
486
573
|
return keyFieldsOf(op);
|
|
487
574
|
}
|
|
@@ -556,6 +643,44 @@ function synthesizeBatchTransaction(commandSpec, op) {
|
|
|
556
643
|
};
|
|
557
644
|
return { params, items: [item] };
|
|
558
645
|
}
|
|
646
|
+
function synthesizeBatchWriteTransaction(contractName, methodName, commandSpec) {
|
|
647
|
+
const at = `Contract '${contractName}.${methodName}'`;
|
|
648
|
+
if (commandSpec.type === "UpdateItem") {
|
|
649
|
+
throw new Error(
|
|
650
|
+
`${at}: mode 'batchWrite' cannot express an update \u2014 DynamoDB BatchWriteItem carries only PutRequest / DeleteRequest (internal invariant; the authoring layer should have rejected this).`
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
if (commandSpec.condition !== void 0) {
|
|
654
|
+
throw new Error(
|
|
655
|
+
`${at}: mode 'batchWrite' cannot carry a write condition \u2014 DynamoDB BatchWriteItem has no ConditionExpression (internal invariant; the authoring layer should have rejected this).`
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
const elementFields = {};
|
|
659
|
+
for (const name of Object.keys(commandSpec.params).sort()) {
|
|
660
|
+
elementFields[name] = commandSpec.params[name];
|
|
661
|
+
}
|
|
662
|
+
const params = {
|
|
663
|
+
items: { type: "array", required: true, element: elementFields }
|
|
664
|
+
};
|
|
665
|
+
const allParams = new Set(Object.keys(commandSpec.params));
|
|
666
|
+
const toElement = (record) => {
|
|
667
|
+
const out = {};
|
|
668
|
+
for (const field of Object.keys(record)) {
|
|
669
|
+
const v = record[field];
|
|
670
|
+
out[field] = typeof v === "string" ? toElementTemplate(v, allParams) : v;
|
|
671
|
+
}
|
|
672
|
+
return out;
|
|
673
|
+
};
|
|
674
|
+
const item = {
|
|
675
|
+
type: TX_ITEM_TYPE[commandSpec.type],
|
|
676
|
+
tableName: commandSpec.tableName,
|
|
677
|
+
entity: commandSpec.entity,
|
|
678
|
+
...commandSpec.item !== void 0 ? { item: toElement(commandSpec.item) } : {},
|
|
679
|
+
...commandSpec.keyCondition !== void 0 ? { keyCondition: toElement(commandSpec.keyCondition) } : {},
|
|
680
|
+
forEach: { source: "items" }
|
|
681
|
+
};
|
|
682
|
+
return { params, items: [item], batchWrite: true };
|
|
683
|
+
}
|
|
559
684
|
function composeFragmentTransaction(contractName, methodName, ops) {
|
|
560
685
|
const items = [];
|
|
561
686
|
const params = {};
|
|
@@ -923,6 +1048,15 @@ function modeTargetFor(mode, op, commandSpec, contractName, methodName, opName,
|
|
|
923
1048
|
if (mode === "parallel") {
|
|
924
1049
|
return { mode: "parallel", operation: opName };
|
|
925
1050
|
}
|
|
1051
|
+
if (mode === "batchWrite") {
|
|
1052
|
+
const txName2 = batchWriteTxName(contractName, methodName);
|
|
1053
|
+
transactionSpecs[txName2] = synthesizeBatchWriteTransaction(
|
|
1054
|
+
contractName,
|
|
1055
|
+
methodName,
|
|
1056
|
+
commandSpec
|
|
1057
|
+
);
|
|
1058
|
+
return { mode: "batchWrite", transaction: txName2 };
|
|
1059
|
+
}
|
|
926
1060
|
const txName = batchTxName(contractName, methodName);
|
|
927
1061
|
transactionSpecs[txName] = synthesizeBatchTransaction(commandSpec, op);
|
|
928
1062
|
return { mode: "transaction", transaction: txName };
|
|
@@ -1138,6 +1272,11 @@ function serializeCommandContract(contractName, contract, commandSpecs, transact
|
|
|
1138
1272
|
returnSelection
|
|
1139
1273
|
);
|
|
1140
1274
|
}
|
|
1275
|
+
if (method.mode === "batchWrite" && promotedToTransaction) {
|
|
1276
|
+
throw new Error(
|
|
1277
|
+
`Contract '${contractName}.${methodName}': mode 'batchWrite' on a method promoted to an atomic TransactWriteItems (derived effects / multi-fragment) \u2014 BatchWriteItem cannot compose those items (internal invariant).`
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1141
1280
|
const batch = method.mode !== void 0 && !promotedToTransaction ? modeTargetFor(
|
|
1142
1281
|
method.mode,
|
|
1143
1282
|
op,
|
|
@@ -1314,6 +1453,417 @@ ${rest.map((v) => ` - ${v.message}`).join("\n")})` : "";
|
|
|
1314
1453
|
throw new Error(first.message + suffix);
|
|
1315
1454
|
}
|
|
1316
1455
|
|
|
1456
|
+
// src/spec/components.ts
|
|
1457
|
+
import {
|
|
1458
|
+
buildComponentDefinition
|
|
1459
|
+
} from "behavior-contracts";
|
|
1460
|
+
function registerComponent(name, inputPorts, body, output, plan) {
|
|
1461
|
+
return buildComponentDefinition(
|
|
1462
|
+
{ name, inputPorts, body, output, plan },
|
|
1463
|
+
{ catalog: GRAPHDDB_CATALOG }
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
function remapRefRoots(node, resultRoot, itemRoot) {
|
|
1467
|
+
if (node === null || typeof node !== "object") return node;
|
|
1468
|
+
if (Array.isArray(node)) {
|
|
1469
|
+
return node.map((e) => remapRefRoots(e, resultRoot, itemRoot));
|
|
1470
|
+
}
|
|
1471
|
+
const obj = node;
|
|
1472
|
+
const keys = Object.keys(obj);
|
|
1473
|
+
if (keys.length === 1) {
|
|
1474
|
+
const key = keys[0];
|
|
1475
|
+
const arg = obj[key];
|
|
1476
|
+
if (key === "ref" || key === "refOpt") {
|
|
1477
|
+
const path = arg;
|
|
1478
|
+
const [root, ...rest] = path;
|
|
1479
|
+
const remapped = root === "input" ? rest : root === "result" ? [...resultRoot, ...rest] : root === "item" && itemRoot !== void 0 ? [...itemRoot, ...rest] : path;
|
|
1480
|
+
return { [key]: remapped };
|
|
1481
|
+
}
|
|
1482
|
+
if (key === "int" || key === "float") return node;
|
|
1483
|
+
if (key === "obj") {
|
|
1484
|
+
const out = {};
|
|
1485
|
+
for (const [k, v] of Object.entries(arg)) {
|
|
1486
|
+
out[k] = remapRefRoots(v, resultRoot, itemRoot);
|
|
1487
|
+
}
|
|
1488
|
+
return { obj: out };
|
|
1489
|
+
}
|
|
1490
|
+
if (Array.isArray(arg)) {
|
|
1491
|
+
return {
|
|
1492
|
+
[key]: arg.map((e) => remapRefRoots(e, resultRoot, itemRoot))
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
return {
|
|
1496
|
+
[key]: remapRefRoots(arg, resultRoot, itemRoot)
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
return node;
|
|
1500
|
+
}
|
|
1501
|
+
function stableJson(value) {
|
|
1502
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
1503
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
1504
|
+
const keys = Object.keys(value).sort();
|
|
1505
|
+
const body = keys.map((k) => `${JSON.stringify(k)}:${stableJson(value[k])}`).join(",");
|
|
1506
|
+
return `{${body}}`;
|
|
1507
|
+
}
|
|
1508
|
+
function collectParamLeaves(tree, out) {
|
|
1509
|
+
if (tree === null || typeof tree !== "object") return;
|
|
1510
|
+
if (Array.isArray(tree)) {
|
|
1511
|
+
for (const e of tree) collectParamLeaves(e, out);
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
const rec = tree;
|
|
1515
|
+
if (typeof rec.$param === "string" && Object.keys(rec).length === 1) {
|
|
1516
|
+
out.add(rec.$param);
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
for (const v of Object.values(rec)) collectParamLeaves(v, out);
|
|
1520
|
+
}
|
|
1521
|
+
function pathToId(resultPath) {
|
|
1522
|
+
if (resultPath === "$") return "root";
|
|
1523
|
+
return resultPath.startsWith("$.") ? resultPath.slice(2) : resultPath;
|
|
1524
|
+
}
|
|
1525
|
+
function parentPath(op) {
|
|
1526
|
+
const tokens = op.resultPath.split(".");
|
|
1527
|
+
const drop = op.resultPath.endsWith(".items") ? 2 : 1;
|
|
1528
|
+
const p = tokens.slice(0, tokens.length - drop).join(".");
|
|
1529
|
+
return p === "" ? "$" : p;
|
|
1530
|
+
}
|
|
1531
|
+
function relationProp(op) {
|
|
1532
|
+
const tokens = op.resultPath.split(".");
|
|
1533
|
+
const idx = op.resultPath.endsWith(".items") ? tokens.length - 2 : tokens.length - 1;
|
|
1534
|
+
return tokens[idx];
|
|
1535
|
+
}
|
|
1536
|
+
function relationKindOf(op) {
|
|
1537
|
+
return op.resultPath.endsWith(".items") ? "connection" : "single";
|
|
1538
|
+
}
|
|
1539
|
+
function readPorts(op, resultRoot) {
|
|
1540
|
+
const ports = { table: op.tableName };
|
|
1541
|
+
if (op.indexName !== void 0) ports.indexName = op.indexName;
|
|
1542
|
+
for (const [attr, node] of Object.entries(op.keyExpr ?? {})) {
|
|
1543
|
+
ports[attr] = remapRefRoots(node, resultRoot);
|
|
1544
|
+
}
|
|
1545
|
+
if (op.rangeCondition !== void 0) {
|
|
1546
|
+
ports.rangeKey = op.rangeCondition.key;
|
|
1547
|
+
ports.rangeOperator = op.rangeCondition.operator;
|
|
1548
|
+
ports.rangeValue = remapRefRoots(
|
|
1549
|
+
op.rangeCondition.valueExpr ?? "",
|
|
1550
|
+
resultRoot
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
if (op.limit !== void 0) ports.limit = op.limit;
|
|
1554
|
+
if (op.filter !== void 0) ports.filter = stableJson(op.filter.declarative);
|
|
1555
|
+
ports.projection = { arr: [...op.projection] };
|
|
1556
|
+
return ports;
|
|
1557
|
+
}
|
|
1558
|
+
function buildQueryComponent(name, query) {
|
|
1559
|
+
const ops = query.operations;
|
|
1560
|
+
const idByPath = /* @__PURE__ */ new Map();
|
|
1561
|
+
const opByPath = /* @__PURE__ */ new Map();
|
|
1562
|
+
for (const op of ops) {
|
|
1563
|
+
idByPath.set(op.resultPath, pathToId(op.resultPath));
|
|
1564
|
+
opByPath.set(op.resultPath, op);
|
|
1565
|
+
}
|
|
1566
|
+
const lineages = /* @__PURE__ */ new Map();
|
|
1567
|
+
const elementInfo = /* @__PURE__ */ new Map();
|
|
1568
|
+
const body = ops.map((op) => {
|
|
1569
|
+
const id = pathToId(op.resultPath);
|
|
1570
|
+
if (op.resultPath === "$") {
|
|
1571
|
+
const ports = readPorts(op, []);
|
|
1572
|
+
if (query.cardinality === "one" && op.type === "Query") ports.cardinality = "one";
|
|
1573
|
+
const node2 = { id, component: op.type, ports };
|
|
1574
|
+
return node2;
|
|
1575
|
+
}
|
|
1576
|
+
const reject = (kind) => {
|
|
1577
|
+
throw new Error(`buildQueryComponent('${name}'): op '${op.resultPath}': ${kind}`);
|
|
1578
|
+
};
|
|
1579
|
+
const pp = parentPath(op);
|
|
1580
|
+
const producerId = idByPath.get(pp) ?? reject(`no producer op at '${pp}'`);
|
|
1581
|
+
if (op.sourceList !== void 0) {
|
|
1582
|
+
if (pp !== "$") reject("nested sourceList not expressible (v2 map)");
|
|
1583
|
+
const as = op.sourceList.as ?? `$${op.sourceList.key}`;
|
|
1584
|
+
const over = remapRefRoots(
|
|
1585
|
+
op.sourceList.over ?? { ref: ["result", op.sourceList.from] },
|
|
1586
|
+
[producerId]
|
|
1587
|
+
);
|
|
1588
|
+
const ports = readPorts(op, [as]);
|
|
1589
|
+
if (op.sourceList.implicit === true) ports.implicitSourceField = op.sourceList.from;
|
|
1590
|
+
const node2 = {
|
|
1591
|
+
id,
|
|
1592
|
+
map: {
|
|
1593
|
+
over,
|
|
1594
|
+
as,
|
|
1595
|
+
component: op.type,
|
|
1596
|
+
ports,
|
|
1597
|
+
parent: producerId,
|
|
1598
|
+
relationKind: relationKindOf(op),
|
|
1599
|
+
batched: true
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
lineages.set(op.resultPath, { tailExpr: { ref: [id] }, tailId: id });
|
|
1603
|
+
return node2;
|
|
1604
|
+
}
|
|
1605
|
+
const producerOp = opByPath.get(pp);
|
|
1606
|
+
const producerIsConnection = producerOp !== void 0 && pp.endsWith(".items");
|
|
1607
|
+
const producerElement = elementInfo.get(pp);
|
|
1608
|
+
if (producerIsConnection || producerElement !== void 0) {
|
|
1609
|
+
const lineageKey = producerElement?.lineageKey ?? pp;
|
|
1610
|
+
const lineage = producerElement !== void 0 && pp.endsWith(".items") ? reject("nested per-element connection not expressible (v2 map)") : lineages.get(lineageKey) ?? reject(`no element chain for '${lineageKey}'`);
|
|
1611
|
+
const as = "$el";
|
|
1612
|
+
const attach = producerElement?.attachSeg ?? [];
|
|
1613
|
+
const into = relationProp(op);
|
|
1614
|
+
const node2 = {
|
|
1615
|
+
id,
|
|
1616
|
+
map: {
|
|
1617
|
+
over: lineage.tailExpr,
|
|
1618
|
+
as,
|
|
1619
|
+
component: op.type,
|
|
1620
|
+
ports: readPorts(op, [as, ...attach]),
|
|
1621
|
+
parent: lineage.tailId,
|
|
1622
|
+
relationKind: relationKindOf(op),
|
|
1623
|
+
into,
|
|
1624
|
+
// A belongsTo child the old path resolves via ONE deduped BatchGetItem
|
|
1625
|
+
// (gap 6) is batched; a per-element Query stays per-element.
|
|
1626
|
+
...op.type === "BatchGetItem" ? { batched: true } : {}
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
lineages.set(lineageKey, { tailExpr: { ref: [id] }, tailId: id });
|
|
1630
|
+
elementInfo.set(op.resultPath, { lineageKey, attachSeg: [into] });
|
|
1631
|
+
return node2;
|
|
1632
|
+
}
|
|
1633
|
+
const node = {
|
|
1634
|
+
id,
|
|
1635
|
+
component: op.type,
|
|
1636
|
+
ports: readPorts(op, [producerId]),
|
|
1637
|
+
parent: producerId,
|
|
1638
|
+
...op.sourceField !== void 0 ? { bindField: op.sourceField } : {},
|
|
1639
|
+
relationKind: relationKindOf(op)
|
|
1640
|
+
};
|
|
1641
|
+
if (relationKindOf(op) === "connection") {
|
|
1642
|
+
lineages.set(op.resultPath, { tailExpr: { ref: [id, "items"] }, tailId: id });
|
|
1643
|
+
}
|
|
1644
|
+
return node;
|
|
1645
|
+
});
|
|
1646
|
+
const relOps = ops.filter((op) => op.resultPath !== "$");
|
|
1647
|
+
let output;
|
|
1648
|
+
if (relOps.length === 0) {
|
|
1649
|
+
output = { ref: ["root"] };
|
|
1650
|
+
} else {
|
|
1651
|
+
const outputObj = { root: { ref: ["root"] } };
|
|
1652
|
+
for (const op of relOps) {
|
|
1653
|
+
const id = pathToId(op.resultPath);
|
|
1654
|
+
if (elementInfo.has(op.resultPath)) continue;
|
|
1655
|
+
const lineage = lineages.get(op.resultPath);
|
|
1656
|
+
const key = parentPath(op) === "$" ? relationProp(op) : id;
|
|
1657
|
+
if (lineage !== void 0 && lineage.tailId !== id && relationKindOf(op) === "connection" && op.sourceList === void 0) {
|
|
1658
|
+
outputObj[key] = {
|
|
1659
|
+
obj: { cursor: { ref: [id, "cursor"] }, items: lineage.tailExpr }
|
|
1660
|
+
};
|
|
1661
|
+
} else {
|
|
1662
|
+
outputObj[key] = { ref: [id] };
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
output = { obj: outputObj };
|
|
1666
|
+
}
|
|
1667
|
+
let plan;
|
|
1668
|
+
if (query.executionPlan) {
|
|
1669
|
+
const idToIndex = new Map(body.map((n, i) => [n.id, i]));
|
|
1670
|
+
const stageOf = [];
|
|
1671
|
+
const groups = [];
|
|
1672
|
+
body.forEach((n, i) => {
|
|
1673
|
+
const pid = "map" in n ? n.map.parent : "cond" in n ? n.cond.parent : n.parent;
|
|
1674
|
+
const stage = pid === void 0 ? 0 : stageOf[idToIndex.get(pid)] + 1;
|
|
1675
|
+
stageOf[i] = stage;
|
|
1676
|
+
(groups[stage] ??= []).push(i);
|
|
1677
|
+
});
|
|
1678
|
+
plan = { groups, concurrency: query.executionPlan.concurrency };
|
|
1679
|
+
}
|
|
1680
|
+
return registerComponent(name, portSchemas(query.params), body, output, plan ?? null);
|
|
1681
|
+
}
|
|
1682
|
+
function lowerTransactionTemplate(template) {
|
|
1683
|
+
const PLACEHOLDER2 = /\{([^{}]+)\}/g;
|
|
1684
|
+
const parts = [];
|
|
1685
|
+
let last = 0;
|
|
1686
|
+
PLACEHOLDER2.lastIndex = 0;
|
|
1687
|
+
for (let m = PLACEHOLDER2.exec(template); m !== null; m = PLACEHOLDER2.exec(template)) {
|
|
1688
|
+
const literal = template.slice(last, m.index);
|
|
1689
|
+
if (literal !== "") parts.push(literal);
|
|
1690
|
+
const token = m[1];
|
|
1691
|
+
parts.push(
|
|
1692
|
+
token.startsWith("item.") ? { ref: ["$item", token.slice("item.".length)] } : { ref: [token] }
|
|
1693
|
+
);
|
|
1694
|
+
last = m.index + m[0].length;
|
|
1695
|
+
}
|
|
1696
|
+
const tail = template.slice(last);
|
|
1697
|
+
if (tail !== "") parts.push(tail);
|
|
1698
|
+
if (parts.length === 0) return "";
|
|
1699
|
+
if (parts.length === 1) return parts[0];
|
|
1700
|
+
return { concat: parts };
|
|
1701
|
+
}
|
|
1702
|
+
function lowerTransactionValue(value) {
|
|
1703
|
+
return typeof value === "string" ? lowerTransactionTemplate(value) : value;
|
|
1704
|
+
}
|
|
1705
|
+
function conditionPorts(condition, ports) {
|
|
1706
|
+
ports["condition.kind"] = condition.kind;
|
|
1707
|
+
switch (condition.kind) {
|
|
1708
|
+
case "notExists":
|
|
1709
|
+
return;
|
|
1710
|
+
case "attributeExists":
|
|
1711
|
+
case "attributeNotExists":
|
|
1712
|
+
ports["condition.field"] = condition.field;
|
|
1713
|
+
return;
|
|
1714
|
+
case "equals": {
|
|
1715
|
+
for (const field of Object.keys(condition.fields).sort()) {
|
|
1716
|
+
ports[`condition.equals.${field}`] = lowerTransactionTemplate(condition.fields[field]);
|
|
1717
|
+
}
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
case "expr": {
|
|
1721
|
+
ports["condition.declarative"] = stableJson(condition.declarative);
|
|
1722
|
+
const params = /* @__PURE__ */ new Set();
|
|
1723
|
+
collectParamLeaves(condition.declarative, params);
|
|
1724
|
+
for (const p of [...params].sort()) {
|
|
1725
|
+
ports[`condition.params.${p}`] = { ref: [p] };
|
|
1726
|
+
}
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
case "raw": {
|
|
1730
|
+
ports["condition.expression"] = condition.expression;
|
|
1731
|
+
ports["condition.names"] = stableJson(condition.names);
|
|
1732
|
+
for (const slot of Object.keys(condition.values).sort()) {
|
|
1733
|
+
const v = condition.values[slot];
|
|
1734
|
+
const leaf = v !== null && typeof v === "object" && typeof v.$param === "string" ? { ref: [v.$param] } : v;
|
|
1735
|
+
ports[`condition.values.${slot}`] = leaf;
|
|
1736
|
+
}
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
case "scpExpr":
|
|
1740
|
+
ports["condition.expression"] = stableJson(condition.expression);
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
function transactionItemPorts(item) {
|
|
1745
|
+
const ports = { table: item.tableName, entity: item.entity };
|
|
1746
|
+
const record = (src, group) => {
|
|
1747
|
+
if (src === void 0) return;
|
|
1748
|
+
for (const [field, value] of Object.entries(src)) {
|
|
1749
|
+
ports[`${group}.${field}`] = lowerTransactionValue(value);
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
record(item.item, "item");
|
|
1753
|
+
record(item.keyCondition, "key");
|
|
1754
|
+
record(item.changes, "changes");
|
|
1755
|
+
record(item.add, "add");
|
|
1756
|
+
if (item.literalKey === true) ports.literalKey = true;
|
|
1757
|
+
if (item.condition !== void 0) conditionPorts(item.condition, ports);
|
|
1758
|
+
if (item.maintain !== void 0) ports.maintain = stableJson(item.maintain);
|
|
1759
|
+
return ports;
|
|
1760
|
+
}
|
|
1761
|
+
function lowerItemGuard(item) {
|
|
1762
|
+
if (item.guard !== void 0) {
|
|
1763
|
+
return remapRefRoots(item.guard.expr, [], ["$item"]);
|
|
1764
|
+
}
|
|
1765
|
+
const when = item.when;
|
|
1766
|
+
if (when === void 0) return void 0;
|
|
1767
|
+
const args = [lowerTransactionTemplate(when.left), lowerTransactionTemplate(when.right)];
|
|
1768
|
+
return when.op === "eq" ? { eq: args } : { ne: args };
|
|
1769
|
+
}
|
|
1770
|
+
function buildTransactionComponent(name, tx) {
|
|
1771
|
+
const catalog = {
|
|
1772
|
+
Put: "PutItem",
|
|
1773
|
+
Update: "UpdateItem",
|
|
1774
|
+
Delete: "DeleteItem",
|
|
1775
|
+
ConditionCheck: "ConditionCheck"
|
|
1776
|
+
};
|
|
1777
|
+
const body = tx.items.map((item, i) => {
|
|
1778
|
+
const id = `item${i}`;
|
|
1779
|
+
const component = catalog[item.type];
|
|
1780
|
+
const when = lowerItemGuard(item);
|
|
1781
|
+
if (tx.batchWrite === true) {
|
|
1782
|
+
const reject = (kind) => {
|
|
1783
|
+
throw new Error(
|
|
1784
|
+
`buildTransactionComponent('${name}'): batchWrite item${i}: ${kind} \u2014 DynamoDB BatchWriteItem carries only unconditional PutRequest / DeleteRequest (internal invariant; the emitter synthesizes this form).`
|
|
1785
|
+
);
|
|
1786
|
+
};
|
|
1787
|
+
if (component !== "PutItem" && component !== "DeleteItem") reject(`'${component}' item`);
|
|
1788
|
+
if (item.condition !== void 0) reject("a write condition");
|
|
1789
|
+
if (when !== void 0) reject("a when/guard");
|
|
1790
|
+
if (item.forEach === void 0) reject("no forEach (the bulk form is forEach-over-items)");
|
|
1791
|
+
const node2 = {
|
|
1792
|
+
id,
|
|
1793
|
+
map: {
|
|
1794
|
+
over: { ref: [item.forEach.source] },
|
|
1795
|
+
as: "$item",
|
|
1796
|
+
component,
|
|
1797
|
+
ports: transactionItemPorts(item),
|
|
1798
|
+
batched: true
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
return node2;
|
|
1802
|
+
}
|
|
1803
|
+
if (item.forEach !== void 0) {
|
|
1804
|
+
const node2 = {
|
|
1805
|
+
id,
|
|
1806
|
+
map: {
|
|
1807
|
+
over: { ref: [item.forEach.source] },
|
|
1808
|
+
as: "$item",
|
|
1809
|
+
component,
|
|
1810
|
+
ports: transactionItemPorts(item),
|
|
1811
|
+
...when !== void 0 ? { when } : {}
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
return node2;
|
|
1815
|
+
}
|
|
1816
|
+
if (when !== void 0) {
|
|
1817
|
+
const node2 = {
|
|
1818
|
+
id,
|
|
1819
|
+
map: {
|
|
1820
|
+
over: { arr: [true] },
|
|
1821
|
+
as: "$item",
|
|
1822
|
+
component,
|
|
1823
|
+
ports: transactionItemPorts(item),
|
|
1824
|
+
when
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
return node2;
|
|
1828
|
+
}
|
|
1829
|
+
const node = { id, component, ports: transactionItemPorts(item) };
|
|
1830
|
+
return node;
|
|
1831
|
+
});
|
|
1832
|
+
return registerComponent(
|
|
1833
|
+
name,
|
|
1834
|
+
portSchemas(tx.params),
|
|
1835
|
+
body,
|
|
1836
|
+
{ arr: body.map((n) => ({ ref: [n.id] })) },
|
|
1837
|
+
null
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
function portSchemas(params) {
|
|
1841
|
+
const out = {};
|
|
1842
|
+
for (const name of Object.keys(params).sort()) {
|
|
1843
|
+
const p = params[name];
|
|
1844
|
+
out[name] = { type: p.type, required: p.required };
|
|
1845
|
+
}
|
|
1846
|
+
return out;
|
|
1847
|
+
}
|
|
1848
|
+
function buildComponents(queries, transactions) {
|
|
1849
|
+
const components = [];
|
|
1850
|
+
for (const name of Object.keys(queries).sort()) {
|
|
1851
|
+
components.push(buildQueryComponent(name, queries[name]));
|
|
1852
|
+
}
|
|
1853
|
+
for (const name of Object.keys(transactions).sort()) {
|
|
1854
|
+
components.push(buildTransactionComponent(name, transactions[name]));
|
|
1855
|
+
}
|
|
1856
|
+
assertComponentsInCatalog(components);
|
|
1857
|
+
return components;
|
|
1858
|
+
}
|
|
1859
|
+
function portableIrDocument(components) {
|
|
1860
|
+
return {
|
|
1861
|
+
irVersion: 1,
|
|
1862
|
+
exprVersion: EXPR_VERSION,
|
|
1863
|
+
components: [...components]
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1317
1867
|
// src/spec/operations.ts
|
|
1318
1868
|
function paramSpecs(params) {
|
|
1319
1869
|
const out = {};
|
|
@@ -1415,7 +1965,20 @@ function buildReadOperations(def) {
|
|
|
1415
1965
|
};
|
|
1416
1966
|
const ops = [root];
|
|
1417
1967
|
ops.push(...buildRelationOperations(metadata, select, "$"));
|
|
1418
|
-
return injectRefsSourceProjections(ops);
|
|
1968
|
+
return injectRefsSourceProjections(ops).map(lowerOperationKeyExprs);
|
|
1969
|
+
}
|
|
1970
|
+
function lowerOperationKeyExprs(op) {
|
|
1971
|
+
const keyExpr = op.keyCondition ? lowerKeyCondition(op.keyCondition) : {};
|
|
1972
|
+
const { keyCondition: _dropKeyCondition, rangeCondition, ...rest } = op;
|
|
1973
|
+
const nextRange = rangeCondition ? (() => {
|
|
1974
|
+
const { value: _dropValue, ...rc } = rangeCondition;
|
|
1975
|
+
return { ...rc, valueExpr: lowerKeyTemplate(rangeCondition.value ?? "") };
|
|
1976
|
+
})() : void 0;
|
|
1977
|
+
return {
|
|
1978
|
+
...rest,
|
|
1979
|
+
keyExpr,
|
|
1980
|
+
...nextRange ? { rangeCondition: nextRange } : {}
|
|
1981
|
+
};
|
|
1419
1982
|
}
|
|
1420
1983
|
function projectionFields(select) {
|
|
1421
1984
|
const out = [];
|
|
@@ -1424,7 +1987,7 @@ function projectionFields(select) {
|
|
|
1424
1987
|
}
|
|
1425
1988
|
return out.sort();
|
|
1426
1989
|
}
|
|
1427
|
-
function buildRelationOperations(metadata, select,
|
|
1990
|
+
function buildRelationOperations(metadata, select, parentPath2) {
|
|
1428
1991
|
const ops = [];
|
|
1429
1992
|
const relations = detectRelationFields(select, metadata);
|
|
1430
1993
|
for (const rel of [...relations].sort(
|
|
@@ -1434,7 +1997,7 @@ function buildRelationOperations(metadata, select, parentPath) {
|
|
|
1434
1997
|
const childSelect = spec.select ?? {};
|
|
1435
1998
|
const targetClass = rel.targetFactory();
|
|
1436
1999
|
const targetMeta = MetadataRegistry.get(targetClass);
|
|
1437
|
-
const childPath = `${
|
|
2000
|
+
const childPath = `${parentPath2}.${rel.propertyName}`;
|
|
1438
2001
|
if (rel.type === "refs") {
|
|
1439
2002
|
if (spec.filter !== void 0) {
|
|
1440
2003
|
throw new Error(
|
|
@@ -1587,7 +2150,16 @@ function buildRefsOperation(rel, targetMeta, select, resultPath) {
|
|
|
1587
2150
|
projection: projectionFields(select),
|
|
1588
2151
|
resultPath,
|
|
1589
2152
|
sourceField,
|
|
1590
|
-
|
|
2153
|
+
// #289 P2-5: the sourceList carries its Phase-3 Map shape prep — `over` is
|
|
2154
|
+
// the Expression-IR ref to the parent list attribute the Map iterates, `as`
|
|
2155
|
+
// is the per-element binding name. Descriptive only in Phase 2 (behavior is
|
|
2156
|
+
// still driven by `from`/`key`); Phase 3 lowers the fan-out body onto them.
|
|
2157
|
+
sourceList: {
|
|
2158
|
+
from: refs.from,
|
|
2159
|
+
key: refs.key,
|
|
2160
|
+
over: { ref: ["result", refs.from] },
|
|
2161
|
+
as: `$${refs.key}`
|
|
2162
|
+
}
|
|
1591
2163
|
};
|
|
1592
2164
|
}
|
|
1593
2165
|
function injectRefsSourceProjections(ops) {
|
|
@@ -1595,11 +2167,11 @@ function injectRefsSourceProjections(ops) {
|
|
|
1595
2167
|
const op = ops[i];
|
|
1596
2168
|
if (op.sourceList === void 0) continue;
|
|
1597
2169
|
const tokens = op.resultPath.split(".");
|
|
1598
|
-
const
|
|
1599
|
-
const parentIndex = ops.findIndex((p) => p.resultPath ===
|
|
2170
|
+
const parentPath2 = tokens.slice(0, -2).join(".") || "$";
|
|
2171
|
+
const parentIndex = ops.findIndex((p) => p.resultPath === parentPath2);
|
|
1600
2172
|
if (parentIndex === -1) {
|
|
1601
2173
|
throw new Error(
|
|
1602
|
-
`refs fan-out at '${op.resultPath}': no parent operation at '${
|
|
2174
|
+
`refs fan-out at '${op.resultPath}': no parent operation at '${parentPath2}'.`
|
|
1603
2175
|
);
|
|
1604
2176
|
}
|
|
1605
2177
|
const parent = ops[parentIndex];
|
|
@@ -1728,6 +2300,27 @@ function buildQuerySpec(def) {
|
|
|
1728
2300
|
...def.description !== void 0 ? { description: def.description } : {}
|
|
1729
2301
|
};
|
|
1730
2302
|
}
|
|
2303
|
+
function toPortableQuery(name, spec) {
|
|
2304
|
+
const root = spec.operations[0];
|
|
2305
|
+
if (root === void 0 || root.resultPath !== "$") {
|
|
2306
|
+
throw new Error(
|
|
2307
|
+
`toPortableQuery('${name}'): expected the root op at operations[0] (resultPath '$'); got ${root === void 0 ? "no operations" : `'${root.resultPath}'`}.`
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
const { resultPath: _rp, sourceField: _sf, sourceList: _sl, ...portableRoot } = root;
|
|
2311
|
+
return {
|
|
2312
|
+
params: spec.params,
|
|
2313
|
+
operations: [portableRoot],
|
|
2314
|
+
...spec.cardinality !== void 0 ? { cardinality: spec.cardinality } : {},
|
|
2315
|
+
...spec.description !== void 0 ? { description: spec.description } : {}
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
function toPortableTransaction(spec) {
|
|
2319
|
+
return {
|
|
2320
|
+
params: spec.params,
|
|
2321
|
+
...spec.batchWrite === true ? { batchWrite: true } : {}
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
1731
2324
|
function mergeSpecs(base, synthesized, kind) {
|
|
1732
2325
|
for (const name of Object.keys(synthesized)) {
|
|
1733
2326
|
if (Object.prototype.hasOwnProperty.call(base, name)) {
|
|
@@ -1759,16 +2352,55 @@ function buildOperations(queries = {}, commands = {}, transactions = {}, contrac
|
|
|
1759
2352
|
mergeSpecs(transactionSpecs, built.transactions, "transaction");
|
|
1760
2353
|
const contractSpecs = built.contracts;
|
|
1761
2354
|
const contextSpecs = buildContexts(contextInputMap);
|
|
2355
|
+
const components = buildComponents(querySpecs, transactionSpecs);
|
|
2356
|
+
const behaviorNames = /* @__PURE__ */ new Set();
|
|
2357
|
+
{
|
|
2358
|
+
const behaviorInputs = contractInputs.behaviors ?? {};
|
|
2359
|
+
const taken = new Set(components.map((c) => c.name));
|
|
2360
|
+
for (const key of Object.keys(behaviorInputs).sort()) {
|
|
2361
|
+
for (const c of behaviorInputs[key].components) {
|
|
2362
|
+
const name = `${key}__${c.name}`;
|
|
2363
|
+
if (taken.has(name)) {
|
|
2364
|
+
throw new Error(
|
|
2365
|
+
`publishBehaviors registration '${key}' produced component '${name}', which collides with another emitted component. Rename the behavior registration or the method.`
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
taken.add(name);
|
|
2369
|
+
behaviorNames.add(name);
|
|
2370
|
+
components.push({ ...c, name });
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
if (behaviorNames.size > 0) assertComponentsInCatalog(components);
|
|
2374
|
+
}
|
|
2375
|
+
assertPublishSplitMatchesDerived(
|
|
2376
|
+
deriveComponentEffects(components),
|
|
2377
|
+
{
|
|
2378
|
+
queries: new Set(Object.keys(querySpecs)),
|
|
2379
|
+
transactions: new Set(Object.keys(transactionSpecs)),
|
|
2380
|
+
commands: new Set(Object.keys(commandSpecs)),
|
|
2381
|
+
behaviors: behaviorNames
|
|
2382
|
+
},
|
|
2383
|
+
contractSpecs
|
|
2384
|
+
);
|
|
2385
|
+
const portableQueries = {};
|
|
2386
|
+
for (const [name, spec] of Object.entries(querySpecs)) {
|
|
2387
|
+
portableQueries[name] = toPortableQuery(name, spec);
|
|
2388
|
+
}
|
|
2389
|
+
const portableTransactions = {};
|
|
2390
|
+
for (const [name, spec] of Object.entries(transactionSpecs)) {
|
|
2391
|
+
portableTransactions[name] = toPortableTransaction(spec);
|
|
2392
|
+
}
|
|
1762
2393
|
const content = {
|
|
1763
|
-
queries:
|
|
2394
|
+
queries: portableQueries,
|
|
1764
2395
|
commands: commandSpecs,
|
|
1765
|
-
...Object.keys(
|
|
2396
|
+
...Object.keys(portableTransactions).length > 0 ? { transactions: portableTransactions } : {},
|
|
1766
2397
|
...Object.keys(contractSpecs).length > 0 ? { contracts: contractSpecs } : {},
|
|
1767
2398
|
...Object.keys(contextSpecs).length > 0 ? { contexts: contextSpecs } : {}
|
|
1768
2399
|
};
|
|
1769
2400
|
return {
|
|
1770
2401
|
version: operationsSpecVersion(content),
|
|
1771
|
-
...content
|
|
2402
|
+
...content,
|
|
2403
|
+
...components.length > 0 ? { components } : {}
|
|
1772
2404
|
};
|
|
1773
2405
|
}
|
|
1774
2406
|
|
|
@@ -1957,7 +2589,9 @@ function buildPreparedPlanDocument(plans) {
|
|
|
1957
2589
|
for (const id of Object.keys(plans).sort()) sorted[id] = plans[id];
|
|
1958
2590
|
return {
|
|
1959
2591
|
formatVersion: PREPARED_FORMAT_VERSION,
|
|
1960
|
-
|
|
2592
|
+
// #289 Phase 2: the embedded operations IR is 2.0 (major bump); the prepared
|
|
2593
|
+
// artifact's specVersion tracks it so a version-skewed plan fails closed.
|
|
2594
|
+
specVersion: SPEC_VERSION_KEY_EXPR,
|
|
1961
2595
|
plans: sorted
|
|
1962
2596
|
};
|
|
1963
2597
|
}
|
|
@@ -1973,12 +2607,17 @@ function buildBridgeBundle(queries = {}, commands = {}, registry = MetadataRegis
|
|
|
1973
2607
|
}
|
|
1974
2608
|
|
|
1975
2609
|
export {
|
|
2610
|
+
renderKeyExprToTemplate,
|
|
1976
2611
|
collectContractN1Violations,
|
|
1977
2612
|
assertContractN1Safe,
|
|
1978
2613
|
buildContracts,
|
|
1979
2614
|
buildContexts,
|
|
1980
2615
|
collectContractBoundaryViolations,
|
|
1981
2616
|
assertContractBoundaries,
|
|
2617
|
+
buildQueryComponent,
|
|
2618
|
+
buildTransactionComponent,
|
|
2619
|
+
buildComponents,
|
|
2620
|
+
portableIrDocument,
|
|
1982
2621
|
buildQuerySpec,
|
|
1983
2622
|
buildOperations,
|
|
1984
2623
|
compilePreparedPlan,
|