zod-nest 2.1.2 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -531,11 +531,14 @@ See [`docs/recipes/custom-openapi-overrides.md`](docs/recipes/custom-openapi-ove
531
531
  | ------------------------- | ------ | ---------------- | -------------- | ----------------- | ------ | ------------------ |
532
532
  | `floor` | 4.4.0 | 11.0.1 | 11.0.1 | 11.0.0 | 7.6.0 | 0.2.0 |
533
533
  | `zod-latest` | latest | 11.0.1 | 11.0.1 | 11.0.0 | 7.6.0 | 0.2.0 |
534
+ | `zod-4.5` | 4.5.4 | 11.0.1 | 11.0.1 | 11.0.0 | 7.6.0 | 0.2.0 |
534
535
  | `nest-latest` | 4.4.0 | latest | latest | latest | 7.6.0 | 0.2.0 |
535
536
  | `rxjs-latest` | 4.4.0 | 11.0.1 | 11.0.1 | 11.0.0 | latest | 0.2.0 |
536
537
  | `reflect-metadata-latest` | 4.4.0 | 11.0.1 | 11.0.1 | 11.0.0 | 7.6.0 | latest |
537
538
  | `all-latest` | latest | latest | latest | latest | latest | latest |
538
539
 
540
+ One emission difference is visible across the supported `zod` range: from 4.5, a union of primitives — including `.nullable()` — emits `{ "type": ["string", "null"] }` where 4.4 emitted `{ "anyOf": [{ "type": "string" }, { "type": "null" }] }`. Both are valid OpenAPI 3.1 and validate identically; only the spelling changes. Everything else — `$ref` layout, composition `allOf`, input/output siblings, `paths` — is byte-identical across 4.4 and 4.5.
541
+
539
542
  Cell definitions live in [`.github/compat-matrix.json`](.github/compat-matrix.json). The CI workflow ([`.github/workflows/compat-matrix.yml`](.github/workflows/compat-matrix.yml)) runs on every push to `main` and weekly on Monday — when a cell fails, the workflow opens (or comments on) a GitHub issue labelled `compat-matrix-failure` so the regression is tracked outside the Actions UI. Editing the JSON is the formal way to extend or shrink supported ranges. Node is not matrixed — the `>=22` floor is enforced by `engines`.
540
543
 
541
544
  ## Migration from `nestjs-zod`
package/dist/index.js CHANGED
@@ -419,10 +419,10 @@ var combine = /* @__PURE__ */ __name((...overrides) => {
419
419
  }, "combine");
420
420
 
421
421
  // src/schema/post-process.ts
422
- var rewriteRefs = /* @__PURE__ */ __name((node, selfRef) => {
422
+ var rewriteRefs = /* @__PURE__ */ __name((node, rootRef) => {
423
423
  if (Array.isArray(node)) {
424
424
  for (const item of node) {
425
- rewriteRefs(item, selfRef);
425
+ rewriteRefs(item, rootRef);
426
426
  }
427
427
  return;
428
428
  }
@@ -433,32 +433,120 @@ var rewriteRefs = /* @__PURE__ */ __name((node, selfRef) => {
433
433
  const ref = obj.$ref;
434
434
  if (typeof ref === "string" && ref.startsWith(DEFS_PREFIX)) {
435
435
  obj.$ref = COMPONENTS_SCHEMAS_PREFIX + ref.slice(DEFS_PREFIX.length);
436
- } else if (ref === "#" && selfRef !== void 0) {
437
- obj.$ref = selfRef;
436
+ } else if (ref === "#" && rootRef !== void 0) {
437
+ obj.$ref = rootRef;
438
438
  }
439
439
  for (const value of Object.values(obj)) {
440
- rewriteRefs(value, selfRef);
440
+ rewriteRefs(value, rootRef);
441
441
  }
442
442
  }, "rewriteRefs");
443
- var postProcess = /* @__PURE__ */ __name((raw) => {
444
- const refs = /* @__PURE__ */ new Map();
445
- const rawDefs = raw.$defs;
446
- if (rawDefs !== void 0) {
447
- for (const [id, body] of Object.entries(rawDefs)) {
448
- refs.set(id, body);
443
+ var refsIn = /* @__PURE__ */ __name(function* (node) {
444
+ if (Array.isArray(node)) {
445
+ for (const item of node) {
446
+ yield* refsIn(item);
449
447
  }
448
+ return;
449
+ }
450
+ if (node === null || typeof node !== "object") {
451
+ return;
452
+ }
453
+ const obj = node;
454
+ if (typeof obj.$ref === "string") {
455
+ yield obj.$ref;
450
456
  }
457
+ for (const value of Object.values(obj)) {
458
+ yield* refsIn(value);
459
+ }
460
+ }, "refsIn");
461
+ var rootDefId = /* @__PURE__ */ __name((raw) => {
462
+ const ref = raw.$ref;
463
+ if (typeof ref !== "string" || !ref.startsWith(DEFS_PREFIX)) {
464
+ return void 0;
465
+ }
466
+ return ref.slice(DEFS_PREFIX.length);
467
+ }, "rootDefId");
468
+ var isReferenced = /* @__PURE__ */ __name((defs, id) => {
469
+ const ownRef = `${DEFS_PREFIX}${id}`;
470
+ for (const ref of refsIn(defs)) {
471
+ if (ref === "#" || ref === ownRef) {
472
+ return true;
473
+ }
474
+ }
475
+ return false;
476
+ }, "isReferenced");
477
+ var referencesRoot = /* @__PURE__ */ __name((raw) => {
478
+ for (const ref of refsIn(raw)) {
479
+ if (ref === "#") {
480
+ return true;
481
+ }
482
+ }
483
+ return false;
484
+ }, "referencesRoot");
485
+ var stripEnvelope = /* @__PURE__ */ __name((raw) => {
451
486
  const root = {
452
487
  ...raw
453
488
  };
454
489
  delete root.$schema;
455
490
  delete root.$defs;
456
- rewriteRefs(root, void 0);
457
- for (const [id, body] of refs) {
458
- rewriteRefs(body, `${COMPONENTS_SCHEMAS_PREFIX}${id}`);
491
+ return root;
492
+ }, "stripEnvelope");
493
+ var resolveRoot = /* @__PURE__ */ __name((raw, refs, options) => {
494
+ const liftedId = rootDefId(raw);
495
+ if (liftedId !== void 0) {
496
+ const body = refs.get(liftedId);
497
+ if (body !== void 0 && !isReferenced(raw.$defs, liftedId)) {
498
+ refs.delete(liftedId);
499
+ return {
500
+ schema: body,
501
+ rootRef: void 0
502
+ };
503
+ }
504
+ return {
505
+ schema: stripEnvelope(raw),
506
+ rootRef: `${COMPONENTS_SCHEMAS_PREFIX}${liftedId}`
507
+ };
459
508
  }
509
+ const root = stripEnvelope(raw);
510
+ if (!referencesRoot(raw)) {
511
+ return {
512
+ schema: root,
513
+ rootRef: void 0
514
+ };
515
+ }
516
+ const rootId = options?.rootId;
517
+ if (rootId === void 0) {
518
+ if (options?.strict ?? true) {
519
+ throw new ZodNestError('A schema referenced from inside its own emission has no id, so the reference cannot be resolved. Add `.meta({ id: "\u2026" })` to the root schema, or set strict: false to leave the reference unresolved.');
520
+ }
521
+ return {
522
+ schema: root,
523
+ rootRef: void 0
524
+ };
525
+ }
526
+ refs.set(rootId, root);
527
+ const rootRef = `${COMPONENTS_SCHEMAS_PREFIX}${rootId}`;
460
528
  return {
461
- schema: root,
529
+ schema: {
530
+ $ref: rootRef
531
+ },
532
+ rootRef
533
+ };
534
+ }, "resolveRoot");
535
+ var postProcess = /* @__PURE__ */ __name((raw, options) => {
536
+ const refs = /* @__PURE__ */ new Map();
537
+ const rawDefs = raw.$defs;
538
+ if (rawDefs !== void 0) {
539
+ for (const [id, body] of Object.entries(rawDefs)) {
540
+ refs.set(id, body);
541
+ }
542
+ }
543
+ const { schema, rootRef } = resolveRoot(raw, refs, options);
544
+ rewriteRefs(schema, rootRef);
545
+ for (const body of refs.values()) {
546
+ rewriteRefs(body, rootRef);
547
+ }
548
+ return {
549
+ schema,
462
550
  refs
463
551
  };
464
552
  }, "postProcess");
@@ -545,6 +633,10 @@ var buildToJsonSchemaOptions = /* @__PURE__ */ __name((params) => {
545
633
  }, "consumeUnrepresentable")
546
634
  };
547
635
  }, "buildToJsonSchemaOptions");
636
+ var resolveRootId = /* @__PURE__ */ __name((schema, registry) => {
637
+ const id = registry.zodRegistry.get(schema)?.id;
638
+ return typeof id === "string" && id !== "" ? id : void 0;
639
+ }, "resolveRootId");
548
640
  var toOpenApi = /* @__PURE__ */ __name((schema, opts) => {
549
641
  const built = buildToJsonSchemaOptions({
550
642
  registry: opts.registry,
@@ -555,7 +647,10 @@ var toOpenApi = /* @__PURE__ */ __name((schema, opts) => {
555
647
  });
556
648
  const raw = zod.z.toJSONSchema(schema, built.options);
557
649
  built.consumeUnrepresentable();
558
- const result = postProcess(raw);
650
+ const result = postProcess(raw, {
651
+ rootId: resolveRootId(schema, opts.registry),
652
+ strict: opts.strict
653
+ });
559
654
  for (const [id] of opts.registry.getCollisions()) {
560
655
  if (!result.refs.has(id)) {
561
656
  continue;