@effect-dynamodb/schema 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/Aggregate.d.ts +128 -0
  4. package/dist/Aggregate.d.ts.map +1 -0
  5. package/dist/Aggregate.js +69 -0
  6. package/dist/Aggregate.js.map +1 -0
  7. package/dist/DynamoModel.d.ts +370 -0
  8. package/dist/DynamoModel.d.ts.map +1 -0
  9. package/dist/DynamoModel.js +541 -0
  10. package/dist/DynamoModel.js.map +1 -0
  11. package/dist/DynamoSchema.d.ts +137 -0
  12. package/dist/DynamoSchema.d.ts.map +1 -0
  13. package/dist/DynamoSchema.js +188 -0
  14. package/dist/DynamoSchema.js.map +1 -0
  15. package/dist/Entity.d.ts +239 -0
  16. package/dist/Entity.d.ts.map +1 -0
  17. package/dist/Entity.js +558 -0
  18. package/dist/Entity.js.map +1 -0
  19. package/dist/Errors.d.ts +387 -0
  20. package/dist/Errors.d.ts.map +1 -0
  21. package/dist/Errors.js +218 -0
  22. package/dist/Errors.js.map +1 -0
  23. package/dist/KeyComposer.d.ts +264 -0
  24. package/dist/KeyComposer.d.ts.map +1 -0
  25. package/dist/KeyComposer.js +516 -0
  26. package/dist/KeyComposer.js.map +1 -0
  27. package/dist/Projection.d.ts +28 -0
  28. package/dist/Projection.d.ts.map +1 -0
  29. package/dist/Projection.js +39 -0
  30. package/dist/Projection.js.map +1 -0
  31. package/dist/index.d.ts +32 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +26 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/internal/AggregateCursor.d.ts +44 -0
  36. package/dist/internal/AggregateCursor.d.ts.map +1 -0
  37. package/dist/internal/AggregateCursor.js +18 -0
  38. package/dist/internal/AggregateCursor.js.map +1 -0
  39. package/dist/internal/AggregateEdges.d.ts +117 -0
  40. package/dist/internal/AggregateEdges.d.ts.map +1 -0
  41. package/dist/internal/AggregateEdges.js +51 -0
  42. package/dist/internal/AggregateEdges.js.map +1 -0
  43. package/dist/internal/AggregateSchemas.d.ts +76 -0
  44. package/dist/internal/AggregateSchemas.d.ts.map +1 -0
  45. package/dist/internal/AggregateSchemas.js +164 -0
  46. package/dist/internal/AggregateSchemas.js.map +1 -0
  47. package/dist/internal/AggregateTypes.d.ts +132 -0
  48. package/dist/internal/AggregateTypes.d.ts.map +1 -0
  49. package/dist/internal/AggregateTypes.js +7 -0
  50. package/dist/internal/AggregateTypes.js.map +1 -0
  51. package/dist/internal/DefaultCrypto.d.ts +26 -0
  52. package/dist/internal/DefaultCrypto.d.ts.map +1 -0
  53. package/dist/internal/DefaultCrypto.js +40 -0
  54. package/dist/internal/DefaultCrypto.js.map +1 -0
  55. package/dist/internal/EntityConfig.d.ts +152 -0
  56. package/dist/internal/EntityConfig.d.ts.map +1 -0
  57. package/dist/internal/EntityConfig.js +7 -0
  58. package/dist/internal/EntityConfig.js.map +1 -0
  59. package/dist/internal/EntitySchemas.d.ts +198 -0
  60. package/dist/internal/EntitySchemas.d.ts.map +1 -0
  61. package/dist/internal/EntitySchemas.js +798 -0
  62. package/dist/internal/EntitySchemas.js.map +1 -0
  63. package/dist/internal/EntityTypes.d.ts +233 -0
  64. package/dist/internal/EntityTypes.d.ts.map +1 -0
  65. package/dist/internal/EntityTypes.js +7 -0
  66. package/dist/internal/EntityTypes.js.map +1 -0
  67. package/dist/internal/SchemaAccessors.d.ts +90 -0
  68. package/dist/internal/SchemaAccessors.d.ts.map +1 -0
  69. package/dist/internal/SchemaAccessors.js +171 -0
  70. package/dist/internal/SchemaAccessors.js.map +1 -0
  71. package/package.json +70 -0
@@ -0,0 +1,516 @@
1
+ /**
2
+ * KeyComposer — Composite key composition for DynamoDB keys.
3
+ *
4
+ * v2 uses attribute-list composition (composite: ["userId"]) instead of
5
+ * template strings ("USER#${userId}"). The DynamoSchema module handles
6
+ * the key format; KeyComposer extracts attribute values and delegates.
7
+ */
8
+ import { DateTime } from "effect";
9
+ import { applyCasing, composeClusteredSortKey, composeCollectionKey, composeIsolatedSortKey, composeKey, } from "./DynamoSchema.js";
10
+ /** Normalize a GsiConfig (entity input) to an IndexDefinition (internal format). */
11
+ export const normalizeGsiConfig = (config) => {
12
+ // Detect old format and give helpful migration error
13
+ if ("index" in config && typeof config.index === "object") {
14
+ throw new Error(`[EDD-9003] GsiConfig uses old format with "index" property. ` +
15
+ `Migrate to: { name: "gsi1", pk: { field: "gsi1pk", composite: [...] }, sk: { field: "gsi1sk", composite: [...] } }`);
16
+ }
17
+ if (!config.name || !config.pk || !config.sk) {
18
+ throw new Error(`[EDD-9003] Invalid GsiConfig: requires name, pk: { field, composite }, sk: { field, composite }`);
19
+ }
20
+ return {
21
+ index: config.name,
22
+ collection: config.collection,
23
+ type: config.type ?? "isolated",
24
+ pk: { field: config.pk.field, composite: [...config.pk.composite] },
25
+ sk: { field: config.sk.field, composite: [...config.sk.composite] },
26
+ indexPolicy: config.indexPolicy,
27
+ };
28
+ };
29
+ /**
30
+ * Extract composite attribute values from an entity record.
31
+ *
32
+ * Given composite: ["tenantId", "email"] and record: { tenantId: "t-1", email: "a@b.com" }
33
+ * Returns: ["t-1", "a@b.com"]
34
+ */
35
+ export const extractComposites = (composite, record) => composite.map((attr) => {
36
+ const value = record[attr];
37
+ if (value === undefined || value === null) {
38
+ throw new Error(`Missing composite attribute "${attr}" in record`);
39
+ }
40
+ return serializeValue(value);
41
+ });
42
+ /**
43
+ * Non-throwing variant of extractComposites. Returns undefined when any
44
+ * composite attribute is missing or null. Used for sparse GSI support —
45
+ * if a GSI's composites aren't all present, the index is simply skipped.
46
+ */
47
+ export const tryExtractComposites = (composite, record) => {
48
+ const values = [];
49
+ for (const attr of composite) {
50
+ const value = record[attr];
51
+ if (value === undefined || value === null)
52
+ return undefined;
53
+ values.push(serializeValue(value));
54
+ }
55
+ return values;
56
+ };
57
+ /**
58
+ * Serialize a value for use in a composite key.
59
+ *
60
+ * Numeric values are zero-padded for correct lexicographic sort order:
61
+ * - number → 16-digit zero-padded string (covers Number.MAX_SAFE_INTEGER)
62
+ * - bigint → 38-digit zero-padded string (covers DynamoDB's max precision)
63
+ *
64
+ * DateTime values are formatted as ISO strings for correct sort order:
65
+ * - DateTime.Zoned → ISO string with offset (preserves timezone info)
66
+ * - DateTime.Utc → ISO string
67
+ * - Date → ISO string
68
+ */
69
+ export const serializeValue = (value) => {
70
+ if (typeof value === "string")
71
+ return value;
72
+ if (typeof value === "number")
73
+ return String(value).padStart(16, "0");
74
+ if (typeof value === "bigint")
75
+ return String(value).padStart(38, "0");
76
+ if (typeof value === "boolean")
77
+ return value ? "true" : "false";
78
+ // DateTime types → ISO string (Zoned normalized to UTC for sort order)
79
+ if (typeof value === "object" && value !== null && DateTime.isDateTime(value)) {
80
+ return DateTime.formatIso(value);
81
+ }
82
+ // Native Date
83
+ if (value instanceof Date)
84
+ return value.toISOString();
85
+ return String(value);
86
+ };
87
+ /**
88
+ * Compose a partition key for an entity index.
89
+ *
90
+ * For non-collection indexes: `$schema#v<version>#entityType#attr1#attr2`
91
+ * For collection indexes: `$schema#v<version>#collectionName#attr1#attr2`
92
+ */
93
+ export const composePk = (schema, entityType, index, record) => {
94
+ const composites = extractComposites(index.pk.composite, record);
95
+ const names = [...index.pk.composite];
96
+ const collection = index.collection;
97
+ if (collection !== undefined) {
98
+ // Collection index — PK uses collection name
99
+ const collectionName = Array.isArray(collection) ? collection[0] : collection;
100
+ return composeCollectionKey(schema, collectionName, composites, { casing: index.casing, names });
101
+ }
102
+ // Regular entity index — PK uses entity type
103
+ return composeKey(schema, entityType, composites, { casing: index.casing, names });
104
+ };
105
+ /**
106
+ * Compose a sort key for an entity index.
107
+ *
108
+ * For isolated indexes: `$schema#v<version>#entityType_version#attr1#attr2`
109
+ * For clustered indexes: `$schema#v<version>#collectionName#entityType_version#attr1#attr2`
110
+ * For non-collection indexes: `$schema#v<version>#entityType#attr1#attr2`
111
+ */
112
+ export const composeSk = (schema, entityType, entityVersion, index, record) => {
113
+ const composites = extractComposites(index.sk.composite, record);
114
+ const names = [...index.sk.composite];
115
+ const collection = index.collection;
116
+ const collectionType = index.type ?? "isolated";
117
+ if (collection !== undefined) {
118
+ if (collectionType === "clustered") {
119
+ // For sub-collections (collection: ["parent", "child"]) the FULL hierarchy
120
+ // is written into the SK so a begins_with query at any level matches.
121
+ return composeClusteredSortKey(schema, collection, entityType, entityVersion, composites, {
122
+ casing: index.casing,
123
+ names,
124
+ });
125
+ }
126
+ // Isolated
127
+ return composeIsolatedSortKey(schema, entityType, entityVersion, composites, {
128
+ casing: index.casing,
129
+ names,
130
+ });
131
+ }
132
+ // Non-collection — simple entity key
133
+ return composeKey(schema, entityType, composites, { casing: index.casing, names });
134
+ };
135
+ /**
136
+ * Compose all key attributes for a single index.
137
+ * Returns a record mapping field names to composed key values.
138
+ */
139
+ export const composeIndexKeys = (schema, entityType, entityVersion, index, record) => ({
140
+ [index.pk.field]: composePk(schema, entityType, index, record),
141
+ [index.sk.field]: composeSk(schema, entityType, entityVersion, index, record),
142
+ });
143
+ /**
144
+ * Non-throwing variant of composeIndexKeys. Returns undefined if any composite
145
+ * attribute is missing. Used for sparse GSI support.
146
+ */
147
+ export const tryComposeIndexKeys = (schema, entityType, entityVersion, index, record) => {
148
+ if (tryExtractComposites(index.pk.composite, record) === undefined)
149
+ return undefined;
150
+ if (tryExtractComposites(index.sk.composite, record) === undefined)
151
+ return undefined;
152
+ return composeIndexKeys(schema, entityType, entityVersion, index, record);
153
+ };
154
+ /**
155
+ * Compose keys for all indexes of an entity.
156
+ * Returns a flat record of all key field -> value mappings.
157
+ */
158
+ export const composeAllKeys = (schema, entityType, entityVersion, indexes, record) => {
159
+ const result = {};
160
+ for (const [indexName, index] of Object.entries(indexes)) {
161
+ if (indexName === "primary") {
162
+ // Primary index always required — throws on missing composites
163
+ Object.assign(result, composeIndexKeys(schema, entityType, entityVersion, index, record));
164
+ }
165
+ else {
166
+ // GSI — sparse-aware: skip if any composite is missing
167
+ const keys = tryComposeIndexKeys(schema, entityType, entityVersion, index, record);
168
+ if (keys !== undefined)
169
+ Object.assign(result, keys);
170
+ }
171
+ }
172
+ return result;
173
+ };
174
+ /**
175
+ * Compose a partition key from the leading prefix
176
+ * `[pk_0, ..., pk_(stopBefore-1)]` of an index's PK composites. Used by v3's
177
+ * structural composition rule when a trailing PK composite is absent and the
178
+ * half's `indexPolicy.pk` is `"preserve"` (or absent — defaults to preserve).
179
+ *
180
+ * `stopBefore === 0` produces the bare entity/collection prefix with no
181
+ * composite values.
182
+ */
183
+ const composePkPrefixUpTo = (schema, entityType, index, record, stopBefore) => {
184
+ const slice = index.pk.composite.slice(0, stopBefore);
185
+ const composites = extractComposites(slice, record);
186
+ const collection = index.collection;
187
+ if (collection !== undefined) {
188
+ const collectionName = Array.isArray(collection) ? collection[0] : collection;
189
+ return composeCollectionKey(schema, collectionName, composites, {
190
+ casing: index.casing,
191
+ names: [...slice],
192
+ });
193
+ }
194
+ return composeKey(schema, entityType, composites, {
195
+ casing: index.casing,
196
+ names: [...slice],
197
+ });
198
+ };
199
+ /**
200
+ * Compose a sort key from the leading prefix `[sk_0, ..., sk_(stopBefore-1)]`
201
+ * of an index's SK composites. Used by v3's structural composition rule when a
202
+ * trailing SK composite is absent and the half's `indexPolicy.sk` is
203
+ * `"preserve"` (or absent — defaults to preserve), or when a hole pattern is
204
+ * encountered under `"sparse"`.
205
+ *
206
+ * `stopBefore === 0` produces the bare entity/collection prefix with no
207
+ * composite values (the item still belongs to the GSI but at the broadest
208
+ * scope — `begins_with(sk, "<prefix>")` matches it).
209
+ *
210
+ * See `DESIGN.md §7 Policy-Aware GSI Composition`.
211
+ */
212
+ export const composeSkPrefixUpTo = (schema, entityType, entityVersion, index, record, stopBefore) => {
213
+ const slice = index.sk.composite.slice(0, stopBefore);
214
+ const composites = extractComposites(slice, record);
215
+ const collection = index.collection;
216
+ const collectionType = index.type ?? "isolated";
217
+ if (collection !== undefined) {
218
+ if (collectionType === "clustered") {
219
+ return composeClusteredSortKey(schema, collection, entityType, entityVersion, composites, {
220
+ casing: index.casing,
221
+ names: [...slice],
222
+ });
223
+ }
224
+ return composeIsolatedSortKey(schema, entityType, entityVersion, composites, {
225
+ casing: index.casing,
226
+ names: [...slice],
227
+ });
228
+ }
229
+ return composeKey(schema, entityType, composites, {
230
+ casing: index.casing,
231
+ names: [...slice],
232
+ });
233
+ };
234
+ /**
235
+ * Apply v1.7.1's structural composition rule to a single half that the
236
+ * skip-predicate gate (v1.7.3) decided not to skip.
237
+ *
238
+ * The caller is responsible for the per-half evaluation gate — `classifyHalf`
239
+ * is only called once the gate has decided this half should be evaluated
240
+ * (the half has no composites; OR a composite is in `removedSet`; OR a
241
+ * composite is in `updatePayload` or `keyRecord`).
242
+ *
243
+ * Walks the composite list left-to-right, finds the longest leading prefix of
244
+ * present values in `record`, and classifies the result:
245
+ *
246
+ * - **Compose succeeded** (non-empty leading prefix AND no hole — i.e. all
247
+ * absent composites are at trailing positions only): SET.
248
+ * - **Compose failed** (empty leading prefix OR hole pattern): outcome
249
+ * depends on `policy` and whether any composite of this half is in
250
+ * `removedSet`:
251
+ * - `'sparse'` → drop.
252
+ * - `'preserve'` AND any composite in `removedSet` → drop (cascade
253
+ * override under preserve — the consumer's explicit signal trumps
254
+ * stale-data preservation).
255
+ * - `'preserve'` AND no composite in `removedSet` → noop (preserve
256
+ * preservation; stored key may be stale).
257
+ *
258
+ * Hole patterns collapse into can't-compose — silent partial composition
259
+ * (truncate-and-discard-trailing) is a worse failure mode than dropping the
260
+ * half because it would lie about the data shape to readers. The previous
261
+ * v1.7.0 sparse-truncate-on-hole behavior is gone.
262
+ */
263
+ const classifyHalf = (composites, record, policy, halfHasRemoved) => {
264
+ // No composites at all (e.g. sk.composite = []) — emit a SET of length 0
265
+ // (the bare entity prefix). Policy is irrelevant; this is the standard
266
+ // "primary key with empty SK composite" shape.
267
+ if (composites.length === 0) {
268
+ return { kind: "set", length: 0 };
269
+ }
270
+ let leadingLen = 0;
271
+ while (leadingLen < composites.length) {
272
+ const v = record[composites[leadingLen]];
273
+ if (v === undefined || v === null)
274
+ break;
275
+ leadingLen++;
276
+ }
277
+ // All present → SET full length.
278
+ if (leadingLen === composites.length) {
279
+ return { kind: "set", length: leadingLen };
280
+ }
281
+ // Some absent. Check for hole (a present composite after the absent run).
282
+ let hasHole = false;
283
+ for (let j = leadingLen + 1; j < composites.length; j++) {
284
+ const v = record[composites[j]];
285
+ if (v !== undefined && v !== null) {
286
+ hasHole = true;
287
+ break;
288
+ }
289
+ }
290
+ // Compose-can-succeed iff non-empty leading prefix AND no hole.
291
+ // (Trailing-absent without a hole is a clean truncation.)
292
+ if (!hasHole && leadingLen > 0) {
293
+ return { kind: "set", length: leadingLen };
294
+ }
295
+ // Compose failed (empty leading prefix OR hole pattern). Per-half outcome:
296
+ if (policy === "sparse")
297
+ return { kind: "drop" };
298
+ // preserve — cascade override under removedSet, otherwise noop.
299
+ return halfHasRemoved ? { kind: "drop" } : { kind: "noop" };
300
+ };
301
+ /**
302
+ * Resolve the per-half policy for an index. Defaults each half to `"preserve"`
303
+ * when omitted (or when `indexPolicy` is omitted entirely).
304
+ */
305
+ const resolveIndexPolicy = (policy) => ({
306
+ pk: policy?.pk ?? "preserve",
307
+ sk: policy?.sk ?? "preserve",
308
+ });
309
+ /**
310
+ * Policy-aware GSI key composition for `Entity.update` and time-series
311
+ * `.append` — v1.7.1 per-half structural composition with per-half evaluation
312
+ * gate and per-half cascade.
313
+ *
314
+ * **Per-half evaluation gate (reframed in v1.7.3 as a skip-predicate).** Each
315
+ * half (PK and SK) is independently asked one question: *can this half be
316
+ * safely skipped?* The gate exists for exactly one reason — multi-writer
317
+ * protection: prevent writer A from clobbering keys for halves it doesn't
318
+ * own when writer B does own them. The skip-predicate states that purpose
319
+ * directly: skip iff multi-writer protection actually applies (the half has
320
+ * composites, no composite was explicitly removed, and every composite is
321
+ * absent from BOTH `updatePayload` AND `keyRecord` — so this writer is
322
+ * genuinely not claiming ownership of this half on this call). If the half
323
+ * cannot be safely skipped, it is evaluated by the structural rule.
324
+ *
325
+ * Pre-v1.7.3 the predicate was inverted ("list cases for which to evaluate"
326
+ * as a chain of `||` clauses) and had to enumerate every shape: payload
327
+ * membership (v1.7.0), `removedSet` membership (v1.7.1), `keyRecord`
328
+ * membership (v1.7.2). Each missed shape required another tactical patch:
329
+ * v1.7.1 missed the multi-writer leak (#41), v1.7.2 missed the
330
+ * PK-composites-only case (#43), v1.7.2 then missed the empty-composite-half
331
+ * case (#46) because every `.some(...)` clause trivially returned `false`
332
+ * for an empty composite array. The skip-predicate closes the entire class
333
+ * of degenerate-case bugs structurally.
334
+ *
335
+ * **Two-way payload classification.**
336
+ * - **present** (`attr: <value>` in payload, or inherited from `keyRecord`)
337
+ * — value is used in composition.
338
+ * - **absent** (key omitted, or `attr: null`, or `attr: undefined`) — the
339
+ * structural rule treats all three identically.
340
+ *
341
+ * **Per-half outcome (v1.7.1 unified rule).** For each touched half:
342
+ * 1. Run the structural rule (longest leading prefix of present values in
343
+ * the merged record).
344
+ * 2. If the prefix is non-empty AND there's no hole → SET (full or truncated).
345
+ * 3. Otherwise (empty leading prefix OR hole pattern):
346
+ * - `'sparse'` → REMOVE this half's key.
347
+ * - `'preserve'` AND any composite of this half is in `removedSet` →
348
+ * REMOVE (cascade override under preserve).
349
+ * - `'preserve'` AND no composite in `removedSet` → noop (stored key
350
+ * retained, may be stale until next write touches the half).
351
+ *
352
+ * **Per-half roll-up (v1.7.1).** Each half's outcome applies to its own key
353
+ * attribute only. PK dropping doesn't drop SK; SK dropping doesn't drop PK.
354
+ * The item may be invisible in the GSI during a single-half-dropped period
355
+ * (DDB projection rule needs both keys for query visibility), but the
356
+ * surviving half's value persists for rejoin without other writers re-firing.
357
+ * The v1.7.0 GSI-wide cascade is gone.
358
+ *
359
+ * **`Entity.remove([attr])` cascade (v1.7.1: per-half).** Composites in
360
+ * `removedSet` contribute to the merged record as "absent" (same as
361
+ * undefined-in-payload) AND make their containing half "touched" for the
362
+ * evaluation gate AND trigger the cascade override under preserve. The
363
+ * cascade no longer drops both halves of every GSI containing the cleared
364
+ * attribute — only the half(s) whose composite list contains it.
365
+ *
366
+ * See `DESIGN.md §7 Policy-Aware GSI Composition` for the full decision
367
+ * table and worked multi-writer examples.
368
+ */
369
+ export const composeGsiKeysForUpdatePolicyAware = (schema, entityType, entityVersion, indexes, updatePayload, keyRecord, options) => {
370
+ const sets = {};
371
+ const removes = [];
372
+ const removedSet = options?.removedSet;
373
+ for (const [indexName, index] of Object.entries(indexes)) {
374
+ if (indexName === "primary")
375
+ continue;
376
+ const pkComposites = index.pk.composite;
377
+ const skComposites = index.sk.composite;
378
+ // Per-half evaluation gate (reframed in v1.7.3 as a skip-predicate —
379
+ // closes #46 and the class of degenerate-case bugs that v1.7.0–v1.7.2
380
+ // were chasing as separate `||` arms). The gate's only purpose is
381
+ // multi-writer protection — preventing writer A from clobbering keys
382
+ // for halves it doesn't own when writer B does own them. The skip
383
+ // predicate states that purpose directly: skip iff
384
+ //
385
+ // - the half has composites (otherwise the value is a constant
386
+ // entity/collection prefix — nothing to multi-writer-clobber, so
387
+ // skipping would just leave a missing key forever — closes #46),
388
+ // - AND no composite of this half was explicitly removed (cascade
389
+ // intent must always evaluate),
390
+ // - AND every composite is absent from BOTH `updatePayload` AND
391
+ // `keyRecord` (so this writer is genuinely not claiming ownership
392
+ // of this half on this call — preserves the v1.7.1 multi-writer
393
+ // fix #41 and the v1.7.2 PK-composites-only fix #43, since
394
+ // entity-PK composites are always present in `keyRecord`).
395
+ //
396
+ // The negation `!shouldSkip` is observably equivalent to the previous
397
+ // touched-predicate `||`-chain extended with `composites.length === 0`:
398
+ // every fix the touched-predicate accumulated falls out of one of the
399
+ // skip-predicate's three structural conditions. See `DESIGN.md §7`.
400
+ const pkHasRemoved = removedSet !== undefined && pkComposites.some((attr) => removedSet.has(attr));
401
+ const skHasRemoved = removedSet !== undefined && skComposites.some((attr) => removedSet.has(attr));
402
+ const skipPk = pkComposites.length > 0 &&
403
+ !pkHasRemoved &&
404
+ pkComposites.every((attr) => !(attr in updatePayload) && !(attr in keyRecord));
405
+ const skipSk = skComposites.length > 0 &&
406
+ !skHasRemoved &&
407
+ skComposites.every((attr) => !(attr in updatePayload) && !(attr in keyRecord));
408
+ if (skipPk && skipSk)
409
+ continue;
410
+ // Build merged record for value extraction. Two-way classification: any
411
+ // payload value that is `null` or `undefined` is treated as absent. Any
412
+ // composite in `removedSet` is also treated as absent (the cascade
413
+ // signal). `keyRecord` provides values for composites the consumer did
414
+ // not touch (typically the entity's primary-key composites pulled
415
+ // through to GSI composites that share the names).
416
+ const merged = { ...keyRecord };
417
+ for (const [k, v] of Object.entries(updatePayload)) {
418
+ if (v === null || v === undefined) {
419
+ delete merged[k];
420
+ continue;
421
+ }
422
+ merged[k] = v;
423
+ }
424
+ if (removedSet !== undefined) {
425
+ for (const attr of removedSet) {
426
+ delete merged[attr];
427
+ }
428
+ }
429
+ const policy = resolveIndexPolicy(index.indexPolicy);
430
+ if (!skipPk) {
431
+ const pkOutcome = classifyHalf(pkComposites, merged, policy.pk, pkHasRemoved);
432
+ if (pkOutcome.kind === "drop") {
433
+ removes.push(index.pk.field);
434
+ }
435
+ else if (pkOutcome.kind === "set") {
436
+ sets[index.pk.field] =
437
+ pkOutcome.length === pkComposites.length
438
+ ? composePk(schema, entityType, index, merged)
439
+ : composePkPrefixUpTo(schema, entityType, index, merged, pkOutcome.length);
440
+ }
441
+ // noop — emit nothing for this half.
442
+ }
443
+ if (!skipSk) {
444
+ const skOutcome = classifyHalf(skComposites, merged, policy.sk, skHasRemoved);
445
+ if (skOutcome.kind === "drop") {
446
+ removes.push(index.sk.field);
447
+ }
448
+ else if (skOutcome.kind === "set") {
449
+ sets[index.sk.field] =
450
+ skOutcome.length === skComposites.length
451
+ ? composeSk(schema, entityType, entityVersion, index, merged)
452
+ : composeSkPrefixUpTo(schema, entityType, entityVersion, index, merged, skOutcome.length);
453
+ }
454
+ // noop — emit nothing for this half.
455
+ }
456
+ }
457
+ return { sets, removes };
458
+ };
459
+ /**
460
+ * Compose a partial sort key prefix for query operations.
461
+ * Used when not all SK composite attributes are provided.
462
+ *
463
+ * For example, if SK composite is ["department", "hireDate"] and only "department"
464
+ * is provided, this generates a begins_with prefix.
465
+ */
466
+ export const composeSortKeyPrefix = (schema, entityType, entityVersion, index, record) => {
467
+ // Collect available composites and their names (stop at first missing)
468
+ const available = [];
469
+ const names = [];
470
+ for (const attr of index.sk.composite) {
471
+ const value = record[attr];
472
+ if (value === undefined || value === null)
473
+ break;
474
+ available.push(serializeValue(value));
475
+ names.push(attr);
476
+ }
477
+ const collection = index.collection;
478
+ const collectionType = index.type ?? "isolated";
479
+ if (collection !== undefined) {
480
+ if (collectionType === "clustered") {
481
+ // For sub-collections, pass the full hierarchy so the SK prefix matches
482
+ // the same hierarchy written by composeSk during put.
483
+ return composeClusteredSortKey(schema, collection, entityType, entityVersion, available, {
484
+ casing: index.casing,
485
+ names,
486
+ });
487
+ }
488
+ return composeIsolatedSortKey(schema, entityType, entityVersion, available, {
489
+ casing: index.casing,
490
+ names,
491
+ });
492
+ }
493
+ return composeKey(schema, entityType, available, { casing: index.casing, names });
494
+ };
495
+ // ---------------------------------------------------------------------------
496
+ // Time-series key helpers (used by `Entity.append()` / `.history()`).
497
+ // Event item SK format: `<currentSk>#e#<serialised-orderBy-value>`.
498
+ // Casing is applied to both the `#e#` infix and the value for consistency with
499
+ // the rest of the SK — matches how every other composite segment is cased.
500
+ // ---------------------------------------------------------------------------
501
+ const EVENT_SK_INFIX = "e";
502
+ /**
503
+ * Compose an event-item sort key by decorating the current-item SK.
504
+ *
505
+ * Given `currentSk = "$app#v1#telemetry_1"` and `orderByValue = 42`, returns
506
+ * `"$app#v1#telemetry_1#e#0000000000000042"` (with default lowercase casing).
507
+ */
508
+ export const composeEventSk = (currentSk, orderByValue, casing = "lowercase") => `${currentSk}#${applyCasing(EVENT_SK_INFIX, casing)}#${applyCasing(serializeValue(orderByValue), casing)}`;
509
+ /**
510
+ * Compose the prefix used to scope `.history()` queries to event items only.
511
+ *
512
+ * Given `currentSk = "$app#v1#telemetry_1"`, returns
513
+ * `"$app#v1#telemetry_1#e#"`.
514
+ */
515
+ export const composeEventSkPrefix = (currentSk, casing = "lowercase") => `${currentSk}#${applyCasing(EVENT_SK_INFIX, casing)}#`;
516
+ //# sourceMappingURL=KeyComposer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyComposer.js","sourceRoot":"","sources":["../src/KeyComposer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Projection — Select specific attributes in get/query operations.
3
+ *
4
+ * Projections reduce read capacity and network transfer by fetching only
5
+ * the requested attributes. The result is a partial record (not decoded
6
+ * through the full entity schema, since projected items may not match).
7
+ *
8
+ * Usage:
9
+ * - projection(["name", "email"]) → { expression, names }
10
+ * - Pass to get/query operations via ProjectionExpression + ExpressionAttributeNames
11
+ */
12
+ export interface ProjectionResult {
13
+ readonly expression: string;
14
+ readonly names: Record<string, string>;
15
+ }
16
+ /**
17
+ * Build a ProjectionExpression from a list of attribute names.
18
+ * Uses ExpressionAttributeNames to safely handle reserved words.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const proj = Projection.projection(["name", "email", "status"])
23
+ * // proj.expression === "#proj_name, #proj_email, #proj_status"
24
+ * // proj.names === { "#proj_name": "name", "#proj_email": "email", "#proj_status": "status" }
25
+ * ```
26
+ */
27
+ export declare const projection: (attributes: ReadonlyArray<string>) => ProjectionResult;
28
+ //# sourceMappingURL=Projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Projection.d.ts","sourceRoot":"","sources":["../src/Projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACvC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GAAI,YAAY,aAAa,CAAC,MAAM,CAAC,KAAG,gBAkB9D,CAAA"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Projection — Select specific attributes in get/query operations.
3
+ *
4
+ * Projections reduce read capacity and network transfer by fetching only
5
+ * the requested attributes. The result is a partial record (not decoded
6
+ * through the full entity schema, since projected items may not match).
7
+ *
8
+ * Usage:
9
+ * - projection(["name", "email"]) → { expression, names }
10
+ * - Pass to get/query operations via ProjectionExpression + ExpressionAttributeNames
11
+ */
12
+ /**
13
+ * Build a ProjectionExpression from a list of attribute names.
14
+ * Uses ExpressionAttributeNames to safely handle reserved words.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const proj = Projection.projection(["name", "email", "status"])
19
+ * // proj.expression === "#proj_name, #proj_email, #proj_status"
20
+ * // proj.names === { "#proj_name": "name", "#proj_email": "email", "#proj_status": "status" }
21
+ * ```
22
+ */
23
+ export const projection = (attributes) => {
24
+ if (attributes.length === 0) {
25
+ return { expression: "", names: {} };
26
+ }
27
+ const names = {};
28
+ const parts = [];
29
+ for (const attr of attributes) {
30
+ const placeholder = `#proj_${attr}`;
31
+ names[placeholder] = attr;
32
+ parts.push(placeholder);
33
+ }
34
+ return {
35
+ expression: parts.join(", "),
36
+ names,
37
+ };
38
+ };
39
+ //# sourceMappingURL=Projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Projection.js","sourceRoot":"","sources":["../src/Projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,UAAiC,EAAoB,EAAE;IAChF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IACtC,CAAC;IAED,MAAM,KAAK,GAA2B,EAAE,CAAA;IACxC,MAAM,KAAK,GAAkB,EAAE,CAAA;IAE/B,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,SAAS,IAAI,EAAE,CAAA;QACnC,KAAK,CAAC,WAAW,CAAC,GAAG,IAAI,CAAA;QACzB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IACzB,CAAC;IAED,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,KAAK;KACN,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @effect-dynamodb/schema — Pure schema/relationship-derivation layer.
3
+ *
4
+ * This package contains the AWS-free core of effect-dynamodb: the domain-model
5
+ * annotation system (`DynamoModel`), the application namespace (`DynamoSchema`),
6
+ * composite key composition (`KeyComposer`), tagged errors, and the pure
7
+ * `Entity` / `Aggregate` definition builders with their derived `inputSchema` /
8
+ * `updateSchema` / `createSchema` codecs.
9
+ *
10
+ * It has ZERO dependency on the AWS SDK — neither at runtime nor in its emitted
11
+ * `.d.ts` surface — so consumers can import an entity/aggregate's derived schemas
12
+ * (e.g. for HttpApi payloads or validation) without pulling the AWS SDK into
13
+ * their dependency graph or type surface.
14
+ *
15
+ * The full `effect-dynamodb` package depends on and re-exports everything here,
16
+ * adding the AWS runtime (DynamoClient, CRUD operations, Batch/Transaction/
17
+ * Collection, Marshaller).
18
+ */
19
+ export type { AggregateDefinition, BoundAggregate } from "./Aggregate.js";
20
+ export * as Aggregate from "./Aggregate.js";
21
+ export * as DynamoModel from "./DynamoModel.js";
22
+ export type { Casing, DynamoSchema as DynamoSchemaType } from "./DynamoSchema.js";
23
+ export * as DynamoSchema from "./DynamoSchema.js";
24
+ export type { AnyRefValue, EntityDefinition, EntityDefinition as EntityType, } from "./Entity.js";
25
+ export * as Entity from "./Entity.js";
26
+ export { AggregateAssemblyError, AggregateDecompositionError, AggregateTransactionOverflow, CascadePartialFailure, CompositeKeyHoleError, CompositeNullableError, ConditionalCheckFailed, DynamoError, DynamoValidationError, InternalServerError, ItemDeleted, ItemNotDeleted, ItemNotFound, makeCompositeKeyHoleError, makeCompositeNullableError, OptimisticLockError, RefNotFound, ResourceNotFoundError, StaleAppend, ThrottlingError, TransactionCancelled, TransactionOverflow, UniqueConstraintViolation, ValidationError, VersionConflict, } from "./Errors.js";
27
+ export type { AppendInputType, AppendSuccess, EntityInputType, EntityKeyType, EntityRecordType, EntityRefInputType, EntityRefUpdateType, EntityUpdateType, IndexPkComposites, IndexPkInput, IndexSkComposites, ModelType, PrimaryKeyComposites, RefErrors, SystemFieldsType, } from "./internal/EntityTypes.js";
28
+ export type { GsiConfig, IndexDefinition, KeyPart } from "./KeyComposer.js";
29
+ export * as KeyComposer from "./KeyComposer.js";
30
+ export type { ProjectionResult } from "./Projection.js";
31
+ export * as Projection from "./Projection.js";
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,YAAY,EAAE,MAAM,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACjF,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AACjD,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,gBAAgB,IAAI,UAAU,GAC/B,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,4BAA4B,EAC5B,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,yBAAyB,EACzB,0BAA0B,EAC1B,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,yBAAyB,EACzB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,eAAe,EACf,aAAa,EACb,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,oBAAoB,EACpB,SAAS,EACT,gBAAgB,GACjB,MAAM,2BAA2B,CAAA;AAClC,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA"}