@protobase/server 0.8.2 → 0.9.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/dist/batch/run-batch.js +4 -2
- package/dist/changes/actor.js +7 -1
- package/dist/changes/capture-schema.js +1 -1
- package/dist/changes/capture-sql.js +13 -2
- package/dist/changes/coalesce.d.ts +4 -0
- package/dist/changes/coalesce.js +2 -1
- package/dist/changes/outbox.d.ts +14 -0
- package/dist/changes/outbox.js +51 -0
- package/dist/changes/resource-change.d.ts +2 -0
- package/dist/changes/resource-change.js +2 -1
- package/dist/create-admin.d.ts +10 -5
- package/dist/create-admin.js +20 -7
- package/dist/index.d.ts +10 -4
- package/dist/index.js +13 -8
- package/dist/jobs/current.d.ts +2 -0
- package/dist/jobs/current.js +3 -1
- package/dist/jobs/runtime.js +7 -5
- package/dist/project-config.d.ts +4 -1
- package/dist/project-config.js +10 -2
- package/dist/resource-source.d.ts +8 -3
- package/dist/resource-source.js +6 -3
- package/dist/types.d.ts +5 -1
- package/dist/webhooks/attempt.js +91 -0
- package/dist/webhooks/retry-schedule.js +40 -0
- package/dist/webhooks/route-events.js +61 -0
- package/dist/webhooks/standard-webhooks.d.ts +4 -0
- package/dist/webhooks/standard-webhooks.js +36 -0
- package/dist/webhooks/subscriber-view.js +76 -0
- package/dist/webhooks/subscription.d.ts +92 -0
- package/dist/webhooks/subscription.js +28 -0
- package/dist/webhooks/table/address-check.js +28 -0
- package/dist/webhooks/table/options.d.ts +23 -0
- package/dist/webhooks/table/options.js +14 -0
- package/dist/webhooks/table/owner-roles.js +23 -0
- package/dist/webhooks/table/plugin.d.ts +10 -0
- package/dist/webhooks/table/plugin.js +18 -0
- package/dist/webhooks/table/schema-sql.js +116 -0
- package/dist/webhooks/table/schema.d.ts +13 -0
- package/dist/webhooks/table/schema.js +44 -0
- package/dist/webhooks/table/table-subscriptions.js +77 -0
- package/dist/webhooks/table/validate-hook.js +32 -0
- package/dist/webhooks/table/webhook-table.js +23 -0
- package/dist/webhooks/test-event.js +48 -0
- package/dist/webhooks/webhooks.js +233 -0
- package/dist/write-pipeline.js +4 -2
- package/package.json +4 -4
package/dist/batch/run-batch.js
CHANGED
|
@@ -2,7 +2,7 @@ import { HttpProblem, badRequest, notFound } from "../problem.js";
|
|
|
2
2
|
import { problemOf } from "../error-handler.js";
|
|
3
3
|
import { requestAccess } from "../request-access.js";
|
|
4
4
|
import { writeTransaction } from "../transactions.js";
|
|
5
|
-
import { actorSettings } from "../changes/actor.js";
|
|
5
|
+
import { actorSettings, wakeDispatch } from "../changes/actor.js";
|
|
6
6
|
import { keyOf } from "../write-shared.js";
|
|
7
7
|
import { writeIn } from "../write-pipeline.js";
|
|
8
8
|
import { resolveData, resolveKey } from "./refs.js";
|
|
@@ -38,7 +38,7 @@ const runBatch = async (deps, session, operations) => {
|
|
|
38
38
|
accesses.set(entry.name, access);
|
|
39
39
|
return access;
|
|
40
40
|
};
|
|
41
|
-
|
|
41
|
+
const written = await writeTransaction(deps.db, deps.statementTimeoutMs, async (trx) => {
|
|
42
42
|
const created = /* @__PURE__ */ new Map();
|
|
43
43
|
const results = [];
|
|
44
44
|
for (const [index, operation] of operations.entries()) try {
|
|
@@ -101,6 +101,8 @@ const runBatch = async (deps, session, operations) => {
|
|
|
101
101
|
}
|
|
102
102
|
return results;
|
|
103
103
|
}, actorSettings(deps, session));
|
|
104
|
+
wakeDispatch(deps);
|
|
105
|
+
return written;
|
|
104
106
|
};
|
|
105
107
|
//#endregion
|
|
106
108
|
export { runBatch };
|
package/dist/changes/actor.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runningJobs } from "../jobs/current.js";
|
|
2
|
+
import { dispatchJobName } from "./capture-sql.js";
|
|
1
3
|
//#region src/changes/actor.ts
|
|
2
4
|
/** The setting a write transaction sets so the change capture records who wrote, when a resource has events. */
|
|
3
5
|
const actorSettings = (deps, session) => {
|
|
@@ -8,5 +10,9 @@ const actorSettings = (deps, session) => {
|
|
|
8
10
|
};
|
|
9
11
|
return { [deps.actorSetting]: JSON.stringify(actor) };
|
|
10
12
|
};
|
|
13
|
+
/** After a write committed: has this process's dispatcher deliver its events now, rather than at its next poll. */
|
|
14
|
+
const wakeDispatch = (deps) => {
|
|
15
|
+
if (deps.actorSetting) runningJobs()?.wake(dispatchJobName);
|
|
16
|
+
};
|
|
11
17
|
//#endregion
|
|
12
|
-
export { actorSettings };
|
|
18
|
+
export { actorSettings, wakeDispatch };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { changesSchema } from "./events-options.js";
|
|
2
1
|
import { quoteIdent, tableSql } from "./capture-sql.js";
|
|
2
|
+
import { changesSchema } from "./events-options.js";
|
|
3
3
|
import { capturePlan, planSql, uncaptured } from "./capture-plan.js";
|
|
4
4
|
import { sql } from "kysely";
|
|
5
5
|
//#region src/changes/capture-schema.ts
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { jobsSchema } from "../jobs/schema.js";
|
|
1
2
|
//#region src/changes/capture-sql.ts
|
|
2
3
|
const quoteIdent = (name) => `"${name.replaceAll("\"", "\"\"")}"`;
|
|
3
4
|
const quoteLiteral = (text) => `'${text.replaceAll("'", "''")}'`;
|
|
@@ -31,10 +32,16 @@ const outboxSql = (schema) => `create table if not exists ${quoteIdent(schema)}.
|
|
|
31
32
|
"dispatch_after" timestamptz not null default now(),
|
|
32
33
|
"created_at" timestamptz not null default now()
|
|
33
34
|
)`;
|
|
35
|
+
/** The job that turns captured changes into events and deliveries: one per writing transaction, run one at a time. */
|
|
36
|
+
const dispatchJobName = "changes.dispatch";
|
|
37
|
+
/** Every dispatch job's group in its ordered queue, so only one runs at a time, across processes too. */
|
|
38
|
+
const dispatchGroup = "changes";
|
|
34
39
|
/**
|
|
35
40
|
* The body of `capture()`: its arguments are the resource, the columns to keep and the sensitive columns, whose names
|
|
36
41
|
* it lists when they change but whose values it never keeps. It skips a transaction that set `<schema>.capture` to
|
|
37
|
-
* `off`, and an update that changed none of those columns.
|
|
42
|
+
* `off`, and an update that changed none of those columns. The first row a transaction captures enqueues a dispatch
|
|
43
|
+
* job in the same transaction; `<schema>.dispatch` remembers that for the rest of it, and a rolled-back savepoint
|
|
44
|
+
* forgets it with the job.
|
|
38
45
|
*/
|
|
39
46
|
const captureBody = (schema) => `
|
|
40
47
|
declare
|
|
@@ -71,6 +78,10 @@ begin
|
|
|
71
78
|
changed,
|
|
72
79
|
nullif(current_setting('${schema}.actor', true), '')::jsonb
|
|
73
80
|
);
|
|
81
|
+
if current_setting('${schema}.dispatch', true) is distinct from 'queued' then
|
|
82
|
+
perform ${jobsSchema}.enqueue('${dispatchJobName}', null, null, null, '${dispatchGroup}');
|
|
83
|
+
perform set_config('${schema}.dispatch', 'queued', true);
|
|
84
|
+
end if;
|
|
74
85
|
return null;
|
|
75
86
|
end
|
|
76
87
|
`;
|
|
@@ -105,4 +116,4 @@ const createTriggerSql = (schema, trigger, table) => {
|
|
|
105
116
|
return `create trigger ${quoteIdent(trigger.name)} ${timing} on ${table} for each row${when} execute function ${quoteIdent(schema)}."capture"(${args})`;
|
|
106
117
|
};
|
|
107
118
|
//#endregion
|
|
108
|
-
export { captureBody, captureFunctionSql, createTriggerSql, dropTriggerSql, outboxSql, quoteIdent, resourceTriggers, tableSql };
|
|
119
|
+
export { captureBody, captureFunctionSql, createTriggerSql, dispatchGroup, dispatchJobName, dropTriggerSql, outboxSql, quoteIdent, resourceTriggers, tableSql };
|
|
@@ -14,6 +14,8 @@ export type CapturedRow = {
|
|
|
14
14
|
/** The columns an update changed, sensitive ones included; `null` for inserts and deletes. */
|
|
15
15
|
changed: string[] | null;
|
|
16
16
|
actor: EventActor | null;
|
|
17
|
+
/** When the writing transaction started, ISO 8601. */
|
|
18
|
+
at: string;
|
|
17
19
|
};
|
|
18
20
|
/**
|
|
19
21
|
* One record's changes in one transaction, as one: its columns at the start (`before`, none when it was inserted) and
|
|
@@ -28,6 +30,8 @@ export type CoalescedChange = {
|
|
|
28
30
|
actor: EventActor | null;
|
|
29
31
|
/** The id of its last outbox row. */
|
|
30
32
|
sequence: string;
|
|
33
|
+
/** When its last change's transaction started. */
|
|
34
|
+
at: string;
|
|
31
35
|
};
|
|
32
36
|
/**
|
|
33
37
|
* The outbox rows of one or more transactions, in id order, as one change per (transaction, record). An insert then a
|
package/dist/changes/coalesce.js
CHANGED
|
@@ -17,7 +17,8 @@ const merged = ({ first, last, actor, listed }) => {
|
|
|
17
17
|
transaction: first.transaction,
|
|
18
18
|
resource: first.resource,
|
|
19
19
|
actor,
|
|
20
|
-
sequence: last.id
|
|
20
|
+
sequence: last.id,
|
|
21
|
+
at: last.at
|
|
21
22
|
};
|
|
22
23
|
const before = first.op === "INSERT" ? null : first.old;
|
|
23
24
|
const after = last.op === "DELETE" ? null : last.new;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Row } from "../types.js";
|
|
2
|
+
import { CapturedRow } from "./coalesce.js";
|
|
3
|
+
import { ResourceModel } from "@protobase/schema";
|
|
4
|
+
import { Db } from "@protobase/query";
|
|
5
|
+
//#region src/changes/outbox.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* A captured row's columns from the JSON text the database stores: the columns of 64-bit integer and decimal fields as
|
|
8
|
+
* their exact digits, other numbers as numbers. Needs a runtime that passes JSON.parse revivers the source text
|
|
9
|
+
* (Node 21 and later, Bun).
|
|
10
|
+
*/
|
|
11
|
+
export declare const parseColumns: (text: string | null, model?: ResourceModel) => Row | null;
|
|
12
|
+
/** The oldest `limit` rows of the outbox that committed, in id order. */
|
|
13
|
+
export declare const readOutbox: (db: Db, schema: string, models: readonly ResourceModel[], limit: number) => Promise<CapturedRow[]>;
|
|
14
|
+
//#endregion
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { quoteIdent } from "./capture-sql.js";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
//#region src/changes/outbox.ts
|
|
4
|
+
var ExactNumber = class {
|
|
5
|
+
source;
|
|
6
|
+
constructor(source) {
|
|
7
|
+
this.source = source;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
const exact = (_key, value, context) => typeof value === "number" && context?.source !== void 0 ? new ExactNumber(context.source) : value;
|
|
11
|
+
const plain = (value) => {
|
|
12
|
+
if (value instanceof ExactNumber) return Number(value.source);
|
|
13
|
+
if (Array.isArray(value)) return value.map(plain);
|
|
14
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, plain(item)]));
|
|
15
|
+
return value;
|
|
16
|
+
};
|
|
17
|
+
const textTypes = /* @__PURE__ */ new Set(["bigint", "decimal"]);
|
|
18
|
+
/**
|
|
19
|
+
* A captured row's columns from the JSON text the database stores: the columns of 64-bit integer and decimal fields as
|
|
20
|
+
* their exact digits, other numbers as numbers. Needs a runtime that passes JSON.parse revivers the source text
|
|
21
|
+
* (Node 21 and later, Bun).
|
|
22
|
+
*/
|
|
23
|
+
const parseColumns = (text, model) => {
|
|
24
|
+
if (text === null) return null;
|
|
25
|
+
const parsed = JSON.parse(text, exact);
|
|
26
|
+
const types = new Map(Object.values(model?.fields ?? {}).map((field) => [field.column, field.type]));
|
|
27
|
+
return Object.fromEntries(Object.entries(parsed).map(([column, value]) => [column, value instanceof ExactNumber && textTypes.has(types.get(column) ?? "") ? value.source : plain(value)]));
|
|
28
|
+
};
|
|
29
|
+
/** The oldest `limit` rows of the outbox that committed, in id order. */
|
|
30
|
+
const readOutbox = async (db, schema, models, limit) => {
|
|
31
|
+
const byName = new Map(models.map((model) => [model.name, model]));
|
|
32
|
+
const { rows } = await sql`select "id"::text as id, "transaction"::text as transaction, "resource", "op", "old"::text as old, "new"::text as new, "changed",
|
|
33
|
+
"actor"::text as actor, to_char("created_at" at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') as at
|
|
34
|
+
from ${sql.raw(`${quoteIdent(schema)}."outbox"`)} where "dispatch_after" <= now() order by "id" limit ${limit}`.execute(db);
|
|
35
|
+
return rows.map((row) => {
|
|
36
|
+
const model = byName.get(row.resource);
|
|
37
|
+
return {
|
|
38
|
+
...row,
|
|
39
|
+
old: parseColumns(row.old, model),
|
|
40
|
+
new: parseColumns(row.new, model),
|
|
41
|
+
actor: row.actor === null ? null : JSON.parse(row.actor)
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
/** Removes dispatched rows from the outbox. */
|
|
46
|
+
const deleteOutbox = async (db, schema, ids) => {
|
|
47
|
+
if (ids.length === 0) return;
|
|
48
|
+
await sql`delete from ${sql.raw(`${quoteIdent(schema)}."outbox"`)} where "id" = any(${ids}::bigint[])`.execute(db);
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
export { deleteOutbox, parseColumns, readOutbox };
|
|
@@ -19,6 +19,8 @@ export type ResourceChange = {
|
|
|
19
19
|
transaction: string;
|
|
20
20
|
/** Grows with every change of a record, so receivers can drop stale events. */
|
|
21
21
|
sequence: string;
|
|
22
|
+
/** When the change's transaction started, ISO 8601. */
|
|
23
|
+
at: string;
|
|
22
24
|
};
|
|
23
25
|
/** A captured row by field name, fields without a captured column left out. */
|
|
24
26
|
export declare const recordOf: (model: ResourceModel, columns: Row) => Row;
|
|
@@ -36,7 +36,8 @@ const resourceChange = (model, change) => {
|
|
|
36
36
|
source: change.actor ? "api" : "database",
|
|
37
37
|
...tenant != null && { tenant },
|
|
38
38
|
transaction: change.transaction,
|
|
39
|
-
sequence: change.sequence
|
|
39
|
+
sequence: change.sequence,
|
|
40
|
+
at: change.at
|
|
40
41
|
};
|
|
41
42
|
};
|
|
42
43
|
//#endregion
|
package/dist/create-admin.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { AdminAuth } from "./better-auth/create-auth.js";
|
|
2
2
|
import { EventsOptions } from "./changes/events-options.js";
|
|
3
|
+
import { JobDefinition } from "./jobs/job-types.js";
|
|
4
|
+
import { Admin, Authenticator, PageSource, ViewSource } from "./types.js";
|
|
5
|
+
import { WebhooksOptions } from "./webhooks/table/options.js";
|
|
6
|
+
import { SubscriptionDefinition } from "./webhooks/subscription.js";
|
|
3
7
|
import { FilesOptions } from "./files/options.js";
|
|
4
|
-
import { AdminEnv, Authenticator, PageSource, ViewSource } from "./types.js";
|
|
5
8
|
import { ResourceSource } from "./resource-source.js";
|
|
6
9
|
import { FunctionSource } from "./functions/define-function.js";
|
|
7
10
|
import { AdminOptions } from "./admin-options.js";
|
|
8
|
-
import { JobDefinition } from "./jobs/job-types.js";
|
|
9
|
-
import { Hono } from "hono";
|
|
10
11
|
import { UserMenuSource } from "@protobase/schema";
|
|
11
12
|
import { Db } from "@protobase/query";
|
|
12
13
|
//#region src/create-admin.d.ts
|
|
@@ -27,10 +28,14 @@ export type CreateAdminInput = {
|
|
|
27
28
|
files?: FilesOptions;
|
|
28
29
|
/** Where the changes that resources' events are raised from are captured (see `EventsOptions`). */
|
|
29
30
|
events?: EventsOptions;
|
|
30
|
-
/** The app's jobs (see `defineJob`): with any, admins get the failed jobs page (`failedJobs`). */
|
|
31
|
+
/** The app's jobs (see `defineJob`): with any, or a resource with events, admins get the failed jobs page (`failedJobs`). */
|
|
31
32
|
jobs?: JobDefinition[];
|
|
33
|
+
/** Where the resources' events are delivered (`subscription(...)` exports of the config module). */
|
|
34
|
+
subscriptions?: SubscriptionDefinition[];
|
|
35
|
+
/** Webhook subscriptions made in the admin app, from `extends: [webhooks()]` (see `WebhooksOptions`). */
|
|
36
|
+
webhooks?: WebhooksOptions;
|
|
32
37
|
options?: AdminOptions;
|
|
33
38
|
};
|
|
34
39
|
/** The admin REST API as a Hono app. It uses only Web APIs, so it runs on Node, Workers and anywhere else Hono does. */
|
|
35
|
-
export declare const createAdmin: ({ resources: appResources, views: appViews, pages, userMenu, db, authenticate, auth, functions, files: filesOptions, events, jobs, options }: CreateAdminInput) =>
|
|
40
|
+
export declare const createAdmin: ({ resources: appResources, views: appViews, pages, userMenu, db, authenticate, auth, functions, files: filesOptions, events, jobs, subscriptions, webhooks: webhooksOptions, options }: CreateAdminInput) => Admin;
|
|
36
41
|
//#endregion
|
package/dist/create-admin.js
CHANGED
|
@@ -7,12 +7,15 @@ import { authBasePath } from "./better-auth/create-auth.js";
|
|
|
7
7
|
import { organizationRoutes } from "./better-auth/organizations/admin-routes.js";
|
|
8
8
|
import { statusRoute } from "./better-auth/status-route.js";
|
|
9
9
|
import { changesSchema } from "./changes/events-options.js";
|
|
10
|
+
import { callerRecords } from "./functions/records.js";
|
|
11
|
+
import { toApiFunction } from "./functions/define-function.js";
|
|
12
|
+
import { createWebhooks } from "./webhooks/webhooks.js";
|
|
13
|
+
import { webhookTable } from "./webhooks/table/webhook-table.js";
|
|
14
|
+
import { subscriptionWriteCheck } from "./webhooks/table/validate-hook.js";
|
|
10
15
|
import { checkShell } from "./check-shell.js";
|
|
11
16
|
import { checkViews } from "./check-views.js";
|
|
12
17
|
import { createFilesRuntime } from "./files/runtime.js";
|
|
13
18
|
import { fileRoutes } from "./files/routes.js";
|
|
14
|
-
import { toApiFunction } from "./functions/define-function.js";
|
|
15
|
-
import { callerRecords } from "./functions/records.js";
|
|
16
19
|
import { functionRoutes } from "./functions/routes.js";
|
|
17
20
|
import { runtimeUrl } from "./runtime/runtime-settings.js";
|
|
18
21
|
import { createMeta } from "./meta.js";
|
|
@@ -28,10 +31,11 @@ import { Hono } from "hono";
|
|
|
28
31
|
import { checkPages } from "@protobase/layout";
|
|
29
32
|
//#region src/create-admin.ts
|
|
30
33
|
/** The admin REST API as a Hono app. It uses only Web APIs, so it runs on Node, Workers and anywhere else Hono does. */
|
|
31
|
-
const createAdmin = ({ resources: appResources, views: appViews = [], pages = [], userMenu, db, authenticate, auth, functions = {}, files: filesOptions, events, jobs = [], options = {} }) => {
|
|
34
|
+
const createAdmin = ({ resources: appResources, views: appViews = [], pages = [], userMenu, db, authenticate, auth, functions = {}, files: filesOptions, events, jobs = [], subscriptions = [], webhooks: webhooksOptions, options = {} }) => {
|
|
32
35
|
const basePath = options.basePath ?? "/api/v1";
|
|
33
|
-
const
|
|
34
|
-
const
|
|
36
|
+
const withJobs = jobs.length > 0 || appResources.some((resource) => resource.toModel().events);
|
|
37
|
+
const resources = withJobs ? [...appResources, failedJobs] : appResources;
|
|
38
|
+
const views = withJobs ? [...appViews, failedJobsView] : appViews;
|
|
35
39
|
const registry = buildRegistry(resources);
|
|
36
40
|
const apiFunctions = Object.fromEntries(Object.entries(functions).map(([name, source]) => [name, toApiFunction(name, source)]));
|
|
37
41
|
const viewModels = views.map((view) => view.toModel());
|
|
@@ -52,6 +56,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
52
56
|
models: registry.entries.map((entry) => entry.model),
|
|
53
57
|
systemPath
|
|
54
58
|
});
|
|
59
|
+
const table = webhooksOptions && webhookTable(webhooksOptions, auth);
|
|
55
60
|
const deps = {
|
|
56
61
|
db,
|
|
57
62
|
registry,
|
|
@@ -62,7 +67,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
62
67
|
systemPath,
|
|
63
68
|
statementTimeoutMs: options.statementTimeoutMs ?? 15e3,
|
|
64
69
|
guard: createScanGuard(db, options.scanGuard),
|
|
65
|
-
hooks: options.writeHooks ?? [],
|
|
70
|
+
hooks: [...options.writeHooks ?? [], ...table ? [subscriptionWriteCheck(registry.entries.map((entry) => entry.model), table.checkUrl)] : []],
|
|
66
71
|
audit: options.audit ?? consoleAuditQueue(),
|
|
67
72
|
accessOptions: {
|
|
68
73
|
...options.roles && { roles: options.roles },
|
|
@@ -136,7 +141,15 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
136
141
|
app.route("/", fileRoutes(deps, authenticate));
|
|
137
142
|
app.route(basePath, api);
|
|
138
143
|
app.route(deps.systemPath || "/", system);
|
|
139
|
-
|
|
144
|
+
const webhooks = createWebhooks({
|
|
145
|
+
deps,
|
|
146
|
+
subscriptions,
|
|
147
|
+
functions: apiFunctions,
|
|
148
|
+
schema: changesSchema(events),
|
|
149
|
+
log: (line) => console.log(line),
|
|
150
|
+
...table && { table }
|
|
151
|
+
});
|
|
152
|
+
return Object.assign(app, { jobs: webhooks.jobs });
|
|
140
153
|
};
|
|
141
154
|
//#endregion
|
|
142
155
|
export { createAdmin };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,9 +7,13 @@ import { OrganizationsOptions } from "./better-auth/organizations/options.js";
|
|
|
7
7
|
import { PlatformSignIn, readPlatformSignIn } from "./better-auth/platform-sign-in.js";
|
|
8
8
|
import { AdminAuth, CreateAuthOptions, UserCreatedHook, createAuth, normalizeRoles } from "./better-auth/create-auth.js";
|
|
9
9
|
import { EventsOptions } from "./changes/events-options.js";
|
|
10
|
+
import { StandardSchema, readJson } from "./functions/read-json.js";
|
|
11
|
+
import { EnqueueOptions, JobContext, JobDefinition, JobOptions } from "./jobs/job-types.js";
|
|
12
|
+
import { Admin, AuditEvent, AuditQueue, Authenticator, PipelineHook, ScanGuardMode, Session, SessionOrganization, TenantValue, User, ViewSource, WriteEvent, WriteOperation } from "./types.js";
|
|
13
|
+
import { WebhooksOptions } from "./webhooks/table/options.js";
|
|
14
|
+
import { EventData, RunContext, SubscriptionDefinition, SubscriptionEvent, SubscriptionOptions, subscription } from "./webhooks/subscription.js";
|
|
10
15
|
import { FileProvider, FileStore, StoredObject } from "./files/store.js";
|
|
11
16
|
import { FilesOptions } from "./files/options.js";
|
|
12
|
-
import { Admin, AuditEvent, AuditQueue, Authenticator, PipelineHook, ScanGuardMode, Session, SessionOrganization, TenantValue, User, ViewSource, WriteEvent, WriteOperation } from "./types.js";
|
|
13
17
|
import { CleanupEntry, CleanupSchedule, ScheduledDelete, storageCleanupSchedule } from "./files/cleanup-schedule.js";
|
|
14
18
|
import { CleanupReport } from "./files/cleanup-runner.js";
|
|
15
19
|
import { ResourceSource, configExports } from "./resource-source.js";
|
|
@@ -24,8 +28,6 @@ import { AssistantTool, ToolContext, defineTool } from "./assistant/tools.js";
|
|
|
24
28
|
import { AssistantOptions, AssistantSettings, assistantRoles, assistantSettings, assistantVariables, seesAssistant } from "./assistant/assistant-settings.js";
|
|
25
29
|
import { RuntimeOptions, runtimeUrl, runtimeVariable, seesRuntime } from "./runtime/runtime-settings.js";
|
|
26
30
|
import { AdminOptions } from "./admin-options.js";
|
|
27
|
-
import { StandardSchema, readJson } from "./functions/read-json.js";
|
|
28
|
-
import { EnqueueOptions, JobContext, JobDefinition, JobOptions } from "./jobs/job-types.js";
|
|
29
31
|
import { CreateAdminInput, createAdmin } from "./create-admin.js";
|
|
30
32
|
import { ProjectConfig, defineConfig, mergeConfig } from "./project-config.js";
|
|
31
33
|
import { consoleAuditQueue } from "./audit/console-queue.js";
|
|
@@ -40,6 +42,10 @@ import { changesMigration, checkChanges, migrationCommand } from "./changes/capt
|
|
|
40
42
|
import { CapturedRow, CoalescedChange, coalesce } from "./changes/coalesce.js";
|
|
41
43
|
import { ResourceChange, recordOf, resourceChange } from "./changes/resource-change.js";
|
|
42
44
|
import { eventMatcher } from "./changes/match-events.js";
|
|
45
|
+
import { parseColumns, readOutbox } from "./changes/outbox.js";
|
|
46
|
+
import { newWebhookSecret } from "./webhooks/standard-webhooks.js";
|
|
47
|
+
import { webhooks } from "./webhooks/table/plugin.js";
|
|
48
|
+
import { checkWebhooksSchema, webhooksMigration } from "./webhooks/table/schema.js";
|
|
43
49
|
import { MemberInput, NewOrganization, addAppRoleToOwners, addMember, createOrganization, listOrganizations, removeMember, setMemberRoles } from "./better-auth/organizations/host.js";
|
|
44
50
|
import { AssistantBackend, assistantProtocolRoutes } from "./assistant/protocol-routes.js";
|
|
45
51
|
import { fileConversationStore } from "./assistant/file-conversation-store.js";
|
|
@@ -60,4 +66,4 @@ import { FatalJobError } from "./jobs/run-job.js";
|
|
|
60
66
|
import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/runtime.js";
|
|
61
67
|
import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
62
68
|
import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
|
|
63
|
-
export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EnqueueOptions, type EventsOptions, FatalJobError, type FileCleanupInput, type FileObject, type FileProvider, type FileStore, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type LinkedAccount, type ListParams, type LocalFilesOptions, type MemberInput, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReasoningDetail, type ReasoningEffort, type RecordKeyInput, type RecordPage, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RuntimeOptions, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type SignInPolicy, type SignInRule, type SignatureOptions, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineJob, defineTool, deleteUser, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, listOrganizations, listUsers, localFileStore, localFiles, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, normalizeRoles, notFound, parseRoles, processor, readJson, readOnlyQueryTool, readOperatorSettings, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, startJobs, storageCleanupSchedule, streamChatCompletion, unauthorized, verifySignature, verifyStandardWebhook, widget, withCacheBreakpoints };
|
|
69
|
+
export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EnqueueOptions, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileObject, type FileProvider, type FileStore, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type LinkedAccount, type ListParams, type LocalFilesOptions, type MemberInput, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReasoningDetail, type ReasoningEffort, type RecordKeyInput, type RecordPage, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type SignInPolicy, type SignInRule, type SignatureOptions, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineJob, defineTool, deleteUser, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, listOrganizations, listUsers, localFileStore, localFiles, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, startJobs, storageCleanupSchedule, streamChatCompletion, subscription, unauthorized, verifySignature, verifyStandardWebhook, webhooks, webhooksMigration, widget, withCacheBreakpoints };
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { readOnlyQueryTool, readWriteQueryTool } from "./assistant/query-tools.j
|
|
|
9
9
|
import { requestApproval } from "./assistant/approval.js";
|
|
10
10
|
import { withCacheBreakpoints } from "./assistant/cache-breakpoints.js";
|
|
11
11
|
import { ModelError, streamChatCompletion } from "./assistant/chat-completions.js";
|
|
12
|
+
import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
|
|
12
13
|
import { runTurn } from "./assistant/turn.js";
|
|
13
14
|
import { consoleAuditQueue } from "./audit/console-queue.js";
|
|
14
15
|
import { readOperatorSettings } from "./better-auth/operator-provider.js";
|
|
@@ -18,23 +19,28 @@ import { readPlatformSignIn } from "./better-auth/platform-sign-in.js";
|
|
|
18
19
|
import { createAuth, normalizeRoles } from "./better-auth/create-auth.js";
|
|
19
20
|
import { addAppRoleToOwners, addMember, createOrganization, listOrganizations, removeMember, setMemberRoles } from "./better-auth/organizations/host.js";
|
|
20
21
|
import { createUser, deleteUser, hasUsers, listUsers, roleChoices, setUserBanned, setUserRole } from "./better-auth/users.js";
|
|
22
|
+
import { coalesce } from "./changes/coalesce.js";
|
|
23
|
+
import { eventMatcher } from "./changes/match-events.js";
|
|
24
|
+
import { parseColumns, readOutbox } from "./changes/outbox.js";
|
|
25
|
+
import { recordOf, resourceChange } from "./changes/resource-change.js";
|
|
26
|
+
import { defineJob, isJobDefinition, jobDefinitions } from "./jobs/define-job.js";
|
|
27
|
+
import { FatalJobError } from "./jobs/run-job.js";
|
|
28
|
+
import { defineFunction } from "./functions/define-function.js";
|
|
29
|
+
import { newWebhookSecret } from "./webhooks/standard-webhooks.js";
|
|
30
|
+
import { subscription } from "./webhooks/subscription.js";
|
|
21
31
|
import { storageCleanupSchedule } from "./files/cleanup-schedule.js";
|
|
22
32
|
import { localFileStore, localFiles } from "./files/local-files.js";
|
|
23
|
-
import { defineFunction } from "./functions/define-function.js";
|
|
24
33
|
import { runtimeUrl, runtimeVariable, seesRuntime } from "./runtime/runtime-settings.js";
|
|
25
|
-
import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
|
|
26
34
|
import { createAdmin } from "./create-admin.js";
|
|
27
35
|
import { configExports } from "./resource-source.js";
|
|
28
|
-
import { defineJob, isJobDefinition, jobDefinitions } from "./jobs/define-job.js";
|
|
29
36
|
import { defineConfig, mergeConfig } from "./project-config.js";
|
|
30
37
|
import { jwtAuthenticator } from "./auth/jwt.js";
|
|
31
38
|
import { betterAuthAuthenticator } from "./better-auth/authenticator.js";
|
|
32
39
|
import { issueToken, maxTokenTtlSeconds } from "./better-auth/tokens.js";
|
|
33
40
|
import { authSchemaMigration, checkAuthSchema } from "./better-auth/auth-schema.js";
|
|
34
41
|
import { changesMigration, checkChanges, migrationCommand } from "./changes/capture-schema.js";
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import { eventMatcher } from "./changes/match-events.js";
|
|
42
|
+
import { webhooks } from "./webhooks/table/plugin.js";
|
|
43
|
+
import { checkWebhooksSchema, webhooksMigration } from "./webhooks/table/schema.js";
|
|
38
44
|
import { maxWidgetPropsBytes, widget } from "./assistant/widget.js";
|
|
39
45
|
import { functionApp } from "./functions/function-app.js";
|
|
40
46
|
import { readJson } from "./functions/read-json.js";
|
|
@@ -42,7 +48,6 @@ import { verifySignature, verifyStandardWebhook } from "./functions/verify-signa
|
|
|
42
48
|
import { requireEnv } from "./functions/require-env.js";
|
|
43
49
|
import { createFileCleanup } from "./files/file-cleanup.js";
|
|
44
50
|
import { aspectRatio, fileSize, fileType, imageSize, processor } from "./files/processors.js";
|
|
45
|
-
import { FatalJobError } from "./jobs/run-job.js";
|
|
46
51
|
import { startJobs } from "./jobs/runtime.js";
|
|
47
52
|
import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
48
|
-
export { FatalJobError, HttpProblem, ModelError, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineJob, defineTool, deleteUser, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, listOrganizations, listUsers, localFileStore, localFiles, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, normalizeRoles, notFound, parseRoles, processor, readJson, readOnlyQueryTool, readOperatorSettings, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, startJobs, storageCleanupSchedule, streamChatCompletion, unauthorized, verifySignature, verifyStandardWebhook, widget, withCacheBreakpoints };
|
|
53
|
+
export { FatalJobError, HttpProblem, ModelError, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineJob, defineTool, deleteUser, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, listOrganizations, listUsers, localFileStore, localFiles, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, startJobs, storageCleanupSchedule, streamChatCompletion, subscription, unauthorized, verifySignature, verifyStandardWebhook, webhooks, webhooksMigration, widget, withCacheBreakpoints };
|
package/dist/jobs/current.d.ts
CHANGED
|
@@ -4,5 +4,7 @@ import { Db } from "@protobase/query";
|
|
|
4
4
|
/** What `defineJob(...).enqueue` reaches: the jobs runtime this process started. */
|
|
5
5
|
export type JobsEnqueuer = {
|
|
6
6
|
enqueue(definition: JobDefinition, db: Db, payload: unknown, options?: EnqueueOptions): Promise<string>;
|
|
7
|
+
/** Has this process's worker of the job look for due jobs now rather than at its next poll; nothing without one. */
|
|
8
|
+
wake(name: string): void;
|
|
7
9
|
};
|
|
8
10
|
//#endregion
|
package/dist/jobs/current.js
CHANGED
|
@@ -7,6 +7,8 @@ const currentJobs = () => {
|
|
|
7
7
|
if (!runtime) throw new Error("Jobs are not running in this process: start them with startJobs(), as protobase dev, serve and worker do");
|
|
8
8
|
return runtime;
|
|
9
9
|
};
|
|
10
|
+
/** The running jobs runtime, if this process started one. */
|
|
11
|
+
const runningJobs = () => holder[key];
|
|
10
12
|
const setCurrentJobs = (runtime) => {
|
|
11
13
|
if (holder[key]) throw new Error("Jobs are already running in this process; stop them before starting them again");
|
|
12
14
|
holder[key] = runtime;
|
|
@@ -15,4 +17,4 @@ const clearCurrentJobs = (runtime) => {
|
|
|
15
17
|
if (holder[key] === runtime) delete holder[key];
|
|
16
18
|
};
|
|
17
19
|
//#endregion
|
|
18
|
-
export { clearCurrentJobs, currentJobs, setCurrentJobs };
|
|
20
|
+
export { clearCurrentJobs, currentJobs, runningJobs, setCurrentJobs };
|
package/dist/jobs/runtime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { jobsSchema } from "./schema.js";
|
|
2
1
|
import { clearCurrentJobs, setCurrentJobs } from "./current.js";
|
|
2
|
+
import { jobsSchema } from "./schema.js";
|
|
3
3
|
import "./schedule.js";
|
|
4
4
|
import { runJob, validatePayload } from "./run-job.js";
|
|
5
5
|
import { jobsDatabase } from "./database.js";
|
|
@@ -98,10 +98,7 @@ const startJobs = async ({ db, jobs, work = true, pollSeconds = 2, shutdownSecon
|
|
|
98
98
|
};
|
|
99
99
|
const id = options.key === void 0 ? await boss.send(name, data, send) : await keyed(options.key);
|
|
100
100
|
if (!id) throw new Error(`Job "${name}" was not enqueued`);
|
|
101
|
-
if (!target.isTransaction)
|
|
102
|
-
const worker = workers.get(name);
|
|
103
|
-
if (worker) boss.notifyWorker(worker.id);
|
|
104
|
-
}
|
|
101
|
+
if (!target.isTransaction) wake(name);
|
|
105
102
|
return id;
|
|
106
103
|
};
|
|
107
104
|
const nextDue = async () => (await sql`select ${sql.id(jobsSchema, "next_due")}() as due`.execute(db)).rows[0].due;
|
|
@@ -129,9 +126,14 @@ const startJobs = async ({ db, jobs, work = true, pollSeconds = 2, shutdownSecon
|
|
|
129
126
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
130
127
|
}
|
|
131
128
|
};
|
|
129
|
+
const wake = (name) => {
|
|
130
|
+
const worker = workers.get(name);
|
|
131
|
+
if (worker) boss.notifyWorker(worker.id);
|
|
132
|
+
};
|
|
132
133
|
const runtime = {
|
|
133
134
|
works: work,
|
|
134
135
|
enqueue,
|
|
136
|
+
wake,
|
|
135
137
|
update,
|
|
136
138
|
drain,
|
|
137
139
|
nextDue,
|
package/dist/project-config.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { AdminAuth } from "./better-auth/create-auth.js";
|
|
2
2
|
import { EventsOptions } from "./changes/events-options.js";
|
|
3
|
-
import { FilesOptions } from "./files/options.js";
|
|
4
3
|
import { Authenticator } from "./types.js";
|
|
4
|
+
import { WebhooksOptions } from "./webhooks/table/options.js";
|
|
5
|
+
import { FilesOptions } from "./files/options.js";
|
|
5
6
|
import { FunctionSource } from "./functions/define-function.js";
|
|
6
7
|
import { AdminOptions } from "./admin-options.js";
|
|
7
8
|
import { Db } from "@protobase/query";
|
|
@@ -24,6 +25,8 @@ export type ProjectConfig = {
|
|
|
24
25
|
files?: FilesOptions;
|
|
25
26
|
/** Where the changes that resources' events are raised from are captured (see `EventsOptions`). */
|
|
26
27
|
events?: EventsOptions;
|
|
28
|
+
/** Webhook subscriptions made in the admin app; set by `extends: [webhooks()]` (see `WebhooksOptions`). */
|
|
29
|
+
webhooks?: WebhooksOptions;
|
|
27
30
|
/** The module of the app's jobs, e.g. `import * as jobs from './config/jobs'`: every `defineJob` it exports. */
|
|
28
31
|
jobs?: Record<string, unknown>;
|
|
29
32
|
options?: AdminOptions;
|
package/dist/project-config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jobDefinitions } from "./jobs/define-job.js";
|
|
2
|
+
import { isSubscription } from "./webhooks/subscription.js";
|
|
2
3
|
import { isPageDefinition } from "@protobase/layout";
|
|
3
4
|
//#region src/project-config.ts
|
|
4
5
|
/** Typed identity, for `export default defineConfig({ extends: [...], config, auth, ... })` in `protobase.config.ts`. */
|
|
@@ -41,6 +42,11 @@ const mergedModule = (named) => {
|
|
|
41
42
|
value,
|
|
42
43
|
label
|
|
43
44
|
})));
|
|
45
|
+
checkUnique("The subscription", exports.filter(({ value }) => isSubscription(value)).map(({ value, label }) => ({
|
|
46
|
+
name: value.name,
|
|
47
|
+
value,
|
|
48
|
+
label
|
|
49
|
+
})));
|
|
44
50
|
const menus = values.filter(({ value }) => typeof value?.toUserMenuModel === "function");
|
|
45
51
|
if (new Set(menus.map(({ value }) => value)).size > 1) throw new Error(`Only one config may export a user menu; ${menus.map(({ label }) => label).join(" and ")} each do`);
|
|
46
52
|
const module = {};
|
|
@@ -133,11 +139,12 @@ const mergedOptions = (named) => {
|
|
|
133
139
|
*/
|
|
134
140
|
const mergeConfig = (...configs) => {
|
|
135
141
|
const named = configs.flatMap((config, index) => flatten(config, config.name ?? `config ${index + 1}`));
|
|
136
|
-
const { db, authenticate, auth, events } = lastSet(named.map(({ config }) => ({
|
|
142
|
+
const { db, authenticate, auth, events, webhooks } = lastSet(named.map(({ config }) => ({
|
|
137
143
|
db: config.db,
|
|
138
144
|
authenticate: config.authenticate,
|
|
139
145
|
auth: config.auth,
|
|
140
|
-
events: config.events
|
|
146
|
+
events: config.events,
|
|
147
|
+
webhooks: config.webhooks
|
|
141
148
|
})));
|
|
142
149
|
const module = mergedModule(named);
|
|
143
150
|
const functions = mergedFunctions(named);
|
|
@@ -154,6 +161,7 @@ const mergeConfig = (...configs) => {
|
|
|
154
161
|
...functions && { functions },
|
|
155
162
|
...files && { files },
|
|
156
163
|
...events && { events },
|
|
164
|
+
...webhooks && { webhooks },
|
|
157
165
|
...jobs && { jobs },
|
|
158
166
|
...options && { options }
|
|
159
167
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { SubscriptionDefinition } from "./webhooks/subscription.js";
|
|
2
|
+
import "./index.js";
|
|
1
3
|
import { PageDefinition } from "@protobase/layout";
|
|
2
|
-
import { AccessSource, FileProcessor, UserMenuSource, ViewModel } from "@protobase/schema";
|
|
4
|
+
import { AccessSource, EventMatch, FileProcessor, UserMenuSource, ViewModel } from "@protobase/schema";
|
|
3
5
|
import { z } from "zod";
|
|
4
6
|
//#region src/resource-source.d.ts
|
|
5
7
|
type FieldSource = {
|
|
@@ -22,10 +24,12 @@ export type ResourceSource = AccessSource & {
|
|
|
22
24
|
message: string;
|
|
23
25
|
}> | undefined)[];
|
|
24
26
|
recordSchema(): z.ZodType;
|
|
27
|
+
/** The code predicates of its events, by event name. */
|
|
28
|
+
eventMatches?(): Record<string, EventMatch<any>>;
|
|
25
29
|
};
|
|
26
30
|
/**
|
|
27
|
-
* Splits a config module (`import * as config from './config'`) into its resources, views, pages and,
|
|
28
|
-
* one `userMenu(...)`, the user menu.
|
|
31
|
+
* Splits a config module (`import * as config from './config'`) into its resources, views, pages, subscriptions and,
|
|
32
|
+
* when it exports one `userMenu(...)`, the user menu.
|
|
29
33
|
*/
|
|
30
34
|
export declare const configExports: (module: Record<string, unknown>) => {
|
|
31
35
|
userMenu?: UserMenuSource | undefined;
|
|
@@ -34,5 +38,6 @@ export declare const configExports: (module: Record<string, unknown>) => {
|
|
|
34
38
|
toModel(): ViewModel;
|
|
35
39
|
}>;
|
|
36
40
|
pages: PageDefinition[];
|
|
41
|
+
subscriptions: SubscriptionDefinition[];
|
|
37
42
|
};
|
|
38
43
|
//#endregion
|
package/dist/resource-source.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { isSubscription } from "./webhooks/subscription.js";
|
|
1
2
|
import { isPageDefinition } from "@protobase/layout";
|
|
2
3
|
//#region src/resource-source.ts
|
|
3
4
|
/**
|
|
4
|
-
* Splits a config module (`import * as config from './config'`) into its resources, views, pages and,
|
|
5
|
-
* one `userMenu(...)`, the user menu.
|
|
5
|
+
* Splits a config module (`import * as config from './config'`) into its resources, views, pages, subscriptions and,
|
|
6
|
+
* when it exports one `userMenu(...)`, the user menu.
|
|
6
7
|
*/
|
|
7
8
|
const configExports = (module) => {
|
|
8
9
|
const values = Object.values(module);
|
|
9
10
|
const pages = values.filter(isPageDefinition);
|
|
10
|
-
const
|
|
11
|
+
const subscriptions = [...new Set(values.filter(isSubscription))];
|
|
12
|
+
const others = values.filter((value) => !isPageDefinition(value) && !isSubscription(value));
|
|
11
13
|
const buildable = others.filter((value) => typeof value?.toModel === "function");
|
|
12
14
|
const menus = others.filter((value) => typeof value?.toUserMenuModel === "function");
|
|
13
15
|
if (menus.length > 1) throw new Error(`The config module exports ${menus.length} user menus; export one userMenu(...)`);
|
|
@@ -15,6 +17,7 @@ const configExports = (module) => {
|
|
|
15
17
|
resources: buildable.filter((value) => typeof value.recordSchema === "function"),
|
|
16
18
|
views: buildable.filter((value) => typeof value.recordSchema !== "function"),
|
|
17
19
|
pages,
|
|
20
|
+
subscriptions,
|
|
18
21
|
...menus[0] && { userMenu: menus[0] }
|
|
19
22
|
};
|
|
20
23
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { JobDefinition } from "./jobs/job-types.js";
|
|
1
2
|
import { Hono } from "hono";
|
|
2
3
|
import { PageModel } from "@protobase/layout";
|
|
3
4
|
import { ResourceModel, ViewModel } from "@protobase/schema";
|
|
@@ -88,5 +89,8 @@ export type ViewSource = {
|
|
|
88
89
|
export type PageSource = {
|
|
89
90
|
toModel(): PageModel;
|
|
90
91
|
};
|
|
91
|
-
|
|
92
|
+
/** The admin API, and the jobs it needs a worker to run: the delivery of resource events. */
|
|
93
|
+
export type Admin = Hono<AdminEnv> & {
|
|
94
|
+
jobs: JobDefinition[];
|
|
95
|
+
};
|
|
92
96
|
//#endregion
|