stitchkit 0.2.0 → 0.4.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 +11 -10
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/http.d.ts +5 -0
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/socket-io.d.ts +17 -0
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.d.ts +23 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +21 -0
- package/dist/contract/define.d.ts +35 -2
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +2 -1
- package/dist/index-0ed3bx43.js +33 -0
- package/dist/index-0ma1eqv4.js +1007 -0
- package/dist/index-1cf8jkhf.js +219 -0
- package/dist/index-37x76zdn.js +4 -0
- package/dist/{index-v2z2v3mq.js → index-3qmtdfpa.js} +304 -62
- package/dist/index-48ffdxgk.js +6 -0
- package/dist/{index-5sxnvwb1.js → index-78q1qm7v.js} +6 -3
- package/dist/{index-kckky6zw.js → index-afzt3nmx.js} +1 -1
- package/dist/{index-n7bmdwmz.js → index-d4rwrjbc.js} +8 -4
- package/dist/index-kzfs85xp.js +9 -0
- package/dist/index-mwmpw6j1.js +60 -0
- package/dist/{index-a35v22fh.js → index-vhdvv00d.js} +13 -5
- package/dist/index-x3fcszf8.js +8 -0
- package/dist/index.js +17 -4
- package/dist/internal/errors.d.ts +13 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/http-input.d.ts +13 -0
- package/dist/internal/http-input.d.ts.map +1 -0
- package/dist/internal/safe-json.d.ts +19 -0
- package/dist/internal/safe-json.d.ts.map +1 -0
- package/dist/internal/secure-fetch.d.ts +27 -0
- package/dist/internal/secure-fetch.d.ts.map +1 -0
- package/dist/internal/within-dir.d.ts +7 -0
- package/dist/internal/within-dir.d.ts.map +1 -0
- package/dist/node.d.ts +10 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +41 -7
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/context.d.ts +9 -1
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/event.d.ts +4 -0
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/observability/index.js +38 -15
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/react.js +2 -0
- package/dist/server/context.d.ts +3 -2
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/event-bus.d.ts +10 -1
- package/dist/server/event-bus.d.ts.map +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +6 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +203 -193
- package/dist/server/logger.d.ts +5 -4
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware/auth.d.ts +41 -1
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/middleware/cookies.d.ts.map +1 -1
- package/dist/server/middleware/cors.d.ts +7 -0
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/middleware/pkce.d.ts +17 -0
- package/dist/server/middleware/pkce.d.ts.map +1 -0
- package/dist/server/multipart.d.ts +2 -1
- package/dist/server/multipart.d.ts.map +1 -1
- package/dist/server/node.d.ts +9 -0
- package/dist/server/node.d.ts.map +1 -1
- package/dist/server/openapi.d.ts +52 -0
- package/dist/server/openapi.d.ts.map +1 -0
- package/dist/server/rate-limit.d.ts +7 -2
- package/dist/server/rate-limit.d.ts.map +1 -1
- package/dist/server/request.d.ts +27 -4
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/router.d.ts +4 -2
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/socket-io.d.ts +60 -15
- package/dist/server/socket-io.d.ts.map +1 -1
- package/dist/server/stream.d.ts +3 -1
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/types.d.ts +17 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/websocket.d.ts +73 -0
- package/dist/server/websocket.d.ts.map +1 -0
- package/dist/tools/cli-args.d.ts +68 -0
- package/dist/tools/cli-args.d.ts.map +1 -0
- package/dist/tools/cli-format.d.ts +39 -0
- package/dist/tools/cli-format.d.ts.map +1 -0
- package/dist/tools/cli-wait.d.ts +45 -0
- package/dist/tools/cli-wait.d.ts.map +1 -0
- package/dist/tools/cli.d.ts +77 -0
- package/dist/tools/cli.d.ts.map +1 -0
- package/dist/tools/coerce.d.ts +8 -4
- package/dist/tools/coerce.d.ts.map +1 -1
- package/dist/tools/execute.d.ts +1 -1
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/json-schema.d.ts +29 -4
- package/dist/tools/json-schema.d.ts.map +1 -1
- package/dist/tools/manifest.d.ts +4 -0
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools/mcp-app.d.ts +59 -0
- package/dist/tools/mcp-app.d.ts.map +1 -0
- package/dist/tools/mcp-handler.d.ts +18 -0
- package/dist/tools/mcp-handler.d.ts.map +1 -1
- package/dist/tools/mcp.d.ts +9 -0
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mount-download.d.ts +30 -0
- package/dist/tools/mount-download.d.ts.map +1 -0
- package/dist/tools/mount-upload.d.ts +19 -0
- package/dist/tools/mount-upload.d.ts.map +1 -0
- package/dist/tools/mount-wait.d.ts +36 -0
- package/dist/tools/mount-wait.d.ts.map +1 -0
- package/dist/tools/mount.d.ts +8 -4
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/native-result.d.ts +17 -0
- package/dist/tools/native-result.d.ts.map +1 -0
- package/dist/tools/oauth-metadata.d.ts +28 -0
- package/dist/tools/oauth-metadata.d.ts.map +1 -0
- package/dist/tools/oauth-provider.d.ts +85 -0
- package/dist/tools/oauth-provider.d.ts.map +1 -0
- package/dist/tools/remote.d.ts +2 -2
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools/toolkit.d.ts +51 -0
- package/dist/tools/toolkit.d.ts.map +1 -0
- package/dist/tools/view-file.d.ts +12 -0
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools/wait-core.d.ts +33 -0
- package/dist/tools/wait-core.d.ts.map +1 -0
- package/dist/tools.d.ts +13 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +637 -424
- package/package.json +22 -9
- package/dist/index-ke4mx4ea.js +0 -38
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forbidden,
|
|
3
|
+
unauthorized
|
|
4
|
+
} from "./index-afzt3nmx.js";
|
|
5
|
+
import {
|
|
6
|
+
isUnsafeKey,
|
|
7
|
+
safeJsonParse
|
|
8
|
+
} from "./index-kzfs85xp.js";
|
|
9
|
+
import {
|
|
10
|
+
isRecord
|
|
11
|
+
} from "./index-809wc1tt.js";
|
|
12
|
+
|
|
13
|
+
// src/server/middleware/cookies.ts
|
|
14
|
+
function parseCookies(header) {
|
|
15
|
+
if (!header)
|
|
16
|
+
return {};
|
|
17
|
+
const cookies = {};
|
|
18
|
+
for (const pair of header.split(";")) {
|
|
19
|
+
const eq = pair.indexOf("=");
|
|
20
|
+
if (eq === -1)
|
|
21
|
+
continue;
|
|
22
|
+
const key = pair.slice(0, eq).trim();
|
|
23
|
+
if (!key || isUnsafeKey(key))
|
|
24
|
+
continue;
|
|
25
|
+
const raw = pair.slice(eq + 1).trim();
|
|
26
|
+
try {
|
|
27
|
+
cookies[key] = decodeURIComponent(raw);
|
|
28
|
+
} catch {
|
|
29
|
+
cookies[key] = raw;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return cookies;
|
|
33
|
+
}
|
|
34
|
+
function serializeCookie(name, value, opts = {}) {
|
|
35
|
+
let cookie = `${name}=${encodeURIComponent(value)}`;
|
|
36
|
+
if (opts.maxAge !== undefined)
|
|
37
|
+
cookie += `; Max-Age=${opts.maxAge}`;
|
|
38
|
+
if (opts.path)
|
|
39
|
+
cookie += `; Path=${opts.path}`;
|
|
40
|
+
if (opts.domain)
|
|
41
|
+
cookie += `; Domain=${opts.domain}`;
|
|
42
|
+
if (opts.secure)
|
|
43
|
+
cookie += "; Secure";
|
|
44
|
+
if (opts.httpOnly)
|
|
45
|
+
cookie += "; HttpOnly";
|
|
46
|
+
if (opts.sameSite)
|
|
47
|
+
cookie += `; SameSite=${opts.sameSite}`;
|
|
48
|
+
return cookie;
|
|
49
|
+
}
|
|
50
|
+
function defineCookie(config) {
|
|
51
|
+
const { name, ...options } = config;
|
|
52
|
+
return {
|
|
53
|
+
get(req) {
|
|
54
|
+
return parseCookies(req.headers.get("cookie"))[name];
|
|
55
|
+
},
|
|
56
|
+
set(value) {
|
|
57
|
+
return serializeCookie(name, value, options);
|
|
58
|
+
},
|
|
59
|
+
clear() {
|
|
60
|
+
return serializeCookie(name, "", { ...options, maxAge: 0 });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/server/middleware/auth.ts
|
|
66
|
+
var MAX_TOKEN_BYTES = 8192;
|
|
67
|
+
function decodeBase64Url(segment) {
|
|
68
|
+
if (!/^[A-Za-z0-9_-]*$/.test(segment) || segment.length % 4 === 1) {
|
|
69
|
+
throw new Error("invalid base64url segment");
|
|
70
|
+
}
|
|
71
|
+
const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
72
|
+
const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, "=");
|
|
73
|
+
return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
74
|
+
}
|
|
75
|
+
async function verifyJwt(token, secret, options = {}) {
|
|
76
|
+
if (!secret)
|
|
77
|
+
throw new Error("verifyJwt: a non-empty secret is required");
|
|
78
|
+
if (token.length > MAX_TOKEN_BYTES)
|
|
79
|
+
throw unauthorized("Token too large");
|
|
80
|
+
const [headerB64, payloadB64, signatureB64] = token.split(".");
|
|
81
|
+
if (!headerB64 || !payloadB64 || !signatureB64)
|
|
82
|
+
throw unauthorized("Invalid token format");
|
|
83
|
+
let header;
|
|
84
|
+
let payloadRaw;
|
|
85
|
+
let signature;
|
|
86
|
+
try {
|
|
87
|
+
const decoder = new TextDecoder;
|
|
88
|
+
header = JSON.parse(decoder.decode(decodeBase64Url(headerB64)));
|
|
89
|
+
payloadRaw = safeJsonParse(decoder.decode(decodeBase64Url(payloadB64)));
|
|
90
|
+
signature = decodeBase64Url(signatureB64);
|
|
91
|
+
} catch {
|
|
92
|
+
throw unauthorized("Malformed token");
|
|
93
|
+
}
|
|
94
|
+
if (!isRecord(payloadRaw))
|
|
95
|
+
throw unauthorized("Malformed token");
|
|
96
|
+
const payload = payloadRaw;
|
|
97
|
+
if (header.alg !== "HS256")
|
|
98
|
+
throw unauthorized("Unsupported token algorithm");
|
|
99
|
+
const encoder = new TextEncoder;
|
|
100
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
|
|
101
|
+
const data = encoder.encode(`${headerB64}.${payloadB64}`);
|
|
102
|
+
const valid = await crypto.subtle.verify("HMAC", key, signature, data);
|
|
103
|
+
if (!valid)
|
|
104
|
+
throw unauthorized("Invalid token signature");
|
|
105
|
+
const leeway = options.leewaySeconds ?? 60;
|
|
106
|
+
const now = Date.now() / 1000;
|
|
107
|
+
if ("exp" in payload) {
|
|
108
|
+
if (typeof payload.exp !== "number")
|
|
109
|
+
throw unauthorized("Malformed token");
|
|
110
|
+
if (payload.exp < now - leeway)
|
|
111
|
+
throw unauthorized("Token expired");
|
|
112
|
+
}
|
|
113
|
+
if ("nbf" in payload) {
|
|
114
|
+
if (typeof payload.nbf !== "number")
|
|
115
|
+
throw unauthorized("Malformed token");
|
|
116
|
+
if (payload.nbf > now + leeway)
|
|
117
|
+
throw unauthorized("Token not yet valid");
|
|
118
|
+
}
|
|
119
|
+
if (options.issuer !== undefined && payload.iss !== options.issuer) {
|
|
120
|
+
throw unauthorized("Token issuer mismatch");
|
|
121
|
+
}
|
|
122
|
+
if (options.audience !== undefined) {
|
|
123
|
+
const aud = payload.aud;
|
|
124
|
+
const ok = Array.isArray(aud) ? aud.includes(options.audience) : aud === options.audience;
|
|
125
|
+
if (!ok)
|
|
126
|
+
throw unauthorized("Token audience mismatch");
|
|
127
|
+
}
|
|
128
|
+
return payload;
|
|
129
|
+
}
|
|
130
|
+
function encodeBase64Url(bytes) {
|
|
131
|
+
let binary = "";
|
|
132
|
+
for (const byte of bytes)
|
|
133
|
+
binary += String.fromCharCode(byte);
|
|
134
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
135
|
+
}
|
|
136
|
+
async function signJwt(payload, secret, options = {}) {
|
|
137
|
+
if (!secret)
|
|
138
|
+
throw new Error("signJwt: a non-empty secret is required");
|
|
139
|
+
const now = Math.floor(Date.now() / 1000);
|
|
140
|
+
const claims = {
|
|
141
|
+
...payload,
|
|
142
|
+
iat: now,
|
|
143
|
+
...options.expiresInSec !== undefined && { exp: now + options.expiresInSec },
|
|
144
|
+
...options.issuer !== undefined && { iss: options.issuer },
|
|
145
|
+
...options.audience !== undefined && { aud: options.audience },
|
|
146
|
+
...options.subject !== undefined && { sub: options.subject }
|
|
147
|
+
};
|
|
148
|
+
const encoder = new TextEncoder;
|
|
149
|
+
const headerB64 = encodeBase64Url(encoder.encode(JSON.stringify({ alg: "HS256", typ: "JWT" })));
|
|
150
|
+
const payloadB64 = encodeBase64Url(encoder.encode(JSON.stringify(claims)));
|
|
151
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
152
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
153
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(signingInput));
|
|
154
|
+
const signatureB64 = encodeBase64Url(new Uint8Array(signature));
|
|
155
|
+
return `${signingInput}.${signatureB64}`;
|
|
156
|
+
}
|
|
157
|
+
function extractToken(req, cookieName) {
|
|
158
|
+
const authHeader = req.headers.get("authorization");
|
|
159
|
+
if (authHeader?.startsWith("Bearer "))
|
|
160
|
+
return authHeader.slice(7);
|
|
161
|
+
if (cookieName) {
|
|
162
|
+
const cookies = parseCookies(req.headers.get("cookie"));
|
|
163
|
+
return cookies[cookieName] ?? null;
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
function createAuthHook(config) {
|
|
168
|
+
const onAnonymous = config.onAnonymous ?? (() => unauthorized());
|
|
169
|
+
const onForbidden = config.onForbidden ?? (() => forbidden());
|
|
170
|
+
return async (ctx, endpoint) => {
|
|
171
|
+
const identity = ctx.source === "http" ? await config.resolve(ctx) : await config.resolveFromContext?.(ctx) ?? null;
|
|
172
|
+
config.inject?.(ctx, identity);
|
|
173
|
+
const scope = endpoint.scope ?? config.defaultScope;
|
|
174
|
+
if (!scope)
|
|
175
|
+
return;
|
|
176
|
+
const rule = config.rules[scope];
|
|
177
|
+
if (!rule) {
|
|
178
|
+
throw new Error(`[stitchkit] auth: no rule for scope "${scope}"`);
|
|
179
|
+
}
|
|
180
|
+
if (rule === "public")
|
|
181
|
+
return;
|
|
182
|
+
if (!identity) {
|
|
183
|
+
onAnonymous();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (rule === "authenticated")
|
|
187
|
+
return;
|
|
188
|
+
if (!await rule(identity, ctx))
|
|
189
|
+
onForbidden();
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function createBearerResolver(config) {
|
|
193
|
+
return async (req) => {
|
|
194
|
+
const token = extractToken(req);
|
|
195
|
+
if (!token)
|
|
196
|
+
return null;
|
|
197
|
+
return config.lookup(token, req);
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/server/middleware/pkce.ts
|
|
202
|
+
function base64UrlEncode(bytes) {
|
|
203
|
+
let binary = "";
|
|
204
|
+
for (const byte of bytes)
|
|
205
|
+
binary += String.fromCharCode(byte);
|
|
206
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
207
|
+
}
|
|
208
|
+
async function deriveCodeChallenge(verifier) {
|
|
209
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
210
|
+
return base64UrlEncode(new Uint8Array(digest));
|
|
211
|
+
}
|
|
212
|
+
async function verifyPkce(verifier, challenge) {
|
|
213
|
+
if (!verifier || !challenge)
|
|
214
|
+
return false;
|
|
215
|
+
const derived = await deriveCodeChallenge(verifier);
|
|
216
|
+
return derived === challenge;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export { parseCookies, serializeCookie, defineCookie, verifyJwt, signJwt, extractToken, createAuthHook, createBearerResolver, deriveCodeChallenge, verifyPkce };
|