@browserlessapi/inferlo 0.5.0 → 0.7.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 (138) hide show
  1. package/README.md +14 -5
  2. package/dist/ai/ai-sdk.d.ts +48 -0
  3. package/dist/ai/ai-sdk.js +235 -0
  4. package/dist/ai/ai-sdk.js.map +1 -0
  5. package/dist/ai/index.d.ts +44 -3
  6. package/dist/ai/index.js +164 -9
  7. package/dist/ai/index.js.map +1 -1
  8. package/dist/ai/public.d.ts +6 -1
  9. package/dist/ai/public.js +2 -0
  10. package/dist/ai/public.js.map +1 -1
  11. package/dist/ai/ui.d.ts +47 -0
  12. package/dist/ai/ui.js +33 -0
  13. package/dist/ai/ui.js.map +1 -0
  14. package/dist/api-version.d.ts +32 -0
  15. package/dist/api-version.js +45 -0
  16. package/dist/api-version.js.map +1 -0
  17. package/dist/app.d.ts +60 -6
  18. package/dist/app.js +310 -66
  19. package/dist/app.js.map +1 -1
  20. package/dist/astro.d.ts +60 -0
  21. package/dist/astro.js +68 -0
  22. package/dist/astro.js.map +1 -0
  23. package/dist/audit.d.ts +57 -0
  24. package/dist/audit.js +99 -0
  25. package/dist/audit.js.map +1 -0
  26. package/dist/auth.d.ts +1 -0
  27. package/dist/auth.js +1 -0
  28. package/dist/auth.js.map +1 -1
  29. package/dist/azure.d.ts +66 -0
  30. package/dist/azure.js +119 -0
  31. package/dist/azure.js.map +1 -0
  32. package/dist/cache.d.ts +96 -0
  33. package/dist/cache.js +247 -0
  34. package/dist/cache.js.map +1 -0
  35. package/dist/cli/main.js +28 -7
  36. package/dist/cli/main.js.map +1 -1
  37. package/dist/client-ip.d.ts +36 -1
  38. package/dist/client-ip.js +80 -1
  39. package/dist/client-ip.js.map +1 -1
  40. package/dist/db/memory.js +2 -1
  41. package/dist/db/memory.js.map +1 -1
  42. package/dist/db/postgres.js +4 -1
  43. package/dist/db/postgres.js.map +1 -1
  44. package/dist/db/table.d.ts +37 -5
  45. package/dist/db/table.js +96 -15
  46. package/dist/db/table.js.map +1 -1
  47. package/dist/db/types.d.ts +2 -0
  48. package/dist/describe.d.ts +54 -2
  49. package/dist/describe.js +34 -3
  50. package/dist/describe.js.map +1 -1
  51. package/dist/events.d.ts +134 -0
  52. package/dist/events.js +196 -0
  53. package/dist/events.js.map +1 -0
  54. package/dist/fastify.d.ts +10 -2
  55. package/dist/fastify.js +22 -4
  56. package/dist/fastify.js.map +1 -1
  57. package/dist/h3.d.ts +76 -0
  58. package/dist/h3.js +61 -0
  59. package/dist/h3.js.map +1 -0
  60. package/dist/hapi.d.ts +45 -0
  61. package/dist/hapi.js +57 -0
  62. package/dist/hapi.js.map +1 -0
  63. package/dist/index.d.ts +25 -5
  64. package/dist/index.js +9 -1
  65. package/dist/index.js.map +1 -1
  66. package/dist/jobs.d.ts +70 -10
  67. package/dist/jobs.js +78 -13
  68. package/dist/jobs.js.map +1 -1
  69. package/dist/koa.d.ts +51 -0
  70. package/dist/koa.js +45 -0
  71. package/dist/koa.js.map +1 -0
  72. package/dist/lambda.d.ts +110 -0
  73. package/dist/lambda.js +169 -0
  74. package/dist/lambda.js.map +1 -0
  75. package/dist/langchain.d.ts +58 -0
  76. package/dist/langchain.js +220 -0
  77. package/dist/langchain.js.map +1 -0
  78. package/dist/mcp.d.ts +1 -1
  79. package/dist/mcp.js +7 -3
  80. package/dist/mcp.js.map +1 -1
  81. package/dist/model.d.ts +23 -6
  82. package/dist/model.js +35 -6
  83. package/dist/model.js.map +1 -1
  84. package/dist/module.d.ts +52 -0
  85. package/dist/module.js +152 -0
  86. package/dist/module.js.map +1 -0
  87. package/dist/nextjs.d.ts +35 -0
  88. package/dist/nextjs.js +33 -0
  89. package/dist/nextjs.js.map +1 -0
  90. package/dist/openapi.d.ts +3 -0
  91. package/dist/openapi.js +12 -3
  92. package/dist/openapi.js.map +1 -1
  93. package/dist/permissions.d.ts +26 -0
  94. package/dist/permissions.js +78 -0
  95. package/dist/permissions.js.map +1 -0
  96. package/dist/rate-limit.d.ts +5 -3
  97. package/dist/rate-limit.js +3 -3
  98. package/dist/rate-limit.js.map +1 -1
  99. package/dist/react-router.d.ts +41 -0
  100. package/dist/react-router.js +42 -0
  101. package/dist/react-router.js.map +1 -0
  102. package/dist/redis-events.d.ts +27 -0
  103. package/dist/redis-events.js +163 -0
  104. package/dist/redis-events.js.map +1 -0
  105. package/dist/redis-jobs.d.ts +24 -0
  106. package/dist/redis-jobs.js +203 -0
  107. package/dist/redis-jobs.js.map +1 -0
  108. package/dist/redis.d.ts +30 -0
  109. package/dist/redis.js +106 -0
  110. package/dist/redis.js.map +1 -0
  111. package/dist/request-matcher.d.ts +4 -0
  112. package/dist/request-matcher.js +14 -0
  113. package/dist/request-matcher.js.map +1 -0
  114. package/dist/response-body.d.ts +6 -0
  115. package/dist/response-body.js +7 -0
  116. package/dist/response-body.js.map +1 -0
  117. package/dist/route.d.ts +50 -2
  118. package/dist/route.js +24 -0
  119. package/dist/route.js.map +1 -1
  120. package/dist/schema.d.ts +2 -2
  121. package/dist/serve.d.ts +26 -2
  122. package/dist/serve.js +111 -10
  123. package/dist/serve.js.map +1 -1
  124. package/dist/service-client.d.ts +91 -0
  125. package/dist/service-client.js +177 -0
  126. package/dist/service-client.js.map +1 -0
  127. package/dist/sessions.d.ts +74 -0
  128. package/dist/sessions.js +172 -0
  129. package/dist/sessions.js.map +1 -0
  130. package/dist/sveltekit.d.ts +72 -0
  131. package/dist/sveltekit.js +68 -0
  132. package/dist/sveltekit.js.map +1 -0
  133. package/dist/tracing.d.ts +122 -0
  134. package/dist/tracing.js +210 -0
  135. package/dist/tracing.js.map +1 -0
  136. package/llms.txt +8 -3
  137. package/package.json +78 -4
  138. package/templates/app/AGENTS.md +10 -6
package/dist/route.d.ts CHANGED
@@ -11,15 +11,19 @@
11
11
  import type { Deprecation } from "./deprecation.ts";
12
12
  import type { AI } from "./ai/index.ts";
13
13
  import type { Db } from "./db/table.ts";
14
+ import type { Events } from "./events.ts";
14
15
  import type { Jobs } from "./jobs.ts";
15
16
  import type { AnyModel } from "./model.ts";
17
+ import type { Audit } from "./audit.ts";
18
+ import type { Cache, RouteCache } from "./cache.ts";
19
+ import type { PermissionInput, Permissions } from "./permissions.ts";
16
20
  import type { RateLimit } from "./rate-limit.ts";
17
- import type { AnySchema, Infer, ObjectSchema } from "./schema.ts";
21
+ import type { AnySchema, Infer, InferInput, ObjectSchema } from "./schema.ts";
18
22
  /**
19
23
  * Register your app's models and user type once, so every handler is fully typed:
20
24
  *
21
25
  * declare module "@browserlessapi/inferlo" {
22
- * interface Register { models: typeof models; user: User }
26
+ * interface Register { models: typeof models; user: User; permissions: typeof access }
23
27
  * }
24
28
  */
25
29
  export interface Register {
@@ -33,6 +37,15 @@ export type AppUser = Registered<"user", {
33
37
  roles?: string[];
34
38
  }>;
35
39
  export type AppDb = Db<AppModels>;
40
+ /**
41
+ * The permission names a route's `permission` option accepts. With `permissions: typeof access` in Register, only the
42
+ * names whose rule needs nothing but { user } (roles rules and rules of the user alone), so a typo is a type error.
43
+ */
44
+ export type AppPermission = Registered<"permissions", undefined> extends Permissions<infer R> ? {
45
+ [K in keyof R & string]: {
46
+ user: AppUser;
47
+ } extends PermissionInput<R[K]> ? K : never;
48
+ }[keyof R & string] : string;
36
49
  export type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
37
50
  /** "public": anyone. "user": any signed-in user. Any other string: the user must have that role. */
38
51
  export type Auth = "public" | "user" | (string & {});
@@ -45,6 +58,12 @@ export interface ToolOptions {
45
58
  export interface RouteOptions<B extends AnySchema | undefined, Q extends ObjectSchema<any> | undefined, P extends ObjectSchema<any> | undefined> {
46
59
  /** Who may call this route. Default "user" (signed in). */
47
60
  auth?: Auth;
61
+ /**
62
+ * A permission from createApp({ permissions }) the signed-in user must have, checked with { user } before the
63
+ * handler runs (also when the route is called as an AI tool). Denied: 403 IL3004. For rules that also need a
64
+ * resource, call access.authorize(name, { user, ticket }) in the handler instead. Typed from Register's permissions.
65
+ */
66
+ permission?: AppPermission;
48
67
  /** Request body schema (JSON or form). */
49
68
  body?: B;
50
69
  /** Query string schema. Strings are converted to numbers and booleans automatically. */
@@ -71,6 +90,12 @@ export interface RouteOptions<B extends AnySchema | undefined, Q extends ObjectS
71
90
  * Link rel="deprecation" headers on every response. Shown in OpenAPI, describe and MCP tool descriptions.
72
91
  */
73
92
  deprecated?: boolean | Deprecation;
93
+ /**
94
+ * Cache this GET route's JSON responses in the app cache: { ttlMs: 60_000, by: "user", tags: ["tickets"] }.
95
+ * "user" (default) keeps one response per signed-in user, "public" one for every caller. Remove them early with
96
+ * ctx.cache.invalidate(tag). Responses carry an ETag, so clients revalidate with If-None-Match (304).
97
+ */
98
+ cache?: RouteCache;
74
99
  }
75
100
  type PathParams<P extends string> = P extends `${string}:${infer Param}/${infer Rest}` ? {
76
101
  [K in Param | keyof PathParams<`/${Rest}`>]: string;
@@ -88,6 +113,10 @@ export interface Context<Body, Query, Params, User> {
88
113
  ai: AI;
89
114
  /** Queue background jobs: await jobs.enqueue(sendEmail, { to }). */
90
115
  jobs: Jobs;
116
+ /** Publish events: await events.publish(orderPaid, { orderId }) queues the jobs subscribed with on(event, job). */
117
+ events: Events;
118
+ /** The app cache: await cache.getOrSet(key, { ttlMs: 60_000, tags: ["tickets"] }, compute), cache.invalidate("tickets"). */
119
+ cache: Cache;
91
120
  req: Request;
92
121
  url: URL;
93
122
  headers: Headers;
@@ -106,8 +135,22 @@ export interface Context<Body, Query, Params, User> {
106
135
  requestId: string;
107
136
  /** Run work after the response is sent (logging, webhooks). Errors are logged, not thrown. */
108
137
  waitUntil(promise: Promise<unknown>): void;
138
+ /**
139
+ * Write an audit log entry with the signed-in user and this request's id (needs AuditLog in createApp({ models })):
140
+ * await audit("tickets.update", { model: "tickets", before: ticket, after: updated }).
141
+ */
142
+ audit: Audit;
109
143
  }
110
144
  export type AnyContext = Context<any, any, any, any>;
145
+ /**
146
+ * What a caller sends to a route, before validation (defaults not yet applied): the body, query and path params.
147
+ * Type only ($input on the route returned by handle()), used by serviceClient for typed calls.
148
+ */
149
+ export interface RouteInput<Path extends string, B, Q, P> {
150
+ body: B extends AnySchema ? InferInput<B> : undefined;
151
+ query: Q extends AnySchema ? InferInput<Q> : Record<string, string | undefined>;
152
+ params: P extends AnySchema ? InferInput<P> : PathParams<Path>;
153
+ }
111
154
  export type Handler<C, R> = (ctx: C) => R | Promise<R>;
112
155
  export interface RouteDef {
113
156
  readonly kind: "inferlo.route";
@@ -117,6 +160,8 @@ export interface RouteDef {
117
160
  readonly handler: Handler<AnyContext, unknown>;
118
161
  /** Export name, filled in by createApp when routes are passed as modules. */
119
162
  name?: string;
163
+ /** API version ("v1") when the route comes from apiVersion("v1", routes). */
164
+ readonly version?: string;
120
165
  }
121
166
  export declare class RouteBuilder<Path extends string, B extends AnySchema | undefined, Q extends ObjectSchema<any> | undefined, P extends ObjectSchema<any> | undefined, A> {
122
167
  readonly method: Method;
@@ -126,6 +171,7 @@ export declare class RouteBuilder<Path extends string, B extends AnySchema | und
126
171
  /** The request handler. Return data (sent as JSON), a Response, or nothing (204). */
127
172
  handle<R>(handler: Handler<Context<B extends AnySchema ? Infer<B> : undefined, Q extends AnySchema ? Infer<Q> : Record<string, string | undefined>, P extends AnySchema ? Infer<P> : PathParams<Path>, UserFor<A>>, R>): RouteDef & {
128
173
  readonly $response: Awaited<R>;
174
+ readonly $input: RouteInput<Path, B, Q, P>;
129
175
  };
130
176
  }
131
177
  export declare const route: {
@@ -146,4 +192,6 @@ export declare const route: {
146
192
  }) => RouteBuilder<Path, B, Q, P, A>;
147
193
  };
148
194
  export declare function isRoute(value: unknown): value is RouteDef;
195
+ /** The routes in route sources: routes, arrays and modules (import * as tickets), named after their exports. */
196
+ export declare function collectRoutes(sources: unknown): RouteDef[];
149
197
  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":"AA2KA,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,EAG5I,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/schema.d.ts CHANGED
@@ -26,8 +26,8 @@ export interface Def {
26
26
  example?: unknown;
27
27
  unique?: boolean;
28
28
  index?: boolean;
29
- /** Filled in by the framework (id, createdAt, updatedAt). Never accepted as input. */
30
- system?: "id" | "createdAt" | "updatedAt";
29
+ /** Filled in by the framework (id, createdAt, updatedAt, and tenantId on tenant-scoped models). Never accepted as input. */
30
+ system?: "id" | "createdAt" | "updatedAt" | "tenantId";
31
31
  }
32
32
  export interface Issue {
33
33
  path: (string | number)[];
package/dist/serve.d.ts CHANGED
@@ -19,8 +19,16 @@ 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;
27
+ /**
28
+ * The exact bytes of a body the host already read, as Google Cloud Run functions (the Functions Framework)
29
+ * and Firebase set them. Preferred over body when present, so forms and JSON reach the app unchanged.
30
+ */
31
+ rawBody?: unknown;
24
32
  [Symbol.asyncIterator](): AsyncIterator<unknown>;
25
33
  }
26
34
  /** The parts of a Node.js http.ServerResponse that Inferlo writes to. */
@@ -40,12 +48,28 @@ export interface NodeHandlerOptions {
40
48
  * Leave it out when the host framework already strips the mount path (Express app.use("/ai", ...)).
41
49
  */
42
50
  prefix?: string;
51
+ /**
52
+ * Pass requests the app has no route for (by method and path, outside its OpenAPI, MCP, health and metrics
53
+ * endpoints) to the host framework's next() instead of answering 404 or 405. Mount the app before the host's
54
+ * routes with it, to move routes to Inferlo one at a time at the same URLs. Needs an app from createApp and
55
+ * a host that calls the handler with next (Express, NestJS, Connect). Default false.
56
+ */
57
+ fallthrough?: boolean;
43
58
  }
44
59
  export interface FromNodeRequestOptions extends NodeHandlerOptions {
45
60
  /** Aborts the request (for example when the client disconnects). */
46
61
  signal?: AbortSignal;
47
62
  }
48
63
  export type NodeHandler = (req: NodeRequest, res: NodeResponse, next?: (error?: unknown) => void) => Promise<void>;
64
+ export declare function normalizePrefix(prefix: string | undefined): string;
65
+ /** The path and query of a Node request with the prefix removed, or null when it is outside the prefix. */
66
+ export declare function stripPrefix(rawUrl: string, prefix: string): string | null;
67
+ /**
68
+ * Whether toNodeHandler(app, { prefix, fallthrough: true }) answers a Node request itself instead of passing it
69
+ * on. For hosts that must decide before handing the request over, such as a Fastify onRequest hook.
70
+ */
71
+ export declare function answersNodeRequest(app: object, req: Pick<NodeRequest, "url" | "method">, prefix?: string): boolean;
72
+ export declare function buildRequest(req: NodeRequest, path: string, signal?: AbortSignal): Request;
49
73
  /**
50
74
  * Convert a Node.js request (node:http, Express, Fastify's request.raw, NestJS) to a Web Request.
51
75
  * With prefix, the prefix is removed from the path; a request outside it keeps its path.
@@ -59,5 +83,5 @@ export declare function writeResponse(res: NodeResponse, response: Response, met
59
83
  */
60
84
  export declare function toNodeHandler(app: Pick<App<any>, "fetch">, options?: NodeHandlerOptions): NodeHandler;
61
85
  export declare function serve(app: Pick<App<any>, "fetch">, options?: ServeOptions): Promise<Server>;
62
- /** Closes every open connection of a Node server from serve(), for app.close() after its timeout. */
86
+ /** Closes every open connection of a server from serve() (Node.js, Bun and Deno), for app.close() after its timeout. */
63
87
  export declare const forceClosers: WeakMap<Server, () => void>;
package/dist/serve.js CHANGED
@@ -6,14 +6,17 @@
6
6
  * http.createServer(toNodeHandler(app, { prefix: "/ai" })) // or strip it with prefix
7
7
  */
8
8
  import { createRedactor } from "./redact.js";
9
- import { clientIps } from "./client-ip.js";
10
- function normalizePrefix(prefix) {
9
+ import { appAnswers } from "./request-matcher.js";
10
+ import { InferloError } from "./errors.js";
11
+ import { clientIps, clientSignals } from "./client-ip.js";
12
+ import { knownBodies } from "./response-body.js";
13
+ export function normalizePrefix(prefix) {
11
14
  if (!prefix || prefix === "/")
12
15
  return "";
13
16
  return ("/" + prefix.replace(/^\/+|\/+$/g, "")).replace(/\/+/g, "/");
14
17
  }
15
18
  /** 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) {
19
+ export function stripPrefix(rawUrl, prefix) {
17
20
  const url = rawUrl.startsWith("/") ? rawUrl : "/" + rawUrl;
18
21
  if (!prefix)
19
22
  return url;
@@ -26,6 +29,25 @@ function stripPrefix(rawUrl, prefix) {
26
29
  return null;
27
30
  return rest;
28
31
  }
32
+ /** Whether the app answers a method and a path (with query, prefix already removed) itself. */
33
+ function answersPath(app, method, path) {
34
+ let pathname = path.split("?")[0];
35
+ try {
36
+ pathname = new URL(pathname, "http://localhost").pathname;
37
+ }
38
+ catch {
39
+ // Leave a path the URL parser rejects to the app, which answers it.
40
+ }
41
+ return appAnswers(app, method, pathname) === true;
42
+ }
43
+ /**
44
+ * Whether toNodeHandler(app, { prefix, fallthrough: true }) answers a Node request itself instead of passing it
45
+ * on. For hosts that must decide before handing the request over, such as a Fastify onRequest hook.
46
+ */
47
+ export function answersNodeRequest(app, req, prefix) {
48
+ const path = stripPrefix(req.url ?? "/", normalizePrefix(prefix));
49
+ return path !== null && answersPath(app, req.method ?? "GET", path);
50
+ }
29
51
  function bodyStream(req) {
30
52
  const iterator = req[Symbol.asyncIterator]();
31
53
  return new ReadableStream({
@@ -45,7 +67,20 @@ function bodyStream(req) {
45
67
  },
46
68
  });
47
69
  }
48
- function buildRequest(req, path, signal) {
70
+ /**
71
+ * True when req.body holds a body the host framework already read: a string or bytes (body-parser text and
72
+ * raw), or a plain object or array (express.json(), urlencoded forms). An instance of some other class is the
73
+ * host's own object, such as the body reader of an Encore raw request, and the stream is read instead.
74
+ */
75
+ function isParsedBody(body) {
76
+ if (body === undefined)
77
+ return false;
78
+ if (body === null || typeof body !== "object" || body instanceof Uint8Array || Array.isArray(body))
79
+ return true;
80
+ const proto = Object.getPrototypeOf(body);
81
+ return proto === Object.prototype || proto === null;
82
+ }
83
+ export function buildRequest(req, path, signal) {
49
84
  const host = [req.headers.host].flat()[0] ?? "localhost";
50
85
  const url = new URL(path, `${req.socket?.encrypted ? "https" : "http"}://${host}`);
51
86
  const headers = new Headers();
@@ -61,7 +96,12 @@ function buildRequest(req, path, signal) {
61
96
  const method = (req.method ?? "GET").toUpperCase();
62
97
  let body;
63
98
  if (method !== "GET" && method !== "HEAD") {
64
- if (req.body !== undefined) {
99
+ if (req.rawBody instanceof Uint8Array && isParsedBody(req.body)) {
100
+ // The host read the stream and kept the bytes: the app gets them unchanged, with their own content type.
101
+ headers.delete("content-length");
102
+ body = req.rawBody;
103
+ }
104
+ else if (isParsedBody(req.body)) {
65
105
  // The host framework already read the stream: use what it parsed.
66
106
  headers.delete("content-length");
67
107
  if (typeof req.body === "string" || req.body instanceof Uint8Array)
@@ -95,6 +135,12 @@ export function fromNodeRequest(req, options = {}) {
95
135
  const path = stripPrefix(raw, normalizePrefix(options.prefix)) ?? (raw.startsWith("/") ? raw : "/" + raw);
96
136
  return buildRequest(req, path, options.signal);
97
137
  }
138
+ const encoder = new TextEncoder();
139
+ /** UTF-8 length of a string, with Buffer where the runtime has it (Node.js, Bun). */
140
+ function byteLength(text) {
141
+ const B = globalThis.Buffer;
142
+ return B ? B.byteLength(text) : encoder.encode(text).byteLength;
143
+ }
98
144
  /** Write a Web Response (status, headers with every Set-Cookie, streamed body) to a Node response. */
99
145
  export async function writeResponse(res, response, method) {
100
146
  const outHeaders = {};
@@ -106,6 +152,13 @@ export async function writeResponse(res, response, method) {
106
152
  const cookies = response.headers.getSetCookie?.() ?? [];
107
153
  if (cookies.length)
108
154
  outHeaders["set-cookie"] = cookies;
155
+ // A JSON answer Inferlo built from a string: headers and body in one write, with its length.
156
+ const known = knownBodies.get(response);
157
+ if (known !== undefined && !response.bodyUsed && method !== "HEAD" && outHeaders["content-length"] === undefined && outHeaders["transfer-encoding"] === undefined) {
158
+ outHeaders["content-length"] = String(byteLength(known));
159
+ res.writeHead(response.status, outHeaders);
160
+ return void res.end(known);
161
+ }
109
162
  res.writeHead(response.status, outHeaders);
110
163
  if (!response.body || method === "HEAD")
111
164
  return void res.end();
@@ -125,17 +178,28 @@ export async function writeResponse(res, response, method) {
125
178
  */
126
179
  export function toNodeHandler(app, options = {}) {
127
180
  const prefix = normalizePrefix(options.prefix);
181
+ if (options.fallthrough !== undefined && typeof options.fallthrough !== "boolean") {
182
+ throw new InferloError("IL5019", "toNodeHandler fallthrough must be true or false.", { fix: "Use toNodeHandler(app, { fallthrough: true })." });
183
+ }
184
+ if (options.fallthrough && appAnswers(app, "GET", "/") === undefined) {
185
+ throw new InferloError("IL5019", "toNodeHandler(app, { fallthrough: true }) needs an app from createApp.", {
186
+ fix: "Pass the app returned by createApp, which knows its routes; a plain fetch handler cannot tell which requests to pass on.",
187
+ });
188
+ }
128
189
  return async (req, res, next) => {
129
190
  const path = stripPrefix(req.url ?? "/", prefix);
130
191
  if (path === null && next)
131
192
  return next();
193
+ if (options.fallthrough && next && path !== null && !answersPath(app, req.method ?? "GET", path))
194
+ return next();
132
195
  try {
133
196
  const controller = new AbortController();
134
197
  res.on("close", () => {
135
198
  if (!res.writableFinished)
136
199
  controller.abort();
137
200
  });
138
- const request = buildRequest(req, path ?? (req.url ?? "/"), controller.signal);
201
+ const request = buildRequest(req, path ?? (req.url ?? "/"));
202
+ clientSignals.set(request, controller.signal);
139
203
  const response = await app.fetch(request);
140
204
  await writeResponse(res, response, request.method);
141
205
  }
@@ -153,11 +217,48 @@ export async function serve(app, options = {}) {
153
217
  const g = globalThis;
154
218
  if (g.Bun) {
155
219
  const server = g.Bun.serve({ port, hostname, fetch: (req, bunServer) => app.fetch(req, bunServer) });
156
- return { port: server.port, close: async () => server.stop(true) };
220
+ // Wait until the open requests (and streamed answers) are done, then stop, closing the idle keep-alive
221
+ // connections too. Requests that arrive meanwhile get 503 IL2006 from app.close(). app.close() calls the force
222
+ // closer, stop(true), when its timeout is up. (stop() without true would leave keep-alive connections served on
223
+ // Bun 1.3, and a later stop(true) would then do nothing.)
224
+ let forced = false;
225
+ const result = {
226
+ port: server.port,
227
+ close: async () => {
228
+ while (server.pendingRequests > 0 && !forced)
229
+ await new Promise((resolve) => setTimeout(resolve, 20));
230
+ await server.stop(true);
231
+ },
232
+ };
233
+ forceClosers.set(result, () => {
234
+ forced = true;
235
+ void server.stop(true);
236
+ });
237
+ return result;
157
238
  }
158
239
  if (g.Deno) {
159
- const server = g.Deno.serve({ port, hostname }, (req, info) => app.fetch(req, info));
160
- return { port: server.addr.port, close: () => server.shutdown() };
240
+ // shutdown() waits for every open request and streamed answer, with no way to cut them off. So each answer
241
+ // with a body is passed through a stream that the force closer can abort when app.close()'s timeout is up.
242
+ const open = new Set();
243
+ const server = g.Deno.serve({ port, hostname, onListen() { } }, async (req, info) => {
244
+ const response = await app.fetch(req, info);
245
+ if (!response.body)
246
+ return response;
247
+ const abort = new AbortController();
248
+ const { readable, writable } = new TransformStream();
249
+ open.add(abort);
250
+ void response.body
251
+ .pipeTo(writable, { signal: abort.signal })
252
+ .catch(() => { })
253
+ .finally(() => open.delete(abort));
254
+ return new Response(readable, { status: response.status, statusText: response.statusText, headers: response.headers });
255
+ });
256
+ const result = { port: server.addr.port, close: () => server.shutdown() };
257
+ forceClosers.set(result, () => {
258
+ for (const abort of open)
259
+ abort.abort();
260
+ });
261
+ return result;
161
262
  }
162
263
  const http = await import("node:http");
163
264
  const handler = toNodeHandler(app);
@@ -177,6 +278,6 @@ export async function serve(app, options = {}) {
177
278
  forceClosers.set(result, () => server.closeAllConnections());
178
279
  return result;
179
280
  }
180
- /** Closes every open connection of a Node server from serve(), for app.close() after its timeout. */
281
+ /** Closes every open connection of a server from serve() (Node.js, Bun and Deno), for app.close() after its timeout. */
181
282
  export const forceClosers = new WeakMap();
182
283
  //# sourceMappingURL=serve.js.map
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,91 @@
1
+ /**
2
+ * Typed calls from one Inferlo service to another (microservices, part 1).
3
+ *
4
+ * import { getTicket, createTicket } from "../../tickets/src/routes/tickets.ts"; // the other service's routes
5
+ * const tickets = serviceClient({ baseUrl: settings.TICKETS_URL, token: settings.TICKETS_TOKEN });
6
+ *
7
+ * // in a route handler: input and answer are typed from the route
8
+ * const ticket = await tickets.call(getTicket, { params: { id }, ctx });
9
+ *
10
+ * The route definition gives the method, the path and the types; nothing is generated and nothing is global.
11
+ * Passing ctx forwards the request id (X-Request-Id) and cancels the call when the request is aborted. With
12
+ * tracing: { api: otel }, every attempt is a client span that sends its W3C traceparent, so the other service's
13
+ * spans join the caller's trace.
14
+ */
15
+ import { InferloError } from "./errors.ts";
16
+ import type { RouteDef } from "./route.ts";
17
+ import { type TracingConfig } from "./tracing.ts";
18
+ export interface ServiceClientOptions {
19
+ /** Where the other service is reached, with any path prefix it is mounted under: "http://tickets.internal" or "https://api.example.com/v1". */
20
+ baseUrl: string;
21
+ /** Sent as Authorization: Bearer <token> on every call. */
22
+ token?: string;
23
+ /** Headers sent on every call. */
24
+ headers?: Record<string, string>;
25
+ /** Longest wait for one attempt, in milliseconds. Default 10000. */
26
+ timeoutMs?: number;
27
+ /**
28
+ * Extra attempts for idempotent calls (GET, PUT, DELETE) that fail with a network error, a timeout, or a 502, 503
29
+ * or 504 answer, waiting 100 ms, 200 ms, 400 ms... in between. POST and PATCH are never retried. Default 0.
30
+ */
31
+ retries?: number;
32
+ /** The fetch to use. Default globalThis.fetch; pass otherApp.fetch to call an app in the same process (tests). */
33
+ fetch?: (request: Request) => Promise<Response>;
34
+ /**
35
+ * OpenTelemetry tracing: import * as otel from "@opentelemetry/api", then tracing: { api: otel }. Every attempt
36
+ * gets a client span ("GET /tickets/:id") under the active span and sends its trace context (traceparent), so a
37
+ * receiving Inferlo app with createApp({ tracing }) continues the same trace.
38
+ */
39
+ tracing?: TracingConfig;
40
+ }
41
+ /** A route made with route.<method>(...).handle(...): carries the types of its input and answer. */
42
+ export type TypedRoute = RouteDef & {
43
+ readonly $response: unknown;
44
+ readonly $input: {
45
+ body: unknown;
46
+ query: unknown;
47
+ params: unknown;
48
+ };
49
+ };
50
+ type ParamsInput<P> = keyof P extends never ? {
51
+ params?: P;
52
+ } : {
53
+ params: P;
54
+ };
55
+ type BodyInput<B> = undefined extends B ? {
56
+ body?: B;
57
+ } : {
58
+ body: B;
59
+ };
60
+ /** The request context to forward: pass the handler's ctx. */
61
+ export interface CallContext {
62
+ requestId: string;
63
+ signal: AbortSignal;
64
+ }
65
+ /** Options of one call, next to its params, query and body. */
66
+ export interface CallOptions {
67
+ /** The handler's context: forwards its request id and cancels the call when the request is aborted. */
68
+ ctx?: CallContext;
69
+ /** Cancels the call (in addition to ctx). */
70
+ signal?: AbortSignal;
71
+ /** Headers for this call. */
72
+ headers?: Record<string, string>;
73
+ }
74
+ export type CallInput<R extends TypedRoute> = ParamsInput<R["$input"]["params"]> & BodyInput<R["$input"]["body"]> & {
75
+ query?: Partial<R["$input"]["query"]>;
76
+ } & CallOptions;
77
+ /** The answer of a route: its JSON data, undefined for 204, or the Response itself for routes that return one (streams, files). */
78
+ export type CallResult<R extends TypedRoute> = R["$response"] extends Response ? Response : R["$response"] extends void | undefined ? undefined : R["$response"];
79
+ export interface ServiceClient {
80
+ /** Calls a route of the other service. Rejects with IL2008 when the call fails or answers with an error status. */
81
+ call<R extends TypedRoute>(route: R, ...input: {} extends CallInput<R> ? [input?: CallInput<R>] : [input: CallInput<R>]): Promise<CallResult<R>>;
82
+ }
83
+ /** Raised when a call to another service fails: remoteStatus is its HTTP status (0 when no answer came), remoteCode its error code. */
84
+ export declare class ServiceCallError extends InferloError {
85
+ readonly remoteStatus: number;
86
+ readonly remoteCode: string | undefined;
87
+ constructor(message: string, remoteStatus: number, remoteCode: string | undefined, fix: string, details: Record<string, unknown>);
88
+ }
89
+ /** A client for calls to another Inferlo service. See ServiceClientOptions. */
90
+ export declare function serviceClient(options: ServiceClientOptions): ServiceClient;
91
+ export {};