better-supabase 0.4.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 +2 -0
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/cli/bin.js +1 -1
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/{config-D9R0o1LH.js → config-CPVpXP5I.js} +78 -8
- package/dist/cli/{config-SSKSTUl2.js → config-dtoekM0v.js} +1 -1
- package/dist/cli/{read-sets-CMa29t9a.js → diff-SjkO5rNr.js} +1 -220
- package/dist/cli/{dist-CFgGknmZ.js → dist-DBPdlPYK.js} +4 -4
- package/dist/cli/{dist-B1MvLv-M.js → dist-DreZ24Sz.js} +1 -1
- package/dist/cli/{dist-Du7VnEUG.js → dist-xn4wlboc.js} +112 -1
- package/dist/cli/{doctor-BOym3mbL.js → doctor-L7Z8mYTx.js} +531 -50
- package/dist/cli/{env-BZ0uy8XK.js → env-DmKv_G4A.js} +1 -1
- package/dist/cli/gen-BJc9vdyu.js +2 -0
- package/dist/cli/{gen-Ccn_p90L.js → gen-C2osRcr5.js} +283 -87
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/{init-ClnxRYBF.js → init-Saf5-R9T.js} +2 -2
- package/dist/cli/{introspect-DxqfkVIv.js → introspect--H7oGmcd.js} +2 -2
- package/dist/cli/{openapi-8WHjxiTJ.js → openapi-Cp5Fyta-.js} +1 -1
- package/dist/cli/read-sets-BfMQcKy8.js +289 -0
- package/dist/cli/{run-CP20LKk_.js → run-BKR2W_79.js} +16 -12
- package/dist/cli/{seed-5J6eu_Xg.js → seed-CUlqWh-8.js} +1 -1
- package/dist/cli/sql-DzPbfngx.js +348 -0
- package/dist/cli/{version-5mW_8sY1.js → version-BDJuO0UR.js} +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +3 -10
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-DB3aSFOA.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +4 -4
- package/dist/{config-CbvFmGD8.js → config-DreERlKo.js} +1 -1
- package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
- package/dist/{define-CRS2MRV3.d.ts → define-Bp_BZa5h.d.ts} +11 -7
- package/dist/{define-Bh0AqTik.js → define-cNKzqwL_.js} +202 -16
- package/dist/edge/index.d.ts +16 -5
- package/dist/edge/index.js +10 -7
- package/dist/{entitlements-Dm-FHpNH.d.ts → entitlements-DUxU4oHe.d.ts} +1 -1
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-L0xK41y_.js → errors-DtwXa6b8.js} +1 -0
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +65 -12
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/{result-CqEsbgYs.d.ts → executor-BunPOO-k.d.ts} +200 -1
- package/dist/{executor-CZ7ImRiD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-CVt6dD0B.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +17 -6
- package/dist/hono/index.js +40 -5
- package/dist/impersonation-VfaGc4Q_.js +41 -0
- package/dist/{index-BBdvo-5d.d.ts → index-BGdgBPdf.d.ts} +49 -6
- package/dist/{index-BhHEC04r.d.ts → index-C7f8cOBM.d.ts} +2 -2
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-D5IfUIDh.d.ts → index-DOESKG27.d.ts} +33 -8
- package/dist/{index-DFmOpBGC.d.ts → index-DgILJRXj.d.ts} +35 -9
- package/dist/{index-RlLXd8dL.d.ts → index-DmXSxbLi.d.ts} +4 -4
- package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
- package/dist/{index-528Ugmyp.d.ts → index-SaTsEfGP.d.ts} +76 -8
- package/dist/{index-BAy4tsAP.d.ts → index-fag8VJZ2.d.ts} +3 -4
- package/dist/index.d.ts +30 -13
- package/dist/index.js +10 -9
- package/dist/jobs/index.d.ts +147 -101
- package/dist/jobs/index.js +744 -237
- package/dist/{json-schema-DP6V7cbr.js → json-schema-m1fiwKgN.js} +22 -1
- 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-BpxDfnbg.js → kits-CGLLrNPd.js} +37 -9
- package/dist/list/index.d.ts +1 -1
- package/dist/{live-BHakSWWo.d.ts → live-D9W2M5aO.d.ts} +6 -5
- package/dist/mcp/index.d.ts +25 -8
- package/dist/mcp/index.js +23 -14
- package/dist/{mfa-C7MNQ1Ob.d.ts → mfa-CusDgPaD.d.ts} +5 -1
- package/dist/next/index.d.ts +31 -10
- package/dist/next/index.js +136 -27
- 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 +2 -2
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +28 -8
- package/dist/orpc/index.js +16 -3
- package/dist/otel/index.d.ts +21 -2
- package/dist/otel/index.js +50 -9
- 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-bNhvJwRh.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 +15 -10
- 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 +2 -2
- package/dist/plugins/tenant/index.js +38 -20
- 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-DFwEQwmN.d.ts → pool-C9voM4Kw.d.ts} +17 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -11
- package/dist/{postgrest-Bc-0Xw62.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-BihjPR0t.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-U8UEKccv.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-BMMSWbI7.js → query-CCbo44lG.js} +2 -2
- 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 +14 -3
- package/dist/react/server.js +10 -2
- package/dist/react/session.d.ts +7 -1
- package/dist/react/session.js +9 -1
- package/dist/{react-CQHMaESR.js → react-C_s7kiNz.js} +103 -11
- package/dist/{read-set-FWK8qQ58.js → read-set-DQFt1N_Y.js} +22 -1
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +5 -4
- package/dist/{refresh-BwYkyNOQ.d.ts → refresh-BTrQ_ZTp.d.ts} +1 -1
- package/dist/{resolve-BAqQcwMW.d.ts → resolve-C15TJq77.d.ts} +11 -5
- package/dist/{resource-My_y-Syh.js → resource-CRxxwkQq.js} +22 -7
- package/dist/{resource-BJcaIfxi.d.ts → resource-D8iqWMr-.d.ts} +5 -5
- package/dist/respond-D2OQl62-.d.ts +371 -0
- package/dist/{result-Bj6yt33D.js → result-BKiliPY7.js} +1 -1
- package/dist/server/index.d.ts +14 -12
- package/dist/server/index.js +11 -17
- package/dist/{server-BA75B0mT.js → server-Csx2sRjg.js} +251 -34
- package/dist/{shared-DnoCdMDr.js → shared-D1dRUrAg.js} +16 -2
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-Cqvfr9dj.js → simplify-_0oUKqCd.js} +4 -115
- package/dist/sql/index.d.ts +247 -10
- package/dist/sql/index.js +8590 -779
- package/dist/{sql-B34HU2DI.js → sql-Coh6n4cC.js} +9 -2
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-BRyBGTtp.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-BsaCYQJc.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-C_YxA2r1.js → stats-Bqob3WqT.js} +4 -4
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +155 -26
- 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/{permdock-sql-C4KFzLc9.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CBNHT6ni.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +100 -10
- package/dist/testing/index.js +336 -8
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-Bh_Jf_-l.js → verify-Ce49ETPz.js} +5 -5
- package/dist/{version-Bmf7_FzM.js → version-DM_Roffb.js} +1 -1
- package/dist/{view-B64o81wp.d.ts → view-BEf0diC5.d.ts} +8 -3
- package/dist/{mfa-byfjruR9.js → view-Bq64ki8-.js} +95 -38
- package/dist/webhooks/index.d.ts +135 -4
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +16 -8
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +10 -0
- package/skills/better-supabase/SKILL.md +9 -6
- package/skills/better-supabase/references/plugins.md +6 -3
- package/skills/better-supabase/references/troubleshooting.md +1 -0
- package/skills/better-supabase-api/SKILL.md +9 -2
- package/skills/better-supabase-api/references/adapters.md +11 -8
- package/skills/better-supabase-auth/SKILL.md +3 -0
- package/skills/better-supabase-auth/references/permdock.md +56 -11
- package/dist/cli/gen-D5cLCULx.js +0 -2
- package/dist/cli/sql-D4VbqmXi.js +0 -199
- package/dist/events-B21UOhvP.d.ts +0 -84
- package/dist/executor-B5IoUxLE.d.ts +0 -195
- package/dist/invalidate-CgxmRamI.js +0 -24
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-DgIbdrt8.d.ts +0 -194
- package/dist/view-BrJI2LKp.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";
|
|
@@ -145,6 +146,10 @@ function serverTimingValue(metrics) {
|
|
|
145
146
|
//#region src/next/create.ts
|
|
146
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`.
|
|
@@ -256,6 +261,16 @@ function createNext(betterSupabase, 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()`. */
|
|
261
276
|
const incoming = cache(async () => {
|
|
@@ -267,14 +282,27 @@ function createNext(betterSupabase, options = {}) {
|
|
|
267
282
|
});
|
|
268
283
|
const current = cache(async () => {
|
|
269
284
|
const { request, resolution } = await incoming();
|
|
270
|
-
|
|
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
|
+
});
|
|
271
291
|
});
|
|
272
292
|
const context = (request, contextOptions) => request ? base.context(request, contextOptions) : current();
|
|
273
|
-
|
|
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
|
+
};
|
|
274
302
|
const contextForSession = async (view, { token }) => {
|
|
275
303
|
const request = new Request("http://next.local/", { headers: token ? { authorization: `Bearer ${token}` } : {} });
|
|
276
304
|
const ctx = await base.context(request);
|
|
277
|
-
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;
|
|
278
306
|
return base.contextFor({
|
|
279
307
|
kind: "invalid",
|
|
280
308
|
reason: "token",
|
|
@@ -300,7 +328,11 @@ function createNext(betterSupabase, options = {}) {
|
|
|
300
328
|
...life?.revalidate === void 0 ? {} : { revalidate: life.revalidate },
|
|
301
329
|
...life?.expire === void 0 ? {} : { expire: life.expire }
|
|
302
330
|
});
|
|
303
|
-
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
|
+
];
|
|
304
336
|
if (tags.length > 0) cacheTag(...tags);
|
|
305
337
|
return withExtra(ctx, { session: view });
|
|
306
338
|
},
|
|
@@ -357,13 +389,21 @@ function createNext(betterSupabase, options = {}) {
|
|
|
357
389
|
expose
|
|
358
390
|
});
|
|
359
391
|
const params = await segment.params;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
} 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();
|
|
367
407
|
if (collector) try {
|
|
368
408
|
response.headers.set(statsHeader, formatStats(ctx.stats()));
|
|
369
409
|
} catch {}
|
|
@@ -404,12 +444,8 @@ function createNext(betterSupabase, options = {}) {
|
|
|
404
444
|
parsed = checked.data;
|
|
405
445
|
}
|
|
406
446
|
const settled = await settle(() => fn(parsed, ctx));
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
maxAge: Math.max(1, Math.ceil(pinMs / 1e3)),
|
|
410
|
-
httpOnly: true,
|
|
411
|
-
sameSite: "lax"
|
|
412
|
-
});
|
|
447
|
+
flushAfter();
|
|
448
|
+
if (ctx.replica?.wrote) (await cookies()).set(PRIMARY_COOKIE, String(Date.now() + pinMs), primaryCookieOptions(pinMs));
|
|
413
449
|
return settled.ok ? {
|
|
414
450
|
ok: true,
|
|
415
451
|
data: settled.data,
|
|
@@ -421,6 +457,59 @@ function createNext(betterSupabase, options = {}) {
|
|
|
421
457
|
};
|
|
422
458
|
};
|
|
423
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
|
+
},
|
|
424
513
|
cacheTag(table, id) {
|
|
425
514
|
cacheTag(tagFor(table), ...id === void 0 ? [] : [tagFor(table, id)]);
|
|
426
515
|
},
|
|
@@ -436,5 +525,25 @@ function createNext(betterSupabase, options = {}) {
|
|
|
436
525
|
}
|
|
437
526
|
});
|
|
438
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
|
+
}
|
|
439
548
|
//#endregion
|
|
440
|
-
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 };
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { c as mapDbError, n as dbError } from "../errors-DtwXa6b8.js";
|
|
2
|
+
import { t as temporal } from "../temporal-required-CAt4Dc9Z.js";
|
|
3
|
+
import { i as ok, n as err, o as toDbError, t as AsyncResult } from "../result-BKiliPY7.js";
|
|
4
|
+
import { t as validate } from "../standard-D7r6dMUu.js";
|
|
5
|
+
import { n as emitKitEvent } from "../kit-events-CTFDfSwb.js";
|
|
6
|
+
import { n as rpcTransport, r as sqlTransport, t as rawError } from "../kit-transport-DJlHQUBU.js";
|
|
7
|
+
//#region src/notifications/notifications.ts
|
|
8
|
+
const DEFAULT_SCHEMA = "better_supabase";
|
|
9
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
const textOf = (value) => value === null || value === void 0 ? null : String(value);
|
|
11
|
+
const instantOf = (value) => typeof value === "string" ? temporal().Instant.from(value) : null;
|
|
12
|
+
function toItem(row) {
|
|
13
|
+
const type = textOf(row["subject_type"]);
|
|
14
|
+
const id = textOf(row["subject_id"]);
|
|
15
|
+
const label = textOf(row["subject_label"]);
|
|
16
|
+
return {
|
|
17
|
+
id: String(row["id"]),
|
|
18
|
+
eventId: String(row["event_id"]),
|
|
19
|
+
type: String(row["type"]),
|
|
20
|
+
data: row["data"] ?? {},
|
|
21
|
+
tenant: textOf(row["tenant"]),
|
|
22
|
+
actorId: textOf(row["actor_id"]),
|
|
23
|
+
subject: type === null || id === null ? null : {
|
|
24
|
+
type,
|
|
25
|
+
id,
|
|
26
|
+
...label === null ? {} : { label }
|
|
27
|
+
},
|
|
28
|
+
summary: textOf(row["summary"]),
|
|
29
|
+
actionPath: textOf(row["action_path"]),
|
|
30
|
+
priority: textOf(row["priority"]),
|
|
31
|
+
createdAt: instantOf(row["created_at"]) ?? temporal().Now.instant(),
|
|
32
|
+
readAt: instantOf(row["read_at"]),
|
|
33
|
+
resolvedAt: instantOf(row["resolved_at"])
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function cursorOf(before) {
|
|
37
|
+
if (before === void 0) return {
|
|
38
|
+
before: null,
|
|
39
|
+
before_id: null
|
|
40
|
+
};
|
|
41
|
+
if ("epochNanoseconds" in before) return {
|
|
42
|
+
before: before.toString(),
|
|
43
|
+
before_id: null
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
before: before.createdAt.toString(),
|
|
47
|
+
before_id: before.id
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const errorText = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
51
|
+
function createNotifications(options) {
|
|
52
|
+
const { transport } = options;
|
|
53
|
+
const schema = options.schema ?? DEFAULT_SCHEMA;
|
|
54
|
+
const mappers = options.errorMappers ?? [];
|
|
55
|
+
const call = async (fn, args) => transport.call(schema, fn, args);
|
|
56
|
+
function run(fn, args, then) {
|
|
57
|
+
return AsyncResult.from(async () => {
|
|
58
|
+
let value;
|
|
59
|
+
try {
|
|
60
|
+
value = await call(fn, args);
|
|
61
|
+
} catch (cause) {
|
|
62
|
+
const raw = rawError(cause);
|
|
63
|
+
return err(raw ? mapDbError(raw, mappers) : toDbError(cause));
|
|
64
|
+
}
|
|
65
|
+
return ok(await then(value));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function emit(type, data, tenant) {
|
|
69
|
+
if (!options.events) return;
|
|
70
|
+
emitKitEvent(options.events, type, data, {
|
|
71
|
+
subject: `notifications/${data.notificationId}`,
|
|
72
|
+
...tenant ? { tenant } : {},
|
|
73
|
+
...options.actorId ? { actorId: options.actorId } : {},
|
|
74
|
+
...options.context ? { context: options.context } : {}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const render = (item, locale) => {
|
|
78
|
+
if (!options.render) return item;
|
|
79
|
+
return {
|
|
80
|
+
...item,
|
|
81
|
+
text: options.render(item, locale === void 0 ? {} : { locale })
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
async function deliverOne(channel, row, maxAttempts) {
|
|
85
|
+
const notification = isRecord(row["notification"]) ? toItem(row["notification"]) : void 0;
|
|
86
|
+
if (!notification) throw new TypeError("claim returned no notification");
|
|
87
|
+
const attempts = Number(row["attempts"] ?? 1);
|
|
88
|
+
const message = {
|
|
89
|
+
deliveryId: String(row["delivery_id"]),
|
|
90
|
+
channel: channel.name,
|
|
91
|
+
attempts,
|
|
92
|
+
userId: String(row["user_id"]),
|
|
93
|
+
email: textOf(row["email"]),
|
|
94
|
+
notification,
|
|
95
|
+
...options.render ? { text: options.render(notification, {}) } : {}
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
const result = await channel.send(message);
|
|
99
|
+
const status = result?.status ?? "sent";
|
|
100
|
+
await call("complete_notification_delivery", {
|
|
101
|
+
delivery: message.deliveryId,
|
|
102
|
+
status,
|
|
103
|
+
provider: result?.provider ?? null,
|
|
104
|
+
provider_message_id: result?.providerMessageId ?? null
|
|
105
|
+
});
|
|
106
|
+
if (status === "sent") emit("notification.delivered", {
|
|
107
|
+
notificationId: notification.eventId,
|
|
108
|
+
type: notification.type,
|
|
109
|
+
channel: channel.name
|
|
110
|
+
}, notification.tenant);
|
|
111
|
+
return status;
|
|
112
|
+
} catch (cause) {
|
|
113
|
+
const error = errorText(cause);
|
|
114
|
+
await call("complete_notification_delivery", {
|
|
115
|
+
delivery: message.deliveryId,
|
|
116
|
+
status: "pending",
|
|
117
|
+
error,
|
|
118
|
+
max_attempts: maxAttempts
|
|
119
|
+
});
|
|
120
|
+
emit("notification.failed", {
|
|
121
|
+
notificationId: notification.eventId,
|
|
122
|
+
type: notification.type,
|
|
123
|
+
channel: channel.name,
|
|
124
|
+
error
|
|
125
|
+
}, notification.tenant);
|
|
126
|
+
return "failed";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
send(type, input) {
|
|
131
|
+
return AsyncResult.from(async () => {
|
|
132
|
+
const schemaOf = Object.hasOwn(options.types, type) ? options.types[type] : void 0;
|
|
133
|
+
if (schemaOf === void 0) return err(dbError("validation", `Unknown notification type "${type}"`, {
|
|
134
|
+
issues: [{
|
|
135
|
+
path: ["type"],
|
|
136
|
+
message: `Unknown type "${type}"`
|
|
137
|
+
}],
|
|
138
|
+
hint: "NOTIFICATION_TYPE_UNKNOWN"
|
|
139
|
+
}));
|
|
140
|
+
const data = await validate(schemaOf, input.data, `${type} data`);
|
|
141
|
+
if (!data.ok) return err(data.error);
|
|
142
|
+
const sent = await run("notify", { notification: {
|
|
143
|
+
type,
|
|
144
|
+
data: data.data,
|
|
145
|
+
recipients: input.recipients ?? [],
|
|
146
|
+
tenant: input.tenant,
|
|
147
|
+
subject_type: input.subject?.type,
|
|
148
|
+
subject_id: input.subject?.id,
|
|
149
|
+
subject_label: input.subject?.label,
|
|
150
|
+
summary: input.summary,
|
|
151
|
+
action_path: input.actionPath,
|
|
152
|
+
priority: input.priority,
|
|
153
|
+
key: input.key,
|
|
154
|
+
channels: input.channels,
|
|
155
|
+
include_actor: input.includeActor,
|
|
156
|
+
activity: input.activity,
|
|
157
|
+
resolved: input.resolved,
|
|
158
|
+
actor: input.actorId
|
|
159
|
+
} }, (value) => value === null ? null : String(value));
|
|
160
|
+
if (!sent.ok || sent.data === null) return sent;
|
|
161
|
+
const recipients = input.recipients ?? [];
|
|
162
|
+
emit("notification.created", {
|
|
163
|
+
notificationId: sent.data,
|
|
164
|
+
type,
|
|
165
|
+
recipientIds: recipients
|
|
166
|
+
}, input.tenant ?? null);
|
|
167
|
+
if (options.onSent) try {
|
|
168
|
+
await options.onSent({
|
|
169
|
+
id: sent.data,
|
|
170
|
+
type,
|
|
171
|
+
tenant: input.tenant ?? null,
|
|
172
|
+
recipients
|
|
173
|
+
});
|
|
174
|
+
} catch (cause) {
|
|
175
|
+
return err(toDbError(cause));
|
|
176
|
+
}
|
|
177
|
+
return sent;
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
list(listOptions = {}) {
|
|
181
|
+
return run("list_notifications", {
|
|
182
|
+
tenant: listOptions.tenant ?? null,
|
|
183
|
+
status: listOptions.status ?? "all",
|
|
184
|
+
types: listOptions.types ?? null,
|
|
185
|
+
...cursorOf(listOptions.before),
|
|
186
|
+
max_items: listOptions.limit ?? 50
|
|
187
|
+
}, (value) => (Array.isArray(value) ? value : []).filter(isRecord).map((row) => render(toItem(row), listOptions.locale)));
|
|
188
|
+
},
|
|
189
|
+
counts(countOptions = {}) {
|
|
190
|
+
return run("notification_counts", {
|
|
191
|
+
tenant: countOptions.tenant ?? null,
|
|
192
|
+
actionable: options.actionable ?? []
|
|
193
|
+
}, (value) => ({
|
|
194
|
+
unread: Number(isRecord(value) ? value["unread"] ?? 0 : 0),
|
|
195
|
+
actionable: Number(isRecord(value) ? value["actionable"] ?? 0 : 0)
|
|
196
|
+
}));
|
|
197
|
+
},
|
|
198
|
+
markRead(readOptions = {}) {
|
|
199
|
+
return run("mark_notifications_read", {
|
|
200
|
+
ids: readOptions.ids ?? null,
|
|
201
|
+
tenant: readOptions.tenant ?? null
|
|
202
|
+
}, Number);
|
|
203
|
+
},
|
|
204
|
+
dismiss(ids) {
|
|
205
|
+
return run("dismiss_notifications", { ids }, Number);
|
|
206
|
+
},
|
|
207
|
+
resolve(input) {
|
|
208
|
+
return run("resolve_notifications", {
|
|
209
|
+
type: input.type,
|
|
210
|
+
subject_type: input.subject.type,
|
|
211
|
+
subject_id: input.subject.id,
|
|
212
|
+
tenant: input.tenant ?? null
|
|
213
|
+
}, Number);
|
|
214
|
+
},
|
|
215
|
+
subscribe(input) {
|
|
216
|
+
return run("set_notification_subscription", {
|
|
217
|
+
subject_type: input.subject.type,
|
|
218
|
+
subject_id: input.subject.id,
|
|
219
|
+
level: input.level,
|
|
220
|
+
tenant: input.tenant ?? null,
|
|
221
|
+
member: input.userId ?? null
|
|
222
|
+
}, () => void 0);
|
|
223
|
+
},
|
|
224
|
+
setPreference(input) {
|
|
225
|
+
return run("set_notification_preference", {
|
|
226
|
+
type: input.type,
|
|
227
|
+
channel: input.channel,
|
|
228
|
+
enabled: input.enabled,
|
|
229
|
+
tenant: input.tenant ?? null
|
|
230
|
+
}, () => void 0);
|
|
231
|
+
},
|
|
232
|
+
async deliver(deliverOptions = {}) {
|
|
233
|
+
const wanted = deliverOptions.channels;
|
|
234
|
+
const channels = (options.channels ?? []).filter((channel) => !wanted || wanted.includes(channel.name));
|
|
235
|
+
const deadline = deliverOptions.budgetMs === void 0 ? Number.POSITIVE_INFINITY : Date.now() + deliverOptions.budgetMs;
|
|
236
|
+
const batch = deliverOptions.batch ?? 50;
|
|
237
|
+
const counts = {
|
|
238
|
+
sent: 0,
|
|
239
|
+
skipped: 0,
|
|
240
|
+
failed: 0
|
|
241
|
+
};
|
|
242
|
+
for (const channel of channels) while (Date.now() < deadline) {
|
|
243
|
+
const claimed = await call("claim_notification_deliveries", {
|
|
244
|
+
channel: channel.name,
|
|
245
|
+
max_items: batch,
|
|
246
|
+
lease: deliverOptions.lease ?? "5 minutes",
|
|
247
|
+
max_attempts: deliverOptions.maxAttempts ?? 5
|
|
248
|
+
});
|
|
249
|
+
const rows = (Array.isArray(claimed) ? claimed : []).filter(isRecord);
|
|
250
|
+
for (const row of rows) counts[await deliverOne(channel, row, deliverOptions.maxAttempts ?? 5)] += 1;
|
|
251
|
+
if (rows.length < batch) break;
|
|
252
|
+
}
|
|
253
|
+
return counts;
|
|
254
|
+
},
|
|
255
|
+
purge(olderThan, batch) {
|
|
256
|
+
return run("purge_notifications", {
|
|
257
|
+
older_than: olderThan ?? null,
|
|
258
|
+
batch: batch ?? null
|
|
259
|
+
}, Number);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
export { createNotifications, rpcTransport, sqlTransport };
|
package/dist/openapi/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as ResourceOptions, i as ResourceOperation, n as OpenApiOptions, o as createOpenApi, r as PROBLEM_SCHEMA, t as OpenApiDocument } from "../index-
|
|
1
|
+
import { a as ResourceOptions, i as ResourceOperation, n as OpenApiOptions, o as createOpenApi, r as PROBLEM_SCHEMA, t as OpenApiDocument } from "../index-C7f8cOBM.js";
|
|
2
2
|
export { OpenApiDocument, OpenApiOptions, PROBLEM_SCHEMA, ResourceOperation, ResourceOptions, createOpenApi };
|
package/dist/openapi/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-
|
|
1
|
+
import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-BW1KhzSG.js";
|
|
2
2
|
import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
|
|
3
|
-
import { t as buildJsonSchema } from "../json-schema-
|
|
3
|
+
import { t as buildJsonSchema } from "../json-schema-m1fiwKgN.js";
|
|
4
4
|
//#region src/openapi/index.ts
|
|
5
5
|
/** JSON Schema for RFC 9457 Problem Details as better-supabase sends them. */
|
|
6
6
|
const PROBLEM_SCHEMA = {
|