@haddowg/json-api-codegen 1.8.1 → 1.10.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/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{index-COQQrn7a.d.mts → index-DqeWPRAD.d.mts} +37 -3
- package/dist/index-DqeWPRAD.d.mts.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-AWTGjMiX.mjs → src-C3noZURK.mjs} +223 -69
- package/dist/src-C3noZURK.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/index-COQQrn7a.d.mts.map +0 -1
- package/dist/src-AWTGjMiX.mjs.map +0 -1
package/dist/cli.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as CodegenConfig } from "./index-
|
|
1
|
+
import { r as CodegenConfig } from "./index-DqeWPRAD.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-C3noZURK.mjs";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
//#region src/cli.ts
|
|
@@ -56,6 +56,12 @@ interface OperationObject {
|
|
|
56
56
|
parameters?: readonly ParameterObject[];
|
|
57
57
|
requestBody?: RequestBodyObject;
|
|
58
58
|
responses?: Record<string, ResponseObject>;
|
|
59
|
+
/** What a custom action declares about its effects (generator contract 15). */
|
|
60
|
+
'x-safety'?: {
|
|
61
|
+
readOnly?: unknown;
|
|
62
|
+
destructive?: unknown;
|
|
63
|
+
idempotent?: unknown;
|
|
64
|
+
};
|
|
59
65
|
}
|
|
60
66
|
type HttpMethod = 'get' | 'post' | 'patch' | 'put' | 'delete';
|
|
61
67
|
type PathItemObject = Partial<Record<HttpMethod, OperationObject>>;
|
|
@@ -152,9 +158,12 @@ declare class DescriptorBuilder {
|
|
|
152
158
|
* `{collection}/{id}/-actions/{name}` paths. The action name is the last segment; scope
|
|
153
159
|
* is `resource` when the path carries `/{id}/`, else `collection`. `input` is `document`
|
|
154
160
|
* (a JSON:API requestBody), `none` (no requestBody) or `raw` (a non-JSON:API body);
|
|
155
|
-
* `output` is `document` (a 2xx returns a JSON:API document) or `none` (only `204`).
|
|
161
|
+
* `output` is `document` (a 2xx returns a JSON:API document) or `none` (only `204`). An action
|
|
162
|
+
* answering to several methods gives one entry per method (see {@link pickOperations}).
|
|
156
163
|
*/
|
|
157
164
|
private actions;
|
|
165
|
+
/** The descriptor for one operation of a custom action. */
|
|
166
|
+
private action;
|
|
158
167
|
/**
|
|
159
168
|
* The non-JSON:API media types an action answers with under its 2xx responses, each once, in
|
|
160
169
|
* the document's order, with how the client reads its body.
|
|
@@ -295,7 +304,7 @@ declare function buildExamples(doc: OpenApiDocument, descriptor: ApiDescriptor):
|
|
|
295
304
|
/** Below this the document predates the structures this codegen reads. */
|
|
296
305
|
declare const MINIMUM_CONTRACT = 1;
|
|
297
306
|
/** Above this the document carries structures this codegen does not read. */
|
|
298
|
-
declare const MAXIMUM_CONTRACT =
|
|
307
|
+
declare const MAXIMUM_CONTRACT = 15;
|
|
299
308
|
/**
|
|
300
309
|
* Check the document's `info.x-generator.contract` against the range this codegen reads. A
|
|
301
310
|
* document older than the range throws; a newer one returns a warning, because its new
|
|
@@ -346,6 +355,8 @@ declare class Emitter {
|
|
|
346
355
|
private readonly rawBodyAliases;
|
|
347
356
|
/** JSDoc for entries of the `resourceMap` literal, keyed by their path joined with `/`. */
|
|
348
357
|
private readonly literalDocs;
|
|
358
|
+
/** Wire type -> the links it declares, for the types that declare any. */
|
|
359
|
+
private readonly declaredLinks;
|
|
349
360
|
/** Provenance of the source document, stamped into the generated header for drift tracking. */
|
|
350
361
|
private readonly provenance;
|
|
351
362
|
private readonly doc;
|
|
@@ -360,6 +371,22 @@ declare class Emitter {
|
|
|
360
371
|
emit(): string;
|
|
361
372
|
private header;
|
|
362
373
|
private imports;
|
|
374
|
+
/**
|
|
375
|
+
* The links interface of a type that declares links, e.g. `ImagesLinks`: each declared link an
|
|
376
|
+
* optional member, since the server leaves a link out when its href resolves to `null`. It
|
|
377
|
+
* extends the loose `ResourceLinks`, so `self` and any link the type doesn't declare stay
|
|
378
|
+
* reachable as before.
|
|
379
|
+
*/
|
|
380
|
+
private linksInterface;
|
|
381
|
+
/**
|
|
382
|
+
* A declared link object as a multi-line type: `href`, each static member as its `const`, and a
|
|
383
|
+
* `meta` object whose members are typed as attributes are, each with its own TSDoc.
|
|
384
|
+
*/
|
|
385
|
+
private linkObjectType;
|
|
386
|
+
/** An object type with one line, and its TSDoc, per property. */
|
|
387
|
+
private documentedObject;
|
|
388
|
+
/** A static link member's `const` as a literal type, else its schema's type. */
|
|
389
|
+
private constOrType;
|
|
363
390
|
/** Per-type attribute interface, e.g. `export interface AlbumsAttributes { ... }`. */
|
|
364
391
|
private attributeInterface;
|
|
365
392
|
/**
|
|
@@ -458,6 +485,13 @@ declare class Emitter {
|
|
|
458
485
|
* argument to the runtime factory).
|
|
459
486
|
*/
|
|
460
487
|
private attributesMap;
|
|
488
|
+
/**
|
|
489
|
+
* Emit the `Links` interface mapping each type that declares links to its links interface. The
|
|
490
|
+
* runtime threads it beside the `Attributes` map, so a read result's `$links` carries the
|
|
491
|
+
* declared links. A type absent from it keeps the loose `ResourceLinks`. Passed as the sixth
|
|
492
|
+
* type argument to the runtime factory.
|
|
493
|
+
*/
|
|
494
|
+
private linksMap;
|
|
461
495
|
/**
|
|
462
496
|
* Emit the `WriteAttributes` interface mapping each writable wire type to its
|
|
463
497
|
* `{ create; update }` attribute pair. A read-only type (no create/update component)
|
|
@@ -658,4 +692,4 @@ declare function generateFiles(config: CodegenConfig): Promise<string[]>;
|
|
|
658
692
|
declare function check(config: CodegenConfig): Promise<CheckResult>;
|
|
659
693
|
//#endregion
|
|
660
694
|
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-
|
|
695
|
+
//# sourceMappingURL=index-DqeWPRAD.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-DqeWPRAD.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, 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-
|
|
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-DqeWPRAD.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 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-C3noZURK.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 };
|
|
@@ -74,7 +74,7 @@ function sortRecord(record) {
|
|
|
74
74
|
function refName$1(ref) {
|
|
75
75
|
return ref.startsWith(REF_PREFIX$2) ? ref.slice(21) : void 0;
|
|
76
76
|
}
|
|
77
|
-
function isSchema$
|
|
77
|
+
function isSchema$3(value) {
|
|
78
78
|
return typeof value === "object" && value !== null;
|
|
79
79
|
}
|
|
80
80
|
/** The operationId prefixes of a relation's related read and its relationship endpoint. */
|
|
@@ -164,20 +164,20 @@ var DescriptorBuilder = class {
|
|
|
164
164
|
resourceType(name, schema) {
|
|
165
165
|
if (!name.endsWith("Resource") || name.endsWith("ResourceIdentifier")) return;
|
|
166
166
|
const constValue = schema.properties?.["type"];
|
|
167
|
-
if (isSchema$
|
|
167
|
+
if (isSchema$3(constValue) && typeof constValue.const === "string") return constValue.const;
|
|
168
168
|
}
|
|
169
169
|
/** Follow a `$ref` to its identifier/resource schema and read `properties.type.const`. */
|
|
170
170
|
resolveConst(ref) {
|
|
171
171
|
const target = refName$1(ref);
|
|
172
172
|
if (target === void 0) return;
|
|
173
173
|
const typeProp = this.schemas[target]?.properties?.["type"];
|
|
174
|
-
return isSchema$
|
|
174
|
+
return isSchema$3(typeProp) && typeof typeProp.const === "string" ? typeProp.const : void 0;
|
|
175
175
|
}
|
|
176
176
|
attributes(base) {
|
|
177
177
|
const props = this.schemas[`${base}Attributes`]?.properties;
|
|
178
178
|
if (props === void 0) return {};
|
|
179
179
|
const out = {};
|
|
180
|
-
for (const [name, prop] of Object.entries(props)) if (isSchema$
|
|
180
|
+
for (const [name, prop] of Object.entries(props)) if (isSchema$3(prop)) out[name] = formatHint(prop);
|
|
181
181
|
return sortRecord(out);
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
@@ -188,7 +188,7 @@ var DescriptorBuilder = class {
|
|
|
188
188
|
* request pivot off the write bodies.
|
|
189
189
|
*/
|
|
190
190
|
relations(wireType, base, resource, collection) {
|
|
191
|
-
const relProps = isSchema$
|
|
191
|
+
const relProps = isSchema$3(resource.properties?.["relationships"]) ? resource.properties["relationships"].properties ?? {} : {};
|
|
192
192
|
const writes = this.writeRelationships(base);
|
|
193
193
|
const endpoints = this.relationEndpoints(wireType);
|
|
194
194
|
const out = {};
|
|
@@ -210,7 +210,7 @@ var DescriptorBuilder = class {
|
|
|
210
210
|
out[name] = collection === void 0 ? descriptor : this.withRelationExposure(descriptor, collection, name, endpoints.get(name));
|
|
211
211
|
};
|
|
212
212
|
for (const [name, prop] of Object.entries(relProps)) {
|
|
213
|
-
if (!isSchema$
|
|
213
|
+
if (!isSchema$3(prop) || typeof prop.$ref !== "string" || writes.get(name)?.writeOnly) continue;
|
|
214
214
|
add(name, this.schemas[refName$1(prop.$ref) ?? ""], false);
|
|
215
215
|
}
|
|
216
216
|
for (const [name, write] of writes) if (write.writeOnly || !(name in relProps)) add(name, this.schemas[refName$1(write.ref) ?? ""], true);
|
|
@@ -224,10 +224,10 @@ var DescriptorBuilder = class {
|
|
|
224
224
|
const out = /* @__PURE__ */ new Map();
|
|
225
225
|
for (const request of [`${base}CreateRequest`, `${base}UpdateRequest`]) {
|
|
226
226
|
const data = this.schemas[request]?.properties?.["data"];
|
|
227
|
-
const relationships = isSchema$
|
|
228
|
-
const props = isSchema$
|
|
227
|
+
const relationships = isSchema$3(data) ? data.properties?.["relationships"] : void 0;
|
|
228
|
+
const props = isSchema$3(relationships) ? relationships.properties ?? {} : {};
|
|
229
229
|
for (const [name, prop] of Object.entries(props)) {
|
|
230
|
-
if (!isSchema$
|
|
230
|
+
if (!isSchema$3(prop) || typeof prop.$ref !== "string") continue;
|
|
231
231
|
const writeOnly = prop["writeOnly"] === true || out.get(name)?.writeOnly === true;
|
|
232
232
|
out.set(name, {
|
|
233
233
|
ref: out.get(name)?.ref ?? prop.$ref,
|
|
@@ -327,7 +327,8 @@ var DescriptorBuilder = class {
|
|
|
327
327
|
* `{collection}/{id}/-actions/{name}` paths. The action name is the last segment; scope
|
|
328
328
|
* is `resource` when the path carries `/{id}/`, else `collection`. `input` is `document`
|
|
329
329
|
* (a JSON:API requestBody), `none` (no requestBody) or `raw` (a non-JSON:API body);
|
|
330
|
-
* `output` is `document` (a 2xx returns a JSON:API document) or `none` (only `204`).
|
|
330
|
+
* `output` is `document` (a 2xx returns a JSON:API document) or `none` (only `204`). An action
|
|
331
|
+
* answering to several methods gives one entry per method (see {@link pickOperations}).
|
|
331
332
|
*/
|
|
332
333
|
actions(collection) {
|
|
333
334
|
const out = {};
|
|
@@ -344,47 +345,66 @@ var DescriptorBuilder = class {
|
|
|
344
345
|
name = path.slice(collectionPrefix.length);
|
|
345
346
|
} else continue;
|
|
346
347
|
if (name.length === 0 || name.includes("/")) continue;
|
|
347
|
-
const picked =
|
|
348
|
-
if (picked ===
|
|
348
|
+
const picked = pickOperations(item);
|
|
349
|
+
if (picked.length === 0) {
|
|
349
350
|
console.warn(`json-api codegen: custom action path "${path}" advertises no usable operation; skipping.`);
|
|
350
351
|
continue;
|
|
351
352
|
}
|
|
352
|
-
const { method, op }
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
path,
|
|
358
|
-
input,
|
|
359
|
-
output
|
|
360
|
-
};
|
|
361
|
-
if (method !== "post") action.method = method.toUpperCase();
|
|
362
|
-
if (input === "document") {
|
|
363
|
-
const inputType = this.documentPrimary(actionRequestRef(op))?.type;
|
|
364
|
-
if (inputType !== void 0) action.inputType = inputType;
|
|
365
|
-
}
|
|
366
|
-
if (output === "document") {
|
|
367
|
-
const primary = this.documentPrimary(this.firstOkJsonApiRef(op));
|
|
368
|
-
if (primary !== void 0) {
|
|
369
|
-
action.outputType = primary.type;
|
|
370
|
-
action.outputCardinality = primary.cardinality;
|
|
353
|
+
for (const [index, { method, op }] of picked.entries()) {
|
|
354
|
+
const key = index === 0 ? name : methodActionName(name, method);
|
|
355
|
+
if (key in out) {
|
|
356
|
+
console.warn(`json-api codegen: the ${method.toUpperCase()} operation of custom action path "${path}" would be named "${key}", which another action already uses; skipping.`);
|
|
357
|
+
continue;
|
|
371
358
|
}
|
|
359
|
+
out[key] = this.action(scope, path, method, op);
|
|
372
360
|
}
|
|
373
|
-
if (input === "raw") {
|
|
374
|
-
const contentType = rawContentType(op);
|
|
375
|
-
if (contentType !== void 0) action.contentType = contentType;
|
|
376
|
-
}
|
|
377
|
-
const rawOutput = this.rawOutput(op);
|
|
378
|
-
if (rawOutput.length > 0) action.rawOutput = rawOutput;
|
|
379
|
-
const requestContent = Object.entries(op.requestBody?.content ?? {});
|
|
380
|
-
if (requestContent.length > 1) action.inputMedia = requestContent.map(([mediaType, media]) => ({
|
|
381
|
-
mediaType,
|
|
382
|
-
kind: this.inputMediaKind(mediaType, media.schema)
|
|
383
|
-
}));
|
|
384
|
-
out[name] = action;
|
|
385
361
|
}
|
|
386
362
|
return sortRecord(out);
|
|
387
363
|
}
|
|
364
|
+
/** The descriptor for one operation of a custom action. */
|
|
365
|
+
action(scope, path, method, op) {
|
|
366
|
+
const input = actionInput(op);
|
|
367
|
+
const output = actionOutput(op);
|
|
368
|
+
const action = {
|
|
369
|
+
scope,
|
|
370
|
+
path,
|
|
371
|
+
input,
|
|
372
|
+
output
|
|
373
|
+
};
|
|
374
|
+
if (method !== "post") action.method = method.toUpperCase();
|
|
375
|
+
if (input === "document") {
|
|
376
|
+
const inputType = this.documentPrimary(actionRequestRef(op))?.type;
|
|
377
|
+
if (inputType !== void 0) action.inputType = inputType;
|
|
378
|
+
}
|
|
379
|
+
if (output === "document") {
|
|
380
|
+
const primary = this.documentPrimary(this.firstOkJsonApiRef(op));
|
|
381
|
+
if (primary !== void 0) {
|
|
382
|
+
action.outputType = primary.type;
|
|
383
|
+
action.outputCardinality = primary.cardinality;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (input === "raw") {
|
|
387
|
+
const contentType = rawContentType(op);
|
|
388
|
+
if (contentType !== void 0) action.contentType = contentType;
|
|
389
|
+
}
|
|
390
|
+
const rawOutput = this.rawOutput(op);
|
|
391
|
+
if (rawOutput.length > 0) action.rawOutput = rawOutput;
|
|
392
|
+
const requestContent = Object.entries(op.requestBody?.content ?? {});
|
|
393
|
+
if (requestContent.length > 1) action.inputMedia = requestContent.map(([mediaType, media]) => ({
|
|
394
|
+
mediaType,
|
|
395
|
+
kind: this.inputMediaKind(mediaType, media.schema)
|
|
396
|
+
}));
|
|
397
|
+
const safety = op["x-safety"];
|
|
398
|
+
for (const flag of [
|
|
399
|
+
"readOnly",
|
|
400
|
+
"destructive",
|
|
401
|
+
"idempotent"
|
|
402
|
+
]) {
|
|
403
|
+
const value = safety?.[flag];
|
|
404
|
+
if (typeof value === "boolean") action[flag] = value;
|
|
405
|
+
}
|
|
406
|
+
return action;
|
|
407
|
+
}
|
|
388
408
|
/**
|
|
389
409
|
* The non-JSON:API media types an action answers with under its 2xx responses, each once, in
|
|
390
410
|
* the document's order, with how the client reads its body.
|
|
@@ -439,8 +459,8 @@ var DescriptorBuilder = class {
|
|
|
439
459
|
if (typeof ref !== "string") return;
|
|
440
460
|
const component = refName$1(ref);
|
|
441
461
|
const data = component === void 0 ? void 0 : this.schemas[component]?.properties?.["data"];
|
|
442
|
-
if (!isSchema$
|
|
443
|
-
if (isSchema$
|
|
462
|
+
if (!isSchema$3(data)) return;
|
|
463
|
+
if (isSchema$3(data.items)) {
|
|
444
464
|
const itemRef = data.items.$ref;
|
|
445
465
|
const many = typeof itemRef === "string" ? this.resolveConst(itemRef) : void 0;
|
|
446
466
|
return many === void 0 ? void 0 : {
|
|
@@ -458,7 +478,7 @@ var DescriptorBuilder = class {
|
|
|
458
478
|
dataConst(data) {
|
|
459
479
|
if (typeof data.$ref === "string") return this.resolveConst(data.$ref);
|
|
460
480
|
const typeProp = data.properties?.["type"];
|
|
461
|
-
return isSchema$
|
|
481
|
+
return isSchema$3(typeProp) && typeof typeProp.const === "string" ? typeProp.const : void 0;
|
|
462
482
|
}
|
|
463
483
|
/** The `$ref` of the first 2xx JSON:API response body on an operation (the output document). */
|
|
464
484
|
firstOkJsonApiRef(op) {
|
|
@@ -471,7 +491,7 @@ var DescriptorBuilder = class {
|
|
|
471
491
|
/** Resolve a relationship component's `data` into a {@link RelationDescriptor}. */
|
|
472
492
|
relationFromComponent(component) {
|
|
473
493
|
const data = component.properties?.["data"];
|
|
474
|
-
if (!isSchema$
|
|
494
|
+
if (!isSchema$3(data)) return;
|
|
475
495
|
if (data.type === "array") {
|
|
476
496
|
const items = data.items;
|
|
477
497
|
if (items === void 0) return;
|
|
@@ -550,7 +570,7 @@ var DescriptorBuilder = class {
|
|
|
550
570
|
const fields = {};
|
|
551
571
|
const nullable = [];
|
|
552
572
|
for (const [name, prop] of Object.entries(schema?.properties ?? {})) {
|
|
553
|
-
if (!isSchema$
|
|
573
|
+
if (!isSchema$3(prop) || prop[side === "read" ? "writeOnly" : "readOnly"] === true) continue;
|
|
554
574
|
fields[name] = formatHint(prop);
|
|
555
575
|
if (isNullable(prop)) nullable.push(name);
|
|
556
576
|
}
|
|
@@ -580,7 +600,7 @@ var DescriptorBuilder = class {
|
|
|
580
600
|
}
|
|
581
601
|
clientId(base) {
|
|
582
602
|
const create = this.schemas[`${base}CreateRequest`];
|
|
583
|
-
const data = isSchema$
|
|
603
|
+
const data = isSchema$3(create?.properties?.["data"]) ? create.properties["data"] : void 0;
|
|
584
604
|
if (data === void 0) return "forbidden";
|
|
585
605
|
if (data.properties?.["id"] === false) return "forbidden";
|
|
586
606
|
return data.required?.includes("id") ? "required" : "optional";
|
|
@@ -746,8 +766,8 @@ var DescriptorBuilder = class {
|
|
|
746
766
|
function pivotNodeFromAllOf(allOf) {
|
|
747
767
|
for (const entry of allOf) {
|
|
748
768
|
const meta = entry.properties?.["meta"];
|
|
749
|
-
const pivot = isSchema$
|
|
750
|
-
if (isSchema$
|
|
769
|
+
const pivot = isSchema$3(meta) ? meta.properties?.["pivot"] : void 0;
|
|
770
|
+
if (isSchema$3(pivot)) return pivot;
|
|
751
771
|
}
|
|
752
772
|
}
|
|
753
773
|
/**
|
|
@@ -756,7 +776,7 @@ function pivotNodeFromAllOf(allOf) {
|
|
|
756
776
|
*/
|
|
757
777
|
function pivotNodeOf(component) {
|
|
758
778
|
const data = component.properties?.["data"];
|
|
759
|
-
if (!isSchema$
|
|
779
|
+
if (!isSchema$3(data)) return;
|
|
760
780
|
if (Array.isArray(data.items?.allOf)) return pivotNodeFromAllOf(data.items.allOf);
|
|
761
781
|
for (const entry of data.anyOf ?? []) if (Array.isArray(entry.allOf)) return pivotNodeFromAllOf(entry.allOf);
|
|
762
782
|
}
|
|
@@ -837,18 +857,39 @@ const ACTION_METHODS = [
|
|
|
837
857
|
"delete"
|
|
838
858
|
];
|
|
839
859
|
/**
|
|
840
|
-
* The
|
|
841
|
-
* the first advertised method.
|
|
860
|
+
* The operations an action is invoked over, the preferred one first: POST when advertised (the
|
|
861
|
+
* common default), else the first advertised method. The other methods follow only when their
|
|
862
|
+
* `operationId` differs from the preferred one's, as contract 15 makes it for an action answering
|
|
863
|
+
* to several methods. An older document gave every method one id, and its client exposed only the
|
|
864
|
+
* preferred one. Empty when the path item carries no operation at all.
|
|
842
865
|
*/
|
|
843
|
-
function
|
|
866
|
+
function pickOperations(item) {
|
|
844
867
|
const available = ACTION_METHODS.filter((method) => item[method] !== void 0);
|
|
845
|
-
const
|
|
846
|
-
if (
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
method,
|
|
850
|
-
op
|
|
851
|
-
};
|
|
868
|
+
const preferred = available.includes("post") ? "post" : available[0];
|
|
869
|
+
if (preferred === void 0) return [];
|
|
870
|
+
const first = item[preferred];
|
|
871
|
+
const out = [{
|
|
872
|
+
method: preferred,
|
|
873
|
+
op: first
|
|
874
|
+
}];
|
|
875
|
+
for (const method of available) {
|
|
876
|
+
const op = item[method];
|
|
877
|
+
if (method !== preferred && op.operationId !== first.operationId) out.push({
|
|
878
|
+
method,
|
|
879
|
+
op
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
return out;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* The name a multi-method action's other methods go under: the action's name and the method,
|
|
886
|
+
* joined the way the name joins its words. So `export` gives `exportGet` and `bulk-import` gives
|
|
887
|
+
* `bulk-import-get`. Core refuses action ids that differ only in case or punctuation, so the
|
|
888
|
+
* name can't clash with another action's.
|
|
889
|
+
*/
|
|
890
|
+
function methodActionName(name, method) {
|
|
891
|
+
const separator = /[-_.]/.exec(name)?.[0];
|
|
892
|
+
return separator === void 0 ? `${name}${method[0].toUpperCase()}${method.slice(1)}` : `${name}${separator}${method}`;
|
|
852
893
|
}
|
|
853
894
|
/** The body shape a custom action accepts: a JSON:API document, none, or a raw payload. */
|
|
854
895
|
function actionInput(op) {
|
|
@@ -1044,7 +1085,7 @@ function buildExamples(doc, descriptor) {
|
|
|
1044
1085
|
/** Below this the document predates the structures this codegen reads. */
|
|
1045
1086
|
const MINIMUM_CONTRACT = 1;
|
|
1046
1087
|
/** Above this the document carries structures this codegen does not read. */
|
|
1047
|
-
const MAXIMUM_CONTRACT =
|
|
1088
|
+
const MAXIMUM_CONTRACT = 15;
|
|
1048
1089
|
/**
|
|
1049
1090
|
* Check the document's `info.x-generator.contract` against the range this codegen reads. A
|
|
1050
1091
|
* document older than the range throws; a newer one returns a warning, because its new
|
|
@@ -1054,8 +1095,38 @@ const MAXIMUM_CONTRACT = 13;
|
|
|
1054
1095
|
function checkContract(doc) {
|
|
1055
1096
|
const contract = doc.info?.["x-generator"]?.contract;
|
|
1056
1097
|
if (typeof contract !== "number") return;
|
|
1057
|
-
if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to
|
|
1058
|
-
if (contract >
|
|
1098
|
+
if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to 15). Use an older codegen release, or regenerate the document from a newer server.`);
|
|
1099
|
+
if (contract > 15) return `the document declares contract ${contract} and this codegen reads up to 15. It describes capabilities that were not generated; upgrade the codegen to pick them up.`;
|
|
1100
|
+
}
|
|
1101
|
+
//#endregion
|
|
1102
|
+
//#region src/declared-links.ts
|
|
1103
|
+
const LINK_OBJECT_REF = "#/components/schemas/LinkObject";
|
|
1104
|
+
function isSchema$2(value) {
|
|
1105
|
+
return typeof value === "object" && value !== null;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* The declared links on a `<Type>ResourceLinks` component, in document order. `self` is never
|
|
1109
|
+
* declared, and a link the server builds itself, such as an action's link, points at
|
|
1110
|
+
* `RenderedLink`, so neither is read. A server that renders its links as strings points those at
|
|
1111
|
+
* `LinkObject` instead, so they read as bare links, which is the shape they render in. Expects
|
|
1112
|
+
* the document's single-entry `allOf` wrappers already read as their `$ref`.
|
|
1113
|
+
*/
|
|
1114
|
+
function readDeclaredLinks(component) {
|
|
1115
|
+
const out = [];
|
|
1116
|
+
for (const [name, schema] of Object.entries(component?.properties ?? {})) {
|
|
1117
|
+
if (name === "self" || !isSchema$2(schema)) continue;
|
|
1118
|
+
if (schema.$ref === LINK_OBJECT_REF) out.push({
|
|
1119
|
+
name,
|
|
1120
|
+
kind: "bare",
|
|
1121
|
+
schema
|
|
1122
|
+
});
|
|
1123
|
+
else if (schema.$ref === void 0 && isSchema$2(schema.properties?.["href"])) out.push({
|
|
1124
|
+
name,
|
|
1125
|
+
kind: "object",
|
|
1126
|
+
schema
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
return out;
|
|
1059
1130
|
}
|
|
1060
1131
|
//#endregion
|
|
1061
1132
|
//#region src/source.ts
|
|
@@ -1397,7 +1468,9 @@ const MODULE_NAMES = [
|
|
|
1397
1468
|
"ErrorCodeByStatus",
|
|
1398
1469
|
"ErrorCodeGuard",
|
|
1399
1470
|
"ErrorCodes",
|
|
1471
|
+
"Links",
|
|
1400
1472
|
"Record",
|
|
1473
|
+
"ResourceLinks",
|
|
1401
1474
|
"ResourceMap",
|
|
1402
1475
|
"WriteAttributes"
|
|
1403
1476
|
];
|
|
@@ -1489,6 +1562,19 @@ function actionDoc(name, op) {
|
|
|
1489
1562
|
if (description && !generated.test(description)) parts.push(description);
|
|
1490
1563
|
return parts.length === 0 ? void 0 : parts.join("\n\n");
|
|
1491
1564
|
}
|
|
1565
|
+
/**
|
|
1566
|
+
* What an action declares about its effects (generator contract 15), as one sentence, or
|
|
1567
|
+
* `undefined` when it declares nothing.
|
|
1568
|
+
*/
|
|
1569
|
+
function safetyDoc(action) {
|
|
1570
|
+
const words = [
|
|
1571
|
+
[action.readOnly, "read-only"],
|
|
1572
|
+
[action.destructive, "destructive"],
|
|
1573
|
+
[action.idempotent, "idempotent"]
|
|
1574
|
+
].filter(([value]) => value !== void 0).map(([value, word]) => value === true ? word : `not ${word}`);
|
|
1575
|
+
if (words.length === 0) return;
|
|
1576
|
+
return `The server declares this action ${words.length === 1 ? words[0] : `${words.slice(0, -1).join(", ")} and ${words.at(-1)}`}.`;
|
|
1577
|
+
}
|
|
1492
1578
|
/** Whether a media type carries JSON: JSON:API, `application/json` or a `+json` suffix. */
|
|
1493
1579
|
function isJsonMedia(mediaType) {
|
|
1494
1580
|
const essence = mediaType.split(";")[0].trim().toLowerCase();
|
|
@@ -1564,6 +1650,8 @@ var Emitter = class {
|
|
|
1564
1650
|
rawBodyAliases = /* @__PURE__ */ new Map();
|
|
1565
1651
|
/** JSDoc for entries of the `resourceMap` literal, keyed by their path joined with `/`. */
|
|
1566
1652
|
literalDocs = /* @__PURE__ */ new Map();
|
|
1653
|
+
/** Wire type -> the links it declares, for the types that declare any. */
|
|
1654
|
+
declaredLinks = /* @__PURE__ */ new Map();
|
|
1567
1655
|
/** Provenance of the source document, stamped into the generated header for drift tracking. */
|
|
1568
1656
|
provenance;
|
|
1569
1657
|
doc;
|
|
@@ -1575,12 +1663,18 @@ var Emitter = class {
|
|
|
1575
1663
|
this.baseByType = this.indexBases();
|
|
1576
1664
|
this.provenance = deriveProvenance(source);
|
|
1577
1665
|
this.takenNames = new Set(MODULE_NAMES);
|
|
1666
|
+
for (const type of Object.keys(descriptor)) {
|
|
1667
|
+
const base = this.baseByType.get(type);
|
|
1668
|
+
const links = base === void 0 ? [] : readDeclaredLinks(this.schemas[`${base}ResourceLinks`]);
|
|
1669
|
+
if (links.length > 0) this.declaredLinks.set(type, links);
|
|
1670
|
+
}
|
|
1578
1671
|
for (const type of Object.keys(descriptor)) {
|
|
1579
1672
|
const stem = typeStem(type);
|
|
1580
1673
|
for (const suffix of [
|
|
1581
1674
|
"Attributes",
|
|
1582
1675
|
"CreateAttributes",
|
|
1583
|
-
"UpdateAttributes"
|
|
1676
|
+
"UpdateAttributes",
|
|
1677
|
+
"Links"
|
|
1584
1678
|
]) this.takenNames.add(`${stem}${suffix}`);
|
|
1585
1679
|
for (const [name, action] of Object.entries(descriptor[type]?.actions ?? {})) {
|
|
1586
1680
|
this.takenNames.add(`${stem}${pascalCase(name)}Input`);
|
|
@@ -1590,7 +1684,11 @@ var Emitter = class {
|
|
|
1590
1684
|
const single = action.contentType;
|
|
1591
1685
|
if (single !== void 0 && (single.startsWith("multipart/") || single === "application/x-www-form-urlencoded")) this.takenNames.add(`${stem}${pascalCase(name)}${pascalCase(single)}Input`);
|
|
1592
1686
|
const op = actionOperation(this.doc, action);
|
|
1593
|
-
const parts = [
|
|
1687
|
+
const parts = [
|
|
1688
|
+
actionDoc(action.path.slice(action.path.lastIndexOf("/") + 1), op),
|
|
1689
|
+
safetyDoc(action),
|
|
1690
|
+
exampleDoc(op)
|
|
1691
|
+
].filter((part) => part !== void 0);
|
|
1594
1692
|
const doc = parts.length === 0 ? void 0 : parts.join("\n\n");
|
|
1595
1693
|
if (doc !== void 0) this.literalDocs.set(`${type}/actions/${name}`, doc);
|
|
1596
1694
|
}
|
|
@@ -1629,6 +1727,7 @@ var Emitter = class {
|
|
|
1629
1727
|
emit() {
|
|
1630
1728
|
const interfaces = Object.keys(this.descriptor).map((type) => this.attributeInterface(type));
|
|
1631
1729
|
const writeInterfaces = Object.keys(this.descriptor).flatMap((type) => this.writeAttributeInterfaces(type));
|
|
1730
|
+
const linkInterfaces = [...this.declaredLinks].map(([type, links]) => this.linksInterface(type, links));
|
|
1632
1731
|
const actionAliases = this.actionTypeAliases();
|
|
1633
1732
|
const enums = this.enumAliases();
|
|
1634
1733
|
return `${[
|
|
@@ -1637,9 +1736,11 @@ var Emitter = class {
|
|
|
1637
1736
|
...enums ? [enums] : [],
|
|
1638
1737
|
...interfaces,
|
|
1639
1738
|
...writeInterfaces,
|
|
1739
|
+
...linkInterfaces,
|
|
1640
1740
|
...actionAliases,
|
|
1641
1741
|
this.attributesMap(),
|
|
1642
1742
|
this.writeAttributesMap(),
|
|
1743
|
+
this.linksMap(),
|
|
1643
1744
|
this.actionTypesMap(),
|
|
1644
1745
|
this.resourceMap(),
|
|
1645
1746
|
"export type ResourceMap = typeof resourceMap",
|
|
@@ -1667,9 +1768,51 @@ var Emitter = class {
|
|
|
1667
1768
|
" createClient as createClientRuntime,",
|
|
1668
1769
|
" type ErrorCodeGuard,",
|
|
1669
1770
|
" hasErrorCode as hasErrorCodeRuntime,",
|
|
1771
|
+
...this.declaredLinks.size > 0 ? [" type ResourceLinks,"] : [],
|
|
1670
1772
|
"} from '@haddowg/json-api-client'"
|
|
1671
1773
|
].join("\n");
|
|
1672
1774
|
}
|
|
1775
|
+
/**
|
|
1776
|
+
* The links interface of a type that declares links, e.g. `ImagesLinks`: each declared link an
|
|
1777
|
+
* optional member, since the server leaves a link out when its href resolves to `null`. It
|
|
1778
|
+
* extends the loose `ResourceLinks`, so `self` and any link the type doesn't declare stay
|
|
1779
|
+
* reachable as before.
|
|
1780
|
+
*/
|
|
1781
|
+
linksInterface(type, links) {
|
|
1782
|
+
const lines = [];
|
|
1783
|
+
for (const link of links) {
|
|
1784
|
+
const doc = jsDoc(memberDoc(link.schema), " ");
|
|
1785
|
+
if (doc !== void 0) lines.push(doc);
|
|
1786
|
+
const value = link.kind === "bare" ? "string" : this.linkObjectType(link.schema, " ");
|
|
1787
|
+
lines.push(` ${propertyKey(link.name)}?: ${value}`);
|
|
1788
|
+
}
|
|
1789
|
+
return `export interface ${typeStem(type)}Links extends ResourceLinks {\n${lines.join("\n")}\n}`;
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* A declared link object as a multi-line type: `href`, each static member as its `const`, and a
|
|
1793
|
+
* `meta` object whose members are typed as attributes are, each with its own TSDoc.
|
|
1794
|
+
*/
|
|
1795
|
+
linkObjectType(schema, indent) {
|
|
1796
|
+
return this.documentedObject(schema, indent, (name, member) => name === "meta" ? this.documentedObject(member, `${indent} `, (_, field) => this.tsType(field)) : this.constOrType(member));
|
|
1797
|
+
}
|
|
1798
|
+
/** An object type with one line, and its TSDoc, per property. */
|
|
1799
|
+
documentedObject(schema, indent, type) {
|
|
1800
|
+
const required = new Set(schema.required ?? []);
|
|
1801
|
+
const inner = `${indent} `;
|
|
1802
|
+
const lines = [];
|
|
1803
|
+
for (const [name, member] of Object.entries(schema.properties ?? {})) {
|
|
1804
|
+
if (!isSchema(member)) continue;
|
|
1805
|
+
const doc = jsDoc(memberDoc(member), inner);
|
|
1806
|
+
if (doc !== void 0) lines.push(doc);
|
|
1807
|
+
lines.push(`${inner}${propertyKey(name)}${required.has(name) ? "" : "?"}: ${type(name, member)}`);
|
|
1808
|
+
}
|
|
1809
|
+
return lines.length === 0 ? "Record<string, unknown>" : `{\n${lines.join("\n")}\n${indent}}`;
|
|
1810
|
+
}
|
|
1811
|
+
/** A static link member's `const` as a literal type, else its schema's type. */
|
|
1812
|
+
constOrType(schema) {
|
|
1813
|
+
const value = schema["const"];
|
|
1814
|
+
return typeof value === "string" ? stringLiteral(value) : this.tsType(schema);
|
|
1815
|
+
}
|
|
1673
1816
|
/** Per-type attribute interface, e.g. `export interface AlbumsAttributes { ... }`. */
|
|
1674
1817
|
attributeInterface(type) {
|
|
1675
1818
|
const base = this.baseByType.get(type);
|
|
@@ -2067,6 +2210,17 @@ var Emitter = class {
|
|
|
2067
2210
|
return `export interface Attributes {\n${Object.keys(this.descriptor).map((type) => ` ${propertyKey(type)}: ${typeStem(type)}Attributes`).join("\n")}\n}`;
|
|
2068
2211
|
}
|
|
2069
2212
|
/**
|
|
2213
|
+
* Emit the `Links` interface mapping each type that declares links to its links interface. The
|
|
2214
|
+
* runtime threads it beside the `Attributes` map, so a read result's `$links` carries the
|
|
2215
|
+
* declared links. A type absent from it keeps the loose `ResourceLinks`. Passed as the sixth
|
|
2216
|
+
* type argument to the runtime factory.
|
|
2217
|
+
*/
|
|
2218
|
+
linksMap() {
|
|
2219
|
+
const lines = [...this.declaredLinks.keys()].map((type) => ` ${propertyKey(type)}: ${typeStem(type)}Links`);
|
|
2220
|
+
if (lines.length === 0) return "export interface Links {}";
|
|
2221
|
+
return `export interface Links {\n${lines.join("\n")}\n}`;
|
|
2222
|
+
}
|
|
2223
|
+
/**
|
|
2070
2224
|
* Emit the `WriteAttributes` interface mapping each writable wire type to its
|
|
2071
2225
|
* `{ create; update }` attribute pair. A read-only type (no create/update component)
|
|
2072
2226
|
* contributes no entry, so the bound client only exposes writes where the API allows
|
|
@@ -2121,7 +2275,7 @@ var Emitter = class {
|
|
|
2121
2275
|
" * so bindings that only see the client (the Angular refs) type its errors.",
|
|
2122
2276
|
" */",
|
|
2123
2277
|
"export const createClient = (options: ClientOptions) =>",
|
|
2124
|
-
" createClientRuntime<typeof resourceMap, Attributes, WriteAttributes, ActionTypes, ErrorCode>(",
|
|
2278
|
+
" createClientRuntime<typeof resourceMap, Attributes, WriteAttributes, ActionTypes, ErrorCode, Links>(",
|
|
2125
2279
|
" resourceMap,",
|
|
2126
2280
|
" {",
|
|
2127
2281
|
" atomic,",
|
|
@@ -2364,4 +2518,4 @@ async function check(config) {
|
|
|
2364
2518
|
//#endregion
|
|
2365
2519
|
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 };
|
|
2366
2520
|
|
|
2367
|
-
//# sourceMappingURL=src-
|
|
2521
|
+
//# sourceMappingURL=src-C3noZURK.mjs.map
|