kayto_ts 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 +299 -0
- package/bin/kayto.mjs +29 -0
- package/bun.lock +26 -0
- package/package.json +20 -0
- package/scripts/postinstall.mjs +541 -0
- package/src/index.ts +177 -0
- package/src/types.ts +95 -0
- package/src/utils.ts +201 -0
- package/tsconfig.json +29 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Vladislav Yemelyanov
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# kayto_ts
|
|
2
|
+
|
|
3
|
+
🚀 Build robust API integrations faster with `kayto_ts`.
|
|
4
|
+
|
|
5
|
+
- 🔒 End-to-end type safety for `method + path + params + body + response`
|
|
6
|
+
- ⚡ Zero-boilerplate HTTP client generation from schema
|
|
7
|
+
- 🧩 Request/response hooks for auth, tracing, and custom logic
|
|
8
|
+
- ⏱️ Built-in timeout and cancellation support
|
|
9
|
+
- 🛡️ Predictable, unified error model for cleaner handling
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun add kayto_ts
|
|
15
|
+
# alternatives:
|
|
16
|
+
# npm i kayto_ts
|
|
17
|
+
# pnpm add kayto_ts
|
|
18
|
+
# yarn add kayto_ts
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
During install, `kayto_ts` automatically downloads `kayto` binary into local package directory: `.kayto/bin`.
|
|
22
|
+
No global `kayto` install is required.
|
|
23
|
+
|
|
24
|
+
## Using CLI
|
|
25
|
+
|
|
26
|
+
Generate TypeScript schema from OpenAPI in one command:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bunx kayto --lang ts --input "https://example.com/openapi.json" --output "generated/schema.ts"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Alternatives:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx kayto --help
|
|
36
|
+
pnpm exec kayto --help
|
|
37
|
+
yarn kayto --help
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Multiple Services
|
|
41
|
+
|
|
42
|
+
If you generate one schema file per microservice, keep clients centralized in one place.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { clientApi, type EndpointsMap } from "kayto_ts";
|
|
46
|
+
import type { Endpoints as AccountsEndpoints } from "./schemas/accounts";
|
|
47
|
+
import type { Endpoints as BillingEndpoints } from "./schemas/billing";
|
|
48
|
+
import type { Endpoints as NotificationsEndpoints } from "./schemas/notifications";
|
|
49
|
+
|
|
50
|
+
const SERVICE_URLS = {
|
|
51
|
+
accounts: "https://accounts.example.com",
|
|
52
|
+
billing: "https://billing.example.com",
|
|
53
|
+
notifications: "https://notifications.example.com",
|
|
54
|
+
} as const;
|
|
55
|
+
|
|
56
|
+
function createServiceClient<TEndpoints extends EndpointsMap>(baseUrl: string) {
|
|
57
|
+
return clientApi<TEndpoints>({
|
|
58
|
+
baseUrl,
|
|
59
|
+
onRequest: ({ init }) => {
|
|
60
|
+
const headers = new Headers(init.headers);
|
|
61
|
+
headers.set("authorization", `Bearer ${getAccessToken()}`);
|
|
62
|
+
init.headers = headers;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const clients = {
|
|
68
|
+
accounts: createServiceClient<AccountsEndpoints>(SERVICE_URLS.accounts),
|
|
69
|
+
billing: createServiceClient<BillingEndpoints>(SERVICE_URLS.billing),
|
|
70
|
+
notifications: createServiceClient<NotificationsEndpoints>(SERVICE_URLS.notifications),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function getAccessToken(): string {
|
|
74
|
+
return "token";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// usage
|
|
78
|
+
const me = await clients.accounts.get("/v1/me");
|
|
79
|
+
const invoices = await clients.billing.get("/v1/invoices");
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Suggested structure:
|
|
83
|
+
- `schemas/accounts.ts`, `schemas/billing.ts`, `schemas/notifications.ts`
|
|
84
|
+
- one shared `clients.ts` that exports preconfigured clients
|
|
85
|
+
- app code imports only `clients` and never constructs clients ad-hoc
|
|
86
|
+
|
|
87
|
+
## Quick Start (Cats API)
|
|
88
|
+
|
|
89
|
+
### With `baseUrl`
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { clientApi } from "kayto_ts";
|
|
93
|
+
import type { Endpoints as CatsEndpoints } from "./schemas/cats";
|
|
94
|
+
|
|
95
|
+
const clientWithBaseUrl = clientApi<CatsEndpoints>({
|
|
96
|
+
baseUrl: "https://api.example.com",
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`baseUrl` applies only to relative paths.
|
|
101
|
+
If you pass an absolute URL (`https://...`), it is used as-is and `baseUrl` is ignored.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { clientApi } from "kayto_ts";
|
|
105
|
+
import type { Endpoints as CatsEndpoints } from "./schemas/cats";
|
|
106
|
+
|
|
107
|
+
const client = clientApi<CatsEndpoints>({
|
|
108
|
+
baseUrl: "https://api.example.com",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await client.get("/api/cats");
|
|
112
|
+
// -> https://api.example.com/api/cats
|
|
113
|
+
|
|
114
|
+
await client.get("https://other.example.com/api/cats" as "/api/cats");
|
|
115
|
+
// -> https://other.example.com/api/cats
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Basic Requests
|
|
119
|
+
|
|
120
|
+
### GET with query params
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
const listResult = await client.get("/api/cats", {
|
|
124
|
+
params: {
|
|
125
|
+
query: {
|
|
126
|
+
page: 1,
|
|
127
|
+
search: "british",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### GET with path params
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const oneResult = await client.get("/api/cats/{id}", {
|
|
137
|
+
params: {
|
|
138
|
+
path: {
|
|
139
|
+
id: "cat_42",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### POST with body
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
const createResult = await client.post("/api/cats", {
|
|
149
|
+
body: {
|
|
150
|
+
name: "Milo",
|
|
151
|
+
age: 2,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Request headers
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const authorizedResult = await client.get("/api/cats", {
|
|
160
|
+
headers: {
|
|
161
|
+
Authorization: "Bearer <token>",
|
|
162
|
+
"x-cats-trace-id": "trace_123",
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### DELETE with path params
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const deleteResult = await client.delete("/api/cats/{id}", {
|
|
171
|
+
params: {
|
|
172
|
+
path: {
|
|
173
|
+
id: "cat_42",
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Timeout and Cancellation
|
|
180
|
+
|
|
181
|
+
### Timeout
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
const result = await client.get("/api/cats", {
|
|
185
|
+
timeoutMs: 5_000,
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### AbortController
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const controller = new AbortController();
|
|
193
|
+
|
|
194
|
+
const promise = client.get("/api/cats", {
|
|
195
|
+
signal: controller.signal,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
controller.abort();
|
|
199
|
+
|
|
200
|
+
const result = await promise;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Hooks and Interceptor
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import { clientApi } from "kayto_ts";
|
|
207
|
+
import type { Endpoints as CatsEndpoints } from "./schemas/cats";
|
|
208
|
+
|
|
209
|
+
const clientWithHooks = clientApi<CatsEndpoints>({
|
|
210
|
+
baseUrl: "https://api.example.com",
|
|
211
|
+
|
|
212
|
+
onRequest: ({ method, path, init }) => {
|
|
213
|
+
const headers = new Headers(init.headers);
|
|
214
|
+
headers.set("x-cats-trace-id", "trace_123");
|
|
215
|
+
init.headers = headers;
|
|
216
|
+
|
|
217
|
+
console.log("request", method, path);
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
responseInterceptor: async ({ response }) => {
|
|
221
|
+
// Example: could refresh token and retry in your own wrapper logic.
|
|
222
|
+
return response;
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
onResponse: ({ method, path, response, durationMs }) => {
|
|
226
|
+
console.log("response", method, path, response.status, `${durationMs}ms`);
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Error Handling
|
|
232
|
+
|
|
233
|
+
All requests return a discriminated union:
|
|
234
|
+
|
|
235
|
+
- success: `{ ok: true, result, response }`
|
|
236
|
+
- failure: `{ ok: false, error, response? }`
|
|
237
|
+
|
|
238
|
+
`error.kind` values:
|
|
239
|
+
|
|
240
|
+
- `network`
|
|
241
|
+
- `timeout`
|
|
242
|
+
- `aborted`
|
|
243
|
+
- `http`
|
|
244
|
+
- `parse`
|
|
245
|
+
- `hook`
|
|
246
|
+
|
|
247
|
+
You can handle errors by `kind`, but it is optional.
|
|
248
|
+
You can also handle them generically via `message` / `status` / `cause` without branching by `kind`.
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
const result = await client.get("/api/cats");
|
|
252
|
+
|
|
253
|
+
if (!result.ok) {
|
|
254
|
+
console.error("Request failed:", result.error.message);
|
|
255
|
+
|
|
256
|
+
if (result.error.status != null) {
|
|
257
|
+
console.error("HTTP status:", result.error.status);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (result.error.cause) {
|
|
261
|
+
console.error("Cause:", result.error.cause);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
const result = await client.get("/api/cats");
|
|
268
|
+
|
|
269
|
+
if (!result.ok) {
|
|
270
|
+
switch (result.error.kind) {
|
|
271
|
+
case "timeout":
|
|
272
|
+
console.error("Cats API timeout");
|
|
273
|
+
break;
|
|
274
|
+
case "aborted":
|
|
275
|
+
console.error("Request was cancelled");
|
|
276
|
+
break;
|
|
277
|
+
case "http":
|
|
278
|
+
console.error("HTTP error", result.error.status);
|
|
279
|
+
break;
|
|
280
|
+
default:
|
|
281
|
+
console.error(result.error.message, result.error.cause);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Response Parsing Rules
|
|
287
|
+
|
|
288
|
+
Client parses response body automatically by `content-type`:
|
|
289
|
+
|
|
290
|
+
- JSON: `application/json`, `application/problem+json`, `*+json`
|
|
291
|
+
- Text: `text/*`
|
|
292
|
+
- Other content-types: `Blob`
|
|
293
|
+
- Empty body statuses (`204`, `205`, `304`): `null`
|
|
294
|
+
|
|
295
|
+
## Notes
|
|
296
|
+
|
|
297
|
+
- Runtime shape validation is not built in yet (current typing is compile-time only).
|
|
298
|
+
- If you need runtime validation, validate `result.result` in consumer code (ArkType/Zod/etc.).
|
|
299
|
+
- Current entrypoint is `src/index.ts`.
|
package/bin/kayto.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { platform } from "node:os";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
const rootDir = dirname(__dirname);
|
|
11
|
+
|
|
12
|
+
const binary = platform() === "win32" ? "kayto.exe" : "kayto";
|
|
13
|
+
const binaryPath = join(rootDir, ".kayto", "bin", binary);
|
|
14
|
+
|
|
15
|
+
if (!existsSync(binaryPath)) {
|
|
16
|
+
console.error("[kayto_ts] kayto binary is missing. Reinstall package: npm i kayto_ts (or bun/pnpm/yarn install)");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
21
|
+
stdio: "inherit",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (result.error) {
|
|
25
|
+
console.error("[kayto_ts] failed to execute kayto:", result.error.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
process.exit(result.status ?? 1);
|
package/bun.lock
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "kayto_ts",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@types/bun": "latest",
|
|
9
|
+
},
|
|
10
|
+
"peerDependencies": {
|
|
11
|
+
"typescript": "^5",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
"packages": {
|
|
16
|
+
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
|
17
|
+
|
|
18
|
+
"@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="],
|
|
19
|
+
|
|
20
|
+
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
|
21
|
+
|
|
22
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
23
|
+
|
|
24
|
+
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
|
25
|
+
}
|
|
26
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kayto_ts",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Type-safe HTTP client and CLI wrapper for generating and using schemas with kayto.",
|
|
5
|
+
"module": "src/index.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"kayto": "./bin/kayto.mjs"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"postinstall": "bun ./scripts/postinstall.mjs || node ./scripts/postinstall.mjs",
|
|
12
|
+
"test": "bun test"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/bun": "latest"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"typescript": "^5"
|
|
19
|
+
}
|
|
20
|
+
}
|