@kurotako/gen-openapi 0.2.1 → 0.2.2

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # @kurotako/gen-openapi
2
2
 
3
+ ## 0.2.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [a3d63ef]
8
+ - @kurotako/ir@0.4.0
9
+ - @kurotako/config@0.1.5
10
+ - @kurotako/core@0.2.2
11
+
3
12
  ## 0.2.1
4
13
 
5
14
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -170,6 +170,9 @@ function buildArtifact(ir, options) {
170
170
  return { entities };
171
171
  }
172
172
 
173
+ // src/document.ts
174
+ var import_ir3 = require("@kurotako/ir");
175
+
173
176
  // src/render/entity.ts
174
177
  var import_ir2 = require("@kurotako/ir");
175
178
 
@@ -304,9 +307,7 @@ function buildDocument(source, options, namespace, logger) {
304
307
  const names = [
305
308
  ...Object.keys(source.entities),
306
309
  ...enumsByName.keys(),
307
- ...Object.keys(source.typeAliases ?? {}).filter(
308
- (name) => !enumsByName.has(name) && source.entities[name] === void 0
309
- )
310
+ ...(0, import_ir3.nonRedundantTypeAliases)(source).map((alias) => alias.name).filter((name) => source.entities[name] === void 0)
310
311
  ].sort((a, b) => a.localeCompare(b));
311
312
  for (const name of names) {
312
313
  if (!SCHEMA_NAME_PATTERN.test(name)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/generator.ts","../src/artifact.ts","../src/render/schema.ts","../src/render/entity.ts","../src/render/constraints.ts","../src/document.ts","../src/options.ts","../src/serialize.ts"],"sourcesContent":["/**\n * `@kurotako/gen-openapi` — the OpenAPI generator driver.\n *\n * Maps the IR to one OpenAPI document (`components/schemas` only, no\n * `paths`/operations) per namespace. Single entry point: the driver object,\n * its options schema/type and the error classes.\n */\nexport {\n OpenApiGenError,\n OpenApiGenInvalidSchemaNameError,\n} from './errors.js';\nexport { openapiGenerator } from './generator.js';\nexport { OpenApiGeneratorOptions } from './options.js';\n","/**\n * `@kurotako/gen-openapi` error classes.\n *\n * `OpenApiGenError` is a plain `Error` subclass carrying a stable `code`; the\n * OpenAPI generator has no dependency on `@kurotako/core` at runtime, and\n * `@kurotako/core` wraps any throw from `generate()` as a `DriverError` for the\n * CLI's single `instanceof TakoError` catch.\n *\n * Codes: `openapi_gen_invalid_schema_name`.\n */\n\nexport class OpenApiGenError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n this.code = code;\n }\n}\n\n/**\n * An entity or type-alias name is not a valid OpenAPI `components/schemas` key\n * (the spec restricts it to `^[a-zA-Z0-9._-]+$`). `parser-openapi` never\n * produces such a name, but a name coming from `parser-prisma` (arbitrary model\n * name) is unconstrained.\n */\nexport class OpenApiGenInvalidSchemaNameError extends OpenApiGenError {\n readonly schemaName: string;\n\n constructor(schemaName: string) {\n super(\n 'openapi_gen_invalid_schema_name',\n `'${schemaName}' is not a valid OpenAPI components/schemas key (must match ^[a-zA-Z0-9._-]+$)`,\n );\n this.schemaName = schemaName;\n }\n}\n","/**\n * `openapiGenerator` — the `@kurotako/gen-openapi` driver.\n *\n * `@kurotako/config` validates `options` against `optionsSchema` and curries it\n * away; `@kurotako/core` then calls `generate(ctx)` with a namespace-filtered IR.\n * One `VirtualFile` per namespace: a single `openapi.<ext>` document, not one\n * file per entity — a spec is normally not split, and external tooling\n * consuming it expects one document.\n */\nimport { defineGenerator } from '@kurotako/config';\nimport type { GenerateContext, GenOutput, VirtualFile } from '@kurotako/core';\nimport { buildArtifact } from './artifact.js';\nimport { buildDocument } from './document.js';\nimport { OpenApiGeneratorOptions } from './options.js';\nimport { serialize } from './serialize.js';\n\nexport const openapiGenerator = defineGenerator({\n name: 'openapi',\n optionsSchema: OpenApiGeneratorOptions,\n\n generate(ctx: GenerateContext, options): GenOutput {\n const files: VirtualFile[] = [];\n const ext = options.format === 'yaml' ? 'yaml' : 'json';\n\n for (const [namespace, source] of Object.entries(ctx.ir.sources)) {\n const document = buildDocument(source, options, namespace, ctx.logger);\n files.push({\n path: `${namespace}/openapi/openapi.${ext}`,\n content: serialize(document, options.format),\n });\n }\n\n return { files, artifact: buildArtifact(ctx.ir, options) };\n },\n});\n","/**\n * Assemble the `GeneratorArtifact` — one `EntitySymbols` per `${namespace}.${entity}`,\n * pointing at the emitted document and the entity's `#/components/schemas/<name>`\n * pointer. No `dependsOn` (this generator reads only the IR), no\n * `peerDependencies` (the emitted document has no runtime import surface), no\n * `extra` (no consumer needs OpenAPI-specific artifact data yet).\n */\nimport type { EntitySymbols, GeneratorArtifact } from '@kurotako/core';\nimport type { IR } from '@kurotako/ir';\nimport { iterEntities } from '@kurotako/ir';\nimport type { OpenApiGeneratorOptions } from './options.js';\nimport { schemaRef } from './render/schema.js';\n\nexport function buildArtifact(\n ir: IR,\n options: OpenApiGeneratorOptions,\n): GeneratorArtifact {\n const ext = options.format === 'yaml' ? 'yaml' : 'json';\n const entities: Record<string, EntitySymbols> = {};\n\n for (const { namespace, entity } of iterEntities(ir)) {\n entities[`${namespace}.${entity.name}`] = {\n module: `${namespace}/openapi/openapi.${ext}`,\n symbols: { schema: schemaRef(entity.name) },\n };\n }\n\n return { entities };\n}\n","/**\n * `FieldType` -> JSON Schema fragment, symmetric to `parser-openapi`'s\n * `mapSchema` (inverted). Also carries the `Field.list` and nullability\n * wrapping, which are properties of a `Field`/`Relation`, not of a\n * `FieldType` — applied by the caller once the base fragment is built.\n */\nimport type { FieldType, ScalarType } from '@kurotako/ir';\n\nexport type JsonSchemaFragment = Record<string, unknown>;\n\nexport function schemaRef(name: string): string {\n return `#/components/schemas/${name}`;\n}\n\nfunction scalarSchema(scalar: ScalarType): JsonSchemaFragment {\n switch (scalar) {\n case 'string':\n return { type: 'string' };\n case 'boolean':\n return { type: 'boolean' };\n case 'int':\n return { type: 'integer' };\n case 'bigint':\n return { type: 'integer', format: 'int64' };\n case 'float':\n return { type: 'number' };\n case 'decimal':\n return { type: 'number' };\n case 'date':\n return { type: 'string', format: 'date' };\n case 'datetime':\n return { type: 'string', format: 'date-time' };\n case 'uuid':\n return { type: 'string', format: 'uuid' };\n case 'bytes':\n return { type: 'string', format: 'byte' };\n case 'json':\n return {};\n }\n}\n\n/** Pure recursive mapping of a `FieldType` to a JSON Schema fragment. */\nexport function renderFieldType(type: FieldType): JsonSchemaFragment {\n switch (type.kind) {\n case 'scalar':\n return scalarSchema(type.scalar);\n case 'enum':\n return { $ref: schemaRef(type.ref) };\n case 'unknown':\n return {};\n case 'ref':\n return { $ref: schemaRef(type.ref) };\n case 'map':\n return {\n type: 'object',\n additionalProperties:\n type.value.kind === 'unknown' ? true : renderFieldType(type.value),\n };\n case 'array':\n return { type: 'array', items: renderFieldType(type.element) };\n case 'union': {\n const schema: JsonSchemaFragment = {\n oneOf: type.variants.map((variant) => renderFieldType(variant)),\n };\n if (type.discriminator !== undefined) {\n const discriminator: JsonSchemaFragment = {\n propertyName: type.discriminator.propertyName,\n };\n if (type.discriminator.mapping !== undefined) {\n discriminator.mapping = Object.fromEntries(\n Object.entries(type.discriminator.mapping).map(([key, target]) => [\n key,\n schemaRef(target),\n ]),\n );\n }\n schema.discriminator = discriminator;\n }\n return schema;\n }\n }\n}\n\n/**\n * `Field.list` (legacy boolean, still present alongside `FieldType.kind ===\n * 'array'`) wraps the mapped schema one more time in an array — skipped when\n * `type.kind` is already `'array'`, to avoid a double array-of-array.\n */\nexport function wrapListed(\n schema: JsonSchemaFragment,\n type: FieldType,\n): JsonSchemaFragment {\n if (type.kind === 'array') {\n return schema;\n }\n return { type: 'array', items: schema };\n}\n\nfunction hasRef(schema: JsonSchemaFragment): boolean {\n return typeof schema.$ref === 'string';\n}\n\n/**\n * Nullability wrapping for a mapped schema, per OpenAPI version.\n *\n * - 3.1: widen `type` to include `'null'`; a `$ref` or `oneOf` (ref / union /\n * enum, and anything with no bare `type` keyword to widen) wraps instead in\n * `{ oneOf: [<schema>, { type: 'null' }] }`.\n * - 3.0: add `nullable: true` as a sibling keyword; a `$ref` cannot carry a\n * sibling per the 3.0 spec, so it wraps in `{ allOf: [<schema>], nullable:\n * true }` instead.\n */\nexport function wrapNullable(\n schema: JsonSchemaFragment,\n openapiVersion: '3.0' | '3.1',\n): JsonSchemaFragment {\n if (openapiVersion === '3.0') {\n if (hasRef(schema)) {\n return { allOf: [schema], nullable: true };\n }\n return { ...schema, nullable: true };\n }\n\n if (hasRef(schema) || schema.oneOf !== undefined) {\n return { oneOf: [schema, { type: 'null' }] };\n }\n if (typeof schema.type === 'string') {\n return { ...schema, type: [schema.type, 'null'] };\n }\n return { oneOf: [schema, { type: 'null' }] };\n}\n","/**\n * `Entity` -> a `components/schemas/<Entity>` object: own fields (via\n * `render/schema.ts` + `render/constraints.ts`), `required`, `additionalProperties`\n * and relations rendered nested via `$ref` (the \"deep\" family in `gen-zod`'s\n * vocabulary — see `packages/gen-zod/src/render/relations.ts`).\n *\n * Cross-source relations degrade to omitting the relation property entirely\n * (logged at `debug`), same choice `gen-zod`'s deep family makes: v1 cannot\n * reference a schema defined in another namespace's document. Circular\n * relations need no special handling — `$ref` in JSON Schema is natively\n * self- and mutually-recursive.\n */\nimport type { Logger } from '@kurotako/core';\nimport type { Entity, Relation, TypeAlias } from '@kurotako/ir';\nimport { isCrossSource } from '@kurotako/ir';\nimport { applyConstraints } from './constraints.js';\nimport {\n type JsonSchemaFragment,\n renderFieldType,\n schemaRef,\n wrapListed,\n wrapNullable,\n} from './schema.js';\n\nexport interface RenderEntityOptions {\n openapiVersion: '3.0' | '3.1';\n /** The namespace the entity belongs to, for cross-source relation detection. */\n namespace: string;\n logger?: Logger;\n}\n\nfunction renderRelation(\n rel: Relation,\n opts: RenderEntityOptions,\n): JsonSchemaFragment | null {\n if (isCrossSource(opts.namespace, rel)) {\n opts.logger?.debug(\n `gen-openapi: relation '${rel.name}' targets another source ('${rel.target.namespace}.${rel.target.entity}'); omitting it from the emitted schema`,\n );\n return null;\n }\n\n const target = schemaRef(rel.target.entity);\n let schema: JsonSchemaFragment =\n rel.cardinality === 'many'\n ? { type: 'array', items: { $ref: target } }\n : { $ref: target };\n\n if (rel.cardinality === 'one' && rel.optional) {\n schema = wrapNullable(schema, opts.openapiVersion);\n }\n\n return schema;\n}\n\nexport function renderEntity(\n entity: Entity,\n opts: RenderEntityOptions,\n): JsonSchemaFragment {\n const properties: Record<string, JsonSchemaFragment> = {};\n const required: string[] = [];\n\n for (const field of entity.fields) {\n let schema = renderFieldType(field.type);\n schema = applyConstraints(schema, field.type, field.constraints);\n if (field.list) {\n schema = wrapListed(schema, field.type);\n }\n if (field.nullable) {\n schema = wrapNullable(schema, opts.openapiVersion);\n }\n properties[field.name] = schema;\n if (!field.optional) {\n required.push(field.name);\n }\n }\n\n for (const relation of entity.relations) {\n const schema = renderRelation(relation, opts);\n if (schema !== null) {\n properties[relation.name] = schema;\n }\n }\n\n const schema: JsonSchemaFragment = {\n type: 'object',\n properties,\n };\n if (required.length > 0) {\n schema.required = required;\n }\n if (entity.additionalProperties !== undefined) {\n schema.additionalProperties =\n entity.additionalProperties.kind === 'unknown'\n ? true\n : renderFieldType(entity.additionalProperties);\n }\n if (entity.doc !== undefined) {\n schema.description = entity.doc;\n }\n\n return schema;\n}\n\nexport function renderTypeAlias(alias: TypeAlias): JsonSchemaFragment {\n const schema = renderFieldType(alias.type);\n if (alias.doc !== undefined) {\n return { ...schema, description: alias.doc };\n }\n return schema;\n}\n","/**\n * `Constraints` -> JSON Schema keywords, symmetric to `parser-openapi`'s\n * `field()`. `unique` has no JSON Schema keyword for object-property\n * uniqueness (`uniqueItems` only applies to arrays) and is dropped silently.\n */\nimport type { Constraints, FieldType, StringFormat } from '@kurotako/ir';\nimport type { JsonSchemaFragment } from './schema.js';\n\n/**\n * Inverse of `parser-openapi`'s `STRING_FORMATS`. `datetime`/`date`/`uuid`/\n * `cuid`/`cuid2`/`ulid` never reach here as a `Constraints.format` value —\n * those scalars carry their own fixed format via `renderFieldType` instead;\n * an unrecognised format (`cuid`/`cuid2`/`ulid`) emits no keyword, same\n * escape `parser-openapi` takes in the reverse direction.\n */\nconst FORMAT_KEYWORDS: Partial<Record<StringFormat, string>> = {\n email: 'email',\n url: 'uri',\n ipv4: 'ipv4',\n ipv6: 'ipv6',\n time: 'time',\n duration: 'duration',\n};\n\nexport function applyConstraints(\n schema: JsonSchemaFragment,\n type: FieldType,\n constraints: Constraints,\n): JsonSchemaFragment {\n const out: JsonSchemaFragment = { ...schema };\n\n if (constraints.min !== undefined) {\n out.minimum = constraints.min;\n }\n if (constraints.max !== undefined) {\n out.maximum = constraints.max;\n }\n if (constraints.minLength !== undefined) {\n out.minLength = constraints.minLength;\n }\n if (constraints.maxLength !== undefined) {\n out.maxLength = constraints.maxLength;\n }\n if (constraints.regex !== undefined) {\n out.pattern = constraints.regex;\n }\n if (\n constraints.format !== undefined &&\n type.kind === 'scalar' &&\n type.scalar === 'string'\n ) {\n const keyword = FORMAT_KEYWORDS[constraints.format];\n if (keyword !== undefined) {\n out.format = keyword;\n }\n }\n\n return out;\n}\n","/**\n * Assemble the full `OpenApiDocument` object for one namespace's `SourceIR`.\n *\n * `components.schemas` covers both `source.entities` and `source.typeAliases`\n * (unions, refs-only aliases, enums) — the IR's cross-reference pass already\n * guarantees the two never collide within one source (`docs/ir.md` \"Closed\n * points\" §3), so both can be merged into the same flat namespace safely.\n */\nimport type { Logger } from '@kurotako/core';\nimport type { EnumDef, SourceIR } from '@kurotako/ir';\nimport { OpenApiGenInvalidSchemaNameError } from './errors.js';\nimport type { OpenApiGeneratorOptions } from './options.js';\nimport { renderEntity, renderTypeAlias } from './render/entity.js';\nimport type { JsonSchemaFragment } from './render/schema.js';\n\n/**\n * Every reachable `EnumDef` of a source (source-level + entity-local, entity\n * shadowing on a name collision), each rendered as its own\n * `components/schemas` entry (`{ type: 'string', enum: [...] }`).\n *\n * Not called out as its own bullet in the technical design's \"Document\n * assembly\" section, but required for `$ref` resolvability: `render/schema.ts`\n * maps `FieldType.kind === 'enum'` to a bare `$ref` (mirroring\n * `parser-openapi`'s self-referencing `typeAliases[name] = { kind: 'enum', ref:\n * name }` entry), so every enum name needs a real schema behind it — the\n * synthetic self-referencing alias is skipped in favour of this one.\n */\nfunction collectEnums(source: SourceIR): EnumDef[] {\n const byName = new Map<string, EnumDef>();\n for (const def of Object.values(source.enums)) {\n byName.set(def.name, def);\n }\n for (const entity of Object.values(source.entities)) {\n for (const def of Object.values(entity.enums ?? {})) {\n byName.set(def.name, def);\n }\n }\n return [...byName.values()];\n}\n\nfunction renderEnum(def: EnumDef): JsonSchemaFragment {\n const schema: JsonSchemaFragment = {\n type: 'string',\n enum: def.values.map((value) => value.name),\n };\n if (def.doc !== undefined) {\n schema.description = def.doc;\n }\n return schema;\n}\n\nexport interface OpenApiDocument {\n openapi: string;\n info: { title: string; version: string };\n paths: Record<string, never>;\n components: { schemas: Record<string, JsonSchemaFragment> };\n}\n\n/** OpenAPI restricts a `components/schemas` key to this pattern. */\nconst SCHEMA_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;\n\nfunction pinnedOpenapiVersion(version: '3.0' | '3.1'): string {\n return version === '3.0' ? '3.0.3' : '3.1.0';\n}\n\nexport function buildDocument(\n source: SourceIR,\n options: OpenApiGeneratorOptions,\n namespace: string,\n logger?: Logger,\n): OpenApiDocument {\n const schemas: Record<string, JsonSchemaFragment> = {};\n const enumsByName = new Map(\n collectEnums(source).map((def) => [def.name, def]),\n );\n const names = [\n ...Object.keys(source.entities),\n ...enumsByName.keys(),\n ...Object.keys(source.typeAliases ?? {}).filter(\n (name) => !enumsByName.has(name) && source.entities[name] === undefined,\n ),\n ].sort((a, b) => a.localeCompare(b));\n\n for (const name of names) {\n if (!SCHEMA_NAME_PATTERN.test(name)) {\n throw new OpenApiGenInvalidSchemaNameError(name);\n }\n const entity = source.entities[name];\n if (entity !== undefined) {\n schemas[name] = renderEntity(entity, {\n openapiVersion: options.openapiVersion,\n namespace,\n logger,\n });\n continue;\n }\n const enumDef = enumsByName.get(name);\n if (enumDef !== undefined) {\n schemas[name] = renderEnum(enumDef);\n continue;\n }\n const alias = source.typeAliases?.[name];\n if (alias !== undefined) {\n schemas[name] = renderTypeAlias(alias);\n }\n }\n\n return {\n openapi: pinnedOpenapiVersion(options.openapiVersion),\n info: {\n title: options.title ?? namespace,\n version: options.version,\n },\n paths: {},\n components: { schemas },\n };\n}\n","/**\n * Valibot schema for `@kurotako/gen-openapi`'s `options`, plus the inferred type.\n * `@kurotako/config` validates a config entry's `options` against this schema and\n * curries it away before `@kurotako/core` sees the generator.\n *\n * `title` has no static default here: it depends on the namespace, which this\n * schema cannot see — `generator.ts` applies `options.title ?? namespace`.\n */\nimport * as v from 'valibot';\n\nexport const OpenApiGeneratorOptions = v.object({\n openapiVersion: v.optional(v.picklist(['3.0', '3.1']), '3.1'),\n format: v.optional(v.picklist(['json', 'yaml']), 'json'),\n title: v.optional(v.string()),\n version: v.optional(v.string(), '0.0.0'),\n});\n\nexport type OpenApiGeneratorOptions = v.InferOutput<\n typeof OpenApiGeneratorOptions\n>;\n","/**\n * `OpenApiDocument` -> JSON or YAML string.\n */\nimport * as YAML from 'yaml';\nimport type { OpenApiDocument } from './document.js';\nimport type { OpenApiGeneratorOptions } from './options.js';\n\nexport function serialize(\n document: OpenApiDocument,\n format: OpenApiGeneratorOptions['format'],\n): string {\n if (format === 'yaml') {\n return YAML.stringify(document);\n }\n return `${JSON.stringify(document, null, 2)}\\n`;\n}\n"],"mappings":"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","names":["import_ir","schema"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/generator.ts","../src/artifact.ts","../src/render/schema.ts","../src/document.ts","../src/render/entity.ts","../src/render/constraints.ts","../src/options.ts","../src/serialize.ts"],"sourcesContent":["/**\n * `@kurotako/gen-openapi` — the OpenAPI generator driver.\n *\n * Maps the IR to one OpenAPI document (`components/schemas` only, no\n * `paths`/operations) per namespace. Single entry point: the driver object,\n * its options schema/type and the error classes.\n */\nexport {\n OpenApiGenError,\n OpenApiGenInvalidSchemaNameError,\n} from './errors.js';\nexport { openapiGenerator } from './generator.js';\nexport { OpenApiGeneratorOptions } from './options.js';\n","/**\n * `@kurotako/gen-openapi` error classes.\n *\n * `OpenApiGenError` is a plain `Error` subclass carrying a stable `code`; the\n * OpenAPI generator has no dependency on `@kurotako/core` at runtime, and\n * `@kurotako/core` wraps any throw from `generate()` as a `DriverError` for the\n * CLI's single `instanceof TakoError` catch.\n *\n * Codes: `openapi_gen_invalid_schema_name`.\n */\n\nexport class OpenApiGenError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n this.code = code;\n }\n}\n\n/**\n * An entity or type-alias name is not a valid OpenAPI `components/schemas` key\n * (the spec restricts it to `^[a-zA-Z0-9._-]+$`). `parser-openapi` never\n * produces such a name, but a name coming from `parser-prisma` (arbitrary model\n * name) is unconstrained.\n */\nexport class OpenApiGenInvalidSchemaNameError extends OpenApiGenError {\n readonly schemaName: string;\n\n constructor(schemaName: string) {\n super(\n 'openapi_gen_invalid_schema_name',\n `'${schemaName}' is not a valid OpenAPI components/schemas key (must match ^[a-zA-Z0-9._-]+$)`,\n );\n this.schemaName = schemaName;\n }\n}\n","/**\n * `openapiGenerator` — the `@kurotako/gen-openapi` driver.\n *\n * `@kurotako/config` validates `options` against `optionsSchema` and curries it\n * away; `@kurotako/core` then calls `generate(ctx)` with a namespace-filtered IR.\n * One `VirtualFile` per namespace: a single `openapi.<ext>` document, not one\n * file per entity — a spec is normally not split, and external tooling\n * consuming it expects one document.\n */\nimport { defineGenerator } from '@kurotako/config';\nimport type { GenerateContext, GenOutput, VirtualFile } from '@kurotako/core';\nimport { buildArtifact } from './artifact.js';\nimport { buildDocument } from './document.js';\nimport { OpenApiGeneratorOptions } from './options.js';\nimport { serialize } from './serialize.js';\n\nexport const openapiGenerator = defineGenerator({\n name: 'openapi',\n optionsSchema: OpenApiGeneratorOptions,\n\n generate(ctx: GenerateContext, options): GenOutput {\n const files: VirtualFile[] = [];\n const ext = options.format === 'yaml' ? 'yaml' : 'json';\n\n for (const [namespace, source] of Object.entries(ctx.ir.sources)) {\n const document = buildDocument(source, options, namespace, ctx.logger);\n files.push({\n path: `${namespace}/openapi/openapi.${ext}`,\n content: serialize(document, options.format),\n });\n }\n\n return { files, artifact: buildArtifact(ctx.ir, options) };\n },\n});\n","/**\n * Assemble the `GeneratorArtifact` — one `EntitySymbols` per `${namespace}.${entity}`,\n * pointing at the emitted document and the entity's `#/components/schemas/<name>`\n * pointer. No `dependsOn` (this generator reads only the IR), no\n * `peerDependencies` (the emitted document has no runtime import surface), no\n * `extra` (no consumer needs OpenAPI-specific artifact data yet).\n */\nimport type { EntitySymbols, GeneratorArtifact } from '@kurotako/core';\nimport type { IR } from '@kurotako/ir';\nimport { iterEntities } from '@kurotako/ir';\nimport type { OpenApiGeneratorOptions } from './options.js';\nimport { schemaRef } from './render/schema.js';\n\nexport function buildArtifact(\n ir: IR,\n options: OpenApiGeneratorOptions,\n): GeneratorArtifact {\n const ext = options.format === 'yaml' ? 'yaml' : 'json';\n const entities: Record<string, EntitySymbols> = {};\n\n for (const { namespace, entity } of iterEntities(ir)) {\n entities[`${namespace}.${entity.name}`] = {\n module: `${namespace}/openapi/openapi.${ext}`,\n symbols: { schema: schemaRef(entity.name) },\n };\n }\n\n return { entities };\n}\n","/**\n * `FieldType` -> JSON Schema fragment, symmetric to `parser-openapi`'s\n * `mapSchema` (inverted). Also carries the `Field.list` and nullability\n * wrapping, which are properties of a `Field`/`Relation`, not of a\n * `FieldType` — applied by the caller once the base fragment is built.\n */\nimport type { FieldType, ScalarType } from '@kurotako/ir';\n\nexport type JsonSchemaFragment = Record<string, unknown>;\n\nexport function schemaRef(name: string): string {\n return `#/components/schemas/${name}`;\n}\n\nfunction scalarSchema(scalar: ScalarType): JsonSchemaFragment {\n switch (scalar) {\n case 'string':\n return { type: 'string' };\n case 'boolean':\n return { type: 'boolean' };\n case 'int':\n return { type: 'integer' };\n case 'bigint':\n return { type: 'integer', format: 'int64' };\n case 'float':\n return { type: 'number' };\n case 'decimal':\n return { type: 'number' };\n case 'date':\n return { type: 'string', format: 'date' };\n case 'datetime':\n return { type: 'string', format: 'date-time' };\n case 'uuid':\n return { type: 'string', format: 'uuid' };\n case 'bytes':\n return { type: 'string', format: 'byte' };\n case 'json':\n return {};\n }\n}\n\n/** Pure recursive mapping of a `FieldType` to a JSON Schema fragment. */\nexport function renderFieldType(type: FieldType): JsonSchemaFragment {\n switch (type.kind) {\n case 'scalar':\n return scalarSchema(type.scalar);\n case 'enum':\n return { $ref: schemaRef(type.ref) };\n case 'unknown':\n return {};\n case 'ref':\n return { $ref: schemaRef(type.ref) };\n case 'map':\n return {\n type: 'object',\n additionalProperties:\n type.value.kind === 'unknown' ? true : renderFieldType(type.value),\n };\n case 'array':\n return { type: 'array', items: renderFieldType(type.element) };\n case 'union': {\n const schema: JsonSchemaFragment = {\n oneOf: type.variants.map((variant) => renderFieldType(variant)),\n };\n if (type.discriminator !== undefined) {\n const discriminator: JsonSchemaFragment = {\n propertyName: type.discriminator.propertyName,\n };\n if (type.discriminator.mapping !== undefined) {\n discriminator.mapping = Object.fromEntries(\n Object.entries(type.discriminator.mapping).map(([key, target]) => [\n key,\n schemaRef(target),\n ]),\n );\n }\n schema.discriminator = discriminator;\n }\n return schema;\n }\n }\n}\n\n/**\n * `Field.list` (legacy boolean, still present alongside `FieldType.kind ===\n * 'array'`) wraps the mapped schema one more time in an array — skipped when\n * `type.kind` is already `'array'`, to avoid a double array-of-array.\n */\nexport function wrapListed(\n schema: JsonSchemaFragment,\n type: FieldType,\n): JsonSchemaFragment {\n if (type.kind === 'array') {\n return schema;\n }\n return { type: 'array', items: schema };\n}\n\nfunction hasRef(schema: JsonSchemaFragment): boolean {\n return typeof schema.$ref === 'string';\n}\n\n/**\n * Nullability wrapping for a mapped schema, per OpenAPI version.\n *\n * - 3.1: widen `type` to include `'null'`; a `$ref` or `oneOf` (ref / union /\n * enum, and anything with no bare `type` keyword to widen) wraps instead in\n * `{ oneOf: [<schema>, { type: 'null' }] }`.\n * - 3.0: add `nullable: true` as a sibling keyword; a `$ref` cannot carry a\n * sibling per the 3.0 spec, so it wraps in `{ allOf: [<schema>], nullable:\n * true }` instead.\n */\nexport function wrapNullable(\n schema: JsonSchemaFragment,\n openapiVersion: '3.0' | '3.1',\n): JsonSchemaFragment {\n if (openapiVersion === '3.0') {\n if (hasRef(schema)) {\n return { allOf: [schema], nullable: true };\n }\n return { ...schema, nullable: true };\n }\n\n if (hasRef(schema) || schema.oneOf !== undefined) {\n return { oneOf: [schema, { type: 'null' }] };\n }\n if (typeof schema.type === 'string') {\n return { ...schema, type: [schema.type, 'null'] };\n }\n return { oneOf: [schema, { type: 'null' }] };\n}\n","/**\n * Assemble the full `OpenApiDocument` object for one namespace's `SourceIR`.\n *\n * `components.schemas` covers both `source.entities` and `source.typeAliases`\n * (unions, refs-only aliases, enums) — the IR's cross-reference pass already\n * guarantees the two never collide within one source (`docs/ir.md` \"Closed\n * points\" §3), so both can be merged into the same flat namespace safely.\n */\nimport type { Logger } from '@kurotako/core';\nimport type { EnumDef, SourceIR } from '@kurotako/ir';\nimport { nonRedundantTypeAliases } from '@kurotako/ir';\nimport { OpenApiGenInvalidSchemaNameError } from './errors.js';\nimport type { OpenApiGeneratorOptions } from './options.js';\nimport { renderEntity, renderTypeAlias } from './render/entity.js';\nimport type { JsonSchemaFragment } from './render/schema.js';\n\n/**\n * Every reachable `EnumDef` of a source (source-level + entity-local, entity\n * shadowing on a name collision), each rendered as its own\n * `components/schemas` entry (`{ type: 'string', enum: [...] }`).\n *\n * Not called out as its own bullet in the technical design's \"Document\n * assembly\" section, but required for `$ref` resolvability: `render/schema.ts`\n * maps `FieldType.kind === 'enum'` to a bare `$ref` (mirroring\n * `parser-openapi`'s self-referencing `typeAliases[name] = { kind: 'enum', ref:\n * name }` entry), so every enum name needs a real schema behind it — the\n * synthetic self-referencing alias is skipped in favour of this one.\n */\nfunction collectEnums(source: SourceIR): EnumDef[] {\n const byName = new Map<string, EnumDef>();\n for (const def of Object.values(source.enums)) {\n byName.set(def.name, def);\n }\n for (const entity of Object.values(source.entities)) {\n for (const def of Object.values(entity.enums ?? {})) {\n byName.set(def.name, def);\n }\n }\n return [...byName.values()];\n}\n\nfunction renderEnum(def: EnumDef): JsonSchemaFragment {\n const schema: JsonSchemaFragment = {\n type: 'string',\n enum: def.values.map((value) => value.name),\n };\n if (def.doc !== undefined) {\n schema.description = def.doc;\n }\n return schema;\n}\n\nexport interface OpenApiDocument {\n openapi: string;\n info: { title: string; version: string };\n paths: Record<string, never>;\n components: { schemas: Record<string, JsonSchemaFragment> };\n}\n\n/** OpenAPI restricts a `components/schemas` key to this pattern. */\nconst SCHEMA_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;\n\nfunction pinnedOpenapiVersion(version: '3.0' | '3.1'): string {\n return version === '3.0' ? '3.0.3' : '3.1.0';\n}\n\nexport function buildDocument(\n source: SourceIR,\n options: OpenApiGeneratorOptions,\n namespace: string,\n logger?: Logger,\n): OpenApiDocument {\n const schemas: Record<string, JsonSchemaFragment> = {};\n const enumsByName = new Map(\n collectEnums(source).map((def) => [def.name, def]),\n );\n const names = [\n ...Object.keys(source.entities),\n ...enumsByName.keys(),\n ...nonRedundantTypeAliases(source)\n .map((alias) => alias.name)\n .filter((name) => source.entities[name] === undefined),\n ].sort((a, b) => a.localeCompare(b));\n\n for (const name of names) {\n if (!SCHEMA_NAME_PATTERN.test(name)) {\n throw new OpenApiGenInvalidSchemaNameError(name);\n }\n const entity = source.entities[name];\n if (entity !== undefined) {\n schemas[name] = renderEntity(entity, {\n openapiVersion: options.openapiVersion,\n namespace,\n logger,\n });\n continue;\n }\n const enumDef = enumsByName.get(name);\n if (enumDef !== undefined) {\n schemas[name] = renderEnum(enumDef);\n continue;\n }\n const alias = source.typeAliases?.[name];\n if (alias !== undefined) {\n schemas[name] = renderTypeAlias(alias);\n }\n }\n\n return {\n openapi: pinnedOpenapiVersion(options.openapiVersion),\n info: {\n title: options.title ?? namespace,\n version: options.version,\n },\n paths: {},\n components: { schemas },\n };\n}\n","/**\n * `Entity` -> a `components/schemas/<Entity>` object: own fields (via\n * `render/schema.ts` + `render/constraints.ts`), `required`, `additionalProperties`\n * and relations rendered nested via `$ref` (the \"deep\" family in `gen-zod`'s\n * vocabulary — see `packages/gen-zod/src/render/relations.ts`).\n *\n * Cross-source relations degrade to omitting the relation property entirely\n * (logged at `debug`), same choice `gen-zod`'s deep family makes: v1 cannot\n * reference a schema defined in another namespace's document. Circular\n * relations need no special handling — `$ref` in JSON Schema is natively\n * self- and mutually-recursive.\n */\nimport type { Logger } from '@kurotako/core';\nimport type { Entity, Relation, TypeAlias } from '@kurotako/ir';\nimport { isCrossSource } from '@kurotako/ir';\nimport { applyConstraints } from './constraints.js';\nimport {\n type JsonSchemaFragment,\n renderFieldType,\n schemaRef,\n wrapListed,\n wrapNullable,\n} from './schema.js';\n\nexport interface RenderEntityOptions {\n openapiVersion: '3.0' | '3.1';\n /** The namespace the entity belongs to, for cross-source relation detection. */\n namespace: string;\n logger?: Logger;\n}\n\nfunction renderRelation(\n rel: Relation,\n opts: RenderEntityOptions,\n): JsonSchemaFragment | null {\n if (isCrossSource(opts.namespace, rel)) {\n opts.logger?.debug(\n `gen-openapi: relation '${rel.name}' targets another source ('${rel.target.namespace}.${rel.target.entity}'); omitting it from the emitted schema`,\n );\n return null;\n }\n\n const target = schemaRef(rel.target.entity);\n let schema: JsonSchemaFragment =\n rel.cardinality === 'many'\n ? { type: 'array', items: { $ref: target } }\n : { $ref: target };\n\n if (rel.cardinality === 'one' && rel.optional) {\n schema = wrapNullable(schema, opts.openapiVersion);\n }\n\n return schema;\n}\n\nexport function renderEntity(\n entity: Entity,\n opts: RenderEntityOptions,\n): JsonSchemaFragment {\n const properties: Record<string, JsonSchemaFragment> = {};\n const required: string[] = [];\n\n for (const field of entity.fields) {\n let schema = renderFieldType(field.type);\n schema = applyConstraints(schema, field.type, field.constraints);\n if (field.list) {\n schema = wrapListed(schema, field.type);\n }\n if (field.nullable) {\n schema = wrapNullable(schema, opts.openapiVersion);\n }\n properties[field.name] = schema;\n if (!field.optional) {\n required.push(field.name);\n }\n }\n\n for (const relation of entity.relations) {\n const schema = renderRelation(relation, opts);\n if (schema !== null) {\n properties[relation.name] = schema;\n }\n }\n\n const schema: JsonSchemaFragment = {\n type: 'object',\n properties,\n };\n if (required.length > 0) {\n schema.required = required;\n }\n if (entity.additionalProperties !== undefined) {\n schema.additionalProperties =\n entity.additionalProperties.kind === 'unknown'\n ? true\n : renderFieldType(entity.additionalProperties);\n }\n if (entity.doc !== undefined) {\n schema.description = entity.doc;\n }\n\n return schema;\n}\n\nexport function renderTypeAlias(alias: TypeAlias): JsonSchemaFragment {\n const schema = renderFieldType(alias.type);\n if (alias.doc !== undefined) {\n return { ...schema, description: alias.doc };\n }\n return schema;\n}\n","/**\n * `Constraints` -> JSON Schema keywords, symmetric to `parser-openapi`'s\n * `field()`. `unique` has no JSON Schema keyword for object-property\n * uniqueness (`uniqueItems` only applies to arrays) and is dropped silently.\n */\nimport type { Constraints, FieldType, StringFormat } from '@kurotako/ir';\nimport type { JsonSchemaFragment } from './schema.js';\n\n/**\n * Inverse of `parser-openapi`'s `STRING_FORMATS`. `datetime`/`date`/`uuid`/\n * `cuid`/`cuid2`/`ulid` never reach here as a `Constraints.format` value —\n * those scalars carry their own fixed format via `renderFieldType` instead;\n * an unrecognised format (`cuid`/`cuid2`/`ulid`) emits no keyword, same\n * escape `parser-openapi` takes in the reverse direction.\n */\nconst FORMAT_KEYWORDS: Partial<Record<StringFormat, string>> = {\n email: 'email',\n url: 'uri',\n ipv4: 'ipv4',\n ipv6: 'ipv6',\n time: 'time',\n duration: 'duration',\n};\n\nexport function applyConstraints(\n schema: JsonSchemaFragment,\n type: FieldType,\n constraints: Constraints,\n): JsonSchemaFragment {\n const out: JsonSchemaFragment = { ...schema };\n\n if (constraints.min !== undefined) {\n out.minimum = constraints.min;\n }\n if (constraints.max !== undefined) {\n out.maximum = constraints.max;\n }\n if (constraints.minLength !== undefined) {\n out.minLength = constraints.minLength;\n }\n if (constraints.maxLength !== undefined) {\n out.maxLength = constraints.maxLength;\n }\n if (constraints.regex !== undefined) {\n out.pattern = constraints.regex;\n }\n if (\n constraints.format !== undefined &&\n type.kind === 'scalar' &&\n type.scalar === 'string'\n ) {\n const keyword = FORMAT_KEYWORDS[constraints.format];\n if (keyword !== undefined) {\n out.format = keyword;\n }\n }\n\n return out;\n}\n","/**\n * Valibot schema for `@kurotako/gen-openapi`'s `options`, plus the inferred type.\n * `@kurotako/config` validates a config entry's `options` against this schema and\n * curries it away before `@kurotako/core` sees the generator.\n *\n * `title` has no static default here: it depends on the namespace, which this\n * schema cannot see — `generator.ts` applies `options.title ?? namespace`.\n */\nimport * as v from 'valibot';\n\nexport const OpenApiGeneratorOptions = v.object({\n openapiVersion: v.optional(v.picklist(['3.0', '3.1']), '3.1'),\n format: v.optional(v.picklist(['json', 'yaml']), 'json'),\n title: v.optional(v.string()),\n version: v.optional(v.string(), '0.0.0'),\n});\n\nexport type OpenApiGeneratorOptions = v.InferOutput<\n typeof OpenApiGeneratorOptions\n>;\n","/**\n * `OpenApiDocument` -> JSON or YAML string.\n */\nimport * as YAML from 'yaml';\nimport type { OpenApiDocument } from './document.js';\nimport type { OpenApiGeneratorOptions } from './options.js';\n\nexport function serialize(\n document: OpenApiDocument,\n format: OpenApiGeneratorOptions['format'],\n): string {\n if (format === 'yaml') {\n return YAML.stringify(document);\n }\n return `${JSON.stringify(document, null, 2)}\\n`;\n}\n"],"mappings":"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","names":["import_ir","import_ir","schema"]}
package/dist/index.js CHANGED
@@ -131,6 +131,9 @@ function buildArtifact(ir, options) {
131
131
  return { entities };
132
132
  }
133
133
 
134
+ // src/document.ts
135
+ import { nonRedundantTypeAliases } from "@kurotako/ir";
136
+
134
137
  // src/render/entity.ts
135
138
  import { isCrossSource } from "@kurotako/ir";
136
139
 
@@ -265,9 +268,7 @@ function buildDocument(source, options, namespace, logger) {
265
268
  const names = [
266
269
  ...Object.keys(source.entities),
267
270
  ...enumsByName.keys(),
268
- ...Object.keys(source.typeAliases ?? {}).filter(
269
- (name) => !enumsByName.has(name) && source.entities[name] === void 0
270
- )
271
+ ...nonRedundantTypeAliases(source).map((alias) => alias.name).filter((name) => source.entities[name] === void 0)
271
272
  ].sort((a, b) => a.localeCompare(b));
272
273
  for (const name of names) {
273
274
  if (!SCHEMA_NAME_PATTERN.test(name)) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/generator.ts","../src/artifact.ts","../src/render/schema.ts","../src/render/entity.ts","../src/render/constraints.ts","../src/document.ts","../src/options.ts","../src/serialize.ts"],"sourcesContent":["/**\n * `@kurotako/gen-openapi` error classes.\n *\n * `OpenApiGenError` is a plain `Error` subclass carrying a stable `code`; the\n * OpenAPI generator has no dependency on `@kurotako/core` at runtime, and\n * `@kurotako/core` wraps any throw from `generate()` as a `DriverError` for the\n * CLI's single `instanceof TakoError` catch.\n *\n * Codes: `openapi_gen_invalid_schema_name`.\n */\n\nexport class OpenApiGenError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n this.code = code;\n }\n}\n\n/**\n * An entity or type-alias name is not a valid OpenAPI `components/schemas` key\n * (the spec restricts it to `^[a-zA-Z0-9._-]+$`). `parser-openapi` never\n * produces such a name, but a name coming from `parser-prisma` (arbitrary model\n * name) is unconstrained.\n */\nexport class OpenApiGenInvalidSchemaNameError extends OpenApiGenError {\n readonly schemaName: string;\n\n constructor(schemaName: string) {\n super(\n 'openapi_gen_invalid_schema_name',\n `'${schemaName}' is not a valid OpenAPI components/schemas key (must match ^[a-zA-Z0-9._-]+$)`,\n );\n this.schemaName = schemaName;\n }\n}\n","/**\n * `openapiGenerator` — the `@kurotako/gen-openapi` driver.\n *\n * `@kurotako/config` validates `options` against `optionsSchema` and curries it\n * away; `@kurotako/core` then calls `generate(ctx)` with a namespace-filtered IR.\n * One `VirtualFile` per namespace: a single `openapi.<ext>` document, not one\n * file per entity — a spec is normally not split, and external tooling\n * consuming it expects one document.\n */\nimport { defineGenerator } from '@kurotako/config';\nimport type { GenerateContext, GenOutput, VirtualFile } from '@kurotako/core';\nimport { buildArtifact } from './artifact.js';\nimport { buildDocument } from './document.js';\nimport { OpenApiGeneratorOptions } from './options.js';\nimport { serialize } from './serialize.js';\n\nexport const openapiGenerator = defineGenerator({\n name: 'openapi',\n optionsSchema: OpenApiGeneratorOptions,\n\n generate(ctx: GenerateContext, options): GenOutput {\n const files: VirtualFile[] = [];\n const ext = options.format === 'yaml' ? 'yaml' : 'json';\n\n for (const [namespace, source] of Object.entries(ctx.ir.sources)) {\n const document = buildDocument(source, options, namespace, ctx.logger);\n files.push({\n path: `${namespace}/openapi/openapi.${ext}`,\n content: serialize(document, options.format),\n });\n }\n\n return { files, artifact: buildArtifact(ctx.ir, options) };\n },\n});\n","/**\n * Assemble the `GeneratorArtifact` — one `EntitySymbols` per `${namespace}.${entity}`,\n * pointing at the emitted document and the entity's `#/components/schemas/<name>`\n * pointer. No `dependsOn` (this generator reads only the IR), no\n * `peerDependencies` (the emitted document has no runtime import surface), no\n * `extra` (no consumer needs OpenAPI-specific artifact data yet).\n */\nimport type { EntitySymbols, GeneratorArtifact } from '@kurotako/core';\nimport type { IR } from '@kurotako/ir';\nimport { iterEntities } from '@kurotako/ir';\nimport type { OpenApiGeneratorOptions } from './options.js';\nimport { schemaRef } from './render/schema.js';\n\nexport function buildArtifact(\n ir: IR,\n options: OpenApiGeneratorOptions,\n): GeneratorArtifact {\n const ext = options.format === 'yaml' ? 'yaml' : 'json';\n const entities: Record<string, EntitySymbols> = {};\n\n for (const { namespace, entity } of iterEntities(ir)) {\n entities[`${namespace}.${entity.name}`] = {\n module: `${namespace}/openapi/openapi.${ext}`,\n symbols: { schema: schemaRef(entity.name) },\n };\n }\n\n return { entities };\n}\n","/**\n * `FieldType` -> JSON Schema fragment, symmetric to `parser-openapi`'s\n * `mapSchema` (inverted). Also carries the `Field.list` and nullability\n * wrapping, which are properties of a `Field`/`Relation`, not of a\n * `FieldType` — applied by the caller once the base fragment is built.\n */\nimport type { FieldType, ScalarType } from '@kurotako/ir';\n\nexport type JsonSchemaFragment = Record<string, unknown>;\n\nexport function schemaRef(name: string): string {\n return `#/components/schemas/${name}`;\n}\n\nfunction scalarSchema(scalar: ScalarType): JsonSchemaFragment {\n switch (scalar) {\n case 'string':\n return { type: 'string' };\n case 'boolean':\n return { type: 'boolean' };\n case 'int':\n return { type: 'integer' };\n case 'bigint':\n return { type: 'integer', format: 'int64' };\n case 'float':\n return { type: 'number' };\n case 'decimal':\n return { type: 'number' };\n case 'date':\n return { type: 'string', format: 'date' };\n case 'datetime':\n return { type: 'string', format: 'date-time' };\n case 'uuid':\n return { type: 'string', format: 'uuid' };\n case 'bytes':\n return { type: 'string', format: 'byte' };\n case 'json':\n return {};\n }\n}\n\n/** Pure recursive mapping of a `FieldType` to a JSON Schema fragment. */\nexport function renderFieldType(type: FieldType): JsonSchemaFragment {\n switch (type.kind) {\n case 'scalar':\n return scalarSchema(type.scalar);\n case 'enum':\n return { $ref: schemaRef(type.ref) };\n case 'unknown':\n return {};\n case 'ref':\n return { $ref: schemaRef(type.ref) };\n case 'map':\n return {\n type: 'object',\n additionalProperties:\n type.value.kind === 'unknown' ? true : renderFieldType(type.value),\n };\n case 'array':\n return { type: 'array', items: renderFieldType(type.element) };\n case 'union': {\n const schema: JsonSchemaFragment = {\n oneOf: type.variants.map((variant) => renderFieldType(variant)),\n };\n if (type.discriminator !== undefined) {\n const discriminator: JsonSchemaFragment = {\n propertyName: type.discriminator.propertyName,\n };\n if (type.discriminator.mapping !== undefined) {\n discriminator.mapping = Object.fromEntries(\n Object.entries(type.discriminator.mapping).map(([key, target]) => [\n key,\n schemaRef(target),\n ]),\n );\n }\n schema.discriminator = discriminator;\n }\n return schema;\n }\n }\n}\n\n/**\n * `Field.list` (legacy boolean, still present alongside `FieldType.kind ===\n * 'array'`) wraps the mapped schema one more time in an array — skipped when\n * `type.kind` is already `'array'`, to avoid a double array-of-array.\n */\nexport function wrapListed(\n schema: JsonSchemaFragment,\n type: FieldType,\n): JsonSchemaFragment {\n if (type.kind === 'array') {\n return schema;\n }\n return { type: 'array', items: schema };\n}\n\nfunction hasRef(schema: JsonSchemaFragment): boolean {\n return typeof schema.$ref === 'string';\n}\n\n/**\n * Nullability wrapping for a mapped schema, per OpenAPI version.\n *\n * - 3.1: widen `type` to include `'null'`; a `$ref` or `oneOf` (ref / union /\n * enum, and anything with no bare `type` keyword to widen) wraps instead in\n * `{ oneOf: [<schema>, { type: 'null' }] }`.\n * - 3.0: add `nullable: true` as a sibling keyword; a `$ref` cannot carry a\n * sibling per the 3.0 spec, so it wraps in `{ allOf: [<schema>], nullable:\n * true }` instead.\n */\nexport function wrapNullable(\n schema: JsonSchemaFragment,\n openapiVersion: '3.0' | '3.1',\n): JsonSchemaFragment {\n if (openapiVersion === '3.0') {\n if (hasRef(schema)) {\n return { allOf: [schema], nullable: true };\n }\n return { ...schema, nullable: true };\n }\n\n if (hasRef(schema) || schema.oneOf !== undefined) {\n return { oneOf: [schema, { type: 'null' }] };\n }\n if (typeof schema.type === 'string') {\n return { ...schema, type: [schema.type, 'null'] };\n }\n return { oneOf: [schema, { type: 'null' }] };\n}\n","/**\n * `Entity` -> a `components/schemas/<Entity>` object: own fields (via\n * `render/schema.ts` + `render/constraints.ts`), `required`, `additionalProperties`\n * and relations rendered nested via `$ref` (the \"deep\" family in `gen-zod`'s\n * vocabulary — see `packages/gen-zod/src/render/relations.ts`).\n *\n * Cross-source relations degrade to omitting the relation property entirely\n * (logged at `debug`), same choice `gen-zod`'s deep family makes: v1 cannot\n * reference a schema defined in another namespace's document. Circular\n * relations need no special handling — `$ref` in JSON Schema is natively\n * self- and mutually-recursive.\n */\nimport type { Logger } from '@kurotako/core';\nimport type { Entity, Relation, TypeAlias } from '@kurotako/ir';\nimport { isCrossSource } from '@kurotako/ir';\nimport { applyConstraints } from './constraints.js';\nimport {\n type JsonSchemaFragment,\n renderFieldType,\n schemaRef,\n wrapListed,\n wrapNullable,\n} from './schema.js';\n\nexport interface RenderEntityOptions {\n openapiVersion: '3.0' | '3.1';\n /** The namespace the entity belongs to, for cross-source relation detection. */\n namespace: string;\n logger?: Logger;\n}\n\nfunction renderRelation(\n rel: Relation,\n opts: RenderEntityOptions,\n): JsonSchemaFragment | null {\n if (isCrossSource(opts.namespace, rel)) {\n opts.logger?.debug(\n `gen-openapi: relation '${rel.name}' targets another source ('${rel.target.namespace}.${rel.target.entity}'); omitting it from the emitted schema`,\n );\n return null;\n }\n\n const target = schemaRef(rel.target.entity);\n let schema: JsonSchemaFragment =\n rel.cardinality === 'many'\n ? { type: 'array', items: { $ref: target } }\n : { $ref: target };\n\n if (rel.cardinality === 'one' && rel.optional) {\n schema = wrapNullable(schema, opts.openapiVersion);\n }\n\n return schema;\n}\n\nexport function renderEntity(\n entity: Entity,\n opts: RenderEntityOptions,\n): JsonSchemaFragment {\n const properties: Record<string, JsonSchemaFragment> = {};\n const required: string[] = [];\n\n for (const field of entity.fields) {\n let schema = renderFieldType(field.type);\n schema = applyConstraints(schema, field.type, field.constraints);\n if (field.list) {\n schema = wrapListed(schema, field.type);\n }\n if (field.nullable) {\n schema = wrapNullable(schema, opts.openapiVersion);\n }\n properties[field.name] = schema;\n if (!field.optional) {\n required.push(field.name);\n }\n }\n\n for (const relation of entity.relations) {\n const schema = renderRelation(relation, opts);\n if (schema !== null) {\n properties[relation.name] = schema;\n }\n }\n\n const schema: JsonSchemaFragment = {\n type: 'object',\n properties,\n };\n if (required.length > 0) {\n schema.required = required;\n }\n if (entity.additionalProperties !== undefined) {\n schema.additionalProperties =\n entity.additionalProperties.kind === 'unknown'\n ? true\n : renderFieldType(entity.additionalProperties);\n }\n if (entity.doc !== undefined) {\n schema.description = entity.doc;\n }\n\n return schema;\n}\n\nexport function renderTypeAlias(alias: TypeAlias): JsonSchemaFragment {\n const schema = renderFieldType(alias.type);\n if (alias.doc !== undefined) {\n return { ...schema, description: alias.doc };\n }\n return schema;\n}\n","/**\n * `Constraints` -> JSON Schema keywords, symmetric to `parser-openapi`'s\n * `field()`. `unique` has no JSON Schema keyword for object-property\n * uniqueness (`uniqueItems` only applies to arrays) and is dropped silently.\n */\nimport type { Constraints, FieldType, StringFormat } from '@kurotako/ir';\nimport type { JsonSchemaFragment } from './schema.js';\n\n/**\n * Inverse of `parser-openapi`'s `STRING_FORMATS`. `datetime`/`date`/`uuid`/\n * `cuid`/`cuid2`/`ulid` never reach here as a `Constraints.format` value —\n * those scalars carry their own fixed format via `renderFieldType` instead;\n * an unrecognised format (`cuid`/`cuid2`/`ulid`) emits no keyword, same\n * escape `parser-openapi` takes in the reverse direction.\n */\nconst FORMAT_KEYWORDS: Partial<Record<StringFormat, string>> = {\n email: 'email',\n url: 'uri',\n ipv4: 'ipv4',\n ipv6: 'ipv6',\n time: 'time',\n duration: 'duration',\n};\n\nexport function applyConstraints(\n schema: JsonSchemaFragment,\n type: FieldType,\n constraints: Constraints,\n): JsonSchemaFragment {\n const out: JsonSchemaFragment = { ...schema };\n\n if (constraints.min !== undefined) {\n out.minimum = constraints.min;\n }\n if (constraints.max !== undefined) {\n out.maximum = constraints.max;\n }\n if (constraints.minLength !== undefined) {\n out.minLength = constraints.minLength;\n }\n if (constraints.maxLength !== undefined) {\n out.maxLength = constraints.maxLength;\n }\n if (constraints.regex !== undefined) {\n out.pattern = constraints.regex;\n }\n if (\n constraints.format !== undefined &&\n type.kind === 'scalar' &&\n type.scalar === 'string'\n ) {\n const keyword = FORMAT_KEYWORDS[constraints.format];\n if (keyword !== undefined) {\n out.format = keyword;\n }\n }\n\n return out;\n}\n","/**\n * Assemble the full `OpenApiDocument` object for one namespace's `SourceIR`.\n *\n * `components.schemas` covers both `source.entities` and `source.typeAliases`\n * (unions, refs-only aliases, enums) — the IR's cross-reference pass already\n * guarantees the two never collide within one source (`docs/ir.md` \"Closed\n * points\" §3), so both can be merged into the same flat namespace safely.\n */\nimport type { Logger } from '@kurotako/core';\nimport type { EnumDef, SourceIR } from '@kurotako/ir';\nimport { OpenApiGenInvalidSchemaNameError } from './errors.js';\nimport type { OpenApiGeneratorOptions } from './options.js';\nimport { renderEntity, renderTypeAlias } from './render/entity.js';\nimport type { JsonSchemaFragment } from './render/schema.js';\n\n/**\n * Every reachable `EnumDef` of a source (source-level + entity-local, entity\n * shadowing on a name collision), each rendered as its own\n * `components/schemas` entry (`{ type: 'string', enum: [...] }`).\n *\n * Not called out as its own bullet in the technical design's \"Document\n * assembly\" section, but required for `$ref` resolvability: `render/schema.ts`\n * maps `FieldType.kind === 'enum'` to a bare `$ref` (mirroring\n * `parser-openapi`'s self-referencing `typeAliases[name] = { kind: 'enum', ref:\n * name }` entry), so every enum name needs a real schema behind it — the\n * synthetic self-referencing alias is skipped in favour of this one.\n */\nfunction collectEnums(source: SourceIR): EnumDef[] {\n const byName = new Map<string, EnumDef>();\n for (const def of Object.values(source.enums)) {\n byName.set(def.name, def);\n }\n for (const entity of Object.values(source.entities)) {\n for (const def of Object.values(entity.enums ?? {})) {\n byName.set(def.name, def);\n }\n }\n return [...byName.values()];\n}\n\nfunction renderEnum(def: EnumDef): JsonSchemaFragment {\n const schema: JsonSchemaFragment = {\n type: 'string',\n enum: def.values.map((value) => value.name),\n };\n if (def.doc !== undefined) {\n schema.description = def.doc;\n }\n return schema;\n}\n\nexport interface OpenApiDocument {\n openapi: string;\n info: { title: string; version: string };\n paths: Record<string, never>;\n components: { schemas: Record<string, JsonSchemaFragment> };\n}\n\n/** OpenAPI restricts a `components/schemas` key to this pattern. */\nconst SCHEMA_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;\n\nfunction pinnedOpenapiVersion(version: '3.0' | '3.1'): string {\n return version === '3.0' ? '3.0.3' : '3.1.0';\n}\n\nexport function buildDocument(\n source: SourceIR,\n options: OpenApiGeneratorOptions,\n namespace: string,\n logger?: Logger,\n): OpenApiDocument {\n const schemas: Record<string, JsonSchemaFragment> = {};\n const enumsByName = new Map(\n collectEnums(source).map((def) => [def.name, def]),\n );\n const names = [\n ...Object.keys(source.entities),\n ...enumsByName.keys(),\n ...Object.keys(source.typeAliases ?? {}).filter(\n (name) => !enumsByName.has(name) && source.entities[name] === undefined,\n ),\n ].sort((a, b) => a.localeCompare(b));\n\n for (const name of names) {\n if (!SCHEMA_NAME_PATTERN.test(name)) {\n throw new OpenApiGenInvalidSchemaNameError(name);\n }\n const entity = source.entities[name];\n if (entity !== undefined) {\n schemas[name] = renderEntity(entity, {\n openapiVersion: options.openapiVersion,\n namespace,\n logger,\n });\n continue;\n }\n const enumDef = enumsByName.get(name);\n if (enumDef !== undefined) {\n schemas[name] = renderEnum(enumDef);\n continue;\n }\n const alias = source.typeAliases?.[name];\n if (alias !== undefined) {\n schemas[name] = renderTypeAlias(alias);\n }\n }\n\n return {\n openapi: pinnedOpenapiVersion(options.openapiVersion),\n info: {\n title: options.title ?? namespace,\n version: options.version,\n },\n paths: {},\n components: { schemas },\n };\n}\n","/**\n * Valibot schema for `@kurotako/gen-openapi`'s `options`, plus the inferred type.\n * `@kurotako/config` validates a config entry's `options` against this schema and\n * curries it away before `@kurotako/core` sees the generator.\n *\n * `title` has no static default here: it depends on the namespace, which this\n * schema cannot see — `generator.ts` applies `options.title ?? namespace`.\n */\nimport * as v from 'valibot';\n\nexport const OpenApiGeneratorOptions = v.object({\n openapiVersion: v.optional(v.picklist(['3.0', '3.1']), '3.1'),\n format: v.optional(v.picklist(['json', 'yaml']), 'json'),\n title: v.optional(v.string()),\n version: v.optional(v.string(), '0.0.0'),\n});\n\nexport type OpenApiGeneratorOptions = v.InferOutput<\n typeof OpenApiGeneratorOptions\n>;\n","/**\n * `OpenApiDocument` -> JSON or YAML string.\n */\nimport * as YAML from 'yaml';\nimport type { OpenApiDocument } from './document.js';\nimport type { OpenApiGeneratorOptions } from './options.js';\n\nexport function serialize(\n document: OpenApiDocument,\n format: OpenApiGeneratorOptions['format'],\n): string {\n if (format === 'yaml') {\n return YAML.stringify(document);\n }\n return `${JSON.stringify(document, null, 2)}\\n`;\n}\n"],"mappings":"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","names":["schema"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/generator.ts","../src/artifact.ts","../src/render/schema.ts","../src/document.ts","../src/render/entity.ts","../src/render/constraints.ts","../src/options.ts","../src/serialize.ts"],"sourcesContent":["/**\n * `@kurotako/gen-openapi` error classes.\n *\n * `OpenApiGenError` is a plain `Error` subclass carrying a stable `code`; the\n * OpenAPI generator has no dependency on `@kurotako/core` at runtime, and\n * `@kurotako/core` wraps any throw from `generate()` as a `DriverError` for the\n * CLI's single `instanceof TakoError` catch.\n *\n * Codes: `openapi_gen_invalid_schema_name`.\n */\n\nexport class OpenApiGenError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n this.code = code;\n }\n}\n\n/**\n * An entity or type-alias name is not a valid OpenAPI `components/schemas` key\n * (the spec restricts it to `^[a-zA-Z0-9._-]+$`). `parser-openapi` never\n * produces such a name, but a name coming from `parser-prisma` (arbitrary model\n * name) is unconstrained.\n */\nexport class OpenApiGenInvalidSchemaNameError extends OpenApiGenError {\n readonly schemaName: string;\n\n constructor(schemaName: string) {\n super(\n 'openapi_gen_invalid_schema_name',\n `'${schemaName}' is not a valid OpenAPI components/schemas key (must match ^[a-zA-Z0-9._-]+$)`,\n );\n this.schemaName = schemaName;\n }\n}\n","/**\n * `openapiGenerator` — the `@kurotako/gen-openapi` driver.\n *\n * `@kurotako/config` validates `options` against `optionsSchema` and curries it\n * away; `@kurotako/core` then calls `generate(ctx)` with a namespace-filtered IR.\n * One `VirtualFile` per namespace: a single `openapi.<ext>` document, not one\n * file per entity — a spec is normally not split, and external tooling\n * consuming it expects one document.\n */\nimport { defineGenerator } from '@kurotako/config';\nimport type { GenerateContext, GenOutput, VirtualFile } from '@kurotako/core';\nimport { buildArtifact } from './artifact.js';\nimport { buildDocument } from './document.js';\nimport { OpenApiGeneratorOptions } from './options.js';\nimport { serialize } from './serialize.js';\n\nexport const openapiGenerator = defineGenerator({\n name: 'openapi',\n optionsSchema: OpenApiGeneratorOptions,\n\n generate(ctx: GenerateContext, options): GenOutput {\n const files: VirtualFile[] = [];\n const ext = options.format === 'yaml' ? 'yaml' : 'json';\n\n for (const [namespace, source] of Object.entries(ctx.ir.sources)) {\n const document = buildDocument(source, options, namespace, ctx.logger);\n files.push({\n path: `${namespace}/openapi/openapi.${ext}`,\n content: serialize(document, options.format),\n });\n }\n\n return { files, artifact: buildArtifact(ctx.ir, options) };\n },\n});\n","/**\n * Assemble the `GeneratorArtifact` — one `EntitySymbols` per `${namespace}.${entity}`,\n * pointing at the emitted document and the entity's `#/components/schemas/<name>`\n * pointer. No `dependsOn` (this generator reads only the IR), no\n * `peerDependencies` (the emitted document has no runtime import surface), no\n * `extra` (no consumer needs OpenAPI-specific artifact data yet).\n */\nimport type { EntitySymbols, GeneratorArtifact } from '@kurotako/core';\nimport type { IR } from '@kurotako/ir';\nimport { iterEntities } from '@kurotako/ir';\nimport type { OpenApiGeneratorOptions } from './options.js';\nimport { schemaRef } from './render/schema.js';\n\nexport function buildArtifact(\n ir: IR,\n options: OpenApiGeneratorOptions,\n): GeneratorArtifact {\n const ext = options.format === 'yaml' ? 'yaml' : 'json';\n const entities: Record<string, EntitySymbols> = {};\n\n for (const { namespace, entity } of iterEntities(ir)) {\n entities[`${namespace}.${entity.name}`] = {\n module: `${namespace}/openapi/openapi.${ext}`,\n symbols: { schema: schemaRef(entity.name) },\n };\n }\n\n return { entities };\n}\n","/**\n * `FieldType` -> JSON Schema fragment, symmetric to `parser-openapi`'s\n * `mapSchema` (inverted). Also carries the `Field.list` and nullability\n * wrapping, which are properties of a `Field`/`Relation`, not of a\n * `FieldType` — applied by the caller once the base fragment is built.\n */\nimport type { FieldType, ScalarType } from '@kurotako/ir';\n\nexport type JsonSchemaFragment = Record<string, unknown>;\n\nexport function schemaRef(name: string): string {\n return `#/components/schemas/${name}`;\n}\n\nfunction scalarSchema(scalar: ScalarType): JsonSchemaFragment {\n switch (scalar) {\n case 'string':\n return { type: 'string' };\n case 'boolean':\n return { type: 'boolean' };\n case 'int':\n return { type: 'integer' };\n case 'bigint':\n return { type: 'integer', format: 'int64' };\n case 'float':\n return { type: 'number' };\n case 'decimal':\n return { type: 'number' };\n case 'date':\n return { type: 'string', format: 'date' };\n case 'datetime':\n return { type: 'string', format: 'date-time' };\n case 'uuid':\n return { type: 'string', format: 'uuid' };\n case 'bytes':\n return { type: 'string', format: 'byte' };\n case 'json':\n return {};\n }\n}\n\n/** Pure recursive mapping of a `FieldType` to a JSON Schema fragment. */\nexport function renderFieldType(type: FieldType): JsonSchemaFragment {\n switch (type.kind) {\n case 'scalar':\n return scalarSchema(type.scalar);\n case 'enum':\n return { $ref: schemaRef(type.ref) };\n case 'unknown':\n return {};\n case 'ref':\n return { $ref: schemaRef(type.ref) };\n case 'map':\n return {\n type: 'object',\n additionalProperties:\n type.value.kind === 'unknown' ? true : renderFieldType(type.value),\n };\n case 'array':\n return { type: 'array', items: renderFieldType(type.element) };\n case 'union': {\n const schema: JsonSchemaFragment = {\n oneOf: type.variants.map((variant) => renderFieldType(variant)),\n };\n if (type.discriminator !== undefined) {\n const discriminator: JsonSchemaFragment = {\n propertyName: type.discriminator.propertyName,\n };\n if (type.discriminator.mapping !== undefined) {\n discriminator.mapping = Object.fromEntries(\n Object.entries(type.discriminator.mapping).map(([key, target]) => [\n key,\n schemaRef(target),\n ]),\n );\n }\n schema.discriminator = discriminator;\n }\n return schema;\n }\n }\n}\n\n/**\n * `Field.list` (legacy boolean, still present alongside `FieldType.kind ===\n * 'array'`) wraps the mapped schema one more time in an array — skipped when\n * `type.kind` is already `'array'`, to avoid a double array-of-array.\n */\nexport function wrapListed(\n schema: JsonSchemaFragment,\n type: FieldType,\n): JsonSchemaFragment {\n if (type.kind === 'array') {\n return schema;\n }\n return { type: 'array', items: schema };\n}\n\nfunction hasRef(schema: JsonSchemaFragment): boolean {\n return typeof schema.$ref === 'string';\n}\n\n/**\n * Nullability wrapping for a mapped schema, per OpenAPI version.\n *\n * - 3.1: widen `type` to include `'null'`; a `$ref` or `oneOf` (ref / union /\n * enum, and anything with no bare `type` keyword to widen) wraps instead in\n * `{ oneOf: [<schema>, { type: 'null' }] }`.\n * - 3.0: add `nullable: true` as a sibling keyword; a `$ref` cannot carry a\n * sibling per the 3.0 spec, so it wraps in `{ allOf: [<schema>], nullable:\n * true }` instead.\n */\nexport function wrapNullable(\n schema: JsonSchemaFragment,\n openapiVersion: '3.0' | '3.1',\n): JsonSchemaFragment {\n if (openapiVersion === '3.0') {\n if (hasRef(schema)) {\n return { allOf: [schema], nullable: true };\n }\n return { ...schema, nullable: true };\n }\n\n if (hasRef(schema) || schema.oneOf !== undefined) {\n return { oneOf: [schema, { type: 'null' }] };\n }\n if (typeof schema.type === 'string') {\n return { ...schema, type: [schema.type, 'null'] };\n }\n return { oneOf: [schema, { type: 'null' }] };\n}\n","/**\n * Assemble the full `OpenApiDocument` object for one namespace's `SourceIR`.\n *\n * `components.schemas` covers both `source.entities` and `source.typeAliases`\n * (unions, refs-only aliases, enums) — the IR's cross-reference pass already\n * guarantees the two never collide within one source (`docs/ir.md` \"Closed\n * points\" §3), so both can be merged into the same flat namespace safely.\n */\nimport type { Logger } from '@kurotako/core';\nimport type { EnumDef, SourceIR } from '@kurotako/ir';\nimport { nonRedundantTypeAliases } from '@kurotako/ir';\nimport { OpenApiGenInvalidSchemaNameError } from './errors.js';\nimport type { OpenApiGeneratorOptions } from './options.js';\nimport { renderEntity, renderTypeAlias } from './render/entity.js';\nimport type { JsonSchemaFragment } from './render/schema.js';\n\n/**\n * Every reachable `EnumDef` of a source (source-level + entity-local, entity\n * shadowing on a name collision), each rendered as its own\n * `components/schemas` entry (`{ type: 'string', enum: [...] }`).\n *\n * Not called out as its own bullet in the technical design's \"Document\n * assembly\" section, but required for `$ref` resolvability: `render/schema.ts`\n * maps `FieldType.kind === 'enum'` to a bare `$ref` (mirroring\n * `parser-openapi`'s self-referencing `typeAliases[name] = { kind: 'enum', ref:\n * name }` entry), so every enum name needs a real schema behind it — the\n * synthetic self-referencing alias is skipped in favour of this one.\n */\nfunction collectEnums(source: SourceIR): EnumDef[] {\n const byName = new Map<string, EnumDef>();\n for (const def of Object.values(source.enums)) {\n byName.set(def.name, def);\n }\n for (const entity of Object.values(source.entities)) {\n for (const def of Object.values(entity.enums ?? {})) {\n byName.set(def.name, def);\n }\n }\n return [...byName.values()];\n}\n\nfunction renderEnum(def: EnumDef): JsonSchemaFragment {\n const schema: JsonSchemaFragment = {\n type: 'string',\n enum: def.values.map((value) => value.name),\n };\n if (def.doc !== undefined) {\n schema.description = def.doc;\n }\n return schema;\n}\n\nexport interface OpenApiDocument {\n openapi: string;\n info: { title: string; version: string };\n paths: Record<string, never>;\n components: { schemas: Record<string, JsonSchemaFragment> };\n}\n\n/** OpenAPI restricts a `components/schemas` key to this pattern. */\nconst SCHEMA_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;\n\nfunction pinnedOpenapiVersion(version: '3.0' | '3.1'): string {\n return version === '3.0' ? '3.0.3' : '3.1.0';\n}\n\nexport function buildDocument(\n source: SourceIR,\n options: OpenApiGeneratorOptions,\n namespace: string,\n logger?: Logger,\n): OpenApiDocument {\n const schemas: Record<string, JsonSchemaFragment> = {};\n const enumsByName = new Map(\n collectEnums(source).map((def) => [def.name, def]),\n );\n const names = [\n ...Object.keys(source.entities),\n ...enumsByName.keys(),\n ...nonRedundantTypeAliases(source)\n .map((alias) => alias.name)\n .filter((name) => source.entities[name] === undefined),\n ].sort((a, b) => a.localeCompare(b));\n\n for (const name of names) {\n if (!SCHEMA_NAME_PATTERN.test(name)) {\n throw new OpenApiGenInvalidSchemaNameError(name);\n }\n const entity = source.entities[name];\n if (entity !== undefined) {\n schemas[name] = renderEntity(entity, {\n openapiVersion: options.openapiVersion,\n namespace,\n logger,\n });\n continue;\n }\n const enumDef = enumsByName.get(name);\n if (enumDef !== undefined) {\n schemas[name] = renderEnum(enumDef);\n continue;\n }\n const alias = source.typeAliases?.[name];\n if (alias !== undefined) {\n schemas[name] = renderTypeAlias(alias);\n }\n }\n\n return {\n openapi: pinnedOpenapiVersion(options.openapiVersion),\n info: {\n title: options.title ?? namespace,\n version: options.version,\n },\n paths: {},\n components: { schemas },\n };\n}\n","/**\n * `Entity` -> a `components/schemas/<Entity>` object: own fields (via\n * `render/schema.ts` + `render/constraints.ts`), `required`, `additionalProperties`\n * and relations rendered nested via `$ref` (the \"deep\" family in `gen-zod`'s\n * vocabulary — see `packages/gen-zod/src/render/relations.ts`).\n *\n * Cross-source relations degrade to omitting the relation property entirely\n * (logged at `debug`), same choice `gen-zod`'s deep family makes: v1 cannot\n * reference a schema defined in another namespace's document. Circular\n * relations need no special handling — `$ref` in JSON Schema is natively\n * self- and mutually-recursive.\n */\nimport type { Logger } from '@kurotako/core';\nimport type { Entity, Relation, TypeAlias } from '@kurotako/ir';\nimport { isCrossSource } from '@kurotako/ir';\nimport { applyConstraints } from './constraints.js';\nimport {\n type JsonSchemaFragment,\n renderFieldType,\n schemaRef,\n wrapListed,\n wrapNullable,\n} from './schema.js';\n\nexport interface RenderEntityOptions {\n openapiVersion: '3.0' | '3.1';\n /** The namespace the entity belongs to, for cross-source relation detection. */\n namespace: string;\n logger?: Logger;\n}\n\nfunction renderRelation(\n rel: Relation,\n opts: RenderEntityOptions,\n): JsonSchemaFragment | null {\n if (isCrossSource(opts.namespace, rel)) {\n opts.logger?.debug(\n `gen-openapi: relation '${rel.name}' targets another source ('${rel.target.namespace}.${rel.target.entity}'); omitting it from the emitted schema`,\n );\n return null;\n }\n\n const target = schemaRef(rel.target.entity);\n let schema: JsonSchemaFragment =\n rel.cardinality === 'many'\n ? { type: 'array', items: { $ref: target } }\n : { $ref: target };\n\n if (rel.cardinality === 'one' && rel.optional) {\n schema = wrapNullable(schema, opts.openapiVersion);\n }\n\n return schema;\n}\n\nexport function renderEntity(\n entity: Entity,\n opts: RenderEntityOptions,\n): JsonSchemaFragment {\n const properties: Record<string, JsonSchemaFragment> = {};\n const required: string[] = [];\n\n for (const field of entity.fields) {\n let schema = renderFieldType(field.type);\n schema = applyConstraints(schema, field.type, field.constraints);\n if (field.list) {\n schema = wrapListed(schema, field.type);\n }\n if (field.nullable) {\n schema = wrapNullable(schema, opts.openapiVersion);\n }\n properties[field.name] = schema;\n if (!field.optional) {\n required.push(field.name);\n }\n }\n\n for (const relation of entity.relations) {\n const schema = renderRelation(relation, opts);\n if (schema !== null) {\n properties[relation.name] = schema;\n }\n }\n\n const schema: JsonSchemaFragment = {\n type: 'object',\n properties,\n };\n if (required.length > 0) {\n schema.required = required;\n }\n if (entity.additionalProperties !== undefined) {\n schema.additionalProperties =\n entity.additionalProperties.kind === 'unknown'\n ? true\n : renderFieldType(entity.additionalProperties);\n }\n if (entity.doc !== undefined) {\n schema.description = entity.doc;\n }\n\n return schema;\n}\n\nexport function renderTypeAlias(alias: TypeAlias): JsonSchemaFragment {\n const schema = renderFieldType(alias.type);\n if (alias.doc !== undefined) {\n return { ...schema, description: alias.doc };\n }\n return schema;\n}\n","/**\n * `Constraints` -> JSON Schema keywords, symmetric to `parser-openapi`'s\n * `field()`. `unique` has no JSON Schema keyword for object-property\n * uniqueness (`uniqueItems` only applies to arrays) and is dropped silently.\n */\nimport type { Constraints, FieldType, StringFormat } from '@kurotako/ir';\nimport type { JsonSchemaFragment } from './schema.js';\n\n/**\n * Inverse of `parser-openapi`'s `STRING_FORMATS`. `datetime`/`date`/`uuid`/\n * `cuid`/`cuid2`/`ulid` never reach here as a `Constraints.format` value —\n * those scalars carry their own fixed format via `renderFieldType` instead;\n * an unrecognised format (`cuid`/`cuid2`/`ulid`) emits no keyword, same\n * escape `parser-openapi` takes in the reverse direction.\n */\nconst FORMAT_KEYWORDS: Partial<Record<StringFormat, string>> = {\n email: 'email',\n url: 'uri',\n ipv4: 'ipv4',\n ipv6: 'ipv6',\n time: 'time',\n duration: 'duration',\n};\n\nexport function applyConstraints(\n schema: JsonSchemaFragment,\n type: FieldType,\n constraints: Constraints,\n): JsonSchemaFragment {\n const out: JsonSchemaFragment = { ...schema };\n\n if (constraints.min !== undefined) {\n out.minimum = constraints.min;\n }\n if (constraints.max !== undefined) {\n out.maximum = constraints.max;\n }\n if (constraints.minLength !== undefined) {\n out.minLength = constraints.minLength;\n }\n if (constraints.maxLength !== undefined) {\n out.maxLength = constraints.maxLength;\n }\n if (constraints.regex !== undefined) {\n out.pattern = constraints.regex;\n }\n if (\n constraints.format !== undefined &&\n type.kind === 'scalar' &&\n type.scalar === 'string'\n ) {\n const keyword = FORMAT_KEYWORDS[constraints.format];\n if (keyword !== undefined) {\n out.format = keyword;\n }\n }\n\n return out;\n}\n","/**\n * Valibot schema for `@kurotako/gen-openapi`'s `options`, plus the inferred type.\n * `@kurotako/config` validates a config entry's `options` against this schema and\n * curries it away before `@kurotako/core` sees the generator.\n *\n * `title` has no static default here: it depends on the namespace, which this\n * schema cannot see — `generator.ts` applies `options.title ?? namespace`.\n */\nimport * as v from 'valibot';\n\nexport const OpenApiGeneratorOptions = v.object({\n openapiVersion: v.optional(v.picklist(['3.0', '3.1']), '3.1'),\n format: v.optional(v.picklist(['json', 'yaml']), 'json'),\n title: v.optional(v.string()),\n version: v.optional(v.string(), '0.0.0'),\n});\n\nexport type OpenApiGeneratorOptions = v.InferOutput<\n typeof OpenApiGeneratorOptions\n>;\n","/**\n * `OpenApiDocument` -> JSON or YAML string.\n */\nimport * as YAML from 'yaml';\nimport type { OpenApiDocument } from './document.js';\nimport type { OpenApiGeneratorOptions } from './options.js';\n\nexport function serialize(\n document: OpenApiDocument,\n format: OpenApiGeneratorOptions['format'],\n): string {\n if (format === 'yaml') {\n return YAML.stringify(document);\n }\n return `${JSON.stringify(document, null, 2)}\\n`;\n}\n"],"mappings":"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","names":["schema"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kurotako/gen-openapi",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "kurotako generator that emits an OpenAPI document (components/schemas) from the intermediate representation.",
5
5
  "keywords": [
6
6
  "kurotako",
@@ -47,18 +47,18 @@
47
47
  "access": "public"
48
48
  },
49
49
  "dependencies": {
50
- "@kurotako/ir": "^0.3.0",
50
+ "@kurotako/ir": "^0.4.0",
51
51
  "yaml": "^2.9.0",
52
52
  "valibot": "1.4.2"
53
53
  },
54
54
  "peerDependencies": {
55
- "@kurotako/config": "^0.1.4",
56
- "@kurotako/core": "^0.2.0"
55
+ "@kurotako/config": "^0.1.5",
56
+ "@kurotako/core": "^0.2.2"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@apidevtools/json-schema-ref-parser": "^15.1.3",
60
- "@kurotako/config": "^0.1.4",
61
- "@kurotako/core": "^0.2.0",
60
+ "@kurotako/config": "^0.1.5",
61
+ "@kurotako/core": "^0.2.2",
62
62
  "typescript": "5.9.3"
63
63
  }
64
64
  }