@rayfold/cli 0.1.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.
@@ -0,0 +1,302 @@
1
+ /**
2
+ * An OpenAPI document as a Rayfold schema.
3
+ *
4
+ * The mapping is the inverse of the one `GET /rayfold/openapi.json` publishes: a `GET` or a `QUERY` is a query, anything that
5
+ * changes data is a command, `components.schemas` are types, and every operation keeps the URL it already has with
6
+ * `@http`, so existing clients go on working while new ones move to batches and shapes.
7
+ *
8
+ * It is a starting point, not a finished schema: an OpenAPI document says nothing about which fields belong to an
9
+ * entity's identity, what may be cached, or who may read what. Whatever cannot be mapped is reported in `notes`
10
+ * rather than guessed at.
11
+ */
12
+ import { RESERVED_FIELD_NAMES, assertValid, named, list, builtinTypes } from "@rayfold/schema";
13
+ // `query` is OpenAPI 3.2's read-with-a-body, which is what a shaped read is bound to
14
+ const METHODS = ["get", "query", "put", "post", "patch", "delete"];
15
+ export function irFromOpenApi(doc) {
16
+ const notes = [];
17
+ const ir = { rayfold: "0.1", types: builtinTypes(), ops: {}, views: {} };
18
+ const components = doc["components"]?.["schemas"] ?? {};
19
+ for (const [name, schema] of Object.entries(components)) {
20
+ // a document that describes Page or PageArgs is describing the protocol's own types back to us
21
+ if (ir.types[name]?.builtin) {
22
+ notes.push(`${name}: the protocol defines it, so the document's version was left out and references point at the built-in.`);
23
+ continue;
24
+ }
25
+ const def = typeFrom(name, schema, ir, notes);
26
+ if (def)
27
+ ir.types[name] = def;
28
+ }
29
+ const paths = doc["paths"] ?? {};
30
+ const used = new Set();
31
+ for (const [path, item] of Object.entries(paths)) {
32
+ for (const method of METHODS) {
33
+ const operation = item[method];
34
+ if (!operation)
35
+ continue;
36
+ const op = opFrom(method, path, operation, ir, notes, used);
37
+ if (op)
38
+ ir.ops[op.name] = op;
39
+ }
40
+ }
41
+ if (!Object.keys(ir.ops).length)
42
+ notes.push("No operations were found: the document has no paths this importer could read.");
43
+ toInputs(ir, notes);
44
+ assertValid(ir);
45
+ return { ir, notes };
46
+ }
47
+ /**
48
+ * What a caller sends is an input type, what it gets back is an entity or an object: OpenAPI draws no such line, so
49
+ * anything reachable from an argument becomes an input here. A schema used in both directions is copied, since the
50
+ * two sides drift apart as soon as either changes.
51
+ */
52
+ function toInputs(ir, notes) {
53
+ const reach = (t, into) => {
54
+ if (t.kind === "list")
55
+ return reach(t.of, into);
56
+ for (const arg of t.args ?? [])
57
+ reach(arg, into);
58
+ const def = ir.types[t.name];
59
+ if (!def || def.builtin || into.has(t.name))
60
+ return;
61
+ into.add(t.name);
62
+ if ("fields" in def)
63
+ for (const f of def.fields)
64
+ reach(f.type, into);
65
+ if (def.kind === "union")
66
+ for (const m of def.members)
67
+ reach(named(m), into);
68
+ };
69
+ const sent = new Set();
70
+ const returned = new Set();
71
+ for (const op of Object.values(ir.ops)) {
72
+ for (const a of op.args)
73
+ reach(a.type, sent);
74
+ reach(op.returns, returned);
75
+ }
76
+ const renamed = new Map();
77
+ for (const name of sent) {
78
+ const def = ir.types[name];
79
+ if (!def || def.builtin || !("fields" in def) || def.kind === "input")
80
+ continue;
81
+ const fields = def.fields.map((f) => ({ ...f }));
82
+ const described = def.description !== undefined ? { description: def.description } : {};
83
+ if (returned.has(name)) {
84
+ const copy = uniqueName(`${name}Input`, ir);
85
+ ir.types[copy] = { kind: "input", name: copy, annotations: [], ...described, fields };
86
+ renamed.set(name, copy);
87
+ notes.push(`${name}: it is both sent and returned, so ${copy} carries the sending side.`);
88
+ }
89
+ else {
90
+ ir.types[name] = { kind: "input", name, annotations: [], ...described, fields };
91
+ }
92
+ }
93
+ if (!renamed.size)
94
+ return;
95
+ for (const op of Object.values(ir.ops))
96
+ for (const a of op.args)
97
+ a.type = renameRef(a.type, renamed);
98
+ for (const def of Object.values(ir.types)) {
99
+ if (def.kind !== "input")
100
+ continue;
101
+ for (const f of def.fields)
102
+ f.type = renameRef(f.type, renamed);
103
+ }
104
+ }
105
+ function renameRef(t, renamed) {
106
+ if (t.kind === "list")
107
+ return list(renameRef(t.of, renamed), t.nullable);
108
+ const name = renamed.get(t.name) ?? t.name;
109
+ return t.args ? named(name, t.nullable, t.args.map((a) => renameRef(a, renamed))) : named(name, t.nullable);
110
+ }
111
+ // ------------------------------------------------------------------ types
112
+ function typeFrom(name, schema, ir, notes) {
113
+ const values = schema["enum"];
114
+ if (Array.isArray(values) && values.every((v) => typeof v === "string")) {
115
+ return {
116
+ kind: "enum",
117
+ name,
118
+ annotations: [],
119
+ ...describe(schema),
120
+ values: values.map((v, i) => ({ name: enumValueName(v), annotations: [], ordinal: i + 1 })),
121
+ };
122
+ }
123
+ const composed = schema["oneOf"] ?? schema["anyOf"];
124
+ if (Array.isArray(composed)) {
125
+ const members = composed.map((s) => refName(s)).filter((n) => n !== undefined);
126
+ if (members.length === composed.length && members.length > 1) {
127
+ return { kind: "union", name, annotations: [], ...describe(schema), members };
128
+ }
129
+ notes.push(`${name}: a oneOf/anyOf of inline schemas became an object with the fields they share.`);
130
+ }
131
+ const properties = schema["properties"] ?? {};
132
+ if (!Object.keys(properties).length && schema["type"] !== "object") {
133
+ notes.push(`${name}: a schema with no properties became a scalar; give it a format if it needs one.`);
134
+ return { kind: "scalar", name, annotations: [], ...describe(schema) };
135
+ }
136
+ const required = new Set(schema["required"] ?? []);
137
+ // `$type` is how the wire says what a value is; the schema owns it, so a document that carries it is not copied
138
+ const declared = Object.entries(properties).filter(([field]) => {
139
+ if (!RESERVED_FIELD_NAMES.has(field))
140
+ return true;
141
+ notes.push(`${name}.${field}: the protocol owns that name, so the field was left out.`);
142
+ return false;
143
+ });
144
+ const fields = declared.map(([field, sub], i) => ({
145
+ name: field,
146
+ ...describe(sub),
147
+ type: typeRefFrom(sub, !required.has(field), ir, `${name}_${field}`, notes),
148
+ args: [],
149
+ annotations: [],
150
+ ordinal: i + 1,
151
+ }));
152
+ // an object with a string id is an entity: it has an identity the cache and patches can address
153
+ const id = fields.find((f) => f.name === "id");
154
+ if (id && !id.type.nullable) {
155
+ id.type = named("ID");
156
+ return { kind: "entity", name, annotations: [], ...describe(schema), fields, implements: [] };
157
+ }
158
+ return { kind: "object", name, annotations: [], ...describe(schema), fields };
159
+ }
160
+ const FORMATS = {
161
+ "date-time": "Instant",
162
+ date: "Date",
163
+ duration: "Duration",
164
+ byte: "Bytes",
165
+ binary: "Bytes",
166
+ decimal: "Decimal",
167
+ int64: "Long",
168
+ };
169
+ function typeRefFrom(schema, nullable, ir, hoistAs, notes) {
170
+ const ref = refName(schema);
171
+ if (ref)
172
+ return named(ref, nullable);
173
+ const declared = schema["type"];
174
+ const types = Array.isArray(declared) ? declared.filter((t) => t !== "null") : declared === undefined ? [] : [declared];
175
+ const nullFromType = Array.isArray(declared) && declared.includes("null");
176
+ const isNullable = nullable || nullFromType || schema["nullable"] === true;
177
+ const type = types[0];
178
+ if (type === "array") {
179
+ const items = schema["items"] ?? {};
180
+ return list(typeRefFrom(items, false, ir, hoistAs, notes), isNullable);
181
+ }
182
+ if (type === "object" || (schema["properties"] !== undefined && type === undefined)) {
183
+ // an inline object needs a name of its own to be referred to
184
+ const name = uniqueName(pascal(hoistAs), ir);
185
+ const def = typeFrom(name, schema, ir, notes);
186
+ if (def)
187
+ ir.types[name] = def;
188
+ notes.push(`${name}: an inline object was given a name of its own.`);
189
+ return named(name, isNullable);
190
+ }
191
+ if (type === "string") {
192
+ const format = String(schema["format"] ?? "");
193
+ return named(FORMATS[format] ?? "String", isNullable);
194
+ }
195
+ if (type === "integer")
196
+ return named(FORMATS[String(schema["format"] ?? "")] ?? "Int", isNullable);
197
+ if (type === "number")
198
+ return named(String(schema["format"] ?? "") === "decimal" ? "Decimal" : "Float", isNullable);
199
+ if (type === "boolean")
200
+ return named("Boolean", isNullable);
201
+ return named("JSON", isNullable);
202
+ }
203
+ function refName(schema) {
204
+ const ref = schema["$ref"];
205
+ return typeof ref === "string" ? ref.slice(ref.lastIndexOf("/") + 1) : undefined;
206
+ }
207
+ // ------------------------------------------------------------------ operations
208
+ function opFrom(method, path, operation, ir, notes, used) {
209
+ const kind = method === "get" || method === "query" ? "query" : "command";
210
+ const name = uniqueOpName(operationName(method, path, operation), used);
211
+ const args = [];
212
+ for (const parameter of operation["parameters"] ?? []) {
213
+ const where = parameter["in"];
214
+ if (where !== "path" && where !== "query")
215
+ continue; // headers and cookies are transport, not arguments
216
+ const schema = parameter["schema"] ?? { type: "string" };
217
+ args.push({
218
+ name: String(parameter["name"]),
219
+ ...describe(parameter),
220
+ type: typeRefFrom(schema, parameter["required"] !== true, ir, `${name}_${String(parameter["name"])}`, notes),
221
+ annotations: [],
222
+ });
223
+ }
224
+ const body = bodySchema(operation);
225
+ if (body) {
226
+ const ref = refName(body);
227
+ if (ref) {
228
+ args.push({ name: "input", type: named(ref), annotations: [] });
229
+ }
230
+ else {
231
+ const properties = body["properties"] ?? {};
232
+ const required = new Set(body["required"] ?? []);
233
+ for (const [field, sub] of Object.entries(properties)) {
234
+ args.push({ name: field, type: typeRefFrom(sub, !required.has(field), ir, `${name}_${field}`, notes), annotations: [] });
235
+ }
236
+ if (!Object.keys(properties).length)
237
+ notes.push(`${name}: the request body had no properties to read, so it takes none.`);
238
+ }
239
+ }
240
+ const returns = resultType(operation, ir, name, notes);
241
+ const annotations = [{ name: "http", args: { method: { $ident: method.toUpperCase() }, path: path } }];
242
+ if (operation["deprecated"] === true)
243
+ annotations.push({ name: "deprecated", args: {} });
244
+ return { kind, name, ...describe(operation), args, returns, throws: [], emits: [], annotations };
245
+ }
246
+ function bodySchema(operation) {
247
+ const content = operation["requestBody"]?.["content"];
248
+ if (!content)
249
+ return undefined;
250
+ const json = (content["application/json"] ?? content["application/merge-patch+json"]);
251
+ return json?.["schema"] ?? undefined;
252
+ }
253
+ function resultType(operation, ir, name, notes) {
254
+ const responses = operation["responses"] ?? {};
255
+ for (const status of ["200", "201", "202", "default"]) {
256
+ const response = responses[status];
257
+ const schema = response?.["content"]?.["application/json"]?.["schema"];
258
+ if (schema)
259
+ return typeRefFrom(schema, false, ir, `${name}_result`, notes);
260
+ }
261
+ notes.push(`${name}: no JSON response was described, so it returns JSON.`);
262
+ return named("JSON");
263
+ }
264
+ /** `operationId` when the document has one; otherwise the method and the path's own words. */
265
+ function operationName(method, path, operation) {
266
+ const id = operation["operationId"];
267
+ if (typeof id === "string" && id.trim())
268
+ return camel(id);
269
+ const words = path.split("/").filter((s) => s && !s.startsWith("{"));
270
+ return camel([method, ...words].join("_"));
271
+ }
272
+ function uniqueOpName(name, used) {
273
+ let unique = name;
274
+ for (let n = 2; used.has(unique); n++)
275
+ unique = `${name}${n}`;
276
+ used.add(unique);
277
+ return unique;
278
+ }
279
+ function uniqueName(name, ir) {
280
+ let unique = name;
281
+ for (let n = 2; ir.types[unique]; n++)
282
+ unique = `${name}${n}`;
283
+ return unique;
284
+ }
285
+ function describe(schema) {
286
+ const text = schema["description"] ?? schema["summary"];
287
+ return typeof text === "string" && text.trim() ? { description: text.trim() } : {};
288
+ }
289
+ const words = (s) => s.split(/[^A-Za-z0-9]+/).filter(Boolean);
290
+ function camel(s) {
291
+ const parts = words(s);
292
+ return parts.map((p, i) => (i === 0 ? p[0].toLowerCase() + p.slice(1) : p[0].toUpperCase() + p.slice(1))).join("");
293
+ }
294
+ function pascal(s) {
295
+ const c = camel(s);
296
+ return c ? c[0].toUpperCase() + c.slice(1) : c;
297
+ }
298
+ function enumValueName(value) {
299
+ const name = words(value).join("_").toUpperCase();
300
+ return /^[A-Za-z_]/.test(name) ? name : `V_${name}`;
301
+ }
302
+ //# sourceMappingURL=import-openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-openapi.js","sourceRoot":"","sources":["../src/import-openapi.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * An OpenAPI document as a Rayfold schema.\n *\n * The mapping is the inverse of the one `GET /rayfold/openapi.json` publishes: a `GET` or a `QUERY` is a query, anything that\n * changes data is a command, `components.schemas` are types, and every operation keeps the URL it already has with\n * `@http`, so existing clients go on working while new ones move to batches and shapes.\n *\n * It is a starting point, not a finished schema: an OpenAPI document says nothing about which fields belong to an\n * entity's identity, what may be cached, or who may read what. Whatever cannot be mapped is reported in `notes`\n * rather than guessed at.\n */\nimport { RESERVED_FIELD_NAMES, assertValid, named, list, type Annotation, type ArgDef, type FieldDef, type JsonValue, type OpDef, type RayfoldSchemaIR, type TypeDef, type TypeRef, builtinTypes } from \"@rayfold/schema\";\n\ntype Json = Record<string, unknown>;\n\nexport interface Imported {\n ir: RayfoldSchemaIR;\n /** What the document said that the schema cannot say, and what was assumed instead. */\n notes: string[];\n}\n\n// `query` is OpenAPI 3.2's read-with-a-body, which is what a shaped read is bound to\nconst METHODS = [\"get\", \"query\", \"put\", \"post\", \"patch\", \"delete\"] as const;\n\nexport function irFromOpenApi(doc: Json): Imported {\n const notes: string[] = [];\n const ir: RayfoldSchemaIR = { rayfold: \"0.1\", types: builtinTypes(), ops: {}, views: {} };\n const components = ((doc[\"components\"] as Json | undefined)?.[\"schemas\"] as Json | undefined) ?? {};\n\n for (const [name, schema] of Object.entries(components)) {\n // a document that describes Page or PageArgs is describing the protocol's own types back to us\n if (ir.types[name]?.builtin) {\n notes.push(`${name}: the protocol defines it, so the document's version was left out and references point at the built-in.`);\n continue;\n }\n const def = typeFrom(name, schema as Json, ir, notes);\n if (def) ir.types[name] = def;\n }\n\n const paths = (doc[\"paths\"] as Json | undefined) ?? {};\n const used = new Set<string>();\n for (const [path, item] of Object.entries(paths)) {\n for (const method of METHODS) {\n const operation = (item as Json)[method] as Json | undefined;\n if (!operation) continue;\n const op = opFrom(method, path, operation, ir, notes, used);\n if (op) ir.ops[op.name] = op;\n }\n }\n\n if (!Object.keys(ir.ops).length) notes.push(\"No operations were found: the document has no paths this importer could read.\");\n toInputs(ir, notes);\n assertValid(ir);\n return { ir, notes };\n}\n\n/**\n * What a caller sends is an input type, what it gets back is an entity or an object: OpenAPI draws no such line, so\n * anything reachable from an argument becomes an input here. A schema used in both directions is copied, since the\n * two sides drift apart as soon as either changes.\n */\nfunction toInputs(ir: RayfoldSchemaIR, notes: string[]): void {\n const reach = (t: TypeRef, into: Set<string>): void => {\n if (t.kind === \"list\") return reach(t.of, into);\n for (const arg of t.args ?? []) reach(arg, into);\n const def = ir.types[t.name];\n if (!def || def.builtin || into.has(t.name)) return;\n into.add(t.name);\n if (\"fields\" in def) for (const f of def.fields) reach(f.type, into);\n if (def.kind === \"union\") for (const m of def.members) reach(named(m), into);\n };\n\n const sent = new Set<string>();\n const returned = new Set<string>();\n for (const op of Object.values(ir.ops)) {\n for (const a of op.args) reach(a.type, sent);\n reach(op.returns, returned);\n }\n\n const renamed = new Map<string, string>();\n for (const name of sent) {\n const def = ir.types[name];\n if (!def || def.builtin || !(\"fields\" in def) || def.kind === \"input\") continue;\n const fields = def.fields.map((f) => ({ ...f }));\n const described = def.description !== undefined ? { description: def.description } : {};\n if (returned.has(name)) {\n const copy = uniqueName(`${name}Input`, ir);\n ir.types[copy] = { kind: \"input\", name: copy, annotations: [], ...described, fields };\n renamed.set(name, copy);\n notes.push(`${name}: it is both sent and returned, so ${copy} carries the sending side.`);\n } else {\n ir.types[name] = { kind: \"input\", name, annotations: [], ...described, fields };\n }\n }\n\n if (!renamed.size) return;\n for (const op of Object.values(ir.ops)) for (const a of op.args) a.type = renameRef(a.type, renamed);\n for (const def of Object.values(ir.types)) {\n if (def.kind !== \"input\") continue;\n for (const f of def.fields) f.type = renameRef(f.type, renamed);\n }\n}\n\nfunction renameRef(t: TypeRef, renamed: Map<string, string>): TypeRef {\n if (t.kind === \"list\") return list(renameRef(t.of, renamed), t.nullable);\n const name = renamed.get(t.name) ?? t.name;\n return t.args ? named(name, t.nullable, t.args.map((a) => renameRef(a, renamed))) : named(name, t.nullable);\n}\n\n// ------------------------------------------------------------------ types\n\nfunction typeFrom(name: string, schema: Json, ir: RayfoldSchemaIR, notes: string[]): TypeDef | undefined {\n const values = schema[\"enum\"];\n if (Array.isArray(values) && values.every((v) => typeof v === \"string\")) {\n return {\n kind: \"enum\",\n name,\n annotations: [],\n ...describe(schema),\n values: (values as string[]).map((v, i) => ({ name: enumValueName(v), annotations: [], ordinal: i + 1 })),\n };\n }\n\n const composed = schema[\"oneOf\"] ?? schema[\"anyOf\"];\n if (Array.isArray(composed)) {\n const members = composed.map((s) => refName(s as Json)).filter((n): n is string => n !== undefined);\n if (members.length === composed.length && members.length > 1) {\n return { kind: \"union\", name, annotations: [], ...describe(schema), members };\n }\n notes.push(`${name}: a oneOf/anyOf of inline schemas became an object with the fields they share.`);\n }\n\n const properties = (schema[\"properties\"] as Json | undefined) ?? {};\n if (!Object.keys(properties).length && schema[\"type\"] !== \"object\") {\n notes.push(`${name}: a schema with no properties became a scalar; give it a format if it needs one.`);\n return { kind: \"scalar\", name, annotations: [], ...describe(schema) };\n }\n\n const required = new Set((schema[\"required\"] as string[] | undefined) ?? []);\n // `$type` is how the wire says what a value is; the schema owns it, so a document that carries it is not copied\n const declared = Object.entries(properties).filter(([field]) => {\n if (!RESERVED_FIELD_NAMES.has(field)) return true;\n notes.push(`${name}.${field}: the protocol owns that name, so the field was left out.`);\n return false;\n });\n const fields: FieldDef[] = declared.map(([field, sub], i) => ({\n name: field,\n ...describe(sub as Json),\n type: typeRefFrom(sub as Json, !required.has(field), ir, `${name}_${field}`, notes),\n args: [],\n annotations: [],\n ordinal: i + 1,\n }));\n\n // an object with a string id is an entity: it has an identity the cache and patches can address\n const id = fields.find((f) => f.name === \"id\");\n if (id && !id.type.nullable) {\n id.type = named(\"ID\");\n return { kind: \"entity\", name, annotations: [], ...describe(schema), fields, implements: [] };\n }\n return { kind: \"object\", name, annotations: [], ...describe(schema), fields };\n}\n\nconst FORMATS: Record<string, string> = {\n \"date-time\": \"Instant\",\n date: \"Date\",\n duration: \"Duration\",\n byte: \"Bytes\",\n binary: \"Bytes\",\n decimal: \"Decimal\",\n int64: \"Long\",\n};\n\nfunction typeRefFrom(schema: Json, nullable: boolean, ir: RayfoldSchemaIR, hoistAs: string, notes: string[]): TypeRef {\n const ref = refName(schema);\n if (ref) return named(ref, nullable);\n\n const declared = schema[\"type\"];\n const types = Array.isArray(declared) ? declared.filter((t) => t !== \"null\") : declared === undefined ? [] : [declared];\n const nullFromType = Array.isArray(declared) && declared.includes(\"null\");\n const isNullable = nullable || nullFromType || schema[\"nullable\"] === true;\n const type = types[0];\n\n if (type === \"array\") {\n const items = (schema[\"items\"] as Json | undefined) ?? {};\n return list(typeRefFrom(items, false, ir, hoistAs, notes), isNullable);\n }\n if (type === \"object\" || (schema[\"properties\"] !== undefined && type === undefined)) {\n // an inline object needs a name of its own to be referred to\n const name = uniqueName(pascal(hoistAs), ir);\n const def = typeFrom(name, schema, ir, notes);\n if (def) ir.types[name] = def;\n notes.push(`${name}: an inline object was given a name of its own.`);\n return named(name, isNullable);\n }\n if (type === \"string\") {\n const format = String(schema[\"format\"] ?? \"\");\n return named(FORMATS[format] ?? \"String\", isNullable);\n }\n if (type === \"integer\") return named(FORMATS[String(schema[\"format\"] ?? \"\")] ?? \"Int\", isNullable);\n if (type === \"number\") return named(String(schema[\"format\"] ?? \"\") === \"decimal\" ? \"Decimal\" : \"Float\", isNullable);\n if (type === \"boolean\") return named(\"Boolean\", isNullable);\n return named(\"JSON\", isNullable);\n}\n\nfunction refName(schema: Json): string | undefined {\n const ref = schema[\"$ref\"];\n return typeof ref === \"string\" ? ref.slice(ref.lastIndexOf(\"/\") + 1) : undefined;\n}\n\n// ------------------------------------------------------------------ operations\n\nfunction opFrom(method: string, path: string, operation: Json, ir: RayfoldSchemaIR, notes: string[], used: Set<string>): OpDef | undefined {\n const kind = method === \"get\" || method === \"query\" ? \"query\" : \"command\";\n const name = uniqueOpName(operationName(method, path, operation), used);\n const args: ArgDef[] = [];\n\n for (const parameter of (operation[\"parameters\"] as Json[] | undefined) ?? []) {\n const where = parameter[\"in\"];\n if (where !== \"path\" && where !== \"query\") continue; // headers and cookies are transport, not arguments\n const schema = (parameter[\"schema\"] as Json | undefined) ?? { type: \"string\" };\n args.push({\n name: String(parameter[\"name\"]),\n ...describe(parameter),\n type: typeRefFrom(schema, parameter[\"required\"] !== true, ir, `${name}_${String(parameter[\"name\"])}`, notes),\n annotations: [],\n });\n }\n\n const body = bodySchema(operation);\n if (body) {\n const ref = refName(body);\n if (ref) {\n args.push({ name: \"input\", type: named(ref), annotations: [] });\n } else {\n const properties = (body[\"properties\"] as Json | undefined) ?? {};\n const required = new Set((body[\"required\"] as string[] | undefined) ?? []);\n for (const [field, sub] of Object.entries(properties)) {\n args.push({ name: field, type: typeRefFrom(sub as Json, !required.has(field), ir, `${name}_${field}`, notes), annotations: [] });\n }\n if (!Object.keys(properties).length) notes.push(`${name}: the request body had no properties to read, so it takes none.`);\n }\n }\n\n const returns = resultType(operation, ir, name, notes);\n const annotations: Annotation[] = [{ name: \"http\", args: { method: { $ident: method.toUpperCase() }, path: path as JsonValue } }];\n if (operation[\"deprecated\"] === true) annotations.push({ name: \"deprecated\", args: {} });\n\n return { kind, name, ...describe(operation), args, returns, throws: [], emits: [], annotations };\n}\n\nfunction bodySchema(operation: Json): Json | undefined {\n const content = (operation[\"requestBody\"] as Json | undefined)?.[\"content\"] as Json | undefined;\n if (!content) return undefined;\n const json = (content[\"application/json\"] ?? content[\"application/merge-patch+json\"]) as Json | undefined;\n return (json?.[\"schema\"] as Json | undefined) ?? undefined;\n}\n\nfunction resultType(operation: Json, ir: RayfoldSchemaIR, name: string, notes: string[]): TypeRef {\n const responses = (operation[\"responses\"] as Json | undefined) ?? {};\n for (const status of [\"200\", \"201\", \"202\", \"default\"]) {\n const response = responses[status] as Json | undefined;\n const schema = ((response?.[\"content\"] as Json | undefined)?.[\"application/json\"] as Json | undefined)?.[\"schema\"] as Json | undefined;\n if (schema) return typeRefFrom(schema, false, ir, `${name}_result`, notes);\n }\n notes.push(`${name}: no JSON response was described, so it returns JSON.`);\n return named(\"JSON\");\n}\n\n/** `operationId` when the document has one; otherwise the method and the path's own words. */\nfunction operationName(method: string, path: string, operation: Json): string {\n const id = operation[\"operationId\"];\n if (typeof id === \"string\" && id.trim()) return camel(id);\n const words = path.split(\"/\").filter((s) => s && !s.startsWith(\"{\"));\n return camel([method, ...words].join(\"_\"));\n}\n\nfunction uniqueOpName(name: string, used: Set<string>): string {\n let unique = name;\n for (let n = 2; used.has(unique); n++) unique = `${name}${n}`;\n used.add(unique);\n return unique;\n}\n\nfunction uniqueName(name: string, ir: RayfoldSchemaIR): string {\n let unique = name;\n for (let n = 2; ir.types[unique]; n++) unique = `${name}${n}`;\n return unique;\n}\n\nfunction describe(schema: Json): { description?: string } {\n const text = schema[\"description\"] ?? schema[\"summary\"];\n return typeof text === \"string\" && text.trim() ? { description: text.trim() } : {};\n}\n\nconst words = (s: string): string[] => s.split(/[^A-Za-z0-9]+/).filter(Boolean);\n\nfunction camel(s: string): string {\n const parts = words(s);\n return parts.map((p, i) => (i === 0 ? p[0]!.toLowerCase() + p.slice(1) : p[0]!.toUpperCase() + p.slice(1))).join(\"\");\n}\n\nfunction pascal(s: string): string {\n const c = camel(s);\n return c ? c[0]!.toUpperCase() + c.slice(1) : c;\n}\n\nfunction enumValueName(value: string): string {\n const name = words(value).join(\"_\").toUpperCase();\n return /^[A-Za-z_]/.test(name) ? name : `V_${name}`;\n}\n"]}