@haddowg/json-api-codegen 1.0.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/LICENSE +21 -0
- package/README.md +170 -0
- package/dist/cli.d.mts +14 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +66 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index-C0becRSc.d.mts +471 -0
- package/dist/index-C0becRSc.d.mts.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/dist/src-B24cYyjM.mjs +1278 -0
- package/dist/src-B24cYyjM.mjs.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
import { ApiDescriptor, AtomicDescriptor } from "@haddowg/json-api-client";
|
|
2
|
+
|
|
3
|
+
//#region src/openapi.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A minimal structural model of the slice of OpenAPI 3.1 the codegen consumes
|
|
6
|
+
* (`components.schemas` + `paths`). Deliberately not a full OpenAPI type — we only
|
|
7
|
+
* model what the descriptor builder reads, and treat schemas as a loose recursive
|
|
8
|
+
* shape since JSON Schema is open-ended.
|
|
9
|
+
*/
|
|
10
|
+
/** A JSON Schema node (or a `$ref` to one), loosely typed for the bits we walk. */
|
|
11
|
+
interface SchemaObject {
|
|
12
|
+
$ref?: string;
|
|
13
|
+
type?: string | readonly string[];
|
|
14
|
+
const?: unknown;
|
|
15
|
+
format?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
enum?: readonly unknown[];
|
|
18
|
+
properties?: Record<string, SchemaOrBool>;
|
|
19
|
+
items?: SchemaObject;
|
|
20
|
+
required?: readonly string[];
|
|
21
|
+
allOf?: readonly SchemaObject[];
|
|
22
|
+
anyOf?: readonly SchemaObject[];
|
|
23
|
+
oneOf?: readonly SchemaObject[];
|
|
24
|
+
'x-enum-varnames'?: readonly string[];
|
|
25
|
+
'x-enum-descriptions'?: readonly string[];
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
/** A property entry: either a schema, or the literal `false` (a forbidden member). */
|
|
29
|
+
type SchemaOrBool = SchemaObject | boolean;
|
|
30
|
+
interface MediaTypeObject {
|
|
31
|
+
schema?: SchemaObject;
|
|
32
|
+
}
|
|
33
|
+
interface RequestBodyObject {
|
|
34
|
+
content?: Record<string, MediaTypeObject>;
|
|
35
|
+
required?: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface ResponseObject {
|
|
38
|
+
content?: Record<string, MediaTypeObject>;
|
|
39
|
+
}
|
|
40
|
+
interface ParameterObject {
|
|
41
|
+
name?: string;
|
|
42
|
+
in?: string;
|
|
43
|
+
$ref?: string;
|
|
44
|
+
schema?: SchemaObject;
|
|
45
|
+
/** The negotiation profile a client must opt into to use this parameter (e.g. `withCount`). */
|
|
46
|
+
'x-profile'?: string;
|
|
47
|
+
}
|
|
48
|
+
interface OperationObject {
|
|
49
|
+
parameters?: readonly ParameterObject[];
|
|
50
|
+
requestBody?: RequestBodyObject;
|
|
51
|
+
responses?: Record<string, ResponseObject>;
|
|
52
|
+
}
|
|
53
|
+
type HttpMethod = 'get' | 'post' | 'patch' | 'put' | 'delete';
|
|
54
|
+
type PathItemObject = Partial<Record<HttpMethod, OperationObject>>;
|
|
55
|
+
interface InfoObject {
|
|
56
|
+
title?: string;
|
|
57
|
+
version?: string;
|
|
58
|
+
}
|
|
59
|
+
interface OpenApiDocument {
|
|
60
|
+
openapi?: string;
|
|
61
|
+
info?: InfoObject;
|
|
62
|
+
servers?: readonly {
|
|
63
|
+
url?: string;
|
|
64
|
+
}[];
|
|
65
|
+
paths?: Record<string, PathItemObject>;
|
|
66
|
+
components?: {
|
|
67
|
+
schemas?: Record<string, SchemaObject>;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/build-descriptor.d.ts
|
|
72
|
+
declare class DescriptorBuilder {
|
|
73
|
+
private readonly doc;
|
|
74
|
+
private readonly schemas;
|
|
75
|
+
private readonly paths;
|
|
76
|
+
/** Lazily-built wire-type -> collection-level paginator kind (for per-relation divergence, D6a). */
|
|
77
|
+
private typePaginators?;
|
|
78
|
+
constructor(doc: OpenApiDocument);
|
|
79
|
+
/**
|
|
80
|
+
* The collection-level paginator kind for a WIRE type (`tracks`), memoised. Resolves each
|
|
81
|
+
* resource schema's wire type (its `type.const`) to the paginator of its collection GET — the
|
|
82
|
+
* baseline a per-relation related-endpoint paginator is compared against (D6a).
|
|
83
|
+
*/
|
|
84
|
+
private typePaginator;
|
|
85
|
+
build(): ApiDescriptor;
|
|
86
|
+
/**
|
|
87
|
+
* The server-level Atomic Operations capability: the path whose `POST` requestBody
|
|
88
|
+
* declares the atomic ext media type ({@link ATOMIC_EXT}). `null` when no such endpoint
|
|
89
|
+
* exists. Server-level, so it rides {@link buildAtomic}, not the per-type descriptor.
|
|
90
|
+
*/
|
|
91
|
+
buildAtomic(): AtomicDescriptor | null;
|
|
92
|
+
/**
|
|
93
|
+
* A resource schema is one whose name ends in `Resource` (but not
|
|
94
|
+
* `ResourceIdentifier`) and carries `properties.type.const` (a string) — that const
|
|
95
|
+
* is the descriptor key (the wire type). Never inferred from the schema name.
|
|
96
|
+
*/
|
|
97
|
+
private resourceType;
|
|
98
|
+
/** Follow a `$ref` to its identifier/resource schema and read `properties.type.const`. */
|
|
99
|
+
private resolveConst;
|
|
100
|
+
private attributes;
|
|
101
|
+
private relations;
|
|
102
|
+
/**
|
|
103
|
+
* The per-relation mutation capability, derived from the HTTP methods the relationship
|
|
104
|
+
* endpoint (`{collection}/{id}/relationships/{rel}`) advertises. For a to-many:
|
|
105
|
+
* `add` <- POST, `remove` <- DELETE, `replace` <- PATCH. For a to-one: `set` <- PATCH.
|
|
106
|
+
* Only `true` verbs are emitted (a missing verb is simply absent). Returns `undefined`
|
|
107
|
+
* when the relation exposes no relationship endpoint at all.
|
|
108
|
+
*/
|
|
109
|
+
private relationMutations;
|
|
110
|
+
/**
|
|
111
|
+
* Attach the per-relation endpoint-exposure signal to a relation descriptor (D24). The bundle
|
|
112
|
+
* can suppress a relation's related or relationship endpoint (`withoutRelatedEndpoint()` /
|
|
113
|
+
* `withoutRelationshipEndpoint()`, ADR 0027), which the OpenAPI reflects by simply omitting that
|
|
114
|
+
* relation's path — but codegen synthesizes generic `{rel}` templates, erasing the per-relation
|
|
115
|
+
* distinction. So mark `related: false` / `relationship: false` when this relation's specific
|
|
116
|
+
* path is absent, and ALWAYS emit an explicit `mutations` object (the verb flags when the
|
|
117
|
+
* relationship endpoint exists, else `{}`) so the client gates a suppressed relation OFF rather
|
|
118
|
+
* than failing open (offering `.get()`/`.related()`/mutations that 404).
|
|
119
|
+
*/
|
|
120
|
+
private withRelationExposure;
|
|
121
|
+
/**
|
|
122
|
+
* Collect the custom actions for a type from its `{collection}/-actions/{name}` and
|
|
123
|
+
* `{collection}/{id}/-actions/{name}` paths. The action name is the last segment; scope
|
|
124
|
+
* is `resource` when the path carries `/{id}/`, else `collection`. `input` is `document`
|
|
125
|
+
* (a JSON:API requestBody), `none` (no requestBody) or `raw` (a non-JSON:API body);
|
|
126
|
+
* `output` is `document` (a 2xx returns a JSON:API document) or `none` (only `204`).
|
|
127
|
+
*/
|
|
128
|
+
private actions;
|
|
129
|
+
/**
|
|
130
|
+
* The wire type (and cardinality) of a document component's primary `data` — a `$ref` to a
|
|
131
|
+
* resource, an inline object carrying `properties.type.const` (a create-request body), or an
|
|
132
|
+
* array of resource/identifier refs (a collection). `undefined` when the type can't be resolved.
|
|
133
|
+
*/
|
|
134
|
+
private documentPrimary;
|
|
135
|
+
/** The wire type of a single `data` schema — a `$ref` to a resource, or an inline `type.const`. */
|
|
136
|
+
private dataConst;
|
|
137
|
+
/** The `$ref` of the first 2xx JSON:API response body on an operation (the output document). */
|
|
138
|
+
private firstOkJsonApiRef;
|
|
139
|
+
/** Resolve a relationship component's `data` into a {@link RelationDescriptor}. */
|
|
140
|
+
private relationFromComponent;
|
|
141
|
+
/**
|
|
142
|
+
* Extract the pivot field-shape from a pivot `allOf` — the entry carrying
|
|
143
|
+
* `properties.meta.properties.pivot.properties` (a `belongsToMany` member's `meta.pivot`), as a
|
|
144
|
+
* field-name → wire-format-hint map (D33). Returns `undefined` when the allOf carries no pivot
|
|
145
|
+
* property block.
|
|
146
|
+
*/
|
|
147
|
+
private pivotFieldsFromAllOf;
|
|
148
|
+
/** Pull the related type from the `$ref` entry of an `allOf` (the pivot shape). */
|
|
149
|
+
private constFromAllOf;
|
|
150
|
+
/** Resolve every `$ref` entry in a list to its wire type. */
|
|
151
|
+
private constsFromRefs;
|
|
152
|
+
private clientId;
|
|
153
|
+
/**
|
|
154
|
+
* Find the collection path for a type, then derive each per-operation path that
|
|
155
|
+
* actually exists. The collection path P is the one whose POST requestBody refs
|
|
156
|
+
* `<Base>CreateRequest`, else the GET whose 200 response refs `<Base>Collection`.
|
|
157
|
+
*/
|
|
158
|
+
private operationPaths;
|
|
159
|
+
private collectionPath;
|
|
160
|
+
/** Any `P/{id}/<rel>` related path (excludes relationships/ and custom `-actions`). */
|
|
161
|
+
private hasRelatedPath;
|
|
162
|
+
private hasRelationshipPath;
|
|
163
|
+
/** Detect the paginator kind from the fetchMany operation's query parameter names. */
|
|
164
|
+
private paginator;
|
|
165
|
+
/**
|
|
166
|
+
* Detect the paginator kind an operation advertises from its `page[...]` query parameter names.
|
|
167
|
+
* `page[number]`+`page[size]` = `page`; `page[offset]`+`page[limit]` = `offset`; either cursor
|
|
168
|
+
* bound (`page[after]`/`page[before]`) = `cursor` (link-driven, D44); anything else = `none`.
|
|
169
|
+
* `label` (a path, for the drift warning) names the endpoint when page params match no known kind.
|
|
170
|
+
*/
|
|
171
|
+
private paginatorKindOf;
|
|
172
|
+
/**
|
|
173
|
+
* The `withCount` capability (Countable profile) for a type's COLLECTION read (`list`): the
|
|
174
|
+
* count tokens (`schema.items.enum`) + the negotiation profile (`x-profile`) carried by the
|
|
175
|
+
* collection GET's `withCount` query parameter. Returns `undefined` when the collection
|
|
176
|
+
* doesn't exist or advertises no `withCount`. The profile URI is never hardcoded — it is read
|
|
177
|
+
* from the parameter's `x-profile`.
|
|
178
|
+
*
|
|
179
|
+
* Deliberately scoped to the collection GET only. The per-endpoint token sets differ
|
|
180
|
+
* (a collection counts e.g. `tracks`; a related/relationship endpoint counts `_self_` and its
|
|
181
|
+
* own relations), so a single per-type `countable` can only honestly describe one endpoint.
|
|
182
|
+
* The fluent surface exposes `withCount` on `list` (the collection), so `countable` mirrors
|
|
183
|
+
* that endpoint — a related-GET fallback would advertise tokens `list` cannot legally send.
|
|
184
|
+
*/
|
|
185
|
+
private countable;
|
|
186
|
+
/**
|
|
187
|
+
* The relation paths the read endpoints accept in `include` — the `include` query
|
|
188
|
+
* parameter's `schema.items.enum` (already including nested dotted paths) on the collection
|
|
189
|
+
* GET. Returns `undefined` when the collection advertises no `include` (nothing includable).
|
|
190
|
+
*
|
|
191
|
+
* Read from the collection GET to mirror {@see sortable}/{@see filterable}: the whole builder
|
|
192
|
+
* is collection-centric (a type with no collection path gets no `paths` at all), and the bundle
|
|
193
|
+
* advertises the same `include` enum on the collection and single-resource GETs.
|
|
194
|
+
*/
|
|
195
|
+
private includable;
|
|
196
|
+
/**
|
|
197
|
+
* The sort tokens the COLLECTION read accepts — the `sort` query parameter's
|
|
198
|
+
* `schema.items.enum` (signed field names) on the collection GET. Returns `undefined` when
|
|
199
|
+
* the collection advertises no `sort` parameter (sorting unsupported).
|
|
200
|
+
*/
|
|
201
|
+
private sortable;
|
|
202
|
+
/**
|
|
203
|
+
* The filter keys the COLLECTION read accepts in `filter[...]` (the `filter[<key>]` parameter
|
|
204
|
+
* names, `<key>` extracted), sorted for deterministic output. Returns `undefined` when the
|
|
205
|
+
* collection advertises no `filter[...]` parameters (filtering unsupported). Value shapes are
|
|
206
|
+
* deliberately not captured — `filter` values stay `unknown` (out of scope for v0.1).
|
|
207
|
+
*/
|
|
208
|
+
private filterable;
|
|
209
|
+
private queryParamNames;
|
|
210
|
+
private operation;
|
|
211
|
+
private requestBodyRef;
|
|
212
|
+
private okResponseRef;
|
|
213
|
+
}
|
|
214
|
+
/** Build the {@link ApiDescriptor} from a parsed OpenAPI document. */
|
|
215
|
+
declare function buildDescriptor(doc: OpenApiDocument): ApiDescriptor;
|
|
216
|
+
/** Detect the server-level Atomic Operations capability (`null` when none). */
|
|
217
|
+
declare function buildAtomic(doc: OpenApiDocument): AtomicDescriptor | null;
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/emit.d.ts
|
|
220
|
+
/** A relation name that will later collide with a fluent-surface verb. */
|
|
221
|
+
interface VerbCollision {
|
|
222
|
+
type: string;
|
|
223
|
+
relation: string;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Find relations whose name shadows a reserved fluent-surface member (a handle accessor or a
|
|
227
|
+
* future write verb). Such relations are routed via `.rel(name)`; the codegen warns at build time.
|
|
228
|
+
*/
|
|
229
|
+
declare function detectVerbCollisions(descriptor: ApiDescriptor): VerbCollision[];
|
|
230
|
+
declare class Emitter {
|
|
231
|
+
private readonly doc;
|
|
232
|
+
private readonly descriptor;
|
|
233
|
+
private readonly atomic;
|
|
234
|
+
private readonly schemas;
|
|
235
|
+
/** Wire type -> the schema `Base` (the `<Base>Resource` name minus `Resource`). */
|
|
236
|
+
private readonly baseByType;
|
|
237
|
+
/** Enum component names referenced by an attribute, in first-seen order. */
|
|
238
|
+
private readonly usedEnums;
|
|
239
|
+
/**
|
|
240
|
+
* Component-schema names whose dedicated interface is emitted (e.g. `AlbumsAttributes`,
|
|
241
|
+
* `AlbumsCreateAttributes`). A `$ref` to one of these resolves to the interface name
|
|
242
|
+
* rather than being re-expanded structurally — so an action envelope nests the precise,
|
|
243
|
+
* already-named attribute type. Populated as interfaces are emitted.
|
|
244
|
+
*/
|
|
245
|
+
private readonly emittedInterfaces;
|
|
246
|
+
/** Component names mid-expansion in {@link tsType} — a cycle guard for self/mutually-referential `$ref`s. */
|
|
247
|
+
private readonly expanding;
|
|
248
|
+
/**
|
|
249
|
+
* The per-action alias names actually emitted, keyed `type -> actionName -> { input?; output? }`.
|
|
250
|
+
* Populated by {@link actionTypeAliases}; consumed by {@link actionTypesMap} so the action
|
|
251
|
+
* type-map only references aliases that exist (a `none` input/output contributes neither).
|
|
252
|
+
*/
|
|
253
|
+
private readonly actionAliasNames;
|
|
254
|
+
/** Provenance of the source document, stamped into the generated header for drift tracking. */
|
|
255
|
+
private readonly provenance;
|
|
256
|
+
constructor(doc: OpenApiDocument, descriptor: ApiDescriptor, atomic: AtomicDescriptor | null);
|
|
257
|
+
/** Map each declared wire type back to the `Base` of its resource schema. */
|
|
258
|
+
private indexBases;
|
|
259
|
+
emit(): string;
|
|
260
|
+
private header;
|
|
261
|
+
private imports;
|
|
262
|
+
/** Per-type attribute interface, e.g. `export interface AlbumsAttributes { ... }`. */
|
|
263
|
+
private attributeInterface;
|
|
264
|
+
/**
|
|
265
|
+
* The per-type WRITE attribute interfaces — `<Pascal>CreateAttributes` and
|
|
266
|
+
* `<Pascal>UpdateAttributes` — from the `<Base>CreateAttributes`/`<Base>UpdateAttributes`
|
|
267
|
+
* components (readOnly fields are already excluded vs the read attributes by the bundle).
|
|
268
|
+
* Create marks a field required iff it sits in the component's `required`; Update makes
|
|
269
|
+
* every field optional. A read-only type (no create/update component) contributes nothing.
|
|
270
|
+
*/
|
|
271
|
+
private writeAttributeInterfaces;
|
|
272
|
+
/**
|
|
273
|
+
* Per-action input/output type aliases for every type that declares custom actions. An
|
|
274
|
+
* action whose `input`/`output` resolves to a component schema gets a named alias
|
|
275
|
+
* (`<Pascal><Action>Input` / `<Pascal><Action>Output`) expanding that component via the
|
|
276
|
+
* shared {@link tsType} machinery — a `none` input/output (no body / a `204`) contributes
|
|
277
|
+
* no alias. Deterministic order: types, then action names (the descriptor is sorted).
|
|
278
|
+
*/
|
|
279
|
+
private actionTypeAliases;
|
|
280
|
+
/**
|
|
281
|
+
* Emit the `ActionTypes` interface wiring the emitted per-action aliases onto the typed
|
|
282
|
+
* action surface — `type -> actionName -> { input?; output? }`, the client's fourth type
|
|
283
|
+
* argument. So `client.albums.id(id).actions.reissue(body)` takes `AlbumsReissueInput` and
|
|
284
|
+
* resolves `AlbumsReissueOutput` rather than a loose `Record<string,unknown>` / `unknown`.
|
|
285
|
+
* Only actions whose alias was emitted appear (a `none`-input/output action contributes the
|
|
286
|
+
* absent side, which the runtime types fall back to loosely). Emits `{}` when none exist.
|
|
287
|
+
*/
|
|
288
|
+
private actionTypesMap;
|
|
289
|
+
/**
|
|
290
|
+
* The TS type alias (if any) for an action's request body.
|
|
291
|
+
*
|
|
292
|
+
* An action's RESULT and its FLAT input are derived at the type level from the descriptor
|
|
293
|
+
* (`outputType`/`outputCardinality` → the materialised resource view; `inputType` → the
|
|
294
|
+
* `CreateInput` of that type), so no `output` alias is emitted and a `document` input whose
|
|
295
|
+
* resource type resolves emits no `input` alias either. Only a bespoke command document (a
|
|
296
|
+
* `document` input with no resolvable `inputType`) keeps a raw-envelope alias as its typed body,
|
|
297
|
+
* expanded via {@link tsType} (falling back to a loose JSON:API document shape).
|
|
298
|
+
*/
|
|
299
|
+
private actionBodyType;
|
|
300
|
+
/** Emit the server-level atomic capability constant (`{ path }` or `null`). */
|
|
301
|
+
private atomicConst;
|
|
302
|
+
/**
|
|
303
|
+
* Render an attribute interface from a property map, a field order, and a `required`
|
|
304
|
+
* predicate (true => no `?`). Shared by the read and write attribute interfaces so the
|
|
305
|
+
* tsType/JSDoc logic stays in one place.
|
|
306
|
+
*/
|
|
307
|
+
private renderInterface;
|
|
308
|
+
/** Map a JSON Schema attribute node to a precise TypeScript type. */
|
|
309
|
+
private tsType;
|
|
310
|
+
/** The non-nullable TS type for the declared JSON type(s). */
|
|
311
|
+
private baseTsType;
|
|
312
|
+
private scalarOrComposite;
|
|
313
|
+
/** A nested object type from its `properties` (falls back to an index signature). */
|
|
314
|
+
private objectType;
|
|
315
|
+
private arrayType;
|
|
316
|
+
/** Union aliases for every enum component an attribute referenced, sorted by name. */
|
|
317
|
+
private enumAliases;
|
|
318
|
+
/**
|
|
319
|
+
* Documentation for an enum alias: a per-value list built from `x-enum-descriptions`
|
|
320
|
+
* (the richest hover DX, since a union alias can't carry per-member JSDoc), falling
|
|
321
|
+
* back to the component's own `description`.
|
|
322
|
+
*/
|
|
323
|
+
private enumDoc;
|
|
324
|
+
/**
|
|
325
|
+
* Emit the `Attributes` interface mapping each wire type to its per-type attribute
|
|
326
|
+
* interface. The runtime descriptor only carries coarse format hints; this is how the
|
|
327
|
+
* precise per-type attribute types reach the bound client (passed as the second type
|
|
328
|
+
* argument to the runtime factory).
|
|
329
|
+
*/
|
|
330
|
+
private attributesMap;
|
|
331
|
+
/**
|
|
332
|
+
* Emit the `WriteAttributes` interface mapping each writable wire type to its
|
|
333
|
+
* `{ create; update }` attribute pair. A read-only type (no create/update component)
|
|
334
|
+
* contributes no entry, so the bound client only exposes writes where the API allows
|
|
335
|
+
* them. Passed as the third type argument to the runtime factory.
|
|
336
|
+
*/
|
|
337
|
+
private writeAttributesMap;
|
|
338
|
+
/** Emit the descriptor as a literal `as const satisfies ApiDescriptor`. */
|
|
339
|
+
private resourceMap;
|
|
340
|
+
/** Render a JSON-serialisable descriptor value as deterministic TS object-literal source. */
|
|
341
|
+
private literal;
|
|
342
|
+
private boundFactory;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Generate the full source of the client module for a document + its built descriptor.
|
|
346
|
+
* The server-level atomic capability is derived from the document (override via `atomic`).
|
|
347
|
+
*/
|
|
348
|
+
declare function emit(doc: OpenApiDocument, descriptor: ApiDescriptor, atomic?: AtomicDescriptor | null): string;
|
|
349
|
+
//#endregion
|
|
350
|
+
//#region src/reader.d.ts
|
|
351
|
+
/** A self-contained JSON Schema document, keyed by JSON:API type, as served at `GET /schemas.json`. */
|
|
352
|
+
type SchemaBundle = Record<string, SchemaObject>;
|
|
353
|
+
/**
|
|
354
|
+
* Read an OpenAPI document from an http(s) URL or a local file path. Supports both JSON
|
|
355
|
+
* and YAML (decided by extension, falling back to a content sniff).
|
|
356
|
+
*/
|
|
357
|
+
declare function readDocument(input: string): Promise<OpenApiDocument>;
|
|
358
|
+
/**
|
|
359
|
+
* Read the bundle's JSON Schema bundle (`GET /schemas.json`) from an http(s) URL or a
|
|
360
|
+
* local file path — a map keyed by JSON:API type, each value a self-contained JSON Schema
|
|
361
|
+
* 2020-12 resource-object document. Supports both JSON and YAML, like {@link readDocument}.
|
|
362
|
+
*/
|
|
363
|
+
declare function readSchemas(input: string): Promise<SchemaBundle>;
|
|
364
|
+
//#endregion
|
|
365
|
+
//#region src/emit-schemas.d.ts
|
|
366
|
+
/**
|
|
367
|
+
* Emit the per-type JSON Schema map as a standalone generated artifact:
|
|
368
|
+
*
|
|
369
|
+
* ```ts
|
|
370
|
+
* export const schemas = { <type>: { …json schema… }, … } as const
|
|
371
|
+
* ```
|
|
372
|
+
*
|
|
373
|
+
* The values are the raw, self-contained resource-object JSON Schema documents the bundle
|
|
374
|
+
* serves at `GET /schemas.json` — suitable to hand straight to a validation engine via the
|
|
375
|
+
* opt-in `validate?` seam (ADR 0004). This is a separate file from the client so the main
|
|
376
|
+
* descriptor/client output is unaffected when `--schemas` is absent.
|
|
377
|
+
*
|
|
378
|
+
* The schemas are emitted as JSON (deterministic, key order preserved) so the artifact is
|
|
379
|
+
* stable to diff; `as const` keeps the literal shapes narrow.
|
|
380
|
+
*/
|
|
381
|
+
declare function emitSchemas(bundle: SchemaBundle): string;
|
|
382
|
+
//#endregion
|
|
383
|
+
//#region src/provenance.d.ts
|
|
384
|
+
/**
|
|
385
|
+
* A deterministic provenance record identifying the spec a client was generated from.
|
|
386
|
+
* Stamped into every generated artifact's header so a reader (and the `--check` drift
|
|
387
|
+
* gate) can tell which spec produced the committed output. Deliberately timestamp-free
|
|
388
|
+
* — the same spec must always produce byte-identical output so `--check` can compare.
|
|
389
|
+
*/
|
|
390
|
+
interface Provenance {
|
|
391
|
+
/** `<title> <version>` from the document's `info` (falls back to `unknown` parts). */
|
|
392
|
+
readonly source: string;
|
|
393
|
+
/** The first declared server URL, or `undefined` when none is declared. */
|
|
394
|
+
readonly server: string | undefined;
|
|
395
|
+
/** A SHA-256 (first 16 hex chars) of the canonicalised source document. */
|
|
396
|
+
readonly hash: string;
|
|
397
|
+
}
|
|
398
|
+
/** A SHA-256 over an arbitrary JSON value (canonicalised), truncated to 16 hex chars (64 bits). */
|
|
399
|
+
declare function hashJson(value: unknown): string;
|
|
400
|
+
/**
|
|
401
|
+
* Derive the provenance record for a source OpenAPI document. The `source` identifier is
|
|
402
|
+
* `<info.title> <info.version>` (each part `unknown` when absent); the `hash` is a stable
|
|
403
|
+
* content hash of the whole document.
|
|
404
|
+
*/
|
|
405
|
+
declare function deriveProvenance(doc: OpenApiDocument): Provenance;
|
|
406
|
+
/**
|
|
407
|
+
* Render the provenance record as the JSDoc header lines stamped into a generated artifact
|
|
408
|
+
* (each already prefixed with ` * `). Deterministic: no timestamp, so re-generating from the
|
|
409
|
+
* same spec yields byte-identical output.
|
|
410
|
+
*/
|
|
411
|
+
declare function provenanceLines(provenance: Provenance): string[];
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/index.d.ts
|
|
414
|
+
interface CodegenConfig {
|
|
415
|
+
/** OpenAPI document source: an http(s) URL or a local file path. */
|
|
416
|
+
input: string;
|
|
417
|
+
/** Output file (or directory) for the generated client. */
|
|
418
|
+
output: string;
|
|
419
|
+
/** Server name to target (the spec is per-server). Defaults to the default server. */
|
|
420
|
+
server?: string;
|
|
421
|
+
/** JSON Schema source for the validation seam (URL or path). Off when omitted. */
|
|
422
|
+
schemas?: string;
|
|
423
|
+
/**
|
|
424
|
+
* Drift-check mode: regenerate in-memory and compare against the committed `output`
|
|
425
|
+
* (and its schema artifact when `schemas` is set) instead of writing. Nothing is
|
|
426
|
+
* written; {@link check} reports whether the committed artifacts are up to date.
|
|
427
|
+
*/
|
|
428
|
+
check?: boolean;
|
|
429
|
+
}
|
|
430
|
+
/** One artifact's drift status: the file it stands for, and whether it is up to date. */
|
|
431
|
+
interface ArtifactDrift {
|
|
432
|
+
/** The committed artifact path (the client output, or its schema sibling). */
|
|
433
|
+
path: string;
|
|
434
|
+
/** True when the committed file matches the freshly generated content. */
|
|
435
|
+
upToDate: boolean;
|
|
436
|
+
}
|
|
437
|
+
/** The outcome of a {@link check} run: `ok` iff every checked artifact is up to date. */
|
|
438
|
+
interface CheckResult {
|
|
439
|
+
ok: boolean;
|
|
440
|
+
artifacts: ArtifactDrift[];
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* The schema-artifact path beside a client output path: `client.gen.ts` -> `client.schemas.gen.ts`
|
|
444
|
+
* (any `.gen.ts`/`.ts` suffix is preserved; a suffix-less path just gains `.schemas`).
|
|
445
|
+
*/
|
|
446
|
+
declare function schemasOutputPath(output: string): string;
|
|
447
|
+
/**
|
|
448
|
+
* Read the OpenAPI document, build the runtime descriptor, emit the generated client
|
|
449
|
+
* module and write it to `config.output` (creating parent directories as needed).
|
|
450
|
+
*
|
|
451
|
+
* When `config.schemas` is set, also read the bundle's JSON Schema bundle and write a
|
|
452
|
+
* separate per-type schema artifact (`<output>.schemas.gen.ts`) beside the client, wiring
|
|
453
|
+
* the opt-in validation seam (ADR 0004). The main client output is unchanged either way.
|
|
454
|
+
*
|
|
455
|
+
* `config.server` is metadata-only for now: the served document is already per-server,
|
|
456
|
+
* so the caller selects the server by pointing `input` at the right document.
|
|
457
|
+
*
|
|
458
|
+
* Returns the generated client source. When `config.check` is set nothing is written;
|
|
459
|
+
* call {@link check} instead for the drift-gate result.
|
|
460
|
+
*/
|
|
461
|
+
declare function generate(config: CodegenConfig): Promise<string>;
|
|
462
|
+
/**
|
|
463
|
+
* Drift gate: regenerate every artifact in memory and compare it byte-for-byte against the
|
|
464
|
+
* committed file on disk, WITHOUT writing anything. A missing committed file counts as drift
|
|
465
|
+
* (not up to date). Returns per-artifact status plus an aggregate `ok` (true iff all match) —
|
|
466
|
+
* the CI drift check reads `ok` and exits non-zero when it is false.
|
|
467
|
+
*/
|
|
468
|
+
declare function check(config: CodegenConfig): Promise<CheckResult>;
|
|
469
|
+
//#endregion
|
|
470
|
+
export { ResponseObject as A, InfoObject as C, ParameterObject as D, OperationObject as E, SchemaOrBool as M, PathItemObject as O, HttpMethod as S, OpenApiDocument as T, detectVerbCollisions as _, generate as a, buildAtomic as b, deriveProvenance as c, emitSchemas as d, SchemaBundle as f, VerbCollision as g, Emitter as h, check as i, SchemaObject as j, RequestBodyObject as k, hashJson as l, readSchemas as m, CheckResult as n, schemasOutputPath as o, readDocument as p, CodegenConfig as r, Provenance as s, ArtifactDrift as t, provenanceLines as u, emit as v, MediaTypeObject as w, buildDescriptor as x, DescriptorBuilder as y };
|
|
471
|
+
//# sourceMappingURL=index-C0becRSc.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-C0becRSc.d.mts","names":[],"sources":["../src/openapi.ts","../src/build-descriptor.ts","../src/emit.ts","../src/reader.ts","../src/emit-schemas.ts","../src/provenance.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { A as ResponseObject, C as InfoObject, D as ParameterObject, E as OperationObject, M as SchemaOrBool, O as PathItemObject, S as HttpMethod, T as OpenApiDocument, _ as detectVerbCollisions, a as generate, b as buildAtomic, c as deriveProvenance, d as emitSchemas, f as SchemaBundle, g as VerbCollision, h as Emitter, i as check, j as SchemaObject, k as RequestBodyObject, l as hashJson, m as readSchemas, n as CheckResult, o as schemasOutputPath, p as readDocument, r as CodegenConfig, s as Provenance, t as ArtifactDrift, u as provenanceLines, v as emit, w as MediaTypeObject, x as buildDescriptor, y as DescriptorBuilder } from "./index-C0becRSc.mjs";
|
|
2
|
+
export { ArtifactDrift, CheckResult, CodegenConfig, DescriptorBuilder, Emitter, type HttpMethod, type InfoObject, type MediaTypeObject, type OpenApiDocument, type OperationObject, type ParameterObject, type PathItemObject, type Provenance, type RequestBodyObject, type ResponseObject, type SchemaBundle, type SchemaObject, type SchemaOrBool, type VerbCollision, buildAtomic, buildDescriptor, check, deriveProvenance, detectVerbCollisions, emit, emitSchemas, generate, hashJson, provenanceLines, readDocument, readSchemas, schemasOutputPath };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as readSchemas, c as detectVerbCollisions, d as hashJson, f as provenanceLines, h as buildDescriptor, i as readDocument, l as emit, m as buildAtomic, n as generate, o as emitSchemas, p as DescriptorBuilder, r as schemasOutputPath, s as Emitter, t as check, u as deriveProvenance } from "./src-B24cYyjM.mjs";
|
|
2
|
+
export { DescriptorBuilder, Emitter, buildAtomic, buildDescriptor, check, deriveProvenance, detectVerbCollisions, emit, emitSchemas, generate, hashJson, provenanceLines, readDocument, readSchemas, schemasOutputPath };
|