graphddb 0.7.9 → 0.8.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.
Files changed (45) hide show
  1. package/README.md +6 -6
  2. package/dist/cdc/index.d.ts +389 -4
  3. package/dist/cdc/index.js +4 -3
  4. package/dist/{chunk-ZPNRLOKA.js → chunk-GS4C5VGO.js} +4 -6
  5. package/dist/chunk-HNY2EJPV.js +1184 -0
  6. package/dist/{chunk-NYM7K2ST.js → chunk-I4LEJ4TF.js} +3812 -6724
  7. package/dist/{chunk-PFFPLD4B.js → chunk-L2NEDS7U.js} +725 -2112
  8. package/dist/chunk-L4QRCHRQ.js +278 -0
  9. package/dist/chunk-LGHSZIEE.js +187 -0
  10. package/dist/chunk-N4NWYNGZ.js +1987 -0
  11. package/dist/{chunk-PDUVTYC5.js → chunk-XTWXMOHD.js} +0 -1
  12. package/dist/cli.js +63 -252
  13. package/dist/index.d.ts +23 -1548
  14. package/dist/index.js +100 -1778
  15. package/dist/internal/index.d.ts +84 -0
  16. package/dist/internal/index.js +701 -0
  17. package/dist/{maintenance-view-adapter-BATUh_I8.d.ts → key-DR7_lpyk.d.ts} +538 -2975
  18. package/dist/linter/index.d.ts +39 -6
  19. package/dist/linter/index.js +22 -4
  20. package/dist/{registry-CXhP4TaE.d.ts → linter-C-vypgut.d.ts} +22 -22
  21. package/dist/prepared-artifact-BpPgkXEo.d.ts +281 -0
  22. package/dist/spec/index.d.ts +506 -4
  23. package/dist/spec/index.js +36 -17
  24. package/dist/testing/index.d.ts +2 -2
  25. package/dist/testing/index.js +4 -3
  26. package/dist/transform/index.d.ts +460 -1
  27. package/dist/transform/index.js +2085 -2
  28. package/dist/types-2PMXEn5x.d.ts +1205 -0
  29. package/dist/types-BXLzIcQD.d.ts +450 -0
  30. package/docs/cdc-projection.md +5 -5
  31. package/docs/class-hydration.md +1 -1
  32. package/docs/cqrs-contract.md +28 -20
  33. package/docs/design-patterns.md +5 -5
  34. package/docs/docs-generation.md +6 -6
  35. package/docs/middleware.md +15 -15
  36. package/docs/mutation-command-derivation.md +52 -42
  37. package/docs/prepared-statements.md +14 -14
  38. package/docs/python-bridge.md +113 -66
  39. package/docs/spec.md +153 -124
  40. package/docs/testing.md +9 -8
  41. package/package.json +20 -5
  42. package/dist/chunk-MMVHOUM4.js +0 -24
  43. package/dist/from-change-DanwjE5b.d.ts +0 -327
  44. package/dist/index-CtPJSMrc.d.ts +0 -934
  45. package/dist/relation-depth-Dg3yhl7S.d.ts +0 -36
@@ -1,4 +1,506 @@
1
- export { a1 as BridgeBundle, a4 as CommandContractMethodSpec, a5 as CommandResolutionTarget, H as CommandSpec, a6 as CompiledFragment, a7 as CompiledMutationPlan, a8 as ComposeSpec, a9 as CompositionPlanSpec, a2 as ConditionSpec, J as ContextSpec, aa as ContractCardinality, ab as ContractCommandResult, ac as ContractInputArity, ad as ContractKeySpec, ae as ContractKind, af as ContractResolution, A as ContractSpec, ag as DerivedConditionCheck, ah as DerivedEdgeWrite, ai as DerivedIdempotencyGuard, aj as DerivedMaintainOutbox, ak as DerivedMaintainWrite, al as DerivedOutboxEvent, am as DerivedUniqueGuard, an as DerivedUpdate, ao as EntityRefResolver, ap as ExecutionPlanSpec, aq as FilterSpec, ar as MAX_TRANSACT_COMPOSE_ITEMS, a0 as Manifest, as as ManifestEntity, at as ManifestField, au as ManifestFieldType, av as ManifestGsi, aw as ManifestKey, ax as ManifestRelation, ay as ManifestTable, az as OperationSpec, _ as OperationsDocument, L as ParamSpec, aA as QueryContractMethodSpec, G as QuerySpec, aB as RangeConditionSpec, aC as ReadOperationType, K as SPEC_VERSION, aD as TransactionItemSpec, aE as TransactionItemType, I as TransactionSpec, aF as WhenSpec, aG as WriteOperationType, aH as assertNoCrossFragmentMaintainCollision, aI as compileFragment, aJ as compileMutationPlan, aK as compileSingleFragmentPlan, aL as resetMaintenanceGraphCache, aM as resolveLifecycle, aN as resolveMaintainers } from '../maintenance-view-adapter-BATUh_I8.js';
2
- export { A as AnyModelContract, B as BuiltContracts, C as ContextOwnership, b as ContextOwnershipMap, c as ContractBoundaryViolation, d as ContractInputs, e as ContractMap, f as ContractN1Violation, O as PREPARED_FORMAT_VERSION, Q as PreparedBindMap, g as PreparedBindSpec, a as PreparedPlanDocument, h as PreparedPlanSpec, i as PreparedReadRouteSpec, P as PreparedWriteOpSpec, s as assertBundleSerializable, t as assertContractBoundaries, u as assertContractN1Safe, v as assertJsonSerializable, w as assertSupportedCondition, x as buildBridgeBundle, y as buildContexts, z as buildContracts, D as buildManifest, E as buildOperations, R as buildPreparedPlanDocument, F as buildQuerySpec, G as buildTransactionSpec, H as buildTransactions, S as canonicalJson, I as collectContractBoundaryViolations, J as collectContractN1Violations, U as compilePreparedPlan, V as entityFingerprint, X as planFingerprint } from '../index-CtPJSMrc.js';
3
- import '../registry-CXhP4TaE.js';
4
- import '@aws-sdk/client-dynamodb';
1
+ import { $ as AnyOperationDefinition, a0 as TransactionDefinition, b as PreparedBody } from '../key-DR7_lpyk.js';
2
+ export { a1 as CompiledFragment, a2 as CompiledMutationPlan, a3 as DerivedConditionCheck, a4 as DerivedEdgeWrite, a5 as DerivedIdempotencyGuard, a6 as DerivedMaintainOutbox, a7 as DerivedMaintainWrite, a8 as DerivedOutboxEvent, a9 as DerivedUniqueGuard, aa as DerivedUpdate, ab as EntityRefResolver, ac as MAX_TRANSACT_COMPOSE_ITEMS, ad as assertNoCrossFragmentMaintainCollision, ae as compileFragment, af as compileMutationPlan, ag as compileSingleFragmentPlan, ah as resetMaintenanceGraphCache, ai as resolveLifecycle, aj as resolveMaintainers } from '../key-DR7_lpyk.js';
3
+ import { M as MetadataRegistry } from '../linter-C-vypgut.js';
4
+ import { C as ContractMap, e as ContextOwnershipMap, c as PreparedPlanSpec, a as PreparedPlanDocument } from '../prepared-artifact-BpPgkXEo.js';
5
+ export { A as AnyModelContract, B as BuiltContracts, f as ContextOwnership, g as PREPARED_FORMAT_VERSION, h as PreparedBindMap, b as PreparedBindSpec, d as PreparedReadRouteSpec, P as PreparedWriteOpSpec, i as buildContexts, j as buildContracts, k as canonicalJson, l as entityFingerprint, p as planFingerprint } from '../prepared-artifact-BpPgkXEo.js';
6
+ import { O as OperationsDocument, Q as QuerySpec, M as Manifest, C as ContractSpec, T as TransactionSpec, B as BridgeBundle, c as ConditionSpec, E as ExpressionSpec, a as CommandSpec, d as SpecVersion } from '../types-2PMXEn5x.js';
7
+ export { e as CommandContractMethodSpec, f as CommandResolutionTarget, g as ComposeSpec, h as CompositionPlanSpec, b as ContextSpec, i as ContractCardinality, j as ContractCommandResult, k as ContractInputArity, l as ContractKeySpec, m as ContractKind, n as ContractResolution, o as EXPR_VERSION, p as ExecutionPlanSpec, q as ExpressionArrNode, r as ExpressionFloatNode, s as ExpressionIntNode, t as ExpressionNode, u as ExpressionObjNode, v as ExpressionOpNode, w as ExpressionOperator, x as ExpressionRefNode, y as ExpressionRefOptNode, z as ExpressionScalar, F as FilterSpec, A as ManifestEntity, D as ManifestField, G as ManifestFieldType, H as ManifestGsi, I as ManifestKey, J as ManifestRelation, K as ManifestTable, L as OperationSpec, P as ParamSpec, N as QueryContractMethodSpec, R as RangeConditionSpec, U as ReadOperationType, S as SPEC_VERSION, V as SPEC_VERSION_SCP, W as SPEC_VERSION_SUPPORTED, X as TransactionItemSpec, Y as TransactionItemType, Z as WhenSpec, _ as WriteOperationType } from '../types-2PMXEn5x.js';
8
+ import '../types-BXLzIcQD.js';
9
+
10
+ /**
11
+ * Static parameterized operation-spec generation (issue #42, Phase 0b).
12
+ *
13
+ * Turns the parameterized definition IR (#41) into JSON-serializable
14
+ * {@link OperationSpec} / {@link CommandSpec} via **symbolic evaluation** of the
15
+ * key / GSI mapping functions. The result is deterministic and contains only
16
+ * `{param}` / `{result.field}` template placeholders, never concrete values.
17
+ *
18
+ * Relation chains in a read `select` become **multiple operations** wired with
19
+ * `resultPath` and `{result.*}` key templates, mirroring the runtime
20
+ * `relation-planner` policy: `hasMany` → `Query` + `begins_with`, `belongsTo` /
21
+ * `hasOne` → `BatchGetItem`.
22
+ */
23
+
24
+ /** Build the {@link QuerySpec} for a single read definition. */
25
+ declare function buildQuerySpec(def: AnyOperationDefinition): QuerySpec;
26
+ /**
27
+ * Optional contract-layer inputs to {@link buildOperations} (issue #59). Both are
28
+ * absent on a pre-#59 (contract-free) build, in which case the produced document
29
+ * is byte-identical to the pre-#59 shape (no `contracts` / `contexts` keys).
30
+ */
31
+ interface ContractInputs {
32
+ /** Resolved contracts (publishQuery / publishCommand output). */
33
+ readonly contracts?: ContractMap;
34
+ /** Context-ownership declarations (context → member models / contracts). */
35
+ readonly contexts?: ContextOwnershipMap;
36
+ }
37
+ /**
38
+ * Build the operations document from grouped read (`queries`) and write
39
+ * (`commands`) definition maps, plus the optional CQRS contract layer (#59).
40
+ * All inputs are optional. Output is deterministic (definitions emitted in
41
+ * sorted-key order; the runtime resolves by name).
42
+ *
43
+ * The contract layer (#59) is layered **on top of** the existing specs: each
44
+ * contract method's underlying op is synthesized into `queries` / `commands` and
45
+ * referenced by name, and the `contracts` / `contexts` maps are emitted. A
46
+ * contract-free input (`contractInputs` empty / absent) reproduces the pre-#59
47
+ * document byte-for-byte — no `contracts` / `contexts` keys appear.
48
+ *
49
+ * @throws if a command carries a write condition outside the supported subset
50
+ * (`{ notExists }` or pure equality) — the bridge guard; or if a contract op
51
+ * name collides with a hand-written definition.
52
+ */
53
+ declare function buildOperations(queries?: Record<string, AnyOperationDefinition>, commands?: Record<string, AnyOperationDefinition>, transactions?: Record<string, TransactionDefinition>, contractInputs?: ContractInputs): OperationsDocument;
54
+
55
+ /**
56
+ * Manifest generation (issue #42, Python-bridge Phase 0b).
57
+ *
58
+ * Produces the JSON-serializable {@link Manifest} from a {@link MetadataRegistry}
59
+ * snapshot: entity → table / physicalName / prefix / field types / key & GSI
60
+ * templates / relation metadata. Generation is **deterministic** — entities,
61
+ * fields, GSIs, and relations are emitted in a stable (sorted) order so the same
62
+ * registry always yields byte-identical JSON.
63
+ */
64
+
65
+ /**
66
+ * Build the full {@link Manifest} from the {@link MetadataRegistry}.
67
+ *
68
+ * Every registered entity is included. Output ordering is deterministic
69
+ * (entities / fields / relations sorted by name, GSIs by index name).
70
+ */
71
+ declare function buildManifest(registry?: typeof MetadataRegistry): Manifest;
72
+
73
+ /**
74
+ * N+1 static checker for the CQRS Contract layer (issue #60, Epic #57; proposal
75
+ * `docs/cqrs-contract.md`, "N+1 Safety (Static Restriction)").
76
+ *
77
+ * Whether a resolution is N+1-safe depends on **two** facts the planner already
78
+ * decided in #58 and #59 serialized into the SSoT — the checker **honors** them,
79
+ * it does **not** re-derive policy ("decide once, declare in the SSoT"):
80
+ *
81
+ * - `resolution` — `'point'` (target keys known → `BatchGetItem` / another
82
+ * contract's `get(keys[])`; coalesces) or `'range'` (target key set unknown →
83
+ * partition `Query`; one request per partition key).
84
+ * - `inputArity` / parent `cardinality` — how many keys are fed in. A `range`
85
+ * method is one query for one key but N queries for N keys.
86
+ *
87
+ * The rule (absolute, no opt-in escape hatch): **a `range` resolution is allowed
88
+ * only with single-key input.** Three forms are build-time errors:
89
+ *
90
+ * (a) **array into a `range` method** — a `range` method whose `inputArity` is
91
+ * not `'single'` (`'array'` / `'either'`). A `range` method must be
92
+ * `'single'`; anything else is an N partition-`Query` fan-out.
93
+ * (a′) **array into a unique-GSI `point` method** (issue #71) — a `point` method
94
+ * whose Key resolves via a **unique GSI** (a per-key `Query`, since
95
+ * `BatchGetItem` cannot read a GSI) but whose `inputArity` is not `'single'`.
96
+ * A base-table `point` coalesces a key array into one `BatchGetItem`, but a
97
+ * unique-GSI `point` would issue N `Query`s — an N+1 fan-out, the same shape
98
+ * as (a). graphddb's official spec does not include N+1-producing queries, so
99
+ * it is rejected at build time; the application-side loop is the escape (same
100
+ * philosophy as `range`).
101
+ * (b) **"list under a list"** — a `range` child nested under a parent step whose
102
+ * output cardinality is `'many'`. The composition's parent yields N records,
103
+ * so a `range` child fans out to N partition `Query`s.
104
+ * (c) **`compose` child that is `range`** — the cross-contract / composition form
105
+ * of (b): a composed (External Query) child declared `range` under a parent
106
+ * that can yield many. (Rule (b) and (c) are the same axis — a `range` child
107
+ * under a many-yielding parent — surfaced through nested model relations vs.
108
+ * cross-contract composition respectively.)
109
+ *
110
+ * `point` resolutions are always allowed (they coalesce). If a caller genuinely
111
+ * needs "just these few", it loops in its own application code (the proposal's
112
+ * `Promise.all(ids.map(...))`), keeping the N visible at the call site —
113
+ * the contract surface never hides a fan-out.
114
+ *
115
+ * ## Placement — build path, not the model `LintRule` framework
116
+ *
117
+ * This checker runs on the **contract build path** ({@link buildContracts} in
118
+ * `src/spec/contracts.ts`), where every other contract validation surfaces
119
+ * (#59's projection audit, write-condition guard, and the original inline
120
+ * `compose` rule all `throw` there). It operates on the serialized
121
+ * {@link ContractSpec} facts, not on {@link EntityMetadata}: the existing
122
+ * `src/linter/` `LintRule` framework is keyed on a single model's `EntityMetadata`
123
+ * (model relations / GSIs), which carries none of the contract `resolution` /
124
+ * `inputArity` / `compose` facts the N+1 rule needs. Surfacing the violation as a
125
+ * thrown build error — exactly as `relation/traversal.ts` `validateDepth` and the
126
+ * #59 contract validations do — fails the build the same way `inputArity`
127
+ * mismatches and `range` composed children already failed it.
128
+ *
129
+ * The cross-contract `range`-child form (c) and the nested-relation form (b) are
130
+ * only fully reachable once the composition DSL primitive
131
+ * (`query(Contract.method, { from() })`, owned by #63) lands; until then no #58
132
+ * DSL produces a `compose` node. The checker implements the full logic now so it
133
+ * is ready, and the forward forms are exercised by feeding synthesized SSoT nodes
134
+ * (exactly as #59's serializer tests do).
135
+ */
136
+
137
+ /** A single N+1 rule violation, carrying the rule label and a clear message. */
138
+ interface ContractN1Violation {
139
+ /** Which of the N+1 forms was violated. */
140
+ readonly rule: 'array-into-range' | 'array-into-gsi-point' | 'list-under-list' | 'compose-range-under-many';
141
+ /** The offending contract name. */
142
+ readonly contract: string;
143
+ /** The offending method name. */
144
+ readonly method: string;
145
+ /** A clear, actionable error message (the same text the thrown error carries). */
146
+ readonly message: string;
147
+ }
148
+ /**
149
+ * Tells, for a query contract method name, whether its `point` read resolves via
150
+ * a **unique GSI** (a per-key `Query`) rather than the base-table primary key (a
151
+ * coalescible `GetItem` / `BatchGetItem`). Supplied by the serializer (issue #71),
152
+ * which recovers the fact from the method's synthesized read op (a root `Query`
153
+ * carrying an `indexName`). Absent → no method is treated as a GSI point (the
154
+ * pre-#71 default; e.g. synthesized-SSoT checker tests that exercise only the
155
+ * `range` / `compose` rules).
156
+ */
157
+ type IsGsiPointMethod = (methodName: string) => boolean;
158
+ /**
159
+ * Collect every N+1 violation in a single serialized {@link ContractSpec}.
160
+ * Command contracts carry no `resolution` / `compose` and are always N+1-safe
161
+ * (writes resolve to known keys), so only query contracts are inspected.
162
+ *
163
+ * @param isGsiPoint Optional predicate (issue #71): does the named method's
164
+ * `point` read resolve via a unique GSI? Used to reject array-into-GSI-point
165
+ * (rule a′). Defaults to "never" when not supplied.
166
+ */
167
+ declare function collectContractN1Violations(contractName: string, spec: ContractSpec, isGsiPoint?: IsGsiPointMethod): ContractN1Violation[];
168
+ /**
169
+ * Assert a single serialized contract is N+1-safe, throwing the first violation
170
+ * as a build error (mirroring `relation/traversal.ts` `validateDepth` and the
171
+ * #59 contract validations, which `throw` on the build path). Multiple violations
172
+ * are summarized in the thrown message so the build surfaces them all at once.
173
+ *
174
+ * @throws if any of the N+1 forms is present.
175
+ *
176
+ * @param isGsiPoint Optional predicate (issue #71) identifying unique-GSI `point`
177
+ * methods, so rule (a′) (array into a GSI point) can be enforced.
178
+ */
179
+ declare function assertContractN1Safe(contractName: string, spec: ContractSpec, isGsiPoint?: IsGsiPointMethod): void;
180
+
181
+ /**
182
+ * Bounded-context boundary checker for the CQRS Contract layer (issue #61, Epic
183
+ * #57; spec `docs/cqrs-contract.md`, External Query section "Why it
184
+ * exists: boundary enforcement").
185
+ *
186
+ * The rule, stated in the proposal and the issue: a **Model is a private
187
+ * implementation detail of its bounded context; a Contract is the published
188
+ * interface**. Within its own context a contract resolves its own Models directly
189
+ * (internal `query` / `list` / `put` / `update` / `delete`). Across a context
190
+ * boundary it may depend on another context **only through that context's
191
+ * published Contract** (an External Query / Command). Reaching directly into a
192
+ * **foreign** Model is a **build-time error** — it couples one context to
193
+ * another's private storage shape and silently defeats the boundary the contexts
194
+ * declaration is meant to enforce.
195
+ *
196
+ * ## What "own" vs. "foreign" means here
197
+ *
198
+ * The decision is made entirely from the **context-ownership declaration** (#59,
199
+ * the `contexts` map: context name → `{ models, contracts }`). For each contract:
200
+ *
201
+ * - its **owning context** is the (unique) context whose `contracts` list names
202
+ * it; and
203
+ * - for every Model its method ops touch (the recorded {@link ContractMethodOp}
204
+ * `entity` refs, #58), that Model's **owning context** is the context whose
205
+ * `models` list names it.
206
+ *
207
+ * A method op against a Model owned by a **different** context than the contract's
208
+ * own is the violation. Three cases are deliberately **allowed**, never flagged:
209
+ *
210
+ * - the Model and the contract are in the **same** context (the normal internal
211
+ * case);
212
+ * - the **contract is in no declared context** — there is no boundary to enforce
213
+ * *from*, so the contract is unconstrained (a partial / incremental adoption of
214
+ * the contexts declaration must not turn unrelated contracts into errors);
215
+ * - the touched **Model is in no declared context** — an undeclared Model has no
216
+ * owner to protect, so it is not "foreign" to anyone. (Declaring the Model into
217
+ * a context is what opts it into boundary protection.)
218
+ *
219
+ * In short: the check fires **only** when both the contract *and* the foreign
220
+ * Model have declared, *different* owners. This is the least-surprising reading of
221
+ * a *partial* contexts declaration and keeps the rule purely additive — declaring
222
+ * more ownership can only ever surface *more* violations, never fewer.
223
+ *
224
+ * ## Placement — build path, not the model `LintRule` framework (decision B)
225
+ *
226
+ * This checker runs in **{@link buildOperations}** (`src/spec/operations.ts`),
227
+ * immediately before the {@link buildContracts} call — the seam where both the
228
+ * raw contract IR (the recorded op `entity` refs) and the `contexts` map are
229
+ * still available, alongside #59's projection audit, the write-condition guard,
230
+ * and #60's N+1 checker — every contract validation surfaces there as a thrown
231
+ * build error. It is **not** added to the `src/linter/` `LintRule`
232
+ * framework, even though the issue text suggests a `context-boundary` rule there,
233
+ * because that framework's contract — `check(metadata: EntityMetadata, registry)`
234
+ * — is keyed on a **single model's** {@link EntityMetadata} and carries **none** of
235
+ * the facts this rule needs:
236
+ *
237
+ * - which context a *contract* belongs to (a contract is not an `EntityMetadata`);
238
+ * - which Models a contract's *methods* touch (these live on the unserialized
239
+ * {@link ContractMethodOp} `entity` refs, not on any model's metadata);
240
+ * - the cross-cutting `contexts` map relating the two.
241
+ *
242
+ * A `LintRule` sees one model at a time and no contract facts at all, so a naive
243
+ * rule physically cannot observe a contract→foreign-Model reference. Bending the
244
+ * framework to carry contracts + contexts would be an invasive, parallel
245
+ * "rule kind" that no other rule uses — exactly the ugly refactor the brief warns
246
+ * against. Placing the check here is consistent with how #60 already resolved the
247
+ * identical tension (`src/spec/contract-n1-check.ts`, "Placement"), and it reads
248
+ * the **same facts the rule actually depends on** at the one point where contracts
249
+ * and contexts are both in hand. The boundary violation fails the build the same
250
+ * way an `inputArity` mismatch or a `range` composed child already does.
251
+ */
252
+
253
+ /** A single context-boundary violation, carrying the offending facts + message. */
254
+ interface ContractBoundaryViolation {
255
+ /** The offending contract name. */
256
+ readonly contract: string;
257
+ /** The context that owns the offending contract. */
258
+ readonly contractContext: string;
259
+ /** The contract method whose op touched the foreign Model. */
260
+ readonly method: string;
261
+ /** The foreign Model (entity / model-class name) referenced directly. */
262
+ readonly foreignModel: string;
263
+ /** The context that owns the foreign Model. */
264
+ readonly foreignContext: string;
265
+ /** A clear, actionable error message (the same text the thrown error carries). */
266
+ readonly message: string;
267
+ }
268
+ /**
269
+ * Collect every context-boundary violation across a map of resolved contracts,
270
+ * given the context-ownership declaration. A violation is a contract method whose
271
+ * recorded op touches a Model owned by a **different** context than the contract's
272
+ * own (see the module doc for the precise own / foreign / undeclared rule).
273
+ *
274
+ * @param contracts Contract name → resolved {@link QueryModelContract} /
275
+ * {@link CommandModelContract} (the #58 factory output).
276
+ * @param contexts The context-ownership declaration (#59). When empty, no
277
+ * contract has a declared owner, so there are no boundaries to enforce → no
278
+ * violations.
279
+ * @throws if the contexts declaration claims a Model / Contract in two contexts
280
+ * (an ambiguous, self-contradictory declaration).
281
+ */
282
+ declare function collectContractBoundaryViolations(contracts: ContractMap, contexts: ContextOwnershipMap): ContractBoundaryViolation[];
283
+ /**
284
+ * Assert a map of resolved contracts respects bounded-context boundaries, throwing
285
+ * the first violation as a build error (mirroring #60's {@link assertContractN1Safe}
286
+ * and the other #59 contract validations, which `throw` on the build path).
287
+ * Multiple violations are summarized in the thrown message so the build surfaces
288
+ * them all at once.
289
+ *
290
+ * @param contracts Contract name → resolved contract IR.
291
+ * @param contexts The context-ownership declaration (#59).
292
+ * @throws if any contract directly references a foreign context's Model, or if the
293
+ * contexts declaration is ambiguous (a Model / Contract claimed twice).
294
+ */
295
+ declare function assertContractBoundaries(contracts: ContractMap, contexts: ContextOwnershipMap): void;
296
+
297
+ /**
298
+ * Static planning for declarative transactions (issue #46, Phase 4).
299
+ *
300
+ * Turns a {@link TransactionDefinition} (captured by `defineTransaction`) into a
301
+ * serializable {@link TransactionSpec}. Each `tx.put/update/delete` becomes a
302
+ * templated {@link TransactionItemSpec}; a `tx.forEach` block marks its items
303
+ * with a `forEach` binding so the runtime expands them once per array-param
304
+ * element. Key templates are derived with the **same** symbolic key-mapping
305
+ * evaluator (#42 `evaluateKey`) the single-write planner uses — the
306
+ * declarativity of the field references is what lets that reuse work: every
307
+ * key / item / changes leaf is either a field reference (whose token the planner
308
+ * substitutes) or a concrete literal.
309
+ */
310
+
311
+ /** Build the {@link TransactionSpec} for one transaction definition. */
312
+ declare function buildTransactionSpec(txName: string, def: TransactionDefinition): TransactionSpec;
313
+ /** Build the transactions map from a record of transaction definitions. */
314
+ declare function buildTransactions(transactions?: Record<string, TransactionDefinition>): Record<string, TransactionSpec>;
315
+
316
+ /**
317
+ * Bridge guard (issue #42, Phase 0b).
318
+ *
319
+ * The Python bridge can only carry a **declarative, serializable subset** of
320
+ * graphddb. This module enforces that subset at build time:
321
+ *
322
+ * 1. **Write conditions** must be `{ notExists }`, an existence primitive
323
+ * (`{ attributeExists }` / `{ attributeNotExists }` on any field, issue #81),
324
+ * *or* pure field equality (mirroring `expression/condition-expression.ts`,
325
+ * which only emits `attribute_(not_)exists(...)` or `#f = :v` clauses). Any
326
+ * other condition shape is rejected — there is no serializable representation
327
+ * for it. {@link conditionInputToSpec} is the single place the IR-shaped input
328
+ * is mapped to the {@link ConditionSpec} subset.
329
+ * 2. The produced bundle must be **JSON-serializable** — no functions, classes,
330
+ * `Date`, `Set`, `Map`, `Symbol`, or non-finite numbers may leak into the
331
+ * spec. {@link assertJsonSerializable} walks the value and throws on any.
332
+ *
333
+ * (Note: the post-load TS predicate `refine` was removed in #49, so no guard for
334
+ * it is needed; see `docs/python-bridge.md`.)
335
+ */
336
+
337
+ /**
338
+ * Assert a write {@link ConditionSpec} is within the supported subset. The spec
339
+ * type already constrains it to `notExists` | `attributeExists` |
340
+ * `attributeNotExists` | `equals`, so this defends against a malformed `equals`
341
+ * (empty / non-template values) or a blank existence field and gives a clear,
342
+ * single-source build-time error message for any future widening.
343
+ */
344
+ declare function assertSupportedCondition(commandName: string, condition: ConditionSpec): void;
345
+ /**
346
+ * Walk an arbitrary value and throw if it contains anything that does not
347
+ * survive `JSON.stringify` round-tripping as itself (functions, symbols,
348
+ * `undefined`, `Date`, `Map`, `Set`, class instances, non-finite numbers).
349
+ */
350
+ declare function assertJsonSerializable(value: unknown, path?: string): void;
351
+ /** Convenience: assert an entire {@link BridgeBundle} is JSON-serializable. */
352
+ declare function assertBundleSerializable(bundle: BridgeBundle): void;
353
+
354
+ /**
355
+ * SCP Expression IR — build-time validation + canonicalization (issue #261,
356
+ * Phase 3 S1; behavior-contracts `expression-ir.md` §2).
357
+ *
358
+ * This module is the spec-side **vocabulary** gate: it validates an authored
359
+ * expression against the closed v1 node/operator set (fail-closed — anything
360
+ * outside the set is a build error, never a silently-carried unknown) and
361
+ * rewrites it into the **canonical serialization form** of §2.3/§2.4:
362
+ *
363
+ * - object keys sorted in **Unicode code-point order** at every level
364
+ * (canonical-serialization.md §2 — NOT JS default `.sort()`, which compares
365
+ * UTF-16 code units and diverges on astral-plane keys);
366
+ * - an integer-valued JSON number within the safe range stays a plain number
367
+ * (an int literal); beyond ±2^53−1 it must be `{int:"…"}` (raw is invalid);
368
+ * - `{int:"…"}` whose value fits the safe range canonicalizes to the plain
369
+ * number; outside it stays `{int:"…"}` (must fit i64);
370
+ * - `{float:n}` with an integer value stays wrapped; a fractional `{float:n}`
371
+ * canonicalizes to the plain number (JSON preserves its decimal part).
372
+ *
373
+ * Because the canonical form of the expression NODES is produced HERE (at
374
+ * build time), the documents flow through graphddb's ordinary JSON emitters
375
+ * unchanged — no special-cased serializer. Note the distinction: §2.3/§2.4
376
+ * key-sorting governs the expression nodes (`obj` construction keys; operator
377
+ * nodes are single-key by definition); the `{ exprVersion, expr }` ENVELOPE and
378
+ * the surrounding spec documents serialize in graphddb's usual emitter order
379
+ * like every other spec object (conformance value comparison is structural,
380
+ * expression-ir.md §2.4).
381
+ *
382
+ * Evaluation lives in the Phase 3 S5/S6/S7 sub-issues — this module never
383
+ * evaluates anything. It also hosts the conditional spec-version presence scan
384
+ * ({@link operationsSpecVersion}): a document that actually CONTAINS an SCP
385
+ * node is stamped `1.2`, everything else keeps `1.1` byte-identically.
386
+ */
387
+
388
+ /**
389
+ * Validate an authored `{ exprVersion: 1, expr: <node> }` envelope against the
390
+ * closed v1 vocabulary and return it in canonical form (§2.3/§2.4: key-sorted,
391
+ * normalized `{int:"…"}` / `{float:n}` literal wrapping). Fail-closed: an
392
+ * unknown `exprVersion`, an extra envelope key, or any node outside the closed
393
+ * set is a build error with the offending path.
394
+ */
395
+ declare function canonicalizeExpressionSpec(input: unknown, context: string): ExpressionSpec;
396
+ /**
397
+ * Does this operations-document content actually contain an SCP Expression IR
398
+ * node — a `{ kind: 'scpExpr' }` write condition or a `guard` (transaction
399
+ * item / contract command method)? This is the **presence scan** behind the
400
+ * conditional stamp (issue #261): the walk visits exactly the slots the spec
401
+ * vocabulary defines, so an SCP-free document is provably stamped `1.1` and
402
+ * stays byte-identical.
403
+ */
404
+ declare function operationsContainScpNodes(doc: {
405
+ readonly commands: Readonly<Record<string, CommandSpec>>;
406
+ readonly transactions?: Readonly<Record<string, TransactionSpec>>;
407
+ readonly contracts?: Readonly<Record<string, ContractSpec>>;
408
+ }): boolean;
409
+ /**
410
+ * The spec version to stamp on an operations document: `1.2` iff it actually
411
+ * contains an SCP node ({@link operationsContainScpNodes}), else the base
412
+ * `1.1` — so every pre-#261 bundle serializes byte-identically.
413
+ */
414
+ declare function operationsSpecVersion(doc: Parameters<typeof operationsContainScpNodes>[0]): SpecVersion;
415
+
416
+ /**
417
+ * Static prepared-plan artifact — the **build-time** (AOT) compilation of
418
+ * `DDBModel.prepare` bodies (issue #208; design #203 phase 4, on top of the #205
419
+ * runtime and the #206 compile-time hoist).
420
+ *
421
+ * `graphddb transform prepared --aot` evaluates each no-capture-verified prepare
422
+ * body at BUILD time (the #206 lint proves build-time evaluability) and this
423
+ * module compiles the evaluated routes into a JSON-serializable
424
+ * {@link PreparedPlanDocument}:
425
+ *
426
+ * - a **write** plan runs the SAME mutation compiler the runtime `prepare` uses
427
+ * (`compileWriteFragment` → lifecycle resolution → derived-effect derivation)
428
+ * and serializes the result through the SAME contract-SSoT path a
429
+ * `.plan(mutation)` command method uses
430
+ * ({@link serializePreparedWriteFragment} → one declarative
431
+ * {@link TransactionSpec} per op: base write + edges / counters / uniqueness
432
+ * guards / outbox / idempotency / maintenance / ConditionChecks) — so the
433
+ * runtime never invokes the mutation compiler again, **including the first
434
+ * call** (the #206 audit's cold-start gap);
435
+ * - a **read** plan serializes the analyzed route products (entity name, select
436
+ * template, per-field param binds, static/dynamic options) plus the
437
+ * {@link QuerySpec} the spec planner derives for it — the same
438
+ * `operations.json` vocabulary the Python runtime consumes (templated
439
+ * `{param}` key conditions, physical ops, execution plan), so a broken read
440
+ * plan fails the BUILD, not the first request.
441
+ *
442
+ * ## Model references are entity NAMES
443
+ *
444
+ * The artifact never carries a class: each plan records the entity names it
445
+ * touches plus a **fingerprint** of each entity's canonical manifest entry
446
+ * ({@link entityFingerprint}). The TS loader (`src/runtime/prepared-loader.ts`)
447
+ * re-binds names against the live {@link MetadataRegistry} and recomputes the
448
+ * fingerprints — a mismatch (model declaration / TableMapping drift since the
449
+ * artifact was generated) is a LOUD reject, never a stale-plan execution.
450
+ *
451
+ * Everything emitted is JSON-serializable by construction (the #206 no-capture
452
+ * lint guarantees the body's templates are static data; the only non-JSON
453
+ * scalars a template may carry — `Date` / `bigint` literals — are encoded
454
+ * explicitly in {@link PreparedBindSpec}).
455
+ *
456
+ * ## Layering (#223)
457
+ *
458
+ * This module is BUILD-TIME ONLY. The artifact shapes, the format version, and
459
+ * the fingerprint/canonical-JSON helpers the runtime loader also needs live in
460
+ * the shared, dependency-minimal `./prepared-artifact.ts` — both this emitter
461
+ * and the loader (`src/runtime/prepared-loader.ts`) depend on that module, and
462
+ * the loader NEVER imports this one (a loader→emitter import leaks the whole
463
+ * build-time spec machinery into the runtime bundle — the #223 size-limit
464
+ * failure). The shared symbols are re-exported here for API continuity.
465
+ */
466
+
467
+ /**
468
+ * Compile ONE `DDBModel.prepare` body into its static {@link PreparedPlanSpec}
469
+ * (build-time). The body is evaluated once with the recording `$` proxy — the
470
+ * #206 no-capture lint has already proven this is side-effect-free and
471
+ * params-independent — then each route is analyzed and serialized.
472
+ *
473
+ * @param body The prepare body function (as extracted by the transform).
474
+ * @param planLabel A human-readable label woven into build errors (the plan id).
475
+ */
476
+ declare function compilePreparedPlan(body: PreparedBody, planLabel?: string): PreparedPlanSpec;
477
+ /** Assemble the full artifact document from compiled plans. */
478
+ declare function buildPreparedPlanDocument(plans: Readonly<Record<string, PreparedPlanSpec>>): PreparedPlanDocument;
479
+
480
+ /**
481
+ * Static parameterized planner: serializable OperationSpec + manifest + bridge
482
+ * guard (issue #42, Python-bridge Phase 0b). Public surface.
483
+ *
484
+ * The CLI / file output is #43's responsibility; this module stops at the
485
+ * **generation functions** producing the typed, JSON-serializable
486
+ * `{ manifest, operations }` bundle.
487
+ */
488
+
489
+ /**
490
+ * Build the full `{ manifest, operations }` bridge bundle from grouped query /
491
+ * command definition maps. The bundle is typed and JSON-serializable; the bridge
492
+ * guard runs (unsupported write conditions throw during `buildOperations`, and
493
+ * the whole bundle is asserted JSON-serializable here).
494
+ *
495
+ * @param queries Read operation IR map. Always empty since #251 (the define*
496
+ * single-op surface was removed); reads come from the contract layer.
497
+ * @param commands Write operation IR map. Always empty since #251; writes come
498
+ * from the contract layer.
499
+ * @param registry Metadata source (defaults to the global registry).
500
+ * @param transactions Declarative transactions (`defineTransactions(...)`). Optional.
501
+ * @param contractInputs The CQRS contract layer (#59): resolved contracts +
502
+ * context-ownership. Optional — absent reproduces the pre-#59 document.
503
+ */
504
+ declare function buildBridgeBundle(queries?: Record<string, AnyOperationDefinition>, commands?: Record<string, AnyOperationDefinition>, registry?: typeof MetadataRegistry, transactions?: Record<string, TransactionDefinition>, contractInputs?: ContractInputs): BridgeBundle;
505
+
506
+ export { BridgeBundle, CommandSpec, ConditionSpec, ContextOwnershipMap, type ContractBoundaryViolation, type ContractInputs, ContractMap, type ContractN1Violation, ContractSpec, ExpressionSpec, Manifest, OperationsDocument, PreparedPlanDocument, PreparedPlanSpec, QuerySpec, SpecVersion, TransactionSpec, assertBundleSerializable, assertContractBoundaries, assertContractN1Safe, assertJsonSerializable, assertSupportedCondition, buildBridgeBundle, buildManifest, buildOperations, buildPreparedPlanDocument, buildQuerySpec, buildTransactionSpec, buildTransactions, canonicalizeExpressionSpec, collectContractBoundaryViolations, collectContractN1Violations, compilePreparedPlan, operationsContainScpNodes, operationsSpecVersion };
@@ -1,41 +1,57 @@
1
1
  import {
2
- MAX_TRANSACT_COMPOSE_ITEMS,
3
- PREPARED_FORMAT_VERSION,
4
- SPEC_VERSION,
5
- assertBundleSerializable,
6
2
  assertContractBoundaries,
7
3
  assertContractN1Safe,
8
- assertJsonSerializable,
9
- assertNoCrossFragmentMaintainCollision,
10
- assertSupportedCondition,
11
4
  buildBridgeBundle,
12
5
  buildContexts,
13
6
  buildContracts,
14
- buildManifest,
15
7
  buildOperations,
16
8
  buildPreparedPlanDocument,
17
9
  buildQuerySpec,
18
- buildTransactionSpec,
19
- buildTransactions,
20
- canonicalJson,
21
10
  collectContractBoundaryViolations,
22
11
  collectContractN1Violations,
12
+ compilePreparedPlan
13
+ } from "../chunk-N4NWYNGZ.js";
14
+ import {
15
+ PREPARED_FORMAT_VERSION,
16
+ buildManifest,
17
+ canonicalJson,
18
+ entityFingerprint,
19
+ planFingerprint
20
+ } from "../chunk-LGHSZIEE.js";
21
+ import {
22
+ MAX_TRANSACT_COMPOSE_ITEMS,
23
+ assertBundleSerializable,
24
+ assertJsonSerializable,
25
+ assertNoCrossFragmentMaintainCollision,
26
+ assertSupportedCondition,
27
+ buildTransactionSpec,
28
+ buildTransactions,
23
29
  compileFragment,
24
30
  compileMutationPlan,
25
- compilePreparedPlan,
26
31
  compileSingleFragmentPlan,
27
- entityFingerprint,
28
- planFingerprint,
29
32
  resetMaintenanceGraphCache,
30
33
  resolveLifecycle,
31
34
  resolveMaintainers
32
- } from "../chunk-NYM7K2ST.js";
33
- import "../chunk-PFFPLD4B.js";
34
- import "../chunk-PDUVTYC5.js";
35
+ } from "../chunk-I4LEJ4TF.js";
36
+ import "../chunk-HNY2EJPV.js";
37
+ import {
38
+ EXPR_VERSION,
39
+ SPEC_VERSION,
40
+ SPEC_VERSION_SCP,
41
+ SPEC_VERSION_SUPPORTED,
42
+ canonicalizeExpressionSpec,
43
+ operationsContainScpNodes,
44
+ operationsSpecVersion
45
+ } from "../chunk-L4QRCHRQ.js";
46
+ import "../chunk-L2NEDS7U.js";
47
+ import "../chunk-XTWXMOHD.js";
35
48
  export {
49
+ EXPR_VERSION,
36
50
  MAX_TRANSACT_COMPOSE_ITEMS,
37
51
  PREPARED_FORMAT_VERSION,
38
52
  SPEC_VERSION,
53
+ SPEC_VERSION_SCP,
54
+ SPEC_VERSION_SUPPORTED,
39
55
  assertBundleSerializable,
40
56
  assertContractBoundaries,
41
57
  assertContractN1Safe,
@@ -52,6 +68,7 @@ export {
52
68
  buildTransactionSpec,
53
69
  buildTransactions,
54
70
  canonicalJson,
71
+ canonicalizeExpressionSpec,
55
72
  collectContractBoundaryViolations,
56
73
  collectContractN1Violations,
57
74
  compileFragment,
@@ -59,6 +76,8 @@ export {
59
76
  compilePreparedPlan,
60
77
  compileSingleFragmentPlan,
61
78
  entityFingerprint,
79
+ operationsContainScpNodes,
80
+ operationsSpecVersion,
62
81
  planFingerprint,
63
82
  resetMaintenanceGraphCache,
64
83
  resolveLifecycle,
@@ -1,5 +1,5 @@
1
- import { n as Executor, D as DynamoDBOperation, o as ReadExecOptions, p as ExecutorResult, q as BatchGetExecInput, P as PutInput, W as WriteExecOptions, r as WriteResult, s as UpdateInput, t as DeleteInput, u as BatchWriteExecItem, v as BatchExecOptions, T as TransactWriteExecItem, M as ModelStatic, w as DDBModel, c as ChangeEvent } from '../maintenance-view-adapter-BATUh_I8.js';
2
- import '@aws-sdk/client-dynamodb';
1
+ import { n as Executor, o as DynamoDBOperation, R as ReadExecOptions, p as ExecutorResult, B as BatchGetExecInput, q as PutInput, W as WriteExecOptions, r as WriteResult, U as UpdateInput, s as DeleteInput, t as BatchWriteExecItem, u as BatchExecOptions, T as TransactWriteExecItem, M as ModelStatic, D as DDBModel, v as ChangeEvent } from '../key-DR7_lpyk.js';
2
+ import '../types-2PMXEn5x.js';
3
3
 
4
4
  /**
5
5
  * In-process physical store backing the GraphDDB Memory Test Adapter
@@ -1,14 +1,15 @@
1
1
  import {
2
2
  createCdcEmulator
3
- } from "../chunk-ZPNRLOKA.js";
3
+ } from "../chunk-GS4C5VGO.js";
4
+ import "../chunk-HNY2EJPV.js";
4
5
  import {
5
6
  ClientManager,
6
7
  MetadataRegistry,
7
8
  resolveModelClass
8
- } from "../chunk-PFFPLD4B.js";
9
+ } from "../chunk-L2NEDS7U.js";
9
10
  import {
10
11
  TableMapping
11
- } from "../chunk-PDUVTYC5.js";
12
+ } from "../chunk-XTWXMOHD.js";
12
13
 
13
14
  // src/memory/memory-store.ts
14
15
  function deepClone(value) {