@fonderie/adapter-express 1.0.3 → 3.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.
package/dist/index.cjs CHANGED
@@ -80,7 +80,11 @@ async function expressRequestToWeb(req) {
80
80
  }
81
81
  async function webResponseToExpress(webRes, res) {
82
82
  res.statusCode = webRes.status;
83
- webRes.headers.forEach((value, key) => res.setHeader(key, value));
83
+ const setCookies = webRes.headers.getSetCookie?.() ?? [];
84
+ if (setCookies.length) res.setHeader("Set-Cookie", setCookies);
85
+ webRes.headers.forEach((value, key) => {
86
+ if (key.toLowerCase() !== "set-cookie") res.setHeader(key, value);
87
+ });
84
88
  res.end(Buffer.from(await webRes.arrayBuffer()));
85
89
  }
86
90
  function readStream(req) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport type { FonderieApp, IFonderieContext, Middleware } from '@fonderie/core';\nimport { requireAuth as _requireAuth, resolveClientIp } from '@fonderie/core/middlewares';\n// Optional peers: type-only imports (erased at runtime). The guard factories\n// below load them lazily so installing this adapter never requires\n// @fonderie/workspaces, @fonderie/permissions, or @fonderie/billing unless\n// the corresponding guard is actually used.\nimport type { withWorkspace as _withWorkspace } from '@fonderie/workspaces';\nimport type { requirePermission as _requirePermission } from '@fonderie/permissions';\n\nexport { OPERATIONS } from '@fonderie/core';\n\nasync function loadOptionalPeer<T>(load: () => Promise<T>, pkg: string, api: string): Promise<T> {\n\ttry {\n\t\treturn await load();\n\t} catch (err) {\n\t\tconst e = err as { code?: string; message?: string } | undefined;\n\t\tconst notFound = e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND';\n\t\t// Only claim the peer is missing when the unresolved specifier IS the\n\t\t// peer — a transitive failure inside an installed peer must surface\n\t\t// as-is, not as a misleading install hint.\n\t\tconst missing = notFound ? /Cannot find (?:package|module) '([^']+)'/.exec(e?.message ?? '')?.[1] : undefined;\n\t\tif (missing === pkg || missing?.startsWith(pkg + '/')) {\n\t\t\tthrow new Error(\n\t\t\t\t`[fonderie] ${api} requires the optional peer dependency \"${pkg}\". Install it: npm install ${pkg}`,\n\t\t\t);\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nexport type ExpressRequest = IncomingMessage & { body?: unknown; _fonderie?: IFonderieContext };\nexport type ExpressResponse = ServerResponse;\nexport type ExpressNext = (err?: unknown) => void;\n\n// ── Web Standard ↔ Express translation ───────────────────────────\n\nexport async function expressRequestToWeb(req: ExpressRequest): Promise<Request> {\n\tconst encrypted = (req.socket as { encrypted?: boolean }).encrypted;\n\tconst protocol = encrypted ? 'https' : 'http';\n\tconst host = req.headers['host'] ?? 'localhost';\n\tconst url = `${protocol}://${host}${req.url ?? '/'}`;\n\n\tconst headers = new Headers();\n\tfor (const [key, value] of Object.entries(req.headers)) {\n\t\tif (!value) continue;\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) headers.append(key, v);\n\t\t} else {\n\t\t\theaders.set(key, value);\n\t\t}\n\t}\n\n\tconst method = req.method ?? 'GET';\n\tconst hasBody = !['GET', 'HEAD', 'OPTIONS'].includes(method.toUpperCase());\n\tconst body = hasBody ? await readStream(req) : null;\n\n\treturn new Request(url, { method, headers, body });\n}\n\nexport async function webResponseToExpress(webRes: Response, res: ExpressResponse): Promise<void> {\n\tres.statusCode = webRes.status;\n\twebRes.headers.forEach((value, key) => res.setHeader(key, value));\n\tres.end(Buffer.from(await webRes.arrayBuffer()));\n}\n\nfunction readStream(req: IncomingMessage): Promise<ArrayBuffer> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\treq.on('data', (chunk: Buffer) => chunks.push(chunk));\n\t\treq.on('end', () => {\n\t\t\tconst buf = Buffer.concat(chunks);\n\t\t\t// slice creates a correctly-sized ArrayBuffer (buf.buffer is a shared pool)\n\t\t\tresolve(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer);\n\t\t});\n\t\treq.on('error', reject);\n\t});\n}\n\n// ── bridge ────────────────────────────────────────────────────────\n//\n// Express middleware. Populates req._fonderie with the fonderie context\n// (user, workspace, meta) for all subsequent route handlers.\n// Also forwards the parsed body to req.body.\n//\n// app.use(bridge(fonderie))\n\nexport function bridge(fonderie: FonderieApp) {\n\treturn async (req: ExpressRequest, _res: ExpressResponse, next: ExpressNext) => {\n\t\ttry {\n\t\t\tconst webReq = await expressRequestToWeb(req);\n\t\t\t// Cache so the infra handler in mount() can reuse it without re-reading\n\t\t\t// the body stream (which can only be consumed once).\n\t\t\t(req as any)._fonterieReq = webReq;\n\t\t\treq._fonderie = await fonderie.buildContext(webReq.clone());\n\t\t\tconst clientIp = resolveClientIp(req.socket?.remoteAddress ?? undefined, webReq.headers);\n\t\t\tif (clientIp) req._fonderie.meta.clientIp = clientIp;\n\t\t\tif (req._fonderie.meta['body'] !== undefined) {\n\t\t\t\treq.body = req._fonderie.meta['body'];\n\t\t\t}\n\t\t\tnext();\n\t\t} catch (err) {\n\t\t\tnext(err);\n\t\t}\n\t};\n}\n\n// ── adapt ─────────────────────────────────────────────────────────\n//\n// Low-level escape hatch — wraps any fonderie Middleware into an Express\n// middleware function. Use this for custom fonderie middleware; prefer the\n// named exports below for the built-in fonderie guards.\n\nexport function adapt(middleware: Middleware) {\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tconst ctx = req._fonderie;\n\t\tif (!ctx) {\n\t\t\tnext(new Error('[fonderie] bridge() must be registered before adapt()'));\n\t\t\treturn;\n\t\t}\n\n\t\tlet continued = false;\n\t\tconst result = await middleware(ctx, async () => {\n\t\t\tcontinued = true;\n\t\t\treturn new Response();\n\t\t});\n\n\t\tif (continued) {\n\t\t\tnext();\n\t\t} else {\n\t\t\tawait webResponseToExpress(result, res);\n\t\t}\n\t};\n}\n\n// ── Pre-adapted middleware ────────────────────────────────────────\n//\n// Drop-in replacements for the fonderie middleware functions — no adapt()\n// needed. Import directly from this package instead of from the source\n// packages, and use them as native Express middleware.\n//\n// app.get('/jobs', requireAuth, withWorkspace(store), ...)\n\nexport const requireAuth = adapt(_requireAuth);\n\n// The three guards below wrap OPTIONAL peers, so the peer is imported lazily\n// on first request — not at module load. adapt() returns an async Express\n// middleware either way, so the extra await changes nothing for callers.\n\nexport function withWorkspace(store: Parameters<typeof _withWorkspace>[0]) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/workspaces'),\n\t\t\t\t'@fonderie/workspaces',\n\t\t\t\t'withWorkspace()',\n\t\t\t);\n\t\t\tinner = adapt(mod.withWorkspace(store));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\nexport function requirePermission(\n\toperation: Parameters<typeof _requirePermission>[0],\n\tpermissionKey: Parameters<typeof _requirePermission>[1],\n) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/permissions'),\n\t\t\t\t'@fonderie/permissions',\n\t\t\t\t'requirePermission()',\n\t\t\t);\n\t\t\tinner = adapt(mod.requirePermission(operation, permissionKey));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\nexport function requireFeature(key: string) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/billing'),\n\t\t\t\t'@fonderie/billing',\n\t\t\t\t'requireFeature()',\n\t\t\t);\n\t\t\tinner = adapt(mod.requireFeature(key));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\n// ── mount ─────────────────────────────────────────────────────────\n//\n// Wires up fonderie to an Express app. Returns the same app so you can add\n// routes after mount() and before app.listen() — infra is sealed lazily\n// when app.listen() is first called:\n//\n// const api = mount(app, fonderie)\n// api.use(buildTodoRouter(store))\n// app.listen(port)\n//\n// Alternatively pass a register callback to be explicit about ordering:\n//\n// mount(app, fonderie, (app) => {\n// app.use(buildTodoRouter(store))\n// })\n\ntype ExpressApp = {\n\tuse: (...args: any[]) => any;\n\tall: (path: string, handler: (req: ExpressRequest, res: ExpressResponse) => void) => void;\n\tlisten: (...args: any[]) => any;\n};\n\nexport function mount<T extends ExpressApp>(\n\tapp: T,\n\tfonderie: FonderieApp,\n\tregister?: (app: T) => void,\n): T {\n\tconst infraHandler = async (req: ExpressRequest, res: ExpressResponse) => {\n\t\tconst webReq = (req as any)._fonterieReq as Request ?? await expressRequestToWeb(req);\n\t\tconst webRes = await fonderie.handle(webReq);\n\t\tawait webResponseToExpress(webRes, res);\n\t};\n\n\tapp.use(bridge(fonderie));\n\n\tif (register) {\n\t\tregister(app);\n\t\tapp.use(infraHandler);\n\t} else {\n\t\tlet sealed = false;\n\t\tconst origListen = app.listen.bind(app);\n\t\t(app as ExpressApp).listen = (...args: any[]) => {\n\t\t\tif (!sealed) {\n\t\t\t\tsealed = true;\n\t\t\t\tapp.use(infraHandler);\n\t\t\t}\n\t\t\t(app as ExpressApp).listen = origListen;\n\t\t\treturn origListen(...args);\n\t\t};\n\t}\n\n\treturn app;\n}\n"],"mappings":"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","names":["_requireAuth"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport type { FonderieApp, IFonderieContext, Middleware } from '@fonderie/core';\nimport { requireAuth as _requireAuth, resolveClientIp } from '@fonderie/core/middlewares';\n// Optional peers: type-only imports (erased at runtime). The guard factories\n// below load them lazily so installing this adapter never requires\n// @fonderie/workspaces, @fonderie/permissions, or @fonderie/billing unless\n// the corresponding guard is actually used.\nimport type { withWorkspace as _withWorkspace } from '@fonderie/workspaces';\nimport type { requirePermission as _requirePermission } from '@fonderie/permissions';\n\nexport { OPERATIONS } from '@fonderie/core';\n\nasync function loadOptionalPeer<T>(load: () => Promise<T>, pkg: string, api: string): Promise<T> {\n\ttry {\n\t\treturn await load();\n\t} catch (err) {\n\t\tconst e = err as { code?: string; message?: string } | undefined;\n\t\tconst notFound = e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND';\n\t\t// Only claim the peer is missing when the unresolved specifier IS the\n\t\t// peer — a transitive failure inside an installed peer must surface\n\t\t// as-is, not as a misleading install hint.\n\t\tconst missing = notFound ? /Cannot find (?:package|module) '([^']+)'/.exec(e?.message ?? '')?.[1] : undefined;\n\t\tif (missing === pkg || missing?.startsWith(pkg + '/')) {\n\t\t\tthrow new Error(\n\t\t\t\t`[fonderie] ${api} requires the optional peer dependency \"${pkg}\". Install it: npm install ${pkg}`,\n\t\t\t);\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nexport type ExpressRequest = IncomingMessage & { body?: unknown; _fonderie?: IFonderieContext };\nexport type ExpressResponse = ServerResponse;\nexport type ExpressNext = (err?: unknown) => void;\n\n// ── Web Standard ↔ Express translation ───────────────────────────\n\nexport async function expressRequestToWeb(req: ExpressRequest): Promise<Request> {\n\tconst encrypted = (req.socket as { encrypted?: boolean }).encrypted;\n\tconst protocol = encrypted ? 'https' : 'http';\n\tconst host = req.headers['host'] ?? 'localhost';\n\tconst url = `${protocol}://${host}${req.url ?? '/'}`;\n\n\tconst headers = new Headers();\n\tfor (const [key, value] of Object.entries(req.headers)) {\n\t\tif (!value) continue;\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) headers.append(key, v);\n\t\t} else {\n\t\t\theaders.set(key, value);\n\t\t}\n\t}\n\n\tconst method = req.method ?? 'GET';\n\tconst hasBody = !['GET', 'HEAD', 'OPTIONS'].includes(method.toUpperCase());\n\tconst body = hasBody ? await readStream(req) : null;\n\n\treturn new Request(url, { method, headers, body });\n}\n\nexport async function webResponseToExpress(webRes: Response, res: ExpressResponse): Promise<void> {\n\tres.statusCode = webRes.status;\n\t// Set-Cookie is special: a response may carry SEVERAL, and `forEach` +\n\t// `setHeader` would overwrite all but the last (and coalescing them into one\n\t// comma-joined header is invalid). Forward the full list via getSetCookie().\n\tconst setCookies = webRes.headers.getSetCookie?.() ?? [];\n\tif (setCookies.length) res.setHeader('Set-Cookie', setCookies);\n\twebRes.headers.forEach((value, key) => {\n\t\tif (key.toLowerCase() !== 'set-cookie') res.setHeader(key, value);\n\t});\n\tres.end(Buffer.from(await webRes.arrayBuffer()));\n}\n\nfunction readStream(req: IncomingMessage): Promise<ArrayBuffer> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\treq.on('data', (chunk: Buffer) => chunks.push(chunk));\n\t\treq.on('end', () => {\n\t\t\tconst buf = Buffer.concat(chunks);\n\t\t\t// slice creates a correctly-sized ArrayBuffer (buf.buffer is a shared pool)\n\t\t\tresolve(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer);\n\t\t});\n\t\treq.on('error', reject);\n\t});\n}\n\n// ── bridge ────────────────────────────────────────────────────────\n//\n// Express middleware. Populates req._fonderie with the fonderie context\n// (user, workspace, meta) for all subsequent route handlers.\n// Also forwards the parsed body to req.body.\n//\n// app.use(bridge(fonderie))\n\nexport function bridge(fonderie: FonderieApp) {\n\treturn async (req: ExpressRequest, _res: ExpressResponse, next: ExpressNext) => {\n\t\ttry {\n\t\t\tconst webReq = await expressRequestToWeb(req);\n\t\t\t// Cache so the infra handler in mount() can reuse it without re-reading\n\t\t\t// the body stream (which can only be consumed once).\n\t\t\t(req as any)._fonterieReq = webReq;\n\t\t\treq._fonderie = await fonderie.buildContext(webReq.clone());\n\t\t\tconst clientIp = resolveClientIp(req.socket?.remoteAddress ?? undefined, webReq.headers);\n\t\t\tif (clientIp) req._fonderie.meta.clientIp = clientIp;\n\t\t\tif (req._fonderie.meta['body'] !== undefined) {\n\t\t\t\treq.body = req._fonderie.meta['body'];\n\t\t\t}\n\t\t\tnext();\n\t\t} catch (err) {\n\t\t\tnext(err);\n\t\t}\n\t};\n}\n\n// ── adapt ─────────────────────────────────────────────────────────\n//\n// Low-level escape hatch — wraps any fonderie Middleware into an Express\n// middleware function. Use this for custom fonderie middleware; prefer the\n// named exports below for the built-in fonderie guards.\n\nexport function adapt(middleware: Middleware) {\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tconst ctx = req._fonderie;\n\t\tif (!ctx) {\n\t\t\tnext(new Error('[fonderie] bridge() must be registered before adapt()'));\n\t\t\treturn;\n\t\t}\n\n\t\tlet continued = false;\n\t\tconst result = await middleware(ctx, async () => {\n\t\t\tcontinued = true;\n\t\t\treturn new Response();\n\t\t});\n\n\t\tif (continued) {\n\t\t\tnext();\n\t\t} else {\n\t\t\tawait webResponseToExpress(result, res);\n\t\t}\n\t};\n}\n\n// ── Pre-adapted middleware ────────────────────────────────────────\n//\n// Drop-in replacements for the fonderie middleware functions — no adapt()\n// needed. Import directly from this package instead of from the source\n// packages, and use them as native Express middleware.\n//\n// app.get('/jobs', requireAuth, withWorkspace(store), ...)\n\nexport const requireAuth = adapt(_requireAuth);\n\n// The three guards below wrap OPTIONAL peers, so the peer is imported lazily\n// on first request — not at module load. adapt() returns an async Express\n// middleware either way, so the extra await changes nothing for callers.\n\nexport function withWorkspace(store: Parameters<typeof _withWorkspace>[0]) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/workspaces'),\n\t\t\t\t'@fonderie/workspaces',\n\t\t\t\t'withWorkspace()',\n\t\t\t);\n\t\t\tinner = adapt(mod.withWorkspace(store));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\nexport function requirePermission(\n\toperation: Parameters<typeof _requirePermission>[0],\n\tpermissionKey: Parameters<typeof _requirePermission>[1],\n) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/permissions'),\n\t\t\t\t'@fonderie/permissions',\n\t\t\t\t'requirePermission()',\n\t\t\t);\n\t\t\tinner = adapt(mod.requirePermission(operation, permissionKey));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\nexport function requireFeature(key: string) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/billing'),\n\t\t\t\t'@fonderie/billing',\n\t\t\t\t'requireFeature()',\n\t\t\t);\n\t\t\tinner = adapt(mod.requireFeature(key));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\n// ── mount ─────────────────────────────────────────────────────────\n//\n// Wires up fonderie to an Express app. Returns the same app so you can add\n// routes after mount() and before app.listen() — infra is sealed lazily\n// when app.listen() is first called:\n//\n// const api = mount(app, fonderie)\n// api.use(buildTodoRouter(store))\n// app.listen(port)\n//\n// Alternatively pass a register callback to be explicit about ordering:\n//\n// mount(app, fonderie, (app) => {\n// app.use(buildTodoRouter(store))\n// })\n\ntype ExpressApp = {\n\tuse: (...args: any[]) => any;\n\tall: (path: string, handler: (req: ExpressRequest, res: ExpressResponse) => void) => void;\n\tlisten: (...args: any[]) => any;\n};\n\nexport function mount<T extends ExpressApp>(\n\tapp: T,\n\tfonderie: FonderieApp,\n\tregister?: (app: T) => void,\n): T {\n\tconst infraHandler = async (req: ExpressRequest, res: ExpressResponse) => {\n\t\tconst webReq = (req as any)._fonterieReq as Request ?? await expressRequestToWeb(req);\n\t\tconst webRes = await fonderie.handle(webReq);\n\t\tawait webResponseToExpress(webRes, res);\n\t};\n\n\tapp.use(bridge(fonderie));\n\n\tif (register) {\n\t\tregister(app);\n\t\tapp.use(infraHandler);\n\t} else {\n\t\tlet sealed = false;\n\t\tconst origListen = app.listen.bind(app);\n\t\t(app as ExpressApp).listen = (...args: any[]) => {\n\t\t\tif (!sealed) {\n\t\t\t\tsealed = true;\n\t\t\t\tapp.use(infraHandler);\n\t\t\t}\n\t\t\t(app as ExpressApp).listen = origListen;\n\t\t\treturn origListen(...args);\n\t\t};\n\t}\n\n\treturn app;\n}\n"],"mappings":"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","names":["_requireAuth"]}
package/dist/index.js CHANGED
@@ -37,7 +37,11 @@ async function expressRequestToWeb(req) {
37
37
  }
38
38
  async function webResponseToExpress(webRes, res) {
39
39
  res.statusCode = webRes.status;
40
- webRes.headers.forEach((value, key) => res.setHeader(key, value));
40
+ const setCookies = webRes.headers.getSetCookie?.() ?? [];
41
+ if (setCookies.length) res.setHeader("Set-Cookie", setCookies);
42
+ webRes.headers.forEach((value, key) => {
43
+ if (key.toLowerCase() !== "set-cookie") res.setHeader(key, value);
44
+ });
41
45
  res.end(Buffer.from(await webRes.arrayBuffer()));
42
46
  }
43
47
  function readStream(req) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport type { FonderieApp, IFonderieContext, Middleware } from '@fonderie/core';\nimport { requireAuth as _requireAuth, resolveClientIp } from '@fonderie/core/middlewares';\n// Optional peers: type-only imports (erased at runtime). The guard factories\n// below load them lazily so installing this adapter never requires\n// @fonderie/workspaces, @fonderie/permissions, or @fonderie/billing unless\n// the corresponding guard is actually used.\nimport type { withWorkspace as _withWorkspace } from '@fonderie/workspaces';\nimport type { requirePermission as _requirePermission } from '@fonderie/permissions';\n\nexport { OPERATIONS } from '@fonderie/core';\n\nasync function loadOptionalPeer<T>(load: () => Promise<T>, pkg: string, api: string): Promise<T> {\n\ttry {\n\t\treturn await load();\n\t} catch (err) {\n\t\tconst e = err as { code?: string; message?: string } | undefined;\n\t\tconst notFound = e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND';\n\t\t// Only claim the peer is missing when the unresolved specifier IS the\n\t\t// peer — a transitive failure inside an installed peer must surface\n\t\t// as-is, not as a misleading install hint.\n\t\tconst missing = notFound ? /Cannot find (?:package|module) '([^']+)'/.exec(e?.message ?? '')?.[1] : undefined;\n\t\tif (missing === pkg || missing?.startsWith(pkg + '/')) {\n\t\t\tthrow new Error(\n\t\t\t\t`[fonderie] ${api} requires the optional peer dependency \"${pkg}\". Install it: npm install ${pkg}`,\n\t\t\t);\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nexport type ExpressRequest = IncomingMessage & { body?: unknown; _fonderie?: IFonderieContext };\nexport type ExpressResponse = ServerResponse;\nexport type ExpressNext = (err?: unknown) => void;\n\n// ── Web Standard ↔ Express translation ───────────────────────────\n\nexport async function expressRequestToWeb(req: ExpressRequest): Promise<Request> {\n\tconst encrypted = (req.socket as { encrypted?: boolean }).encrypted;\n\tconst protocol = encrypted ? 'https' : 'http';\n\tconst host = req.headers['host'] ?? 'localhost';\n\tconst url = `${protocol}://${host}${req.url ?? '/'}`;\n\n\tconst headers = new Headers();\n\tfor (const [key, value] of Object.entries(req.headers)) {\n\t\tif (!value) continue;\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) headers.append(key, v);\n\t\t} else {\n\t\t\theaders.set(key, value);\n\t\t}\n\t}\n\n\tconst method = req.method ?? 'GET';\n\tconst hasBody = !['GET', 'HEAD', 'OPTIONS'].includes(method.toUpperCase());\n\tconst body = hasBody ? await readStream(req) : null;\n\n\treturn new Request(url, { method, headers, body });\n}\n\nexport async function webResponseToExpress(webRes: Response, res: ExpressResponse): Promise<void> {\n\tres.statusCode = webRes.status;\n\twebRes.headers.forEach((value, key) => res.setHeader(key, value));\n\tres.end(Buffer.from(await webRes.arrayBuffer()));\n}\n\nfunction readStream(req: IncomingMessage): Promise<ArrayBuffer> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\treq.on('data', (chunk: Buffer) => chunks.push(chunk));\n\t\treq.on('end', () => {\n\t\t\tconst buf = Buffer.concat(chunks);\n\t\t\t// slice creates a correctly-sized ArrayBuffer (buf.buffer is a shared pool)\n\t\t\tresolve(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer);\n\t\t});\n\t\treq.on('error', reject);\n\t});\n}\n\n// ── bridge ────────────────────────────────────────────────────────\n//\n// Express middleware. Populates req._fonderie with the fonderie context\n// (user, workspace, meta) for all subsequent route handlers.\n// Also forwards the parsed body to req.body.\n//\n// app.use(bridge(fonderie))\n\nexport function bridge(fonderie: FonderieApp) {\n\treturn async (req: ExpressRequest, _res: ExpressResponse, next: ExpressNext) => {\n\t\ttry {\n\t\t\tconst webReq = await expressRequestToWeb(req);\n\t\t\t// Cache so the infra handler in mount() can reuse it without re-reading\n\t\t\t// the body stream (which can only be consumed once).\n\t\t\t(req as any)._fonterieReq = webReq;\n\t\t\treq._fonderie = await fonderie.buildContext(webReq.clone());\n\t\t\tconst clientIp = resolveClientIp(req.socket?.remoteAddress ?? undefined, webReq.headers);\n\t\t\tif (clientIp) req._fonderie.meta.clientIp = clientIp;\n\t\t\tif (req._fonderie.meta['body'] !== undefined) {\n\t\t\t\treq.body = req._fonderie.meta['body'];\n\t\t\t}\n\t\t\tnext();\n\t\t} catch (err) {\n\t\t\tnext(err);\n\t\t}\n\t};\n}\n\n// ── adapt ─────────────────────────────────────────────────────────\n//\n// Low-level escape hatch — wraps any fonderie Middleware into an Express\n// middleware function. Use this for custom fonderie middleware; prefer the\n// named exports below for the built-in fonderie guards.\n\nexport function adapt(middleware: Middleware) {\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tconst ctx = req._fonderie;\n\t\tif (!ctx) {\n\t\t\tnext(new Error('[fonderie] bridge() must be registered before adapt()'));\n\t\t\treturn;\n\t\t}\n\n\t\tlet continued = false;\n\t\tconst result = await middleware(ctx, async () => {\n\t\t\tcontinued = true;\n\t\t\treturn new Response();\n\t\t});\n\n\t\tif (continued) {\n\t\t\tnext();\n\t\t} else {\n\t\t\tawait webResponseToExpress(result, res);\n\t\t}\n\t};\n}\n\n// ── Pre-adapted middleware ────────────────────────────────────────\n//\n// Drop-in replacements for the fonderie middleware functions — no adapt()\n// needed. Import directly from this package instead of from the source\n// packages, and use them as native Express middleware.\n//\n// app.get('/jobs', requireAuth, withWorkspace(store), ...)\n\nexport const requireAuth = adapt(_requireAuth);\n\n// The three guards below wrap OPTIONAL peers, so the peer is imported lazily\n// on first request — not at module load. adapt() returns an async Express\n// middleware either way, so the extra await changes nothing for callers.\n\nexport function withWorkspace(store: Parameters<typeof _withWorkspace>[0]) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/workspaces'),\n\t\t\t\t'@fonderie/workspaces',\n\t\t\t\t'withWorkspace()',\n\t\t\t);\n\t\t\tinner = adapt(mod.withWorkspace(store));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\nexport function requirePermission(\n\toperation: Parameters<typeof _requirePermission>[0],\n\tpermissionKey: Parameters<typeof _requirePermission>[1],\n) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/permissions'),\n\t\t\t\t'@fonderie/permissions',\n\t\t\t\t'requirePermission()',\n\t\t\t);\n\t\t\tinner = adapt(mod.requirePermission(operation, permissionKey));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\nexport function requireFeature(key: string) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/billing'),\n\t\t\t\t'@fonderie/billing',\n\t\t\t\t'requireFeature()',\n\t\t\t);\n\t\t\tinner = adapt(mod.requireFeature(key));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\n// ── mount ─────────────────────────────────────────────────────────\n//\n// Wires up fonderie to an Express app. Returns the same app so you can add\n// routes after mount() and before app.listen() — infra is sealed lazily\n// when app.listen() is first called:\n//\n// const api = mount(app, fonderie)\n// api.use(buildTodoRouter(store))\n// app.listen(port)\n//\n// Alternatively pass a register callback to be explicit about ordering:\n//\n// mount(app, fonderie, (app) => {\n// app.use(buildTodoRouter(store))\n// })\n\ntype ExpressApp = {\n\tuse: (...args: any[]) => any;\n\tall: (path: string, handler: (req: ExpressRequest, res: ExpressResponse) => void) => void;\n\tlisten: (...args: any[]) => any;\n};\n\nexport function mount<T extends ExpressApp>(\n\tapp: T,\n\tfonderie: FonderieApp,\n\tregister?: (app: T) => void,\n): T {\n\tconst infraHandler = async (req: ExpressRequest, res: ExpressResponse) => {\n\t\tconst webReq = (req as any)._fonterieReq as Request ?? await expressRequestToWeb(req);\n\t\tconst webRes = await fonderie.handle(webReq);\n\t\tawait webResponseToExpress(webRes, res);\n\t};\n\n\tapp.use(bridge(fonderie));\n\n\tif (register) {\n\t\tregister(app);\n\t\tapp.use(infraHandler);\n\t} else {\n\t\tlet sealed = false;\n\t\tconst origListen = app.listen.bind(app);\n\t\t(app as ExpressApp).listen = (...args: any[]) => {\n\t\t\tif (!sealed) {\n\t\t\t\tsealed = true;\n\t\t\t\tapp.use(infraHandler);\n\t\t\t}\n\t\t\t(app as ExpressApp).listen = origListen;\n\t\t\treturn origListen(...args);\n\t\t};\n\t}\n\n\treturn app;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport type { FonderieApp, IFonderieContext, Middleware } from '@fonderie/core';\nimport { requireAuth as _requireAuth, resolveClientIp } from '@fonderie/core/middlewares';\n// Optional peers: type-only imports (erased at runtime). The guard factories\n// below load them lazily so installing this adapter never requires\n// @fonderie/workspaces, @fonderie/permissions, or @fonderie/billing unless\n// the corresponding guard is actually used.\nimport type { withWorkspace as _withWorkspace } from '@fonderie/workspaces';\nimport type { requirePermission as _requirePermission } from '@fonderie/permissions';\n\nexport { OPERATIONS } from '@fonderie/core';\n\nasync function loadOptionalPeer<T>(load: () => Promise<T>, pkg: string, api: string): Promise<T> {\n\ttry {\n\t\treturn await load();\n\t} catch (err) {\n\t\tconst e = err as { code?: string; message?: string } | undefined;\n\t\tconst notFound = e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND';\n\t\t// Only claim the peer is missing when the unresolved specifier IS the\n\t\t// peer — a transitive failure inside an installed peer must surface\n\t\t// as-is, not as a misleading install hint.\n\t\tconst missing = notFound ? /Cannot find (?:package|module) '([^']+)'/.exec(e?.message ?? '')?.[1] : undefined;\n\t\tif (missing === pkg || missing?.startsWith(pkg + '/')) {\n\t\t\tthrow new Error(\n\t\t\t\t`[fonderie] ${api} requires the optional peer dependency \"${pkg}\". Install it: npm install ${pkg}`,\n\t\t\t);\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nexport type ExpressRequest = IncomingMessage & { body?: unknown; _fonderie?: IFonderieContext };\nexport type ExpressResponse = ServerResponse;\nexport type ExpressNext = (err?: unknown) => void;\n\n// ── Web Standard ↔ Express translation ───────────────────────────\n\nexport async function expressRequestToWeb(req: ExpressRequest): Promise<Request> {\n\tconst encrypted = (req.socket as { encrypted?: boolean }).encrypted;\n\tconst protocol = encrypted ? 'https' : 'http';\n\tconst host = req.headers['host'] ?? 'localhost';\n\tconst url = `${protocol}://${host}${req.url ?? '/'}`;\n\n\tconst headers = new Headers();\n\tfor (const [key, value] of Object.entries(req.headers)) {\n\t\tif (!value) continue;\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) headers.append(key, v);\n\t\t} else {\n\t\t\theaders.set(key, value);\n\t\t}\n\t}\n\n\tconst method = req.method ?? 'GET';\n\tconst hasBody = !['GET', 'HEAD', 'OPTIONS'].includes(method.toUpperCase());\n\tconst body = hasBody ? await readStream(req) : null;\n\n\treturn new Request(url, { method, headers, body });\n}\n\nexport async function webResponseToExpress(webRes: Response, res: ExpressResponse): Promise<void> {\n\tres.statusCode = webRes.status;\n\t// Set-Cookie is special: a response may carry SEVERAL, and `forEach` +\n\t// `setHeader` would overwrite all but the last (and coalescing them into one\n\t// comma-joined header is invalid). Forward the full list via getSetCookie().\n\tconst setCookies = webRes.headers.getSetCookie?.() ?? [];\n\tif (setCookies.length) res.setHeader('Set-Cookie', setCookies);\n\twebRes.headers.forEach((value, key) => {\n\t\tif (key.toLowerCase() !== 'set-cookie') res.setHeader(key, value);\n\t});\n\tres.end(Buffer.from(await webRes.arrayBuffer()));\n}\n\nfunction readStream(req: IncomingMessage): Promise<ArrayBuffer> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\treq.on('data', (chunk: Buffer) => chunks.push(chunk));\n\t\treq.on('end', () => {\n\t\t\tconst buf = Buffer.concat(chunks);\n\t\t\t// slice creates a correctly-sized ArrayBuffer (buf.buffer is a shared pool)\n\t\t\tresolve(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer);\n\t\t});\n\t\treq.on('error', reject);\n\t});\n}\n\n// ── bridge ────────────────────────────────────────────────────────\n//\n// Express middleware. Populates req._fonderie with the fonderie context\n// (user, workspace, meta) for all subsequent route handlers.\n// Also forwards the parsed body to req.body.\n//\n// app.use(bridge(fonderie))\n\nexport function bridge(fonderie: FonderieApp) {\n\treturn async (req: ExpressRequest, _res: ExpressResponse, next: ExpressNext) => {\n\t\ttry {\n\t\t\tconst webReq = await expressRequestToWeb(req);\n\t\t\t// Cache so the infra handler in mount() can reuse it without re-reading\n\t\t\t// the body stream (which can only be consumed once).\n\t\t\t(req as any)._fonterieReq = webReq;\n\t\t\treq._fonderie = await fonderie.buildContext(webReq.clone());\n\t\t\tconst clientIp = resolveClientIp(req.socket?.remoteAddress ?? undefined, webReq.headers);\n\t\t\tif (clientIp) req._fonderie.meta.clientIp = clientIp;\n\t\t\tif (req._fonderie.meta['body'] !== undefined) {\n\t\t\t\treq.body = req._fonderie.meta['body'];\n\t\t\t}\n\t\t\tnext();\n\t\t} catch (err) {\n\t\t\tnext(err);\n\t\t}\n\t};\n}\n\n// ── adapt ─────────────────────────────────────────────────────────\n//\n// Low-level escape hatch — wraps any fonderie Middleware into an Express\n// middleware function. Use this for custom fonderie middleware; prefer the\n// named exports below for the built-in fonderie guards.\n\nexport function adapt(middleware: Middleware) {\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tconst ctx = req._fonderie;\n\t\tif (!ctx) {\n\t\t\tnext(new Error('[fonderie] bridge() must be registered before adapt()'));\n\t\t\treturn;\n\t\t}\n\n\t\tlet continued = false;\n\t\tconst result = await middleware(ctx, async () => {\n\t\t\tcontinued = true;\n\t\t\treturn new Response();\n\t\t});\n\n\t\tif (continued) {\n\t\t\tnext();\n\t\t} else {\n\t\t\tawait webResponseToExpress(result, res);\n\t\t}\n\t};\n}\n\n// ── Pre-adapted middleware ────────────────────────────────────────\n//\n// Drop-in replacements for the fonderie middleware functions — no adapt()\n// needed. Import directly from this package instead of from the source\n// packages, and use them as native Express middleware.\n//\n// app.get('/jobs', requireAuth, withWorkspace(store), ...)\n\nexport const requireAuth = adapt(_requireAuth);\n\n// The three guards below wrap OPTIONAL peers, so the peer is imported lazily\n// on first request — not at module load. adapt() returns an async Express\n// middleware either way, so the extra await changes nothing for callers.\n\nexport function withWorkspace(store: Parameters<typeof _withWorkspace>[0]) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/workspaces'),\n\t\t\t\t'@fonderie/workspaces',\n\t\t\t\t'withWorkspace()',\n\t\t\t);\n\t\t\tinner = adapt(mod.withWorkspace(store));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\nexport function requirePermission(\n\toperation: Parameters<typeof _requirePermission>[0],\n\tpermissionKey: Parameters<typeof _requirePermission>[1],\n) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/permissions'),\n\t\t\t\t'@fonderie/permissions',\n\t\t\t\t'requirePermission()',\n\t\t\t);\n\t\t\tinner = adapt(mod.requirePermission(operation, permissionKey));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\nexport function requireFeature(key: string) {\n\tlet inner: ReturnType<typeof adapt> | undefined;\n\treturn async (req: ExpressRequest, res: ExpressResponse, next: ExpressNext) => {\n\t\tif (!inner) {\n\t\t\tconst mod = await loadOptionalPeer(\n\t\t\t\t() => import('@fonderie/billing'),\n\t\t\t\t'@fonderie/billing',\n\t\t\t\t'requireFeature()',\n\t\t\t);\n\t\t\tinner = adapt(mod.requireFeature(key));\n\t\t}\n\t\treturn inner(req, res, next);\n\t};\n}\n\n// ── mount ─────────────────────────────────────────────────────────\n//\n// Wires up fonderie to an Express app. Returns the same app so you can add\n// routes after mount() and before app.listen() — infra is sealed lazily\n// when app.listen() is first called:\n//\n// const api = mount(app, fonderie)\n// api.use(buildTodoRouter(store))\n// app.listen(port)\n//\n// Alternatively pass a register callback to be explicit about ordering:\n//\n// mount(app, fonderie, (app) => {\n// app.use(buildTodoRouter(store))\n// })\n\ntype ExpressApp = {\n\tuse: (...args: any[]) => any;\n\tall: (path: string, handler: (req: ExpressRequest, res: ExpressResponse) => void) => void;\n\tlisten: (...args: any[]) => any;\n};\n\nexport function mount<T extends ExpressApp>(\n\tapp: T,\n\tfonderie: FonderieApp,\n\tregister?: (app: T) => void,\n): T {\n\tconst infraHandler = async (req: ExpressRequest, res: ExpressResponse) => {\n\t\tconst webReq = (req as any)._fonterieReq as Request ?? await expressRequestToWeb(req);\n\t\tconst webRes = await fonderie.handle(webReq);\n\t\tawait webResponseToExpress(webRes, res);\n\t};\n\n\tapp.use(bridge(fonderie));\n\n\tif (register) {\n\t\tregister(app);\n\t\tapp.use(infraHandler);\n\t} else {\n\t\tlet sealed = false;\n\t\tconst origListen = app.listen.bind(app);\n\t\t(app as ExpressApp).listen = (...args: any[]) => {\n\t\t\tif (!sealed) {\n\t\t\t\tsealed = true;\n\t\t\t\tapp.use(infraHandler);\n\t\t\t}\n\t\t\t(app as ExpressApp).listen = origListen;\n\t\t\treturn origListen(...args);\n\t\t};\n\t}\n\n\treturn app;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonderie/adapter-express",
3
- "version": "1.0.3",
3
+ "version": "3.0.0",
4
4
  "description": "Express adapter for fonderie-js — bridge(), adapt(), mount() to use fonderie middleware in native Express routes.",
5
5
  "keywords": [
6
6
  "fonderie-js",
@@ -35,10 +35,10 @@
35
35
  "check": "biome check --write src"
36
36
  },
37
37
  "peerDependencies": {
38
- "@fonderie/core": "^0.1.1",
39
- "@fonderie/workspaces": "^1.0.1",
40
- "@fonderie/permissions": "^1.0.1",
41
- "@fonderie/billing": "^1.0.1",
38
+ "@fonderie/core": "^0.3.0",
39
+ "@fonderie/workspaces": "^3.0.0",
40
+ "@fonderie/permissions": "^3.0.0",
41
+ "@fonderie/billing": "^3.0.0",
42
42
  "express": "^4.0.0 || ^5.0.0"
43
43
  },
44
44
  "peerDependenciesMeta": {