@silkweave/fastify 3.2.0 → 4.0.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/fastify.ts"],"mappings":";;;;;;;UAqBiB,qBAAA,SAA8B,kBAAA,CAAmB,MAAA,EAAQ,iBAAA;EACxE,IAAA;EACA,IAAA;EACA,IAAA,GAAO,UAAA;;EAEP,IAAA,GAAO,oBAAA;AAAA;AAAA,cA2KI,OAAA,EAAS,cAAA,CAAe,qBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/fastify.ts"],"mappings":";;;;;;;UAqBiB,qBAAA,SAA8B,kBAAA,CAAmB,MAAA,EAAQ,iBAAA;EACxE,IAAA;EACA,IAAA;EACA,IAAA,GAAO,UAAA;;EAEP,IAAA,GAAO,oBAAA;AAAA;AAAA,cAgNI,OAAA,EAAS,cAAA,CAAe,qBAAA"}
package/build/index.mjs CHANGED
@@ -28,10 +28,11 @@ function sendOAuth(reply, oauthRes) {
28
28
  }
29
29
  function mountOAuthRoutes(instance, auth) {
30
30
  const provider = auth.provider;
31
+ const callbackPath = auth.callbackPath ?? "/auth/callback";
31
32
  const paths = new Set([
32
33
  "/.well-known/oauth-authorization-server",
33
34
  "/authorize",
34
- "/auth/callback",
35
+ callbackPath,
35
36
  "/token",
36
37
  "/register"
37
38
  ]);
@@ -41,7 +42,7 @@ function mountOAuthRoutes(instance, auth) {
41
42
  instance.get("/authorize", async (req, reply) => {
42
43
  return sendOAuth(reply, await provider.authorize(toOAuthReq(req)));
43
44
  });
44
- instance.get("/auth/callback", async (req, reply) => {
45
+ instance.get(callbackPath, async (req, reply) => {
45
46
  return sendOAuth(reply, await provider.callback(toOAuthReq(req)));
46
47
  });
47
48
  instance.post("/token", async (req, reply) => {
@@ -70,30 +71,46 @@ function pickStreamFormat(acceptHeader) {
70
71
  if (acceptHeader.includes("application/x-ndjson") || acceptHeader.includes("application/ndjson")) return "ndjson";
71
72
  return null;
72
73
  }
74
+ function encodeChunk(format, chunk) {
75
+ const payload = JSON.stringify(chunk);
76
+ return format === "sse" ? `data: ${payload}\n\n` : `${payload}\n`;
77
+ }
78
+ function encodeStreamError(format, error) {
79
+ const payload = JSON.stringify({
80
+ error: error instanceof Error ? error.message : String(error),
81
+ name: error instanceof Error ? error.name : "Error"
82
+ });
83
+ return format === "sse" ? `event: error\ndata: ${payload}\n\n` : `${payload}\n`;
84
+ }
73
85
  async function streamAction(reply, format, action, input, context) {
74
86
  reply.raw.setHeader("Content-Type", format === "sse" ? "text/event-stream" : "application/x-ndjson");
75
87
  reply.raw.setHeader("Cache-Control", "no-cache, no-transform");
76
88
  reply.raw.setHeader("Connection", "keep-alive");
77
89
  reply.raw.flushHeaders?.();
78
90
  reply.hijack();
79
- const streamRun = action.run;
80
- const iter = streamRun(input, context);
91
+ const raw = reply.raw;
92
+ const iter = action.run(input, context);
93
+ const gone = { value: raw.destroyed || raw.writableEnded };
94
+ const onClose = () => {
95
+ gone.value = true;
96
+ };
97
+ raw.on("close", onClose);
98
+ const closed = once(raw, "close");
81
99
  try {
82
100
  for await (const chunk of iter) {
83
- const payload = JSON.stringify(chunk);
84
- const line = format === "sse" ? `data: ${payload}\n\n` : `${payload}\n`;
85
- if (!reply.raw.write(line)) await once(reply.raw, "drain");
101
+ if (gone.value) break;
102
+ if (!raw.write(encodeChunk(format, chunk))) {
103
+ await Promise.race([once(raw, "drain"), closed]);
104
+ if (gone.value) break;
105
+ }
86
106
  }
87
- if (format === "sse") reply.raw.write("event: done\ndata: {}\n\n");
107
+ if (!gone.value && format === "sse") raw.write("event: done\ndata: {}\n\n");
88
108
  } catch (error) {
89
- const payload = JSON.stringify({
90
- error: error instanceof Error ? error.message : String(error),
91
- name: error instanceof Error ? error.name : "Error"
92
- });
93
- if (format === "sse") reply.raw.write(`event: error\ndata: ${payload}\n\n`);
94
- else reply.raw.write(`${payload}\n`);
109
+ if (!gone.value) raw.write(encodeStreamError(format, error));
95
110
  } finally {
96
- reply.raw.end();
111
+ raw.off("close", onClose);
112
+ await iter.return?.().catch(() => {});
113
+ if (!raw.writableEnded) raw.end();
97
114
  }
98
115
  }
99
116
  function createActionLogger(instance) {
@@ -154,6 +171,23 @@ function splitInputSchema(action, method) {
154
171
  body: hasBody && Object.keys(body.properties).length ? body : void 0
155
172
  };
156
173
  }
174
+ /**
175
+ * Register the Scalar API-reference UI. It's an optional peer, so a missing
176
+ * module is skipped with a hint rather than crashing a headless deployment.
177
+ * Resolving the import here (vs. handing `register` a rejecting plugin promise)
178
+ * is what makes the absence catchable now instead of later during `listen`.
179
+ */
180
+ async function registerScalarDocs(instance) {
181
+ const scalar = await import("@scalar/fastify-api-reference").catch((error) => {
182
+ const code = error.code;
183
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
184
+ instance.log.info("@scalar/fastify-api-reference is not installed - skipping the API reference UI. Install it to enable the docs UI.");
185
+ return null;
186
+ }
187
+ throw error;
188
+ });
189
+ if (scalar) await instance.register(scalar.default, { routePrefix: "/" });
190
+ }
157
191
  const fastify = ({ host, port, auth, cors: corsConfig, ...fastifyOptions }) => {
158
192
  const instance = fastify$1(fastifyOptions);
159
193
  return (options, baseContext) => {
@@ -173,7 +207,7 @@ const fastify = ({ host, port, auth, cors: corsConfig, ...fastifyOptions }) => {
173
207
  description: options.description,
174
208
  version: options.version
175
209
  } } });
176
- await instance.register(import("@scalar/fastify-api-reference"), { routePrefix: "/" });
210
+ await registerScalarDocs(instance);
177
211
  if (auth?.authorizationServers?.length && auth.resourceUrl) instance.get("/.well-known/oauth-protected-resource", () => {
178
212
  return generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers, auth.requiredScopes);
179
213
  });
@@ -223,11 +257,11 @@ const fastify = ({ host, port, auth, cors: corsConfig, ...fastifyOptions }) => {
223
257
  request,
224
258
  ...authInfo ? { auth: authInfo } : {}
225
259
  });
226
- const input = resolveActionInput(action, {
260
+ const input = action.input.parse(resolveActionInput(action, {
227
261
  params: request.params,
228
262
  query: request.query,
229
263
  body: request.body
230
- });
264
+ }));
231
265
  if (streaming) {
232
266
  const format = pickStreamFormat(request.headers.accept);
233
267
  if (format) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["fastifyInstance"],"sources":["../src/adapter/fastify.ts"],"sourcesContent":["import type { FastifyCorsOptions } from '@fastify/cors'\nimport { AuthConfig, AuthInfo, generateProtectedResourceMetadata, OAuthRequest, OAuthResponse, validateToken } from '@silkweave/auth'\nimport { Action, actionMethod, ActionStreamRun, AdapterFactory, buildLogLevels, HttpMethod, isStreamingAction, Logger, LogLevel, methodHasBody, pathParamNames, resolveActionInput, runStreamingAction, SilkweaveContext, SilkweaveError, validateActionRouting } from '@silkweave/core'\nimport { once } from 'events'\nimport { FastifyBaseLogger, FastifyHttpOptions, fastify as fastifyInstance, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport { Server } from 'http'\nimport z from 'zod/v4'\n\ntype FastifyLogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'\n\nconst PINO_LEVEL_MAP: Record<LogLevel, FastifyLogLevel> = {\n emergency: 'fatal',\n alert: 'fatal',\n critical: 'fatal',\n error: 'error',\n warning: 'warn',\n notice: 'debug',\n info: 'info',\n debug: 'debug'\n}\n\nexport interface FastifyAdapterOptions extends FastifyHttpOptions<Server, FastifyBaseLogger> {\n host?: string\n port?: number\n auth?: AuthConfig\n /** CORS configuration. `false` to disable, `true`/`undefined` for permissive defaults, or a FastifyCorsOptions object. */\n cors?: FastifyCorsOptions | boolean\n}\n\nfunction toOAuthReq(request: FastifyRequest): OAuthRequest {\n return {\n method: request.method,\n url: new URL(request.url, `http://${request.hostname}`),\n headers: Object.fromEntries(Object.entries(request.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])),\n body: request.body as Record<string, string> | undefined\n }\n}\n\nfunction sendOAuth(reply: FastifyReply, oauthRes: OAuthResponse) {\n for (const [key, value] of Object.entries(oauthRes.headers)) { reply.header(key, value) }\n return reply.status(oauthRes.status).send(oauthRes.body)\n}\n\nfunction mountOAuthRoutes(instance: FastifyInstance, auth: AuthConfig): Set<string> {\n const provider = auth.provider!\n\n const paths = new Set([\n '/.well-known/oauth-authorization-server',\n '/authorize',\n '/auth/callback',\n '/token',\n '/register'\n ])\n\n instance.get('/.well-known/oauth-authorization-server', async (_req, reply) => {\n return sendOAuth(reply, provider.metadata())\n })\n instance.get('/authorize', async (req, reply) => {\n return sendOAuth(reply, await provider.authorize(toOAuthReq(req)))\n })\n instance.get('/auth/callback', async (req, reply) => {\n return sendOAuth(reply, await provider.callback(toOAuthReq(req)))\n })\n instance.post('/token', async (req, reply) => {\n return sendOAuth(reply, await provider.token(toOAuthReq(req)))\n })\n instance.post('/register', async (req, reply) => {\n return sendOAuth(reply, await provider.register(toOAuthReq(req)))\n })\n\n return paths\n}\n\nfunction mountAuthMiddleware(instance: FastifyInstance, auth: AuthConfig, oauthPaths: Set<string>, context: SilkweaveContext) {\n instance.decorateRequest('__silkweave_auth', undefined)\n instance.addHook('onRequest', async (request: FastifyRequest, reply: FastifyReply) => {\n if (request.url.startsWith('/.well-known/') || oauthPaths.has(request.url.split('?')[0])) { return }\n const result = await validateToken(request.headers.authorization, auth, context.fork({ request }))\n if (result.error) {\n for (const [key, value] of Object.entries(result.error.headers)) {\n reply.header(key, value)\n }\n return reply.status(result.error.statusCode).send(result.error.body)\n }\n if (result.auth) {\n (request as FastifyRequest & { __silkweave_auth?: AuthInfo }).__silkweave_auth = result.auth\n }\n })\n}\n\ntype StreamFormat = 'sse' | 'ndjson'\n\nfunction pickStreamFormat(acceptHeader: string | undefined): StreamFormat | null {\n if (!acceptHeader) { return null }\n if (acceptHeader.includes('text/event-stream')) { return 'sse' }\n if (acceptHeader.includes('application/x-ndjson') || acceptHeader.includes('application/ndjson')) { return 'ndjson' }\n return null\n}\n\nasync function streamAction(\n reply: FastifyReply,\n format: StreamFormat,\n action: Action,\n input: object,\n context: SilkweaveContext\n): Promise<void> {\n reply.raw.setHeader('Content-Type', format === 'sse' ? 'text/event-stream' : 'application/x-ndjson')\n reply.raw.setHeader('Cache-Control', 'no-cache, no-transform')\n reply.raw.setHeader('Connection', 'keep-alive')\n reply.raw.flushHeaders?.()\n reply.hijack()\n const streamRun = action.run as ActionStreamRun<object, unknown>\n const iter = streamRun(input, context)\n try {\n for await (const chunk of iter) {\n const payload = JSON.stringify(chunk)\n const line = format === 'sse' ? `data: ${payload}\\n\\n` : `${payload}\\n`\n if (!reply.raw.write(line)) {\n await once(reply.raw, 'drain')\n }\n }\n if (format === 'sse') {\n reply.raw.write('event: done\\ndata: {}\\n\\n')\n }\n } catch (error) {\n const payload = JSON.stringify({\n error: error instanceof Error ? error.message : String(error),\n name: error instanceof Error ? error.name : 'Error'\n })\n if (format === 'sse') {\n reply.raw.write(`event: error\\ndata: ${payload}\\n\\n`)\n } else {\n reply.raw.write(`${payload}\\n`)\n }\n } finally {\n reply.raw.end()\n }\n}\n\nfunction createActionLogger(instance: FastifyInstance): Logger {\n return {\n ...buildLogLevels((level, data) => {\n const pinoLevel = PINO_LEVEL_MAP[level] ?? 'info'\n instance.log[pinoLevel](data)\n }),\n progress: ({ progress, total, message }) => { instance.log.trace({ progress, total }, message) }\n }\n}\n\ninterface ObjectSchema {\n type: 'object'\n properties: Record<string, unknown>\n required: string[]\n}\n\n/**\n * Split an action's input JSON Schema into Fastify `params`/`querystring`/`body`\n * schemas based on its `path` placeholders and `queryParams`. Fastify's own AJV\n * validation, type coercion, and default-filling then apply per source, and the\n * three are merged back into a single input by `resolveActionInput` at runtime.\n */\nfunction splitInputSchema(action: Action, method: HttpMethod): {\n params?: ObjectSchema\n querystring?: ObjectSchema\n body?: ObjectSchema\n} {\n const json = z.toJSONSchema(action.input) as { properties?: Record<string, unknown>; required?: string[] }\n const properties = json.properties ?? {}\n const required = new Set(json.required ?? [])\n const pathParams = new Set(pathParamNames(action.path))\n const queryKeys = new Set((action.queryParams ?? []).map(String))\n const hasBody = methodHasBody(method)\n\n const params: ObjectSchema = { type: 'object', properties: {}, required: [] }\n const querystring: ObjectSchema = { type: 'object', properties: {}, required: [] }\n const body: ObjectSchema = { type: 'object', properties: {}, required: [] }\n\n for (const [key, prop] of Object.entries(properties)) {\n if (pathParams.has(key)) {\n params.properties[key] = prop\n params.required.push(key)\n } else if (!hasBody || queryKeys.has(key)) {\n querystring.properties[key] = prop\n if (required.has(key)) { querystring.required.push(key) }\n } else {\n body.properties[key] = prop\n if (required.has(key)) { body.required.push(key) }\n }\n }\n\n return {\n params: Object.keys(params.properties).length ? params : undefined,\n querystring: Object.keys(querystring.properties).length ? querystring : undefined,\n body: hasBody && Object.keys(body.properties).length ? body : undefined\n }\n}\n\nexport const fastify: AdapterFactory<FastifyAdapterOptions> = ({ host, port, auth, cors: corsConfig, ...fastifyOptions }) => {\n const instance = fastifyInstance(fastifyOptions)\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'fastify' })\n return {\n context,\n start: async (actions) => {\n if (corsConfig !== false) {\n const userConfig = corsConfig === true || corsConfig === undefined ? {} : corsConfig\n await instance.register(import('@fastify/cors'), { origin: '*', ...userConfig })\n }\n\n await instance.register(import('@fastify/swagger'), {\n openapi: {\n info: {\n title: options.name,\n description: options.description,\n version: options.version\n }\n }\n })\n await instance.register(import('@scalar/fastify-api-reference'), { routePrefix: '/' })\n\n if (auth?.authorizationServers?.length && auth.resourceUrl) {\n instance.get('/.well-known/oauth-protected-resource', () => {\n return generateProtectedResourceMetadata(auth.resourceUrl!, auth.authorizationServers!, auth.requiredScopes)\n })\n }\n\n const oauthPaths = auth?.provider ? mountOAuthRoutes(instance, auth) : new Set<string>()\n\n if (auth) {\n mountAuthMiddleware(instance, auth, oauthPaths, context)\n }\n\n instance.setErrorHandler((error, _request, reply) => {\n if (error instanceof SilkweaveError) {\n return reply.status(error.statusCode).send({ error: error.code, message: error.message })\n }\n if (error instanceof z.ZodError) {\n return reply.status(400).send({ error: 'validation_error', issues: error.issues })\n }\n // Fastify's schema (AJV) validation failures for body/querystring/params.\n const validation = (error as { validation?: unknown }).validation\n if (validation) {\n return reply.status(400).send({ error: 'validation_error', issues: validation })\n }\n instance.log.error(error)\n return reply.status(500).send({ error: 'internal', message: 'Internal server error' })\n })\n\n const logger = createActionLogger(instance)\n\n for (const action of actions) {\n validateActionRouting(action)\n const method = actionMethod(action)\n const url = action.path ? `/${action.path.replace(/^\\//, '')}` : `/${action.name}`\n const { params, querystring, body } = splitInputSchema(action, method)\n const streaming = isStreamingAction(action)\n instance.route({\n method,\n url,\n schema: {\n description: action.description,\n ...(params ? { params } : {}),\n ...(querystring ? { querystring } : {}),\n ...(body ? { body } : {}),\n response: {\n 200: { description: 'Successful response' }\n }\n },\n handler: async (request, reply) => {\n const authInfo = auth ? (request as FastifyRequest & { __silkweave_auth?: AuthInfo }).__silkweave_auth : undefined\n const actionContext = context.fork({ logger, request, ...(authInfo ? { auth: authInfo } : {}) })\n const input = resolveActionInput(action, {\n params: request.params as Record<string, string | undefined>,\n query: request.query as Record<string, unknown>,\n body: request.body\n })\n if (streaming) {\n const format = pickStreamFormat(request.headers.accept)\n if (format) {\n await streamAction(reply, format, action, input, actionContext)\n return reply\n }\n return runStreamingAction(action, input, actionContext)\n }\n return action.run(input, actionContext)\n }\n })\n }\n await instance.listen({ host, port })\n },\n stop: async () => { await instance.close() }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["fastifyInstance"],"sources":["../src/adapter/fastify.ts"],"sourcesContent":["import type { FastifyCorsOptions } from '@fastify/cors'\nimport { AuthConfig, AuthInfo, generateProtectedResourceMetadata, OAuthRequest, OAuthResponse, validateToken } from '@silkweave/auth'\nimport { Action, actionMethod, ActionStreamRun, AdapterFactory, buildLogLevels, HttpMethod, isStreamingAction, Logger, LogLevel, methodHasBody, pathParamNames, resolveActionInput, runStreamingAction, SilkweaveContext, SilkweaveError, validateActionRouting } from '@silkweave/core'\nimport { once } from 'events'\nimport { FastifyBaseLogger, FastifyHttpOptions, fastify as fastifyInstance, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport { Server } from 'http'\nimport z from 'zod/v4'\n\ntype FastifyLogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'\n\nconst PINO_LEVEL_MAP: Record<LogLevel, FastifyLogLevel> = {\n emergency: 'fatal',\n alert: 'fatal',\n critical: 'fatal',\n error: 'error',\n warning: 'warn',\n notice: 'debug',\n info: 'info',\n debug: 'debug'\n}\n\nexport interface FastifyAdapterOptions extends FastifyHttpOptions<Server, FastifyBaseLogger> {\n host?: string\n port?: number\n auth?: AuthConfig\n /** CORS configuration. `false` to disable, `true`/`undefined` for permissive defaults, or a FastifyCorsOptions object. */\n cors?: FastifyCorsOptions | boolean\n}\n\nfunction toOAuthReq(request: FastifyRequest): OAuthRequest {\n return {\n method: request.method,\n url: new URL(request.url, `http://${request.hostname}`),\n headers: Object.fromEntries(Object.entries(request.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])),\n body: request.body as Record<string, string> | undefined\n }\n}\n\nfunction sendOAuth(reply: FastifyReply, oauthRes: OAuthResponse) {\n for (const [key, value] of Object.entries(oauthRes.headers)) { reply.header(key, value) }\n return reply.status(oauthRes.status).send(oauthRes.body)\n}\n\nfunction mountOAuthRoutes(instance: FastifyInstance, auth: AuthConfig): Set<string> {\n const provider = auth.provider!\n // The provider builds its upstream redirect_uri as `${resourceUrl}${callbackPath}`,\n // so the callback route must match the configured callbackPath, not a hardcoded one.\n const callbackPath = auth.callbackPath ?? '/auth/callback'\n\n const paths = new Set([\n '/.well-known/oauth-authorization-server',\n '/authorize',\n callbackPath,\n '/token',\n '/register'\n ])\n\n instance.get('/.well-known/oauth-authorization-server', async (_req, reply) => {\n return sendOAuth(reply, provider.metadata())\n })\n instance.get('/authorize', async (req, reply) => {\n return sendOAuth(reply, await provider.authorize(toOAuthReq(req)))\n })\n instance.get(callbackPath, async (req, reply) => {\n return sendOAuth(reply, await provider.callback(toOAuthReq(req)))\n })\n instance.post('/token', async (req, reply) => {\n return sendOAuth(reply, await provider.token(toOAuthReq(req)))\n })\n instance.post('/register', async (req, reply) => {\n return sendOAuth(reply, await provider.register(toOAuthReq(req)))\n })\n\n return paths\n}\n\nfunction mountAuthMiddleware(instance: FastifyInstance, auth: AuthConfig, oauthPaths: Set<string>, context: SilkweaveContext) {\n instance.decorateRequest('__silkweave_auth', undefined)\n instance.addHook('onRequest', async (request: FastifyRequest, reply: FastifyReply) => {\n if (request.url.startsWith('/.well-known/') || oauthPaths.has(request.url.split('?')[0])) { return }\n const result = await validateToken(request.headers.authorization, auth, context.fork({ request }))\n if (result.error) {\n for (const [key, value] of Object.entries(result.error.headers)) {\n reply.header(key, value)\n }\n return reply.status(result.error.statusCode).send(result.error.body)\n }\n if (result.auth) {\n (request as FastifyRequest & { __silkweave_auth?: AuthInfo }).__silkweave_auth = result.auth\n }\n })\n}\n\ntype StreamFormat = 'sse' | 'ndjson'\n\nfunction pickStreamFormat(acceptHeader: string | undefined): StreamFormat | null {\n if (!acceptHeader) { return null }\n if (acceptHeader.includes('text/event-stream')) { return 'sse' }\n if (acceptHeader.includes('application/x-ndjson') || acceptHeader.includes('application/ndjson')) { return 'ndjson' }\n return null\n}\n\nfunction encodeChunk(format: StreamFormat, chunk: unknown): string {\n const payload = JSON.stringify(chunk)\n return format === 'sse' ? `data: ${payload}\\n\\n` : `${payload}\\n`\n}\n\nfunction encodeStreamError(format: StreamFormat, error: unknown): string {\n const payload = JSON.stringify({\n error: error instanceof Error ? error.message : String(error),\n name: error instanceof Error ? error.name : 'Error'\n })\n return format === 'sse' ? `event: error\\ndata: ${payload}\\n\\n` : `${payload}\\n`\n}\n\nasync function streamAction(\n reply: FastifyReply,\n format: StreamFormat,\n action: Action,\n input: object,\n context: SilkweaveContext\n): Promise<void> {\n reply.raw.setHeader('Content-Type', format === 'sse' ? 'text/event-stream' : 'application/x-ndjson')\n reply.raw.setHeader('Cache-Control', 'no-cache, no-transform')\n reply.raw.setHeader('Connection', 'keep-alive')\n reply.raw.flushHeaders?.()\n reply.hijack()\n const raw = reply.raw\n const iter = (action.run as ActionStreamRun<object, unknown>)(input, context)\n // On client disconnect the next write() returns false and a plain\n // `once(raw, 'drain')` would park forever - the generator would never resume,\n // its finally/cleanup would never run, and response/context stay pinned. Track\n // 'close' and race the drain against it so we bail and tear the generator down.\n const gone = { value: raw.destroyed || raw.writableEnded }\n const onClose = () => { gone.value = true }\n raw.on('close', onClose)\n const closed = once(raw, 'close')\n try {\n for await (const chunk of iter) {\n if (gone.value) { break }\n if (!raw.write(encodeChunk(format, chunk))) {\n await Promise.race([once(raw, 'drain'), closed])\n if (gone.value) { break }\n }\n }\n if (!gone.value && format === 'sse') { raw.write('event: done\\ndata: {}\\n\\n') }\n } catch (error) {\n if (!gone.value) { raw.write(encodeStreamError(format, error)) }\n } finally {\n raw.off('close', onClose)\n // Ask the generator to run its finally/cleanup (release DB handles etc).\n await iter.return?.().catch(() => { /* generator cleanup best-effort */ })\n if (!raw.writableEnded) { raw.end() }\n }\n}\n\nfunction createActionLogger(instance: FastifyInstance): Logger {\n return {\n ...buildLogLevels((level, data) => {\n const pinoLevel = PINO_LEVEL_MAP[level] ?? 'info'\n instance.log[pinoLevel](data)\n }),\n progress: ({ progress, total, message }) => { instance.log.trace({ progress, total }, message) }\n }\n}\n\ninterface ObjectSchema {\n type: 'object'\n properties: Record<string, unknown>\n required: string[]\n}\n\n/**\n * Split an action's input JSON Schema into Fastify `params`/`querystring`/`body`\n * schemas based on its `path` placeholders and `queryParams`. Fastify's own AJV\n * validation, type coercion, and default-filling then apply per source, and the\n * three are merged back into a single input by `resolveActionInput` at runtime.\n */\nfunction splitInputSchema(action: Action, method: HttpMethod): {\n params?: ObjectSchema\n querystring?: ObjectSchema\n body?: ObjectSchema\n} {\n const json = z.toJSONSchema(action.input) as { properties?: Record<string, unknown>; required?: string[] }\n const properties = json.properties ?? {}\n const required = new Set(json.required ?? [])\n const pathParams = new Set(pathParamNames(action.path))\n const queryKeys = new Set((action.queryParams ?? []).map(String))\n const hasBody = methodHasBody(method)\n\n const params: ObjectSchema = { type: 'object', properties: {}, required: [] }\n const querystring: ObjectSchema = { type: 'object', properties: {}, required: [] }\n const body: ObjectSchema = { type: 'object', properties: {}, required: [] }\n\n for (const [key, prop] of Object.entries(properties)) {\n if (pathParams.has(key)) {\n params.properties[key] = prop\n params.required.push(key)\n } else if (!hasBody || queryKeys.has(key)) {\n querystring.properties[key] = prop\n if (required.has(key)) { querystring.required.push(key) }\n } else {\n body.properties[key] = prop\n if (required.has(key)) { body.required.push(key) }\n }\n }\n\n return {\n params: Object.keys(params.properties).length ? params : undefined,\n querystring: Object.keys(querystring.properties).length ? querystring : undefined,\n body: hasBody && Object.keys(body.properties).length ? body : undefined\n }\n}\n\n/**\n * Register the Scalar API-reference UI. It's an optional peer, so a missing\n * module is skipped with a hint rather than crashing a headless deployment.\n * Resolving the import here (vs. handing `register` a rejecting plugin promise)\n * is what makes the absence catchable now instead of later during `listen`.\n */\nasync function registerScalarDocs(instance: FastifyInstance): Promise<void> {\n const scalar = await import('@scalar/fastify-api-reference').catch((error: unknown) => {\n const code = (error as { code?: string }).code\n if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {\n instance.log.info('@scalar/fastify-api-reference is not installed - skipping the API reference UI. Install it to enable the docs UI.')\n return null\n }\n throw error\n })\n if (scalar) {\n await instance.register(scalar.default, { routePrefix: '/' })\n }\n}\n\nexport const fastify: AdapterFactory<FastifyAdapterOptions> = ({ host, port, auth, cors: corsConfig, ...fastifyOptions }) => {\n const instance = fastifyInstance(fastifyOptions)\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'fastify' })\n return {\n context,\n start: async (actions) => {\n if (corsConfig !== false) {\n const userConfig = corsConfig === true || corsConfig === undefined ? {} : corsConfig\n await instance.register(import('@fastify/cors'), { origin: '*', ...userConfig })\n }\n\n await instance.register(import('@fastify/swagger'), {\n openapi: {\n info: {\n title: options.name,\n description: options.description,\n version: options.version\n }\n }\n })\n await registerScalarDocs(instance)\n\n if (auth?.authorizationServers?.length && auth.resourceUrl) {\n instance.get('/.well-known/oauth-protected-resource', () => {\n return generateProtectedResourceMetadata(auth.resourceUrl!, auth.authorizationServers!, auth.requiredScopes)\n })\n }\n\n const oauthPaths = auth?.provider ? mountOAuthRoutes(instance, auth) : new Set<string>()\n\n if (auth) {\n mountAuthMiddleware(instance, auth, oauthPaths, context)\n }\n\n instance.setErrorHandler((error, _request, reply) => {\n if (error instanceof SilkweaveError) {\n return reply.status(error.statusCode).send({ error: error.code, message: error.message })\n }\n if (error instanceof z.ZodError) {\n return reply.status(400).send({ error: 'validation_error', issues: error.issues })\n }\n // Fastify's schema (AJV) validation failures for body/querystring/params.\n const validation = (error as { validation?: unknown }).validation\n if (validation) {\n return reply.status(400).send({ error: 'validation_error', issues: validation })\n }\n instance.log.error(error)\n return reply.status(500).send({ error: 'internal', message: 'Internal server error' })\n })\n\n const logger = createActionLogger(instance)\n\n for (const action of actions) {\n validateActionRouting(action)\n const method = actionMethod(action)\n const url = action.path ? `/${action.path.replace(/^\\//, '')}` : `/${action.name}`\n const { params, querystring, body } = splitInputSchema(action, method)\n const streaming = isStreamingAction(action)\n instance.route({\n method,\n url,\n schema: {\n description: action.description,\n ...(params ? { params } : {}),\n ...(querystring ? { querystring } : {}),\n ...(body ? { body } : {}),\n response: {\n 200: { description: 'Successful response' }\n }\n },\n handler: async (request, reply) => {\n const authInfo = auth ? (request as FastifyRequest & { __silkweave_auth?: AuthInfo }).__silkweave_auth : undefined\n const actionContext = context.fork({ logger, request, ...(authInfo ? { auth: authInfo } : {}) })\n // Fastify's AJV only validates the JSON-Schema projection, which\n // cannot express Zod refinements/transforms. Parse the merged input\n // so .refine()/.email()/.transform() are enforced here as they are\n // over MCP/tRPC/CLI (a ZodError becomes a 400 via setErrorHandler).\n const input = action.input.parse(resolveActionInput(action, {\n params: request.params as Record<string, string | undefined>,\n query: request.query as Record<string, unknown>,\n body: request.body\n }))\n if (streaming) {\n const format = pickStreamFormat(request.headers.accept)\n if (format) {\n await streamAction(reply, format, action, input, actionContext)\n return reply\n }\n return runStreamingAction(action, input, actionContext)\n }\n return action.run(input, actionContext)\n }\n })\n }\n await instance.listen({ host, port })\n },\n stop: async () => { await instance.close() }\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/fastify",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "Silkweave Fastify REST Adapter",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.silkweave.dev",
@@ -28,14 +28,22 @@
28
28
  "dependencies": {
29
29
  "@fastify/cors": "^11.2.0",
30
30
  "@fastify/swagger": "^9.7.0",
31
- "@scalar/fastify-api-reference": "^1.49.0",
32
31
  "fastify": "^5.8.2",
33
32
  "zod": "^3.25.0",
34
- "@silkweave/auth": "3.2.0",
35
- "@silkweave/core": "3.2.0"
33
+ "@silkweave/auth": "4.0.0",
34
+ "@silkweave/core": "4.0.0"
35
+ },
36
+ "peerDependencies": {
37
+ "@scalar/fastify-api-reference": "^1.49.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "@scalar/fastify-api-reference": {
41
+ "optional": true
42
+ }
36
43
  },
37
44
  "devDependencies": {
38
45
  "@eslint/js": "^10.0.1",
46
+ "@scalar/fastify-api-reference": "^1.49.0",
39
47
  "@stylistic/eslint-plugin": "^5.10.0",
40
48
  "@types/node": "^22.0.0",
41
49
  "eslint": "^10.4.0",