@browserlessapi/inferlo 0.6.0 → 0.8.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 +9 -5
- package/dist/ai/ai-sdk.d.ts +48 -0
- package/dist/ai/ai-sdk.js +235 -0
- package/dist/ai/ai-sdk.js.map +1 -0
- package/dist/ai/index.d.ts +37 -3
- package/dist/ai/index.js +96 -7
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/public.d.ts +5 -1
- package/dist/ai/public.js +2 -0
- package/dist/ai/public.js.map +1 -1
- package/dist/ai/ui.d.ts +47 -0
- package/dist/ai/ui.js +33 -0
- package/dist/ai/ui.js.map +1 -0
- package/dist/app.d.ts +55 -4
- package/dist/app.js +439 -39
- package/dist/app.js.map +1 -1
- package/dist/astro.d.ts +60 -0
- package/dist/astro.js +68 -0
- package/dist/astro.js.map +1 -0
- package/dist/audit.d.ts +8 -1
- package/dist/audit.js +9 -2
- package/dist/audit.js.map +1 -1
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +1 -0
- package/dist/auth.js.map +1 -1
- package/dist/authjs.d.ts +70 -0
- package/dist/authjs.js +94 -0
- package/dist/authjs.js.map +1 -0
- package/dist/azure.d.ts +66 -0
- package/dist/azure.js +119 -0
- package/dist/azure.js.map +1 -0
- package/dist/better-auth.d.ts +89 -0
- package/dist/better-auth.js +75 -0
- package/dist/better-auth.js.map +1 -0
- package/dist/cache.d.ts +13 -9
- package/dist/cache.js +11 -9
- package/dist/cache.js.map +1 -1
- package/dist/cli/main.js +5 -5
- package/dist/cli/main.js.map +1 -1
- package/dist/client-ip.d.ts +41 -1
- package/dist/client-ip.js +85 -1
- package/dist/client-ip.js.map +1 -1
- package/dist/cross-site.d.ts +14 -0
- package/dist/cross-site.js +56 -0
- package/dist/cross-site.js.map +1 -0
- package/dist/db/memory.js +2 -1
- package/dist/db/memory.js.map +1 -1
- package/dist/db/postgres.js +23 -6
- package/dist/db/postgres.js.map +1 -1
- package/dist/db/table.d.ts +37 -5
- package/dist/db/table.js +96 -15
- package/dist/db/table.js.map +1 -1
- package/dist/db/types.d.ts +2 -0
- package/dist/describe.d.ts +23 -5
- package/dist/describe.js +20 -2
- package/dist/describe.js.map +1 -1
- package/dist/disk-storage.d.ts +6 -0
- package/dist/disk-storage.js +96 -0
- package/dist/disk-storage.js.map +1 -0
- package/dist/events.d.ts +134 -0
- package/dist/events.js +196 -0
- package/dist/events.js.map +1 -0
- package/dist/files.d.ts +23 -0
- package/dist/files.js +168 -0
- package/dist/files.js.map +1 -0
- package/dist/index.d.ts +26 -8
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +56 -10
- package/dist/jobs.js +71 -13
- package/dist/jobs.js.map +1 -1
- package/dist/jwt.d.ts +83 -0
- package/dist/jwt.js +312 -0
- package/dist/jwt.js.map +1 -0
- package/dist/langchain.d.ts +58 -0
- package/dist/langchain.js +220 -0
- package/dist/langchain.js.map +1 -0
- package/dist/model.d.ts +23 -6
- package/dist/model.js +41 -7
- package/dist/model.js.map +1 -1
- package/dist/oidc.d.ts +70 -0
- package/dist/oidc.js +379 -0
- package/dist/oidc.js.map +1 -0
- package/dist/openapi.js +17 -2
- package/dist/openapi.js.map +1 -1
- package/dist/passport.d.ts +54 -0
- package/dist/passport.js +68 -0
- package/dist/passport.js.map +1 -0
- package/dist/postgres-js.d.ts +39 -0
- package/dist/postgres-js.js +71 -0
- package/dist/postgres-js.js.map +1 -0
- package/dist/rate-limit.d.ts +5 -3
- package/dist/rate-limit.js +3 -3
- package/dist/rate-limit.js.map +1 -1
- package/dist/redis-events.d.ts +27 -0
- package/dist/redis-events.js +163 -0
- package/dist/redis-events.js.map +1 -0
- package/dist/redis-jobs.d.ts +24 -0
- package/dist/redis-jobs.js +203 -0
- package/dist/redis-jobs.js.map +1 -0
- package/dist/redis-sockets.d.ts +24 -0
- package/dist/redis-sockets.js +75 -0
- package/dist/redis-sockets.js.map +1 -0
- package/dist/redis.d.ts +30 -0
- package/dist/redis.js +106 -0
- package/dist/redis.js.map +1 -0
- package/dist/response-body.d.ts +6 -0
- package/dist/response-body.js +7 -0
- package/dist/response-body.js.map +1 -0
- package/dist/route.d.ts +117 -3
- package/dist/route.js +27 -0
- package/dist/route.js.map +1 -1
- package/dist/schema.d.ts +23 -3
- package/dist/schema.js +70 -0
- package/dist/schema.js.map +1 -1
- package/dist/serve.d.ts +24 -1
- package/dist/serve.js +200 -8
- package/dist/serve.js.map +1 -1
- package/dist/service-client.d.ts +91 -0
- package/dist/service-client.js +177 -0
- package/dist/service-client.js.map +1 -0
- package/dist/sessions.d.ts +2 -0
- package/dist/sessions.js +36 -4
- package/dist/sessions.js.map +1 -1
- package/dist/sockets.d.ts +58 -0
- package/dist/sockets.js +111 -0
- package/dist/sockets.js.map +1 -0
- package/dist/storage.d.ts +47 -0
- package/dist/storage.js +123 -0
- package/dist/storage.js.map +1 -0
- package/dist/tracing.d.ts +7 -3
- package/dist/tracing.js +8 -2
- package/dist/tracing.js.map +1 -1
- package/dist/websocket.d.ts +81 -0
- package/dist/websocket.js +251 -0
- package/dist/websocket.js.map +1 -0
- package/llms.txt +2 -0
- package/package.json +77 -5
- package/templates/app/AGENTS.md +9 -5
- package/templates/app/test/notes.test.ts +2 -0
package/dist/describe.js
CHANGED
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
import { describeDeprecation, resolveDeprecation } from "./deprecation.js";
|
|
6
6
|
import { toolName } from "./mcp.js";
|
|
7
7
|
import { columnName } from "./model.js";
|
|
8
|
-
|
|
8
|
+
import { containsFile } from "./schema.js";
|
|
9
|
+
export const INFERLO_VERSION = "0.8.0";
|
|
9
10
|
function field(name, schema) {
|
|
10
11
|
const d = schema.def;
|
|
11
12
|
const out = {
|
|
12
13
|
type: d.kind,
|
|
13
|
-
required: !d.optional && !d.hasDefault,
|
|
14
|
+
required: !d.optional && !d.hasDefault && !d.system,
|
|
14
15
|
nullable: d.nullable,
|
|
15
16
|
column: columnName(name),
|
|
16
17
|
};
|
|
@@ -55,6 +56,7 @@ export function describeApp(app, options) {
|
|
|
55
56
|
models: app.models.map((m) => ({
|
|
56
57
|
name: m.name,
|
|
57
58
|
...(info.modelOwner.has(m.name) ? { module: info.modelOwner.get(m.name) } : {}),
|
|
59
|
+
...(m.tenant ? { tenant: true } : {}),
|
|
58
60
|
fields: Object.fromEntries([
|
|
59
61
|
["id", { type: "uuid", required: false, nullable: false, column: "id", description: "Set automatically" }],
|
|
60
62
|
...Object.entries(m.fields).map(([k, s]) => [k, field(k, s)]),
|
|
@@ -65,6 +67,12 @@ export function describeApp(app, options) {
|
|
|
65
67
|
routes: app.routes.map((r) => {
|
|
66
68
|
const o = r.options;
|
|
67
69
|
const out = { method: r.method, path: r.path, auth: o.auth ?? "user" };
|
|
70
|
+
if (r.websocket) {
|
|
71
|
+
out.websocket = true;
|
|
72
|
+
const message = o.message;
|
|
73
|
+
if (message)
|
|
74
|
+
out.message = message.toJSONSchema();
|
|
75
|
+
}
|
|
68
76
|
if (r.name)
|
|
69
77
|
out.name = r.name;
|
|
70
78
|
if (r.version)
|
|
@@ -92,12 +100,22 @@ export function describeApp(app, options) {
|
|
|
92
100
|
out.query = o.query.toJSONSchema();
|
|
93
101
|
if (o.body)
|
|
94
102
|
out.body = o.body.toJSONSchema();
|
|
103
|
+
if (o.body && containsFile(o.body))
|
|
104
|
+
out.bodyType = "multipart/form-data";
|
|
95
105
|
return out;
|
|
96
106
|
}),
|
|
97
107
|
ai: { configured: app.ai.configured, aliases: app.ai.aliases },
|
|
98
108
|
jobQueue: app.jobs.queue,
|
|
99
109
|
cache: app.cache.store,
|
|
110
|
+
storage: app.storage.store,
|
|
100
111
|
crons: app.jobs.crons.map((c) => ({ schedule: c.schedule, job: c.job.name })),
|
|
112
|
+
events: app.events.list.map((e) => ({
|
|
113
|
+
name: e.name,
|
|
114
|
+
...(e.summary ? { summary: e.summary } : {}),
|
|
115
|
+
payload: e.payload.toJSONSchema(),
|
|
116
|
+
jobs: app.events.subscriptions.filter((s) => s.event === e).map((s) => s.job.name),
|
|
117
|
+
})),
|
|
118
|
+
...(app.events.transport ? { eventTransport: app.events.transport } : {}),
|
|
101
119
|
jobs: app.jobs.list.map((j) => ({ name: j.name, ...(info.jobModule.has(j.name) ? { module: info.jobModule.get(j.name) } : {}), ...(j.options.summary ? { summary: j.options.summary } : {}), retries: j.options.retries ?? 3, input: j.options.input.toJSONSchema() })),
|
|
102
120
|
};
|
|
103
121
|
}
|
package/dist/describe.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3E,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type StorageStore } from "./storage.ts";
|
|
2
|
+
export interface DiskStorageOptions {
|
|
3
|
+
/** The directory files are kept in (created on first write). Relative paths start at the working directory. */
|
|
4
|
+
dir: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function diskStorage(options: DiskStorageOptions): StorageStore;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* diskStorage({ dir }): files in a local or mounted directory (Node.js, Bun and Deno).
|
|
3
|
+
*
|
|
4
|
+
* Each file is written to a temporary file and renamed into place, so readers never see half a file. Its media type
|
|
5
|
+
* is kept next to it in dir/.inferlo-meta/. Keys never start a segment with ".", so they cannot reach those files.
|
|
6
|
+
* node:fs is loaded on first use, so importing Inferlo stays free of Node-only modules.
|
|
7
|
+
*/
|
|
8
|
+
import { InferloError } from "./errors.js";
|
|
9
|
+
import { checkStorageKey } from "./storage.js";
|
|
10
|
+
const META_DIR = ".inferlo-meta";
|
|
11
|
+
const TMP_DIR = ".inferlo-tmp";
|
|
12
|
+
function isMissing(error) {
|
|
13
|
+
return error?.code === "ENOENT";
|
|
14
|
+
}
|
|
15
|
+
export function diskStorage(options) {
|
|
16
|
+
if (!options || typeof options.dir !== "string" || options.dir.trim() === "") {
|
|
17
|
+
throw new InferloError("IL6038", "diskStorage() needs a dir.", { fix: 'Pass the directory to keep files in: diskStorage({ dir: "./uploads" }), or env("UPLOADS_DIR") in production.' });
|
|
18
|
+
}
|
|
19
|
+
let loaded;
|
|
20
|
+
const load = () => (loaded ??= Promise.all([import("node:fs/promises"), import("node:path")]).then(([fs, path]) => ({ fs, path, root: path.resolve(options.dir) })));
|
|
21
|
+
/** The file's path, checked to stay inside the directory. */
|
|
22
|
+
const resolve = async (base, key) => {
|
|
23
|
+
const { path, root } = await load();
|
|
24
|
+
checkStorageKey(key);
|
|
25
|
+
const top = path.join(root, base);
|
|
26
|
+
const full = path.resolve(top, ...key.split("/"));
|
|
27
|
+
if (!full.startsWith(top + path.sep))
|
|
28
|
+
throw new InferloError("IL6039", `Invalid storage key ${JSON.stringify(key)}: it leaves the storage directory.`);
|
|
29
|
+
return full;
|
|
30
|
+
};
|
|
31
|
+
const writeAtomic = async (target, data) => {
|
|
32
|
+
const { fs, path, root } = await load();
|
|
33
|
+
const tmpDir = path.join(root, TMP_DIR);
|
|
34
|
+
await fs.mkdir(tmpDir, { recursive: true });
|
|
35
|
+
const tmp = path.join(tmpDir, crypto.randomUUID());
|
|
36
|
+
try {
|
|
37
|
+
await fs.writeFile(tmp, data);
|
|
38
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
39
|
+
await fs.rename(tmp, target);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
await fs.rm(tmp, { force: true });
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
name: "disk",
|
|
48
|
+
async put(key, bytes, type) {
|
|
49
|
+
const file = await resolve("", key);
|
|
50
|
+
// The type first: a reader that sees the new bytes also sees their type.
|
|
51
|
+
await writeAtomic(await resolve(META_DIR, key), type);
|
|
52
|
+
await writeAtomic(file, bytes);
|
|
53
|
+
},
|
|
54
|
+
async get(key) {
|
|
55
|
+
const { fs } = await load();
|
|
56
|
+
let bytes;
|
|
57
|
+
try {
|
|
58
|
+
bytes = new Uint8Array(await fs.readFile(await resolve("", key)));
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
// A directory under the key (other keys below it) is not a file either.
|
|
62
|
+
if (isMissing(error) || error.code === "EISDIR")
|
|
63
|
+
return null;
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
let type = "application/octet-stream";
|
|
67
|
+
try {
|
|
68
|
+
type = (await fs.readFile(await resolve(META_DIR, key), "utf8")).trim() || type;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (!isMissing(error))
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
return { bytes, type };
|
|
75
|
+
},
|
|
76
|
+
async delete(key) {
|
|
77
|
+
const { fs } = await load();
|
|
78
|
+
const file = await resolve("", key);
|
|
79
|
+
let existed = true;
|
|
80
|
+
try {
|
|
81
|
+
const stat = await fs.stat(file);
|
|
82
|
+
if (!stat.isFile())
|
|
83
|
+
return false;
|
|
84
|
+
await fs.rm(file);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (!isMissing(error))
|
|
88
|
+
throw error;
|
|
89
|
+
existed = false;
|
|
90
|
+
}
|
|
91
|
+
await fs.rm(await resolve(META_DIR, key), { force: true });
|
|
92
|
+
return existed;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=disk-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk-storage.js","sourceRoot":"","sources":["../src/disk-storage.ts"],"names":[],"mappings":"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"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { InferloError } from "./errors.ts";
|
|
2
|
+
import type { JobDef, JobInput } from "./jobs.ts";
|
|
3
|
+
import { type AnySchema, type Infer, type InferInput } from "./schema.ts";
|
|
4
|
+
import type { JobTraceContext } from "./tracing.ts";
|
|
5
|
+
export interface EventOptions<P extends AnySchema> {
|
|
6
|
+
/** Schema of the payload. Checked when the event is published. */
|
|
7
|
+
payload: P;
|
|
8
|
+
/** One line shown in describe. */
|
|
9
|
+
summary?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface EventDef<Input = any, Payload = any, S extends AnySchema = AnySchema> {
|
|
12
|
+
readonly kind: "inferlo.event";
|
|
13
|
+
/** Lowercase words separated by dots: "order.paid". */
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/** The payload schema, also usable as a subscribed job's input: job("send-receipt", { input: orderPaid.payload }). */
|
|
16
|
+
readonly payload: S;
|
|
17
|
+
readonly summary?: string;
|
|
18
|
+
/** Type only: what publish accepts. */
|
|
19
|
+
readonly $input?: Input;
|
|
20
|
+
/** Type only: the payload after validation, which subscribed jobs receive. */
|
|
21
|
+
readonly $payload?: Payload;
|
|
22
|
+
}
|
|
23
|
+
/** What publish accepts for an event. */
|
|
24
|
+
export type EventPayload<E> = E extends EventDef<infer I, any, any> ? I : never;
|
|
25
|
+
/** A subscription: on(event, job) in createApp({ jobs }) queues the job with the payload of every published event. */
|
|
26
|
+
export interface OnDef<E extends EventDef = EventDef, J extends JobDef = JobDef> {
|
|
27
|
+
readonly kind: "inferlo.on";
|
|
28
|
+
readonly event: E;
|
|
29
|
+
readonly job: J;
|
|
30
|
+
}
|
|
31
|
+
export interface PublishOptions {
|
|
32
|
+
/** The event id. Default: a new UUID. Publishing the same id again queues no job twice (for outboxes and redelivery). */
|
|
33
|
+
id?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The tenant the event belongs to: the subscribed jobs get it (their ctx.db is scoped to it). Default: the tenant
|
|
36
|
+
* of the request or job that publishes it, none from app.events. Pass null to publish it without a tenant.
|
|
37
|
+
*/
|
|
38
|
+
tenantId?: string | null;
|
|
39
|
+
}
|
|
40
|
+
export interface PublishedEvent {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
publishedAt: Date;
|
|
44
|
+
/**
|
|
45
|
+
* Ids of the jobs for it, one per subscription in this app. Without a transport they are queued now; with one,
|
|
46
|
+
* they are queued with these ids when this app receives the event.
|
|
47
|
+
*/
|
|
48
|
+
jobs: string[];
|
|
49
|
+
}
|
|
50
|
+
export interface Events {
|
|
51
|
+
/** Validate the payload and queue every job subscribed to the event with on(event, job). */
|
|
52
|
+
publish<E extends EventDef>(event: E, payload: EventPayload<E>, options?: PublishOptions): Promise<PublishedEvent>;
|
|
53
|
+
}
|
|
54
|
+
/** app.events: publish events and see the subscriptions. */
|
|
55
|
+
export interface AppEvents extends Events {
|
|
56
|
+
/** Events with at least one subscription in this app, in the order they were registered. */
|
|
57
|
+
readonly list: readonly EventDef[];
|
|
58
|
+
/** The subscriptions: on(event, job) values in createApp({ jobs }) and in modules. */
|
|
59
|
+
readonly subscriptions: readonly OnDef[];
|
|
60
|
+
/** The transport that carries events between services (createApp({ events: { transport } })), or undefined: events stay in this app. */
|
|
61
|
+
readonly transport: string | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* With a transport: take the events waiting for this app's subscriptions and queue their jobs, then return how
|
|
64
|
+
* many events were received. app.jobs.start() and inferlo worker call it on every poll; call it yourself in tests.
|
|
65
|
+
* Without a transport it returns 0.
|
|
66
|
+
*/
|
|
67
|
+
receive(): Promise<number>;
|
|
68
|
+
}
|
|
69
|
+
/** One published event as a transport carries it. */
|
|
70
|
+
export interface EventMessage {
|
|
71
|
+
/** The event id: the same id queues each subscribed job once, also when the transport delivers it twice. */
|
|
72
|
+
id: string;
|
|
73
|
+
name: string;
|
|
74
|
+
/** The validated payload, as JSON. */
|
|
75
|
+
payload: unknown;
|
|
76
|
+
publishedAt: Date;
|
|
77
|
+
/** With createApp({ tracing }): the trace the event was published in, which the subscribed jobs continue. */
|
|
78
|
+
trace?: JobTraceContext;
|
|
79
|
+
/** The tenant the event belongs to (multi-tenancy), which the subscribed jobs get. */
|
|
80
|
+
tenantId?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Carries events between services: createApp({ events: { transport } }). Inferlo has a Redis Streams transport
|
|
84
|
+
* built in (transport: "redis"); other brokers implement this interface.
|
|
85
|
+
*/
|
|
86
|
+
export interface EventTransport {
|
|
87
|
+
/** Shown as app.events.transport and in describe: "redis". */
|
|
88
|
+
readonly name: string;
|
|
89
|
+
/** Send one event to every service subscribed to its name. */
|
|
90
|
+
send(message: EventMessage): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Deliver the waiting events with these names to deliver(), one at a time, and acknowledge each after deliver()
|
|
93
|
+
* resolves; an event whose deliver() rejects is delivered again later. Returns how many events were delivered.
|
|
94
|
+
*/
|
|
95
|
+
receive(names: readonly string[], deliver: (message: EventMessage) => Promise<void>): Promise<number>;
|
|
96
|
+
}
|
|
97
|
+
export interface EventsConfig {
|
|
98
|
+
/**
|
|
99
|
+
* How events reach other services. Default: none, publishing queues the subscribed jobs of this app only.
|
|
100
|
+
* "redis": Redis Streams in the Redis of createApp({ redis }), one consumer group per app name, so every
|
|
101
|
+
* service subscribed to an event gets it once (at least once, with each job queued once).
|
|
102
|
+
*/
|
|
103
|
+
transport?: "redis" | EventTransport;
|
|
104
|
+
}
|
|
105
|
+
/** Define an event: event("order.paid", { payload: t.object({ ... }) }). */
|
|
106
|
+
export declare function event<P extends AnySchema>(name: string, options: EventOptions<P>): EventDef<InferInput<P>, Infer<P>, P>;
|
|
107
|
+
export declare function isEvent(value: unknown): value is EventDef;
|
|
108
|
+
export declare function isOn(value: unknown): value is OnDef;
|
|
109
|
+
/**
|
|
110
|
+
* Subscribe a job to an event: on(orderPaid, sendReceipt) in createApp({ jobs }). Every published event queues the
|
|
111
|
+
* job with the payload as its input (use input: orderPaid.payload); a job whose input is an object with fewer fields
|
|
112
|
+
* gets only those fields.
|
|
113
|
+
*/
|
|
114
|
+
export declare function on<E extends EventDef, J extends JobDef>(event: E, job: J & (NonNullable<E["$payload"]> extends JobInput<J> ? unknown : never)): OnDef<E, J>;
|
|
115
|
+
/** Checks the subscriptions of an app: one definition per event name, and each job subscribed once per event. */
|
|
116
|
+
export declare function checkSubscriptions(subscriptions: readonly OnDef[]): EventDef[];
|
|
117
|
+
/** The id of the job queued for one subscription of one event: the same for the same event id, so it is queued once. */
|
|
118
|
+
export declare function eventJobId(eventId: string, jobName: string): Promise<string>;
|
|
119
|
+
export interface EventsRuntime {
|
|
120
|
+
/** Queue one job with a fixed id (returning its id), continuing the given trace or the active one. */
|
|
121
|
+
enqueue(job: JobDef, input: unknown, id: string, trace?: JobTraceContext, tenantId?: string): Promise<string>;
|
|
122
|
+
transport?: EventTransport;
|
|
123
|
+
/** The active trace when an event is published, sent with it. */
|
|
124
|
+
capture?(): JobTraceContext | undefined;
|
|
125
|
+
logger?: {
|
|
126
|
+
error(message: string, data?: unknown): void;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** IL6033: an invalid events option or transport. */
|
|
130
|
+
export declare function invalidTransport(message: string): InferloError;
|
|
131
|
+
/** Checks createApp({ events }) and returns the custom transport, "redis", or undefined. */
|
|
132
|
+
export declare function checkEventsConfig(config: EventsConfig | undefined): EventsConfig["transport"];
|
|
133
|
+
/** app.events, built by createJobs. */
|
|
134
|
+
export declare function createEvents(subscriptions: readonly OnDef[], runtime: EventsRuntime): AppEvents;
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Events: something that happened, published once and handled by any number of jobs.
|
|
3
|
+
*
|
|
4
|
+
* export const orderPaid = event("order.paid", { payload: t.object({ orderId: t.uuid(), amount: t.int() }) });
|
|
5
|
+
* export const sendReceipt = job("send-receipt", { input: orderPaid.payload }).handle(async ({ input }) => { ... });
|
|
6
|
+
*
|
|
7
|
+
* createApp({ jobs: [on(orderPaid, sendReceipt), on(orderPaid, addPoints)] })
|
|
8
|
+
* await ctx.events.publish(orderPaid, { orderId, amount }); // in a route handler or job, validated
|
|
9
|
+
*
|
|
10
|
+
* An event is delivered through the job queue: publishing queues every subscribed job with the payload, so
|
|
11
|
+
* retries, the memory, database and Redis stores, the worker and tracing work as for any job. The queued jobs get
|
|
12
|
+
* ids derived from the event id, so publishing the same event id again (a repeated delivery) queues nothing new.
|
|
13
|
+
*/
|
|
14
|
+
import { checkTenantId } from "./db/table.js";
|
|
15
|
+
import { InferloError } from "./errors.js";
|
|
16
|
+
import { ValidationError } from "./schema.js";
|
|
17
|
+
const EVENT_NAME = /^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)*$/;
|
|
18
|
+
const FIX = 'Use event("order.paid", { payload: t.object({ orderId: t.uuid() }) }) and subscribe jobs in createApp({ jobs: [on(orderPaid, sendReceipt)] }).';
|
|
19
|
+
function invalid(message, fix = FIX) {
|
|
20
|
+
return new InferloError("IL6032", message, { fix });
|
|
21
|
+
}
|
|
22
|
+
/** Define an event: event("order.paid", { payload: t.object({ ... }) }). */
|
|
23
|
+
export function event(name, options) {
|
|
24
|
+
if (typeof name !== "string" || name.length > 100 || !EVENT_NAME.test(name)) {
|
|
25
|
+
throw invalid(`Invalid event name ${JSON.stringify(name)}: the name must be lowercase words of letters, digits and dashes, separated by dots, such as "order.paid".`);
|
|
26
|
+
}
|
|
27
|
+
if (!options || typeof options !== "object" || !options.payload || typeof options.payload.safeParse !== "function") {
|
|
28
|
+
throw invalid(`Invalid event "${name}": it needs a payload schema such as t.object({ ... }).`);
|
|
29
|
+
}
|
|
30
|
+
for (const key of Object.keys(options)) {
|
|
31
|
+
if (key !== "payload" && key !== "summary")
|
|
32
|
+
throw invalid(`Invalid event "${name}": unknown option "${key}". An event takes payload and summary; retries belong to the subscribed jobs.`);
|
|
33
|
+
}
|
|
34
|
+
if (options.summary !== undefined && typeof options.summary !== "string")
|
|
35
|
+
throw invalid(`Invalid event "${name}": summary must be a string.`);
|
|
36
|
+
const def = { kind: "inferlo.event", name, payload: options.payload, ...(options.summary ? { summary: options.summary } : {}) };
|
|
37
|
+
return Object.freeze(def);
|
|
38
|
+
}
|
|
39
|
+
export function isEvent(value) {
|
|
40
|
+
return typeof value === "object" && value !== null && value.kind === "inferlo.event";
|
|
41
|
+
}
|
|
42
|
+
export function isOn(value) {
|
|
43
|
+
return typeof value === "object" && value !== null && value.kind === "inferlo.on";
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Subscribe a job to an event: on(orderPaid, sendReceipt) in createApp({ jobs }). Every published event queues the
|
|
47
|
+
* job with the payload as its input (use input: orderPaid.payload); a job whose input is an object with fewer fields
|
|
48
|
+
* gets only those fields.
|
|
49
|
+
*/
|
|
50
|
+
export function on(event, job) {
|
|
51
|
+
if (!isEvent(event))
|
|
52
|
+
throw invalid("on() needs an event made with event(name, { payload }) as its first argument.");
|
|
53
|
+
const j = job;
|
|
54
|
+
if (typeof j !== "object" || j === null || j.kind !== "inferlo.job")
|
|
55
|
+
throw invalid(`on("${event.name}") needs a job made with job(name, { input }).handle(fn) as its second argument.`);
|
|
56
|
+
// The payload must have every field the job requires (checked on the JSON Schemas; publish validates again).
|
|
57
|
+
const payload = event.payload.toJSONSchema();
|
|
58
|
+
const input = j.options.input.toJSONSchema();
|
|
59
|
+
const missing = payload.type === "object" && input.type === "object" ? (input.required ?? []).filter((key) => !(key in (payload.properties ?? {}))) : [];
|
|
60
|
+
if (missing.length) {
|
|
61
|
+
throw invalid(`Job "${j.name}" cannot handle event "${event.name}": its input requires ${missing.map((k) => `"${k}"`).join(", ")}, which the payload does not have.`, `Use the event's payload as the job's input: job("${j.name}", { input: ${event.name.replace(/\W(\w)/g, (_, c) => c.toUpperCase())}.payload }).`);
|
|
62
|
+
}
|
|
63
|
+
return Object.freeze({ kind: "inferlo.on", event, job: job });
|
|
64
|
+
}
|
|
65
|
+
/** Checks the subscriptions of an app: one definition per event name, and each job subscribed once per event. */
|
|
66
|
+
export function checkSubscriptions(subscriptions) {
|
|
67
|
+
const events = new Map();
|
|
68
|
+
const pairs = new Set();
|
|
69
|
+
for (const s of subscriptions) {
|
|
70
|
+
const known = events.get(s.event.name);
|
|
71
|
+
if (known && known !== s.event) {
|
|
72
|
+
throw invalid(`Two different events are named "${s.event.name}".`, "Define each event once (export const orderPaid = event(...)) and import it wherever it is published or subscribed to.");
|
|
73
|
+
}
|
|
74
|
+
events.set(s.event.name, s.event);
|
|
75
|
+
const pair = `${s.event.name}\u0000${s.job.name}`;
|
|
76
|
+
if (pairs.has(pair))
|
|
77
|
+
throw invalid(`Job "${s.job.name}" is subscribed to event "${s.event.name}" twice.`, "Keep one on(event, job) for each pair.");
|
|
78
|
+
pairs.add(pair);
|
|
79
|
+
}
|
|
80
|
+
return [...events.values()];
|
|
81
|
+
}
|
|
82
|
+
/** The id of the job queued for one subscription of one event: the same for the same event id, so it is queued once. */
|
|
83
|
+
export async function eventJobId(eventId, jobName) {
|
|
84
|
+
const data = new TextEncoder().encode(`inferlo.event:${eventId}:${jobName}`);
|
|
85
|
+
const bytes = new Uint8Array(await crypto.subtle.digest("SHA-256", data)).slice(0, 16);
|
|
86
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x80; // version 8 (custom)
|
|
87
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80; // RFC 4122 variant
|
|
88
|
+
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
89
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
90
|
+
}
|
|
91
|
+
/** IL6033: an invalid events option or transport. */
|
|
92
|
+
export function invalidTransport(message) {
|
|
93
|
+
return new InferloError("IL6033", message, {
|
|
94
|
+
fix: 'Use createApp({ name: "billing", redis, events: { transport: "redis" } }), or pass an object with name, send(message) and receive(names, deliver).',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** Checks createApp({ events }) and returns the custom transport, "redis", or undefined. */
|
|
98
|
+
export function checkEventsConfig(config) {
|
|
99
|
+
if (config === undefined)
|
|
100
|
+
return undefined;
|
|
101
|
+
if (typeof config !== "object" || config === null)
|
|
102
|
+
throw invalidTransport("createApp({ events }) must be an object such as { transport: \"redis\" }.");
|
|
103
|
+
for (const key of Object.keys(config))
|
|
104
|
+
if (key !== "transport")
|
|
105
|
+
throw invalidTransport(`Unknown events option "${key}". createApp({ events }) takes transport.`);
|
|
106
|
+
const t = config.transport;
|
|
107
|
+
if (t === undefined || t === "redis")
|
|
108
|
+
return t;
|
|
109
|
+
if (typeof t !== "object" || t === null || typeof t.name !== "string" || !t.name || typeof t.send !== "function" || typeof t.receive !== "function") {
|
|
110
|
+
throw invalidTransport(`Invalid events transport ${typeof t === "string" ? JSON.stringify(t) : "object"}: use "redis" or an object with name, send(message) and receive(names, deliver).`);
|
|
111
|
+
}
|
|
112
|
+
return t;
|
|
113
|
+
}
|
|
114
|
+
/** app.events, built by createJobs. */
|
|
115
|
+
export function createEvents(subscriptions, runtime) {
|
|
116
|
+
const { enqueue, transport } = runtime;
|
|
117
|
+
const list = checkSubscriptions(subscriptions);
|
|
118
|
+
const byName = new Map(list.map((e) => [e.name, e]));
|
|
119
|
+
const names = list.map((e) => e.name);
|
|
120
|
+
// A job may take part of the payload (input: t.object({ orderId })): it gets only the fields its input has.
|
|
121
|
+
const fields = new Map();
|
|
122
|
+
for (const s of subscriptions) {
|
|
123
|
+
const input = s.job.options.input.toJSONSchema();
|
|
124
|
+
fields.set(s, input.type === "object" && input.properties ? Object.keys(input.properties) : undefined);
|
|
125
|
+
}
|
|
126
|
+
const inputFor = (s, payload) => {
|
|
127
|
+
const keys = fields.get(s);
|
|
128
|
+
if (!keys || typeof payload !== "object" || payload === null || Array.isArray(payload))
|
|
129
|
+
return payload;
|
|
130
|
+
return Object.fromEntries(keys.filter((k) => k in payload).map((k) => [k, payload[k]]));
|
|
131
|
+
};
|
|
132
|
+
/** Queue the jobs subscribed to one event in this app. */
|
|
133
|
+
const queueJobs = async (name, id, payload, trace, tenantId) => {
|
|
134
|
+
const jobs = [];
|
|
135
|
+
for (const s of subscriptions) {
|
|
136
|
+
if (s.event.name !== name)
|
|
137
|
+
continue;
|
|
138
|
+
jobs.push(await enqueue(s.job, inputFor(s, payload), await eventJobId(id, s.job.name), trace, tenantId));
|
|
139
|
+
}
|
|
140
|
+
return jobs;
|
|
141
|
+
};
|
|
142
|
+
/** An event from the transport: checked against this app's definition, then its jobs are queued. */
|
|
143
|
+
const deliver = async (message) => {
|
|
144
|
+
const def = byName.get(message.name);
|
|
145
|
+
if (!def)
|
|
146
|
+
return; // not subscribed here (a transport may deliver more than it was asked for)
|
|
147
|
+
const parsed = def.payload.safeParse(message.payload);
|
|
148
|
+
if (!parsed.ok) {
|
|
149
|
+
// Delivering it again cannot help: log it (without the payload, which may hold personal data) and acknowledge it.
|
|
150
|
+
const error = new InferloError("IL6034", `Received event "${message.name}" (id ${message.id}) does not match this service's payload schema and was dropped: ${new ValidationError(parsed.issues, "payload").message}`, {
|
|
151
|
+
fix: "Publish and subscribe with the same event definition, from a shared package, and change payloads compatibly (add optional fields only).",
|
|
152
|
+
});
|
|
153
|
+
runtime.logger?.error(`event "${message.name}" dropped`, error);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// A tenant id that is not valid is dropped rather than trusted: the jobs then run without a tenant.
|
|
157
|
+
const tenantId = typeof message.tenantId === "string" && message.tenantId.length > 0 && message.tenantId.length <= 200 ? message.tenantId : undefined;
|
|
158
|
+
await queueJobs(message.name, message.id, parsed.value, message.trace, tenantId);
|
|
159
|
+
};
|
|
160
|
+
return {
|
|
161
|
+
list,
|
|
162
|
+
subscriptions,
|
|
163
|
+
transport: transport?.name,
|
|
164
|
+
async receive() {
|
|
165
|
+
if (!transport || !names.length)
|
|
166
|
+
return 0;
|
|
167
|
+
return transport.receive(names, deliver);
|
|
168
|
+
},
|
|
169
|
+
async publish(def, payload, options = {}) {
|
|
170
|
+
if (!isEvent(def))
|
|
171
|
+
throw invalid("events.publish() needs an event made with event(name, { payload }).", "Pass the event and its payload: await events.publish(orderPaid, { orderId }).");
|
|
172
|
+
if (options.id !== undefined && (typeof options.id !== "string" || options.id.length === 0 || options.id.length > 200)) {
|
|
173
|
+
throw invalid(`The id for event "${def.name}" must be a non-empty string of at most 200 characters.`, "Leave it out for a new UUID, or pass the id of the record the event comes from (an outbox row).");
|
|
174
|
+
}
|
|
175
|
+
const tenantId = options.tenantId === undefined || options.tenantId === null ? undefined : checkTenantId(options.tenantId);
|
|
176
|
+
const parsed = def.payload.safeParse(payload);
|
|
177
|
+
if (!parsed.ok)
|
|
178
|
+
throw new ValidationError(parsed.issues, `payload for event "${def.name}"`);
|
|
179
|
+
const id = options.id ?? crypto.randomUUID();
|
|
180
|
+
const publishedAt = new Date();
|
|
181
|
+
if (transport) {
|
|
182
|
+
// Every service (this one too) queues its jobs when it receives the event; their ids are known already.
|
|
183
|
+
const trace = runtime.capture?.();
|
|
184
|
+
const json = JSON.parse(JSON.stringify(parsed.value ?? null));
|
|
185
|
+
await transport.send({ id, name: def.name, payload: json, publishedAt, ...(trace?.traceparent ? { trace } : {}), ...(tenantId ? { tenantId } : {}) });
|
|
186
|
+
const jobs = [];
|
|
187
|
+
for (const s of subscriptions)
|
|
188
|
+
if (s.event.name === def.name)
|
|
189
|
+
jobs.push(await eventJobId(id, s.job.name));
|
|
190
|
+
return { id, name: def.name, publishedAt, jobs };
|
|
191
|
+
}
|
|
192
|
+
return { id, name: def.name, publishedAt, jobs: await queueJobs(def.name, id, parsed.value, undefined, tenantId) };
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type AnySchema, type Issue } from "./schema.ts";
|
|
2
|
+
/** Where a t.file() is allowed in a route, checked once by createApp. */
|
|
3
|
+
export declare function checkFileFields(route: {
|
|
4
|
+
method: string;
|
|
5
|
+
path: string;
|
|
6
|
+
options: object;
|
|
7
|
+
}): void;
|
|
8
|
+
/** Bytes the file fields of a body may add on top of maxBodySize (0 without files). */
|
|
9
|
+
export declare function fileBudget(body: AnySchema | undefined): number;
|
|
10
|
+
export declare function isMultipart(contentType: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The fields of a multipart/form-data body as an object for the body schema. Repeated fields become lists, a single
|
|
13
|
+
* value of a list field becomes a one-item list, empty file inputs (no file chosen) count as left out, and the
|
|
14
|
+
* sessions() CSRF field _csrf is dropped.
|
|
15
|
+
*/
|
|
16
|
+
export declare function readMultipart(bytes: Uint8Array, contentType: string, body: AnySchema): Promise<Record<string, unknown>>;
|
|
17
|
+
/** Media types whose first bytes Inferlo checks when a t.file() field limits its types. */
|
|
18
|
+
export declare const CHECKED_FILE_TYPES: readonly string[];
|
|
19
|
+
/**
|
|
20
|
+
* Checks that files of a field with types declare a type whose content matches (a PNG is a PNG), so a client cannot
|
|
21
|
+
* pass a script off as an image by naming its type. Returns validation issues; empty when every file passes.
|
|
22
|
+
*/
|
|
23
|
+
export declare function checkFileContents(body: AnySchema, value: Record<string, unknown>): Promise<Issue[]>;
|