@browserlessapi/inferlo 0.3.0 → 0.5.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 (115) hide show
  1. package/README.md +18 -3
  2. package/dist/adonisjs.d.ts +33 -0
  3. package/dist/adonisjs.js +30 -0
  4. package/dist/adonisjs.js.map +1 -0
  5. package/dist/ai/index.d.ts +51 -2
  6. package/dist/ai/index.js +186 -37
  7. package/dist/ai/index.js.map +1 -1
  8. package/dist/ai/providers.d.ts +56 -3
  9. package/dist/ai/providers.js +102 -11
  10. package/dist/ai/providers.js.map +1 -1
  11. package/dist/ai/public.d.ts +20 -0
  12. package/dist/ai/public.js +17 -0
  13. package/dist/ai/public.js.map +1 -0
  14. package/dist/ai/tool.d.ts +47 -0
  15. package/dist/ai/tool.js +85 -0
  16. package/dist/ai/tool.js.map +1 -0
  17. package/dist/ai-client.d.ts +3 -0
  18. package/dist/ai-client.js +8 -0
  19. package/dist/ai-client.js.map +1 -0
  20. package/dist/app.d.ts +102 -2
  21. package/dist/app.js +509 -111
  22. package/dist/app.js.map +1 -1
  23. package/dist/cli/main.js +76 -4
  24. package/dist/cli/main.js.map +1 -1
  25. package/dist/client-ip.d.ts +7 -0
  26. package/dist/client-ip.js +27 -0
  27. package/dist/client-ip.js.map +1 -0
  28. package/dist/config.d.ts +16 -0
  29. package/dist/config.js +101 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/cron.d.ts +17 -0
  32. package/dist/cron.js +143 -0
  33. package/dist/cron.js.map +1 -0
  34. package/dist/db/migrate.d.ts +6 -3
  35. package/dist/db/migrate.js +126 -10
  36. package/dist/db/migrate.js.map +1 -1
  37. package/dist/db/table.d.ts +83 -7
  38. package/dist/db/table.js +245 -9
  39. package/dist/db/table.js.map +1 -1
  40. package/dist/deprecation.d.ts +23 -0
  41. package/dist/deprecation.js +70 -0
  42. package/dist/deprecation.js.map +1 -0
  43. package/dist/describe.d.ts +45 -5
  44. package/dist/describe.js +19 -2
  45. package/dist/describe.js.map +1 -1
  46. package/dist/elysia.d.ts +49 -0
  47. package/dist/elysia.js +10 -0
  48. package/dist/elysia.js.map +1 -0
  49. package/dist/env.d.ts +2 -0
  50. package/dist/env.js +5 -4
  51. package/dist/env.js.map +1 -1
  52. package/dist/express.d.ts +38 -0
  53. package/dist/express.js +18 -0
  54. package/dist/express.js.map +1 -0
  55. package/dist/fastify.d.ts +44 -0
  56. package/dist/fastify.js +47 -0
  57. package/dist/fastify.js.map +1 -0
  58. package/dist/hono.d.ts +26 -0
  59. package/dist/hono.js +10 -0
  60. package/dist/hono.js.map +1 -0
  61. package/dist/index.d.ts +20 -6
  62. package/dist/index.js +7 -3
  63. package/dist/index.js.map +1 -1
  64. package/dist/jobs.d.ts +181 -0
  65. package/dist/jobs.js +391 -0
  66. package/dist/jobs.js.map +1 -0
  67. package/dist/logging.d.ts +23 -0
  68. package/dist/logging.js +87 -0
  69. package/dist/logging.js.map +1 -0
  70. package/dist/mcp.d.ts +4 -6
  71. package/dist/mcp.js +53 -38
  72. package/dist/mcp.js.map +1 -1
  73. package/dist/metrics.d.ts +48 -0
  74. package/dist/metrics.js +184 -0
  75. package/dist/metrics.js.map +1 -0
  76. package/dist/nestjs.d.ts +43 -0
  77. package/dist/nestjs.js +47 -0
  78. package/dist/nestjs.js.map +1 -0
  79. package/dist/openapi.d.ts +0 -1
  80. package/dist/openapi.js +7 -0
  81. package/dist/openapi.js.map +1 -1
  82. package/dist/rate-limit.d.ts +45 -0
  83. package/dist/rate-limit.js +93 -0
  84. package/dist/rate-limit.js.map +1 -0
  85. package/dist/redact.d.ts +19 -0
  86. package/dist/redact.js +150 -0
  87. package/dist/redact.js.map +1 -0
  88. package/dist/route.d.ts +24 -0
  89. package/dist/route.js.map +1 -1
  90. package/dist/schema.d.ts +10 -2
  91. package/dist/schema.js +13 -1
  92. package/dist/schema.js.map +1 -1
  93. package/dist/secure-headers.d.ts +8 -0
  94. package/dist/secure-headers.js +61 -0
  95. package/dist/secure-headers.js.map +1 -0
  96. package/dist/serve.d.ts +53 -1
  97. package/dist/serve.js +158 -59
  98. package/dist/serve.js.map +1 -1
  99. package/dist/trpc.d.ts +57 -0
  100. package/dist/trpc.js +142 -0
  101. package/dist/trpc.js.map +1 -0
  102. package/dist/tsed.d.ts +32 -0
  103. package/dist/tsed.js +13 -0
  104. package/dist/tsed.js.map +1 -0
  105. package/llms.txt +10 -4
  106. package/package.json +57 -3
  107. package/templates/app/AGENTS.md +14 -8
  108. package/templates/app/README.md +2 -0
  109. package/templates/app/_env.example +2 -0
  110. package/templates/app/package.json.tpl +1 -0
  111. package/templates/app/src/app.ts +15 -8
  112. package/templates/app/src/config.ts +17 -0
  113. package/templates/app/src/routes/notes.ts +1 -1
  114. package/templates/app/src/server.ts +13 -3
  115. package/templates/app/src/routes/health.ts +0 -3
package/dist/serve.js CHANGED
@@ -1,83 +1,182 @@
1
- export async function serve(app, options = {}) {
2
- const port = options.port ?? 3000;
3
- const hostname = options.hostname ?? "0.0.0.0";
4
- const g = globalThis;
5
- if (g.Bun) {
6
- const server = g.Bun.serve({ port, hostname, fetch: (req) => app.fetch(req) });
7
- return { port: server.port, close: async () => server.stop(true) };
1
+ /**
2
+ * Run an app's fetch handler as an HTTP server (Bun, Deno or Node), or inside another Node framework:
3
+ *
4
+ * await serve(app, { port: 3000 })
5
+ * expressApp.use("/ai", toNodeHandler(app)) // Express strips the mount path
6
+ * http.createServer(toNodeHandler(app, { prefix: "/ai" })) // or strip it with prefix
7
+ */
8
+ import { createRedactor } from "./redact.js";
9
+ import { clientIps } from "./client-ip.js";
10
+ function normalizePrefix(prefix) {
11
+ if (!prefix || prefix === "/")
12
+ return "";
13
+ return ("/" + prefix.replace(/^\/+|\/+$/g, "")).replace(/\/+/g, "/");
14
+ }
15
+ /** 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) {
17
+ const url = rawUrl.startsWith("/") ? rawUrl : "/" + rawUrl;
18
+ if (!prefix)
19
+ return url;
20
+ if (!url.startsWith(prefix))
21
+ return null;
22
+ const rest = url.slice(prefix.length);
23
+ if (rest === "" || rest[0] === "?")
24
+ return "/" + rest;
25
+ if (rest[0] !== "/")
26
+ return null;
27
+ return rest;
28
+ }
29
+ function bodyStream(req) {
30
+ const iterator = req[Symbol.asyncIterator]();
31
+ return new ReadableStream({
32
+ async pull(controller) {
33
+ try {
34
+ const { value, done } = await iterator.next();
35
+ if (done)
36
+ return controller.close();
37
+ controller.enqueue(typeof value === "string" ? new TextEncoder().encode(value) : value);
38
+ }
39
+ catch (error) {
40
+ controller.error(error);
41
+ }
42
+ },
43
+ async cancel() {
44
+ await iterator.return?.();
45
+ },
46
+ });
47
+ }
48
+ function buildRequest(req, path, signal) {
49
+ const host = [req.headers.host].flat()[0] ?? "localhost";
50
+ const url = new URL(path, `${req.socket?.encrypted ? "https" : "http"}://${host}`);
51
+ const headers = new Headers();
52
+ for (const [k, v] of Object.entries(req.headers)) {
53
+ if (v === undefined || k.startsWith(":"))
54
+ continue;
55
+ if (Array.isArray(v))
56
+ for (const item of v)
57
+ headers.append(k, item);
58
+ else
59
+ headers.set(k, v);
8
60
  }
9
- if (g.Deno) {
10
- const server = g.Deno.serve({ port, hostname }, (req) => app.fetch(req));
11
- return { port: server.addr.port, close: () => server.shutdown() };
61
+ const method = (req.method ?? "GET").toUpperCase();
62
+ let body;
63
+ if (method !== "GET" && method !== "HEAD") {
64
+ if (req.body !== undefined) {
65
+ // The host framework already read the stream: use what it parsed.
66
+ headers.delete("content-length");
67
+ if (typeof req.body === "string" || req.body instanceof Uint8Array)
68
+ body = req.body;
69
+ else {
70
+ body = JSON.stringify(req.body);
71
+ headers.set("content-type", "application/json");
72
+ }
73
+ }
74
+ else
75
+ body = bodyStream(req);
12
76
  }
13
- const http = await import("node:http");
14
- const { Readable } = await import("node:stream");
15
- const server = http.createServer(async (req, res) => {
77
+ const request = new Request(url, {
78
+ method,
79
+ headers,
80
+ body,
81
+ signal,
82
+ // @ts-expect-error duplex is required by Node for streaming bodies
83
+ duplex: "half",
84
+ });
85
+ if (req.socket?.remoteAddress)
86
+ clientIps.set(request, req.socket.remoteAddress);
87
+ return request;
88
+ }
89
+ /**
90
+ * Convert a Node.js request (node:http, Express, Fastify's request.raw, NestJS) to a Web Request.
91
+ * With prefix, the prefix is removed from the path; a request outside it keeps its path.
92
+ */
93
+ export function fromNodeRequest(req, options = {}) {
94
+ const raw = req.url ?? "/";
95
+ const path = stripPrefix(raw, normalizePrefix(options.prefix)) ?? (raw.startsWith("/") ? raw : "/" + raw);
96
+ return buildRequest(req, path, options.signal);
97
+ }
98
+ /** Write a Web Response (status, headers with every Set-Cookie, streamed body) to a Node response. */
99
+ export async function writeResponse(res, response, method) {
100
+ const outHeaders = {};
101
+ response.headers.forEach((value, key) => {
102
+ if (key === "set-cookie")
103
+ return;
104
+ outHeaders[key] = value;
105
+ });
106
+ const cookies = response.headers.getSetCookie?.() ?? [];
107
+ if (cookies.length)
108
+ outHeaders["set-cookie"] = cookies;
109
+ res.writeHead(response.status, outHeaders);
110
+ if (!response.body || method === "HEAD")
111
+ return void res.end();
112
+ const reader = response.body.getReader();
113
+ while (true) {
114
+ const { value, done } = await reader.read();
115
+ if (done)
116
+ break;
117
+ if (!res.write(value))
118
+ await new Promise((r) => res.once("drain", () => r()));
119
+ }
120
+ res.end();
121
+ }
122
+ /**
123
+ * A (req, res, next) handler that runs a whole Inferlo app (routes, OpenAPI, MCP) inside node:http,
124
+ * Express, Fastify, NestJS or any framework that hands out Node requests and responses.
125
+ */
126
+ export function toNodeHandler(app, options = {}) {
127
+ const prefix = normalizePrefix(options.prefix);
128
+ return async (req, res, next) => {
129
+ const path = stripPrefix(req.url ?? "/", prefix);
130
+ if (path === null && next)
131
+ return next();
16
132
  try {
17
- const host = req.headers.host ?? `localhost:${port}`;
18
- const url = new URL(req.url ?? "/", `http://${host}`);
19
- const headers = new Headers();
20
- for (const [k, v] of Object.entries(req.headers)) {
21
- if (v === undefined)
22
- continue;
23
- if (Array.isArray(v))
24
- for (const item of v)
25
- headers.append(k, item);
26
- else
27
- headers.set(k, v);
28
- }
29
- const hasBody = req.method !== "GET" && req.method !== "HEAD";
30
133
  const controller = new AbortController();
31
134
  res.on("close", () => {
32
135
  if (!res.writableFinished)
33
136
  controller.abort();
34
137
  });
35
- const request = new Request(url, {
36
- method: req.method,
37
- headers,
38
- body: hasBody ? Readable.toWeb(req) : undefined,
39
- signal: controller.signal,
40
- // @ts-expect-error duplex is required by Node for streaming bodies
41
- duplex: "half",
42
- });
138
+ const request = buildRequest(req, path ?? (req.url ?? "/"), controller.signal);
43
139
  const response = await app.fetch(request);
44
- const outHeaders = {};
45
- response.headers.forEach((value, key) => {
46
- if (key === "set-cookie")
47
- return;
48
- outHeaders[key] = value;
49
- });
50
- const cookies = response.headers.getSetCookie?.() ?? [];
51
- if (cookies.length)
52
- outHeaders["set-cookie"] = cookies;
53
- res.writeHead(response.status, outHeaders);
54
- if (!response.body || req.method === "HEAD")
55
- return void res.end();
56
- const reader = response.body.getReader();
57
- while (true) {
58
- const { value, done } = await reader.read();
59
- if (done)
60
- break;
61
- if (!res.write(value))
62
- await new Promise((r) => res.once("drain", r));
63
- }
64
- res.end();
140
+ await writeResponse(res, response, request.method);
65
141
  }
66
142
  catch (error) {
67
- console.error("[inferlo] server error", error);
143
+ console.error("[inferlo] server error", createRedactor([]).value(error));
68
144
  if (!res.headersSent)
69
145
  res.writeHead(500, { "content-type": "application/json" });
70
146
  res.end(JSON.stringify({ error: { code: "IL9000", message: "Internal server error" } }));
71
147
  }
72
- });
148
+ };
149
+ }
150
+ export async function serve(app, options = {}) {
151
+ const port = options.port ?? 3000;
152
+ const hostname = options.hostname ?? "0.0.0.0";
153
+ const g = globalThis;
154
+ if (g.Bun) {
155
+ const server = g.Bun.serve({ port, hostname, fetch: (req, bunServer) => app.fetch(req, bunServer) });
156
+ return { port: server.port, close: async () => server.stop(true) };
157
+ }
158
+ 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() };
161
+ }
162
+ const http = await import("node:http");
163
+ const handler = toNodeHandler(app);
164
+ const server = http.createServer((req, res) => void handler(req, res));
73
165
  await new Promise((resolve, reject) => {
74
166
  server.once("error", reject);
75
167
  server.listen(port, hostname, () => resolve());
76
168
  });
77
169
  const address = server.address();
78
- return {
170
+ const result = {
79
171
  port: typeof address === "object" && address ? address.port : port,
80
- close: () => new Promise((resolve) => server.close(() => resolve())),
172
+ close: () => new Promise((resolve) => {
173
+ server.close(() => resolve());
174
+ server.closeIdleConnections();
175
+ }),
81
176
  };
177
+ forceClosers.set(result, () => server.closeAllConnections());
178
+ return result;
82
179
  }
180
+ /** Closes every open connection of a Node server from serve(), for app.close() after its timeout. */
181
+ export const forceClosers = new WeakMap();
83
182
  //# 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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"}
package/dist/trpc.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Inferlo inside tRPC (11.0 and later), without a runtime dependency on tRPC:
3
+ *
4
+ * import { initTRPC } from "@trpc/server";
5
+ * import { createAI, anthropic, t } from "@browserlessapi/inferlo/ai";
6
+ * import { inferloAI, trpcTools } from "@browserlessapi/inferlo/trpc";
7
+ *
8
+ * const trpc = initTRPC.context<Context>().create();
9
+ * const aiProcedure = trpc.procedure.use(inferloAI(ai)); // ctx.ai in every procedure, typed
10
+ *
11
+ * export const appRouter = trpc.router({
12
+ * summarize: aiProcedure.input(t.object({ text: t.string() })) // Inferlo schemas work as tRPC inputs
13
+ * .mutation(async ({ ctx, input }) => (await ctx.ai.generate({ model: "fast", prompt: input.text })).text),
14
+ * chat: aiProcedure.input(t.object({ prompt: t.string() }))
15
+ * .subscription(async function* ({ ctx, input, signal }) {
16
+ * yield* ctx.ai.stream({ model: "fast", prompt: input.prompt, signal }); // text chunks
17
+ * }),
18
+ * });
19
+ *
20
+ * // Procedures as AI tools, called as the current user through a caller:
21
+ * const tools = trpcTools(appRouter, {
22
+ * caller: appRouter.createCaller(ctx),
23
+ * procedures: { "tickets.create": "Create a support ticket for the current user." },
24
+ * });
25
+ * await ai.generate({ model: "fast", prompt, tools, maxSteps: 5 });
26
+ *
27
+ * This entry point never loads the router, database or jobs code.
28
+ */
29
+ import type { MiddlewareFunction } from "@trpc/server/unstable-core-do-not-import";
30
+ import type { AI } from "./ai/index.ts";
31
+ import { type ToolDef } from "./ai/tool.ts";
32
+ import { ObjectSchema, type Shape } from "./schema.ts";
33
+ /**
34
+ * A tRPC middleware that puts an AI client (createAI(...) or an Inferlo app's app.ai) on the context:
35
+ * const aiProcedure = trpc.procedure.use(inferloAI(ai)), then ctx.ai in its procedures.
36
+ */
37
+ export declare function inferloAI(ai: AI): MiddlewareFunction<any, any, any, {
38
+ ai: AI;
39
+ }, any>;
40
+ /** A procedure offered as a tool: its description, or { description, input } for procedures whose input is not a t.object. */
41
+ export type TrpcToolProcedure = string | {
42
+ description: string;
43
+ input: ObjectSchema<Shape>;
44
+ };
45
+ export interface TrpcToolsOptions {
46
+ /** Runs the procedures: appRouter.createCaller(ctx) or createCallerFactory(appRouter)(ctx), with the current user's context. */
47
+ caller: object;
48
+ /** Procedure paths ("tickets.create") and what each does. The model reads the description to decide when to call it. */
49
+ procedures: Record<string, TrpcToolProcedure>;
50
+ }
51
+ /**
52
+ * Makes AI tools from tRPC queries and mutations, for ai.generate({ tools }). Each tool is named after its
53
+ * path with "." replaced by "_" ("tickets.create" becomes tickets_create), takes the procedure's t.object input,
54
+ * and runs through the caller, so the procedure's middleware, auth and validation apply. TRPCErrors such as
55
+ * NOT_FOUND or FORBIDDEN go back to the model as failed tool results; server errors reject ai.generate.
56
+ */
57
+ export declare function trpcTools(router: object, options: TrpcToolsOptions): ToolDef[];
package/dist/trpc.js ADDED
@@ -0,0 +1,142 @@
1
+ import { tool } from "./ai/tool.js";
2
+ import { assertAIClient } from "./ai-client.js";
3
+ import { httpError, InferloError } from "./errors.js";
4
+ import { ObjectSchema, t } from "./schema.js";
5
+ /**
6
+ * A tRPC middleware that puts an AI client (createAI(...) or an Inferlo app's app.ai) on the context:
7
+ * const aiProcedure = trpc.procedure.use(inferloAI(ai)), then ctx.ai in its procedures.
8
+ */
9
+ export function inferloAI(ai) {
10
+ assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: trpc.procedure.use(inferloAI(ai)).');
11
+ return ({ next }) => next({ ctx: { ai } });
12
+ }
13
+ /** HTTP status of each tRPC error code a model can correct (the others reject ai.generate). */
14
+ const CLIENT_ERRORS = {
15
+ BAD_REQUEST: 400,
16
+ PARSE_ERROR: 400,
17
+ UNAUTHORIZED: 401,
18
+ PAYMENT_REQUIRED: 402,
19
+ FORBIDDEN: 403,
20
+ NOT_FOUND: 404,
21
+ METHOD_NOT_SUPPORTED: 405,
22
+ TIMEOUT: 408,
23
+ CONFLICT: 409,
24
+ PRECONDITION_FAILED: 412,
25
+ PAYLOAD_TOO_LARGE: 413,
26
+ UNSUPPORTED_MEDIA_TYPE: 415,
27
+ UNPROCESSABLE_CONTENT: 422,
28
+ TOO_MANY_REQUESTS: 429,
29
+ CLIENT_CLOSED_REQUEST: 499,
30
+ };
31
+ /**
32
+ * Makes AI tools from tRPC queries and mutations, for ai.generate({ tools }). Each tool is named after its
33
+ * path with "." replaced by "_" ("tickets.create" becomes tickets_create), takes the procedure's t.object input,
34
+ * and runs through the caller, so the procedure's middleware, auth and validation apply. TRPCErrors such as
35
+ * NOT_FOUND or FORBIDDEN go back to the model as failed tool results; server errors reject ai.generate.
36
+ */
37
+ export function trpcTools(router, options) {
38
+ const problems = [];
39
+ const procedures = router?._def?.procedures;
40
+ if (!procedures || typeof procedures !== "object")
41
+ problems.push("the first argument must be a tRPC router (trpc.router({ ... }))");
42
+ if (!options || typeof options !== "object") {
43
+ problems.push("options must be { caller, procedures }");
44
+ throw invalid(problems);
45
+ }
46
+ if (!options.caller || (typeof options.caller !== "object" && typeof options.caller !== "function"))
47
+ problems.push("caller must be appRouter.createCaller(ctx)");
48
+ const entries = options.procedures && typeof options.procedures === "object" ? Object.entries(options.procedures) : [];
49
+ if (!entries.length)
50
+ problems.push('procedures must name at least one procedure: { "tickets.create": "Create a support ticket." }');
51
+ if (problems.length)
52
+ throw invalid(problems);
53
+ const tools = [];
54
+ for (const [path, entry] of entries) {
55
+ const procedure = procedures[path];
56
+ if (!procedure) {
57
+ const known = Object.keys(procedures);
58
+ problems.push(`"${path}" is not a procedure of this router${known.length ? ` (procedures: ${known.join(", ")})` : ""}`);
59
+ continue;
60
+ }
61
+ const type = procedure._def?.type;
62
+ if (type !== "query" && type !== "mutation") {
63
+ problems.push(`"${path}" is a ${type ?? "unknown"} procedure; only queries and mutations can be tools`);
64
+ continue;
65
+ }
66
+ const description = typeof entry === "string" ? entry : entry?.description;
67
+ if (typeof description !== "string" || !description.trim()) {
68
+ problems.push(`"${path}" needs a description of what it does`);
69
+ continue;
70
+ }
71
+ const inputs = procedure._def?.inputs ?? [];
72
+ let input;
73
+ if (typeof entry === "object" && entry !== null && entry.input !== undefined) {
74
+ if (!(entry.input instanceof ObjectSchema)) {
75
+ problems.push(`"${path}" input must be a t.object({ ... }) schema`);
76
+ continue;
77
+ }
78
+ input = entry.input;
79
+ }
80
+ else if (inputs.every((parser) => parser instanceof ObjectSchema)) {
81
+ const shape = {};
82
+ for (const parser of inputs)
83
+ Object.assign(shape, parser.shape);
84
+ input = t.object(shape);
85
+ }
86
+ else {
87
+ problems.push(`"${path}" has an input that is not a t.object schema from Inferlo; pass { description, input: t.object({ ... }) } for it`);
88
+ continue;
89
+ }
90
+ const name = path.replace(/\./g, "_");
91
+ const hasInput = inputs.length > 0;
92
+ const call = resolve(options.caller, path);
93
+ try {
94
+ tools.push(tool(name, { description, input }).handle(async ({ input: args }) => {
95
+ try {
96
+ return await call(hasInput ? args : undefined);
97
+ }
98
+ catch (error) {
99
+ throw toToolError(error);
100
+ }
101
+ }));
102
+ }
103
+ catch (error) {
104
+ if (error instanceof InferloError)
105
+ problems.push(`"${path}" does not make a valid tool name (${name})`);
106
+ else
107
+ throw error;
108
+ }
109
+ }
110
+ if (problems.length)
111
+ throw invalid(problems);
112
+ return tools;
113
+ }
114
+ /** caller.tickets.create, looked up when the tool runs (callers are proxies). */
115
+ function resolve(caller, path) {
116
+ return async (input) => {
117
+ let target = caller;
118
+ for (const key of path.split("."))
119
+ target = target[key];
120
+ return target(input);
121
+ };
122
+ }
123
+ /** TRPCErrors a model can correct become httpErrors (failed tool results); anything else is rethrown. */
124
+ function toToolError(error) {
125
+ if (error instanceof InferloError)
126
+ return error;
127
+ const candidate = error;
128
+ if (!candidate || typeof candidate !== "object" || candidate.name !== "TRPCError" || typeof candidate.code !== "string")
129
+ return error;
130
+ if (candidate.cause instanceof InferloError && candidate.cause.status < 500)
131
+ return candidate.cause;
132
+ const status = CLIENT_ERRORS[candidate.code];
133
+ if (status === undefined)
134
+ return error;
135
+ return httpError(status, typeof candidate.message === "string" ? candidate.message : candidate.code, { code: candidate.code });
136
+ }
137
+ function invalid(problems) {
138
+ return new InferloError("IL5017", `Invalid trpcTools() options: ${problems.join("; ")}.`, {
139
+ fix: 'Use trpcTools(appRouter, { caller: appRouter.createCaller(ctx), procedures: { "tickets.create": "Create a support ticket." } }) with queries and mutations whose input is a t.object({ ... }).',
140
+ });
141
+ }
142
+ //# sourceMappingURL=trpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trpc.js","sourceRoot":"","sources":["../src/trpc.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,IAAI,EAAgB,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,CAAC,EAAc,MAAM,aAAa,CAAC;AAE1D;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EAAM;IAC9B,cAAc,CAAC,EAAE,EAAE,yIAAyI,CAAC,CAAC;IAC9J,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAiBD,+FAA+F;AAC/F,MAAM,aAAa,GAA2B;IAC5C,WAAW,EAAE,GAAG;IAChB,WAAW,EAAE,GAAG;IAChB,YAAY,EAAE,GAAG;IACjB,gBAAgB,EAAE,GAAG;IACrB,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,GAAG;IACd,oBAAoB,EAAE,GAAG;IACzB,OAAO,EAAE,GAAG;IACZ,QAAQ,EAAE,GAAG;IACb,mBAAmB,EAAE,GAAG;IACxB,iBAAiB,EAAE,GAAG;IACtB,sBAAsB,EAAE,GAAG;IAC3B,qBAAqB,EAAE,GAAG;IAC1B,iBAAiB,EAAE,GAAG;IACtB,qBAAqB,EAAE,GAAG;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,OAAyB;IACjE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAI,MAAiC,EAAE,IAAI,EAAE,UAAU,CAAC;IACxE,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ;QAAE,QAAQ,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;IACpI,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,QAAQ,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACxD,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IACjK,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvH,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,QAAQ,CAAC,IAAI,CAAC,+FAA+F,CAAC,CAAC;IACpI,IAAI,QAAQ,CAAC,MAAM;QAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE7C,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,UAAW,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,UAAW,CAAC,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,sCAAsC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxH,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC;QAClC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YAC5C,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,UAAU,IAAI,IAAI,SAAS,qDAAqD,CAAC,CAAC;YACxG,SAAS;QACX,CAAC;QACD,MAAM,WAAW,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,CAAC;QAC3E,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,uCAAuC,CAAC,CAAC;YAC/D,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC;QAC5C,IAAI,KAA0B,CAAC;QAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7E,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY,YAAY,CAAC,EAAE,CAAC;gBAC3C,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,4CAA4C,CAAC,CAAC;gBACpE,SAAS;YACX,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACtB,CAAC;aAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,YAAY,YAAY,CAAC,EAAE,CAAC;YACpE,MAAM,KAAK,GAAU,EAAE,CAAC;YACxB,KAAK,MAAM,MAAM,IAAI,MAA+B;gBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YACzF,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,kHAAkH,CAAC,CAAC;YAC1I,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CACR,IAAI,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE;gBAClE,IAAI,CAAC;oBACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBACjD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC,CAAC,CACH,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,YAAY;gBAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,sCAAsC,IAAI,GAAG,CAAC,CAAC;;gBACnG,MAAM,KAAK,CAAC;QACnB,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM;QAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iFAAiF;AACjF,SAAS,OAAO,CAAC,MAAc,EAAE,IAAY;IAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,EAAE;QACrB,IAAI,MAAM,GAAQ,MAAM,CAAC;QACzB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC,CAAC;AACJ,CAAC;AAED,yGAAyG;AACzG,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,YAAY,YAAY;QAAE,OAAO,KAAK,CAAC;IAChD,MAAM,SAAS,GAAG,KAAsF,CAAC;IACzG,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtI,IAAI,SAAS,CAAC,KAAK,YAAY,YAAY,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC;IACpG,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,SAAS,CAAC,MAAM,EAAE,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;AACjI,CAAC;AAED,SAAS,OAAO,CAAC,QAAkB;IACjC,OAAO,IAAI,YAAY,CAAC,QAAQ,EAAE,gCAAgC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;QACxF,GAAG,EAAE,gMAAgM;KACtM,CAAC,CAAC;AACL,CAAC"}
package/dist/tsed.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Inferlo inside Ts.ED (8 and later), without a runtime dependency on Ts.ED:
3
+ *
4
+ * import { InferloAI, inferloAI } from "@browserlessapi/inferlo/tsed";
5
+ *
6
+ * @Configuration({ imports: [inferloAI(ai)] }) // or { token: InferloAI, useFactory: () => createAI(...) }
7
+ * export class Server {}
8
+ *
9
+ * @Injectable()
10
+ * export class TicketsService {
11
+ * constructor(private readonly ai: InferloAI) {} // or: ai = inject(InferloAI)
12
+ * }
13
+ *
14
+ * A whole Inferlo app mounts with app.use("/api", toNodeHandler(inferloApp)) in $beforeRoutesInit on the
15
+ * Express platform. This entry point never loads the router, database or jobs code.
16
+ */
17
+ import type { AI } from "./ai/index.ts";
18
+ /**
19
+ * Injection token and type of the AI client in Ts.ED: constructor(private readonly ai: InferloAI) {},
20
+ * @Inject(InferloAI) ai: InferloAI, or inject(InferloAI). It has every method of AI.
21
+ */
22
+ export declare abstract class InferloAI {
23
+ }
24
+ export interface InferloAI extends AI {
25
+ }
26
+ /** A Ts.ED provider that registers an AI client as InferloAI, for @Configuration({ imports }) and PlatformTest.create({ imports }). */
27
+ export interface InferloAIProvider {
28
+ token: typeof InferloAI;
29
+ use: AI;
30
+ }
31
+ /** Registers an AI client (createAI(...) or an Inferlo app's app.ai) for injection: @Configuration({ imports: [inferloAI(ai)] }). */
32
+ export declare function inferloAI(ai: AI): InferloAIProvider;
package/dist/tsed.js ADDED
@@ -0,0 +1,13 @@
1
+ import { assertAIClient } from "./ai-client.js";
2
+ /**
3
+ * Injection token and type of the AI client in Ts.ED: constructor(private readonly ai: InferloAI) {},
4
+ * @Inject(InferloAI) ai: InferloAI, or inject(InferloAI). It has every method of AI.
5
+ */
6
+ export class InferloAI {
7
+ }
8
+ /** Registers an AI client (createAI(...) or an Inferlo app's app.ai) for injection: @Configuration({ imports: [inferloAI(ai)] }). */
9
+ export function inferloAI(ai) {
10
+ assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: @Configuration({ imports: [inferloAI(ai)] }).');
11
+ return { token: InferloAI, use: ai };
12
+ }
13
+ //# sourceMappingURL=tsed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsed.js","sourceRoot":"","sources":["../src/tsed.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAgB,SAAS;CAAG;AASlC,qIAAqI;AACrI,MAAM,UAAU,SAAS,CAAC,EAAM;IAC9B,cAAc,CAAC,EAAE,EAAE,oJAAoJ,CAAC,CAAC;IACzK,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;AACvC,CAAC"}
package/llms.txt CHANGED
@@ -1,22 +1,28 @@
1
1
  # Inferlo
2
2
 
3
- > Inferlo is a batteries-included TypeScript backend framework with built-in AI model support (Anthropic, OpenAI, Gemini, Ollama and OpenAI-compatible providers with fallback, structured output, streaming, embeddings and usage tracking), plus routes, schemas, database (PostgreSQL and in-memory), auth, an MCP server and OpenAPI, with zero runtime dependencies. Runs on Node.js 22.18+, Bun, Deno and edge runtimes. Package: @browserlessapi/inferlo.
3
+ > Inferlo is an enterprise-ready, batteries-included TypeScript backend framework (routes, schemas, database with PostgreSQL and migrations, auth, rate limiting, background jobs, cron, OpenAPI) with AI models built in (Anthropic, OpenAI, Gemini, Ollama and OpenAI-compatible providers with fallback, structured output, streaming, tool calling, embeddings and usage tracking) and an MCP server, with zero runtime dependencies. Runs on Node.js 22.18+, Bun, Deno and edge runtimes. Package: @browserlessapi/inferlo.
4
4
 
5
5
  Key rules: routes are private unless `auth: "public"`; validate input with `t.*` schemas; use `db.<table>` methods (never string-built SQL); every model has id, createdAt, updatedAt automatically; relative imports end in `.ts`; run `inferlo describe --json` to see an app and `inferlo check --json` after changes.
6
6
 
7
7
  ## Docs
8
8
 
9
9
  - [Getting started](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/getting-started.md): install, project layout, minimal app
10
+ - [Configuration](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/config.md): config({ schema, secrets, environments }), typed settings validated at startup, secrets redacted
10
11
  - [Schemas](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/schema.md): t.* types, modifiers, validation, JSON Schema
11
12
  - [Models and database](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/database.md): model(), queries, adapters, db push, transactions
12
- - [Routes](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/routes.md): options, handler context, return values, errors, middleware
13
+ - [Routes](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/routes.md): options, handler context, return values, errors, middleware, deprecated routes (Deprecation and Sunset headers)
13
14
  - [Auth](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/auth.md): auth.resolve, roles, bearer API keys, passwords
14
15
  - [AI](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/ai.md): providers, aliases and fallback, structured output, streaming, embeddings, usage and budgets
15
16
  - [MCP, OpenAPI and describe](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/mcp.md)
16
17
  - [CLI](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/cli.md)
17
18
  - [Testing](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/testing.md)
18
- - [Deployment](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/deploy.md)
19
- - [Error codes](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/errors.md): every KRxxxx code with its fix
19
+ - [Use with other frameworks](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/integrations.md): the AI module in any framework, toNodeHandler and fromNodeRequest to mount an app in Express, Fastify, NestJS or node:http, inferloAI() middleware for Hono (c.var.ai), Express (req.ai), Elysia (ai) and tRPC (ctx.ai), inferloPlugin for Fastify (request.ai, app under a prefix), InferloModule for NestJS, an AdonisJS provider (ctx.ai, config/ai.ts), InferloAI and inferloAI() for Ts.ED injection, trpcTools() for tRPC procedures as AI tools
20
+ - [Migrate from Express](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/migrate-from-express.md): move an Express app to Inferlo route by route at the same URLs, with a concept mapping table, shared auth and tests
21
+ - [Security](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/security.md): secure defaults and security headers (createApp({ secureHeaders }))
22
+ - [Observability](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/observability.md): request ids (X-Request-Id, ctx.requestId), request logs and JSON logs with createApp({ logging: { format: "json", requests: true } }), your own logger, Prometheus metrics with createApp({ metrics: { token } })
23
+ - [Deployment](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/deploy.md): Node, Bun, Deno, edge, health and readiness endpoints, graceful shutdown with app.close()
24
+ - [Support policy](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/support.md): semantic versioning, which versions get fixes, LTS after 1.0, deprecation rules and supported runtime, database and framework versions
25
+ - [Error codes](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/errors.md): every ILxxxx code with its fix
20
26
 
21
27
  ## Full text
22
28