@gialicus/smart-object 1.0.0 → 2.0.1
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 +38 -1
- package/README.md +127 -19
- package/dist/errors.d.ts +20 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +40 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/smart-object/apply-operations.d.ts +5 -0
- package/dist/smart-object/apply-operations.d.ts.map +1 -0
- package/dist/smart-object/apply-operations.js +21 -0
- package/dist/smart-object/build-class.d.ts +3 -0
- package/dist/smart-object/build-class.d.ts.map +1 -0
- package/dist/smart-object/build-class.js +31 -0
- 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 +4 -0
- package/dist/smart-object/define-prototype.d.ts.map +1 -0
- package/dist/smart-object/define-prototype.js +76 -0
- package/dist/{smart-object.d.ts → smart-object/factory.d.ts} +4 -2
- package/dist/smart-object/factory.d.ts.map +1 -0
- package/dist/smart-object/factory.js +27 -0
- package/dist/smart-object/index.d.ts +2 -0
- package/dist/smart-object/index.d.ts.map +1 -0
- package/dist/smart-object/index.js +1 -0
- package/dist/smart-object/instance-state.d.ts +9 -0
- package/dist/smart-object/instance-state.d.ts.map +1 -0
- package/dist/smart-object/instance-state.js +23 -0
- package/dist/smart-object/json-patch.d.ts +4 -0
- package/dist/smart-object/json-patch.d.ts.map +1 -0
- package/dist/smart-object/json-patch.js +27 -0
- package/dist/smart-object/read-field.d.ts +2 -0
- package/dist/smart-object/read-field.d.ts.map +1 -0
- package/dist/smart-object/read-field.js +13 -0
- package/dist/smart-object/setters/object-field.d.ts +5 -0
- package/dist/smart-object/setters/object-field.d.ts.map +1 -0
- package/dist/smart-object/setters/object-field.js +56 -0
- 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/union-field.d.ts +4 -0
- package/dist/smart-object/setters/union-field.d.ts.map +1 -0
- package/dist/smart-object/setters/union-field.js +25 -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/smart-object/union-variant.d.ts +5 -0
- package/dist/smart-object/union-variant.d.ts.map +1 -0
- package/dist/smart-object/union-variant.js +38 -0
- package/dist/types.d.ts +36 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/zod-introspect.d.ts +50 -0
- package/dist/zod-introspect.d.ts.map +1 -0
- package/dist/zod-introspect.js +252 -0
- package/package.json +1 -1
- package/dist/smart-object.d.ts.map +0 -1
- package/dist/smart-object.js +0 -157
package/CHANGELOG.md
CHANGED
|
@@ -5,14 +5,51 @@ 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` 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
|
+
|
|
19
|
+
## [2.0.0] - 2026-06-27
|
|
20
|
+
|
|
21
|
+
### Breaking
|
|
22
|
+
|
|
23
|
+
- Getters for object and array fields always return deep clones; in-place mutation no longer affects internal state
|
|
24
|
+
- Removed `SmartObjectOptions` and the `immutableReads` factory option
|
|
25
|
+
- `operations` getter returns a defensive copy instead of the internal array reference
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `toJSON()` instance method for deep-cloned, JSON-safe snapshots (also used by `JSON.stringify`)
|
|
30
|
+
- `SmartObjectError` with structured `code`, optional `field`, and `cause` for programmatic error handling
|
|
31
|
+
- Zod schema introspection isolated in `src/zod-introspect.ts`
|
|
32
|
+
- CI matrix job testing Zod peer compatibility (`4.0.0` and `latest`)
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- Getter and setter methods are defined on the class prototype (one definition per schema, not per instance)
|
|
37
|
+
- Object field setters use a single `deepClone` per write instead of two
|
|
38
|
+
- `fromOperations` re-validates state with Zod after applying patches; invalid replay throws `SmartObjectError` and rolls back
|
|
39
|
+
- Setter and union validation errors throw `SmartObjectError` instead of generic `Error`
|
|
40
|
+
- Unsupported schema at factory time throws `SmartObjectError` with code `UnsupportedSchema`
|
|
41
|
+
|
|
8
42
|
## [1.0.0] - 2026-06-27
|
|
9
43
|
|
|
10
44
|
### Added
|
|
11
45
|
|
|
12
46
|
- `SmartObject(schema)` factory: typed getters and `set*` methods generated from a Zod object schema
|
|
47
|
+
- Support for `z.union([...])` and `z.discriminatedUnion(...)` at schema root
|
|
13
48
|
- RFC 6902 operation log (`operations`) for every validated change
|
|
14
49
|
- `clearOperations()` to reset the audit trail without rolling back state
|
|
15
50
|
- `fromOperations(initial, operations)` static method for deterministic replay
|
|
16
|
-
- Exported types: `Operation`, `SetMethods`, `OperationsAccessor`, `SmartObjectConstructor`, `SmartObjectInstance`
|
|
51
|
+
- Exported types: `Operation`, `SetMethods`, `SetMethodsUnion`, `AllKeys`, `UnionDataShape`, `OperationsAccessor`, `SmartObjectConstructor`, `SmartObjectInstance`
|
|
17
52
|
|
|
53
|
+
[2.0.1]: https://github.com/gialicus/smart-object/compare/v2.0.0...v2.0.1
|
|
54
|
+
[2.0.0]: https://github.com/gialicus/smart-object/compare/v1.0.0...v2.0.0
|
|
18
55
|
[1.0.0]: https://github.com/gialicus/smart-object/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ const Person = SmartObject(z.object({
|
|
|
23
23
|
|
|
24
24
|
const person = new Person({ name: "Mario", age: 30 });
|
|
25
25
|
|
|
26
|
-
// Reads expose validated state
|
|
26
|
+
// Reads expose validated state
|
|
27
27
|
console.log(person.name); // "Mario"
|
|
28
28
|
|
|
29
29
|
// Writes validate first, then append patches to person.operations
|
|
@@ -40,6 +40,9 @@ person.setName("Luigi"); // Unchanged value — no operation added (keeps sync p
|
|
|
40
40
|
|
|
41
41
|
person.clearOperations(); // Drops the audit trail after persist/sync; state is unchanged
|
|
42
42
|
|
|
43
|
+
// Snapshot for serialization — deep clone, safe for JSON.stringify
|
|
44
|
+
console.log(person.toJSON());
|
|
45
|
+
|
|
43
46
|
// Initial construction is the replay baseline — it never emits operations
|
|
44
47
|
console.log(new Person({ name: "Mario", age: 30 }).operations); // []
|
|
45
48
|
|
|
@@ -48,37 +51,85 @@ const initial = { name: "Mario", age: 30 };
|
|
|
48
51
|
const person2 = Person.fromOperations(initial, [...person.operations]);
|
|
49
52
|
```
|
|
50
53
|
|
|
54
|
+
### Union root schemas
|
|
55
|
+
|
|
56
|
+
`SmartObject` also accepts `z.discriminatedUnion(...)` and `z.union([...])` when every option is a `z.object(...)`:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const Event = SmartObject(z.discriminatedUnion("type", [
|
|
60
|
+
z.object({ type: z.literal("click"), x: z.number(), y: z.number() }),
|
|
61
|
+
z.object({ type: z.literal("scroll"), delta: z.number() }),
|
|
62
|
+
]));
|
|
63
|
+
|
|
64
|
+
const event = new Event({ type: "click", x: 10, y: 20 });
|
|
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 });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
See [`examples/event.ts`](examples/event.ts) and [`examples/profile.ts`](examples/profile.ts) for full demos.
|
|
73
|
+
|
|
74
|
+
Object and array getters always return deep clones — in-place mutation does not affect internal state or the operation log. Only `set*` methods generate RFC 6902 operations.
|
|
75
|
+
|
|
51
76
|
## API
|
|
52
77
|
|
|
53
78
|
### `SmartObject(schema)`
|
|
54
79
|
|
|
55
|
-
Factory that accepts a
|
|
80
|
+
Factory that accepts a Zod schema and returns an instantiable class.
|
|
56
81
|
|
|
57
82
|
| Parameter | Type | Description |
|
|
58
83
|
|-----------|------|-------------|
|
|
59
|
-
| `schema` | `z.ZodObject` | Zod schema defining the object shape |
|
|
84
|
+
| `schema` | `z.ZodObject` \| `z.ZodUnion` \| `z.ZodDiscriminatedUnion` \| `z.ZodIntersection` \| `z.ZodLazy` | Zod schema defining the object shape |
|
|
60
85
|
|
|
61
86
|
**Members generated for each schema field `foo`:**
|
|
62
87
|
|
|
63
88
|
| Member | Type | Description |
|
|
64
89
|
|--------|------|-------------|
|
|
65
|
-
| `foo` | getter | Exposes the current validated field value |
|
|
90
|
+
| `foo` | getter | Exposes the current validated field value (deep clone for objects and arrays) |
|
|
66
91
|
| `setFoo(value)` | `(value: T) => void` | Validates, updates state, and records patches only when the value actually changes |
|
|
67
92
|
|
|
93
|
+
`set*` method names follow camelCase with the field name capitalized (`name` → `setName`, `userId` → `setUserId`).
|
|
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 field extras** (for each `z.record(...)` field `tags`):
|
|
103
|
+
|
|
104
|
+
| Member | Type | Description |
|
|
105
|
+
|--------|------|-------------|
|
|
106
|
+
| `getTagsEntry(key)` | `(key: string) => V \| undefined` | Reads a single record 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 a record entry |
|
|
109
|
+
|
|
68
110
|
**Instance members:**
|
|
69
111
|
|
|
70
112
|
| Member | Type | Description |
|
|
71
113
|
|--------|------|-------------|
|
|
72
|
-
| `operations` | `readonly Operation[]` | Chronological RFC 6902 patch log |
|
|
114
|
+
| `operations` | `readonly Operation[]` | Chronological RFC 6902 patch log (defensive copy) |
|
|
73
115
|
| `clearOperations()` | `() => void` | Clears the patch log without rolling back state |
|
|
74
|
-
|
|
75
|
-
`set*` method names follow camelCase with the field name capitalized (`name` → `setName`, `address` → `setAddress`).
|
|
116
|
+
| `toJSON()` | `() => T` | Deep clone of current state, safe for `JSON.stringify` |
|
|
76
117
|
|
|
77
118
|
**Static members:**
|
|
78
119
|
|
|
79
120
|
| Member | Type | Description |
|
|
80
121
|
|--------|------|-------------|
|
|
81
|
-
| `fromOperations(initial, operations)` | `(initial, Operation[]) => Instance` | Builds an instance from a baseline, replays operations, and copies them into the accumulator |
|
|
122
|
+
| `fromOperations(initial, operations)` | `(initial, Operation[]) => Instance` | Builds an instance from a baseline, replays and validates operations, and copies them into the accumulator |
|
|
123
|
+
|
|
124
|
+
### `SmartObjectError`
|
|
125
|
+
|
|
126
|
+
Structured error thrown on validation failures, invalid union field access, and failed replay:
|
|
127
|
+
|
|
128
|
+
| Property | Type | Description |
|
|
129
|
+
|----------|------|-------------|
|
|
130
|
+
| `code` | `"InvalidValue"` \| `"InvalidUnionField"` \| `"InvalidReplay"` \| `"UnsupportedSchema"` | Error category |
|
|
131
|
+
| `field` | `string` \| `undefined` | Schema field path when applicable |
|
|
132
|
+
| `cause` | `unknown` | Original error (e.g. `ZodError`) |
|
|
82
133
|
|
|
83
134
|
### `Operation`
|
|
84
135
|
|
|
@@ -93,8 +144,24 @@ RFC 6902 operation emitted by [fast-json-patch](https://github.com/Starcounter-J
|
|
|
93
144
|
|
|
94
145
|
- `Operation` — JSON Patch operation (re-export from `fast-json-patch`)
|
|
95
146
|
- `SetMethods<T>` — mapped type of inferred `set*` methods for shape `T`
|
|
147
|
+
- `SetMethodsUnion<T>` — `set*` methods for union root schemas
|
|
148
|
+
- `AllKeys<T>` — all keys across union members
|
|
149
|
+
- `UnionDataShape<U>` — flattened data shape for union roots
|
|
150
|
+
- `VariantSwitchMethods<T>` — `switchVariant` for union roots
|
|
151
|
+
- `DiscriminatedVariantSwitchMethods<T, D>` — `switchVariant` plus generated `switchTo*` methods
|
|
152
|
+
- `RecordFieldMethods<T>` — dynamic entry accessors for `z.record` fields
|
|
96
153
|
- `OperationsAccessor` — `operations` and `clearOperations()`
|
|
154
|
+
- `SnapshotAccessor<T>` — `toJSON()`
|
|
97
155
|
- `SmartObjectConstructor<T>` — constructor type including `fromOperations`
|
|
156
|
+
- `SmartObjectInstance<T>` — full instance type (getters + set* + operations + toJSON)
|
|
157
|
+
|
|
158
|
+
## Limitations
|
|
159
|
+
|
|
160
|
+
- **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.
|
|
161
|
+
- **Union field on wrong variant** — Setting a field that does not exist on the active variant throws `SmartObjectError`.
|
|
162
|
+
- **Date fields** — `z.date()` and `z.coerce.date()` are supported; operations store ISO 8601 strings while getters return `Date` instances.
|
|
163
|
+
- **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. Whole-field replace is used for Map/Set updates. Non-string map keys are not supported.
|
|
164
|
+
- **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.
|
|
98
165
|
|
|
99
166
|
## Design rationale
|
|
100
167
|
|
|
@@ -103,25 +170,66 @@ RFC 6902 operation emitted by [fast-json-patch](https://github.com/Starcounter-J
|
|
|
103
170
|
3. **No-op writes** — Identical values are skipped to keep the patch log minimal and suitable for network sync.
|
|
104
171
|
4. **Patch-based updates** — Changes are expressed as RFC 6902 operations so deltas are standard, composable, and replayable.
|
|
105
172
|
5. **Operation accumulation** — Patches from `compare` are appended in order, preserving causality for audit and replay.
|
|
106
|
-
6. **Replay** — `fromOperations(initial, operations)` requires the same baseline used when the operations were produced
|
|
107
|
-
|
|
108
|
-
- `SmartObjectInstance<T>` — full instance type (getters + set* + operations)
|
|
173
|
+
6. **Replay** — `fromOperations(initial, operations)` replays patches, re-validates with Zod, and requires the same baseline used when the operations were produced.
|
|
109
174
|
|
|
110
|
-
##
|
|
175
|
+
## Examples
|
|
111
176
|
|
|
112
|
-
|
|
177
|
+
- [`examples/person.ts`](examples/person.ts) — primitives, nested objects, and arrays
|
|
178
|
+
- [`examples/event.ts`](examples/event.ts) — discriminated union root
|
|
179
|
+
- [`examples/profile.ts`](examples/profile.ts) — generic union root
|
|
113
180
|
|
|
114
181
|
## Project structure
|
|
115
182
|
|
|
116
183
|
```
|
|
117
184
|
smart-object/
|
|
118
185
|
├── src/
|
|
119
|
-
│ ├── index.ts
|
|
120
|
-
│ ├──
|
|
121
|
-
│
|
|
186
|
+
│ ├── index.ts # Public API barrel export
|
|
187
|
+
│ ├── types.ts # Operation and inferred types
|
|
188
|
+
│ ├── errors.ts # SmartObjectError
|
|
189
|
+
│ ├── zod-introspect.ts # Zod schema introspection
|
|
190
|
+
│ └── smart-object/
|
|
191
|
+
│ ├── index.ts # Re-export SmartObject
|
|
192
|
+
│ ├── factory.ts # Public SmartObject() factory
|
|
193
|
+
│ ├── build-class.ts # Class generation orchestration
|
|
194
|
+
│ ├── instance-state.ts # WeakMap-backed instance storage
|
|
195
|
+
│ ├── read-field.ts # Defensive getter reads
|
|
196
|
+
│ ├── json-patch.ts # fast-json-patch wrapper + Date-safe deepClone
|
|
197
|
+
│ ├── codecs.ts # ISO 8601 serialization for date fields
|
|
198
|
+
│ ├── apply-operations.ts # Replay and rollback
|
|
199
|
+
│ ├── union-variant.ts # Union variant matching
|
|
200
|
+
│ ├── define-prototype.ts # Getter/setter prototype setup
|
|
201
|
+
│ └── setters/
|
|
202
|
+
│ ├── object-field.ts
|
|
203
|
+
│ ├── union-field.ts
|
|
204
|
+
│ ├── variant-switch.ts
|
|
205
|
+
│ └── record-field.ts
|
|
122
206
|
├── examples/
|
|
123
|
-
│
|
|
207
|
+
│ ├── person.ts
|
|
208
|
+
│ ├── event.ts
|
|
209
|
+
│ └── profile.ts
|
|
124
210
|
├── tests/
|
|
125
|
-
│
|
|
126
|
-
|
|
211
|
+
│ ├── fixtures/
|
|
212
|
+
│ │ ├── person.ts
|
|
213
|
+
│ │ ├── entity.ts
|
|
214
|
+
│ │ ├── event.ts
|
|
215
|
+
│ │ └── profile.ts
|
|
216
|
+
│ ├── smart-object/
|
|
217
|
+
│ │ ├── construction.test.ts
|
|
218
|
+
│ │ ├── getters.test.ts
|
|
219
|
+
│ │ ├── setters.test.ts
|
|
220
|
+
│ │ ├── clear-operations.test.ts
|
|
221
|
+
│ │ ├── from-operations.test.ts
|
|
222
|
+
│ │ ├── union-fields.test.ts
|
|
223
|
+
│ │ ├── discriminated-union-root.test.ts
|
|
224
|
+
│ │ ├── generic-union-root.test.ts
|
|
225
|
+
│ │ ├── robustness.test.ts
|
|
226
|
+
│ │ ├── setter-naming.test.ts
|
|
227
|
+
│ │ ├── to-json.test.ts
|
|
228
|
+
│ │ ├── schema-variants.test.ts
|
|
229
|
+
│ │ ├── record-fields.test.ts
|
|
230
|
+
│ │ ├── date-codec.test.ts
|
|
231
|
+
│ │ ├── intersection-lazy.test.ts
|
|
232
|
+
│ │ └── types.test.ts
|
|
233
|
+
│ └── zod-introspect.test.ts
|
|
234
|
+
└── dist/ # Build output (generated)
|
|
127
235
|
```
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type SmartObjectErrorCode = "InvalidValue" | "InvalidUnionField" | "InvalidReplay" | "UnsupportedSchema";
|
|
2
|
+
/**
|
|
3
|
+
* Structured error for SmartObject operations — enables programmatic handling
|
|
4
|
+
* without parsing generic Error messages.
|
|
5
|
+
*/
|
|
6
|
+
export declare class SmartObjectError extends Error {
|
|
7
|
+
readonly code: SmartObjectErrorCode;
|
|
8
|
+
readonly field?: string;
|
|
9
|
+
readonly cause?: unknown;
|
|
10
|
+
constructor(code: SmartObjectErrorCode, message: string, options?: {
|
|
11
|
+
field?: string;
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
});
|
|
14
|
+
static invalidValue(field: string, cause: unknown): SmartObjectError;
|
|
15
|
+
static invalidUnionField(field: string, message?: string): SmartObjectError;
|
|
16
|
+
static invalidUnionState(): SmartObjectError;
|
|
17
|
+
static invalidReplay(cause: unknown): SmartObjectError;
|
|
18
|
+
static unsupportedSchema(message: string): SmartObjectError;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAC5B,cAAc,GACd,mBAAmB,GACnB,eAAe,GACf,mBAAmB,CAAC;AAExB;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;gBAGvB,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;IAS/C,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,gBAAgB;IAapE,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,gBAAgB;IAQ3E,MAAM,CAAC,iBAAiB,IAAI,gBAAgB;IAI5C,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB;IAStD,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB;CAG5D"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error for SmartObject operations — enables programmatic handling
|
|
3
|
+
* without parsing generic Error messages.
|
|
4
|
+
*/
|
|
5
|
+
export class SmartObjectError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
field;
|
|
8
|
+
cause;
|
|
9
|
+
constructor(code, message, options) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "SmartObjectError";
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.field = options?.field;
|
|
14
|
+
this.cause = options?.cause;
|
|
15
|
+
}
|
|
16
|
+
static invalidValue(field, cause) {
|
|
17
|
+
if (cause instanceof SmartObjectError) {
|
|
18
|
+
return cause;
|
|
19
|
+
}
|
|
20
|
+
const message = cause instanceof Error && "issues" in cause
|
|
21
|
+
? `Invalid value for "${field}": ${cause.issues.map((i) => i.message).join(", ")}`
|
|
22
|
+
: `Invalid value for "${field}"`;
|
|
23
|
+
return new SmartObjectError("InvalidValue", message, { field, cause });
|
|
24
|
+
}
|
|
25
|
+
static invalidUnionField(field, message) {
|
|
26
|
+
return new SmartObjectError("InvalidUnionField", message ?? `Cannot set "${field}" on the active union variant`, { field });
|
|
27
|
+
}
|
|
28
|
+
static invalidUnionState() {
|
|
29
|
+
return new SmartObjectError("InvalidUnionField", "Cannot set field on invalid union state");
|
|
30
|
+
}
|
|
31
|
+
static invalidReplay(cause) {
|
|
32
|
+
const message = cause instanceof Error
|
|
33
|
+
? `Operation replay failed: ${cause.message}`
|
|
34
|
+
: "Operation replay failed";
|
|
35
|
+
return new SmartObjectError("InvalidReplay", message, { cause });
|
|
36
|
+
}
|
|
37
|
+
static unsupportedSchema(message) {
|
|
38
|
+
return new SmartObjectError("UnsupportedSchema", message);
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
1
|
+
export type { SmartObjectErrorCode } from "./errors.js";
|
|
2
|
+
export { SmartObjectError } from "./errors.js";
|
|
3
|
+
export { SmartObject } from "./smart-object/index.js";
|
|
4
|
+
export type { AllKeys, DiscriminatedVariantSwitchMethods, Operation, OperationsAccessor, RecordFieldMethods, SetMethods, SetMethodsUnion, SmartObjectConstructor, SmartObjectInstance, SmartObjectSchema, SnapshotAccessor, UnionDataShape, VariantSwitchMethods, } from "./types.js";
|
|
3
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,OAAO,EAAE,WAAW,EAAE,MAAM,
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { SmartObjectError } from "./errors.js";
|
|
2
|
+
export { SmartObject } from "./smart-object/index.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Operation } from "fast-json-patch";
|
|
2
|
+
import type { SmartObjectSchema } from "../types.js";
|
|
3
|
+
import type { InstanceState } from "./instance-state.js";
|
|
4
|
+
export declare function applyOperations<T>(state: InstanceState<T>, zodSchema: SmartObjectSchema, instance: object, operations: Operation[]): void;
|
|
5
|
+
//# sourceMappingURL=apply-operations.d.ts.map
|
|
@@ -0,0 +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;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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SmartObjectError } from "../errors.js";
|
|
2
|
+
import { deserializeDataFromPatch } from "./codecs.js";
|
|
3
|
+
import { applyPatch, deepClone } from "./json-patch.js";
|
|
4
|
+
export function applyOperations(state, zodSchema, instance, operations) {
|
|
5
|
+
if (operations.length === 0) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const data = state.getData(instance);
|
|
9
|
+
const snapshot = deepClone(data);
|
|
10
|
+
try {
|
|
11
|
+
applyPatch(data, operations, false, true);
|
|
12
|
+
const deserialized = deserializeDataFromPatch(data, zodSchema);
|
|
13
|
+
const validated = zodSchema.parse(deserialized);
|
|
14
|
+
state.setData(instance, validated);
|
|
15
|
+
state.getOperations(instance).push(...operations);
|
|
16
|
+
}
|
|
17
|
+
catch (cause) {
|
|
18
|
+
state.setData(instance, snapshot);
|
|
19
|
+
throw SmartObjectError.invalidReplay(cause);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +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,CAqC3B"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getSchemaShapeKeys } from "../zod-introspect.js";
|
|
2
|
+
import { applyOperations } from "./apply-operations.js";
|
|
3
|
+
import { definePrototype } from "./define-prototype.js";
|
|
4
|
+
import { createInstanceState } from "./instance-state.js";
|
|
5
|
+
import { deepClone } from "./json-patch.js";
|
|
6
|
+
export function buildSmartObjectClass(zodSchema) {
|
|
7
|
+
const keys = getSchemaShapeKeys(zodSchema);
|
|
8
|
+
const state = createInstanceState();
|
|
9
|
+
class SmartObjectClass {
|
|
10
|
+
static fromOperations(initial, operations) {
|
|
11
|
+
const instance = new SmartObjectClass(initial);
|
|
12
|
+
applyOperations(state, zodSchema, instance, operations);
|
|
13
|
+
return instance;
|
|
14
|
+
}
|
|
15
|
+
get operations() {
|
|
16
|
+
return [...state.getOperations(this)];
|
|
17
|
+
}
|
|
18
|
+
clearOperations() {
|
|
19
|
+
state.getOperations(this).length = 0;
|
|
20
|
+
}
|
|
21
|
+
toJSON() {
|
|
22
|
+
return deepClone(state.getData(this));
|
|
23
|
+
}
|
|
24
|
+
constructor(initial) {
|
|
25
|
+
state.setData(this, zodSchema.parse(initial ?? {}));
|
|
26
|
+
state.initOperations(this);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
definePrototype(SmartObjectClass.prototype, state, zodSchema, keys);
|
|
30
|
+
return SmartObjectClass;
|
|
31
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SmartObjectSchema } from "../types.js";
|
|
2
|
+
import type { InstanceState } from "./instance-state.js";
|
|
3
|
+
export declare function definePrototype<T>(prototype: object, state: InstanceState<T>, zodSchema: SmartObjectSchema, keys: string[]): void;
|
|
4
|
+
//# sourceMappingURL=define-prototype.d.ts.map
|
|
@@ -0,0 +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;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"}
|