@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.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/Aggregate.d.ts +128 -0
- package/dist/Aggregate.d.ts.map +1 -0
- package/dist/Aggregate.js +69 -0
- package/dist/Aggregate.js.map +1 -0
- package/dist/DynamoModel.d.ts +370 -0
- package/dist/DynamoModel.d.ts.map +1 -0
- package/dist/DynamoModel.js +541 -0
- package/dist/DynamoModel.js.map +1 -0
- package/dist/DynamoSchema.d.ts +137 -0
- package/dist/DynamoSchema.d.ts.map +1 -0
- package/dist/DynamoSchema.js +188 -0
- package/dist/DynamoSchema.js.map +1 -0
- package/dist/Entity.d.ts +239 -0
- package/dist/Entity.d.ts.map +1 -0
- package/dist/Entity.js +558 -0
- package/dist/Entity.js.map +1 -0
- package/dist/Errors.d.ts +387 -0
- package/dist/Errors.d.ts.map +1 -0
- package/dist/Errors.js +218 -0
- package/dist/Errors.js.map +1 -0
- package/dist/KeyComposer.d.ts +264 -0
- package/dist/KeyComposer.d.ts.map +1 -0
- package/dist/KeyComposer.js +516 -0
- package/dist/KeyComposer.js.map +1 -0
- package/dist/Projection.d.ts +28 -0
- package/dist/Projection.d.ts.map +1 -0
- package/dist/Projection.js +39 -0
- package/dist/Projection.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/AggregateCursor.d.ts +44 -0
- package/dist/internal/AggregateCursor.d.ts.map +1 -0
- package/dist/internal/AggregateCursor.js +18 -0
- package/dist/internal/AggregateCursor.js.map +1 -0
- package/dist/internal/AggregateEdges.d.ts +117 -0
- package/dist/internal/AggregateEdges.d.ts.map +1 -0
- package/dist/internal/AggregateEdges.js +51 -0
- package/dist/internal/AggregateEdges.js.map +1 -0
- package/dist/internal/AggregateSchemas.d.ts +76 -0
- package/dist/internal/AggregateSchemas.d.ts.map +1 -0
- package/dist/internal/AggregateSchemas.js +164 -0
- package/dist/internal/AggregateSchemas.js.map +1 -0
- package/dist/internal/AggregateTypes.d.ts +132 -0
- package/dist/internal/AggregateTypes.d.ts.map +1 -0
- package/dist/internal/AggregateTypes.js +7 -0
- package/dist/internal/AggregateTypes.js.map +1 -0
- package/dist/internal/DefaultCrypto.d.ts +26 -0
- package/dist/internal/DefaultCrypto.d.ts.map +1 -0
- package/dist/internal/DefaultCrypto.js +40 -0
- package/dist/internal/DefaultCrypto.js.map +1 -0
- package/dist/internal/EntityConfig.d.ts +152 -0
- package/dist/internal/EntityConfig.d.ts.map +1 -0
- package/dist/internal/EntityConfig.js +7 -0
- package/dist/internal/EntityConfig.js.map +1 -0
- package/dist/internal/EntitySchemas.d.ts +198 -0
- package/dist/internal/EntitySchemas.d.ts.map +1 -0
- package/dist/internal/EntitySchemas.js +798 -0
- package/dist/internal/EntitySchemas.js.map +1 -0
- package/dist/internal/EntityTypes.d.ts +233 -0
- package/dist/internal/EntityTypes.d.ts.map +1 -0
- package/dist/internal/EntityTypes.js +7 -0
- package/dist/internal/EntityTypes.js.map +1 -0
- package/dist/internal/SchemaAccessors.d.ts +90 -0
- package/dist/internal/SchemaAccessors.d.ts.map +1 -0
- package/dist/internal/SchemaAccessors.js +171 -0
- package/dist/internal/SchemaAccessors.js.map +1 -0
- 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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|