@volter/twin-standard 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.
Files changed (69) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +20 -0
  3. package/dist/src/check-sources.d.ts +33 -0
  4. package/dist/src/check-sources.js +128 -0
  5. package/dist/src/cli.d.ts +2 -0
  6. package/dist/src/cli.js +105 -0
  7. package/dist/src/derive.d.ts +2 -0
  8. package/dist/src/derive.js +349 -0
  9. package/dist/src/gate.d.ts +9 -0
  10. package/dist/src/gate.js +109 -0
  11. package/dist/src/grade.d.ts +30 -0
  12. package/dist/src/grade.js +36 -0
  13. package/dist/src/index.d.ts +7 -0
  14. package/dist/src/index.js +11 -0
  15. package/dist/src/lanes.d.ts +2 -0
  16. package/dist/src/lanes.js +33 -0
  17. package/dist/src/p3-rules.d.ts +8 -0
  18. package/dist/src/p3-rules.js +43 -0
  19. package/dist/src/protocol-3.d.ts +5 -0
  20. package/dist/src/protocol-3.js +721 -0
  21. package/dist/src/published.d.ts +13 -0
  22. package/dist/src/published.js +36 -0
  23. package/dist/src/spec-documents.d.ts +12 -0
  24. package/dist/src/spec-documents.js +61 -0
  25. package/dist/src/spec-ir-client.d.ts +20 -0
  26. package/dist/src/spec-ir-client.js +77 -0
  27. package/dist/src/spec-ir-commands.d.ts +38 -0
  28. package/dist/src/spec-ir-commands.js +38 -0
  29. package/dist/src/spec-ir-discovery.d.ts +11 -0
  30. package/dist/src/spec-ir-discovery.js +106 -0
  31. package/dist/src/spec-ir-graphql.d.ts +25 -0
  32. package/dist/src/spec-ir-graphql.js +46 -0
  33. package/dist/src/spec-ir-lines.d.ts +19 -0
  34. package/dist/src/spec-ir-lines.js +76 -0
  35. package/dist/src/spec-ir-proto.d.ts +80 -0
  36. package/dist/src/spec-ir-proto.js +339 -0
  37. package/dist/src/spec-ir.d.ts +104 -0
  38. package/dist/src/spec-ir.js +691 -0
  39. package/dist/src/spec-patches.d.ts +8 -0
  40. package/dist/src/spec-patches.js +24 -0
  41. package/dist/src/spec.d.ts +5 -0
  42. package/dist/src/spec.js +7 -0
  43. package/dist/src/types.d.ts +75 -0
  44. package/dist/src/types.js +4 -0
  45. package/dist/src/unit.d.ts +5 -0
  46. package/dist/src/unit.js +14 -0
  47. package/package.json +71 -0
  48. package/src/check-sources.ts +109 -0
  49. package/src/cli.ts +75 -0
  50. package/src/derive.ts +316 -0
  51. package/src/gate.ts +95 -0
  52. package/src/grade.ts +47 -0
  53. package/src/index.ts +12 -0
  54. package/src/lanes.ts +29 -0
  55. package/src/p3-rules.ts +44 -0
  56. package/src/protocol-3.ts +617 -0
  57. package/src/published.ts +37 -0
  58. package/src/spec-documents.ts +58 -0
  59. package/src/spec-ir-client.ts +86 -0
  60. package/src/spec-ir-commands.ts +50 -0
  61. package/src/spec-ir-discovery.ts +104 -0
  62. package/src/spec-ir-graphql.ts +64 -0
  63. package/src/spec-ir-lines.ts +76 -0
  64. package/src/spec-ir-proto.ts +289 -0
  65. package/src/spec-ir.ts +689 -0
  66. package/src/spec-patches.ts +23 -0
  67. package/src/spec.ts +8 -0
  68. package/src/types.ts +53 -0
  69. package/src/unit.ts +15 -0
@@ -0,0 +1,80 @@
1
+ import type { SpecIR } from './spec-ir.js';
2
+ type ProtoField = {
3
+ name: string;
4
+ jsonName: string;
5
+ number: number;
6
+ type: string;
7
+ repeated: boolean;
8
+ map?: {
9
+ key: string;
10
+ value: string;
11
+ };
12
+ oneof?: string;
13
+ };
14
+ type ProtoMessage = {
15
+ fullName: string;
16
+ fields: ProtoField[];
17
+ };
18
+ type ProtoEnum = {
19
+ fullName: string;
20
+ values: string[];
21
+ numbers: Record<string, number>;
22
+ };
23
+ type ProtoRpc = {
24
+ name: string;
25
+ request: string;
26
+ response: string;
27
+ clientStreams: boolean;
28
+ serverStreams: boolean;
29
+ };
30
+ type ProtoService = {
31
+ fullName: string;
32
+ rpcs: ProtoRpc[];
33
+ };
34
+ export type ProtoFile = {
35
+ path: string;
36
+ pkg: string;
37
+ messages: ProtoMessage[];
38
+ enums: ProtoEnum[];
39
+ services: ProtoService[];
40
+ };
41
+ /** One `.proto` file's declarations. */
42
+ export declare function parseProto(path: string, text: string): ProtoFile;
43
+ /** The vendored tree (path → text) as data, so a spec patch corrects it as it corrects a JSON spec (derive-pack). */
44
+ export type ProtoSpec = {
45
+ format: 'proto';
46
+ files: ProtoFile[];
47
+ };
48
+ export declare function readProtoFiles(files: Record<string, string>): ProtoSpec;
49
+ /** The IR of the services the vendored files declare: every rpc of every service one operation. */
50
+ export declare function fromProto(spec: ProtoSpec): SpecIR;
51
+ /** A field as the kernel's protobuf codec reads it: its number, its JSON name, and its type resolved (a scalar's name, or
52
+ * a message's or an enum's full name with its kind). */
53
+ export type ProtoSchemaField = {
54
+ name: string;
55
+ json: string;
56
+ number: number;
57
+ type: string;
58
+ kind: 'scalar' | 'message' | 'enum';
59
+ repeated?: true;
60
+ map?: {
61
+ key: string;
62
+ value: string;
63
+ valueKind: 'scalar' | 'message' | 'enum';
64
+ };
65
+ };
66
+ /** The schema the derive writes for a proto unit (`src/generated/proto.gen.json`): every message's fields, every enum's
67
+ * numbers, and each rpc's path, request and response, all by full name. */
68
+ export type ProtoSchema = {
69
+ messages: Record<string, ProtoSchemaField[]>;
70
+ enums: Record<string, Record<string, number>>;
71
+ rpcs: Record<string, {
72
+ operation: string;
73
+ request: string;
74
+ response: string;
75
+ serverStreams?: true;
76
+ clientStreams?: true;
77
+ }>;
78
+ };
79
+ export declare function protoSchema(spec: ProtoSpec): ProtoSchema;
80
+ export {};
@@ -0,0 +1,339 @@
1
+ // A Protocol Buffers service as the IR (spec-ir.ts): the spec of a vendor whose API is RPCs over HTTP rather than
2
+ // REST paths (PlanetScale's psdb data plane, `psdb.v1alpha1.Database`, which @planetscale/database calls). The
3
+ // `.proto` files are the vendor's published words, vendored under `spec/proto/` with every file they import.
4
+ //
5
+ // Connect's unary protocol (connectrpc.com/docs/protocol) addresses an rpc as `POST /<package>.<Service>/<Method>`
6
+ // with the request message as its body in the proto3 JSON mapping (protobuf.dev/programming-guides/json): a field is
7
+ // named by its lowerCamelCase JSON name, 64-bit integers are strings, bytes are base64, enums are their value names,
8
+ // a map is an object, and a field holding its default value may be omitted. So each rpc is one operation: its request
9
+ // message's fields are the body, and its response message is what it answers. A service's operations act on no
10
+ // resource path, so each is an `action`; the response messages are the resources, as a spec that declares none yields
11
+ // resources named after its response schemas.
12
+ const SCALARS = {
13
+ double: 'number', float: 'number',
14
+ int32: 'integer', uint32: 'integer', sint32: 'integer', fixed32: 'integer', sfixed32: 'integer',
15
+ // proto3 JSON writes 64-bit integers as decimal strings
16
+ int64: 'string', uint64: 'string', sint64: 'string', fixed64: 'string', sfixed64: 'string',
17
+ bool: 'boolean', string: 'string', bytes: 'string',
18
+ };
19
+ /** protoc's JSON name: underscores dropped, the letter after each upper-cased. */
20
+ const jsonNameOf = (name) => name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
21
+ function tokens(text) {
22
+ // strings, comments and tokens in one pass: a `//` inside a string ("https://…") is the string's, not a comment
23
+ const all = text.match(/"(?:[^"\\]|\\.)*"|\/\/[^\n]*|\/\*[\s\S]*?\*\/|[A-Za-z_][\w.]*|0[xX][0-9a-fA-F]+|\d+(?:\.\d+)?|[{}()<>;=,[\]-]/g) ?? [];
24
+ return all.filter((x) => !x.startsWith('//') && !x.startsWith('/*'));
25
+ }
26
+ /** One `.proto` file's declarations. */
27
+ export function parseProto(path, text) {
28
+ const t = tokens(text);
29
+ let i = 0;
30
+ const file = { path, pkg: '', messages: [], enums: [], services: [] };
31
+ const expect = (tok) => { if (t[i] !== tok)
32
+ throw new Error(`spec-ir-proto: ${path}: expected "${tok}", found "${t[i]}"`); i++; };
33
+ // a statement ends at its `;`, or at the `}` closing a block that is its body (`extend X { … }`); braces inside
34
+ // brackets or an option's value (`[declaration = { … }]`, `option (x) = { … };`) do not end it
35
+ const skipStatement = () => {
36
+ let depth = 0;
37
+ while (i < t.length) {
38
+ const x = t[i++];
39
+ if (x === '{' || x === '[')
40
+ depth++;
41
+ else if (x === '}' || x === ']') {
42
+ depth--;
43
+ if (depth <= 0 && x === '}' && t[i] !== ';')
44
+ return;
45
+ }
46
+ else if (x === ';' && depth <= 0)
47
+ return;
48
+ }
49
+ };
50
+ // a field's options, nested brackets and all (`[(google.api.field_behavior) = REQUIRED, (x) = { a: [1] }]`)
51
+ const skipOptions = () => { if (t[i] !== '[')
52
+ return; let depth = 0; do {
53
+ if (t[i] === '[')
54
+ depth++;
55
+ else if (t[i] === ']')
56
+ depth--;
57
+ i++;
58
+ } while (depth > 0 && i < t.length); };
59
+ const scope = () => file.pkg;
60
+ const parseEnum = (prefix) => {
61
+ const name = t[i++];
62
+ expect('{');
63
+ const values = [];
64
+ const numbers = {};
65
+ while (t[i] !== '}') {
66
+ if (t[i] === 'option' || t[i] === 'reserved') {
67
+ skipStatement();
68
+ continue;
69
+ }
70
+ const v = t[i++];
71
+ expect('=');
72
+ // a negative number is two tokens
73
+ const negative = t[i] === '-' ? (i++, true) : false;
74
+ numbers[v] = (negative ? -1 : 1) * Number(t[i++]);
75
+ skipOptions();
76
+ expect(';');
77
+ values.push(v);
78
+ }
79
+ i++;
80
+ file.enums.push({ fullName: `${prefix}.${name}`, values, numbers });
81
+ };
82
+ const parseField = (fields, oneof) => {
83
+ let repeated = false;
84
+ if (t[i] === 'repeated') {
85
+ repeated = true;
86
+ i++;
87
+ }
88
+ else if (t[i] === 'optional' || t[i] === 'required')
89
+ i++;
90
+ let type;
91
+ let map;
92
+ if (t[i] === 'map') {
93
+ i++;
94
+ expect('<');
95
+ const key = t[i++];
96
+ expect(',');
97
+ const value = t[i++];
98
+ expect('>');
99
+ map = { key, value };
100
+ type = value;
101
+ }
102
+ else
103
+ type = t[i++];
104
+ const name = t[i++];
105
+ expect('=');
106
+ const number = Number(t[i++]);
107
+ let jsonName = jsonNameOf(name);
108
+ if (t[i] === '[') {
109
+ // a declared json_name overrides the derived one
110
+ const end = t.indexOf(']', i);
111
+ const at = t.slice(i, end).indexOf('json_name');
112
+ if (at >= 0)
113
+ jsonName = JSON.parse(t[i + at + 2]);
114
+ i = end + 1;
115
+ }
116
+ expect(';');
117
+ fields.push({ name, jsonName, number, type, repeated, ...(map ? { map } : {}), ...(oneof ? { oneof } : {}) });
118
+ };
119
+ const parseMessage = (prefix) => {
120
+ const name = t[i++];
121
+ const fullName = `${prefix}.${name}`;
122
+ expect('{');
123
+ const fields = [];
124
+ while (t[i] !== '}') {
125
+ const x = t[i];
126
+ if (x === 'message') {
127
+ i++;
128
+ parseMessage(fullName);
129
+ }
130
+ else if (x === 'enum') {
131
+ i++;
132
+ parseEnum(fullName);
133
+ }
134
+ else if (x === 'oneof') {
135
+ i++;
136
+ const group = t[i++];
137
+ expect('{');
138
+ while (t[i] !== '}') {
139
+ if (t[i] === 'option') {
140
+ skipStatement();
141
+ continue;
142
+ }
143
+ parseField(fields, group);
144
+ }
145
+ i++;
146
+ }
147
+ else if (x === 'option' || x === 'reserved' || x === 'extensions' || x === 'extend')
148
+ skipStatement();
149
+ else if (x === ';')
150
+ i++;
151
+ else
152
+ parseField(fields);
153
+ }
154
+ i++;
155
+ file.messages.push({ fullName, fields });
156
+ };
157
+ while (i < t.length) {
158
+ const x = t[i++];
159
+ if (x === 'package') {
160
+ file.pkg = t[i++];
161
+ expect(';');
162
+ }
163
+ else if (x === 'message')
164
+ parseMessage(scope());
165
+ else if (x === 'enum')
166
+ parseEnum(scope());
167
+ else if (x === 'service') {
168
+ const name = t[i++];
169
+ expect('{');
170
+ const rpcs = [];
171
+ while (t[i] !== '}') {
172
+ if (t[i] !== 'rpc') {
173
+ skipStatement();
174
+ continue;
175
+ }
176
+ i++;
177
+ const rpc = t[i++];
178
+ expect('(');
179
+ const clientStreams = t[i] === 'stream' ? (i++, true) : false;
180
+ const request = t[i++];
181
+ expect(')');
182
+ expect('returns');
183
+ expect('(');
184
+ const serverStreams = t[i] === 'stream' ? (i++, true) : false;
185
+ const response = t[i++];
186
+ expect(')');
187
+ if (t[i] === '{') {
188
+ let depth = 0;
189
+ do {
190
+ if (t[i] === '{')
191
+ depth++;
192
+ else if (t[i] === '}')
193
+ depth--;
194
+ i++;
195
+ } while (depth > 0);
196
+ }
197
+ else
198
+ expect(';');
199
+ rpcs.push({ name: rpc, request, response, clientStreams, serverStreams });
200
+ }
201
+ i++;
202
+ file.services.push({ fullName: `${file.pkg}.${name}`, rpcs });
203
+ }
204
+ else if (x === 'syntax' || x === 'import' || x === 'option' || x === 'edition' || x === 'extend')
205
+ skipStatement(); // an `extend` adds options, never a wire type
206
+ else if (x === ';')
207
+ continue;
208
+ else
209
+ throw new Error(`spec-ir-proto: ${path}: unexpected "${x}"`);
210
+ }
211
+ return file;
212
+ }
213
+ export function readProtoFiles(files) {
214
+ return { format: 'proto', files: Object.entries(files).sort(([a], [b]) => a.localeCompare(b)).map(([path, text]) => parseProto(path, text)) };
215
+ }
216
+ /** The IR of the services the vendored files declare: every rpc of every service one operation. */
217
+ export function fromProto(spec) {
218
+ const parsed = spec.files;
219
+ const messages = new Map(parsed.flatMap((f) => f.messages.map((m) => [m.fullName, m])));
220
+ const enums = new Map(parsed.flatMap((f) => f.enums.map((e) => [e.fullName, e])));
221
+ // a type name as written resolves from the innermost scope outward (protobuf's scoping rule)
222
+ const resolve = (name, scopeName) => {
223
+ if (name.startsWith('.'))
224
+ return name.slice(1);
225
+ const parts = scopeName.split('.');
226
+ for (let n = parts.length; n >= 0; n--) {
227
+ const candidate = [...parts.slice(0, n), name].filter(Boolean).join('.');
228
+ if (messages.has(candidate) || enums.has(candidate))
229
+ return candidate;
230
+ }
231
+ return undefined;
232
+ };
233
+ const short = (fullName) => fullName.split('.').pop();
234
+ const typeOf = (f, scopeName) => {
235
+ if (f.map)
236
+ return { type: 'object' };
237
+ if (f.repeated)
238
+ return { type: 'array' };
239
+ if (SCALARS[f.type])
240
+ return { type: SCALARS[f.type] };
241
+ const full = resolve(f.type, scopeName);
242
+ if (full && enums.has(full))
243
+ return { type: 'string', enum: enums.get(full).values };
244
+ if (!full)
245
+ throw new Error(`spec-ir-proto: ${scopeName}: type ${f.type} is not declared in the vendored files`);
246
+ return { type: short(full) };
247
+ };
248
+ const services = parsed.flatMap((f) => f.services);
249
+ if (!services.length)
250
+ throw new Error('spec-ir-proto: the vendored files declare no service');
251
+ const operations = [];
252
+ const answered = new Set();
253
+ for (const svc of services) {
254
+ const pkg = svc.fullName.split('.').slice(0, -1).join('.');
255
+ for (const rpc of svc.rpcs) {
256
+ const request = resolve(rpc.request, pkg);
257
+ const response = resolve(rpc.response, pkg);
258
+ if (!request || !response)
259
+ throw new Error(`spec-ir-proto: ${svc.fullName}.${rpc.name}: an argument type is not declared`);
260
+ answered.add(response);
261
+ const body = messages.get(request).fields
262
+ .map((f) => ({ name: f.jsonName, type: typeOf(f, request).type, required: false }))
263
+ .sort((a, b) => a.name.localeCompare(b.name));
264
+ operations.push({
265
+ id: rpc.name,
266
+ method: 'post',
267
+ path: `/${svc.fullName}/${rpc.name}`,
268
+ pathParams: [],
269
+ query: [],
270
+ body,
271
+ bodyEncoding: 'json',
272
+ successStatus: 200,
273
+ ...(rpc.serverStreams ? { streams: true } : {}),
274
+ answers: { resource: short(response), list: false },
275
+ class: 'action',
276
+ });
277
+ }
278
+ }
279
+ // proto3 JSON may omit any field holding its default, so none is required; a message field may be null
280
+ const resources = [...answered].sort().map((full) => {
281
+ const m = messages.get(full);
282
+ const fields = m.fields.map((f) => {
283
+ const t = typeOf(f, full);
284
+ return { name: f.jsonName, type: t.type, nullable: !SCALARS[f.type] && !t.enum && !f.repeated && !f.map, ...(t.enum ? { enum: t.enum } : {}) };
285
+ });
286
+ return {
287
+ name: short(full),
288
+ schema: short(full),
289
+ fields,
290
+ stateCandidates: fields.filter((f) => f.enum && f.enum.length > 1).map((f) => ({ field: f.name, values: f.enum })),
291
+ expandable: [],
292
+ };
293
+ });
294
+ return { format: 'proto', version: services.map((s) => s.fullName).join(', '), resources, operations };
295
+ }
296
+ export function protoSchema(spec) {
297
+ const parsed = spec.files;
298
+ const messages = new Map(parsed.flatMap((f) => f.messages.map((m) => [m.fullName, m])));
299
+ const enums = new Map(parsed.flatMap((f) => f.enums.map((e) => [e.fullName, e])));
300
+ const resolve = (name, scopeName) => {
301
+ if (name.startsWith('.'))
302
+ return name.slice(1);
303
+ const parts = scopeName.split('.');
304
+ for (let n = parts.length; n >= 0; n--) {
305
+ const candidate = [...parts.slice(0, n), name].filter(Boolean).join('.');
306
+ if (messages.has(candidate) || enums.has(candidate))
307
+ return candidate;
308
+ }
309
+ return undefined;
310
+ };
311
+ const kindOf = (type, scopeName) => {
312
+ if (SCALARS[type])
313
+ return { type, kind: 'scalar' };
314
+ const full = resolve(type, scopeName);
315
+ if (!full)
316
+ throw new Error(`spec-ir-proto: ${scopeName}: type ${type} is not declared in the vendored files`);
317
+ return { type: full, kind: enums.has(full) ? 'enum' : 'message' };
318
+ };
319
+ const out = { messages: {}, enums: {}, rpcs: {} };
320
+ for (const [full, m] of messages) {
321
+ out.messages[full] = m.fields.map((f) => {
322
+ const t = kindOf(f.type, full);
323
+ const map = f.map ? (() => { const v = kindOf(f.map.value, full); return { key: f.map.key, value: v.type, valueKind: v.kind }; })() : undefined;
324
+ return { name: f.name, json: f.jsonName, number: f.number, type: t.type, kind: t.kind, ...(f.repeated ? { repeated: true } : {}), ...(map ? { map } : {}) };
325
+ });
326
+ }
327
+ for (const [full, e] of enums)
328
+ out.enums[full] = e.numbers;
329
+ for (const svc of parsed.flatMap((f) => f.services)) {
330
+ const pkg = svc.fullName.split('.').slice(0, -1).join('.');
331
+ // an rpc's message type the tree does not declare is a spec that cannot be served, named, never an undefined schema
332
+ const resolved = (type) => { const r = resolve(type, pkg); if (!r)
333
+ throw new Error(`proto: ${svc.fullName} names message ${type}, which no file of the tree declares`); return r; };
334
+ for (const rpc of svc.rpcs) {
335
+ out.rpcs[`/${svc.fullName}/${rpc.name}`] = { operation: rpc.name, request: resolved(rpc.request), response: resolved(rpc.response), ...(rpc.serverStreams ? { serverStreams: true } : {}), ...(rpc.clientStreams ? { clientStreams: true } : {}) };
336
+ }
337
+ }
338
+ return out;
339
+ }
@@ -0,0 +1,104 @@
1
+ type Json = any;
2
+ export type OperationClass = 'create' | 'retrieve' | 'list' | 'update' | 'delete' | 'action' | 'computed' | 'non-resource';
3
+ export declare const CRUD_CLASSES: ReadonlySet<OperationClass>;
4
+ /** A parameter or body field: its name, type and whether it is required, and the numeric bounds the spec states
5
+ * (`minimum`, `maximum`), which a strict body (the manifest's `body.strict`, `ctx.fields`) refuses outside of. */
6
+ export type IrParam = {
7
+ name: string;
8
+ type: string;
9
+ required: boolean;
10
+ minimum?: number;
11
+ maximum?: number;
12
+ };
13
+ export type IrOperation = {
14
+ /** The spec's operationId, or the `method_path` slug when the spec has none. */
15
+ id: string;
16
+ method: string;
17
+ path: string;
18
+ /** Query values that tell this operation from another at the same method and path (a spec key
19
+ * such as `/responses?beta=true`); `null`: the key present with any value (S3's UploadPart, `uploadId`). */
20
+ discriminator?: Record<string, string | null>;
21
+ /** request headers that name the operation (AWS JSON's X-Amz-Target), for a wire whose operations share a path;
22
+ * `null`: the header present with any value (S3's CopyObject, `x-amz-copy-source`) */
23
+ headers?: Record<string, string | null>;
24
+ pathParams: string[];
25
+ /** The spec's own grouping of the operation, where it has one (a client-derived spec: the client library that makes
26
+ * the call), which names the family file its handler is written in; absent, the family is the path's (the grade's
27
+ * interim rule) */
28
+ family?: string;
29
+ /** The path the operation's own server puts before it, when its path item or operation names a server of its own
30
+ * (OpenAPI's path-level `servers`: Upstash's /auditlogs at https://api.upstash.com, beside the document's /v2) */
31
+ basePath?: string;
32
+ query: IrParam[];
33
+ /** Top-level body fields; `bodyEncoding` says how the vendor reads them. */
34
+ body: IrParam[];
35
+ bodyEncoding: 'json' | 'form' | 'multipart' | 'xml' | 'none';
36
+ /** A form-encoded operation's number and boolean fields, by bracket path (formScalarsOf). */
37
+ scalars?: Record<string, 'integer' | 'number' | 'boolean'>;
38
+ successStatus: number;
39
+ /** the success is documented with no content at all (a 200 with nothing in it) */
40
+ emptySuccess?: true;
41
+ /** The success answer may be a server-sent event stream (`text/event-stream`). */
42
+ streams?: boolean;
43
+ /** The resource the success answer is (or lists), when it is one: the first of a union, whose
44
+ * other members are `alternatives` (a deleted form, or the kinds of a polymorphic answer); `key`
45
+ * when the answer is an envelope holding it under that property (`{ ok, channel: {...} }`). */
46
+ /** `extends`: fields the answer adds to its resource (GitHub's ruleset-version-with-state: allOf ruleset-version and `state`). */
47
+ answers?: {
48
+ resource: string;
49
+ list: boolean;
50
+ alternatives?: string[];
51
+ key?: string;
52
+ extends?: string[];
53
+ };
54
+ /** The credential scopes any one of which lets a caller make this call, from the spec's security. */
55
+ scopes?: string[];
56
+ /** The security schemes any one of which the call is made with (the spec's security, each requirement's scheme
57
+ * names); absent when the call takes none. `anonymous` when a requirement is empty (`{}`: the call may also go
58
+ * without). */
59
+ credentials?: string[];
60
+ /** The resource the operation acts on: the one at its path. */
61
+ resource?: string;
62
+ class: OperationClass;
63
+ };
64
+ export type IrStateCandidate = {
65
+ field: string;
66
+ values: string[];
67
+ };
68
+ export type IrResource = {
69
+ name: string;
70
+ /** The component schema that defines it. */
71
+ schema: string;
72
+ /** `discriminator`: a union's field each alternative gives one value (which alternative it is). */
73
+ fields: Array<{
74
+ name: string;
75
+ type: string;
76
+ nullable: boolean;
77
+ required?: boolean;
78
+ enum?: string[];
79
+ discriminator?: boolean;
80
+ }>;
81
+ /** Top-level enum fields with more than one value. The manifest rules on each. */
82
+ stateCandidates: IrStateCandidate[];
83
+ /** Fields holding another resource's id that a caller may ask to receive embedded. */
84
+ expandable: string[];
85
+ };
86
+ export type SpecIR = {
87
+ format: 'openapi' | 'smithy' | 'proto' | 'client';
88
+ version: string;
89
+ /** The path the vendor's server URL puts before every operation path (`/v1` for OpenAI), when any. */
90
+ basePath?: string;
91
+ /** The path parameters whose values span segments: Smithy's greedy labels (`{Key+}`), which the path keeps as the label. */
92
+ spanning?: string[];
93
+ resources: IrResource[];
94
+ operations: IrOperation[];
95
+ };
96
+ export declare function slug(method: string, path: string): string;
97
+ export declare function fromOpenAPI(spec: Json): SpecIR;
98
+ /** A Swagger 2.0 document as the OpenAPI 3 shape `fromOpenAPI` reads: definitions become component
99
+ * schemas, formData and body parameters a request body, response schemas JSON content, and
100
+ * host + basePath the server. Nothing vendor-specific. */
101
+ export declare function fromSwagger2(doc: Json): SpecIR;
102
+ /** An AWS service's Smithy JSON model as the IR (`service` names the service shape when the model holds several). */
103
+ export declare function fromSmithy(model: Json, service?: string): SpecIR;
104
+ export {};