graphddb 0.2.4 → 0.3.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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ChangeCaptureRegistry,
3
3
  resolveModelClass
4
- } from "./chunk-GCPEUPAA.js";
4
+ } from "./chunk-6AIAHP3A.js";
5
5
 
6
6
  // src/cdc/prng.ts
7
7
  var SeededRandom = class {
package/dist/cli.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  buildBridgeBundle,
4
4
  normalizeSelectSpec
5
- } from "./chunk-IQEOJVHI.js";
5
+ } from "./chunk-QEOFIXTN.js";
6
6
  import {
7
7
  MetadataRegistry
8
- } from "./chunk-GCPEUPAA.js";
8
+ } from "./chunk-6AIAHP3A.js";
9
9
 
10
10
  // src/cli.ts
11
11
  import { createRequire } from "module";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SelectableOf, g as PrimaryKeyOf, E as Executor, h as RetryPolicy, i as ExecutionPlanSpec, j as SegmentedKey, k as SelectBuilderSpec, l as RawCondition, m as TransactionSpec, n as Manifest, o as RetryOverride, p as ExecutionPlan, q as ResolvedKey, D as DynamoDBOperation, R as ReadExecOptions, a as ExecutorResult, B as BatchGetExecInput, P as PutInput, W as WriteExecOptions, b as WriteResult, U as UpdateInput, c as DeleteInput, d as BatchWriteExecItem, e as BatchExecOptions, T as TransactWriteExecItem, r as CdcEmulatorOptions, s as ChangeHandler, t as Unsubscribe, F as FaultSpec, u as ConcurrentRecomputeRef, C as ChangeEvent, v as EventLog, w as ReplayOptions, x as ShardId, y as TransactionItemSpec, z as Param, A as ParamDescriptor, G as DefinitionMap, O as OperationDefinition, f as DDBModel, M as ModelStatic, H as WriteDefinitionOptions, I as PartialQueryKeyOf, J as StrictSelectSpec, K as EntityInput, L as UniqueQueryKeyOf, N as EntityRef, Q as ConditionInput, V as QueryModelContract, X as QueryMethodSpec, Y as CommandModelContract, Z as CommandMethodSpec, _ as ContractSpec, $ as QuerySpec, a0 as CommandSpec, a1 as ContextSpec, a2 as OperationsDocument, a3 as AnyOperationDefinition, a4 as BridgeBundle, a5 as ConditionSpec } from './types-BEeQ_nSd.js';
2
- export { a6 as BatchDeleteRequest, a7 as BatchGetRequest, a8 as BatchGetResult, a9 as BatchPutRequest, aa as BatchResult, ab as BatchWriteRequest, ac as CONTRACT_RANGE_FANOUT_CONCURRENCY, ad as CdcMode, ae as ChangeBatch, af as ChangeEventName, ag as ClockMode, ah as Column, ai as ColumnMap, aj as CommandContractMethodSpec, ak as CommandInputShape, al as CommandMethod, am as CommandPlan, an as CommandResolutionTarget, ao as CommandResultKind, ap as CommandSelectShape, aq as CompiledFragment, ar as ComposeSpec, as as CondSlot, at as ConditionCheckInput, au as Connection, av as ContractCallSignature, aw as ContractCardinality, ax as ContractCommandParams, ay as ContractCommandResult, az as ContractComposeNode, aA as ContractFromRef, aB as ContractInputArity, aC as ContractItem, aD as ContractKeyFieldRef, aE as ContractKeyInput, aF as ContractKeyRef, aG as ContractKeySpec, aH as ContractKind, aI as ContractMethodOp, aJ as ContractParamRef, aK as ContractQueryParams, aL as ContractResolution, aM as DEFAULT_MAX_ATTEMPTS, aN as DEFAULT_RETRY_POLICY, aO as DeleteOptions, aP as DeriveEffect, aQ as DerivedEdgeWrite, aR as DerivedUpdate, aS as DescriptorBinding, aT as ENTITY_WRITES_MARKER, aU as EdgeEffect, aV as EffectPath, aW as EmitEffect, aX as EntityWritesDefinition, aY as EntityWritesShape, aZ as ExecutableCommandContract, a_ as ExecutableQueryContract, a$ as FilterInput, b0 as FilterSpec, b1 as FragmentInput, b2 as GsiDefinitionMarker, b3 as GsiOptions, b4 as IdempotencyEffect, b5 as InProcessWriteDescriptor, b6 as InputArity, b7 as KeyDefinitionMarker, b8 as KeySegment, b9 as KeySlot, ba as KeyStructure, bb as KeyedResult, bc as LIFECYCLE_CONTRACT_MARKER, bd as LifecycleContract, be as LifecycleEffects, bf as LiteralParam, bg as ManifestEntity, bh as ManifestField, bi as ManifestFieldType, bj as ManifestGsi, bk as ManifestKey, bl as ManifestRelation, bm as ManifestTable, bn as ModelRef, bo as MutateMode, bp as MutateOptions, bq as MutateParallelResult, br as MutateTransactionResult, bs as MutationBody, bt as MutationDescriptorMap, bu as MutationFragment, bv as MutationInputProxy, bw as MutationInputRef, bx as MutationIntent, by as NumberParam, bz as OperationKind, bA as OperationSpec, bB as ParallelOpResult, bC as ParamKind, bD as ParamSpec, bE as ParamStructure, bF as PlannedCommandMethod, bG as PutOptions, bH as QueryContractMethodSpec, bI as QueryEnvelopeResult, bJ as QueryKeyOf, bK as QueryMethod, bL as QueryResult, bM as RangeConditionSpec, bN as ReadEnvelope, bO as ReadOperationType, bP as ReadRouteDescriptor, bQ as ReadRouteOptions, bR as ReadRouteResult, bS as RecordedCompose, bT as RelationBuilder, bU as RelationSelect, bV as RelationSpec, bW as RequiresEffect, bX as Resolution, bY as RetryInfo, bZ as RetryOperationKind, b_ as SPEC_VERSION, b$ as SegmentSpec, c0 as SelectBuilder, c1 as SelectOf, c2 as StartingPosition, c3 as StreamViewType, c4 as StringParam, c5 as TransactionContext, c6 as TransactionItemType, c7 as UniqueEffect, c8 as Updatable, c9 as UpdateOptions, ca as WhenSpec, cb as WriteDescriptor, cc as WriteEnvelope, cd as WriteLifecyclePhase, ce as WriteOperationType, cf as WriteRecorder, cg as WriteResultProjection, ch as attachModelClass, ci as buildDeleteInput, cj as buildPutInput, ck as buildUpdateInput, cl as compileFragment, cm as compileMutationPlan, cn as compileSingleFragmentPlan, co as cond, cp as contractOfMethodSpec, cq as definePlan, cr as entityWrites, cs as executeBatchGet, ct as executeBatchWrite, cu as executeCommandMethod, cv as executeDelete, cw as executeKeyedBatchGet, cx as executePut, cy as executeQueryMethod, cz as executeRangeFanout, cA as executeTransaction, cB as executeUpdate, cC as from, cD as getEntityWrites, cE as gsi, cF as isColumn, cG as isCommandModelContract, cH as isCommandPlan, cI as isContractComposeNode, cJ as isContractFromRef, cK as isContractKeyFieldRef, cL as isContractKeyRef, cM as isContractParamRef, cN as isEntityWritesDefinition, cO as isKeySegment, cP as isLifecycleContract, cQ as isMutationFragment, cR as isMutationInputRef, cS as isParam, cT as isPlannedCommandMethod, cU as isQueryModelContract, cV as isRetryableError, cW as isRetryableTransactionCancellation, cX as k, cY as key, cZ as lifecyclePhaseForIntent, c_ as mintContractKeyFieldRef, c$ as mintContractParamRef, d0 as mutation, d1 as param, d2 as publicCommandModel, d3 as publicQueryModel, d4 as query, d5 as resolveLifecycle, d6 as wholeKeysSentinel } from './types-BEeQ_nSd.js';
1
+ import { S as SelectableOf, g as PrimaryKeyOf, h as RequestContext, i as Middleware, j as ReadRequestKind, k as CtxModel, l as ReadParams, m as ReadRequestCtx, D as DynamoDBOperation, I as Item, E as Executor, n as RetryPolicy, o as ExecutionPlanSpec, p as EntityMetadata, K as KeyDefinition, G as GsiDefinition, F as FieldOptions, q as DynamoType, r as RelationOptions, A as AggregateOptions, s as AggregateValue, t as SelectBuilderSpec, u as RawCondition, v as TransactionSpec, w as Manifest, x as RetryOverride, y as ExecutionPlan, z as FieldMetadata, H as ResolvedKey, R as ReadExecOptions, a as ExecutorResult, B as BatchGetExecInput, P as PutInput, W as WriteExecOptions, b as WriteResult, U as UpdateInput, c as DeleteInput, d as BatchWriteExecItem, e as BatchExecOptions, T as TransactWriteExecItem, J as CdcEmulatorOptions, L as ChangeHandler, N as Unsubscribe, O as FaultSpec, Q as ConcurrentRecomputeRef, C as ChangeEvent, V as EventLog, X as ReplayOptions, Y as ShardId, Z as RelationMetadata, _ as TransactionItemSpec, $ as Param, a0 as ParamDescriptor, a1 as DefinitionMap, a2 as OperationDefinition, f as DDBModel, M as ModelStatic, a3 as WriteDefinitionOptions, a4 as PartialQueryKeyOf, a5 as StrictSelectSpec, a6 as EntityInput, a7 as UniqueQueryKeyOf, a8 as EntityRef, a9 as ConditionInput, aa as QueryModelContract, ab as QueryMethodSpec, ac as CommandModelContract, ad as CommandMethodSpec, ae as ContractSpec, af as QuerySpec, ag as CommandSpec, ah as ContextSpec, ai as OperationsDocument, aj as AnyOperationDefinition, ak as BridgeBundle, al as ConditionSpec } from './types-xJRn5qkv.js';
2
+ export { am as AggregateMetadata, an as BatchDeleteRequest, ao as BatchGetOptions, ap as BatchGetRequest, aq as BatchGetResult, ar as BatchPutRequest, as as BatchResult, at as BatchWriteRequest, au as CONTRACT_RANGE_FANOUT_CONCURRENCY, av as CdcMode, aw as Change, ax as ChangeBatch, ay as ChangeEventName, az as ClockMode, aA as CollectionEffect, aB as CollectionOptions, aC as Column, aD as ColumnMap, aE as CommandContractMethodSpec, aF as CommandInputShape, aG as CommandMethod, aH as CommandPlan, aI as CommandResolutionTarget, aJ as CommandResultKind, aK as CommandSelectShape, aL as CompiledFragment, aM as ComposeSpec, aN as CondSlot, aO as ConditionCheckInput, aP as Connection, aQ as ContractCallSignature, aR as ContractCardinality, aS as ContractCommandParams, aT as ContractCommandResult, aU as ContractComposeNode, aV as ContractFromRef, aW as ContractInputArity, aX as ContractItem, aY as ContractKeyFieldRef, aZ as ContractKeyInput, a_ as ContractKeyRef, a$ as ContractKeySpec, b0 as ContractKind, b1 as ContractMethodOp, b2 as ContractParamRef, b3 as ContractQueryParams, b4 as ContractResolution, b5 as CounterAggregate, b6 as CounterEffect, b7 as CtxBase, b8 as DEFAULT_MAX_ATTEMPTS, b9 as DEFAULT_RETRY_POLICY, ba as DeleteOptions, bb as DeriveEffect, bc as DerivedEdgeWrite, bd as DerivedUpdate, be as DescriptorBinding, bf as ENTITY_WRITES_MARKER, bg as EdgeEffect, bh as EffectPath, bi as EmbeddedMetadata, bj as EmitEffect, bk as EntityWritesDefinition, bl as EntityWritesShape, bm as ExecutableCommandContract, bn as ExecutableQueryContract, bo as FilterInput, bp as FilterSpec, bq as FragmentInput, br as GsiDefinitionMarker, bs as GsiOptions, bt as IdempotencyEffect, bu as InProcessWriteDescriptor, bv as InputArity, bw as KeyDefinitionMarker, bx as KeySegment, by as KeySlot, bz as KeyStructure, bA as KeyedResult, bB as LIFECYCLE_CONTRACT_MARKER, bC as LifecycleContract, bD as LifecycleEffects, bE as LiteralParam, bF as MaintainConsistency, bG as MaintainEffect, bH as MaintainEvent, bI as MaintainItem, bJ as MaintainTrigger, bK as MaintainUpdateMode, bL as MaintenanceGraph, bM as ManifestEntity, bN as ManifestField, bO as ManifestFieldType, bP as ManifestGsi, bQ as ManifestKey, bR as ManifestRelation, bS as ManifestTable, bT as ModelRef, bU as MutateMode, bV as MutateOptions, bW as MutateParallelResult, bX as MutateTransactionResult, bY as MutationBody, bZ as MutationDescriptorMap, b_ as MutationFragment, b$ as MutationInputProxy, c0 as MutationInputRef, c1 as MutationIntent, c2 as NumberParam, c3 as OperationKind, c4 as OperationSpec, c5 as ParallelOpResult, c6 as ParamKind, c7 as ParamSpec, c8 as ParamStructure, c9 as PersistCtx, ca as PersistOrigin, cb as PlannedCommandMethod, cc as ProjectionMap, cd as ProjectionTransform, ce as ProjectionTransformOp, cf as PutOptions, cg as QueryContractMethodSpec, ch as QueryEnvelopeResult, ci as QueryKeyOf, cj as QueryMethod, ck as QueryResult, cl as RangeConditionSpec, cm as ReadEnvelope, cn as ReadOpCtx, co as ReadOpKind, cp as ReadOperationType, cq as ReadRouteDescriptor, cr as ReadRouteOptions, cs as ReadRouteResult, ct as RecordedCompose, cu as RelationBuilder, cv as RelationConsistency, cw as RelationLimitOptions, cx as RelationPattern, cy as RelationProjection, cz as RelationReadOptions, cA as RelationSelect, cB as RelationSpec, cC as RelationUpdateMode, cD as RelationWriteOptions, cE as RequiresEffect, cF as Resolution, cG as RetryInfo, cH as RetryOperationKind, cI as SPEC_VERSION, cJ as SegmentSpec, cK as SegmentedKey, cL as SelectBuilder, cM as SelectOf, cN as SnapshotEffect, cO as StartingPosition, cP as StreamViewType, cQ as StringParam, cR as TransactionContext, cS as TransactionItemType, cT as UniqueEffect, cU as Updatable, cV as UpdateOptions, cW as WhenSpec, cX as WriteCtx, cY as WriteDescriptor, cZ as WriteEnvelope, c_ as WriteInput, c$ as WriteKind, d0 as WriteLifecyclePhase, d1 as WriteMiddleware, d2 as WriteOperationType, d3 as WriteRecorder, d4 as WriteResultProjection, d5 as attachModelClass, d6 as buildDeleteInput, d7 as buildMaintenanceGraph, d8 as buildPutInput, d9 as buildUpdateInput, da as compileFragment, db as compileMutationPlan, dc as compileSingleFragmentPlan, dd as cond, de as contractOfMethodSpec, df as definePlan, dg as entityWrites, dh as executeBatchGet, di as executeBatchWrite, dj as executeCommandMethod, dk as executeDelete, dl as executeKeyedBatchGet, dm as executePut, dn as executeQueryMethod, dp as executeRangeFanout, dq as executeTransaction, dr as executeUpdate, ds as from, dt as getEntityWrites, du as gsi, dv as identity, dw as isColumn, dx as isCommandModelContract, dy as isCommandPlan, dz as isContractComposeNode, dA as isContractFromRef, dB as isContractKeyFieldRef, dC as isContractKeyRef, dD as isContractParamRef, dE as isEntityWritesDefinition, dF as isKeySegment, dG as isLifecycleContract, dH as isMaintainTrigger, dI as isMutationFragment, dJ as isMutationInputRef, dK as isParam, dL as isPlannedCommandMethod, dM as isQueryModelContract, dN as isRetryableError, dO as isRetryableTransactionCancellation, dP as k, dQ as key, dR as lifecyclePhaseForIntent, dS as maintainTrigger, dT as mintContractKeyFieldRef, dU as mintContractParamRef, dV as mutation, dW as param, dX as preview, dY as publicCommandModel, dZ as publicQueryModel, d_ as query, d$ as resolveLifecycle, e0 as wholeKeysSentinel } from './types-xJRn5qkv.js';
3
3
  import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
4
4
  import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
5
5
 
@@ -32,6 +32,99 @@ type ListOptions<T, S extends SelectableOf<T> = SelectableOf<T>> = {
32
32
  order?: 'ASC' | 'DESC';
33
33
  };
34
34
 
35
+ /**
36
+ * Read-middleware runtime (issue #138). Drives the registered {@link Middleware}
37
+ * list through the read hook points R1–R5 with the ordering docs/middleware.md
38
+ * specifies:
39
+ *
40
+ * - `before*` (R1 / R2) run **first-registered-first** (FIFO);
41
+ * - `afterFetch` (R3 / R4) and `onError` (R5) run **last-registered-first**
42
+ * (LIFO) — onion nesting without a `next()` callback.
43
+ *
44
+ * All hooks are async-first: each is always `await`ed, so a sync hook (returns
45
+ * `void` / a value) and an async hook (returns a `Promise`) compose uniformly.
46
+ *
47
+ * A {@link MiddlewareRuntime} is constructed ONCE per read at the request entry
48
+ * (R1) from the snapshot of registered middleware + the per-call `context`, then
49
+ * threaded — unchanged — through the root read and every relation fan-out op
50
+ * (the same way `retry` is threaded, see `RelationTraversalOptions`). Threading
51
+ * an immutable runtime (not re-reading the registry per op) keeps a read's hook
52
+ * set stable even if middleware is (un)registered mid-flight, and is
53
+ * concurrency-safe under parallel fan-out (#31).
54
+ *
55
+ * @see docs/middleware.md
56
+ */
57
+
58
+ /**
59
+ * The per-read middleware runtime. Immutable wrt the middleware list and the
60
+ * request `context`; threaded by reference into every fan-out op. A read with no
61
+ * registered middleware is represented by an EMPTY runtime whose `active` is
62
+ * `false`, so the hot read path can skip all hook plumbing with one check.
63
+ */
64
+ declare class MiddlewareRuntime {
65
+ /** The frozen, registration-ordered middleware snapshot for this read. */
66
+ private readonly chain;
67
+ /** The host-injected per-call context (`{}` when the read passed none). */
68
+ readonly context: RequestContext;
69
+ constructor(chain: readonly Middleware[], context: RequestContext);
70
+ /** `true` when at least one middleware is registered for this read. */
71
+ get active(): boolean;
72
+ /**
73
+ * Build the request-level context (R1 / R4 / R5 share it). Pure — no hooks run
74
+ * — so a caller can create it up front and still reference it from R5 even if
75
+ * R1 throws mid-chain.
76
+ */
77
+ requestCtx(kind: ReadRequestKind, model: CtxModel, params: ReadParams): ReadRequestCtx;
78
+ /**
79
+ * R1 — request entry, before key-resolution / plan. Builds the
80
+ * {@link ReadRequestCtx} then runs every `read.before` FIFO; a hook may mutate
81
+ * `ctx.params` and may `throw` to cancel the read. Returns the (possibly
82
+ * hook-mutated) ctx so the caller can re-read `params` and reuse the SAME ctx
83
+ * for R4 / R5 (shared `state`).
84
+ */
85
+ runRequestBefore(kind: ReadRequestKind, model: CtxModel, params: ReadParams): Promise<ReadRequestCtx>;
86
+ /**
87
+ * R4 — final hydrated, relation-merged result. Runs every `read.afterFetch`
88
+ * LIFO, threading each hook's return value into the next (onion), and returns
89
+ * the final (possibly replaced) result. `ctx` is the SAME object R1 produced,
90
+ * so a `before`/`afterFetch` pair shares `ctx.state`.
91
+ */
92
+ runRequestAfter<T>(ctx: ReadRequestCtx, result: T): Promise<T>;
93
+ /**
94
+ * R5 (request-level) — runs every `read.onError` LIFO. A hook may **recover**
95
+ * by RETURNING a non-`undefined` value: it becomes the read's resolved result
96
+ * in place of throwing, and the FIRST such hook (in LIFO order) wins and
97
+ * short-circuits the rest of the chain. If every hook declines (returns
98
+ * `undefined`/`void`), the original error rethrows — preserving the
99
+ * "failed read still rejects" default. (Proposal R5 + appendix A: onError may
100
+ * "rethrow / recover"; hooks are unrestricted.)
101
+ *
102
+ * The recovered value is returned as `T` (the read's result type); its shape
103
+ * is the host's responsibility — a hook recovering a `query` should return an
104
+ * item / `null`, one recovering a `list` a `{ items, cursor }`.
105
+ */
106
+ runRequestError<T>(ctx: ReadRequestCtx, err: unknown): Promise<T>;
107
+ /**
108
+ * Drive ONE physical op (root read or any fan-out fetch) through R2 → send →
109
+ * R3, with R5 (op-level) on failure:
110
+ *
111
+ * 1. R2 (`read.op.before`, FIFO) — may mutate `ctx.operation`, may `throw`;
112
+ * 2. the caller's `send` runs against the (possibly mutated) operation;
113
+ * 3. R3 (`read.op.afterFetch`, LIFO) — transforms the raw items (onion);
114
+ * 4. on any throw from `send` (or R2 / R3), R5 (`read.op.onError`, LIFO) runs:
115
+ * a hook may **recover** by RETURNING an `Item[]` (those become this op's
116
+ * items and the pipeline continues); the FIRST hook that returns items wins
117
+ * and short-circuits the chain. If none recovers, the original error
118
+ * rethrows.
119
+ *
120
+ * Returns the (possibly R3-replaced, or R5-recovered) items. The `send` closure
121
+ * receives the final operation so a caller that built its operation from
122
+ * `ctx.operation` before this call still observes an R2 mutation (the caller
123
+ * MUST send `ctx.operation`, which this method passes through).
124
+ */
125
+ runOp(operation: DynamoDBOperation, relationPath: readonly string[], model: CtxModel, send: (operation: DynamoDBOperation) => Promise<Item[]>): Promise<Item[]>;
126
+ }
127
+
35
128
  /**
36
129
  * The ONE shared `TransactWriteItems` commit orchestration (issue #97).
37
130
  *
@@ -99,6 +192,15 @@ declare class ClientManager {
99
192
  * read at call time by the default {@link RetryingExecutor}.
100
193
  */
101
194
  private static retryPolicy;
195
+ /**
196
+ * The host-runtime read-middleware registry (issue #50 / #138) — a process-wide
197
+ * ordered list of {@link Middleware}, the same global-config + injectable-seam
198
+ * pattern as {@link retryPolicy} / {@link executor}. Registered via
199
+ * {@link DDBModel.use} / {@link use}, snapshotted per read, and NEVER serialized
200
+ * (it never touches the planner / spec / `operations.json`, so the bridge #48 is
201
+ * unaffected). {@link reset} clears it.
202
+ */
203
+ private static middleware;
102
204
  static setClient(client: DynamoDBClient): void;
103
205
  static getClient(): DynamoDBClient;
104
206
  static getDocumentClient(): Promise<DynamoDBDocumentClient>;
@@ -129,6 +231,21 @@ declare class ClientManager {
129
231
  static setRetryPolicy(policy: RetryPolicy | null): void;
130
232
  /** The globally-configured retry policy, or `null` for the built-in default. */
131
233
  static getRetryPolicy(): RetryPolicy | null;
234
+ /**
235
+ * Register a read {@link Middleware} (issue #50 / #138). Appended last, so its
236
+ * `before*` hooks run last (FIFO) and its `afterFetch` / `onError` hooks run
237
+ * first (LIFO). Mirrors {@link setRetryPolicy}; backs {@link DDBModel.use}.
238
+ */
239
+ static use(mw: Middleware): void;
240
+ /** Remove every registered read middleware (teardown / tests). Backs {@link DDBModel.clearMiddleware}. */
241
+ static clearMiddleware(): void;
242
+ /**
243
+ * A point-in-time, registration-ordered snapshot of the registered read
244
+ * middleware. A read takes this once at entry (R1) and threads it down, so the
245
+ * read's hook set stays stable even if middleware is registered / cleared
246
+ * mid-flight.
247
+ */
248
+ static getMiddleware(): readonly Middleware[];
132
249
  /** @internal — for testing only */
133
250
  static reset(): void;
134
251
  }
@@ -196,71 +313,6 @@ interface RelationExecutionPlan {
196
313
  readonly resultPaths: readonly string[];
197
314
  }
198
315
 
199
- type DynamoType = 'S' | 'N' | 'BOOL' | 'B' | 'SS' | 'NS' | 'L' | 'M';
200
- interface FieldOptions {
201
- format?: 'datetime' | 'date';
202
- readonly?: boolean;
203
- serialize?: (value: unknown) => unknown;
204
- deserialize?: (value: unknown) => unknown;
205
- }
206
- interface FieldMetadata {
207
- propertyName: string;
208
- dynamoType: DynamoType;
209
- options?: FieldOptions;
210
- /**
211
- * For a `@literal(...)` field — a `string`-stored field whose value is one of a
212
- * known, finite set — the allowed literal values, in declaration order (issue
213
- * #75). Recorded purely as model metadata so a Contract param bound into the
214
- * field can recover the precise `literal` param-kind from its bind position
215
- * (`src/spec/contracts.ts`), serializing as `{ type: 'literal', literals: [...] }`
216
- * instead of the imprecise `'string'`. JSON-safe by construction (a non-empty
217
- * array of strings). `undefined` for every other field decorator (`@string`,
218
- * `@number`, …), so plain fields are byte-for-byte unchanged (backward compat).
219
- */
220
- literals?: readonly string[];
221
- }
222
-
223
- interface KeyDefinition {
224
- /** The canonical structured key (PK/SK segment lists). */
225
- segmented: SegmentedKey;
226
- inputFieldNames: string[];
227
- }
228
- interface GsiDefinition {
229
- indexName: string;
230
- /** The canonical structured key (PK/SK segment lists). */
231
- segmented: SegmentedKey;
232
- inputFieldNames: string[];
233
- unique: boolean;
234
- }
235
- interface RelationLimitOptions {
236
- default: number;
237
- max: number;
238
- }
239
- interface RelationOptions {
240
- limit?: RelationLimitOptions;
241
- order?: 'ASC' | 'DESC';
242
- }
243
- interface RelationMetadata {
244
- type: 'hasMany' | 'hasOne' | 'belongsTo';
245
- propertyName: string;
246
- targetFactory: () => new (...args: unknown[]) => unknown;
247
- keyBinding: Record<string, string>;
248
- options?: RelationOptions;
249
- }
250
- interface EmbeddedMetadata {
251
- propertyName: string;
252
- modelFactory: () => new (...args: unknown[]) => unknown;
253
- }
254
- interface EntityMetadata {
255
- tableName: string;
256
- prefix: string;
257
- fields: FieldMetadata[];
258
- primaryKey: KeyDefinition | null;
259
- gsiDefinitions: GsiDefinition[];
260
- relations: RelationMetadata[];
261
- embeddedFields: EmbeddedMetadata[];
262
- }
263
-
264
316
  interface LintRule {
265
317
  id: string;
266
318
  severity: 'error' | 'warning';
@@ -284,6 +336,22 @@ declare class Linter {
284
336
  declare class MetadataRegistry {
285
337
  private static store;
286
338
  private static _linter;
339
+ /**
340
+ * A monotonically-increasing counter bumped on every change to the set of
341
+ * registered models ({@link register} / {@link clear}). It lets a consumer that
342
+ * caches a structure derived from the whole registry (e.g. the maintenance graph
343
+ * in `src/spec/mutation-command.ts`) detect that the registry has changed since
344
+ * the cache was built and rebuild — closing the silent-drop window where a model
345
+ * registered after a cache build would be invisible to it. Only the membership
346
+ * of {@link store} changes here; per-model lazy finalize ({@link finalize}) does
347
+ * not bump it (it adds no relations/triggers, so it cannot change the graph).
348
+ */
349
+ private static _generation;
350
+ /**
351
+ * The current registry generation (see {@link _generation}). A consumer caches
352
+ * the value it built against and rebuilds when it no longer matches.
353
+ */
354
+ static get generation(): number;
287
355
  static register(target: Function, metadata: EntityMetadata): void;
288
356
  static get(target: Function): EntityMetadata;
289
357
  static has(target: Function): boolean;
@@ -414,9 +482,92 @@ declare const map: FieldDecorator<Record<string, unknown>>;
414
482
  declare function embedded(modelFactory: () => new (...args: unknown[]) => unknown): (_value: undefined, context: ClassFieldDecoratorContext) => void;
415
483
 
416
484
  declare function hasMany(targetFactory: () => new (...args: unknown[]) => unknown, keyBinding: Record<string, string>, options?: RelationOptions): (_value: undefined, context: ClassFieldDecoratorContext) => void;
417
- declare function belongsTo(targetFactory: () => new (...args: unknown[]) => unknown, keyBinding: Record<string, string>): (_value: undefined, context: ClassFieldDecoratorContext) => void;
485
+ declare function belongsTo(targetFactory: () => new (...args: unknown[]) => unknown, keyBinding: Record<string, string>, options?: RelationOptions): (_value: undefined, context: ClassFieldDecoratorContext) => void;
418
486
  declare function hasOne(targetFactory: () => new (...args: unknown[]) => unknown, keyBinding: Record<string, string>): (_value: undefined, context: ClassFieldDecoratorContext) => void;
419
487
 
488
+ /**
489
+ * `@aggregate` — a **scalar-aggregate** field decorator (Epic #118 §5.2 counter /
490
+ * latest preset; issue #122).
491
+ *
492
+ * Where `@hasMany` / `@belongsTo` declare a *navigation* (a relation yielding
493
+ * model instances) and `@string` / `@number` declare a *stored* attribute,
494
+ * `@aggregate` declares a **scalar value derived by aggregating a source
495
+ * entity's rows**. It is a field (the decorated property holds a `number` /
496
+ * `string`), but — like a relation — it carries a source `targetFactory` +
497
+ * `keyBinding` selecting the rows it aggregates. The declaration surface mirrors
498
+ * the RFC §5.2 example:
499
+ *
500
+ * ```ts
501
+ * @model({ table: 'AppTable', prefix: 'THREAD' })
502
+ * class ThreadSummaryModel extends DDBModel {
503
+ * @string threadId!: string;
504
+ *
505
+ * @aggregate(() => PostModel, { threadId: 'threadId' }, {
506
+ * pattern: 'counter', value: count(),
507
+ * write: { maintainedOn: ['Post.created', 'Post.removed'] },
508
+ * })
509
+ * postCount!: number;
510
+ *
511
+ * @aggregate(() => PostModel, { threadId: 'threadId' }, {
512
+ * pattern: 'counter', value: max('createdAt'),
513
+ * write: { maintainedOn: ['Post.created'] },
514
+ * })
515
+ * lastPostAt!: string;
516
+ * }
517
+ * ```
518
+ *
519
+ * ## Scope (issue #122)
520
+ *
521
+ * This is the **declaration surface only** — the decorator records an
522
+ * {@link AggregateMetadata} into its own collector array (drained by `@model`
523
+ * into `EntityMetadata.aggregates`), exactly as the relation decorators push to
524
+ * the relation collector. The aggregation *effect* (maintenance graph #124,
525
+ * compile injection #125, runtime #127) is out of scope and consumes this
526
+ * metadata later.
527
+ *
528
+ * ## Vocabulary reuse (no double definition)
529
+ *
530
+ * `options.pattern` is the shared {@link RelationPattern} (typically `'counter'`)
531
+ * and `options.write` is the shared {@link RelationWriteOptions} — `maintainedOn`
532
+ * triggers on the `created|updated|removed` event vocabulary plus
533
+ * `consistency` (`transactional|eventual`) / `updateMode` (`mutation|stream`).
534
+ * Both were defined by B (#121) in `src/metadata/types.ts` and are **reused**
535
+ * here, never re-defined.
536
+ *
537
+ * @param targetFactory - Resolves the source entity whose rows are aggregated
538
+ * (lazy, mirroring the relation decorators, to tolerate circular imports).
539
+ * @param keyBinding - The key binding selecting the source rows
540
+ * (target key field → source field).
541
+ * @param options - The aggregate preset / value / maintenance-write declaration.
542
+ */
543
+ declare function aggregate(targetFactory: () => new (...args: unknown[]) => unknown, keyBinding: Record<string, string>, options: AggregateOptions): (_value: undefined, context: ClassFieldDecoratorContext) => void;
544
+ /**
545
+ * Value helper for an `@aggregate` field: the **count** aggregation — the
546
+ * cardinality of source rows matched by the key binding (Epic #118 §5.2
547
+ * `counter` preset, `count()`). It takes no source field (it counts rows, not a
548
+ * value), so the decorated field is a `number` (e.g. `postCount!: number`).
549
+ *
550
+ * Produces the {@link AggregateValue} IR node `{ op: 'count' }`. This is the
551
+ * standalone, recorder-free authoring helper used directly in the decorator
552
+ * argument (mirroring the standalone `preview(...)` / `identity(...)` projection
553
+ * helpers A (#120) exposes for relation options).
554
+ */
555
+ declare function count(): AggregateValue;
556
+ /**
557
+ * Value helper for an `@aggregate` field: the **max** aggregation — the maximum
558
+ * value of a named source `field` across the rows matched by the key binding
559
+ * (Epic #118 §5.2 `counter` / latest preset, `max('createdAt')`). The decorated
560
+ * field's type matches the source field's (e.g. `lastPostAt!: string` ←
561
+ * `max('createdAt')`).
562
+ *
563
+ * Produces the {@link AggregateValue} IR node `{ op: 'max', field }`.
564
+ *
565
+ * @param field - The source field whose maximum is tracked (e.g. `'createdAt'`).
566
+ * @throws if `field` is not a non-empty string (an aggregation over no source
567
+ * field is meaningless — a defensive runtime check mirroring `@literal`'s).
568
+ */
569
+ declare function max(field: string): AggregateValue;
570
+
420
571
  declare class TableMapping {
421
572
  private static mapping;
422
573
  static set(mapping: Record<string, string>): void;
@@ -558,6 +709,14 @@ interface QueryOptions {
558
709
  * "What to fetch" stays declarative; "what object to load it onto" is host-side.
559
710
  */
560
711
  hydrate?: (raw: Record<string, unknown> | null) => unknown;
712
+ /**
713
+ * Host-injected per-call read context (issue #50 / #138) — exposed to every
714
+ * read hook (R1–R5) as `ctx.context`, and threaded UNCHANGED to every relation
715
+ * fan-out op so a hook (e.g. a tenant-scope R2 filter) applies across the root
716
+ * read AND the fan-out fetches. A host-language value, NEVER serialized into the
717
+ * SSoT / `operations.json` (the bridge #48 is unaffected). Absent ⇒ `{}`.
718
+ */
719
+ context?: RequestContext;
561
720
  }
562
721
  declare function executeQuery(modelClass: Function, key: Record<string, unknown>, selectSpec: Record<string, unknown>, options?: QueryOptions): Promise<Record<string, unknown> | null>;
563
722
 
@@ -580,6 +739,28 @@ interface ListInput {
580
739
  * retry. Omit to use the configured (or built-in default) policy.
581
740
  */
582
741
  retry?: RetryOverride;
742
+ /**
743
+ * Host-injected per-call read context (issue #50 / #138) — exposed to every
744
+ * read hook as `ctx.context`. Used only when this `list` is the read ENTRY
745
+ * point (the public `DDBModel.list` / a `list` envelope route): a fresh
746
+ * {@link MiddlewareRuntime} is built from it. Ignored when a relation fan-out
747
+ * threads an already-built {@link mw} (the runtime then carries the root read's
748
+ * context). Absent ⇒ `{}`.
749
+ */
750
+ context?: RequestContext;
751
+ /**
752
+ * @internal The per-read middleware runtime, threaded UNCHANGED from a relation
753
+ * fan-out (issue #138) so the hasMany Query's op-level hooks (R2/R3/R5) fire
754
+ * with the root read's context + the fan-out's {@link relationPath}. When absent
755
+ * at the entry point, one is built from {@link context}.
756
+ */
757
+ mw?: MiddlewareRuntime;
758
+ /**
759
+ * @internal The relation path to THIS list read — `[]` (or absent) for a
760
+ * top-level `list`; the fan-out leg (e.g. `['orders']`) for a hasMany Query.
761
+ * Populates the op-level ctx's `relationPath` (issue #138).
762
+ */
763
+ relationPath?: readonly string[];
583
764
  }
584
765
  /**
585
766
  * Public list result returned by `DDBModel.list()`. Contains exactly the
@@ -978,10 +1159,14 @@ declare function decodePerKeyCursor(cursor: string, expectedKey: Record<string,
978
1159
  /**
979
1160
  * Creates a Linter pre-loaded with rules safe for Entity registration.
980
1161
  *
981
- * Rules included: no-scan, require-limit, gsi-ambiguity, missing-gsi, relation-depth.
982
- * query-boundary is excluded because it flags non-unique GSIs which
983
- * are valid for list() operations; boundary enforcement is handled
984
- * at runtime by the planner's boundary-check.
1162
+ * Rules included: no-scan, require-limit, gsi-ambiguity, missing-gsi,
1163
+ * relation-depth, same-partition-preset, plus the Epic #118 Phase 1 maintenance
1164
+ * validators (issue #126): missing-context, 400kb, hot-partition, fan-out,
1165
+ * multi-maintainer-same-row. The maintenance validators are no-ops for any
1166
+ * relation that does not declare `write.maintainedOn`, so unannotated models are
1167
+ * unaffected. query-boundary is excluded because it flags non-unique GSIs which
1168
+ * are valid for list() operations; boundary enforcement is handled at runtime by
1169
+ * the planner's boundary-check.
985
1170
  */
986
1171
  declare function createDefaultLinter(): Linter;
987
1172
 
@@ -1031,6 +1216,23 @@ interface RelationTraversalOptions {
1031
1216
  * grouping / bound and observe the traversal execute accordingly.
1032
1217
  */
1033
1218
  plan?: RelationExecutionPlan;
1219
+ /**
1220
+ * The per-read middleware runtime (issue #50 / #138), threaded UNCHANGED from
1221
+ * the read's entry point into every relation fan-out fetch — exactly like
1222
+ * `retry`. Each fan-out send is wrapped so the op-level hooks R2 (before send) /
1223
+ * R3 (raw items) / R5 (op error) fire for that physical op too, with
1224
+ * `ctx.relationPath` identifying the fan-out leg (`['orders']`,
1225
+ * `['orders','product']`, …) and `ctx.model` the relation's target model.
1226
+ * Absent → {@link NO_MIDDLEWARE} (no hooks).
1227
+ */
1228
+ mw?: MiddlewareRuntime;
1229
+ /**
1230
+ * The relation path from the root read to the CURRENT traversal level — `[]`
1231
+ * at the root, extended by each relation's property name as the traversal
1232
+ * recurses. Used only to populate `ctx.relationPath` for the op-level hooks
1233
+ * (R2/R3/R5). Threaded alongside {@link mw}.
1234
+ */
1235
+ relationPath?: readonly string[];
1034
1236
  }
1035
1237
  /**
1036
1238
  * Validates that the select depth does not exceed maxDepth.
@@ -2126,4 +2328,4 @@ declare function assertBundleSerializable(bundle: BridgeBundle): void;
2126
2328
  */
2127
2329
  declare function buildBridgeBundle(queries?: DefinitionMap, commands?: DefinitionMap, registry?: typeof MetadataRegistry, transactions?: Record<string, TransactionDefinition>, contractInputs?: ContractInputs): BridgeBundle;
2128
2330
 
2129
- export { type AnyModelContract, AnyOperationDefinition, BATCH_GET_MAX_KEYS, BATCH_WRITE_MAX_ITEMS, BatchExecOptions, BatchGetExecInput, BatchWriteExecItem, BridgeBundle, type BuiltContracts, CdcEmulator, CdcEmulatorOptions, ChangeEvent, ChangeHandler, ClientManager, type CollectParams, CommandMethodSpec, CommandModelContract, CommandSpec, ConcurrentRecomputeRef, type ConditionExpressionResult, ConditionInput, ConditionSpec, type ContextOwnership, type ContextOwnershipMap, ContextSpec, type ContractBoundaryViolation, type ContractInputs, type ContractMap, type ContractN1Violation, ContractSpec, DDBModel, DefinitionMap, DeleteInput, DynamoDBOperation, DynamoExecutor, type DynamoType, EDGE_WRITES_MARKER, type EdgeLifecycle, type EdgeWriteDeclaration, type EdgeWriteRecorder, type EdgeWritesDefinition, type EmbeddedMetadata, type EntityMetadata, EntityRef, EventLog, ExecutionPlan, Executor, ExecutorResult, type ExplainInput, FaultSpec, type FieldMetadata, type FieldOptions, type FilterExpressionResult, type GsiDefinition, type KeyDefinition, type LintResult, type LintRule, Linter, type ListInput, type ListOptions, MAX_TRANSACT_ITEMS, Manifest, MetadataRegistry, type ModelOptions, ModelStatic, OLD_VALUE_NAMESPACE, OperationDefinition, OperationsDocument, Param, ParamDescriptor, type Parameterize, PartialQueryKeyOf, type PerKeyCursorEnvelope, type PlanInput, PrimaryKeyOf, type ProjectionResult, PutInput, QueryMethodSpec, QueryModelContract, type QueryOptions$1 as QueryOptions, QuerySpec, RawCondition, ReadExecOptions, type RelationLimitOptions, type RelationMetadata, type RelationOptions, type RelationTraversalOptions, ReplayOptions, ResolvedKey, RetryOverride, RetryPolicy, RetryingExecutor, SegmentedKey, SelectableOf, ShardId, TableMapping, TransactWriteExecItem, type TransactionDefinition, TransactionItemSpec, type TransactionParamShape, type TransactionRef, TransactionSpec, type TxConditionCheckOptions, type TxForEachInstruction, type TxForEachOptions, type TxInstruction, type TxRecorder, type TxWriteInstruction, type TxWriteOptions, UniqueQueryKeyOf, type UpdateExpressionResult, UpdateInput, type WhenComparison, WriteDefinitionOptions, WriteExecOptions, WriteResult, assertBundleSerializable, assertContractBoundaries, assertContractN1Safe, assertJsonSerializable, assertSupportedCondition, belongsTo, binary, boolean, buildBridgeBundle, buildConditionExpression, buildContexts, buildContracts, buildManifest, buildOperations, buildProjection, buildQuerySpec, buildTransactionSpec, buildTransactions, buildUpdateExpression, collectContractBoundaryViolations, collectContractN1Violations, compileFilterExpression, createCdcEmulator, createDefaultLinter, datetime, decodeCursor, decodePerKeyCursor, defineCommands, defineDelete, defineList, definePut, defineQueries, defineQuery, defineTransaction, defineTransactions, defineUpdate, deriveEdgeWriteItems, deriveEdgeWriteItemsFor, deriveModelEdgeWriteItems, derivePrefix, detectRelationFields, edgeWrites, embedded, encodeCursor, encodePerKeyCursor, evaluateFilter, execute, executeDeclarativeTransaction, executeExplain, executeList, executeQuery, expandTransaction, field, getEdgeWrites, getImplicitKeyFields, gsiAmbiguityRule, hasMany, hasOne, hydrate, isEdgeWritesDefinition, isSelectBuilder, isTransactionRef, list, literal, map, missingGsiRule, model, noScanRule, number, numberSet, plan, queryBoundaryRule, relationDepthRule, requireLimitRule, resolveKey, resolveRelations, serializeContractKey, serializeFieldValue, string, stringSet, validateDepth, validateGsiAmbiguity, when };
2331
+ export { AggregateOptions, AggregateValue, type AnyModelContract, AnyOperationDefinition, BATCH_GET_MAX_KEYS, BATCH_WRITE_MAX_ITEMS, BatchExecOptions, BatchGetExecInput, BatchWriteExecItem, BridgeBundle, type BuiltContracts, CdcEmulator, CdcEmulatorOptions, ChangeEvent, ChangeHandler, ClientManager, type CollectParams, CommandMethodSpec, CommandModelContract, CommandSpec, ConcurrentRecomputeRef, type ConditionExpressionResult, ConditionInput, ConditionSpec, type ContextOwnership, type ContextOwnershipMap, ContextSpec, type ContractBoundaryViolation, type ContractInputs, type ContractMap, type ContractN1Violation, ContractSpec, CtxModel, DDBModel, DefinitionMap, DeleteInput, DynamoDBOperation, DynamoExecutor, DynamoType, EDGE_WRITES_MARKER, type EdgeLifecycle, type EdgeWriteDeclaration, type EdgeWriteRecorder, type EdgeWritesDefinition, EntityMetadata, EntityRef, EventLog, ExecutionPlan, Executor, ExecutorResult, type ExplainInput, FaultSpec, FieldMetadata, FieldOptions, type FilterExpressionResult, GsiDefinition, Item, KeyDefinition, type LintResult, type LintRule, Linter, type ListInput, type ListOptions, MAX_TRANSACT_ITEMS, Manifest, MetadataRegistry, Middleware, type ModelOptions, ModelStatic, OLD_VALUE_NAMESPACE, OperationDefinition, OperationsDocument, Param, ParamDescriptor, type Parameterize, PartialQueryKeyOf, type PerKeyCursorEnvelope, type PlanInput, PrimaryKeyOf, type ProjectionResult, PutInput, QueryMethodSpec, QueryModelContract, type QueryOptions$1 as QueryOptions, QuerySpec, RawCondition, ReadExecOptions, ReadParams, ReadRequestCtx, ReadRequestKind, RelationMetadata, RelationOptions, type RelationTraversalOptions, ReplayOptions, RequestContext, ResolvedKey, RetryOverride, RetryPolicy, RetryingExecutor, SelectableOf, ShardId, TableMapping, TransactWriteExecItem, type TransactionDefinition, TransactionItemSpec, type TransactionParamShape, type TransactionRef, TransactionSpec, type TxConditionCheckOptions, type TxForEachInstruction, type TxForEachOptions, type TxInstruction, type TxRecorder, type TxWriteInstruction, type TxWriteOptions, UniqueQueryKeyOf, type UpdateExpressionResult, UpdateInput, type WhenComparison, WriteDefinitionOptions, WriteExecOptions, WriteResult, aggregate, assertBundleSerializable, assertContractBoundaries, assertContractN1Safe, assertJsonSerializable, assertSupportedCondition, belongsTo, binary, boolean, buildBridgeBundle, buildConditionExpression, buildContexts, buildContracts, buildManifest, buildOperations, buildProjection, buildQuerySpec, buildTransactionSpec, buildTransactions, buildUpdateExpression, collectContractBoundaryViolations, collectContractN1Violations, compileFilterExpression, count, createCdcEmulator, createDefaultLinter, datetime, decodeCursor, decodePerKeyCursor, defineCommands, defineDelete, defineList, definePut, defineQueries, defineQuery, defineTransaction, defineTransactions, defineUpdate, deriveEdgeWriteItems, deriveEdgeWriteItemsFor, deriveModelEdgeWriteItems, derivePrefix, detectRelationFields, edgeWrites, embedded, encodeCursor, encodePerKeyCursor, evaluateFilter, execute, executeDeclarativeTransaction, executeExplain, executeList, executeQuery, expandTransaction, field, getEdgeWrites, getImplicitKeyFields, gsiAmbiguityRule, hasMany, hasOne, hydrate, isEdgeWritesDefinition, isSelectBuilder, isTransactionRef, list, literal, map, max, missingGsiRule, model, noScanRule, number, numberSet, plan, queryBoundaryRule, relationDepthRule, requireLimitRule, resolveKey, resolveRelations, serializeContractKey, serializeFieldValue, string, stringSet, validateDepth, validateGsiAmbiguity, when };