@silkweave/fastify 1.3.0 → 1.3.2

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,18 +1,17 @@
1
-
2
- 
3
- > @silkweave/fastify@1.3.0 build /Users/atomic/projects/ai/silkweave/packages/fastify
4
- > tsup
5
-
6
- CLI Building entry: src/index.ts
7
- CLI Using tsconfig: tsconfig.json
8
- CLI tsup v8.5.1
9
- CLI Using tsup config: /Users/atomic/projects/ai/silkweave/packages/fastify/tsup.config.ts
10
- CLI Target: es2022
11
- CLI Cleaning output folder
12
- ESM Build start
13
- ESM build/index.js 4.95 KB
14
- ESM build/index.js.map 8.98 KB
15
- ESM ⚡️ Build success in 9ms
16
- DTS Build start
17
- DTS ⚡️ Build success in 975ms
18
- DTS build/index.d.ts 455.00 B
1
+
2
+ > @silkweave/fastify@1.3.1 build /Users/atomic/projects/ai/silkweave/packages/fastify
3
+ > tsup
4
+
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.1
8
+ CLI Using tsup config: /Users/atomic/projects/ai/silkweave/packages/fastify/tsup.config.ts
9
+ CLI Target: es2022
10
+ CLI Cleaning output folder
11
+ ESM Build start
12
+ ESM build/index.js 5.31 KB
13
+ ESM build/index.js.map 10.01 KB
14
+ ESM ⚡️ Build success in 6ms
15
+ DTS Build start
16
+ DTS ⚡️ Build success in 898ms
17
+ DTS build/index.d.ts 455.00 B
@@ -1,13 +1,12 @@
1
-
2
- 
3
- > @silkweave/fastify@1.3.0 check /Users/atomic/projects/ai/silkweave/packages/fastify
4
- > pnpm lint && pnpm typecheck
5
-
6
-
7
- > @silkweave/fastify@1.3.0 lint /Users/atomic/projects/ai/silkweave/packages/fastify
8
- > eslint
9
-
10
-
11
- > @silkweave/fastify@1.3.0 typecheck /Users/atomic/projects/ai/silkweave/packages/fastify
12
- > tsc --noEmit
13
-
1
+
2
+ > @silkweave/fastify@1.3.1 check /Users/atomic/projects/ai/silkweave/packages/fastify
3
+ > pnpm lint && pnpm typecheck
4
+
5
+
6
+ > @silkweave/fastify@1.3.1 lint /Users/atomic/projects/ai/silkweave/packages/fastify
7
+ > eslint
8
+
9
+
10
+ > @silkweave/fastify@1.3.1 typecheck /Users/atomic/projects/ai/silkweave/packages/fastify
11
+ > tsc --noEmit
12
+
@@ -1,6 +1,5 @@
1
1
 
2
2
  
3
- > @silkweave/fastify@1.3.0 lint /Users/atomic/projects/ai/silkweave/packages/fastify
3
+ > @silkweave/fastify@1.3.1 lint /Users/atomic/projects/ai/silkweave/packages/fastify
4
4
  > eslint
5
5
 
6
-  ELIFECYCLE  Command failed.
@@ -1,26 +1,26 @@
1
1
 
2
2
  
3
- > @silkweave/fastify@1.3.0 prepack /Users/atomic/projects/ai/mcphero/packages/fastify
3
+ > @silkweave/fastify@1.3.0 prepack /Users/atomic/projects/ai/silkweave/packages/fastify
4
4
  > pnpm clean && pnpm build
5
5
 
6
6
 
7
- > @silkweave/fastify@1.3.0 clean /Users/atomic/projects/ai/mcphero/packages/fastify
7
+ > @silkweave/fastify@1.3.0 clean /Users/atomic/projects/ai/silkweave/packages/fastify
8
8
  > rimraf build
9
9
 
10
10
 
11
- > @silkweave/fastify@1.3.0 build /Users/atomic/projects/ai/mcphero/packages/fastify
11
+ > @silkweave/fastify@1.3.0 build /Users/atomic/projects/ai/silkweave/packages/fastify
12
12
  > tsup
13
13
 
14
14
  CLI Building entry: src/index.ts
15
15
  CLI Using tsconfig: tsconfig.json
16
16
  CLI tsup v8.5.1
17
- CLI Using tsup config: /Users/atomic/projects/ai/mcphero/packages/fastify/tsup.config.ts
17
+ CLI Using tsup config: /Users/atomic/projects/ai/silkweave/packages/fastify/tsup.config.ts
18
18
  CLI Target: es2022
19
19
  CLI Cleaning output folder
20
20
  ESM Build start
21
- ESM build/index.js 4.95 KB
22
- ESM build/index.js.map 8.98 KB
23
- ESM ⚡️ Build success in 6ms
21
+ ESM build/index.js 5.54 KB
22
+ ESM build/index.js.map 9.94 KB
23
+ ESM ⚡️ Build success in 13ms
24
24
  DTS Build start
25
- DTS ⚡️ Build success in 782ms
25
+ DTS ⚡️ Build success in 984ms
26
26
  DTS build/index.d.ts 455.00 B
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @silkweave/fastify
2
2
 
3
- Fastify REST adapter for [Silkweave](https://github.com/atomicbi/silkweave) — expose your actions as a REST API with auto-generated OpenAPI/Swagger documentation.
3
+ Fastify REST adapter for [Silkweave](https://github.com/silkweave/silkweave) — expose your actions as a REST API with auto-generated OpenAPI/Swagger documentation.
4
4
 
5
5
  ## Install
6
6
 
@@ -56,5 +56,5 @@ fastify({
56
56
 
57
57
  ## See Also
58
58
 
59
- - [Silkweave README](https://github.com/atomicbi/silkweave) — Full documentation
59
+ - [Silkweave README](https://github.com/silkweave/silkweave) — Full documentation
60
60
  - [`@silkweave/core`](https://www.npmjs.com/package/@silkweave/core) — Core library
package/build/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  // src/adapter/fastify.ts
2
2
  import { generateProtectedResourceMetadata, validateToken } from "@silkweave/auth";
3
+ import { SilkweaveError } from "@silkweave/core";
3
4
  import { buildLogLevels } from "@silkweave/logger";
4
5
  import { fastify as fastifyInstance } from "fastify";
6
+ import z from "zod";
5
7
  var PINO_LEVEL_MAP = {
6
8
  emergency: "fatal",
7
9
  alert: "fatal",
@@ -12,6 +14,75 @@ var PINO_LEVEL_MAP = {
12
14
  info: "info",
13
15
  debug: "debug"
14
16
  };
17
+ function toOAuthReq(request) {
18
+ return {
19
+ method: request.method,
20
+ url: new URL(request.url, `http://${request.hostname}`),
21
+ headers: Object.fromEntries(Object.entries(request.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])),
22
+ body: request.body
23
+ };
24
+ }
25
+ function sendOAuth(reply, oauthRes) {
26
+ for (const [key, value] of Object.entries(oauthRes.headers)) {
27
+ reply.header(key, value);
28
+ }
29
+ return reply.status(oauthRes.status).send(oauthRes.body);
30
+ }
31
+ function mountOAuthRoutes(instance, auth) {
32
+ const provider = auth.provider;
33
+ const paths = /* @__PURE__ */ new Set([
34
+ "/.well-known/oauth-authorization-server",
35
+ "/authorize",
36
+ "/auth/callback",
37
+ "/token",
38
+ "/register"
39
+ ]);
40
+ instance.get("/.well-known/oauth-authorization-server", async (_req, reply) => {
41
+ return sendOAuth(reply, provider.metadata());
42
+ });
43
+ instance.get("/authorize", async (req, reply) => {
44
+ return sendOAuth(reply, await provider.authorize(toOAuthReq(req)));
45
+ });
46
+ instance.get("/auth/callback", async (req, reply) => {
47
+ return sendOAuth(reply, await provider.callback(toOAuthReq(req)));
48
+ });
49
+ instance.post("/token", async (req, reply) => {
50
+ return sendOAuth(reply, await provider.token(toOAuthReq(req)));
51
+ });
52
+ instance.post("/register", async (req, reply) => {
53
+ return sendOAuth(reply, await provider.register(toOAuthReq(req)));
54
+ });
55
+ return paths;
56
+ }
57
+ function mountAuthMiddleware(instance, auth, oauthPaths) {
58
+ instance.decorateRequest("__silkweave_auth", void 0);
59
+ instance.addHook("onRequest", async (request, reply) => {
60
+ if (request.url.startsWith("/.well-known/") || oauthPaths.has(request.url.split("?")[0])) {
61
+ return;
62
+ }
63
+ const result = await validateToken(request.headers.authorization, auth);
64
+ if (result.error) {
65
+ for (const [key, value] of Object.entries(result.error.headers)) {
66
+ reply.header(key, value);
67
+ }
68
+ return reply.status(result.error.statusCode).send(result.error.body);
69
+ }
70
+ if (result.auth) {
71
+ request.__silkweave_auth = result.auth;
72
+ }
73
+ });
74
+ }
75
+ function createActionLogger(instance) {
76
+ return {
77
+ ...buildLogLevels((level, data) => {
78
+ const pinoLevel = PINO_LEVEL_MAP[level] ?? "info";
79
+ instance.log[pinoLevel](data);
80
+ }),
81
+ progress: ({ progress, total, message }) => {
82
+ instance.log.trace({ progress, total }, message);
83
+ }
84
+ };
85
+ }
15
86
  var fastify = ({ host, port, auth, ...fastifyOptions }) => {
16
87
  const instance = fastifyInstance(fastifyOptions);
17
88
  return (options, baseContext) => {
@@ -34,60 +105,21 @@ var fastify = ({ host, port, auth, ...fastifyOptions }) => {
34
105
  return generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers);
35
106
  });
36
107
  }
37
- const oauthPaths = /* @__PURE__ */ new Set();
38
- if (auth?.provider) {
39
- const provider = auth.provider;
40
- const toOAuthReq = (request) => ({
41
- method: request.method,
42
- url: new URL(request.url, `http://${request.hostname}`),
43
- headers: Object.fromEntries(Object.entries(request.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])),
44
- body: request.body
45
- });
46
- const sendOAuth = (reply, oauthRes) => {
47
- for (const [key, value] of Object.entries(oauthRes.headers)) {
48
- reply.header(key, value);
49
- }
50
- return reply.status(oauthRes.status).send(oauthRes.body);
51
- };
52
- oauthPaths.add("/.well-known/oauth-authorization-server");
53
- oauthPaths.add("/authorize");
54
- oauthPaths.add("/auth/callback");
55
- oauthPaths.add("/token");
56
- oauthPaths.add("/register");
57
- instance.get("/.well-known/oauth-authorization-server", async (_req, reply) => {
58
- return sendOAuth(reply, provider.metadata());
59
- });
60
- instance.get("/authorize", async (req, reply) => {
61
- return sendOAuth(reply, await provider.authorize(toOAuthReq(req)));
62
- });
63
- instance.get("/auth/callback", async (req, reply) => {
64
- return sendOAuth(reply, await provider.callback(toOAuthReq(req)));
65
- });
66
- instance.post("/token", async (req, reply) => {
67
- return sendOAuth(reply, await provider.token(toOAuthReq(req)));
68
- });
69
- instance.post("/register", async (req, reply) => {
70
- return sendOAuth(reply, await provider.register(toOAuthReq(req)));
71
- });
72
- }
108
+ const oauthPaths = auth?.provider ? mountOAuthRoutes(instance, auth) : /* @__PURE__ */ new Set();
73
109
  if (auth) {
74
- instance.decorateRequest("__silkweave_auth", void 0);
75
- instance.addHook("onRequest", async (request, reply) => {
76
- if (request.url.startsWith("/.well-known/") || oauthPaths.has(request.url.split("?")[0])) {
77
- return;
78
- }
79
- const result = await validateToken(request.headers.authorization, auth);
80
- if (result.error) {
81
- for (const [key, value] of Object.entries(result.error.headers)) {
82
- reply.header(key, value);
83
- }
84
- return reply.status(result.error.statusCode).send(result.error.body);
85
- }
86
- if (result.auth) {
87
- request.__silkweave_auth = result.auth;
88
- }
89
- });
110
+ mountAuthMiddleware(instance, auth, oauthPaths);
90
111
  }
112
+ instance.setErrorHandler((error, _request, reply) => {
113
+ if (error instanceof SilkweaveError) {
114
+ return reply.status(error.statusCode).send({ error: error.code, message: error.message });
115
+ }
116
+ if (error instanceof z.ZodError) {
117
+ return reply.status(400).send({ error: "validation_error", issues: error.issues });
118
+ }
119
+ instance.log.error(error);
120
+ return reply.status(500).send({ error: "internal", message: "Internal server error" });
121
+ });
122
+ const logger = createActionLogger(instance);
91
123
  for (const action of actions) {
92
124
  const schema = action.input.toJSONSchema();
93
125
  instance.post(`/${action.name}`, {
@@ -103,15 +135,6 @@ var fastify = ({ host, port, auth, ...fastifyOptions }) => {
103
135
  }
104
136
  }
105
137
  }, (request) => {
106
- const logger = {
107
- ...buildLogLevels((level, data) => {
108
- const pinoLevel = PINO_LEVEL_MAP[level] ?? "info";
109
- instance.log[pinoLevel](data);
110
- }),
111
- progress: ({ progress, total, message }) => {
112
- instance.log.trace({ progress, total }, message);
113
- }
114
- };
115
138
  const authInfo = auth ? request.__silkweave_auth : void 0;
116
139
  return action.run(request.body, context.fork({ logger, request, ...authInfo ? { auth: authInfo } : {} }));
117
140
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter/fastify.ts"],"sourcesContent":["import { AuthConfig, AuthInfo, generateProtectedResourceMetadata, OAuthRequest, OAuthResponse, validateToken } from '@silkweave/auth'\nimport { AdapterFactory } from '@silkweave/core'\nimport { buildLogLevels, Logger, LogLevel } from '@silkweave/logger'\nimport { FastifyBaseLogger, FastifyHttpOptions, fastify as fastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport { Server } from 'http'\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}\n\nexport const fastify: AdapterFactory<FastifyAdapterOptions> = ({ host, port, auth, ...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 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: '/' as `/${string}` })\n\n if (auth?.authorizationServers?.length && auth.resourceUrl) {\n instance.get('/.well-known/oauth-protected-resource', () => {\n return generateProtectedResourceMetadata(auth.resourceUrl!, auth.authorizationServers!)\n })\n }\n\n const oauthPaths = new Set<string>()\n if (auth?.provider) {\n const provider = auth.provider\n const toOAuthReq = (request: FastifyRequest): OAuthRequest => ({\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 const 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\n oauthPaths.add('/.well-known/oauth-authorization-server')\n oauthPaths.add('/authorize')\n oauthPaths.add('/auth/callback')\n oauthPaths.add('/token')\n oauthPaths.add('/register')\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\n if (auth) {\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)\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\n for (const action of actions) {\n const schema = action.input.toJSONSchema()\n instance.post(`/${action.name}`, {\n schema: {\n description: action.description,\n body: {\n type: 'object',\n properties: schema.properties,\n required: schema.required\n },\n response: {\n 200: { description: 'Successful response' }\n }\n }\n }, (request) => {\n const logger: Logger = {\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 const authInfo = auth ? (request as FastifyRequest & { __silkweave_auth?: AuthInfo }).__silkweave_auth : undefined\n return action.run(request.body, context.fork({ logger, request, ...(authInfo ? { auth: authInfo } : {}) }))\n })\n }\n await instance.listen({ host, port })\n },\n stop: async () => { await instance.close() }\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/adapter/fastify.ts"],"sourcesContent":["import { AuthConfig, AuthInfo, generateProtectedResourceMetadata, OAuthRequest, OAuthResponse, validateToken } from '@silkweave/auth'\nimport { AdapterFactory, SilkweaveError } from '@silkweave/core'\nimport { buildLogLevels, Logger, LogLevel } from '@silkweave/logger'\nimport { FastifyBaseLogger, FastifyHttpOptions, fastify as fastifyInstance, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport { Server } from 'http'\nimport z from 'zod'\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}\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>) {\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)\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\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\nexport const fastify: AdapterFactory<FastifyAdapterOptions> = ({ host, port, auth, ...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 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: '/' as `/${string}` })\n\n if (auth?.authorizationServers?.length && auth.resourceUrl) {\n instance.get('/.well-known/oauth-protected-resource', () => {\n return generateProtectedResourceMetadata(auth.resourceUrl!, auth.authorizationServers!)\n })\n }\n\n const oauthPaths = auth?.provider ? mountOAuthRoutes(instance, auth) : new Set<string>()\n\n if (auth) {\n mountAuthMiddleware(instance, auth, oauthPaths)\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 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 const schema = action.input.toJSONSchema()\n instance.post(`/${action.name}`, {\n schema: {\n description: action.description,\n body: {\n type: 'object',\n properties: schema.properties,\n required: schema.required\n },\n response: {\n 200: { description: 'Successful response' }\n }\n }\n }, (request) => {\n const authInfo = auth ? (request as FastifyRequest & { __silkweave_auth?: AuthInfo }).__silkweave_auth : undefined\n return action.run(request.body, context.fork({ logger, request, ...(authInfo ? { auth: authInfo } : {}) }))\n })\n }\n await instance.listen({ host, port })\n },\n stop: async () => { await instance.close() }\n }\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@silkweave/fastify",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Silkweave Fastify REST Adapter",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+ssh://git@github.com/atomicbi/silkweave.git",
7
+ "url": "git+ssh://git@github.com/silkweave/silkweave.git",
8
8
  "directory": "packages/fastify"
9
9
  },
10
10
  "type": "module",
@@ -15,9 +15,9 @@
15
15
  "@scalar/fastify-api-reference": "^1.49.0",
16
16
  "fastify": "^5.8.2",
17
17
  "zod": "^4.3.6",
18
- "@silkweave/core": "1.3.0",
19
- "@silkweave/auth": "1.3.0",
20
- "@silkweave/logger": "1.3.0"
18
+ "@silkweave/auth": "1.3.2",
19
+ "@silkweave/core": "1.3.2",
20
+ "@silkweave/logger": "1.3.2"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@eslint/js": "^10.0.1",