@browserlessapi/inferlo 0.5.0 → 0.6.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.
Files changed (83) hide show
  1. package/README.md +8 -2
  2. package/dist/ai/index.d.ts +7 -0
  3. package/dist/ai/index.js +68 -2
  4. package/dist/ai/index.js.map +1 -1
  5. package/dist/ai/public.d.ts +1 -0
  6. package/dist/ai/public.js.map +1 -1
  7. package/dist/api-version.d.ts +32 -0
  8. package/dist/api-version.js +45 -0
  9. package/dist/api-version.js.map +1 -0
  10. package/dist/app.d.ts +30 -3
  11. package/dist/app.js +210 -43
  12. package/dist/app.js.map +1 -1
  13. package/dist/audit.d.ts +50 -0
  14. package/dist/audit.js +92 -0
  15. package/dist/audit.js.map +1 -0
  16. package/dist/cache.d.ts +92 -0
  17. package/dist/cache.js +245 -0
  18. package/dist/cache.js.map +1 -0
  19. package/dist/cli/main.js +25 -4
  20. package/dist/cli/main.js.map +1 -1
  21. package/dist/describe.d.ts +42 -1
  22. package/dist/describe.js +25 -2
  23. package/dist/describe.js.map +1 -1
  24. package/dist/fastify.d.ts +10 -2
  25. package/dist/fastify.js +22 -4
  26. package/dist/fastify.js.map +1 -1
  27. package/dist/h3.d.ts +76 -0
  28. package/dist/h3.js +61 -0
  29. package/dist/h3.js.map +1 -0
  30. package/dist/hapi.d.ts +45 -0
  31. package/dist/hapi.js +57 -0
  32. package/dist/hapi.js.map +1 -0
  33. package/dist/index.d.ts +14 -1
  34. package/dist/index.js +5 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/jobs.d.ts +20 -0
  37. package/dist/jobs.js +19 -7
  38. package/dist/jobs.js.map +1 -1
  39. package/dist/koa.d.ts +51 -0
  40. package/dist/koa.js +45 -0
  41. package/dist/koa.js.map +1 -0
  42. package/dist/lambda.d.ts +110 -0
  43. package/dist/lambda.js +169 -0
  44. package/dist/lambda.js.map +1 -0
  45. package/dist/mcp.d.ts +1 -1
  46. package/dist/mcp.js +7 -3
  47. package/dist/mcp.js.map +1 -1
  48. package/dist/module.d.ts +52 -0
  49. package/dist/module.js +152 -0
  50. package/dist/module.js.map +1 -0
  51. package/dist/nextjs.d.ts +35 -0
  52. package/dist/nextjs.js +33 -0
  53. package/dist/nextjs.js.map +1 -0
  54. package/dist/openapi.d.ts +3 -0
  55. package/dist/openapi.js +12 -3
  56. package/dist/openapi.js.map +1 -1
  57. package/dist/permissions.d.ts +26 -0
  58. package/dist/permissions.js +78 -0
  59. package/dist/permissions.js.map +1 -0
  60. package/dist/react-router.d.ts +41 -0
  61. package/dist/react-router.js +42 -0
  62. package/dist/react-router.js.map +1 -0
  63. package/dist/request-matcher.d.ts +4 -0
  64. package/dist/request-matcher.js +14 -0
  65. package/dist/request-matcher.js.map +1 -0
  66. package/dist/route.d.ts +36 -1
  67. package/dist/route.js +24 -0
  68. package/dist/route.js.map +1 -1
  69. package/dist/serve.d.ts +20 -1
  70. package/dist/serve.js +48 -4
  71. package/dist/serve.js.map +1 -1
  72. package/dist/sessions.d.ts +72 -0
  73. package/dist/sessions.js +171 -0
  74. package/dist/sessions.js.map +1 -0
  75. package/dist/sveltekit.d.ts +72 -0
  76. package/dist/sveltekit.js +68 -0
  77. package/dist/sveltekit.js.map +1 -0
  78. package/dist/tracing.d.ts +118 -0
  79. package/dist/tracing.js +204 -0
  80. package/dist/tracing.js.map +1 -0
  81. package/llms.txt +7 -3
  82. package/package.json +44 -2
  83. package/templates/app/AGENTS.md +5 -3
package/dist/route.d.ts CHANGED
@@ -13,13 +13,16 @@ import type { AI } from "./ai/index.ts";
13
13
  import type { Db } from "./db/table.ts";
14
14
  import type { Jobs } from "./jobs.ts";
15
15
  import type { AnyModel } from "./model.ts";
16
+ import type { Audit } from "./audit.ts";
17
+ import type { Cache, RouteCache } from "./cache.ts";
18
+ import type { PermissionInput, Permissions } from "./permissions.ts";
16
19
  import type { RateLimit } from "./rate-limit.ts";
17
20
  import type { AnySchema, Infer, ObjectSchema } from "./schema.ts";
18
21
  /**
19
22
  * Register your app's models and user type once, so every handler is fully typed:
20
23
  *
21
24
  * declare module "@browserlessapi/inferlo" {
22
- * interface Register { models: typeof models; user: User }
25
+ * interface Register { models: typeof models; user: User; permissions: typeof access }
23
26
  * }
24
27
  */
25
28
  export interface Register {
@@ -33,6 +36,15 @@ export type AppUser = Registered<"user", {
33
36
  roles?: string[];
34
37
  }>;
35
38
  export type AppDb = Db<AppModels>;
39
+ /**
40
+ * The permission names a route's `permission` option accepts. With `permissions: typeof access` in Register, only the
41
+ * names whose rule needs nothing but { user } (roles rules and rules of the user alone), so a typo is a type error.
42
+ */
43
+ export type AppPermission = Registered<"permissions", undefined> extends Permissions<infer R> ? {
44
+ [K in keyof R & string]: {
45
+ user: AppUser;
46
+ } extends PermissionInput<R[K]> ? K : never;
47
+ }[keyof R & string] : string;
36
48
  export type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
37
49
  /** "public": anyone. "user": any signed-in user. Any other string: the user must have that role. */
38
50
  export type Auth = "public" | "user" | (string & {});
@@ -45,6 +57,12 @@ export interface ToolOptions {
45
57
  export interface RouteOptions<B extends AnySchema | undefined, Q extends ObjectSchema<any> | undefined, P extends ObjectSchema<any> | undefined> {
46
58
  /** Who may call this route. Default "user" (signed in). */
47
59
  auth?: Auth;
60
+ /**
61
+ * A permission from createApp({ permissions }) the signed-in user must have, checked with { user } before the
62
+ * handler runs (also when the route is called as an AI tool). Denied: 403 IL3004. For rules that also need a
63
+ * resource, call access.authorize(name, { user, ticket }) in the handler instead. Typed from Register's permissions.
64
+ */
65
+ permission?: AppPermission;
48
66
  /** Request body schema (JSON or form). */
49
67
  body?: B;
50
68
  /** Query string schema. Strings are converted to numbers and booleans automatically. */
@@ -71,6 +89,12 @@ export interface RouteOptions<B extends AnySchema | undefined, Q extends ObjectS
71
89
  * Link rel="deprecation" headers on every response. Shown in OpenAPI, describe and MCP tool descriptions.
72
90
  */
73
91
  deprecated?: boolean | Deprecation;
92
+ /**
93
+ * Cache this GET route's JSON responses in the app cache: { ttlMs: 60_000, by: "user", tags: ["tickets"] }.
94
+ * "user" (default) keeps one response per signed-in user, "public" one for every caller. Remove them early with
95
+ * ctx.cache.invalidate(tag). Responses carry an ETag, so clients revalidate with If-None-Match (304).
96
+ */
97
+ cache?: RouteCache;
74
98
  }
75
99
  type PathParams<P extends string> = P extends `${string}:${infer Param}/${infer Rest}` ? {
76
100
  [K in Param | keyof PathParams<`/${Rest}`>]: string;
@@ -88,6 +112,8 @@ export interface Context<Body, Query, Params, User> {
88
112
  ai: AI;
89
113
  /** Queue background jobs: await jobs.enqueue(sendEmail, { to }). */
90
114
  jobs: Jobs;
115
+ /** The app cache: await cache.getOrSet(key, { ttlMs: 60_000, tags: ["tickets"] }, compute), cache.invalidate("tickets"). */
116
+ cache: Cache;
91
117
  req: Request;
92
118
  url: URL;
93
119
  headers: Headers;
@@ -106,6 +132,11 @@ export interface Context<Body, Query, Params, User> {
106
132
  requestId: string;
107
133
  /** Run work after the response is sent (logging, webhooks). Errors are logged, not thrown. */
108
134
  waitUntil(promise: Promise<unknown>): void;
135
+ /**
136
+ * Write an audit log entry with the signed-in user and this request's id (needs AuditLog in createApp({ models })):
137
+ * await audit("tickets.update", { model: "tickets", before: ticket, after: updated }).
138
+ */
139
+ audit: Audit;
109
140
  }
110
141
  export type AnyContext = Context<any, any, any, any>;
111
142
  export type Handler<C, R> = (ctx: C) => R | Promise<R>;
@@ -117,6 +148,8 @@ export interface RouteDef {
117
148
  readonly handler: Handler<AnyContext, unknown>;
118
149
  /** Export name, filled in by createApp when routes are passed as modules. */
119
150
  name?: string;
151
+ /** API version ("v1") when the route comes from apiVersion("v1", routes). */
152
+ readonly version?: string;
120
153
  }
121
154
  export declare class RouteBuilder<Path extends string, B extends AnySchema | undefined, Q extends ObjectSchema<any> | undefined, P extends ObjectSchema<any> | undefined, A> {
122
155
  readonly method: Method;
@@ -146,4 +179,6 @@ export declare const route: {
146
179
  }) => RouteBuilder<Path, B, Q, P, A>;
147
180
  };
148
181
  export declare function isRoute(value: unknown): value is RouteDef;
182
+ /** The routes in route sources: routes, arrays and modules (import * as tickets), named after their exports. */
183
+ export declare function collectRoutes(sources: unknown): RouteDef[];
149
184
  export {};
package/dist/route.js CHANGED
@@ -25,4 +25,28 @@ export const route = {
25
25
  export function isRoute(value) {
26
26
  return typeof value === "object" && value !== null && value.kind === "inferlo.route";
27
27
  }
28
+ /** The routes in route sources: routes, arrays and modules (import * as tickets), named after their exports. */
29
+ export function collectRoutes(sources) {
30
+ const out = [];
31
+ const seen = new Set();
32
+ const visit = (source, name) => {
33
+ if (!source || typeof source !== "object" || seen.has(source))
34
+ return;
35
+ seen.add(source);
36
+ if (isRoute(source)) {
37
+ out.push(name && !source.name ? { ...source, name } : source);
38
+ return;
39
+ }
40
+ if (Array.isArray(source)) {
41
+ for (const item of source)
42
+ visit(item);
43
+ return;
44
+ }
45
+ for (const [key, value] of Object.entries(source))
46
+ if (isRoute(value) || Array.isArray(value))
47
+ visit(value, key === "default" ? undefined : key);
48
+ };
49
+ visit(sources);
50
+ return out;
51
+ }
28
52
  //# sourceMappingURL=route.js.map
package/dist/route.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../src/route.ts"],"names":[],"mappings":"AA+HA,MAAM,OAAO,YAAY;IACd,MAAM,CAAS;IACf,IAAI,CAAO;IACX,OAAO,CAAwB;IAExC,YAAY,MAAc,EAAE,IAAU,EAAE,OAA8B;QACpE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,qFAAqF;IACrF,MAAM,CACJ,OAQC;QAED,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,OAAuC,EAE5I,CAAC;IACJ,CAAC;CACF;AAED,SAAS,OAAO,CAAC,MAAc;IAC7B,OAAO,CAOL,IAAU,EACV,UAAgD,EAAE,EAClD,EAAE,CAAC,IAAI,YAAY,CAAmB,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC;CAC1B,CAAC;AAEF,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAAkB,CAAC,IAAI,KAAK,eAAe,CAAC;AACrG,CAAC"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../src/route.ts"],"names":[],"mappings":"AA8JA,MAAM,OAAO,YAAY;IACd,MAAM,CAAS;IACf,IAAI,CAAO;IACX,OAAO,CAAwB;IAExC,YAAY,MAAc,EAAE,IAAU,EAAE,OAA8B;QACpE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,qFAAqF;IACrF,MAAM,CACJ,OAQC;QAED,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,OAAuC,EAE5I,CAAC;IACJ,CAAC;CACF;AAED,SAAS,OAAO,CAAC,MAAc;IAC7B,OAAO,CAOL,IAAU,EACV,UAAgD,EAAE,EAClD,EAAE,CAAC,IAAI,YAAY,CAAmB,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC;CAC1B,CAAC;AAEF,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAAkB,CAAC,IAAI,KAAK,eAAe,CAAC;AACrG,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,MAAM,GAAG,GAAe,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAW,CAAC;IAChC,MAAM,KAAK,GAAG,CAAC,MAAe,EAAE,IAAa,EAAE,EAAE;QAC/C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO;QACtE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjB,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpB,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC9D,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,KAAK,MAAM,IAAI,IAAI,MAAM;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACvC,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACnJ,CAAC,CAAC;IACF,KAAK,CAAC,OAAO,CAAC,CAAC;IACf,OAAO,GAAG,CAAC;AACb,CAAC"}
package/dist/serve.d.ts CHANGED
@@ -19,7 +19,10 @@ export interface NodeRequest {
19
19
  remoteAddress?: string;
20
20
  encrypted?: boolean;
21
21
  } | null;
22
- /** A body the host framework already read (express.json(), body-parser). Used instead of the stream. */
22
+ /**
23
+ * A body the host framework already read (express.json(), body-parser): a string, bytes, or a plain object or
24
+ * array. Used instead of the stream. Instances of other classes are ignored and the stream is read.
25
+ */
23
26
  body?: unknown;
24
27
  [Symbol.asyncIterator](): AsyncIterator<unknown>;
25
28
  }
@@ -40,12 +43,28 @@ export interface NodeHandlerOptions {
40
43
  * Leave it out when the host framework already strips the mount path (Express app.use("/ai", ...)).
41
44
  */
42
45
  prefix?: string;
46
+ /**
47
+ * Pass requests the app has no route for (by method and path, outside its OpenAPI, MCP, health and metrics
48
+ * endpoints) to the host framework's next() instead of answering 404 or 405. Mount the app before the host's
49
+ * routes with it, to move routes to Inferlo one at a time at the same URLs. Needs an app from createApp and
50
+ * a host that calls the handler with next (Express, NestJS, Connect). Default false.
51
+ */
52
+ fallthrough?: boolean;
43
53
  }
44
54
  export interface FromNodeRequestOptions extends NodeHandlerOptions {
45
55
  /** Aborts the request (for example when the client disconnects). */
46
56
  signal?: AbortSignal;
47
57
  }
48
58
  export type NodeHandler = (req: NodeRequest, res: NodeResponse, next?: (error?: unknown) => void) => Promise<void>;
59
+ export declare function normalizePrefix(prefix: string | undefined): string;
60
+ /** The path and query of a Node request with the prefix removed, or null when it is outside the prefix. */
61
+ export declare function stripPrefix(rawUrl: string, prefix: string): string | null;
62
+ /**
63
+ * Whether toNodeHandler(app, { prefix, fallthrough: true }) answers a Node request itself instead of passing it
64
+ * on. For hosts that must decide before handing the request over, such as a Fastify onRequest hook.
65
+ */
66
+ export declare function answersNodeRequest(app: object, req: Pick<NodeRequest, "url" | "method">, prefix?: string): boolean;
67
+ export declare function buildRequest(req: NodeRequest, path: string, signal?: AbortSignal): Request;
49
68
  /**
50
69
  * Convert a Node.js request (node:http, Express, Fastify's request.raw, NestJS) to a Web Request.
51
70
  * With prefix, the prefix is removed from the path; a request outside it keeps its path.
package/dist/serve.js CHANGED
@@ -6,14 +6,16 @@
6
6
  * http.createServer(toNodeHandler(app, { prefix: "/ai" })) // or strip it with prefix
7
7
  */
8
8
  import { createRedactor } from "./redact.js";
9
+ import { appAnswers } from "./request-matcher.js";
10
+ import { InferloError } from "./errors.js";
9
11
  import { clientIps } from "./client-ip.js";
10
- function normalizePrefix(prefix) {
12
+ export function normalizePrefix(prefix) {
11
13
  if (!prefix || prefix === "/")
12
14
  return "";
13
15
  return ("/" + prefix.replace(/^\/+|\/+$/g, "")).replace(/\/+/g, "/");
14
16
  }
15
17
  /** The path and query of a Node request with the prefix removed, or null when it is outside the prefix. */
16
- function stripPrefix(rawUrl, prefix) {
18
+ export function stripPrefix(rawUrl, prefix) {
17
19
  const url = rawUrl.startsWith("/") ? rawUrl : "/" + rawUrl;
18
20
  if (!prefix)
19
21
  return url;
@@ -26,6 +28,25 @@ function stripPrefix(rawUrl, prefix) {
26
28
  return null;
27
29
  return rest;
28
30
  }
31
+ /** Whether the app answers a method and a path (with query, prefix already removed) itself. */
32
+ function answersPath(app, method, path) {
33
+ let pathname = path.split("?")[0];
34
+ try {
35
+ pathname = new URL(pathname, "http://localhost").pathname;
36
+ }
37
+ catch {
38
+ // Leave a path the URL parser rejects to the app, which answers it.
39
+ }
40
+ return appAnswers(app, method, pathname) === true;
41
+ }
42
+ /**
43
+ * Whether toNodeHandler(app, { prefix, fallthrough: true }) answers a Node request itself instead of passing it
44
+ * on. For hosts that must decide before handing the request over, such as a Fastify onRequest hook.
45
+ */
46
+ export function answersNodeRequest(app, req, prefix) {
47
+ const path = stripPrefix(req.url ?? "/", normalizePrefix(prefix));
48
+ return path !== null && answersPath(app, req.method ?? "GET", path);
49
+ }
29
50
  function bodyStream(req) {
30
51
  const iterator = req[Symbol.asyncIterator]();
31
52
  return new ReadableStream({
@@ -45,7 +66,20 @@ function bodyStream(req) {
45
66
  },
46
67
  });
47
68
  }
48
- function buildRequest(req, path, signal) {
69
+ /**
70
+ * True when req.body holds a body the host framework already read: a string or bytes (body-parser text and
71
+ * raw), or a plain object or array (express.json(), urlencoded forms). An instance of some other class is the
72
+ * host's own object, such as the body reader of an Encore raw request, and the stream is read instead.
73
+ */
74
+ function isParsedBody(body) {
75
+ if (body === undefined)
76
+ return false;
77
+ if (body === null || typeof body !== "object" || body instanceof Uint8Array || Array.isArray(body))
78
+ return true;
79
+ const proto = Object.getPrototypeOf(body);
80
+ return proto === Object.prototype || proto === null;
81
+ }
82
+ export function buildRequest(req, path, signal) {
49
83
  const host = [req.headers.host].flat()[0] ?? "localhost";
50
84
  const url = new URL(path, `${req.socket?.encrypted ? "https" : "http"}://${host}`);
51
85
  const headers = new Headers();
@@ -61,7 +95,7 @@ function buildRequest(req, path, signal) {
61
95
  const method = (req.method ?? "GET").toUpperCase();
62
96
  let body;
63
97
  if (method !== "GET" && method !== "HEAD") {
64
- if (req.body !== undefined) {
98
+ if (isParsedBody(req.body)) {
65
99
  // The host framework already read the stream: use what it parsed.
66
100
  headers.delete("content-length");
67
101
  if (typeof req.body === "string" || req.body instanceof Uint8Array)
@@ -125,10 +159,20 @@ export async function writeResponse(res, response, method) {
125
159
  */
126
160
  export function toNodeHandler(app, options = {}) {
127
161
  const prefix = normalizePrefix(options.prefix);
162
+ if (options.fallthrough !== undefined && typeof options.fallthrough !== "boolean") {
163
+ throw new InferloError("IL5019", "toNodeHandler fallthrough must be true or false.", { fix: "Use toNodeHandler(app, { fallthrough: true })." });
164
+ }
165
+ if (options.fallthrough && appAnswers(app, "GET", "/") === undefined) {
166
+ throw new InferloError("IL5019", "toNodeHandler(app, { fallthrough: true }) needs an app from createApp.", {
167
+ fix: "Pass the app returned by createApp, which knows its routes; a plain fetch handler cannot tell which requests to pass on.",
168
+ });
169
+ }
128
170
  return async (req, res, next) => {
129
171
  const path = stripPrefix(req.url ?? "/", prefix);
130
172
  if (path === null && next)
131
173
  return next();
174
+ if (options.fallthrough && next && path !== null && !answersPath(app, req.method ?? "GET", path))
175
+ return next();
132
176
  try {
133
177
  const controller = new AbortController();
134
178
  res.on("close", () => {
package/dist/serve.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Cookie sessions for browser apps: signed, HttpOnly cookies and CSRF protection, with no session table.
3
+ *
4
+ * export const session = sessions({
5
+ * secret: config.SESSION_SECRET,
6
+ * user: ({ userId }, { db }) => db.users.find(userId),
7
+ * });
8
+ * createApp({ auth: session, ... });
9
+ *
10
+ * // in a login route (auth: "public"), after checking the password:
11
+ * const { csrfToken } = await session.signIn(ctx, user.id);
12
+ * // in a logout route:
13
+ * session.signOut(ctx);
14
+ *
15
+ * Requests that change data (POST, PUT, PATCH, DELETE) with a session cookie must send the CSRF token in an
16
+ * X-CSRF-Token header or a _csrf form field, or they are rejected with IL3005.
17
+ */
18
+ import type { AuthConfig } from "./app.ts";
19
+ import type { AppDb, AppUser } from "./route.ts";
20
+ export interface SessionData {
21
+ /** The id passed to session.signIn(ctx, userId). */
22
+ userId: string;
23
+ /** When the session was created (milliseconds since 1970). Compare it to a "signed out everywhere" time to revoke older sessions. */
24
+ issuedAt: number;
25
+ }
26
+ export interface SessionCookieOptions {
27
+ /** Cookie name. Default "inferlo_session"; the CSRF cookie is the same name with "_csrf" added. */
28
+ name?: string;
29
+ /** SameSite attribute. Default "lax" (links from other sites keep the session, cross-site form posts do not send it). */
30
+ sameSite?: "lax" | "strict";
31
+ /** Secure attribute. Default true, except for plain http on localhost, 127.0.0.1 and ::1 during development. */
32
+ secure?: boolean;
33
+ /** Domain attribute, to share the session with subdomains. Default: none (only the host that set it). */
34
+ domain?: string;
35
+ /** Path attribute. Default "/". */
36
+ path?: string;
37
+ }
38
+ export interface SessionsOptions {
39
+ /**
40
+ * The signing secret, at least 32 characters: keep it in config({ secrets }). To rotate it, pass
41
+ * [newSecret, oldSecret]: new sessions are signed with the first, and sessions signed with any of them stay valid.
42
+ */
43
+ secret: string | readonly string[];
44
+ /** Load the signed-in user for a session, or return null to end it (deleted or disabled users, revoked sessions). */
45
+ user: (session: SessionData, ctx: {
46
+ db: AppDb;
47
+ req: Request;
48
+ }) => AppUser | null | undefined | Promise<AppUser | null | undefined>;
49
+ /** How long a session lasts after sign-in, in milliseconds. Default 7 days; 1 minute to 400 days. */
50
+ maxAgeMs?: number;
51
+ cookie?: SessionCookieOptions;
52
+ /** Roles of a user, checked when a route uses auth: "<role>". Default: user.roles ?? []. */
53
+ roles?: (user: AppUser) => string[];
54
+ }
55
+ /** What session.signIn and session.signOut need from the route context. */
56
+ export interface SessionResponseContext {
57
+ url: URL;
58
+ header(name: string, value: string): void;
59
+ }
60
+ /** createApp({ auth: session }) with sign-in, sign-out and the CSRF token of the current session. */
61
+ export interface Sessions extends AuthConfig {
62
+ /** Start a session for this user: sets the session and CSRF cookies. Returns the CSRF token to send with changes. */
63
+ signIn(ctx: SessionResponseContext, userId: string): Promise<{
64
+ csrfToken: string;
65
+ }>;
66
+ /** End the session: clears both cookies. */
67
+ signOut(ctx: SessionResponseContext): void;
68
+ /** The CSRF token of the request's session (to put in a form's _csrf field), or null without a valid session. */
69
+ csrfToken(req: Request): Promise<string | null>;
70
+ }
71
+ /** Signed cookie sessions with CSRF protection: createApp({ auth: sessions({ secret, user }) }). */
72
+ export declare function sessions(options: SessionsOptions): Sessions;
@@ -0,0 +1,171 @@
1
+ import { safeEqual } from "./auth.js";
2
+ import { InferloError } from "./errors.js";
3
+ const DAY = 86_400_000;
4
+ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
5
+ const MAX_FORM_BYTES = 64 * 1024;
6
+ const fix = 'Pass sessions({ secret: config.SESSION_SECRET, user: ({ userId }, { db }) => db.users.find(userId) }) with a secret of at least 32 random characters (openssl rand -base64 32).';
7
+ /** Signed cookie sessions with CSRF protection: createApp({ auth: sessions({ secret, user }) }). */
8
+ export function sessions(options) {
9
+ if (!options || typeof options !== "object")
10
+ throw new InferloError("IL6029", "sessions() needs { secret, user }.", { fix });
11
+ const secrets = typeof options.secret === "string" ? [options.secret] : Array.isArray(options.secret) ? [...options.secret] : [];
12
+ if (!secrets.length || secrets.some((s) => typeof s !== "string" || s.length < 32)) {
13
+ throw new InferloError("IL6029", "sessions() secret must be a string of at least 32 characters (or a list of them, newest first).", { fix });
14
+ }
15
+ if (typeof options.user !== "function")
16
+ throw new InferloError("IL6029", "sessions() needs a user({ userId }, { db }) function that loads the signed-in user.", { fix });
17
+ const maxAgeMs = options.maxAgeMs ?? 7 * DAY;
18
+ if (!Number.isInteger(maxAgeMs) || maxAgeMs < 60_000 || maxAgeMs > 400 * DAY) {
19
+ throw new InferloError("IL6029", "sessions() maxAgeMs must be a whole number of milliseconds from 60000 (1 minute) to 34560000000 (400 days).", { fix });
20
+ }
21
+ const cookie = options.cookie ?? {};
22
+ const name = cookie.name ?? "inferlo_session";
23
+ if (!/^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/.test(name))
24
+ throw new InferloError("IL6029", `sessions() cookie name "${name}" is not a valid cookie name.`, { fix: 'Use letters, digits, "-" and "_", such as "app_session".' });
25
+ if (cookie.sameSite !== undefined && cookie.sameSite !== "lax" && cookie.sameSite !== "strict") {
26
+ throw new InferloError("IL6029", 'sessions() cookie.sameSite must be "lax" or "strict".', { fix: 'Use sameSite: "lax" (the default) or "strict".' });
27
+ }
28
+ for (const key of ["domain", "path"]) {
29
+ const value = cookie[key];
30
+ if (value !== undefined && (typeof value !== "string" || !value || /[;,\s]/.test(value))) {
31
+ throw new InferloError("IL6029", `sessions() cookie.${key} must be a non-empty string without spaces, commas or semicolons.`, { fix });
32
+ }
33
+ }
34
+ const csrfName = `${name}_csrf`;
35
+ const sameSite = cookie.sameSite === "strict" ? "Strict" : "Lax";
36
+ const path = cookie.path ?? "/";
37
+ const keys = secrets.map((secret) => crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]));
38
+ const sign = async (key, text) => base64url(new Uint8Array(await crypto.subtle.sign("HMAC", await key, new TextEncoder().encode(text))));
39
+ const csrfFor = (sessionId) => sign(keys[0], `csrf.${sessionId}`);
40
+ const attributes = (url, httpOnly, maxAge) => {
41
+ const local = url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
42
+ const secure = cookie.secure ?? !local;
43
+ return [`Path=${path}`, cookie.domain ? `Domain=${cookie.domain}` : "", `Max-Age=${maxAge}`, httpOnly ? "HttpOnly" : "", secure ? "Secure" : "", `SameSite=${sameSite}`].filter(Boolean).join("; ");
44
+ };
45
+ /** The verified session of a request, or null. */
46
+ const read = async (req) => {
47
+ const value = readCookie(req.headers.get("cookie"), name);
48
+ if (!value)
49
+ return null;
50
+ const [version, payload, signature, extra] = value.split(".");
51
+ if (version !== "v1" || !payload || !signature || extra !== undefined)
52
+ return null;
53
+ let valid = false;
54
+ for (const key of keys)
55
+ if (safeEqual(await sign(key, `v1.${payload}`), signature))
56
+ valid = true;
57
+ if (!valid)
58
+ return null;
59
+ try {
60
+ const parsed = JSON.parse(new TextDecoder().decode(unbase64url(payload)));
61
+ if (typeof parsed.u !== "string" || typeof parsed.s !== "string" || typeof parsed.i !== "number" || typeof parsed.e !== "number")
62
+ return null;
63
+ if (parsed.e <= Date.now())
64
+ return null;
65
+ return { data: { userId: parsed.u, issuedAt: parsed.i }, sessionId: parsed.s };
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ };
71
+ const session = {
72
+ async resolve(req, { db }) {
73
+ const current = await read(req);
74
+ if (!current)
75
+ return null;
76
+ if (!SAFE_METHODS.has(req.method.toUpperCase())) {
77
+ const sent = req.headers.get("x-csrf-token") ?? (await formField(req, "_csrf"));
78
+ if (!sent || !safeEqual(sent, await csrfFor(current.sessionId))) {
79
+ throw new InferloError("IL3005", "Missing or invalid CSRF token.", {
80
+ status: 403,
81
+ fix: "Send the session's CSRF token in an X-CSRF-Token header (from session.signIn or the " + csrfName + " cookie) or a _csrf form field (from session.csrfToken(req)).",
82
+ });
83
+ }
84
+ }
85
+ return (await options.user(current.data, { db, req })) ?? null;
86
+ },
87
+ roles: options.roles,
88
+ async signIn(ctx, userId) {
89
+ if (typeof userId !== "string" || !userId)
90
+ throw new InferloError("IL6029", "session.signIn(ctx, userId) needs the user's id as a non-empty string.", { fix: "Call it after checking the password: await session.signIn(ctx, user.id)." });
91
+ if (!ctx || typeof ctx.header !== "function" || !(ctx.url instanceof URL))
92
+ throw new InferloError("IL6029", "session.signIn(ctx, userId) needs the route context.", { fix: "Pass the handler's context: route.post(\"/login\", ...).handle(async (ctx) => { await session.signIn(ctx, user.id) })." });
93
+ const now = Date.now();
94
+ const sessionId = base64url(crypto.getRandomValues(new Uint8Array(18)));
95
+ const payload = base64url(new TextEncoder().encode(JSON.stringify({ u: userId, s: sessionId, i: now, e: now + maxAgeMs })));
96
+ const value = `v1.${payload}.${await sign(keys[0], `v1.${payload}`)}`;
97
+ const csrfToken = await csrfFor(sessionId);
98
+ const seconds = Math.floor(maxAgeMs / 1000);
99
+ ctx.header("set-cookie", `${name}=${value}; ${attributes(ctx.url, true, seconds)}`);
100
+ ctx.header("set-cookie", `${csrfName}=${csrfToken}; ${attributes(ctx.url, false, seconds)}`);
101
+ return { csrfToken };
102
+ },
103
+ signOut(ctx) {
104
+ if (!ctx || typeof ctx.header !== "function" || !(ctx.url instanceof URL))
105
+ throw new InferloError("IL6029", "session.signOut(ctx) needs the route context.", { fix: "Pass the handler's context: session.signOut(ctx)." });
106
+ ctx.header("set-cookie", `${name}=; ${attributes(ctx.url, true, 0)}`);
107
+ ctx.header("set-cookie", `${csrfName}=; ${attributes(ctx.url, false, 0)}`);
108
+ },
109
+ async csrfToken(req) {
110
+ const current = await read(req);
111
+ return current ? csrfFor(current.sessionId) : null;
112
+ },
113
+ };
114
+ return Object.freeze(session);
115
+ }
116
+ function readCookie(header, name) {
117
+ if (!header)
118
+ return undefined;
119
+ for (const part of header.split(";")) {
120
+ const i = part.indexOf("=");
121
+ if (i > 0 && part.slice(0, i).trim() === name)
122
+ return part.slice(i + 1).trim();
123
+ }
124
+ return undefined;
125
+ }
126
+ /** A field of a URL-encoded form body, read from a copy of the request (at most 64 KiB) so the route can still read it. */
127
+ async function formField(req, field) {
128
+ if (!(req.headers.get("content-type") ?? "").toLowerCase().includes("application/x-www-form-urlencoded"))
129
+ return null;
130
+ if (Number(req.headers.get("content-length") ?? "0") > MAX_FORM_BYTES)
131
+ return null;
132
+ // Cloned before req.body is touched: on Bun 1.3, a body stream read before clone() comes back empty.
133
+ const body = req.clone().body;
134
+ if (!body)
135
+ return null;
136
+ const reader = body.getReader();
137
+ const chunks = [];
138
+ let size = 0;
139
+ try {
140
+ while (size <= MAX_FORM_BYTES) {
141
+ const { done, value } = await reader.read();
142
+ if (done)
143
+ break;
144
+ chunks.push(value);
145
+ size += value.byteLength;
146
+ }
147
+ }
148
+ finally {
149
+ void reader.cancel().catch(() => { });
150
+ }
151
+ if (size > MAX_FORM_BYTES)
152
+ return null;
153
+ const bytes = new Uint8Array(size);
154
+ let offset = 0;
155
+ for (const chunk of chunks) {
156
+ bytes.set(chunk, offset);
157
+ offset += chunk.byteLength;
158
+ }
159
+ return new URLSearchParams(new TextDecoder().decode(bytes)).get(field);
160
+ }
161
+ function base64url(bytes) {
162
+ let text = "";
163
+ for (const b of bytes)
164
+ text += String.fromCharCode(b);
165
+ return btoa(text).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
166
+ }
167
+ function unbase64url(text) {
168
+ const padded = text.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - (text.length % 4)) % 4);
169
+ return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
170
+ }
171
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}