@faable/deploy-sdk 2.1.0 → 2.2.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 @@
1
+ {"version":3,"file":"generated-client.d.ts","sourceRoot":"","sources":["../../src/api/generated-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,315 @@
1
+ // AUTO-GENERATED by scripts/gen-client.mjs — do not edit by hand.
2
+ // Regenerated from the OpenAPI spec on every build (npm run gentypes).
3
+ // Source of truth: the deploy API's operationIds + security.
4
+ import { FaableApi } from "@faable/sdk-base";
5
+ import { requireId } from "../helpers.js";
6
+ /**
7
+ * Auto-generated deploy-API methods, one per secured operation in the OpenAPI
8
+ * spec. `DeployApi` extends this and adds the constructor, custom-logic
9
+ * helpers, and deprecated aliases.
10
+ */
11
+ export class GeneratedFaableDeployApi extends FaableApi {
12
+ /**
13
+ * `GET /app/checkname` — operationId: `app/checkname`
14
+ *
15
+ * Check if an app name is availiable
16
+ */
17
+ appCheckname(params) {
18
+ return this.fetcher.request({ method: "GET", url: `/app/checkname`, params });
19
+ }
20
+ /**
21
+ * `POST /app` — operationId: `app/create`
22
+ *
23
+ * Create App
24
+ */
25
+ appCreate(data) {
26
+ return this.fetcher.post(`/app`, data);
27
+ }
28
+ /**
29
+ * `DELETE /app/{id}` — operationId: `app/delete`
30
+ *
31
+ * Delete App
32
+ */
33
+ appDelete(id) {
34
+ requireId("id", id);
35
+ return this.fetcher.delete(`/app/${id}`);
36
+ }
37
+ /**
38
+ * `GET /app/{id}` — operationId: `app/get`
39
+ *
40
+ * Get App
41
+ */
42
+ appGet(id, params) {
43
+ requireId("id", id);
44
+ return this.fetcher.request({ method: "GET", url: `/app/${id}`, params });
45
+ }
46
+ /**
47
+ * `GET /app/{id}/deploy-workflow` — operationId: `app/get_deploy_workflow`
48
+ *
49
+ * Get deploy workflow status
50
+ */
51
+ appGetDeployWorkflow(id) {
52
+ requireId("id", id);
53
+ return this.fetcher.get(`/app/${id}/deploy-workflow`);
54
+ }
55
+ /**
56
+ * `GET /app/{app_id}/registry` — operationId: `app/get_registry`
57
+ *
58
+ * Get app registry
59
+ */
60
+ appGetRegistry(app_id) {
61
+ requireId("app_id", app_id);
62
+ return this.fetcher.get(`/app/${app_id}/registry`);
63
+ }
64
+ /**
65
+ * `POST /app/{id}/link-repository` — operationId: `app/link_repository`
66
+ *
67
+ * Link a GitHub repository to an app
68
+ */
69
+ appLinkRepository(id, data) {
70
+ requireId("id", id);
71
+ return this.fetcher.post(`/app/${id}/link-repository`, data);
72
+ }
73
+ /**
74
+ * `GET /app` — operationId: `app/list`
75
+ *
76
+ * List Apps
77
+ */
78
+ appList(params) {
79
+ return this.paginator({ url: `/app`, params });
80
+ }
81
+ /**
82
+ * `GET /app/{app_id}/logs` — operationId: `app/logs`
83
+ *
84
+ * Get app logs
85
+ */
86
+ appLogs(app_id, params) {
87
+ requireId("app_id", app_id);
88
+ return this.fetcher.request({ method: "GET", url: `/app/${app_id}/logs`, params });
89
+ }
90
+ /**
91
+ * `POST /app/{id}/deploy-workflow` — operationId: `app/setup_deploy_workflow`
92
+ *
93
+ * Set up deploy workflow
94
+ */
95
+ appSetupDeployWorkflow(id) {
96
+ requireId("id", id);
97
+ return this.fetcher.post(`/app/${id}/deploy-workflow`, {});
98
+ }
99
+ /**
100
+ * `GET /app/slug/{slug}` — operationId: `app/slug`
101
+ *
102
+ * Get App by unique slug
103
+ */
104
+ appSlug(slug) {
105
+ requireId("slug", slug);
106
+ return this.fetcher.get(`/app/slug/${slug}`);
107
+ }
108
+ /**
109
+ * `GET /app/{app_id}/traffic` — operationId: `app/traffic`
110
+ *
111
+ * Get App Traffic
112
+ */
113
+ appTraffic(app_id, params) {
114
+ requireId("app_id", app_id);
115
+ return this.fetcher.request({ method: "GET", url: `/app/${app_id}/traffic`, params });
116
+ }
117
+ /**
118
+ * `POST /app/{id}/unlink-repository` — operationId: `app/unlink_repository`
119
+ *
120
+ * Unlink the GitHub repository from an app
121
+ */
122
+ appUnlinkRepository(id) {
123
+ requireId("id", id);
124
+ return this.fetcher.post(`/app/${id}/unlink-repository`, {});
125
+ }
126
+ /**
127
+ * `POST /app/{id}` — operationId: `app/update`
128
+ *
129
+ * Update App
130
+ */
131
+ appUpdate(id, data) {
132
+ requireId("id", id);
133
+ return this.fetcher.post(`/app/${id}`, data);
134
+ }
135
+ /**
136
+ * `POST /deployment` — operationId: `deployment/create`
137
+ *
138
+ * Create Deployment
139
+ */
140
+ deploymentCreate(data) {
141
+ return this.fetcher.post(`/deployment`, data);
142
+ }
143
+ /**
144
+ * `DELETE /deployment/{id}` — operationId: `deployment/delete`
145
+ *
146
+ * Delete Deployment
147
+ */
148
+ deploymentDelete(id) {
149
+ requireId("id", id);
150
+ return this.fetcher.delete(`/deployment/${id}`);
151
+ }
152
+ /**
153
+ * `GET /deployment/{id}` — operationId: `deployment/get`
154
+ *
155
+ * Get Deployment
156
+ */
157
+ deploymentGet(id) {
158
+ requireId("id", id);
159
+ return this.fetcher.get(`/deployment/${id}`);
160
+ }
161
+ /**
162
+ * `GET /deployment` — operationId: `deployment/list`
163
+ *
164
+ * List Deployments
165
+ */
166
+ deploymentList(params) {
167
+ return this.paginator({ url: `/deployment`, params });
168
+ }
169
+ /**
170
+ * `POST /domain` — operationId: `domain/create`
171
+ *
172
+ * Create Domain
173
+ */
174
+ domainCreate(data) {
175
+ return this.fetcher.post(`/domain`, data);
176
+ }
177
+ /**
178
+ * `DELETE /domain/{id}` — operationId: `domain/delete`
179
+ *
180
+ * Delete Domain
181
+ */
182
+ domainDelete(id) {
183
+ requireId("id", id);
184
+ return this.fetcher.delete(`/domain/${id}`);
185
+ }
186
+ /**
187
+ * `GET /domain/fqdn/{fqdn}` — operationId: `domain/fqdn`
188
+ *
189
+ * Get domain by FQDN
190
+ */
191
+ domainFqdn(fqdn) {
192
+ requireId("fqdn", fqdn);
193
+ return this.fetcher.get(`/domain/fqdn/${fqdn}`);
194
+ }
195
+ /**
196
+ * `GET /domain/{id}` — operationId: `domain/get`
197
+ *
198
+ * Get Domain
199
+ */
200
+ domainGet(id) {
201
+ requireId("id", id);
202
+ return this.fetcher.get(`/domain/${id}`);
203
+ }
204
+ /**
205
+ * `GET /domain` — operationId: `domain/list`
206
+ *
207
+ * List Domains
208
+ */
209
+ domainList(params) {
210
+ return this.paginator({ url: `/domain`, params });
211
+ }
212
+ /**
213
+ * `POST /domain/{id}` — operationId: `domain/update`
214
+ *
215
+ * Update Domain
216
+ */
217
+ domainUpdate(id, data) {
218
+ requireId("id", id);
219
+ return this.fetcher.post(`/domain/${id}`, data);
220
+ }
221
+ /**
222
+ * `POST /project` — operationId: `project/create`
223
+ *
224
+ * Create Project
225
+ */
226
+ projectCreate(data) {
227
+ return this.fetcher.post(`/project`, data);
228
+ }
229
+ /**
230
+ * `DELETE /project/{id}` — operationId: `project/delete`
231
+ *
232
+ * Delete Project
233
+ */
234
+ projectDelete(id) {
235
+ requireId("id", id);
236
+ return this.fetcher.delete(`/project/${id}`);
237
+ }
238
+ /**
239
+ * `GET /project/{id}` — operationId: `project/get`
240
+ *
241
+ * Get Project
242
+ */
243
+ projectGet(id) {
244
+ requireId("id", id);
245
+ return this.fetcher.get(`/project/${id}`);
246
+ }
247
+ /**
248
+ * `POST /project/{id}/invite` — operationId: `project/invite`
249
+ *
250
+ * Invite collaborators to project
251
+ */
252
+ projectInvite(id, data) {
253
+ requireId("id", id);
254
+ return this.fetcher.post(`/project/${id}/invite`, data);
255
+ }
256
+ /**
257
+ * `GET /project` — operationId: `project/list`
258
+ *
259
+ * List Projects
260
+ */
261
+ projectList(params) {
262
+ return this.paginator({ url: `/project`, params });
263
+ }
264
+ /**
265
+ * `POST /project/{id}` — operationId: `project/update`
266
+ *
267
+ * Update Project
268
+ */
269
+ projectUpdate(id, data) {
270
+ requireId("id", id);
271
+ return this.fetcher.post(`/project/${id}`, data);
272
+ }
273
+ /**
274
+ * `POST /secret/createbatch` — operationId: `secrets/create_batch`
275
+ *
276
+ * Create secrets in batch
277
+ */
278
+ secretsCreateBatch(data) {
279
+ return this.fetcher.post(`/secret/createbatch`, data);
280
+ }
281
+ /**
282
+ * `DELETE /secret/{secret_id}` — operationId: `secrets/delete`
283
+ *
284
+ * Delete a secret
285
+ */
286
+ secretsDelete(secret_id) {
287
+ requireId("secret_id", secret_id);
288
+ return this.fetcher.delete(`/secret/${secret_id}`);
289
+ }
290
+ /**
291
+ * `GET /secret/{context_id}` — operationId: `secrets/list_app`
292
+ *
293
+ * List all secrets assigned to an App or Profile contexts
294
+ */
295
+ secretsListApp(context_id, params) {
296
+ requireId("context_id", context_id);
297
+ return this.fetcher.request({ method: "GET", url: `/secret/${context_id}`, params });
298
+ }
299
+ /**
300
+ * `POST /secret` — operationId: `secrets/upsert`
301
+ *
302
+ * Upsert a secret
303
+ */
304
+ secretsUpsert(data) {
305
+ return this.fetcher.post(`/secret`, data);
306
+ }
307
+ /**
308
+ * `GET /usage/summary` — operationId: `usage/summary`
309
+ *
310
+ * Get team usage summary for the current billing period
311
+ */
312
+ usageSummary() {
313
+ return this.fetcher.get(`/usage/summary`);
314
+ }
315
+ }
@@ -0,0 +1,2 @@
1
+ export declare const requireId: (name: string, value: unknown) => string;
2
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,SAAS,SAAU,MAAM,SAAS,OAAO,KAAG,MASxD,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { FaableApiError } from "@faable/sdk-base";
2
+ // Guard required path params (ids) before interpolating them into a URL.
3
+ // Without this, a `null`/`undefined` id silently builds e.g. `/app/null` and
4
+ // the server rejects it with a confusing 400 — failing fast here gives the
5
+ // caller a clear error. Used by the auto-generated client methods.
6
+ export const requireId = (name, value) => {
7
+ if (typeof value !== "string" || value.trim() === "") {
8
+ throw new FaableApiError(`FaableApiError: ${name} is required and must be a non-empty string (received ${JSON.stringify(value)})`);
9
+ }
10
+ return value;
11
+ };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export * from "./FaableDeployApi.js";
2
- export * from "./api/api-types.js";
2
+ export * from "./api/generated-client.js";
3
+ export * from "./api/custom-types.js";
3
4
  export * from "./api/types.js";
4
5
  export * from "@faable/sdk-base";
5
- import * as types from "./api/api-types.js";
6
+ import * as types from "./api/custom-types.js";
6
7
  export { types };
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAM/B,cAAc,kBAAkB,CAAC;AAMjC,OAAO,KAAK,KAAK,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAG1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAM/B,cAAc,kBAAkB,CAAC;AAMjC,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  export * from "./FaableDeployApi.js";
2
- export * from "./api/api-types.js";
2
+ export * from "./api/generated-client.js";
3
+ // custom-types re-exports the auto-generated api-types plus the hand-maintained
4
+ // extras (friendlier event names, the AppTraffic interface).
5
+ export * from "./api/custom-types.js";
3
6
  export * from "./api/types.js";
4
7
  // Re-export the @faable/sdk-base public surface (auth strategies like
5
8
  // `authClientCredentials` / `authApikey`, `ApiParams`, `FaableApiError`, …) so
@@ -10,5 +13,5 @@ export * from "@faable/sdk-base";
10
13
  // as a namespace — `import { types } from "@faable/deploy-sdk"` then
11
14
  // `types.App` / `types.Deployment`. Kept alongside the flat re-exports above so
12
15
  // both import styles resolve. Dropping it silently broke `import { types }`.
13
- import * as types from "./api/api-types.js";
16
+ import * as types from "./api/custom-types.js";
14
17
  export { types };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@faable/deploy-sdk",
3
3
  "description": "Codebase for SDK building",
4
- "version": "2.1.0",
4
+ "version": "2.2.0",
5
5
  "author": "Marc Pomar <marc@faable.com>",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -27,9 +27,11 @@
27
27
  "typescript": "^5.6.2"
28
28
  },
29
29
  "scripts": {
30
- "generate-types": "openapi-typescript https://api.faable.com/docs/json -o src/api/types.ts",
30
+ "fetch-spec": "node scripts/fetch-spec.mjs",
31
+ "generate-types": "openapi-typescript ${DEPLOY_OPENAPI:-spec/openapi.json} -o src/api/types.ts",
32
+ "gentypes": "npm run fetch-spec && npm run generate-types && node scripts/gen-client.mjs",
31
33
  "clean": "rimraf dist",
32
- "prebuild": "npm run clean && npm run generate-types",
34
+ "prebuild": "npm run clean && npm run gentypes",
33
35
  "build": "tsc",
34
36
  "test": "ava",
35
37
  "release": "semantic-release"
@@ -0,0 +1,24 @@
1
+ // Downloads the live OpenAPI spec to spec/openapi.json so that BOTH
2
+ // openapi-typescript (types.ts) and gen-client.mjs (client + api-types) read the
3
+ // exact same snapshot. Fetching the URL independently from each tool risks a
4
+ // version skew if the API changes between calls.
5
+
6
+ import { writeFileSync, mkdirSync } from "node:fs";
7
+ import { fileURLToPath } from "node:url";
8
+ import { dirname, resolve } from "node:path";
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const ROOT = resolve(__dirname, "..");
12
+
13
+ const URL =
14
+ process.env.DEPLOY_OPENAPI_URL || "https://api.faable.com/docs/json";
15
+ const OUT = resolve(ROOT, "spec/openapi.json");
16
+
17
+ const res = await fetch(URL);
18
+ if (!res.ok) {
19
+ throw new Error(`fetch-spec: ${URL} returned ${res.status}`);
20
+ }
21
+ const spec = await res.json();
22
+ mkdirSync(dirname(OUT), { recursive: true });
23
+ writeFileSync(OUT, JSON.stringify(spec, null, 2));
24
+ console.warn(`fetch-spec: ${URL} → ${OUT.replace(ROOT + "/", "")}`);
@@ -0,0 +1,241 @@
1
+ // Generates `src/api/generated-client.ts` + `src/api/api-types.ts` from the
2
+ // OpenAPI spec. Ported from auth-sdk's generator (kept in sync by hand).
3
+ //
4
+ // Why: the SDK's typed methods (appGet, deploymentList, …) used to be
5
+ // hand-written and always lagged the API. The TYPES are already generated by
6
+ // openapi-typescript; this does the same for the METHODS, so the SDK covers the
7
+ // whole deploy API for free and stays in sync on every build.
8
+ //
9
+ // Scope: every secured operation (all of them carry the same
10
+ // apikey/faable_cli/faable_machine/faable_user scheme), MINUS internal
11
+ // resources in EXCLUDE_RESOURCES and the unsecured github/webhook/oidc
12
+ // endpoints. Reads the spec from a LOCAL file (downloaded by fetch-spec.mjs) so
13
+ // types.ts and this client are generated from the exact same snapshot.
14
+ //
15
+ // Run via `npm run gentypes`. Output is committed and reviewed in diff like
16
+ // `types.ts`. Do not edit the output by hand.
17
+
18
+ import { readFileSync, writeFileSync } from "node:fs";
19
+ import { fileURLToPath } from "node:url";
20
+ import { dirname, resolve } from "node:path";
21
+
22
+ const __dirname = dirname(fileURLToPath(import.meta.url));
23
+ const ROOT = resolve(__dirname, "..");
24
+
25
+ const SPEC = process.env.DEPLOY_OPENAPI || resolve(ROOT, "spec/openapi.json");
26
+ const OUT = resolve(ROOT, "src/api/generated-client.ts");
27
+ const OUT_TYPES = resolve(ROOT, "src/api/api-types.ts");
28
+
29
+ const spec = JSON.parse(readFileSync(SPEC, "utf8"));
30
+
31
+ // operationId (`resource/action`, possibly snake/kebab in the action) →
32
+ // camelCase. `app/list`→appList, `app/get_deploy_workflow`→appGetDeployWorkflow,
33
+ // `secrets/list_app`→secretsListApp.
34
+ const toMethodName = (operationId) =>
35
+ operationId
36
+ .split(/[/_-]/)
37
+ .filter(Boolean)
38
+ .map((seg, i) =>
39
+ i === 0
40
+ ? seg[0].toLowerCase() + seg.slice(1)
41
+ : seg[0].toUpperCase() + seg.slice(1),
42
+ )
43
+ .join("");
44
+
45
+ // `/app/{id}/traffic` → ["id"], in order of appearance.
46
+ const pathParamNames = (path) =>
47
+ [...path.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]);
48
+
49
+ // `/app/{id}` → "/app/${id}" (template-literal body).
50
+ const toUrlTemplate = (path) =>
51
+ path.replace(/\{([^}]+)\}/g, (_, name) => "${" + name + "}");
52
+
53
+ // Include every operation that requires auth (all deploy ops share one scheme)
54
+ // and isn't marked internal. Two exclusion layers:
55
+ // 1. no `security` → infra (github setup/webhook, oidc token exchange)
56
+ // 2. `x-internal: true` → explicitly marked private in the API
57
+ const isIncluded = (op) => {
58
+ if (!Array.isArray(op.security) || op.security.length === 0) return false;
59
+ return op["x-internal"] !== true;
60
+ };
61
+
62
+ const successSchema = (op) => {
63
+ const responses = op.responses || {};
64
+ const res = responses["200"] || responses["201"];
65
+ return res?.content?.["application/json"]?.schema;
66
+ };
67
+
68
+ // A paginated list response is `{ next, results }` (see buildPaginator in
69
+ // sdk-base). Detected structurally so it tracks the server, not the op name.
70
+ const isPaginated = (op) => {
71
+ const schema = successSchema(op);
72
+ const req = schema?.required;
73
+ return Array.isArray(req) && req.includes("next") && req.includes("results");
74
+ };
75
+
76
+ const escape = (s) => (s || "").replace(/\*\//g, "*\\/").replace(/\r?\n/g, " ");
77
+
78
+ // Collect + sort operations by operationId for a stable, reviewable diff.
79
+ const operations = [];
80
+ for (const [path, methods] of Object.entries(spec.paths || {})) {
81
+ for (const [httpMethod, op] of Object.entries(methods)) {
82
+ if (!op || typeof op !== "object" || !op.operationId) continue;
83
+ if (!["get", "post", "delete"].includes(httpMethod)) continue;
84
+ if (!isIncluded(op)) continue;
85
+ operations.push({ path, httpMethod, op });
86
+ }
87
+ }
88
+ operations.sort((a, b) => a.op.operationId.localeCompare(b.op.operationId));
89
+
90
+ const emitMethod = ({ path, httpMethod, op }) => {
91
+ const id = op.operationId;
92
+ const name = toMethodName(id);
93
+ const pathParams = pathParamNames(path);
94
+ const queryParams = (op.parameters || []).filter((p) => p.in === "query");
95
+ const hasBody = !!op.requestBody;
96
+ const paginated = isPaginated(op);
97
+ const url = "`" + toUrlTemplate(path) + "`";
98
+
99
+ const hasQuery = queryParams.length > 0;
100
+ const args = pathParams.map((p) => `${p}: string`);
101
+ if (hasBody) args.push(`data: OpBody<"${id}">`);
102
+
103
+ // fetcher.post takes a strict FetcherConfig (string-only params), so a POST
104
+ // carrying query params needs extra handling. None exist today; fail loud if
105
+ // that changes rather than silently dropping the query.
106
+ if (httpMethod === "post" && hasQuery) {
107
+ throw new Error(
108
+ `gen-client: POST with query params not supported yet (${id}). ` +
109
+ "Extend emitMethod to thread query params through fetcher.post's config.",
110
+ );
111
+ }
112
+
113
+ let body;
114
+ if (paginated) {
115
+ args.push(`params?: Omit<OpQuery<"${id}">, "cursor" | "next">`);
116
+ // The paginator's request params are untyped (`any`), so array/number/enum
117
+ // query values pass straight through.
118
+ body = `return this.paginator<OpItem<"${id}">>({ url: ${url}, params });`;
119
+ } else if (httpMethod === "post") {
120
+ // fetcher.post rejects a falsy body ("empty body"), so bodyless POSTs send
121
+ // an empty object.
122
+ const data = hasBody ? "data" : "{}";
123
+ body = `return this.fetcher.post<OpResult<"${id}">>(${url}, ${data});`;
124
+ } else if (hasQuery) {
125
+ // GET/DELETE with query params route through fetcher.request: its `params`
126
+ // is untyped, sidestepping FetcherConfig's string-only constraint, and the
127
+ // GET path still goes through the ETag cache.
128
+ args.push(`params?: OpQuery<"${id}">`);
129
+ const method = httpMethod.toUpperCase();
130
+ body = `return this.fetcher.request<OpResult<"${id}">>({ method: "${method}", url: ${url}, params });`;
131
+ } else if (httpMethod === "delete") {
132
+ body = `return this.fetcher.delete<OpResult<"${id}">>(${url});`;
133
+ } else {
134
+ body = `return this.fetcher.get<OpResult<"${id}">>(${url});`;
135
+ }
136
+
137
+ const guards = pathParams.map((p) => ` requireId("${p}", ${p});`);
138
+ const doc = op.summary || op.description;
139
+ const jsdoc = [
140
+ " /**",
141
+ ` * \`${httpMethod.toUpperCase()} ${path}\` — operationId: \`${id}\``,
142
+ ...(doc ? [` *`, ` * ${escape(doc)}`] : []),
143
+ " */",
144
+ ].join("\n");
145
+
146
+ return [
147
+ jsdoc,
148
+ ` ${name}(${args.join(", ")}) {`,
149
+ ...guards,
150
+ ` ${body}`,
151
+ " }",
152
+ ].join("\n");
153
+ };
154
+
155
+ const header = `// AUTO-GENERATED by scripts/gen-client.mjs — do not edit by hand.
156
+ // Regenerated from the OpenAPI spec on every build (npm run gentypes).
157
+ // Source of truth: the deploy API's operationIds + security.
158
+
159
+ import type { operations } from "./types.js";
160
+ import { FaableApi } from "@faable/sdk-base";
161
+ import { requireId } from "../helpers.js";
162
+
163
+ // JSON body of an operation's request (typed from the generated \`operations\`).
164
+ type OpBody<K extends keyof operations> = operations[K] extends {
165
+ requestBody: { content: { "application/json": infer B } };
166
+ }
167
+ ? B
168
+ : never;
169
+
170
+ // JSON body of an operation's 2xx response.
171
+ type Content2xx<R> = R extends {
172
+ 200: { content: { "application/json": infer T } };
173
+ }
174
+ ? T
175
+ : R extends { 201: { content: { "application/json": infer T } } }
176
+ ? T
177
+ : void;
178
+
179
+ type OpResult<K extends keyof operations> = operations[K] extends {
180
+ responses: infer R;
181
+ }
182
+ ? Content2xx<R>
183
+ : void;
184
+
185
+ // Item type of a paginated (\`{ next, results }\`) list response.
186
+ type OpItem<K extends keyof operations> =
187
+ OpResult<K> extends { results: (infer I)[] } ? I : never;
188
+
189
+ // Query parameters of an operation.
190
+ type OpQuery<K extends keyof operations> = operations[K] extends {
191
+ parameters: { query?: infer Q };
192
+ }
193
+ ? NonNullable<Q>
194
+ : Record<string, never>;
195
+ `;
196
+
197
+ const classBody = operations.map(emitMethod).join("\n\n");
198
+
199
+ const out = `${header}
200
+ /**
201
+ * Auto-generated deploy-API methods, one per secured operation in the OpenAPI
202
+ * spec. \`DeployApi\` extends this and adds the constructor, custom-logic
203
+ * helpers, and deprecated aliases.
204
+ */
205
+ export abstract class GeneratedFaableDeployApi extends FaableApi {
206
+ ${classBody}
207
+ }
208
+ `;
209
+
210
+ writeFileSync(OUT, out);
211
+ console.warn(
212
+ `gen-client: wrote ${operations.length} methods → ${OUT.replace(ROOT + "/", "")}`,
213
+ );
214
+
215
+ // Simple-named aliases for every component schema (App, Deployment, Secret, …),
216
+ // so consumers import `App` instead of `components["schemas"]["App"]`. One alias
217
+ // per schema, sorted for a stable diff. Hand-maintained extras (friendlier event
218
+ // names, the AppTraffic interface) live in custom-types.ts, which re-exports
219
+ // this file.
220
+ const schemaNames = Object.keys(spec.components?.schemas || {})
221
+ .filter((name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name))
222
+ .sort();
223
+
224
+ const typeAliases = schemaNames
225
+ .map((name) => `export type ${name} = components["schemas"]["${name}"];`)
226
+ .join("\n");
227
+
228
+ const typesOut = `// AUTO-GENERATED by scripts/gen-client.mjs — do not edit by hand.
229
+ // Regenerated from the OpenAPI spec on every build (npm run gentypes).
230
+ // One simple-named alias per schema in \`components["schemas"]\`.
231
+ // Hand-maintained extras live in custom-types.ts.
232
+
233
+ import type { components } from "./types.js";
234
+
235
+ ${typeAliases}
236
+ `;
237
+
238
+ writeFileSync(OUT_TYPES, typesOut);
239
+ console.warn(
240
+ `gen-client: wrote ${schemaNames.length} type aliases → ${OUT_TYPES.replace(ROOT + "/", "")}`,
241
+ );