@protobase/server 0.45.20 → 0.45.22

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.
@@ -9,6 +9,7 @@ export type AppPoolConfig = {
9
9
  connectionString: string;
10
10
  max: number;
11
11
  connectionTimeoutMillis: number;
12
+ allowExitOnIdle: true;
12
13
  statement_timeout?: number;
13
14
  types: {
14
15
  getTypeParser: TypeParser;
@@ -34,7 +35,8 @@ export type PgModule<P extends PgPool> = {
34
35
  * Whoever asks first opens it, and everyone in the process shares it, across a dev server's reloads too, until it ends;
35
36
  * so the app never opens more connections than it may. Date and timestamp (without zone) come back as text, which the
36
37
  * API formats itself, a dropped connection is printed rather than fatal, and a query waits at most `poolWaitMs` for a
37
- * free connection.
38
+ * free connection. Idle connections keep no process alive, so a command exits when its work is done even while
39
+ * nobody ends the pool, such as one that sent no query of its own after Better Auth's schema check.
38
40
  */
39
41
  export declare const appPool: <P extends PgPool>(pg: PgModule<P>, url: string, max?: number) => P;
40
42
  /**
package/dist/app-pool.js CHANGED
@@ -18,7 +18,8 @@ const store = globalThis;
18
18
  * Whoever asks first opens it, and everyone in the process shares it, across a dev server's reloads too, until it ends;
19
19
  * so the app never opens more connections than it may. Date and timestamp (without zone) come back as text, which the
20
20
  * API formats itself, a dropped connection is printed rather than fatal, and a query waits at most `poolWaitMs` for a
21
- * free connection.
21
+ * free connection. Idle connections keep no process alive, so a command exits when its work is done even while
22
+ * nobody ends the pool, such as one that sent no query of its own after Better Auth's schema check.
22
23
  */
23
24
  const appPool = (pg, url, max = readDbConnections(globalThis.process?.env ?? {})) => {
24
25
  const pools = store.__protobaseAppPools ??= /* @__PURE__ */ new Map();
@@ -32,6 +33,7 @@ const appPool = (pg, url, max = readDbConnections(globalThis.process?.env ?? {})
32
33
  connectionString: url,
33
34
  max,
34
35
  connectionTimeoutMillis: poolWaitMs,
36
+ allowExitOnIdle: true,
35
37
  types: textDates(pg)
36
38
  }));
37
39
  pools.set(url, {
@@ -50,6 +52,7 @@ const longQueryPool = (pg, url) => handleDroppedConnections(new pg.Pool({
50
52
  connectionString: url,
51
53
  max: 1,
52
54
  connectionTimeoutMillis: longQueryTimeoutMs,
55
+ allowExitOnIdle: true,
53
56
  statement_timeout: longQueryTimeoutMs,
54
57
  types: textDates(pg)
55
58
  }));
@@ -211,6 +211,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
211
211
  ...runtime && { runtime },
212
212
  ...errorReporting && { errorReporting },
213
213
  ...maps && { maps },
214
+ ...notifications && notifications.ui !== false && { bell: true },
214
215
  ...pushKey && { webPushKey: pushKey },
215
216
  ...locale && { locale },
216
217
  plugins,
package/dist/deps.d.ts CHANGED
@@ -47,6 +47,8 @@ export type Deps = {
47
47
  errorReporting?: ErrorReportingSettings;
48
48
  /** The map style the admin app draws maps with and the layers they may switch to, which `/meta` names to everyone. */
49
49
  maps?: MapSettings;
50
+ /** Whether the shell draws the notification bell: notifications are on and their `ui` is not false. */
51
+ bell?: boolean;
50
52
  /** The VAPID public key browsers subscribe for Web Push with, when notifications have a web sender, which `/meta` names to everyone. */
51
53
  webPushKey?: string;
52
54
  /** The language the admin app's screens are in for everyone, which `/meta` names to everyone. */
package/dist/meta.d.ts CHANGED
@@ -49,9 +49,13 @@ export type CallerMeta = {
49
49
  errorReporting?: ErrorReportingSettings;
50
50
  /** The map style the admin app draws its maps with, and the layers they may switch to, when the app has one: public, as the tiles are. */
51
51
  maps?: MapSettings;
52
- /** The VAPID public key browsers subscribe for Web Push with, when the app's notifications deliver to browsers: public, as any VAPID key is. */
52
+ /**
53
+ * With notifications: `bell` when the shell draws the bell, which `ui: false` turns off, and the VAPID public key
54
+ * browsers subscribe for Web Push with, when the app's notifications deliver to browsers: public, as any VAPID key is.
55
+ */
53
56
  notifications?: {
54
- webPushKey: string;
57
+ bell?: true;
58
+ webPushKey?: string;
55
59
  };
56
60
  /** The language the admin app's screens are in, when the app sets one for everyone. */
57
61
  locale?: Locale;
package/dist/meta.js CHANGED
@@ -90,7 +90,10 @@ const createMeta = (deps, appName, appIcon) => {
90
90
  ...deps.runtime && seesRuntime(session.user.roles) && { runtime: { url: deps.runtime } },
91
91
  ...deps.errorReporting && { errorReporting: deps.errorReporting },
92
92
  ...deps.maps && { maps: deps.maps },
93
- ...deps.webPushKey && { notifications: { webPushKey: deps.webPushKey } },
93
+ ...(deps.bell || deps.webPushKey) && { notifications: {
94
+ ...deps.bell && { bell: true },
95
+ ...deps.webPushKey && { webPushKey: deps.webPushKey }
96
+ } },
94
97
  ...deps.locale && { locale: deps.locale },
95
98
  ...deps.files && deps.files.trustedHosts.length > 0 && { files: { trustedHosts: deps.files.trustedHosts } },
96
99
  ...plugins.length > 0 && { plugins },
@@ -1,4 +1,5 @@
1
1
  import "../types.js";
2
+ import "./history-store.js";
2
3
  import { Hono } from "hono";
3
4
  //#region src/notifications/history-routes.d.ts
4
5
  /** How many entries a page of the history holds when the request names no `limit`. */
@@ -5,7 +5,7 @@ import { ownSignInUser } from "./caller.js";
5
5
  import { historyCursor, historyPosition } from "./history-cursor.js";
6
6
  import { jsonBody, jsonOnly, limitedBody, queryOf } from "./history-request.js";
7
7
  import { historyUrl } from "./history-url.js";
8
- import "./paths.js";
8
+ import { historyIdParam } from "./paths.js";
9
9
  import { Hono } from "hono";
10
10
  import { z } from "zod";
11
11
  //#region src/notifications/history-routes.ts
@@ -70,11 +70,11 @@ const historyRoutes = (store, origins) => {
70
70
  await store.markAllRead(c.get("historyUser"), new Date(parsed.data.upTo));
71
71
  return c.body(null, 204);
72
72
  });
73
- app.post(`/:id/read`, ownSignIn, jsonOnly, limitedBody, async (c) => {
73
+ app.post(`/:${historyIdParam}/read`, ownSignIn, jsonOnly, limitedBody, async (c) => {
74
74
  queryOf(c, []);
75
75
  const body = await jsonBody(c);
76
76
  if (body !== void 0 && (typeof body !== "object" || body === null || Array.isArray(body) || Object.keys(body).length > 0)) throw badRequest("invalid-mark", "Marking one read takes no body");
77
- const id = c.req.param("id");
77
+ const id = c.req.param(historyIdParam);
78
78
  if (historyIdPattern.test(id)) await store.markRead(c.get("historyUser"), id);
79
79
  return c.body(null, 204);
80
80
  });
@@ -1,4 +1,5 @@
1
1
  import { JsonObject } from "./json-value.js";
2
+ import { historyIdPattern } from "@protobase/schema/internal";
2
3
  //#region src/notifications/history.d.ts
3
4
  /**
4
5
  * A notification as the history keeps it for its user's inbox: what a panel shows of it. `title` and `body` are plain
@@ -22,8 +23,6 @@ export type HistoryPosition = {
22
23
  createdAt: Date;
23
24
  id: string;
24
25
  };
25
- /** What an entry id looks like in every history store: it goes into a path, so no `/`, `.`, `?`, `#` or `%`. */
26
- export declare const historyIdPattern: RegExp;
27
26
  /** The longest title, body and url, as a push message takes them, and the most bytes of `data` as JSON. */
28
27
  export declare const historyLimits: {
29
28
  readonly title: 256;
@@ -31,4 +30,5 @@ export declare const historyLimits: {
31
30
  readonly url: 2048;
32
31
  readonly dataBytes: 4096;
33
32
  };
34
- //#endregion
33
+ //#endregion
34
+ export { historyIdPattern };
@@ -1,7 +1,6 @@
1
1
  import { isJsonObject, jsonProblem } from "./json-value.js";
2
+ import { historyIdPattern } from "@protobase/schema/internal";
2
3
  //#region src/notifications/history.ts
3
- /** What an entry id looks like in every history store: it goes into a path, so no `/`, `.`, `?`, `#` or `%`. */
4
- const historyIdPattern = /^[A-Za-z0-9_-]{1,64}$/;
5
4
  /** The longest title, body and url, as a push message takes them, and the most bytes of `data` as JSON. */
6
5
  const historyLimits = {
7
6
  title: 256,
@@ -1,14 +1,2 @@
1
- import { deviceIdParam, devicesPath } from "@protobase/schema/internal";
2
- //#region src/notifications/paths.d.ts
3
- /** The name of the entry id in the path that marks one entry of the history read. */
4
- export declare const historyIdParam = "id";
5
- /**
6
- * The path of the caller's notification history under the API's system path (`/api`): the list itself, `unread` its
7
- * unread count, `read` marking all read, or `{ read: id }` marking one entry read. An id that could leave the history's
8
- * path, any but letters, digits, `_` and `-`, is refused.
9
- */
10
- export declare const historyPath: (systemPath: string, part?: "unread" | "read" | {
11
- read: string;
12
- }) => string;
13
- //#endregion
14
- export { deviceIdParam, devicesPath };
1
+ import { deviceIdParam, devicesPath, historyIdParam, historyPath } from "@protobase/schema/internal";
2
+ export { deviceIdParam, devicesPath, historyIdParam, historyPath };
@@ -1,19 +1,2 @@
1
- import { historyIdPattern } from "./history.js";
2
- import { deviceIdParam, devicesPath } from "@protobase/schema/internal";
3
- //#region src/notifications/paths.ts
4
- /** The name of the entry id in the path that marks one entry of the history read. */
5
- const historyIdParam = "id";
6
- /**
7
- * The path of the caller's notification history under the API's system path (`/api`): the list itself, `unread` its
8
- * unread count, `read` marking all read, or `{ read: id }` marking one entry read. An id that could leave the history's
9
- * path, any but letters, digits, `_` and `-`, is refused.
10
- */
11
- const historyPath = (systemPath, part) => {
12
- const base = `${systemPath}/notifications/history`;
13
- if (part === void 0) return base;
14
- if (typeof part === "string") return `${base}/${part}`;
15
- if (!historyIdPattern.test(part.read)) throw new Error("A history entry id is letters, digits, \"_\" and \"-\" only");
16
- return `${base}/${part.read}/read`;
17
- };
18
- //#endregion
1
+ import { deviceIdParam, devicesPath, historyIdParam, historyPath } from "@protobase/schema/internal";
19
2
  export { deviceIdParam, devicesPath, historyIdParam, historyPath };
@@ -1,3 +1,4 @@
1
+ import "./history.js";
1
2
  import { PushMessage } from "./message.js";
2
3
  import { Db } from "@protobase/query";
3
4
  //#region src/notifications/send-input.d.ts
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.45.20";
2
+ var version = "0.45.22";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.45.20",
3
+ "version": "0.45.22",
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": {
@@ -36,9 +36,9 @@
36
36
  "@better-auth/passkey": "^1.7.12",
37
37
  "@better-auth/sso": "^1.7.12",
38
38
  "@modelcontextprotocol/server": "^2.3.1",
39
- "@protobase/layout": "0.45.20",
40
- "@protobase/query": "0.45.20",
41
- "@protobase/schema": "0.45.20",
39
+ "@protobase/layout": "0.45.22",
40
+ "@protobase/query": "0.45.22",
41
+ "@protobase/schema": "0.45.22",
42
42
  "@react-email/render": "^2.1.0",
43
43
  "@scalar/hono-api-reference": "^0.12.10",
44
44
  "@sentry/core": "^11.7.0",