@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
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Inferlo inside ElysiaJS (1.0 and later), without a runtime dependency on Elysia:
3
+ *
4
+ * import { Elysia } from "elysia";
5
+ * import { createAI, anthropic } from "@browserlessapi/inferlo/ai";
6
+ * import { inferloAI } from "@browserlessapi/inferlo/elysia";
7
+ *
8
+ * const server = new Elysia()
9
+ * .use(inferloAI(ai)) // ({ ai }) in every handler, typed
10
+ * .mount("/api", inferloApp.fetch); // or a whole Inferlo app under a path
11
+ *
12
+ * This entry point never loads the router, database or jobs code.
13
+ */
14
+ import type { Elysia } from "elysia";
15
+ import type { AI } from "./ai/index.ts";
16
+ /**
17
+ * An Elysia plugin that decorates the context with an AI client (createAI(...) or an Inferlo app's app.ai):
18
+ * new Elysia().use(inferloAI(ai)).post("/classify", ({ ai, body }) => ...).
19
+ */
20
+ export declare function inferloAI(ai: AI): (app: Elysia) => Elysia<"", {
21
+ decorator: {
22
+ ai: AI;
23
+ };
24
+ store: {};
25
+ derive: {};
26
+ resolve: {};
27
+ }, {
28
+ typebox: {};
29
+ error: {};
30
+ }, {
31
+ schema: {};
32
+ standaloneSchema: {};
33
+ macro: {};
34
+ macroFn: {};
35
+ parser: {};
36
+ response: {};
37
+ }, {}, {
38
+ derive: {};
39
+ resolve: {};
40
+ schema: {};
41
+ standaloneSchema: {};
42
+ response: {};
43
+ }, {
44
+ derive: {};
45
+ resolve: {};
46
+ schema: {};
47
+ standaloneSchema: {};
48
+ response: {};
49
+ }>;
package/dist/elysia.js ADDED
@@ -0,0 +1,10 @@
1
+ import { assertAIClient } from "./ai-client.js";
2
+ /**
3
+ * An Elysia plugin that decorates the context with an AI client (createAI(...) or an Inferlo app's app.ai):
4
+ * new Elysia().use(inferloAI(ai)).post("/classify", ({ ai, body }) => ...).
5
+ */
6
+ export function inferloAI(ai) {
7
+ assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: new Elysia().use(inferloAI(ai)).');
8
+ return (app) => app.decorate("ai", ai);
9
+ }
10
+ //# sourceMappingURL=elysia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elysia.js","sourceRoot":"","sources":["../src/elysia.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EAAM;IAC9B,cAAc,CAAC,EAAE,EAAE,uIAAuI,CAAC,CAAC;IAC5J,OAAO,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACjD,CAAC"}
package/dist/env.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  /** Use a fixed set of variables (tests, Cloudflare Workers bindings). */
2
2
  export declare function setEnv(values: Record<string, string | undefined> | undefined): void;
3
+ /** The raw value of an environment variable (or of setEnv overrides). */
4
+ export declare function readEnv(name: string): string | undefined;
3
5
  interface EnvFn {
4
6
  (name: string): string;
5
7
  (name: string, options: {
package/dist/env.js CHANGED
@@ -12,7 +12,8 @@ let overrides;
12
12
  export function setEnv(values) {
13
13
  overrides = values;
14
14
  }
15
- function read(name) {
15
+ /** The raw value of an environment variable (or of setEnv overrides). */
16
+ export function readEnv(name) {
16
17
  if (overrides && name in overrides)
17
18
  return overrides[name];
18
19
  const g = globalThis;
@@ -34,7 +35,7 @@ function missing(name) {
34
35
  });
35
36
  }
36
37
  export const env = Object.assign((name, options = {}) => {
37
- const value = read(name);
38
+ const value = readEnv(name);
38
39
  if (value !== undefined && value !== "")
39
40
  return value;
40
41
  if (options.default !== undefined)
@@ -44,7 +45,7 @@ export const env = Object.assign((name, options = {}) => {
44
45
  throw missing(name);
45
46
  }, {
46
47
  int(name, options = {}) {
47
- const value = read(name);
48
+ const value = readEnv(name);
48
49
  if (value === undefined || value === "") {
49
50
  if (options.default !== undefined)
50
51
  return options.default;
@@ -56,7 +57,7 @@ export const env = Object.assign((name, options = {}) => {
56
57
  return n;
57
58
  },
58
59
  bool(name, options = {}) {
59
- const value = read(name)?.toLowerCase();
60
+ const value = readEnv(name)?.toLowerCase();
60
61
  if (value === undefined || value === "") {
61
62
  if (options.default !== undefined)
62
63
  return options.default;
package/dist/env.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Inferlo inside Express (4 and 5), without a runtime dependency on Express:
3
+ *
4
+ * import express from "express";
5
+ * import { createAI, anthropic } from "@browserlessapi/inferlo/ai";
6
+ * import { toNodeHandler } from "@browserlessapi/inferlo";
7
+ * import { inferloAI, sendResponse } from "@browserlessapi/inferlo/express";
8
+ *
9
+ * const server = express();
10
+ * server.use(inferloAI(ai)); // req.ai in every handler
11
+ * server.get("/chat", (req, res) => sendResponse(res, req.ai.stream({ prompt: "Hi" }).toResponse()));
12
+ * server.use("/api", toNodeHandler(inferloApp)); // or a whole Inferlo app under a path
13
+ *
14
+ * Importing this entry point adds `ai` to Express's Request type. It never loads the router, database or jobs code.
15
+ */
16
+ import type { AI } from "./ai/index.ts";
17
+ import { type NodeResponse } from "./serve.ts";
18
+ declare global {
19
+ namespace Express {
20
+ interface Request {
21
+ /** The AI client set by inferloAI() from @browserlessapi/inferlo/express. */
22
+ ai: AI;
23
+ }
24
+ }
25
+ }
26
+ /** The part of an Express request that inferloAI() uses. */
27
+ export interface ExpressRequestLike {
28
+ ai?: AI;
29
+ }
30
+ /** An Express middleware: server.use(inferloAI(ai)) or server.use("/chat", inferloAI(ai)). */
31
+ export type InferloAIMiddleware = (req: ExpressRequestLike, res: unknown, next: (error?: unknown) => void) => void;
32
+ /** Puts an AI client (createAI(...) or an Inferlo app's app.ai) on every Express request as req.ai. */
33
+ export declare function inferloAI(ai: AI): InferloAIMiddleware;
34
+ /**
35
+ * Send a Web Response from an Express (or node:http) handler: status, headers (every Set-Cookie) and the
36
+ * body, streamed. Use it for AI streams: sendResponse(res, req.ai.stream({ prompt }).toResponse()).
37
+ */
38
+ export declare function sendResponse(res: NodeResponse, response: Response): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import { assertAIClient } from "./ai-client.js";
2
+ import { writeResponse } from "./serve.js";
3
+ /** Puts an AI client (createAI(...) or an Inferlo app's app.ai) on every Express request as req.ai. */
4
+ export function inferloAI(ai) {
5
+ assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: server.use(inferloAI(ai)).');
6
+ return (req, _res, next) => {
7
+ req.ai = ai;
8
+ next();
9
+ };
10
+ }
11
+ /**
12
+ * Send a Web Response from an Express (or node:http) handler: status, headers (every Set-Cookie) and the
13
+ * body, streamed. Use it for AI streams: sendResponse(res, req.ai.stream({ prompt }).toResponse()).
14
+ */
15
+ export function sendResponse(res, response) {
16
+ return writeResponse(res, response, "GET");
17
+ }
18
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAqB,MAAM,YAAY,CAAC;AAoB9D,uGAAuG;AACvG,MAAM,UAAU,SAAS,CAAC,EAAM;IAC9B,cAAc,CAAC,EAAE,EAAE,iIAAiI,CAAC,CAAC;IACtJ,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QACzB,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;QACZ,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAiB,EAAE,QAAkB;IAChE,OAAO,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Inferlo inside Fastify (5 and later), without a runtime dependency on Fastify:
3
+ *
4
+ * import Fastify from "fastify";
5
+ * import { createAI, anthropic } from "@browserlessapi/inferlo/ai";
6
+ * import { inferloPlugin } from "@browserlessapi/inferlo/fastify";
7
+ *
8
+ * const fastify = Fastify();
9
+ * await fastify.register(inferloPlugin, { ai }); // fastify.ai and request.ai
10
+ * await fastify.register(inferloPlugin, { app: inferloApp, prefix: "/api" }); // or a whole Inferlo app under /api
11
+ *
12
+ * Importing this entry point adds `ai` to Fastify's instance and request types. It never loads the router,
13
+ * database or jobs code.
14
+ */
15
+ import type { FastifyPluginAsync } from "fastify";
16
+ import type { AI } from "./ai/index.ts";
17
+ declare module "fastify" {
18
+ interface FastifyInstance {
19
+ /** The AI client registered with inferloPlugin from @browserlessapi/inferlo/fastify. */
20
+ ai: AI;
21
+ }
22
+ interface FastifyRequest {
23
+ /** The AI client registered with inferloPlugin from @browserlessapi/inferlo/fastify. */
24
+ ai: AI;
25
+ }
26
+ }
27
+ export interface InferloPluginOptions {
28
+ /** AI client for fastify.ai and request.ai: createAI(...) or an Inferlo app's app.ai. Default: app.ai, unless fastify.ai is already set. */
29
+ ai?: AI;
30
+ /** A whole Inferlo app (routes, auth, OpenAPI, MCP) to serve under prefix. */
31
+ app?: {
32
+ fetch(req: Request, ...rest: unknown[]): Promise<Response>;
33
+ ai?: AI;
34
+ };
35
+ /** Path the app is served under, for example "/api". Required with app. */
36
+ prefix?: string;
37
+ }
38
+ /** A Fastify plugin, registered with fastify.register(inferloPlugin, options). */
39
+ export type InferloPlugin = FastifyPluginAsync<InferloPluginOptions>;
40
+ /**
41
+ * Fastify plugin. { ai } decorates fastify.ai and request.ai everywhere in the Fastify app; { app, prefix }
42
+ * serves a whole Inferlo app (routes, auth, validation, OpenAPI, MCP) under prefix. Both can be combined.
43
+ */
44
+ export declare const inferloPlugin: InferloPlugin;
@@ -0,0 +1,47 @@
1
+ import { assertAIClient } from "./ai-client.js";
2
+ import { InferloError } from "./errors.js";
3
+ import { toNodeHandler } from "./serve.js";
4
+ const bad = (message, fix) => new InferloError("IL5015", message, { fix });
5
+ async function plugin(fastify, options) {
6
+ const { app, prefix } = options ?? {};
7
+ if (app !== undefined && (typeof app !== "object" || app === null || typeof app.fetch !== "function")) {
8
+ throw bad("inferloPlugin's app must be an Inferlo app (createApp(...)).", 'Use fastify.register(inferloPlugin, { app: inferloApp, prefix: "/api" }).');
9
+ }
10
+ // app.ai is the default, unless an earlier register(inferloPlugin, { ai }) already set fastify.ai.
11
+ const ai = options?.ai ?? (fastify.hasDecorator("ai") ? undefined : app?.ai);
12
+ if (!app && !options?.ai)
13
+ throw bad("inferloPlugin needs ai, app or both.", 'Use fastify.register(inferloPlugin, { ai }) for fastify.ai and request.ai, or { app, prefix: "/api" } to serve an Inferlo app.');
14
+ if (ai !== undefined) {
15
+ assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: fastify.register(inferloPlugin, { ai }).');
16
+ fastify.decorate("ai", ai);
17
+ fastify.decorateRequest("ai", { getter: () => ai });
18
+ }
19
+ if (!app)
20
+ return;
21
+ if (typeof prefix !== "string" || !/^\/[^*?#]*[^/*?#]$/.test(prefix)) {
22
+ throw bad(`inferloPlugin needs a prefix such as "/api" to serve an app, got ${JSON.stringify(prefix)}.`, 'Use fastify.register(inferloPlugin, { app, prefix: "/api" }).');
23
+ }
24
+ const handler = toNodeHandler(app, { prefix });
25
+ // In a child context, so the catch-all body parser applies to the app's routes only: Inferlo reads the
26
+ // body stream itself (JSON, forms, any size limit), and Fastify answers nothing for these paths.
27
+ await fastify.register(async (child) => {
28
+ child.removeAllContentTypeParsers();
29
+ child.addContentTypeParser("*", (_request, _payload, done) => done(null));
30
+ const mount = async (request, reply) => {
31
+ reply.hijack();
32
+ await handler(request.raw, reply.raw);
33
+ };
34
+ child.all(prefix, mount);
35
+ child.all(`${prefix}/*`, mount);
36
+ });
37
+ }
38
+ /**
39
+ * Fastify plugin. { ai } decorates fastify.ai and request.ai everywhere in the Fastify app; { app, prefix }
40
+ * serves a whole Inferlo app (routes, auth, validation, OpenAPI, MCP) under prefix. Both can be combined.
41
+ */
42
+ export const inferloPlugin = Object.assign(plugin, {
43
+ // Like fastify-plugin: the decorators are visible to the whole Fastify app, not only inside this plugin.
44
+ [Symbol.for("skip-override")]: true,
45
+ [Symbol.for("fastify.display-name")]: "inferlo",
46
+ });
47
+ //# sourceMappingURL=fastify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.js","sourceRoot":"","sources":["../src/fastify.ts"],"names":[],"mappings":"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"}
package/dist/hono.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Inferlo inside Hono (4.0 and later), without a runtime dependency on Hono:
3
+ *
4
+ * import { Hono } from "hono";
5
+ * import { createAI, anthropic } from "@browserlessapi/inferlo/ai";
6
+ * import { inferloAI, type InferloVariables } from "@browserlessapi/inferlo/hono";
7
+ *
8
+ * const hono = new Hono<{ Variables: InferloVariables }>();
9
+ * hono.use(inferloAI(ai)); // c.var.ai in every handler
10
+ * hono.mount("/api", inferloApp.fetch); // or a whole Inferlo app under a path
11
+ *
12
+ * This entry point never loads the router, database or jobs code.
13
+ */
14
+ import type { AI } from "./ai/index.ts";
15
+ /** Context variables set by inferloAI(): new Hono<{ Variables: InferloVariables }>(). */
16
+ export type InferloVariables = {
17
+ ai: AI;
18
+ };
19
+ /** The part of Hono's Context that inferloAI() uses. */
20
+ export interface HonoContextLike {
21
+ set(key: "ai", value: AI): void;
22
+ }
23
+ /** A Hono middleware: hono.use(inferloAI(ai)) or hono.use("/chat/*", inferloAI(ai)). */
24
+ export type InferloAIMiddleware = (c: HonoContextLike, next: () => Promise<void>) => Promise<void>;
25
+ /** Puts an AI client (createAI(...) or an Inferlo app's app.ai) on the Hono context as c.var.ai. */
26
+ export declare function inferloAI(ai: AI): InferloAIMiddleware;
package/dist/hono.js ADDED
@@ -0,0 +1,10 @@
1
+ import { assertAIClient } from "./ai-client.js";
2
+ /** Puts an AI client (createAI(...) or an Inferlo app's app.ai) on the Hono context as c.var.ai. */
3
+ export function inferloAI(ai) {
4
+ assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: hono.use(inferloAI(ai)).');
5
+ return async (c, next) => {
6
+ c.set("ai", ai);
7
+ await next();
8
+ };
9
+ }
10
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAahD,oGAAoG;AACpG,MAAM,UAAU,SAAS,CAAC,EAAM;IAC9B,cAAc,CAAC,EAAE,EAAE,+HAA+H,CAAC,CAAC;IACpJ,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAChB,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,26 +1,40 @@
1
1
  /**
2
- * Inferlo: the TypeScript backend framework with built-in AI model support.
2
+ * Inferlo: the enterprise-ready TypeScript backend framework, with AI models built in.
3
3
  * https://github.com/BrowserlessAPI/Inferlo
4
4
  */
5
5
  export { createApp } from "./app.ts";
6
- export type { App, AppConfig, AuthConfig, CorsConfig, Middleware, MiddlewareContext, RequestOptions, RouteSource, Logger } from "./app.ts";
6
+ export type { LogFields, LoggingConfig } from "./logging.ts";
7
+ export type { MetricsConfig } from "./metrics.ts";
8
+ export type { Deprecation } from "./deprecation.ts";
9
+ export type { App, AppConfig, AuthConfig, CorsConfig, Middleware, MiddlewareContext, RequestOptions, RouteSource, Logger, HealthConfig, HealthCheck, CheckResult, ReadinessReport, CloseOptions } from "./app.ts";
10
+ export type { SecureHeadersConfig } from "./secure-headers.ts";
7
11
  export { route, isRoute } from "./route.ts";
8
12
  export type { Register, Context, RouteDef, RouteOptions, Auth, Method, ToolOptions, AppUser, AppDb } from "./route.ts";
9
- export { t, Schema, ObjectSchema, ArraySchema, ValidationError, toJSONSchema } from "./schema.ts";
13
+ export { t, Schema, ObjectSchema, ArraySchema, RefSchema, ValidationError, toJSONSchema } from "./schema.ts";
10
14
  export type { Infer, InferInput, Issue, JSONSchema, ParseResult, Shape, AnySchema } from "./schema.ts";
11
15
  export { model, Model, columnName } from "./model.ts";
12
16
  export type { Row, CreateInput, UpdateInput, AnyModel } from "./model.ts";
13
17
  export { memory } from "./db/memory.ts";
14
18
  export { createDb, Table } from "./db/table.ts";
15
- export type { Db, Where, OrderBy, FindManyOptions } from "./db/table.ts";
19
+ export type { Db, Where, OrderBy, FindManyOptions, FindOptions, PaginateOptions, Page, Include, Relations, ReverseRelations, WithInclude } from "./db/table.ts";
16
20
  export type { Adapter, Condition, Query, DbRecord } from "./db/types.ts";
17
21
  export { planMigration, applyMigrations } from "./db/migrate.ts";
18
22
  export type { ApplyMigrationsOptions, Migration, MigrationPlan, MigrationSnapshot, MigrationSnapshotColumn, MigrationSnapshotV1, PlanMigrationOptions, } from "./db/migrate.ts";
19
23
  export { createAI, cosineSimilarity, extractJson, openai, openaiCompatible, anthropic, gemini, ollama, mockProvider, ProviderError } from "./ai/index.ts";
20
- export type { AI, AIConfig, GenerateOptions, GenerateResult, StreamResult, Usage, Pricing, Provider, ProviderRequest, Message } from "./ai/index.ts";
24
+ export { tool, isTool, ToolBuilder } from "./ai/tool.ts";
25
+ export type { ToolDef, FunctionToolOptions, ToolContext } from "./ai/tool.ts";
26
+ export type { AI, AIConfig, GenerateOptions, GenerateResult, StreamResult, Usage, Pricing, Provider, ProviderRequest, ProviderResponse, Image, ImageMediaType, Message, MockReply, Step, ToolCall, ToolCallResult, ToolSpec, } from "./ai/index.ts";
27
+ export type { RateLimit, RateLimitConfig } from "./rate-limit.ts";
28
+ export { job, isJob, retryDelayMs } from "./jobs.ts";
29
+ export { cron, isCron } from "./cron.ts";
30
+ export type { CronDef } from "./cron.ts";
31
+ export type { JobDef, JobOptions, JobContext, JobInput, JobRecord, JobStatus, JobSource, JobQueueConfig, Jobs, AppJobs, EnqueueOptions, RunResult, Worker } from "./jobs.ts";
21
32
  export { bearer, hashToken, createToken, hashPassword, verifyPassword, safeEqual } from "./auth.ts";
22
33
  export { env, setEnv } from "./env.ts";
34
+ export { config } from "./config.ts";
35
+ export type { Config, ConfigOptions } from "./config.ts";
23
36
  export { InferloError, httpError, notFound, forbidden, badRequest, docsUrl } from "./errors.ts";
24
- export { serve } from "./serve.ts";
37
+ export { serve, toNodeHandler, fromNodeRequest } from "./serve.ts";
38
+ export type { ServeOptions, Server, NodeRequest, NodeResponse, NodeHandler, NodeHandlerOptions, FromNodeRequestOptions } from "./serve.ts";
25
39
  export { INFERLO_VERSION } from "./describe.ts";
26
40
  export type { AppDescription } from "./describe.ts";
package/dist/index.js CHANGED
@@ -1,18 +1,22 @@
1
1
  /**
2
- * Inferlo: the TypeScript backend framework with built-in AI model support.
2
+ * Inferlo: the enterprise-ready TypeScript backend framework, with AI models built in.
3
3
  * https://github.com/BrowserlessAPI/Inferlo
4
4
  */
5
5
  export { createApp } from "./app.js";
6
6
  export { route, isRoute } from "./route.js";
7
- export { t, Schema, ObjectSchema, ArraySchema, ValidationError, toJSONSchema } from "./schema.js";
7
+ export { t, Schema, ObjectSchema, ArraySchema, RefSchema, ValidationError, toJSONSchema } from "./schema.js";
8
8
  export { model, Model, columnName } from "./model.js";
9
9
  export { memory } from "./db/memory.js";
10
10
  export { createDb, Table } from "./db/table.js";
11
11
  export { planMigration, applyMigrations } from "./db/migrate.js";
12
12
  export { createAI, cosineSimilarity, extractJson, openai, openaiCompatible, anthropic, gemini, ollama, mockProvider, ProviderError } from "./ai/index.js";
13
+ export { tool, isTool, ToolBuilder } from "./ai/tool.js";
14
+ export { job, isJob, retryDelayMs } from "./jobs.js";
15
+ export { cron, isCron } from "./cron.js";
13
16
  export { bearer, hashToken, createToken, hashPassword, verifyPassword, safeEqual } from "./auth.js";
14
17
  export { env, setEnv } from "./env.js";
18
+ export { config } from "./config.js";
15
19
  export { InferloError, httpError, notFound, forbidden, badRequest, docsUrl } from "./errors.js";
16
- export { serve } from "./serve.js";
20
+ export { serve, toNodeHandler, fromNodeRequest } from "./serve.js";
17
21
  export { INFERLO_VERSION } from "./describe.js";
18
22
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGrC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGlG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGtD,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAWjE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG1J,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACpG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAChG,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAOrC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG7G,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGtD,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAWjE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1J,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAwBzD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACpG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAChG,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
package/dist/jobs.d.ts ADDED
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Background jobs: work that runs after the request, with retries.
3
+ *
4
+ * export const sendWelcome = job("send-welcome", { input: t.object({ userId: t.uuid() }) })
5
+ * .handle(async ({ input, db }) => { ... })
6
+ *
7
+ * createApp({ jobs: [jobs] }) // import * as jobs from "./jobs.ts"
8
+ * await ctx.jobs.enqueue(sendWelcome, { userId }) // in a route handler, validated
9
+ *
10
+ * Jobs are plain values like routes. The queue is kept by a JobStore; app.jobs.start() runs due
11
+ * jobs in this process, app.jobs.runDue() runs them once (tests).
12
+ */
13
+ import type { AI } from "./ai/index.ts";
14
+ import { type CronDef } from "./cron.ts";
15
+ import type { Adapter } from "./db/types.ts";
16
+ import type { AppDb } from "./route.ts";
17
+ import { type AnySchema, type Infer, type InferInput } from "./schema.ts";
18
+ export interface JobOptions<I extends AnySchema> {
19
+ /** Schema of the job's input. Checked when the job is enqueued and again when it runs. */
20
+ input: I;
21
+ /** Extra attempts after a failure, with growing delays (10s, 20s, 40s, ... up to 1 hour). Default 3. */
22
+ retries?: number;
23
+ /** One line shown in describe. */
24
+ summary?: string;
25
+ }
26
+ export interface JobContext<Input> {
27
+ /** The validated input. */
28
+ input: Input;
29
+ db: AppDb;
30
+ ai: AI;
31
+ /** Enqueue follow-up jobs. */
32
+ jobs: Jobs;
33
+ /** Job id. */
34
+ id: string;
35
+ /** 1 on the first run, 2 on the first retry, and so on. */
36
+ attempt: number;
37
+ }
38
+ export interface JobDef<Input = any, Output = any> {
39
+ readonly kind: "inferlo.job";
40
+ readonly name: string;
41
+ readonly options: JobOptions<AnySchema>;
42
+ readonly handler: (ctx: JobContext<Output>) => unknown;
43
+ /** Type only: what enqueue accepts. */
44
+ readonly $input?: Input;
45
+ }
46
+ /** What enqueue accepts for a job. */
47
+ export type JobInput<J> = J extends JobDef<infer I, any> ? I : never;
48
+ export interface EnqueueOptions {
49
+ /** Run no earlier than this time. Default: now. */
50
+ runAt?: Date;
51
+ }
52
+ export type JobStatus = "pending" | "running" | "done" | "failed";
53
+ export interface JobRecord {
54
+ id: string;
55
+ name: string;
56
+ /** The input as stored (JSON). */
57
+ input: unknown;
58
+ status: JobStatus;
59
+ /** Runs started so far. */
60
+ attempts: number;
61
+ /** 1 + retries. */
62
+ maxAttempts: number;
63
+ /** When the job runs next (pending) or last ran. */
64
+ runAt: Date;
65
+ /** Message of the last failure. */
66
+ lastError?: string;
67
+ createdAt: Date;
68
+ updatedAt: Date;
69
+ }
70
+ export interface Jobs {
71
+ /** Validate the input and queue the job. Returns the queued record. */
72
+ enqueue<J extends JobDef>(job: J, input: JobInput<J>, options?: EnqueueOptions): Promise<JobRecord>;
73
+ }
74
+ export interface JobQueueConfig {
75
+ /**
76
+ * Where queued jobs are kept. "memory" (default): in this process, for development and tests.
77
+ * "database": in the inferlo_jobs table of the app's Postgres database, shared by every instance.
78
+ */
79
+ store?: "memory" | "database";
80
+ }
81
+ /** Where queued jobs are kept. */
82
+ export interface JobStore {
83
+ /** Add a record. A record whose id is already stored is ignored (cron ticks use fixed ids). */
84
+ add(record: JobRecord): Promise<void>;
85
+ /** Mark up to limit pending jobs named in names with runAt <= now as running (attempts + 1) and return them, oldest first. */
86
+ claim(now: Date, limit: number, names: readonly string[]): Promise<JobRecord[]>;
87
+ update(id: string, patch: Partial<Pick<JobRecord, "status" | "runAt" | "lastError">>): Promise<void>;
88
+ get(id: string): Promise<JobRecord | null>;
89
+ /** Record that a running job is still alive (database store), so it is not picked up again as stuck. */
90
+ touch?(id: string): Promise<void>;
91
+ }
92
+ export interface RunResult {
93
+ /** Jobs that finished. */
94
+ done: number;
95
+ /** Jobs that failed and will run again later. */
96
+ retried: number;
97
+ /** Jobs that failed for the last time. */
98
+ failed: number;
99
+ }
100
+ export interface Worker {
101
+ /** Stop polling and wait for running jobs to finish. */
102
+ stop(): Promise<void>;
103
+ }
104
+ /** app.jobs: enqueue, inspect and run jobs. */
105
+ export interface AppJobs extends Jobs {
106
+ /** The registered jobs. */
107
+ readonly list: readonly JobDef[];
108
+ /** The registered crons: cron(schedule, job) values in createApp({ jobs }). */
109
+ readonly crons: readonly CronDef[];
110
+ /** Where queued jobs are kept (createApp({ jobQueue: { store } })). */
111
+ readonly queue: "memory" | "database";
112
+ /** A queued job by id, or null. */
113
+ get(id: string): Promise<JobRecord | null>;
114
+ /** Run every job that is due now (or at options.now), one after another, including follow-ups they enqueue, then return. For tests and scripts. */
115
+ runDue(options?: {
116
+ now?: Date;
117
+ }): Promise<RunResult>;
118
+ /** Run due jobs in this process every pollMs (default 1000) until stop() is called. Also queues each cron's job at every tick after the start. */
119
+ start(options?: {
120
+ pollMs?: number;
121
+ }): Worker;
122
+ }
123
+ /** Stop every worker started with jobs.start() and wait for their running jobs (used by app.close()). */
124
+ export declare function stopWorkers(jobs: AppJobs): Promise<void>;
125
+ /** Delay before retry n (1-based): 10s, 20s, 40s, ... at most one hour. */
126
+ export declare function retryDelayMs(attempt: number): number;
127
+ export declare class JobBuilder<I extends AnySchema> {
128
+ readonly name: string;
129
+ readonly options: JobOptions<I>;
130
+ constructor(name: string, options: JobOptions<I>);
131
+ /** The job's work. Throw to fail the attempt; it is retried until retries run out. */
132
+ handle(handler: (ctx: JobContext<Infer<I>>) => unknown): JobDef<InferInput<I>, Infer<I>>;
133
+ }
134
+ /** Define a background job: job("send-email", { input: t.object({ to: t.email() }) }).handle(fn). */
135
+ export declare function job<I extends AnySchema>(name: string, options: JobOptions<I>): JobBuilder<I>;
136
+ export declare function isJob(value: unknown): value is JobDef;
137
+ /** Anything that contains jobs: a job, a cron, an array, or a module (import * as jobs from "./jobs.ts"). */
138
+ export type JobSource = JobDef | CronDef | readonly JobSource[] | {
139
+ readonly [name: string]: unknown;
140
+ };
141
+ /** The jobs and crons in createApp({ jobs }). A cron's job is registered with it. */
142
+ export declare function collectJobs(sources: readonly JobSource[] | undefined): JobDef[];
143
+ export declare function collectCrons(sources: readonly JobSource[] | undefined): CronDef[];
144
+ /** Jobs kept in this process. They are lost when it stops, so use it for development and tests. */
145
+ export declare function memoryJobStore(): JobStore;
146
+ /** A running job whose worker has not reported for this long is treated as stuck (its process stopped) and run again. */
147
+ export declare const STALE_AFTER_MS: number;
148
+ /** How often a worker reports that a running job is still alive. */
149
+ export declare const HEARTBEAT_MS = 30000;
150
+ export declare const STOPPED_MESSAGE = "The worker stopped while running this job (the process exited or lost its database connection).";
151
+ /**
152
+ * Jobs in the inferlo_jobs table (created on first use), shared by every instance. Due jobs are
153
+ * claimed with FOR UPDATE SKIP LOCKED, so each one runs in one place. A running job whose worker
154
+ * stopped reporting (see STALE_AFTER_MS) is claimed again, or marked failed if it has no attempts left.
155
+ */
156
+ export declare function databaseJobStore(adapter: Adapter, options?: {
157
+ staleAfterMs?: number;
158
+ }): JobStore;
159
+ /** Pick the job store for createApp({ jobQueue }). Raises IL6011 for an unknown store, IL6012 for "database" without Postgres. */
160
+ export declare function jobStoreFor(config: JobQueueConfig | undefined, adapter: Adapter): JobStore;
161
+ export interface JobRuntime {
162
+ db: AppDb;
163
+ ai: AI;
164
+ store: JobStore;
165
+ /** Name of the store, shown as app.jobs.queue. Default "memory". */
166
+ queue?: "memory" | "database";
167
+ /** How often a running job is reported alive to stores that support it. Default HEARTBEAT_MS. */
168
+ heartbeatMs?: number;
169
+ logger: {
170
+ info(message: string, data?: unknown): void;
171
+ error(message: string, data?: unknown): void;
172
+ };
173
+ /** Called after each attempt with its outcome and duration (metrics). */
174
+ onRun?(name: string, outcome: keyof RunResult, durationMs: number): void;
175
+ }
176
+ /** app.jobs plus what the framework tests reach directly. */
177
+ export interface InternalJobs extends AppJobs {
178
+ /** Queue the job of each cron's latest tick in (from, to]. */
179
+ queueCrons(from: Date, to: Date): Promise<void>;
180
+ }
181
+ export declare function createJobs(list: readonly JobDef[], runtime: JobRuntime, crons?: readonly CronDef[]): InternalJobs;