@haddowg/json-api-codegen 1.7.1 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.mts CHANGED
@@ -1,11 +1,11 @@
1
- import { r as CodegenConfig } from "./index-CeSU_OuC.mjs";
1
+ import { r as CodegenConfig } from "./index-COQQrn7a.mjs";
2
2
 
3
3
  //#region src/cli.d.ts
4
- declare const USAGE = "Usage: json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--check]";
4
+ declare const USAGE = "Usage: json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--examples] [--check]";
5
5
  /**
6
6
  * Thin CLI wrapper. Real arg parsing + `japi.config.ts` discovery land with the
7
7
  * generator itself; this stub establishes the bin entry and the shape of invocation.
8
- * `--check` is a boolean flag (drift gate) with no value.
8
+ * `--check` (drift gate) and `--examples` are boolean flags with no value.
9
9
  */
10
10
  declare function parseArgs(argv: string[]): Partial<CodegenConfig>;
11
11
  declare function run(argv: string[]): Promise<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";;;cAKa,KAAA;AAAb;;;;AAAkB;AAAlB,iBAQgB,SAAA,CAAU,IAAA,aAAiB,OAAO,CAAC,aAAA;AAAA,iBA6B7B,GAAA,CAAI,IAAA,aAAiB,OAAO;;;;;;iBAgClC,OAAA,CAAQ,SAAA,UAAmB,KAAyB"}
1
+ {"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";;;cAKa,KAAA;AAAb;;;;AAAkB;AAAlB,iBAQgB,SAAA,CAAU,IAAA,aAAiB,OAAO,CAAC,aAAA;AAAA,iBAiC7B,GAAA,CAAI,IAAA,aAAiB,OAAO;;;;;;iBA+BlC,OAAA,CAAQ,SAAA,UAAmB,KAAyB"}
package/dist/cli.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { n as generate, r as schemasOutputPath, t as check } from "./src-DhZyiWge.mjs";
2
+ import { i as generateFiles, t as check } from "./src-AWTGjMiX.mjs";
3
3
  import { realpathSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  //#region src/cli.ts
6
- const USAGE = "Usage: json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--check]";
6
+ const USAGE = "Usage: json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--examples] [--check]";
7
7
  /**
8
8
  * Thin CLI wrapper. Real arg parsing + `japi.config.ts` discovery land with the
9
9
  * generator itself; this stub establishes the bin entry and the shape of invocation.
10
- * `--check` is a boolean flag (drift gate) with no value.
10
+ * `--check` (drift gate) and `--examples` are boolean flags with no value.
11
11
  */
12
12
  function parseArgs(argv) {
13
13
  const config = {};
@@ -17,6 +17,10 @@ function parseArgs(argv) {
17
17
  config.check = true;
18
18
  continue;
19
19
  }
20
+ if (arg === "--examples") {
21
+ config.examples = true;
22
+ continue;
23
+ }
20
24
  const next = argv[i + 1];
21
25
  if (next === void 0) continue;
22
26
  if (arg === "--input") {
@@ -50,9 +54,8 @@ async function run(argv) {
50
54
  }
51
55
  return 0;
52
56
  }
53
- await generate(config);
54
- console.error(`wrote ${config.output}`);
55
- if (config.schemas !== void 0) console.error(`wrote ${schemasOutputPath(config.output)}`);
57
+ const written = await generateFiles(config);
58
+ for (const path of written) console.error(`wrote ${path}`);
56
59
  return 0;
57
60
  }
58
61
  /**
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpathSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { check, generate, schemasOutputPath, type CodegenConfig } from './index'\n\nexport const USAGE =\n 'Usage: json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--check]'\n\n/**\n * Thin CLI wrapper. Real arg parsing + `japi.config.ts` discovery land with the\n * generator itself; this stub establishes the bin entry and the shape of invocation.\n * `--check` is a boolean flag (drift gate) with no value.\n */\nexport function parseArgs(argv: string[]): Partial<CodegenConfig> {\n const config: Partial<CodegenConfig> = {}\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--check') {\n config.check = true\n continue\n }\n const next = argv[i + 1]\n if (next === undefined) {\n continue\n }\n if (arg === '--input') {\n config.input = next\n i++\n } else if (arg === '--output') {\n config.output = next\n i++\n } else if (arg === '--server') {\n config.server = next\n i++\n } else if (arg === '--schemas') {\n config.schemas = next\n i++\n }\n }\n return config\n}\n\nexport async function run(argv: string[]): Promise<number> {\n const config = parseArgs(argv)\n if (!config.input || !config.output) {\n console.error(USAGE)\n return 1\n }\n if (config.check) {\n const result = await check(config as CodegenConfig)\n for (const { path, upToDate } of result.artifacts) {\n console.error(`${upToDate ? 'ok ' : 'DRIFT'} ${path}`)\n }\n if (!result.ok) {\n console.error(\n 'Generated client is out of date with the source spec. Regenerate (drop --check) and commit the result.',\n )\n return 1\n }\n return 0\n }\n await generate(config as CodegenConfig)\n console.error(`wrote ${config.output}`)\n if (config.schemas !== undefined) {\n console.error(`wrote ${schemasOutputPath(config.output)}`)\n }\n return 0\n}\n\n/**\n * Whether the module at `moduleUrl` is the script node was asked to run (`argv1`). Compares real\n * paths, because a package manager installs the bin as a symlink (`node_modules/.bin/...`) and\n * node reports the symlink in `process.argv[1]` but the resolved file in `import.meta.url`.\n */\nexport function isEntry(moduleUrl: string, argv1: string | undefined): boolean {\n if (argv1 === undefined) {\n return false\n }\n try {\n return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(argv1)\n } catch {\n return false\n }\n}\n\nasync function main(): Promise<void> {\n process.exitCode = await run(process.argv.slice(2))\n}\n\n// Only run when invoked as the bin, not when imported (e.g. by tests).\nif (isEntry(import.meta.url, process.argv[1])) {\n main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error)\n process.exitCode = 1\n })\n}\n"],"mappings":";;;;;AAKA,MAAa,QACX;;;;;;AAOF,SAAgB,UAAU,MAAwC;CAChE,MAAM,SAAiC,CAAC;CACxC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;EACjB,IAAI,QAAQ,WAAW;GACrB,OAAO,QAAQ;GACf;EACF;EACA,MAAM,OAAO,KAAK,IAAI;EACtB,IAAI,SAAS,KAAA,GACX;EAEF,IAAI,QAAQ,WAAW;GACrB,OAAO,QAAQ;GACf;EACF,OAAO,IAAI,QAAQ,YAAY;GAC7B,OAAO,SAAS;GAChB;EACF,OAAO,IAAI,QAAQ,YAAY;GAC7B,OAAO,SAAS;GAChB;EACF,OAAO,IAAI,QAAQ,aAAa;GAC9B,OAAO,UAAU;GACjB;EACF;CACF;CACA,OAAO;AACT;AAEA,eAAsB,IAAI,MAAiC;CACzD,MAAM,SAAS,UAAU,IAAI;CAC7B,IAAI,CAAC,OAAO,SAAS,CAAC,OAAO,QAAQ;EACnC,QAAQ,MAAM,KAAK;EACnB,OAAO;CACT;CACA,IAAI,OAAO,OAAO;EAChB,MAAM,SAAS,MAAM,MAAM,MAAuB;EAClD,KAAK,MAAM,EAAE,MAAM,cAAc,OAAO,WACtC,QAAQ,MAAM,GAAG,WAAW,SAAS,QAAQ,GAAG,MAAM;EAExD,IAAI,CAAC,OAAO,IAAI;GACd,QAAQ,MACN,wGACF;GACA,OAAO;EACT;EACA,OAAO;CACT;CACA,MAAM,SAAS,MAAuB;CACtC,QAAQ,MAAM,SAAS,OAAO,QAAQ;CACtC,IAAI,OAAO,YAAY,KAAA,GACrB,QAAQ,MAAM,SAAS,kBAAkB,OAAO,MAAM,GAAG;CAE3D,OAAO;AACT;;;;;;AAOA,SAAgB,QAAQ,WAAmB,OAAoC;CAC7E,IAAI,UAAU,KAAA,GACZ,OAAO;CAET,IAAI;EACF,OAAO,aAAa,cAAc,SAAS,CAAC,MAAM,aAAa,KAAK;CACtE,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAe,OAAsB;CACnC,QAAQ,WAAW,MAAM,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC;AACpD;AAGA,IAAI,QAAQ,OAAO,KAAK,KAAK,QAAQ,KAAK,EAAE,GAC1C,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;CAC5D,QAAQ,WAAW;AACrB,CAAC"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpathSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { check, generateFiles, type CodegenConfig } from './index'\n\nexport const USAGE =\n 'Usage: json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--examples] [--check]'\n\n/**\n * Thin CLI wrapper. Real arg parsing + `japi.config.ts` discovery land with the\n * generator itself; this stub establishes the bin entry and the shape of invocation.\n * `--check` (drift gate) and `--examples` are boolean flags with no value.\n */\nexport function parseArgs(argv: string[]): Partial<CodegenConfig> {\n const config: Partial<CodegenConfig> = {}\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--check') {\n config.check = true\n continue\n }\n if (arg === '--examples') {\n config.examples = true\n continue\n }\n const next = argv[i + 1]\n if (next === undefined) {\n continue\n }\n if (arg === '--input') {\n config.input = next\n i++\n } else if (arg === '--output') {\n config.output = next\n i++\n } else if (arg === '--server') {\n config.server = next\n i++\n } else if (arg === '--schemas') {\n config.schemas = next\n i++\n }\n }\n return config\n}\n\nexport async function run(argv: string[]): Promise<number> {\n const config = parseArgs(argv)\n if (!config.input || !config.output) {\n console.error(USAGE)\n return 1\n }\n if (config.check) {\n const result = await check(config as CodegenConfig)\n for (const { path, upToDate } of result.artifacts) {\n console.error(`${upToDate ? 'ok ' : 'DRIFT'} ${path}`)\n }\n if (!result.ok) {\n console.error(\n 'Generated client is out of date with the source spec. Regenerate (drop --check) and commit the result.',\n )\n return 1\n }\n return 0\n }\n const written = await generateFiles(config as CodegenConfig)\n for (const path of written) {\n console.error(`wrote ${path}`)\n }\n return 0\n}\n\n/**\n * Whether the module at `moduleUrl` is the script node was asked to run (`argv1`). Compares real\n * paths, because a package manager installs the bin as a symlink (`node_modules/.bin/...`) and\n * node reports the symlink in `process.argv[1]` but the resolved file in `import.meta.url`.\n */\nexport function isEntry(moduleUrl: string, argv1: string | undefined): boolean {\n if (argv1 === undefined) {\n return false\n }\n try {\n return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(argv1)\n } catch {\n return false\n }\n}\n\nasync function main(): Promise<void> {\n process.exitCode = await run(process.argv.slice(2))\n}\n\n// Only run when invoked as the bin, not when imported (e.g. by tests).\nif (isEntry(import.meta.url, process.argv[1])) {\n main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error)\n process.exitCode = 1\n })\n}\n"],"mappings":";;;;;AAKA,MAAa,QACX;;;;;;AAOF,SAAgB,UAAU,MAAwC;CAChE,MAAM,SAAiC,CAAC;CACxC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;EACjB,IAAI,QAAQ,WAAW;GACrB,OAAO,QAAQ;GACf;EACF;EACA,IAAI,QAAQ,cAAc;GACxB,OAAO,WAAW;GAClB;EACF;EACA,MAAM,OAAO,KAAK,IAAI;EACtB,IAAI,SAAS,KAAA,GACX;EAEF,IAAI,QAAQ,WAAW;GACrB,OAAO,QAAQ;GACf;EACF,OAAO,IAAI,QAAQ,YAAY;GAC7B,OAAO,SAAS;GAChB;EACF,OAAO,IAAI,QAAQ,YAAY;GAC7B,OAAO,SAAS;GAChB;EACF,OAAO,IAAI,QAAQ,aAAa;GAC9B,OAAO,UAAU;GACjB;EACF;CACF;CACA,OAAO;AACT;AAEA,eAAsB,IAAI,MAAiC;CACzD,MAAM,SAAS,UAAU,IAAI;CAC7B,IAAI,CAAC,OAAO,SAAS,CAAC,OAAO,QAAQ;EACnC,QAAQ,MAAM,KAAK;EACnB,OAAO;CACT;CACA,IAAI,OAAO,OAAO;EAChB,MAAM,SAAS,MAAM,MAAM,MAAuB;EAClD,KAAK,MAAM,EAAE,MAAM,cAAc,OAAO,WACtC,QAAQ,MAAM,GAAG,WAAW,SAAS,QAAQ,GAAG,MAAM;EAExD,IAAI,CAAC,OAAO,IAAI;GACd,QAAQ,MACN,wGACF;GACA,OAAO;EACT;EACA,OAAO;CACT;CACA,MAAM,UAAU,MAAM,cAAc,MAAuB;CAC3D,KAAK,MAAM,QAAQ,SACjB,QAAQ,MAAM,SAAS,MAAM;CAE/B,OAAO;AACT;;;;;;AAOA,SAAgB,QAAQ,WAAmB,OAAoC;CAC7E,IAAI,UAAU,KAAA,GACZ,OAAO;CAET,IAAI;EACF,OAAO,aAAa,cAAc,SAAS,CAAC,MAAM,aAAa,KAAK;CACtE,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAe,OAAsB;CACnC,QAAQ,WAAW,MAAM,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC;AACpD;AAGA,IAAI,QAAQ,OAAO,KAAK,KAAK,QAAQ,KAAK,EAAE,GAC1C,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;CAC5D,QAAQ,WAAW;AACrB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { ApiDescriptor, AtomicDescriptor } from "@haddowg/json-api-client";
1
+ import { ApiDescriptor, ApiExamples, AtomicDescriptor, OperationExamples } from "@haddowg/json-api-client";
2
2
 
3
3
  //#region src/openapi.d.ts
4
4
  /**
@@ -29,6 +29,8 @@ interface SchemaObject {
29
29
  type SchemaOrBool = SchemaObject | boolean;
30
30
  interface MediaTypeObject {
31
31
  schema?: SchemaObject;
32
+ /** A whole worked body for this media type (generator contract 12 on an operation). */
33
+ example?: unknown;
32
34
  }
33
35
  interface RequestBodyObject {
34
36
  content?: Record<string, MediaTypeObject>;
@@ -273,11 +275,27 @@ declare function buildDescriptor(doc: OpenApiDocument): ApiDescriptor;
273
275
  /** Detect the server-level Atomic Operations capability (`null` when none). */
274
276
  declare function buildAtomic(doc: OpenApiDocument): AtomicDescriptor | null;
275
277
  //#endregion
278
+ //#region src/build-examples.d.ts
279
+ /**
280
+ * The request example and the `2xx` response examples of one operation, or `undefined` when it
281
+ * declares none. Error responses are shared components rather than per-operation, so they are
282
+ * left out.
283
+ */
284
+ declare function operationExamples(op: OperationObject | undefined): OperationExamples | undefined;
285
+ /**
286
+ * Collect the worked examples a contract 12 document puts on each operation, per type. A type's
287
+ * operations are found by the `operationId`s the projector stamps (`create.albums`,
288
+ * `fetchRelated.albums.tracks`) and keyed by that id without the type; its actions are keyed by
289
+ * name, as the descriptor keys them. Types, operations and parts with no example are left out, so
290
+ * a document without examples gives `{}`.
291
+ */
292
+ declare function buildExamples(doc: OpenApiDocument, descriptor: ApiDescriptor): ApiExamples;
293
+ //#endregion
276
294
  //#region src/contract.d.ts
277
295
  /** Below this the document predates the structures this codegen reads. */
278
296
  declare const MINIMUM_CONTRACT = 1;
279
297
  /** Above this the document carries structures this codegen does not read. */
280
- declare const MAXIMUM_CONTRACT = 11;
298
+ declare const MAXIMUM_CONTRACT = 13;
281
299
  /**
282
300
  * Check the document's `info.x-generator.contract` against the range this codegen reads. A
283
301
  * document older than the range throws; a newer one returns a warning, because its new
@@ -332,8 +350,6 @@ declare class Emitter {
332
350
  private readonly provenance;
333
351
  private readonly doc;
334
352
  constructor(source: OpenApiDocument, descriptor: ApiDescriptor, atomic: AtomicDescriptor | null);
335
- /** The operation an action is invoked over: its declared method, else `POST`. */
336
- private actionOperation;
337
353
  /**
338
354
  * The alias an enum component is emitted under: its own name when that's a usable type name,
339
355
  * else one derived from it (with a warning), kept clear of every other name in the module.
@@ -496,6 +512,21 @@ declare function readErrorCatalogue(doc: OpenApiDocument): ErrorCatalogue;
496
512
  */
497
513
  declare function emitErrorCodes(catalogue: ErrorCatalogue): string;
498
514
  //#endregion
515
+ //#region src/emit-examples.d.ts
516
+ /**
517
+ * Emit the operation examples as a standalone generated artifact:
518
+ *
519
+ * ```ts
520
+ * export const examples: ApiExamples = { <type>: { operations: { … }, actions: { … } }, … }
521
+ * ```
522
+ *
523
+ * It sits beside the client rather than in it because the examples outweigh the rest of the
524
+ * client several times over and only a few consumers, such as a tool describing each operation,
525
+ * read them. The const is annotated rather than `as const`, so a large document costs the type
526
+ * checker nothing beyond the assignability check.
527
+ */
528
+ declare function emitExamples(doc: OpenApiDocument, examples: ApiExamples): string;
529
+ //#endregion
499
530
  //#region src/reader.d.ts
500
531
  /** A self-contained JSON Schema document, keyed by JSON:API type, as served at `GET /schemas.json`. */
501
532
  type SchemaBundle = Record<string, SchemaObject>;
@@ -569,6 +600,11 @@ interface CodegenConfig {
569
600
  server?: string;
570
601
  /** JSON Schema source for the validation seam (URL or path). Off when omitted. */
571
602
  schemas?: string;
603
+ /**
604
+ * Also write the operation examples the document declares (generator contract 12) to a sibling
605
+ * module, `<output>.examples.gen.ts`. Nothing is written when the document has none.
606
+ */
607
+ examples?: boolean;
572
608
  /**
573
609
  * Drift-check mode: regenerate in-memory and compare against the committed `output`
574
610
  * (and its schema artifact when `schemas` is set) instead of writing. Nothing is
@@ -593,6 +629,8 @@ interface CheckResult {
593
629
  * (any `.gen.ts`/`.ts` suffix is preserved; a suffix-less path just gains `.schemas`).
594
630
  */
595
631
  declare function schemasOutputPath(output: string): string;
632
+ /** The examples-artifact path beside a client output path, named as {@link schemasOutputPath} names its. */
633
+ declare function examplesOutputPath(output: string): string;
596
634
  /**
597
635
  * Read the OpenAPI document, build the runtime descriptor, emit the generated client
598
636
  * module and write it to `config.output` (creating parent directories as needed).
@@ -600,6 +638,7 @@ declare function schemasOutputPath(output: string): string;
600
638
  * When `config.schemas` is set, also read the bundle's JSON Schema bundle and write a
601
639
  * separate per-type schema artifact (`<output>.schemas.gen.ts`) beside the client, wiring
602
640
  * the opt-in validation seam (ADR 0004). The main client output is unchanged either way.
641
+ * `config.examples` writes the operation examples the same way (ADR 0011).
603
642
  *
604
643
  * `config.server` is metadata-only for now: the served document is already per-server,
605
644
  * so the caller selects the server by pointing `input` at the right document.
@@ -608,6 +647,8 @@ declare function schemasOutputPath(output: string): string;
608
647
  * call {@link check} instead for the drift-gate result.
609
648
  */
610
649
  declare function generate(config: CodegenConfig): Promise<string>;
650
+ /** {@link generate}, returning the path of every artifact written, the client's first. */
651
+ declare function generateFiles(config: CodegenConfig): Promise<string[]>;
611
652
  /**
612
653
  * Drift gate: regenerate every artifact in memory and compare it byte-for-byte against the
613
654
  * committed file on disk, WITHOUT writing anything. A missing committed file counts as drift
@@ -616,5 +657,5 @@ declare function generate(config: CodegenConfig): Promise<string>;
616
657
  */
617
658
  declare function check(config: CodegenConfig): Promise<CheckResult>;
618
659
  //#endregion
619
- export { InfoObject as A, MAXIMUM_CONTRACT as C, buildAtomic as D, DescriptorBuilder as E, PathItemObject as F, RequestBodyObject as I, ResponseObject as L, OpenApiDocument as M, OperationObject as N, buildDescriptor as O, ParameterObject as P, SchemaObject as R, emit as S, checkContract as T, emitErrorCodes as _, generate as a, VerbCollision as b, deriveProvenance as c, emitSchemas as d, SchemaBundle as f, ErrorCatalogue as g, DocumentedErrorCode as h, check as i, MediaTypeObject as j, HttpMethod as k, hashJson as l, readSchemas as m, CheckResult as n, schemasOutputPath as o, readDocument as p, CodegenConfig as r, Provenance as s, ArtifactDrift as t, provenanceLines as u, readErrorCatalogue as v, MINIMUM_CONTRACT as w, detectVerbCollisions as x, Emitter as y, SchemaOrBool as z };
620
- //# sourceMappingURL=index-CeSU_OuC.d.mts.map
660
+ export { operationExamples as A, PathItemObject as B, VerbCollision as C, MINIMUM_CONTRACT as D, MAXIMUM_CONTRACT as E, InfoObject as F, ResponseObject as H, MediaTypeObject as I, OpenApiDocument as L, buildAtomic as M, buildDescriptor as N, checkContract as O, HttpMethod as P, OperationObject as R, Emitter as S, emit as T, SchemaObject as U, RequestBodyObject as V, SchemaOrBool as W, emitExamples as _, examplesOutputPath as a, emitErrorCodes as b, schemasOutputPath as c, hashJson as d, provenanceLines as f, readSchemas as g, readDocument as h, check as i, DescriptorBuilder as j, buildExamples as k, Provenance as l, SchemaBundle as m, CheckResult as n, generate as o, emitSchemas as p, CodegenConfig as r, generateFiles as s, ArtifactDrift as t, deriveProvenance as u, DocumentedErrorCode as v, detectVerbCollisions as w, readErrorCatalogue as x, ErrorCatalogue as y, ParameterObject as z };
661
+ //# sourceMappingURL=index-COQQrn7a.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-COQQrn7a.d.mts","names":[],"sources":["../src/openapi.ts","../src/build-descriptor.ts","../src/build-examples.ts","../src/contract.ts","../src/emit.ts","../src/error-codes.ts","../src/emit-examples.ts","../src/reader.ts","../src/emit-schemas.ts","../src/provenance.ts","../src/index.ts"],"mappings":";;;;;;AAQA;;;;UAAiB,YAAA;EACf,IAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,WAAA;EACA,IAAA;EACA,UAAA,GAAa,MAAA,SAAe,YAAA;EAC5B,KAAA,GAAQ,YAAA;EACR,QAAA;EACA,KAAA,YAAiB,YAAA;EACjB,KAAA,YAAiB,YAAA;EACjB,KAAA,YAAiB,YAAA;EACjB,iBAAA;EACA,qBAAA;EAAA,CACC,GAAA;AAAA;;KAIS,YAAA,GAAe,YAAY;AAAA,UAEtB,eAAA;EACf,MAAA,GAAS,YAAY;EAXrB;EAaA,OAAA;AAAA;AAAA,UAGe,iBAAA;EACf,OAAA,GAAU,MAAM,SAAS,eAAA;EACzB,QAAA;AAAA;AAAA,UAGe,cAAA;EACf,OAAA,GAAU,MAAM,SAAS,eAAA;AAAA;AAAA,UAGV,eAAA;EACf,IAAA;EACA,EAAA;EACA,IAAA;EACA,MAAA,GAAS,YAAY;EACrB,KAAA;EACA,OAAA;EApBqB;EAsBrB,WAAA;AAAA;AAAA,UAGe,eAAA;EACf,WAAA;EACA,OAAA;EACA,WAAA;EACA,UAAA,YAAsB,eAAA;EACtB,WAAA,GAAc,iBAAA;EACd,SAAA,GAAY,MAAA,SAAe,cAAA;AAAA;AAAA,KAGjB,UAAA;AAAA,KAEA,cAAA,GAAiB,OAAA,CAAQ,MAAA,CAAO,UAAA,EAAY,eAAA;AAAA,UAEvC,UAAA;EACf,KAAA;EACA,OAAA;EA9Be;EAgCf,aAAA;IAAkB,QAAA;EAAA;AAAA;AAAA,UAGH,eAAA;EACf,OAAA;EACA,IAAA,GAAO,UAAA;EACP,OAAA;IAAqB,GAAA;EAAA;EACrB,KAAA,GAAQ,MAAA,SAAe,cAAA;EACvB,UAAA;IACE,OAAA,GAAU,MAAA,SAAe,YAAA;EAAA;AAAA;;;cCHhB,iBAAA;EAAA,iBACM,OAAA;EAAA,iBACA,KAAA;;UAET,cAAA;cAEI,MAAA,EAAQ,eAAA;EDpEZ;;;;;EAAA,QC+EA,aAAA;EAaR,KAAA,IAAS,aAAA;EDlGT;;;;;EC2JA,WAAA,IAAe,gBAAA;EDtJF;;;;;EAAA,QCqKL,YAAA;EDlKS;EAAA,QC8KT,YAAA;EAAA,QASA,UAAA;EDrLR;;;;;;AAGY;EAHZ,QC2MQ,SAAA;EDpMc;;;AAAe;EAAf,QCkQd,kBAAA;EDhQsB;;;;;;EAAA,QCuRtB,iBAAA;EDpRD;AAGT;;;;;;EAHS,QCiUC,iBAAA;ED5TR;;AAAQ;AAGV;;;;;;;EAHE,QC+VQ,oBAAA;ED3VgC;AAG1C;;;;;;EAH0C,QCyZhC,OAAA;EDlZR;;;;EAAA,QCweQ,SAAA;EDpeG;AAAA;AAGb;;;EAHa,QCyfH,YAAA;EDjfM;EAAA,QCggBN,cAAA;ED/fI;EAAA,QC6gBJ,aAAA;ED7gBU;;;;;EAAA,QC0hBV,eAAA;ED3hBR;EAAA,QCgjBQ,SAAA;ED/iBR;EAAA,QCwjBQ,iBAAA;EDxjBmB;EAAA,QCskBnB,qBAAA;EDtkBiC;AAG3C;;;;EAH2C,QCqpBjC,UAAA;EDhpBE;;;;;;;EAAA,QC+pBF,eAAA;ED/pB0B;EAAA,QCssB1B,cAAA;EDtsB2B;EAAA,QCmtB3B,cAAA;EAAA,QAaA,QAAA;EDhuB6D;AAAA;AAEvE;;;EAFuE,QCivB7D,cAAA;EAAA,QAmCA,cAAA;EDhxBR;EAAA,QCsyBQ,cAAA;EAAA,QAWA,mBAAA;ED/yBkB;EAAA,QCuzBlB,SAAA;EDpzBO;;;;;;;EAAA,QCg0BP,WAAA;ED1zBU;;;;;;;EAAA,QCw2BV,WAAA;ED12BA;;;;;;;AAE+B;;;;ACHzC;;EDCU,QCg5BA,SAAA;EA34BY;;;;;;;;;EAAA,QA25BZ,UAAA;EA35BI;;;;;EAAA,QAw6BJ,QAAA;EAx0BA;;;;;;EAAA,QAs1BA,UAAA;EAAA,QAeA,SAAA;EAAA,QAIA,cAAA;EAAA,QAIA,aAAA;AAAA;;iBA4MM,eAAA,CAAgB,GAAA,EAAK,eAAA,GAAkB,aAAa;;iBAKpD,WAAA,CAAY,GAAA,EAAK,eAAA,GAAkB,gBAAgB;;;;AD1uCnE;;;;iBEuBgB,iBAAA,CAAkB,EAAA,EAAI,eAAA,eAA8B,iBAAiB;;;;;;;;iBA4CrE,aAAA,CAAc,GAAA,EAAK,eAAA,EAAiB,UAAA,EAAY,aAAA,GAAgB,WAAA;;;;cCxEnE,gBAAA;AHKb;AAAA,cGFa,gBAAA;;;;;;;iBAQG,aAAA,CAAc,GAAoB,EAAf,eAAe;;;;UCkBjC,aAAA;EACf,IAAA;EACA,QAAQ;AAAA;;;;;iBAsBM,oBAAA,CAAqB,UAAA,EAAY,aAAA,GAAgB,aAAa;AAAA,cAmPjE,OAAA;EAAA,iBAqCQ,UAAA;EAAA,iBACA,MAAA;EAAA,iBArCF,OAAA;EJ/RjB;EAAA,iBIiSiB,UAAA;EJ/RjB;EAAA,iBIiSiB,SAAA;EJjSW;;;;;;EAAA,iBIwSX,iBAAA;EJpSA;EAAA,iBIsSA,SAAA;EJrSA;EAAA,iBIuSA,UAAA;EJrSjB;EAAA,iBIuSiB,SAAA;EJtSL;AAAA;AAId;;;EAJc,iBI4SK,gBAAA;EJxSoB;EAAA,iBI0SpB,cAAA;EJxSa;EAAA,iBI0Sb,WAAA;EJzSI;EAAA,iBI4SJ,UAAA;EAAA,iBAEA,GAAA;cAGf,MAAA,EAAQ,eAAA,EACS,UAAA,EAAY,aAAA,EACZ,MAAA,EAAQ,gBAAA;EJjTpB;AAAA;AAGT;;EAHS,QI2VC,QAAA;EJvVQ;EAAA,QIiXR,UAAA;EAcR,IAAA;EAAA,QA+BQ,MAAA;EAAA,QAWA,OAAA;EJxaA;EAAA,QIqbA,kBAAA;EJlbO;;;;;;;EAAA,QIkcP,wBAAA;EJjcgC;AAG1C;;;;;;EAH0C,QIyehC,iBAAA;EJleR;;;;;EAAA,QI0jBQ,YAAA;EJtjBG;AAGb;;;;EAHa,QI+kBH,cAAA;EJtkBmB;;;;;EAAA,QIinBnB,aAAA;EJpnBR;EAAA,QI0oBQ,kBAAA;EJzoBc;;;;;;;AAEmB;EAFnB,QIwpBd,cAAA;EJnpBY;;;AAAA;AAEtB;;;;;;EAFsB,QI6rBZ,cAAA;EJ3rB0B;EAAA,QI0sB1B,WAAA;EJ1sBmB;;;;;AAA0C;EAA1C,QI2tBnB,eAAA;EJztBiB;EAAA,QI+vBjB,gBAAA;EJ/vBiB;;;;;EAAA,QI4xBjB,eAAA;EJxxBkB;EAAA,QIkzBlB,MAAA;EJ/yBsB;EAAA,QIg2BtB,UAAA;EAAA,QAaA,iBAAA;EJz2Be;EAAA,QIk4Bf,UAAA;EAAA,QAoBA,SAAA;EJp5BI;EAAA,QI+5BJ,WAAA;EJ/5BU;;;;;EAAA,QIs7BV,OAAA;EJx7BR;;;;;;EAAA,QIy8BQ,aAAA;EJv8B+B;AAAA;;;;ACHzC;EDGyC,QIo9B/B,kBAAA;;UA0BA,WAAA;EHn9BC;EAAA,QGy9BD,OAAA;EAAA,QA6BA,YAAA;AAAA;;;;;iBAwBM,IAAA,CACd,GAAA,EAAK,eAAA,EACL,UAAA,EAAY,aAAA,EACZ,MAAA,GAAQ,gBAAA;;;;UCtnCO,mBAAA;EACf,IAAA;ELA2B;EKE3B,WAAA;ELK4B;EKH5B,QAAA;AAAA;;UAIe,cAAA;ELIE;EKFjB,KAAA,EAAO,mBAAA;ELEsB;;;;EKG7B,QAAA,EAAU,GAAG;AAAA;;;;;;;iBAuCC,kBAAA,CAAmB,GAAA,EAAK,eAAA,GAAkB,cAAc;;;;;;;;iBAiFxD,cAAA,CAAe,SAAyB,EAAd,cAAc;;;;ALvIxD;;;;;;;;;;;iBMSgB,YAAA,CAAa,GAAA,EAAK,eAAA,EAAiB,QAAA,EAAU,WAAW;;;;KCX5D,YAAA,GAAe,MAAM,SAAS,YAAA;;;;;iBA+CpB,YAAA,CAAa,KAAA,WAAgB,OAAO,CAAC,eAAA;;;;;;iBASrC,WAAA,CAAY,KAAA,WAAgB,OAAO,CAAC,YAAA;;;;;APtD1D;;;;;;;;;;;;;iBQWgB,WAAA,CAAY,MAAoB,EAAZ,YAAY;;;;;ARXhD;;;;USEiB,UAAA;ETMP;EAAA,SSJC,MAAA;ETOQ;EAAA,SSLR,MAAA;ETMoB;EAAA,SSJpB,IAAA;AAAA;;iBAwBK,QAAA,CAAS,KAAc;;;;;;iBAYvB,gBAAA,CAAiB,GAAA,EAAK,eAAA,GAAkB,UAAU;;;;;;iBAelD,eAAA,CAAgB,UAAsB,EAAV,UAAU;;;UC7BrC,aAAA;EVpBE;EUsBjB,KAAA;EVrBiB;EUuBjB,MAAA;EVtBiB;EUwBjB,MAAA;EVtBA;EUwBA,OAAA;EVvBY;AAAA;AAId;;EUwBE,QAAA;EVxBqC;AAAA;AAEvC;;;EU4BE,KAAA;AAAA;;UAIe,aAAA;EV7BR;EU+BP,IAAA;EV5Be;EU8Bf,QAAQ;AAAA;;UAIO,WAAA;EACf,EAAA;EACA,SAAA,EAAW,aAAa;AAAA;;AVlChB;AAGV;;iBUsCgB,iBAAA,CAAkB,MAAc;;iBAWhC,kBAAA,CAAmB,MAAc;;;;AVhDP;AAG1C;;;;;;;;;;;iBUiIsB,QAAA,CAAS,MAAA,EAAQ,aAAA,GAAgB,OAAO;;iBAMxC,aAAA,CAAc,MAAA,EAAQ,aAAA,GAAgB,OAAO;AV5HnE;;;;;;AAAA,iBU+IsB,KAAA,CAAM,MAAA,EAAQ,aAAA,GAAgB,OAAA,CAAQ,WAAA"}
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as InfoObject, C as MAXIMUM_CONTRACT, D as buildAtomic, E as DescriptorBuilder, F as PathItemObject, I as RequestBodyObject, L as ResponseObject, M as OpenApiDocument, N as OperationObject, O as buildDescriptor, P as ParameterObject, R as SchemaObject, S as emit, T as checkContract, _ as emitErrorCodes, a as generate, b as VerbCollision, c as deriveProvenance, d as emitSchemas, f as SchemaBundle, g as ErrorCatalogue, h as DocumentedErrorCode, i as check, j as MediaTypeObject, k as HttpMethod, l as hashJson, m as readSchemas, n as CheckResult, o as schemasOutputPath, p as readDocument, r as CodegenConfig, s as Provenance, t as ArtifactDrift, u as provenanceLines, v as readErrorCatalogue, w as MINIMUM_CONTRACT, x as detectVerbCollisions, y as Emitter, z as SchemaOrBool } from "./index-CeSU_OuC.mjs";
2
- export { ArtifactDrift, CheckResult, CodegenConfig, DescriptorBuilder, type DocumentedErrorCode, Emitter, type ErrorCatalogue, type HttpMethod, type InfoObject, MAXIMUM_CONTRACT, MINIMUM_CONTRACT, type MediaTypeObject, type OpenApiDocument, type OperationObject, type ParameterObject, type PathItemObject, type Provenance, type RequestBodyObject, type ResponseObject, type SchemaBundle, type SchemaObject, type SchemaOrBool, type VerbCollision, buildAtomic, buildDescriptor, check, checkContract, deriveProvenance, detectVerbCollisions, emit, emitErrorCodes, emitSchemas, generate, hashJson, provenanceLines, readDocument, readErrorCatalogue, readSchemas, schemasOutputPath };
1
+ import { A as operationExamples, B as PathItemObject, C as VerbCollision, D as MINIMUM_CONTRACT, E as MAXIMUM_CONTRACT, F as InfoObject, H as ResponseObject, I as MediaTypeObject, L as OpenApiDocument, M as buildAtomic, N as buildDescriptor, O as checkContract, P as HttpMethod, R as OperationObject, S as Emitter, T as emit, U as SchemaObject, V as RequestBodyObject, W as SchemaOrBool, _ as emitExamples, a as examplesOutputPath, b as emitErrorCodes, c as schemasOutputPath, d as hashJson, f as provenanceLines, g as readSchemas, h as readDocument, i as check, j as DescriptorBuilder, k as buildExamples, l as Provenance, m as SchemaBundle, n as CheckResult, o as generate, p as emitSchemas, r as CodegenConfig, s as generateFiles, t as ArtifactDrift, u as deriveProvenance, v as DocumentedErrorCode, w as detectVerbCollisions, x as readErrorCatalogue, y as ErrorCatalogue, z as ParameterObject } from "./index-COQQrn7a.mjs";
2
+ export { ArtifactDrift, CheckResult, CodegenConfig, DescriptorBuilder, type DocumentedErrorCode, Emitter, type ErrorCatalogue, type HttpMethod, type InfoObject, MAXIMUM_CONTRACT, MINIMUM_CONTRACT, type MediaTypeObject, type OpenApiDocument, type OperationObject, type ParameterObject, type PathItemObject, type Provenance, type RequestBodyObject, type ResponseObject, type SchemaBundle, type SchemaObject, type SchemaOrBool, type VerbCollision, buildAtomic, buildDescriptor, buildExamples, check, checkContract, deriveProvenance, detectVerbCollisions, emit, emitErrorCodes, emitExamples, emitSchemas, examplesOutputPath, generate, generateFiles, hashJson, operationExamples, provenanceLines, readDocument, readErrorCatalogue, readSchemas, schemasOutputPath };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as checkContract, a as readSchemas, b as buildDescriptor, c as detectVerbCollisions, d as hashJson, f as provenanceLines, g as MINIMUM_CONTRACT, h as MAXIMUM_CONTRACT, i as readDocument, l as emit, m as readErrorCatalogue, n as generate, o as emitSchemas, p as emitErrorCodes, r as schemasOutputPath, s as Emitter, t as check, u as deriveProvenance, v as DescriptorBuilder, y as buildAtomic } from "./src-DhZyiWge.mjs";
2
- export { DescriptorBuilder, Emitter, MAXIMUM_CONTRACT, MINIMUM_CONTRACT, buildAtomic, buildDescriptor, check, checkContract, deriveProvenance, detectVerbCollisions, emit, emitErrorCodes, emitSchemas, generate, hashJson, provenanceLines, readDocument, readErrorCatalogue, readSchemas, schemasOutputPath };
1
+ import { C as DescriptorBuilder, S as operationExamples, T as buildDescriptor, _ as readErrorCatalogue, a as schemasOutputPath, b as checkContract, c as emitSchemas, d as detectVerbCollisions, f as emit, g as emitErrorCodes, h as provenanceLines, i as generateFiles, l as emitExamples, m as hashJson, n as examplesOutputPath, o as readDocument, p as deriveProvenance, r as generate, s as readSchemas, t as check, u as Emitter, v as MAXIMUM_CONTRACT, w as buildAtomic, x as buildExamples, y as MINIMUM_CONTRACT } from "./src-AWTGjMiX.mjs";
2
+ export { DescriptorBuilder, Emitter, MAXIMUM_CONTRACT, MINIMUM_CONTRACT, buildAtomic, buildDescriptor, buildExamples, check, checkContract, deriveProvenance, detectVerbCollisions, emit, emitErrorCodes, emitExamples, emitSchemas, examplesOutputPath, generate, generateFiles, hashJson, operationExamples, provenanceLines, readDocument, readErrorCatalogue, readSchemas, schemasOutputPath };
@@ -962,11 +962,89 @@ function stampedMenu(schema, stamp, warn) {
962
962
  };
963
963
  }
964
964
  //#endregion
965
+ //#region src/build-examples.ts
966
+ /** Operations that belong to no single type, so they have no place under one. */
967
+ const SERVER_OPERATIONS = /* @__PURE__ */ new Set(["action", "atomic"]);
968
+ /** The `example` of each media type that declares one, in the document's order. */
969
+ function mediaExamples(content) {
970
+ const out = {};
971
+ for (const [mediaType, media] of Object.entries(content ?? {})) if (media !== null && typeof media === "object" && "example" in media) out[mediaType] = media.example;
972
+ return Object.keys(out).length === 0 ? void 0 : out;
973
+ }
974
+ /**
975
+ * The request example and the `2xx` response examples of one operation, or `undefined` when it
976
+ * declares none. Error responses are shared components rather than per-operation, so they are
977
+ * left out.
978
+ */
979
+ function operationExamples(op) {
980
+ if (op === void 0) return;
981
+ const out = {};
982
+ const request = mediaExamples(op.requestBody?.content);
983
+ if (request !== void 0) out.request = request;
984
+ for (const [status, response] of Object.entries(op.responses ?? {})) {
985
+ const examples = status.startsWith("2") ? mediaExamples(response.content) : void 0;
986
+ if (examples !== void 0) {
987
+ out.responses ??= {};
988
+ out.responses[status] = examples;
989
+ }
990
+ }
991
+ return out.request === void 0 && out.responses === void 0 ? void 0 : out;
992
+ }
993
+ /** The operation an action is invoked over: its declared method, else `POST`. */
994
+ function actionOperation(doc, action) {
995
+ const method = (action.method ?? "POST").toLowerCase();
996
+ return doc.paths?.[action.path]?.[method];
997
+ }
998
+ function sorted(record) {
999
+ const out = {};
1000
+ for (const key of Object.keys(record).sort()) out[key] = record[key];
1001
+ return out;
1002
+ }
1003
+ /**
1004
+ * Collect the worked examples a contract 12 document puts on each operation, per type. A type's
1005
+ * operations are found by the `operationId`s the projector stamps (`create.albums`,
1006
+ * `fetchRelated.albums.tracks`) and keyed by that id without the type; its actions are keyed by
1007
+ * name, as the descriptor keys them. Types, operations and parts with no example are left out, so
1008
+ * a document without examples gives `{}`.
1009
+ */
1010
+ function buildExamples(doc, descriptor) {
1011
+ const operations = /* @__PURE__ */ new Map();
1012
+ for (const item of Object.values(doc.paths ?? {})) for (const op of Object.values(item)) {
1013
+ const id = op?.operationId;
1014
+ if (typeof id !== "string") continue;
1015
+ const [prefix, type, ...relation] = id.split(".");
1016
+ if (prefix === void 0 || type === void 0 || SERVER_OPERATIONS.has(prefix) || !Object.hasOwn(descriptor, type)) continue;
1017
+ const examples = operationExamples(op);
1018
+ if (examples === void 0) continue;
1019
+ let byName = operations.get(type);
1020
+ if (byName === void 0) {
1021
+ byName = {};
1022
+ operations.set(type, byName);
1023
+ }
1024
+ byName[[prefix, ...relation].join(".")] = examples;
1025
+ }
1026
+ const out = {};
1027
+ for (const [type, resource] of Object.entries(descriptor)) {
1028
+ const entry = {};
1029
+ const byName = operations.get(type);
1030
+ if (byName !== void 0) entry.operations = sorted(byName);
1031
+ for (const [name, action] of Object.entries(resource.actions ?? {})) {
1032
+ const examples = operationExamples(actionOperation(doc, action));
1033
+ if (examples !== void 0) {
1034
+ entry.actions ??= {};
1035
+ entry.actions[name] = examples;
1036
+ }
1037
+ }
1038
+ if (entry.operations !== void 0 || entry.actions !== void 0) out[type] = entry;
1039
+ }
1040
+ return out;
1041
+ }
1042
+ //#endregion
965
1043
  //#region src/contract.ts
966
1044
  /** Below this the document predates the structures this codegen reads. */
967
1045
  const MINIMUM_CONTRACT = 1;
968
1046
  /** Above this the document carries structures this codegen does not read. */
969
- const MAXIMUM_CONTRACT = 11;
1047
+ const MAXIMUM_CONTRACT = 13;
970
1048
  /**
971
1049
  * Check the document's `info.x-generator.contract` against the range this codegen reads. A
972
1050
  * document older than the range throws; a newer one returns a warning, because its new
@@ -976,8 +1054,8 @@ const MAXIMUM_CONTRACT = 11;
976
1054
  function checkContract(doc) {
977
1055
  const contract = doc.info?.["x-generator"]?.contract;
978
1056
  if (typeof contract !== "number") return;
979
- if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to 11). Use an older codegen release, or regenerate the document from a newer server.`);
980
- if (contract > 11) return `the document declares contract ${contract} and this codegen reads up to 11. It describes capabilities that were not generated; upgrade the codegen to pick them up.`;
1057
+ if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to 13). Use an older codegen release, or regenerate the document from a newer server.`);
1058
+ if (contract > 13) return `the document declares contract ${contract} and this codegen reads up to 13. It describes capabilities that were not generated; upgrade the codegen to pick them up.`;
981
1059
  }
982
1060
  //#endregion
983
1061
  //#region src/source.ts
@@ -992,6 +1070,13 @@ const LINE_BREAK = /\r\n|[\n\r\u2028\u2029]/;
992
1070
  function stringLiteral(value) {
993
1071
  return JSON.stringify(value).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
994
1072
  }
1073
+ /**
1074
+ * A JSON value as a TS literal, indented two spaces with the value's key order kept. A
1075
+ * `__proto__` key becomes computed so it stays an own key, and U+2028/U+2029 are escaped.
1076
+ */
1077
+ function jsonLiteral(value) {
1078
+ return JSON.stringify(value, null, 2).replace(/^(\s*)"__proto__":/gm, "$1[\"__proto__\"]:").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1079
+ }
995
1080
  /** True when a string is a bare identifier. */
996
1081
  function isIdentifier(value) {
997
1082
  return IDENTIFIER.test(value);
@@ -1404,6 +1489,47 @@ function actionDoc(name, op) {
1404
1489
  if (description && !generated.test(description)) parts.push(description);
1405
1490
  return parts.length === 0 ? void 0 : parts.join("\n\n");
1406
1491
  }
1492
+ /** Whether a media type carries JSON: JSON:API, `application/json` or a `+json` suffix. */
1493
+ function isJsonMedia(mediaType) {
1494
+ const essence = mediaType.split(";")[0].trim().toLowerCase();
1495
+ return essence === "application/json" || essence.endsWith("+json");
1496
+ }
1497
+ /** The media type to show: JSON:API when it has an example, else the first that does. */
1498
+ function shownMedia(examples) {
1499
+ return JSON_API_MEDIA_TYPE in examples ? JSON_API_MEDIA_TYPE : Object.keys(examples)[0];
1500
+ }
1501
+ /**
1502
+ * An `@example` for an action: its request body when it declares one, else its first success
1503
+ * response, as a fenced block of pretty-printed JSON (a text body as is). A body over
1504
+ * {@link EXAMPLE_DOC_MAX_LINES} lines is named but not shown. The fence is longer than any run of
1505
+ * backticks in the body, so the body can't close it.
1506
+ */
1507
+ function exampleDoc(op) {
1508
+ const examples = operationExamples(op);
1509
+ if (examples === void 0) return;
1510
+ let label;
1511
+ let media;
1512
+ if (examples.request !== void 0) {
1513
+ label = "Request body";
1514
+ media = examples.request;
1515
+ } else {
1516
+ const first = Object.entries(examples.responses ?? {})[0];
1517
+ if (first === void 0) return;
1518
+ label = `Response \`${first[0]}\``;
1519
+ media = first[1];
1520
+ }
1521
+ const mediaType = shownMedia(media);
1522
+ if (mediaType === void 0) return;
1523
+ if (mediaType !== JSON_API_MEDIA_TYPE) label += ` as \`${mediaType}\``;
1524
+ const value = media[mediaType];
1525
+ const raw = typeof value === "string" && !isJsonMedia(mediaType);
1526
+ const text = raw ? value.replace(/(?:\r\n|\r|\n)+$/, "") : JSON.stringify(value, null, 2);
1527
+ const lines = text.split(/\r\n|\r|\n/).length;
1528
+ if (lines > 80) return `@example ${label}\nThe example is ${lines} lines long, too long to show here.`;
1529
+ const longest = Math.max(0, ...(text.match(/`+/g) ?? []).map((run) => run.length));
1530
+ const fence = "`".repeat(Math.max(3, longest + 1));
1531
+ return `@example ${label}\n${fence}${raw ? "" : "json"}\n${text}\n${fence}`;
1532
+ }
1407
1533
  function escapeRegExp(value) {
1408
1534
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1409
1535
  }
@@ -1463,16 +1589,13 @@ var Emitter = class {
1463
1589
  for (const { mediaType } of action.inputMedia ?? []) this.takenNames.add(`${stem}${pascalCase(name)}${pascalCase(mediaType)}Input`);
1464
1590
  const single = action.contentType;
1465
1591
  if (single !== void 0 && (single.startsWith("multipart/") || single === "application/x-www-form-urlencoded")) this.takenNames.add(`${stem}${pascalCase(name)}${pascalCase(single)}Input`);
1466
- const doc = actionDoc(name, this.actionOperation(action));
1592
+ const op = actionOperation(this.doc, action);
1593
+ const parts = [actionDoc(name, op), exampleDoc(op)].filter((part) => part !== void 0);
1594
+ const doc = parts.length === 0 ? void 0 : parts.join("\n\n");
1467
1595
  if (doc !== void 0) this.literalDocs.set(`${type}/actions/${name}`, doc);
1468
1596
  }
1469
1597
  }
1470
1598
  }
1471
- /** The operation an action is invoked over: its declared method, else `POST`. */
1472
- actionOperation(action) {
1473
- const method = (action.method ?? "POST").toLowerCase();
1474
- return this.doc.paths?.[action.path]?.[method];
1475
- }
1476
1599
  /**
1477
1600
  * The alias an enum component is emitted under: its own name when that's a usable type name,
1478
1601
  * else one derived from it (with a warning), kept clear of every other name in the module.
@@ -1605,7 +1728,7 @@ var Emitter = class {
1605
1728
  out.push(`export type ${member}Output = ${outputType}`);
1606
1729
  names.output = `${member}Output`;
1607
1730
  }
1608
- const op = this.actionOperation(action);
1731
+ const op = actionOperation(this.doc, action);
1609
1732
  for (const { mediaType, kind } of action.rawOutput ?? []) if (kind === "json") {
1610
1733
  const schema = firstSuccessSchema(op, mediaType);
1611
1734
  names.responses ??= /* @__PURE__ */ new Map();
@@ -2016,6 +2139,34 @@ function emit(doc, descriptor, atomic = buildAtomic(doc)) {
2016
2139
  return new Emitter(doc, descriptor, atomic).emit();
2017
2140
  }
2018
2141
  //#endregion
2142
+ //#region src/emit-examples.ts
2143
+ /**
2144
+ * Emit the operation examples as a standalone generated artifact:
2145
+ *
2146
+ * ```ts
2147
+ * export const examples: ApiExamples = { <type>: { operations: { … }, actions: { … } }, … }
2148
+ * ```
2149
+ *
2150
+ * It sits beside the client rather than in it because the examples outweigh the rest of the
2151
+ * client several times over and only a few consumers, such as a tool describing each operation,
2152
+ * read them. The const is annotated rather than `as const`, so a large document costs the type
2153
+ * checker nothing beyond the assignability check.
2154
+ */
2155
+ function emitExamples(doc, examples) {
2156
+ return `${[
2157
+ [
2158
+ "/**",
2159
+ " * AUTO-GENERATED by @haddowg/json-api-codegen — do not edit by hand.",
2160
+ " * Worked examples of each operation, as the source OpenAPI document declares them.",
2161
+ " *",
2162
+ ...provenanceLines(deriveProvenance(doc)),
2163
+ " */"
2164
+ ].join("\n"),
2165
+ "import type { ApiExamples } from '@haddowg/json-api-client'",
2166
+ `export const examples: ApiExamples = ${jsonLiteral(examples)}`
2167
+ ].join("\n\n")}\n`;
2168
+ }
2169
+ //#endregion
2019
2170
  //#region src/emit-schemas.ts
2020
2171
  /**
2021
2172
  * Emit the per-type JSON Schema map as a standalone generated artifact:
@@ -2046,7 +2197,7 @@ function header(bundle) {
2046
2197
  ].join("\n");
2047
2198
  }
2048
2199
  function body(bundle) {
2049
- return `export const schemas = ${JSON.stringify(bundle, null, 2).replace(/^(\s*)"__proto__":/gm, "$1[\"__proto__\"]:").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029")} as const`;
2200
+ return `export const schemas = ${jsonLiteral(bundle)} as const`;
2050
2201
  }
2051
2202
  //#endregion
2052
2203
  //#region src/reader.ts
@@ -2115,9 +2266,16 @@ function schemasOutputPath(output) {
2115
2266
  if (output.endsWith(".ts")) return `${output.slice(0, -3)}.schemas.ts`;
2116
2267
  return `${output}.schemas`;
2117
2268
  }
2269
+ /** The examples-artifact path beside a client output path, named as {@link schemasOutputPath} names its. */
2270
+ function examplesOutputPath(output) {
2271
+ if (output.endsWith(".gen.ts")) return `${output.slice(0, -7)}.examples.gen.ts`;
2272
+ if (output.endsWith(".ts")) return `${output.slice(0, -3)}.examples.ts`;
2273
+ return `${output}.examples`;
2274
+ }
2118
2275
  /**
2119
2276
  * Read the source spec(s), build the descriptor and render every generated artifact in
2120
- * memory (the client source first, then the schema artifact when `config.schemas` is set)
2277
+ * memory (the client source first, then the schema artifact when `config.schemas` is set and
2278
+ * the examples artifact when `config.examples` is)
2121
2279
  * — the shared core of {@link generate} (which writes them) and {@link check} (which
2122
2280
  * compares them). Verb-collision warnings are emitted here so both paths surface them.
2123
2281
  */
@@ -2139,6 +2297,14 @@ async function render(config) {
2139
2297
  content: emitSchemas(bundle)
2140
2298
  });
2141
2299
  }
2300
+ if (config.examples === true) {
2301
+ const examples = buildExamples(doc, descriptor);
2302
+ if (Object.keys(examples).length > 0) artifacts.push({
2303
+ path: examplesOutputPath(config.output),
2304
+ content: emitExamples(doc, examples)
2305
+ });
2306
+ else console.warn(`[json-api-codegen] ${config.input} declares no operation examples; no examples module was written.`);
2307
+ }
2142
2308
  return artifacts;
2143
2309
  }
2144
2310
  /**
@@ -2148,6 +2314,7 @@ async function render(config) {
2148
2314
  * When `config.schemas` is set, also read the bundle's JSON Schema bundle and write a
2149
2315
  * separate per-type schema artifact (`<output>.schemas.gen.ts`) beside the client, wiring
2150
2316
  * the opt-in validation seam (ADR 0004). The main client output is unchanged either way.
2317
+ * `config.examples` writes the operation examples the same way (ADR 0011).
2151
2318
  *
2152
2319
  * `config.server` is metadata-only for now: the served document is already per-server,
2153
2320
  * so the caller selects the server by pointing `input` at the right document.
@@ -2156,10 +2323,17 @@ async function render(config) {
2156
2323
  * call {@link check} instead for the drift-gate result.
2157
2324
  */
2158
2325
  async function generate(config) {
2326
+ return (await write(config))[0].content;
2327
+ }
2328
+ /** {@link generate}, returning the path of every artifact written, the client's first. */
2329
+ async function generateFiles(config) {
2330
+ return (await write(config)).map(({ path }) => path);
2331
+ }
2332
+ async function write(config) {
2159
2333
  const artifacts = await render(config);
2160
2334
  await mkdir(dirname(config.output), { recursive: true });
2161
2335
  for (const { path, content } of artifacts) await writeFile(path, content, "utf8");
2162
- return artifacts[0].content;
2336
+ return artifacts;
2163
2337
  }
2164
2338
  /**
2165
2339
  * Drift gate: regenerate every artifact in memory and compare it byte-for-byte against the
@@ -2188,6 +2362,6 @@ async function check(config) {
2188
2362
  };
2189
2363
  }
2190
2364
  //#endregion
2191
- export { checkContract as _, readSchemas as a, buildDescriptor as b, detectVerbCollisions as c, hashJson as d, provenanceLines as f, MINIMUM_CONTRACT as g, MAXIMUM_CONTRACT as h, readDocument as i, emit as l, readErrorCatalogue as m, generate as n, emitSchemas as o, emitErrorCodes as p, schemasOutputPath as r, Emitter as s, check as t, deriveProvenance as u, DescriptorBuilder as v, buildAtomic as y };
2365
+ export { DescriptorBuilder as C, operationExamples as S, buildDescriptor as T, readErrorCatalogue as _, schemasOutputPath as a, checkContract as b, emitSchemas as c, detectVerbCollisions as d, emit as f, emitErrorCodes as g, provenanceLines as h, generateFiles as i, emitExamples as l, hashJson as m, examplesOutputPath as n, readDocument as o, deriveProvenance as p, generate as r, readSchemas as s, check as t, Emitter as u, MAXIMUM_CONTRACT as v, buildAtomic as w, buildExamples as x, MINIMUM_CONTRACT as y };
2192
2366
 
2193
- //# sourceMappingURL=src-DhZyiWge.mjs.map
2367
+ //# sourceMappingURL=src-AWTGjMiX.mjs.map