dyna-record 0.6.3 → 0.6.4
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 +52 -1
- package/dist/src/decorators/attributes/ObjectAttribute.d.ts +6 -1
- package/dist/src/decorators/attributes/ObjectAttribute.d.ts.map +1 -1
- package/dist/src/decorators/attributes/ObjectAttribute.js +47 -4
- package/dist/src/decorators/attributes/index.d.ts +1 -1
- package/dist/src/decorators/attributes/index.d.ts.map +1 -1
- package/dist/src/decorators/attributes/serializers.d.ts +19 -0
- package/dist/src/decorators/attributes/serializers.d.ts.map +1 -1
- package/dist/src/decorators/attributes/serializers.js +58 -0
- package/dist/src/decorators/attributes/types.d.ts +103 -8
- package/dist/src/decorators/attributes/types.d.ts.map +1 -1
- package/dist/src/operations/utils/flattenObjectForUpdate.d.ts +2 -1
- package/dist/src/operations/utils/flattenObjectForUpdate.d.ts.map +1 -1
- package/dist/src/operations/utils/flattenObjectForUpdate.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -212,7 +212,7 @@ class Store extends MyTable {
|
|
|
212
212
|
}
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
-
- **Supported field types:** `"string"`, `"number"`, `"boolean"`, `"date"` (stored as ISO strings, exposed as `Date` objects), `"enum"` (via `values`), nested `"object"` (via `fields`),
|
|
215
|
+
- **Supported field types:** `"string"`, `"number"`, `"boolean"`, `"date"` (stored as ISO strings, exposed as `Date` objects), `"enum"` (via `values`), nested `"object"` (via `fields`), `"array"` (via `items`), and `"discriminatedUnion"` (via `discriminator` + `variants`)
|
|
216
216
|
- **Nullable fields:** Set `nullable: true` on individual non-object fields within the schema to make them optional
|
|
217
217
|
- **Object attributes are never nullable:** DynamoDB cannot update nested document paths (e.g., `address.geo.lat`) if the parent object does not exist. To prevent this, `@ObjectAttribute` fields always exist as at least an empty object `{}`. Nested object fields within the schema are also never nullable. Non-object fields (primitives, enums, dates, arrays) can still be nullable.
|
|
218
218
|
- **Alias support:** Use the `alias` option to map to a different DynamoDB attribute name
|
|
@@ -249,6 +249,48 @@ const schema = {
|
|
|
249
249
|
|
|
250
250
|
The schema must be declared with `as const satisfies ObjectSchema` so TypeScript preserves the literal string values for type inference. At runtime, providing an invalid value (e.g., `status: "unknown"`) throws a `ValidationError`.
|
|
251
251
|
|
|
252
|
+
##### Discriminated union fields
|
|
253
|
+
|
|
254
|
+
Use `{ type: "discriminatedUnion", discriminator: "...", variants: { ... } }` to define a field that is a tagged union of object types. Each variant is an `ObjectSchema` keyed by its discriminator value. The discriminator key is automatically included in each variant's inferred type as a string literal.
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
const drawingSchema = {
|
|
258
|
+
shape: {
|
|
259
|
+
type: "discriminatedUnion",
|
|
260
|
+
discriminator: "kind",
|
|
261
|
+
variants: {
|
|
262
|
+
circle: { radius: { type: "number" } },
|
|
263
|
+
square: { side: { type: "number" } }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
} as const satisfies ObjectSchema;
|
|
267
|
+
|
|
268
|
+
@Entity
|
|
269
|
+
class Drawing extends MyTable {
|
|
270
|
+
declare readonly type: "Drawing";
|
|
271
|
+
|
|
272
|
+
@ObjectAttribute({ alias: "Drawing", schema: drawingSchema })
|
|
273
|
+
public readonly drawing: InferObjectSchema<typeof drawingSchema>;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// TypeScript infers:
|
|
277
|
+
// drawing.shape → { kind: "circle"; radius: number } | { kind: "square"; side: number }
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Unlike nested `"object"` fields, discriminated union fields **can be nullable** (`nullable: true`) because they always use **full replacement** on update rather than document path expressions:
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// Replaces the entire shape field — no partial merge
|
|
284
|
+
await Drawing.update("123", {
|
|
285
|
+
drawing: { shape: { kind: "square", side: 10 } }
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Scoping constraints (initial release):**
|
|
290
|
+
|
|
291
|
+
- Supported at the ObjectAttribute root level and as fields within an ObjectSchema
|
|
292
|
+
- Not supported inside array items or nested inside other discriminated unions
|
|
293
|
+
|
|
252
294
|
### Foreign Keys
|
|
253
295
|
|
|
254
296
|
Define foreign keys in order to support [@BelongsTo](https://docs.dyna-record.com/functions/BelongsTo.html) relationships. A foreign key is required for [@HasOne](https://docs.dyna-record.com/functions/HasOne.html) and [@HasMany](https://docs.dyna-record.com/functions/HasMany.html) relationships.
|
|
@@ -990,6 +1032,15 @@ await Store.update("123", {
|
|
|
990
1032
|
});
|
|
991
1033
|
```
|
|
992
1034
|
|
|
1035
|
+
**Discriminated unions** within objects are also **full replacement** (not merged):
|
|
1036
|
+
|
|
1037
|
+
```typescript
|
|
1038
|
+
// Replaces the entire shape — switches from circle to square
|
|
1039
|
+
await Drawing.update("123", {
|
|
1040
|
+
drawing: { shape: { kind: "square", side: 10 } }
|
|
1041
|
+
});
|
|
1042
|
+
```
|
|
1043
|
+
|
|
993
1044
|
The instance `update` method returns a deep-merged result, preserving existing fields:
|
|
994
1045
|
|
|
995
1046
|
```typescript
|
|
@@ -10,7 +10,7 @@ import type { ObjectSchema, InferObjectSchema } from "./types";
|
|
|
10
10
|
* `ValidationException: The document path provided in the update expression is invalid for update`.
|
|
11
11
|
* To avoid this, `@ObjectAttribute` fields always exist as at least an empty object `{}`.
|
|
12
12
|
*
|
|
13
|
-
* The schema supports all {@link FieldDef} types: primitives, enums, nested objects, and
|
|
13
|
+
* The schema supports all {@link FieldDef} types: primitives, enums, nested objects, arrays, and discriminated unions.
|
|
14
14
|
* Non-object fields within the schema may still be nullable.
|
|
15
15
|
*
|
|
16
16
|
* @template S The specific ObjectSchema type used for type inference
|
|
@@ -47,6 +47,7 @@ export interface ObjectAttributeOptions<S extends ObjectSchema> extends NonNullA
|
|
|
47
47
|
* - `"date"` — dates stored as ISO strings (support `nullable: true`)
|
|
48
48
|
* - `"object"` — nested objects, arbitrarily deep (**never nullable**)
|
|
49
49
|
* - `"array"` — lists of any field type (support `nullable: true`, full replacement on update)
|
|
50
|
+
* - `"discriminatedUnion"` — tagged unions via `discriminator` + `variants` (support `nullable: true`, full replacement on update)
|
|
50
51
|
*
|
|
51
52
|
* Objects within arrays are not subject to the document path limitation because arrays
|
|
52
53
|
* use full replacement on update. Partial updates of individual objects within arrays
|
|
@@ -102,6 +103,10 @@ export interface ObjectAttributeOptions<S extends ObjectSchema> extends NonNullA
|
|
|
102
103
|
* await MyEntity.update("id", { address: { zip: null } });
|
|
103
104
|
* ```
|
|
104
105
|
*
|
|
106
|
+
* **Discriminated union fields** always use **full replacement** on update — the user
|
|
107
|
+
* must provide a complete variant object. See {@link DiscriminatedUnionFieldDef} for
|
|
108
|
+
* the rationale.
|
|
109
|
+
*
|
|
105
110
|
* Object attributes support filtering in queries using dot-path notation for nested fields
|
|
106
111
|
* and the {@link ContainsFilter | $contains} operator for List membership checks.
|
|
107
112
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectAttribute.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/ObjectAttribute.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EACV,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ObjectAttribute.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/ObjectAttribute.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EACV,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EAGlB,MAAM,SAAS,CAAC;AAGjB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,YAAY,CAC5D,SAAQ,uBAAuB;IAC/B;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC;CACX;AAgKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FG;AACH,iBAAS,eAAe,CAAC,CAAC,SAAS,UAAU,EAAE,KAAK,CAAC,CAAC,SAAS,YAAY,EACzE,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,YAGtB,SAAS,WACR,yBAAyB,CAChC,CAAC,EACD,iBAAiB,CAAC,CAAC,CAAC,EACpB,sBAAsB,CAAC,CAAC,CAAC,CAC1B,UAoBJ;AAED,eAAe,eAAe,CAAC"}
|
|
@@ -27,11 +27,17 @@ function objectSchemaToZodPartial(schema) {
|
|
|
27
27
|
* Converts a single {@link FieldDef} to the corresponding partial Zod type.
|
|
28
28
|
* Nested objects use partial schemas; all other types use the standard schema.
|
|
29
29
|
* Object fields are never nullable — they always exist as at least `{}`.
|
|
30
|
+
* Discriminated union fields use the full schema (not partial) since they
|
|
31
|
+
* always use full replacement on update.
|
|
30
32
|
*/
|
|
31
33
|
function fieldDefToZodPartial(fieldDef) {
|
|
32
34
|
if (fieldDef.type === "object") {
|
|
33
35
|
return objectSchemaToZodPartial(fieldDef.fields);
|
|
34
36
|
}
|
|
37
|
+
if (fieldDef.type === "discriminatedUnion") {
|
|
38
|
+
// Discriminated unions use full replacement — same schema as create
|
|
39
|
+
return discriminatedUnionToZod(fieldDef);
|
|
40
|
+
}
|
|
35
41
|
// For non-object fields, use the standard schema (includes nullable wrapping)
|
|
36
42
|
return fieldDefToZod(fieldDef);
|
|
37
43
|
}
|
|
@@ -48,12 +54,39 @@ function objectSchemaToZod(schema) {
|
|
|
48
54
|
}
|
|
49
55
|
return zod_1.z.object(shape);
|
|
50
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Builds a Zod `discriminatedUnion` schema from a {@link DiscriminatedUnionFieldDef}.
|
|
59
|
+
*
|
|
60
|
+
* Each variant's ObjectSchema is converted to a `z.object()` and extended with a
|
|
61
|
+
* `z.literal()` for the discriminator key. The resulting schemas are wrapped in
|
|
62
|
+
* `z.discriminatedUnion()`.
|
|
63
|
+
*
|
|
64
|
+
* @param fieldDef The discriminated union field definition
|
|
65
|
+
* @returns A ZodType that validates discriminated union values
|
|
66
|
+
*/
|
|
67
|
+
function discriminatedUnionToZod(fieldDef) {
|
|
68
|
+
if (Object.keys(fieldDef.variants).length === 0) {
|
|
69
|
+
throw new Error("DiscriminatedUnionFieldDef requires at least one variant");
|
|
70
|
+
}
|
|
71
|
+
const variantSchemas = Object.entries(fieldDef.variants).map(([variantKey, variantObjectSchema]) => {
|
|
72
|
+
const variantZod = objectSchemaToZod(variantObjectSchema);
|
|
73
|
+
return variantZod.extend({
|
|
74
|
+
[fieldDef.discriminator]: zod_1.z.literal(variantKey)
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
let zodType = zod_1.z.discriminatedUnion(fieldDef.discriminator, variantSchemas);
|
|
78
|
+
if (fieldDef.nullable === true) {
|
|
79
|
+
zodType = zodType.optional().nullable();
|
|
80
|
+
}
|
|
81
|
+
return zodType;
|
|
82
|
+
}
|
|
51
83
|
/**
|
|
52
84
|
* Converts a single {@link FieldDef} to the corresponding Zod type for runtime validation.
|
|
53
85
|
*
|
|
54
86
|
* Handles all field types:
|
|
55
87
|
* - `"object"` → recursively builds a `z.object()` via {@link objectSchemaToZod}.
|
|
56
88
|
* Object fields are never nullable — DynamoDB requires them to exist for document path updates.
|
|
89
|
+
* - `"discriminatedUnion"` → `z.discriminatedUnion()` via {@link discriminatedUnionToZod}
|
|
57
90
|
* - `"array"` → `z.array()` wrapping a recursive call for the `items` type
|
|
58
91
|
* - `"string"` → `z.string()`
|
|
59
92
|
* - `"number"` → `z.number()`
|
|
@@ -70,6 +103,10 @@ function fieldDefToZod(fieldDef) {
|
|
|
70
103
|
if (fieldDef.type === "object") {
|
|
71
104
|
return objectSchemaToZod(fieldDef.fields);
|
|
72
105
|
}
|
|
106
|
+
// Discriminated union fields handle their own nullable wrapping
|
|
107
|
+
if (fieldDef.type === "discriminatedUnion") {
|
|
108
|
+
return discriminatedUnionToZod(fieldDef);
|
|
109
|
+
}
|
|
73
110
|
let zodType;
|
|
74
111
|
switch (fieldDef.type) {
|
|
75
112
|
case "array":
|
|
@@ -118,6 +155,7 @@ function fieldDefToZod(fieldDef) {
|
|
|
118
155
|
* - `"date"` — dates stored as ISO strings (support `nullable: true`)
|
|
119
156
|
* - `"object"` — nested objects, arbitrarily deep (**never nullable**)
|
|
120
157
|
* - `"array"` — lists of any field type (support `nullable: true`, full replacement on update)
|
|
158
|
+
* - `"discriminatedUnion"` — tagged unions via `discriminator` + `variants` (support `nullable: true`, full replacement on update)
|
|
121
159
|
*
|
|
122
160
|
* Objects within arrays are not subject to the document path limitation because arrays
|
|
123
161
|
* use full replacement on update. Partial updates of individual objects within arrays
|
|
@@ -173,6 +211,10 @@ function fieldDefToZod(fieldDef) {
|
|
|
173
211
|
* await MyEntity.update("id", { address: { zip: null } });
|
|
174
212
|
* ```
|
|
175
213
|
*
|
|
214
|
+
* **Discriminated union fields** always use **full replacement** on update — the user
|
|
215
|
+
* must provide a complete variant object. See {@link DiscriminatedUnionFieldDef} for
|
|
216
|
+
* the rationale.
|
|
217
|
+
*
|
|
176
218
|
* Object attributes support filtering in queries using dot-path notation for nested fields
|
|
177
219
|
* and the {@link ContainsFilter | $contains} operator for List membership checks.
|
|
178
220
|
*
|
|
@@ -188,11 +230,12 @@ function fieldDefToZod(fieldDef) {
|
|
|
188
230
|
*/
|
|
189
231
|
function ObjectAttribute(props) {
|
|
190
232
|
return function (_value, context) {
|
|
233
|
+
// Fail fast: surface schema validation errors at class definition time.
|
|
234
|
+
const { schema, ...restProps } = props;
|
|
235
|
+
const zodSchema = objectSchemaToZod(schema);
|
|
236
|
+
const partialZodSchema = objectSchemaToZodPartial(schema);
|
|
237
|
+
const serializers = (0, serializers_1.createObjectSerializer)(schema);
|
|
191
238
|
context.addInitializer(function () {
|
|
192
|
-
const { schema, ...restProps } = props;
|
|
193
|
-
const zodSchema = objectSchemaToZod(schema);
|
|
194
|
-
const partialZodSchema = objectSchemaToZodPartial(schema);
|
|
195
|
-
const serializers = (0, serializers_1.createObjectSerializer)(schema);
|
|
196
239
|
metadata_1.default.addEntityAttribute(this.constructor.name, {
|
|
197
240
|
attributeName: context.name.toString(),
|
|
198
241
|
type: zodSchema,
|
|
@@ -10,5 +10,5 @@ export { default as IdAttribute } from "./IdAttribute";
|
|
|
10
10
|
export { default as ObjectAttribute } from "./ObjectAttribute";
|
|
11
11
|
export type { ObjectAttributeOptions } from "./ObjectAttribute";
|
|
12
12
|
export * from "./serializers";
|
|
13
|
-
export type { ObjectSchema, InferObjectSchema, FieldDef, PrimitiveFieldDef, ObjectFieldDef, ArrayFieldDef, EnumFieldDef, DateFieldDef } from "./types";
|
|
13
|
+
export type { ObjectSchema, NonUnionObjectSchema, InferObjectSchema, InferDiscriminatedUnion, FieldDef, NonUnionFieldDef, PrimitiveFieldDef, ObjectFieldDef, ArrayFieldDef, EnumFieldDef, DateFieldDef, DiscriminatedUnionFieldDef } from "./types";
|
|
14
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,cAAc,eAAe,CAAC;AAC9B,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,YAAY,EACZ,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,cAAc,eAAe,CAAC;AAC9B,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,0BAA0B,EAC3B,MAAM,SAAS,CAAC"}
|
|
@@ -19,6 +19,8 @@ export declare const dateSerializer: {
|
|
|
19
19
|
* - `"date"` fields are converted from `Date` objects to ISO 8601 strings.
|
|
20
20
|
* - `"object"` fields recurse into their nested schema.
|
|
21
21
|
* - `"array"` fields map each item through the same conversion.
|
|
22
|
+
* - `"discriminatedUnion"` fields look up the variant schema by discriminator value
|
|
23
|
+
* and recurse into that variant's object schema, preserving the discriminator key.
|
|
22
24
|
* - `null` and `undefined` values are stripped from the result so that nullable
|
|
23
25
|
* fields set to `null` are removed from the stored object rather than persisted
|
|
24
26
|
* as `null` in DynamoDB.
|
|
@@ -29,6 +31,21 @@ export declare const dateSerializer: {
|
|
|
29
31
|
* @returns A new object suitable for DynamoDB storage
|
|
30
32
|
*/
|
|
31
33
|
export declare function objectToTableItem(schema: ObjectSchema, value: Record<string, unknown>): Record<string, unknown>;
|
|
34
|
+
/**
|
|
35
|
+
* Converts a single field value to its DynamoDB table representation based on
|
|
36
|
+
* the field definition.
|
|
37
|
+
*
|
|
38
|
+
* - `"date"` → ISO 8601 string
|
|
39
|
+
* - `"object"` → recursively converts via {@link objectToTableItem}
|
|
40
|
+
* - `"array"` → maps each item through the same conversion
|
|
41
|
+
* - `"discriminatedUnion"` → looks up the variant schema by discriminator value,
|
|
42
|
+
* converts via {@link objectToTableItem}, and preserves the discriminator key
|
|
43
|
+
* - All other types pass through unchanged
|
|
44
|
+
*
|
|
45
|
+
* @param fieldDef The {@link FieldDef} describing the field's type
|
|
46
|
+
* @param val The entity-level value to convert
|
|
47
|
+
* @returns The DynamoDB-compatible value
|
|
48
|
+
*/
|
|
32
49
|
export declare function convertFieldToTableItem(fieldDef: FieldDef, val: unknown): unknown;
|
|
33
50
|
/**
|
|
34
51
|
* Recursively walks an {@link ObjectSchema} and converts a DynamoDB table item
|
|
@@ -37,6 +54,8 @@ export declare function convertFieldToTableItem(fieldDef: FieldDef, val: unknown
|
|
|
37
54
|
* - `"date"` fields are converted from ISO 8601 strings to `Date` objects.
|
|
38
55
|
* - `"object"` fields recurse into their nested schema.
|
|
39
56
|
* - `"array"` fields map each item through the same conversion.
|
|
57
|
+
* - `"discriminatedUnion"` fields look up the variant schema by discriminator value
|
|
58
|
+
* and recurse into that variant's object schema, preserving the discriminator key.
|
|
40
59
|
* - `null` and `undefined` values are stripped from the result so that absent
|
|
41
60
|
* fields are represented as `undefined` (omitted) on the entity, consistent
|
|
42
61
|
* with root-level nullable attribute behaviour.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializers.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/serializers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;6BACA,oBAAoB,KAAG,OAAO;4BAM/B,OAAO;CAEhC,CAAC;
|
|
1
|
+
{"version":3,"file":"serializers.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/serializers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;6BACA,oBAAoB,KAAG,OAAO;4BAM/B,OAAO;CAEhC,CAAC;AAuBF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAqBT;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAgCD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAOxE"}
|
|
@@ -21,6 +21,20 @@ exports.dateSerializer = {
|
|
|
21
21
|
},
|
|
22
22
|
toTableAttribute: (val) => val instanceof Date ? val.toISOString() : undefined
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Resolves the variant schema for a discriminated union value by reading the
|
|
26
|
+
* discriminator key and looking up the corresponding variant in the field definition.
|
|
27
|
+
*
|
|
28
|
+
* Uses `Object.hasOwn` to guard against prototype property pollution — only
|
|
29
|
+
* own-enumerable variant keys are matched.
|
|
30
|
+
*/
|
|
31
|
+
function resolveVariantSchema(fieldDef, value) {
|
|
32
|
+
const discriminatorValue = value[fieldDef.discriminator];
|
|
33
|
+
if (!Object.hasOwn(fieldDef.variants, discriminatorValue)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
return fieldDef.variants[discriminatorValue];
|
|
37
|
+
}
|
|
24
38
|
/**
|
|
25
39
|
* Recursively walks an {@link ObjectSchema} and converts the entity value to its
|
|
26
40
|
* DynamoDB representation.
|
|
@@ -28,6 +42,8 @@ exports.dateSerializer = {
|
|
|
28
42
|
* - `"date"` fields are converted from `Date` objects to ISO 8601 strings.
|
|
29
43
|
* - `"object"` fields recurse into their nested schema.
|
|
30
44
|
* - `"array"` fields map each item through the same conversion.
|
|
45
|
+
* - `"discriminatedUnion"` fields look up the variant schema by discriminator value
|
|
46
|
+
* and recurse into that variant's object schema, preserving the discriminator key.
|
|
31
47
|
* - `null` and `undefined` values are stripped from the result so that nullable
|
|
32
48
|
* fields set to `null` are removed from the stored object rather than persisted
|
|
33
49
|
* as `null` in DynamoDB.
|
|
@@ -48,6 +64,21 @@ function objectToTableItem(schema, value) {
|
|
|
48
64
|
}
|
|
49
65
|
return result;
|
|
50
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Converts a single field value to its DynamoDB table representation based on
|
|
69
|
+
* the field definition.
|
|
70
|
+
*
|
|
71
|
+
* - `"date"` → ISO 8601 string
|
|
72
|
+
* - `"object"` → recursively converts via {@link objectToTableItem}
|
|
73
|
+
* - `"array"` → maps each item through the same conversion
|
|
74
|
+
* - `"discriminatedUnion"` → looks up the variant schema by discriminator value,
|
|
75
|
+
* converts via {@link objectToTableItem}, and preserves the discriminator key
|
|
76
|
+
* - All other types pass through unchanged
|
|
77
|
+
*
|
|
78
|
+
* @param fieldDef The {@link FieldDef} describing the field's type
|
|
79
|
+
* @param val The entity-level value to convert
|
|
80
|
+
* @returns The DynamoDB-compatible value
|
|
81
|
+
*/
|
|
51
82
|
function convertFieldToTableItem(fieldDef, val) {
|
|
52
83
|
switch (fieldDef.type) {
|
|
53
84
|
case "date":
|
|
@@ -56,6 +87,15 @@ function convertFieldToTableItem(fieldDef, val) {
|
|
|
56
87
|
return objectToTableItem(fieldDef.fields, val);
|
|
57
88
|
case "array":
|
|
58
89
|
return val.map(item => convertFieldToTableItem(fieldDef.items, item));
|
|
90
|
+
case "discriminatedUnion": {
|
|
91
|
+
const value = val;
|
|
92
|
+
const variantSchema = resolveVariantSchema(fieldDef, value);
|
|
93
|
+
if (variantSchema === undefined)
|
|
94
|
+
return val;
|
|
95
|
+
const result = objectToTableItem(variantSchema, value);
|
|
96
|
+
result[fieldDef.discriminator] = value[fieldDef.discriminator];
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
59
99
|
default:
|
|
60
100
|
return val;
|
|
61
101
|
}
|
|
@@ -67,6 +107,8 @@ function convertFieldToTableItem(fieldDef, val) {
|
|
|
67
107
|
* - `"date"` fields are converted from ISO 8601 strings to `Date` objects.
|
|
68
108
|
* - `"object"` fields recurse into their nested schema.
|
|
69
109
|
* - `"array"` fields map each item through the same conversion.
|
|
110
|
+
* - `"discriminatedUnion"` fields look up the variant schema by discriminator value
|
|
111
|
+
* and recurse into that variant's object schema, preserving the discriminator key.
|
|
70
112
|
* - `null` and `undefined` values are stripped from the result so that absent
|
|
71
113
|
* fields are represented as `undefined` (omitted) on the entity, consistent
|
|
72
114
|
* with root-level nullable attribute behaviour.
|
|
@@ -87,6 +129,13 @@ function tableItemToObject(schema, value) {
|
|
|
87
129
|
}
|
|
88
130
|
return result;
|
|
89
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Converts a single DynamoDB field value back to its entity representation.
|
|
134
|
+
*
|
|
135
|
+
* Mirrors {@link convertFieldToTableItem} in reverse: dates become `Date` objects,
|
|
136
|
+
* objects and discriminated unions recurse through their schemas, arrays map each item,
|
|
137
|
+
* and all other types pass through unchanged.
|
|
138
|
+
*/
|
|
90
139
|
function convertFieldToEntityValue(fieldDef, val) {
|
|
91
140
|
switch (fieldDef.type) {
|
|
92
141
|
case "date":
|
|
@@ -95,6 +144,15 @@ function convertFieldToEntityValue(fieldDef, val) {
|
|
|
95
144
|
return tableItemToObject(fieldDef.fields, val);
|
|
96
145
|
case "array":
|
|
97
146
|
return val.map(item => convertFieldToEntityValue(fieldDef.items, item));
|
|
147
|
+
case "discriminatedUnion": {
|
|
148
|
+
const value = val;
|
|
149
|
+
const variantSchema = resolveVariantSchema(fieldDef, value);
|
|
150
|
+
if (variantSchema === undefined)
|
|
151
|
+
return val;
|
|
152
|
+
const result = tableItemToObject(variantSchema, value);
|
|
153
|
+
result[fieldDef.discriminator] = value[fieldDef.discriminator];
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
98
156
|
default:
|
|
99
157
|
return val;
|
|
100
158
|
}
|
|
@@ -91,8 +91,13 @@ export interface ObjectFieldDef {
|
|
|
91
91
|
export interface ArrayFieldDef {
|
|
92
92
|
/** Must be `"array"` to indicate a list/array field. */
|
|
93
93
|
type: "array";
|
|
94
|
-
/**
|
|
95
|
-
|
|
94
|
+
/**
|
|
95
|
+
* A {@link NonUnionFieldDef} describing the type of each array element.
|
|
96
|
+
* Discriminated unions are not supported as array items because arrays use
|
|
97
|
+
* full replacement on update and the variant-aware serialization semantics
|
|
98
|
+
* are not defined for array item context.
|
|
99
|
+
*/
|
|
100
|
+
items: NonUnionFieldDef;
|
|
96
101
|
/** When `true`, the field becomes optional. */
|
|
97
102
|
nullable?: boolean;
|
|
98
103
|
}
|
|
@@ -171,6 +176,71 @@ export interface DateFieldDef {
|
|
|
171
176
|
/** When `true`, the field becomes optional (`Date | undefined`). */
|
|
172
177
|
nullable?: boolean;
|
|
173
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* A schema field definition for a discriminated union type.
|
|
181
|
+
*
|
|
182
|
+
* The `discriminator` names the key used to distinguish variants, and `variants`
|
|
183
|
+
* maps each discriminator value to an {@link ObjectSchema} describing that variant's
|
|
184
|
+
* fields. The discriminator key is automatically added to each variant's inferred type
|
|
185
|
+
* as a string literal.
|
|
186
|
+
*
|
|
187
|
+
* **Update semantics:** Discriminated union fields always use **full replacement** on
|
|
188
|
+
* update (`SET #field = :value`), never document path merging. This is because:
|
|
189
|
+
* - Different variants have different field sets — a partial document path update could
|
|
190
|
+
* leave orphaned fields from a previous variant leaking through when variants change.
|
|
191
|
+
* - Avoiding orphaned fields without full replacement would require a read-before-write
|
|
192
|
+
* to determine the current discriminator value.
|
|
193
|
+
* - This matches array update semantics (also full replacement).
|
|
194
|
+
*
|
|
195
|
+
* Unlike {@link ObjectFieldDef}, discriminated union fields **can be nullable** because
|
|
196
|
+
* they always use full replacement on update rather than document path expressions,
|
|
197
|
+
* so there is no risk of DynamoDB failing on a missing parent path.
|
|
198
|
+
*
|
|
199
|
+
* **Scoping constraints (initial release):**
|
|
200
|
+
* - Supported at the ObjectAttribute root level and as fields within an ObjectSchema
|
|
201
|
+
* - Not supported inside array items or nested inside other discriminated unions
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* const schema = {
|
|
206
|
+
* shape: {
|
|
207
|
+
* type: "discriminatedUnion",
|
|
208
|
+
* discriminator: "kind",
|
|
209
|
+
* variants: {
|
|
210
|
+
* circle: { radius: { type: "number" } },
|
|
211
|
+
* square: { side: { type: "number" } }
|
|
212
|
+
* }
|
|
213
|
+
* }
|
|
214
|
+
* } as const satisfies ObjectSchema;
|
|
215
|
+
*
|
|
216
|
+
* type T = InferObjectSchema<typeof schema>;
|
|
217
|
+
* // { shape: { kind: "circle"; radius: number } | { kind: "square"; side: number } }
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
export interface DiscriminatedUnionFieldDef {
|
|
221
|
+
/** Must be `"discriminatedUnion"` to indicate a discriminated union field. */
|
|
222
|
+
type: "discriminatedUnion";
|
|
223
|
+
/** The key name used to discriminate between variants. */
|
|
224
|
+
discriminator: string;
|
|
225
|
+
/**
|
|
226
|
+
* A record mapping each discriminator value to a {@link NonUnionObjectSchema}
|
|
227
|
+
* describing that variant's fields (excluding the discriminator itself).
|
|
228
|
+
* Discriminated unions cannot be nested inside other discriminated unions.
|
|
229
|
+
*/
|
|
230
|
+
variants: Readonly<Record<string, NonUnionObjectSchema>>;
|
|
231
|
+
/** When `true`, the field becomes optional (`T | undefined`). */
|
|
232
|
+
nullable?: boolean;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* A field definition that excludes {@link DiscriminatedUnionFieldDef}.
|
|
236
|
+
*
|
|
237
|
+
* Used in contexts where discriminated unions are not allowed:
|
|
238
|
+
* - {@link ArrayFieldDef} `items` — arrays use full replacement and variant-aware
|
|
239
|
+
* serialization is not defined for array item context
|
|
240
|
+
* - {@link DiscriminatedUnionFieldDef} `variants` — discriminated unions cannot
|
|
241
|
+
* be nested inside other discriminated unions
|
|
242
|
+
*/
|
|
243
|
+
export type NonUnionFieldDef = PrimitiveFieldDef | DateFieldDef | ObjectFieldDef | ArrayFieldDef | EnumFieldDef;
|
|
174
244
|
/**
|
|
175
245
|
* A field definition within an {@link ObjectSchema}.
|
|
176
246
|
*
|
|
@@ -180,10 +250,17 @@ export interface DateFieldDef {
|
|
|
180
250
|
* - {@link ObjectFieldDef} — nested objects via `fields`
|
|
181
251
|
* - {@link ArrayFieldDef} — arrays/lists via `items`
|
|
182
252
|
* - {@link EnumFieldDef} — string literal enums via `values`
|
|
253
|
+
* - {@link DiscriminatedUnionFieldDef} — discriminated unions via `discriminator` + `variants`
|
|
183
254
|
*
|
|
184
255
|
* Each variant is discriminated by the `type` property.
|
|
185
256
|
*/
|
|
186
|
-
export type FieldDef =
|
|
257
|
+
export type FieldDef = NonUnionFieldDef | DiscriminatedUnionFieldDef;
|
|
258
|
+
/**
|
|
259
|
+
* ObjectSchema that excludes discriminated union fields.
|
|
260
|
+
* Used for {@link DiscriminatedUnionFieldDef} variant schemas where
|
|
261
|
+
* nested discriminated unions are not allowed.
|
|
262
|
+
*/
|
|
263
|
+
export type NonUnionObjectSchema = Record<string, NonUnionFieldDef>;
|
|
187
264
|
/**
|
|
188
265
|
* Declarative schema for describing the shape of an object attribute.
|
|
189
266
|
*
|
|
@@ -205,18 +282,36 @@ export type FieldDef = PrimitiveFieldDef | DateFieldDef | ObjectFieldDef | Array
|
|
|
205
282
|
* ```
|
|
206
283
|
*/
|
|
207
284
|
export type ObjectSchema = Record<string, FieldDef>;
|
|
285
|
+
/**
|
|
286
|
+
* Infers the TypeScript type of a {@link DiscriminatedUnionFieldDef}.
|
|
287
|
+
*
|
|
288
|
+
* Iterates over the variant keys and for each produces a union member that is
|
|
289
|
+
* `{ [discriminator]: VariantKey } & InferObjectSchema<VariantSchema>`.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```typescript
|
|
293
|
+
* // Given: discriminator: "kind", variants: { circle: { radius: { type: "number" } }, square: { side: { type: "number" } } }
|
|
294
|
+
* // Produces: { kind: "circle"; radius: number } | { kind: "square"; side: number }
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
export type InferDiscriminatedUnion<F extends DiscriminatedUnionFieldDef> = {
|
|
298
|
+
[V in keyof F["variants"] & string]: {
|
|
299
|
+
[D in F["discriminator"]]: V;
|
|
300
|
+
} & InferObjectSchema<F["variants"][V]>;
|
|
301
|
+
}[keyof F["variants"] & string];
|
|
208
302
|
/**
|
|
209
303
|
* Infers the TypeScript type of a single {@link FieldDef}.
|
|
210
304
|
*
|
|
211
305
|
* Used internally by {@link InferObjectSchema} and for recursive array item inference.
|
|
212
306
|
*
|
|
213
307
|
* Resolution order:
|
|
214
|
-
* 1. {@link
|
|
215
|
-
* 2. {@link
|
|
216
|
-
* 3. {@link
|
|
217
|
-
* 4. {@link
|
|
308
|
+
* 1. {@link DiscriminatedUnionFieldDef} → `InferDiscriminatedUnion<F>`
|
|
309
|
+
* 2. {@link ArrayFieldDef} → `Array<InferFieldDef<items>>`
|
|
310
|
+
* 3. {@link ObjectFieldDef} → `InferObjectSchema<fields>`
|
|
311
|
+
* 4. {@link EnumFieldDef} → `values[number]` (string literal union)
|
|
312
|
+
* 5. {@link PrimitiveFieldDef} → `PrimitiveTypeMap[type]`
|
|
218
313
|
*/
|
|
219
|
-
export type InferFieldDef<F extends FieldDef> = F extends ArrayFieldDef ? Array<InferFieldDef<F["items"]>> : F extends ObjectFieldDef ? InferObjectSchema<F["fields"]> : F extends EnumFieldDef ? F["values"][number] : F extends PrimitiveFieldDef ? PrimitiveTypeMap[F["type"]] : never;
|
|
314
|
+
export type InferFieldDef<F extends FieldDef> = F extends DiscriminatedUnionFieldDef ? InferDiscriminatedUnion<F> : F extends ArrayFieldDef ? Array<InferFieldDef<F["items"]>> : F extends ObjectFieldDef ? InferObjectSchema<F["fields"]> : F extends EnumFieldDef ? F["values"][number] : F extends PrimitiveFieldDef ? PrimitiveTypeMap[F["type"]] : never;
|
|
220
315
|
/**
|
|
221
316
|
* Infers the TypeScript type from an {@link ObjectSchema} definition.
|
|
222
317
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,gBAAgB,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,IAAI,EAAE,kBAAkB,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,IAAI,EAAE,QAAQ,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,IAAI,EAAE,OAAO,CAAC;IACd
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,8 @@ import type { DocumentPathOperation } from "./types";
|
|
|
8
8
|
* - `undefined` → skip (not being updated)
|
|
9
9
|
* - `null` → REMOVE operation
|
|
10
10
|
* - Nested object (`fieldDef.type === "object"`) → recurse, prepending parent path
|
|
11
|
-
* - Everything else (primitives, arrays, dates, enums) → serialize and SET
|
|
11
|
+
* - Everything else (primitives, arrays, dates, enums, discriminated unions) → serialize and SET
|
|
12
|
+
*
|
|
12
13
|
*
|
|
13
14
|
* @param parentPath Path segments leading to this object (e.g. ["address"] or ["address", "geo"])
|
|
14
15
|
* @param schema The ObjectSchema describing the object shape
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flattenObjectForUpdate.d.ts","sourceRoot":"","sources":["../../../../src/operations/utils/flattenObjectForUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAErD
|
|
1
|
+
{"version":3,"file":"flattenObjectForUpdate.d.ts","sourceRoot":"","sources":["../../../../src/operations/utils/flattenObjectForUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAErD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAAE,EACpB,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACpC,qBAAqB,EAAE,CAkCzB"}
|
|
@@ -10,7 +10,8 @@ const serializers_1 = require("../../decorators/attributes/serializers");
|
|
|
10
10
|
* - `undefined` → skip (not being updated)
|
|
11
11
|
* - `null` → REMOVE operation
|
|
12
12
|
* - Nested object (`fieldDef.type === "object"`) → recurse, prepending parent path
|
|
13
|
-
* - Everything else (primitives, arrays, dates, enums) → serialize and SET
|
|
13
|
+
* - Everything else (primitives, arrays, dates, enums, discriminated unions) → serialize and SET
|
|
14
|
+
*
|
|
14
15
|
*
|
|
15
16
|
* @param parentPath Path segments leading to this object (e.g. ["address"] or ["address", "geo"])
|
|
16
17
|
* @param schema The ObjectSchema describing the object shape
|
|
@@ -37,7 +38,6 @@ function flattenObjectForUpdate(parentPath, schema, partialValue) {
|
|
|
37
38
|
ops.push(...nestedOps);
|
|
38
39
|
continue;
|
|
39
40
|
}
|
|
40
|
-
// Primitives, arrays, dates, enums → serialize and SET
|
|
41
41
|
const serialized = (0, serializers_1.convertFieldToTableItem)(fieldDef, val);
|
|
42
42
|
ops.push({ type: "set", path: fieldPath, value: serialized });
|
|
43
43
|
}
|