( ctx: ToJSONSchemaContext, schema: T // params: EmitParams )
| 215 | } |
| 216 | |
| 217 | export function extractDefs<T extends schemas.$ZodType>( |
| 218 | ctx: ToJSONSchemaContext, |
| 219 | schema: T |
| 220 | // params: EmitParams |
| 221 | ): void { |
| 222 | // iterate over seen map; |
| 223 | const root = ctx.seen.get(schema); |
| 224 | |
| 225 | if (!root) throw new Error("Unprocessed schema. This is a bug in Zod."); |
| 226 | |
| 227 | // Track ids to detect duplicates across different schemas |
| 228 | const idToSchema = new Map<string, schemas.$ZodType>(); |
| 229 | for (const entry of ctx.seen.entries()) { |
| 230 | const id = ctx.metadataRegistry.get(entry[0])?.id; |
| 231 | if (id) { |
| 232 | const existing = idToSchema.get(id); |
| 233 | if (existing && existing !== entry[0]) { |
| 234 | throw new Error( |
| 235 | `Duplicate schema id "${id}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.` |
| 236 | ); |
| 237 | } |
| 238 | idToSchema.set(id, entry[0]); |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | // returns a ref to the schema |
| 243 | // defId will be empty if the ref points to an external schema (or #) |
| 244 | const makeURI = (entry: [schemas.$ZodType<unknown, unknown>, Seen]): { ref: string; defId?: string } => { |
| 245 | // comparing the seen objects because sometimes |
| 246 | // multiple schemas map to the same seen object. |
| 247 | // e.g. lazy |
| 248 | |
| 249 | // external is configured |
| 250 | const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions"; |
| 251 | if (ctx.external) { |
| 252 | const externalId = ctx.external.registry.get(entry[0])?.id; // ?? "__shared";// `__schema${ctx.counter++}`; |
| 253 | |
| 254 | // check if schema is in the external registry |
| 255 | const uriGenerator = ctx.external.uri ?? ((id: string) => id); |
| 256 | if (externalId) { |
| 257 | return { ref: uriGenerator(externalId) }; |
| 258 | } |
| 259 | |
| 260 | // otherwise, add to __shared |
| 261 | const id: string = entry[1].defId ?? (entry[1].schema.id as string) ?? `schema${ctx.counter++}`; |
| 262 | entry[1].defId = id; // set defId so it will be reused if needed |
| 263 | return { defId: id, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id}` }; |
| 264 | } |
| 265 | |
| 266 | if (entry[1] === root) { |
| 267 | return { ref: "#" }; |
| 268 | } |
| 269 | |
| 270 | // self-contained schema |
| 271 | const uriPrefix = `#`; |
| 272 | const defUriPrefix = `${uriPrefix}/${defsSegment}/`; |
| 273 | const defId = entry[1].schema.id ?? `__schema${ctx.counter++}`; |
| 274 | return { defId, ref: defUriPrefix + defId }; |
no test coverage detected