graphddb 0.1.0 → 0.2.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/README.md +63 -9
- package/dist/{chunk-6LEHSX45.js → chunk-F27INYI2.js} +2839 -1081
- package/dist/cli.js +28 -5
- package/dist/index.d.ts +59 -3318
- package/dist/index.js +22 -1822
- package/dist/testing/index.d.ts +2 -2
- package/dist/types-D6qLpw2M.d.ts +4514 -0
- package/package.json +7 -2
- package/dist/types-CDrWiPxp.d.ts +0 -1203
package/dist/types-CDrWiPxp.d.ts
DELETED
|
@@ -1,1203 +0,0 @@
|
|
|
1
|
-
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
2
|
-
|
|
3
|
-
interface RangeCondition {
|
|
4
|
-
operator: 'begins_with';
|
|
5
|
-
key: string;
|
|
6
|
-
value: unknown;
|
|
7
|
-
}
|
|
8
|
-
interface DynamoDBOperationBase {
|
|
9
|
-
tableName: string;
|
|
10
|
-
projectionExpression?: string;
|
|
11
|
-
expressionAttributeNames?: Record<string, string>;
|
|
12
|
-
consistentRead?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* Server-side DynamoDB FilterExpression (independent of the
|
|
15
|
-
* KeyConditionExpression and ProjectionExpression). Names/values here are
|
|
16
|
-
* merged into the command's ExpressionAttributeNames/Values by the executor.
|
|
17
|
-
*/
|
|
18
|
-
filterExpression?: string;
|
|
19
|
-
filterExpressionAttributeNames?: Record<string, string>;
|
|
20
|
-
filterExpressionAttributeValues?: Record<string, unknown>;
|
|
21
|
-
}
|
|
22
|
-
interface GetItemOperation extends DynamoDBOperationBase {
|
|
23
|
-
type: 'GetItem';
|
|
24
|
-
keyCondition: Record<string, unknown>;
|
|
25
|
-
}
|
|
26
|
-
interface QueryOperation extends DynamoDBOperationBase {
|
|
27
|
-
type: 'Query';
|
|
28
|
-
indexName?: string;
|
|
29
|
-
keyCondition: Record<string, unknown>;
|
|
30
|
-
rangeCondition?: RangeCondition;
|
|
31
|
-
scanIndexForward?: boolean;
|
|
32
|
-
limit?: number;
|
|
33
|
-
exclusiveStartKey?: Record<string, unknown>;
|
|
34
|
-
}
|
|
35
|
-
interface BatchGetItemOperation extends DynamoDBOperationBase {
|
|
36
|
-
type: 'BatchGetItem';
|
|
37
|
-
keys: Record<string, unknown>[];
|
|
38
|
-
}
|
|
39
|
-
type DynamoDBOperation = GetItemOperation | QueryOperation | BatchGetItemOperation;
|
|
40
|
-
interface ExecutionPlan {
|
|
41
|
-
operations: DynamoDBOperation[];
|
|
42
|
-
}
|
|
43
|
-
type ResolvedKey = {
|
|
44
|
-
type: 'pk';
|
|
45
|
-
partial: boolean;
|
|
46
|
-
inputFieldNames: string[];
|
|
47
|
-
} | {
|
|
48
|
-
type: 'gsi';
|
|
49
|
-
indexName: string;
|
|
50
|
-
unique: boolean;
|
|
51
|
-
partial: boolean;
|
|
52
|
-
inputFieldNames: string[];
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Internal brand identifying a {@link RawCondition} produced by {@link cond}.
|
|
57
|
-
*/
|
|
58
|
-
declare const RAW_COND: unique symbol;
|
|
59
|
-
/**
|
|
60
|
-
* A compiled raw filter fragment produced by the {@link cond} escape hatch.
|
|
61
|
-
* Column references are pre-resolved to `#`-aliased placeholders and embedded
|
|
62
|
-
* values to `:`-aliased parameters; the executor merges these alongside the
|
|
63
|
-
* declarative filter's names/values.
|
|
64
|
-
*
|
|
65
|
-
* @typeParam M - The owning model brand. A `cond` is only assignable to a
|
|
66
|
-
* `FilterInput<M>` of the same model, so embedding a different model's column
|
|
67
|
-
* is a compile error.
|
|
68
|
-
*/
|
|
69
|
-
interface RawCondition<M = unknown> {
|
|
70
|
-
readonly [RAW_COND]: true;
|
|
71
|
-
/** @internal phantom — owning-model brand. */
|
|
72
|
-
readonly __model?: M;
|
|
73
|
-
/** Expression fragment with `{name}` / `{value}` markers already inlined. */
|
|
74
|
-
readonly template: TemplateStringsArray;
|
|
75
|
-
/** The interpolated parts, each either a Column ref or an embedded value. */
|
|
76
|
-
readonly parts: unknown[];
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* The interpolation slot type accepted by {@link cond}. A column reference
|
|
80
|
-
* (`Model.col.<field>`) — required for naming a column — or an embedded value
|
|
81
|
-
* that is type-checked against that column. Bare column-name strings are NOT
|
|
82
|
-
* accepted; you must use a {@link Column} reference, which keeps the fragment
|
|
83
|
-
* refactor-safe.
|
|
84
|
-
*
|
|
85
|
-
* @typeParam M - The owning model brand. Constrains all columns to one model,
|
|
86
|
-
* so passing another model's column is a compile error.
|
|
87
|
-
*/
|
|
88
|
-
type CondSlot<M> = Column<any, M> | string | number | boolean | Date;
|
|
89
|
-
/**
|
|
90
|
-
* Raw DynamoDB condition escape hatch.
|
|
91
|
-
*
|
|
92
|
-
* Use only for conditions the declarative operator objects cannot express.
|
|
93
|
-
* Column names MUST be embedded via `Model.col.<field>` references (never bare
|
|
94
|
-
* strings); values are embedded directly and parameterized.
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```ts
|
|
98
|
-
* filter: cond`${User.col.age} > ${18} and attribute_exists(${User.col.email})`
|
|
99
|
-
* ```
|
|
100
|
-
*/
|
|
101
|
-
declare function cond<M = unknown>(template: TemplateStringsArray, ...parts: CondSlot<M>[]): RawCondition<M>;
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Type-safe attribute condition API (DynamoDB `FilterExpression`).
|
|
105
|
-
*
|
|
106
|
-
* Modeled on AWS AppSync's `ModelFilterInput`: field keys map to either an
|
|
107
|
-
* equality shorthand (a bare value) or an operator object. Operators are
|
|
108
|
-
* constrained per field type so that e.g. `beginsWith` on a numeric field is a
|
|
109
|
-
* compile error. Logical groups are expressed with the reserved `and` / `or`
|
|
110
|
-
* keys (arrays) and `not` (a single nested filter).
|
|
111
|
-
*
|
|
112
|
-
* The condition is typed against the *full* Entity `T` (its scalar fields),
|
|
113
|
-
* **not** the `select` projection — so server-side filters can reference
|
|
114
|
-
* attributes that are not projected, and nested-relation filters stay fully
|
|
115
|
-
* type-checked without callbacks (this is what structurally closes the #29
|
|
116
|
-
* type hole).
|
|
117
|
-
*/
|
|
118
|
-
type IsString<V> = [V] extends [string] ? true : false;
|
|
119
|
-
type IsNumber<V> = [V] extends [number | bigint] ? true : false;
|
|
120
|
-
/**
|
|
121
|
-
* Operators common to all scalar field types.
|
|
122
|
-
*
|
|
123
|
-
* @typeParam V - The declared field value type.
|
|
124
|
-
*/
|
|
125
|
-
interface CommonOperators<V> {
|
|
126
|
-
/** `#f = :v` */
|
|
127
|
-
eq?: V;
|
|
128
|
-
/** `#f <> :v` */
|
|
129
|
-
ne?: V;
|
|
130
|
-
/** `#f IN (:v0, :v1, …)` */
|
|
131
|
-
in?: readonly V[];
|
|
132
|
-
/** `attribute_exists(#f)` (true) / `attribute_not_exists(#f)` (false) */
|
|
133
|
-
attributeExists?: boolean;
|
|
134
|
-
/** `attribute_type(#f, :v)` — DynamoDB attribute type code, e.g. `'S'`, `'N'`. */
|
|
135
|
-
attributeType?: string;
|
|
136
|
-
}
|
|
137
|
-
/** Comparison operators valid on orderable (string / number / Date) fields. */
|
|
138
|
-
interface ComparableOperators<V> {
|
|
139
|
-
/** `#f > :v` */
|
|
140
|
-
gt?: V;
|
|
141
|
-
/** `#f >= :v` */
|
|
142
|
-
ge?: V;
|
|
143
|
-
/** `#f < :v` */
|
|
144
|
-
lt?: V;
|
|
145
|
-
/** `#f <= :v` */
|
|
146
|
-
le?: V;
|
|
147
|
-
/** `#f BETWEEN :lo AND :hi` */
|
|
148
|
-
between?: readonly [V, V];
|
|
149
|
-
}
|
|
150
|
-
/** Operators valid only on string fields. */
|
|
151
|
-
interface StringOnlyOperators {
|
|
152
|
-
/** `begins_with(#f, :v)` */
|
|
153
|
-
beginsWith?: string;
|
|
154
|
-
/** `contains(#f, :v)` */
|
|
155
|
-
contains?: string;
|
|
156
|
-
/** `NOT contains(#f, :v)` */
|
|
157
|
-
notContains?: string;
|
|
158
|
-
/** `size(#f) = :v` (string length) */
|
|
159
|
-
size?: number;
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* The operator object accepted for a field of value type `V`.
|
|
163
|
-
*
|
|
164
|
-
* - string → common + comparable + string-only (`beginsWith`, `contains`, …)
|
|
165
|
-
* - number → common + comparable (no `beginsWith` / `contains`)
|
|
166
|
-
* - Date → common + comparable (compared against `Date`)
|
|
167
|
-
* - boolean → common only
|
|
168
|
-
* - other → common only
|
|
169
|
-
*/
|
|
170
|
-
type FieldCondition<V> = IsString<V> extends true ? CommonOperators<V> & ComparableOperators<V> & StringOnlyOperators : IsNumber<V> extends true ? CommonOperators<V> & ComparableOperators<V> : [NonNullable<V>] extends [Date] ? CommonOperators<V> & ComparableOperators<V> : CommonOperators<V>;
|
|
171
|
-
/** A field key may be given as an equality shorthand or an operator object. */
|
|
172
|
-
type FieldFilter<V> = V | FieldCondition<V>;
|
|
173
|
-
/**
|
|
174
|
-
* Same scalar classification used across the public API: a field is filterable
|
|
175
|
-
* when its value type is a leaf scalar (string/number/boolean/Date/bigint/…),
|
|
176
|
-
* not a relation (`DDBModel` / `DDBModel[]`) or embedded object.
|
|
177
|
-
*/
|
|
178
|
-
type FilterableScalar = string | number | boolean | bigint | Date | null | undefined;
|
|
179
|
-
/**
|
|
180
|
-
* The set of filterable scalar field keys of `T` (relations and embedded
|
|
181
|
-
* objects excluded). Mirrors the projection rules in `selectable.ts`.
|
|
182
|
-
*/
|
|
183
|
-
type ScalarKeys<T> = {
|
|
184
|
-
[K in keyof T]: T[K] extends Function ? never : T[K] extends Array<infer E> ? [E] extends [DDBModel] ? never : K : NonNullable<T[K]> extends DDBModel ? never : NonNullable<T[K]> extends FilterableScalar ? K : never;
|
|
185
|
-
}[keyof T];
|
|
186
|
-
/**
|
|
187
|
-
* AppSync `ModelFilterInput`-compatible filter for the full Entity `T`.
|
|
188
|
-
*
|
|
189
|
-
* Field keys (scalar attributes of `T`) accept an equality shorthand or an
|
|
190
|
-
* operator object; the reserved `and` / `or` keys take arrays of nested
|
|
191
|
-
* filters and `not` takes a single nested filter.
|
|
192
|
-
*/
|
|
193
|
-
type FilterInput<T> = ({
|
|
194
|
-
[K in ScalarKeys<T>]?: FieldFilter<T[K]>;
|
|
195
|
-
} & {
|
|
196
|
-
and?: readonly (FilterInput<T> | RawCondition<T>)[];
|
|
197
|
-
or?: readonly (FilterInput<T> | RawCondition<T>)[];
|
|
198
|
-
not?: FilterInput<T> | RawCondition<T>;
|
|
199
|
-
}) | RawCondition<T>;
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Internal brand symbol identifying a compiled select builder at runtime.
|
|
203
|
-
* Consumers should not depend on this; it is used by the execution layer to
|
|
204
|
-
* distinguish a builder instance from a plain select object.
|
|
205
|
-
*/
|
|
206
|
-
declare const SELECT_BUILDER: unique symbol;
|
|
207
|
-
/**
|
|
208
|
-
* The normalized spec a select builder resolves to. This is the shape the
|
|
209
|
-
* planner / traversal layer consumes. `filter` is the declarative server-side
|
|
210
|
-
* DynamoDB FilterExpression input.
|
|
211
|
-
*
|
|
212
|
-
* @internal
|
|
213
|
-
*/
|
|
214
|
-
interface SelectBuilderSpec {
|
|
215
|
-
readonly [SELECT_BUILDER]: true;
|
|
216
|
-
select: Record<string, unknown>;
|
|
217
|
-
filter?: Record<string, unknown>;
|
|
218
|
-
limit?: number;
|
|
219
|
-
after?: string;
|
|
220
|
-
order?: 'ASC' | 'DESC';
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Top-level select builder produced by `Model.project(...)`.
|
|
224
|
-
*
|
|
225
|
-
* Two-stage, model-bound inference: the entity type `T` is fixed by the model,
|
|
226
|
-
* and the projection `S` is inferred from the `project(...)` argument.
|
|
227
|
-
*
|
|
228
|
-
* `.filter(...)` is the declarative server-side DynamoDB FilterExpression,
|
|
229
|
-
* typed against the full Entity `T` (so it may reference unprojected attributes
|
|
230
|
-
* and type-checks operators / value types).
|
|
231
|
-
*
|
|
232
|
-
* @typeParam T - The (model-bound) Entity type.
|
|
233
|
-
* @typeParam S - The (fixed) select projection.
|
|
234
|
-
*/
|
|
235
|
-
interface SelectBuilder<T, S> {
|
|
236
|
-
/** Declarative server-side DynamoDB FilterExpression, typed against `T`. */
|
|
237
|
-
filter(filter: FilterInput<T>): SelectBuilder<T, S>;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Relation select builder produced by `Model.relation(...)`. Same model-bound
|
|
241
|
-
* two-stage inference as {@link SelectBuilder}, plus per-relation pagination
|
|
242
|
-
* controls.
|
|
243
|
-
*
|
|
244
|
-
* @typeParam T - The (model-bound) relation target Entity type.
|
|
245
|
-
* @typeParam S - The (fixed) select projection for this relation.
|
|
246
|
-
*/
|
|
247
|
-
interface RelationBuilder<T, S> {
|
|
248
|
-
filter(filter: FilterInput<T>): RelationBuilder<T, S>;
|
|
249
|
-
limit(limit: number): RelationBuilder<T, S>;
|
|
250
|
-
after(cursor: string): RelationBuilder<T, S>;
|
|
251
|
-
order(order: 'ASC' | 'DESC'): RelationBuilder<T, S>;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Extracts the projection `S` carried by a top-level or relation builder.
|
|
255
|
-
* Used by `QueryResult` so a builder in a select position resolves to the same
|
|
256
|
-
* result type as the bare projection it wraps.
|
|
257
|
-
*/
|
|
258
|
-
type SelectOf<B> = B extends SelectBuilder<infer _T, infer S> ? S : B extends RelationBuilder<infer _RT, infer RS> ? RS : never;
|
|
259
|
-
|
|
260
|
-
type Scalar = string | number | boolean | bigint | symbol | null | undefined | Date | Uint8Array | Set<any>;
|
|
261
|
-
/**
|
|
262
|
-
* Derives the select specification type from an Entity type.
|
|
263
|
-
*
|
|
264
|
-
* - Scalar fields → `true`
|
|
265
|
-
* - Embedded objects (non-DDBModel) → nested `SelectableOf<E>`
|
|
266
|
-
* - hasMany relations (`DDBModel[]`) → `RelationSelect<E>`
|
|
267
|
-
* - belongsTo/hasOne relations (`DDBModel | null`) → `RelationSelect<E>`
|
|
268
|
-
* - Function properties → excluded
|
|
269
|
-
*/
|
|
270
|
-
type SelectableOf<T> = {
|
|
271
|
-
[K in keyof T as T[K] extends Function ? never : K]?: T[K] extends Array<infer E> ? [E] extends [DDBModel] ? RelationSpec<E> : true : NonNullable<T[K]> extends DDBModel ? null extends T[K] ? RelationSpec<NonNullable<T[K]>> : SelectableOf<T[K]> : T[K] extends Scalar ? true : SelectableOf<T[K]>;
|
|
272
|
-
};
|
|
273
|
-
/**
|
|
274
|
-
* What a relation field accepts in a `select`: either the plain
|
|
275
|
-
* {@link RelationSelect} object, or a {@link RelationBuilder} produced by the
|
|
276
|
-
* target model's `Model.relation(...)`. The model-bound builder pins the
|
|
277
|
-
* relation's target type, so passing another model's builder here is a compile
|
|
278
|
-
* error (closes #33).
|
|
279
|
-
*/
|
|
280
|
-
type RelationSpec<T> = RelationSelect<T> | RelationBuilder<T, any>;
|
|
281
|
-
/**
|
|
282
|
-
* Plain-object selection spec for a Relation field. Wraps `SelectableOf<T>`
|
|
283
|
-
* with pagination controls and an optional declarative server-side `filter`
|
|
284
|
-
* (typed against the full relation target `T`, so it may reference attributes
|
|
285
|
-
* not in the projection).
|
|
286
|
-
*
|
|
287
|
-
* For arbitrary post-load TypeScript filtering, narrow as far as possible with
|
|
288
|
-
* the declarative server-side `filter`, then apply any remaining JS-only
|
|
289
|
-
* condition on the (already fully typed) result with `result.items.filter(...)`.
|
|
290
|
-
*
|
|
291
|
-
* @typeParam T - The target relation Entity type
|
|
292
|
-
* @typeParam S - The select specification for this relation (inferred per call)
|
|
293
|
-
*/
|
|
294
|
-
type RelationSelect<T, S extends SelectableOf<T> = SelectableOf<T>> = {
|
|
295
|
-
select: S;
|
|
296
|
-
limit?: number;
|
|
297
|
-
after?: string;
|
|
298
|
-
order?: 'ASC' | 'DESC';
|
|
299
|
-
filter?: FilterInput<T>;
|
|
300
|
-
};
|
|
301
|
-
/**
|
|
302
|
-
* Strict ("exact") version of a select projection that rejects excess
|
|
303
|
-
* properties.
|
|
304
|
-
*
|
|
305
|
-
* # Why this exists
|
|
306
|
-
*
|
|
307
|
-
* `query` / `list` infer the projection as a free generic parameter
|
|
308
|
-
* (`<Sel extends SelectSpec<T>>`, `select: Sel`). When an object literal is
|
|
309
|
-
* matched against a *bare type parameter* TypeScript infers `Sel` to the
|
|
310
|
-
* literal's exact shape and then only checks the constraint
|
|
311
|
-
* `Sel extends SelectSpec<T>` **structurally** — there is no object-literal
|
|
312
|
-
* *freshness* (excess-property) check against a concrete target, because the
|
|
313
|
-
* target (`Sel`) is whatever the literal is. `SelectableOf<T>` has all-optional
|
|
314
|
-
* properties, so an extra key like `BOGUS: true` satisfies the constraint and
|
|
315
|
-
* compiles. The `SelectableOf<T> | SelectBuilder<…>` union compounds this
|
|
316
|
-
* (freshness checks are also suppressed against unions).
|
|
317
|
-
*
|
|
318
|
-
* `StrictSelect` closes the hole without `any`: it maps every key of the
|
|
319
|
-
* inferred `Sel` that is **not** a known key of `SelectableOf<T>` to `never`.
|
|
320
|
-
* Used as an *additional* constraint at the call site
|
|
321
|
-
* (`select: Sel & StrictSelect<T, Sel>`), a stray key forces that property's
|
|
322
|
-
* type to `never`, which the supplied `true` (or nested object) is not
|
|
323
|
-
* assignable to — so the call is a compile error. Known keys keep their
|
|
324
|
-
* inferred value type, so the result-type inference (`QueryResult`) and the
|
|
325
|
-
* builder branch are unaffected.
|
|
326
|
-
*
|
|
327
|
-
* Recurses into the plain-object nested forms so the same rejection applies to
|
|
328
|
-
* nested **relation** selects (`{ select: { … } }`) and **embedded** object
|
|
329
|
-
* selects. Builder values (`RelationBuilder` / `SelectBuilder`) are left as-is —
|
|
330
|
-
* their projection was already constrained to `SelectableOf<T>` when the
|
|
331
|
-
* `Model.relation(...)` / `Model.project(...)` factory produced them.
|
|
332
|
-
*
|
|
333
|
-
* @typeParam T - The Entity type the projection is taken against.
|
|
334
|
-
* @typeParam Sel - The inferred select object (per call).
|
|
335
|
-
*/
|
|
336
|
-
type StrictSelect<T, Sel> = {
|
|
337
|
-
[K in keyof Sel]: K extends keyof SelectableOf<T> ? Sel[K] extends {
|
|
338
|
-
select: infer Nested;
|
|
339
|
-
} ? K extends keyof T ? Sel[K] & {
|
|
340
|
-
select: StrictSelect<RelationTargetOf<T[K]>, Nested>;
|
|
341
|
-
} : Sel[K] : Sel[K] extends object ? Sel[K] extends RelationBuilder<infer _RT, infer _RS> ? Sel[K] : K extends keyof T ? Sel[K] & StrictSelect<NonNullable<T[K]>, Sel[K]> : Sel[K] : Sel[K] : never;
|
|
342
|
-
};
|
|
343
|
-
/**
|
|
344
|
-
* Extracts the relation **target entity type** from an entity field type:
|
|
345
|
-
* `E[]` → `E`, `E | null` → `E`. Mirrors the helper in `select-builder.ts`;
|
|
346
|
-
* used by {@link StrictSelect} to recurse a nested relation select against the
|
|
347
|
-
* related model's fields.
|
|
348
|
-
*/
|
|
349
|
-
type RelationTargetOf<F> = F extends Array<infer E> ? E : NonNullable<F> extends DDBModel ? NonNullable<F> : NonNullable<F>;
|
|
350
|
-
/**
|
|
351
|
-
* Call-site excess-property constraint for a top-level `select` (which may be a
|
|
352
|
-
* plain projection **or** a `Model.project(...)` builder).
|
|
353
|
-
*
|
|
354
|
-
* - If `Sel` is a {@link SelectBuilder}, it passes through untouched — the
|
|
355
|
-
* builder's projection was already constrained to `SelectableOf<T>` by the
|
|
356
|
-
* `Model.project(...)` factory, and applying {@link StrictSelect} would
|
|
357
|
-
* wrongly poison the builder's own `filter` member.
|
|
358
|
-
* - Otherwise `Sel` is a plain object and {@link StrictSelect} rejects any key
|
|
359
|
-
* not declared on `T` (top-level and nested).
|
|
360
|
-
*
|
|
361
|
-
* Applied as `select: Sel & StrictSelectSpec<T, Sel>`, so known keys keep their
|
|
362
|
-
* inferred type (result inference unaffected) and stray keys become `never`.
|
|
363
|
-
*
|
|
364
|
-
* @typeParam T - The Entity type the projection is taken against.
|
|
365
|
-
* @typeParam Sel - The inferred select spec (per call).
|
|
366
|
-
*/
|
|
367
|
-
type StrictSelectSpec<T, Sel> = Sel extends SelectBuilder<any, any> ? Sel : StrictSelect<T, Sel>;
|
|
368
|
-
/**
|
|
369
|
-
* Derives the persistable input type for `put()` from an Entity type.
|
|
370
|
-
*
|
|
371
|
-
* - Function properties → excluded
|
|
372
|
-
* - Relation fields (`DDBModel[]`, `DDBModel | null`) → excluded
|
|
373
|
-
* (relations are not written by `put`)
|
|
374
|
-
* - Scalar fields → kept with their declared type
|
|
375
|
-
* - Embedded objects (non-`DDBModel`) → recursively `EntityInput`
|
|
376
|
-
*/
|
|
377
|
-
type EntityInput<T> = {
|
|
378
|
-
[K in keyof T as T[K] extends Function ? never : T[K] extends Array<infer E> ? [E] extends [DDBModel] ? never : K : NonNullable<T[K]> extends DDBModel ? null extends T[K] ? never : K : K]: T[K] extends Scalar ? T[K] : NonNullable<T[K]> extends DDBModel ? EntityInput<NonNullable<T[K]>> : T[K] extends object ? EntityInput<T[K]> : T[K];
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Derives the return type from an Entity type `T` and a select specification `S`.
|
|
383
|
-
*
|
|
384
|
-
* - `S[K] === true` → `T[K]` (scalar value)
|
|
385
|
-
* - `S[K]` has `select` property and `T[K]` is an array (hasMany) →
|
|
386
|
-
* `{ items: QueryResult<E, Sel>[]; cursor: string | null }`
|
|
387
|
-
* - `S[K]` has `select` property and `T[K]` is nullable (belongsTo/hasOne) →
|
|
388
|
-
* `QueryResult<E, Sel> | null`
|
|
389
|
-
* - `S[K]` is a plain object → nested `QueryResult` (embedded)
|
|
390
|
-
* - fallback → `T[K]` (for dynamic/widened select types)
|
|
391
|
-
*
|
|
392
|
-
* Distributes over union types in `S` so that dynamically constructed
|
|
393
|
-
* selects produce a union of result types.
|
|
394
|
-
*/
|
|
395
|
-
|
|
396
|
-
type QueryResult<T, S> = S extends unknown ? {
|
|
397
|
-
[K in keyof S & keyof T]: S[K] extends true ? T[K] : S[K] extends RelationBuilder<infer _RT, infer Sel> ? T[K] extends Array<infer E> ? {
|
|
398
|
-
items: QueryResult<E, Sel>[];
|
|
399
|
-
cursor: string | null;
|
|
400
|
-
} : QueryResult<NonNullable<T[K]>, Sel> | null : S[K] extends {
|
|
401
|
-
select: infer Sel;
|
|
402
|
-
} ? T[K] extends Array<infer E> ? {
|
|
403
|
-
items: QueryResult<E, Sel>[];
|
|
404
|
-
cursor: string | null;
|
|
405
|
-
} : QueryResult<NonNullable<T[K]>, Sel> | null : S[K] extends Record<string, unknown> ? QueryResult<T[K], S[K]> : T[K];
|
|
406
|
-
} : never;
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Compile-time brand for read results that carry a hidden, resolved base-table
|
|
410
|
-
* key (issue #54). A `query(..., { updatable: true })` / `list(..., { updatable:
|
|
411
|
-
* true })` result is typed `QueryResult<...> & Updatable` so it can be passed
|
|
412
|
-
* straight back to `update()`.
|
|
413
|
-
*
|
|
414
|
-
* The brand is phantom (no runtime field). Spreading / cloning / JSON
|
|
415
|
-
* round-tripping a branded value produces a plain object that no longer
|
|
416
|
-
* structurally matches `Updatable`, so the compiler rejects passing a
|
|
417
|
-
* stripped copy where an updatable entity is required. (Runtime existence of
|
|
418
|
-
* the hidden key is enforced separately by `update()`, which throws when it is
|
|
419
|
-
* absent — see `resolveUpdateKey`.)
|
|
420
|
-
*
|
|
421
|
-
* @example
|
|
422
|
-
* ```ts
|
|
423
|
-
* const user = await User.query({ userId: 'u' }, { name: true }, { updatable: true });
|
|
424
|
-
* if (user) await User.update(user, { name: 'New' }); // ok — branded
|
|
425
|
-
* // await User.update({ ...user }, { name: 'New' }); // compile error — spread drops the brand
|
|
426
|
-
* ```
|
|
427
|
-
*/
|
|
428
|
-
declare const updatableBrand: unique symbol;
|
|
429
|
-
interface Updatable {
|
|
430
|
-
readonly [updatableBrand]: true;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
declare const GSI_MARKER: unique symbol;
|
|
434
|
-
interface GsiDefinitionMarker<T extends Record<string, unknown> = Record<string, unknown>, U extends boolean = boolean> {
|
|
435
|
-
readonly [GSI_MARKER]: true;
|
|
436
|
-
readonly indexName: string;
|
|
437
|
-
readonly segmented: SegmentedKey;
|
|
438
|
-
readonly inputFieldNames: string[];
|
|
439
|
-
readonly unique: U;
|
|
440
|
-
/** @internal Phantom field for type-level input type extraction. */
|
|
441
|
-
readonly _phantom?: T;
|
|
442
|
-
}
|
|
443
|
-
interface GsiOptions {
|
|
444
|
-
unique?: boolean;
|
|
445
|
-
}
|
|
446
|
-
type GsiBuilder<T extends Record<string, unknown>> = (c: {
|
|
447
|
-
readonly [K in keyof T]-?: Column<T[K], T>;
|
|
448
|
-
}) => KeyStructure;
|
|
449
|
-
declare function gsi<T extends Record<string, unknown>>(indexName: string, builder: GsiBuilder<T>, options: GsiOptions & {
|
|
450
|
-
unique: true;
|
|
451
|
-
}): GsiDefinitionMarker<T, true>;
|
|
452
|
-
declare function gsi<T extends Record<string, unknown>>(indexName: string, builder: GsiBuilder<T>, options?: GsiOptions): GsiDefinitionMarker<T, false>;
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Extracts the Primary Key input type from a model class.
|
|
456
|
-
* Scans static properties for `KeyDefinitionMarker<T>` and returns `T`.
|
|
457
|
-
*
|
|
458
|
-
* @typeParam C - The model class constructor (`typeof UserModel`)
|
|
459
|
-
*/
|
|
460
|
-
type PrimaryKeyOf<C> = {
|
|
461
|
-
[K in keyof C]: C[K] extends KeyDefinitionMarker<infer I> ? I : never;
|
|
462
|
-
}[keyof C];
|
|
463
|
-
/**
|
|
464
|
-
* Union of all query key input types (PK + all GSIs).
|
|
465
|
-
* Used as the constraint for `list()`.
|
|
466
|
-
*
|
|
467
|
-
* @typeParam C - The model class constructor (`typeof UserModel`)
|
|
468
|
-
*/
|
|
469
|
-
type QueryKeyOf<C> = {
|
|
470
|
-
[K in keyof C]: C[K] extends KeyDefinitionMarker<infer I> ? I : C[K] extends GsiDefinitionMarker<infer I, any> ? I : never;
|
|
471
|
-
}[keyof C];
|
|
472
|
-
/**
|
|
473
|
-
* Union of unique query key input types (PK + unique GSIs only).
|
|
474
|
-
* Used as the constraint for `query()` — only keys guaranteed to
|
|
475
|
-
* return a single item are allowed.
|
|
476
|
-
*
|
|
477
|
-
* @typeParam C - The model class constructor (`typeof UserModel`)
|
|
478
|
-
*/
|
|
479
|
-
type UniqueQueryKeyOf<C> = {
|
|
480
|
-
[K in keyof C]: C[K] extends KeyDefinitionMarker<infer I> ? I : C[K] extends GsiDefinitionMarker<infer I, true> ? I : never;
|
|
481
|
-
}[keyof C];
|
|
482
|
-
/**
|
|
483
|
-
* "At least one" relaxation of a single key input type `T`: every field is
|
|
484
|
-
* known and correctly typed, but only a non-empty subset need be supplied (the
|
|
485
|
-
* remaining fields become optional).
|
|
486
|
-
*
|
|
487
|
-
* For a composite key `{ groupId: string; userId: string }` this yields
|
|
488
|
-
* `{ groupId: string; userId?: string } | { groupId?: string; userId: string }`
|
|
489
|
-
* — i.e. you may supply just the partition-key field(s) without the full
|
|
490
|
-
* sort-key tail. Unknown fields are still rejected (object-literal excess
|
|
491
|
-
* property checking), so it stays strictly typed.
|
|
492
|
-
*/
|
|
493
|
-
type AtLeastOneKey<T> = {
|
|
494
|
-
[K in keyof T]-?: {
|
|
495
|
-
[P in K]-?: T[P];
|
|
496
|
-
} & {
|
|
497
|
-
[P in Exclude<keyof T, K>]?: T[P];
|
|
498
|
-
} extends infer O ? {
|
|
499
|
-
[P in keyof O]: O[P];
|
|
500
|
-
} : never;
|
|
501
|
-
}[keyof T];
|
|
502
|
-
/**
|
|
503
|
-
* Key type accepted by `list()` / `explain()`. These issue a DynamoDB *Query*
|
|
504
|
-
* against a partition (PK or GSI), so a **partial** key — the partition-key
|
|
505
|
-
* subset of a PK/GSI input — is sufficient; the sort-key portion is optional
|
|
506
|
-
* (it narrows the range, e.g. `begins_with`). This mirrors the runtime key
|
|
507
|
-
* resolver, which accepts any field subset that produces a valid partition key
|
|
508
|
-
* (`src/planner/key-resolver.ts`) and throws otherwise.
|
|
509
|
-
*
|
|
510
|
-
* Distinct from {@link QueryKeyOf} (full key, used as the constraint label by
|
|
511
|
-
* the type tests) and {@link UniqueQueryKeyOf} (full unique key for `query()`,
|
|
512
|
-
* which must resolve to a single item). `query()` deliberately stays strict —
|
|
513
|
-
* only this `list`/`explain` boundary is relaxed.
|
|
514
|
-
*
|
|
515
|
-
* @typeParam C - The model class constructor (`typeof UserModel`)
|
|
516
|
-
*/
|
|
517
|
-
type PartialQueryKeyOf<C> = QueryKeyOf<C> extends infer U ? U extends Record<string, unknown> ? AtLeastOneKey<U> : never : never;
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* A top-level `select` may be either a plain projection (`SelectableOf<T>`) or
|
|
521
|
-
* a {@link SelectBuilder} produced by `Model.project(...)`. The builder is
|
|
522
|
-
* model-bound to `T`, so its `.filter(...)` is typed against the full entity.
|
|
523
|
-
*/
|
|
524
|
-
type SelectSpec<T> = SelectableOf<T> | SelectBuilder<T, any>;
|
|
525
|
-
/**
|
|
526
|
-
* Resolves the projection `S` from a top-level select spec (plain object or
|
|
527
|
-
* builder), so the result type is computed identically for both forms.
|
|
528
|
-
*/
|
|
529
|
-
type ProjectionOf<T, Sel> = Sel extends SelectableOf<T> ? Sel : SelectOf<Sel>;
|
|
530
|
-
/**
|
|
531
|
-
* Default model-class constraint used when a `ModelStatic` is referenced
|
|
532
|
-
* without its concrete constructor type (e.g. `ModelStatic<UserModel>`).
|
|
533
|
-
*
|
|
534
|
-
* In that case key/option types fall back to permissive shapes so existing
|
|
535
|
-
* usages keep compiling, while the precise inference is available when the
|
|
536
|
-
* constructor type is supplied (as it is from `DDBModel.asModel()`).
|
|
537
|
-
*/
|
|
538
|
-
type AnyModelClass = abstract new (...args: any[]) => DDBModel;
|
|
539
|
-
/**
|
|
540
|
-
* The accepted PK / GSI key type for `list()` / `explain()`.
|
|
541
|
-
*
|
|
542
|
-
* `list`/`explain` issue a DynamoDB *Query* against a partition, so a **partial**
|
|
543
|
-
* key (partition-key subset of a PK/GSI input) is accepted — the sort-key tail
|
|
544
|
-
* is optional. This matches the runtime key resolver. `query()` stays strict via
|
|
545
|
-
* {@link QueryKey}. Derived from the model class when available, otherwise
|
|
546
|
-
* permissive.
|
|
547
|
-
*/
|
|
548
|
-
type ListKey<C> = [C] extends [AnyModelClass] ? PartialQueryKeyOf<C> : Record<string, unknown>;
|
|
549
|
-
/**
|
|
550
|
-
* The accepted key type for `query()` — only keys guaranteed to return a
|
|
551
|
-
* single item (PK + unique GSIs). Derived from the model class when available.
|
|
552
|
-
*/
|
|
553
|
-
type QueryKey<C> = [C] extends [AnyModelClass] ? UniqueQueryKeyOf<C> : Record<string, unknown>;
|
|
554
|
-
/**
|
|
555
|
-
* The accepted key type for `delete()` (PK only). Derived from the model
|
|
556
|
-
* class when available.
|
|
557
|
-
*/
|
|
558
|
-
type DeleteKey<C> = [C] extends [AnyModelClass] ? PrimaryKeyOf<C> : Record<string, unknown>;
|
|
559
|
-
/**
|
|
560
|
-
* The accepted *explicit* key type for `update()` — the base-table primary key
|
|
561
|
-
* only. DynamoDB `UpdateItem` can target an item solely by its base-table PK
|
|
562
|
-
* (GSI-based updates are impossible), so this is `PrimaryKeyOf<C>`, not
|
|
563
|
-
* `UniqueQueryKeyOf<C>`. Derived from the model class when available.
|
|
564
|
-
*/
|
|
565
|
-
type UpdateExplicitKey<C> = [C] extends [AnyModelClass] ? PrimaryKeyOf<C> : Record<string, unknown>;
|
|
566
|
-
/**
|
|
567
|
-
* Options accepted by `list()`.
|
|
568
|
-
*
|
|
569
|
-
* - `select` is either a plain projection or a `project(...)` builder.
|
|
570
|
-
* - `filter` is the declarative server-side DynamoDB FilterExpression, typed
|
|
571
|
-
* against the full Entity `T` (may reference unprojected attributes). For
|
|
572
|
-
* arbitrary JS-only post-load filtering, narrow with `filter` then apply
|
|
573
|
-
* `result.items.filter(...)` (already fully typed).
|
|
574
|
-
*
|
|
575
|
-
* @typeParam T - The Entity type.
|
|
576
|
-
* @typeParam Sel - The select spec (plain object or builder), inferred per call.
|
|
577
|
-
*/
|
|
578
|
-
type ListCallOptions<T, Sel extends SelectSpec<T>> = {
|
|
579
|
-
select: Sel & StrictSelectSpec<T, Sel>;
|
|
580
|
-
limit?: number;
|
|
581
|
-
after?: string;
|
|
582
|
-
order?: 'ASC' | 'DESC';
|
|
583
|
-
filter?: FilterInput<T>;
|
|
584
|
-
/**
|
|
585
|
-
* When `true`, each returned item is updatable: it carries its resolved
|
|
586
|
-
* base-table key as a hidden, non-enumerable property and is branded
|
|
587
|
-
* `& Updatable`, so it can be passed straight back to `update()`.
|
|
588
|
-
*/
|
|
589
|
-
updatable?: boolean;
|
|
590
|
-
};
|
|
591
|
-
/**
|
|
592
|
-
* Public, type-inferring API surface produced by {@link DDBModel.asModel}.
|
|
593
|
-
*
|
|
594
|
-
* @typeParam T - The Entity type of the model.
|
|
595
|
-
* @typeParam C - The model class constructor type. Supplied automatically by
|
|
596
|
-
* `asModel()`; when omitted, key/option types widen to permissive shapes so
|
|
597
|
-
* that `ModelStatic<T>` keeps compiling for callers that only name the entity.
|
|
598
|
-
*/
|
|
599
|
-
interface ModelStatic<T extends DDBModel, C = unknown> {
|
|
600
|
-
/**
|
|
601
|
-
* Type-safe column references for this model, one per scalar field. Used by
|
|
602
|
-
* the `cond` raw escape hatch (`cond\`${Model.col.age} > ${18}\``). Each
|
|
603
|
-
* column carries its declared type and is branded to this model, so passing
|
|
604
|
-
* another model's column into this model's `cond` is a compile error.
|
|
605
|
-
*/
|
|
606
|
-
readonly col: ColumnMap<T>;
|
|
607
|
-
/**
|
|
608
|
-
* Begin a model-bound, top-level select builder. The entity type `T` is fixed
|
|
609
|
-
* by this model and the projection `S` is inferred from the argument.
|
|
610
|
-
* `.filter(...)` is typed `FilterInput<T>` against the full entity.
|
|
611
|
-
*
|
|
612
|
-
* @example
|
|
613
|
-
* ```ts
|
|
614
|
-
* Order.list({ userId: 'u' }, {
|
|
615
|
-
* select: Order.project({ orderId: true, amount: true })
|
|
616
|
-
* .filter({ status: 'confirmed', amount: { gt: 100 } }),
|
|
617
|
-
* });
|
|
618
|
-
* ```
|
|
619
|
-
*/
|
|
620
|
-
project<const S extends SelectableOf<T>>(select: S): SelectBuilder<T, S>;
|
|
621
|
-
/**
|
|
622
|
-
* Begin a model-bound relation select builder for use as a relation field's
|
|
623
|
-
* value inside a parent `select`. `T` is this (relation target) model's
|
|
624
|
-
* entity type; `S` is inferred from the argument. Same model-bound inference
|
|
625
|
-
* as {@link ModelStatic.project}, plus per-relation pagination
|
|
626
|
-
* (`limit` / `after` / `order`).
|
|
627
|
-
*
|
|
628
|
-
* @example
|
|
629
|
-
* ```ts
|
|
630
|
-
* User.query({ userId: 'u' }, {
|
|
631
|
-
* name: true,
|
|
632
|
-
* orders: Order.relation({ orderId: true, amount: true })
|
|
633
|
-
* .filter({ amount: { gt: 100 } }),
|
|
634
|
-
* });
|
|
635
|
-
* ```
|
|
636
|
-
*/
|
|
637
|
-
relation<const S extends SelectableOf<T>>(select: S): RelationBuilder<T, S>;
|
|
638
|
-
/**
|
|
639
|
-
* Fetch a single item by a unique key and **hydrate** the plain result onto a
|
|
640
|
-
* host-side object (issue #53). The `hydrate` factory receives the fully-built
|
|
641
|
-
* plain object — `QueryResult<T, ProjectionOf<T, Sel>>`, typed against exactly
|
|
642
|
-
* the `select`ed fields — and its return type `R` becomes the read's return
|
|
643
|
-
* type (subsuming a `queryAs(Ctor, …)` style: `hydrate: (raw) => new Ctor(raw)`).
|
|
644
|
-
*
|
|
645
|
-
* Applied as the **final after-fetch step**, only to a present item: a missing
|
|
646
|
-
* item resolves to `null` and the factory is NOT invoked (hence `R | null`). A
|
|
647
|
-
* throw from the factory propagates verbatim as the read's rejection.
|
|
648
|
-
*
|
|
649
|
-
* Host-runtime ONLY: the factory is a host-language closure, never serialized
|
|
650
|
-
* into the SSoT / `operations.json`, so it does not impede the bridge (#48).
|
|
651
|
-
*/
|
|
652
|
-
query<Sel extends SelectSpec<T>, R>(key: QueryKey<C>, select: Sel & StrictSelectSpec<T, Sel>, options: {
|
|
653
|
-
consistentRead?: boolean;
|
|
654
|
-
maxDepth?: number;
|
|
655
|
-
updatable?: boolean;
|
|
656
|
-
hydrate: (raw: QueryResult<T, ProjectionOf<T, Sel>>) => R;
|
|
657
|
-
}): Promise<R | null>;
|
|
658
|
-
/**
|
|
659
|
-
* Fetch a single item by a unique key, requesting an **updatable** result.
|
|
660
|
-
* The returned item (when non-null) carries its resolved base-table key as a
|
|
661
|
-
* hidden, non-enumerable property and is branded `& Updatable`, so it can be
|
|
662
|
-
* passed straight back to {@link ModelStatic.update} — even for a partial
|
|
663
|
-
* `select` or a GSI-based query. Do not spread / clone / JSON round-trip the
|
|
664
|
-
* result before updating: that drops both the hidden key and the brand.
|
|
665
|
-
*/
|
|
666
|
-
query<Sel extends SelectSpec<T>>(key: QueryKey<C>, select: Sel & StrictSelectSpec<T, Sel>, options: {
|
|
667
|
-
consistentRead?: boolean;
|
|
668
|
-
maxDepth?: number;
|
|
669
|
-
updatable: true;
|
|
670
|
-
}): Promise<(QueryResult<T, ProjectionOf<T, Sel>> & Updatable) | null>;
|
|
671
|
-
/**
|
|
672
|
-
* Fetch a single item by a unique key. The return type contains only the
|
|
673
|
-
* selected fields, derived from `select`.
|
|
674
|
-
*/
|
|
675
|
-
query<Sel extends SelectSpec<T>>(key: QueryKey<C>, select: Sel & StrictSelectSpec<T, Sel>, options?: {
|
|
676
|
-
consistentRead?: boolean;
|
|
677
|
-
maxDepth?: number;
|
|
678
|
-
updatable?: false;
|
|
679
|
-
}): Promise<QueryResult<T, ProjectionOf<T, Sel>> | null>;
|
|
680
|
-
/**
|
|
681
|
-
* List items for a partition. `options.select` may be a plain projection or a
|
|
682
|
-
* `project(...)` builder. `options.filter` is the declarative server-side
|
|
683
|
-
* FilterExpression typed against the full Entity. Returned items contain only
|
|
684
|
-
* the selected fields.
|
|
685
|
-
*/
|
|
686
|
-
list<Sel extends SelectSpec<T>>(key: ListKey<C>, options: ListCallOptions<T, Sel> & {
|
|
687
|
-
updatable: true;
|
|
688
|
-
}): Promise<{
|
|
689
|
-
items: (QueryResult<T, ProjectionOf<T, Sel>> & Updatable)[];
|
|
690
|
-
cursor: string | null;
|
|
691
|
-
}>;
|
|
692
|
-
list<Sel extends SelectSpec<T>>(key: ListKey<C>, options: ListCallOptions<T, Sel>): Promise<{
|
|
693
|
-
items: QueryResult<T, ProjectionOf<T, Sel>>[];
|
|
694
|
-
cursor: string | null;
|
|
695
|
-
}>;
|
|
696
|
-
explain<Sel extends SelectSpec<T>>(key: ListKey<C>, options?: {
|
|
697
|
-
select?: Sel & StrictSelectSpec<T, Sel>;
|
|
698
|
-
limit?: number;
|
|
699
|
-
after?: string;
|
|
700
|
-
order?: 'ASC' | 'DESC';
|
|
701
|
-
consistentRead?: boolean;
|
|
702
|
-
filter?: FilterInput<T>;
|
|
703
|
-
}): ExecutionPlan;
|
|
704
|
-
put(item: EntityInput<T>, options?: Record<string, unknown>): Promise<void>;
|
|
705
|
-
/**
|
|
706
|
-
* Update an item identified by its **explicit base-table primary key**
|
|
707
|
-
* (`PrimaryKeyOf<C>`). This is the canonical form — it requires the key input
|
|
708
|
-
* up front, so it can never silently target the wrong item.
|
|
709
|
-
*
|
|
710
|
-
* @example `await User.update({ userId: 'alice' }, { status: 'disabled' });`
|
|
711
|
-
*/
|
|
712
|
-
update(key: UpdateExplicitKey<C>, changes: Partial<T>, options?: Record<string, unknown>): Promise<void>;
|
|
713
|
-
/**
|
|
714
|
-
* Update an item using an **updatable** entity obtained from
|
|
715
|
-
* `query(..., { updatable: true })` / `list(..., { updatable: true })`. The
|
|
716
|
-
* hidden base-table key carried by that entity is used directly, so this
|
|
717
|
-
* works even for a partial `select` or a GSI-based read.
|
|
718
|
-
*
|
|
719
|
-
* Pass the entity as-is. A spread / clone / JSON round-trip drops both the
|
|
720
|
-
* brand (rejected here at compile time) and the hidden key (rejected at
|
|
721
|
-
* runtime), preventing an accidental wrong-item update.
|
|
722
|
-
*/
|
|
723
|
-
update(entity: Partial<T> & Updatable, changes: Partial<T>, options?: Record<string, unknown>): Promise<void>;
|
|
724
|
-
delete(key: DeleteKey<C>, options?: Record<string, unknown>): Promise<void>;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
interface PutOptions {
|
|
728
|
-
condition?: Record<string, unknown>;
|
|
729
|
-
}
|
|
730
|
-
interface UpdateOptions {
|
|
731
|
-
condition?: Record<string, unknown>;
|
|
732
|
-
}
|
|
733
|
-
interface DeleteOptions {
|
|
734
|
-
condition?: Record<string, unknown>;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
interface PutInput {
|
|
738
|
-
TableName: string;
|
|
739
|
-
Item: Record<string, unknown>;
|
|
740
|
-
ConditionExpression?: string;
|
|
741
|
-
ExpressionAttributeNames?: Record<string, string>;
|
|
742
|
-
ExpressionAttributeValues?: Record<string, unknown>;
|
|
743
|
-
}
|
|
744
|
-
declare function buildPutInput(modelClass: Function, item: Record<string, unknown>, options?: PutOptions): PutInput;
|
|
745
|
-
declare function executePut(modelClass: Function, item: Record<string, unknown>, options?: PutOptions): Promise<void>;
|
|
746
|
-
|
|
747
|
-
interface UpdateInput {
|
|
748
|
-
TableName: string;
|
|
749
|
-
Key: Record<string, unknown>;
|
|
750
|
-
UpdateExpression: string;
|
|
751
|
-
ExpressionAttributeNames: Record<string, string>;
|
|
752
|
-
ExpressionAttributeValues?: Record<string, unknown>;
|
|
753
|
-
ConditionExpression?: string;
|
|
754
|
-
}
|
|
755
|
-
declare function buildUpdateInput(modelClass: Function, entity: Record<string, unknown>, changes: Record<string, unknown>, options?: UpdateOptions): UpdateInput;
|
|
756
|
-
declare function executeUpdate(modelClass: Function, entity: Record<string, unknown>, changes: Record<string, unknown>, options?: UpdateOptions): Promise<void>;
|
|
757
|
-
|
|
758
|
-
interface DeleteInput {
|
|
759
|
-
TableName: string;
|
|
760
|
-
Key: Record<string, unknown>;
|
|
761
|
-
ConditionExpression?: string;
|
|
762
|
-
ExpressionAttributeNames?: Record<string, string>;
|
|
763
|
-
ExpressionAttributeValues?: Record<string, unknown>;
|
|
764
|
-
}
|
|
765
|
-
declare function buildDeleteInput(modelClass: Function, keyObj: Record<string, unknown>, options?: DeleteOptions): DeleteInput;
|
|
766
|
-
declare function executeDelete(modelClass: Function, keyObj: Record<string, unknown>, options?: DeleteOptions): Promise<void>;
|
|
767
|
-
|
|
768
|
-
/** Result of a read operation (GetItem / Query / BatchGetItem). */
|
|
769
|
-
interface ExecutorResult {
|
|
770
|
-
items: Record<string, unknown>[];
|
|
771
|
-
lastEvaluatedKey?: Record<string, unknown>;
|
|
772
|
-
}
|
|
773
|
-
/**
|
|
774
|
-
* Result of a single structured write. `oldItem` is the pre-write image,
|
|
775
|
-
* present only when the caller requested it (`returnOldImage: true`, mapped to
|
|
776
|
-
* DynamoDB `ReturnValues: ALL_OLD`) and an item existed before the write. The
|
|
777
|
-
* write-capture seam (issue #72) reads it to derive INSERT vs MODIFY and the
|
|
778
|
-
* REMOVE OldImage.
|
|
779
|
-
*/
|
|
780
|
-
interface WriteResult {
|
|
781
|
-
oldItem?: Record<string, unknown>;
|
|
782
|
-
}
|
|
783
|
-
/** Options shared by the single-item structured write methods. */
|
|
784
|
-
interface WriteExecOptions {
|
|
785
|
-
/** Request the pre-write image (DynamoDB `ReturnValues: ALL_OLD`). */
|
|
786
|
-
returnOldImage?: boolean;
|
|
787
|
-
}
|
|
788
|
-
/**
|
|
789
|
-
* One BatchGetItem sub-request: a single physical table plus its keys and an
|
|
790
|
-
* optional projection. Mirrors {@link import('../planner/types.js').BatchGetItemOperation}
|
|
791
|
-
* without the discriminant `type` field, so callers that already chunked keys
|
|
792
|
-
* (relation traversal) and the planner-driven read path share one entry point.
|
|
793
|
-
*/
|
|
794
|
-
interface BatchGetExecInput {
|
|
795
|
-
tableName: string;
|
|
796
|
-
keys: Record<string, unknown>[];
|
|
797
|
-
projectionExpression?: string;
|
|
798
|
-
expressionAttributeNames?: Record<string, string>;
|
|
799
|
-
consistentRead?: boolean;
|
|
800
|
-
}
|
|
801
|
-
/** A single item in a {@link Executor.batchWrite} request, grouped by table. */
|
|
802
|
-
type BatchWriteExecItem = {
|
|
803
|
-
type: 'put';
|
|
804
|
-
item: Record<string, unknown>;
|
|
805
|
-
} | {
|
|
806
|
-
type: 'delete';
|
|
807
|
-
key: Record<string, unknown>;
|
|
808
|
-
};
|
|
809
|
-
/**
|
|
810
|
-
* A read-only `ConditionCheck` entry for {@link Executor.transactWrite} (issue
|
|
811
|
-
* #81): it asserts a `ConditionExpression` holds for the keyed item without
|
|
812
|
-
* mutating it. A failed assertion cancels the **whole** `TransactWriteItems`
|
|
813
|
-
* atomically — the foundation for referential-integrity derivation.
|
|
814
|
-
*/
|
|
815
|
-
interface ConditionCheckInput {
|
|
816
|
-
TableName: string;
|
|
817
|
-
Key: Record<string, unknown>;
|
|
818
|
-
ConditionExpression: string;
|
|
819
|
-
ExpressionAttributeNames?: Record<string, string>;
|
|
820
|
-
ExpressionAttributeValues?: Record<string, unknown>;
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* A `{ Put | Update | Delete | ConditionCheck }` entry for
|
|
824
|
-
* {@link Executor.transactWrite}. The first three mutate an item; `ConditionCheck`
|
|
825
|
-
* (issue #81) is a read-only assertion on another item.
|
|
826
|
-
*/
|
|
827
|
-
type TransactWriteExecItem = {
|
|
828
|
-
Put: PutInput;
|
|
829
|
-
} | {
|
|
830
|
-
Update: UpdateInput;
|
|
831
|
-
} | {
|
|
832
|
-
Delete: DeleteInput;
|
|
833
|
-
} | {
|
|
834
|
-
ConditionCheck: ConditionCheckInput;
|
|
835
|
-
};
|
|
836
|
-
/**
|
|
837
|
-
* The unified I/O seam (issue #76). All of GraphDDB's read and write I/O is
|
|
838
|
-
* funneled through one injectable executor so the engine (planner / hydrator /
|
|
839
|
-
* relation traversal / write paths) can run unchanged against either real
|
|
840
|
-
* DynamoDB ({@link DynamoExecutor}, the default) or an in-process memory store
|
|
841
|
-
* (`MemoryExecutor`, the test adapter).
|
|
842
|
-
*
|
|
843
|
-
* Reads take a structured {@link DynamoDBOperation}; writes take the structured
|
|
844
|
-
* inputs the operation layer already builds (`PutInput` / `UpdateInput` / …),
|
|
845
|
-
* never an AWS SDK command. This keeps the seam at the *structured operation
|
|
846
|
-
* boundary* (proposal option B): no expression-string parsing is required to
|
|
847
|
-
* implement a fake.
|
|
848
|
-
*/
|
|
849
|
-
interface Executor {
|
|
850
|
-
/** Read path: GetItem / Query / BatchGetItem. */
|
|
851
|
-
execute(operation: DynamoDBOperation): Promise<ExecutorResult>;
|
|
852
|
-
/**
|
|
853
|
-
* BatchGetItem for a single table. The caller is responsible for chunking
|
|
854
|
-
* keys ≤100 (the relation traversal and the planner-driven path both do).
|
|
855
|
-
*/
|
|
856
|
-
batchGet(input: BatchGetExecInput): Promise<ExecutorResult>;
|
|
857
|
-
/** Put one item. */
|
|
858
|
-
put(input: PutInput, options?: WriteExecOptions): Promise<WriteResult>;
|
|
859
|
-
/** Update one item (applies its structured UpdateExpression). */
|
|
860
|
-
update(input: UpdateInput, options?: WriteExecOptions): Promise<WriteResult>;
|
|
861
|
-
/** Delete one item. */
|
|
862
|
-
delete(input: DeleteInput, options?: WriteExecOptions): Promise<WriteResult>;
|
|
863
|
-
/**
|
|
864
|
-
* BatchWriteItem for a single table (non-atomic). Keys/items are chunked ≤25
|
|
865
|
-
* with UnprocessedItems retry by the implementation.
|
|
866
|
-
*/
|
|
867
|
-
batchWrite(tableName: string, items: BatchWriteExecItem[]): Promise<void>;
|
|
868
|
-
/** TransactWriteItems — all-or-nothing atomic. */
|
|
869
|
-
transactWrite(items: TransactWriteExecItem[]): Promise<void>;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
type TransactWriteItemInput = {
|
|
873
|
-
Put: PutInput;
|
|
874
|
-
} | {
|
|
875
|
-
Update: UpdateInput;
|
|
876
|
-
} | {
|
|
877
|
-
Delete: DeleteInput;
|
|
878
|
-
} | {
|
|
879
|
-
ConditionCheck: ConditionCheckInput;
|
|
880
|
-
};
|
|
881
|
-
declare function attachModelClass<T extends DDBModel>(modelStatic: ModelStatic<T>, modelClass: new (...args: unknown[]) => T): ModelStatic<T>;
|
|
882
|
-
/**
|
|
883
|
-
* Per-item capture descriptor recorded alongside each transact item, so the
|
|
884
|
-
* write-capture seam (issue #72) can emit a labelled raw record after the
|
|
885
|
-
* transaction commits. `TransactWriteItems` returns no images (spec §14), so
|
|
886
|
-
* these carry the keys + the new item for Put/Update and keys only for Delete.
|
|
887
|
-
*/
|
|
888
|
-
interface TransactCaptureMeta {
|
|
889
|
-
modelName?: string;
|
|
890
|
-
op: 'put' | 'update' | 'delete';
|
|
891
|
-
table: string;
|
|
892
|
-
keys: {
|
|
893
|
-
pk: string;
|
|
894
|
-
sk: string;
|
|
895
|
-
};
|
|
896
|
-
newItem?: Record<string, unknown>;
|
|
897
|
-
}
|
|
898
|
-
declare class TransactionContext {
|
|
899
|
-
private readonly items;
|
|
900
|
-
private readonly captureMeta;
|
|
901
|
-
get itemCount(): number;
|
|
902
|
-
put(model: ModelStatic<DDBModel>, item: Record<string, unknown>, options?: PutOptions): void;
|
|
903
|
-
update(model: ModelStatic<DDBModel>, entity: Record<string, unknown>, changes: Record<string, unknown>, options?: UpdateOptions): void;
|
|
904
|
-
delete(model: ModelStatic<DDBModel>, key: Record<string, unknown>, options?: DeleteOptions): void;
|
|
905
|
-
/**
|
|
906
|
-
* Add a read-only `ConditionCheck` assertion on a keyed item (issue #81). The
|
|
907
|
-
* item is **not** mutated; the `options.condition` (e.g.
|
|
908
|
-
* `{ attributeExists: 'PK' }` to require the row exists) is asserted, and a
|
|
909
|
-
* failed assertion cancels the **whole** `TransactWriteItems` atomically. This
|
|
910
|
-
* is the foundation for referential-integrity derivation (proposal: `requires
|
|
911
|
-
* <Entity> exists`). A ConditionCheck emits no change-capture record (it writes
|
|
912
|
-
* nothing).
|
|
913
|
-
*
|
|
914
|
-
* @throws if `options.condition` is missing — a ConditionCheck without an
|
|
915
|
-
* assertion is meaningless.
|
|
916
|
-
*/
|
|
917
|
-
conditionCheck(model: ModelStatic<DDBModel>, key: Record<string, unknown>, options: {
|
|
918
|
-
condition: Record<string, unknown>;
|
|
919
|
-
}): void;
|
|
920
|
-
/** @internal */
|
|
921
|
-
getTransactItems(): TransactWriteItemInput[];
|
|
922
|
-
/** @internal — capture descriptors for the write-capture seam (issue #72). */
|
|
923
|
-
getCaptureMeta(): TransactCaptureMeta[];
|
|
924
|
-
private assertWithinLimit;
|
|
925
|
-
}
|
|
926
|
-
declare function executeTransaction(fn: (tx: TransactionContext) => void | Promise<void>): Promise<void>;
|
|
927
|
-
|
|
928
|
-
interface BatchGetRequest {
|
|
929
|
-
model: ModelStatic<DDBModel>;
|
|
930
|
-
keys: Record<string, unknown>[];
|
|
931
|
-
}
|
|
932
|
-
interface BatchPutRequest {
|
|
933
|
-
type: 'put';
|
|
934
|
-
model: ModelStatic<DDBModel>;
|
|
935
|
-
item: Record<string, unknown>;
|
|
936
|
-
options?: PutOptions;
|
|
937
|
-
}
|
|
938
|
-
interface BatchDeleteRequest {
|
|
939
|
-
type: 'delete';
|
|
940
|
-
model: ModelStatic<DDBModel>;
|
|
941
|
-
key: Record<string, unknown>;
|
|
942
|
-
options?: DeleteOptions;
|
|
943
|
-
}
|
|
944
|
-
type BatchWriteRequest = BatchPutRequest | BatchDeleteRequest;
|
|
945
|
-
declare class BatchGetResult {
|
|
946
|
-
private readonly groups;
|
|
947
|
-
constructor(groups: Map<Function, Record<string, unknown>[]>);
|
|
948
|
-
get<T extends DDBModel>(model: ModelStatic<T>): Partial<T>[];
|
|
949
|
-
}
|
|
950
|
-
declare function executeBatchGet(requests: BatchGetRequest[]): Promise<BatchGetResult>;
|
|
951
|
-
declare function executeBatchWrite(requests: BatchWriteRequest[]): Promise<void>;
|
|
952
|
-
|
|
953
|
-
declare abstract class DDBModel {
|
|
954
|
-
/** @internal Type brand — prevents primitives from structurally matching DDBModel. */
|
|
955
|
-
private readonly __brand;
|
|
956
|
-
static setClient(client: DynamoDBClient): void;
|
|
957
|
-
static setTableMapping(mapping: Record<string, string>): void;
|
|
958
|
-
static transaction(fn: (tx: TransactionContext) => void | Promise<void>): Promise<void>;
|
|
959
|
-
static batchGet(requests: BatchGetRequest[]): Promise<BatchGetResult>;
|
|
960
|
-
static batchWrite(requests: BatchWriteRequest[]): Promise<void>;
|
|
961
|
-
static asModel<C extends new (...args: never[]) => DDBModel>(this: C): ModelStatic<InstanceType<C>, C>;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
/**
|
|
965
|
-
* Internal brand identifying a {@link Column} reference at runtime, used by the
|
|
966
|
-
* `cond` raw escape hatch to recognize and alias embedded column references.
|
|
967
|
-
*/
|
|
968
|
-
declare const COLUMN_REF: unique symbol;
|
|
969
|
-
/**
|
|
970
|
-
* A type-safe reference to a model field (column).
|
|
971
|
-
*
|
|
972
|
-
* Each column carries its declared value type as a phantom parameter so the
|
|
973
|
-
* `cond` escape hatch can type-check embedded values against the column. The
|
|
974
|
-
* `__model` phantom brands the column to its owning model, so passing a column
|
|
975
|
-
* from a different model into another model's `cond` is a type error.
|
|
976
|
-
*
|
|
977
|
-
* @typeParam V - The declared field value type.
|
|
978
|
-
* @typeParam M - The owning model (brand only; never read at runtime).
|
|
979
|
-
*/
|
|
980
|
-
interface Column<V = unknown, M = unknown> {
|
|
981
|
-
readonly [COLUMN_REF]: true;
|
|
982
|
-
/** The underlying attribute (property) name. */
|
|
983
|
-
readonly name: string;
|
|
984
|
-
/** @internal phantom — value type carried for `cond` value checking. */
|
|
985
|
-
readonly __value?: V;
|
|
986
|
-
/** @internal phantom — owning-model brand. */
|
|
987
|
-
readonly __model?: M;
|
|
988
|
-
}
|
|
989
|
-
/** Runtime guard: is this value a {@link Column} reference? */
|
|
990
|
-
declare function isColumn(value: unknown): value is Column;
|
|
991
|
-
/**
|
|
992
|
-
* The `Model.col` map: one {@link Column} per scalar field of the entity, each
|
|
993
|
-
* typed with the field's declared value type and branded to the model.
|
|
994
|
-
*
|
|
995
|
-
* @typeParam T - The Entity type.
|
|
996
|
-
*/
|
|
997
|
-
type ColumnMap<T> = {
|
|
998
|
-
[K in keyof T as T[K] extends Function ? never : NonNullable<T[K]> extends DDBModel ? never : T[K] extends Array<infer E> ? [E] extends [DDBModel] ? never : K : K]: Column<T[K], T>;
|
|
999
|
-
};
|
|
1000
|
-
|
|
1001
|
-
declare const KEY_MARKER: unique symbol;
|
|
1002
|
-
/**
|
|
1003
|
-
* Brand identifying a {@link KeySegment} produced by the {@link k} tag.
|
|
1004
|
-
*/
|
|
1005
|
-
declare const KEY_SEGMENT: unique symbol;
|
|
1006
|
-
/**
|
|
1007
|
-
* A single structured key segment — the output of one `` k`...` `` tagged
|
|
1008
|
-
* template. Its literal parts and the column references interpolated between
|
|
1009
|
-
* them are preserved verbatim (the column refs are NOT stringified), so the
|
|
1010
|
-
* planner / spec generator can derive `{field}` templates directly from the
|
|
1011
|
-
* structure rather than recovering them by symbolic evaluation.
|
|
1012
|
-
*
|
|
1013
|
-
* Exactly one `` k`...` `` = one segment = one truncation boundary for a
|
|
1014
|
-
* multi-segment sort key.
|
|
1015
|
-
*/
|
|
1016
|
-
interface KeySegment {
|
|
1017
|
-
readonly [KEY_SEGMENT]: true;
|
|
1018
|
-
/** The literal string parts; always one longer than {@link columns}. */
|
|
1019
|
-
readonly literals: readonly string[];
|
|
1020
|
-
/** The interpolated column references, in order. */
|
|
1021
|
-
readonly columns: readonly Column[];
|
|
1022
|
-
}
|
|
1023
|
-
/** Runtime guard: is this value a {@link KeySegment}? */
|
|
1024
|
-
declare function isKeySegment(value: unknown): value is KeySegment;
|
|
1025
|
-
/**
|
|
1026
|
-
* The interpolation slot accepted by {@link k}: a typed column reference
|
|
1027
|
-
* (`c.field`). Bare strings are NOT accepted — a key field must be named via a
|
|
1028
|
-
* {@link Column} reference so the structure stays type-safe and refactor-safe.
|
|
1029
|
-
*/
|
|
1030
|
-
type KeySlot = Column;
|
|
1031
|
-
/**
|
|
1032
|
-
* Tagged-template builder for a single key segment.
|
|
1033
|
-
*
|
|
1034
|
-
* The literal parts and the interpolated column references are kept structurally
|
|
1035
|
-
* (never stringified), mirroring the {@link cond} escape hatch. A segment such
|
|
1036
|
-
* as `` k`USER#${c.userId}` `` becomes `{ literals: ['USER#', ''], columns: [userId] }`,
|
|
1037
|
-
* which the planner turns into the template `USER#{userId}`.
|
|
1038
|
-
*
|
|
1039
|
-
* @example
|
|
1040
|
-
* ```ts
|
|
1041
|
-
* key((c) => ({ pk: k`USER#${c.userId}`, sk: k`PROFILE` }));
|
|
1042
|
-
* key((c) => ({
|
|
1043
|
-
* pk: k`REGION#${c.region}`,
|
|
1044
|
-
* sk: [k`CITY#${c.city}`, k`STORE#${c.store}`],
|
|
1045
|
-
* }));
|
|
1046
|
-
* ```
|
|
1047
|
-
*/
|
|
1048
|
-
declare function k(literals: TemplateStringsArray, ...columns: KeySlot[]): KeySegment;
|
|
1049
|
-
/** A single segment, or an ordered list of segments (multi-segment SK). */
|
|
1050
|
-
type SegmentSpec = KeySegment | KeySegment[];
|
|
1051
|
-
/** The `{ pk, sk }` structure returned by a key builder. */
|
|
1052
|
-
interface KeyStructure {
|
|
1053
|
-
readonly pk: SegmentSpec;
|
|
1054
|
-
readonly sk?: SegmentSpec;
|
|
1055
|
-
}
|
|
1056
|
-
/**
|
|
1057
|
-
* The canonical, structured representation of a key (PK or SK as a list of
|
|
1058
|
-
* segments). This — not an opaque mapping closure — is the single source of
|
|
1059
|
-
* truth for a model's key.
|
|
1060
|
-
*/
|
|
1061
|
-
interface SegmentedKey {
|
|
1062
|
-
/** Partition-key segments (all required at resolve time). */
|
|
1063
|
-
readonly pkSegments: readonly KeySegment[];
|
|
1064
|
-
/** Sort-key segments, evaluated in order with truncation. */
|
|
1065
|
-
readonly skSegments: readonly KeySegment[];
|
|
1066
|
-
}
|
|
1067
|
-
interface KeyDefinitionMarker<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
1068
|
-
readonly [KEY_MARKER]: true;
|
|
1069
|
-
readonly segmented: SegmentedKey;
|
|
1070
|
-
readonly inputFieldNames: string[];
|
|
1071
|
-
/** @internal Phantom field for type-level input type extraction. */
|
|
1072
|
-
readonly _phantom?: T;
|
|
1073
|
-
}
|
|
1074
|
-
/**
|
|
1075
|
-
* Define a model's primary key from a **structured segment** builder.
|
|
1076
|
-
*
|
|
1077
|
-
* The builder receives a typed column accessor `c` (one `Column` per field) and
|
|
1078
|
-
* returns `{ pk, sk }`, where each of `pk` / `sk` is a single `` k`...` ``
|
|
1079
|
-
* segment or an array of them. Column references are captured structurally, so
|
|
1080
|
-
* the key template is recovered directly from the structure — no symbolic
|
|
1081
|
-
* evaluation of an opaque closure.
|
|
1082
|
-
*
|
|
1083
|
-
* @typeParam T - The key input type (drives compile-time key checking for
|
|
1084
|
-
* `query` / `list`). Supplied by the caller's parameter annotation on `c`.
|
|
1085
|
-
*/
|
|
1086
|
-
declare function key<T extends Record<string, unknown>>(builder: (c: {
|
|
1087
|
-
readonly [K in keyof T]-?: Column<T[K], T>;
|
|
1088
|
-
}) => KeyStructure): KeyDefinitionMarker<T>;
|
|
1089
|
-
|
|
1090
|
-
/**
|
|
1091
|
-
* CDC emulator types (issue #72). These types are **cdc-module-owned**; core
|
|
1092
|
-
* never references them. The {@link ChangeEvent} shape mirrors DynamoDB Streams
|
|
1093
|
-
* (`NEW_AND_OLD_IMAGES`) so a consumer written against the emulator runs
|
|
1094
|
-
* unchanged against real Streams in production (spec §4).
|
|
1095
|
-
*/
|
|
1096
|
-
/** Stream view type. Fixed to `NEW_AND_OLD_IMAGES` — incremental aggregation
|
|
1097
|
-
* needs both images (spec §4). */
|
|
1098
|
-
type StreamViewType = 'NEW_AND_OLD_IMAGES';
|
|
1099
|
-
/** Stream event kind, matching DynamoDB Streams. */
|
|
1100
|
-
type ChangeEventName = 'INSERT' | 'MODIFY' | 'REMOVE';
|
|
1101
|
-
/**
|
|
1102
|
-
* A single change event. Within one shard, `sequenceNumber` is monotonically
|
|
1103
|
-
* increasing; across shards order is independent (spec §6).
|
|
1104
|
-
*/
|
|
1105
|
-
interface ChangeEvent<T = Record<string, unknown>> {
|
|
1106
|
-
eventName: ChangeEventName;
|
|
1107
|
-
table: string;
|
|
1108
|
-
/** Resolved model name when known. */
|
|
1109
|
-
model?: string;
|
|
1110
|
-
keys: {
|
|
1111
|
-
pk: string;
|
|
1112
|
-
sk: string;
|
|
1113
|
-
};
|
|
1114
|
-
/** Present for MODIFY / REMOVE. */
|
|
1115
|
-
oldImage?: T;
|
|
1116
|
-
/** Present for INSERT / MODIFY. */
|
|
1117
|
-
newImage?: T;
|
|
1118
|
-
/** Deterministic-clock-derived creation time (ISO 8601). */
|
|
1119
|
-
approximateCreationTime: string;
|
|
1120
|
-
/** Monotonic within a shard (zero-padded for lexicographic ordering). */
|
|
1121
|
-
sequenceNumber: string;
|
|
1122
|
-
/** Partition = hash(pk). */
|
|
1123
|
-
shardId: string;
|
|
1124
|
-
}
|
|
1125
|
-
/** A delivered batch. Records of the same shard are in `sequenceNumber` order. */
|
|
1126
|
-
interface ChangeBatch<T = Record<string, unknown>> {
|
|
1127
|
-
records: ChangeEvent<T>[];
|
|
1128
|
-
}
|
|
1129
|
-
/**
|
|
1130
|
-
* The consumer's response to a batch (spec §6, `ReportBatchItemFailures`
|
|
1131
|
-
* equivalent). Returning the `sequenceNumber`s of records that failed lets the
|
|
1132
|
-
* emulator advance the checkpoint past the successful prefix and redeliver only
|
|
1133
|
-
* the failures.
|
|
1134
|
-
*/
|
|
1135
|
-
interface BatchResult {
|
|
1136
|
-
/** `sequenceNumber`s of records the consumer failed to process. */
|
|
1137
|
-
batchItemFailures?: string[];
|
|
1138
|
-
}
|
|
1139
|
-
/** The consumer handler. */
|
|
1140
|
-
type ChangeHandler = (batch: ChangeBatch) => Promise<BatchResult | void>;
|
|
1141
|
-
/** Unsubscribe handle. */
|
|
1142
|
-
type Unsubscribe = () => void;
|
|
1143
|
-
/** Where a fresh subscriber begins reading. */
|
|
1144
|
-
type StartingPosition = 'TRIM_HORIZON' | 'LATEST';
|
|
1145
|
-
/** Clock mode. `virtual` makes time deterministic via `advanceClock`. */
|
|
1146
|
-
type ClockMode = 'real' | 'virtual';
|
|
1147
|
-
/** Delivery mode (spec §7). */
|
|
1148
|
-
type CdcMode = 'inline' | 'queued' | 'record' | 'replay';
|
|
1149
|
-
/** Deterministic fault-injection spec (spec §8). All probabilities are seeded. */
|
|
1150
|
-
interface FaultSpec {
|
|
1151
|
-
/** Probability [0,1] a delivered record is also re-delivered (duplicate). */
|
|
1152
|
-
duplicate?: number;
|
|
1153
|
-
/** Shuffle within-shard order on delivery (invariant-violation testing). */
|
|
1154
|
-
reorder?: boolean;
|
|
1155
|
-
/** Per-record virtual-time delivery delay range (ms). Requires queued mode. */
|
|
1156
|
-
delay?: {
|
|
1157
|
-
min: number;
|
|
1158
|
-
max: number;
|
|
1159
|
-
};
|
|
1160
|
-
/** Probability [0,1] a record is dropped on first delivery then redelivered. */
|
|
1161
|
-
dropThenRedeliver?: number;
|
|
1162
|
-
/** Probability [0,1] a record is reported as a partial-batch failure. */
|
|
1163
|
-
partialBatchFailure?: number;
|
|
1164
|
-
}
|
|
1165
|
-
/** A reference whose recompute should be forced to race a mark (spec §8, §7). */
|
|
1166
|
-
interface ConcurrentRecomputeRef {
|
|
1167
|
-
/** Opaque node ref the harness recomputes concurrently with marking. */
|
|
1168
|
-
ref: string;
|
|
1169
|
-
}
|
|
1170
|
-
/** A persisted event log (spec §10, record/replay). */
|
|
1171
|
-
interface EventLog {
|
|
1172
|
-
seed: number;
|
|
1173
|
-
events: ChangeEvent[];
|
|
1174
|
-
}
|
|
1175
|
-
/** Replay options (spec §10). */
|
|
1176
|
-
interface ReplayOptions {
|
|
1177
|
-
/** Deliver events in a deterministic shuffled order (any-order replay). */
|
|
1178
|
-
shuffle?: boolean;
|
|
1179
|
-
/** Probability [0,1] each event is delivered twice during replay. */
|
|
1180
|
-
duplicate?: number;
|
|
1181
|
-
}
|
|
1182
|
-
/** Constructor options (spec §11). */
|
|
1183
|
-
interface CdcEmulatorOptions {
|
|
1184
|
-
/** Stream-enabled model classes (opt-in; default none). */
|
|
1185
|
-
models?: Function[];
|
|
1186
|
-
/** Delivery mode. Default `inline`. */
|
|
1187
|
-
mode?: CdcMode;
|
|
1188
|
-
/** Clock mode. Default `virtual` for queued, `real` otherwise. */
|
|
1189
|
-
clock?: ClockMode;
|
|
1190
|
-
/** Records per delivered batch. Default 100. */
|
|
1191
|
-
batchSize?: number;
|
|
1192
|
-
/** Max redelivery attempts before a record goes to the DLQ. Default 3. */
|
|
1193
|
-
maxRetries?: number;
|
|
1194
|
-
/** Where a subscriber starts. Default `TRIM_HORIZON`. */
|
|
1195
|
-
startingPosition?: StartingPosition;
|
|
1196
|
-
/** Seed for fault injection / shard assignment determinism. Default 1. */
|
|
1197
|
-
seed?: number;
|
|
1198
|
-
/** Number of shards events are partitioned across. Default 8. */
|
|
1199
|
-
shardCount?: number;
|
|
1200
|
-
}
|
|
1201
|
-
type ShardId = string;
|
|
1202
|
-
|
|
1203
|
-
export { type KeyStructure as $, type CdcMode as A, type BatchGetExecInput as B, type ChangeEvent as C, type DynamoDBOperation as D, type Executor as E, type FaultSpec as F, type ChangeBatch as G, type ChangeEventName as H, type ClockMode as I, type Column as J, type ColumnMap as K, type CondSlot as L, type ModelStatic as M, type ConditionCheckInput as N, type DeleteOptions as O, type PutInput as P, type FilterInput as Q, type ResolvedKey as R, type SelectableOf as S, type TransactWriteExecItem as T, type UpdateInput as U, type GsiDefinitionMarker as V, type WriteExecOptions as W, type GsiOptions as X, type KeyDefinitionMarker as Y, type KeySegment as Z, type KeySlot as _, type ExecutorResult as a, type PutOptions as a0, type QueryKeyOf as a1, type QueryResult as a2, type RawCondition as a3, type RelationBuilder as a4, type RelationSelect as a5, type RelationSpec as a6, type SegmentSpec as a7, type SelectBuilder as a8, type SelectOf as a9, type StartingPosition as aa, type StreamViewType as ab, TransactionContext as ac, type Updatable as ad, type UpdateOptions as ae, attachModelClass as af, buildDeleteInput as ag, buildPutInput as ah, buildUpdateInput as ai, cond as aj, executeBatchGet as ak, executeBatchWrite as al, executeDelete as am, executePut as an, executeTransaction as ao, executeUpdate as ap, gsi as aq, isColumn as ar, isKeySegment as as, k as at, key as au, type WriteResult as b, type DeleteInput as c, type BatchWriteExecItem as d, DDBModel as e, type PrimaryKeyOf as f, type SegmentedKey as g, type SelectBuilderSpec as h, type ExecutionPlan as i, type CdcEmulatorOptions as j, type ChangeHandler as k, type Unsubscribe as l, type ConcurrentRecomputeRef as m, type EventLog as n, type ReplayOptions as o, type ShardId as p, type PartialQueryKeyOf as q, type StrictSelectSpec as r, type EntityInput as s, type UniqueQueryKeyOf as t, type BatchDeleteRequest as u, type BatchGetRequest as v, BatchGetResult as w, type BatchPutRequest as x, type BatchResult as y, type BatchWriteRequest as z };
|