zodbridge 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,786 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ // src/map/rules.ts
6
+ function fromResolver(field) {
7
+ return { __kind: "fromResolver", field };
8
+ }
9
+ function withDefault(value, source) {
10
+ return { __kind: "default", value, source };
11
+ }
12
+ function isStringRule(rule) {
13
+ return typeof rule === "string";
14
+ }
15
+ function isFromResolverRule(rule) {
16
+ return typeof rule === "object" && rule !== null && rule.__kind === "fromResolver";
17
+ }
18
+ function isPairRule(rule) {
19
+ return typeof rule === "object" && rule !== null && typeof rule.to === "function" && typeof rule.from === "function";
20
+ }
21
+ function isDefaultRule(rule) {
22
+ return typeof rule === "object" && rule !== null && rule.__kind === "default";
23
+ }
24
+ function isFnRule(rule) {
25
+ return typeof rule === "function";
26
+ }
27
+ var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
28
+ function isForbidden(key) {
29
+ return FORBIDDEN_KEYS.has(key);
30
+ }
31
+ function splitPath(path) {
32
+ return path.split(".");
33
+ }
34
+ function getPath(source, path) {
35
+ let current = source;
36
+ for (const segment of splitPath(path)) {
37
+ if (isForbidden(segment)) return void 0;
38
+ if (current === null || current === void 0) return void 0;
39
+ if (typeof current !== "object") return void 0;
40
+ if (!Object.prototype.hasOwnProperty.call(current, segment)) return void 0;
41
+ current = current[segment];
42
+ }
43
+ return current;
44
+ }
45
+ function setPath(target, path, value) {
46
+ const segments = splitPath(path);
47
+ let current = target;
48
+ for (let i = 0; i < segments.length; i++) {
49
+ const segment = segments[i];
50
+ if (isForbidden(segment)) return;
51
+ if (i === segments.length - 1) {
52
+ current[segment] = value;
53
+ return;
54
+ }
55
+ const next = current[segment];
56
+ if (typeof next !== "object" || next === null) {
57
+ const fresh = {};
58
+ current[segment] = fresh;
59
+ current = fresh;
60
+ } else {
61
+ current = next;
62
+ }
63
+ }
64
+ }
65
+
66
+ // src/resolver/context.ts
67
+ var EMPTY_ANCESTORS = /* @__PURE__ */ new Set();
68
+ function defer() {
69
+ let resolve;
70
+ let reject;
71
+ const promise = new Promise((res, rej) => {
72
+ resolve = res;
73
+ reject = rej;
74
+ });
75
+ return { promise, resolve, reject };
76
+ }
77
+ var GraphContext = class {
78
+ adapter;
79
+ seed;
80
+ resolvers;
81
+ schemas;
82
+ onResolve;
83
+ /** Durable promise-cache: every attempted field lives here. */
84
+ cache = /* @__PURE__ */ new Map();
85
+ /** Synchronously-readable settled values, for `get`/`path` peeks. */
86
+ settled = /* @__PURE__ */ new Map();
87
+ /** Fields with an in-flight resolver; transient cycle guard. */
88
+ inProgress = /* @__PURE__ */ new Set();
89
+ /** Fields explicitly invalidated: prefer the resolver over seed on next resolve. */
90
+ invalidated = /* @__PURE__ */ new Set();
91
+ constructor(config) {
92
+ this.adapter = config.adapter;
93
+ this.seed = config.seed ?? {};
94
+ this.resolvers = config.resolvers ?? {};
95
+ this.schemas = config.schemas ?? {};
96
+ this.onResolve = config.onResolve;
97
+ }
98
+ /** Synchronous peek of seed first, then any settled resolved value. */
99
+ peek(field) {
100
+ const seeded = this.seed[field];
101
+ if (seeded !== void 0) return seeded;
102
+ return this.settled.get(field);
103
+ }
104
+ get(field) {
105
+ return this.peek(field);
106
+ }
107
+ path(field, keyPath) {
108
+ const base = this.peek(field);
109
+ if (base === void 0) return void 0;
110
+ if (keyPath.length === 0) return base;
111
+ return getPath(base, keyPath.join("."));
112
+ }
113
+ /** Public entry: a fresh resolution chain with no ancestors. */
114
+ resolve(field) {
115
+ return this.resolveWithin(field, EMPTY_ANCESTORS);
116
+ }
117
+ /**
118
+ * Resolve `field` within a chain whose currently-resolving fields are
119
+ * `ancestors`. If `field` is itself an ancestor, this is a dependency cycle
120
+ * (mutual A<->B, longer A->B->C->A, or self x->x): return `undefined` so the
121
+ * caller (e.g. a `strategies` candidate) falls through to its next route,
122
+ * instead of awaiting its own still-pending promise (which would deadlock).
123
+ *
124
+ * The ancestor check runs BEFORE the `cache.has` short-circuit, so a CONCURRENT
125
+ * caller (a parallel `Promise.all` chain — `field` is in-flight but NOT its
126
+ * ancestor) still receives the shared pending promise and dedups normally.
127
+ */
128
+ resolveWithin(field, ancestors) {
129
+ if (ancestors.has(field)) {
130
+ return Promise.resolve(void 0);
131
+ }
132
+ const cached = this.cache.get(field);
133
+ if (cached !== void 0) return cached;
134
+ const resolver = this.resolvers[field];
135
+ const preferResolver = this.invalidated.has(field) && resolver !== void 0;
136
+ const seeded = this.seed[field];
137
+ if (seeded !== void 0 && !preferResolver) {
138
+ const p2 = Promise.resolve(seeded);
139
+ this.cache.set(field, p2);
140
+ return p2;
141
+ }
142
+ if (resolver) {
143
+ this.invalidated.delete(field);
144
+ const deferred = defer();
145
+ this.cache.set(field, deferred.promise);
146
+ this.inProgress.add(field);
147
+ const childAncestors = new Set(ancestors).add(field);
148
+ this.runResolver(field, resolver, deferred, childAncestors);
149
+ return deferred.promise;
150
+ }
151
+ const p = Promise.resolve(void 0);
152
+ this.cache.set(field, p);
153
+ return p;
154
+ }
155
+ /**
156
+ * A context bound to `self` and the chain's `ancestors`: identical to the
157
+ * top-level context except `resolve`/`fallback` carry the chain so cycles
158
+ * break (see {@link resolveWithin}) and `fallback` retries the right field.
159
+ * Binding per invocation (not shared instance state) keeps concurrent
160
+ * resolutions from contaminating each other.
161
+ */
162
+ boundContext(self, ancestors) {
163
+ return {
164
+ adapter: this.adapter,
165
+ resolve: (field) => this.resolveWithin(field, ancestors),
166
+ resolveMany: (...fields) => this.resolveMany(...fields),
167
+ get: (field) => this.get(field),
168
+ path: (field, keyPath) => this.path(field, keyPath),
169
+ fallback: (deps) => this.fallbackFor(self, deps, ancestors),
170
+ invalidate: (...fields) => this.invalidate(...fields),
171
+ refresh: (field) => this.refresh(field)
172
+ };
173
+ }
174
+ invalidate(...fields) {
175
+ for (const field of fields) {
176
+ this.cache.delete(field);
177
+ this.settled.delete(field);
178
+ if (this.resolvers[field] !== void 0) this.invalidated.add(field);
179
+ }
180
+ return this;
181
+ }
182
+ refresh(field) {
183
+ this.invalidate(field);
184
+ return this.resolve(field);
185
+ }
186
+ /** Fire the best-effort `onResolve` observer; never let it break resolution. */
187
+ emitResolved(field, value) {
188
+ if (!this.onResolve) return;
189
+ try {
190
+ this.onResolve(field, value);
191
+ } catch {
192
+ }
193
+ }
194
+ runResolver(field, resolver, deferred, ancestors) {
195
+ const ctx = this.boundContext(field, ancestors);
196
+ Promise.resolve().then(() => resolver(ctx)).then((value) => this.validate(field, value)).then(
197
+ (value) => {
198
+ this.settled.set(field, value);
199
+ this.inProgress.delete(field);
200
+ this.emitResolved(field, value);
201
+ deferred.resolve(value);
202
+ },
203
+ (error) => {
204
+ this.cache.delete(field);
205
+ this.inProgress.delete(field);
206
+ deferred.reject(error);
207
+ }
208
+ );
209
+ }
210
+ /**
211
+ * Validate a resolver's output against its field schema (if any). A defined
212
+ * value is parsed (unknown keys stripped); a parse failure throws the ZodError,
213
+ * which the reject branch turns into an eviction. `undefined` is the forgiving
214
+ * absence terminal and is never validated.
215
+ */
216
+ async validate(field, value) {
217
+ const schema = this.schemas[field];
218
+ if (schema === void 0 || value === void 0) return value;
219
+ return await schema.parseAsync(value);
220
+ }
221
+ /** Resolve untouched `deps`, then retry `self`'s resolver if any produced a value. */
222
+ async fallbackFor(self, deps, ancestors) {
223
+ let resolvedAny = false;
224
+ for (const dep of deps) {
225
+ if (this.cache.has(dep) || this.inProgress.has(dep)) continue;
226
+ const value = await this.resolveWithin(dep, ancestors);
227
+ if (value) resolvedAny = true;
228
+ }
229
+ if (!resolvedAny) return void 0;
230
+ const resolver = this.resolvers[self];
231
+ return resolver(this.boundContext(self, ancestors));
232
+ }
233
+ /**
234
+ * Top-level `fallback` has no calling field, so there is nothing to retry.
235
+ * Resolvers always receive a field-bound context whose `fallback` does retry;
236
+ * this entry exists only to satisfy the public {@link ResolverContext} shape.
237
+ */
238
+ fallback(_deps) {
239
+ return Promise.resolve(void 0);
240
+ }
241
+ async resolveMany(...fields) {
242
+ const out = {};
243
+ for (const field of fields) {
244
+ const value = await this.resolve(field);
245
+ if (value !== void 0) out[field] = value;
246
+ }
247
+ return out;
248
+ }
249
+ };
250
+
251
+ // src/resolver/createResolver.ts
252
+ function createResolver(config) {
253
+ const schemas = config.maps ? buildSchemas(config.maps, config.fields) : config.schemas;
254
+ return new GraphContext({
255
+ adapter: config.adapter,
256
+ seed: config.seed,
257
+ resolvers: config.resolvers,
258
+ schemas,
259
+ onResolve: config.onResolve
260
+ });
261
+ }
262
+ function buildSchemas(maps, fields) {
263
+ const out = /* @__PURE__ */ Object.create(null);
264
+ for (const key of Object.keys(maps)) {
265
+ out[key] = maps[key].schema;
266
+ }
267
+ if (fields) {
268
+ const shape = fields.shape;
269
+ for (const key of Object.keys(shape)) {
270
+ out[key] = shape[key];
271
+ }
272
+ }
273
+ return out;
274
+ }
275
+
276
+ // src/map/case-convert.ts
277
+ var FORBIDDEN_KEYS2 = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
278
+ function splitWords(key) {
279
+ return key.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Za-z])(\d)/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter((w) => w.length > 0).map((w) => w.toLowerCase());
280
+ }
281
+ function toSnakeKey(key) {
282
+ const words = splitWords(key);
283
+ if (words.length === 0) return key;
284
+ return words.join("_");
285
+ }
286
+ function toCamelKey(key) {
287
+ const words = splitWords(key);
288
+ if (words.length === 0) return key;
289
+ return words.map((word, i) => i === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)).join("");
290
+ }
291
+ function isPlainObject(value) {
292
+ if (value === null || typeof value !== "object") return false;
293
+ const proto = Object.getPrototypeOf(value);
294
+ return proto === Object.prototype || proto === null;
295
+ }
296
+ function convert(value, keyFn) {
297
+ if (Array.isArray(value)) {
298
+ return value.map((item) => convert(item, keyFn));
299
+ }
300
+ if (isPlainObject(value)) {
301
+ const out = {};
302
+ for (const key of Object.keys(value)) {
303
+ const converted = keyFn(key);
304
+ if (FORBIDDEN_KEYS2.has(converted)) continue;
305
+ out[converted] = convert(value[key], keyFn);
306
+ }
307
+ return out;
308
+ }
309
+ return value;
310
+ }
311
+ function toSnakeCase(value) {
312
+ return convert(value, toSnakeKey);
313
+ }
314
+ function toCamelCase(value) {
315
+ return convert(value, toCamelKey);
316
+ }
317
+ var capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
318
+ function toKebabKey(key) {
319
+ const words = splitWords(key);
320
+ if (words.length === 0) return key;
321
+ return words.join("-");
322
+ }
323
+ function toPascalKey(key) {
324
+ const words = splitWords(key);
325
+ if (words.length === 0) return key;
326
+ return words.map(capitalize).join("");
327
+ }
328
+ function toConstantKey(key) {
329
+ const words = splitWords(key);
330
+ if (words.length === 0) return key;
331
+ return words.join("_").toUpperCase();
332
+ }
333
+ function toKebabCase(value) {
334
+ return convert(value, toKebabKey);
335
+ }
336
+ function toPascalCase(value) {
337
+ return convert(value, toPascalKey);
338
+ }
339
+ function toConstantCase(value) {
340
+ return convert(value, toConstantKey);
341
+ }
342
+
343
+ // src/map/map-ops.ts
344
+ function forwardMany(map, sources) {
345
+ return sources.map((s) => map.forward(s));
346
+ }
347
+ function reverseMany(map, dtos) {
348
+ return dtos.map((d) => map.reverse(d));
349
+ }
350
+ function safeForward(map, source) {
351
+ try {
352
+ return { success: true, data: map.forward(source) };
353
+ } catch (err) {
354
+ if (isZodError(err)) return { success: false, error: err };
355
+ throw err;
356
+ }
357
+ }
358
+ function isZodError(err) {
359
+ return typeof err === "object" && err !== null && err.name === "ZodError" && Array.isArray(err.issues);
360
+ }
361
+
362
+ // src/map/createMap.ts
363
+ var OneWayRuleError = class extends Error {
364
+ key;
365
+ constructor(key) {
366
+ super(
367
+ `Cannot reverse dest key "${key}": it is mapped by a one-way function rule. Use a { to, from } pair rule to make it reversible.`
368
+ );
369
+ this.name = "OneWayRuleError";
370
+ this.key = key;
371
+ }
372
+ };
373
+ var MapHasNoResolversError = class extends Error {
374
+ constructor() {
375
+ super(
376
+ "createMap.toResolver requires a resolver graph: pass `createMap(schema, rules, { resolvers })`."
377
+ );
378
+ this.name = "MapHasNoResolversError";
379
+ }
380
+ };
381
+ var ReverseKeyCollisionError = class extends Error {
382
+ constructor(sourceKey, a, b) {
383
+ super(
384
+ `Reverse-key collision: dest keys "${a}" and "${b}" both map back to source key "${sourceKey}". Disambiguate one of them.`
385
+ );
386
+ this.name = "ReverseKeyCollisionError";
387
+ }
388
+ };
389
+ function applyForwardRule(rule, destKey, source) {
390
+ if (rule === void 0) {
391
+ return getPath(source, destKey);
392
+ }
393
+ if (isStringRule(rule)) {
394
+ return getPath(source, rule);
395
+ }
396
+ if (isPairRule(rule)) {
397
+ return rule.to(source);
398
+ }
399
+ if (isDefaultRule(rule)) {
400
+ const read = getPath(source, rule.source ?? destKey);
401
+ return read === void 0 ? rule.value : read;
402
+ }
403
+ if (isFnRule(rule)) {
404
+ return rule(source);
405
+ }
406
+ return void 0;
407
+ }
408
+ function createMap(schema, rules = {}, options = {}) {
409
+ const shape = schema.shape;
410
+ const destKeys = Object.keys(shape);
411
+ const mapResolvers = options.resolvers;
412
+ const reverseTargets = /* @__PURE__ */ new Map();
413
+ for (const destKey of Object.keys(rules)) {
414
+ const rule = rules[destKey];
415
+ if (rule !== void 0 && isStringRule(rule)) {
416
+ const existing = reverseTargets.get(rule);
417
+ if (existing) throw new ReverseKeyCollisionError(rule, existing, destKey);
418
+ reverseTargets.set(rule, destKey);
419
+ }
420
+ }
421
+ function forward(source) {
422
+ const out = {};
423
+ for (const destKey of destKeys) {
424
+ const rule = rules[destKey];
425
+ if (isFromResolverRule(rule)) continue;
426
+ const value = applyForwardRule(rule, destKey, source);
427
+ if (value !== void 0) setPath(out, destKey, value);
428
+ }
429
+ return schema.parse(out);
430
+ }
431
+ function reverse(dto) {
432
+ const out = {};
433
+ const dtoObj = dto;
434
+ for (const destKey of Object.keys(rules)) {
435
+ const rule = rules[destKey];
436
+ if (rule === void 0) continue;
437
+ const destValue = getPath(dtoObj, destKey);
438
+ if (isStringRule(rule)) {
439
+ setPath(out, rule, destValue);
440
+ } else if (isPairRule(rule)) {
441
+ setPath(out, destKey, rule.from(destValue));
442
+ } else if (isDefaultRule(rule)) {
443
+ setPath(out, rule.source ?? destKey, destValue);
444
+ } else if (isFromResolverRule(rule)) {
445
+ continue;
446
+ } else {
447
+ throw new OneWayRuleError(destKey);
448
+ }
449
+ }
450
+ return out;
451
+ }
452
+ function toResolver(adapter, seed) {
453
+ if (!mapResolvers) {
454
+ throw new MapHasNoResolversError();
455
+ }
456
+ const schemas = {};
457
+ for (const key of Object.keys(shape)) {
458
+ schemas[key] = shape[key];
459
+ }
460
+ return createResolver({
461
+ adapter,
462
+ seed,
463
+ resolvers: mapResolvers,
464
+ schemas
465
+ });
466
+ }
467
+ const mapper = {
468
+ forward,
469
+ reverse,
470
+ forwardMany: (sources) => forwardMany(mapper, sources),
471
+ reverseMany: (dtos) => reverseMany(mapper, dtos),
472
+ safeForward: (source) => safeForward(mapper, source),
473
+ toSnakeCase,
474
+ toCamelCase,
475
+ toResolver,
476
+ schema,
477
+ rules: Object.freeze({ ...rules }),
478
+ shape,
479
+ resolvers: mapResolvers
480
+ };
481
+ return mapper;
482
+ }
483
+ function subObject(schema, keys) {
484
+ const shape = schema.shape;
485
+ const picked = {};
486
+ for (const key of keys) {
487
+ const field = shape[key];
488
+ if (field !== void 0) picked[key] = field;
489
+ }
490
+ return zod.z.object(picked);
491
+ }
492
+ function pick(map, keys) {
493
+ return createMap(subObject(map.schema, keys));
494
+ }
495
+ function omit(map, keys) {
496
+ const drop = new Set(keys);
497
+ const remaining = Object.keys(map.schema.shape).filter((k) => !drop.has(k));
498
+ return createMap(subObject(map.schema, remaining));
499
+ }
500
+ function compose(a, b) {
501
+ const base = createMap(b.schema);
502
+ const composed = { ...base };
503
+ composed.forward = (source) => b.forward(a.forward(source));
504
+ composed.reverse = (dto) => a.reverse(b.reverse(dto));
505
+ composed.forwardMany = (sources) => forwardMany(composed, sources);
506
+ composed.reverseMany = (dtos) => reverseMany(composed, dtos);
507
+ composed.safeForward = (source) => safeForward(composed, source);
508
+ return composed;
509
+ }
510
+
511
+ // src/serialize/json-types.ts
512
+ var CodecError = class extends Error {
513
+ /** Path to the offending value, e.g. `user.createdAt`. */
514
+ path;
515
+ constructor(message, path = []) {
516
+ const where = path.length ? ` (at ${path.join(".")})` : "";
517
+ super(`${message}${where}`);
518
+ this.name = "CodecError";
519
+ this.path = path;
520
+ }
521
+ };
522
+ var AsyncSchemaError = class extends Error {
523
+ constructor() {
524
+ super(
525
+ "deserialize is sync-only; this schema has async refinements. Remove async .refine()/.superRefine() or validate separately with parseAsync."
526
+ );
527
+ this.name = "AsyncSchemaError";
528
+ }
529
+ };
530
+ var LIMITS = {
531
+ /** Max characters in a BigInt wire string (sign + digits). */
532
+ bigintStringLength: 4096,
533
+ /** Max entries when rebuilding a Map from a wire array. */
534
+ mapEntries: 1e5
535
+ };
536
+ function dateToIso(value, path) {
537
+ if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
538
+ throw new CodecError("expected a valid Date", path);
539
+ }
540
+ return value.toISOString();
541
+ }
542
+ function isoToDate(value, path) {
543
+ if (typeof value !== "string") {
544
+ throw new CodecError("expected an ISO date string", path);
545
+ }
546
+ const ms = Date.parse(value);
547
+ if (Number.isNaN(ms)) {
548
+ throw new CodecError("invalid ISO date string", path);
549
+ }
550
+ return new Date(ms);
551
+ }
552
+ function bigintToString(value, path) {
553
+ if (typeof value !== "bigint") {
554
+ throw new CodecError("expected a bigint", path);
555
+ }
556
+ return value.toString();
557
+ }
558
+ function stringToBigint(value, path) {
559
+ if (typeof value !== "string") {
560
+ throw new CodecError("expected a bigint string", path);
561
+ }
562
+ if (value.length > LIMITS.bigintStringLength) {
563
+ throw new CodecError(
564
+ `bigint string exceeds ${LIMITS.bigintStringLength} chars`,
565
+ path
566
+ );
567
+ }
568
+ if (!/^-?\d+$/.test(value)) {
569
+ throw new CodecError("malformed bigint string", path);
570
+ }
571
+ return BigInt(value);
572
+ }
573
+ function mapToEntries(value, path) {
574
+ if (!(value instanceof Map)) {
575
+ throw new CodecError("expected a Map", path);
576
+ }
577
+ return Array.from(value.entries());
578
+ }
579
+ function entriesToArray(value, path) {
580
+ if (!Array.isArray(value)) {
581
+ throw new CodecError("expected a Map entries array", path);
582
+ }
583
+ if (value.length > LIMITS.mapEntries) {
584
+ throw new CodecError(`Map entries exceed ${LIMITS.mapEntries}`, path);
585
+ }
586
+ for (const entry of value) {
587
+ if (!Array.isArray(entry) || entry.length !== 2) {
588
+ throw new CodecError("malformed Map entry (expected [key, value])", path);
589
+ }
590
+ }
591
+ return value;
592
+ }
593
+ function setToArray(value, path) {
594
+ if (!(value instanceof Set)) {
595
+ throw new CodecError("expected a Set", path);
596
+ }
597
+ return Array.from(value.values());
598
+ }
599
+ function arrayToSetItems(value, path) {
600
+ if (!Array.isArray(value)) {
601
+ throw new CodecError("expected a Set values array", path);
602
+ }
603
+ if (value.length > LIMITS.mapEntries) {
604
+ throw new CodecError(`Set values exceed ${LIMITS.mapEntries}`, path);
605
+ }
606
+ return value;
607
+ }
608
+
609
+ // src/serialize/codec.ts
610
+ function defOf(schema) {
611
+ return schema._zod.def;
612
+ }
613
+ function unwrap(schema) {
614
+ let current = schema;
615
+ for (; ; ) {
616
+ const def = defOf(current);
617
+ switch (def.type) {
618
+ case "optional":
619
+ case "nullable":
620
+ case "default":
621
+ case "nonoptional":
622
+ case "readonly":
623
+ case "catch":
624
+ current = def.innerType;
625
+ break;
626
+ case "pipe":
627
+ current = def.in;
628
+ break;
629
+ case "lazy":
630
+ current = def.getter();
631
+ break;
632
+ default:
633
+ return current;
634
+ }
635
+ }
636
+ }
637
+ function walk(schema, value, dir, path) {
638
+ if (value === null || value === void 0) return value;
639
+ const node = unwrap(schema);
640
+ const def = defOf(node);
641
+ switch (def.type) {
642
+ case "object": {
643
+ if (typeof value !== "object" || Array.isArray(value)) {
644
+ throw new CodecError("expected an object", path);
645
+ }
646
+ const shape = def.shape;
647
+ const src = value;
648
+ const out = {};
649
+ for (const key of Object.keys(shape)) {
650
+ if (Object.prototype.hasOwnProperty.call(src, key)) {
651
+ out[key] = walk(shape[key], src[key], dir, [...path, key]);
652
+ }
653
+ }
654
+ return out;
655
+ }
656
+ case "array": {
657
+ if (!Array.isArray(value)) throw new CodecError("expected an array", path);
658
+ const element = def.element;
659
+ return value.map((item, i) => walk(element, item, dir, [...path, i]));
660
+ }
661
+ case "map": {
662
+ const keyType = def.keyType;
663
+ const valueType = def.valueType;
664
+ if (dir === "serialize") {
665
+ const entries2 = mapToEntries(value, path);
666
+ return entries2.map(([k, v], i) => [
667
+ walk(keyType, k, dir, [...path, i, "key"]),
668
+ walk(valueType, v, dir, [...path, i, "value"])
669
+ ]);
670
+ }
671
+ const entries = entriesToArray(value, path);
672
+ const out = /* @__PURE__ */ new Map();
673
+ entries.forEach(([k, v], i) => {
674
+ out.set(
675
+ walk(keyType, k, dir, [...path, i, "key"]),
676
+ walk(valueType, v, dir, [...path, i, "value"])
677
+ );
678
+ });
679
+ return out;
680
+ }
681
+ case "set": {
682
+ const valueType = def.valueType;
683
+ if (dir === "serialize") {
684
+ const items2 = setToArray(value, path);
685
+ return items2.map((item, i) => walk(valueType, item, dir, [...path, i]));
686
+ }
687
+ const items = arrayToSetItems(value, path);
688
+ return new Set(items.map((item, i) => walk(valueType, item, dir, [...path, i])));
689
+ }
690
+ case "union": {
691
+ const options = def.options;
692
+ let lastError = new CodecError("no union member matched", path);
693
+ for (const option of options) {
694
+ try {
695
+ return walk(option, value, dir, path);
696
+ } catch (err) {
697
+ if (!(err instanceof CodecError)) throw err;
698
+ lastError = err;
699
+ }
700
+ }
701
+ throw lastError;
702
+ }
703
+ case "date":
704
+ return dir === "serialize" ? dateToIso(value, path) : isoToDate(value, path);
705
+ case "bigint":
706
+ return dir === "serialize" ? bigintToString(value, path) : stringToBigint(value, path);
707
+ // Scalars and explicit escape hatches pass through untouched.
708
+ case "string":
709
+ case "number":
710
+ case "boolean":
711
+ case "nan":
712
+ case "null":
713
+ case "undefined":
714
+ case "literal":
715
+ case "enum":
716
+ case "any":
717
+ case "unknown":
718
+ case "void":
719
+ case "custom":
720
+ return value;
721
+ default:
722
+ throw new CodecError(`unsupported schema node "${def.type}"`, path);
723
+ }
724
+ }
725
+ function serialize(value, schema) {
726
+ return walk(schema, value, "serialize", []);
727
+ }
728
+ function isZodAsyncError(err) {
729
+ const name = err?.constructor?.name;
730
+ return name === "$ZodAsyncError";
731
+ }
732
+ function deserialize(json, schema) {
733
+ const rebuilt = walk(schema, json, "deserialize", []);
734
+ try {
735
+ return schema.parse(rebuilt);
736
+ } catch (err) {
737
+ if (isZodAsyncError(err)) throw new AsyncSchemaError();
738
+ throw err;
739
+ }
740
+ }
741
+ async function deserializeAsync(json, schema) {
742
+ const rebuilt = walk(schema, json, "deserialize", []);
743
+ return await schema.parseAsync(rebuilt);
744
+ }
745
+
746
+ // src/index.ts
747
+ var VERSION = "0.1.0";
748
+
749
+ exports.AsyncSchemaError = AsyncSchemaError;
750
+ exports.CodecError = CodecError;
751
+ exports.MapHasNoResolversError = MapHasNoResolversError;
752
+ exports.OneWayRuleError = OneWayRuleError;
753
+ exports.ReverseKeyCollisionError = ReverseKeyCollisionError;
754
+ exports.VERSION = VERSION;
755
+ exports.compose = compose;
756
+ exports.createMap = createMap;
757
+ exports.deserialize = deserialize;
758
+ exports.deserializeAsync = deserializeAsync;
759
+ exports.forwardMany = forwardMany;
760
+ exports.fromResolver = fromResolver;
761
+ exports.getPath = getPath;
762
+ exports.isDefaultRule = isDefaultRule;
763
+ exports.isFnRule = isFnRule;
764
+ exports.isFromResolverRule = isFromResolverRule;
765
+ exports.isPairRule = isPairRule;
766
+ exports.isStringRule = isStringRule;
767
+ exports.omit = omit;
768
+ exports.pick = pick;
769
+ exports.reverseMany = reverseMany;
770
+ exports.safeForward = safeForward;
771
+ exports.serialize = serialize;
772
+ exports.setPath = setPath;
773
+ exports.splitWords = splitWords;
774
+ exports.toCamelCase = toCamelCase;
775
+ exports.toCamelKey = toCamelKey;
776
+ exports.toConstantCase = toConstantCase;
777
+ exports.toConstantKey = toConstantKey;
778
+ exports.toKebabCase = toKebabCase;
779
+ exports.toKebabKey = toKebabKey;
780
+ exports.toPascalCase = toPascalCase;
781
+ exports.toPascalKey = toPascalKey;
782
+ exports.toSnakeCase = toSnakeCase;
783
+ exports.toSnakeKey = toSnakeKey;
784
+ exports.withDefault = withDefault;
785
+ //# sourceMappingURL=index.cjs.map
786
+ //# sourceMappingURL=index.cjs.map