nestjs-slightly-better-auth 1.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/LICENSE +7 -0
- package/README.md +57 -0
- package/dist/admin.cjs +88 -0
- package/dist/admin.cjs.map +1 -0
- package/dist/admin.d.cts +14 -0
- package/dist/admin.d.cts.map +1 -0
- package/dist/admin.d.mts +14 -0
- package/dist/admin.d.mts.map +1 -0
- package/dist/admin.mjs +85 -0
- package/dist/admin.mjs.map +1 -0
- package/dist/api-key.cjs +163 -0
- package/dist/api-key.cjs.map +1 -0
- package/dist/api-key.d.cts +42 -0
- package/dist/api-key.d.cts.map +1 -0
- package/dist/api-key.d.mts +42 -0
- package/dist/api-key.d.mts.map +1 -0
- package/dist/api-key.mjs +159 -0
- package/dist/api-key.mjs.map +1 -0
- package/dist/auth-contracts-BBA1C1gj.d.cts +1635 -0
- package/dist/auth-contracts-BBA1C1gj.d.cts.map +1 -0
- package/dist/auth-contracts-tqsfBZ8B.d.mts +1635 -0
- package/dist/auth-contracts-tqsfBZ8B.d.mts.map +1 -0
- package/dist/auth-decorators-CwW5JY4y.cjs +1274 -0
- package/dist/auth-decorators-CwW5JY4y.cjs.map +1 -0
- package/dist/auth-decorators-DfAJbHar.mjs +849 -0
- package/dist/auth-decorators-DfAJbHar.mjs.map +1 -0
- package/dist/auth-errors-CQjfgjaO.mjs +283 -0
- package/dist/auth-errors-CQjfgjaO.mjs.map +1 -0
- package/dist/auth-errors-CsERBNWI.cjs +348 -0
- package/dist/auth-errors-CsERBNWI.cjs.map +1 -0
- package/dist/express.cjs +247 -0
- package/dist/express.cjs.map +1 -0
- package/dist/express.d.cts +37 -0
- package/dist/express.d.cts.map +1 -0
- package/dist/express.d.mts +37 -0
- package/dist/express.d.mts.map +1 -0
- package/dist/express.mjs +245 -0
- package/dist/express.mjs.map +1 -0
- package/dist/fastify.cjs +215 -0
- package/dist/fastify.cjs.map +1 -0
- package/dist/fastify.d.cts +28 -0
- package/dist/fastify.d.cts.map +1 -0
- package/dist/fastify.d.mts +28 -0
- package/dist/fastify.d.mts.map +1 -0
- package/dist/fastify.mjs +213 -0
- package/dist/fastify.mjs.map +1 -0
- package/dist/index.cjs +2634 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2556 -0
- package/dist/index.mjs.map +1 -0
- package/dist/organization.cjs +191 -0
- package/dist/organization.cjs.map +1 -0
- package/dist/organization.d.cts +40 -0
- package/dist/organization.d.cts.map +1 -0
- package/dist/organization.d.mts +40 -0
- package/dist/organization.d.mts.map +1 -0
- package/dist/organization.mjs +177 -0
- package/dist/organization.mjs.map +1 -0
- package/dist/platform.cjs +393 -0
- package/dist/platform.cjs.map +1 -0
- package/dist/platform.d.cts +43 -0
- package/dist/platform.d.cts.map +1 -0
- package/dist/platform.d.mts +43 -0
- package/dist/platform.d.mts.map +1 -0
- package/dist/platform.mjs +385 -0
- package/dist/platform.mjs.map +1 -0
- package/dist/plugin.cjs +312 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +26 -0
- package/dist/plugin.d.cts.map +1 -0
- package/dist/plugin.d.mts +26 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +310 -0
- package/dist/plugin.mjs.map +1 -0
- package/package.json +186 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.cjs","names":["BetterAuthConfigurationError","parse","toWebHeaders","appendSetCookie","mayHaveBody","bindAsyncContext","recoverConsumedBody","readBoundedBody","pathToRegexp","writeWebResponse"],"sources":["../src/express-platform.ts"],"sourcesContent":["import type {\n IncomingHttpHeaders,\n IncomingMessage,\n ServerResponse,\n} from \"node:http\";\nimport type { AbstractHttpAdapter } from \"@nestjs/core\";\nimport { parse, pathToRegexp } from \"path-to-regexp\";\nimport type {\n ApplicationRouteDescriptor,\n AuthRouteBinding,\n ExpressPlatformOptions,\n HttpPlatform,\n PlatformMountContext,\n PlatformPrepareContext,\n ProxyTrust,\n} from \"./auth-contracts.js\";\nimport { BetterAuthConfigurationError } from \"./auth-errors.js\";\nimport {\n appendSetCookie,\n bindAsyncContext,\n mayHaveBody,\n readBoundedBody,\n recoverConsumedBody,\n toWebHeaders,\n writeWebResponse,\n} from \"./platform.js\";\n\nconst RAW_BODY = Symbol.for(\"nestjs-slightly-better-auth:raw-body\");\n\ntype Next = (error?: unknown) => void;\ntype CapturedBody = Awaited<ReturnType<typeof readBoundedBody>>;\ntype ExpressResponse = ServerResponse & { req?: ExpressRequest };\ntype ExpressRequest = IncomingMessage & {\n readonly originalUrl?: string;\n readonly protocol?: string;\n readonly host?: string;\n readonly ip?: string;\n readonly res?: ExpressResponse;\n readonly params?: Record<string, unknown>;\n readonly body?: unknown;\n readonly rawBody?: Buffer;\n [RAW_BODY]?: CapturedBody;\n};\ntype ExpressApplication = {\n use(\n handler: (req: ExpressRequest, res: ExpressResponse, next: Next) => void,\n ): unknown;\n get(setting: string): unknown;\n readonly router?: unknown;\n};\ntype ExpressRouterOptions = {\n readonly caseSensitive: boolean;\n readonly strict: boolean;\n};\ntype ApplicationRouteMatcher = {\n readonly method: string;\n readonly regexp: RegExp;\n};\n\nfunction application(adapter: AbstractHttpAdapter): ExpressApplication {\n return adapter.getInstance() as ExpressApplication;\n}\n\nfunction routerOptions(\n app: ExpressApplication | undefined,\n): ExpressRouterOptions {\n const router = app?.router;\n if (\n (typeof router !== \"object\" && typeof router !== \"function\") ||\n router === null\n ) {\n throw new BetterAuthConfigurationError(\n \"UNSUPPORTED_EXPRESS_ROUTER\",\n \"The Express application does not expose its initialized router.\",\n \"Use Express 5.2.x with @nestjs/platform-express 12.x, or move controller routes outside the auth mount.\",\n );\n }\n const options = router as Partial<ExpressRouterOptions>;\n if (\n typeof options.caseSensitive !== \"boolean\" ||\n typeof options.strict !== \"boolean\"\n ) {\n throw new BetterAuthConfigurationError(\n \"UNSUPPORTED_EXPRESS_ROUTER\",\n \"The initialized Express router does not expose compatible routing options.\",\n \"Use Express 5.2.x with @nestjs/platform-express 12.x, or move controller routes outside the auth mount.\",\n );\n }\n return { caseSensitive: options.caseSensitive, strict: options.strict };\n}\n\nfunction routeMayOverlapMount(path: string, basePath: string): boolean {\n const tokens = parse(path).tokens;\n let literalPrefix = \"\";\n let dynamic = false;\n for (const token of tokens) {\n if (token.type !== \"text\") {\n dynamic = true;\n break;\n }\n literalPrefix += token.value;\n }\n const route = literalPrefix.toLowerCase();\n const base = basePath.toLowerCase().replace(/(?<!\\/)\\/+$/, \"\") || \"/\";\n if (base === \"/\") {\n return route.startsWith(\"/\");\n }\n if (!dynamic) {\n const exact = route.replace(/(?<!\\/)\\/+$/, \"\") || \"/\";\n return exact === base || exact.startsWith(`${base}/`);\n }\n if (base.startsWith(route)) {\n return true;\n }\n return route === base || route.startsWith(`${base}/`);\n}\n\nfunction rawTarget(req: ExpressRequest): string {\n return req.originalUrl ?? req.url ?? \"/\";\n}\n\nfunction rawPath(req: ExpressRequest): string {\n const target = rawTarget(req);\n const query = target.indexOf(\"?\");\n return query === -1 ? target : target.slice(0, query);\n}\n\nfunction declaredLength(\n headers: IncomingHttpHeaders,\n limit: number,\n): number | undefined {\n const value = headers[\"content-length\"];\n const first = Array.isArray(value) ? value[0] : value;\n if (typeof first !== \"string\" || !/^\\d+$/.test(first)) {\n return undefined;\n }\n const parsed = Number(first);\n return Number.isSafeInteger(parsed) ? parsed : limit + 1;\n}\n\nfunction requestUrl(req: ExpressRequest): string {\n const socket = req.socket as typeof req.socket & { encrypted?: boolean };\n return `${req.protocol ?? (socket.encrypted ? \"https\" : \"http\")}://${req.host ?? \"\"}${rawTarget(req)}`;\n}\n\nfunction isExpressRequest(value: unknown): value is ExpressRequest {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const req = value as Partial<ExpressRequest>;\n return (\n typeof req.method === \"string\" &&\n typeof req.headers === \"object\" &&\n typeof req.res === \"object\" &&\n req.res !== null &&\n (req.res.req === undefined || req.res.req === value)\n );\n}\n\nfunction nativeResponse(value: unknown): ServerResponse | null {\n if (typeof value !== \"object\" || value === null) {\n return null;\n }\n const response = value as Partial<ServerResponse>;\n return typeof response.setHeader === \"function\" &&\n typeof response.getHeader === \"function\"\n ? (value as ServerResponse)\n : null;\n}\n\nfunction proxyTrustOf(value: unknown): ProxyTrust {\n if (value === false || value === undefined) {\n return { mode: \"none\", detail: \"trust proxy = false\" };\n }\n if (value === true) {\n return { mode: \"all\", detail: \"trust proxy = true\" };\n }\n const rendered =\n typeof value === \"function\"\n ? \"<function>\"\n : Array.isArray(value)\n ? JSON.stringify(value)\n : String(value);\n return { mode: \"partial\", detail: `trust proxy = ${rendered}` };\n}\n\nfunction abortOnDisconnect(\n req: ExpressRequest,\n res: ExpressResponse,\n): { signal: AbortSignal; dispose(): void } {\n const controller = new AbortController();\n const abort = () => controller.abort();\n req.once(\"aborted\", abort);\n res.once(\"close\", abort);\n if (req.aborted || res.destroyed) {\n abort();\n }\n return {\n signal: controller.signal,\n dispose: () => {\n req.removeListener(\"aborted\", abort);\n res.removeListener(\"close\", abort);\n },\n };\n}\n\n/** Express-specific two-phase HTTP integration. */\nexport class ExpressPlatform implements HttpPlatform {\n readonly id = \"express\";\n #warnedConsumedBody = false;\n #applicationRoutes: readonly ApplicationRouteMatcher[] = [];\n #application: ExpressApplication | undefined;\n\n constructor(private readonly options: ExpressPlatformOptions = {}) {}\n\n readonly requests = {\n isRequest: (value: unknown): boolean => isExpressRequest(value),\n isLive: (value: unknown): boolean =>\n isExpressRequest(value) && value.res?.writableEnded === false,\n key: (value: unknown): object => value as object,\n headers: (value: unknown): Headers =>\n toWebHeaders((value as ExpressRequest).headers),\n request: (value: unknown) => {\n const req = value as ExpressRequest;\n return { method: req.method ?? \"GET\", url: requestUrl(req) };\n },\n clientIp: (value: unknown): string | null =>\n this.clientIp(value as ExpressRequest),\n param: (value: unknown, name: string): string | undefined => {\n const parameter = (value as ExpressRequest).params?.[name];\n return typeof parameter === \"string\" ? parameter : undefined;\n },\n cookieSink: (request: unknown, response: unknown) => {\n const req = request as ExpressRequest;\n const res = nativeResponse(response) ?? nativeResponse(req.res);\n return res === null\n ? null\n : {\n append: (cookies: readonly string[]) =>\n appendSetCookie(res, cookies),\n };\n },\n };\n\n supports(adapter: AbstractHttpAdapter): boolean {\n return adapter.getType?.() === \"express\";\n }\n\n prepare(ctx: PlatformPrepareContext): void {\n const app = application(ctx.adapter);\n this.#application = app;\n app.use((req, _res, next) => {\n const binding = ctx.route(rawPath(req));\n const method = (req.method ?? \"GET\").toUpperCase();\n if (\n !binding ||\n this.isApplicationRoute(method, rawPath(req)) ||\n method === \"GET\" ||\n method === \"HEAD\" ||\n method === \"OPTIONS\" ||\n !mayHaveBody(method, req.headers)\n ) {\n next();\n return;\n }\n\n const continueRequest = bindAsyncContext(next);\n if (req.readableEnded || req.readable === false) {\n const recovered = recoverConsumedBody(req);\n req[RAW_BODY] = {\n ok: true,\n bytes: recovered ?? new Uint8Array(),\n };\n if (!this.#warnedConsumedBody) {\n this.#warnedConsumedBody = true;\n ctx.logger.warn(\n \"W_BODY_ALREADY_CONSUMED: an Express body parser ran before the auth capture; raw-signature webhooks may receive re-serialized bytes.\",\n );\n }\n continueRequest();\n return;\n }\n\n void readBoundedBody(req, {\n limit: binding.bodyLimit,\n declaredLength: declaredLength(req.headers, binding.bodyLimit),\n drainOnOverflow: true,\n drainCap: Math.min(Number.MAX_SAFE_INTEGER, binding.bodyLimit * 8),\n }).then(\n (captured) => {\n req[RAW_BODY] = captured;\n continueRequest();\n },\n (error: unknown) => continueRequest(error),\n );\n });\n }\n\n applicationRoutes(\n routes: readonly ApplicationRouteDescriptor[],\n bindings: readonly AuthRouteBinding[],\n ): void {\n const bodyRoutes = routes.flatMap((route) => {\n if ([\"GET\", \"HEAD\", \"OPTIONS\"].includes(route.method)) {\n return [];\n }\n const paths = route.paths.filter((path) =>\n bindings.some((binding) =>\n routeMayOverlapMount(path, binding.basePath),\n ),\n );\n return paths.length ? [{ ...route, paths }] : [];\n });\n const conditional = bodyRoutes.find((route) => route.conditions.length > 0);\n if (conditional) {\n throw new BetterAuthConfigurationError(\n \"CONDITIONAL_ROUTE_SHADOW\",\n `${conditional.source} at '${conditional.paths.join(\", \")}' uses ${conditional.conditions.join(\" and \")} routing under an auth mount.`,\n \"Move the controller route outside the auth mount or make its host and version routing unconditional.\",\n );\n }\n if (bodyRoutes.length === 0) {\n this.#applicationRoutes = [];\n return;\n }\n const { caseSensitive, strict } = routerOptions(this.#application);\n this.#applicationRoutes = Object.freeze(\n bodyRoutes.flatMap((route) =>\n route.paths.map((path) => ({\n method: route.method,\n regexp: pathToRegexp(path, {\n end: true,\n sensitive: caseSensitive,\n trailing: !strict,\n }).regexp,\n })),\n ),\n );\n }\n\n mount(ctx: PlatformMountContext): void {\n application(ctx.adapter).use((req, res, next) => {\n if (!ctx.binding.matches(rawPath(req))) {\n next();\n return;\n }\n const disconnect = abortOnDisconnect(req, res);\n void this.dispatch(ctx.binding, req, res, disconnect.signal).then(\n () => disconnect.dispose(),\n (error: unknown) => {\n disconnect.dispose();\n if (!disconnect.signal.aborted) {\n next(error);\n }\n },\n );\n });\n }\n\n proxyTrust(adapter: AbstractHttpAdapter): ProxyTrust {\n return proxyTrustOf(application(adapter).get(\"trust proxy\"));\n }\n\n private clientIp(req: ExpressRequest): string | null {\n if (this.options.clientIp) {\n return this.options.clientIp(req);\n }\n return req.ip ?? null;\n }\n\n private isApplicationRoute(method: string, pathname: string): boolean {\n return this.#applicationRoutes.some(\n (route) =>\n (route.method === \"ALL\" || route.method === method) &&\n route.regexp.test(pathname),\n );\n }\n\n private async dispatch(\n binding: AuthRouteBinding,\n req: ExpressRequest,\n res: ExpressResponse,\n signal: AbortSignal,\n ): Promise<void> {\n const captured = req[RAW_BODY];\n if (\n (captured && !captured.ok && captured.reason === \"aborted\") ||\n signal.aborted\n ) {\n return;\n }\n const response =\n captured && !captured.ok && captured.reason === \"too-large\"\n ? binding.payloadTooLarge()\n : await binding.handle({\n method: req.method ?? \"GET\",\n url: requestUrl(req),\n headers: toWebHeaders(req.headers),\n body: captured?.ok ? captured.bytes : null,\n clientIp: this.clientIp(req),\n platformRequest: req,\n signal,\n });\n if (signal.aborted) {\n await response.body?.cancel().catch(() => undefined);\n return;\n }\n await writeWebResponse(res, response, {\n head: req.method?.toUpperCase() === \"HEAD\",\n });\n }\n}\n\nexport function expressPlatform(\n options?: ExpressPlatformOptions,\n): ExpressPlatform {\n return new ExpressPlatform(options);\n}\n"],"mappings":";;;;;AA2BA,MAAM,WAAW,OAAO,IAAI,sCAAsC;AAgClE,SAAS,YAAY,SAAkD;CACrE,OAAO,QAAQ,YAAY;AAC7B;AAEA,SAAS,cACP,KACsB;CACtB,MAAM,SAAS,KAAK;CACpB,IACG,OAAO,WAAW,YAAY,OAAO,WAAW,cACjD,WAAW,MAEX,MAAM,IAAIA,oBAAAA,6BACR,8BACA,mEACA,yGACF;CAEF,MAAM,UAAU;CAChB,IACE,OAAO,QAAQ,kBAAkB,aACjC,OAAO,QAAQ,WAAW,WAE1B,MAAM,IAAIA,oBAAAA,6BACR,8BACA,8EACA,yGACF;CAEF,OAAO;EAAE,eAAe,QAAQ;EAAe,QAAQ,QAAQ;CAAO;AACxE;AAEA,SAAS,qBAAqB,MAAc,UAA2B;CACrE,MAAM,UAAA,GAASC,eAAAA,MAAAA,CAAM,IAAI,CAAC,CAAC;CAC3B,IAAI,gBAAgB;CACpB,IAAI,UAAU;CACd,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,SAAS,QAAQ;GACzB,UAAU;GACV;EACF;EACA,iBAAiB,MAAM;CACzB;CACA,MAAM,QAAQ,cAAc,YAAY;CACxC,MAAM,OAAO,SAAS,YAAY,CAAC,CAAC,QAAQ,eAAe,EAAE,KAAK;CAClE,IAAI,SAAS,KACX,OAAO,MAAM,WAAW,GAAG;CAE7B,IAAI,CAAC,SAAS;EACZ,MAAM,QAAQ,MAAM,QAAQ,eAAe,EAAE,KAAK;EAClD,OAAO,UAAU,QAAQ,MAAM,WAAW,GAAG,KAAK,EAAE;CACtD;CACA,IAAI,KAAK,WAAW,KAAK,GACvB,OAAO;CAET,OAAO,UAAU,QAAQ,MAAM,WAAW,GAAG,KAAK,EAAE;AACtD;AAEA,SAAS,UAAU,KAA6B;CAC9C,OAAO,IAAI,eAAe,IAAI,OAAO;AACvC;AAEA,SAAS,QAAQ,KAA6B;CAC5C,MAAM,SAAS,UAAU,GAAG;CAC5B,MAAM,QAAQ,OAAO,QAAQ,GAAG;CAChC,OAAO,UAAU,KAAK,SAAS,OAAO,MAAM,GAAG,KAAK;AACtD;AAEA,SAAS,eACP,SACA,OACoB;CACpB,MAAM,QAAQ,QAAQ;CACtB,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK;CAChD,IAAI,OAAO,UAAU,YAAY,CAAC,QAAQ,KAAK,KAAK,GAClD;CAEF,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,OAAO,cAAc,MAAM,IAAI,SAAS,QAAQ;AACzD;AAEA,SAAS,WAAW,KAA6B;CAC/C,MAAM,SAAS,IAAI;CACnB,OAAO,GAAG,IAAI,aAAa,OAAO,YAAY,UAAU,QAAQ,KAAK,IAAI,QAAQ,KAAK,UAAU,GAAG;AACrG;AAEA,SAAS,iBAAiB,OAAyC;CACjE,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAET,MAAM,MAAM;CACZ,OACE,OAAO,IAAI,WAAW,YACtB,OAAO,IAAI,YAAY,YACvB,OAAO,IAAI,QAAQ,YACnB,IAAI,QAAQ,SACX,IAAI,IAAI,QAAQ,KAAA,KAAa,IAAI,IAAI,QAAQ;AAElD;AAEA,SAAS,eAAe,OAAuC;CAC7D,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAET,MAAM,WAAW;CACjB,OAAO,OAAO,SAAS,cAAc,cACnC,OAAO,SAAS,cAAc,aAC3B,QACD;AACN;AAEA,SAAS,aAAa,OAA4B;CAChD,IAAI,UAAU,SAAS,UAAU,KAAA,GAC/B,OAAO;EAAE,MAAM;EAAQ,QAAQ;CAAsB;CAEvD,IAAI,UAAU,MACZ,OAAO;EAAE,MAAM;EAAO,QAAQ;CAAqB;CAQrD,OAAO;EAAE,MAAM;EAAW,QAAQ,iBALhC,OAAO,UAAU,aACb,eACA,MAAM,QAAQ,KAAK,IACjB,KAAK,UAAU,KAAK,IACpB,OAAO,KAAK;CAC0C;AAChE;AAEA,SAAS,kBACP,KACA,KAC0C;CAC1C,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,cAAc,WAAW,MAAM;CACrC,IAAI,KAAK,WAAW,KAAK;CACzB,IAAI,KAAK,SAAS,KAAK;CACvB,IAAI,IAAI,WAAW,IAAI,WACrB,MAAM;CAER,OAAO;EACL,QAAQ,WAAW;EACnB,eAAe;GACb,IAAI,eAAe,WAAW,KAAK;GACnC,IAAI,eAAe,SAAS,KAAK;EACnC;CACF;AACF;;AAGA,IAAa,kBAAb,MAAqD;CAMtB;CAL7B,KAAc;CACd,sBAAsB;CACtB,qBAAyD,CAAC;CAC1D;CAEA,YAAY,UAAmD,CAAC,GAAG;EAAtC,KAAA,UAAA;CAAuC;CAEpE,WAAoB;EAClB,YAAY,UAA4B,iBAAiB,KAAK;EAC9D,SAAS,UACP,iBAAiB,KAAK,KAAK,MAAM,KAAK,kBAAkB;EAC1D,MAAM,UAA2B;EACjC,UAAU,UACRC,iBAAAA,aAAc,MAAyB,OAAO;EAChD,UAAU,UAAmB;GAC3B,MAAM,MAAM;GACZ,OAAO;IAAE,QAAQ,IAAI,UAAU;IAAO,KAAK,WAAW,GAAG;GAAE;EAC7D;EACA,WAAW,UACT,KAAK,SAAS,KAAuB;EACvC,QAAQ,OAAgB,SAAqC;GAC3D,MAAM,YAAa,MAAyB,SAAS;GACrD,OAAO,OAAO,cAAc,WAAW,YAAY,KAAA;EACrD;EACA,aAAa,SAAkB,aAAsB;GACnD,MAAM,MAAM;GACZ,MAAM,MAAM,eAAe,QAAQ,KAAK,eAAe,IAAI,GAAG;GAC9D,OAAO,QAAQ,OACX,OACA,EACE,SAAS,YACPC,iBAAAA,gBAAgB,KAAK,OAAO,EAChC;EACN;CACF;CAEA,SAAS,SAAuC;EAC9C,OAAO,QAAQ,UAAU,MAAM;CACjC;CAEA,QAAQ,KAAmC;EACzC,MAAM,MAAM,YAAY,IAAI,OAAO;EACnC,KAAK,eAAe;EACpB,IAAI,KAAK,KAAK,MAAM,SAAS;GAC3B,MAAM,UAAU,IAAI,MAAM,QAAQ,GAAG,CAAC;GACtC,MAAM,UAAU,IAAI,UAAU,MAAA,CAAO,YAAY;GACjD,IACE,CAAC,WACD,KAAK,mBAAmB,QAAQ,QAAQ,GAAG,CAAC,KAC5C,WAAW,SACX,WAAW,UACX,WAAW,aACX,CAACC,iBAAAA,YAAY,QAAQ,IAAI,OAAO,GAChC;IACA,KAAK;IACL;GACF;GAEA,MAAM,kBAAkBC,iBAAAA,iBAAiB,IAAI;GAC7C,IAAI,IAAI,iBAAiB,IAAI,aAAa,OAAO;IAC/C,MAAM,YAAYC,iBAAAA,oBAAoB,GAAG;IACzC,IAAI,YAAY;KACd,IAAI;KACJ,OAAO,6BAAa,IAAI,WAAW;IACrC;IACA,IAAI,CAAC,KAAK,qBAAqB;KAC7B,KAAK,sBAAsB;KAC3B,IAAI,OAAO,KACT,sIACF;IACF;IACA,gBAAgB;IAChB;GACF;GAEA,iBAAKC,gBAAgB,KAAK;IACxB,OAAO,QAAQ;IACf,gBAAgB,eAAe,IAAI,SAAS,QAAQ,SAAS;IAC7D,iBAAiB;IACjB,UAAU,KAAK,IAAI,OAAO,kBAAkB,QAAQ,YAAY,CAAC;GACnE,CAAC,CAAC,CAAC,MACA,aAAa;IACZ,IAAI,YAAY;IAChB,gBAAgB;GAClB,IACC,UAAmB,gBAAgB,KAAK,CAC3C;EACF,CAAC;CACH;CAEA,kBACE,QACA,UACM;EACN,MAAM,aAAa,OAAO,SAAS,UAAU;GAC3C,IAAI;IAAC;IAAO;IAAQ;GAAS,CAAC,CAAC,SAAS,MAAM,MAAM,GAClD,OAAO,CAAC;GAEV,MAAM,QAAQ,MAAM,MAAM,QAAQ,SAChC,SAAS,MAAM,YACb,qBAAqB,MAAM,QAAQ,QAAQ,CAC7C,CACF;GACA,OAAO,MAAM,SAAS,CAAC;IAAE,GAAG;IAAO;GAAM,CAAC,IAAI,CAAC;EACjD,CAAC;EACD,MAAM,cAAc,WAAW,MAAM,UAAU,MAAM,WAAW,SAAS,CAAC;EAC1E,IAAI,aACF,MAAM,IAAIP,oBAAAA,6BACR,4BACA,GAAG,YAAY,OAAO,OAAO,YAAY,MAAM,KAAK,IAAI,EAAE,SAAS,YAAY,WAAW,KAAK,OAAO,EAAE,gCACxG,sGACF;EAEF,IAAI,WAAW,WAAW,GAAG;GAC3B,KAAK,qBAAqB,CAAC;GAC3B;EACF;EACA,MAAM,EAAE,eAAe,WAAW,cAAc,KAAK,YAAY;EACjE,KAAK,qBAAqB,OAAO,OAC/B,WAAW,SAAS,UAClB,MAAM,MAAM,KAAK,UAAU;GACzB,QAAQ,MAAM;GACd,SAAA,GAAQQ,eAAAA,aAAAA,CAAa,MAAM;IACzB,KAAK;IACL,WAAW;IACX,UAAU,CAAC;GACb,CAAC,CAAC,CAAC;EACL,EAAE,CACJ,CACF;CACF;CAEA,MAAM,KAAiC;EACrC,YAAY,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,SAAS;GAC/C,IAAI,CAAC,IAAI,QAAQ,QAAQ,QAAQ,GAAG,CAAC,GAAG;IACtC,KAAK;IACL;GACF;GACA,MAAM,aAAa,kBAAkB,KAAK,GAAG;GAC7C,KAAU,SAAS,IAAI,SAAS,KAAK,KAAK,WAAW,MAAM,CAAC,CAAC,WACrD,WAAW,QAAQ,IACxB,UAAmB;IAClB,WAAW,QAAQ;IACnB,IAAI,CAAC,WAAW,OAAO,SACrB,KAAK,KAAK;GAEd,CACF;EACF,CAAC;CACH;CAEA,WAAW,SAA0C;EACnD,OAAO,aAAa,YAAY,OAAO,CAAC,CAAC,IAAI,aAAa,CAAC;CAC7D;CAEA,SAAiB,KAAoC;EACnD,IAAI,KAAK,QAAQ,UACf,OAAO,KAAK,QAAQ,SAAS,GAAG;EAElC,OAAO,IAAI,MAAM;CACnB;CAEA,mBAA2B,QAAgB,UAA2B;EACpE,OAAO,KAAK,mBAAmB,MAC5B,WACE,MAAM,WAAW,SAAS,MAAM,WAAW,WAC5C,MAAM,OAAO,KAAK,QAAQ,CAC9B;CACF;CAEA,MAAc,SACZ,SACA,KACA,KACA,QACe;EACf,MAAM,WAAW,IAAI;EACrB,IACG,YAAY,CAAC,SAAS,MAAM,SAAS,WAAW,aACjD,OAAO,SAEP;EAEF,MAAM,WACJ,YAAY,CAAC,SAAS,MAAM,SAAS,WAAW,cAC5C,QAAQ,gBAAgB,IACxB,MAAM,QAAQ,OAAO;GACnB,QAAQ,IAAI,UAAU;GACtB,KAAK,WAAW,GAAG;GACnB,SAASN,iBAAAA,aAAa,IAAI,OAAO;GACjC,MAAM,UAAU,KAAK,SAAS,QAAQ;GACtC,UAAU,KAAK,SAAS,GAAG;GAC3B,iBAAiB;GACjB;EACF,CAAC;EACP,IAAI,OAAO,SAAS;GAClB,MAAM,SAAS,MAAM,OAAO,CAAC,CAAC,YAAY,KAAA,CAAS;GACnD;EACF;EACA,MAAMO,iBAAAA,iBAAiB,KAAK,UAAU,EACpC,MAAM,IAAI,QAAQ,YAAY,MAAM,OACtC,CAAC;CACH;AACF;AAEA,SAAgB,gBACd,SACiB;CACjB,OAAO,IAAI,gBAAgB,OAAO;AACpC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { A as ExpressPlatformOptions, G as PlatformPrepareContext, W as PlatformMountContext, n as ApplicationRouteDescriptor, s as AuthRouteBinding, tt as ProxyTrust, z as HttpPlatform } from "./auth-contracts-BBA1C1gj.cjs";
|
|
2
|
+
import { AbstractHttpAdapter } from "@nestjs/core";
|
|
3
|
+
//#region src/express-platform.d.ts
|
|
4
|
+
/** Express-specific two-phase HTTP integration. */
|
|
5
|
+
export declare class ExpressPlatform implements HttpPlatform {
|
|
6
|
+
#private;
|
|
7
|
+
private readonly options;
|
|
8
|
+
readonly id = "express";
|
|
9
|
+
constructor(options?: ExpressPlatformOptions);
|
|
10
|
+
readonly requests: {
|
|
11
|
+
isRequest: (value: unknown) => boolean;
|
|
12
|
+
isLive: (value: unknown) => boolean;
|
|
13
|
+
key: (value: unknown) => object;
|
|
14
|
+
headers: (value: unknown) => Headers;
|
|
15
|
+
request: (value: unknown) => {
|
|
16
|
+
method: string;
|
|
17
|
+
url: string;
|
|
18
|
+
};
|
|
19
|
+
clientIp: (value: unknown) => string | null;
|
|
20
|
+
param: (value: unknown, name: string) => string | undefined;
|
|
21
|
+
cookieSink: (request: unknown, response: unknown) => {
|
|
22
|
+
append: (cookies: readonly string[]) => boolean;
|
|
23
|
+
} | null;
|
|
24
|
+
};
|
|
25
|
+
supports(adapter: AbstractHttpAdapter): boolean;
|
|
26
|
+
prepare(ctx: PlatformPrepareContext): void;
|
|
27
|
+
applicationRoutes(routes: readonly ApplicationRouteDescriptor[], bindings: readonly AuthRouteBinding[]): void;
|
|
28
|
+
mount(ctx: PlatformMountContext): void;
|
|
29
|
+
proxyTrust(adapter: AbstractHttpAdapter): ProxyTrust;
|
|
30
|
+
private clientIp;
|
|
31
|
+
private isApplicationRoute;
|
|
32
|
+
private dispatch;
|
|
33
|
+
}
|
|
34
|
+
export declare function expressPlatform(options?: ExpressPlatformOptions): ExpressPlatform;
|
|
35
|
+
//#endregion
|
|
36
|
+
export type { ExpressPlatformOptions };
|
|
37
|
+
//# sourceMappingURL=express.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.d.cts","names":[],"sources":["../src/express-platform.ts"],"mappings":";;;;qBA+Ma,2BAA2B;;mBAMT;WALpB;EAKT,YAA6B,UAAS;WAE7B;IACP,YAAS;IACT,SAAM;IAEN,MAAG;IACH,UAAO,mBAAoB;IAE3B,UAAO;MAEI;MAA6B;;IAExC,WAAQ;IAER,QAAK,gBAAiB;IAItB,aAAU,kBAAmB;MAMH,SAAA;;;EAM5B,SAAS,SAAS;EAIlB,QAAQ,KAAK;EAkDb,kBACE,iBAAiB,8BACjB,mBAAmB;EAwCrB,MAAM,KAAK;EAmBX,WAAW,SAAS,sBAAsB;UAIlC;UAOA;UAQM;;wBAmCA,gBACd,UAAU,yBACT"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { A as ExpressPlatformOptions, G as PlatformPrepareContext, W as PlatformMountContext, n as ApplicationRouteDescriptor, s as AuthRouteBinding, tt as ProxyTrust, z as HttpPlatform } from "./auth-contracts-tqsfBZ8B.mjs";
|
|
2
|
+
import { AbstractHttpAdapter } from "@nestjs/core";
|
|
3
|
+
//#region src/express-platform.d.ts
|
|
4
|
+
/** Express-specific two-phase HTTP integration. */
|
|
5
|
+
export declare class ExpressPlatform implements HttpPlatform {
|
|
6
|
+
#private;
|
|
7
|
+
private readonly options;
|
|
8
|
+
readonly id = "express";
|
|
9
|
+
constructor(options?: ExpressPlatformOptions);
|
|
10
|
+
readonly requests: {
|
|
11
|
+
isRequest: (value: unknown) => boolean;
|
|
12
|
+
isLive: (value: unknown) => boolean;
|
|
13
|
+
key: (value: unknown) => object;
|
|
14
|
+
headers: (value: unknown) => Headers;
|
|
15
|
+
request: (value: unknown) => {
|
|
16
|
+
method: string;
|
|
17
|
+
url: string;
|
|
18
|
+
};
|
|
19
|
+
clientIp: (value: unknown) => string | null;
|
|
20
|
+
param: (value: unknown, name: string) => string | undefined;
|
|
21
|
+
cookieSink: (request: unknown, response: unknown) => {
|
|
22
|
+
append: (cookies: readonly string[]) => boolean;
|
|
23
|
+
} | null;
|
|
24
|
+
};
|
|
25
|
+
supports(adapter: AbstractHttpAdapter): boolean;
|
|
26
|
+
prepare(ctx: PlatformPrepareContext): void;
|
|
27
|
+
applicationRoutes(routes: readonly ApplicationRouteDescriptor[], bindings: readonly AuthRouteBinding[]): void;
|
|
28
|
+
mount(ctx: PlatformMountContext): void;
|
|
29
|
+
proxyTrust(adapter: AbstractHttpAdapter): ProxyTrust;
|
|
30
|
+
private clientIp;
|
|
31
|
+
private isApplicationRoute;
|
|
32
|
+
private dispatch;
|
|
33
|
+
}
|
|
34
|
+
export declare function expressPlatform(options?: ExpressPlatformOptions): ExpressPlatform;
|
|
35
|
+
//#endregion
|
|
36
|
+
export type { ExpressPlatformOptions };
|
|
37
|
+
//# sourceMappingURL=express.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.d.mts","names":[],"sources":["../src/express-platform.ts"],"mappings":";;;;qBA+Ma,2BAA2B;;mBAMT;WALpB;EAKT,YAA6B,UAAS;WAE7B;IACP,YAAS;IACT,SAAM;IAEN,MAAG;IACH,UAAO,mBAAoB;IAE3B,UAAO;MAEI;MAA6B;;IAExC,WAAQ;IAER,QAAK,gBAAiB;IAItB,aAAU,kBAAmB;MAMH,SAAA;;;EAM5B,SAAS,SAAS;EAIlB,QAAQ,KAAK;EAkDb,kBACE,iBAAiB,8BACjB,mBAAmB;EAwCrB,MAAM,KAAK;EAmBX,WAAW,SAAS,sBAAsB;UAIlC;UAOA;UAQM;;wBAmCA,gBACd,UAAU,yBACT"}
|
package/dist/express.mjs
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { n as BetterAuthConfigurationError } from "./auth-errors-CQjfgjaO.mjs";
|
|
2
|
+
import { appendSetCookie, bindAsyncContext, mayHaveBody, readBoundedBody, recoverConsumedBody, toWebHeaders, writeWebResponse } from "./platform.mjs";
|
|
3
|
+
import { parse, pathToRegexp } from "path-to-regexp";
|
|
4
|
+
//#region src/express-platform.ts
|
|
5
|
+
const RAW_BODY = Symbol.for("nestjs-slightly-better-auth:raw-body");
|
|
6
|
+
function application(adapter) {
|
|
7
|
+
return adapter.getInstance();
|
|
8
|
+
}
|
|
9
|
+
function routerOptions(app) {
|
|
10
|
+
const router = app?.router;
|
|
11
|
+
if (typeof router !== "object" && typeof router !== "function" || router === null) throw new BetterAuthConfigurationError("UNSUPPORTED_EXPRESS_ROUTER", "The Express application does not expose its initialized router.", "Use Express 5.2.x with @nestjs/platform-express 12.x, or move controller routes outside the auth mount.");
|
|
12
|
+
const options = router;
|
|
13
|
+
if (typeof options.caseSensitive !== "boolean" || typeof options.strict !== "boolean") throw new BetterAuthConfigurationError("UNSUPPORTED_EXPRESS_ROUTER", "The initialized Express router does not expose compatible routing options.", "Use Express 5.2.x with @nestjs/platform-express 12.x, or move controller routes outside the auth mount.");
|
|
14
|
+
return {
|
|
15
|
+
caseSensitive: options.caseSensitive,
|
|
16
|
+
strict: options.strict
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function routeMayOverlapMount(path, basePath) {
|
|
20
|
+
const tokens = parse(path).tokens;
|
|
21
|
+
let literalPrefix = "";
|
|
22
|
+
let dynamic = false;
|
|
23
|
+
for (const token of tokens) {
|
|
24
|
+
if (token.type !== "text") {
|
|
25
|
+
dynamic = true;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
literalPrefix += token.value;
|
|
29
|
+
}
|
|
30
|
+
const route = literalPrefix.toLowerCase();
|
|
31
|
+
const base = basePath.toLowerCase().replace(/(?<!\/)\/+$/, "") || "/";
|
|
32
|
+
if (base === "/") return route.startsWith("/");
|
|
33
|
+
if (!dynamic) {
|
|
34
|
+
const exact = route.replace(/(?<!\/)\/+$/, "") || "/";
|
|
35
|
+
return exact === base || exact.startsWith(`${base}/`);
|
|
36
|
+
}
|
|
37
|
+
if (base.startsWith(route)) return true;
|
|
38
|
+
return route === base || route.startsWith(`${base}/`);
|
|
39
|
+
}
|
|
40
|
+
function rawTarget(req) {
|
|
41
|
+
return req.originalUrl ?? req.url ?? "/";
|
|
42
|
+
}
|
|
43
|
+
function rawPath(req) {
|
|
44
|
+
const target = rawTarget(req);
|
|
45
|
+
const query = target.indexOf("?");
|
|
46
|
+
return query === -1 ? target : target.slice(0, query);
|
|
47
|
+
}
|
|
48
|
+
function declaredLength(headers, limit) {
|
|
49
|
+
const value = headers["content-length"];
|
|
50
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
51
|
+
if (typeof first !== "string" || !/^\d+$/.test(first)) return;
|
|
52
|
+
const parsed = Number(first);
|
|
53
|
+
return Number.isSafeInteger(parsed) ? parsed : limit + 1;
|
|
54
|
+
}
|
|
55
|
+
function requestUrl(req) {
|
|
56
|
+
const socket = req.socket;
|
|
57
|
+
return `${req.protocol ?? (socket.encrypted ? "https" : "http")}://${req.host ?? ""}${rawTarget(req)}`;
|
|
58
|
+
}
|
|
59
|
+
function isExpressRequest(value) {
|
|
60
|
+
if (typeof value !== "object" || value === null) return false;
|
|
61
|
+
const req = value;
|
|
62
|
+
return typeof req.method === "string" && typeof req.headers === "object" && typeof req.res === "object" && req.res !== null && (req.res.req === void 0 || req.res.req === value);
|
|
63
|
+
}
|
|
64
|
+
function nativeResponse(value) {
|
|
65
|
+
if (typeof value !== "object" || value === null) return null;
|
|
66
|
+
const response = value;
|
|
67
|
+
return typeof response.setHeader === "function" && typeof response.getHeader === "function" ? value : null;
|
|
68
|
+
}
|
|
69
|
+
function proxyTrustOf(value) {
|
|
70
|
+
if (value === false || value === void 0) return {
|
|
71
|
+
mode: "none",
|
|
72
|
+
detail: "trust proxy = false"
|
|
73
|
+
};
|
|
74
|
+
if (value === true) return {
|
|
75
|
+
mode: "all",
|
|
76
|
+
detail: "trust proxy = true"
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
mode: "partial",
|
|
80
|
+
detail: `trust proxy = ${typeof value === "function" ? "<function>" : Array.isArray(value) ? JSON.stringify(value) : String(value)}`
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function abortOnDisconnect(req, res) {
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
const abort = () => controller.abort();
|
|
86
|
+
req.once("aborted", abort);
|
|
87
|
+
res.once("close", abort);
|
|
88
|
+
if (req.aborted || res.destroyed) abort();
|
|
89
|
+
return {
|
|
90
|
+
signal: controller.signal,
|
|
91
|
+
dispose: () => {
|
|
92
|
+
req.removeListener("aborted", abort);
|
|
93
|
+
res.removeListener("close", abort);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Express-specific two-phase HTTP integration. */
|
|
98
|
+
var ExpressPlatform = class {
|
|
99
|
+
options;
|
|
100
|
+
id = "express";
|
|
101
|
+
#warnedConsumedBody = false;
|
|
102
|
+
#applicationRoutes = [];
|
|
103
|
+
#application;
|
|
104
|
+
constructor(options = {}) {
|
|
105
|
+
this.options = options;
|
|
106
|
+
}
|
|
107
|
+
requests = {
|
|
108
|
+
isRequest: (value) => isExpressRequest(value),
|
|
109
|
+
isLive: (value) => isExpressRequest(value) && value.res?.writableEnded === false,
|
|
110
|
+
key: (value) => value,
|
|
111
|
+
headers: (value) => toWebHeaders(value.headers),
|
|
112
|
+
request: (value) => {
|
|
113
|
+
const req = value;
|
|
114
|
+
return {
|
|
115
|
+
method: req.method ?? "GET",
|
|
116
|
+
url: requestUrl(req)
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
clientIp: (value) => this.clientIp(value),
|
|
120
|
+
param: (value, name) => {
|
|
121
|
+
const parameter = value.params?.[name];
|
|
122
|
+
return typeof parameter === "string" ? parameter : void 0;
|
|
123
|
+
},
|
|
124
|
+
cookieSink: (request, response) => {
|
|
125
|
+
const req = request;
|
|
126
|
+
const res = nativeResponse(response) ?? nativeResponse(req.res);
|
|
127
|
+
return res === null ? null : { append: (cookies) => appendSetCookie(res, cookies) };
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
supports(adapter) {
|
|
131
|
+
return adapter.getType?.() === "express";
|
|
132
|
+
}
|
|
133
|
+
prepare(ctx) {
|
|
134
|
+
const app = application(ctx.adapter);
|
|
135
|
+
this.#application = app;
|
|
136
|
+
app.use((req, _res, next) => {
|
|
137
|
+
const binding = ctx.route(rawPath(req));
|
|
138
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
139
|
+
if (!binding || this.isApplicationRoute(method, rawPath(req)) || method === "GET" || method === "HEAD" || method === "OPTIONS" || !mayHaveBody(method, req.headers)) {
|
|
140
|
+
next();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const continueRequest = bindAsyncContext(next);
|
|
144
|
+
if (req.readableEnded || req.readable === false) {
|
|
145
|
+
const recovered = recoverConsumedBody(req);
|
|
146
|
+
req[RAW_BODY] = {
|
|
147
|
+
ok: true,
|
|
148
|
+
bytes: recovered ?? /* @__PURE__ */ new Uint8Array()
|
|
149
|
+
};
|
|
150
|
+
if (!this.#warnedConsumedBody) {
|
|
151
|
+
this.#warnedConsumedBody = true;
|
|
152
|
+
ctx.logger.warn("W_BODY_ALREADY_CONSUMED: an Express body parser ran before the auth capture; raw-signature webhooks may receive re-serialized bytes.");
|
|
153
|
+
}
|
|
154
|
+
continueRequest();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
readBoundedBody(req, {
|
|
158
|
+
limit: binding.bodyLimit,
|
|
159
|
+
declaredLength: declaredLength(req.headers, binding.bodyLimit),
|
|
160
|
+
drainOnOverflow: true,
|
|
161
|
+
drainCap: Math.min(Number.MAX_SAFE_INTEGER, binding.bodyLimit * 8)
|
|
162
|
+
}).then((captured) => {
|
|
163
|
+
req[RAW_BODY] = captured;
|
|
164
|
+
continueRequest();
|
|
165
|
+
}, (error) => continueRequest(error));
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
applicationRoutes(routes, bindings) {
|
|
169
|
+
const bodyRoutes = routes.flatMap((route) => {
|
|
170
|
+
if ([
|
|
171
|
+
"GET",
|
|
172
|
+
"HEAD",
|
|
173
|
+
"OPTIONS"
|
|
174
|
+
].includes(route.method)) return [];
|
|
175
|
+
const paths = route.paths.filter((path) => bindings.some((binding) => routeMayOverlapMount(path, binding.basePath)));
|
|
176
|
+
return paths.length ? [{
|
|
177
|
+
...route,
|
|
178
|
+
paths
|
|
179
|
+
}] : [];
|
|
180
|
+
});
|
|
181
|
+
const conditional = bodyRoutes.find((route) => route.conditions.length > 0);
|
|
182
|
+
if (conditional) throw new BetterAuthConfigurationError("CONDITIONAL_ROUTE_SHADOW", `${conditional.source} at '${conditional.paths.join(", ")}' uses ${conditional.conditions.join(" and ")} routing under an auth mount.`, "Move the controller route outside the auth mount or make its host and version routing unconditional.");
|
|
183
|
+
if (bodyRoutes.length === 0) {
|
|
184
|
+
this.#applicationRoutes = [];
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const { caseSensitive, strict } = routerOptions(this.#application);
|
|
188
|
+
this.#applicationRoutes = Object.freeze(bodyRoutes.flatMap((route) => route.paths.map((path) => ({
|
|
189
|
+
method: route.method,
|
|
190
|
+
regexp: pathToRegexp(path, {
|
|
191
|
+
end: true,
|
|
192
|
+
sensitive: caseSensitive,
|
|
193
|
+
trailing: !strict
|
|
194
|
+
}).regexp
|
|
195
|
+
}))));
|
|
196
|
+
}
|
|
197
|
+
mount(ctx) {
|
|
198
|
+
application(ctx.adapter).use((req, res, next) => {
|
|
199
|
+
if (!ctx.binding.matches(rawPath(req))) {
|
|
200
|
+
next();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const disconnect = abortOnDisconnect(req, res);
|
|
204
|
+
this.dispatch(ctx.binding, req, res, disconnect.signal).then(() => disconnect.dispose(), (error) => {
|
|
205
|
+
disconnect.dispose();
|
|
206
|
+
if (!disconnect.signal.aborted) next(error);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
proxyTrust(adapter) {
|
|
211
|
+
return proxyTrustOf(application(adapter).get("trust proxy"));
|
|
212
|
+
}
|
|
213
|
+
clientIp(req) {
|
|
214
|
+
if (this.options.clientIp) return this.options.clientIp(req);
|
|
215
|
+
return req.ip ?? null;
|
|
216
|
+
}
|
|
217
|
+
isApplicationRoute(method, pathname) {
|
|
218
|
+
return this.#applicationRoutes.some((route) => (route.method === "ALL" || route.method === method) && route.regexp.test(pathname));
|
|
219
|
+
}
|
|
220
|
+
async dispatch(binding, req, res, signal) {
|
|
221
|
+
const captured = req[RAW_BODY];
|
|
222
|
+
if (captured && !captured.ok && captured.reason === "aborted" || signal.aborted) return;
|
|
223
|
+
const response = captured && !captured.ok && captured.reason === "too-large" ? binding.payloadTooLarge() : await binding.handle({
|
|
224
|
+
method: req.method ?? "GET",
|
|
225
|
+
url: requestUrl(req),
|
|
226
|
+
headers: toWebHeaders(req.headers),
|
|
227
|
+
body: captured?.ok ? captured.bytes : null,
|
|
228
|
+
clientIp: this.clientIp(req),
|
|
229
|
+
platformRequest: req,
|
|
230
|
+
signal
|
|
231
|
+
});
|
|
232
|
+
if (signal.aborted) {
|
|
233
|
+
await response.body?.cancel().catch(() => void 0);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
await writeWebResponse(res, response, { head: req.method?.toUpperCase() === "HEAD" });
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
function expressPlatform(options) {
|
|
240
|
+
return new ExpressPlatform(options);
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
243
|
+
export { ExpressPlatform, expressPlatform };
|
|
244
|
+
|
|
245
|
+
//# sourceMappingURL=express.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.mjs","names":[],"sources":["../src/express-platform.ts"],"sourcesContent":["import type {\n IncomingHttpHeaders,\n IncomingMessage,\n ServerResponse,\n} from \"node:http\";\nimport type { AbstractHttpAdapter } from \"@nestjs/core\";\nimport { parse, pathToRegexp } from \"path-to-regexp\";\nimport type {\n ApplicationRouteDescriptor,\n AuthRouteBinding,\n ExpressPlatformOptions,\n HttpPlatform,\n PlatformMountContext,\n PlatformPrepareContext,\n ProxyTrust,\n} from \"./auth-contracts.js\";\nimport { BetterAuthConfigurationError } from \"./auth-errors.js\";\nimport {\n appendSetCookie,\n bindAsyncContext,\n mayHaveBody,\n readBoundedBody,\n recoverConsumedBody,\n toWebHeaders,\n writeWebResponse,\n} from \"./platform.js\";\n\nconst RAW_BODY = Symbol.for(\"nestjs-slightly-better-auth:raw-body\");\n\ntype Next = (error?: unknown) => void;\ntype CapturedBody = Awaited<ReturnType<typeof readBoundedBody>>;\ntype ExpressResponse = ServerResponse & { req?: ExpressRequest };\ntype ExpressRequest = IncomingMessage & {\n readonly originalUrl?: string;\n readonly protocol?: string;\n readonly host?: string;\n readonly ip?: string;\n readonly res?: ExpressResponse;\n readonly params?: Record<string, unknown>;\n readonly body?: unknown;\n readonly rawBody?: Buffer;\n [RAW_BODY]?: CapturedBody;\n};\ntype ExpressApplication = {\n use(\n handler: (req: ExpressRequest, res: ExpressResponse, next: Next) => void,\n ): unknown;\n get(setting: string): unknown;\n readonly router?: unknown;\n};\ntype ExpressRouterOptions = {\n readonly caseSensitive: boolean;\n readonly strict: boolean;\n};\ntype ApplicationRouteMatcher = {\n readonly method: string;\n readonly regexp: RegExp;\n};\n\nfunction application(adapter: AbstractHttpAdapter): ExpressApplication {\n return adapter.getInstance() as ExpressApplication;\n}\n\nfunction routerOptions(\n app: ExpressApplication | undefined,\n): ExpressRouterOptions {\n const router = app?.router;\n if (\n (typeof router !== \"object\" && typeof router !== \"function\") ||\n router === null\n ) {\n throw new BetterAuthConfigurationError(\n \"UNSUPPORTED_EXPRESS_ROUTER\",\n \"The Express application does not expose its initialized router.\",\n \"Use Express 5.2.x with @nestjs/platform-express 12.x, or move controller routes outside the auth mount.\",\n );\n }\n const options = router as Partial<ExpressRouterOptions>;\n if (\n typeof options.caseSensitive !== \"boolean\" ||\n typeof options.strict !== \"boolean\"\n ) {\n throw new BetterAuthConfigurationError(\n \"UNSUPPORTED_EXPRESS_ROUTER\",\n \"The initialized Express router does not expose compatible routing options.\",\n \"Use Express 5.2.x with @nestjs/platform-express 12.x, or move controller routes outside the auth mount.\",\n );\n }\n return { caseSensitive: options.caseSensitive, strict: options.strict };\n}\n\nfunction routeMayOverlapMount(path: string, basePath: string): boolean {\n const tokens = parse(path).tokens;\n let literalPrefix = \"\";\n let dynamic = false;\n for (const token of tokens) {\n if (token.type !== \"text\") {\n dynamic = true;\n break;\n }\n literalPrefix += token.value;\n }\n const route = literalPrefix.toLowerCase();\n const base = basePath.toLowerCase().replace(/(?<!\\/)\\/+$/, \"\") || \"/\";\n if (base === \"/\") {\n return route.startsWith(\"/\");\n }\n if (!dynamic) {\n const exact = route.replace(/(?<!\\/)\\/+$/, \"\") || \"/\";\n return exact === base || exact.startsWith(`${base}/`);\n }\n if (base.startsWith(route)) {\n return true;\n }\n return route === base || route.startsWith(`${base}/`);\n}\n\nfunction rawTarget(req: ExpressRequest): string {\n return req.originalUrl ?? req.url ?? \"/\";\n}\n\nfunction rawPath(req: ExpressRequest): string {\n const target = rawTarget(req);\n const query = target.indexOf(\"?\");\n return query === -1 ? target : target.slice(0, query);\n}\n\nfunction declaredLength(\n headers: IncomingHttpHeaders,\n limit: number,\n): number | undefined {\n const value = headers[\"content-length\"];\n const first = Array.isArray(value) ? value[0] : value;\n if (typeof first !== \"string\" || !/^\\d+$/.test(first)) {\n return undefined;\n }\n const parsed = Number(first);\n return Number.isSafeInteger(parsed) ? parsed : limit + 1;\n}\n\nfunction requestUrl(req: ExpressRequest): string {\n const socket = req.socket as typeof req.socket & { encrypted?: boolean };\n return `${req.protocol ?? (socket.encrypted ? \"https\" : \"http\")}://${req.host ?? \"\"}${rawTarget(req)}`;\n}\n\nfunction isExpressRequest(value: unknown): value is ExpressRequest {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const req = value as Partial<ExpressRequest>;\n return (\n typeof req.method === \"string\" &&\n typeof req.headers === \"object\" &&\n typeof req.res === \"object\" &&\n req.res !== null &&\n (req.res.req === undefined || req.res.req === value)\n );\n}\n\nfunction nativeResponse(value: unknown): ServerResponse | null {\n if (typeof value !== \"object\" || value === null) {\n return null;\n }\n const response = value as Partial<ServerResponse>;\n return typeof response.setHeader === \"function\" &&\n typeof response.getHeader === \"function\"\n ? (value as ServerResponse)\n : null;\n}\n\nfunction proxyTrustOf(value: unknown): ProxyTrust {\n if (value === false || value === undefined) {\n return { mode: \"none\", detail: \"trust proxy = false\" };\n }\n if (value === true) {\n return { mode: \"all\", detail: \"trust proxy = true\" };\n }\n const rendered =\n typeof value === \"function\"\n ? \"<function>\"\n : Array.isArray(value)\n ? JSON.stringify(value)\n : String(value);\n return { mode: \"partial\", detail: `trust proxy = ${rendered}` };\n}\n\nfunction abortOnDisconnect(\n req: ExpressRequest,\n res: ExpressResponse,\n): { signal: AbortSignal; dispose(): void } {\n const controller = new AbortController();\n const abort = () => controller.abort();\n req.once(\"aborted\", abort);\n res.once(\"close\", abort);\n if (req.aborted || res.destroyed) {\n abort();\n }\n return {\n signal: controller.signal,\n dispose: () => {\n req.removeListener(\"aborted\", abort);\n res.removeListener(\"close\", abort);\n },\n };\n}\n\n/** Express-specific two-phase HTTP integration. */\nexport class ExpressPlatform implements HttpPlatform {\n readonly id = \"express\";\n #warnedConsumedBody = false;\n #applicationRoutes: readonly ApplicationRouteMatcher[] = [];\n #application: ExpressApplication | undefined;\n\n constructor(private readonly options: ExpressPlatformOptions = {}) {}\n\n readonly requests = {\n isRequest: (value: unknown): boolean => isExpressRequest(value),\n isLive: (value: unknown): boolean =>\n isExpressRequest(value) && value.res?.writableEnded === false,\n key: (value: unknown): object => value as object,\n headers: (value: unknown): Headers =>\n toWebHeaders((value as ExpressRequest).headers),\n request: (value: unknown) => {\n const req = value as ExpressRequest;\n return { method: req.method ?? \"GET\", url: requestUrl(req) };\n },\n clientIp: (value: unknown): string | null =>\n this.clientIp(value as ExpressRequest),\n param: (value: unknown, name: string): string | undefined => {\n const parameter = (value as ExpressRequest).params?.[name];\n return typeof parameter === \"string\" ? parameter : undefined;\n },\n cookieSink: (request: unknown, response: unknown) => {\n const req = request as ExpressRequest;\n const res = nativeResponse(response) ?? nativeResponse(req.res);\n return res === null\n ? null\n : {\n append: (cookies: readonly string[]) =>\n appendSetCookie(res, cookies),\n };\n },\n };\n\n supports(adapter: AbstractHttpAdapter): boolean {\n return adapter.getType?.() === \"express\";\n }\n\n prepare(ctx: PlatformPrepareContext): void {\n const app = application(ctx.adapter);\n this.#application = app;\n app.use((req, _res, next) => {\n const binding = ctx.route(rawPath(req));\n const method = (req.method ?? \"GET\").toUpperCase();\n if (\n !binding ||\n this.isApplicationRoute(method, rawPath(req)) ||\n method === \"GET\" ||\n method === \"HEAD\" ||\n method === \"OPTIONS\" ||\n !mayHaveBody(method, req.headers)\n ) {\n next();\n return;\n }\n\n const continueRequest = bindAsyncContext(next);\n if (req.readableEnded || req.readable === false) {\n const recovered = recoverConsumedBody(req);\n req[RAW_BODY] = {\n ok: true,\n bytes: recovered ?? new Uint8Array(),\n };\n if (!this.#warnedConsumedBody) {\n this.#warnedConsumedBody = true;\n ctx.logger.warn(\n \"W_BODY_ALREADY_CONSUMED: an Express body parser ran before the auth capture; raw-signature webhooks may receive re-serialized bytes.\",\n );\n }\n continueRequest();\n return;\n }\n\n void readBoundedBody(req, {\n limit: binding.bodyLimit,\n declaredLength: declaredLength(req.headers, binding.bodyLimit),\n drainOnOverflow: true,\n drainCap: Math.min(Number.MAX_SAFE_INTEGER, binding.bodyLimit * 8),\n }).then(\n (captured) => {\n req[RAW_BODY] = captured;\n continueRequest();\n },\n (error: unknown) => continueRequest(error),\n );\n });\n }\n\n applicationRoutes(\n routes: readonly ApplicationRouteDescriptor[],\n bindings: readonly AuthRouteBinding[],\n ): void {\n const bodyRoutes = routes.flatMap((route) => {\n if ([\"GET\", \"HEAD\", \"OPTIONS\"].includes(route.method)) {\n return [];\n }\n const paths = route.paths.filter((path) =>\n bindings.some((binding) =>\n routeMayOverlapMount(path, binding.basePath),\n ),\n );\n return paths.length ? [{ ...route, paths }] : [];\n });\n const conditional = bodyRoutes.find((route) => route.conditions.length > 0);\n if (conditional) {\n throw new BetterAuthConfigurationError(\n \"CONDITIONAL_ROUTE_SHADOW\",\n `${conditional.source} at '${conditional.paths.join(\", \")}' uses ${conditional.conditions.join(\" and \")} routing under an auth mount.`,\n \"Move the controller route outside the auth mount or make its host and version routing unconditional.\",\n );\n }\n if (bodyRoutes.length === 0) {\n this.#applicationRoutes = [];\n return;\n }\n const { caseSensitive, strict } = routerOptions(this.#application);\n this.#applicationRoutes = Object.freeze(\n bodyRoutes.flatMap((route) =>\n route.paths.map((path) => ({\n method: route.method,\n regexp: pathToRegexp(path, {\n end: true,\n sensitive: caseSensitive,\n trailing: !strict,\n }).regexp,\n })),\n ),\n );\n }\n\n mount(ctx: PlatformMountContext): void {\n application(ctx.adapter).use((req, res, next) => {\n if (!ctx.binding.matches(rawPath(req))) {\n next();\n return;\n }\n const disconnect = abortOnDisconnect(req, res);\n void this.dispatch(ctx.binding, req, res, disconnect.signal).then(\n () => disconnect.dispose(),\n (error: unknown) => {\n disconnect.dispose();\n if (!disconnect.signal.aborted) {\n next(error);\n }\n },\n );\n });\n }\n\n proxyTrust(adapter: AbstractHttpAdapter): ProxyTrust {\n return proxyTrustOf(application(adapter).get(\"trust proxy\"));\n }\n\n private clientIp(req: ExpressRequest): string | null {\n if (this.options.clientIp) {\n return this.options.clientIp(req);\n }\n return req.ip ?? null;\n }\n\n private isApplicationRoute(method: string, pathname: string): boolean {\n return this.#applicationRoutes.some(\n (route) =>\n (route.method === \"ALL\" || route.method === method) &&\n route.regexp.test(pathname),\n );\n }\n\n private async dispatch(\n binding: AuthRouteBinding,\n req: ExpressRequest,\n res: ExpressResponse,\n signal: AbortSignal,\n ): Promise<void> {\n const captured = req[RAW_BODY];\n if (\n (captured && !captured.ok && captured.reason === \"aborted\") ||\n signal.aborted\n ) {\n return;\n }\n const response =\n captured && !captured.ok && captured.reason === \"too-large\"\n ? binding.payloadTooLarge()\n : await binding.handle({\n method: req.method ?? \"GET\",\n url: requestUrl(req),\n headers: toWebHeaders(req.headers),\n body: captured?.ok ? captured.bytes : null,\n clientIp: this.clientIp(req),\n platformRequest: req,\n signal,\n });\n if (signal.aborted) {\n await response.body?.cancel().catch(() => undefined);\n return;\n }\n await writeWebResponse(res, response, {\n head: req.method?.toUpperCase() === \"HEAD\",\n });\n }\n}\n\nexport function expressPlatform(\n options?: ExpressPlatformOptions,\n): ExpressPlatform {\n return new ExpressPlatform(options);\n}\n"],"mappings":"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"}
|
package/dist/fastify.cjs
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_platform = require("./platform.cjs");
|
|
3
|
+
//#region src/fastify-platform.ts
|
|
4
|
+
function instanceOf(adapter) {
|
|
5
|
+
return adapter.getInstance();
|
|
6
|
+
}
|
|
7
|
+
function asRequest(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null) return;
|
|
9
|
+
const request = value;
|
|
10
|
+
return typeof request.raw === "object" && request.raw !== null ? request : void 0;
|
|
11
|
+
}
|
|
12
|
+
function varyParts(value) {
|
|
13
|
+
return (Array.isArray(value) ? value : value === void 0 ? [] : [value]).flatMap((item) => String(item).split(","));
|
|
14
|
+
}
|
|
15
|
+
function mergedVary(current, added) {
|
|
16
|
+
const values = [...varyParts(current), ...varyParts(added)].map((item) => item.trim()).filter(Boolean);
|
|
17
|
+
if (values.includes("*")) return "*";
|
|
18
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19
|
+
return values.filter((item) => {
|
|
20
|
+
const key = item.toLowerCase();
|
|
21
|
+
if (seen.has(key)) return false;
|
|
22
|
+
seen.add(key);
|
|
23
|
+
return true;
|
|
24
|
+
}).join(", ");
|
|
25
|
+
}
|
|
26
|
+
function abortOnDisconnect(request, response) {
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
let disposed = false;
|
|
29
|
+
const abort = () => {
|
|
30
|
+
if (!controller.signal.aborted) controller.abort();
|
|
31
|
+
};
|
|
32
|
+
const onRequestAborted = () => abort();
|
|
33
|
+
const onRequestError = () => abort();
|
|
34
|
+
const onResponseClose = () => {
|
|
35
|
+
if (!response.writableFinished) abort();
|
|
36
|
+
};
|
|
37
|
+
const onResponseError = () => abort();
|
|
38
|
+
const dispose = () => {
|
|
39
|
+
if (disposed) return;
|
|
40
|
+
disposed = true;
|
|
41
|
+
request.removeListener("aborted", onRequestAborted);
|
|
42
|
+
request.removeListener("error", onRequestError);
|
|
43
|
+
response.removeListener("close", onResponseClose);
|
|
44
|
+
response.removeListener("error", onResponseError);
|
|
45
|
+
};
|
|
46
|
+
request.once("aborted", onRequestAborted);
|
|
47
|
+
request.once("error", onRequestError);
|
|
48
|
+
response.once("close", onResponseClose);
|
|
49
|
+
response.once("error", onResponseError);
|
|
50
|
+
if (request.aborted || response.destroyed || request.socket.destroyed && !response.writableFinished) abort();
|
|
51
|
+
return {
|
|
52
|
+
signal: controller.signal,
|
|
53
|
+
dispose
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function sendWebResponse(reply, response, head) {
|
|
57
|
+
reply.code(response.status);
|
|
58
|
+
for (const [name, value] of response.headers) {
|
|
59
|
+
if (name === "set-cookie") continue;
|
|
60
|
+
reply.header(name, name === "vary" ? mergedVary(reply.getHeader(name), value) : value);
|
|
61
|
+
}
|
|
62
|
+
for (const cookie of response.headers.getSetCookie()) reply.header("set-cookie", cookie);
|
|
63
|
+
if (reply.request.raw.httpVersionMajor === 2) for (const name of [
|
|
64
|
+
"connection",
|
|
65
|
+
"keep-alive",
|
|
66
|
+
"proxy-connection",
|
|
67
|
+
"transfer-encoding",
|
|
68
|
+
"upgrade"
|
|
69
|
+
]) reply.removeHeader(name);
|
|
70
|
+
if (head && response.body !== null) await response.body.cancel().catch(() => void 0);
|
|
71
|
+
return reply.send(head ? void 0 : response.body ?? void 0);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Fastify's encapsulated auth-route platform adapter.
|
|
75
|
+
*
|
|
76
|
+
* Fastify applies `trustProxy` to `request.ip`, but does not expose that option
|
|
77
|
+
* through its public instance metadata. Proxy-trust reporting is therefore
|
|
78
|
+
* intentionally unavailable instead of guessing a diagnostic mode.
|
|
79
|
+
*/
|
|
80
|
+
var FastifyPlatform = class {
|
|
81
|
+
options;
|
|
82
|
+
id = "fastify";
|
|
83
|
+
capabilities = {
|
|
84
|
+
http2: true,
|
|
85
|
+
prepareAtInit: true
|
|
86
|
+
};
|
|
87
|
+
requests;
|
|
88
|
+
#replies = /* @__PURE__ */ new WeakMap();
|
|
89
|
+
constructor(options = {}) {
|
|
90
|
+
this.options = options;
|
|
91
|
+
this.requests = {
|
|
92
|
+
isRequest: (value) => {
|
|
93
|
+
const request = asRequest(value);
|
|
94
|
+
return request !== void 0 && this.#replies.has(request.raw);
|
|
95
|
+
},
|
|
96
|
+
isLive: (value) => {
|
|
97
|
+
const request = asRequest(value);
|
|
98
|
+
const reply = request && this.#replies.get(request.raw);
|
|
99
|
+
return reply !== void 0 && !reply.sent;
|
|
100
|
+
},
|
|
101
|
+
key: (value) => {
|
|
102
|
+
return asRequest(value)?.raw ?? value;
|
|
103
|
+
},
|
|
104
|
+
headers: (value) => {
|
|
105
|
+
const request = asRequest(value);
|
|
106
|
+
return request ? require_platform.toWebHeaders(request.raw.headers) : new Headers();
|
|
107
|
+
},
|
|
108
|
+
request: (value) => {
|
|
109
|
+
const request = asRequest(value);
|
|
110
|
+
if (!request) return {
|
|
111
|
+
method: "GET",
|
|
112
|
+
url: ""
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
method: request.method,
|
|
116
|
+
url: `${request.protocol}://${request.host}${request.originalUrl}`
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
clientIp: (value) => {
|
|
120
|
+
const request = asRequest(value);
|
|
121
|
+
return request ? this.options.clientIp?.(request) ?? request.ip ?? null : null;
|
|
122
|
+
},
|
|
123
|
+
param: (value, name) => {
|
|
124
|
+
const request = asRequest(value);
|
|
125
|
+
if (!request || typeof request.params !== "object" || request.params === null) return;
|
|
126
|
+
const parameter = Reflect.get(request.params, name);
|
|
127
|
+
return typeof parameter === "string" ? parameter : void 0;
|
|
128
|
+
},
|
|
129
|
+
cookieSink: (value, response) => {
|
|
130
|
+
const request = asRequest(value);
|
|
131
|
+
if (!request) return null;
|
|
132
|
+
const supplied = response;
|
|
133
|
+
const reply = supplied && typeof supplied.header === "function" && typeof supplied.sent === "boolean" ? supplied : this.#replies.get(request.raw);
|
|
134
|
+
if (!reply) return null;
|
|
135
|
+
return { append: (cookies) => {
|
|
136
|
+
if (reply.sent || reply.raw.headersSent) return false;
|
|
137
|
+
for (const cookie of cookies) reply.header("set-cookie", cookie);
|
|
138
|
+
return true;
|
|
139
|
+
} };
|
|
140
|
+
},
|
|
141
|
+
responseFor: (value) => {
|
|
142
|
+
const request = asRequest(value);
|
|
143
|
+
return request ? this.#replies.get(request.raw) : void 0;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
supports(adapter) {
|
|
148
|
+
return adapter.getType() === "fastify";
|
|
149
|
+
}
|
|
150
|
+
prepare(context) {
|
|
151
|
+
const root = instanceOf(context.adapter);
|
|
152
|
+
this.#replies = /* @__PURE__ */ new WeakMap();
|
|
153
|
+
root.addHook("onRequest", (request, reply, done) => {
|
|
154
|
+
this.#replies.set(request.raw, reply);
|
|
155
|
+
done();
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async mount(context) {
|
|
159
|
+
const root = instanceOf(context.adapter);
|
|
160
|
+
const { binding } = context;
|
|
161
|
+
await root.register(async (scope) => {
|
|
162
|
+
scope.removeAllContentTypeParsers();
|
|
163
|
+
scope.addContentTypeParser("*", {
|
|
164
|
+
parseAs: "buffer",
|
|
165
|
+
bodyLimit: binding.bodyLimit
|
|
166
|
+
}, (_request, body, done) => done(null, body));
|
|
167
|
+
scope.setErrorHandler(function(error, request, reply) {
|
|
168
|
+
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "FST_ERR_CTP_BODY_TOO_LARGE") return sendWebResponse(reply, binding.payloadTooLarge(), false);
|
|
169
|
+
return root.errorHandler.call(this, error, request, reply);
|
|
170
|
+
});
|
|
171
|
+
const handler = async (request, reply) => {
|
|
172
|
+
const cancellation = abortOnDisconnect(request.raw, reply.raw);
|
|
173
|
+
try {
|
|
174
|
+
const response = await binding.handle({
|
|
175
|
+
method: request.method,
|
|
176
|
+
url: `${request.protocol}://${request.host}${request.originalUrl}`,
|
|
177
|
+
headers: require_platform.toWebHeaders(request.raw.headers),
|
|
178
|
+
body: request.body instanceof Uint8Array ? request.body : null,
|
|
179
|
+
clientIp: this.options.clientIp?.(request) ?? request.ip ?? null,
|
|
180
|
+
platformRequest: request,
|
|
181
|
+
signal: cancellation.signal
|
|
182
|
+
});
|
|
183
|
+
if (cancellation.signal.aborted) return;
|
|
184
|
+
return await sendWebResponse(reply, response, request.method.toUpperCase() === "HEAD");
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (cancellation.signal.aborted) return;
|
|
187
|
+
throw error;
|
|
188
|
+
} finally {
|
|
189
|
+
cancellation.dispose();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const methods = [...scope.supportedMethods];
|
|
193
|
+
scope.route({
|
|
194
|
+
method: methods,
|
|
195
|
+
url: "/",
|
|
196
|
+
handler,
|
|
197
|
+
exposeHeadRoute: false
|
|
198
|
+
});
|
|
199
|
+
scope.route({
|
|
200
|
+
method: methods,
|
|
201
|
+
url: "/*",
|
|
202
|
+
handler,
|
|
203
|
+
exposeHeadRoute: false
|
|
204
|
+
});
|
|
205
|
+
}, { prefix: binding.basePath });
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
function fastifyPlatform(options) {
|
|
209
|
+
return new FastifyPlatform(options);
|
|
210
|
+
}
|
|
211
|
+
//#endregion
|
|
212
|
+
exports.FastifyPlatform = FastifyPlatform;
|
|
213
|
+
exports.fastifyPlatform = fastifyPlatform;
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=fastify.cjs.map
|