@gusnips/sdkgen 0.1.0 → 0.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.
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # @gusnips/sdkgen
2
2
 
3
- The parts of a script that writes a TypeScript SDK for your API. You keep the script, because the
4
- methods it writes are yours. This package holds the parts that are easy to get wrong.
3
+ The parts of a script that writes a TypeScript SDK for your API. You keep the script: it picks the
4
+ names, the doc text and anything written by hand. This package writes the rest, including the
5
+ parts that are easy to get wrong.
5
6
 
6
7
  ```bash
7
8
  bun add -d @gusnips/sdkgen prettier
@@ -39,6 +40,9 @@ fieldsOf(inputJsonSchema(z.object({ to: z.string().describe("Who gets it.") })))
39
40
  | `camelCase`, `pascalCase` | `send_message` gives `sendMessage` and `SendMessage`. |
40
41
  | `inputJsonSchema` | Turns a zod schema into JSON Schema. JSON Schema passes through as it is. |
41
42
  | `writeGenerated` | Formats and writes the files, or lists the ones that are out of date. |
43
+ | `sdkMethods` | Writes one SDK method per operation, and the params type each one takes. |
44
+ | `transportSource` | Returns the code every method runs on: the request, the retries, the error. |
45
+ | `retrySource` | Returns the rule for when a failed call is worth another try. |
42
46
 
43
47
  ## Copy your API's types
44
48
 
@@ -76,6 +80,137 @@ output drops the comments. It finds `export interface`, `export type`, `export c
76
80
  If a real type has a name that short, the SDK will not compile, so you find out.
77
81
  - A quote inside a regex literal, like `/"/`, is read as the start of a string.
78
82
 
83
+ ## Write the methods
84
+
85
+ Give each operation an `sdk` field that says what its method is called and what it returns. An
86
+ operation without one gets no method.
87
+
88
+ ```ts
89
+ import { sdkMethods } from "@gusnips/sdkgen";
90
+
91
+ const { members } = sdkMethods([
92
+ {
93
+ name: "health",
94
+ method: "get",
95
+ path: "/health",
96
+ summary: "Check the API is up.",
97
+ sdk: { method: "health", returns: "HealthDto" },
98
+ },
99
+ ]);
100
+ ```
101
+
102
+ `members` is the method as text, ready to go inside a class:
103
+
104
+ ```ts
105
+ /**
106
+ * Check the API is up.
107
+ */
108
+ health(opts?: RequestOptions): Promise<HealthDto> {
109
+ return this.request({ method: "GET", path: "/health" }, undefined, opts);
110
+ }
111
+ ```
112
+
113
+ The operations are the same list you hand `buildOpenApi` from `@gusnips/server`, so you write them
114
+ once. `sdk.method` is a name like `sendMessage`, or `numbers.pair` to put the method in a `numbers`
115
+ group. `sdk.returns` is the type `data` holds: `MessageDto`, `NumberDto[]`, or `void` for a 204.
116
+ From `@gusnips/server` 0.8.24, `buildOpenApi` checks the `sdk` field and writes it into the
117
+ reference as `x-sdk`.
118
+
119
+ Each method calls `this.request(spec, params, opts)`. Your class declares it and sends the call
120
+ through the transport, below. `sdkMethods` also returns:
121
+
122
+ - `params`: an `export interface …Params` for each method that takes arguments, written from the
123
+ operation's `input` schema. A path slot is filled from the field of its name, or the one
124
+ `params` maps to it.
125
+ - `paramTypes` and `returnTypes`: the names your file has to import.
126
+
127
+ Four options:
128
+
129
+ - `namespaces`: the groups, in the order the client lists them. A group missing from the list is an
130
+ error, so a typo cannot start a new one.
131
+ - `doc(op)`: the method's doc comment, one string per paragraph. Default: the summary, then the
132
+ description.
133
+ - `specExtra(op)`: more fields on the spec, for your own `request` to read.
134
+ - `inject`: methods you write by hand, such as one that polls a job. They go beside the generated
135
+ ones and must not take one of their names.
136
+
137
+ It stops with an error that names the operation when a method name has more than one dot, two
138
+ methods want one name, a status with no body (204, 205, 304) returns something, or a path slot is
139
+ filled by a field the caller may leave out.
140
+
141
+ ## Send the calls
142
+
143
+ Two more files go into the SDK. `transportSource()` is the code that sends each call: it fills the
144
+ path, tries again when that is safe, and turns a failure into your SDK's own error.
145
+ `retrySource()` is the rule it asks, from `@gusnips/http`. Both are plain TypeScript that import
146
+ nothing else, so the SDK installs nothing.
147
+
148
+ ```ts
149
+ import { retrySource, transportSource } from "@gusnips/sdkgen";
150
+
151
+ const files = {
152
+ "packages/sdk/src/generated/retry.ts": retrySource(),
153
+ "packages/sdk/src/generated/transport.ts": transportSource(),
154
+ };
155
+ ```
156
+
157
+ Your client hands its settings to `send`, which returns `{ data, meta }`:
158
+
159
+ ```ts
160
+ import { GeneratedOperations } from "./generated/operations.ts";
161
+ import {
162
+ send,
163
+ type RequestOptions,
164
+ type RequestSpec,
165
+ type Transport,
166
+ } from "./generated/transport.ts";
167
+
168
+ export class Example extends GeneratedOperations {
169
+ private readonly transport: Transport;
170
+
171
+ constructor(apiKey: string) {
172
+ super();
173
+ this.transport = {
174
+ baseUrl: "https://api.example.com/v1",
175
+ headers: { authorization: `Bearer ${apiKey}` },
176
+ error: (failure) => new ExampleError(failure),
177
+ };
178
+ }
179
+
180
+ protected async request<T>(spec: RequestSpec, params?: object, opts?: RequestOptions) {
181
+ return (await send<T>(this.transport, spec, params, opts)).data;
182
+ }
183
+ }
184
+ ```
185
+
186
+ | Setting | Default | What it does |
187
+ | ------------------------- | --------- | --------------------------------------------------------------------------- |
188
+ | `baseUrl` | required | Where the API lives, with its base path. |
189
+ | `error(failure)` | required | Builds your SDK's error. The transport throws what it returns. |
190
+ | `headers` | none | Sent on every call. |
191
+ | `fetch` | global | The fetch to call, such as a fake one in tests. |
192
+ | `timeoutMs(spec, params)` | 30,000 ms | How long one try waits for an answer. A call's `opts.timeoutMs` wins. |
193
+ | `maxRetries` | 2 | Extra tries after a failure worth repeating. |
194
+ | `durableCodes` | none | Error codes that waiting does not fix, such as a spent monthly quota. |
195
+ | `mintKeys` | false | Makes up an idempotency key for a call that takes one, so it can try again. |
196
+
197
+ `failure` has the status (0 when no answer came back), the API's `error`, the `Retry-After` wait,
198
+ the request id, and the idempotency key the call went out with. A call that may have run can be
199
+ sent again with that key, and the API answers from the first run.
200
+
201
+ A failed call is tried again:
202
+
203
+ - **After a 408, 425 or 429**, whatever it is. Those say the API did not run it.
204
+ - **After no answer, a 5xx, or a 409 that says when to come back**, only if running it twice is
205
+ safe. A GET is. A write is when its operation reads an `Idempotency-Key` and the call has one, or
206
+ when its `sdk.repeatable` is true. A write without a key is not sent twice, because it may
207
+ already have run.
208
+ - **Never** when the answer says waiting will not help: a code in `durableCodes`,
209
+ `details.retryAfterSecs: null`, or a wait longer than 10 seconds.
210
+
211
+ It waits what the `Retry-After` header says, in seconds or as a date, then what
212
+ `details.retryAfterSecs` says. With neither, it waits about 1 second, then 2.
213
+
79
214
  ## Keep the SDK current
80
215
 
81
216
  ```ts
@@ -108,6 +243,10 @@ error and not "out of date".
108
243
  - **A `*/` in a description is broken up**, so it cannot end the doc comment early.
109
244
  - **A zod schema needs zod 4.4 or later.** Older versions cannot describe themselves as JSON
110
245
  Schema, and the error says so.
246
+ - **A call with a missing path value throws a TypeError** before anything is sent, rather than
247
+ calling `/numbers//pair`.
248
+ - **An idempotency key on a call that takes none throws.** The API would ignore it, so it could not
249
+ stop the call running twice.
111
250
 
112
251
  ## Why it exists
113
252
 
@@ -121,7 +260,7 @@ had fixed something the others had not, and every one still had these bugs:
121
260
  - `export interface Empty {}` on one line swallowed the declaration after it.
122
261
  - A comment written `/*/` ended on the character that opened it.
123
262
 
124
- This package is the merge, with each fix pinned by a test. Two of those generators were rewritten
125
- on it and wrote every file byte for byte as before, 268 and 281 lines shorter.
263
+ This package is the merge, with each fix pinned by a test. All five generators were then rewritten
264
+ on it and wrote every file byte for byte as before, each 267 to 281 lines shorter.
126
265
 
127
266
  MIT
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { liftContract, type LiftOptions } from "./contract.ts";
2
+ export { retrySource, sdkMethods, transportSource, type InjectedMember, type SdkMethods, type SdkMethodsOptions, type SdkOperation, type SdkPlacement, } from "./methods.ts";
2
3
  export { camelCase, docComment, fieldsOf, inputJsonSchema, paramsInterface, pascalCase, typeNames, typeOf, wrapLines, type JsonObject, type SchemaSource, type StandardJsonSchema, } from "./types.ts";
3
4
  export { writeGenerated, type GeneratedFiles, type WriteOptions, type WriteResult, } from "./write.ts";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,SAAS,EACT,UAAU,EACV,QAAQ,EACR,eAAe,EACf,eAAe,EACf,UAAU,EACV,SAAS,EACT,MAAM,EACN,SAAS,EACT,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,WAAW,EACX,UAAU,EACV,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,UAAU,EACV,QAAQ,EACR,eAAe,EACf,eAAe,EACf,UAAU,EACV,SAAS,EACT,MAAM,EACN,SAAS,EACT,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { liftContract } from "./contract.js";
2
+ export { retrySource, sdkMethods, transportSource, } from "./methods.js";
2
3
  export { camelCase, docComment, fieldsOf, inputJsonSchema, paramsInterface, pascalCase, typeNames, typeOf, wrapLines, } from "./types.js";
3
4
  export { writeGenerated, } from "./write.js";
4
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAoB,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,SAAS,EACT,UAAU,EACV,QAAQ,EACR,eAAe,EACf,eAAe,EACf,UAAU,EACV,SAAS,EACT,MAAM,EACN,SAAS,GAIV,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,GAIf,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAoB,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,WAAW,EACX,UAAU,EACV,eAAe,GAMhB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,UAAU,EACV,QAAQ,EACR,eAAe,EACf,eAAe,EACf,UAAU,EACV,SAAS,EACT,MAAM,EACN,SAAS,GAIV,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,GAIf,MAAM,YAAY,CAAC"}
@@ -0,0 +1,105 @@
1
+ import { type SchemaSource } from "./types.ts";
2
+ /** Where the generated SDK puts an operation. */
3
+ export interface SdkPlacement {
4
+ /** `sendMessage`, or `numbers.pair` in a namespace. One dot at most; unique. */
5
+ method: string;
6
+ /** What `data` holds, as the SDK names it: `MessageDto`, `NumberDto[]`; `void` for a 204. */
7
+ returns: string;
8
+ /**
9
+ * Safe to run twice with no key: a read sent as a POST, or a write the server dedupes on its
10
+ * own. Default: a GET only.
11
+ */
12
+ repeatable?: boolean;
13
+ }
14
+ /**
15
+ * The part of an operation the generator reads. It is a structural subset of `@gusnips/server`'s
16
+ * `OpenApiOperation`, so the list an API hands `buildOpenApi` fits here unchanged.
17
+ */
18
+ export interface SdkOperation {
19
+ name: string;
20
+ method: "get" | "post" | "put" | "patch" | "delete";
21
+ /** The route as the router spells it: `/numbers/:id`. */
22
+ path: string;
23
+ summary: string;
24
+ description?: string;
25
+ /** Everything the operation reads. It becomes the method's params interface. */
26
+ input?: SchemaSource;
27
+ /** Input field → path slot, where the two names differ: `{ numberId: "id" }`. */
28
+ params?: Readonly<Record<string, string>>;
29
+ /** Fields the route fills in itself. The SDK leaves them out. */
30
+ fixed?: readonly string[];
31
+ /** Request headers the operation reads. An `Idempotency-Key` here makes the call keyed. */
32
+ headers?: readonly {
33
+ readonly name: string;
34
+ }[];
35
+ /** The success status. A 204, 205 or 304 has no body, so it returns `void`. */
36
+ status?: number;
37
+ extensions?: Readonly<Record<`x-${string}`, unknown>>;
38
+ /** Where the generated SDK puts this operation. Absent: no method. */
39
+ sdk?: SdkPlacement;
40
+ }
41
+ /** A member written by hand, placed beside the generated ones and held to the same names. */
42
+ export interface InjectedMember {
43
+ /** `jobs.wait`, or `wait` at the top level. */
44
+ method: string;
45
+ /** Its parameters and return type: `(jobId: string, opts?: WaitOptions): Promise<JobDto>`. */
46
+ signature: string;
47
+ /** What it returns: `this.waitForJob(jobId, opts)`. */
48
+ call: string;
49
+ /** Its doc comment, one string per paragraph. */
50
+ doc?: readonly string[];
51
+ }
52
+ export interface SdkMethodsOptions {
53
+ /**
54
+ * The namespaces, in the order the client lists them. Given, a namespace not in it throws, so a
55
+ * typo cannot start a new one. Default: the order they first appear.
56
+ */
57
+ namespaces?: readonly string[];
58
+ /**
59
+ * A method's doc comment, one string per paragraph; `undefined` ones are skipped. Default: the
60
+ * summary, then the description.
61
+ */
62
+ doc?: (op: SdkOperation) => readonly (string | undefined)[];
63
+ /** Fields to add to the spec an operation hands `request`, such as its proxy block's fields. */
64
+ specExtra?: (op: SdkOperation) => Readonly<Record<string, unknown>> | undefined;
65
+ /** Members written by hand, each after the generated ones in its place. */
66
+ inject?: readonly InjectedMember[];
67
+ }
68
+ export interface SdkMethods {
69
+ /** An `export interface …Params` for each method that takes arguments: the body of `params.ts`. */
70
+ params: string;
71
+ /**
72
+ * The class members: top-level methods first, then one object per namespace. Each calls
73
+ * `this.request(spec, params, opts)`, which the class declares and the SDK implements.
74
+ */
75
+ members: string;
76
+ /** The params interfaces the members name, sorted, for their import line. */
77
+ paramTypes: string[];
78
+ /** The declared types the `returns` name, sorted, for their import from the contract. */
79
+ returnTypes: string[];
80
+ }
81
+ /**
82
+ * The SDK's methods for every operation that names an `sdk` place, with the params interfaces they
83
+ * take.
84
+ *
85
+ * ```ts
86
+ * const { params, members, paramTypes, returnTypes } = sdkMethods(operations);
87
+ * const file = `export abstract class GeneratedOperations {
88
+ * protected abstract request<T>(spec: RequestSpec, params?: object, opts?: RequestOptions): Promise<T>;
89
+ * ${members}}\n`;
90
+ * ```
91
+ */
92
+ export declare function sdkMethods(operations: readonly SdkOperation[], options?: SdkMethodsOptions): SdkMethods;
93
+ /**
94
+ * `@gusnips/http`'s retry rule, its `src/retry.ts` whole, for an SDK to carry as
95
+ * `generated/retry.ts`. It is read from this package's own dependency, never the adopter's, so
96
+ * every SDK built with one sdkgen carries one rule. When an sdkgen release moves the rule, each
97
+ * SDK's `--check` fails, and that is the signal to release the SDK.
98
+ */
99
+ export declare function retrySource(): string;
100
+ /**
101
+ * The transport every generated method runs on, for an SDK to carry as `generated/transport.ts`
102
+ * beside `retry.ts`: this package's own `src/transport.ts`, importing the rule from `./retry.ts`.
103
+ */
104
+ export declare function transportSource(): string;
105
+ //# sourceMappingURL=methods.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"methods.d.ts","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"AAmBA,OAAO,EAOL,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAEpB,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,iFAAiF;IACjF,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1C,iEAAiE;IACjE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,2FAA2F;IAC3F,OAAO,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/C,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,sEAAsE;IACtE,GAAG,CAAC,EAAE,YAAY,CAAC;CACpB;AAED,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;OAGG;IACH,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IAC5D,gGAAgG;IAChG,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAChF,2EAA2E;IAC3E,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,mGAAmG;IACnG,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,yFAAyF;IACzF,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAuBD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACxB,UAAU,EAAE,SAAS,YAAY,EAAE,EACnC,OAAO,GAAE,iBAAsB,GAC9B,UAAU,CA0EZ;AAgID;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAGpC;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAGxC"}
@@ -0,0 +1,237 @@
1
+ /**
2
+ * The methods an SDK exposes, written from the operation list the API already mounts, and the two
3
+ * files they run on.
4
+ *
5
+ * Five generators each wrote this loop, and what they shared is here: one method per operation,
6
+ * grouped by namespace, taking the params interface its input schema gives and returning the type
7
+ * the operation says `data` holds; a path whose slots carry the argument's own name, so the
8
+ * transport can fill them; and the checks that stop a wrong SDK at generation rather than in the
9
+ * hands of whoever installs it. What differs by product comes in through options: the doc
10
+ * comment's paragraphs, what the spec carries beyond the route, and members written by hand.
11
+ *
12
+ * It reads the operation list in-process, never the published OpenAPI document. The document has
13
+ * lost the type names, the argument renames and the typed query params, and a type JSON cannot
14
+ * carry reaches it as `{}`, where the schema writer here would have stopped.
15
+ */
16
+ import { readFileSync } from "node:fs";
17
+ import { createRequire } from "node:module";
18
+ import { dirname, join } from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+ import { inputJsonSchema, paramsInterface, pascalCase, typeNames, wrapLines, } from "./types.js";
21
+ /** Success statuses with no body (RFC 9110), the same three the server's reference documents so. */
22
+ const NO_BODY = new Set([204, 205, 304]);
23
+ /** Class members a method must not be named: the seam every method calls, and the constructor. */
24
+ const RESERVED = new Set(["request", "constructor"]);
25
+ /** The spec fields the generator writes, which `specExtra` must not overwrite. */
26
+ const SPEC_FIELDS = new Set(["method", "path", "keyed", "repeatable"]);
27
+ const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
28
+ /**
29
+ * The SDK's methods for every operation that names an `sdk` place, with the params interfaces they
30
+ * take.
31
+ *
32
+ * ```ts
33
+ * const { params, members, paramTypes, returnTypes } = sdkMethods(operations);
34
+ * const file = `export abstract class GeneratedOperations {
35
+ * protected abstract request<T>(spec: RequestSpec, params?: object, opts?: RequestOptions): Promise<T>;
36
+ * ${members}}\n`;
37
+ * ```
38
+ */
39
+ export function sdkMethods(operations, options = {}) {
40
+ const { doc = (op) => [op.summary, op.description], specExtra, inject = [] } = options;
41
+ const members = [];
42
+ const params = [];
43
+ const paramTypes = [];
44
+ const returnTypes = new Set();
45
+ for (const op of operations) {
46
+ const sdk = op.sdk;
47
+ if (sdk === undefined)
48
+ continue;
49
+ const where = `${op.method.toUpperCase()} ${op.path} (${op.name})`;
50
+ if (op.status !== undefined && NO_BODY.has(op.status) && sdk.returns !== "void") {
51
+ throw new Error(`${where} answers ${op.status}, which has no body, so \`sdk.returns\` must be "void".`);
52
+ }
53
+ const schema = op.input === undefined ? undefined : schemaOf(op.input, where);
54
+ const fixed = op.fixed ?? [];
55
+ const typeName = `${pascalCase(op.name)}Params`;
56
+ const iface = schema === undefined ? null : paramsInterface(typeName, schema, fixed);
57
+ if (iface !== null) {
58
+ params.push(`/** Arguments for \`${sdk.method}()\`. */\n${iface.source}`);
59
+ paramTypes.push(typeName);
60
+ }
61
+ for (const name of typeNames(sdk.returns))
62
+ returnTypes.add(name);
63
+ const listed = schema?.["required"];
64
+ const required = new Set(Array.isArray(listed)
65
+ ? listed.filter((f) => typeof f === "string" && !fixed.includes(f))
66
+ : []);
67
+ const fields = [
68
+ `method: "${op.method.toUpperCase()}"`,
69
+ `path: ${JSON.stringify(sdkPath(op, required, where))}`,
70
+ ];
71
+ if (op.headers?.some((h) => h.name.toLowerCase() === "idempotency-key"))
72
+ fields.push("keyed: true");
73
+ if (sdk.repeatable !== undefined)
74
+ fields.push(`repeatable: ${sdk.repeatable}`);
75
+ for (const [key, value] of Object.entries(specExtra?.(op) ?? {})) {
76
+ if (SPEC_FIELDS.has(key)) {
77
+ throw new Error(`${where}: \`specExtra\` sets "${key}", which the generator writes itself.`);
78
+ }
79
+ if (value !== undefined)
80
+ fields.push(`${propertyKey(key)}: ${JSON.stringify(value)}`);
81
+ }
82
+ const argument = iface === null ? "" : `params${iface.optional ? "?" : ""}: ${typeName}, `;
83
+ members.push({
84
+ where,
85
+ ...place(sdk.method, where),
86
+ doc: doc(op),
87
+ signature: `(${argument}opts?: RequestOptions): Promise<${sdk.returns}>`,
88
+ call: `this.request({ ${fields.join(", ")} }, ${iface === null ? "undefined" : "params"}, opts)`,
89
+ });
90
+ }
91
+ for (const member of inject) {
92
+ const where = `The hand-written ${member.method}`;
93
+ members.push({
94
+ where,
95
+ ...place(member.method, where),
96
+ doc: member.doc ?? [],
97
+ signature: member.signature,
98
+ call: member.call,
99
+ });
100
+ }
101
+ return {
102
+ params: params.join("\n"),
103
+ members: writeMembers(members, options.namespaces),
104
+ paramTypes: paramTypes.sort(),
105
+ returnTypes: [...returnTypes].sort(),
106
+ };
107
+ }
108
+ /** `numbers.pair` → its namespace and name, each checked to be a name a class member can have. */
109
+ function place(method, where) {
110
+ const parts = method.split(".");
111
+ if (parts.length > 2 || !parts.every((part) => IDENTIFIER.test(part))) {
112
+ throw new Error(`${where}: \`${method}\` is not a method name. Write \`name\`, or \`namespace.name\` with one dot.`);
113
+ }
114
+ const [first = "", second] = parts;
115
+ return second === undefined
116
+ ? { namespace: undefined, name: first }
117
+ : { namespace: first, name: second };
118
+ }
119
+ /** Top-level methods first, then one `readonly namespace = { … }` per namespace. */
120
+ function writeMembers(members, order) {
121
+ const seen = new Map();
122
+ const topLevel = new Set();
123
+ const namespaces = [];
124
+ for (const { where, namespace, name } of members) {
125
+ const full = namespace === undefined ? name : `${namespace}.${name}`;
126
+ const first = seen.get(full);
127
+ if (first !== undefined) {
128
+ throw new Error(`Two methods want to be \`${full}\`: ${first}, and ${where}. Rename one.`);
129
+ }
130
+ seen.set(full, where);
131
+ if (namespace === undefined)
132
+ topLevel.add(name);
133
+ else if (!namespaces.includes(namespace))
134
+ namespaces.push(namespace);
135
+ }
136
+ for (const name of [...topLevel, ...namespaces]) {
137
+ if (RESERVED.has(name)) {
138
+ throw new Error(`\`${name}\` is a class member every SDK already has. Pick another name.`);
139
+ }
140
+ if (topLevel.has(name) && namespaces.includes(name)) {
141
+ throw new Error(`\`${name}\` is both a method and a namespace. Rename one.`);
142
+ }
143
+ }
144
+ if (order !== undefined) {
145
+ const unknown = namespaces.find((namespace) => !order.includes(namespace));
146
+ if (unknown !== undefined) {
147
+ throw new Error(`The namespace \`${unknown}\` is not in \`namespaces\`. Add it there, or use one of: ${order.join(", ")}.`);
148
+ }
149
+ }
150
+ let out = "";
151
+ for (const member of members.filter((m) => m.namespace === undefined)) {
152
+ out += `\n${jsdoc(member.doc, " ")} ${member.name}${member.signature} {\n`;
153
+ out += ` return ${member.call};\n }\n`;
154
+ }
155
+ for (const namespace of (order ?? namespaces).filter((ns) => namespaces.includes(ns))) {
156
+ out += `\n readonly ${namespace} = {\n`;
157
+ for (const member of members.filter((m) => m.namespace === namespace)) {
158
+ out += `${jsdoc(member.doc, " ")} ${member.name}: ${member.signature} =>\n`;
159
+ out += ` ${member.call},\n`;
160
+ }
161
+ out += ` };\n`;
162
+ }
163
+ return out;
164
+ }
165
+ /**
166
+ * The route with each slot named for the argument that fills it: `/numbers/:id` with
167
+ * `{ numberId: "id" }` → `/numbers/:numberId`. A slot's argument must be required, or the SDK
168
+ * would let a caller leave out part of the address.
169
+ */
170
+ function sdkPath(op, required, where) {
171
+ // A constraint such as `:id{[0-9]+}` is the router's business, and the SDK drops it.
172
+ const slot = /:(\w+)(\{[^}]*\})?(\?)?/g;
173
+ const slots = new Set([...op.path.matchAll(slot)].map((match) => match[1]));
174
+ const fieldIn = new Map();
175
+ for (const [field, name] of Object.entries(op.params ?? {})) {
176
+ if (!slots.has(name)) {
177
+ throw new Error(`${where}: \`params\` sends ${field} in ":${name}", which the path does not have.`);
178
+ }
179
+ fieldIn.set(name, field);
180
+ }
181
+ return op.path.replace(slot, (_match, name, _re, optional) => {
182
+ if (optional) {
183
+ throw new Error(`${where} has an optional slot ":${name}?". List it as two operations, with and without it.`);
184
+ }
185
+ const field = fieldIn.get(name) ?? name;
186
+ if (!/^\w+$/.test(field)) {
187
+ throw new Error(`${where}: the path's ":${name}" is filled from \`${field}\`, which a path cannot name. Rename the field.`);
188
+ }
189
+ if (!required.has(field)) {
190
+ throw new Error(`${where}: the path's ":${name}" is filled from \`${field}\`, which the input does not require. ` +
191
+ "Make it required, or map the slot to another field with `params`.");
192
+ }
193
+ return `:${field}`;
194
+ });
195
+ }
196
+ /** The input as JSON Schema, with the operation named when it cannot be written. */
197
+ function schemaOf(input, where) {
198
+ try {
199
+ return inputJsonSchema(input);
200
+ }
201
+ catch (err) {
202
+ throw new Error(`${where}: ${err instanceof Error ? err.message : String(err)}`, {
203
+ cause: err,
204
+ });
205
+ }
206
+ }
207
+ /** `/**` over paragraphs, a blank ` *` line between them, each wrapped for its indent. */
208
+ function jsdoc(paragraphs, indent) {
209
+ const blocks = paragraphs
210
+ .map((paragraph) => (paragraph === undefined ? [] : wrapLines(paragraph, indent)))
211
+ .filter((lines) => lines.length > 0);
212
+ if (blocks.length === 0)
213
+ return "";
214
+ const line = `\n${indent} * `;
215
+ return `${indent}/**${line}${blocks.map((lines) => lines.join(line)).join(`\n${indent} *${line}`)}\n${indent} */\n`;
216
+ }
217
+ /** A key that is not an identifier has to be quoted. */
218
+ const propertyKey = (key) => (IDENTIFIER.test(key) ? key : JSON.stringify(key));
219
+ /**
220
+ * `@gusnips/http`'s retry rule, its `src/retry.ts` whole, for an SDK to carry as
221
+ * `generated/retry.ts`. It is read from this package's own dependency, never the adopter's, so
222
+ * every SDK built with one sdkgen carries one rule. When an sdkgen release moves the rule, each
223
+ * SDK's `--check` fails, and that is the signal to release the SDK.
224
+ */
225
+ export function retrySource() {
226
+ const entry = createRequire(import.meta.url).resolve("@gusnips/http/retry");
227
+ return readFileSync(join(dirname(entry), "../src/retry.ts"), "utf8");
228
+ }
229
+ /**
230
+ * The transport every generated method runs on, for an SDK to carry as `generated/transport.ts`
231
+ * beside `retry.ts`: this package's own `src/transport.ts`, importing the rule from `./retry.ts`.
232
+ */
233
+ export function transportSource() {
234
+ const path = fileURLToPath(new URL("../src/transport.ts", import.meta.url));
235
+ return readFileSync(path, "utf8").replace(`from "@gusnips/http/retry";`, `from "./retry.ts";`);
236
+ }
237
+ //# sourceMappingURL=methods.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"methods.js","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
2
+ /** What a generated method tells the transport about its operation. */
3
+ export interface RequestSpec {
4
+ method: HttpMethod;
5
+ /** `/numbers/:numberId/pair`. Each `:name` is filled from the params field of that name. */
6
+ path: string;
7
+ /** The operation reads an `Idempotency-Key`, so the server answers a repeat from the first run. */
8
+ keyed?: boolean;
9
+ /** Safe to run twice with no key, such as a read sent as a POST. Absent: true for a GET only. */
10
+ repeatable?: boolean;
11
+ }
12
+ /** The last argument of every generated method. */
13
+ export interface RequestOptions {
14
+ /**
15
+ * Your own key for a call that takes one, such as a UUID saved with the message you send.
16
+ * Every attempt sends the same key, and so can your own retry after a crash.
17
+ */
18
+ idempotencyKey?: string;
19
+ /** How long one attempt waits for an answer, in milliseconds. Overrides the SDK's. */
20
+ timeoutMs?: number;
21
+ }
22
+ /** The `error` of the envelope, as the answer carried it. */
23
+ export interface EnvelopeError {
24
+ /** What went wrong, as a word a program can check. */
25
+ code: string;
26
+ message: string | undefined;
27
+ messageKey: string | undefined;
28
+ params: Record<string, string | number> | undefined;
29
+ /** As sent. `{ retryAfterSecs: null }` means waiting never helps. */
30
+ details: unknown;
31
+ }
32
+ /** A call that did not work, as `error` gets it to build the SDK's own error. */
33
+ export interface Failure {
34
+ method: HttpMethod;
35
+ /** The path with its values filled in: `/numbers/n_1/pair`. */
36
+ path: string;
37
+ /** The HTTP status, or 0 when no answer came back: offline, a dropped connection, a timeout. */
38
+ status: number;
39
+ /** No answer within `timeoutMs`. The call may still have run. */
40
+ timedOut: boolean;
41
+ timeoutMs: number;
42
+ /** The envelope's `error`, when the answer carried one. */
43
+ error: EnvelopeError | undefined;
44
+ /** An answer that was not the envelope, such as a gateway's HTML page: its first 500 characters. */
45
+ text: string | undefined;
46
+ /** `Retry-After` in seconds, whichever of its two forms it came in. */
47
+ retryAfterSecs: number | undefined;
48
+ /** The `x-request-id` header, for the API's support to find the call. */
49
+ requestId: string | undefined;
50
+ /** The key the call was sent with. Retry with it, and a call that ran answers from its first run. */
51
+ idempotencyKey: string | undefined;
52
+ /** What fetch threw, when no answer came back. */
53
+ cause: unknown;
54
+ }
55
+ /** One SDK's settings. */
56
+ export interface Transport {
57
+ /** Where the API lives, with its base path: `https://api.example.com/v1`. */
58
+ baseUrl: string;
59
+ /** Sent on every call, such as the credential and the SDK's version. */
60
+ headers?: Readonly<Record<string, string>>;
61
+ /** Default: the global `fetch`. */
62
+ fetch?: typeof fetch;
63
+ /** How long one attempt of this call waits for an answer, in milliseconds. Default 30,000. */
64
+ timeoutMs?: (spec: RequestSpec, params: object) => number;
65
+ /** Extra attempts after a failure the retry rule says is worth repeating. Default 2. */
66
+ maxRetries?: number;
67
+ /** Error codes that waiting does not clear, such as a spent monthly quota. */
68
+ durableCodes?: readonly string[];
69
+ /**
70
+ * Make up a key for a call that takes one when the caller sent none, so the call can be retried.
71
+ * Default false: without a key, a write that may have run is not sent again.
72
+ */
73
+ mintKeys?: boolean;
74
+ /** Builds the SDK's own error for a failed call. The transport throws what this returns. */
75
+ error: (failure: Failure) => Error;
76
+ }
77
+ /** A call that worked. `data` is `undefined` for a 204 or any other empty answer. */
78
+ export interface Answer<T, M> {
79
+ data: T;
80
+ meta: M | undefined;
81
+ }
82
+ /**
83
+ * Sends one call, retries it while the rule says so, and returns `{ data, meta }` or throws
84
+ * `transport.error(failure)`. A value the path needs and did not get throws a TypeError before
85
+ * anything is sent.
86
+ */
87
+ export declare function send<T = unknown, M = unknown>(transport: Transport, spec: RequestSpec, params?: object, opts?: RequestOptions): Promise<Answer<T, M>>;
88
+ //# sourceMappingURL=transport.d.ts.map