@haddowg/json-api-codegen 1.13.0 → 1.15.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/README.md +25 -10
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{index-C0T-tofN.d.mts → index-FaJ8Xx6v.d.mts} +55 -10
- package/dist/index-FaJ8Xx6v.d.mts.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-Qe0YLO-w.mjs → src-i5j2I9fk.mjs} +225 -38
- package/dist/src-i5j2I9fk.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/index-C0T-tofN.d.mts.map +0 -1
- package/dist/src-Qe0YLO-w.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -30,19 +30,23 @@ pnpm add -D @haddowg/json-api-codegen
|
|
|
30
30
|
|
|
31
31
|
Two bin names are installed: `json-api-codegen` and the short alias `japi`.
|
|
32
32
|
|
|
33
|
+
The codegen reads documents up to generator contract 18 (`info.x-generator.contract`). A newer
|
|
34
|
+
document still generates, with a warning that the client may miss what the server added.
|
|
35
|
+
|
|
33
36
|
## CLI
|
|
34
37
|
|
|
35
38
|
```bash
|
|
36
|
-
json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--check]
|
|
39
|
+
json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--examples] [--check]
|
|
37
40
|
```
|
|
38
41
|
|
|
39
|
-
| Flag
|
|
40
|
-
|
|
|
41
|
-
| `--input`
|
|
42
|
-
| `--output`
|
|
43
|
-
| `--server`
|
|
44
|
-
| `--schemas`
|
|
45
|
-
| `--
|
|
42
|
+
| Flag | Required | Description |
|
|
43
|
+
| ------------ | -------- | ------------------------------------------------------------------------------------- |
|
|
44
|
+
| `--input` | yes | OpenAPI document source — an http(s) URL or a local file path (JSON or YAML). |
|
|
45
|
+
| `--output` | yes | Output file for the generated client (e.g. `src/api/music.gen.ts`). |
|
|
46
|
+
| `--server` | no | Server name to target. Metadata-only — the served document is already per-server. |
|
|
47
|
+
| `--schemas` | no | JSON Schema source (URL or path) for the validation seam. Omit to skip schema output. |
|
|
48
|
+
| `--examples` | no | Also write the operations' worked examples to a sibling `.examples.gen.ts` module. |
|
|
49
|
+
| `--check` | no | Drift gate — regenerate in memory and exit non-zero if the committed output differs. |
|
|
46
50
|
|
|
47
51
|
Example — generate against a served API, with the validation schemas:
|
|
48
52
|
|
|
@@ -57,6 +61,8 @@ This writes:
|
|
|
57
61
|
|
|
58
62
|
- `src/api/music.gen.ts` — the typed client (descriptor + types + bound `createClient`);
|
|
59
63
|
- `src/api/music.schemas.gen.ts` — the per-type JSON Schema map (only with `--schemas`).
|
|
64
|
+
- `src/api/music.examples.gen.ts` — each operation's worked examples (only with `--examples`, and
|
|
65
|
+
only when the document declares some).
|
|
60
66
|
|
|
61
67
|
The schema artifact path is derived from `--output`: a `.gen.ts` suffix becomes `.schemas.gen.ts`,
|
|
62
68
|
a plain `.ts` becomes `.schemas.ts`.
|
|
@@ -118,6 +124,13 @@ The output module exports:
|
|
|
118
124
|
- `Attributes` / `WriteAttributes` — the per-type attribute maps (read vs create/update).
|
|
119
125
|
- Per-type `…Attributes` / `…CreateAttributes` / `…UpdateAttributes` interfaces, enum types
|
|
120
126
|
(e.g. `AlbumStatus`), and action input/output types.
|
|
127
|
+
- `Links` — the typed declared links of each type that declares any.
|
|
128
|
+
- `ErrorCodes` / `ErrorCode` / `ErrorCodeByStatus` / `hasErrorCode` — the server's documented
|
|
129
|
+
error codes and a type guard bound to them.
|
|
130
|
+
- `pageLimits` / `relatedPageLimits` — the page defaults and page-size caps of each collection and
|
|
131
|
+
each related collection.
|
|
132
|
+
- `pageKinds` / `relatedPageKinds` — the pagination strategies each collection and related
|
|
133
|
+
collection offers, the server's default first.
|
|
121
134
|
|
|
122
135
|
With `--schemas`, the sibling module exports `schemas` — the per-type JSON Schema 2020-12 map.
|
|
123
136
|
|
|
@@ -162,8 +175,10 @@ if (!result.ok) process.exit(1)
|
|
|
162
175
|
```
|
|
163
176
|
|
|
164
177
|
Lower-level building blocks are also exported for advanced use: `readDocument` / `readSchemas`,
|
|
165
|
-
`buildDescriptor`, `emit`, `emitSchemas`, `
|
|
166
|
-
|
|
178
|
+
`buildDescriptor`, `buildExamples`, `emit`, `emitSchemas`, `emitExamples`, `readErrorCatalogue`,
|
|
179
|
+
`emitErrorCodes`, `checkContract`, `detectVerbCollisions`, `schemasOutputPath`,
|
|
180
|
+
`examplesOutputPath`, and the provenance helpers `deriveProvenance` / `provenanceLines` /
|
|
181
|
+
`hashJson`. `generateFiles(config)` generates like `generate` and returns the paths it wrote.
|
|
167
182
|
|
|
168
183
|
## License
|
|
169
184
|
|
package/dist/cli.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as CodegenConfig } from "./index-
|
|
1
|
+
import { r as CodegenConfig } from "./index-FaJ8Xx6v.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/cli.d.ts
|
|
4
4
|
declare const USAGE = "Usage: json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--examples] [--check]";
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as generateFiles, t as check } from "./src-
|
|
2
|
+
import { i as generateFiles, t as check } from "./src-i5j2I9fk.mjs";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
//#region src/cli.ts
|
|
@@ -169,9 +169,9 @@ declare class DescriptorBuilder {
|
|
|
169
169
|
*/
|
|
170
170
|
private relationMutations;
|
|
171
171
|
/**
|
|
172
|
-
* Attach the per-relation endpoint-exposure signal to a relation descriptor
|
|
172
|
+
* Attach the per-relation endpoint-exposure signal to a relation descriptor. The bundle
|
|
173
173
|
* can suppress a relation's related or relationship endpoint (`withoutRelatedEndpoint()` /
|
|
174
|
-
* `withoutRelationshipEndpoint()
|
|
174
|
+
* `withoutRelationshipEndpoint()`), which the OpenAPI reflects by simply omitting that
|
|
175
175
|
* relation's path — but codegen synthesizes generic `{rel}` templates, erasing the per-relation
|
|
176
176
|
* distinction. So mark `related: false` / `relationship: false` when this relation's specific
|
|
177
177
|
* path is absent, and ALWAYS emit an explicit `mutations` object (the verb flags when the
|
|
@@ -213,6 +213,12 @@ declare class DescriptorBuilder {
|
|
|
213
213
|
private documentPrimary;
|
|
214
214
|
/** The wire type of a single `data` schema — a `$ref` to a resource, or an inline `type.const`. */
|
|
215
215
|
private dataConst;
|
|
216
|
+
/**
|
|
217
|
+
* The wire types a read's compound document can carry in `included`: the members of its
|
|
218
|
+
* document's `included.items` union. `undefined` when the document has no `included`, or types
|
|
219
|
+
* its items only as a generic `{type, id}` object.
|
|
220
|
+
*/
|
|
221
|
+
private includedTypes;
|
|
216
222
|
/** The `$ref` of the first 2xx JSON:API response body on an operation (the output document). */
|
|
217
223
|
private firstOkJsonApiRef;
|
|
218
224
|
/** Resolve a relationship component's `data` into a {@link RelationDescriptor}. */
|
|
@@ -233,6 +239,12 @@ declare class DescriptorBuilder {
|
|
|
233
239
|
private pivotDescriptor;
|
|
234
240
|
/** Pull the related type from the `$ref` entry of an `allOf` (the pivot shape). */
|
|
235
241
|
private constFromAllOf;
|
|
242
|
+
/**
|
|
243
|
+
* The wire types of a union of `$ref`s: a `oneOf` discriminated by `type`, or the `anyOf` an
|
|
244
|
+
* older document writes. A member whose component carries no `type` const falls back to the
|
|
245
|
+
* discriminator mapping's key for its `$ref`. `undefined` when the schema is no union.
|
|
246
|
+
*/
|
|
247
|
+
private unionTypes;
|
|
236
248
|
/** Resolve every `$ref` entry in a list to its wire type. */
|
|
237
249
|
private constsFromRefs;
|
|
238
250
|
private clientId;
|
|
@@ -330,7 +342,7 @@ declare function buildExamples(doc: OpenApiDocument, descriptor: ApiDescriptor):
|
|
|
330
342
|
/** Below this the document predates the structures this codegen reads. */
|
|
331
343
|
declare const MINIMUM_CONTRACT = 1;
|
|
332
344
|
/** Above this the document carries structures this codegen does not read. */
|
|
333
|
-
declare const MAXIMUM_CONTRACT =
|
|
345
|
+
declare const MAXIMUM_CONTRACT = 19;
|
|
334
346
|
/**
|
|
335
347
|
* Check the document's `info.x-generator.contract` against the range this codegen reads. A
|
|
336
348
|
* document older than the range throws; a newer one returns a warning, because its new
|
|
@@ -397,6 +409,11 @@ declare class Emitter {
|
|
|
397
409
|
emit(): string;
|
|
398
410
|
private header;
|
|
399
411
|
private imports;
|
|
412
|
+
/**
|
|
413
|
+
* The typed `included` of each read: a resource union discriminated by `type` over the types
|
|
414
|
+
* the document's typed `included` lists, or over every type when the document doesn't type it.
|
|
415
|
+
*/
|
|
416
|
+
private includedAliases;
|
|
400
417
|
/**
|
|
401
418
|
* The links interface of a type that declares links, e.g. `ImagesLinks`: each declared link an
|
|
402
419
|
* optional member, since the server leaves a link out when its href resolves to `null`. It
|
|
@@ -475,11 +492,39 @@ declare class Emitter {
|
|
|
475
492
|
private atomicConst;
|
|
476
493
|
/**
|
|
477
494
|
* The `pageLimits` const: per collection, the `default`, `minimum` and `maximum` each `page`
|
|
478
|
-
* member declares
|
|
479
|
-
*
|
|
480
|
-
*
|
|
495
|
+
* member declares. A menu nests them under each arm's `kind`. Members declaring none, and
|
|
496
|
+
* collections with no such member, are left out. The client never clamps a `page` argument
|
|
497
|
+
* to these.
|
|
481
498
|
*/
|
|
482
499
|
private pageLimitsConst;
|
|
500
|
+
/**
|
|
501
|
+
* The `relatedPageLimits` const: the `pageLimits` shape for each related collection
|
|
502
|
+
* (`GET /{type}/{id}/{rel}`), keyed by parent type, then by relation name. Relations with no
|
|
503
|
+
* bounded member, and parents left with none, are left out.
|
|
504
|
+
*/
|
|
505
|
+
private relatedPageLimitsConst;
|
|
506
|
+
/**
|
|
507
|
+
* The `pageKinds` const: per paginated collection, the strategies its `page` parameter offers,
|
|
508
|
+
* the server's default first.
|
|
509
|
+
*/
|
|
510
|
+
private pageKindsConst;
|
|
511
|
+
/**
|
|
512
|
+
* The `relatedPageKinds` const: `pageKinds` for each related collection, keyed by parent type,
|
|
513
|
+
* then by relation name.
|
|
514
|
+
*/
|
|
515
|
+
private relatedPageKindsConst;
|
|
516
|
+
/**
|
|
517
|
+
* One line per parent type holding `render`'s value for each relation whose related endpoint
|
|
518
|
+
* it answers for. Relations it skips, and parents left with none, are left out.
|
|
519
|
+
*/
|
|
520
|
+
private byRelation;
|
|
521
|
+
/** The `page` parameter schema of the GET at `path`, if it has one. */
|
|
522
|
+
private pageSchema;
|
|
523
|
+
/**
|
|
524
|
+
* A page schema's bounded members as literal lines at `depth` (two spaces per level): flat for
|
|
525
|
+
* one strategy, nested under each arm's `kind` for a menu.
|
|
526
|
+
*/
|
|
527
|
+
private pageLimitLines;
|
|
483
528
|
/** One page schema's members that declare a `default`, `minimum` or `maximum`, as literal lines. */
|
|
484
529
|
private pageMemberLimits;
|
|
485
530
|
/**
|
|
@@ -612,7 +657,7 @@ declare function readSchemas(input: string): Promise<SchemaBundle>;
|
|
|
612
657
|
*
|
|
613
658
|
* The values are the raw, self-contained resource-object JSON Schema documents the bundle
|
|
614
659
|
* serves at `GET /schemas.json` — suitable to hand straight to a validation engine via the
|
|
615
|
-
* opt-in `validate?` seam
|
|
660
|
+
* opt-in `validate?` seam. This is a separate file from the client so the main
|
|
616
661
|
* descriptor/client output is unaffected when `--schemas` is absent.
|
|
617
662
|
*
|
|
618
663
|
* The schemas are emitted as JSON (deterministic, key order preserved) so the artifact is
|
|
@@ -697,8 +742,8 @@ declare function examplesOutputPath(output: string): string;
|
|
|
697
742
|
*
|
|
698
743
|
* When `config.schemas` is set, also read the bundle's JSON Schema bundle and write a
|
|
699
744
|
* separate per-type schema artifact (`<output>.schemas.gen.ts`) beside the client, wiring
|
|
700
|
-
* the opt-in validation seam
|
|
701
|
-
* `config.examples` writes the operation examples the same way
|
|
745
|
+
* the opt-in validation seam. The main client output is unchanged either way.
|
|
746
|
+
* `config.examples` writes the operation examples the same way.
|
|
702
747
|
*
|
|
703
748
|
* `config.server` is metadata-only for now: the served document is already per-server,
|
|
704
749
|
* so the caller selects the server by pointing `input` at the right document.
|
|
@@ -718,4 +763,4 @@ declare function generateFiles(config: CodegenConfig): Promise<string[]>;
|
|
|
718
763
|
declare function check(config: CodegenConfig): Promise<CheckResult>;
|
|
719
764
|
//#endregion
|
|
720
765
|
export { operationExamples as A, PathItemObject as B, VerbCollision as C, MINIMUM_CONTRACT as D, MAXIMUM_CONTRACT as E, InfoObject as F, SecurityRequirement as G, ResponseObject as H, MediaTypeObject as I, SecuritySchemeObject as K, 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 };
|
|
721
|
-
//# sourceMappingURL=index-
|
|
766
|
+
//# sourceMappingURL=index-FaJ8Xx6v.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-FaJ8Xx6v.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":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as operationExamples, B as PathItemObject, C as VerbCollision, D as MINIMUM_CONTRACT, E as MAXIMUM_CONTRACT, F as InfoObject, G as SecurityRequirement, H as ResponseObject, I as MediaTypeObject, K as SecuritySchemeObject, 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-
|
|
1
|
+
import { A as operationExamples, B as PathItemObject, C as VerbCollision, D as MINIMUM_CONTRACT, E as MAXIMUM_CONTRACT, F as InfoObject, G as SecurityRequirement, H as ResponseObject, I as MediaTypeObject, K as SecuritySchemeObject, 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-FaJ8Xx6v.mjs";
|
|
2
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 SecurityRequirement, type SecuritySchemeObject, 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 { 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-
|
|
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-i5j2I9fk.mjs";
|
|
2
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 };
|
|
@@ -173,6 +173,8 @@ var DescriptorBuilder = class {
|
|
|
173
173
|
if (countable !== void 0) descriptor.countable = countable;
|
|
174
174
|
const includable = this.includable(collection);
|
|
175
175
|
if (includable !== void 0) descriptor.includable = includable;
|
|
176
|
+
const included = this.includedTypes(collection === void 0 ? void 0 : this.operation(collection, "get"));
|
|
177
|
+
if (included !== void 0) descriptor.included = included;
|
|
176
178
|
const sortable = this.sortable(collection);
|
|
177
179
|
if (sortable !== void 0) descriptor.sortable = sortable;
|
|
178
180
|
const filterable = this.filterable(collection);
|
|
@@ -337,9 +339,9 @@ var DescriptorBuilder = class {
|
|
|
337
339
|
return mutations;
|
|
338
340
|
}
|
|
339
341
|
/**
|
|
340
|
-
* Attach the per-relation endpoint-exposure signal to a relation descriptor
|
|
342
|
+
* Attach the per-relation endpoint-exposure signal to a relation descriptor. The bundle
|
|
341
343
|
* can suppress a relation's related or relationship endpoint (`withoutRelatedEndpoint()` /
|
|
342
|
-
* `withoutRelationshipEndpoint()
|
|
344
|
+
* `withoutRelationshipEndpoint()`), which the OpenAPI reflects by simply omitting that
|
|
343
345
|
* relation's path — but codegen synthesizes generic `{rel}` templates, erasing the per-relation
|
|
344
346
|
* distinction. So mark `related: false` / `relationship: false` when this relation's specific
|
|
345
347
|
* path is absent, and ALWAYS emit an explicit `mutations` object (the verb flags when the
|
|
@@ -359,6 +361,8 @@ var DescriptorBuilder = class {
|
|
|
359
361
|
out.relationship = false;
|
|
360
362
|
out.mutations = {};
|
|
361
363
|
} else out.mutations = mutations;
|
|
364
|
+
const included = this.includedTypes(relatedGet);
|
|
365
|
+
if (included !== void 0) out.included = included;
|
|
362
366
|
const countable = withCountParam(relatedGet) ?? withCountParam(this.operation(relationshipPath, "get"));
|
|
363
367
|
if (countable !== void 0) out.countable = countable;
|
|
364
368
|
if (descriptor.cardinality === "many") {
|
|
@@ -532,6 +536,18 @@ var DescriptorBuilder = class {
|
|
|
532
536
|
const typeProp = data.properties?.["type"];
|
|
533
537
|
return isSchema$3(typeProp) && typeof typeProp.const === "string" ? typeProp.const : void 0;
|
|
534
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* The wire types a read's compound document can carry in `included`: the members of its
|
|
541
|
+
* document's `included.items` union. `undefined` when the document has no `included`, or types
|
|
542
|
+
* its items only as a generic `{type, id}` object.
|
|
543
|
+
*/
|
|
544
|
+
includedTypes(op) {
|
|
545
|
+
const ref = op === void 0 ? void 0 : this.firstOkJsonApiRef(op);
|
|
546
|
+
const included = (ref === void 0 ? void 0 : this.schemas[refName$1(ref) ?? ""])?.properties?.["included"];
|
|
547
|
+
const items = isSchema$3(included) ? included.items : void 0;
|
|
548
|
+
const types = items === void 0 || !Array.isArray(items.oneOf) ? void 0 : this.unionTypes(items);
|
|
549
|
+
return types !== void 0 && types.length > 0 ? types : void 0;
|
|
550
|
+
}
|
|
535
551
|
/** The `$ref` of the first 2xx JSON:API response body on an operation (the output document). */
|
|
536
552
|
firstOkJsonApiRef(op) {
|
|
537
553
|
for (const [code, response] of Object.entries(op.responses ?? {})) {
|
|
@@ -568,15 +584,13 @@ var DescriptorBuilder = class {
|
|
|
568
584
|
if (readPivot !== void 0) relation.readPivot = readPivot;
|
|
569
585
|
return relation;
|
|
570
586
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
};
|
|
579
|
-
}
|
|
587
|
+
const types = this.unionTypes(items);
|
|
588
|
+
if (types !== void 0) return types.length === 0 ? void 0 : {
|
|
589
|
+
cardinality: "many",
|
|
590
|
+
types,
|
|
591
|
+
polymorphic: true,
|
|
592
|
+
pivot: false
|
|
593
|
+
};
|
|
580
594
|
return;
|
|
581
595
|
}
|
|
582
596
|
if (Array.isArray(data.anyOf)) {
|
|
@@ -594,9 +608,12 @@ var DescriptorBuilder = class {
|
|
|
594
608
|
pivot = true;
|
|
595
609
|
readPivot = this.pivotDescriptor(pivotNodeFromAllOf(entry.allOf), "read");
|
|
596
610
|
}
|
|
597
|
-
} else
|
|
598
|
-
|
|
599
|
-
|
|
611
|
+
} else {
|
|
612
|
+
const union = this.unionTypes(entry);
|
|
613
|
+
if (union !== void 0) {
|
|
614
|
+
types.push(...union);
|
|
615
|
+
polymorphic = true;
|
|
616
|
+
}
|
|
600
617
|
}
|
|
601
618
|
if (types.length === 0) return;
|
|
602
619
|
const relation = polymorphic ? {
|
|
@@ -655,6 +672,24 @@ var DescriptorBuilder = class {
|
|
|
655
672
|
if (type !== void 0) return type;
|
|
656
673
|
}
|
|
657
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* The wire types of a union of `$ref`s: a `oneOf` discriminated by `type`, or the `anyOf` an
|
|
677
|
+
* older document writes. A member whose component carries no `type` const falls back to the
|
|
678
|
+
* discriminator mapping's key for its `$ref`. `undefined` when the schema is no union.
|
|
679
|
+
*/
|
|
680
|
+
unionTypes(schema) {
|
|
681
|
+
if (Array.isArray(schema.oneOf)) {
|
|
682
|
+
const byRef = discriminatorTypesByRef(schema);
|
|
683
|
+
const types = [];
|
|
684
|
+
for (const entry of schema.oneOf) {
|
|
685
|
+
if (typeof entry.$ref !== "string") continue;
|
|
686
|
+
const type = this.resolveConst(entry.$ref) ?? byRef.get(entry.$ref);
|
|
687
|
+
if (type !== void 0) types.push(type);
|
|
688
|
+
}
|
|
689
|
+
return types;
|
|
690
|
+
}
|
|
691
|
+
return Array.isArray(schema.anyOf) ? this.constsFromRefs(schema.anyOf) : void 0;
|
|
692
|
+
}
|
|
658
693
|
/** Resolve every `$ref` entry in a list to its wire type. */
|
|
659
694
|
constsFromRefs(entries) {
|
|
660
695
|
const types = [];
|
|
@@ -846,6 +881,16 @@ function pivotNodeOf(component) {
|
|
|
846
881
|
if (Array.isArray(data.items?.allOf)) return pivotNodeFromAllOf(data.items.allOf);
|
|
847
882
|
for (const entry of data.anyOf ?? []) if (Array.isArray(entry.allOf)) return pivotNodeFromAllOf(entry.allOf);
|
|
848
883
|
}
|
|
884
|
+
/** A `type` discriminator's mapping, inverted to the wire type each `$ref` stands for. */
|
|
885
|
+
function discriminatorTypesByRef(schema) {
|
|
886
|
+
const byRef = /* @__PURE__ */ new Map();
|
|
887
|
+
const discriminator = schema["discriminator"];
|
|
888
|
+
if (typeof discriminator !== "object" || discriminator === null) return byRef;
|
|
889
|
+
const { propertyName, mapping } = discriminator;
|
|
890
|
+
if (propertyName !== "type" || typeof mapping !== "object" || mapping === null) return byRef;
|
|
891
|
+
for (const [type, ref] of Object.entries(mapping)) if (typeof ref === "string") byRef.set(ref, type);
|
|
892
|
+
return byRef;
|
|
893
|
+
}
|
|
849
894
|
/** Whether a schema admits `null`: a `null` type, or a `null` branch in `anyOf`/`oneOf`. */
|
|
850
895
|
function isNullable(schema) {
|
|
851
896
|
const type = schema.type;
|
|
@@ -1032,7 +1077,7 @@ function buildAtomic(doc) {
|
|
|
1032
1077
|
}
|
|
1033
1078
|
/**
|
|
1034
1079
|
* Classify a page member set: `number`+`size` = `page`; `offset`+`limit` = `offset`; either
|
|
1035
|
-
* cursor bound (`after`/`before`) = `cursor` (link-driven
|
|
1080
|
+
* cursor bound (`after`/`before`) = `cursor` (link-driven); `number` alone = `fixed` (the
|
|
1036
1081
|
* server fixes the size); no members = `none`; anything else = `undefined` (unrecognised).
|
|
1037
1082
|
*/
|
|
1038
1083
|
function kindOfMembers(members) {
|
|
@@ -1185,7 +1230,7 @@ function buildExamples(doc, descriptor) {
|
|
|
1185
1230
|
/** Below this the document predates the structures this codegen reads. */
|
|
1186
1231
|
const MINIMUM_CONTRACT = 1;
|
|
1187
1232
|
/** Above this the document carries structures this codegen does not read. */
|
|
1188
|
-
const MAXIMUM_CONTRACT =
|
|
1233
|
+
const MAXIMUM_CONTRACT = 19;
|
|
1189
1234
|
/**
|
|
1190
1235
|
* Check the document's `info.x-generator.contract` against the range this codegen reads. A
|
|
1191
1236
|
* document older than the range throws; a newer one returns a warning, because its new
|
|
@@ -1195,8 +1240,8 @@ const MAXIMUM_CONTRACT = 18;
|
|
|
1195
1240
|
function checkContract(doc) {
|
|
1196
1241
|
const contract = doc.info?.["x-generator"]?.contract;
|
|
1197
1242
|
if (typeof contract !== "number") return;
|
|
1198
|
-
if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to
|
|
1199
|
-
if (contract >
|
|
1243
|
+
if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to 19). Use an older codegen release, or regenerate the document from a newer server.`);
|
|
1244
|
+
if (contract > 19) return `the document declares contract ${contract} and this codegen reads up to 19. It describes capabilities that were not generated; upgrade the codegen to pick them up.`;
|
|
1200
1245
|
}
|
|
1201
1246
|
//#endregion
|
|
1202
1247
|
//#region src/declared-links.ts
|
|
@@ -1559,6 +1604,33 @@ function refName(ref) {
|
|
|
1559
1604
|
function isSchema(value) {
|
|
1560
1605
|
return typeof value === "object" && value !== null;
|
|
1561
1606
|
}
|
|
1607
|
+
/** A `{ … }` object literal of pre-rendered entry lines, or `{}` when there are none. */
|
|
1608
|
+
function objectBody(lines) {
|
|
1609
|
+
return lines.length === 0 ? "{}" : `{\n${lines.join("\n")}\n}`;
|
|
1610
|
+
}
|
|
1611
|
+
/** The `kind` const a page strategy menu arm is selected by. */
|
|
1612
|
+
function armKind(arm) {
|
|
1613
|
+
const kind = arm.properties?.["kind"];
|
|
1614
|
+
return isSchema(kind) && typeof kind.const === "string" ? kind.const : void 0;
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* The strategies a page schema offers, as a tuple literal, read from the `x-paginator` stamp: a
|
|
1618
|
+
* single strategy's name, or a menu's arm kinds with its stamped default first. `undefined` when
|
|
1619
|
+
* the schema is absent, unstamped, or a menu whose default is not one of its arms.
|
|
1620
|
+
*/
|
|
1621
|
+
function pageKinds(schema) {
|
|
1622
|
+
const stamp = schema?.["x-paginator"];
|
|
1623
|
+
if (typeof stamp !== "object" || stamp === null) return;
|
|
1624
|
+
const { strategy, default: fallback } = stamp;
|
|
1625
|
+
let kinds;
|
|
1626
|
+
if (strategy === "multi") {
|
|
1627
|
+
const arms = (schema?.oneOf ?? []).flatMap((arm) => armKind(arm) ?? []);
|
|
1628
|
+
if (typeof fallback !== "string" || !arms.includes(fallback)) return;
|
|
1629
|
+
kinds = [fallback, ...arms.filter((kind) => kind !== fallback)];
|
|
1630
|
+
} else if (typeof strategy === "string") kinds = [strategy];
|
|
1631
|
+
else return;
|
|
1632
|
+
return `[${kinds.map((kind) => stringLiteral(kind)).join(", ")}]`;
|
|
1633
|
+
}
|
|
1562
1634
|
/** PascalCase a JSON:API wire type for use in a TS identifier (e.g. `public-profiles` -> `PublicProfiles`). */
|
|
1563
1635
|
function pascalCase(value) {
|
|
1564
1636
|
return value.split(/[^a-zA-Z0-9]+/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
@@ -1912,8 +1984,12 @@ var Emitter = class {
|
|
|
1912
1984
|
this.actionTypesMap(),
|
|
1913
1985
|
this.resourceMap(),
|
|
1914
1986
|
"export type ResourceMap = typeof resourceMap",
|
|
1987
|
+
this.includedAliases(),
|
|
1915
1988
|
this.atomicConst(),
|
|
1916
1989
|
this.pageLimitsConst(),
|
|
1990
|
+
this.relatedPageLimitsConst(),
|
|
1991
|
+
this.pageKindsConst(),
|
|
1992
|
+
this.relatedPageKindsConst(),
|
|
1917
1993
|
emitErrorCodes(readErrorCatalogue(this.doc)),
|
|
1918
1994
|
this.boundFactory()
|
|
1919
1995
|
].join("\n\n")}\n`;
|
|
@@ -1936,11 +2012,42 @@ var Emitter = class {
|
|
|
1936
2012
|
" createClient as createClientRuntime,",
|
|
1937
2013
|
" type ErrorCodeGuard,",
|
|
1938
2014
|
" hasErrorCode as hasErrorCodeRuntime,",
|
|
2015
|
+
" type IncludedResource,",
|
|
2016
|
+
" type RelatedIncludedResource,",
|
|
2017
|
+
" type RelationName,",
|
|
1939
2018
|
...this.declaredLinks.size > 0 ? [" type ResourceLinks,"] : [],
|
|
2019
|
+
" type TypeName,",
|
|
2020
|
+
" type WithLinks,",
|
|
1940
2021
|
"} from '@haddowg/json-api-client'"
|
|
1941
2022
|
].join("\n");
|
|
1942
2023
|
}
|
|
1943
2024
|
/**
|
|
2025
|
+
* The typed `included` of each read: a resource union discriminated by `type` over the types
|
|
2026
|
+
* the document's typed `included` lists, or over every type when the document doesn't type it.
|
|
2027
|
+
*/
|
|
2028
|
+
includedAliases() {
|
|
2029
|
+
return [
|
|
2030
|
+
"/**",
|
|
2031
|
+
" * One resource of a compound document's `included` for a read of type `T`, discriminated by",
|
|
2032
|
+
" * `type` over the types its `include` paths reach.",
|
|
2033
|
+
" */",
|
|
2034
|
+
"export type Included<T extends TypeName<ResourceMap>> = IncludedResource<",
|
|
2035
|
+
" ResourceMap,",
|
|
2036
|
+
" WithLinks<Attributes, Links>,",
|
|
2037
|
+
" T",
|
|
2038
|
+
">",
|
|
2039
|
+
"",
|
|
2040
|
+
"/**",
|
|
2041
|
+
" * One resource of the `included` of relation `R`'s related read on type `T`, discriminated by",
|
|
2042
|
+
" * `type` over the types its `include` paths reach.",
|
|
2043
|
+
" */",
|
|
2044
|
+
"export type RelatedIncluded<",
|
|
2045
|
+
" T extends TypeName<ResourceMap>,",
|
|
2046
|
+
" R extends RelationName<ResourceMap, T>,",
|
|
2047
|
+
"> = RelatedIncludedResource<ResourceMap, WithLinks<Attributes, Links>, T, R>"
|
|
2048
|
+
].join("\n");
|
|
2049
|
+
}
|
|
2050
|
+
/**
|
|
1944
2051
|
* The links interface of a type that declares links, e.g. `ImagesLinks`: each declared link an
|
|
1945
2052
|
* optional member, since the server leaves a link out when its href resolves to `null`. It
|
|
1946
2053
|
* extends the loose `ResourceLinks`, so `self` and any link the type doesn't declare stay
|
|
@@ -2214,23 +2321,14 @@ var Emitter = class {
|
|
|
2214
2321
|
}
|
|
2215
2322
|
/**
|
|
2216
2323
|
* The `pageLimits` const: per collection, the `default`, `minimum` and `maximum` each `page`
|
|
2217
|
-
* member declares
|
|
2218
|
-
*
|
|
2219
|
-
*
|
|
2324
|
+
* member declares. A menu nests them under each arm's `kind`. Members declaring none, and
|
|
2325
|
+
* collections with no such member, are left out. The client never clamps a `page` argument
|
|
2326
|
+
* to these.
|
|
2220
2327
|
*/
|
|
2221
2328
|
pageLimitsConst() {
|
|
2222
2329
|
const typeLines = [];
|
|
2223
2330
|
for (const type of Object.keys(this.descriptor)) {
|
|
2224
|
-
const
|
|
2225
|
-
const schema = (path === void 0 ? void 0 : this.doc.paths?.[path]?.get?.parameters)?.find((param) => param.name === "page")?.schema;
|
|
2226
|
-
if (schema === void 0) continue;
|
|
2227
|
-
let body = [];
|
|
2228
|
-
if (schema.oneOf !== void 0) for (const arm of schema.oneOf) {
|
|
2229
|
-
const kind = isSchema(arm.properties?.["kind"]) ? arm.properties["kind"].const : void 0;
|
|
2230
|
-
const members = this.pageMemberLimits(arm, " ");
|
|
2231
|
-
if (typeof kind === "string" && members.length > 0) body.push(` ${propertyKey(kind)}: {\n${members.join("\n")}\n },`);
|
|
2232
|
-
}
|
|
2233
|
-
else body = this.pageMemberLimits(schema, " ");
|
|
2331
|
+
const body = this.pageLimitLines(this.pageSchema(this.descriptor[type].paths["fetchMany"]), 2);
|
|
2234
2332
|
if (body.length > 0) typeLines.push(` ${propertyKey(type)}: {\n${body.join("\n")}\n },`);
|
|
2235
2333
|
}
|
|
2236
2334
|
return `${[
|
|
@@ -2240,7 +2338,96 @@ var Emitter = class {
|
|
|
2240
2338
|
" * is absent and `maximum` is the largest value it serves. The client sends the `page` you pass",
|
|
2241
2339
|
" * unchanged, so read these to bound a page-size picker.",
|
|
2242
2340
|
" */"
|
|
2243
|
-
].join("\n")}\nexport const pageLimits = ${typeLines
|
|
2341
|
+
].join("\n")}\nexport const pageLimits = ${objectBody(typeLines)} as const`;
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* The `relatedPageLimits` const: the `pageLimits` shape for each related collection
|
|
2345
|
+
* (`GET /{type}/{id}/{rel}`), keyed by parent type, then by relation name. Relations with no
|
|
2346
|
+
* bounded member, and parents left with none, are left out.
|
|
2347
|
+
*/
|
|
2348
|
+
relatedPageLimitsConst() {
|
|
2349
|
+
const lines = this.byRelation((path) => {
|
|
2350
|
+
const body = this.pageLimitLines(this.pageSchema(path), 3);
|
|
2351
|
+
return body.length === 0 ? void 0 : `{\n${body.join("\n")}\n }`;
|
|
2352
|
+
});
|
|
2353
|
+
return `${[
|
|
2354
|
+
"/**",
|
|
2355
|
+
" * The bounds each related collection's `page` parameter declares, keyed by parent type, then by",
|
|
2356
|
+
" * relation name, then as in `pageLimits`: by strategy for a related collection offering several,",
|
|
2357
|
+
" * then by wire key. Relations whose related endpoint declares no bounds are left out.",
|
|
2358
|
+
" */"
|
|
2359
|
+
].join("\n")}\nexport const relatedPageLimits = ${objectBody(lines)} as const`;
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* The `pageKinds` const: per paginated collection, the strategies its `page` parameter offers,
|
|
2363
|
+
* the server's default first.
|
|
2364
|
+
*/
|
|
2365
|
+
pageKindsConst() {
|
|
2366
|
+
const lines = [];
|
|
2367
|
+
for (const type of Object.keys(this.descriptor)) {
|
|
2368
|
+
const kinds = pageKinds(this.pageSchema(this.descriptor[type].paths["fetchMany"]));
|
|
2369
|
+
if (kinds !== void 0) lines.push(` ${propertyKey(type)}: ${kinds},`);
|
|
2370
|
+
}
|
|
2371
|
+
return `${[
|
|
2372
|
+
"/**",
|
|
2373
|
+
" * The pagination strategies each collection offers, keyed by type. A single strategy is a",
|
|
2374
|
+
" * one-entry tuple; a menu lists its arms' `kind` values with the one the server uses when the",
|
|
2375
|
+
" * request names none first. Unpaginated collections are left out.",
|
|
2376
|
+
" */"
|
|
2377
|
+
].join("\n")}\nexport const pageKinds = ${objectBody(lines)} as const`;
|
|
2378
|
+
}
|
|
2379
|
+
/**
|
|
2380
|
+
* The `relatedPageKinds` const: `pageKinds` for each related collection, keyed by parent type,
|
|
2381
|
+
* then by relation name.
|
|
2382
|
+
*/
|
|
2383
|
+
relatedPageKindsConst() {
|
|
2384
|
+
const lines = this.byRelation((path) => pageKinds(this.pageSchema(path)));
|
|
2385
|
+
return `${[
|
|
2386
|
+
"/**",
|
|
2387
|
+
" * The pagination strategies each related collection offers, keyed by parent type, then by",
|
|
2388
|
+
" * relation name, in the same form as `pageKinds`. Unpaginated relations are left out.",
|
|
2389
|
+
" */"
|
|
2390
|
+
].join("\n")}\nexport const relatedPageKinds = ${objectBody(lines)} as const`;
|
|
2391
|
+
}
|
|
2392
|
+
/**
|
|
2393
|
+
* One line per parent type holding `render`'s value for each relation whose related endpoint
|
|
2394
|
+
* it answers for. Relations it skips, and parents left with none, are left out.
|
|
2395
|
+
*/
|
|
2396
|
+
byRelation(render) {
|
|
2397
|
+
const lines = [];
|
|
2398
|
+
for (const type of Object.keys(this.descriptor)) {
|
|
2399
|
+
const resource = this.descriptor[type];
|
|
2400
|
+
const template = resource.paths["fetchRelated"];
|
|
2401
|
+
if (template === void 0) continue;
|
|
2402
|
+
const relLines = [];
|
|
2403
|
+
for (const [name, relation] of Object.entries(resource.relations)) {
|
|
2404
|
+
if (relation.related === false) continue;
|
|
2405
|
+
const value = render(template.replace("{rel}", relation.uriField ?? name));
|
|
2406
|
+
if (value !== void 0) relLines.push(` ${propertyKey(name)}: ${value},`);
|
|
2407
|
+
}
|
|
2408
|
+
if (relLines.length > 0) lines.push(` ${propertyKey(type)}: {\n${relLines.join("\n")}\n },`);
|
|
2409
|
+
}
|
|
2410
|
+
return lines;
|
|
2411
|
+
}
|
|
2412
|
+
/** The `page` parameter schema of the GET at `path`, if it has one. */
|
|
2413
|
+
pageSchema(path) {
|
|
2414
|
+
return (path === void 0 ? void 0 : this.doc.paths?.[path]?.get?.parameters)?.find((param) => param.name === "page")?.schema;
|
|
2415
|
+
}
|
|
2416
|
+
/**
|
|
2417
|
+
* A page schema's bounded members as literal lines at `depth` (two spaces per level): flat for
|
|
2418
|
+
* one strategy, nested under each arm's `kind` for a menu.
|
|
2419
|
+
*/
|
|
2420
|
+
pageLimitLines(schema, depth) {
|
|
2421
|
+
if (schema === void 0) return [];
|
|
2422
|
+
const indent = " ".repeat(depth);
|
|
2423
|
+
if (schema.oneOf === void 0) return this.pageMemberLimits(schema, indent);
|
|
2424
|
+
const lines = [];
|
|
2425
|
+
for (const arm of schema.oneOf) {
|
|
2426
|
+
const kind = armKind(arm);
|
|
2427
|
+
const members = this.pageMemberLimits(arm, `${indent} `);
|
|
2428
|
+
if (kind !== void 0 && members.length > 0) lines.push(`${indent}${propertyKey(kind)}: {\n${members.join("\n")}\n${indent}},`);
|
|
2429
|
+
}
|
|
2430
|
+
return lines;
|
|
2244
2431
|
}
|
|
2245
2432
|
/** One page schema's members that declare a `default`, `minimum` or `maximum`, as literal lines. */
|
|
2246
2433
|
pageMemberLimits(schema, indent) {
|
|
@@ -2501,7 +2688,7 @@ function emitExamples(doc, examples) {
|
|
|
2501
2688
|
*
|
|
2502
2689
|
* The values are the raw, self-contained resource-object JSON Schema documents the bundle
|
|
2503
2690
|
* serves at `GET /schemas.json` — suitable to hand straight to a validation engine via the
|
|
2504
|
-
* opt-in `validate?` seam
|
|
2691
|
+
* opt-in `validate?` seam. This is a separate file from the client so the main
|
|
2505
2692
|
* descriptor/client output is unaffected when `--schemas` is absent.
|
|
2506
2693
|
*
|
|
2507
2694
|
* The schemas are emitted as JSON (deterministic, key order preserved) so the artifact is
|
|
@@ -2572,10 +2759,10 @@ async function readSchemas(input) {
|
|
|
2572
2759
|
/**
|
|
2573
2760
|
* @haddowg/json-api-codegen — reads the bundle's OpenAPI 3.1 document (from a URL or
|
|
2574
2761
|
* a local file) and emits the runtime descriptor + derived types + a bound
|
|
2575
|
-
* `createClient
|
|
2762
|
+
* `createClient`. Optionally reads the bundle's JSON Schemas to wire the
|
|
2576
2763
|
* opt-in validation seam.
|
|
2577
2764
|
*
|
|
2578
|
-
* The spec is fully self-describing for our needs (see
|
|
2765
|
+
* The spec is fully self-describing for our needs (see docs/concepts.md): type identity is
|
|
2579
2766
|
* `properties.type.const`; relationships, cardinality and related types come from the
|
|
2580
2767
|
* relationship components' `data`; allowed `?include` paths are a bounded, pre-expanded
|
|
2581
2768
|
* enum per endpoint; the create client-id policy is encoded in the create request
|
|
@@ -2637,8 +2824,8 @@ async function render(config) {
|
|
|
2637
2824
|
*
|
|
2638
2825
|
* When `config.schemas` is set, also read the bundle's JSON Schema bundle and write a
|
|
2639
2826
|
* separate per-type schema artifact (`<output>.schemas.gen.ts`) beside the client, wiring
|
|
2640
|
-
* the opt-in validation seam
|
|
2641
|
-
* `config.examples` writes the operation examples the same way
|
|
2827
|
+
* the opt-in validation seam. The main client output is unchanged either way.
|
|
2828
|
+
* `config.examples` writes the operation examples the same way.
|
|
2642
2829
|
*
|
|
2643
2830
|
* `config.server` is metadata-only for now: the served document is already per-server,
|
|
2644
2831
|
* so the caller selects the server by pointing `input` at the right document.
|
|
@@ -2688,4 +2875,4 @@ async function check(config) {
|
|
|
2688
2875
|
//#endregion
|
|
2689
2876
|
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 };
|
|
2690
2877
|
|
|
2691
|
-
//# sourceMappingURL=src-
|
|
2878
|
+
//# sourceMappingURL=src-i5j2I9fk.mjs.map
|