better-supabase 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/bin/better-supabase.js +4 -0
- package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/claims-BAf6c7is.js +48 -0
- package/dist/cli/_format-Cax5EsM9.js +101 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +48 -0
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-CPVpXP5I.js +1965 -0
- package/dist/cli/config-dtoekM0v.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/diff-SjkO5rNr.js +752 -0
- package/dist/cli/dist-DBPdlPYK.js +1112 -0
- package/dist/cli/dist-DreZ24Sz.js +129 -0
- package/dist/cli/dist-xn4wlboc.js +732 -0
- package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
- package/dist/cli/env-DmKv_G4A.js +108 -0
- package/dist/cli/env-jIFUADLY.js +511 -0
- package/dist/cli/errors-4v4DTtda.js +56 -0
- package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
- package/dist/cli/gen-BJc9vdyu.js +2 -0
- package/dist/cli/gen-C2osRcr5.js +2204 -0
- package/dist/cli/index.d.ts +493 -0
- package/dist/cli/index.js +184 -0
- package/dist/cli/init-Saf5-R9T.js +550 -0
- package/dist/cli/introspect--H7oGmcd.js +81 -0
- package/dist/cli/io-k41_z-jT.js +24 -0
- package/dist/cli/json5-ChRycquj.js +787 -0
- package/dist/cli/jsonc-BUniUpOk.js +2 -0
- package/dist/cli/jsonc-D5Fu67jw.js +45 -0
- package/dist/cli/keys-ByKHnvP9.js +83 -0
- package/dist/cli/ohash-D7bG_EOb.js +277 -0
- package/dist/cli/openapi-Cp5Fyta-.js +66 -0
- package/dist/cli/project-B_GeXyHh.js +70 -0
- package/dist/cli/prompts-BNcNfAve.js +1142 -0
- package/dist/cli/prompts-BTYQGunc.js +2 -0
- package/dist/cli/read-sets-BfMQcKy8.js +289 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-BKR2W_79.js +351 -0
- package/dist/cli/seed-CUlqWh-8.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-DzPbfngx.js +348 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/cli/version-BDJuO0UR.js +4 -0
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +15 -22
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +5 -5
- package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
- package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
- package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
- package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
- package/dist/edge/index.d.ts +20 -13
- package/dist/edge/index.js +55 -26
- package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +69 -16
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/executor-BunPOO-k.d.ts +402 -0
- package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +27 -23
- package/dist/hono/index.js +46 -11
- package/dist/impersonation-VfaGc4Q_.js +41 -0
- package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
- package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
- package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
- package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
- package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
- package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
- package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
- package/dist/index.d.ts +34 -17
- package/dist/index.js +15 -14
- package/dist/jobs/index.d.ts +147 -95
- package/dist/jobs/index.js +773 -250
- package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
- package/dist/kit-events-CTFDfSwb.js +60 -0
- package/dist/kit-transport-B82k1djG.d.ts +33 -0
- package/dist/kit-transport-DJlHQUBU.js +55 -0
- package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.js +3 -3
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +39 -22
- package/dist/mcp/index.js +39 -25
- package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +60 -36
- package/dist/next/index.js +167 -50
- package/dist/notifications/index.d.ts +162 -0
- package/dist/notifications/index.js +264 -0
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +6 -6
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +37 -20
- package/dist/orpc/index.js +24 -11
- package/dist/otel/index.d.ts +24 -5
- package/dist/otel/index.js +124 -64
- package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
- package/dist/permdock-sql-DgOfeixi.js +51 -0
- package/dist/plugin-DwARMuQp.js +59 -0
- package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
- package/dist/plugins/actor/index.d.ts +5 -2
- package/dist/plugins/actor/index.js +22 -8
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +52 -17
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +14 -5
- package/dist/plugins/tenant/index.d.ts +3 -3
- package/dist/plugins/tenant/index.js +45 -21
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +4 -3
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +26 -6
- package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -10
- package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +3 -3
- package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
- package/dist/queue-BYUCjr59.d.ts +252 -0
- package/dist/react/index.d.ts +8 -7
- package/dist/react/index.js +4 -3
- package/dist/react/server.d.ts +16 -5
- package/dist/react/server.js +12 -4
- package/dist/react/session.d.ts +10 -4
- package/dist/react/session.js +11 -3
- package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
- package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +14 -12
- package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
- package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
- package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
- package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
- package/dist/respond-D2OQl62-.d.ts +371 -0
- package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
- package/dist/server/index.d.ts +17 -15
- package/dist/server/index.js +16 -22
- package/dist/server-Csx2sRjg.js +655 -0
- package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
- package/dist/sql/index.d.ts +247 -10
- package/dist/sql/index.js +8591 -780
- package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +174 -27
- package/dist/support-DT-OhSxD.d.ts +88 -0
- package/dist/support-Dk6vdmEw.js +291 -0
- package/dist/support-view-Da5peKFg.d.ts +17 -0
- package/dist/support-view-g4YLBeqX.js +18 -0
- package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
- package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +113 -24
- package/dist/testing/index.js +375 -47
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
- package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
- package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
- package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
- package/dist/webhooks/index.d.ts +136 -5
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +50 -10
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +10 -0
- package/skills/better-supabase/SKILL.md +60 -10
- package/skills/better-supabase/references/plugins.md +13 -14
- package/skills/better-supabase/references/troubleshooting.md +25 -4
- package/skills/better-supabase-api/SKILL.md +74 -19
- package/skills/better-supabase-api/references/adapters.md +48 -27
- package/skills/better-supabase-auth/SKILL.md +149 -0
- package/skills/better-supabase-auth/references/permdock.md +100 -0
- package/skills/better-supabase-testing/SKILL.md +58 -4
- package/dist/claims-CoTobNC-.js +0 -25
- package/dist/events-Do8jXh4D.d.ts +0 -84
- package/dist/executor-DynmIWhq.d.ts +0 -195
- package/dist/invalidate-DbxuL2jL.js +0 -18
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-C44EZAJo.js +0 -399
- package/dist/respond-DAkPIdTH.d.ts +0 -175
- package/dist/result-c2OYgCV2.d.ts +0 -203
- package/dist/view-wZ-9qIUz.js +0 -45
package/dist/next/index.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { n as dbError } from "../errors-
|
|
2
|
-
import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-
|
|
3
|
-
import { o as isReadSet } from "../read-set-
|
|
4
|
-
import { t as validate } from "../standard-
|
|
5
|
-
import { a as problemResponse } from "../problem-
|
|
1
|
+
import { n as dbError } from "../errors-DtwXa6b8.js";
|
|
2
|
+
import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-Bqob3WqT.js";
|
|
3
|
+
import { o as isReadSet } from "../read-set-DQFt1N_Y.js";
|
|
4
|
+
import { t as validate } from "../standard-D7r6dMUu.js";
|
|
5
|
+
import { a as problemResponse } from "../problem-BW1KhzSG.js";
|
|
6
6
|
import { t as isList } from "../guards-BnzDvFrz.js";
|
|
7
|
+
import { t as supportOf } from "../support-view-g4YLBeqX.js";
|
|
7
8
|
import { t as hasEntitlement } from "../entitlements-CTEBm7IB.js";
|
|
8
|
-
import { a as
|
|
9
|
-
import {
|
|
10
|
-
import { t as
|
|
9
|
+
import { a as withExtra, b as withPrimaryPin, d as guard, m as settle, n as createServer, p as respond, r as extendServer, u as defaultExpose, v as PRIMARY_COOKIE, y as primaryCookieOptions } from "../server-Csx2sRjg.js";
|
|
10
|
+
import { a as checkAal, n as toSession } from "../view-Bq64ki8-.js";
|
|
11
|
+
import { t as supportSessions } from "../support-Dk6vdmEw.js";
|
|
11
12
|
import { cache } from "react";
|
|
12
13
|
import { cacheLife, cacheTag, revalidateTag, updateTag } from "next/cache.js";
|
|
13
14
|
import { cookies, headers } from "next/headers.js";
|
|
14
|
-
import { NextResponse } from "next/server.js";
|
|
15
|
+
import { NextResponse, after } from "next/server.js";
|
|
15
16
|
//#region src/next/collector.ts
|
|
16
17
|
/** Forwarded by the proxy so every scope of one render shares an id. */
|
|
17
18
|
const REQUEST_ID_HEADER = "x-bs-request-id";
|
|
@@ -143,8 +144,12 @@ function serverTimingValue(metrics) {
|
|
|
143
144
|
}
|
|
144
145
|
//#endregion
|
|
145
146
|
//#region src/next/create.ts
|
|
146
|
-
/** Response header with the URL of the request's totals on `
|
|
147
|
+
/** Response header with the URL of the request's totals on `bs.debugRoute()`. */
|
|
147
148
|
const STATS_URL_HEADER = "x-bs-stats";
|
|
149
|
+
/** The tag `bs.cached()` adds to entries rendered in a support session. */
|
|
150
|
+
function supportTag(sessionId) {
|
|
151
|
+
return `bs:support:${sessionId}`;
|
|
152
|
+
}
|
|
148
153
|
/**
|
|
149
154
|
* `cacheLife` `stale` seconds for a view of `session`: `max`, but never past
|
|
150
155
|
* the token's expiry, and at least `min`.
|
|
@@ -156,7 +161,7 @@ function sessionStale(session, options = {}, now = Date.now()) {
|
|
|
156
161
|
const remaining = session.expiresAt - Math.floor(now / 1e3);
|
|
157
162
|
return Math.min(max, Math.max(min, remaining));
|
|
158
163
|
}
|
|
159
|
-
/** The tag `
|
|
164
|
+
/** The tag `bs.cached()` puts on a user's entries. */
|
|
160
165
|
function sessionTag(userId) {
|
|
161
166
|
return `bs:session:${userId}`;
|
|
162
167
|
}
|
|
@@ -202,7 +207,7 @@ function formDataObject(form) {
|
|
|
202
207
|
* combine it with your sign-in redirect.
|
|
203
208
|
*
|
|
204
209
|
* ```ts
|
|
205
|
-
*
|
|
210
|
+
* bs.proxy(request, {
|
|
206
211
|
* protect: requireAal('aal2', { redirect: '/mfa', match: (path) => path.startsWith('/settings') }),
|
|
207
212
|
* });
|
|
208
213
|
* ```
|
|
@@ -235,20 +240,20 @@ function shouldRefresh(request) {
|
|
|
235
240
|
* actions and cache tags, on top of `createServer`.
|
|
236
241
|
*
|
|
237
242
|
* ```ts
|
|
238
|
-
* export const
|
|
243
|
+
* export const bs = createNext(betterSupabase);
|
|
239
244
|
* ```
|
|
240
245
|
*/
|
|
241
|
-
function createNext(
|
|
242
|
-
const base = createServer(
|
|
246
|
+
function createNext(betterSupabase, options = {}) {
|
|
247
|
+
const base = createServer(betterSupabase, options);
|
|
243
248
|
const pinMs = options.replicas?.pinMs ?? 5e3;
|
|
244
249
|
const expose = options.exposeErrors ?? defaultExpose();
|
|
245
|
-
if (options.cacheTags !== false)
|
|
250
|
+
if (options.cacheTags !== false) betterSupabase.cache(nextCache());
|
|
246
251
|
const debug = options.debug;
|
|
247
252
|
const statsHeader = debug?.header ?? "x-bs-db-calls";
|
|
248
253
|
const statsRoute = debug?.route ?? "/api/bs-stats";
|
|
249
254
|
const development = process.env["NODE_ENV"] === "development";
|
|
250
255
|
const collector = debug?.enabled ? sharedCollector({
|
|
251
|
-
logger:
|
|
256
|
+
logger: betterSupabase.events.logger,
|
|
252
257
|
warn: development,
|
|
253
258
|
...debug.budget ? { budget: debug.budget } : {}
|
|
254
259
|
}) : void 0;
|
|
@@ -256,35 +261,66 @@ function createNext(sb, options = {}) {
|
|
|
256
261
|
const id = collector && request.headers.get("x-bs-request-id");
|
|
257
262
|
return id ? { stats: collector.recorderFor(id) } : {};
|
|
258
263
|
};
|
|
264
|
+
/** Keeps the function alive for event sink sends the handler started. */
|
|
265
|
+
const flushAfter = () => {
|
|
266
|
+
const { events } = betterSupabase;
|
|
267
|
+
if (!events.pending) return;
|
|
268
|
+
try {
|
|
269
|
+
after(() => events.settled());
|
|
270
|
+
} catch (cause) {
|
|
271
|
+
events.logger.warn("after() is unavailable; event sends may be cut off", { cause });
|
|
272
|
+
}
|
|
273
|
+
};
|
|
259
274
|
const incomingRequest = async () => new Request("http://next.local/", { headers: new Headers(await headers()) });
|
|
260
|
-
|
|
275
|
+
/** One token verification per render scope backs `context()`, `session()` and `cached()`. */
|
|
276
|
+
const incoming = cache(async () => {
|
|
261
277
|
const request = await incomingRequest();
|
|
262
|
-
return
|
|
278
|
+
return {
|
|
279
|
+
request,
|
|
280
|
+
resolution: await base.resolve(request, { refresh: false })
|
|
281
|
+
};
|
|
263
282
|
});
|
|
264
|
-
const
|
|
265
|
-
const resolution = await
|
|
266
|
-
|
|
283
|
+
const current = cache(async () => {
|
|
284
|
+
const { request, resolution } = await incoming();
|
|
285
|
+
const [tenant, support] = await Promise.all([options.tenant?.(request, resolution.auth), base.support.current(request, resolution.auth)]);
|
|
286
|
+
return base.contextFromResolution(resolution, request, {
|
|
287
|
+
...statsFor(request),
|
|
288
|
+
...tenant === void 0 ? {} : { tenant },
|
|
289
|
+
...support ? { support } : {}
|
|
290
|
+
});
|
|
267
291
|
});
|
|
268
|
-
const
|
|
292
|
+
const context = (request, contextOptions) => request ? base.context(request, contextOptions) : current();
|
|
293
|
+
/** In a support session, the target's view: `impersonator` names the admin. */
|
|
294
|
+
const session = cache(async () => toSession(options.support ? (await current()).auth : (await incoming()).resolution.auth));
|
|
295
|
+
const supportCookieName = options.support?.options.cookie?.name ?? "bs-support";
|
|
296
|
+
const supportCookieOptions = {
|
|
297
|
+
httpOnly: true,
|
|
298
|
+
sameSite: "lax",
|
|
299
|
+
path: options.support?.options.cookie?.path ?? "/",
|
|
300
|
+
secure: options.support?.options.cookie?.secure ?? true
|
|
301
|
+
};
|
|
302
|
+
const contextForSession = async (view, { token }) => {
|
|
269
303
|
const request = new Request("http://next.local/", { headers: token ? { authorization: `Bearer ${token}` } : {} });
|
|
270
304
|
const ctx = await base.context(request);
|
|
271
|
-
if (ctx.auth.kind === "user" ? view.kind === "user" && view.user.id === ctx.auth.user.id : view.kind
|
|
305
|
+
if (ctx.auth.kind === "user" ? view.kind === "user" && view.user.id === ctx.auth.user.id : view.kind === ctx.auth.kind) return ctx;
|
|
272
306
|
return base.contextFor({
|
|
273
307
|
kind: "invalid",
|
|
274
308
|
reason: "token",
|
|
275
|
-
error: dbError("unauthorized", "The token does not belong to the session passed to
|
|
309
|
+
error: dbError("unauthorized", "The token does not belong to the session passed to contextForSession()")
|
|
276
310
|
});
|
|
277
311
|
};
|
|
278
|
-
const withAccounts = withExtra(base, {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
312
|
+
const withAccounts = withExtra(base, {
|
|
313
|
+
context,
|
|
314
|
+
deleteAccount: ((userId, deleteOptions) => base.deleteAccount(userId, deleteOptions).map((result) => {
|
|
315
|
+
invalidate(sessionTag(userId));
|
|
316
|
+
return result;
|
|
317
|
+
}))
|
|
318
|
+
});
|
|
282
319
|
return extendServer(withAccounts, {
|
|
283
|
-
server,
|
|
284
320
|
session,
|
|
285
|
-
|
|
321
|
+
contextForSession,
|
|
286
322
|
async cached(cachedOptions = {}) {
|
|
287
|
-
const [view, ctx] = await Promise.all([session(),
|
|
323
|
+
const [view, ctx] = await Promise.all([session(), current()]);
|
|
288
324
|
const life = cachedOptions.life;
|
|
289
325
|
const stale = sessionStale(view, life);
|
|
290
326
|
cacheLife({
|
|
@@ -292,7 +328,11 @@ function createNext(sb, options = {}) {
|
|
|
292
328
|
...life?.revalidate === void 0 ? {} : { revalidate: life.revalidate },
|
|
293
329
|
...life?.expire === void 0 ? {} : { expire: life.expire }
|
|
294
330
|
});
|
|
295
|
-
const tags = [
|
|
331
|
+
const tags = [
|
|
332
|
+
...view.kind === "user" ? [sessionTag(view.user.id)] : [],
|
|
333
|
+
...ctx.support ? [supportTag(ctx.support.session.id)] : [],
|
|
334
|
+
...cachedOptions.tags ?? []
|
|
335
|
+
];
|
|
296
336
|
if (tags.length > 0) cacheTag(...tags);
|
|
297
337
|
return withExtra(ctx, { session: view });
|
|
298
338
|
},
|
|
@@ -301,7 +341,7 @@ function createNext(sb, options = {}) {
|
|
|
301
341
|
for (const tag of invalidateOptions.tags ?? []) invalidate(tag);
|
|
302
342
|
},
|
|
303
343
|
async liveCount(spec, db) {
|
|
304
|
-
const result = await (db ?? (await
|
|
344
|
+
const result = await (db ?? (await current()).db).$run(spec);
|
|
305
345
|
return {
|
|
306
346
|
spec,
|
|
307
347
|
count: result.ok && typeof result.data === "number" ? result.data : null
|
|
@@ -349,13 +389,21 @@ function createNext(sb, options = {}) {
|
|
|
349
389
|
expose
|
|
350
390
|
});
|
|
351
391
|
const params = await segment.params;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
} catch {
|
|
392
|
+
let response;
|
|
393
|
+
try {
|
|
394
|
+
response = await respond(() => handler(request, withExtra(ctx, { params })), {
|
|
395
|
+
instance,
|
|
396
|
+
expose
|
|
397
|
+
});
|
|
398
|
+
} catch (cause) {
|
|
399
|
+
rethrowNextControlFlow(cause);
|
|
400
|
+
response = problemResponse(dbError("unexpected", expose && cause instanceof Error ? cause.message : "Internal server error"), {
|
|
401
|
+
instance,
|
|
402
|
+
expose
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
response = withPrimaryPin(response, ctx.replica, pinMs);
|
|
406
|
+
flushAfter();
|
|
359
407
|
if (collector) try {
|
|
360
408
|
response.headers.set(statsHeader, formatStats(ctx.stats()));
|
|
361
409
|
} catch {}
|
|
@@ -378,7 +426,7 @@ function createNext(sb, options = {}) {
|
|
|
378
426
|
},
|
|
379
427
|
action(actionOptions, fn) {
|
|
380
428
|
return async (input) => {
|
|
381
|
-
const ctx = await
|
|
429
|
+
const ctx = await current();
|
|
382
430
|
const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal, actionOptions.scopes);
|
|
383
431
|
if (denied) return {
|
|
384
432
|
ok: false,
|
|
@@ -396,12 +444,8 @@ function createNext(sb, options = {}) {
|
|
|
396
444
|
parsed = checked.data;
|
|
397
445
|
}
|
|
398
446
|
const settled = await settle(() => fn(parsed, ctx));
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
maxAge: Math.max(1, Math.ceil(pinMs / 1e3)),
|
|
402
|
-
httpOnly: true,
|
|
403
|
-
sameSite: "lax"
|
|
404
|
-
});
|
|
447
|
+
flushAfter();
|
|
448
|
+
if (ctx.replica?.wrote) (await cookies()).set(PRIMARY_COOKIE, String(Date.now() + pinMs), primaryCookieOptions(pinMs));
|
|
405
449
|
return settled.ok ? {
|
|
406
450
|
ok: true,
|
|
407
451
|
data: settled.data,
|
|
@@ -413,6 +457,59 @@ function createNext(sb, options = {}) {
|
|
|
413
457
|
};
|
|
414
458
|
};
|
|
415
459
|
},
|
|
460
|
+
async startSupport(request) {
|
|
461
|
+
const { resolution } = await incoming();
|
|
462
|
+
const started = await base.support.start(resolution.auth, request);
|
|
463
|
+
flushAfter();
|
|
464
|
+
if (!started.ok) return {
|
|
465
|
+
ok: false,
|
|
466
|
+
data: null,
|
|
467
|
+
error: started.error
|
|
468
|
+
};
|
|
469
|
+
const { session: support } = started.data;
|
|
470
|
+
(await cookies()).set(supportCookieName, support.id, {
|
|
471
|
+
...supportCookieOptions,
|
|
472
|
+
maxAge: Math.max(0, Math.floor((support.expiresAt.epochMilliseconds - Date.now()) / 1e3))
|
|
473
|
+
});
|
|
474
|
+
if (resolution.auth.kind === "user") invalidate(sessionTag(resolution.auth.user.id));
|
|
475
|
+
return {
|
|
476
|
+
ok: true,
|
|
477
|
+
data: {
|
|
478
|
+
sessionId: support.id,
|
|
479
|
+
targetUserId: support.targetUserId,
|
|
480
|
+
readOnly: support.readOnly,
|
|
481
|
+
expiresAt: support.expiresAt.toString()
|
|
482
|
+
},
|
|
483
|
+
error: null
|
|
484
|
+
};
|
|
485
|
+
},
|
|
486
|
+
async stopSupport() {
|
|
487
|
+
const { request, resolution } = await incoming();
|
|
488
|
+
const jar = await cookies();
|
|
489
|
+
const id = base.support.sessionIdOf(request);
|
|
490
|
+
jar.delete({
|
|
491
|
+
name: supportCookieName,
|
|
492
|
+
path: supportCookieOptions.path
|
|
493
|
+
});
|
|
494
|
+
if (!id) return {
|
|
495
|
+
ok: true,
|
|
496
|
+
data: { ended: false },
|
|
497
|
+
error: null
|
|
498
|
+
};
|
|
499
|
+
const stopped = await base.support.stop(resolution.auth, id);
|
|
500
|
+
flushAfter();
|
|
501
|
+
invalidate(supportTag(id));
|
|
502
|
+
if (resolution.auth.kind === "user") invalidate(sessionTag(resolution.auth.user.id));
|
|
503
|
+
return stopped.ok ? {
|
|
504
|
+
ok: true,
|
|
505
|
+
data: { ended: stopped.data.ended },
|
|
506
|
+
error: null
|
|
507
|
+
} : {
|
|
508
|
+
ok: false,
|
|
509
|
+
data: null,
|
|
510
|
+
error: stopped.error
|
|
511
|
+
};
|
|
512
|
+
},
|
|
416
513
|
cacheTag(table, id) {
|
|
417
514
|
cacheTag(tagFor(table), ...id === void 0 ? [] : [tagFor(table, id)]);
|
|
418
515
|
},
|
|
@@ -420,7 +517,7 @@ function createNext(sb, options = {}) {
|
|
|
420
517
|
const specs = isReadSet(target) ? Object.values(target.specs) : isList(target) ? target : [target];
|
|
421
518
|
const tags = /* @__PURE__ */ new Set();
|
|
422
519
|
for (const spec of specs) {
|
|
423
|
-
for (const table of
|
|
520
|
+
for (const table of betterSupabase.tablesOf(spec)) tags.add(tagFor(table));
|
|
424
521
|
const [id] = spec.args;
|
|
425
522
|
if (spec.method === "findById" && (typeof id === "string" || typeof id === "number")) tags.add(tagFor(spec.table, id));
|
|
426
523
|
}
|
|
@@ -428,5 +525,25 @@ function createNext(sb, options = {}) {
|
|
|
428
525
|
}
|
|
429
526
|
});
|
|
430
527
|
}
|
|
528
|
+
const NEXT_DIGESTS = /* @__PURE__ */ new Set([
|
|
529
|
+
"NEXT_REDIRECT",
|
|
530
|
+
"NEXT_HTTP_ERROR_FALLBACK",
|
|
531
|
+
"DYNAMIC_SERVER_USAGE",
|
|
532
|
+
"BAILOUT_TO_CLIENT_SIDE_RENDERING",
|
|
533
|
+
"HANGING_PROMISE_REJECTION",
|
|
534
|
+
"NEXT_PRERENDER_INTERRUPTED"
|
|
535
|
+
]);
|
|
536
|
+
const REACT_POSTPONE = Symbol.for("react.postpone");
|
|
537
|
+
/**
|
|
538
|
+
* `unstable_rethrow` without `next/navigation`: that entry loads the client
|
|
539
|
+
* router context, which Turbopack can't resolve inside route handlers. The
|
|
540
|
+
* digests are the ones Next 16 checks.
|
|
541
|
+
*/
|
|
542
|
+
function rethrowNextControlFlow(error) {
|
|
543
|
+
if (typeof error !== "object" || error === null) return;
|
|
544
|
+
const digest = "digest" in error ? error.digest : void 0;
|
|
545
|
+
if (typeof digest === "string" && NEXT_DIGESTS.has(digest.split(";", 1)[0] ?? "") || "$$typeof" in error && error.$$typeof === REACT_POSTPONE || error instanceof Error && error.message.includes("needs to bail out of prerendering")) throw error;
|
|
546
|
+
if ("cause" in error) rethrowNextControlFlow(error.cause);
|
|
547
|
+
}
|
|
431
548
|
//#endregion
|
|
432
|
-
export { REQUEST_ID_HEADER, STATS_URL_HEADER, createNext, hasEntitlement, nextCache, requireAal, sessionStale, sessionTag, shouldRefresh, tagFor, toSession };
|
|
549
|
+
export { REQUEST_ID_HEADER, STATS_URL_HEADER, createNext, hasEntitlement, nextCache, requireAal, sessionStale, sessionTag, shouldRefresh, supportOf, supportSessions, supportTag, tagFor, toSession };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { S as ErrorMapper, a as AsyncResult } from "../executor-BunPOO-k.js";
|
|
3
|
+
import { a as EventHub } from "../events-aIO7vsIk.js";
|
|
4
|
+
import { g as RequestContext } from "../plugin-o144f322.js";
|
|
5
|
+
import { i as sqlTransport, n as RpcClient, r as rpcTransport, t as KitTransport } from "../kit-transport-B82k1djG.js";
|
|
6
|
+
import { a as NotificationSubject, i as NotificationItem, n as NotificationMessage, o as RenderedText, r as NotificationSendResult, t as NotificationChannel } from "../channel-FQW-CnGR.js";
|
|
7
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
8
|
+
//#region src/notifications/notifications.d.ts
|
|
9
|
+
/** Notification type to the Standard Schema of its `data`. */
|
|
10
|
+
type NotificationTypes = Readonly<Record<string, StandardSchemaV1>>;
|
|
11
|
+
type TypeName<K extends NotificationTypes> = keyof K & string;
|
|
12
|
+
interface SendInput<D = unknown> {
|
|
13
|
+
/** Who gets it. Watchers of `subject` and the `notification_audience` hook add more. */
|
|
14
|
+
readonly recipients?: readonly string[];
|
|
15
|
+
/** The organization; required when an authenticated user sends. */
|
|
16
|
+
readonly tenant?: string;
|
|
17
|
+
readonly subject?: NotificationSubject;
|
|
18
|
+
readonly summary?: string;
|
|
19
|
+
readonly actionPath?: string;
|
|
20
|
+
/** One of `kits.notifications.options.priorities`. Defaults to `normal`. */
|
|
21
|
+
readonly priority?: string;
|
|
22
|
+
readonly data: D;
|
|
23
|
+
/** With a key, sending again (per tenant) returns the first notification. */
|
|
24
|
+
readonly key?: string;
|
|
25
|
+
/** Defaults to `kits.notifications.options.channels`. */
|
|
26
|
+
readonly channels?: readonly string[];
|
|
27
|
+
/** Also notify the user who caused it. Defaults to `false`. */
|
|
28
|
+
readonly includeActor?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* `participating` (default) reaches subject watchers at both levels;
|
|
31
|
+
* `all` is routine activity that only `all` watchers get.
|
|
32
|
+
*/
|
|
33
|
+
readonly activity?: "participating" | "all";
|
|
34
|
+
/** Create it already resolved, e.g. for a record of a finished approval. */
|
|
35
|
+
readonly resolved?: boolean;
|
|
36
|
+
/** The actor, when the service sends on a user's behalf. */
|
|
37
|
+
readonly actorId?: string;
|
|
38
|
+
}
|
|
39
|
+
interface ListOptions<K extends string = string> {
|
|
40
|
+
readonly tenant?: string;
|
|
41
|
+
readonly status?: "all" | "unread" | "read" | "unresolved";
|
|
42
|
+
readonly types?: readonly K[];
|
|
43
|
+
/**
|
|
44
|
+
* Page: only notifications older than this. Pass the last item of the
|
|
45
|
+
* previous page; a bare instant skips items created at that same instant.
|
|
46
|
+
*/
|
|
47
|
+
readonly before?: Temporal.Instant | Pick<NotificationItem, "createdAt" | "id">;
|
|
48
|
+
/** Up to 200. Defaults to 50. */
|
|
49
|
+
readonly limit?: number;
|
|
50
|
+
/** Passed to `render`. */
|
|
51
|
+
readonly locale?: string;
|
|
52
|
+
}
|
|
53
|
+
interface NotificationCounts {
|
|
54
|
+
readonly unread: number;
|
|
55
|
+
/** Unresolved notifications of the `actionable` types. */
|
|
56
|
+
readonly actionable: number;
|
|
57
|
+
}
|
|
58
|
+
type SubscriptionLevel = "participating" | "all" | "ignore";
|
|
59
|
+
interface DeliverOptions {
|
|
60
|
+
/** Channel names to work on. Defaults to every channel in `channels`. */
|
|
61
|
+
readonly channels?: readonly string[];
|
|
62
|
+
/** Deliveries per claim. Defaults to 50. */
|
|
63
|
+
readonly batch?: number;
|
|
64
|
+
/** How long a claimed delivery waits before another worker retries it. */
|
|
65
|
+
readonly lease?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Tries before a delivery is `failed`. Defaults to 5. Retries wait a random
|
|
68
|
+
* time up to 30 seconds, doubling per attempt to an hour.
|
|
69
|
+
*/
|
|
70
|
+
readonly maxAttempts?: number;
|
|
71
|
+
readonly budgetMs?: number;
|
|
72
|
+
}
|
|
73
|
+
interface DeliverResult {
|
|
74
|
+
readonly sent: number;
|
|
75
|
+
readonly skipped: number;
|
|
76
|
+
/** Deliveries that threw; retried later unless they ran out of attempts. */
|
|
77
|
+
readonly failed: number;
|
|
78
|
+
}
|
|
79
|
+
interface Rendered<K extends string = string, D = unknown> extends NotificationItem<K, D> {
|
|
80
|
+
/** What `render` returned. Absent without a `render` option. */
|
|
81
|
+
readonly text?: RenderedText;
|
|
82
|
+
}
|
|
83
|
+
interface NotificationsOptions<K extends NotificationTypes> {
|
|
84
|
+
/** `sqlTransport(postgres.asUser(claims))`, or over `postgres.admin` for the service. */
|
|
85
|
+
readonly transport: KitTransport;
|
|
86
|
+
/** Each type's `data` schema: `send` validates against it. */
|
|
87
|
+
readonly types: K;
|
|
88
|
+
/** `kits.notifications.schema`. Defaults to `better_supabase`. */
|
|
89
|
+
readonly schema?: string;
|
|
90
|
+
/** Turns a notification into text at read time, in the reader's locale. */
|
|
91
|
+
readonly render?: (item: NotificationItem, context: {
|
|
92
|
+
readonly locale?: string;
|
|
93
|
+
}) => RenderedText;
|
|
94
|
+
/** Types that wait for an action; `counts().actionable` counts them. */
|
|
95
|
+
readonly actionable?: readonly TypeName<K>[];
|
|
96
|
+
/** Senders for the non-`in_app` channels, used by `deliver()`. */
|
|
97
|
+
readonly channels?: readonly NotificationChannel[];
|
|
98
|
+
/** Runs after a notification is stored, e.g. to call Next's `updateTag`. */
|
|
99
|
+
readonly onSent?: (sent: {
|
|
100
|
+
readonly id: string;
|
|
101
|
+
readonly type: string;
|
|
102
|
+
readonly tenant: string | null;
|
|
103
|
+
readonly recipients: readonly string[];
|
|
104
|
+
}) => void | Promise<void>;
|
|
105
|
+
/** `betterSupabase.events`, for `notification.*` kit events. */
|
|
106
|
+
readonly events?: EventHub;
|
|
107
|
+
readonly actorId?: string;
|
|
108
|
+
readonly context?: RequestContext;
|
|
109
|
+
readonly errorMappers?: readonly ErrorMapper[];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The `notifications` SQL kit module as typed calls. `send` validates the
|
|
113
|
+
* data against its type's schema; the database checks the send permission,
|
|
114
|
+
* leaves out the actor and non-members and applies subscriptions and
|
|
115
|
+
* preferences.
|
|
116
|
+
*/
|
|
117
|
+
interface Notifications<K extends NotificationTypes> {
|
|
118
|
+
/** The notification id, or `null` when nobody was left to notify. */
|
|
119
|
+
send<N extends TypeName<K>>(type: N, input: SendInput<StandardSchemaV1.InferInput<K[N]>>): AsyncResult<string | null>;
|
|
120
|
+
list(options?: ListOptions<TypeName<K>>): AsyncResult<readonly Rendered<TypeName<K>>[]>;
|
|
121
|
+
counts(options?: {
|
|
122
|
+
readonly tenant?: string;
|
|
123
|
+
}): AsyncResult<NotificationCounts>;
|
|
124
|
+
/** Marks the given notifications, or all of them, read. */
|
|
125
|
+
markRead(options?: {
|
|
126
|
+
readonly ids?: readonly string[];
|
|
127
|
+
readonly tenant?: string;
|
|
128
|
+
}): AsyncResult<number>;
|
|
129
|
+
dismiss(ids: readonly string[]): AsyncResult<number>;
|
|
130
|
+
/** Resolves every recipient's notification of a type about a subject. */
|
|
131
|
+
resolve(input: {
|
|
132
|
+
readonly type: TypeName<K>;
|
|
133
|
+
readonly subject: NotificationSubject;
|
|
134
|
+
readonly tenant?: string;
|
|
135
|
+
}): AsyncResult<number>;
|
|
136
|
+
/** Sets the user's level for a subject; `null` removes it. */
|
|
137
|
+
subscribe(input: {
|
|
138
|
+
readonly subject: NotificationSubject;
|
|
139
|
+
readonly level: SubscriptionLevel | null;
|
|
140
|
+
readonly tenant?: string;
|
|
141
|
+
/** Another member; only the service can set it. */
|
|
142
|
+
readonly userId?: string;
|
|
143
|
+
}): AsyncResult<void>;
|
|
144
|
+
/** Turns a type (`*` for all) on or off on a channel; `null` removes it. */
|
|
145
|
+
setPreference(input: {
|
|
146
|
+
readonly type: TypeName<K> | "*";
|
|
147
|
+
readonly channel: string;
|
|
148
|
+
readonly enabled: boolean | null;
|
|
149
|
+
/** One organization, or everywhere when omitted. */
|
|
150
|
+
readonly tenant?: string;
|
|
151
|
+
}): AsyncResult<void>;
|
|
152
|
+
/** Sends pending deliveries through `channels`. Run it from a cron or a job. */
|
|
153
|
+
deliver(options?: DeliverOptions): Promise<DeliverResult>;
|
|
154
|
+
/**
|
|
155
|
+
* Deletes up to `batch` (10,000) notifications older than `olderThan`
|
|
156
|
+
* (`90 days`) with their recipients and deliveries. Service only.
|
|
157
|
+
*/
|
|
158
|
+
purge(olderThan?: string, batch?: number): AsyncResult<number>;
|
|
159
|
+
}
|
|
160
|
+
export declare function createNotifications<const K extends NotificationTypes>(options: NotificationsOptions<K>): Notifications<K>;
|
|
161
|
+
//#endregion
|
|
162
|
+
export { type DeliverOptions, type DeliverResult, type KitTransport, type ListOptions, type NotificationChannel, type NotificationCounts, type NotificationItem, type NotificationMessage, type NotificationSendResult, type NotificationSubject, type NotificationTypes, type Notifications, type NotificationsOptions, type Rendered, type RenderedText, type RpcClient, type SendInput, type SubscriptionLevel, rpcTransport, sqlTransport };
|