@silkweave/trpc 3.2.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.d.mts CHANGED
@@ -78,7 +78,8 @@ declare function resolveAuth(auth: AuthConfig | undefined, authHeader: string |
78
78
  declare function mapError(error: unknown): TRPCError;
79
79
  //#endregion
80
80
  //#region src/lib/inferRouter.d.ts
81
- type CamelCase<S extends string> = S extends `${infer A}-${infer B}` ? `${A}${Capitalize<CamelCase<B>>}` : (S extends `${infer A}_${infer B}` ? `${A}${Capitalize<CamelCase<B>>}` : S);
81
+ type CamelJoin<S extends string> = S extends `${infer A}${'.' | '-' | '_' | ' '}${infer B}` ? `${A}${Capitalize<CamelJoin<B>>}` : S;
82
+ type CamelCase<S extends string> = Uncapitalize<CamelJoin<S>>;
82
83
  type ChunkOf<A extends Action> = A['run'] extends ((...args: any[]) => AsyncGenerator<infer C, unknown, unknown>) ? C : never;
83
84
  type IsStreaming<A extends Action> = A['run'] extends ((...args: any[]) => AsyncGenerator<unknown, unknown, unknown>) ? true : false;
84
85
  type ActionToProcedure<A extends Action> = IsStreaming<A> extends true ? TRPCSubscriptionProcedure<{
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/fetch.ts","../src/adapter/trpc.ts","../src/lib/buildRouter.ts","../src/lib/createContext.ts","../src/lib/errors.ts","../src/lib/inferRouter.ts"],"mappings":";;;;;;;;UAMiB,uBAAA;;EAEf,QAAA;EACA,IAAA,GAAO,UAAA;AAAA;AAAA,KAGG,YAAA,IAAgB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;AAAA,UAExC,gBAAA;EACf,OAAA,EAAS,gBAAA;EACT,OAAA,EAAS,YAAA;EACT,GAAA,EAAK,YAAA;EACL,IAAA,EAAM,YAAA;AAAA;;;;AANR;;;;;;;;;iBAqBgB,SAAA,CAAU,OAAA,GAAS,uBAAA,GAA+B,gBAAA;;;UCzBjD,kBAAA;EACf,IAAA;EACA,IAAA;;EAEA,QAAA;EDNe;ECQf,IAAA,GAAO,WAAA;EACP,IAAA,GAAO,UAAA;AAAA;AAAA,cAWI,IAAA,EAAM,cAAA,CAAe,kBAAA;;;UCpBjB,kBAAA;EACf,gBAAA,EAAkB,gBAAA;AAAA;AAAA,iBAGJ,WAAA,CAAY,OAAA,EAAS,MAAA,qBAAQ,eAAA;;;;;;;;iBCI7B,kBAAA,CAAA,GAAsB,MAAA;AAAA,UASrB,gBAAA;EACf,UAAA;EACA,OAAA,EAAS,MAAA;EACT,IAAA;IAAQ,KAAA;IAAe,iBAAA;EAAA;AAAA;AAAA,KAGb,YAAA;EACN,IAAA;EAAY,QAAA,GAAW,QAAA;AAAA;EACvB,IAAA;EAAe,KAAA,EAAO,gBAAA;AAAA;AAAA,iBAEN,WAAA,CACpB,IAAA,EAAM,UAAA,cACN,UAAA,6BACA,OAAA,EAAS,gBAAA,GACR,OAAA,CAAQ,YAAA;;;iBCbK,QAAA,CAAS,KAAA,YAAiB,SAAA;;;KCpBrC,SAAA,qBAA8B,CAAA,sCAC5B,CAAA,GAAI,UAAA,CAAW,SAAA,CAAU,CAAA,QAC3B,CAAA,sCACI,CAAA,GAAI,UAAA,CAAW,SAAA,CAAU,CAAA,OAC5B,CAAA;AAAA,KAGD,OAAA,WAAkB,MAAA,IAAU,CAAA,qBAAqB,IAAA,YAAgB,cAAA,+BAA4C,CAAA;AAAA,KAG7G,WAAA,WAAsB,MAAA,IAAU,CAAA,qBAAqB,IAAA,YAAgB,cAAA;AAAA,KAErE,iBAAA,WAA4B,MAAA,IAAU,WAAA,CAAY,CAAA,iBACnD,yBAAA;EACA,IAAA;EACA,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,CAAA;EACf,MAAA,EAAQ,OAAA,CAAQ,CAAA;AAAA,sBAEC,WAAA,CAAY,CAAA,YAC3B,kBAAA;EACA,IAAA;EACA,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,CAAA;EACf,MAAA,EAAQ,OAAA,CAAQ,UAAA,CAAW,CAAA;AAAA,KAE3B,qBAAA;EACA,IAAA;EACA,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,CAAA;EACf,MAAA,EAAQ,OAAA,CAAQ,UAAA,CAAW,CAAA;AAAA;AAAA,KAG5B,qBAAA,iBAAsC,MAAA,SAAe,MAAA,mBAC5C,OAAA,aAAoB,SAAA,CAAU,OAAA,CAAQ,CAAA,aAAc,iBAAA,CAAkB,OAAA,CAAQ,CAAA;AAAA,KAGvF,aAAA;EACH,GAAA;EACA,IAAA;EACA,UAAA;EACA,WAAA;AAAA,IACE,gBAAA;;;;AL7BJ;;;;;;KKwCY,eAAA,MAAqB,CAAA,SAAU,SAAA,kBACvC,eAAA,CAAgB,aAAA,EAAe,qBAAA,CAAsB,OAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/fetch.ts","../src/adapter/trpc.ts","../src/lib/buildRouter.ts","../src/lib/createContext.ts","../src/lib/errors.ts","../src/lib/inferRouter.ts"],"mappings":";;;;;;;;UAMiB,uBAAA;;EAEf,QAAA;EACA,IAAA,GAAO,UAAA;AAAA;AAAA,KAGG,YAAA,IAAgB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;AAAA,UAExC,gBAAA;EACf,OAAA,EAAS,gBAAA;EACT,OAAA,EAAS,YAAA;EACT,GAAA,EAAK,YAAA;EACL,IAAA,EAAM,YAAA;AAAA;;;;AANR;;;;;;;;;iBAqBgB,SAAA,CAAU,OAAA,GAAS,uBAAA,GAA+B,gBAAA;;;UCzBjD,kBAAA;EACf,IAAA;EACA,IAAA;;EAEA,QAAA;EDNe;ECQf,IAAA,GAAO,WAAA;EACP,IAAA,GAAO,UAAA;AAAA;AAAA,cAWI,IAAA,EAAM,cAAA,CAAe,kBAAA;;;UCpBjB,kBAAA;EACf,gBAAA,EAAkB,gBAAA;AAAA;AAAA,iBAGJ,WAAA,CAAY,OAAA,EAAS,MAAA,qBAAQ,eAAA;;;;;;;;iBCK7B,kBAAA,CAAA,GAAsB,MAAA;AAAA,UASrB,gBAAA;EACf,UAAA;EACA,OAAA,EAAS,MAAA;EACT,IAAA;IAAQ,KAAA;IAAe,iBAAA;EAAA;AAAA;AAAA,KAGb,YAAA;EACN,IAAA;EAAY,QAAA,GAAW,QAAA;AAAA;EACvB,IAAA;EAAe,KAAA,EAAO,gBAAA;AAAA;AAAA,iBAEN,WAAA,CACpB,IAAA,EAAM,UAAA,cACN,UAAA,6BACA,OAAA,EAAS,gBAAA,GACR,OAAA,CAAQ,YAAA;;;iBCdK,QAAA,CAAS,KAAA,YAAiB,SAAA;;;KChBrC,SAAA,qBAA8B,CAAA,6DAC5B,CAAA,GAAI,UAAA,CAAW,SAAA,CAAU,CAAA,OAC5B,CAAA;AAAA,KACC,SAAA,qBAA8B,YAAA,CAAa,SAAA,CAAU,CAAA;AAAA,KAGrD,OAAA,WAAkB,MAAA,IAAU,CAAA,qBAAqB,IAAA,YAAgB,cAAA,+BAA4C,CAAA;AAAA,KAG7G,WAAA,WAAsB,MAAA,IAAU,CAAA,qBAAqB,IAAA,YAAgB,cAAA;AAAA,KAErE,iBAAA,WAA4B,MAAA,IAAU,WAAA,CAAY,CAAA,iBACnD,yBAAA;EACA,IAAA;EACA,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,CAAA;EACf,MAAA,EAAQ,OAAA,CAAQ,CAAA;AAAA,sBAEC,WAAA,CAAY,CAAA,YAC3B,kBAAA;EACA,IAAA;EACA,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,CAAA;EACf,MAAA,EAAQ,OAAA,CAAQ,UAAA,CAAW,CAAA;AAAA,KAE3B,qBAAA;EACA,IAAA;EACA,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,CAAA;EACf,MAAA,EAAQ,OAAA,CAAQ,UAAA,CAAW,CAAA;AAAA;AAAA,KAG5B,qBAAA,iBAAsC,MAAA,SAAe,MAAA,mBAC5C,OAAA,aAAoB,SAAA,CAAU,OAAA,CAAQ,CAAA,aAAc,iBAAA,CAAkB,OAAA,CAAQ,CAAA;AAAA,KAGvF,aAAA;EACH,GAAA;EACA,IAAA;EACA,UAAA;EACA,WAAA;AAAA,IACE,gBAAA;;;ALhCJ;;;;;;;KK2CY,eAAA,MAAqB,CAAA,SAAU,SAAA,kBACvC,eAAA,CAAgB,aAAA,EAAe,qBAAA,CAAsB,OAAA"}
package/build/index.mjs CHANGED
@@ -110,6 +110,47 @@ async function resolveAuth(auth, authHeader, context) {
110
110
  authInfo: result.auth
111
111
  };
112
112
  }
113
+ /**
114
+ * Carries the OAuth challenge (status + `WWW-Authenticate`/resource-metadata
115
+ * headers) from a failed `createContext` to `authResponseMeta`. Attached as the
116
+ * `cause` of a `TRPCError` so the tRPC handler owns the whole response
117
+ * lifecycle - the adapter must never write/end the raw response itself (doing so
118
+ * races tRPC's own error write and crashes the process with
119
+ * `ERR_STREAM_WRITE_AFTER_END`).
120
+ */
121
+ var AuthChallengeError = class extends Error {
122
+ constructor(payload) {
123
+ super(payload.body.error_description || payload.body.error || "Unauthorized");
124
+ this.payload = payload;
125
+ this.name = "AuthChallengeError";
126
+ }
127
+ };
128
+ /**
129
+ * Throw a resolved auth error as a `TRPCError` so the tRPC handler (standalone
130
+ * or fetch) produces the response. `authResponseMeta` then upgrades that
131
+ * response to the OAuth status + challenge headers via the `AuthChallengeError`
132
+ * cause. Never write to `res` directly from `createContext`.
133
+ */
134
+ function throwAuthError(payload) {
135
+ throw new TRPCError({
136
+ code: payload.statusCode === 403 ? "FORBIDDEN" : "UNAUTHORIZED",
137
+ message: payload.body.error_description || payload.body.error || "Unauthorized",
138
+ cause: new AuthChallengeError(payload)
139
+ });
140
+ }
141
+ /**
142
+ * tRPC `responseMeta` that surfaces an auth challenge: when a request's errors
143
+ * carry an `AuthChallengeError`, override the HTTP status (401/403) and set the
144
+ * OAuth challenge headers so discovery/step-up flows work. Shared by the
145
+ * standalone (`createHTTPHandler`) and fetch (`fetchRequestHandler`) adapters.
146
+ */
147
+ function authResponseMeta(opts) {
148
+ for (const err of opts.errors) if (err.cause instanceof AuthChallengeError) return {
149
+ status: err.cause.payload.statusCode,
150
+ headers: err.cause.payload.headers
151
+ };
152
+ return {};
153
+ }
113
154
  //#endregion
114
155
  //#region src/adapter/fetch.ts
115
156
  /**
@@ -127,30 +168,28 @@ async function resolveAuth(auth, authHeader, context) {
127
168
  function trpcFetch(options = {}) {
128
169
  const endpoint = (options.endpoint ?? "/trpc").replace(/\/$/, "");
129
170
  let resolveReady;
130
- const ready = new Promise((resolve) => {
171
+ let rejectReady;
172
+ const ready = new Promise((resolve, reject) => {
131
173
  resolveReady = resolve;
174
+ rejectReady = reject;
132
175
  });
133
- let handler = async () => new Response(JSON.stringify({
134
- error: "not_ready",
135
- message: "tRPC adapter has not started yet"
136
- }), {
137
- status: 503,
138
- headers: { "Content-Type": "application/json" }
139
- });
176
+ let handler;
140
177
  const adapter = (_silkweaveOptions, baseContext) => {
141
178
  const context = baseContext.fork({ adapter: "trpc" });
142
179
  return {
143
180
  context,
144
181
  start: async (actions) => {
145
- const router = buildRouter(actions);
182
+ let router;
183
+ try {
184
+ router = buildRouter(actions);
185
+ } catch (error) {
186
+ rejectReady(error);
187
+ throw error;
188
+ }
146
189
  const logger = createActionLogger();
147
190
  const createContext = async (opts) => {
148
191
  const resolved = await resolveAuth(options.auth, opts.req.headers.get("authorization"), context.fork({ request: opts.req }));
149
- if (resolved.kind === "error") {
150
- const err = /* @__PURE__ */ new Error("Unauthorized");
151
- err.silkweaveAuthError = resolved.error;
152
- throw err;
153
- }
192
+ if (resolved.kind === "error") throwAuthError(resolved.error);
154
193
  return { silkweaveContext: context.fork({
155
194
  logger,
156
195
  request: opts.req,
@@ -158,24 +197,13 @@ function trpcFetch(options = {}) {
158
197
  }) };
159
198
  };
160
199
  handler = async (request) => {
161
- try {
162
- return await fetchRequestHandler({
163
- endpoint,
164
- req: request,
165
- router,
166
- createContext
167
- });
168
- } catch (error) {
169
- const authError = error.silkweaveAuthError;
170
- if (authError) return new Response(JSON.stringify(authError.body), {
171
- status: authError.statusCode,
172
- headers: {
173
- "Content-Type": "application/json",
174
- ...authError.headers
175
- }
176
- });
177
- throw error;
178
- }
200
+ return fetchRequestHandler({
201
+ endpoint,
202
+ req: request,
203
+ router,
204
+ createContext,
205
+ responseMeta: authResponseMeta
206
+ });
179
207
  };
180
208
  resolveReady();
181
209
  },
@@ -183,7 +211,17 @@ function trpcFetch(options = {}) {
183
211
  };
184
212
  };
185
213
  const dispatch = async (request) => {
186
- await ready;
214
+ try {
215
+ await ready;
216
+ } catch {
217
+ return new Response(JSON.stringify({
218
+ error: "not_ready",
219
+ message: "tRPC adapter failed to start"
220
+ }), {
221
+ status: 503,
222
+ headers: { "Content-Type": "application/json" }
223
+ });
224
+ }
187
225
  return handler(request);
188
226
  };
189
227
  return {
@@ -217,13 +255,7 @@ const trpc = (options) => {
217
255
  const logger = createActionLogger();
218
256
  const createContext = async (opts) => {
219
257
  const resolved = await resolveAuth(options.auth, opts.req.headers.authorization, context.fork({ request: opts.req }));
220
- if (resolved.kind === "error") {
221
- for (const [key, value] of Object.entries(resolved.error.headers)) opts.res.setHeader(key, value);
222
- opts.res.statusCode = resolved.error.statusCode;
223
- opts.res.setHeader("Content-Type", "application/json");
224
- opts.res.end(JSON.stringify(resolved.error.body));
225
- throw new Error("Unauthorized");
226
- }
258
+ if (resolved.kind === "error") throwAuthError(resolved.error);
227
259
  return { silkweaveContext: context.fork({
228
260
  logger,
229
261
  request: opts.req,
@@ -233,7 +265,8 @@ const trpc = (options) => {
233
265
  const trpcHandler = createHTTPHandler({
234
266
  router,
235
267
  basePath: endpoint,
236
- createContext
268
+ createContext,
269
+ responseMeta: authResponseMeta
237
270
  });
238
271
  const handler = (req, res) => {
239
272
  if (!req.url?.startsWith(endpoint)) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/errors.ts","../src/lib/buildRouter.ts","../src/lib/createContext.ts","../src/adapter/fetch.ts","../src/adapter/trpc.ts"],"sourcesContent":["import { SilkweaveError } from '@silkweave/core'\nimport { TRPCError } from '@trpc/server'\nimport { z } from 'zod/v4'\n\ntype TRPCErrorCode = TRPCError['code']\n\nconst CODE_MAP: Record<number, TRPCErrorCode> = {\n 400: 'BAD_REQUEST',\n 401: 'UNAUTHORIZED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 408: 'TIMEOUT',\n 409: 'CONFLICT',\n 412: 'PRECONDITION_FAILED',\n 413: 'PAYLOAD_TOO_LARGE',\n 429: 'TOO_MANY_REQUESTS',\n 499: 'CLIENT_CLOSED_REQUEST',\n 500: 'INTERNAL_SERVER_ERROR',\n 501: 'NOT_IMPLEMENTED',\n 502: 'BAD_GATEWAY',\n 503: 'SERVICE_UNAVAILABLE',\n 504: 'GATEWAY_TIMEOUT'\n}\n\nexport function mapError(error: unknown): TRPCError {\n if (error instanceof TRPCError) { return error }\n if (error instanceof z.ZodError) {\n return new TRPCError({ code: 'BAD_REQUEST', message: error.message, cause: error })\n }\n if (error instanceof SilkweaveError) {\n return new TRPCError({\n code: CODE_MAP[error.statusCode] ?? 'INTERNAL_SERVER_ERROR',\n message: error.message,\n cause: error\n })\n }\n return new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: error instanceof Error ? error.message : 'Internal error',\n cause: error instanceof Error ? error : undefined\n })\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Action, ActionRun, ActionStreamRun, isStreamingAction, SilkweaveContext } from '@silkweave/core'\nimport { initTRPC } from '@trpc/server'\nimport { camelCase } from 'change-case'\nimport { mapError } from './errors.js'\n\nexport interface TrpcHandlerContext {\n silkweaveContext: SilkweaveContext\n}\n\nexport function buildRouter(actions: Action[]) {\n const t = initTRPC.context<TrpcHandlerContext>().create()\n const record: Record<string, any> = {}\n for (const action of actions) {\n const key = camelCase(action.name)\n const base = t.procedure.input(action.input)\n if (isStreamingAction(action)) {\n const streamRun = action.run as ActionStreamRun<object, unknown>\n record[key] = base.subscription(async function* ({ input, ctx, signal }) {\n try {\n const iter = streamRun(input as object, ctx.silkweaveContext)\n for await (const chunk of iter) {\n if (signal?.aborted) { return }\n yield chunk\n }\n } catch (error) {\n throw mapError(error)\n }\n })\n } else {\n const runFn = action.run as ActionRun<object, object>\n const handler = async ({ input, ctx }: { input: object; ctx: TrpcHandlerContext }) => {\n try {\n return await runFn(input, ctx.silkweaveContext)\n } catch (error) {\n throw mapError(error)\n }\n }\n record[key] = action.kind === 'query' ? base.query(handler) : base.mutation(handler)\n }\n }\n return t.router(record)\n}\n","import { AuthConfig, AuthInfo, validateToken } from '@silkweave/auth'\nimport { buildLogLevels, Logger, LogLevel, SilkweaveContext } from '@silkweave/core'\n\nconst CONSOLE_LEVEL_MAP: Record<LogLevel, 'log' | 'info' | 'warn' | 'error'> = {\n emergency: 'error',\n alert: 'error',\n critical: 'error',\n error: 'error',\n warning: 'warn',\n notice: 'info',\n info: 'info',\n debug: 'log'\n}\n\nexport function createActionLogger(): Logger {\n return {\n ...buildLogLevels((level, data) => {\n console[CONSOLE_LEVEL_MAP[level]](data)\n }),\n progress: () => { /* progress notifications not supported on tRPC HTTP */ }\n }\n}\n\nexport interface AuthErrorPayload {\n statusCode: number\n headers: Record<string, string>\n body: { error: string; error_description: string }\n}\n\nexport type ResolvedAuth =\n | { kind: 'ok'; authInfo?: AuthInfo }\n | { kind: 'error'; error: AuthErrorPayload }\n\nexport async function resolveAuth(\n auth: AuthConfig | undefined,\n authHeader: string | null | undefined,\n context: SilkweaveContext\n): Promise<ResolvedAuth> {\n if (!auth) { return { kind: 'ok' } }\n const result = await validateToken(authHeader, auth, context)\n if (result.error) { return { kind: 'error', error: result.error } }\n return { kind: 'ok', authInfo: result.auth }\n}\n","import { AuthConfig } from '@silkweave/auth'\nimport { Adapter, AdapterGenerator, SilkweaveOptions } from '@silkweave/core'\nimport { fetchRequestHandler } from '@trpc/server/adapters/fetch'\nimport { buildRouter, TrpcHandlerContext } from '../lib/buildRouter.js'\nimport { createActionLogger, resolveAuth } from '../lib/createContext.js'\n\nexport interface TrpcFetchAdapterOptions {\n /** URL prefix stripped from incoming requests before tRPC routing. Default `/trpc`. */\n endpoint?: string\n auth?: AuthConfig\n}\n\nexport type FetchHandler = (request: Request) => Promise<Response>\n\nexport interface TrpcFetchAdapter {\n adapter: AdapterGenerator\n handler: FetchHandler\n GET: FetchHandler\n POST: FetchHandler\n}\n\n/**\n * Creates a tRPC adapter that exposes a fetch-compatible handler instead of\n * binding its own HTTP server. Use in Astro API routes, Vercel serverless\n * functions, Cloudflare Workers, or any Web Standard runtime.\n *\n * The returned `handler` waits for `server.start()` to complete (via an internal\n * `_ready` promise) before dispatching requests, so it's safe to call from a\n * cold-started serverless invocation.\n *\n * CORS is intentionally not configured here - handle it in your host framework\n * (Astro middleware, `vercel.json` headers, Worker response headers, etc).\n */\nexport function trpcFetch(options: TrpcFetchAdapterOptions = {}): TrpcFetchAdapter {\n const endpoint = (options.endpoint ?? '/trpc').replace(/\\/$/, '')\n\n let resolveReady!: () => void\n const ready = new Promise<void>((resolve) => { resolveReady = resolve })\n\n let handler: FetchHandler = async () => new Response(\n JSON.stringify({ error: 'not_ready', message: 'tRPC adapter has not started yet' }),\n { status: 503, headers: { 'Content-Type': 'application/json' } }\n )\n\n const adapter: AdapterGenerator = (_silkweaveOptions: SilkweaveOptions, baseContext): Adapter => {\n const context = baseContext.fork({ adapter: 'trpc' })\n return {\n context,\n start: async (actions) => {\n const router = buildRouter(actions)\n const logger = createActionLogger()\n\n const createContext = async (\n opts: { req: Request }\n ): Promise<TrpcHandlerContext> => {\n const resolved = await resolveAuth(\n options.auth,\n opts.req.headers.get('authorization'),\n context.fork({ request: opts.req })\n )\n if (resolved.kind === 'error') {\n const err = new Error('Unauthorized') as Error & { silkweaveAuthError?: typeof resolved.error }\n err.silkweaveAuthError = resolved.error\n throw err\n }\n return {\n silkweaveContext: context.fork({\n logger,\n request: opts.req,\n ...(resolved.authInfo ? { auth: resolved.authInfo } : {})\n })\n }\n }\n\n handler = async (request: Request): Promise<Response> => {\n try {\n return await fetchRequestHandler({\n endpoint,\n req: request,\n router,\n createContext\n })\n } catch (error) {\n const authError = (error as { silkweaveAuthError?: { statusCode: number; headers: Record<string, string>; body: object } }).silkweaveAuthError\n if (authError) {\n return new Response(JSON.stringify(authError.body), {\n status: authError.statusCode,\n headers: { 'Content-Type': 'application/json', ...authError.headers }\n })\n }\n throw error\n }\n }\n\n resolveReady()\n },\n stop: async () => { /* no-op for fetch adapter */ }\n }\n }\n\n const dispatch: FetchHandler = async (request) => {\n await ready\n return handler(request)\n }\n\n return {\n adapter,\n handler: dispatch,\n GET: dispatch,\n POST: dispatch\n }\n}\n","import { AuthConfig } from '@silkweave/auth'\nimport { AdapterFactory } from '@silkweave/core'\nimport { createHTTPHandler } from '@trpc/server/adapters/standalone'\nimport cors, { CorsOptions } from 'cors'\nimport http, { IncomingMessage, ServerResponse } from 'http'\nimport { buildRouter, TrpcHandlerContext } from '../lib/buildRouter.js'\nimport { createActionLogger, resolveAuth } from '../lib/createContext.js'\n\nexport interface TrpcAdapterOptions {\n host?: string\n port?: number\n /** URL prefix stripped from incoming requests before tRPC routing. Default `/trpc/`. */\n endpoint?: string\n /** CORS configuration. `false` to disable, `true`/`undefined` for permissive defaults, or a CorsOptions object. */\n cors?: CorsOptions | boolean\n auth?: AuthConfig\n}\n\ntype CorsMiddleware = (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => void\n\nfunction resolveCors(corsConfig: CorsOptions | boolean | undefined): CorsMiddleware | null {\n if (corsConfig === false) { return null }\n const userConfig = corsConfig === true || corsConfig === undefined ? {} : corsConfig\n return cors({ origin: '*', ...userConfig }) as CorsMiddleware\n}\n\nexport const trpc: AdapterFactory<TrpcAdapterOptions> = (options) => {\n return (_silkweaveOptions, baseContext) => {\n const context = baseContext.fork({ adapter: 'trpc' })\n const host = options.host ?? '0.0.0.0'\n const port = options.port ?? 8080\n const endpoint = (options.endpoint ?? '/trpc/').replace(/\\/?$/, '/')\n let server: http.Server | undefined\n\n return {\n context,\n start: async (actions) => {\n const router = buildRouter(actions)\n const corsMiddleware = resolveCors(options.cors)\n const logger = createActionLogger()\n\n const createContext = async (\n opts: { req: IncomingMessage; res: ServerResponse }\n ): Promise<TrpcHandlerContext> => {\n const resolved = await resolveAuth(\n options.auth,\n opts.req.headers.authorization,\n context.fork({ request: opts.req })\n )\n if (resolved.kind === 'error') {\n for (const [key, value] of Object.entries(resolved.error.headers)) {\n opts.res.setHeader(key, value)\n }\n opts.res.statusCode = resolved.error.statusCode\n opts.res.setHeader('Content-Type', 'application/json')\n opts.res.end(JSON.stringify(resolved.error.body))\n throw new Error('Unauthorized')\n }\n return {\n silkweaveContext: context.fork({\n logger,\n request: opts.req,\n ...(resolved.authInfo ? { auth: resolved.authInfo } : {})\n })\n }\n }\n\n const trpcHandler = createHTTPHandler({ router, basePath: endpoint, createContext })\n\n const handler = (req: IncomingMessage, res: ServerResponse) => {\n if (!req.url?.startsWith(endpoint)) {\n res.statusCode = 404\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({ error: 'not_found', message: `No route for ${req.url}` }))\n return\n }\n trpcHandler(req, res)\n }\n\n server = http.createServer((req, res) => {\n if (corsMiddleware) {\n corsMiddleware(req, res, () => {\n if (req.method === 'OPTIONS') { res.statusCode = 204; res.end(); return }\n handler(req, res)\n })\n } else {\n handler(req, res)\n }\n })\n\n await new Promise<void>((resolve) => {\n server!.listen(port, host, () => { resolve() })\n })\n },\n stop: async () => {\n if (!server) { return }\n await new Promise<void>((resolve, reject) => {\n server!.close((err) => {\n if (err) { reject(err) } else { resolve() }\n })\n })\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/errors.ts","../src/lib/buildRouter.ts","../src/lib/createContext.ts","../src/adapter/fetch.ts","../src/adapter/trpc.ts"],"sourcesContent":["import { SilkweaveError } from '@silkweave/core'\nimport { TRPCError } from '@trpc/server'\nimport { z } from 'zod/v4'\n\ntype TRPCErrorCode = TRPCError['code']\n\nconst CODE_MAP: Record<number, TRPCErrorCode> = {\n 400: 'BAD_REQUEST',\n 401: 'UNAUTHORIZED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 408: 'TIMEOUT',\n 409: 'CONFLICT',\n 412: 'PRECONDITION_FAILED',\n 413: 'PAYLOAD_TOO_LARGE',\n 429: 'TOO_MANY_REQUESTS',\n 499: 'CLIENT_CLOSED_REQUEST',\n 500: 'INTERNAL_SERVER_ERROR',\n 501: 'NOT_IMPLEMENTED',\n 502: 'BAD_GATEWAY',\n 503: 'SERVICE_UNAVAILABLE',\n 504: 'GATEWAY_TIMEOUT'\n}\n\nexport function mapError(error: unknown): TRPCError {\n if (error instanceof TRPCError) { return error }\n if (error instanceof z.ZodError) {\n return new TRPCError({ code: 'BAD_REQUEST', message: error.message, cause: error })\n }\n if (error instanceof SilkweaveError) {\n return new TRPCError({\n code: CODE_MAP[error.statusCode] ?? 'INTERNAL_SERVER_ERROR',\n message: error.message,\n cause: error\n })\n }\n return new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: error instanceof Error ? error.message : 'Internal error',\n cause: error instanceof Error ? error : undefined\n })\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Action, ActionRun, ActionStreamRun, isStreamingAction, SilkweaveContext } from '@silkweave/core'\nimport { initTRPC } from '@trpc/server'\nimport { camelCase } from 'change-case'\nimport { mapError } from './errors.js'\n\nexport interface TrpcHandlerContext {\n silkweaveContext: SilkweaveContext\n}\n\nexport function buildRouter(actions: Action[]) {\n const t = initTRPC.context<TrpcHandlerContext>().create()\n const record: Record<string, any> = {}\n for (const action of actions) {\n const key = camelCase(action.name)\n const base = t.procedure.input(action.input)\n if (isStreamingAction(action)) {\n const streamRun = action.run as ActionStreamRun<object, unknown>\n record[key] = base.subscription(async function* ({ input, ctx, signal }) {\n try {\n const iter = streamRun(input as object, ctx.silkweaveContext)\n for await (const chunk of iter) {\n if (signal?.aborted) { return }\n yield chunk\n }\n } catch (error) {\n throw mapError(error)\n }\n })\n } else {\n const runFn = action.run as ActionRun<object, object>\n const handler = async ({ input, ctx }: { input: object; ctx: TrpcHandlerContext }) => {\n try {\n return await runFn(input, ctx.silkweaveContext)\n } catch (error) {\n throw mapError(error)\n }\n }\n record[key] = action.kind === 'query' ? base.query(handler) : base.mutation(handler)\n }\n }\n return t.router(record)\n}\n","import { AuthConfig, AuthInfo, validateToken } from '@silkweave/auth'\nimport { buildLogLevels, Logger, LogLevel, SilkweaveContext } from '@silkweave/core'\nimport { TRPCError } from '@trpc/server'\n\nconst CONSOLE_LEVEL_MAP: Record<LogLevel, 'log' | 'info' | 'warn' | 'error'> = {\n emergency: 'error',\n alert: 'error',\n critical: 'error',\n error: 'error',\n warning: 'warn',\n notice: 'info',\n info: 'info',\n debug: 'log'\n}\n\nexport function createActionLogger(): Logger {\n return {\n ...buildLogLevels((level, data) => {\n console[CONSOLE_LEVEL_MAP[level]](data)\n }),\n progress: () => { /* progress notifications not supported on tRPC HTTP */ }\n }\n}\n\nexport interface AuthErrorPayload {\n statusCode: number\n headers: Record<string, string>\n body: { error: string; error_description: string }\n}\n\nexport type ResolvedAuth =\n | { kind: 'ok'; authInfo?: AuthInfo }\n | { kind: 'error'; error: AuthErrorPayload }\n\nexport async function resolveAuth(\n auth: AuthConfig | undefined,\n authHeader: string | null | undefined,\n context: SilkweaveContext\n): Promise<ResolvedAuth> {\n if (!auth) { return { kind: 'ok' } }\n const result = await validateToken(authHeader, auth, context)\n if (result.error) { return { kind: 'error', error: result.error } }\n return { kind: 'ok', authInfo: result.auth }\n}\n\n/**\n * Carries the OAuth challenge (status + `WWW-Authenticate`/resource-metadata\n * headers) from a failed `createContext` to `authResponseMeta`. Attached as the\n * `cause` of a `TRPCError` so the tRPC handler owns the whole response\n * lifecycle - the adapter must never write/end the raw response itself (doing so\n * races tRPC's own error write and crashes the process with\n * `ERR_STREAM_WRITE_AFTER_END`).\n */\nexport class AuthChallengeError extends Error {\n constructor(public readonly payload: AuthErrorPayload) {\n super(payload.body.error_description || payload.body.error || 'Unauthorized')\n this.name = 'AuthChallengeError'\n }\n}\n\n/**\n * Throw a resolved auth error as a `TRPCError` so the tRPC handler (standalone\n * or fetch) produces the response. `authResponseMeta` then upgrades that\n * response to the OAuth status + challenge headers via the `AuthChallengeError`\n * cause. Never write to `res` directly from `createContext`.\n */\nexport function throwAuthError(payload: AuthErrorPayload): never {\n throw new TRPCError({\n code: payload.statusCode === 403 ? 'FORBIDDEN' : 'UNAUTHORIZED',\n message: payload.body.error_description || payload.body.error || 'Unauthorized',\n cause: new AuthChallengeError(payload)\n })\n}\n\n/**\n * tRPC `responseMeta` that surfaces an auth challenge: when a request's errors\n * carry an `AuthChallengeError`, override the HTTP status (401/403) and set the\n * OAuth challenge headers so discovery/step-up flows work. Shared by the\n * standalone (`createHTTPHandler`) and fetch (`fetchRequestHandler`) adapters.\n */\nexport function authResponseMeta(\n opts: { errors: readonly { cause?: unknown }[] }\n): { status?: number; headers?: Record<string, string> } {\n for (const err of opts.errors) {\n if (err.cause instanceof AuthChallengeError) {\n return { status: err.cause.payload.statusCode, headers: err.cause.payload.headers }\n }\n }\n return {}\n}\n","import { AuthConfig } from '@silkweave/auth'\nimport { Adapter, AdapterGenerator, SilkweaveOptions } from '@silkweave/core'\nimport { fetchRequestHandler } from '@trpc/server/adapters/fetch'\nimport { buildRouter, TrpcHandlerContext } from '../lib/buildRouter.js'\nimport { authResponseMeta, createActionLogger, resolveAuth, throwAuthError } from '../lib/createContext.js'\n\nexport interface TrpcFetchAdapterOptions {\n /** URL prefix stripped from incoming requests before tRPC routing. Default `/trpc`. */\n endpoint?: string\n auth?: AuthConfig\n}\n\nexport type FetchHandler = (request: Request) => Promise<Response>\n\nexport interface TrpcFetchAdapter {\n adapter: AdapterGenerator\n handler: FetchHandler\n GET: FetchHandler\n POST: FetchHandler\n}\n\n/**\n * Creates a tRPC adapter that exposes a fetch-compatible handler instead of\n * binding its own HTTP server. Use in Astro API routes, Vercel serverless\n * functions, Cloudflare Workers, or any Web Standard runtime.\n *\n * The returned `handler` waits for `server.start()` to complete (via an internal\n * `_ready` promise) before dispatching requests, so it's safe to call from a\n * cold-started serverless invocation.\n *\n * CORS is intentionally not configured here - handle it in your host framework\n * (Astro middleware, `vercel.json` headers, Worker response headers, etc).\n */\nexport function trpcFetch(options: TrpcFetchAdapterOptions = {}): TrpcFetchAdapter {\n const endpoint = (options.endpoint ?? '/trpc').replace(/\\/$/, '')\n\n let resolveReady!: () => void\n let rejectReady!: (error: unknown) => void\n const ready = new Promise<void>((resolve, reject) => { resolveReady = resolve; rejectReady = reject })\n\n let handler: FetchHandler | undefined\n\n const adapter: AdapterGenerator = (_silkweaveOptions: SilkweaveOptions, baseContext): Adapter => {\n const context = baseContext.fork({ adapter: 'trpc' })\n return {\n context,\n start: async (actions) => {\n let router\n try {\n router = buildRouter(actions)\n } catch (error) {\n // Reject readiness so requests get a 503 instead of hanging forever,\n // then rethrow so the builder's start() rejects too.\n rejectReady(error)\n throw error\n }\n const logger = createActionLogger()\n\n const createContext = async (\n opts: { req: Request }\n ): Promise<TrpcHandlerContext> => {\n const resolved = await resolveAuth(\n options.auth,\n opts.req.headers.get('authorization'),\n context.fork({ request: opts.req })\n )\n if (resolved.kind === 'error') {\n // @trpc/server captures createContext errors internally and never\n // rethrows, so a plain throw would surface as a 500. Signal the\n // 401/403 as a TRPCError whose challenge headers authResponseMeta\n // applies to the Response.\n throwAuthError(resolved.error)\n }\n return {\n silkweaveContext: context.fork({\n logger,\n request: opts.req,\n ...(resolved.authInfo ? { auth: resolved.authInfo } : {})\n })\n }\n }\n\n handler = async (request: Request): Promise<Response> => {\n return fetchRequestHandler({\n endpoint,\n req: request,\n router,\n createContext,\n responseMeta: authResponseMeta\n })\n }\n\n resolveReady()\n },\n stop: async () => { /* no-op for fetch adapter */ }\n }\n }\n\n const dispatch: FetchHandler = async (request) => {\n try {\n await ready\n } catch {\n return new Response(\n JSON.stringify({ error: 'not_ready', message: 'tRPC adapter failed to start' }),\n { status: 503, headers: { 'Content-Type': 'application/json' } }\n )\n }\n return handler!(request)\n }\n\n return {\n adapter,\n handler: dispatch,\n GET: dispatch,\n POST: dispatch\n }\n}\n","import { AuthConfig } from '@silkweave/auth'\nimport { AdapterFactory } from '@silkweave/core'\nimport { createHTTPHandler } from '@trpc/server/adapters/standalone'\nimport cors, { CorsOptions } from 'cors'\nimport http, { IncomingMessage, ServerResponse } from 'http'\nimport { buildRouter, TrpcHandlerContext } from '../lib/buildRouter.js'\nimport { authResponseMeta, createActionLogger, resolveAuth, throwAuthError } from '../lib/createContext.js'\n\nexport interface TrpcAdapterOptions {\n host?: string\n port?: number\n /** URL prefix stripped from incoming requests before tRPC routing. Default `/trpc/`. */\n endpoint?: string\n /** CORS configuration. `false` to disable, `true`/`undefined` for permissive defaults, or a CorsOptions object. */\n cors?: CorsOptions | boolean\n auth?: AuthConfig\n}\n\ntype CorsMiddleware = (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => void\n\nfunction resolveCors(corsConfig: CorsOptions | boolean | undefined): CorsMiddleware | null {\n if (corsConfig === false) { return null }\n const userConfig = corsConfig === true || corsConfig === undefined ? {} : corsConfig\n return cors({ origin: '*', ...userConfig }) as CorsMiddleware\n}\n\nexport const trpc: AdapterFactory<TrpcAdapterOptions> = (options) => {\n return (_silkweaveOptions, baseContext) => {\n const context = baseContext.fork({ adapter: 'trpc' })\n const host = options.host ?? '0.0.0.0'\n const port = options.port ?? 8080\n const endpoint = (options.endpoint ?? '/trpc/').replace(/\\/?$/, '/')\n let server: http.Server | undefined\n\n return {\n context,\n start: async (actions) => {\n const router = buildRouter(actions)\n const corsMiddleware = resolveCors(options.cors)\n const logger = createActionLogger()\n\n const createContext = async (\n opts: { req: IncomingMessage; res: ServerResponse }\n ): Promise<TrpcHandlerContext> => {\n const resolved = await resolveAuth(\n options.auth,\n opts.req.headers.authorization,\n context.fork({ request: opts.req })\n )\n if (resolved.kind === 'error') {\n // Signal the 401/403 through tRPC (see throwAuthError). Writing to\n // opts.res here would race tRPC's own error write and crash the\n // process with ERR_STREAM_WRITE_AFTER_END on the finished response.\n throwAuthError(resolved.error)\n }\n return {\n silkweaveContext: context.fork({\n logger,\n request: opts.req,\n ...(resolved.authInfo ? { auth: resolved.authInfo } : {})\n })\n }\n }\n\n const trpcHandler = createHTTPHandler({ router, basePath: endpoint, createContext, responseMeta: authResponseMeta })\n\n const handler = (req: IncomingMessage, res: ServerResponse) => {\n if (!req.url?.startsWith(endpoint)) {\n res.statusCode = 404\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({ error: 'not_found', message: `No route for ${req.url}` }))\n return\n }\n trpcHandler(req, res)\n }\n\n server = http.createServer((req, res) => {\n if (corsMiddleware) {\n corsMiddleware(req, res, () => {\n if (req.method === 'OPTIONS') { res.statusCode = 204; res.end(); return }\n handler(req, res)\n })\n } else {\n handler(req, res)\n }\n })\n\n await new Promise<void>((resolve) => {\n server!.listen(port, host, () => { resolve() })\n })\n },\n stop: async () => {\n if (!server) { return }\n await new Promise<void>((resolve, reject) => {\n server!.close((err) => {\n if (err) { reject(err) } else { resolve() }\n })\n })\n }\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/trpc",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Silkweave tRPC Adapter",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.silkweave.dev",
@@ -30,8 +30,8 @@
30
30
  "change-case": "^5.4.4",
31
31
  "cors": "^2.8.5",
32
32
  "zod": "^3.25.0",
33
- "@silkweave/auth": "3.2.0",
34
- "@silkweave/core": "3.2.0"
33
+ "@silkweave/auth": "3.2.1",
34
+ "@silkweave/core": "3.2.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@eslint/js": "^10.0.1",