@protobase/server 0.48.0 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/admin-options.d.ts +2 -2
  2. package/dist/app-identity.d.ts +2 -2
  3. package/dist/app-name.d.ts +2 -0
  4. package/dist/better-auth/auth-settings.d.ts +8754 -0
  5. package/dist/better-auth/auth-settings.js +43 -0
  6. package/dist/better-auth/authenticate-settings.d.ts +15 -0
  7. package/dist/better-auth/authenticate-settings.js +12 -0
  8. package/dist/better-auth/create-auth.d.ts +23 -11
  9. package/dist/better-auth/create-auth.js +21 -9
  10. package/dist/better-auth/mcp/mcp-auth-plugins.d.ts +2719 -0
  11. package/dist/better-auth/mcp/mcp-auth-plugins.js +68 -0
  12. package/dist/better-auth/mcp/mcp-plugins.d.ts +7 -2
  13. package/dist/better-auth/mcp/mcp-plugins.js +3 -68
  14. package/dist/better-auth/mcp/mcp-token-claims.js +1 -1
  15. package/dist/better-auth/organizations/store.d.ts +1 -0
  16. package/dist/better-auth/saml-plugin.d.ts +993 -0
  17. package/dist/better-auth/saml-plugin.js +36 -0
  18. package/dist/better-auth/saml-sign-in.d.ts +27 -1
  19. package/dist/better-auth/saml-sign-in.js +1 -33
  20. package/dist/better-auth/user-table.js +1 -1
  21. package/dist/better-auth/users.d.ts +1 -1
  22. package/dist/create-admin.d.ts +5 -3
  23. package/dist/create-admin.js +3 -1
  24. package/dist/encryption/keyring.js +1 -1
  25. package/dist/files/options.d.ts +5 -8
  26. package/dist/files/provider-settings.d.ts +16 -0
  27. package/dist/files/provider-settings.js +17 -0
  28. package/dist/files/runtime.js +4 -4
  29. package/dist/forms/runtime.js +3 -3
  30. package/dist/index.d.ts +16 -15
  31. package/dist/index.js +8 -10
  32. package/dist/internal.d.ts +13 -0
  33. package/dist/internal.js +13 -0
  34. package/dist/jobs/declared-job.d.ts +24 -0
  35. package/dist/jobs/declared-job.js +26 -0
  36. package/dist/jobs/job-types.d.ts +1 -1
  37. package/dist/mcp/mcp-authenticator.js +3 -3
  38. package/dist/notifications/device-store-of.d.ts +1 -1
  39. package/dist/notifications/device-store-of.js +1 -1
  40. package/dist/notifications/device-store.d.ts +1 -1
  41. package/dist/notifications/device-store.js +1 -1
  42. package/dist/notifications/history-store-of.d.ts +1 -1
  43. package/dist/notifications/history-store-of.js +1 -1
  44. package/dist/notifications/history-store.d.ts +1 -1
  45. package/dist/notifications/history-store.js +1 -1
  46. package/dist/notifications/options.d.ts +3 -1
  47. package/dist/notifications/options.js +1 -1
  48. package/dist/notifications/schema.d.ts +2 -2
  49. package/dist/notifications/schema.js +7 -7
  50. package/dist/notifications/send-notification.js +1 -1
  51. package/dist/notifications/sender.d.ts +1 -1
  52. package/dist/notifications/sender.js +2 -2
  53. package/dist/notifications/senders-of.d.ts +1 -1
  54. package/dist/notifications/senders-of.js +1 -1
  55. package/dist/package.js +1 -1
  56. package/dist/webhooks/subscription.d.ts +1 -0
  57. package/dist/webhooks/table/options.d.ts +1 -1
  58. package/dist/webhooks/table/schema.d.ts +2 -2
  59. package/dist/webhooks/table/schema.js +2 -2
  60. package/dist/webhooks/table/webhook-table.js +2 -2
  61. package/dist/webhooks/table/webhooks-module.d.ts +108 -0
  62. package/dist/webhooks/table/webhooks-module.js +11 -0
  63. package/package.json +6 -5
  64. package/dist/notifications/plugin.d.ts +0 -13
  65. package/dist/notifications/plugin.js +0 -16
  66. package/dist/project-config.d.ts +0 -54
  67. package/dist/project-config.js +0 -182
  68. package/dist/webhooks/table/plugin.d.ts +0 -10
  69. package/dist/webhooks/table/plugin.js +0 -18
@@ -19,7 +19,7 @@ export type JobContext = {
19
19
  signal: AbortSignal;
20
20
  /** Sends mail through the app's SMTP server (`PROTOBASE_SMTP_URL`, `PROTOBASE_MAIL_FROM`); `undefined` when the app has no mail. */
21
21
  mail?: Mailer;
22
- /** Sends a notification as `sendNotification` does; `undefined` when the app has no `notifications()`. */
22
+ /** Sends a notification as `sendNotification` does; `undefined` when the app has no `notifications`. */
23
23
  sendNotification?: SendNotification;
24
24
  };
25
25
  export type JobOptions<Payload> = {
@@ -3,7 +3,7 @@ import { isMcpWrites } from "./mcp-writes.js";
3
3
  const hasAudiences = (authenticate) => typeof authenticate.forAudience === "function";
4
4
  const isMcpOptions = (options) => options === void 0 || options === false || typeof options === "object" && options !== null && !Array.isArray(options) && Object.entries(options).every(([name, value]) => value === void 0 || name === "sql" && typeof value === "boolean" || name === "writes" && isMcpWrites(value));
5
5
  /**
6
- * The authenticator of the MCP endpoint's tokens, when MCP is on (`createAuth({ mcp })`); none when the host serves its
6
+ * The authenticator of the MCP endpoint's tokens, when MCP is on (`auth.mcp`); none when the host serves its
7
7
  * own (`options.mcp: false`). Stops the server at startup when `options.mcp` is neither `false` nor
8
8
  * `{ sql?: boolean; writes?: boolean | { resources?: string[]; rolesWithoutApproval?: string[] } }`, when it is set without MCP, or when `authenticate` cannot verify tokens for
9
9
  * another audience.
@@ -11,9 +11,9 @@ const isMcpOptions = (options) => options === void 0 || options === false || typ
11
11
  const mcpAuthenticator = (authenticate, mcp, options) => {
12
12
  if (!isMcpOptions(options)) throw new Error("options.mcp takes false or { sql?: boolean, writes?: boolean | { resources?: [<resource>, ...], rolesWithoutApproval?: [<role>, ...] } }");
13
13
  if (options === false) return void 0;
14
- if (options && !mcp) throw new Error("options.mcp needs createAuth({ mcp }) passed as auth");
14
+ if (options && !mcp) throw new Error("mcpEndpoint needs auth.mcp in protobase.config.ts");
15
15
  if (!mcp) return void 0;
16
- if (!hasAudiences(authenticate)) throw new Error("MCP needs authenticate from betterAuthAuthenticator, whose forAudience verifies the MCP tokens");
16
+ if (!hasAudiences(authenticate)) throw new Error("MCP needs the app's own authenticator, whose forAudience verifies the MCP tokens: leave `authenticate` a module of your own out of protobase.config.ts");
17
17
  return authenticate.forAudience(mcp.resource);
18
18
  };
19
19
  //#endregion
@@ -4,7 +4,7 @@ import { AdminAuth } from "../better-auth/create-auth.js";
4
4
  import { Db } from "@protobase/query";
5
5
  //#region src/notifications/device-store-of.d.ts
6
6
  /**
7
- * The devices' store `notifications()` configures: the app's own `store`, or the devices table in `db`. An app that
7
+ * The devices' store `notifications` configures: the app's own `store`, or the devices table in `db`. An app that
8
8
  * deletes users without Protobase's auth calls its `removeAllForUser` as it deletes one.
9
9
  */
10
10
  export declare const deviceStore: (db: Db, options?: NotificationsOptions) => DeviceStore;
@@ -1,7 +1,7 @@
1
1
  import { postgresDeviceStore } from "./postgres-store.js";
2
2
  //#region src/notifications/device-store-of.ts
3
3
  /**
4
- * The devices' store `notifications()` configures: the app's own `store`, or the devices table in `db`. An app that
4
+ * The devices' store `notifications` configures: the app's own `store`, or the devices table in `db`. An app that
5
5
  * deletes users without Protobase's auth calls its `removeAllForUser` as it deletes one.
6
6
  */
7
7
  const deviceStore = (db, options = {}) => options.store ?? postgresDeviceStore(db, options);
@@ -2,7 +2,7 @@ import { Device, NewDevice } from "./device.js";
2
2
  //#region src/notifications/device-store.d.ts
3
3
  /**
4
4
  * Where each user's devices are kept: the Postgres default (`postgresDeviceStore`), or a store of the app's own passed
5
- * as `notifications({ store })`. Plain data in and out, so any store can implement it. Every method throws on an empty
5
+ * as `notifications.store`. Plain data in and out, so any store can implement it. Every method throws on an empty
6
6
  * user id, id or token, and none touches every device.
7
7
  */
8
8
  export type DeviceStore = {
@@ -10,7 +10,7 @@ const methods = [
10
10
  /** The store, checked to have every method; a missing one is an error naming it. */
11
11
  const checkDeviceStore = (store) => {
12
12
  const missing = methods.find((method) => typeof store[method] !== "function");
13
- if (missing !== void 0) throw new Error(`notifications({ store }): the device store has no ${missing} method`);
13
+ if (missing !== void 0) throw new Error(`notifications.store: the device store has no ${missing} method`);
14
14
  return store;
15
15
  };
16
16
  /** The value, refused when empty: no store call may be read as "every device". */
@@ -4,7 +4,7 @@ import { AdminAuth } from "../better-auth/create-auth.js";
4
4
  import { Db } from "@protobase/query";
5
5
  //#region src/notifications/history-store-of.d.ts
6
6
  /**
7
- * The history's store `notifications()` configures: the app's own `historyStore`, or the history table in `db`. An app
7
+ * The history's store `notifications` configures: the app's own `historyStore`, or the history table in `db`. An app
8
8
  * that deletes users without Protobase's auth calls its `removeAllForUser` as it deletes one.
9
9
  */
10
10
  export declare const historyStore: (db: Db, options?: NotificationsOptions) => HistoryStore;
@@ -1,7 +1,7 @@
1
1
  import { postgresHistoryStore } from "./postgres-history-store.js";
2
2
  //#region src/notifications/history-store-of.ts
3
3
  /**
4
- * The history's store `notifications()` configures: the app's own `historyStore`, or the history table in `db`. An app
4
+ * The history's store `notifications` configures: the app's own `historyStore`, or the history table in `db`. An app
5
5
  * that deletes users without Protobase's auth calls its `removeAllForUser` as it deletes one.
6
6
  */
7
7
  const historyStore = (db, options = {}) => options.historyStore ?? postgresHistoryStore(db, options);
@@ -4,7 +4,7 @@ import { HistoryPosition, NewNotification, StoredNotification } from "./history.
4
4
  export declare const maxUnreadCount = 100;
5
5
  /**
6
6
  * Where each user's inbox notifications are kept: the Postgres default (`postgresHistoryStore`), or a store of the
7
- * app's own passed as `notifications({ historyStore })`. Plain data in and out, so any store can implement it. Every
7
+ * app's own passed as `notifications.historyStore`. Plain data in and out, so any store can implement it. Every
8
8
  * method takes the user and touches that user's entries alone; each throws on an empty user id or id.
9
9
  */
10
10
  export type HistoryStore = {
@@ -12,7 +12,7 @@ const methods = [
12
12
  /** The store, checked to have every method; a missing one is an error naming it. */
13
13
  const checkHistoryStore = (store) => {
14
14
  const missing = methods.find((method) => typeof store[method] !== "function");
15
- if (missing !== void 0) throw new Error(`notifications({ historyStore }): the history store has no ${missing} method`);
15
+ if (missing !== void 0) throw new Error(`notifications.historyStore: the history store has no ${missing} method`);
16
16
  return store;
17
17
  };
18
18
  /** The most entries `list` gives at once. */
@@ -8,7 +8,7 @@ export type DeviceColumn = (typeof deviceColumns)[number];
8
8
  /** The columns of the history table, by the name Protobase gives them. */
9
9
  export declare const historyColumns: readonly ["id", "user_id", "title", "body", "url", "data", "created_at", "read_at"];
10
10
  export type HistoryColumn = (typeof historyColumns)[number];
11
- /** Notifications, turned on with `extends: [notifications()]`. */
11
+ /** Notifications, turned on with `notifications: {}` in `protobase.config.ts`. */
12
12
  export type NotificationsOptions = {
13
13
  /** The bell in the top bar; default true. `false` keeps only the backend. */
14
14
  ui?: boolean;
@@ -34,4 +34,6 @@ export type NotificationsOptions = {
34
34
  */
35
35
  senders?: PushSender[];
36
36
  };
37
+ /** The options, checked as `notifications` takes them: the names, given stores' methods and the senders given. */
38
+ export declare const checkNotificationsOptions: (options: NotificationsOptions) => NotificationsOptions;
37
39
  //#endregion
@@ -51,7 +51,7 @@ const historyNames = (options = {}) => ({
51
51
  table: checkedName("history.table", options.history?.table ?? "history"),
52
52
  columns: columnNames("history", historyColumns, options.history?.columns)
53
53
  });
54
- /** The options, checked as `notifications()` takes them: the names, given stores' methods and the senders given. */
54
+ /** The options, checked as `notifications` takes them: the names, given stores' methods and the senders given. */
55
55
  const checkNotificationsOptions = (options) => {
56
56
  if (options.ui !== void 0 && typeof options.ui !== "boolean") throw new Error("notifications ui is true or false");
57
57
  notificationsNames(options);
@@ -3,12 +3,12 @@ import { AdminAuth } from "../better-auth/create-auth.js";
3
3
  import { Db } from "@protobase/query";
4
4
  //#region src/notifications/schema.d.ts
5
5
  /**
6
- * The SQL that adds the tables `notifications()` keeps devices and each user's history in, under the names its options
6
+ * The SQL that adds the tables `notifications` keeps devices and each user's history in, under the names its options
7
7
  * give; with `auth`, their user columns point at auth's users table. Each table is left out when it exists or a store
8
8
  * of the app's own replaces it, and an existing devices table without its session column gets that column;
9
9
  * `undefined` when nothing is left.
10
10
  */
11
11
  export declare const notificationsMigration: (db: Db, options?: NotificationsOptions, auth?: AdminAuth) => Promise<string | undefined>;
12
- /** Refuses to start while the devices or the history table lacks what `notifications()` needs, naming it. */
12
+ /** Refuses to start while the devices or the history table lacks what `notifications` needs, naming it. */
13
13
  export declare const checkNotificationsSchema: (db: Db, options?: NotificationsOptions, auth?: AdminAuth) => Promise<void>;
14
14
  //#endregion
@@ -13,7 +13,7 @@ const authUsersTable = (auth) => ({
13
13
  const regclass = async (db, name) => (await sql`select to_regclass(${name})::oid as oid`.execute(db)).rows[0].oid;
14
14
  const userIdType = async (db, users) => {
15
15
  const oid = await regclass(db, qualified(users));
16
- if (oid === null) throw new Error(`notifications(): auth's users table ${qualified(users)} is not in the app's database, so devices cannot point at it`);
16
+ if (oid === null) throw new Error(`notifications: auth's users table ${qualified(users)} is not in the app's database, so devices cannot point at it`);
17
17
  const { rows } = await sql`select format_type(atttypid, atttypmod) as type from pg_attribute where attrelid = ${oid} and attname = 'id'`.execute(db);
18
18
  return rows[0].type;
19
19
  };
@@ -26,7 +26,7 @@ const lacksSessionColumn = async (db, names) => {
26
26
  return oid !== null && !(await columnsOf(db, oid)).some((row) => row.name === names.columns.session_id);
27
27
  };
28
28
  /**
29
- * The SQL that adds the tables `notifications()` keeps devices and each user's history in, under the names its options
29
+ * The SQL that adds the tables `notifications` keeps devices and each user's history in, under the names its options
30
30
  * give; with `auth`, their user columns point at auth's users table. Each table is left out when it exists or a store
31
31
  * of the app's own replaces it, and an existing devices table without its session column gets that column;
32
32
  * `undefined` when nothing is left.
@@ -50,9 +50,9 @@ const notificationsMigration = async (db, options = {}, auth) => {
50
50
  ];
51
51
  return `${[...new Set(statements)].join(";\n\n")};`;
52
52
  };
53
- const missingTable = (what, table) => /* @__PURE__ */ new Error(`The ${what} table ${table} of notifications() is missing. Write it as a migration with \`${migrationCommand}\`, apply it with the project's migrations, then start again.`);
53
+ const missingTable = (what, table) => /* @__PURE__ */ new Error(`The ${what} table ${table} of notifications is missing. Write it as a migration with \`${migrationCommand}\`, apply it with the project's migrations, then start again.`);
54
54
  /**
55
- * Refuses to start while the devices table lacks what `notifications()` needs, naming it: the table, each column under
55
+ * Refuses to start while the devices table lacks what `notifications` needs, naming it: the table, each column under
56
56
  * its configured name, a unique index on the token alone, which moving a device relies on, and with `auth` a foreign key
57
57
  * from the user column that deletes a user's devices with the user. Nothing to check with a store of the app's own.
58
58
  */
@@ -65,7 +65,7 @@ const checkDevicesTable = async (db, options, users) => {
65
65
  const found = await columnsOf(db, oid);
66
66
  const numOf = (column) => found.find((row) => row.name === names.columns[column])?.num;
67
67
  const missing = Object.keys(names.columns).find((column) => numOf(column) === void 0);
68
- if (missing !== void 0) throw new Error(`The devices table ${devices} has no column "${names.columns[missing]}": add it, or name the table's own with notifications({ devices: { columns: { ${missing}: ... } } })`);
68
+ if (missing !== void 0) throw new Error(`The devices table ${devices} has no column "${names.columns[missing]}": add it, or name the table's own with notifications: { devices: { columns: { ${missing}: ... } } } in protobase.config.ts`);
69
69
  if (!await uniqueOn(db, oid, numOf("token"))) throw new Error(`The devices table ${devices} needs a unique index on its token column "${names.columns.token}" alone, which moving a device to the user who registers it relies on`);
70
70
  if (users && !await cascadesFrom(db, oid, numOf("user_id"), users)) throw new Error(`The devices table ${devices} needs a foreign key from "${names.columns.user_id}" to ${qualified(users)} with on delete cascade, so a deleted user's devices go with them`);
71
71
  };
@@ -79,10 +79,10 @@ const checkHistoryTable = async (db, options, users) => {
79
79
  const found = await columnsOf(db, oid);
80
80
  const numOf = (column) => found.find((row) => row.name === names.columns[column])?.num;
81
81
  const missing = Object.keys(names.columns).find((column) => numOf(column) === void 0);
82
- if (missing !== void 0) throw new Error(`The history table ${history} has no column "${names.columns[missing]}": add it, or name the table's own with notifications({ history: { columns: { ${missing}: ... } } })`);
82
+ if (missing !== void 0) throw new Error(`The history table ${history} has no column "${names.columns[missing]}": add it, or name the table's own with notifications: { history: { columns: { ${missing}: ... } } } in protobase.config.ts`);
83
83
  if (users && !await cascadesFrom(db, oid, numOf("user_id"), users)) throw new Error(`The history table ${history} needs a foreign key from "${names.columns.user_id}" to ${qualified(users)} with on delete cascade, so a deleted user's history goes with them`);
84
84
  };
85
- /** Refuses to start while the devices or the history table lacks what `notifications()` needs, naming it. */
85
+ /** Refuses to start while the devices or the history table lacks what `notifications` needs, naming it. */
86
86
  const checkNotificationsSchema = async (db, options = {}, auth) => {
87
87
  const users = auth && authUsersTable(auth);
88
88
  await checkDevicesTable(db, options, users);
@@ -12,7 +12,7 @@ import { pushJobName } from "./push-job-name.js";
12
12
  const sendNotification = async (db, to, message) => {
13
13
  const send = checkedSend(to, message);
14
14
  const job = currentJobs().definition(pushJobName);
15
- if (!job) throw new Error("Notifications are off: add extends: [notifications()] to protobase.config.ts to send them");
15
+ if (!job) throw new Error("Notifications are off: add notifications: {} to protobase.config.ts to send them");
16
16
  await job.enqueue(db, send);
17
17
  };
18
18
  //#endregion
@@ -14,7 +14,7 @@ export type PushAnswer = {
14
14
  };
15
15
  /**
16
16
  * Delivers notifications to the devices of its `kinds`, through their push service: the built-in Web Push sender, or
17
- * one of the app's own passed as `notifications({ senders })`, typically from a package. `send` takes a message and a
17
+ * one of the app's own passed as `notifications.senders`, typically from a package. `send` takes a message and a
18
18
  * batch of devices of those kinds only, and answers each device by its id. A sender for `web` devices names the VAPID
19
19
  * `publicKey` browsers subscribe with, which every client may read.
20
20
  */
@@ -2,7 +2,7 @@ import { deviceKinds } from "./device.js";
2
2
  import { isPublicKey } from "./web-push/keys.js";
3
3
  //#region src/notifications/sender.ts
4
4
  const checkSender = (sender, index, claimed) => {
5
- const name = `notifications({ senders }): sender ${index}`;
5
+ const name = `notifications.senders: sender ${index}`;
6
6
  if (typeof sender !== "object" || sender === null || typeof sender.send !== "function") throw new Error(`${name} has no send method`);
7
7
  if (!Array.isArray(sender.kinds) || sender.kinds.length === 0) throw new Error(`${name} names no kinds: ${deviceKinds.join(", ")}`);
8
8
  for (const kind of sender.kinds) {
@@ -14,7 +14,7 @@ const checkSender = (sender, index, claimed) => {
14
14
  };
15
15
  /** The senders, checked: each with a send method and kinds, no kind delivered by two, and a public key when one is named. */
16
16
  const checkSenders = (senders) => {
17
- if (!Array.isArray(senders)) throw new Error("notifications({ senders }) is a list of senders");
17
+ if (!Array.isArray(senders)) throw new Error("notifications.senders is a list of senders");
18
18
  const claimed = /* @__PURE__ */ new Set();
19
19
  senders.forEach((sender, index) => checkSender(sender, index, claimed));
20
20
  return senders;
@@ -2,7 +2,7 @@ import { PushSender } from "./sender.js";
2
2
  import { NotificationsOptions } from "./options.js";
3
3
  //#region src/notifications/senders-of.d.ts
4
4
  /**
5
- * The senders `notifications()` delivers with: the app's own `senders`, which replace the built-in ones, or the
5
+ * The senders `notifications` delivers with: the app's own `senders`, which replace the built-in ones, or the
6
6
  * built-in ones whose environment is set. Each comes guarded (see `guardedSender`): the code that sends calls them only
7
7
  * through what this returns. A VAPID variable that is wrong stops startup.
8
8
  */
@@ -8,7 +8,7 @@ const builtInSenders = (env) => {
8
8
  return vapid ? [webPushSender(vapid)] : [];
9
9
  };
10
10
  /**
11
- * The senders `notifications()` delivers with: the app's own `senders`, which replace the built-in ones, or the
11
+ * The senders `notifications` delivers with: the app's own `senders`, which replace the built-in ones, or the
12
12
  * built-in ones whose environment is set. Each comes guarded (see `guardedSender`): the code that sends calls them only
13
13
  * through what this returns. A VAPID variable that is wrong stops startup.
14
14
  */
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.48.0";
2
+ var version = "0.49.0";
3
3
  //#endregion
4
4
  export { version };
@@ -89,4 +89,5 @@ export type SubscriptionDefinition = {
89
89
  * empty.
90
90
  */
91
91
  export declare const subscription: (name: string, options: SubscriptionOptions) => SubscriptionDefinition;
92
+ export declare const isSubscription: (value: unknown) => value is SubscriptionDefinition;
92
93
  //#endregion
@@ -1,6 +1,6 @@
1
1
  import { TenantValue } from "../../types.js";
2
2
  //#region src/webhooks/table/options.d.ts
3
- /** Webhook subscriptions made in the admin app (`extends: [webhooks()]`), kept in a table of the app's database. */
3
+ /** Webhook subscriptions made in the admin app (`webhooks: {}`), kept in a table of the app's database. */
4
4
  export type WebhooksOptions = {
5
5
  /** The schema of the subscriptions table and the deliveries view; default `webhooks`. */
6
6
  schema?: string;
@@ -3,11 +3,11 @@ import { WebhooksOptions } from "./options.js";
3
3
  import { Db } from "@protobase/query";
4
4
  //#region src/webhooks/table/schema.d.ts
5
5
  /**
6
- * The SQL that brings the `webhooks` schema to what `webhooks()` needs: the subscriptions table, its commands, the
6
+ * The SQL that brings the `webhooks` schema to what `webhooks` needs: the subscriptions table, its commands, the
7
7
  * deliveries view and, with `deliveryLog`, the log table. `undefined` when current. Needs the jobs schema, which the
8
8
  * deliveries view reads. Tables are never dropped.
9
9
  */
10
10
  export declare const webhooksMigration: (db: Db, options?: WebhooksOptions, events?: EventsOptions) => Promise<string | undefined>;
11
- /** Refuses to go on while the `webhooks` schema lacks what `webhooks()` needs; the project's migrations add it. */
11
+ /** Refuses to go on while the `webhooks` schema lacks what `webhooks` needs; the project's migrations add it. */
12
12
  export declare const checkWebhooksSchema: (db: Db, options?: WebhooksOptions) => Promise<void>;
13
13
  //#endregion
@@ -17,7 +17,7 @@ const readWebhooksSchema = async (db, schema) => {
17
17
  };
18
18
  };
19
19
  /**
20
- * The SQL that brings the `webhooks` schema to what `webhooks()` needs: the subscriptions table, its commands, the
20
+ * The SQL that brings the `webhooks` schema to what `webhooks` needs: the subscriptions table, its commands, the
21
21
  * deliveries view and, with `deliveryLog`, the log table. `undefined` when current. Needs the jobs schema, which the
22
22
  * deliveries view reads. Tables are never dropped.
23
23
  */
@@ -33,7 +33,7 @@ const webhooksMigration = async (db, options = {}, events) => {
33
33
  ...needsLog ? [deliveryLogSql(schema)] : []
34
34
  ].join(";\n\n")};`;
35
35
  };
36
- /** Refuses to go on while the `webhooks` schema lacks what `webhooks()` needs; the project's migrations add it. */
36
+ /** Refuses to go on while the `webhooks` schema lacks what `webhooks` needs; the project's migrations add it. */
37
37
  const checkWebhooksSchema = async (db, options = {}) => {
38
38
  const schema = webhooksSchema(options);
39
39
  const found = await readWebhooksSchema(db, schema);
@@ -4,12 +4,12 @@ import { checkWebhookUrl } from "./address-check.js";
4
4
  import { tableSubscriptions } from "./table-subscriptions.js";
5
5
  //#region src/webhooks/table/webhook-table.ts
6
6
  /**
7
- * What `createAdmin` needs for the subscriptions of `webhooks()`: the table, the owners' current roles (from Better Auth
7
+ * What `createAdmin` needs for the subscriptions of `webhooks`: the table, the owners' current roles (from Better Auth
8
8
  * unless the options say otherwise) and, unless allowed, the check that keeps URLs off private addresses.
9
9
  */
10
10
  const webhookTable = (options, auth) => {
11
11
  const ownerRoles = options.ownerRoles ?? (auth && betterAuthOwnerRoles(auth));
12
- if (!ownerRoles) throw new Error("webhooks() delivers as each subscription's owner, with their current roles: pass `auth` (Better Auth) or `ownerRoles` to webhooks()");
12
+ if (!ownerRoles) throw new Error("webhooks deliver as each subscription's owner, with their current roles: give the config `auth` (Better Auth), or `webhooks.ownerRoles`");
13
13
  return {
14
14
  subscriptions: tableSubscriptions({
15
15
  schema: webhooksSchema(options),
@@ -0,0 +1,108 @@
1
+ import { WebhooksOptions } from "./options.js";
2
+ //#region src/webhooks/table/webhooks-module.d.ts
3
+ /**
4
+ * What `webhooks` in `protobase.config.ts` adds to the app's resources: the Webhook subscriptions and Webhook deliveries
5
+ * pages, over a `webhooks` schema that `protobase migration` writes, and the delivery log with `deliveryLog`. Who may
6
+ * create subscriptions is the resources' access (`webhook_subscriptions.create`, ...); each delivers as its owner.
7
+ */
8
+ export declare const webhooksModule: (options?: WebhooksOptions) => {
9
+ webhookDeliveryLog?: import("@protobase/schema").ResourceBuilder<{
10
+ readonly id: import("@protobase/schema").Field<string>;
11
+ readonly subscription_id: import("@protobase/schema").Field<string | number>;
12
+ readonly tenant_id: import("@protobase/schema").Field<string | null>;
13
+ readonly event_id: import("@protobase/schema").Field<string>;
14
+ readonly event_type: import("@protobase/schema").Field<string>;
15
+ readonly status: import("@protobase/schema").Field<"failed" | "delivered">;
16
+ readonly attempt: import("@protobase/schema").Field<number>;
17
+ readonly status_code: import("@protobase/schema").Field<number | null>;
18
+ readonly response: import("@protobase/schema").Field<string | null>;
19
+ readonly duration_ms: import("@protobase/schema").Field<number>;
20
+ readonly at: import("@protobase/schema").Field<string>;
21
+ }, import("@protobase/schema").Ref<"id", string>> | undefined;
22
+ webhookDeliveryLogView?: import("@protobase/schema").ViewBuilder<import("@protobase/schema").ResourceBuilder<{
23
+ readonly id: import("@protobase/schema").Field<string>;
24
+ readonly subscription_id: import("@protobase/schema").Field<string | number>;
25
+ readonly tenant_id: import("@protobase/schema").Field<string | null>;
26
+ readonly event_id: import("@protobase/schema").Field<string>;
27
+ readonly event_type: import("@protobase/schema").Field<string>;
28
+ readonly status: import("@protobase/schema").Field<"failed" | "delivered">;
29
+ readonly attempt: import("@protobase/schema").Field<number>;
30
+ readonly status_code: import("@protobase/schema").Field<number | null>;
31
+ readonly response: import("@protobase/schema").Field<string | null>;
32
+ readonly duration_ms: import("@protobase/schema").Field<number>;
33
+ readonly at: import("@protobase/schema").Field<string>;
34
+ }, import("@protobase/schema").Ref<"id", string>>> | undefined;
35
+ webhookSubscriptions: import("@protobase/schema").ResourceBuilder<{
36
+ readonly id: import("@protobase/schema").Field<string>;
37
+ readonly name: import("@protobase/schema").Field<string>;
38
+ readonly events: import("@protobase/schema").Field<string>;
39
+ readonly url: import("@protobase/schema").Field<string>;
40
+ readonly filter: import("@protobase/schema").Field<string | null>;
41
+ readonly headers: import("@protobase/schema").Field<import("zod").JSONType>;
42
+ readonly payload: import("@protobase/schema").Field<"full" | "thin">;
43
+ readonly enabled: import("@protobase/schema").Field<boolean>;
44
+ readonly secret: import("@protobase/schema").Field<string>;
45
+ readonly previous_secret: null;
46
+ readonly previous_secret_until: null;
47
+ readonly tenant_id: import("@protobase/schema").Field<string | null>;
48
+ readonly owner_id: import("@protobase/schema").Field<string | null>;
49
+ readonly disabled_reason: import("@protobase/schema").Field<string | null>;
50
+ readonly last_success_at: import("@protobase/schema").Field<string | null>;
51
+ readonly failing_since: import("@protobase/schema").Field<string | null>;
52
+ readonly created_at: import("@protobase/schema").Field<string>;
53
+ readonly updated_at: import("@protobase/schema").Field<string>;
54
+ readonly rotate_secret: import("@protobase/schema").Field<boolean>;
55
+ readonly send_test: import("@protobase/schema").Field<boolean>;
56
+ readonly resend_failed: import("@protobase/schema").Field<boolean>;
57
+ }, import("@protobase/schema").Ref<"id", string>>;
58
+ webhookSubscriptionsView: import("@protobase/schema").ViewBuilder<import("@protobase/schema").ResourceBuilder<{
59
+ readonly id: import("@protobase/schema").Field<string>;
60
+ readonly name: import("@protobase/schema").Field<string>;
61
+ readonly events: import("@protobase/schema").Field<string>;
62
+ readonly url: import("@protobase/schema").Field<string>;
63
+ readonly filter: import("@protobase/schema").Field<string | null>;
64
+ readonly headers: import("@protobase/schema").Field<import("zod").JSONType>;
65
+ readonly payload: import("@protobase/schema").Field<"full" | "thin">;
66
+ readonly enabled: import("@protobase/schema").Field<boolean>;
67
+ readonly secret: import("@protobase/schema").Field<string>;
68
+ readonly previous_secret: null;
69
+ readonly previous_secret_until: null;
70
+ readonly tenant_id: import("@protobase/schema").Field<string | null>;
71
+ readonly owner_id: import("@protobase/schema").Field<string | null>;
72
+ readonly disabled_reason: import("@protobase/schema").Field<string | null>;
73
+ readonly last_success_at: import("@protobase/schema").Field<string | null>;
74
+ readonly failing_since: import("@protobase/schema").Field<string | null>;
75
+ readonly created_at: import("@protobase/schema").Field<string>;
76
+ readonly updated_at: import("@protobase/schema").Field<string>;
77
+ readonly rotate_secret: import("@protobase/schema").Field<boolean>;
78
+ readonly send_test: import("@protobase/schema").Field<boolean>;
79
+ readonly resend_failed: import("@protobase/schema").Field<boolean>;
80
+ }, import("@protobase/schema").Ref<"id", string>>>;
81
+ webhookDeliveries: import("@protobase/schema").ResourceBuilder<{
82
+ readonly id: import("@protobase/schema").Field<string>;
83
+ readonly subscription_id: import("@protobase/schema").Field<string | number>;
84
+ readonly tenant_id: import("@protobase/schema").Field<string | null>;
85
+ readonly event_type: import("@protobase/schema").Field<string>;
86
+ readonly event_id: import("@protobase/schema").Field<string>;
87
+ readonly status: import("@protobase/schema").Field<"waiting" | "failed">;
88
+ readonly attempt: import("@protobase/schema").Field<number>;
89
+ readonly next_attempt: import("@protobase/schema").Field<string | null>;
90
+ readonly last_error: import("@protobase/schema").Field<string | null>;
91
+ readonly created_at: import("@protobase/schema").Field<string>;
92
+ readonly resend: import("@protobase/schema").Field<boolean>;
93
+ }, import("@protobase/schema").Ref<"id", string>>;
94
+ webhookDeliveriesView: import("@protobase/schema").ViewBuilder<import("@protobase/schema").ResourceBuilder<{
95
+ readonly id: import("@protobase/schema").Field<string>;
96
+ readonly subscription_id: import("@protobase/schema").Field<string | number>;
97
+ readonly tenant_id: import("@protobase/schema").Field<string | null>;
98
+ readonly event_type: import("@protobase/schema").Field<string>;
99
+ readonly event_id: import("@protobase/schema").Field<string>;
100
+ readonly status: import("@protobase/schema").Field<"waiting" | "failed">;
101
+ readonly attempt: import("@protobase/schema").Field<number>;
102
+ readonly next_attempt: import("@protobase/schema").Field<string | null>;
103
+ readonly last_error: import("@protobase/schema").Field<string | null>;
104
+ readonly created_at: import("@protobase/schema").Field<string>;
105
+ readonly resend: import("@protobase/schema").Field<boolean>;
106
+ }, import("@protobase/schema").Ref<"id", string>>>;
107
+ };
108
+ //#endregion
@@ -0,0 +1,11 @@
1
+ import { webhooksSchema } from "./options.js";
2
+ import { webhookResources } from "@protobase/schema/internal";
3
+ //#region src/webhooks/table/webhooks-module.ts
4
+ /**
5
+ * What `webhooks` in `protobase.config.ts` adds to the app's resources: the Webhook subscriptions and Webhook deliveries
6
+ * pages, over a `webhooks` schema that `protobase migration` writes, and the delivery log with `deliveryLog`. Who may
7
+ * create subscriptions is the resources' access (`webhook_subscriptions.create`, ...); each delivers as its owner.
8
+ */
9
+ const webhooksModule = (options = {}) => webhookResources(webhooksSchema(options), { deliveryLog: Boolean(options.deliveryLog) });
10
+ //#endregion
11
+ export { webhooksModule };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -19,7 +19,8 @@
19
19
  "./auth-store": "./dist/auth-store.js",
20
20
  "./config-exports": "./dist/config-exports.js",
21
21
  "./email": "./dist/email.js",
22
- "./vapid": "./dist/vapid.js"
22
+ "./vapid": "./dist/vapid.js",
23
+ "./internal": "./dist/internal.js"
23
24
  },
24
25
  "types": "./dist/index.d.ts",
25
26
  "files": [
@@ -39,9 +40,9 @@
39
40
  "@better-auth/passkey": "^1.7.12",
40
41
  "@better-auth/sso": "^1.7.12",
41
42
  "@modelcontextprotocol/server": "^2.3.1",
42
- "@protobase/layout": "0.48.0",
43
- "@protobase/query": "0.48.0",
44
- "@protobase/schema": "0.48.0",
43
+ "@protobase/layout": "0.49.0",
44
+ "@protobase/query": "0.49.0",
45
+ "@protobase/schema": "0.49.0",
45
46
  "@react-email/render": "^2.1.0",
46
47
  "@scalar/hono-api-reference": "^0.12.10",
47
48
  "@sentry/core": "^11.7.0",
@@ -1,13 +0,0 @@
1
- import { NotificationsOptions } from "./options.js";
2
- import { ProjectConfig } from "../project-config.js";
3
- //#region src/notifications/plugin.d.ts
4
- /**
5
- * Notifications: `extends: [notifications()]` in `protobase.config.ts`. It keeps each user's devices, in the
6
- * `notifications.devices` table that `protobase migration` writes or in the `store` given, and serves the endpoints
7
- * through which a signed-in user registers and removes their own (`devicesPath`). It keeps each user's inbox
8
- * notifications in the `notifications.history` table or the `historyStore` given, and serves the endpoints through which
9
- * a signed-in user lists theirs and marks them read (`historyPath`). It delivers through `senders`, by default the
10
- * built-in ones whose environment is set (see `notificationSenders`).
11
- */
12
- export declare const notifications: (options?: NotificationsOptions) => ProjectConfig;
13
- //#endregion
@@ -1,16 +0,0 @@
1
- import { checkNotificationsOptions } from "./options.js";
2
- //#region src/notifications/plugin.ts
3
- /**
4
- * Notifications: `extends: [notifications()]` in `protobase.config.ts`. It keeps each user's devices, in the
5
- * `notifications.devices` table that `protobase migration` writes or in the `store` given, and serves the endpoints
6
- * through which a signed-in user registers and removes their own (`devicesPath`). It keeps each user's inbox
7
- * notifications in the `notifications.history` table or the `historyStore` given, and serves the endpoints through which
8
- * a signed-in user lists theirs and marks them read (`historyPath`). It delivers through `senders`, by default the
9
- * built-in ones whose environment is set (see `notificationSenders`).
10
- */
11
- const notifications = (options = {}) => ({
12
- name: "protobase-notifications",
13
- notifications: checkNotificationsOptions(options)
14
- });
15
- //#endregion
16
- export { notifications };
@@ -1,54 +0,0 @@
1
- import { MobileConfig } from "./mobile-config.js";
2
- import { AppIdentity } from "./app-identity.js";
3
- import { EventsOptions } from "./changes/events-options.js";
4
- import { Authenticator } from "./types.js";
5
- import { HistoryOptions } from "./history/options.js";
6
- import { WebhooksOptions } from "./webhooks/table/options.js";
7
- import { NotificationsOptions } from "./notifications/options.js";
8
- import { FilesOptions } from "./files/options.js";
9
- import { FunctionSource } from "./functions/define-function.js";
10
- import { AdminOptions } from "./admin-options.js";
11
- import { AdminAuth } from "./better-auth/create-auth.js";
12
- import { Db } from "@protobase/query";
13
- //#region src/project-config.d.ts
14
- /** What `protobase.config.ts` may default-export. Everything is optional. */
15
- export type ProjectConfig = {
16
- /** Names the config in errors, such as two configs defining the same tool; an extension should set it. */
17
- name?: string;
18
- /** Configs this one extends: merged in order before it (see `mergeConfig`). */
19
- extends?: ProjectConfig[];
20
- /** The config module, e.g. `import * as config from './config'`; default: ./config/index.ts and ./config/*\/ui.ts. */
21
- config?: Record<string, unknown>;
22
- db?: Db;
23
- authenticate?: Authenticator;
24
- /** A Better Auth instance; createAdmin mounts its routes under /api/auth. */
25
- auth?: AdminAuth;
26
- /** The API functions, each served at `/api/functions/<name>`; default: one per file in ./functions. */
27
- functions?: Record<string, FunctionSource>;
28
- /** Where file fields keep their bytes: providers by name, retention and the signing secret (see `FilesOptions`). */
29
- files?: FilesOptions;
30
- /** Where the changes that resources' events are raised from are captured (see `EventsOptions`). */
31
- events?: EventsOptions;
32
- /** How long the history of resources with `.history()` is kept (see `HistoryOptions`). */
33
- history?: HistoryOptions;
34
- /** Webhook subscriptions made in the admin app; set by `extends: [webhooks()]` (see `WebhooksOptions`). */
35
- webhooks?: WebhooksOptions;
36
- /** Notifications and each user's devices; set by `extends: [notifications()]` (see `NotificationsOptions`). */
37
- notifications?: NotificationsOptions;
38
- /** The module of the app's jobs, e.g. `import * as jobs from './config/jobs'`: every `defineJob` it exports. */
39
- jobs?: Record<string, unknown>;
40
- /** The app's iOS and Android build (see `MobileConfig`): `protobase mobile` needs it, and the API answers the app's origins. */
41
- mobile?: MobileConfig;
42
- /** The installed app's colours (see `AppIdentity`), which its web manifest carries. */
43
- app?: AppIdentity;
44
- options?: AdminOptions;
45
- };
46
- /** Typed identity, for `export default defineConfig({ extends: [...], config, auth, ... })` in `protobase.config.ts`. */
47
- export declare const defineConfig: (config: ProjectConfig) => ProjectConfig;
48
- /**
49
- * One config from several, each after the ones it `extends`, the last being the app's own. Named things are all kept:
50
- * resources, views and pages of the config modules, functions, jobs, assistant tools, file providers; one name defined by two configs is an error naming
51
- * both. Write hooks run in the same order. Every other value is the last config's that sets it, so the app's own wins.
52
- */
53
- export declare const mergeConfig: (...configs: ProjectConfig[]) => ProjectConfig;
54
- //#endregion