@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 +37 -0
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -17
- package/dist/index.d.ts +8 -17
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
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
|
|
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
|
|
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
|
|
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:
|
package/dist/index.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACWO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAChC;AAAA,EAET,YAAY,MAAc,SAAiB,SAA+B;AACxE,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO,WAAW;AACvB,SAAK,OAAO;AAAA,EACd;AACF;AAQO,IAAM,mCAAN,cAA+C,gBAAgB;AAAA,EAC3D;AAAA,EAET,YAAY,YAAoB;AAC9B;AAAA,MACE;AAAA,MACA,IAAI,UAAU;AAAA,IAChB;AACA,SAAK,aAAa;AAAA,EACpB;AACF;;;AC5BA,oBAAgC;;;ACAhC,gBAA6B;;;ACCtB,SAAS,UAAU,MAAsB;AAC9C,SAAO,wBAAwB,IAAI;AACrC;AAEA,SAAS,aAAa,QAAwC;AAC5D,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,EAAE,MAAM,SAAS;AAAA,IAC1B,KAAK;AACH,aAAO,EAAE,MAAM,UAAU;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,MAAM,UAAU;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,MAAM,WAAW,QAAQ,QAAQ;AAAA,IAC5C,KAAK;AACH,aAAO,EAAE,MAAM,SAAS;AAAA,IAC1B,KAAK;AACH,aAAO,EAAE,MAAM,SAAS;AAAA,IAC1B,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,QAAQ,OAAO;AAAA,IAC1C,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,QAAQ,YAAY;AAAA,IAC/C,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,QAAQ,OAAO;AAAA,IAC1C,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,QAAQ,OAAO;AAAA,IAC1C,KAAK;AACH,aAAO,CAAC;AAAA,EACZ;AACF;AAGO,SAAS,gBAAgB,MAAqC;AACnE,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,aAAa,KAAK,MAAM;AAAA,IACjC,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,KAAK,GAAG,EAAE;AAAA,IACrC,KAAK;AACH,aAAO,CAAC;AAAA,IACV,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,KAAK,GAAG,EAAE;AAAA,IACrC,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,sBACE,KAAK,MAAM,SAAS,YAAY,OAAO,gBAAgB,KAAK,KAAK;AAAA,MACrE;AAAA,IACF,KAAK;AACH,aAAO,EAAE,MAAM,SAAS,OAAO,gBAAgB,KAAK,OAAO,EAAE;AAAA,IAC/D,KAAK,SAAS;AACZ,YAAM,SAA6B;AAAA,QACjC,OAAO,KAAK,SAAS,IAAI,CAAC,YAAY,gBAAgB,OAAO,CAAC;AAAA,MAChE;AACA,UAAI,KAAK,kBAAkB,QAAW;AACpC,cAAM,gBAAoC;AAAA,UACxC,cAAc,KAAK,cAAc;AAAA,QACnC;AACA,YAAI,KAAK,cAAc,YAAY,QAAW;AAC5C,wBAAc,UAAU,OAAO;AAAA,YAC7B,OAAO,QAAQ,KAAK,cAAc,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,MAAM,MAAM;AAAA,cAChE;AAAA,cACA,UAAU,MAAM;AAAA,YAClB,CAAC;AAAA,UACH;AAAA,QACF;AACA,eAAO,gBAAgB;AAAA,MACzB;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAOO,SAAS,WACd,QACA,MACoB;AACpB,MAAI,KAAK,SAAS,SAAS;AACzB,WAAO;AAAA,EACT;AACA,SAAO,EAAE,MAAM,SAAS,OAAO,OAAO;AACxC;AAEA,SAAS,OAAO,QAAqC;AACnD,SAAO,OAAO,OAAO,SAAS;AAChC;AAYO,SAAS,aACd,QACA,gBACoB;AACpB,MAAI,mBAAmB,OAAO;AAC5B,QAAI,OAAO,MAAM,GAAG;AAClB,aAAO,EAAE,OAAO,CAAC,MAAM,GAAG,UAAU,KAAK;AAAA,IAC3C;AACA,WAAO,EAAE,GAAG,QAAQ,UAAU,KAAK;AAAA,EACrC;AAEA,MAAI,OAAO,MAAM,KAAK,OAAO,UAAU,QAAW;AAChD,WAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,EAAE;AAAA,EAC7C;AACA,MAAI,OAAO,OAAO,SAAS,UAAU;AACnC,WAAO,EAAE,GAAG,QAAQ,MAAM,CAAC,OAAO,MAAM,MAAM,EAAE;AAAA,EAClD;AACA,SAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,EAAE;AAC7C;;;ADrHO,SAAS,cACd,IACA,SACA,SACmB;AACnB,QAAM,MAAM,QAAQ,WAAW,SAAS,SAAS;AACjD,QAAM,WAA0C,CAAC;AAEjD,aAAW,EAAE,WAAW,OAAO,SAAK,wBAAa,EAAE,GAAG;AACpD,aAAS,GAAG,SAAS,IAAI,OAAO,IAAI,EAAE,IAAI;AAAA,MACxC,QAAQ,GAAG,SAAS,IAAI,OAAO,YAAY,GAAG;AAAA,MAC9C,SAAS,EAAE,QAAQ,UAAU,OAAO,IAAI,EAAE;AAAA,IAC5C;AAAA,EACF;AAEA,SAAO,EAAE,SAAS;AACpB;;;AEnBA,IAAAA,aAAwC;;;ACIxC,IAAAC,aAA0C;;;ACC1C,IAAM,kBAAyD;AAAA,EAC7D,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,UAAU;AACZ;AAEO,SAAS,iBACd,QACA,MACA,aACoB;AACpB,QAAM,MAA0B,EAAE,GAAG,OAAO;AAE5C,MAAI,YAAY,QAAQ,QAAW;AACjC,QAAI,UAAU,YAAY;AAAA,EAC5B;AACA,MAAI,YAAY,QAAQ,QAAW;AACjC,QAAI,UAAU,YAAY;AAAA,EAC5B;AACA,MAAI,YAAY,cAAc,QAAW;AACvC,QAAI,YAAY,YAAY;AAAA,EAC9B;AACA,MAAI,YAAY,cAAc,QAAW;AACvC,QAAI,YAAY,YAAY;AAAA,EAC9B;AACA,MAAI,YAAY,UAAU,QAAW;AACnC,QAAI,UAAU,YAAY;AAAA,EAC5B;AACA,MACE,YAAY,WAAW,UACvB,KAAK,SAAS,YACd,KAAK,WAAW,UAChB;AACA,UAAM,UAAU,gBAAgB,YAAY,MAAM;AAClD,QAAI,YAAY,QAAW;AACzB,UAAI,SAAS;AAAA,IACf;AAAA,EACF;AAEA,SAAO;AACT;;;AD3BA,SAAS,eACP,KACA,MAC2B;AAC3B,UAAI,0BAAc,KAAK,WAAW,GAAG,GAAG;AACtC,SAAK,QAAQ;AAAA,MACX,0BAA0B,IAAI,IAAI,8BAA8B,IAAI,OAAO,SAAS,IAAI,IAAI,OAAO,MAAM;AAAA,IAC3G;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,UAAU,IAAI,OAAO,MAAM;AAC1C,MAAI,SACF,IAAI,gBAAgB,SAChB,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,OAAO,EAAE,IACzC,EAAE,MAAM,OAAO;AAErB,MAAI,IAAI,gBAAgB,SAAS,IAAI,UAAU;AAC7C,aAAS,aAAa,QAAQ,KAAK,cAAc;AAAA,EACnD;AAEA,SAAO;AACT;AAEO,SAAS,aACd,QACA,MACoB;AACpB,QAAM,aAAiD,CAAC;AACxD,QAAM,WAAqB,CAAC;AAG5B,aAAW,aAAS,uBAAW,MAAM,GAAG;AACtC,QAAIC,UAAS,gBAAgB,MAAM,IAAI;AACvC,IAAAA,UAAS,iBAAiBA,SAAQ,MAAM,MAAM,MAAM,WAAW;AAC/D,QAAI,MAAM,MAAM;AACd,MAAAA,UAAS,WAAWA,SAAQ,MAAM,IAAI;AAAA,IACxC;AACA,QAAI,MAAM,UAAU;AAClB,MAAAA,UAAS,aAAaA,SAAQ,KAAK,cAAc;AAAA,IACnD;AACA,eAAW,MAAM,IAAI,IAAIA;AACzB,QAAI,CAAC,MAAM,UAAU;AACnB,eAAS,KAAK,MAAM,IAAI;AAAA,IAC1B;AAAA,EACF;AAEA,aAAW,YAAY,OAAO,WAAW;AACvC,UAAMA,UAAS,eAAe,UAAU,IAAI;AAC5C,QAAIA,YAAW,MAAM;AACnB,iBAAW,SAAS,IAAI,IAAIA;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,SAA6B;AAAA,IACjC,MAAM;AAAA,IACN;AAAA,EACF;AACA,MAAI,SAAS,SAAS,GAAG;AACvB,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,OAAO,yBAAyB,QAAW;AAC7C,WAAO,uBACL,OAAO,qBAAqB,SAAS,YACjC,OACA,gBAAgB,OAAO,oBAAoB;AAAA,EACnD;AACA,MAAI,OAAO,QAAQ,QAAW;AAC5B,WAAO,cAAc,OAAO;AAAA,EAC9B;AAEA,SAAO;AACT;AAEO,SAAS,gBAAgB,OAAsC;AACpE,QAAM,SAAS,gBAAgB,MAAM,IAAI;AACzC,MAAI,MAAM,QAAQ,QAAW;AAC3B,WAAO,EAAE,GAAG,QAAQ,aAAa,MAAM,IAAI;AAAA,EAC7C;AACA,SAAO;AACT;;;ADnFA,SAAS,aAAa,QAA6B;AACjD,QAAM,SAAS,oBAAI,IAAqB;AACxC,aAAW,OAAO,OAAO,OAAO,OAAO,KAAK,GAAG;AAC7C,WAAO,IAAI,IAAI,MAAM,GAAG;AAAA,EAC1B;AACA,aAAW,UAAU,OAAO,OAAO,OAAO,QAAQ,GAAG;AACnD,eAAW,OAAO,OAAO,OAAO,OAAO,SAAS,CAAC,CAAC,GAAG;AACnD,aAAO,IAAI,IAAI,MAAM,GAAG;AAAA,IAC1B;AAAA,EACF;AACA,SAAO,CAAC,GAAG,OAAO,OAAO,CAAC;AAC5B;AAEA,SAAS,WAAW,KAAkC;AACpD,QAAM,SAA6B;AAAA,IACjC,MAAM;AAAA,IACN,MAAM,IAAI,OAAO,IAAI,CAAC,UAAU,MAAM,IAAI;AAAA,EAC5C;AACA,MAAI,IAAI,QAAQ,QAAW;AACzB,WAAO,cAAc,IAAI;AAAA,EAC3B;AACA,SAAO;AACT;AAUA,IAAM,sBAAsB;AAE5B,SAAS,qBAAqB,SAAgC;AAC5D,SAAO,YAAY,QAAQ,UAAU;AACvC;AAEO,SAAS,cACd,QACA,SACA,WACA,QACiB;AACjB,QAAM,UAA8C,CAAC;AACrD,QAAM,cAAc,IAAI;AAAA,IACtB,aAAa,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,GAAG,CAAC;AAAA,EACnD;AACA,QAAM,QAAQ;AAAA,IACZ,GAAG,OAAO,KAAK,OAAO,QAAQ;AAAA,IAC9B,GAAG,YAAY,KAAK;AAAA,IACpB,OAAG,oCAAwB,MAAM,EAC9B,IAAI,CAAC,UAAU,MAAM,IAAI,EACzB,OAAO,CAAC,SAAS,OAAO,SAAS,IAAI,MAAM,MAAS;AAAA,EACzD,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AAEnC,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,oBAAoB,KAAK,IAAI,GAAG;AACnC,YAAM,IAAI,iCAAiC,IAAI;AAAA,IACjD;AACA,UAAM,SAAS,OAAO,SAAS,IAAI;AACnC,QAAI,WAAW,QAAW;AACxB,cAAQ,IAAI,IAAI,aAAa,QAAQ;AAAA,QACnC,gBAAgB,QAAQ;AAAA,QACxB;AAAA,QACA;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,UAAM,UAAU,YAAY,IAAI,IAAI;AACpC,QAAI,YAAY,QAAW;AACzB,cAAQ,IAAI,IAAI,WAAW,OAAO;AAClC;AAAA,IACF;AACA,UAAM,QAAQ,OAAO,cAAc,IAAI;AACvC,QAAI,UAAU,QAAW;AACvB,cAAQ,IAAI,IAAI,gBAAgB,KAAK;AAAA,IACvC;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS,qBAAqB,QAAQ,cAAc;AAAA,IACpD,MAAM;AAAA,MACJ,OAAO,QAAQ,SAAS;AAAA,MACxB,SAAS,QAAQ;AAAA,IACnB;AAAA,IACA,OAAO,CAAC;AAAA,IACR,YAAY,EAAE,QAAQ;AAAA,EACxB;AACF;;;AG7GA,QAAmB;AAEZ,IAAM,0BAA4B,SAAO;AAAA,EAC9C,gBAAkB,WAAW,WAAS,CAAC,OAAO,KAAK,CAAC,GAAG,KAAK;AAAA,EAC5D,QAAU,WAAW,WAAS,CAAC,QAAQ,MAAM,CAAC,GAAG,MAAM;AAAA,EACvD,OAAS,WAAW,SAAO,CAAC;AAAA,EAC5B,SAAW,WAAW,SAAO,GAAG,OAAO;AACzC,CAAC;;;ACZD,WAAsB;AAIf,SAAS,UACd,UACA,QACQ;AACR,MAAI,WAAW,QAAQ;AACrB,WAAY,eAAU,QAAQ;AAAA,EAChC;AACA,SAAO,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA;AAC7C;;;APCO,IAAM,uBAAmB,+BAAgB;AAAA,EAC9C,MAAM;AAAA,EACN,eAAe;AAAA,EAEf,SAAS,KAAsB,SAAoB;AACjD,UAAM,QAAuB,CAAC;AAC9B,UAAM,MAAM,QAAQ,WAAW,SAAS,SAAS;AAEjD,eAAW,CAAC,WAAW,MAAM,KAAK,OAAO,QAAQ,IAAI,GAAG,OAAO,GAAG;AAChE,YAAM,WAAW,cAAc,QAAQ,SAAS,WAAW,IAAI,MAAM;AACrE,YAAM,KAAK;AAAA,QACT,MAAM,GAAG,SAAS,IAAI,IAAI,OAAO,YAAY,GAAG;AAAA,QAChD,SAAS,UAAU,UAAU,QAAQ,MAAM;AAAA,MAC7C,CAAC;AAAA,IACH;AAEA,WAAO,EAAE,OAAO,UAAU,cAAc,IAAI,IAAI,SAAS,IAAI,OAAO,EAAE;AAAA,EACxE;AACF,CAAC;","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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
56
|
-
"@kurotako/core": "^0.
|
|
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
|
|
61
|
-
"@kurotako/core": "^0.
|
|
60
|
+
"@kurotako/config": "^0.2.1",
|
|
61
|
+
"@kurotako/core": "^0.4.0",
|
|
62
62
|
"typescript": "5.9.3"
|
|
63
63
|
}
|
|
64
64
|
}
|