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.
Files changed (136) hide show
  1. package/README.md +11 -10
  2. package/dist/browser/client.d.ts.map +1 -1
  3. package/dist/browser/http.d.ts +5 -0
  4. package/dist/browser/http.d.ts.map +1 -1
  5. package/dist/browser/socket-io.d.ts +17 -0
  6. package/dist/browser/socket-io.d.ts.map +1 -1
  7. package/dist/cli.d.ts +23 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +21 -0
  10. package/dist/contract/define.d.ts +35 -2
  11. package/dist/contract/define.d.ts.map +1 -1
  12. package/dist/contract/index.d.ts +1 -1
  13. package/dist/contract/index.d.ts.map +1 -1
  14. package/dist/contract/index.js +2 -1
  15. package/dist/index-0ed3bx43.js +33 -0
  16. package/dist/index-0ma1eqv4.js +1007 -0
  17. package/dist/index-1cf8jkhf.js +219 -0
  18. package/dist/index-37x76zdn.js +4 -0
  19. package/dist/{index-v2z2v3mq.js → index-3qmtdfpa.js} +304 -62
  20. package/dist/index-48ffdxgk.js +6 -0
  21. package/dist/{index-5sxnvwb1.js → index-78q1qm7v.js} +6 -3
  22. package/dist/{index-kckky6zw.js → index-afzt3nmx.js} +1 -1
  23. package/dist/{index-n7bmdwmz.js → index-d4rwrjbc.js} +8 -4
  24. package/dist/index-kzfs85xp.js +9 -0
  25. package/dist/index-mwmpw6j1.js +60 -0
  26. package/dist/{index-a35v22fh.js → index-vhdvv00d.js} +13 -5
  27. package/dist/index-x3fcszf8.js +8 -0
  28. package/dist/index.js +17 -4
  29. package/dist/internal/errors.d.ts +13 -1
  30. package/dist/internal/errors.d.ts.map +1 -1
  31. package/dist/internal/http-input.d.ts +13 -0
  32. package/dist/internal/http-input.d.ts.map +1 -0
  33. package/dist/internal/safe-json.d.ts +19 -0
  34. package/dist/internal/safe-json.d.ts.map +1 -0
  35. package/dist/internal/secure-fetch.d.ts +27 -0
  36. package/dist/internal/secure-fetch.d.ts.map +1 -0
  37. package/dist/internal/within-dir.d.ts +7 -0
  38. package/dist/internal/within-dir.d.ts.map +1 -0
  39. package/dist/node.d.ts +10 -1
  40. package/dist/node.d.ts.map +1 -1
  41. package/dist/node.js +41 -7
  42. package/dist/observability/audit.d.ts.map +1 -1
  43. package/dist/observability/context.d.ts +9 -1
  44. package/dist/observability/context.d.ts.map +1 -1
  45. package/dist/observability/event.d.ts +4 -0
  46. package/dist/observability/event.d.ts.map +1 -1
  47. package/dist/observability/index.js +38 -15
  48. package/dist/observability/sanitize.d.ts.map +1 -1
  49. package/dist/react.js +2 -0
  50. package/dist/server/context.d.ts +3 -2
  51. package/dist/server/context.d.ts.map +1 -1
  52. package/dist/server/create.d.ts.map +1 -1
  53. package/dist/server/event-bus.d.ts +10 -1
  54. package/dist/server/event-bus.d.ts.map +1 -1
  55. package/dist/server/implement.d.ts.map +1 -1
  56. package/dist/server/index.d.ts +6 -3
  57. package/dist/server/index.d.ts.map +1 -1
  58. package/dist/server/index.js +203 -193
  59. package/dist/server/logger.d.ts +5 -4
  60. package/dist/server/logger.d.ts.map +1 -1
  61. package/dist/server/middleware/auth.d.ts +41 -1
  62. package/dist/server/middleware/auth.d.ts.map +1 -1
  63. package/dist/server/middleware/cookies.d.ts.map +1 -1
  64. package/dist/server/middleware/cors.d.ts +7 -0
  65. package/dist/server/middleware/cors.d.ts.map +1 -1
  66. package/dist/server/middleware/pkce.d.ts +17 -0
  67. package/dist/server/middleware/pkce.d.ts.map +1 -0
  68. package/dist/server/multipart.d.ts +2 -1
  69. package/dist/server/multipart.d.ts.map +1 -1
  70. package/dist/server/node.d.ts +9 -0
  71. package/dist/server/node.d.ts.map +1 -1
  72. package/dist/server/openapi.d.ts +52 -0
  73. package/dist/server/openapi.d.ts.map +1 -0
  74. package/dist/server/rate-limit.d.ts +7 -2
  75. package/dist/server/rate-limit.d.ts.map +1 -1
  76. package/dist/server/request.d.ts +27 -4
  77. package/dist/server/request.d.ts.map +1 -1
  78. package/dist/server/router.d.ts +4 -2
  79. package/dist/server/router.d.ts.map +1 -1
  80. package/dist/server/socket-io.d.ts +60 -15
  81. package/dist/server/socket-io.d.ts.map +1 -1
  82. package/dist/server/stream.d.ts +3 -1
  83. package/dist/server/stream.d.ts.map +1 -1
  84. package/dist/server/types.d.ts +17 -1
  85. package/dist/server/types.d.ts.map +1 -1
  86. package/dist/server/websocket.d.ts +73 -0
  87. package/dist/server/websocket.d.ts.map +1 -0
  88. package/dist/tools/cli-args.d.ts +68 -0
  89. package/dist/tools/cli-args.d.ts.map +1 -0
  90. package/dist/tools/cli-format.d.ts +39 -0
  91. package/dist/tools/cli-format.d.ts.map +1 -0
  92. package/dist/tools/cli-wait.d.ts +45 -0
  93. package/dist/tools/cli-wait.d.ts.map +1 -0
  94. package/dist/tools/cli.d.ts +77 -0
  95. package/dist/tools/cli.d.ts.map +1 -0
  96. package/dist/tools/coerce.d.ts +8 -4
  97. package/dist/tools/coerce.d.ts.map +1 -1
  98. package/dist/tools/execute.d.ts +1 -1
  99. package/dist/tools/execute.d.ts.map +1 -1
  100. package/dist/tools/json-schema.d.ts +29 -4
  101. package/dist/tools/json-schema.d.ts.map +1 -1
  102. package/dist/tools/manifest.d.ts +4 -0
  103. package/dist/tools/manifest.d.ts.map +1 -1
  104. package/dist/tools/mcp-app.d.ts +59 -0
  105. package/dist/tools/mcp-app.d.ts.map +1 -0
  106. package/dist/tools/mcp-handler.d.ts +18 -0
  107. package/dist/tools/mcp-handler.d.ts.map +1 -1
  108. package/dist/tools/mcp.d.ts +9 -0
  109. package/dist/tools/mcp.d.ts.map +1 -1
  110. package/dist/tools/mount-download.d.ts +30 -0
  111. package/dist/tools/mount-download.d.ts.map +1 -0
  112. package/dist/tools/mount-upload.d.ts +19 -0
  113. package/dist/tools/mount-upload.d.ts.map +1 -0
  114. package/dist/tools/mount-wait.d.ts +36 -0
  115. package/dist/tools/mount-wait.d.ts.map +1 -0
  116. package/dist/tools/mount.d.ts +8 -4
  117. package/dist/tools/mount.d.ts.map +1 -1
  118. package/dist/tools/native-result.d.ts +17 -0
  119. package/dist/tools/native-result.d.ts.map +1 -0
  120. package/dist/tools/oauth-metadata.d.ts +28 -0
  121. package/dist/tools/oauth-metadata.d.ts.map +1 -0
  122. package/dist/tools/oauth-provider.d.ts +85 -0
  123. package/dist/tools/oauth-provider.d.ts.map +1 -0
  124. package/dist/tools/remote.d.ts +2 -2
  125. package/dist/tools/remote.d.ts.map +1 -1
  126. package/dist/tools/toolkit.d.ts +51 -0
  127. package/dist/tools/toolkit.d.ts.map +1 -0
  128. package/dist/tools/view-file.d.ts +12 -0
  129. package/dist/tools/view-file.d.ts.map +1 -1
  130. package/dist/tools/wait-core.d.ts +33 -0
  131. package/dist/tools/wait-core.d.ts.map +1 -0
  132. package/dist/tools.d.ts +13 -2
  133. package/dist/tools.d.ts.map +1 -1
  134. package/dist/tools.js +637 -424
  135. package/package.json +22 -9
  136. package/dist/index-ke4mx4ea.js +0 -38
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EACL,SAAS,EACT,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,OAAO,EACP,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EACL,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,YAAY,EACjB,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,aAAa,GACd,MAAM,aAAa,CAAC"}
@@ -1,16 +1,42 @@
1
1
  import {
2
2
  parseSSE,
3
3
  streamSSE
4
- } from "../index-n7bmdwmz.js";
4
+ } from "../index-d4rwrjbc.js";
5
5
  import {
6
+ createAuthHook,
7
+ createBearerResolver,
8
+ defineCookie,
9
+ deriveCodeChallenge,
10
+ extractToken,
11
+ parseCookies,
12
+ serializeCookie,
13
+ signJwt,
14
+ verifyJwt,
15
+ verifyPkce
16
+ } from "../index-1cf8jkhf.js";
17
+ import {
18
+ inputIsQuery
19
+ } from "../index-48ffdxgk.js";
20
+ import {
21
+ jsonSchemaFields,
22
+ toJsonSchema
23
+ } from "../index-0ed3bx43.js";
24
+ import {
25
+ composeWebSocketHandlers,
6
26
  corsHeaders,
7
27
  corsPreflightResponse,
8
28
  createHandler,
29
+ createImplement,
9
30
  createServer,
31
+ createSocketIOServer,
32
+ implement,
10
33
  parseMultipart,
11
- staticRoute
12
- } from "../index-v2z2v3mq.js";
13
- import"../index-a35v22fh.js";
34
+ socketIoLane,
35
+ staticRoute,
36
+ webSocketLane
37
+ } from "../index-3qmtdfpa.js";
38
+ import"../index-x3fcszf8.js";
39
+ import"../index-vhdvv00d.js";
14
40
  import {
15
41
  AppError,
16
42
  appError,
@@ -20,13 +46,19 @@ import {
20
46
  notFound,
21
47
  rateLimited,
22
48
  unauthorized
23
- } from "../index-kckky6zw.js";
49
+ } from "../index-afzt3nmx.js";
24
50
  import {
25
51
  extractIp,
26
52
  generateTraceId,
27
53
  getClientInfo,
54
+ resolveSocketIp,
28
55
  resolveTraceId
29
- } from "../index-ke4mx4ea.js";
56
+ } from "../index-mwmpw6j1.js";
57
+ import"../index-kzfs85xp.js";
58
+ import {
59
+ isRecord
60
+ } from "../index-809wc1tt.js";
61
+ import"../index-37x76zdn.js";
30
62
  // src/server/swept-map.ts
31
63
  function createSweptMap(options) {
32
64
  const store = new Map;
@@ -85,7 +117,7 @@ function cacheHeaders(maxAge, scope = "public") {
85
117
  };
86
118
  }
87
119
  // src/server/event-bus.ts
88
- function createEventBus() {
120
+ function createEventBus(options = {}) {
89
121
  const subscriptions = new Map;
90
122
  function add(event, fn, once) {
91
123
  let set = subscriptions.get(event);
@@ -109,11 +141,12 @@ function createEventBus() {
109
141
  set.delete(sub);
110
142
  try {
111
143
  const result = sub.fn(data);
112
- if (result instanceof Promise)
113
- result.catch(() => {
114
- return;
115
- });
116
- } catch {}
144
+ if (result instanceof Promise) {
145
+ result.catch((err) => options.onListenerError?.(err, event));
146
+ }
147
+ } catch (err) {
148
+ options.onListenerError?.(err, event);
149
+ }
117
150
  }
118
151
  },
119
152
  on(event, handler) {
@@ -136,163 +169,150 @@ function createEventBus() {
136
169
  }
137
170
  };
138
171
  }
139
- // src/server/implement.ts
140
- function implement(contract, handlers) {
141
- const methods = {};
142
- const groupScope = contract.meta.scope ?? "public";
143
- for (const [key, endpoint] of Object.entries(contract.endpoints)) {
144
- const typedHandler = handlers[key];
145
- methods[key] = {
146
- method: endpoint.method,
147
- path: endpoint.path,
148
- desc: endpoint.desc,
149
- toolName: "toolName" in endpoint ? endpoint.toolName : undefined,
150
- expose: endpoint.expose,
151
- scope: endpoint.scope ?? groupScope,
152
- paramsSchema: endpoint.params,
153
- inputSchema: endpoint.input,
154
- outputSchema: endpoint.output,
155
- multipart: endpoint.multipart,
156
- handler: (ctx) => typedHandler(ctx)
157
- };
158
- }
159
- return {
160
- name: contract.meta.prefix,
161
- prefix: contract.meta.prefix,
162
- scope: groupScope,
163
- methods
164
- };
172
+ // src/server/openapi.ts
173
+ var ERROR_ENVELOPE_SCHEMA = {
174
+ type: "object",
175
+ properties: {
176
+ error: {
177
+ type: "object",
178
+ properties: {
179
+ code: { type: "string" },
180
+ message: { type: "string" },
181
+ details: {},
182
+ hint: { type: "string" }
183
+ },
184
+ required: ["code"]
185
+ }
186
+ },
187
+ required: ["error"]
188
+ };
189
+ function joinPath(...parts) {
190
+ const joined = parts.filter((p) => Boolean(p)).map((part) => part.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
191
+ return `/${joined}`;
165
192
  }
166
- function createImplement() {
167
- return (contract, handlers) => implement(contract, handlers);
193
+ function toOpenApiPath(path) {
194
+ return path.replace(/:([^/]+)/g, "{$1}");
168
195
  }
169
- // src/server/middleware/cookies.ts
170
- function parseCookies(header) {
171
- if (!header)
196
+ function safeJson(schema, io) {
197
+ if (!schema)
198
+ return {};
199
+ try {
200
+ return toJsonSchema(schema, io, "any");
201
+ } catch {
172
202
  return {};
173
- const cookies = {};
174
- for (const pair of header.split(";")) {
175
- const [key, ...rest] = pair.split("=");
176
- if (!key)
177
- continue;
178
- const raw = rest.join("=").trim();
179
- try {
180
- cookies[key.trim()] = decodeURIComponent(raw);
181
- } catch {
182
- cookies[key.trim()] = raw;
183
- }
184
203
  }
185
- return cookies;
186
204
  }
187
- function serializeCookie(name, value, opts = {}) {
188
- let cookie = `${name}=${encodeURIComponent(value)}`;
189
- if (opts.maxAge !== undefined)
190
- cookie += `; Max-Age=${opts.maxAge}`;
191
- if (opts.path)
192
- cookie += `; Path=${opts.path}`;
193
- if (opts.domain)
194
- cookie += `; Domain=${opts.domain}`;
195
- if (opts.secure)
196
- cookie += "; Secure";
197
- if (opts.httpOnly)
198
- cookie += "; HttpOnly";
199
- if (opts.sameSite)
200
- cookie += `; SameSite=${opts.sameSite}`;
201
- return cookie;
202
- }
203
- function defineCookie(config) {
204
- const { name, ...options } = config;
205
+ function errorResponse(description) {
205
206
  return {
206
- get(req) {
207
- return parseCookies(req.headers.get("cookie"))[name];
208
- },
209
- set(value) {
210
- return serializeCookie(name, value, options);
211
- },
212
- clear() {
213
- return serializeCookie(name, "", { ...options, maxAge: 0 });
214
- }
207
+ description,
208
+ content: { "application/json": { schema: ERROR_ENVELOPE_SCHEMA } }
215
209
  };
216
210
  }
217
-
218
- // src/server/middleware/auth.ts
219
- function decodeBase64Url(segment) {
220
- const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
221
- const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, "=");
222
- return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
223
- }
224
- async function verifyJwt(token, secret) {
225
- const [headerB64, payloadB64, signatureB64] = token.split(".");
226
- if (!headerB64 || !payloadB64 || !signatureB64)
227
- throw unauthorized("Invalid token format");
228
- let header;
229
- let payload;
230
- let signature;
231
- try {
232
- const decoder = new TextDecoder;
233
- header = JSON.parse(decoder.decode(decodeBase64Url(headerB64)));
234
- payload = JSON.parse(decoder.decode(decodeBase64Url(payloadB64)));
235
- signature = decodeBase64Url(signatureB64);
236
- } catch {
237
- throw unauthorized("Malformed token");
238
- }
239
- if (header.alg !== "HS256")
240
- throw unauthorized("Unsupported token algorithm");
241
- const encoder = new TextEncoder;
242
- const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
243
- const data = encoder.encode(`${headerB64}.${payloadB64}`);
244
- const valid = await crypto.subtle.verify("HMAC", key, signature, data);
245
- if (!valid)
246
- throw unauthorized("Invalid token signature");
247
- const now = Date.now() / 1000;
248
- if (typeof payload.exp === "number" && payload.exp < now) {
249
- throw unauthorized("Token expired");
250
- }
251
- if (typeof payload.nbf === "number" && payload.nbf > now) {
252
- throw unauthorized("Token not yet valid");
253
- }
254
- return payload;
255
- }
256
- function extractToken(req, cookieName) {
257
- const authHeader = req.headers.get("authorization");
258
- if (authHeader?.startsWith("Bearer "))
259
- return authHeader.slice(7);
260
- if (cookieName) {
261
- const cookies = parseCookies(req.headers.get("cookie"));
262
- return cookies[cookieName] ?? null;
211
+ function errorResponses(scope) {
212
+ const responses = {
213
+ "400": errorResponse("Validation error"),
214
+ "500": errorResponse("Server error")
215
+ };
216
+ if (scope && scope !== "public") {
217
+ responses["401"] = errorResponse("Unauthorized");
218
+ responses["403"] = errorResponse("Forbidden");
263
219
  }
264
- return null;
220
+ return responses;
265
221
  }
266
- function createAuthHook(config) {
267
- const onAnonymous = config.onAnonymous ?? (() => unauthorized());
268
- const onForbidden = config.onForbidden ?? (() => forbidden());
269
- return async (ctx, endpoint) => {
270
- if (!(ctx.req instanceof Request))
271
- return;
272
- const identity = await config.resolve(ctx);
273
- config.inject?.(ctx, identity);
274
- const scope = endpoint.scope ?? config.defaultScope;
275
- if (!scope)
276
- return;
277
- const rule = config.rules[scope];
278
- if (!rule || rule === "public")
279
- return;
280
- if (!identity) {
281
- onAnonymous();
282
- return;
222
+ function generateOpenApiDocument(config) {
223
+ const groups = [
224
+ ...(config.services ?? []).map((service) => ({ pathPrefix: undefined, service })),
225
+ ...(config.groups ?? []).flatMap((group) => group.services.map((service) => ({ pathPrefix: group.pathPrefix, service })))
226
+ ];
227
+ const paths = {};
228
+ for (const { pathPrefix, service } of groups) {
229
+ for (const [key, method] of Object.entries(service.methods)) {
230
+ if (method.expose && !method.expose.includes("HTTP"))
231
+ continue;
232
+ const servicePath = joinPath("/", service.prefix, method.path === "/" ? "" : method.path);
233
+ const fullPath = toOpenApiPath(pathPrefix ? joinPath(pathPrefix, servicePath) : servicePath);
234
+ const parameters = [];
235
+ for (const field of jsonSchemaFields(safeJson(method.paramsSchema, "input"))) {
236
+ parameters.push({
237
+ name: field.name,
238
+ in: "path",
239
+ required: true,
240
+ schema: field.schema,
241
+ ...field.description && { description: field.description }
242
+ });
243
+ }
244
+ const inputInQuery = inputIsQuery(method.method);
245
+ if (inputInQuery && method.inputSchema) {
246
+ for (const field of jsonSchemaFields(safeJson(method.inputSchema, "input"))) {
247
+ parameters.push({
248
+ name: field.name,
249
+ in: "query",
250
+ required: field.required,
251
+ schema: field.schema,
252
+ ...field.description && { description: field.description }
253
+ });
254
+ }
255
+ }
256
+ const responses = { ...errorResponses(method.scope) };
257
+ if (method.outputSchema) {
258
+ responses["200"] = {
259
+ description: "Success",
260
+ content: { "application/json": { schema: safeJson(method.outputSchema, "output") } }
261
+ };
262
+ } else {
263
+ responses["204"] = { description: "No content" };
264
+ }
265
+ const operation = {
266
+ summary: method.desc,
267
+ operationId: `${service.name}_${key}`,
268
+ ...parameters.length > 0 && { parameters },
269
+ responses
270
+ };
271
+ if (!inputInQuery && (method.inputSchema || method.multipart)) {
272
+ if (method.multipart) {
273
+ const inputJson = safeJson(method.inputSchema, "input");
274
+ const baseProps = isRecord(inputJson.properties) ? inputJson.properties : {};
275
+ const baseRequired = Array.isArray(inputJson.required) ? inputJson.required : [];
276
+ operation.requestBody = {
277
+ required: true,
278
+ content: {
279
+ "multipart/form-data": {
280
+ schema: {
281
+ type: "object",
282
+ properties: {
283
+ ...baseProps,
284
+ [method.multipart]: { type: "string", format: "binary" }
285
+ },
286
+ required: [...baseRequired, method.multipart]
287
+ }
288
+ }
289
+ }
290
+ };
291
+ } else if (method.inputSchema) {
292
+ const required = jsonSchemaFields(safeJson(method.inputSchema, "input")).some((f) => f.required);
293
+ operation.requestBody = {
294
+ required,
295
+ content: { "application/json": { schema: safeJson(method.inputSchema, "input") } }
296
+ };
297
+ }
298
+ }
299
+ const pathItem = paths[fullPath] ?? {};
300
+ pathItem[method.method.toLowerCase()] = operation;
301
+ paths[fullPath] = pathItem;
283
302
  }
284
- if (rule === "authenticated")
285
- return;
286
- if (!await rule(identity, ctx))
287
- onForbidden();
303
+ }
304
+ return {
305
+ openapi: "3.1.0",
306
+ info: config.info,
307
+ ...config.servers && { servers: config.servers },
308
+ paths
288
309
  };
289
310
  }
290
- function createBearerResolver(config) {
291
- return async (req) => {
292
- const token = extractToken(req);
293
- if (!token)
294
- return null;
295
- return config.lookup(token, req);
311
+ function openApiRoute(path, document) {
312
+ return {
313
+ method: "GET",
314
+ path,
315
+ handler: () => Response.json(document)
296
316
  };
297
317
  }
298
318
  // src/server/rate-limit.ts
@@ -301,6 +321,7 @@ function refillBucket(entry, config, now) {
301
321
  entry.tokens = Math.min(config.max, entry.tokens + elapsed / config.window * config.max);
302
322
  entry.lastRefill = now;
303
323
  }
324
+ var MAX_BUCKETS = 1e5;
304
325
  function createRateLimiter() {
305
326
  const { store: buckets, destroy } = createSweptMap({
306
327
  intervalMs: 60000,
@@ -310,15 +331,26 @@ function createRateLimiter() {
310
331
  destroy,
311
332
  check(key, config) {
312
333
  const now = Date.now();
313
- let entry = buckets.get(key);
314
- if (!entry) {
315
- entry = { tokens: config.max, lastRefill: now };
334
+ const entry = buckets.get(key);
335
+ if (entry) {
336
+ buckets.delete(key);
316
337
  buckets.set(key, entry);
338
+ refillBucket(entry, config, now);
339
+ if (entry.tokens < 1)
340
+ return false;
341
+ entry.tokens -= 1;
342
+ return true;
343
+ }
344
+ if (buckets.size >= MAX_BUCKETS) {
345
+ const lru = buckets.keys().next().value;
346
+ if (lru !== undefined)
347
+ buckets.delete(lru);
317
348
  }
318
- refillBucket(entry, config, now);
319
- if (entry.tokens < 1)
349
+ const fresh = { tokens: config.max, lastRefill: now };
350
+ buckets.set(key, fresh);
351
+ if (fresh.tokens < 1)
320
352
  return false;
321
- entry.tokens -= 1;
353
+ fresh.tokens -= 1;
322
354
  return true;
323
355
  },
324
356
  remaining(key, config) {
@@ -330,55 +362,32 @@ function createRateLimiter() {
330
362
  }
331
363
  };
332
364
  }
333
- // src/server/socket-io.ts
334
- import { Server as BunEngine } from "@socket.io/bun-engine";
335
- import { Server as SocketIOServer } from "socket.io";
336
- function createSocketIOServer(config) {
337
- const path = config.path ?? "/socket.io/";
338
- const io = new SocketIOServer({
339
- path,
340
- cors: {
341
- origin: config.cors.origin,
342
- credentials: config.cors.credentials ?? true,
343
- methods: ["GET", "POST"]
344
- },
345
- transports: config.transports ?? ["websocket", "polling"],
346
- pingTimeout: config.pingTimeout ?? 20000,
347
- pingInterval: config.pingInterval ?? 1e4
348
- });
349
- const engine = new BunEngine({ path });
350
- io.bind(engine);
351
- const { websocket } = engine.handler();
352
- const route = {
353
- method: "ALL",
354
- path: `${path.replace(/\/+$/, "")}/*`,
355
- handler: (req, ctx) => {
356
- if (!ctx.server) {
357
- throw new Error("[stitchkit] createSocketIOServer route needs a running Bun server — mount it via createServer.");
358
- }
359
- return engine.handleRequest(req, ctx.server);
360
- }
361
- };
362
- return { io, websocket, route };
363
- }
364
365
  export {
366
+ webSocketLane,
367
+ verifyPkce,
365
368
  verifyJwt,
366
369
  unauthorized,
367
370
  streamSSE,
368
371
  staticRoute,
372
+ socketIoLane,
373
+ signJwt,
369
374
  serializeCookie,
370
375
  resolveTraceId,
376
+ resolveSocketIp,
371
377
  rateLimited,
372
378
  parseSSE,
373
379
  parseMultipart,
374
380
  parseCookies,
381
+ openApiRoute,
375
382
  notFound,
376
383
  implement,
377
384
  getClientInfo,
378
385
  generateTraceId,
386
+ generateOpenApiDocument,
379
387
  forbidden,
380
388
  extractToken,
381
389
  extractIp,
390
+ deriveCodeChallenge,
382
391
  defineCookie,
383
392
  createSocketIOServer,
384
393
  createServer,
@@ -392,6 +401,7 @@ export {
392
401
  corsPreflightResponse,
393
402
  corsHeaders,
394
403
  conflict,
404
+ composeWebSocketHandlers,
395
405
  cacheHeaders,
396
406
  badRequest,
397
407
  appError,
@@ -1,4 +1,5 @@
1
- export declare function elapsedMs(startTime: bigint): number;
1
+ /** Elapsed milliseconds since a `performance.now()` mark. */
2
+ export declare function elapsedMs(startTime: number): number;
2
3
  /** Map an HTTP status to a log level. */
3
4
  export declare function levelForStatus(status: number): 'error' | 'warn' | 'info';
4
5
  /** The structured fields shared by every completed-request log line. */
@@ -11,11 +12,11 @@ export declare function buildLogFields(method: string, path: string, status: num
11
12
  };
12
13
  export interface RequestLog {
13
14
  traceId: string;
14
- startTime: bigint;
15
+ startTime: number;
15
16
  }
16
17
  export declare function shouldLog(pathname: string, method: string): boolean;
17
18
  /** Open the timing window for a request. Development prints a `→` line. */
18
- export declare function logIncoming(req: Request, pathname: string, traceId: string): RequestLog;
19
+ export declare function logIncoming(req: Request, pathname: string, traceId: string, ipAddress?: string): RequestLog;
19
20
  /** Close a request. Development: `←` line. Production: one structured JSON line. */
20
- export declare function logOutgoing(req: Request, pathname: string, status: number, log: RequestLog): void;
21
+ export declare function logOutgoing(req: Request, pathname: string, status: number, log: RequestLog, ipAddress?: string): void;
21
22
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AA0CA,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAIxE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAEvF;AA0BD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CASvF;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,UAAU,GACd,IAAI,CAoBN"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AAwCA,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAQD,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAIxE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAEvF;AA0BD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,UAAU,CASZ;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,UAAU,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI,CAoBN"}
@@ -3,7 +3,33 @@ import type { MethodDef } from '../types';
3
3
  export interface JwtPayload {
4
4
  [key: string]: unknown;
5
5
  }
6
- export declare function verifyJwt(token: string, secret: string): Promise<JwtPayload>;
6
+ /** Tuning for `verifyJwt`. */
7
+ export interface VerifyJwtOptions {
8
+ /** Clock-skew tolerance for `exp` / `nbf`, in seconds. Default `60`. */
9
+ leewaySeconds?: number;
10
+ /** Required `iss` claim — the token is rejected on mismatch. */
11
+ issuer?: string;
12
+ /** Required `aud` claim — the token is rejected if it does not carry it. */
13
+ audience?: string;
14
+ }
15
+ export declare function verifyJwt(token: string, secret: string, options?: VerifyJwtOptions): Promise<JwtPayload>;
16
+ /** Tuning for `signJwt`. */
17
+ export interface SignJwtOptions {
18
+ /** Lifetime in seconds — sets `exp` to now + this. Omit for no expiry. */
19
+ expiresInSec?: number;
20
+ /** `iss` claim. */
21
+ issuer?: string;
22
+ /** `aud` claim. */
23
+ audience?: string;
24
+ /** `sub` claim. */
25
+ subject?: string;
26
+ }
27
+ /**
28
+ * Sign a payload as an HS256 JWT — the issuing counterpart of `verifyJwt`.
29
+ * Used to mint OAuth access tokens whose audience binds them to one resource.
30
+ * `iat` is always set; `exp` / `iss` / `aud` / `sub` follow `options`.
31
+ */
32
+ export declare function signJwt(payload: JwtPayload, secret: string, options?: SignJwtOptions): Promise<string>;
7
33
  export declare function extractToken(req: Request, cookieName?: string): string | null;
8
34
  /**
9
35
  * Per-scope access predicate. `endpoint.scope` selects which rule applies:
@@ -17,6 +43,14 @@ export type AuthRule<TIdentity> = 'public' | 'authenticated' | ((identity: Await
17
43
  export interface AuthHookConfig<TIdentity> {
18
44
  /** Resolve the request identity — cookie / bearer + DB lookup. */
19
45
  resolve: (ctx: RuntimeContext) => Promise<TIdentity | null>;
46
+ /**
47
+ * Resolve identity on a non-HTTP context (a tool call) where there is no
48
+ * `req`. The transport (MCP / agent) has already authenticated the caller;
49
+ * this locates the identity it injected into `ctx` (via `buildMcpServer`'s
50
+ * `context`). Without it, a scoped tool call with no `req` **fails closed**
51
+ * — the scope rule sees no identity and `onAnonymous` rejects the call.
52
+ */
53
+ resolveFromContext?: (ctx: RuntimeContext) => TIdentity | null | Promise<TIdentity | null>;
20
54
  /** Access rule per scope; `endpoint.scope` is the key. */
21
55
  rules: Record<string, AuthRule<TIdentity>>;
22
56
  /** Scope applied when an endpoint declares none. */
@@ -35,6 +69,12 @@ export type AuthHook = (ctx: RuntimeContext, endpoint: MethodDef) => Promise<voi
35
69
  * Identity resolution and the scope vocabulary stay in the project; the
36
70
  * project annotates its `rules` object with `satisfies Record<MyScope, …>` to
37
71
  * keep scope coverage exhaustive.
72
+ *
73
+ * The hook runs on both surfaces: as `createServer`'s `beforeHandle` (HTTP) and
74
+ * as a tool mount's `lifecycle.beforeHandle` (MCP / agent). On HTTP it resolves
75
+ * identity from `ctx.req` via `resolve`; on a tool call — where there is no
76
+ * `req` — it uses `resolveFromContext`. If `resolveFromContext` is omitted, a
77
+ * scoped tool call has no identity and **fails closed** (never silently passes).
38
78
  */
39
79
  export declare function createAuthHook<TIdentity>(config: AuthHookConfig<TIdentity>): AuthHook;
40
80
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AASD,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA4ClF;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU7E;AAUD;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,CAAC,SAAS,IAC1B,QAAQ,GACR,eAAe,GACf,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AAExF,MAAM,WAAW,cAAc,CAAC,SAAS;IACvC,kEAAkE;IAClE,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC5D,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3C,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5E,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC;IAC1B,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,GAAG,QAAQ,CAuBrF;AAID;;;;GAIG;AACH,MAAM,WAAW,oBAAoB,CAAC,SAAS;IAC7C,4EAA4E;IAC5E,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;CACpE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAC5C,MAAM,EAAE,oBAAoB,CAAC,SAAS,CAAC,GACtC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAM7C"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/auth.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cookies.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAc1E;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CACtC;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,aAAkB,GACvB,MAAM,CASR;AAED,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,+EAA+E;IAC/E,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,gEAAgE;IAChE,KAAK,IAAI,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAahF"}
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cookies.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkB1E;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CACtC;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,aAAkB,GACvB,MAAM,CASR;AAED,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,+EAA+E;IAC/E,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,gEAAgE;IAChE,KAAK,IAAI,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAahF"}
@@ -4,6 +4,13 @@ export interface CorsConfig {
4
4
  methods?: string;
5
5
  headers?: string;
6
6
  }
7
+ /**
8
+ * Reject an unsafe CORS config at construction. `credentials: true` with a
9
+ * wildcard origin would reflect *any* caller's `Origin` with
10
+ * `Allow-Credentials: true` — every site could make authenticated cross-origin
11
+ * requests. Credentials require an explicit origin allow-list.
12
+ */
13
+ export declare function assertCorsConfig(config: CorsConfig): void;
7
14
  export declare function corsHeaders(config: CorsConfig, requestOrigin?: string | null): Record<string, string>;
8
15
  export declare function corsPreflightResponse(config: CorsConfig, req: Request): Response;
9
16
  //# sourceMappingURL=cors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmCxB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,QAAQ,CAKhF"}
1
+ {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAOzD;AAoBD,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsBxB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,QAAQ,CAKhF"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * PKCE (RFC 7636) verification for the OAuth authorization-code flow. The client
3
+ * sends a `code_challenge` at `/authorize` and the matching `code_verifier` at
4
+ * `/token`; the server confirms they correspond so an intercepted code is
5
+ * useless without the verifier.
6
+ */
7
+ /** The only PKCE method OAuth 2.1 permits for public clients. */
8
+ export type PkceMethod = 'S256';
9
+ /** Derive the S256 `code_challenge` from a `code_verifier`. */
10
+ export declare function deriveCodeChallenge(verifier: string): Promise<string>;
11
+ /**
12
+ * Verify a `code_verifier` against the stored S256 `code_challenge`. S256 is the
13
+ * only method OAuth 2.1 permits for public clients — `plain` is intentionally
14
+ * not supported (it offers no protection against a leaked challenge).
15
+ */
16
+ export declare function verifyPkce(verifier: string, challenge: string): Promise<boolean>;
17
+ //# sourceMappingURL=pkce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/pkce.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAQhC,+DAA+D;AAC/D,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG3E;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAItF"}