zod-nest 2.0.1 → 2.1.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/dist/index.d.mts CHANGED
@@ -118,6 +118,15 @@ declare const registerSchema: (schema: z.ZodType, registry?: ZodNestRegistry, op
118
118
  interface LineageEntry {
119
119
  readonly op: 'extend';
120
120
  readonly parent: z.ZodObject;
121
+ /**
122
+ * Keys the child redeclared with a *different* schema than the parent's
123
+ * (e.g. narrowing `type: SomeEnum` to `type: z.literal('A')`). Computed by
124
+ * reference comparison at `extend()` time — Zod's `.extend()` keeps the same
125
+ * shape reference for untouched keys and assigns a fresh one for overrides.
126
+ * The composition override keeps these in the delta instead of dropping them
127
+ * as parent-owned, so the narrowing survives in the emitted `allOf`.
128
+ */
129
+ readonly overriddenKeys: ReadonlySet<string>;
121
130
  }
122
131
  /**
123
132
  * Wraps a derived `z.ZodObject` and records the parent → child link so
@@ -831,6 +840,18 @@ interface ApplyZodNestOptions {
831
840
  * `@ZodQuery({ ref })` override takes precedence over this preference.
832
841
  */
833
842
  queryParamStyle?: QueryParamStyle;
843
+ /**
844
+ * Copy each named component's `title` (when set via `.meta({ title })`) onto
845
+ * every `$ref` that targets it, as a sibling: `{ $ref, title }` (default
846
+ * `true`).
847
+ *
848
+ * OpenAPI 3.1 allows siblings next to `$ref`, and Swagger UI's 3.1 renderer
849
+ * inlines referenced schemas without showing their component name
850
+ * (swagger-api/swagger-ui#9540); the sibling `title` gives the renderer (and
851
+ * other 3.1-aware tools) a name to display. The annotation is semantically
852
+ * inert. Set `false` to emit bare `$ref`s.
853
+ */
854
+ refTitles?: boolean;
834
855
  }
835
856
  /**
836
857
  * Post-processor over the OpenAPI document emitted by
@@ -856,6 +877,9 @@ interface ApplyZodNestOptions {
856
877
  * transitive `$ref` deps, plus any `{ expose: true }` opt-ins) are kept —
857
878
  * unreferenced registered schemas are pruned. Exposure is document-scoped, so
858
879
  * several documents sharing one registry each carry only what they use.
880
+ * - Each named component's `title` is copied onto every `$ref` that targets it
881
+ * as a `{ $ref, title }` sibling (`applyRefTitles`, unless `refTitles: false`)
882
+ * so Swagger UI's 3.1 renderer surfaces the component name. Inert annotation.
859
883
  * - Every `$ref` whose target is missing throws `ZodNestDocumentError(DANGLING_REF)`.
860
884
  * - `doc.openapi` is set to `'3.1.0'` — zod-nest emits OpenAPI 3.1 only; this
861
885
  * guarantees the version string matches the emitted body regardless of the
package/dist/index.d.ts CHANGED
@@ -118,6 +118,15 @@ declare const registerSchema: (schema: z.ZodType, registry?: ZodNestRegistry, op
118
118
  interface LineageEntry {
119
119
  readonly op: 'extend';
120
120
  readonly parent: z.ZodObject;
121
+ /**
122
+ * Keys the child redeclared with a *different* schema than the parent's
123
+ * (e.g. narrowing `type: SomeEnum` to `type: z.literal('A')`). Computed by
124
+ * reference comparison at `extend()` time — Zod's `.extend()` keeps the same
125
+ * shape reference for untouched keys and assigns a fresh one for overrides.
126
+ * The composition override keeps these in the delta instead of dropping them
127
+ * as parent-owned, so the narrowing survives in the emitted `allOf`.
128
+ */
129
+ readonly overriddenKeys: ReadonlySet<string>;
121
130
  }
122
131
  /**
123
132
  * Wraps a derived `z.ZodObject` and records the parent → child link so
@@ -831,6 +840,18 @@ interface ApplyZodNestOptions {
831
840
  * `@ZodQuery({ ref })` override takes precedence over this preference.
832
841
  */
833
842
  queryParamStyle?: QueryParamStyle;
843
+ /**
844
+ * Copy each named component's `title` (when set via `.meta({ title })`) onto
845
+ * every `$ref` that targets it, as a sibling: `{ $ref, title }` (default
846
+ * `true`).
847
+ *
848
+ * OpenAPI 3.1 allows siblings next to `$ref`, and Swagger UI's 3.1 renderer
849
+ * inlines referenced schemas without showing their component name
850
+ * (swagger-api/swagger-ui#9540); the sibling `title` gives the renderer (and
851
+ * other 3.1-aware tools) a name to display. The annotation is semantically
852
+ * inert. Set `false` to emit bare `$ref`s.
853
+ */
854
+ refTitles?: boolean;
834
855
  }
835
856
  /**
836
857
  * Post-processor over the OpenAPI document emitted by
@@ -856,6 +877,9 @@ interface ApplyZodNestOptions {
856
877
  * transitive `$ref` deps, plus any `{ expose: true }` opt-ins) are kept —
857
878
  * unreferenced registered schemas are pruned. Exposure is document-scoped, so
858
879
  * several documents sharing one registry each carry only what they use.
880
+ * - Each named component's `title` is copied onto every `$ref` that targets it
881
+ * as a `{ $ref, title }` sibling (`applyRefTitles`, unless `refTitles: false`)
882
+ * so Swagger UI's 3.1 renderer surfaces the component name. Inert annotation.
859
883
  * - Every `$ref` whose target is missing throws `ZodNestDocumentError(DANGLING_REF)`.
860
884
  * - `doc.openapi` is set to `'3.1.0'` — zod-nest emits OpenAPI 3.1 only; this
861
885
  * guarantees the version string matches the emitted body regardless of the
package/dist/index.js CHANGED
@@ -246,11 +246,22 @@ var computeShapeKeys = /* @__PURE__ */ __name((schema) => {
246
246
  required
247
247
  };
248
248
  }, "computeShapeKeys");
249
+ var computeOverriddenKeys = /* @__PURE__ */ __name((parent, child) => {
250
+ const overriddenKeys = /* @__PURE__ */ new Set();
251
+ for (const key of Object.keys(child.shape)) {
252
+ const parentProp = parent.shape[key];
253
+ if (parentProp !== void 0 && parentProp !== child.shape[key]) {
254
+ overriddenKeys.add(key);
255
+ }
256
+ }
257
+ return overriddenKeys;
258
+ }, "computeOverriddenKeys");
249
259
  var extend = /* @__PURE__ */ __name((parent, build) => {
250
260
  const result = build(parent);
251
261
  lineageMap.set(result, {
252
262
  op: "extend",
253
- parent
263
+ parent,
264
+ overriddenKeys: computeOverriddenKeys(parent, result)
254
265
  });
255
266
  if (!propsMap.has(parent)) {
256
267
  propsMap.set(parent, computeShapeKeys(parent));
@@ -284,7 +295,7 @@ var createCompositionOverride = /* @__PURE__ */ __name((opts) => {
284
295
  const parentReqSet = new Set(parentCache.required);
285
296
  const deltaProps = {};
286
297
  for (const [key, value] of Object.entries(childProps)) {
287
- if (parentPropSet.has(key)) {
298
+ if (parentPropSet.has(key) && !entry.overriddenKeys.has(key)) {
288
299
  continue;
289
300
  }
290
301
  deltaProps[key] = value;
@@ -1334,7 +1345,7 @@ var ZodResponse = /* @__PURE__ */ __name((opts) => {
1334
1345
  }, "ZodResponse");
1335
1346
 
1336
1347
  // src/decorators/internal/zod-schema-ref.ts
1337
- var resolveSchemaRef = /* @__PURE__ */ __name((schema, options) => {
1348
+ function resolveSchemaRef(schema, options) {
1338
1349
  const registry = options?.registry ?? defaultRegistry;
1339
1350
  const id = registerSchema(schema, registry, {
1340
1351
  id: options?.id
@@ -1371,7 +1382,8 @@ var resolveSchemaRef = /* @__PURE__ */ __name((schema, options) => {
1371
1382
  kind: "inline",
1372
1383
  schema: body
1373
1384
  };
1374
- }, "resolveSchemaRef");
1385
+ }
1386
+ __name(resolveSchemaRef, "resolveSchemaRef");
1375
1387
 
1376
1388
  // src/decorators/internal/zod-param-expand.ts
1377
1389
  var isZodObject = /* @__PURE__ */ __name((schema) => schema._zod.def.type === "object", "isZodObject");
@@ -1505,12 +1517,11 @@ var resolveBodySchema = /* @__PURE__ */ __name((schema, options) => {
1505
1517
  if (options?.flatten === true) {
1506
1518
  return flattenObjectIntersection(schema, options.registry ?? defaultRegistry, "@ZodBody");
1507
1519
  }
1508
- const resolution = resolveSchemaRef(schema, {
1520
+ return resolveSchemaRef(schema, {
1509
1521
  id: options?.id,
1510
1522
  registry: options?.registry,
1511
1523
  deferAnonInline: true
1512
- });
1513
- return resolution.kind === "ref" ? resolution.ref : resolution.schema;
1524
+ }).ref;
1514
1525
  }, "resolveBodySchema");
1515
1526
 
1516
1527
  // src/decorators/internal/query-marker.ts
@@ -2382,6 +2393,47 @@ var decorateIfPresent = /* @__PURE__ */ __name((schemas, key) => {
2382
2393
  };
2383
2394
  }, "decorateIfPresent");
2384
2395
 
2396
+ // src/document/ref-titles.ts
2397
+ var applyRefTitles = /* @__PURE__ */ __name((doc) => {
2398
+ const schemas = doc.components?.schemas;
2399
+ if (schemas === void 0) {
2400
+ return;
2401
+ }
2402
+ const titleById = /* @__PURE__ */ new Map();
2403
+ for (const [id, body] of Object.entries(schemas)) {
2404
+ if (isPlainRecord5(body) && typeof body.title === "string" && body.title !== "") {
2405
+ titleById.set(id, body.title);
2406
+ }
2407
+ }
2408
+ if (titleById.size === 0) {
2409
+ return;
2410
+ }
2411
+ addRefTitles(doc.paths, titleById);
2412
+ addRefTitles(schemas, titleById);
2413
+ }, "applyRefTitles");
2414
+ var isPlainRecord5 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isPlainRecord");
2415
+ var addRefTitles = /* @__PURE__ */ __name((node, titleById) => {
2416
+ if (Array.isArray(node)) {
2417
+ for (const item of node) {
2418
+ addRefTitles(item, titleById);
2419
+ }
2420
+ return;
2421
+ }
2422
+ if (!isPlainRecord5(node)) {
2423
+ return;
2424
+ }
2425
+ const ref = node.$ref;
2426
+ if (typeof ref === "string" && ref.startsWith(COMPONENTS_SCHEMAS_PREFIX) && node.title === void 0) {
2427
+ const title = titleById.get(ref.slice(COMPONENTS_SCHEMAS_PREFIX.length));
2428
+ if (title !== void 0) {
2429
+ node.title = title;
2430
+ }
2431
+ }
2432
+ for (const value of Object.values(node)) {
2433
+ addRefTitles(value, titleById);
2434
+ }
2435
+ }, "addRefTitles");
2436
+
2385
2437
  // src/document/rewrite-refs.ts
2386
2438
  var rewriteRefs2 = /* @__PURE__ */ __name((params) => {
2387
2439
  const { doc, renames, divergentOutputIds } = params;
@@ -2527,6 +2579,9 @@ var applyZodNest = /* @__PURE__ */ __name((doc, opts = {}) => {
2527
2579
  doc,
2528
2580
  registry
2529
2581
  });
2582
+ if (opts.refTitles !== false) {
2583
+ applyRefTitles(doc);
2584
+ }
2530
2585
  assertNoDanglingRefs({
2531
2586
  doc,
2532
2587
  collected: extended