graphddb 0.8.0 → 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-LGHSZIEE.js → chunk-NWTEUWJD.js} +7 -5
- package/dist/{chunk-N4NWYNGZ.js → chunk-SSVG6NQK.js} +657 -18
- package/dist/{chunk-L4QRCHRQ.js → chunk-WOFRHRXY.js} +6 -25
- package/dist/{chunk-I4LEJ4TF.js → chunk-XZTA4VJH.js} +44 -9
- 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-DR7_lpyk.d.ts → key-Dne-a20Q.d.ts} +204 -7
- package/dist/linter/index.d.ts +6 -5
- package/dist/{linter-C-vypgut.d.ts → linter-Cr1Hnw7n.d.ts} +1 -1
- package/dist/{prepared-artifact-BpPgkXEo.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-BXLzIcQD.d.ts → types-BnFsTZwl.d.ts} +1 -1
- package/dist/{types-2PMXEn5x.d.ts → types-CfxzTEFL.d.ts} +262 -20
- package/docs/cqrs-contract.md +55 -4
- package/docs/python-bridge.md +33 -49
- package/package.json +11 -3
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// src/spec/types.ts
|
|
2
2
|
var SPEC_VERSION = "1.1";
|
|
3
3
|
var SPEC_VERSION_SCP = "1.2";
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var SPEC_VERSION_KEY_EXPR = "2.0";
|
|
5
|
+
var SPEC_VERSION_SUPPORTED = SPEC_VERSION_KEY_EXPR;
|
|
6
|
+
var EXPR_VERSION = 2;
|
|
6
7
|
var MARKER_ROW_ENTITY = "__marker__";
|
|
7
8
|
|
|
8
9
|
// src/define/transaction.ts
|
|
@@ -236,33 +237,14 @@ function canonicalizeExpressionSpec(input, context) {
|
|
|
236
237
|
function isScpExprConditionInput(condition) {
|
|
237
238
|
return "scpExpr" in condition;
|
|
238
239
|
}
|
|
239
|
-
function
|
|
240
|
-
return
|
|
241
|
-
}
|
|
242
|
-
function operationsContainScpNodes(doc) {
|
|
243
|
-
for (const command of Object.values(doc.commands)) {
|
|
244
|
-
if (conditionHasScp(command.condition)) return true;
|
|
245
|
-
}
|
|
246
|
-
for (const transaction of Object.values(doc.transactions ?? {})) {
|
|
247
|
-
for (const item of transaction.items) {
|
|
248
|
-
if (item.guard !== void 0 || conditionHasScp(item.condition)) return true;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
for (const contract of Object.values(doc.contracts ?? {})) {
|
|
252
|
-
if (contract.kind !== "command") continue;
|
|
253
|
-
for (const method of Object.values(contract.methods)) {
|
|
254
|
-
if (method.guard !== void 0) return true;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
function operationsSpecVersion(doc) {
|
|
260
|
-
return operationsContainScpNodes(doc) ? SPEC_VERSION_SCP : SPEC_VERSION;
|
|
240
|
+
function operationsSpecVersion(_doc) {
|
|
241
|
+
return SPEC_VERSION_KEY_EXPR;
|
|
261
242
|
}
|
|
262
243
|
|
|
263
244
|
export {
|
|
264
245
|
SPEC_VERSION,
|
|
265
246
|
SPEC_VERSION_SCP,
|
|
247
|
+
SPEC_VERSION_KEY_EXPR,
|
|
266
248
|
SPEC_VERSION_SUPPORTED,
|
|
267
249
|
EXPR_VERSION,
|
|
268
250
|
MARKER_ROW_ENTITY,
|
|
@@ -273,6 +255,5 @@ export {
|
|
|
273
255
|
SCP_LOWERED_MARKER,
|
|
274
256
|
canonicalizeExpressionSpec,
|
|
275
257
|
isScpExprConditionInput,
|
|
276
|
-
operationsContainScpNodes,
|
|
277
258
|
operationsSpecVersion
|
|
278
259
|
};
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
normalizeTopLevelSelect,
|
|
18
18
|
parseChange,
|
|
19
19
|
validateInlineSnapshotSelect
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-KOIJ4SNO.js";
|
|
21
21
|
import {
|
|
22
22
|
EXPR_VERSION,
|
|
23
23
|
MARKER_ROW_ENTITY,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
isScpExprConditionInput,
|
|
27
27
|
isTransactionDefinition,
|
|
28
28
|
isTransactionRef
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-WOFRHRXY.js";
|
|
30
30
|
import {
|
|
31
31
|
BATCH_GET_MAX_KEYS,
|
|
32
32
|
CONDITION_OPERATOR_KEYS,
|
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
serializeFieldValue,
|
|
62
62
|
serializeRawCondition,
|
|
63
63
|
skTemplate
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-ZNU7OI5I.js";
|
|
65
65
|
import {
|
|
66
66
|
TableMapping,
|
|
67
67
|
createColumnMap,
|
|
@@ -4669,16 +4669,40 @@ function mutate(body, options) {
|
|
|
4669
4669
|
if (isCommandPlan(body)) {
|
|
4670
4670
|
return mode !== void 0 ? { command: body, mode } : { command: body };
|
|
4671
4671
|
}
|
|
4672
|
+
if (isPublicCompositeDescriptor(body)) {
|
|
4673
|
+
return mode !== void 0 ? { ...body, mode } : body;
|
|
4674
|
+
}
|
|
4672
4675
|
throw new Error(
|
|
4673
|
-
"mutate: the body must be a write body \u2014 a `defineScpTransaction(...)` / `defineTransaction(...)` transaction definition or a `mutation(...)` plan. `mutate` is the authoring twin of the runtime `mutate`; its only option is `{ mode }` (the sole home of atomicity)."
|
|
4676
|
+
"mutate: the body must be a write body \u2014 a PUBLIC composite descriptor (`{ writes: $ => ({ alias: descriptor, \u2026 }), input?, result? }`, the import-free multi-entity atomic form, #293), a `defineScpTransaction(...)` / `defineTransaction(...)` transaction definition, or a `mutation(...)` plan. `mutate` is the authoring twin of the runtime `mutate`; its only option is `{ mode }` (the sole home of atomicity)."
|
|
4674
4677
|
);
|
|
4675
4678
|
}
|
|
4676
4679
|
function buildCommandContract(methods) {
|
|
4677
4680
|
const resolved = {};
|
|
4678
4681
|
for (const [name, body] of Object.entries(methods)) {
|
|
4679
4682
|
if (isPublicWriteDescriptor(body)) {
|
|
4680
|
-
const { plan: plan2, select, condition, mode, description } = writeDescriptorToPlan(name, body);
|
|
4681
|
-
resolved[name] = finalizeDescriptorCommand(
|
|
4683
|
+
const { plan: plan2, select, condition, mode, batchWriteEligible, description } = writeDescriptorToPlan(name, body);
|
|
4684
|
+
resolved[name] = finalizeDescriptorCommand(
|
|
4685
|
+
name,
|
|
4686
|
+
plan2,
|
|
4687
|
+
select,
|
|
4688
|
+
condition,
|
|
4689
|
+
mode,
|
|
4690
|
+
description,
|
|
4691
|
+
batchWriteEligible
|
|
4692
|
+
);
|
|
4693
|
+
continue;
|
|
4694
|
+
}
|
|
4695
|
+
if (isPublicCompositeDescriptor(body)) {
|
|
4696
|
+
const plan2 = mutation(name, body.writes);
|
|
4697
|
+
const select = body.result !== void 0 && body.result.select !== void 0 ? body.result.select : void 0;
|
|
4698
|
+
resolved[name] = finalizeDescriptorCommand(
|
|
4699
|
+
name,
|
|
4700
|
+
plan2,
|
|
4701
|
+
select,
|
|
4702
|
+
void 0,
|
|
4703
|
+
body.mode ?? "transaction",
|
|
4704
|
+
body.description
|
|
4705
|
+
);
|
|
4682
4706
|
continue;
|
|
4683
4707
|
}
|
|
4684
4708
|
if (isPublicComposeDescriptor(body)) {
|
|
@@ -4708,7 +4732,7 @@ function buildCommandContract(methods) {
|
|
|
4708
4732
|
continue;
|
|
4709
4733
|
}
|
|
4710
4734
|
throw new Error(
|
|
4711
|
-
`publishCommand: method '${name}' must be a write descriptor (\`{ create | update | remove: Model, key, input?, condition?, result?, mode? }\`), a composite \`{
|
|
4735
|
+
`publishCommand: method '${name}' must be a write descriptor (\`{ create | update | remove: Model, key, input?, condition?, result?, mode? }\`) for a single-entity write, or \u2014 for a multi-entity atomic (composite) write \u2014 a \`{ writes: $ => ({ alias: { create | update | remove: Model, key, input?, condition? }, \u2026 }), input?, result?, mode? }\` descriptor (the PUBLIC composite form; a multi-alias \`writes\` body is compiled internally into one atomic \`TransactWriteItems\`, needing no \`mutation(...)\` / \`defineScpTransaction(...)\` import). The legacy closure form (\`(keys, params) => Model.putItem(...)\`) and the \`command(...).plan(...)\` builder have been removed (#101).`
|
|
4712
4736
|
);
|
|
4713
4737
|
}
|
|
4714
4738
|
return { __isContract: true, kind: "command", methods: resolved };
|
|
@@ -4755,7 +4779,7 @@ function buildPlannedCommandMethod(name, planned) {
|
|
|
4755
4779
|
...returnSelection !== void 0 ? { returnSelection } : {}
|
|
4756
4780
|
};
|
|
4757
4781
|
}
|
|
4758
|
-
function finalizeDescriptorCommand(name, plan2, select, condition,
|
|
4782
|
+
function finalizeDescriptorCommand(name, plan2, select, condition, declaredMode, description, batchWriteEligible = false) {
|
|
4759
4783
|
const planned = {
|
|
4760
4784
|
[PLANNED_COMMAND_BRAND]: true,
|
|
4761
4785
|
plan: plan2,
|
|
@@ -4763,6 +4787,13 @@ function finalizeDescriptorCommand(name, plan2, select, condition, mode, descrip
|
|
|
4763
4787
|
select
|
|
4764
4788
|
};
|
|
4765
4789
|
const built = buildPlannedCommandMethod(name, planned);
|
|
4790
|
+
const promoted = built.ops !== void 0 && built.ops.length > 1 || built.conditionChecks !== void 0 || built.edgeWrites !== void 0 || built.derivedUpdates !== void 0 || built.uniqueGuards !== void 0 || built.outboxEvents !== void 0 || built.idempotencyGuard !== void 0 || built.maintainWrites !== void 0 || built.maintainOutbox !== void 0;
|
|
4791
|
+
if (declaredMode === "batchWrite" && (!batchWriteEligible || promoted)) {
|
|
4792
|
+
throw new Error(
|
|
4793
|
+
`publishCommand: method '${name}' declares mode: 'batchWrite', but DynamoDB's BatchWriteItem carries no ConditionExpression, no Update and no composed derived effects \u2014 only an UNCONDITIONAL put/delete qualifies (an \`upsert\` or a condition-free \`remove\` with no derived effects). This method is ${!batchWriteEligible ? "conditional / not an unconditional put/delete" : "promoted to an atomic TransactWriteItems by its derived effects"}. Use mode: 'transaction' (atomic TransactWriteItems, condition-capable, \u226425) \u2014 or the transaction forEach form for atomic per-item values.`
|
|
4794
|
+
);
|
|
4795
|
+
}
|
|
4796
|
+
const mode = declaredMode ?? (batchWriteEligible && !promoted ? "batchWrite" : "transaction");
|
|
4766
4797
|
const builtOp = built.op;
|
|
4767
4798
|
const op = condition !== void 0 ? { ...builtOp, condition } : builtOp;
|
|
4768
4799
|
return {
|
|
@@ -4801,6 +4832,9 @@ var WRITE_INTENT_KEYS = ["create", "update", "remove", "upsert"];
|
|
|
4801
4832
|
function isPublicComposeDescriptor(value) {
|
|
4802
4833
|
return typeof value === "object" && value !== null && isCommandPlan(value.command);
|
|
4803
4834
|
}
|
|
4835
|
+
function isPublicCompositeDescriptor(value) {
|
|
4836
|
+
return typeof value === "object" && value !== null && typeof value.writes === "function";
|
|
4837
|
+
}
|
|
4804
4838
|
function isPublicReadDescriptor(value) {
|
|
4805
4839
|
return typeof value === "object" && value !== null && READ_INTENT_KEYS2.some((k) => value[k] !== void 0);
|
|
4806
4840
|
}
|
|
@@ -4996,7 +5030,8 @@ function writeDescriptorToPlan(name, d) {
|
|
|
4996
5030
|
});
|
|
4997
5031
|
const select = d.result !== void 0 && d.result.select !== void 0 ? d.result.select : void 0;
|
|
4998
5032
|
const condition = d.condition !== void 0 ? descriptorConditionRecord(d.condition, name) : void 0;
|
|
4999
|
-
|
|
5033
|
+
const batchWriteEligible = (intent === "upsert" || intent === "remove") && d.condition === void 0;
|
|
5034
|
+
return { plan: plan2, select, condition, mode: d.mode, batchWriteEligible, description: d.description };
|
|
5000
5035
|
}
|
|
5001
5036
|
|
|
5002
5037
|
// src/spec/mutation-command.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildMaintenanceGraph
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KOIJ4SNO.js";
|
|
4
4
|
import {
|
|
5
5
|
ChangeCaptureRegistry,
|
|
6
6
|
ClientManager,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
buildDeleteInput,
|
|
10
10
|
buildPutInput,
|
|
11
11
|
resolveModelClass
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZNU7OI5I.js";
|
|
13
13
|
|
|
14
14
|
// src/cdc/prng.ts
|
|
15
15
|
var SeededRandom = class {
|
|
@@ -227,10 +227,35 @@ var param = {
|
|
|
227
227
|
const param2 = { kind: "array", element };
|
|
228
228
|
if (options?.description !== void 0) param2.description = options.description;
|
|
229
229
|
return param2;
|
|
230
|
+
},
|
|
231
|
+
/**
|
|
232
|
+
* A placeholder for a **map / embedded object** value written to a single
|
|
233
|
+
* DynamoDB `M` attribute (issue #295). Free-form by design: the model-side
|
|
234
|
+
* vocabulary (`@map` / `@embedded`) records no nested schema, so the param is
|
|
235
|
+
* typed as the language-native map in generated bindings (`dict` /
|
|
236
|
+
* `map[string]any` / `serde_json::Value` / `array`) while the optional type
|
|
237
|
+
* parameter `T` keeps the TS authoring surface precise.
|
|
238
|
+
*
|
|
239
|
+
* Legal at **value positions only** (a write descriptor's `input`, a
|
|
240
|
+
* transaction fragment value, a batch-write per-item payload field) — a map
|
|
241
|
+
* cannot compose a key, so a `param.map()` in a key position is rejected
|
|
242
|
+
* loudly at build time.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* putLeaf: {
|
|
247
|
+
* upsert: TreeNode,
|
|
248
|
+
* key: { part: param.string(), child: param.string() },
|
|
249
|
+
* input: { value: param.map<AvgValue>(), computedAt: param.number() },
|
|
250
|
+
* }
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
map(options) {
|
|
254
|
+
return makeParam("map", void 0, options?.description);
|
|
230
255
|
}
|
|
231
256
|
};
|
|
232
257
|
function isParam(value) {
|
|
233
|
-
return typeof value === "object" && value !== null && "kind" in value && (value.kind === "string" || value.kind === "number" || value.kind === "literal" || value.kind === "array");
|
|
258
|
+
return typeof value === "object" && value !== null && "kind" in value && (value.kind === "string" || value.kind === "number" || value.kind === "literal" || value.kind === "array" || value.kind === "map");
|
|
234
259
|
}
|
|
235
260
|
|
|
236
261
|
// src/client/lib-dynamodb.ts
|
|
@@ -337,17 +362,220 @@ async function executeBatchGet(docClient, operation) {
|
|
|
337
362
|
return { items: allItems };
|
|
338
363
|
}
|
|
339
364
|
|
|
365
|
+
// src/spec/catalog.ts
|
|
366
|
+
import { classifyBehaviorEffect } from "behavior-contracts";
|
|
367
|
+
var P = (type, required) => required ? { type, required } : { type };
|
|
368
|
+
var READ_PORTS = {
|
|
369
|
+
table: P("string", true),
|
|
370
|
+
indexName: P("string"),
|
|
371
|
+
rangeKey: P("string"),
|
|
372
|
+
rangeOperator: P("string"),
|
|
373
|
+
rangeValue: P("expr"),
|
|
374
|
+
limit: P("number"),
|
|
375
|
+
filter: P("string"),
|
|
376
|
+
projection: P("string[]", true),
|
|
377
|
+
implicitSourceField: P("string")
|
|
378
|
+
};
|
|
379
|
+
var WRITE_PORTS = {
|
|
380
|
+
table: P("string", true),
|
|
381
|
+
entity: P("string", true),
|
|
382
|
+
literalKey: P("boolean"),
|
|
383
|
+
maintain: P("string"),
|
|
384
|
+
"condition.kind": P("string"),
|
|
385
|
+
"condition.field": P("string"),
|
|
386
|
+
"condition.declarative": P("string"),
|
|
387
|
+
"condition.expression": P("string"),
|
|
388
|
+
"condition.names": P("string")
|
|
389
|
+
};
|
|
390
|
+
var entry = (name, inputPorts, shape, portableToIR) => ({
|
|
391
|
+
name,
|
|
392
|
+
inputPorts,
|
|
393
|
+
output: { shape },
|
|
394
|
+
portableToIR,
|
|
395
|
+
// Dynamic port families (key-attribute / write record ports) are validated by
|
|
396
|
+
// assertComponentsInCatalog, not statically enumerable — see the module doc
|
|
397
|
+
// ("Dynamic-port opt-in"). TransactWrite overrides this back to fail-closed.
|
|
398
|
+
additionalPorts: true
|
|
399
|
+
});
|
|
400
|
+
var GRAPHDDB_CATALOG = {
|
|
401
|
+
GetItem: entry("GetItem", READ_PORTS, "item", true),
|
|
402
|
+
Query: entry(
|
|
403
|
+
"Query",
|
|
404
|
+
// `cardinality: 'one'` is the root-collapse port only the Query root emits.
|
|
405
|
+
{ ...READ_PORTS, cardinality: P("string") },
|
|
406
|
+
"items",
|
|
407
|
+
true
|
|
408
|
+
),
|
|
409
|
+
BatchGetItem: entry("BatchGetItem", READ_PORTS, "items", true),
|
|
410
|
+
PutItem: entry("PutItem", WRITE_PORTS, "item", true),
|
|
411
|
+
UpdateItem: entry("UpdateItem", WRITE_PORTS, "item", true),
|
|
412
|
+
DeleteItem: entry("DeleteItem", WRITE_PORTS, "item", true),
|
|
413
|
+
ConditionCheck: entry("ConditionCheck", WRITE_PORTS, "item", true),
|
|
414
|
+
// The executor-side atomic envelope: never referenced by the portable IR
|
|
415
|
+
// (a transaction IS a component over the item primitives) — TS-runtime-only.
|
|
416
|
+
// Its single port is static, so it keeps bc's fail-closed default (no
|
|
417
|
+
// additionalPorts opt-in).
|
|
418
|
+
TransactWrite: {
|
|
419
|
+
...entry("TransactWrite", { TransactItems: P("map[]", true) }, "item", false),
|
|
420
|
+
additionalPorts: false
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
function catalogEntry(name) {
|
|
424
|
+
return GRAPHDDB_CATALOG[name];
|
|
425
|
+
}
|
|
426
|
+
var CATALOG_NAMES = [
|
|
427
|
+
"GetItem",
|
|
428
|
+
"Query",
|
|
429
|
+
"BatchGetItem",
|
|
430
|
+
"PutItem",
|
|
431
|
+
"UpdateItem",
|
|
432
|
+
"DeleteItem",
|
|
433
|
+
"ConditionCheck",
|
|
434
|
+
"TransactWrite"
|
|
435
|
+
];
|
|
436
|
+
var WRITE_CATALOG_NAMES = [
|
|
437
|
+
"PutItem",
|
|
438
|
+
"UpdateItem",
|
|
439
|
+
"DeleteItem",
|
|
440
|
+
"ConditionCheck",
|
|
441
|
+
"TransactWrite"
|
|
442
|
+
];
|
|
443
|
+
var WRITE_SET = new Set(WRITE_CATALOG_NAMES);
|
|
444
|
+
var KEY_ATTR_PORT = /^[A-Za-z0-9_]*(?:PK|SK)$/;
|
|
445
|
+
var WRITE_PORT_FAMILIES = [
|
|
446
|
+
"item.",
|
|
447
|
+
"key.",
|
|
448
|
+
"changes.",
|
|
449
|
+
"add.",
|
|
450
|
+
"condition.equals.",
|
|
451
|
+
"condition.params.",
|
|
452
|
+
"condition.values."
|
|
453
|
+
];
|
|
454
|
+
function assertComponentsInCatalog(components) {
|
|
455
|
+
const errs = [];
|
|
456
|
+
for (const c of components) {
|
|
457
|
+
for (const n of c.body) {
|
|
458
|
+
if ("cond" in n) continue;
|
|
459
|
+
const ref = "map" in n ? n.map : n;
|
|
460
|
+
const entryDef = GRAPHDDB_CATALOG[ref.component];
|
|
461
|
+
if (entryDef === void 0) {
|
|
462
|
+
errs.push(`${c.name}/${n.id}: references '${ref.component}', not a GRAPHDDB_CATALOG entry (known: ${CATALOG_NAMES.join(", ")})`);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const ports = ref.ports;
|
|
466
|
+
for (const [p, s] of Object.entries(entryDef.inputPorts)) {
|
|
467
|
+
if (s.required === true && !(p in ports)) {
|
|
468
|
+
errs.push(`${c.name}/${n.id} (${ref.component}): required port '${p}' is not wired`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const isWriteItem = WRITE_SET.has(ref.component);
|
|
472
|
+
for (const p of Object.keys(ports)) {
|
|
473
|
+
if (p in entryDef.inputPorts) continue;
|
|
474
|
+
if (isWriteItem ? WRITE_PORT_FAMILIES.some((f) => p.startsWith(f)) : KEY_ATTR_PORT.test(p)) continue;
|
|
475
|
+
errs.push(`${c.name}/${n.id} (${ref.component}): port '${p}' is not declared by the catalog entry (nor a ${isWriteItem ? "write record-family" : "key-attribute"} port)`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (errs.length > 0) {
|
|
480
|
+
throw new Error(`graphddb catalog validation failed (${errs.length} violation${errs.length === 1 ? "" : "s"}):
|
|
481
|
+
- ${errs.join("\n - ")}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
function deriveContractEffect(component) {
|
|
485
|
+
return classifyBehaviorEffect(component, WRITE_CATALOG_NAMES) === "writing" ? "command" : "query";
|
|
486
|
+
}
|
|
487
|
+
function deriveComponentEffects(components) {
|
|
488
|
+
const out = /* @__PURE__ */ new Map();
|
|
489
|
+
for (const c of components) out.set(c.name, deriveContractEffect(c));
|
|
490
|
+
return out;
|
|
491
|
+
}
|
|
492
|
+
function assertPublishSplitMatchesDerived(derived, split, contracts) {
|
|
493
|
+
const errs = [];
|
|
494
|
+
const check = (name, declared, at) => {
|
|
495
|
+
const truth = derived.get(name);
|
|
496
|
+
if (truth === void 0) {
|
|
497
|
+
errs.push(`${at}: no emitted component '${name}'`);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (truth !== declared) {
|
|
501
|
+
const verb = declared === "query" ? "publishQuery" : "publishCommand";
|
|
502
|
+
const fix = truth === "command" ? "publishCommand" : "publishQuery";
|
|
503
|
+
errs.push(
|
|
504
|
+
`${at}: published under ${verb} (declares '${declared}') but component '${name}' derives '${truth}' from its graph \u2014 the component graph is the source of truth; publish it with ${fix}`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
for (const name of derived.keys()) {
|
|
509
|
+
if (split.behaviors.has(name)) continue;
|
|
510
|
+
if (split.queries.has(name)) check(name, "query", "components");
|
|
511
|
+
else if (split.transactions.has(name)) check(name, "command", "components");
|
|
512
|
+
else errs.push(`components: '${name}' has no declared publish origin (queries / transactions / behaviors) to verify against`);
|
|
513
|
+
}
|
|
514
|
+
for (const [cname, contract] of Object.entries(contracts)) {
|
|
515
|
+
for (const [mname, m] of Object.entries(contract.methods)) {
|
|
516
|
+
const at = `contract ${cname}.${mname}`;
|
|
517
|
+
if (contract.kind === "query") {
|
|
518
|
+
const q = m;
|
|
519
|
+
if (!split.queries.has(q.operation)) errs.push(`${at}: operation '${q.operation}' is not in queries`);
|
|
520
|
+
else check(q.operation, "query", at);
|
|
521
|
+
} else {
|
|
522
|
+
const cm = m;
|
|
523
|
+
for (const target of [cm.single, cm.batch]) {
|
|
524
|
+
if (target === void 0) continue;
|
|
525
|
+
if (target.mode === "transaction" || target.mode === "batchWrite") {
|
|
526
|
+
if (!split.transactions.has(target.transaction)) errs.push(`${at}: transaction '${target.transaction}' is not in transactions`);
|
|
527
|
+
else check(target.transaction, "command", at);
|
|
528
|
+
} else if (!split.commands.has(target.operation)) {
|
|
529
|
+
errs.push(`${at}: operation '${target.operation}' is not in commands`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (cm.returnSelection !== void 0) check(`${cname}__${mname}__readback`, "query", `${at} readback`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (errs.length > 0) {
|
|
537
|
+
throw new Error(
|
|
538
|
+
`graphddb CQRS classification: the declared publish split contradicts the graph-derived truth (${errs.length}):
|
|
539
|
+
- ${errs.join("\n - ")}`
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/executor/handler-registry.ts
|
|
545
|
+
function resolveReadHandler(registry, name) {
|
|
546
|
+
const handler = registry[name];
|
|
547
|
+
if (handler === void 0) {
|
|
548
|
+
throw new Error(
|
|
549
|
+
`graphddb handler registry: no handler bound for catalog component '${name}' (known: ${Object.keys(registry).join(", ")})`
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
return handler;
|
|
553
|
+
}
|
|
554
|
+
function assertHandlersInCatalog(registry) {
|
|
555
|
+
for (const name of Object.keys(registry)) {
|
|
556
|
+
if (catalogEntry(name) === void 0) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
`graphddb handler registry: handler '${name}' has no catalog entry (every bound handler must declare a Port schema in GRAPHDDB_CATALOG)`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
340
564
|
// src/executor/dynamo-executor.ts
|
|
565
|
+
var READ_HANDLERS = {
|
|
566
|
+
GetItem: (docClient, operation) => executeGetItem(docClient, operation),
|
|
567
|
+
Query: (docClient, operation) => executeQuery(docClient, operation),
|
|
568
|
+
BatchGetItem: (docClient, operation) => {
|
|
569
|
+
const op = operation;
|
|
570
|
+
return executeBatchGet(docClient, op);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
assertHandlersInCatalog(READ_HANDLERS);
|
|
341
574
|
var DynamoExecutor = class {
|
|
342
575
|
async execute(operation, _options) {
|
|
343
576
|
const docClient = await ClientManager.getDocumentClient();
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
if (operation.type === "BatchGetItem") {
|
|
348
|
-
return this.batchGet(operation);
|
|
349
|
-
}
|
|
350
|
-
return executeQuery(docClient, operation);
|
|
577
|
+
const handler = resolveReadHandler(READ_HANDLERS, operation.type);
|
|
578
|
+
return handler(docClient, operation);
|
|
351
579
|
}
|
|
352
580
|
async batchGet(input, _options) {
|
|
353
581
|
const docClient = await ClientManager.getDocumentClient();
|
|
@@ -2448,6 +2676,11 @@ export {
|
|
|
2448
2676
|
isParam,
|
|
2449
2677
|
BATCH_GET_MAX_KEYS,
|
|
2450
2678
|
chunkArray,
|
|
2679
|
+
GRAPHDDB_CATALOG,
|
|
2680
|
+
assertComponentsInCatalog,
|
|
2681
|
+
deriveContractEffect,
|
|
2682
|
+
deriveComponentEffects,
|
|
2683
|
+
assertPublishSplitMatchesDerived,
|
|
2451
2684
|
ClientManager,
|
|
2452
2685
|
isRawCondition,
|
|
2453
2686
|
cond,
|