@gialicus/smart-object 2.0.0 → 2.0.2
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/CHANGELOG.md +12 -0
- package/README.md +42 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/smart-object/apply-operations.d.ts.map +1 -1
- package/dist/smart-object/apply-operations.js +3 -1
- package/dist/smart-object/build-class.d.ts.map +1 -1
- package/dist/smart-object/build-class.js +2 -4
- package/dist/smart-object/codecs.d.ts +15 -0
- package/dist/smart-object/codecs.d.ts.map +1 -0
- package/dist/smart-object/codecs.js +163 -0
- package/dist/smart-object/define-prototype.d.ts.map +1 -1
- package/dist/smart-object/define-prototype.js +55 -3
- package/dist/smart-object/factory.d.ts +4 -2
- package/dist/smart-object/factory.d.ts.map +1 -1
- package/dist/smart-object/factory.js +20 -8
- package/dist/smart-object/json-patch.d.ts +2 -1
- package/dist/smart-object/json-patch.d.ts.map +1 -1
- package/dist/smart-object/json-patch.js +26 -1
- package/dist/smart-object/read-field.d.ts.map +1 -1
- package/dist/smart-object/read-field.js +6 -0
- package/dist/smart-object/setters/object-field.d.ts +3 -2
- package/dist/smart-object/setters/object-field.d.ts.map +1 -1
- package/dist/smart-object/setters/object-field.js +37 -5
- package/dist/smart-object/setters/record-field.d.ts +7 -0
- package/dist/smart-object/setters/record-field.d.ts.map +1 -0
- package/dist/smart-object/setters/record-field.js +133 -0
- package/dist/smart-object/setters/variant-switch.d.ts +5 -0
- package/dist/smart-object/setters/variant-switch.d.ts.map +1 -0
- package/dist/smart-object/setters/variant-switch.js +37 -0
- package/dist/types.d.ts +30 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/zod-introspect.d.ts +36 -0
- package/dist/zod-introspect.d.ts.map +1 -1
- package/dist/zod-introspect.js +216 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.1] - 2026-06-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Support for `z.date()` and `z.coerce.date()` with ISO 8601 operation values and `Date` getters
|
|
13
|
+
- Support for `z.bigint()`, `z.map` (string keys), and `z.set` via explicit JSON-safe codecs
|
|
14
|
+
- Per-entry API for `z.record` and string-key `z.map` fields: `get{Field}Entry`, `set{Field}Entry`, `delete{Field}Entry`
|
|
15
|
+
- Union variant switching: `switchVariant` and generated `switchTo{Variant}` for discriminated unions
|
|
16
|
+
- Root schema variants: `z.intersection` and `z.lazy`
|
|
17
|
+
- Zod schema introspection module (`src/zod-introspect.ts`) and codec layer (`src/smart-object/codecs.ts`)
|
|
18
|
+
|
|
8
19
|
## [2.0.0] - 2026-06-27
|
|
9
20
|
|
|
10
21
|
### Breaking
|
|
@@ -39,5 +50,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
39
50
|
- `fromOperations(initial, operations)` static method for deterministic replay
|
|
40
51
|
- Exported types: `Operation`, `SetMethods`, `SetMethodsUnion`, `AllKeys`, `UnionDataShape`, `OperationsAccessor`, `SmartObjectConstructor`, `SmartObjectInstance`
|
|
41
52
|
|
|
53
|
+
[2.0.1]: https://github.com/gialicus/smart-object/compare/v2.0.0...v2.0.1
|
|
42
54
|
[2.0.0]: https://github.com/gialicus/smart-object/compare/v1.0.0...v2.0.0
|
|
43
55
|
[1.0.0]: https://github.com/gialicus/smart-object/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Typed TypeScript objects backed by Zod schemas, with an RFC 6902 operation log f
|
|
|
8
8
|
npm install @gialicus/smart-object zod
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Dependencies: [Zod](https://zod.dev) (peer dependency — schema validation) and [fast-json-patch](https://github.com/Starcounter-Jack/JSON-Patch) (RFC 6902 patch application, bundled).
|
|
11
|
+
Dependencies: [Zod](https://zod.dev) `^4.0.0` (peer dependency — schema validation) and [fast-json-patch](https://github.com/Starcounter-Jack/JSON-Patch) (RFC 6902 patch application, bundled).
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
@@ -63,6 +63,10 @@ const Event = SmartObject(z.discriminatedUnion("type", [
|
|
|
63
63
|
|
|
64
64
|
const event = new Event({ type: "click", x: 10, y: 20 });
|
|
65
65
|
event.setX(15);
|
|
66
|
+
|
|
67
|
+
// Switch the active union variant atomically
|
|
68
|
+
event.switchToScroll({ delta: 5 });
|
|
69
|
+
// or: event.switchVariant({ type: "scroll", delta: 5 });
|
|
66
70
|
```
|
|
67
71
|
|
|
68
72
|
See [`examples/event.ts`](examples/event.ts) and [`examples/profile.ts`](examples/profile.ts) for full demos.
|
|
@@ -77,7 +81,7 @@ Factory that accepts a Zod schema and returns an instantiable class.
|
|
|
77
81
|
|
|
78
82
|
| Parameter | Type | Description |
|
|
79
83
|
|-----------|------|-------------|
|
|
80
|
-
| `schema` | `z.ZodObject` \| `z.ZodUnion` \| `z.ZodDiscriminatedUnion` | Zod schema defining the object shape |
|
|
84
|
+
| `schema` | `z.ZodObject` \| `z.ZodUnion` \| `z.ZodDiscriminatedUnion` \| `z.ZodIntersection` \| `z.ZodLazy` | Zod schema defining the object shape |
|
|
81
85
|
|
|
82
86
|
**Members generated for each schema field `foo`:**
|
|
83
87
|
|
|
@@ -88,6 +92,21 @@ Factory that accepts a Zod schema and returns an instantiable class.
|
|
|
88
92
|
|
|
89
93
|
`set*` method names follow camelCase with the field name capitalized (`name` → `setName`, `userId` → `setUserId`).
|
|
90
94
|
|
|
95
|
+
**Union root extras** (discriminated and generic unions):
|
|
96
|
+
|
|
97
|
+
| Member | Type | Description |
|
|
98
|
+
|--------|------|-------------|
|
|
99
|
+
| `switchVariant(value)` | `(variant) => void` | Replaces the entire active variant after full schema validation |
|
|
100
|
+
| `switchTo{Variant}(fields)` | `(fields) => void` | Discriminated unions only — switches to a variant without repeating the discriminator (e.g. `switchToScroll({ delta: 5 })`) |
|
|
101
|
+
|
|
102
|
+
**Record and Map field extras** (for each `z.record(...)` field or string-key `z.map(...)` field `tags`):
|
|
103
|
+
|
|
104
|
+
| Member | Type | Description |
|
|
105
|
+
|--------|------|-------------|
|
|
106
|
+
| `getTagsEntry(key)` | `(key: string) => V \| undefined` | Reads a single entry |
|
|
107
|
+
| `setTagsEntry(key, value)` | `(key: string, value: V) => void` | Validates and patches a single entry (`/tags/{key}`) |
|
|
108
|
+
| `deleteTagsEntry(key)` | `(key: string) => void` | Removes an entry |
|
|
109
|
+
|
|
91
110
|
**Instance members:**
|
|
92
111
|
|
|
93
112
|
| Member | Type | Description |
|
|
@@ -124,10 +143,15 @@ RFC 6902 operation emitted by [fast-json-patch](https://github.com/Starcounter-J
|
|
|
124
143
|
### Exported types
|
|
125
144
|
|
|
126
145
|
- `Operation` — JSON Patch operation (re-export from `fast-json-patch`)
|
|
146
|
+
- `SmartObjectSchema` — union of Zod schema types accepted by `SmartObject()`
|
|
147
|
+
- `SmartObjectErrorCode` — `"InvalidValue"` \| `"InvalidUnionField"` \| `"InvalidReplay"` \| `"UnsupportedSchema"`
|
|
127
148
|
- `SetMethods<T>` — mapped type of inferred `set*` methods for shape `T`
|
|
128
149
|
- `SetMethodsUnion<T>` — `set*` methods for union root schemas
|
|
129
150
|
- `AllKeys<T>` — all keys across union members
|
|
130
151
|
- `UnionDataShape<U>` — flattened data shape for union roots
|
|
152
|
+
- `VariantSwitchMethods<T>` — `switchVariant` for union roots
|
|
153
|
+
- `DiscriminatedVariantSwitchMethods<T, D>` — `switchVariant` plus generated `switchTo*` methods
|
|
154
|
+
- `RecordFieldMethods<T>` — dynamic entry accessors for `z.record` and string-key `z.map` fields
|
|
131
155
|
- `OperationsAccessor` — `operations` and `clearOperations()`
|
|
132
156
|
- `SnapshotAccessor<T>` — `toJSON()`
|
|
133
157
|
- `SmartObjectConstructor<T>` — constructor type including `fromOperations`
|
|
@@ -135,9 +159,11 @@ RFC 6902 operation emitted by [fast-json-patch](https://github.com/Starcounter-J
|
|
|
135
159
|
|
|
136
160
|
## Limitations
|
|
137
161
|
|
|
138
|
-
- **Partial
|
|
162
|
+
- **Partial discriminator write** — Changing a discriminated union discriminator alone via `setType(...)` without providing the new variant fields throws `SmartObjectError`. Use `switchVariant(...)` or `switchTo{Variant}(...)` instead.
|
|
139
163
|
- **Union field on wrong variant** — Setting a field that does not exist on the active variant throws `SmartObjectError`.
|
|
140
|
-
- **
|
|
164
|
+
- **Date fields** — `z.date()` and `z.coerce.date()` are supported; operations store ISO 8601 strings while getters return `Date` instances.
|
|
165
|
+
- **Map, Set, and bigint** — `z.map` (string keys), `z.set`, and `z.bigint()` are supported with explicit codecs; operations use JSON-safe plain objects, arrays, and decimal strings respectively. String-key `z.map` fields also expose per-entry `get/set/delete*Entry` methods; whole-field `set*` is used for `z.set` and for map fields with non-string keys (not supported). Non-string map keys are not supported.
|
|
166
|
+
- **Transforms** — `z.transform` / `z.pipe` with preprocessing work at runtime; operations store the **output** value after validation. TypeScript setter input types may not reflect transforms.
|
|
141
167
|
|
|
142
168
|
## Design rationale
|
|
143
169
|
|
|
@@ -153,6 +179,7 @@ RFC 6902 operation emitted by [fast-json-patch](https://github.com/Starcounter-J
|
|
|
153
179
|
- [`examples/person.ts`](examples/person.ts) — primitives, nested objects, and arrays
|
|
154
180
|
- [`examples/event.ts`](examples/event.ts) — discriminated union root
|
|
155
181
|
- [`examples/profile.ts`](examples/profile.ts) — generic union root
|
|
182
|
+
- [`examples/record.ts`](examples/record.ts) — `z.record` and string-key `z.map` entry API
|
|
156
183
|
|
|
157
184
|
## Project structure
|
|
158
185
|
|
|
@@ -169,17 +196,21 @@ smart-object/
|
|
|
169
196
|
│ ├── build-class.ts # Class generation orchestration
|
|
170
197
|
│ ├── instance-state.ts # WeakMap-backed instance storage
|
|
171
198
|
│ ├── read-field.ts # Defensive getter reads
|
|
172
|
-
│ ├── json-patch.ts # fast-json-patch wrapper
|
|
199
|
+
│ ├── json-patch.ts # fast-json-patch wrapper + Date-safe deepClone
|
|
200
|
+
│ ├── codecs.ts # JSON-safe codecs for Date, Map, Set, and bigint
|
|
173
201
|
│ ├── apply-operations.ts # Replay and rollback
|
|
174
202
|
│ ├── union-variant.ts # Union variant matching
|
|
175
203
|
│ ├── define-prototype.ts # Getter/setter prototype setup
|
|
176
204
|
│ └── setters/
|
|
177
205
|
│ ├── object-field.ts
|
|
178
|
-
│
|
|
206
|
+
│ ├── union-field.ts
|
|
207
|
+
│ ├── variant-switch.ts
|
|
208
|
+
│ └── record-field.ts
|
|
179
209
|
├── examples/
|
|
180
210
|
│ ├── person.ts
|
|
181
211
|
│ ├── event.ts
|
|
182
|
-
│
|
|
212
|
+
│ ├── profile.ts
|
|
213
|
+
│ └── record.ts
|
|
183
214
|
├── tests/
|
|
184
215
|
│ ├── fixtures/
|
|
185
216
|
│ │ ├── person.ts
|
|
@@ -199,6 +230,10 @@ smart-object/
|
|
|
199
230
|
│ │ ├── setter-naming.test.ts
|
|
200
231
|
│ │ ├── to-json.test.ts
|
|
201
232
|
│ │ ├── schema-variants.test.ts
|
|
233
|
+
│ │ ├── record-fields.test.ts
|
|
234
|
+
│ │ ├── date-codec.test.ts
|
|
235
|
+
│ │ ├── complex-schema-types.test.ts
|
|
236
|
+
│ │ ├── intersection-lazy.test.ts
|
|
202
237
|
│ │ └── types.test.ts
|
|
203
238
|
│ └── zod-introspect.test.ts
|
|
204
239
|
└── dist/ # Build output (generated)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { SmartObjectErrorCode } from "./errors.js";
|
|
2
2
|
export { SmartObjectError } from "./errors.js";
|
|
3
3
|
export { SmartObject } from "./smart-object/index.js";
|
|
4
|
-
export type { AllKeys, Operation, OperationsAccessor, SetMethods, SetMethodsUnion, SmartObjectConstructor, SmartObjectInstance, SmartObjectSchema, SnapshotAccessor, UnionDataShape, } from "./types.js";
|
|
4
|
+
export type { AllKeys, DiscriminatedVariantSwitchMethods, Operation, OperationsAccessor, RecordFieldMethods, SetMethods, SetMethodsUnion, SmartObjectConstructor, SmartObjectInstance, SmartObjectSchema, SnapshotAccessor, UnionDataShape, VariantSwitchMethods, } from "./types.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,YAAY,EACV,OAAO,EACP,SAAS,EACT,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,YAAY,EACV,OAAO,EACP,iCAAiC,EACjC,SAAS,EACT,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-operations.d.ts","sourceRoot":"","sources":["../../src/smart-object/apply-operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-operations.d.ts","sourceRoot":"","sources":["../../src/smart-object/apply-operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,wBAAgB,eAAe,CAAC,CAAC,EAC/B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,SAAS,EAAE,GACtB,IAAI,CAkBN"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SmartObjectError } from "../errors.js";
|
|
2
|
+
import { deserializeDataFromPatch } from "./codecs.js";
|
|
2
3
|
import { applyPatch, deepClone } from "./json-patch.js";
|
|
3
4
|
export function applyOperations(state, zodSchema, instance, operations) {
|
|
4
5
|
if (operations.length === 0) {
|
|
@@ -8,7 +9,8 @@ export function applyOperations(state, zodSchema, instance, operations) {
|
|
|
8
9
|
const snapshot = deepClone(data);
|
|
9
10
|
try {
|
|
10
11
|
applyPatch(data, operations, false, true);
|
|
11
|
-
const
|
|
12
|
+
const deserialized = deserializeDataFromPatch(data, zodSchema);
|
|
13
|
+
const validated = zodSchema.parse(deserialized);
|
|
12
14
|
state.setData(instance, validated);
|
|
13
15
|
state.getOperations(instance).push(...operations);
|
|
14
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-class.d.ts","sourceRoot":"","sources":["../../src/smart-object/build-class.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAuB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOlG,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,iBAAiB,EAC/D,SAAS,EAAE,CAAC,GACX,sBAAsB,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"build-class.d.ts","sourceRoot":"","sources":["../../src/smart-object/build-class.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAuB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOlG,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,iBAAiB,EAC/D,SAAS,EAAE,CAAC,GACX,sBAAsB,CAAC,CAAC,CAAC,CAqC3B"}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getSchemaShapeKeys } from "../zod-introspect.js";
|
|
2
2
|
import { applyOperations } from "./apply-operations.js";
|
|
3
3
|
import { definePrototype } from "./define-prototype.js";
|
|
4
4
|
import { createInstanceState } from "./instance-state.js";
|
|
5
5
|
import { deepClone } from "./json-patch.js";
|
|
6
6
|
export function buildSmartObjectClass(zodSchema) {
|
|
7
|
-
const keys =
|
|
8
|
-
? getObjectShapeKeys(zodSchema)
|
|
9
|
-
: getUnionObjectKeys(zodSchema);
|
|
7
|
+
const keys = getSchemaShapeKeys(zodSchema);
|
|
10
8
|
const state = createInstanceState();
|
|
11
9
|
class SmartObjectClass {
|
|
12
10
|
static fromOperations(initial, operations) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { SmartObjectSchema } from "../types.js";
|
|
3
|
+
export declare function serializeValue(schema: z.ZodType, value: unknown): unknown;
|
|
4
|
+
export declare function deserializeValue(schema: z.ZodType, value: unknown): unknown;
|
|
5
|
+
export declare function serializeDataForPatch<T extends Record<string, unknown>>(data: T, rootSchema: SmartObjectSchema): T;
|
|
6
|
+
export declare function deserializeDataFromPatch<T extends Record<string, unknown>>(data: T, rootSchema: SmartObjectSchema): T;
|
|
7
|
+
export declare function serializePatchValue(rootSchema: SmartObjectSchema, key: string, value: unknown): unknown;
|
|
8
|
+
export declare function serializeEntryPatchValue(fieldSchema: z.ZodType, valueSchema: z.ZodType, value: unknown): unknown;
|
|
9
|
+
/** Serialize a single entry container for RFC 6902 compare (record or map field). */
|
|
10
|
+
export declare function serializeEntryContainer(fieldSchema: z.ZodType, container: unknown): Record<string, unknown>;
|
|
11
|
+
/** @deprecated Use serializeValue */
|
|
12
|
+
export declare function serializeFieldValue(fieldSchema: z.ZodType, value: unknown): unknown;
|
|
13
|
+
/** @deprecated Use deserializeValue */
|
|
14
|
+
export declare function deserializeFieldValue(fieldSchema: z.ZodType, value: unknown): unknown;
|
|
15
|
+
//# sourceMappingURL=codecs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codecs.d.ts","sourceRoot":"","sources":["../../src/smart-object/codecs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAwBrD,wBAAgB,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CA6DzE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAmE3E;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,IAAI,EAAE,CAAC,EACP,UAAU,EAAE,iBAAiB,GAC5B,CAAC,CAeH;AAED,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxE,IAAI,EAAE,CAAC,EACP,UAAU,EAAE,iBAAiB,GAC5B,CAAC,CAeH;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,iBAAiB,EAC7B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,OAAO,CAQT;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,CAAC,CAAC,OAAO,EACtB,WAAW,EAAE,CAAC,CAAC,OAAO,EACtB,KAAK,EAAE,OAAO,GACb,OAAO,CAYT;AAED,qFAAqF;AACrF,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,CAAC,CAAC,OAAO,EACtB,SAAS,EAAE,OAAO,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB;AAED,qCAAqC;AACrC,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAEnF;AAED,uCAAuC;AACvC,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAErF"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { getFieldSchema, getMergedObjectShape, isZodRecord, unwrapFieldSchema, } from "../zod-introspect.js";
|
|
2
|
+
function getSchemaDef(schema) {
|
|
3
|
+
return schema._zod.def;
|
|
4
|
+
}
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
export function serializeValue(schema, value) {
|
|
9
|
+
const inner = unwrapFieldSchema(schema);
|
|
10
|
+
const def = getSchemaDef(inner);
|
|
11
|
+
if (value instanceof Date && def.type === "date") {
|
|
12
|
+
return value.toISOString();
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === "bigint" && def.type === "bigint") {
|
|
15
|
+
return value.toString();
|
|
16
|
+
}
|
|
17
|
+
if (value instanceof Map && def.type === "map") {
|
|
18
|
+
const keySchema = def.keyType;
|
|
19
|
+
const valueSchema = def.valueType;
|
|
20
|
+
const result = {};
|
|
21
|
+
for (const [key, entryValue] of value.entries()) {
|
|
22
|
+
const serializedKey = serializeValue(keySchema, key);
|
|
23
|
+
if (typeof serializedKey === "string") {
|
|
24
|
+
result[serializedKey] = serializeValue(valueSchema, entryValue);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
if (value instanceof Set && def.type === "set") {
|
|
30
|
+
const valueSchema = def.valueType;
|
|
31
|
+
return [...value].map((item) => serializeValue(valueSchema, item));
|
|
32
|
+
}
|
|
33
|
+
if (def.type === "object" && isPlainObject(value)) {
|
|
34
|
+
const shape = def.shape ?? {};
|
|
35
|
+
const result = {};
|
|
36
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
37
|
+
const fieldSchema = shape[key];
|
|
38
|
+
result[key] = fieldSchema ? serializeValue(fieldSchema, fieldValue) : fieldValue;
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
if (def.type === "array" && Array.isArray(value)) {
|
|
43
|
+
const elementSchema = def.element;
|
|
44
|
+
return value.map((item) => serializeValue(elementSchema, item));
|
|
45
|
+
}
|
|
46
|
+
if (def.type === "record" && isPlainObject(value)) {
|
|
47
|
+
const valueSchema = def.valueType;
|
|
48
|
+
const result = {};
|
|
49
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
50
|
+
result[key] = serializeValue(valueSchema, fieldValue);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
export function deserializeValue(schema, value) {
|
|
57
|
+
const inner = unwrapFieldSchema(schema);
|
|
58
|
+
const def = getSchemaDef(inner);
|
|
59
|
+
if (def.type === "date" && typeof value === "string") {
|
|
60
|
+
const parsed = new Date(value);
|
|
61
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
if (def.type === "bigint" && typeof value === "string") {
|
|
67
|
+
try {
|
|
68
|
+
return BigInt(value);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (def.type === "map" && isPlainObject(value)) {
|
|
75
|
+
const keySchema = def.keyType;
|
|
76
|
+
const valueSchema = def.valueType;
|
|
77
|
+
const map = new Map();
|
|
78
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
79
|
+
map.set(deserializeValue(keySchema, key), deserializeValue(valueSchema, entryValue));
|
|
80
|
+
}
|
|
81
|
+
return map;
|
|
82
|
+
}
|
|
83
|
+
if (def.type === "set" && Array.isArray(value)) {
|
|
84
|
+
const valueSchema = def.valueType;
|
|
85
|
+
return new Set(value.map((item) => deserializeValue(valueSchema, item)));
|
|
86
|
+
}
|
|
87
|
+
if (def.type === "object" && isPlainObject(value)) {
|
|
88
|
+
const shape = def.shape ?? {};
|
|
89
|
+
const result = {};
|
|
90
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
91
|
+
const fieldSchema = shape[key];
|
|
92
|
+
result[key] = fieldSchema ? deserializeValue(fieldSchema, fieldValue) : fieldValue;
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
if (def.type === "array" && Array.isArray(value)) {
|
|
97
|
+
const elementSchema = def.element;
|
|
98
|
+
return value.map((item) => deserializeValue(elementSchema, item));
|
|
99
|
+
}
|
|
100
|
+
if (def.type === "record" && isPlainObject(value)) {
|
|
101
|
+
const valueSchema = def.valueType;
|
|
102
|
+
const result = {};
|
|
103
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
104
|
+
result[key] = deserializeValue(valueSchema, fieldValue);
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
export function serializeDataForPatch(data, rootSchema) {
|
|
111
|
+
const shape = getMergedObjectShape(rootSchema);
|
|
112
|
+
if (Object.keys(shape).length === 0) {
|
|
113
|
+
return data;
|
|
114
|
+
}
|
|
115
|
+
const result = {};
|
|
116
|
+
for (const [key, fieldValue] of Object.entries(data)) {
|
|
117
|
+
const fieldSchema = shape[key];
|
|
118
|
+
result[key] = fieldSchema ? serializeValue(fieldSchema, fieldValue) : fieldValue;
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
export function deserializeDataFromPatch(data, rootSchema) {
|
|
123
|
+
const shape = getMergedObjectShape(rootSchema);
|
|
124
|
+
if (Object.keys(shape).length === 0) {
|
|
125
|
+
return data;
|
|
126
|
+
}
|
|
127
|
+
const result = {};
|
|
128
|
+
for (const [key, fieldValue] of Object.entries(data)) {
|
|
129
|
+
const fieldSchema = shape[key];
|
|
130
|
+
result[key] = fieldSchema ? deserializeValue(fieldSchema, fieldValue) : fieldValue;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
export function serializePatchValue(rootSchema, key, value) {
|
|
135
|
+
const fieldSchema = getFieldSchema(rootSchema, key);
|
|
136
|
+
if (!fieldSchema) {
|
|
137
|
+
return value;
|
|
138
|
+
}
|
|
139
|
+
return serializeValue(fieldSchema, value);
|
|
140
|
+
}
|
|
141
|
+
export function serializeEntryPatchValue(fieldSchema, valueSchema, value) {
|
|
142
|
+
const inner = unwrapFieldSchema(fieldSchema);
|
|
143
|
+
if (isZodRecord(inner)) {
|
|
144
|
+
return serializeValue(valueSchema, value);
|
|
145
|
+
}
|
|
146
|
+
if (isPlainObject(value)) {
|
|
147
|
+
return serializeValue(fieldSchema, value);
|
|
148
|
+
}
|
|
149
|
+
return serializeValue(valueSchema, value);
|
|
150
|
+
}
|
|
151
|
+
/** Serialize a single entry container for RFC 6902 compare (record or map field). */
|
|
152
|
+
export function serializeEntryContainer(fieldSchema, container) {
|
|
153
|
+
const serialized = serializeValue(fieldSchema, container);
|
|
154
|
+
return isPlainObject(serialized) ? serialized : {};
|
|
155
|
+
}
|
|
156
|
+
/** @deprecated Use serializeValue */
|
|
157
|
+
export function serializeFieldValue(fieldSchema, value) {
|
|
158
|
+
return serializeValue(fieldSchema, value);
|
|
159
|
+
}
|
|
160
|
+
/** @deprecated Use deserializeValue */
|
|
161
|
+
export function deserializeFieldValue(fieldSchema, value) {
|
|
162
|
+
return deserializeValue(fieldSchema, value);
|
|
163
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-prototype.d.ts","sourceRoot":"","sources":["../../src/smart-object/define-prototype.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"define-prototype.d.ts","sourceRoot":"","sources":["../../src/smart-object/define-prototype.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAarD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAoEzD,wBAAgB,eAAe,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,SAAS,EAAE,iBAAiB,EAC5B,IAAI,EAAE,MAAM,EAAE,GACb,IAAI,CAiCN"}
|
|
@@ -1,10 +1,56 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getDiscriminatedVariants, getDiscriminator, getFieldSchema, getRecordFieldsFromSchema, isObjectLikeRoot, isZodDiscriminatedUnion, isZodUnionRoot, toRecordEntryMethodPrefix, toSetterMethodName, } from "../zod-introspect.js";
|
|
2
2
|
import { readFieldValue } from "./read-field.js";
|
|
3
3
|
import { createObjectFieldSetter } from "./setters/object-field.js";
|
|
4
|
+
import { createRecordEntryDeleter, createRecordEntryGetter, createRecordEntrySetter, } from "./setters/record-field.js";
|
|
4
5
|
import { createUnionFieldSetter } from "./setters/union-field.js";
|
|
6
|
+
import { createSwitchToVariant, createSwitchVariant } from "./setters/variant-switch.js";
|
|
7
|
+
function defineRecordFieldMethods(prototype, state, zodSchema) {
|
|
8
|
+
for (const field of getRecordFieldsFromSchema(zodSchema)) {
|
|
9
|
+
const prefix = toRecordEntryMethodPrefix(field.fieldName);
|
|
10
|
+
Object.defineProperty(prototype, `get${prefix}Entry`, {
|
|
11
|
+
value: createRecordEntryGetter(state, field),
|
|
12
|
+
enumerable: false,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(prototype, `set${prefix}Entry`, {
|
|
17
|
+
value: createRecordEntrySetter(state, zodSchema, field),
|
|
18
|
+
enumerable: false,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(prototype, `delete${prefix}Entry`, {
|
|
23
|
+
value: createRecordEntryDeleter(state, zodSchema, field),
|
|
24
|
+
enumerable: false,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function defineUnionRootMethods(prototype, state, zodSchema) {
|
|
31
|
+
Object.defineProperty(prototype, "switchVariant", {
|
|
32
|
+
value: createSwitchVariant(state, zodSchema),
|
|
33
|
+
enumerable: false,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
});
|
|
37
|
+
if (isZodDiscriminatedUnion(zodSchema)) {
|
|
38
|
+
const discriminator = getDiscriminator(zodSchema);
|
|
39
|
+
for (const variant of getDiscriminatedVariants(zodSchema)) {
|
|
40
|
+
Object.defineProperty(prototype, variant.methodName, {
|
|
41
|
+
value: createSwitchToVariant(state, zodSchema, variant.schema, variant.tag, discriminator),
|
|
42
|
+
enumerable: false,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
5
49
|
export function definePrototype(prototype, state, zodSchema, keys) {
|
|
50
|
+
const objectSchema = isObjectLikeRoot(zodSchema) ? zodSchema : undefined;
|
|
6
51
|
for (const key of keys) {
|
|
7
52
|
const setMethodName = toSetterMethodName(key);
|
|
53
|
+
const fieldSchema = getFieldSchema(zodSchema, key);
|
|
8
54
|
Object.defineProperty(prototype, key, {
|
|
9
55
|
get() {
|
|
10
56
|
return readFieldValue(state.getData(this)[key]);
|
|
@@ -13,12 +59,18 @@ export function definePrototype(prototype, state, zodSchema, keys) {
|
|
|
13
59
|
configurable: true,
|
|
14
60
|
});
|
|
15
61
|
Object.defineProperty(prototype, setMethodName, {
|
|
16
|
-
value:
|
|
17
|
-
? createObjectFieldSetter(state,
|
|
62
|
+
value: fieldSchema && isObjectLikeRoot(zodSchema)
|
|
63
|
+
? createObjectFieldSetter(state, key, fieldSchema, zodSchema)
|
|
18
64
|
: createUnionFieldSetter(state, zodSchema, key),
|
|
19
65
|
enumerable: false,
|
|
20
66
|
configurable: true,
|
|
21
67
|
writable: true,
|
|
22
68
|
});
|
|
23
69
|
}
|
|
70
|
+
if (objectSchema) {
|
|
71
|
+
defineRecordFieldMethods(prototype, state, zodSchema);
|
|
72
|
+
}
|
|
73
|
+
if (isZodUnionRoot(zodSchema)) {
|
|
74
|
+
defineUnionRootMethods(prototype, state, zodSchema);
|
|
75
|
+
}
|
|
24
76
|
}
|
|
@@ -13,8 +13,8 @@ import type { SmartObjectConstructor } from "../types.js";
|
|
|
13
13
|
* Initial construction does not emit operations because that state is the baseline
|
|
14
14
|
* every subsequent patch is measured against.
|
|
15
15
|
*
|
|
16
|
-
* @typeParam T - Zod
|
|
17
|
-
* @param zodSchema - A `z.object({ ... })`, `z.union([...])`, or `z.
|
|
16
|
+
* @typeParam T - Zod schema that defines the instance shape (object, union, intersection, or lazy root)
|
|
17
|
+
* @param zodSchema - A `z.object({ ... })`, `z.union([...])`, `z.discriminatedUnion(...)`, `z.intersection(...)`, or `z.lazy(...)` schema
|
|
18
18
|
* @returns An instantiable class with types inferred from the schema
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
@@ -33,4 +33,6 @@ import type { SmartObjectConstructor } from "../types.js";
|
|
|
33
33
|
export declare function SmartObject<T extends z.ZodObject>(zodSchema: T): SmartObjectConstructor<T>;
|
|
34
34
|
export declare function SmartObject<T extends z.ZodDiscriminatedUnion>(zodSchema: T): SmartObjectConstructor<T>;
|
|
35
35
|
export declare function SmartObject<T extends z.ZodUnion>(zodSchema: T): SmartObjectConstructor<T>;
|
|
36
|
+
export declare function SmartObject<T extends z.ZodIntersection>(zodSchema: T): SmartObjectConstructor<T>;
|
|
37
|
+
export declare function SmartObject<T extends z.ZodLazy>(zodSchema: T): SmartObjectConstructor<T>;
|
|
36
38
|
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/smart-object/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,sBAAsB,EAAqB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/smart-object/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,sBAAsB,EAAqB,MAAM,aAAa,CAAC;AAwC7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;AAC5F,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,qBAAqB,EAC3D,SAAS,EAAE,CAAC,GACX,sBAAsB,CAAC,CAAC,CAAC,CAAC;AAC7B,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;AAC3F,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,eAAe,EAAE,SAAS,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;AAClG,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { SmartObjectError } from "../errors.js";
|
|
2
|
-
import { isZodObject, isZodUnionOfObjects, isZodUnionRoot } from "../zod-introspect.js";
|
|
2
|
+
import { isZodIntersection, isZodObject, isZodUnionOfObjects, isZodUnionRoot, resolveLazySchema, } from "../zod-introspect.js";
|
|
3
3
|
import { buildSmartObjectClass } from "./build-class.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
function assertBuildableSchema(schema) {
|
|
5
|
+
const resolved = resolveLazySchema(schema);
|
|
6
|
+
if (isZodObject(resolved)) {
|
|
7
|
+
return;
|
|
7
8
|
}
|
|
8
|
-
if (isZodUnionRoot(
|
|
9
|
-
if (!isZodUnionOfObjects(
|
|
9
|
+
if (isZodUnionRoot(resolved)) {
|
|
10
|
+
if (!isZodUnionOfObjects(resolved)) {
|
|
10
11
|
throw SmartObjectError.unsupportedSchema("SmartObject union root requires all options to be z.object(...)");
|
|
11
12
|
}
|
|
12
|
-
return
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (isZodIntersection(resolved)) {
|
|
16
|
+
const left = resolved._zod.def.left;
|
|
17
|
+
const right = resolved._zod.def.right;
|
|
18
|
+
assertBuildableSchema(left);
|
|
19
|
+
assertBuildableSchema(right);
|
|
20
|
+
return;
|
|
13
21
|
}
|
|
14
|
-
throw SmartObjectError.unsupportedSchema("SmartObject requires a z.object(...), z.union([...]), or z.
|
|
22
|
+
throw SmartObjectError.unsupportedSchema("SmartObject requires a buildable z.object(...), z.union([...]), z.discriminatedUnion(...), z.intersection(...), or z.lazy(...) schema");
|
|
23
|
+
}
|
|
24
|
+
export function SmartObject(zodSchema) {
|
|
25
|
+
assertBuildableSchema(zodSchema);
|
|
26
|
+
return buildSmartObjectClass(zodSchema);
|
|
15
27
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import jsonPatch from "fast-json-patch";
|
|
2
|
-
export declare const applyPatch: typeof jsonPatch.applyPatch, compare: typeof jsonPatch.compare
|
|
2
|
+
export declare const applyPatch: typeof jsonPatch.applyPatch, compare: typeof jsonPatch.compare;
|
|
3
|
+
export declare function deepClone<T>(value: T): T;
|
|
3
4
|
//# sourceMappingURL=json-patch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-patch.d.ts","sourceRoot":"","sources":["../../src/smart-object/json-patch.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,iBAAiB,CAAC;AAExC,eAAO,MAAQ,UAAU,+BAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"json-patch.d.ts","sourceRoot":"","sources":["../../src/smart-object/json-patch.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,iBAAiB,CAAC;AAExC,eAAO,MAAQ,UAAU,+BAAE,OAAO,0BAAc,CAAC;AAEjD,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAkCxC"}
|
|
@@ -1,2 +1,27 @@
|
|
|
1
1
|
import jsonPatch from "fast-json-patch";
|
|
2
|
-
export const { applyPatch, compare
|
|
2
|
+
export const { applyPatch, compare } = jsonPatch;
|
|
3
|
+
export function deepClone(value) {
|
|
4
|
+
if (value instanceof Date) {
|
|
5
|
+
return new Date(value.getTime());
|
|
6
|
+
}
|
|
7
|
+
if (typeof value === "bigint") {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
if (value instanceof Map) {
|
|
11
|
+
return new Map([...value.entries()].map(([key, nestedValue]) => [key, deepClone(nestedValue)]));
|
|
12
|
+
}
|
|
13
|
+
if (value instanceof Set) {
|
|
14
|
+
return new Set([...value].map((item) => deepClone(item)));
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
return value.map((item) => deepClone(item));
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === "object" && value !== null) {
|
|
20
|
+
const result = {};
|
|
21
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
22
|
+
result[key] = deepClone(nestedValue);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-field.d.ts","sourceRoot":"","sources":["../../src/smart-object/read-field.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"read-field.d.ts","sourceRoot":"","sources":["../../src/smart-object/read-field.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAetD"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { deepClone } from "./json-patch.js";
|
|
2
2
|
export function readFieldValue(value) {
|
|
3
|
+
if (value instanceof Date ||
|
|
4
|
+
value instanceof Map ||
|
|
5
|
+
value instanceof Set ||
|
|
6
|
+
typeof value === "bigint") {
|
|
7
|
+
return deepClone(value);
|
|
8
|
+
}
|
|
3
9
|
if (typeof value === "object" && value !== null) {
|
|
4
10
|
return deepClone(value);
|
|
5
11
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { SmartObjectSchema } from "../../types.js";
|
|
2
3
|
import type { InstanceState } from "../instance-state.js";
|
|
3
|
-
export declare function createObjectFieldSetter<T>(state: InstanceState<T>,
|
|
4
|
+
export declare function createObjectFieldSetter<T>(state: InstanceState<T>, key: string, fieldSchema: z.ZodType, rootSchema: SmartObjectSchema): (this: object, value: unknown) => void;
|
|
4
5
|
//# sourceMappingURL=object-field.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object-field.d.ts","sourceRoot":"","sources":["../../../src/smart-object/setters/object-field.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"object-field.d.ts","sourceRoot":"","sources":["../../../src/smart-object/setters/object-field.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA8C1D,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,CAAC,CAAC,OAAO,EACtB,UAAU,EAAE,iBAAiB,IAEZ,MAAM,MAAM,EAAE,OAAO,OAAO,UAoC9C"}
|
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
import { SmartObjectError } from "../../errors.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { unwrapFieldSchema } from "../../zod-introspect.js";
|
|
3
|
+
import { serializeDataForPatch, serializeValue } from "../codecs.js";
|
|
4
|
+
import { compare, deepClone } from "../json-patch.js";
|
|
5
|
+
function normalizeFieldPatch(patch, key, fieldSchema, parsed, beforeSerialized) {
|
|
6
|
+
const inner = unwrapFieldSchema(fieldSchema);
|
|
7
|
+
const defType = inner._zod.def.type;
|
|
8
|
+
if (defType !== "set" && defType !== "map") {
|
|
9
|
+
return patch.map((operation) => {
|
|
10
|
+
if ("value" in operation && operation.path === `/${key}`) {
|
|
11
|
+
return {
|
|
12
|
+
...operation,
|
|
13
|
+
value: serializeValue(fieldSchema, parsed),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return operation;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const prefix = `/${key}`;
|
|
20
|
+
const touchesField = patch.some((operation) => operation.path === prefix || operation.path.startsWith(`${prefix}/`));
|
|
21
|
+
if (!touchesField) {
|
|
22
|
+
return patch;
|
|
23
|
+
}
|
|
24
|
+
const hadField = Object.hasOwn(beforeSerialized, key) && beforeSerialized[key] !== undefined;
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
op: hadField ? "replace" : "add",
|
|
28
|
+
path: prefix,
|
|
29
|
+
value: serializeValue(fieldSchema, parsed),
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
export function createObjectFieldSetter(state, key, fieldSchema, rootSchema) {
|
|
5
34
|
return function (value) {
|
|
6
35
|
let parsed;
|
|
7
36
|
try {
|
|
@@ -14,11 +43,14 @@ export function createObjectFieldSetter(state, schema, key) {
|
|
|
14
43
|
const beforeData = deepClone(data);
|
|
15
44
|
const afterData = deepClone(data);
|
|
16
45
|
afterData[key] = parsed;
|
|
17
|
-
const
|
|
46
|
+
const beforeSerialized = serializeDataForPatch(beforeData, rootSchema);
|
|
47
|
+
const afterSerialized = serializeDataForPatch(afterData, rootSchema);
|
|
48
|
+
const patch = normalizeFieldPatch(compare(beforeSerialized, afterSerialized), key, fieldSchema, parsed, beforeSerialized);
|
|
18
49
|
if (patch.length === 0) {
|
|
19
50
|
return;
|
|
20
51
|
}
|
|
21
|
-
|
|
52
|
+
afterData[key] = parsed;
|
|
53
|
+
state.setData(this, afterData);
|
|
22
54
|
state.getOperations(this).push(...patch);
|
|
23
55
|
};
|
|
24
56
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SmartObjectSchema } from "../../types.js";
|
|
2
|
+
import type { RecordFieldInfo } from "../../zod-introspect.js";
|
|
3
|
+
import type { InstanceState } from "../instance-state.js";
|
|
4
|
+
export declare function createRecordEntryGetter<T>(state: InstanceState<T>, field: RecordFieldInfo): (this: object, key: string) => unknown;
|
|
5
|
+
export declare function createRecordEntrySetter<T>(state: InstanceState<T>, rootSchema: SmartObjectSchema, field: RecordFieldInfo): (this: object, key: string, value: unknown) => void;
|
|
6
|
+
export declare function createRecordEntryDeleter<T>(state: InstanceState<T>, rootSchema: SmartObjectSchema, field: RecordFieldInfo): (this: object, key: string) => void;
|
|
7
|
+
//# sourceMappingURL=record-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-field.d.ts","sourceRoot":"","sources":["../../../src/smart-object/setters/record-field.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAuH1D,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,IACvE,MAAM,MAAM,EAAE,KAAK,MAAM,aAU3C;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,UAAU,EAAE,iBAAiB,EAC7B,KAAK,EAAE,eAAe,IAIL,MAAM,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,OAAO,UA0C3D;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,UAAU,EAAE,iBAAiB,EAC7B,KAAK,EAAE,eAAe,IAIL,MAAM,MAAM,EAAE,KAAK,MAAM,UA0B3C"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { SmartObjectError } from "../../errors.js";
|
|
2
|
+
import { serializeDataForPatch, serializeEntryPatchValue } from "../codecs.js";
|
|
3
|
+
import { compare, deepClone } from "../json-patch.js";
|
|
4
|
+
import { readFieldValue } from "../read-field.js";
|
|
5
|
+
function getEntryContainer(data, fieldName, storage) {
|
|
6
|
+
const container = data[fieldName];
|
|
7
|
+
if (storage === "map") {
|
|
8
|
+
return container instanceof Map
|
|
9
|
+
? container
|
|
10
|
+
: new Map();
|
|
11
|
+
}
|
|
12
|
+
if (typeof container === "object" && container !== null && !Array.isArray(container)) {
|
|
13
|
+
return container;
|
|
14
|
+
}
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
function hasEntry(container, key, storage) {
|
|
18
|
+
if (storage === "map") {
|
|
19
|
+
return container instanceof Map && container.has(key);
|
|
20
|
+
}
|
|
21
|
+
return typeof container === "object" && container !== null && Object.hasOwn(container, key);
|
|
22
|
+
}
|
|
23
|
+
function getEntry(container, key, storage) {
|
|
24
|
+
if (storage === "map" && container instanceof Map) {
|
|
25
|
+
return container.get(key);
|
|
26
|
+
}
|
|
27
|
+
if (typeof container === "object" && container !== null) {
|
|
28
|
+
return container[key];
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
function cloneContainer(container, storage) {
|
|
33
|
+
if (storage === "map") {
|
|
34
|
+
return deepClone(container instanceof Map ? container : new Map());
|
|
35
|
+
}
|
|
36
|
+
return deepClone(typeof container === "object" && container !== null
|
|
37
|
+
? container
|
|
38
|
+
: {});
|
|
39
|
+
}
|
|
40
|
+
function setEntry(container, key, value, storage) {
|
|
41
|
+
if (storage === "map" && container instanceof Map) {
|
|
42
|
+
container.set(key, value);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
container[key] = value;
|
|
46
|
+
}
|
|
47
|
+
function deleteEntry(container, key, storage) {
|
|
48
|
+
if (storage === "map" && container instanceof Map) {
|
|
49
|
+
container.delete(key);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
delete container[key];
|
|
53
|
+
}
|
|
54
|
+
function normalizeEntryPatches(patch, fieldName, fieldSchema, valueSchema, nextContainer, storage) {
|
|
55
|
+
const prefix = `/${fieldName}/`;
|
|
56
|
+
return patch.map((operation) => {
|
|
57
|
+
if (!("value" in operation) || !operation.path.startsWith(prefix)) {
|
|
58
|
+
return operation;
|
|
59
|
+
}
|
|
60
|
+
const remainder = operation.path.slice(prefix.length);
|
|
61
|
+
if (remainder.includes("/")) {
|
|
62
|
+
return operation;
|
|
63
|
+
}
|
|
64
|
+
const entryKey = remainder.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
65
|
+
const entryValue = storage === "map" && nextContainer instanceof Map
|
|
66
|
+
? nextContainer.get(entryKey)
|
|
67
|
+
: nextContainer[entryKey];
|
|
68
|
+
return {
|
|
69
|
+
...operation,
|
|
70
|
+
value: serializeEntryPatchValue(fieldSchema, valueSchema, entryValue),
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export function createRecordEntryGetter(state, field) {
|
|
75
|
+
return function (key) {
|
|
76
|
+
const data = state.getData(this);
|
|
77
|
+
const value = getEntry(data[field.fieldName], key, field.storage);
|
|
78
|
+
if (value === undefined) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return readFieldValue(value);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export function createRecordEntrySetter(state, rootSchema, field) {
|
|
85
|
+
const { fieldName, fieldSchema, valueSchema, storage } = field;
|
|
86
|
+
return function (key, value) {
|
|
87
|
+
let parsed;
|
|
88
|
+
try {
|
|
89
|
+
parsed = valueSchema.parse(value);
|
|
90
|
+
}
|
|
91
|
+
catch (cause) {
|
|
92
|
+
throw SmartObjectError.invalidValue(`${fieldName}.${key}`, cause);
|
|
93
|
+
}
|
|
94
|
+
const data = state.getData(this);
|
|
95
|
+
const container = getEntryContainer(data, fieldName, storage);
|
|
96
|
+
const previousValue = getEntry(container, key, storage);
|
|
97
|
+
if (hasEntry(container, key, storage) && Object.is(previousValue, parsed)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const beforeData = deepClone(data);
|
|
101
|
+
const afterData = deepClone(data);
|
|
102
|
+
const nextContainer = cloneContainer(container, storage);
|
|
103
|
+
setEntry(nextContainer, key, parsed, storage);
|
|
104
|
+
afterData[fieldName] = nextContainer;
|
|
105
|
+
const patch = normalizeEntryPatches(compare(serializeDataForPatch(beforeData, rootSchema), serializeDataForPatch(afterData, rootSchema)), fieldName, fieldSchema, valueSchema, nextContainer, storage);
|
|
106
|
+
if (patch.length === 0) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
data[fieldName] = nextContainer;
|
|
110
|
+
state.getOperations(this).push(...patch);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export function createRecordEntryDeleter(state, rootSchema, field) {
|
|
114
|
+
const { fieldName, storage } = field;
|
|
115
|
+
return function (key) {
|
|
116
|
+
const data = state.getData(this);
|
|
117
|
+
const container = data[fieldName];
|
|
118
|
+
if (!hasEntry(container, key, storage)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const beforeData = deepClone(data);
|
|
122
|
+
const afterData = deepClone(data);
|
|
123
|
+
const nextContainer = cloneContainer(getEntryContainer(afterData, fieldName, storage), storage);
|
|
124
|
+
deleteEntry(nextContainer, key, storage);
|
|
125
|
+
afterData[fieldName] = nextContainer;
|
|
126
|
+
const patch = compare(serializeDataForPatch(beforeData, rootSchema), serializeDataForPatch(afterData, rootSchema));
|
|
127
|
+
if (patch.length === 0) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
data[fieldName] = nextContainer;
|
|
131
|
+
state.getOperations(this).push(...patch);
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ZodDiscriminatedUnionLike, ZodObjectLike, ZodUnionRootLike } from "../../zod-introspect.js";
|
|
2
|
+
import type { InstanceState } from "../instance-state.js";
|
|
3
|
+
export declare function createSwitchVariant<T>(state: InstanceState<T>, schema: ZodUnionRootLike): (this: object, value: unknown) => void;
|
|
4
|
+
export declare function createSwitchToVariant<T>(state: InstanceState<T>, schema: ZodDiscriminatedUnionLike, variantSchema: ZodObjectLike, tagValue: string | number | boolean, discriminator: string): (this: object, partial: Record<string, unknown>) => void;
|
|
5
|
+
//# sourceMappingURL=variant-switch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant-switch.d.ts","sourceRoot":"","sources":["../../../src/smart-object/setters/variant-switch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACb,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA8B1D,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,gBAAgB,IACrE,MAAM,MAAM,EAAE,OAAO,OAAO,UAG9C;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,yBAAyB,EACjC,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EACnC,aAAa,EAAE,MAAM,IAEJ,MAAM,MAAM,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,UAahE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { SmartObjectError } from "../../errors.js";
|
|
2
|
+
import { compare, deepClone } from "../json-patch.js";
|
|
3
|
+
function applyVariantSwitch(state, instance, schema, value) {
|
|
4
|
+
const data = state.getData(instance);
|
|
5
|
+
const beforeData = deepClone(data);
|
|
6
|
+
let parsed;
|
|
7
|
+
try {
|
|
8
|
+
parsed = schema.parse(value);
|
|
9
|
+
}
|
|
10
|
+
catch (cause) {
|
|
11
|
+
throw SmartObjectError.invalidValue("variant", cause);
|
|
12
|
+
}
|
|
13
|
+
const patch = compare(beforeData, parsed);
|
|
14
|
+
if (patch.length === 0) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
state.setData(instance, parsed);
|
|
18
|
+
state.getOperations(instance).push(...patch);
|
|
19
|
+
}
|
|
20
|
+
export function createSwitchVariant(state, schema) {
|
|
21
|
+
return function (value) {
|
|
22
|
+
applyVariantSwitch(state, this, schema, value);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function createSwitchToVariant(state, schema, variantSchema, tagValue, discriminator) {
|
|
26
|
+
return function (partial) {
|
|
27
|
+
const candidate = { ...partial, [discriminator]: tagValue };
|
|
28
|
+
let parsed;
|
|
29
|
+
try {
|
|
30
|
+
parsed = variantSchema.parse(candidate);
|
|
31
|
+
}
|
|
32
|
+
catch (cause) {
|
|
33
|
+
throw SmartObjectError.invalidValue("variant", cause);
|
|
34
|
+
}
|
|
35
|
+
applyVariantSwitch(state, this, schema, parsed);
|
|
36
|
+
};
|
|
37
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import type { Operation } from "fast-json-patch";
|
|
|
2
2
|
import type { z } from "zod";
|
|
3
3
|
export type { Operation };
|
|
4
4
|
/**
|
|
5
|
-
* Schemas accepted by SmartObject: plain objects
|
|
5
|
+
* Schemas accepted by SmartObject: plain objects, unions of objects, intersections, or lazy wrappers at root.
|
|
6
6
|
*/
|
|
7
|
-
export type SmartObjectSchema = z.ZodObject | z.ZodDiscriminatedUnion | z.ZodUnion;
|
|
7
|
+
export type SmartObjectSchema = z.ZodObject | z.ZodDiscriminatedUnion | z.ZodUnion | z.ZodIntersection | z.ZodLazy;
|
|
8
8
|
/**
|
|
9
9
|
* Compile-time map of `set*` methods for each schema key.
|
|
10
10
|
*
|
|
@@ -26,6 +26,30 @@ export type AllKeys<T> = T extends unknown ? keyof T : never;
|
|
|
26
26
|
export type SetMethodsUnion<T> = {
|
|
27
27
|
[K in AllKeys<T> as `set${Capitalize<string & K>}`]: (value: T extends unknown ? (K extends keyof T ? T[K] : never) : never) => void;
|
|
28
28
|
};
|
|
29
|
+
type UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (value: infer I) => void ? I : never;
|
|
30
|
+
type OmitDiscriminator<T, D extends PropertyKey> = Omit<T, D & keyof T>;
|
|
31
|
+
type PerVariantSwitchMethod<T, D extends PropertyKey> = T extends Record<D, infer Tag extends string> ? Tag extends string ? {
|
|
32
|
+
[K in `switchTo${Capitalize<Tag>}`]: (value: OmitDiscriminator<T, D>) => void;
|
|
33
|
+
} : never : never;
|
|
34
|
+
/**
|
|
35
|
+
* Variant switching for union root schemas.
|
|
36
|
+
*/
|
|
37
|
+
export type VariantSwitchMethods<T> = {
|
|
38
|
+
switchVariant(value: T): void;
|
|
39
|
+
};
|
|
40
|
+
export type DiscriminatedVariantSwitchMethods<T, D extends PropertyKey> = VariantSwitchMethods<T> & UnionToIntersection<PerVariantSwitchMethod<T, D>>;
|
|
41
|
+
type EntryValue<T> = T extends Record<string, infer V> ? V : T extends Map<string, infer V> ? V : never;
|
|
42
|
+
type IsEntryField<T> = NonNullable<T> extends Record<string, unknown> ? true : NonNullable<T> extends Map<string, infer _V> ? true : false;
|
|
43
|
+
/**
|
|
44
|
+
* Dynamic entry accessors for `z.record` and string-key `z.map` fields.
|
|
45
|
+
*/
|
|
46
|
+
export type RecordFieldMethods<T> = {
|
|
47
|
+
[K in keyof T as IsEntryField<T[K]> extends true ? `get${Capitalize<string & K>}Entry` : never]: (key: string) => EntryValue<NonNullable<T[K]>> | undefined;
|
|
48
|
+
} & {
|
|
49
|
+
[K in keyof T as IsEntryField<T[K]> extends true ? `set${Capitalize<string & K>}Entry` : never]: (key: string, value: EntryValue<NonNullable<T[K]>>) => void;
|
|
50
|
+
} & {
|
|
51
|
+
[K in keyof T as IsEntryField<T[K]> extends true ? `delete${Capitalize<string & K>}Entry` : never]: (key: string) => void;
|
|
52
|
+
};
|
|
29
53
|
/**
|
|
30
54
|
* Read/write surface and audit trail are separate concerns in the public API.
|
|
31
55
|
*
|
|
@@ -50,12 +74,15 @@ export type SnapshotAccessor<T extends SmartObjectSchema> = {
|
|
|
50
74
|
export type UnionDataShape<U> = {
|
|
51
75
|
[K in AllKeys<U>]: U extends unknown ? (K extends keyof U ? U[K] : never) : never;
|
|
52
76
|
};
|
|
77
|
+
type ObjectLikeSchema<T extends SmartObjectSchema> = T extends z.ZodObject | z.ZodIntersection | z.ZodLazy ? z.infer<T> : never;
|
|
78
|
+
type UnionRootExtras<T extends SmartObjectSchema> = T extends z.ZodDiscriminatedUnion<infer _Options, infer Discriminator extends string> ? DiscriminatedVariantSwitchMethods<z.infer<T>, Discriminator> : T extends z.ZodUnion ? VariantSwitchMethods<z.infer<T>> : Record<string, never>;
|
|
79
|
+
type ObjectRootExtras<T extends SmartObjectSchema> = T extends z.ZodObject ? RecordFieldMethods<z.infer<T>> : T extends z.ZodIntersection | z.ZodLazy ? RecordFieldMethods<ObjectLikeSchema<T>> : Record<string, never>;
|
|
53
80
|
/**
|
|
54
81
|
* Full instance contract: validated data shape, typed mutators, and patch log.
|
|
55
82
|
*
|
|
56
83
|
* Intersection types merge these concerns into one consumable surface for callers.
|
|
57
84
|
*/
|
|
58
|
-
export type SmartObjectInstance<T extends SmartObjectSchema> = (T extends z.ZodObject ? z.infer<T> : UnionDataShape<z.infer<T>>) & (T extends z.ZodObject ? SetMethods<z.infer<T>> : SetMethodsUnion<z.infer<T>>) & OperationsAccessor & SnapshotAccessor<T>;
|
|
85
|
+
export type SmartObjectInstance<T extends SmartObjectSchema> = (T extends z.ZodObject ? z.infer<T> : T extends z.ZodIntersection | z.ZodLazy ? z.infer<T> : UnionDataShape<z.infer<T>>) & (T extends z.ZodObject | z.ZodIntersection | z.ZodLazy ? SetMethods<z.infer<T>> : SetMethodsUnion<z.infer<T>>) & ObjectRootExtras<T> & UnionRootExtras<T> & OperationsAccessor & SnapshotAccessor<T>;
|
|
59
86
|
/**
|
|
60
87
|
* Constructor type for a SmartObject class, including replay as a first-class capability.
|
|
61
88
|
*
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B;;GAEG;AACH,MAAM,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/zod-introspect.d.ts
CHANGED
|
@@ -3,6 +3,19 @@ import type { SmartObjectSchema } from "./types.js";
|
|
|
3
3
|
export type ZodObjectLike = z.ZodObject;
|
|
4
4
|
export type ZodUnionRootLike = z.ZodUnion;
|
|
5
5
|
export type ZodDiscriminatedUnionLike = z.ZodDiscriminatedUnion;
|
|
6
|
+
export type ZodIntersectionLike = z.ZodIntersection;
|
|
7
|
+
export type ZodLazyLike = z.ZodLazy;
|
|
8
|
+
export type DiscriminatedVariantInfo = {
|
|
9
|
+
tag: string | number | boolean;
|
|
10
|
+
schema: ZodObjectLike;
|
|
11
|
+
methodName: string;
|
|
12
|
+
};
|
|
13
|
+
export type RecordFieldInfo = {
|
|
14
|
+
fieldName: string;
|
|
15
|
+
fieldSchema: z.ZodType;
|
|
16
|
+
valueSchema: z.ZodType;
|
|
17
|
+
storage: "record" | "map";
|
|
18
|
+
};
|
|
6
19
|
export declare function isZodObject(schema: unknown): schema is ZodObjectLike;
|
|
7
20
|
export declare function isZodUnionRoot(schema: SmartObjectSchema): schema is ZodUnionRootLike;
|
|
8
21
|
export declare function isZodDiscriminatedUnion(schema: ZodUnionRootLike): schema is ZodDiscriminatedUnionLike;
|
|
@@ -11,4 +24,27 @@ export declare function getObjectShapeKeys(schema: ZodObjectLike): string[];
|
|
|
11
24
|
export declare function getUnionObjectKeys(schema: ZodUnionRootLike): string[];
|
|
12
25
|
export declare function getDiscriminator(schema: ZodDiscriminatedUnionLike): string;
|
|
13
26
|
export declare function toSetterMethodName(key: string): string;
|
|
27
|
+
export declare function toVariantSwitchMethodName(tag: string | number | boolean): string;
|
|
28
|
+
export declare function toRecordEntryMethodPrefix(fieldName: string): string;
|
|
29
|
+
export declare function getLiteralValue(schema: unknown): string | number | boolean | undefined;
|
|
30
|
+
export declare function getDiscriminatedVariants(schema: ZodDiscriminatedUnionLike): DiscriminatedVariantInfo[];
|
|
31
|
+
export declare function isZodRecord(schema: unknown): schema is z.ZodRecord;
|
|
32
|
+
export declare function isZodDate(schema: unknown): boolean;
|
|
33
|
+
export declare function isZodMap(schema: unknown): schema is z.ZodMap;
|
|
34
|
+
export declare function isZodSet(schema: unknown): schema is z.ZodSet;
|
|
35
|
+
export declare function isZodBigInt(schema: unknown): boolean;
|
|
36
|
+
export declare function isStringKeyMap(schema: unknown): schema is z.ZodMap;
|
|
37
|
+
export declare function unwrapFieldSchema(schema: z.ZodType): z.ZodType;
|
|
38
|
+
/** @deprecated Use unwrapFieldSchema */
|
|
39
|
+
export declare function unwrapOptionalNullable(schema: z.ZodType): z.ZodType;
|
|
40
|
+
export declare function isZodIntersection(schema: SmartObjectSchema): schema is ZodIntersectionLike;
|
|
41
|
+
export declare function isZodLazy(schema: SmartObjectSchema): schema is ZodLazyLike;
|
|
42
|
+
export declare function resolveLazySchema(schema: SmartObjectSchema): SmartObjectSchema;
|
|
43
|
+
export declare function getRecordFields(schema: ZodObjectLike): RecordFieldInfo[];
|
|
44
|
+
export declare function getRecordFieldsFromSchema(schema: SmartObjectSchema): RecordFieldInfo[];
|
|
45
|
+
export declare function getMergedObjectShape(schema: SmartObjectSchema): Record<string, z.ZodType>;
|
|
46
|
+
export declare function getSchemaShapeKeys(schema: SmartObjectSchema): string[];
|
|
47
|
+
export declare function getFieldSchema(schema: SmartObjectSchema, key: string): z.ZodType | undefined;
|
|
48
|
+
export declare function getObjectSchemaForFields(schema: SmartObjectSchema): ZodObjectLike | undefined;
|
|
49
|
+
export declare function isObjectLikeRoot(schema: SmartObjectSchema): boolean;
|
|
14
50
|
//# sourceMappingURL=zod-introspect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod-introspect.d.ts","sourceRoot":"","sources":["../src/zod-introspect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,SAAS,CAAC;AACxC,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,QAAQ,CAAC;AAC1C,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"zod-introspect.d.ts","sourceRoot":"","sources":["../src/zod-introspect.ts"],"names":[],"mappings":"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"}
|
package/dist/zod-introspect.js
CHANGED
|
@@ -34,3 +34,219 @@ export function getDiscriminator(schema) {
|
|
|
34
34
|
export function toSetterMethodName(key) {
|
|
35
35
|
return `set${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
36
36
|
}
|
|
37
|
+
export function toVariantSwitchMethodName(tag) {
|
|
38
|
+
const label = typeof tag === "string" ? tag : String(tag);
|
|
39
|
+
return `switchTo${label.charAt(0).toUpperCase()}${label.slice(1)}`;
|
|
40
|
+
}
|
|
41
|
+
export function toRecordEntryMethodPrefix(fieldName) {
|
|
42
|
+
return fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
|
|
43
|
+
}
|
|
44
|
+
export function getLiteralValue(schema) {
|
|
45
|
+
if (typeof schema !== "object" || schema === null || !("_zod" in schema)) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const def = schema._zod.def;
|
|
49
|
+
if (def.type === "literal" && Array.isArray(def.values) && def.values.length === 1) {
|
|
50
|
+
const value = def.values[0];
|
|
51
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
export function getDiscriminatedVariants(schema) {
|
|
58
|
+
const discriminator = getDiscriminator(schema);
|
|
59
|
+
const variants = [];
|
|
60
|
+
for (const option of schema.options) {
|
|
61
|
+
if (!isZodObject(option)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const tagSchema = option.shape[discriminator];
|
|
65
|
+
const tag = getLiteralValue(tagSchema);
|
|
66
|
+
if (tag === undefined) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
variants.push({
|
|
70
|
+
tag,
|
|
71
|
+
schema: option,
|
|
72
|
+
methodName: toVariantSwitchMethodName(tag),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return variants;
|
|
76
|
+
}
|
|
77
|
+
export function isZodRecord(schema) {
|
|
78
|
+
return (typeof schema === "object" &&
|
|
79
|
+
schema !== null &&
|
|
80
|
+
"_zod" in schema &&
|
|
81
|
+
schema._zod.def.type === "record");
|
|
82
|
+
}
|
|
83
|
+
export function isZodDate(schema) {
|
|
84
|
+
if (typeof schema !== "object" || schema === null || !("_zod" in schema)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const def = schema._zod.def;
|
|
88
|
+
return def.type === "date";
|
|
89
|
+
}
|
|
90
|
+
export function isZodMap(schema) {
|
|
91
|
+
return (typeof schema === "object" &&
|
|
92
|
+
schema !== null &&
|
|
93
|
+
"_zod" in schema &&
|
|
94
|
+
schema._zod.def.type === "map");
|
|
95
|
+
}
|
|
96
|
+
export function isZodSet(schema) {
|
|
97
|
+
return (typeof schema === "object" &&
|
|
98
|
+
schema !== null &&
|
|
99
|
+
"_zod" in schema &&
|
|
100
|
+
schema._zod.def.type === "set");
|
|
101
|
+
}
|
|
102
|
+
export function isZodBigInt(schema) {
|
|
103
|
+
if (typeof schema !== "object" || schema === null || !("_zod" in schema)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return schema._zod.def.type === "bigint";
|
|
107
|
+
}
|
|
108
|
+
export function isStringKeyMap(schema) {
|
|
109
|
+
if (!isZodMap(schema)) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
const keyType = unwrapFieldSchema(schema._zod.def.keyType);
|
|
113
|
+
return keyType._zod.def.type === "string";
|
|
114
|
+
}
|
|
115
|
+
export function unwrapFieldSchema(schema) {
|
|
116
|
+
const def = schema._zod.def;
|
|
117
|
+
if (def.type === "optional" || def.type === "nullable" || def.type === "default") {
|
|
118
|
+
return unwrapFieldSchema(def.innerType);
|
|
119
|
+
}
|
|
120
|
+
if (def.type === "lazy") {
|
|
121
|
+
return unwrapFieldSchema(def.getter?.());
|
|
122
|
+
}
|
|
123
|
+
if (def.type === "pipe") {
|
|
124
|
+
const outType = def.out?._zod.def.type;
|
|
125
|
+
if (outType === "transform") {
|
|
126
|
+
return unwrapFieldSchema(def.in);
|
|
127
|
+
}
|
|
128
|
+
return unwrapFieldSchema(def.out);
|
|
129
|
+
}
|
|
130
|
+
return schema;
|
|
131
|
+
}
|
|
132
|
+
/** @deprecated Use unwrapFieldSchema */
|
|
133
|
+
export function unwrapOptionalNullable(schema) {
|
|
134
|
+
return unwrapFieldSchema(schema);
|
|
135
|
+
}
|
|
136
|
+
export function isZodIntersection(schema) {
|
|
137
|
+
return schema._zod.def.type === "intersection";
|
|
138
|
+
}
|
|
139
|
+
export function isZodLazy(schema) {
|
|
140
|
+
return schema._zod.def.type === "lazy";
|
|
141
|
+
}
|
|
142
|
+
export function resolveLazySchema(schema) {
|
|
143
|
+
if (isZodLazy(schema)) {
|
|
144
|
+
return resolveLazySchema(schema._zod.def.getter());
|
|
145
|
+
}
|
|
146
|
+
return schema;
|
|
147
|
+
}
|
|
148
|
+
function collectEntryField(fieldName, fieldSchema, unwrapped) {
|
|
149
|
+
if (isZodRecord(unwrapped)) {
|
|
150
|
+
return {
|
|
151
|
+
fieldName,
|
|
152
|
+
fieldSchema,
|
|
153
|
+
valueSchema: unwrapped._zod.def.valueType,
|
|
154
|
+
storage: "record",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (isStringKeyMap(unwrapped)) {
|
|
158
|
+
return {
|
|
159
|
+
fieldName,
|
|
160
|
+
fieldSchema,
|
|
161
|
+
valueSchema: unwrapped._zod.def.valueType,
|
|
162
|
+
storage: "map",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
export function getRecordFields(schema) {
|
|
168
|
+
const fields = [];
|
|
169
|
+
for (const [fieldName, fieldSchema] of Object.entries(schema.shape)) {
|
|
170
|
+
const unwrapped = unwrapFieldSchema(fieldSchema);
|
|
171
|
+
const entryField = collectEntryField(fieldName, fieldSchema, unwrapped);
|
|
172
|
+
if (entryField) {
|
|
173
|
+
fields.push(entryField);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return fields;
|
|
177
|
+
}
|
|
178
|
+
export function getRecordFieldsFromSchema(schema) {
|
|
179
|
+
const resolved = resolveLazySchema(schema);
|
|
180
|
+
if (isZodObject(resolved)) {
|
|
181
|
+
return getRecordFields(resolved);
|
|
182
|
+
}
|
|
183
|
+
if (isZodIntersection(resolved)) {
|
|
184
|
+
const left = resolved._zod.def.left;
|
|
185
|
+
const right = resolved._zod.def.right;
|
|
186
|
+
return [...getRecordFieldsFromSchema(left), ...getRecordFieldsFromSchema(right)];
|
|
187
|
+
}
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
export function getMergedObjectShape(schema) {
|
|
191
|
+
const resolved = resolveLazySchema(schema);
|
|
192
|
+
if (isZodObject(resolved)) {
|
|
193
|
+
return resolved.shape;
|
|
194
|
+
}
|
|
195
|
+
if (isZodIntersection(resolved)) {
|
|
196
|
+
const left = resolved._zod.def.left;
|
|
197
|
+
const right = resolved._zod.def.right;
|
|
198
|
+
return {
|
|
199
|
+
...getMergedObjectShape(left),
|
|
200
|
+
...getMergedObjectShape(right),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {};
|
|
204
|
+
}
|
|
205
|
+
export function getSchemaShapeKeys(schema) {
|
|
206
|
+
const resolved = resolveLazySchema(schema);
|
|
207
|
+
if (isZodObject(resolved)) {
|
|
208
|
+
return getObjectShapeKeys(resolved);
|
|
209
|
+
}
|
|
210
|
+
if (isZodUnionRoot(resolved)) {
|
|
211
|
+
return getUnionObjectKeys(resolved);
|
|
212
|
+
}
|
|
213
|
+
if (isZodIntersection(resolved)) {
|
|
214
|
+
const left = resolved._zod.def.left;
|
|
215
|
+
const right = resolved._zod.def.right;
|
|
216
|
+
return [...new Set([...getSchemaShapeKeys(left), ...getSchemaShapeKeys(right)])];
|
|
217
|
+
}
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
export function getFieldSchema(schema, key) {
|
|
221
|
+
const resolved = resolveLazySchema(schema);
|
|
222
|
+
if (isZodObject(resolved)) {
|
|
223
|
+
return resolved.shape[key];
|
|
224
|
+
}
|
|
225
|
+
if (isZodIntersection(resolved)) {
|
|
226
|
+
const left = resolved._zod.def.left;
|
|
227
|
+
const right = resolved._zod.def.right;
|
|
228
|
+
return getFieldSchema(left, key) ?? getFieldSchema(right, key);
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
export function getObjectSchemaForFields(schema) {
|
|
233
|
+
const resolved = resolveLazySchema(schema);
|
|
234
|
+
if (isZodObject(resolved)) {
|
|
235
|
+
return resolved;
|
|
236
|
+
}
|
|
237
|
+
if (isZodIntersection(resolved)) {
|
|
238
|
+
const left = resolved._zod.def.left;
|
|
239
|
+
const right = resolved._zod.def.right;
|
|
240
|
+
const leftObject = getObjectSchemaForFields(left);
|
|
241
|
+
const rightObject = getObjectSchemaForFields(right);
|
|
242
|
+
if (leftObject && rightObject) {
|
|
243
|
+
return leftObject;
|
|
244
|
+
}
|
|
245
|
+
return leftObject ?? rightObject;
|
|
246
|
+
}
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
export function isObjectLikeRoot(schema) {
|
|
250
|
+
const resolved = resolveLazySchema(schema);
|
|
251
|
+
return isZodObject(resolved) || isZodIntersection(resolved);
|
|
252
|
+
}
|
package/package.json
CHANGED