@gusnips/sdkgen 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.
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/contract.d.ts +40 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +213 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +222 -0
- package/dist/types.js.map +1 -0
- package/dist/write.d.ts +29 -0
- package/dist/write.d.ts.map +1 -0
- package/dist/write.js +52 -0
- package/dist/write.js.map +1 -0
- package/package.json +66 -0
- package/src/contract.test.ts +201 -0
- package/src/contract.ts +249 -0
- package/src/index.ts +21 -0
- package/src/readme.test.ts +24 -0
- package/src/types.test.ts +144 -0
- package/src/types.ts +250 -0
- package/src/write.test.ts +67 -0
- package/src/write.ts +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gustavo Salomé
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @gusnips/sdkgen
|
|
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.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
bun add -d @gusnips/sdkgen prettier
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { typeOf } from "@gusnips/sdkgen";
|
|
12
|
+
|
|
13
|
+
typeOf({ type: ["string", "null"] });
|
|
14
|
+
// → "string | null"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`typeOf` turns a JSON Schema into the TypeScript type it describes. A zod schema works too, through
|
|
18
|
+
`inputJsonSchema`, and each field keeps its description as a doc comment:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
import { fieldsOf, inputJsonSchema } from "@gusnips/sdkgen";
|
|
23
|
+
|
|
24
|
+
fieldsOf(inputJsonSchema(z.object({ to: z.string().describe("Who gets it.") })));
|
|
25
|
+
// → " /** Who gets it. */\n to: string;\n"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## What is in it
|
|
29
|
+
|
|
30
|
+
| Function | What it does |
|
|
31
|
+
| ------------------------- | ---------------------------------------------------------------------------------- |
|
|
32
|
+
| `liftContract` | Copies the types your API answers with into one file, comments included. |
|
|
33
|
+
| `typeOf` | Writes a JSON Schema as a TypeScript type. |
|
|
34
|
+
| `fieldsOf` | Writes an object schema's fields, one per line, each description as a doc comment. |
|
|
35
|
+
| `paramsInterface` | Writes `export interface SendParams { … }`, and says if every field is optional. |
|
|
36
|
+
| `docComment` | Writes `/** … */`, wrapped at 96 columns. |
|
|
37
|
+
| `wrapLines` | Breaks prose into lines for a comment you lay out yourself. |
|
|
38
|
+
| `typeNames` | Lists the type names in an expression: `Page<Job>[]` gives `Page` and `Job`. |
|
|
39
|
+
| `camelCase`, `pascalCase` | `send_message` gives `sendMessage` and `SendMessage`. |
|
|
40
|
+
| `inputJsonSchema` | Turns a zod schema into JSON Schema. JSON Schema passes through as it is. |
|
|
41
|
+
| `writeGenerated` | Formats and writes the files, or lists the ones that are out of date. |
|
|
42
|
+
|
|
43
|
+
## Copy your API's types
|
|
44
|
+
|
|
45
|
+
An SDK is published on its own, so it cannot import your API's code. The types it returns have to
|
|
46
|
+
be copied into it.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { liftContract } from "@gusnips/sdkgen";
|
|
50
|
+
|
|
51
|
+
const contract = liftContract({
|
|
52
|
+
root: repoRoot,
|
|
53
|
+
sources: ["packages/shared/src/http.ts", "packages/shared/src/dto.ts"],
|
|
54
|
+
roots: ["ApiError", "MessageDto"],
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
That returns one file's text. It has each name in `roots` with its doc comment, then every type
|
|
59
|
+
those mention, in the order they appear in `sources`. If a name is declared nowhere, it stops and
|
|
60
|
+
lists the files it searched.
|
|
61
|
+
|
|
62
|
+
Two options change what comes out:
|
|
63
|
+
|
|
64
|
+
- `inlineTuples: true` writes `(typeof STATUSES)[number]` as `"queued" | "sent" | "failed"` and
|
|
65
|
+
leaves the array out. Use it when the SDK has no use for the array. It is off by default, because
|
|
66
|
+
an SDK that re-exports its contract publishes that array, and each member's doc comment goes with
|
|
67
|
+
it.
|
|
68
|
+
- `exportOnlyRoots: true` keeps `export` on the names in `roots` only. The types they mention are
|
|
69
|
+
still written, so the file compiles, but they are not part of the SDK's public names.
|
|
70
|
+
|
|
71
|
+
It reads your files as text, not with the TypeScript compiler, because the compiler's `.d.ts`
|
|
72
|
+
output drops the comments. It finds `export interface`, `export type`, `export const` and
|
|
73
|
+
`export function` at the start of a line. Two limits:
|
|
74
|
+
|
|
75
|
+
- A name of one or two letters, like `T` or `K`, is taken to be a type parameter and not followed.
|
|
76
|
+
If a real type has a name that short, the SDK will not compile, so you find out.
|
|
77
|
+
- A quote inside a regex literal, like `/"/`, is read as the start of a string.
|
|
78
|
+
|
|
79
|
+
## Keep the SDK current
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { writeGenerated } from "@gusnips/sdkgen";
|
|
83
|
+
|
|
84
|
+
const files = { "packages/sdk/src/generated/contract.ts": contract };
|
|
85
|
+
const { stale } = await writeGenerated(files, {
|
|
86
|
+
root: repoRoot,
|
|
87
|
+
check: process.argv.includes("--check"),
|
|
88
|
+
});
|
|
89
|
+
if (stale.length > 0) {
|
|
90
|
+
console.error(`Out of date:\n ${stale.join("\n ")}\nRun \`bun run sdk:gen\` and commit.`);
|
|
91
|
+
process.exitCode = 1;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`files` maps each path to its text. A relative path starts from `root`, and `stale` and `written`
|
|
96
|
+
list paths the way `files` spells them. Each file is formatted with the prettier config for its
|
|
97
|
+
path before it is compared or written, so `prettier --check` and `--check` always agree. With
|
|
98
|
+
`check`, nothing is written. A file it cannot read, for a reason other than not existing, is an
|
|
99
|
+
error and not "out of date".
|
|
100
|
+
|
|
101
|
+
## Rules
|
|
102
|
+
|
|
103
|
+
- **It never guesses a type.** A schema it does not understand, such as `allOf` or a `$ref`, throws
|
|
104
|
+
with the schema in the message. A wrong `unknown` in a published SDK hides the mistake from
|
|
105
|
+
everyone who installs it. It writes `unknown` only where the schema itself allows any value,
|
|
106
|
+
as `{}` does.
|
|
107
|
+
- **A field name that is not an identifier is quoted:** `"content-type"?: string`.
|
|
108
|
+
- **A `*/` in a description is broken up**, so it cannot end the doc comment early.
|
|
109
|
+
- **A zod schema needs zod 4.4 or later.** Older versions cannot describe themselves as JSON
|
|
110
|
+
Schema, and the error says so.
|
|
111
|
+
|
|
112
|
+
## Why it exists
|
|
113
|
+
|
|
114
|
+
Five APIs each wrote their own SDK generator, about 2,500 lines in all. They were not copies. Each
|
|
115
|
+
had fixed something the others had not, and every one still had these bugs:
|
|
116
|
+
|
|
117
|
+
- A backslash at the end of a `//` comment hid the next line, so the declaration under it was never
|
|
118
|
+
found.
|
|
119
|
+
- A string with a backslash before its line break moved every later line by one, so the generator
|
|
120
|
+
copied the wrong text for every declaration after it in the file.
|
|
121
|
+
- `export interface Empty {}` on one line swallowed the declaration after it.
|
|
122
|
+
- A comment written `/*/` ended on the character that opened it.
|
|
123
|
+
|
|
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.
|
|
126
|
+
|
|
127
|
+
MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface LiftOptions {
|
|
2
|
+
/** The directory `sources` are relative to, usually the repo root. */
|
|
3
|
+
root: string;
|
|
4
|
+
/** The files a public type may come from. They are written out in this order. */
|
|
5
|
+
sources: readonly string[];
|
|
6
|
+
/** The names the SDK needs. Everything they mention comes along. */
|
|
7
|
+
roots: readonly string[];
|
|
8
|
+
/**
|
|
9
|
+
* Write `(typeof X)[number]` as its literal union, and leave the array out. Off by default: an SDK
|
|
10
|
+
* that re-exports its contract publishes that array, so dropping it is a breaking change, and
|
|
11
|
+
* each member's doc comment goes with it.
|
|
12
|
+
*/
|
|
13
|
+
inlineTuples?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Keep `export` on the roots only. What they mention is still written, without it, so the SDK's
|
|
16
|
+
* public names are exactly the ones listed. Off by default: everything written is exported.
|
|
17
|
+
*/
|
|
18
|
+
exportOnlyRoots?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The source with every comment and string body blanked, one character for one character and a
|
|
22
|
+
* newline for a newline, so structure can be read off it while the ORIGINAL lines are what gets
|
|
23
|
+
* copied. Without it a `;` inside a sentence ends a declaration early, and every capitalized word
|
|
24
|
+
* in the prose looks like a type. `keepStrings` blanks the comments only, for reading the strings
|
|
25
|
+
* themselves: an apostrophe in a comment is not a quote.
|
|
26
|
+
*
|
|
27
|
+
* ponytail: a regex literal is read as code, so a quote inside one (`/"/`) opens a string that
|
|
28
|
+
* runs to the next quote. Contract files are declarations, where that does not come up; one that
|
|
29
|
+
* needs it would need a real tokenizer here.
|
|
30
|
+
*/
|
|
31
|
+
export declare function blankCommentsAndStrings(source: string, { keepStrings }?: {
|
|
32
|
+
keepStrings?: boolean | undefined;
|
|
33
|
+
}): string;
|
|
34
|
+
/**
|
|
35
|
+
* Every declaration the roots need, in source order, as one file's text. A name nobody declares
|
|
36
|
+
* throws with the list of files searched: a contract with a dangling reference does not compile,
|
|
37
|
+
* and finding out here beats finding out at publish.
|
|
38
|
+
*/
|
|
39
|
+
export declare function liftContract({ root, sources, roots, inlineTuples, exportOnlyRoots, }: LiftOptions): string;
|
|
40
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,oEAAoE;IACpE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,WAAmB,EAAE;;CAAK,GAAG,MAAM,CAyC5F;AAmGD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,YAAoB,EACpB,eAAuB,GACxB,EAAE,WAAW,GAAG,MAAM,CAsCtB"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copying the API's own type declarations into its SDK, with their comments.
|
|
3
|
+
*
|
|
4
|
+
* A published SDK cannot depend on the API's workspace, so the types it answers with have to
|
|
5
|
+
* travel inside it. A `.d.ts` emit would carry the types and drop the prose, and the prose is most
|
|
6
|
+
* of what makes an SDK pleasant to hold, so this reads each declaration as it is written at home
|
|
7
|
+
* and copies it whole. Five generators did this with one hand-written reader, and each copy had
|
|
8
|
+
* fixed something the others had not:
|
|
9
|
+
* - a property KEY is not a reference (`VALIDATION_ERROR: 400` names no type);
|
|
10
|
+
* - an `export function` is a braced block, like an interface;
|
|
11
|
+
* - `(typeof X)[number]` can be written as its literal union, where the array would be dead.
|
|
12
|
+
* And all five broke what the reader rests on, that the blanked text matches the original line for
|
|
13
|
+
* line: an escaped newline in a string moved every later line, a backslash at the end of a `//`
|
|
14
|
+
* comment blanked the next line, a `/*` followed by `/` closed on its own star, and a one-line
|
|
15
|
+
* `{}` block ran on into the next declaration.
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { BUILTIN } from "./types.js";
|
|
20
|
+
/**
|
|
21
|
+
* The source with every comment and string body blanked, one character for one character and a
|
|
22
|
+
* newline for a newline, so structure can be read off it while the ORIGINAL lines are what gets
|
|
23
|
+
* copied. Without it a `;` inside a sentence ends a declaration early, and every capitalized word
|
|
24
|
+
* in the prose looks like a type. `keepStrings` blanks the comments only, for reading the strings
|
|
25
|
+
* themselves: an apostrophe in a comment is not a quote.
|
|
26
|
+
*
|
|
27
|
+
* ponytail: a regex literal is read as code, so a quote inside one (`/"/`) opens a string that
|
|
28
|
+
* runs to the next quote. Contract files are declarations, where that does not come up; one that
|
|
29
|
+
* needs it would need a real tokenizer here.
|
|
30
|
+
*/
|
|
31
|
+
export function blankCommentsAndStrings(source, { keepStrings = false } = {}) {
|
|
32
|
+
let out = "";
|
|
33
|
+
let state = "code";
|
|
34
|
+
const blank = (c) => (c === "\n" ? "\n" : c === undefined ? "" : " ");
|
|
35
|
+
const inString = (c) => (keepStrings ? (c ?? "") : blank(c));
|
|
36
|
+
for (let i = 0; i < source.length; i++) {
|
|
37
|
+
const c = source[i] ?? "";
|
|
38
|
+
const next = source[i + 1];
|
|
39
|
+
if (state === "code") {
|
|
40
|
+
if (c === "/" && (next === "/" || next === "*")) {
|
|
41
|
+
// Both characters of the opener, so the `*` of `/*/` cannot also close it.
|
|
42
|
+
state = next === "/" ? "line" : "block";
|
|
43
|
+
out += " ";
|
|
44
|
+
i++;
|
|
45
|
+
}
|
|
46
|
+
else if (c === '"' || c === "'" || c === "`") {
|
|
47
|
+
state = c;
|
|
48
|
+
out += inString(c);
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
out += c;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (state === "line" || state === "block") {
|
|
55
|
+
if (state === "block" && c === "*" && next === "/") {
|
|
56
|
+
state = "code";
|
|
57
|
+
out += " ";
|
|
58
|
+
i++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (c === "\n" && state === "line")
|
|
62
|
+
state = "code";
|
|
63
|
+
out += blank(c);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (c === "\\") {
|
|
67
|
+
// An escape never closes a string, and the character it escapes may be a newline.
|
|
68
|
+
out += inString(c) + inString(next);
|
|
69
|
+
i++;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (c === state)
|
|
73
|
+
state = "code";
|
|
74
|
+
out += inString(c);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** Split one file into its exported top-level declarations. */
|
|
79
|
+
function readBlocks(text) {
|
|
80
|
+
const lines = text.split("\n");
|
|
81
|
+
const clean = blankCommentsAndStrings(text).split("\n");
|
|
82
|
+
const blocks = [];
|
|
83
|
+
for (let i = 0; i < lines.length; i++) {
|
|
84
|
+
const match = /^export (interface|type|const|function) (\w+)/.exec(clean[i] ?? "");
|
|
85
|
+
if (!match?.[2])
|
|
86
|
+
continue;
|
|
87
|
+
const name = match[2];
|
|
88
|
+
// Back over the doc comment sitting on top of the declaration.
|
|
89
|
+
let start = i;
|
|
90
|
+
while (start > 0) {
|
|
91
|
+
const above = (lines[start - 1] ?? "").trim();
|
|
92
|
+
if (!above || (!above.startsWith("*") && !above.startsWith("/*")))
|
|
93
|
+
break;
|
|
94
|
+
start--;
|
|
95
|
+
}
|
|
96
|
+
// Forward to its end: the first `;` outside every bracket, since a mapped type carries one on
|
|
97
|
+
// an inner line, or for an interface or a function, the line that closes its brackets. That
|
|
98
|
+
// can be its first (`export interface Empty {}`); waiting for a lone `}`, as every copy did,
|
|
99
|
+
// swallowed the declaration after it.
|
|
100
|
+
const braced = match[1] === "interface" || match[1] === "function";
|
|
101
|
+
let end = i;
|
|
102
|
+
let depth = 0;
|
|
103
|
+
outer: for (; end < lines.length; end++) {
|
|
104
|
+
for (const char of clean[end] ?? "") {
|
|
105
|
+
if ("{[(".includes(char))
|
|
106
|
+
depth++;
|
|
107
|
+
else if ("}])".includes(char))
|
|
108
|
+
depth--;
|
|
109
|
+
else if (char === ";" && depth === 0)
|
|
110
|
+
break outer;
|
|
111
|
+
}
|
|
112
|
+
if (braced && depth === 0)
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
const body = clean
|
|
116
|
+
.slice(i, end + 1)
|
|
117
|
+
.join("\n")
|
|
118
|
+
// A property KEY is not a reference: `VALIDATION_ERROR: 400` names a member, not a type.
|
|
119
|
+
.replace(/\b[A-Z][A-Za-z0-9_]*(?=\s*\??:)/g, " ");
|
|
120
|
+
const refs = [...new Set(body.match(/\b[A-Z][A-Za-z0-9_]*\b/g) ?? [])].filter(
|
|
121
|
+
// ponytail: a short name is taken for a type parameter (`T`, `K`). A wrong guess is loud,
|
|
122
|
+
// not silent: a skipped name that WAS needed leaves the SDK missing a type, and it stops
|
|
123
|
+
// compiling.
|
|
124
|
+
(ref) => ref !== name && ref.length > 2 && !BUILTIN.has(ref));
|
|
125
|
+
blocks.push({ name, source: lines.slice(start, end + 1).join("\n"), refs });
|
|
126
|
+
i = end;
|
|
127
|
+
}
|
|
128
|
+
return blocks;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* With `inlineTuples`, `export type Plan = (typeof PLANS)[number];` becomes
|
|
132
|
+
* `export type Plan = "free" | "pro";`, and the array is left out. Only a tuple of plain string
|
|
133
|
+
* literals qualifies; anything else throws, because guessing a union into a published type is
|
|
134
|
+
* worse than stopping.
|
|
135
|
+
*/
|
|
136
|
+
function inlineTupleUnions(index) {
|
|
137
|
+
for (const block of index.values()) {
|
|
138
|
+
const alias = /export type (\w+) = \(?typeof (\w+)\)?\[number\];/.exec(block.source);
|
|
139
|
+
if (!alias?.[2])
|
|
140
|
+
continue;
|
|
141
|
+
const tuple = index.get(alias[2]);
|
|
142
|
+
if (tuple === undefined)
|
|
143
|
+
continue; // reported as missing below
|
|
144
|
+
// Comments out first, strings kept: a member's doc comment can hold an apostrophe.
|
|
145
|
+
const code = blankCommentsAndStrings(tuple.source, { keepStrings: true });
|
|
146
|
+
const literal = /=\s*\[([\s\S]*?)\]\s*as const\s*;/.exec(code);
|
|
147
|
+
const members = literal?.[1]?.match(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g) ?? [];
|
|
148
|
+
const rest = literal?.[1]?.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, "");
|
|
149
|
+
if (literal === null || rest === undefined || /[^\s,]/.test(rest)) {
|
|
150
|
+
throw new Error(`${alias[2]} must be a tuple of string literals \`as const\` for ${block.name} to be written as a union.`);
|
|
151
|
+
}
|
|
152
|
+
const union = members.map((member) => {
|
|
153
|
+
let unreadable = false;
|
|
154
|
+
const text = member.slice(1, -1).replace(/\\([\s\S])/g, (_escape, char) => {
|
|
155
|
+
unreadable ||= !`"'\\`.includes(char);
|
|
156
|
+
return char;
|
|
157
|
+
});
|
|
158
|
+
// ponytail: only a quote or a backslash is unescaped. Any other escape (`\n`, `\u00e9`)
|
|
159
|
+
// needs the language's whole table, so it stops rather than write a wrong member.
|
|
160
|
+
if (unreadable) {
|
|
161
|
+
throw new Error(`${alias[2]} has a member with an escape this cannot read: ${member}. Write it without one, or leave inlineTuples off.`);
|
|
162
|
+
}
|
|
163
|
+
return JSON.stringify(text);
|
|
164
|
+
});
|
|
165
|
+
block.source = block.source.replace(alias[0], `export type ${block.name} = ${union.join(" | ") || "never"};`);
|
|
166
|
+
block.refs = [];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Every declaration the roots need, in source order, as one file's text. A name nobody declares
|
|
171
|
+
* throws with the list of files searched: a contract with a dangling reference does not compile,
|
|
172
|
+
* and finding out here beats finding out at publish.
|
|
173
|
+
*/
|
|
174
|
+
export function liftContract({ root, sources, roots, inlineTuples = false, exportOnlyRoots = false, }) {
|
|
175
|
+
const files = sources.map((file) => ({
|
|
176
|
+
file,
|
|
177
|
+
blocks: readBlocks(readFileSync(join(root, file), "utf8")),
|
|
178
|
+
}));
|
|
179
|
+
const index = new Map();
|
|
180
|
+
for (const { blocks } of files)
|
|
181
|
+
for (const block of blocks)
|
|
182
|
+
index.set(block.name, block);
|
|
183
|
+
if (inlineTuples)
|
|
184
|
+
inlineTupleUnions(index);
|
|
185
|
+
const needed = new Set();
|
|
186
|
+
const missing = new Set();
|
|
187
|
+
const visit = (name) => {
|
|
188
|
+
if (needed.has(name))
|
|
189
|
+
return;
|
|
190
|
+
const block = index.get(name);
|
|
191
|
+
if (block === undefined) {
|
|
192
|
+
missing.add(name);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
needed.add(name);
|
|
196
|
+
block.refs.forEach(visit);
|
|
197
|
+
};
|
|
198
|
+
roots.forEach(visit);
|
|
199
|
+
if (missing.size > 0) {
|
|
200
|
+
throw new Error(`No declaration found for ${[...missing].join(", ")}. Export it from one of:\n ${sources.join("\n ")}`);
|
|
201
|
+
}
|
|
202
|
+
const chunks = [];
|
|
203
|
+
for (const { file, blocks } of files) {
|
|
204
|
+
const wanted = blocks.filter((b) => needed.has(b.name));
|
|
205
|
+
if (wanted.length === 0)
|
|
206
|
+
continue;
|
|
207
|
+
chunks.push(`// ── from ${file} ${"─".repeat(Math.max(0, 60 - file.length))}\n`);
|
|
208
|
+
const text = (b) => exportOnlyRoots && !roots.includes(b.name) ? b.source.replace(/^export /m, "") : b.source;
|
|
209
|
+
chunks.push(wanted.map(text).join("\n\n"));
|
|
210
|
+
}
|
|
211
|
+
return chunks.join("\n");
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { liftContract, type LiftOptions } from "./contract.ts";
|
|
2
|
+
export { camelCase, docComment, fieldsOf, inputJsonSchema, paramsInterface, pascalCase, typeNames, typeOf, wrapLines, type JsonObject, type SchemaSource, type StandardJsonSchema, } from "./types.ts";
|
|
3
|
+
export { writeGenerated, type GeneratedFiles, type WriteOptions, type WriteResult, } from "./write.ts";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing a JSON Schema as a TypeScript type, with each field's description kept as its doc
|
|
3
|
+
* comment.
|
|
4
|
+
*
|
|
5
|
+
* Five generators each wrote this, and the fixes were spread across them: `type: [..., "null"]`
|
|
6
|
+
* in three, `{}` as `unknown` and a record as `Record<string, T>` in two, a boolean schema and a
|
|
7
|
+
* tuple in one. The rule all five kept is the one that matters most: **never guess a type into a
|
|
8
|
+
* published SDK.** A schema node this does not understand throws; it never becomes `unknown`.
|
|
9
|
+
*/
|
|
10
|
+
export type JsonObject = Record<string, unknown>;
|
|
11
|
+
/** The part of the Standard JSON Schema interface this module calls: zod 4.4 and later. */
|
|
12
|
+
export interface StandardJsonSchema {
|
|
13
|
+
"~standard": {
|
|
14
|
+
vendor: string;
|
|
15
|
+
jsonSchema?: {
|
|
16
|
+
input(options: {
|
|
17
|
+
target: "draft-2020-12";
|
|
18
|
+
libraryOptions?: JsonObject;
|
|
19
|
+
}): JsonObject;
|
|
20
|
+
output(options: {
|
|
21
|
+
target: "draft-2020-12";
|
|
22
|
+
libraryOptions?: JsonObject;
|
|
23
|
+
}): JsonObject;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** A schema: a Standard JSON Schema such as a zod object, or JSON Schema itself. */
|
|
28
|
+
export type SchemaSource = StandardJsonSchema | JsonObject;
|
|
29
|
+
/** The schema as JSON Schema, describing what a caller SENDS. */
|
|
30
|
+
export declare function inputJsonSchema(source: SchemaSource): JsonObject;
|
|
31
|
+
/** Names TypeScript itself provides, so a mention of one is not a declaration to find. */
|
|
32
|
+
export declare const BUILTIN: Set<string>;
|
|
33
|
+
/**
|
|
34
|
+
* The TypeScript type for one schema. `indent` is where the enclosing line starts, so an inline
|
|
35
|
+
* object's fields land one level in.
|
|
36
|
+
*/
|
|
37
|
+
export declare function typeOf(schema: JsonObject | boolean, indent?: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* An object schema's fields, one per line, each description kept as a doc comment. `skip` leaves
|
|
40
|
+
* fields out, such as the ones a route fills in itself.
|
|
41
|
+
*/
|
|
42
|
+
export declare function fieldsOf(schema: JsonObject, indent?: string, skip?: readonly string[]): string;
|
|
43
|
+
/**
|
|
44
|
+
* `export interface SendMessageParams { … }`, or `null` when the schema has no fields left. The
|
|
45
|
+
* interface is `optional` when no remaining field is required, so the method's argument can be.
|
|
46
|
+
*/
|
|
47
|
+
export declare function paramsInterface(name: string, schema: JsonObject, skip?: readonly string[]): {
|
|
48
|
+
source: string;
|
|
49
|
+
optional: boolean;
|
|
50
|
+
} | null;
|
|
51
|
+
/**
|
|
52
|
+
* Prose broken into lines of at most `96 - indent.length` characters, to sit inside a comment at
|
|
53
|
+
* that indent. Every caller puts them in one, so a star-slash is broken up here: it would end the
|
|
54
|
+
* comment early and turn the rest of the sentence into code.
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* wrapLines(op.description, " ").join("\n * ");
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function wrapLines(text: string, indent?: string): string[];
|
|
61
|
+
/**
|
|
62
|
+
* `/** text *\/`, with continuation lines under the opener: the format all five generators wrote
|
|
63
|
+
* for a field, byte for byte.
|
|
64
|
+
*/
|
|
65
|
+
export declare function docComment(text: string, indent?: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* The declared names a type expression mentions: `"V1ListPage<JobDto>[] | null"` needs
|
|
68
|
+
* `V1ListPage` and `JobDto`. Splitting on `|` alone, as one generator did, misses what sits
|
|
69
|
+
* inside the generic.
|
|
70
|
+
*/
|
|
71
|
+
export declare function typeNames(expression: string): string[];
|
|
72
|
+
/** `send_message` → `sendMessage`. */
|
|
73
|
+
export declare const camelCase: (name: string) => string;
|
|
74
|
+
/** `send_message` → `SendMessage`. */
|
|
75
|
+
export declare const pascalCase: (name: string) => string;
|
|
76
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,2FAA2F;AAC3F,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE;QACX,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE;YACX,KAAK,CAAC,OAAO,EAAE;gBAAE,MAAM,EAAE,eAAe,CAAC;gBAAC,cAAc,CAAC,EAAE,UAAU,CAAA;aAAE,GAAG,UAAU,CAAC;YACrF,MAAM,CAAC,OAAO,EAAE;gBAAE,MAAM,EAAE,eAAe,CAAC;gBAAC,cAAc,CAAC,EAAE,UAAU,CAAA;aAAE,GAAG,UAAU,CAAC;SACvF,CAAC;KACH,CAAC;CACH;AAED,oFAAoF;AACpF,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,UAAU,CAAC;AAE3D,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAShE;AAmBD,0FAA0F;AAC1F,eAAO,MAAM,OAAO,aA+BlB,CAAC;AAYH;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,EAAE,MAAM,SAAK,GAAG,MAAM,CAqDxE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,MAAM,SAAS,EACf,IAAI,GAAE,SAAS,MAAM,EAAO,GAC3B,MAAM,CAcR;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE,SAAS,MAAM,EAAO,GAC3B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAQ9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG,MAAM,EAAE,CAYjE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG,MAAM,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAGtD;AAED,sCAAsC;AACtC,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,MACsB,CAAC;AAEhE,sCAAsC;AACtC,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,MAGzC,CAAC"}
|