@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.
- package/README.md +18 -3
- package/dist/adonisjs.d.ts +33 -0
- package/dist/adonisjs.js +30 -0
- package/dist/adonisjs.js.map +1 -0
- package/dist/ai/index.d.ts +51 -2
- package/dist/ai/index.js +186 -37
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/providers.d.ts +56 -3
- package/dist/ai/providers.js +102 -11
- package/dist/ai/providers.js.map +1 -1
- package/dist/ai/public.d.ts +20 -0
- package/dist/ai/public.js +17 -0
- package/dist/ai/public.js.map +1 -0
- package/dist/ai/tool.d.ts +47 -0
- package/dist/ai/tool.js +85 -0
- package/dist/ai/tool.js.map +1 -0
- package/dist/ai-client.d.ts +3 -0
- package/dist/ai-client.js +8 -0
- package/dist/ai-client.js.map +1 -0
- package/dist/app.d.ts +102 -2
- package/dist/app.js +509 -111
- package/dist/app.js.map +1 -1
- package/dist/cli/main.js +76 -4
- package/dist/cli/main.js.map +1 -1
- package/dist/client-ip.d.ts +7 -0
- package/dist/client-ip.js +27 -0
- package/dist/client-ip.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/cron.d.ts +17 -0
- package/dist/cron.js +143 -0
- package/dist/cron.js.map +1 -0
- package/dist/db/migrate.d.ts +6 -3
- package/dist/db/migrate.js +126 -10
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/table.d.ts +83 -7
- package/dist/db/table.js +245 -9
- package/dist/db/table.js.map +1 -1
- package/dist/deprecation.d.ts +23 -0
- package/dist/deprecation.js +70 -0
- package/dist/deprecation.js.map +1 -0
- package/dist/describe.d.ts +45 -5
- package/dist/describe.js +19 -2
- package/dist/describe.js.map +1 -1
- package/dist/elysia.d.ts +49 -0
- package/dist/elysia.js +10 -0
- package/dist/elysia.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.js +5 -4
- package/dist/env.js.map +1 -1
- package/dist/express.d.ts +38 -0
- package/dist/express.js +18 -0
- package/dist/express.js.map +1 -0
- package/dist/fastify.d.ts +44 -0
- package/dist/fastify.js +47 -0
- package/dist/fastify.js.map +1 -0
- package/dist/hono.d.ts +26 -0
- package/dist/hono.js +10 -0
- package/dist/hono.js.map +1 -0
- package/dist/index.d.ts +20 -6
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +181 -0
- package/dist/jobs.js +391 -0
- package/dist/jobs.js.map +1 -0
- package/dist/logging.d.ts +23 -0
- package/dist/logging.js +87 -0
- package/dist/logging.js.map +1 -0
- package/dist/mcp.d.ts +4 -6
- package/dist/mcp.js +53 -38
- package/dist/mcp.js.map +1 -1
- package/dist/metrics.d.ts +48 -0
- package/dist/metrics.js +184 -0
- package/dist/metrics.js.map +1 -0
- package/dist/nestjs.d.ts +43 -0
- package/dist/nestjs.js +47 -0
- package/dist/nestjs.js.map +1 -0
- package/dist/openapi.d.ts +0 -1
- package/dist/openapi.js +7 -0
- package/dist/openapi.js.map +1 -1
- package/dist/rate-limit.d.ts +45 -0
- package/dist/rate-limit.js +93 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/redact.d.ts +19 -0
- package/dist/redact.js +150 -0
- package/dist/redact.js.map +1 -0
- package/dist/route.d.ts +24 -0
- package/dist/route.js.map +1 -1
- package/dist/schema.d.ts +10 -2
- package/dist/schema.js +13 -1
- package/dist/schema.js.map +1 -1
- package/dist/secure-headers.d.ts +8 -0
- package/dist/secure-headers.js +61 -0
- package/dist/secure-headers.js.map +1 -0
- package/dist/serve.d.ts +53 -1
- package/dist/serve.js +158 -59
- package/dist/serve.js.map +1 -1
- package/dist/trpc.d.ts +57 -0
- package/dist/trpc.js +142 -0
- package/dist/trpc.js.map +1 -0
- package/dist/tsed.d.ts +32 -0
- package/dist/tsed.js +13 -0
- package/dist/tsed.js.map +1 -0
- package/llms.txt +10 -4
- package/package.json +57 -3
- package/templates/app/AGENTS.md +14 -8
- package/templates/app/README.md +2 -0
- package/templates/app/_env.example +2 -0
- package/templates/app/package.json.tpl +1 -0
- package/templates/app/src/app.ts +15 -8
- package/templates/app/src/config.ts +17 -0
- package/templates/app/src/routes/notes.ts +1 -1
- package/templates/app/src/server.ts +13 -3
- package/templates/app/src/routes/health.ts +0 -3
package/dist/jobs.js
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { cronTickId, isCron } from "./cron.js";
|
|
2
|
+
import { InferloError } from "./errors.js";
|
|
3
|
+
import { ValidationError } from "./schema.js";
|
|
4
|
+
/** Workers started with app.jobs.start() that have not been stopped, so app.close() can stop them. */
|
|
5
|
+
const workers = new WeakMap();
|
|
6
|
+
/** Stop every worker started with jobs.start() and wait for their running jobs (used by app.close()). */
|
|
7
|
+
export async function stopWorkers(jobs) {
|
|
8
|
+
await Promise.all([...(workers.get(jobs) ?? [])].map((w) => w.stop()));
|
|
9
|
+
}
|
|
10
|
+
const MAX_RETRIES = 25;
|
|
11
|
+
const BASE_DELAY_MS = 10_000;
|
|
12
|
+
const MAX_DELAY_MS = 3_600_000;
|
|
13
|
+
/** Delay before retry n (1-based): 10s, 20s, 40s, ... at most one hour. */
|
|
14
|
+
export function retryDelayMs(attempt) {
|
|
15
|
+
return Math.min(BASE_DELAY_MS * 2 ** (attempt - 1), MAX_DELAY_MS);
|
|
16
|
+
}
|
|
17
|
+
export class JobBuilder {
|
|
18
|
+
name;
|
|
19
|
+
options;
|
|
20
|
+
constructor(name, options) {
|
|
21
|
+
this.name = name;
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
/** The job's work. Throw to fail the attempt; it is retried until retries run out. */
|
|
25
|
+
handle(handler) {
|
|
26
|
+
return { kind: "inferlo.job", name: this.name, options: this.options, handler: handler };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const JOB_NAME = /^[a-z][a-z0-9-]{0,62}$/;
|
|
30
|
+
/** Define a background job: job("send-email", { input: t.object({ to: t.email() }) }).handle(fn). */
|
|
31
|
+
export function job(name, options) {
|
|
32
|
+
checkJob(name, options);
|
|
33
|
+
return new JobBuilder(name, options);
|
|
34
|
+
}
|
|
35
|
+
function checkJob(name, options) {
|
|
36
|
+
const o = options;
|
|
37
|
+
const problems = [];
|
|
38
|
+
if (typeof name !== "string" || !JOB_NAME.test(name))
|
|
39
|
+
problems.push("the name must be lowercase letters, digits and dashes, starting with a letter");
|
|
40
|
+
if (!o || typeof o !== "object")
|
|
41
|
+
problems.push("options must be an object with an input schema");
|
|
42
|
+
else {
|
|
43
|
+
if (!o.input || typeof o.input.safeParse !== "function")
|
|
44
|
+
problems.push("input must be a schema such as t.object({ ... })");
|
|
45
|
+
if (o.retries !== undefined && (!Number.isInteger(o.retries) || o.retries < 0 || o.retries > MAX_RETRIES))
|
|
46
|
+
problems.push(`retries must be a whole number from 0 to ${MAX_RETRIES}`);
|
|
47
|
+
}
|
|
48
|
+
if (problems.length) {
|
|
49
|
+
throw new InferloError("IL6009", `Invalid job "${String(name)}": ${problems.join(", ")}.`, {
|
|
50
|
+
fix: 'Use job("send-email", { input: t.object({ to: t.email() }), retries: 3 }).handle(async ({ input }) => { ... }).',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function isJob(value) {
|
|
55
|
+
return typeof value === "object" && value !== null && value.kind === "inferlo.job";
|
|
56
|
+
}
|
|
57
|
+
/** The jobs and crons in createApp({ jobs }). A cron's job is registered with it. */
|
|
58
|
+
export function collectJobs(sources) {
|
|
59
|
+
return collect(sources).jobs;
|
|
60
|
+
}
|
|
61
|
+
export function collectCrons(sources) {
|
|
62
|
+
return collect(sources).crons;
|
|
63
|
+
}
|
|
64
|
+
function collect(sources) {
|
|
65
|
+
const out = [];
|
|
66
|
+
const crons = [];
|
|
67
|
+
const seen = new Set();
|
|
68
|
+
const visit = (source) => {
|
|
69
|
+
if (!source || typeof source !== "object" || seen.has(source))
|
|
70
|
+
return;
|
|
71
|
+
seen.add(source);
|
|
72
|
+
if (isJob(source))
|
|
73
|
+
out.push(source);
|
|
74
|
+
else if (isCron(source)) {
|
|
75
|
+
crons.push(source);
|
|
76
|
+
visit(source.job);
|
|
77
|
+
}
|
|
78
|
+
else if (Array.isArray(source))
|
|
79
|
+
source.forEach(visit);
|
|
80
|
+
else
|
|
81
|
+
for (const value of Object.values(source))
|
|
82
|
+
if (isJob(value) || isCron(value) || Array.isArray(value))
|
|
83
|
+
visit(value);
|
|
84
|
+
};
|
|
85
|
+
visit(sources);
|
|
86
|
+
const names = new Map();
|
|
87
|
+
for (const j of out) {
|
|
88
|
+
if (names.has(j.name))
|
|
89
|
+
throw new InferloError("IL6009", `Two jobs are named "${j.name}".`, { fix: "Give each job a unique name." });
|
|
90
|
+
names.set(j.name, j);
|
|
91
|
+
}
|
|
92
|
+
return { jobs: out, crons };
|
|
93
|
+
}
|
|
94
|
+
/** Jobs kept in this process. They are lost when it stops, so use it for development and tests. */
|
|
95
|
+
export function memoryJobStore() {
|
|
96
|
+
const records = new Map();
|
|
97
|
+
const copy = (r) => ({ ...r, runAt: new Date(r.runAt), createdAt: new Date(r.createdAt), updatedAt: new Date(r.updatedAt) });
|
|
98
|
+
return {
|
|
99
|
+
async add(record) {
|
|
100
|
+
if (!records.has(record.id))
|
|
101
|
+
records.set(record.id, copy(record));
|
|
102
|
+
},
|
|
103
|
+
async claim(now, limit, names) {
|
|
104
|
+
const due = [...records.values()]
|
|
105
|
+
.filter((r) => r.status === "pending" && r.runAt.getTime() <= now.getTime() && names.includes(r.name))
|
|
106
|
+
.sort((a, b) => a.runAt.getTime() - b.runAt.getTime() || a.createdAt.getTime() - b.createdAt.getTime())
|
|
107
|
+
.slice(0, limit);
|
|
108
|
+
for (const r of due)
|
|
109
|
+
Object.assign(r, { status: "running", attempts: r.attempts + 1, updatedAt: new Date() });
|
|
110
|
+
return due.map(copy);
|
|
111
|
+
},
|
|
112
|
+
async update(id, patch) {
|
|
113
|
+
const r = records.get(id);
|
|
114
|
+
if (r)
|
|
115
|
+
Object.assign(r, patch, { updatedAt: new Date() });
|
|
116
|
+
},
|
|
117
|
+
async get(id) {
|
|
118
|
+
const r = records.get(id);
|
|
119
|
+
return r ? copy(r) : null;
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** A running job whose worker has not reported for this long is treated as stuck (its process stopped) and run again. */
|
|
124
|
+
export const STALE_AFTER_MS = 5 * 60_000;
|
|
125
|
+
/** How often a worker reports that a running job is still alive. */
|
|
126
|
+
export const HEARTBEAT_MS = 30_000;
|
|
127
|
+
export const STOPPED_MESSAGE = "The worker stopped while running this job (the process exited or lost its database connection).";
|
|
128
|
+
const JOB_COLUMNS = `"id", "name", "input", "status", "attempts", "max_attempts", "run_at", "last_error", "created_at", "updated_at"`;
|
|
129
|
+
function fromRow(row) {
|
|
130
|
+
const record = {
|
|
131
|
+
id: String(row.id),
|
|
132
|
+
name: String(row.name),
|
|
133
|
+
input: typeof row.input === "string" ? JSON.parse(row.input) : row.input,
|
|
134
|
+
status: row.status,
|
|
135
|
+
attempts: Number(row.attempts),
|
|
136
|
+
maxAttempts: Number(row.max_attempts),
|
|
137
|
+
runAt: new Date(row.run_at),
|
|
138
|
+
createdAt: new Date(row.created_at),
|
|
139
|
+
updatedAt: new Date(row.updated_at),
|
|
140
|
+
};
|
|
141
|
+
if (row.last_error !== null && row.last_error !== undefined)
|
|
142
|
+
record.lastError = String(row.last_error);
|
|
143
|
+
return record;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Jobs in the inferlo_jobs table (created on first use), shared by every instance. Due jobs are
|
|
147
|
+
* claimed with FOR UPDATE SKIP LOCKED, so each one runs in one place. A running job whose worker
|
|
148
|
+
* stopped reporting (see STALE_AFTER_MS) is claimed again, or marked failed if it has no attempts left.
|
|
149
|
+
*/
|
|
150
|
+
export function databaseJobStore(adapter, options = {}) {
|
|
151
|
+
const sql = adapter.sql?.bind(adapter);
|
|
152
|
+
if (!sql) {
|
|
153
|
+
throw new InferloError("IL6012", `jobQueue store "database" needs a Postgres database, but the app uses the ${adapter.dialect} adapter.`, {
|
|
154
|
+
fix: 'Use postgres() from "@browserlessapi/inferlo/postgres" for createApp({ db }), or jobQueue: { store: "memory" }.',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const staleAfterMs = options.staleAfterMs ?? STALE_AFTER_MS;
|
|
158
|
+
let ready;
|
|
159
|
+
const create = async () => {
|
|
160
|
+
try {
|
|
161
|
+
await sql(`CREATE TABLE IF NOT EXISTS "inferlo_jobs" (
|
|
162
|
+
"id" uuid PRIMARY KEY, "name" text NOT NULL, "input" jsonb NOT NULL, "status" text NOT NULL,
|
|
163
|
+
"attempts" integer NOT NULL, "max_attempts" integer NOT NULL, "run_at" timestamptz NOT NULL,
|
|
164
|
+
"last_error" text, "created_at" timestamptz NOT NULL, "updated_at" timestamptz NOT NULL)`);
|
|
165
|
+
await sql('CREATE INDEX IF NOT EXISTS "inferlo_jobs_due_idx" ON "inferlo_jobs" ("status", "run_at")');
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Two instances creating the table at the same moment: the other one won.
|
|
169
|
+
await sql('SELECT 1 FROM "inferlo_jobs" LIMIT 1');
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const table = async () => {
|
|
173
|
+
ready ??= create().catch((error) => {
|
|
174
|
+
ready = undefined;
|
|
175
|
+
throw error;
|
|
176
|
+
});
|
|
177
|
+
await ready;
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
async add(r) {
|
|
181
|
+
await table();
|
|
182
|
+
await sql(`INSERT INTO "inferlo_jobs" (${JOB_COLUMNS}) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT ("id") DO NOTHING`, [
|
|
183
|
+
r.id, r.name, JSON.stringify(r.input), r.status, r.attempts, r.maxAttempts, r.runAt, r.lastError ?? null, r.createdAt, r.updatedAt,
|
|
184
|
+
]);
|
|
185
|
+
},
|
|
186
|
+
async claim(now, limit, names) {
|
|
187
|
+
await table();
|
|
188
|
+
if (!names.length)
|
|
189
|
+
return [];
|
|
190
|
+
const stale = `"status" = 'running' AND "updated_at" < now() - $1 * interval '1 millisecond'`;
|
|
191
|
+
// Stuck jobs without attempts left are failed, not run again.
|
|
192
|
+
await sql(`UPDATE "inferlo_jobs" SET "status" = 'failed', "last_error" = $2, "updated_at" = now() WHERE ${stale} AND "attempts" >= "max_attempts" AND "name" = ANY($3)`, [
|
|
193
|
+
staleAfterMs, STOPPED_MESSAGE, names,
|
|
194
|
+
]);
|
|
195
|
+
const rows = await sql(`WITH due AS (
|
|
196
|
+
SELECT "id" FROM "inferlo_jobs"
|
|
197
|
+
WHERE "name" = ANY($3) AND (("status" = 'pending' AND "run_at" <= $4) OR (${stale} AND "attempts" < "max_attempts"))
|
|
198
|
+
ORDER BY "run_at", "created_at" LIMIT $5 FOR UPDATE SKIP LOCKED)
|
|
199
|
+
UPDATE "inferlo_jobs" j SET "status" = 'running', "attempts" = j."attempts" + 1, "updated_at" = now(),
|
|
200
|
+
"last_error" = CASE WHEN j."status" = 'running' THEN $2 ELSE j."last_error" END
|
|
201
|
+
FROM due WHERE j."id" = due."id"
|
|
202
|
+
RETURNING j.*`, [staleAfterMs, STOPPED_MESSAGE, names, now, limit]);
|
|
203
|
+
return rows
|
|
204
|
+
.map(fromRow)
|
|
205
|
+
.sort((a, b) => a.runAt.getTime() - b.runAt.getTime() || a.createdAt.getTime() - b.createdAt.getTime());
|
|
206
|
+
},
|
|
207
|
+
async update(id, patch) {
|
|
208
|
+
await table();
|
|
209
|
+
const sets = ['"updated_at" = now()'];
|
|
210
|
+
const params = [id];
|
|
211
|
+
if (patch.status !== undefined)
|
|
212
|
+
sets.push(`"status" = $${params.push(patch.status)}`);
|
|
213
|
+
if (patch.runAt !== undefined)
|
|
214
|
+
sets.push(`"run_at" = $${params.push(patch.runAt)}`);
|
|
215
|
+
if (patch.lastError !== undefined)
|
|
216
|
+
sets.push(`"last_error" = $${params.push(patch.lastError)}`);
|
|
217
|
+
await sql(`UPDATE "inferlo_jobs" SET ${sets.join(", ")} WHERE "id" = $1`, params);
|
|
218
|
+
},
|
|
219
|
+
async get(id) {
|
|
220
|
+
await table();
|
|
221
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id))
|
|
222
|
+
return null;
|
|
223
|
+
const rows = await sql(`SELECT ${JOB_COLUMNS} FROM "inferlo_jobs" WHERE "id" = $1`, [id]);
|
|
224
|
+
return rows[0] ? fromRow(rows[0]) : null;
|
|
225
|
+
},
|
|
226
|
+
async touch(id) {
|
|
227
|
+
await table();
|
|
228
|
+
await sql(`UPDATE "inferlo_jobs" SET "updated_at" = now() WHERE "id" = $1 AND "status" = 'running'`, [id]);
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/** Pick the job store for createApp({ jobQueue }). Raises IL6011 for an unknown store, IL6012 for "database" without Postgres. */
|
|
233
|
+
export function jobStoreFor(config, adapter) {
|
|
234
|
+
const store = config?.store ?? "memory";
|
|
235
|
+
if (store === "memory")
|
|
236
|
+
return memoryJobStore();
|
|
237
|
+
if (store === "database")
|
|
238
|
+
return databaseJobStore(adapter);
|
|
239
|
+
throw new InferloError("IL6011", `Invalid jobQueue store "${String(store)}".`, { fix: 'Use createApp({ jobQueue: { store: "memory" } }) (the default) or { store: "database" }.' });
|
|
240
|
+
}
|
|
241
|
+
export function createJobs(list, runtime, crons = []) {
|
|
242
|
+
const byName = new Map(list.map((j) => [j.name, j]));
|
|
243
|
+
const names = list.map((j) => j.name);
|
|
244
|
+
const running = new Set();
|
|
245
|
+
const registered = (j) => {
|
|
246
|
+
const found = isJob(j) ? byName.get(j.name) : undefined;
|
|
247
|
+
if (!found) {
|
|
248
|
+
const name = isJob(j) ? `"${j.name}"` : "This value";
|
|
249
|
+
throw new InferloError("IL6010", `${name} is not a registered job.`, {
|
|
250
|
+
fix: "Pass a job defined with job(...).handle(...) and add it (or its module) to createApp({ jobs: [...] }).",
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return found;
|
|
254
|
+
};
|
|
255
|
+
const parse = (j, input) => {
|
|
256
|
+
const result = j.options.input.safeParse(input);
|
|
257
|
+
if (!result.ok)
|
|
258
|
+
throw new ValidationError(result.issues, `input for job "${j.name}"`);
|
|
259
|
+
return result.value;
|
|
260
|
+
};
|
|
261
|
+
const record = (def, input, runAt, id) => {
|
|
262
|
+
// Stored as JSON, so it survives a restart with a database store; parsed again when it runs.
|
|
263
|
+
const stored = JSON.parse(JSON.stringify(parse(def, input) ?? null));
|
|
264
|
+
const now = new Date();
|
|
265
|
+
return {
|
|
266
|
+
id,
|
|
267
|
+
name: def.name,
|
|
268
|
+
input: stored,
|
|
269
|
+
status: "pending",
|
|
270
|
+
attempts: 0,
|
|
271
|
+
maxAttempts: 1 + (def.options.retries ?? 3),
|
|
272
|
+
runAt: runAt ?? now,
|
|
273
|
+
createdAt: now,
|
|
274
|
+
updatedAt: now,
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
/** Queue the job of every cron tick in (from, to]. Only the latest tick of each cron is queued, so a pause does not cause a burst. */
|
|
278
|
+
const queueCrons = async (from, to) => {
|
|
279
|
+
for (const c of crons) {
|
|
280
|
+
let tick = c.next(from);
|
|
281
|
+
if (tick.getTime() > to.getTime())
|
|
282
|
+
continue;
|
|
283
|
+
for (let later = c.next(tick); later.getTime() <= to.getTime(); later = c.next(later))
|
|
284
|
+
tick = later;
|
|
285
|
+
await runtime.store.add(record(c.job, {}, tick, await cronTickId(c, tick)));
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
const jobs = {
|
|
289
|
+
list,
|
|
290
|
+
crons,
|
|
291
|
+
queueCrons,
|
|
292
|
+
queue: runtime.queue ?? "memory",
|
|
293
|
+
async enqueue(j, input, options = {}) {
|
|
294
|
+
const def = registered(j);
|
|
295
|
+
if (options.runAt !== undefined && (!(options.runAt instanceof Date) || Number.isNaN(options.runAt.getTime()))) {
|
|
296
|
+
throw new InferloError("IL6009", `runAt for job "${def.name}" must be a valid Date.`, { fix: "Pass runAt: new Date(Date.now() + 60_000) or leave it out to run now." });
|
|
297
|
+
}
|
|
298
|
+
const queued = record(def, input, options.runAt, crypto.randomUUID());
|
|
299
|
+
await runtime.store.add(queued);
|
|
300
|
+
return queued;
|
|
301
|
+
},
|
|
302
|
+
get: (id) => runtime.store.get(id),
|
|
303
|
+
async runDue(options = {}) {
|
|
304
|
+
const result = { done: 0, retried: 0, failed: 0 };
|
|
305
|
+
while (true) {
|
|
306
|
+
// Without options.now, jobs enqueued by the jobs that run (follow-ups) run in the same call.
|
|
307
|
+
const now = options.now ?? new Date();
|
|
308
|
+
const [record] = await runtime.store.claim(now, 1, names);
|
|
309
|
+
if (!record)
|
|
310
|
+
return result;
|
|
311
|
+
result[await run(record, now)]++;
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
start(options = {}) {
|
|
315
|
+
const pollMs = options.pollMs ?? 1000;
|
|
316
|
+
let stopped = false;
|
|
317
|
+
let timer;
|
|
318
|
+
let wake;
|
|
319
|
+
let cronFrom = new Date();
|
|
320
|
+
const loop = (async () => {
|
|
321
|
+
while (!stopped) {
|
|
322
|
+
try {
|
|
323
|
+
if (crons.length) {
|
|
324
|
+
const now = new Date();
|
|
325
|
+
await queueCrons(cronFrom, now);
|
|
326
|
+
cronFrom = now;
|
|
327
|
+
}
|
|
328
|
+
await jobs.runDue();
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
runtime.logger.error("job worker error", error);
|
|
332
|
+
}
|
|
333
|
+
if (stopped)
|
|
334
|
+
break;
|
|
335
|
+
await new Promise((resolve) => {
|
|
336
|
+
wake = resolve;
|
|
337
|
+
timer = setTimeout(resolve, pollMs);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
})();
|
|
341
|
+
const worker = {
|
|
342
|
+
async stop() {
|
|
343
|
+
stopped = true;
|
|
344
|
+
if (timer)
|
|
345
|
+
clearTimeout(timer);
|
|
346
|
+
wake?.();
|
|
347
|
+
await loop;
|
|
348
|
+
running.delete(worker);
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
running.add(worker);
|
|
352
|
+
return worker;
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
workers.set(jobs, running);
|
|
356
|
+
async function run(record, now) {
|
|
357
|
+
const started = performance.now();
|
|
358
|
+
const outcome = await attempt(record, now);
|
|
359
|
+
runtime.onRun?.(record.name, outcome, performance.now() - started);
|
|
360
|
+
return outcome;
|
|
361
|
+
}
|
|
362
|
+
async function attempt(record, now) {
|
|
363
|
+
const def = byName.get(record.name);
|
|
364
|
+
const touch = runtime.store.touch?.bind(runtime.store);
|
|
365
|
+
const heartbeat = touch ? setInterval(() => void touch(record.id).catch(() => { }), runtime.heartbeatMs ?? HEARTBEAT_MS) : undefined;
|
|
366
|
+
try {
|
|
367
|
+
if (!def)
|
|
368
|
+
throw new InferloError("IL6010", `"${record.name}" is not a registered job.`, { fix: "Add the job to createApp({ jobs: [...] }) in the process that runs jobs." });
|
|
369
|
+
await def.handler({ input: parse(def, record.input), db: runtime.db, ai: runtime.ai, jobs, id: record.id, attempt: record.attempts });
|
|
370
|
+
await runtime.store.update(record.id, { status: "done" });
|
|
371
|
+
return "done";
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
375
|
+
if (record.attempts < record.maxAttempts) {
|
|
376
|
+
await runtime.store.update(record.id, { status: "pending", lastError: message, runAt: new Date(now.getTime() + retryDelayMs(record.attempts)) });
|
|
377
|
+
runtime.logger.error(`job "${record.name}" failed (attempt ${record.attempts} of ${record.maxAttempts}), will retry`, { id: record.id, error: message });
|
|
378
|
+
return "retried";
|
|
379
|
+
}
|
|
380
|
+
await runtime.store.update(record.id, { status: "failed", lastError: message });
|
|
381
|
+
runtime.logger.error(`job "${record.name}" failed after ${record.attempts} attempt(s)`, { id: record.id, error: message });
|
|
382
|
+
return "failed";
|
|
383
|
+
}
|
|
384
|
+
finally {
|
|
385
|
+
if (heartbeat)
|
|
386
|
+
clearInterval(heartbeat);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return jobs;
|
|
390
|
+
}
|
|
391
|
+
//# sourceMappingURL=jobs.js.map
|
package/dist/jobs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Structured fields of a log entry, such as the request id. Values are strings, numbers or booleans. */
|
|
2
|
+
export type LogFields = Record<string, string | number | boolean | undefined>;
|
|
3
|
+
export interface LoggingConfig {
|
|
4
|
+
/** Output of the built-in logger: "text" (default, readable) or "json" (one JSON object per line, for log collectors). */
|
|
5
|
+
format?: "text" | "json";
|
|
6
|
+
/** Log every request with its id, method, path, status and duration in milliseconds. Default false. */
|
|
7
|
+
requests?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Incoming X-Request-Id values that are kept (with trustProxy): 1 to 128 letters, digits and . _ : - characters. */
|
|
10
|
+
export declare const REQUEST_ID: RegExp;
|
|
11
|
+
export interface LoggerShape {
|
|
12
|
+
info(message: string, data?: unknown, fields?: LogFields): void;
|
|
13
|
+
error(message: string, data?: unknown, fields?: LogFields): void;
|
|
14
|
+
}
|
|
15
|
+
/** Checks createApp({ logging }) and returns it with defaults. */
|
|
16
|
+
export declare function loggingOptions(option: unknown, hasLogger: boolean): {
|
|
17
|
+
format: "text" | "json";
|
|
18
|
+
requests: boolean;
|
|
19
|
+
};
|
|
20
|
+
/** The built-in logger: "[inferlo] message" lines, or one JSON object per line. */
|
|
21
|
+
export declare function defaultLogger(format: "text" | "json", app: string): LoggerShape;
|
|
22
|
+
/** An error as plain JSON: name, message, code and status when present, stack, and causes. */
|
|
23
|
+
export declare function serializeError(error: Error, depth?: number): Record<string, unknown>;
|
package/dist/logging.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging: the built-in logger (text or JSON lines), request ids and request logs.
|
|
3
|
+
*
|
|
4
|
+
* createApp({ logging: { format: "json", requests: true } })
|
|
5
|
+
*
|
|
6
|
+
* prints one JSON object per line, and one line per request with its id, method, path, status and duration.
|
|
7
|
+
*/
|
|
8
|
+
import { InferloError } from "./errors.js";
|
|
9
|
+
/** Incoming X-Request-Id values that are kept (with trustProxy): 1 to 128 letters, digits and . _ : - characters. */
|
|
10
|
+
export const REQUEST_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
11
|
+
/** Checks createApp({ logging }) and returns it with defaults. */
|
|
12
|
+
export function loggingOptions(option, hasLogger) {
|
|
13
|
+
if (option === undefined)
|
|
14
|
+
return { format: "text", requests: false };
|
|
15
|
+
const problems = [];
|
|
16
|
+
if (!option || typeof option !== "object" || Array.isArray(option))
|
|
17
|
+
problems.push("logging must be an object such as { format: \"json\", requests: true }");
|
|
18
|
+
else {
|
|
19
|
+
const { format, requests, ...rest } = option;
|
|
20
|
+
if (format !== undefined && format !== "text" && format !== "json")
|
|
21
|
+
problems.push(`format must be "text" or "json", got ${JSON.stringify(format)}`);
|
|
22
|
+
if (format !== undefined && hasLogger)
|
|
23
|
+
problems.push("format only applies to the built-in logger; your own logger decides how entries are written");
|
|
24
|
+
if (requests !== undefined && typeof requests !== "boolean")
|
|
25
|
+
problems.push("requests must be true or false");
|
|
26
|
+
for (const key of Object.keys(rest))
|
|
27
|
+
problems.push(`unknown option "${key}"`);
|
|
28
|
+
}
|
|
29
|
+
if (problems.length) {
|
|
30
|
+
throw new InferloError("IL6019", `Invalid logging options: ${problems.join("; ")}.`, {
|
|
31
|
+
fix: 'Use createApp({ logging: { format: "json", requests: true } }). With your own logger, leave out format and read the third argument of info and error for fields such as requestId.',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const { format = "text", requests = false } = option;
|
|
35
|
+
return { format, requests };
|
|
36
|
+
}
|
|
37
|
+
/** The built-in logger: "[inferlo] message" lines, or one JSON object per line. */
|
|
38
|
+
export function defaultLogger(format, app) {
|
|
39
|
+
if (format === "text") {
|
|
40
|
+
const line = (message, fields) => `[inferlo] ${message}${fields?.requestId ? ` (request ${fields.requestId})` : ""}`;
|
|
41
|
+
return {
|
|
42
|
+
info: (message, data, fields) => console.log(line(message, fields), data ?? ""),
|
|
43
|
+
error: (message, data, fields) => console.error(line(message, fields), data ?? ""),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const write = (level, message, data, fields) => {
|
|
47
|
+
const entry = { time: new Date().toISOString(), level, app, msg: message };
|
|
48
|
+
for (const [key, value] of Object.entries(fields ?? {}))
|
|
49
|
+
if (value !== undefined && !(key in entry))
|
|
50
|
+
entry[key] = value;
|
|
51
|
+
if (data instanceof Error)
|
|
52
|
+
entry.error = serializeError(data);
|
|
53
|
+
else if (data !== undefined && data !== "")
|
|
54
|
+
entry.data = data;
|
|
55
|
+
let text;
|
|
56
|
+
try {
|
|
57
|
+
text = JSON.stringify(entry);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
text = JSON.stringify({ ...entry, data: String(data) });
|
|
61
|
+
}
|
|
62
|
+
if (level === "error")
|
|
63
|
+
console.error(text);
|
|
64
|
+
else
|
|
65
|
+
console.log(text);
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
info: (message, data, fields) => write("info", message, data, fields),
|
|
69
|
+
error: (message, data, fields) => write("error", message, data, fields),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** An error as plain JSON: name, message, code and status when present, stack, and causes. */
|
|
73
|
+
export function serializeError(error, depth = 0) {
|
|
74
|
+
const out = { name: error.name, message: error.message };
|
|
75
|
+
const extra = error;
|
|
76
|
+
if (typeof extra.code === "string" || typeof extra.code === "number")
|
|
77
|
+
out.code = extra.code;
|
|
78
|
+
if (typeof extra.status === "number")
|
|
79
|
+
out.status = extra.status;
|
|
80
|
+
if (error.stack)
|
|
81
|
+
out.stack = error.stack;
|
|
82
|
+
const cause = error.cause;
|
|
83
|
+
if (cause !== undefined && depth < 5)
|
|
84
|
+
out.cause = cause instanceof Error ? serializeError(cause, depth + 1) : cause;
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Built-in MCP server (Model Context Protocol, Streamable HTTP transport, JSON responses).
|
|
3
|
-
* Routes with `tool: true` become tools. Tool calls run through the normal router, so auth,
|
|
4
|
-
* roles and validation apply exactly as they do for HTTP clients: the caller's Authorization
|
|
5
|
-
* header is forwarded.
|
|
6
|
-
*/
|
|
7
1
|
import type { App } from "./app.ts";
|
|
8
2
|
import type { RouteDef } from "./route.ts";
|
|
9
3
|
import type { JSONSchema } from "./schema.ts";
|
|
@@ -15,4 +9,8 @@ export interface ToolDefinition {
|
|
|
15
9
|
}
|
|
16
10
|
export declare function toolName(route: RouteDef): string;
|
|
17
11
|
export declare function toolDefinitions(app: App<any>): ToolDefinition[];
|
|
12
|
+
/** The tool definition of one route: its name, description and a JSON Schema of params, query and body together. */
|
|
13
|
+
export declare function toolDefinition(route: RouteDef): ToolDefinition;
|
|
18
14
|
export declare function handleMcp(app: App<any>, req: Request, headers: Headers, testUsers: WeakMap<Request, unknown>): Promise<Response>;
|
|
15
|
+
/** Run a tool call as an HTTP request to its route, forwarding the caller's credentials, so auth, roles and validation apply. */
|
|
16
|
+
export declare function callTool(app: App<any>, tool: ToolDefinition, args: Record<string, unknown>, original: Request, testUsers: WeakMap<Request, unknown>): Promise<Response>;
|