@haddowg/json-api-codegen 1.9.0 → 1.11.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-k_oATv7Q.d.mts → index-BLBSnK8y.d.mts} +39 -4
- package/dist/index-BLBSnK8y.d.mts.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{src-py1RBWZr.mjs → src-D29xU79S.mjs} +227 -57
- package/dist/src-D29xU79S.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/index-k_oATv7Q.d.mts.map +0 -1
- package/dist/src-py1RBWZr.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-BLBSnK8y.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-D29xU79S.mjs";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
//#region src/cli.ts
|
|
@@ -56,6 +56,20 @@ 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
|
+
};
|
|
65
|
+
/** The credentials the operation takes: scheme name to the scopes it needs, per alternative. */
|
|
66
|
+
security?: readonly SecurityRequirement[];
|
|
67
|
+
}
|
|
68
|
+
/** One security alternative: each scheme it names, with the scopes it needs under that scheme. */
|
|
69
|
+
type SecurityRequirement = Readonly<Record<string, readonly string[]>>;
|
|
70
|
+
/** A security scheme; the codegen reads only its `type`, to find the `oauth2` ones. */
|
|
71
|
+
interface SecuritySchemeObject {
|
|
72
|
+
type?: string;
|
|
59
73
|
}
|
|
60
74
|
type HttpMethod = 'get' | 'post' | 'patch' | 'put' | 'delete';
|
|
61
75
|
type PathItemObject = Partial<Record<HttpMethod, OperationObject>>;
|
|
@@ -74,8 +88,11 @@ interface OpenApiDocument {
|
|
|
74
88
|
url?: string;
|
|
75
89
|
}[];
|
|
76
90
|
paths?: Record<string, PathItemObject>;
|
|
91
|
+
/** The default security of every operation that declares none of its own. */
|
|
92
|
+
security?: readonly SecurityRequirement[];
|
|
77
93
|
components?: {
|
|
78
94
|
schemas?: Record<string, SchemaObject>;
|
|
95
|
+
securitySchemes?: Record<string, SecuritySchemeObject>;
|
|
79
96
|
};
|
|
80
97
|
}
|
|
81
98
|
//#endregion
|
|
@@ -85,7 +102,22 @@ declare class DescriptorBuilder {
|
|
|
85
102
|
private readonly paths;
|
|
86
103
|
/** Lazily-built wire-type -> collection-level paginator kind (for per-relation divergence, D6a). */
|
|
87
104
|
private typePaginators?;
|
|
105
|
+
/** The names of the document's `oauth2` security schemes, under which operations list scopes. */
|
|
106
|
+
private readonly scopeSchemes;
|
|
107
|
+
/** The document's default security, which an operation without its own inherits. */
|
|
108
|
+
private readonly defaultSecurity;
|
|
88
109
|
constructor(source: OpenApiDocument);
|
|
110
|
+
/**
|
|
111
|
+
* The OAuth scopes an operation needs (generator contract 16): those its `security` lists under
|
|
112
|
+
* an `oauth2` scheme, in order and without repeats. `undefined` when it lists none.
|
|
113
|
+
*/
|
|
114
|
+
private scopes;
|
|
115
|
+
/**
|
|
116
|
+
* The scopes each of a type's CRUD, related and relationship operations needs, keyed by the
|
|
117
|
+
* operationId without the type (`fetchOne`, `fetchRelated.author`). Operations needing none are
|
|
118
|
+
* left out; actions carry their own.
|
|
119
|
+
*/
|
|
120
|
+
private operationScopes;
|
|
89
121
|
/**
|
|
90
122
|
* The collection-level paginator kind for a WIRE type (`tracks`), memoised. Resolves each
|
|
91
123
|
* resource schema's wire type (its `type.const`) to the paginator of its collection GET — the
|
|
@@ -152,9 +184,12 @@ declare class DescriptorBuilder {
|
|
|
152
184
|
* `{collection}/{id}/-actions/{name}` paths. The action name is the last segment; scope
|
|
153
185
|
* is `resource` when the path carries `/{id}/`, else `collection`. `input` is `document`
|
|
154
186
|
* (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`).
|
|
187
|
+
* `output` is `document` (a 2xx returns a JSON:API document) or `none` (only `204`). An action
|
|
188
|
+
* answering to several methods gives one entry per method (see {@link pickOperations}).
|
|
156
189
|
*/
|
|
157
190
|
private actions;
|
|
191
|
+
/** The descriptor for one operation of a custom action. */
|
|
192
|
+
private action;
|
|
158
193
|
/**
|
|
159
194
|
* The non-JSON:API media types an action answers with under its 2xx responses, each once, in
|
|
160
195
|
* the document's order, with how the client reads its body.
|
|
@@ -295,7 +330,7 @@ declare function buildExamples(doc: OpenApiDocument, descriptor: ApiDescriptor):
|
|
|
295
330
|
/** Below this the document predates the structures this codegen reads. */
|
|
296
331
|
declare const MINIMUM_CONTRACT = 1;
|
|
297
332
|
/** Above this the document carries structures this codegen does not read. */
|
|
298
|
-
declare const MAXIMUM_CONTRACT =
|
|
333
|
+
declare const MAXIMUM_CONTRACT = 16;
|
|
299
334
|
/**
|
|
300
335
|
* Check the document's `info.x-generator.contract` against the range this codegen reads. A
|
|
301
336
|
* document older than the range throws; a newer one returns a warning, because its new
|
|
@@ -682,5 +717,5 @@ declare function generateFiles(config: CodegenConfig): Promise<string[]>;
|
|
|
682
717
|
*/
|
|
683
718
|
declare function check(config: CodegenConfig): Promise<CheckResult>;
|
|
684
719
|
//#endregion
|
|
685
|
-
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 };
|
|
686
|
-
//# sourceMappingURL=index-
|
|
720
|
+
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-BLBSnK8y.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-BLBSnK8y.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-
|
|
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 };
|
|
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-BLBSnK8y.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 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-D29xU79S.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 };
|
|
@@ -95,10 +95,49 @@ var DescriptorBuilder = class {
|
|
|
95
95
|
paths;
|
|
96
96
|
/** Lazily-built wire-type -> collection-level paginator kind (for per-relation divergence, D6a). */
|
|
97
97
|
typePaginators;
|
|
98
|
+
/** The names of the document's `oauth2` security schemes, under which operations list scopes. */
|
|
99
|
+
scopeSchemes;
|
|
100
|
+
/** The document's default security, which an operation without its own inherits. */
|
|
101
|
+
defaultSecurity;
|
|
98
102
|
constructor(source) {
|
|
99
103
|
const doc = unwrapRefs(source);
|
|
100
104
|
this.schemas = doc.components?.schemas ?? {};
|
|
101
105
|
this.paths = doc.paths ?? {};
|
|
106
|
+
this.scopeSchemes = new Set(Object.entries(doc.components?.securitySchemes ?? {}).filter(([, scheme]) => scheme?.type === "oauth2").map(([name]) => name));
|
|
107
|
+
this.defaultSecurity = doc.security ?? [];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The OAuth scopes an operation needs (generator contract 16): those its `security` lists under
|
|
111
|
+
* an `oauth2` scheme, in order and without repeats. `undefined` when it lists none.
|
|
112
|
+
*/
|
|
113
|
+
scopes(op) {
|
|
114
|
+
if (op === void 0 || this.scopeSchemes.size === 0) return;
|
|
115
|
+
const out = /* @__PURE__ */ new Set();
|
|
116
|
+
for (const requirement of op.security ?? this.defaultSecurity) for (const [scheme, scopes] of Object.entries(requirement)) if (this.scopeSchemes.has(scheme) && Array.isArray(scopes)) for (const scope of scopes) out.add(scope);
|
|
117
|
+
return out.size === 0 ? void 0 : [...out];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The scopes each of a type's CRUD, related and relationship operations needs, keyed by the
|
|
121
|
+
* operationId without the type (`fetchOne`, `fetchRelated.author`). Operations needing none are
|
|
122
|
+
* left out; actions carry their own.
|
|
123
|
+
*/
|
|
124
|
+
operationScopes(wireType) {
|
|
125
|
+
const out = {};
|
|
126
|
+
for (const item of Object.values(this.paths)) for (const op of Object.values(item)) {
|
|
127
|
+
const id = op?.operationId;
|
|
128
|
+
if (typeof id !== "string") continue;
|
|
129
|
+
const dot = id.indexOf(".");
|
|
130
|
+
const prefix = id.slice(0, dot);
|
|
131
|
+
const rest = id.slice(dot + 1);
|
|
132
|
+
if (dot < 0 || prefix === "action" || prefix === "atomic") continue;
|
|
133
|
+
let key;
|
|
134
|
+
if (rest === wireType) key = prefix;
|
|
135
|
+
else if (rest.startsWith(`${wireType}.`)) key = `${prefix}.${rest.slice(wireType.length + 1)}`;
|
|
136
|
+
else continue;
|
|
137
|
+
const scopes = this.scopes(op);
|
|
138
|
+
if (scopes !== void 0) out[key] = scopes;
|
|
139
|
+
}
|
|
140
|
+
return sortRecord(out);
|
|
102
141
|
}
|
|
103
142
|
/**
|
|
104
143
|
* The collection-level paginator kind for a WIRE type (`tracks`), memoised. Resolves each
|
|
@@ -139,6 +178,8 @@ var DescriptorBuilder = class {
|
|
|
139
178
|
const filterable = this.filterable(collection);
|
|
140
179
|
if (filterable !== void 0) descriptor.filterable = filterable;
|
|
141
180
|
if (Object.keys(actions).length > 0) descriptor.actions = actions;
|
|
181
|
+
const scopes = this.operationScopes(wireType);
|
|
182
|
+
if (Object.keys(scopes).length > 0) descriptor.scopes = scopes;
|
|
142
183
|
if (Object.keys(schema.properties ?? {}).every((prop) => prop === "type") && Object.keys(descriptor.paths).length === 0) continue;
|
|
143
184
|
out[wireType] = descriptor;
|
|
144
185
|
}
|
|
@@ -152,7 +193,13 @@ var DescriptorBuilder = class {
|
|
|
152
193
|
buildAtomic() {
|
|
153
194
|
for (const [path, item] of Object.entries(this.paths)) {
|
|
154
195
|
const content = item.post?.requestBody?.content ?? {};
|
|
155
|
-
if (Object.keys(content).some(isAtomicMediaType))
|
|
196
|
+
if (Object.keys(content).some(isAtomicMediaType)) {
|
|
197
|
+
const scopes = this.scopes(item.post);
|
|
198
|
+
return scopes === void 0 ? { path } : {
|
|
199
|
+
path,
|
|
200
|
+
scopes
|
|
201
|
+
};
|
|
202
|
+
}
|
|
156
203
|
}
|
|
157
204
|
return null;
|
|
158
205
|
}
|
|
@@ -327,7 +374,8 @@ var DescriptorBuilder = class {
|
|
|
327
374
|
* `{collection}/{id}/-actions/{name}` paths. The action name is the last segment; scope
|
|
328
375
|
* is `resource` when the path carries `/{id}/`, else `collection`. `input` is `document`
|
|
329
376
|
* (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`).
|
|
377
|
+
* `output` is `document` (a 2xx returns a JSON:API document) or `none` (only `204`). An action
|
|
378
|
+
* answering to several methods gives one entry per method (see {@link pickOperations}).
|
|
331
379
|
*/
|
|
332
380
|
actions(collection) {
|
|
333
381
|
const out = {};
|
|
@@ -344,47 +392,68 @@ var DescriptorBuilder = class {
|
|
|
344
392
|
name = path.slice(collectionPrefix.length);
|
|
345
393
|
} else continue;
|
|
346
394
|
if (name.length === 0 || name.includes("/")) continue;
|
|
347
|
-
const picked =
|
|
348
|
-
if (picked ===
|
|
395
|
+
const picked = pickOperations(item);
|
|
396
|
+
if (picked.length === 0) {
|
|
349
397
|
console.warn(`json-api codegen: custom action path "${path}" advertises no usable operation; skipping.`);
|
|
350
398
|
continue;
|
|
351
399
|
}
|
|
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;
|
|
400
|
+
for (const [index, { method, op }] of picked.entries()) {
|
|
401
|
+
const key = index === 0 ? name : methodActionName(name, method);
|
|
402
|
+
if (key in out) {
|
|
403
|
+
console.warn(`json-api codegen: the ${method.toUpperCase()} operation of custom action path "${path}" would be named "${key}", which another action already uses; skipping.`);
|
|
404
|
+
continue;
|
|
371
405
|
}
|
|
406
|
+
out[key] = this.action(scope, path, method, op);
|
|
372
407
|
}
|
|
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
408
|
}
|
|
386
409
|
return sortRecord(out);
|
|
387
410
|
}
|
|
411
|
+
/** The descriptor for one operation of a custom action. */
|
|
412
|
+
action(scope, path, method, op) {
|
|
413
|
+
const input = actionInput(op);
|
|
414
|
+
const output = actionOutput(op);
|
|
415
|
+
const action = {
|
|
416
|
+
scope,
|
|
417
|
+
path,
|
|
418
|
+
input,
|
|
419
|
+
output
|
|
420
|
+
};
|
|
421
|
+
if (method !== "post") action.method = method.toUpperCase();
|
|
422
|
+
if (input === "document") {
|
|
423
|
+
const inputType = this.documentPrimary(actionRequestRef(op))?.type;
|
|
424
|
+
if (inputType !== void 0) action.inputType = inputType;
|
|
425
|
+
}
|
|
426
|
+
if (output === "document") {
|
|
427
|
+
const primary = this.documentPrimary(this.firstOkJsonApiRef(op));
|
|
428
|
+
if (primary !== void 0) {
|
|
429
|
+
action.outputType = primary.type;
|
|
430
|
+
action.outputCardinality = primary.cardinality;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (input === "raw") {
|
|
434
|
+
const contentType = rawContentType(op);
|
|
435
|
+
if (contentType !== void 0) action.contentType = contentType;
|
|
436
|
+
}
|
|
437
|
+
const rawOutput = this.rawOutput(op);
|
|
438
|
+
if (rawOutput.length > 0) action.rawOutput = rawOutput;
|
|
439
|
+
const requestContent = Object.entries(op.requestBody?.content ?? {});
|
|
440
|
+
if (requestContent.length > 1) action.inputMedia = requestContent.map(([mediaType, media]) => ({
|
|
441
|
+
mediaType,
|
|
442
|
+
kind: this.inputMediaKind(mediaType, media.schema)
|
|
443
|
+
}));
|
|
444
|
+
const safety = op["x-safety"];
|
|
445
|
+
for (const flag of [
|
|
446
|
+
"readOnly",
|
|
447
|
+
"destructive",
|
|
448
|
+
"idempotent"
|
|
449
|
+
]) {
|
|
450
|
+
const value = safety?.[flag];
|
|
451
|
+
if (typeof value === "boolean") action[flag] = value;
|
|
452
|
+
}
|
|
453
|
+
const scopes = this.scopes(op);
|
|
454
|
+
if (scopes !== void 0) action.scopes = scopes;
|
|
455
|
+
return action;
|
|
456
|
+
}
|
|
388
457
|
/**
|
|
389
458
|
* The non-JSON:API media types an action answers with under its 2xx responses, each once, in
|
|
390
459
|
* the document's order, with how the client reads its body.
|
|
@@ -495,16 +564,21 @@ var DescriptorBuilder = class {
|
|
|
495
564
|
if (readPivot !== void 0) relation.readPivot = readPivot;
|
|
496
565
|
return relation;
|
|
497
566
|
}
|
|
498
|
-
if (Array.isArray(items.anyOf))
|
|
499
|
-
|
|
500
|
-
types:
|
|
501
|
-
|
|
502
|
-
|
|
567
|
+
if (Array.isArray(items.anyOf)) {
|
|
568
|
+
const types = this.constsFromRefs(items.anyOf);
|
|
569
|
+
return types.length === 0 ? void 0 : {
|
|
570
|
+
cardinality: "many",
|
|
571
|
+
types,
|
|
572
|
+
polymorphic: true,
|
|
573
|
+
pivot: false
|
|
574
|
+
};
|
|
575
|
+
}
|
|
503
576
|
return;
|
|
504
577
|
}
|
|
505
578
|
if (Array.isArray(data.anyOf)) {
|
|
506
579
|
const types = [];
|
|
507
580
|
let pivot = false;
|
|
581
|
+
let polymorphic = false;
|
|
508
582
|
let readPivot;
|
|
509
583
|
for (const entry of data.anyOf) if (typeof entry.$ref === "string") {
|
|
510
584
|
const type = this.resolveConst(entry.$ref);
|
|
@@ -516,9 +590,17 @@ var DescriptorBuilder = class {
|
|
|
516
590
|
pivot = true;
|
|
517
591
|
readPivot = this.pivotDescriptor(pivotNodeFromAllOf(entry.allOf), "read");
|
|
518
592
|
}
|
|
519
|
-
} else if (Array.isArray(entry.anyOf))
|
|
593
|
+
} else if (Array.isArray(entry.anyOf)) {
|
|
594
|
+
types.push(...this.constsFromRefs(entry.anyOf));
|
|
595
|
+
polymorphic = true;
|
|
596
|
+
}
|
|
520
597
|
if (types.length === 0) return;
|
|
521
|
-
const relation = {
|
|
598
|
+
const relation = polymorphic ? {
|
|
599
|
+
cardinality: "one",
|
|
600
|
+
types,
|
|
601
|
+
polymorphic: true,
|
|
602
|
+
pivot
|
|
603
|
+
} : {
|
|
522
604
|
cardinality: "one",
|
|
523
605
|
types,
|
|
524
606
|
pivot
|
|
@@ -837,18 +919,39 @@ const ACTION_METHODS = [
|
|
|
837
919
|
"delete"
|
|
838
920
|
];
|
|
839
921
|
/**
|
|
840
|
-
* The
|
|
841
|
-
* the first advertised method.
|
|
922
|
+
* The operations an action is invoked over, the preferred one first: POST when advertised (the
|
|
923
|
+
* common default), else the first advertised method. The other methods follow only when their
|
|
924
|
+
* `operationId` differs from the preferred one's, as contract 15 makes it for an action answering
|
|
925
|
+
* to several methods. An older document gave every method one id, and its client exposed only the
|
|
926
|
+
* preferred one. Empty when the path item carries no operation at all.
|
|
842
927
|
*/
|
|
843
|
-
function
|
|
928
|
+
function pickOperations(item) {
|
|
844
929
|
const available = ACTION_METHODS.filter((method) => item[method] !== void 0);
|
|
845
|
-
const
|
|
846
|
-
if (
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
method,
|
|
850
|
-
op
|
|
851
|
-
};
|
|
930
|
+
const preferred = available.includes("post") ? "post" : available[0];
|
|
931
|
+
if (preferred === void 0) return [];
|
|
932
|
+
const first = item[preferred];
|
|
933
|
+
const out = [{
|
|
934
|
+
method: preferred,
|
|
935
|
+
op: first
|
|
936
|
+
}];
|
|
937
|
+
for (const method of available) {
|
|
938
|
+
const op = item[method];
|
|
939
|
+
if (method !== preferred && op.operationId !== first.operationId) out.push({
|
|
940
|
+
method,
|
|
941
|
+
op
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
return out;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* The name a multi-method action's other methods go under: the action's name and the method,
|
|
948
|
+
* joined the way the name joins its words. So `export` gives `exportGet` and `bulk-import` gives
|
|
949
|
+
* `bulk-import-get`. Core refuses action ids that differ only in case or punctuation, so the
|
|
950
|
+
* name can't clash with another action's.
|
|
951
|
+
*/
|
|
952
|
+
function methodActionName(name, method) {
|
|
953
|
+
const separator = /[-_.]/.exec(name)?.[0];
|
|
954
|
+
return separator === void 0 ? `${name}${method[0].toUpperCase()}${method.slice(1)}` : `${name}${separator}${method}`;
|
|
852
955
|
}
|
|
853
956
|
/** The body shape a custom action accepts: a JSON:API document, none, or a raw payload. */
|
|
854
957
|
function actionInput(op) {
|
|
@@ -1044,7 +1147,7 @@ function buildExamples(doc, descriptor) {
|
|
|
1044
1147
|
/** Below this the document predates the structures this codegen reads. */
|
|
1045
1148
|
const MINIMUM_CONTRACT = 1;
|
|
1046
1149
|
/** Above this the document carries structures this codegen does not read. */
|
|
1047
|
-
const MAXIMUM_CONTRACT =
|
|
1150
|
+
const MAXIMUM_CONTRACT = 16;
|
|
1048
1151
|
/**
|
|
1049
1152
|
* Check the document's `info.x-generator.contract` against the range this codegen reads. A
|
|
1050
1153
|
* document older than the range throws; a newer one returns a warning, because its new
|
|
@@ -1054,8 +1157,8 @@ const MAXIMUM_CONTRACT = 14;
|
|
|
1054
1157
|
function checkContract(doc) {
|
|
1055
1158
|
const contract = doc.info?.["x-generator"]?.contract;
|
|
1056
1159
|
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 >
|
|
1160
|
+
if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to 16). Use an older codegen release, or regenerate the document from a newer server.`);
|
|
1161
|
+
if (contract > 16) return `the document declares contract ${contract} and this codegen reads up to 16. It describes capabilities that were not generated; upgrade the codegen to pick them up.`;
|
|
1059
1162
|
}
|
|
1060
1163
|
//#endregion
|
|
1061
1164
|
//#region src/declared-links.ts
|
|
@@ -1521,6 +1624,57 @@ function actionDoc(name, op) {
|
|
|
1521
1624
|
if (description && !generated.test(description)) parts.push(description);
|
|
1522
1625
|
return parts.length === 0 ? void 0 : parts.join("\n\n");
|
|
1523
1626
|
}
|
|
1627
|
+
/**
|
|
1628
|
+
* What an action declares about its effects (generator contract 15), as one sentence, or
|
|
1629
|
+
* `undefined` when it declares nothing.
|
|
1630
|
+
*/
|
|
1631
|
+
function safetyDoc(action) {
|
|
1632
|
+
const words = [
|
|
1633
|
+
[action.readOnly, "read-only"],
|
|
1634
|
+
[action.destructive, "destructive"],
|
|
1635
|
+
[action.idempotent, "idempotent"]
|
|
1636
|
+
].filter(([value]) => value !== void 0).map(([value, word]) => value === true ? word : `not ${word}`);
|
|
1637
|
+
if (words.length === 0) return;
|
|
1638
|
+
return `The server declares this action ${words.length === 1 ? words[0] : `${words.slice(0, -1).join(", ")} and ${words.at(-1)}`}.`;
|
|
1639
|
+
}
|
|
1640
|
+
/** A list of scopes as inline code joined into prose: `a`, `b` and `c`. */
|
|
1641
|
+
function scopeList(scopes) {
|
|
1642
|
+
const quoted = scopes.map((scope) => `\`${scope}\``);
|
|
1643
|
+
return quoted.length === 1 ? quoted[0] : `${quoted.slice(0, -1).join(", ")} and ${quoted.at(-1)}`;
|
|
1644
|
+
}
|
|
1645
|
+
/** The sentence naming the OAuth scopes an operation needs (generator contract 16). */
|
|
1646
|
+
function scopeDoc(scopes) {
|
|
1647
|
+
if (scopes === void 0 || scopes.length === 0) return;
|
|
1648
|
+
return `Needs the OAuth ${scopes.length === 1 ? "scope" : "scopes"} ${scopeList(scopes)}.`;
|
|
1649
|
+
}
|
|
1650
|
+
/** The descriptor's `paths` keys, by the operation whose scopes they carry. */
|
|
1651
|
+
const PATH_OPERATIONS = {
|
|
1652
|
+
fetchMany: "fetchCollection",
|
|
1653
|
+
create: "create",
|
|
1654
|
+
fetchOne: "fetchOne",
|
|
1655
|
+
update: "update",
|
|
1656
|
+
delete: "delete"
|
|
1657
|
+
};
|
|
1658
|
+
/** A relation's operations, in the order its JSDoc lists their scopes. */
|
|
1659
|
+
const RELATION_OPERATIONS = [
|
|
1660
|
+
"fetchRelated",
|
|
1661
|
+
"fetchRelationship",
|
|
1662
|
+
"updateRelationship",
|
|
1663
|
+
"addRelationship",
|
|
1664
|
+
"removeRelationship"
|
|
1665
|
+
];
|
|
1666
|
+
/** The OAuth scopes each of a relation's endpoints needs, one line per operation, or `undefined`. */
|
|
1667
|
+
function relationScopeDoc(scopes, relation) {
|
|
1668
|
+
const lines = RELATION_OPERATIONS.flatMap((operation) => {
|
|
1669
|
+
const needed = scopes?.[`${operation}.${relation}`];
|
|
1670
|
+
return needed === void 0 || needed.length === 0 ? [] : [`- \`${operation}\` needs ${scopeList(needed)}`];
|
|
1671
|
+
});
|
|
1672
|
+
return lines.length === 0 ? void 0 : [
|
|
1673
|
+
"OAuth scopes:",
|
|
1674
|
+
"",
|
|
1675
|
+
...lines
|
|
1676
|
+
].join("\n");
|
|
1677
|
+
}
|
|
1524
1678
|
/** Whether a media type carries JSON: JSON:API, `application/json` or a `+json` suffix. */
|
|
1525
1679
|
function isJsonMedia(mediaType) {
|
|
1526
1680
|
const essence = mediaType.split(";")[0].trim().toLowerCase();
|
|
@@ -1630,10 +1784,24 @@ var Emitter = class {
|
|
|
1630
1784
|
const single = action.contentType;
|
|
1631
1785
|
if (single !== void 0 && (single.startsWith("multipart/") || single === "application/x-www-form-urlencoded")) this.takenNames.add(`${stem}${pascalCase(name)}${pascalCase(single)}Input`);
|
|
1632
1786
|
const op = actionOperation(this.doc, action);
|
|
1633
|
-
const parts = [
|
|
1787
|
+
const parts = [
|
|
1788
|
+
actionDoc(action.path.slice(action.path.lastIndexOf("/") + 1), op),
|
|
1789
|
+
safetyDoc(action),
|
|
1790
|
+
scopeDoc(action.scopes),
|
|
1791
|
+
exampleDoc(op)
|
|
1792
|
+
].filter((part) => part !== void 0);
|
|
1634
1793
|
const doc = parts.length === 0 ? void 0 : parts.join("\n\n");
|
|
1635
1794
|
if (doc !== void 0) this.literalDocs.set(`${type}/actions/${name}`, doc);
|
|
1636
1795
|
}
|
|
1796
|
+
const scopes = descriptor[type]?.scopes;
|
|
1797
|
+
for (const [key, operation] of Object.entries(PATH_OPERATIONS)) {
|
|
1798
|
+
const doc = scopeDoc(scopes?.[operation]);
|
|
1799
|
+
if (doc !== void 0 && descriptor[type]?.paths[key] !== void 0) this.literalDocs.set(`${type}/paths/${key}`, doc);
|
|
1800
|
+
}
|
|
1801
|
+
for (const relation of Object.keys(descriptor[type]?.relations ?? {})) {
|
|
1802
|
+
const doc = relationScopeDoc(scopes, relation);
|
|
1803
|
+
if (doc !== void 0) this.literalDocs.set(`${type}/relations/${relation}`, doc);
|
|
1804
|
+
}
|
|
1637
1805
|
}
|
|
1638
1806
|
}
|
|
1639
1807
|
/**
|
|
@@ -1977,10 +2145,12 @@ var Emitter = class {
|
|
|
1977
2145
|
}
|
|
1978
2146
|
/** Emit the server-level atomic capability constant (`{ path }` or `null`). */
|
|
1979
2147
|
atomicConst() {
|
|
2148
|
+
const atomicScopes = scopeDoc(this.atomic?.scopes);
|
|
1980
2149
|
return `${[
|
|
1981
2150
|
"/**",
|
|
1982
2151
|
" * The server-level Atomic Operations endpoint (the atomic ext media type), or `null`",
|
|
1983
2152
|
" * when this server exposes none. The runtime `client.atomic` builder posts the batch here.",
|
|
2153
|
+
...atomicScopes === void 0 ? [] : [" *", ` * ${atomicScopes}`],
|
|
1984
2154
|
" */"
|
|
1985
2155
|
].join("\n")}\nexport const atomic = ${this.atomic === null ? "null" : `${this.literal(this.atomic, 0)} as const`}`;
|
|
1986
2156
|
}
|
|
@@ -2460,4 +2630,4 @@ async function check(config) {
|
|
|
2460
2630
|
//#endregion
|
|
2461
2631
|
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 };
|
|
2462
2632
|
|
|
2463
|
-
//# sourceMappingURL=src-
|
|
2633
|
+
//# sourceMappingURL=src-D29xU79S.mjs.map
|