@kurotako/gen-openapi 0.2.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @kurotako/gen-openapi
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 147a177: New `read` variant (`UserReadSchema` / `UserReadDto`, plus `UserReadDeepSchema` / `UserReadDeepDto`) in `gen-zod` and `gen-typescript`, next to `full`, `create`, `update`, `where` and `select`. It is the shape of a stored row: fields that have a default (`id`, `createdAt`, ...) are required, and fields marked `hidden` are absent, nested relations included.
8
+
9
+ `@kurotako/ir` adds `Field.hidden`, `FieldBuilder.hidden()`, `readFields` and `isReadOptional`. `@kurotako/parser-prisma` marks a field hidden from the new `hidden` option (`{ User: ['passwordHash'] }`, Prisma 7 and 8, an unknown entry is an error) or from a `/// @kurotako.hidden` doc annotation (Prisma 7 mode; a Prisma 8 contract carries no doc comments). `gen-openapi` leaves hidden fields out of `components/schemas`.
10
+
11
+ `parser-prisma` also records ORM-generated defaults (`@default(uuid())`, `@updatedAt`, Prisma 8 generators) as expression defaults, so those fields read back as required, and keeps `format: 'time'` on `@db.Time` columns.
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [147a177]
16
+ - Updated dependencies [147a177]
17
+ - Updated dependencies [147a177]
18
+ - @kurotako/ir@0.7.0
19
+ - @kurotako/core@0.4.0
20
+ - @kurotako/config@0.2.1
21
+
22
+ ## 0.3.0
23
+
24
+ ### Minor Changes
25
+
26
+ - f58eccc: `openapiGenerator` now emits `<namespace>/<segment>/openapi.<ext>` and publishes the
27
+ same module in its artifact from `ctx.segment` instead of a hardcoded `openapi`, so it
28
+ can run as a private dependency of another generator. As a top-level generator the
29
+ segment is still `openapi` and the output is unchanged.
30
+
31
+ ### Patch Changes
32
+
33
+ - Updated dependencies [f58eccc]
34
+ - Updated dependencies [f58eccc]
35
+ - Updated dependencies [0ea85b1]
36
+ - @kurotako/config@0.2.0
37
+ - @kurotako/core@0.3.0
38
+ - @kurotako/ir@0.6.0
39
+
3
40
  ## 0.2.3
4
41
 
5
42
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -158,12 +158,12 @@ function wrapNullable(schema, openapiVersion) {
158
158
  }
159
159
 
160
160
  // src/artifact.ts
161
- function buildArtifact(ir, options) {
161
+ function buildArtifact(ir, options, segment) {
162
162
  const ext = options.format === "yaml" ? "yaml" : "json";
163
163
  const entities = {};
164
164
  for (const { namespace, entity } of (0, import_ir.iterEntities)(ir)) {
165
165
  entities[`${namespace}.${entity.name}`] = {
166
- module: `${namespace}/openapi/openapi.${ext}`,
166
+ module: `${namespace}/${segment}/openapi.${ext}`,
167
167
  symbols: { schema: schemaRef(entity.name) }
168
168
  };
169
169
  }
@@ -229,7 +229,7 @@ function renderRelation(rel, opts) {
229
229
  function renderEntity(entity, opts) {
230
230
  const properties = {};
231
231
  const required = [];
232
- for (const field of entity.fields) {
232
+ for (const field of (0, import_ir2.readFields)(entity)) {
233
233
  let schema2 = renderFieldType(field.type);
234
234
  schema2 = applyConstraints(schema2, field.type, field.constraints);
235
235
  if (field.list) {
@@ -372,11 +372,11 @@ var openapiGenerator = (0, import_config.defineGenerator)({
372
372
  for (const [namespace, source] of Object.entries(ctx.ir.sources)) {
373
373
  const document = buildDocument(source, options, namespace, ctx.logger);
374
374
  files.push({
375
- path: `${namespace}/openapi/openapi.${ext}`,
375
+ path: `${namespace}/${ctx.segment}/openapi.${ext}`,
376
376
  content: serialize(document, options.format)
377
377
  });
378
378
  }
379
- return { files, artifact: buildArtifact(ctx.ir, options) };
379
+ return { files, artifact: buildArtifact(ctx.ir, options, ctx.segment) };
380
380
  }
381
381
  });
382
382
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
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"]}
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}/${ctx.segment}/openapi.${ext}`,\n content: serialize(document, options.format),\n });\n }\n\n return { files, artifact: buildArtifact(ctx.ir, options, ctx.segment) };\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 segment: string,\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}/${segment}/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, readFields } 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 // The component is the shape a response carries: a hidden field never appears.\n for (const field of readFields(entity)) {\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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
+ import * as _kurotako_config from '@kurotako/config';
1
2
  import * as v from 'valibot';
2
- import { GenerateContext, GenOutput } from '@kurotako/core';
3
3
 
4
4
  /**
5
5
  * `@kurotako/gen-openapi` error classes.
@@ -28,22 +28,13 @@ declare class OpenApiGenInvalidSchemaNameError extends OpenApiGenError {
28
28
  constructor(schemaName: string);
29
29
  }
30
30
 
31
- declare const openapiGenerator: {
32
- name: string;
33
- dependsOn?: string[];
34
- optionalDependsOn?: string[];
35
- optionsSchema?: v.ObjectSchema<{
36
- readonly openapiVersion: v.OptionalSchema<v.PicklistSchema<["3.0", "3.1"], undefined>, "3.1">;
37
- readonly format: v.OptionalSchema<v.PicklistSchema<["json", "yaml"], undefined>, "json">;
38
- readonly title: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
39
- readonly version: v.OptionalSchema<v.StringSchema<undefined>, "0.0.0">;
40
- }, undefined> | undefined;
41
- generate(ctx: GenerateContext, options: {
42
- openapiVersion: "3.0" | "3.1";
43
- format: "json" | "yaml";
44
- title?: string | undefined;
45
- version: string;
46
- }): GenOutput | Promise<GenOutput>;
31
+ declare const openapiGenerator: _kurotako_config.GeneratorDriverBase<v.ObjectSchema<{
32
+ readonly openapiVersion: v.OptionalSchema<v.PicklistSchema<["3.0", "3.1"], undefined>, "3.1">;
33
+ readonly format: v.OptionalSchema<v.PicklistSchema<["json", "yaml"], undefined>, "json">;
34
+ readonly title: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
35
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, "0.0.0">;
36
+ }, undefined>> & {
37
+ dependsOn?: readonly [] | undefined;
47
38
  };
48
39
 
49
40
  /**
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import * as _kurotako_config from '@kurotako/config';
1
2
  import * as v from 'valibot';
2
- import { GenerateContext, GenOutput } from '@kurotako/core';
3
3
 
4
4
  /**
5
5
  * `@kurotako/gen-openapi` error classes.
@@ -28,22 +28,13 @@ declare class OpenApiGenInvalidSchemaNameError extends OpenApiGenError {
28
28
  constructor(schemaName: string);
29
29
  }
30
30
 
31
- declare const openapiGenerator: {
32
- name: string;
33
- dependsOn?: string[];
34
- optionalDependsOn?: string[];
35
- optionsSchema?: v.ObjectSchema<{
36
- readonly openapiVersion: v.OptionalSchema<v.PicklistSchema<["3.0", "3.1"], undefined>, "3.1">;
37
- readonly format: v.OptionalSchema<v.PicklistSchema<["json", "yaml"], undefined>, "json">;
38
- readonly title: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
39
- readonly version: v.OptionalSchema<v.StringSchema<undefined>, "0.0.0">;
40
- }, undefined> | undefined;
41
- generate(ctx: GenerateContext, options: {
42
- openapiVersion: "3.0" | "3.1";
43
- format: "json" | "yaml";
44
- title?: string | undefined;
45
- version: string;
46
- }): GenOutput | Promise<GenOutput>;
31
+ declare const openapiGenerator: _kurotako_config.GeneratorDriverBase<v.ObjectSchema<{
32
+ readonly openapiVersion: v.OptionalSchema<v.PicklistSchema<["3.0", "3.1"], undefined>, "3.1">;
33
+ readonly format: v.OptionalSchema<v.PicklistSchema<["json", "yaml"], undefined>, "json">;
34
+ readonly title: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
35
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, "0.0.0">;
36
+ }, undefined>> & {
37
+ dependsOn?: readonly [] | undefined;
47
38
  };
48
39
 
49
40
  /**
package/dist/index.js CHANGED
@@ -119,12 +119,12 @@ function wrapNullable(schema, openapiVersion) {
119
119
  }
120
120
 
121
121
  // src/artifact.ts
122
- function buildArtifact(ir, options) {
122
+ function buildArtifact(ir, options, segment) {
123
123
  const ext = options.format === "yaml" ? "yaml" : "json";
124
124
  const entities = {};
125
125
  for (const { namespace, entity } of iterEntities(ir)) {
126
126
  entities[`${namespace}.${entity.name}`] = {
127
- module: `${namespace}/openapi/openapi.${ext}`,
127
+ module: `${namespace}/${segment}/openapi.${ext}`,
128
128
  symbols: { schema: schemaRef(entity.name) }
129
129
  };
130
130
  }
@@ -135,7 +135,7 @@ function buildArtifact(ir, options) {
135
135
  import { nonRedundantTypeAliases } from "@kurotako/ir";
136
136
 
137
137
  // src/render/entity.ts
138
- import { isCrossSource } from "@kurotako/ir";
138
+ import { isCrossSource, readFields } from "@kurotako/ir";
139
139
 
140
140
  // src/render/constraints.ts
141
141
  var FORMAT_KEYWORDS = {
@@ -190,7 +190,7 @@ function renderRelation(rel, opts) {
190
190
  function renderEntity(entity, opts) {
191
191
  const properties = {};
192
192
  const required = [];
193
- for (const field of entity.fields) {
193
+ for (const field of readFields(entity)) {
194
194
  let schema2 = renderFieldType(field.type);
195
195
  schema2 = applyConstraints(schema2, field.type, field.constraints);
196
196
  if (field.list) {
@@ -333,11 +333,11 @@ var openapiGenerator = defineGenerator({
333
333
  for (const [namespace, source] of Object.entries(ctx.ir.sources)) {
334
334
  const document = buildDocument(source, options, namespace, ctx.logger);
335
335
  files.push({
336
- path: `${namespace}/openapi/openapi.${ext}`,
336
+ path: `${namespace}/${ctx.segment}/openapi.${ext}`,
337
337
  content: serialize(document, options.format)
338
338
  });
339
339
  }
340
- return { files, artifact: buildArtifact(ctx.ir, options) };
340
+ return { files, artifact: buildArtifact(ctx.ir, options, ctx.segment) };
341
341
  }
342
342
  });
343
343
  export {
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/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"]}
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}/${ctx.segment}/openapi.${ext}`,\n content: serialize(document, options.format),\n });\n }\n\n return { files, artifact: buildArtifact(ctx.ir, options, ctx.segment) };\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 segment: string,\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}/${segment}/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, readFields } 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 // The component is the shape a response carries: a hidden field never appears.\n for (const field of readFields(entity)) {\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.3",
3
+ "version": "0.4.0",
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.5.0",
50
+ "@kurotako/ir": "^0.7.0",
51
51
  "yaml": "^2.9.0",
52
52
  "valibot": "1.4.2"
53
53
  },
54
54
  "peerDependencies": {
55
- "@kurotako/config": "^0.1.6",
56
- "@kurotako/core": "^0.2.3"
55
+ "@kurotako/config": "^0.2.1",
56
+ "@kurotako/core": "^0.4.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@apidevtools/json-schema-ref-parser": "^15.1.3",
60
- "@kurotako/config": "^0.1.6",
61
- "@kurotako/core": "^0.2.3",
60
+ "@kurotako/config": "^0.2.1",
61
+ "@kurotako/core": "^0.4.0",
62
62
  "typescript": "5.9.3"
63
63
  }
64
64
  }