better-supabase 0.3.0 → 0.4.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 +7 -6
- package/bin/better-supabase.js +4 -0
- package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
- 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/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-D9R0o1LH.js +1895 -0
- package/dist/cli/config-SSKSTUl2.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/dist-B1MvLv-M.js +129 -0
- package/dist/cli/dist-CFgGknmZ.js +1112 -0
- package/dist/cli/dist-Du7VnEUG.js +621 -0
- package/dist/cli/doctor-BOym3mbL.js +2382 -0
- package/dist/cli/env-BZ0uy8XK.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-Ccn_p90L.js +2008 -0
- package/dist/cli/gen-D5cLCULx.js +2 -0
- package/dist/cli/index.d.ts +493 -0
- package/dist/cli/index.js +184 -0
- package/dist/cli/init-ClnxRYBF.js +550 -0
- package/dist/cli/introspect-DxqfkVIv.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-8WHjxiTJ.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-CMa29t9a.js +971 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-CP20LKk_.js +347 -0
- package/dist/cli/seed-5J6eu_Xg.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-D4VbqmXi.js +199 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/cli/version-5mW_8sY1.js +4 -0
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +13 -13
- package/dist/{compiler-2zqGzxVZ.d.ts → compiler-DB3aSFOA.d.ts} +2 -2
- package/dist/config/index.js +4 -4
- package/dist/{config-Be4GwsqI.js → config-BpxDfnbg.js} +1 -1
- package/dist/{config-DOsGYSiT.js → config-CbvFmGD8.js} +1 -1
- package/dist/{define-DF341GDj.js → define-Bh0AqTik.js} +160 -79
- package/dist/{define-DUbydmJa.d.ts → define-CRS2MRV3.d.ts} +32 -28
- package/dist/edge/index.d.ts +8 -12
- package/dist/edge/index.js +50 -24
- package/dist/{entitlements-DvJXoI02.d.ts → entitlements-Dm-FHpNH.d.ts} +2 -2
- package/dist/{errors-Bl3rkOzo.js → errors-L0xK41y_.js} +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +4 -4
- package/dist/{events-Do8jXh4D.d.ts → events-B21UOhvP.d.ts} +4 -4
- package/dist/{executor-DynmIWhq.d.ts → executor-B5IoUxLE.d.ts} +1 -1
- package/dist/{executor-C57jx2pn.js → executor-CVt6dD0B.js} +3 -3
- package/dist/{executor-B26lgbHD.d.ts → executor-CZ7ImRiD.d.ts} +2 -2
- package/dist/hono/index.d.ts +16 -23
- package/dist/hono/index.js +10 -10
- package/dist/{index-CTTMS6_1.d.ts → index-528Ugmyp.d.ts} +12 -3
- package/dist/{index-CADlNHK-.d.ts → index-BAy4tsAP.d.ts} +5 -5
- package/dist/{index-BNsABq7P.d.ts → index-BBdvo-5d.d.ts} +23 -25
- package/dist/{index-qriRM9cZ.d.ts → index-BhHEC04r.d.ts} +3 -3
- package/dist/{index-GV77NpRP.d.ts → index-D5IfUIDh.d.ts} +3 -3
- package/dist/{index-CriFFJ_p.d.ts → index-DFmOpBGC.d.ts} +11 -11
- package/dist/{index-UJ-LUxtI.d.ts → index-RlLXd8dL.d.ts} +12 -12
- package/dist/index.d.ts +15 -15
- package/dist/index.js +13 -13
- package/dist/{invalidate-DbxuL2jL.js → invalidate-CgxmRamI.js} +7 -1
- package/dist/jobs/index.d.ts +10 -4
- package/dist/jobs/index.js +32 -16
- package/dist/{json-schema--aOXAGfU.js → json-schema-DP6V7cbr.js} +1 -1
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.js +3 -3
- package/dist/{live-QE3UPbGm.d.ts → live-BHakSWWo.d.ts} +12 -12
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +18 -18
- package/dist/mcp/index.js +23 -18
- package/dist/{mfa-x3V3vGh-.d.ts → mfa-C7MNQ1Ob.d.ts} +1 -1
- package/dist/{mfa-BxoR1RkN.js → mfa-byfjruR9.js} +118 -29
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +38 -35
- package/dist/next/index.js +41 -33
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +6 -6
- package/dist/orpc/index.d.ts +17 -20
- package/dist/orpc/index.js +11 -11
- package/dist/otel/index.d.ts +5 -5
- package/dist/otel/index.js +81 -62
- package/dist/{permdock-sql-Bta_G0v-.js → permdock-sql-C4KFzLc9.js} +1 -1
- package/dist/{plugin-DVRB8qNE.d.ts → plugin-bNhvJwRh.d.ts} +2 -2
- package/dist/plugins/actor/index.d.ts +1 -1
- package/dist/plugins/actor/index.js +1 -1
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +42 -12
- package/dist/plugins/soft-delete/index.d.ts +2 -2
- package/dist/plugins/soft-delete/index.js +2 -2
- package/dist/plugins/tenant/index.d.ts +2 -2
- package/dist/plugins/tenant/index.js +10 -4
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +1 -1
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +3 -3
- package/dist/{pool-B9qsLyFR.d.ts → pool-DFwEQwmN.d.ts} +11 -4
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +3 -2
- package/dist/{postgrest-DmBlevh-.d.ts → postgrest-Bc-0Xw62.d.ts} +1 -1
- package/dist/{problem-CGt1ADix.js → problem-BihjPR0t.js} +1 -1
- package/dist/{problem-Z2RTQjZD.d.ts → problem-U8UEKccv.d.ts} +1 -1
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +2 -2
- package/dist/{query-BdKLTPlj.js → query-BMMSWbI7.js} +18 -18
- package/dist/react/index.d.ts +6 -6
- package/dist/react/index.js +1 -1
- package/dist/react/server.d.ts +5 -5
- package/dist/react/server.js +3 -3
- package/dist/react/session.d.ts +4 -4
- package/dist/react/session.js +2 -2
- package/dist/{react-DNBYNazu.js → react-CQHMaESR.js} +39 -39
- package/dist/{read-set-D0qBcboM.js → read-set-FWK8qQ58.js} +50 -15
- package/dist/realtime/index.d.ts +1 -1
- package/dist/realtime/index.js +13 -12
- package/dist/{index-kpgopLNv.d.ts → refresh-BwYkyNOQ.d.ts} +6 -3
- package/dist/{resolve-BRciX2jK.d.ts → resolve-BAqQcwMW.d.ts} +15 -10
- package/dist/{resource-CwVgiI5q.d.ts → resource-BJcaIfxi.d.ts} +5 -5
- package/dist/{resource-D30rp0GN.js → resource-My_y-Syh.js} +6 -6
- package/dist/{respond-DAkPIdTH.d.ts → respond-DgIbdrt8.d.ts} +31 -12
- package/dist/{result-DWXatkd6.js → result-Bj6yt33D.js} +2 -2
- package/dist/{result-c2OYgCV2.d.ts → result-CqEsbgYs.d.ts} +3 -3
- package/dist/server/index.d.ts +14 -14
- package/dist/server/index.js +12 -12
- package/dist/{respond-C44EZAJo.js → server-BA75B0mT.js} +139 -100
- package/dist/{shared-_6-uPL7E.js → shared-DnoCdMDr.js} +4 -3
- package/dist/{simplify-LakqK1vH.js → simplify-Cqvfr9dj.js} +67 -24
- package/dist/sql/index.d.ts +1 -1
- package/dist/sql/index.js +7 -7
- package/dist/{sql-BIgk12ew.js → sql-B34HU2DI.js} +3 -2
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +2 -2
- package/dist/{standard-FUi3Z3-7.js → standard-BRyBGTtp.js} +2 -2
- package/dist/{standard-eudBiRk1.d.ts → standard-BsaCYQJc.d.ts} +1 -1
- package/dist/{stats-D78UQ8Yu.js → stats-C_YxA2r1.js} +33 -8
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +26 -8
- package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
- package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CBNHT6ni.js} +1 -1
- package/dist/testing/index.d.ts +20 -21
- package/dist/testing/index.js +45 -45
- package/dist/{version-BfpG2gks.js → version-Bmf7_FzM.js} +1 -1
- package/dist/{view-3Po_PVlr.d.ts → view-B64o81wp.d.ts} +5 -5
- package/dist/{view-wZ-9qIUz.js → view-BrJI2LKp.js} +1 -1
- package/dist/webhooks/index.d.ts +3 -3
- package/dist/webhooks/index.js +1 -1
- package/dist/{webhooks-CAIFQSTM.js → webhooks-Bh_Jf_-l.js} +37 -18
- package/package.json +36 -4
- package/skills/better-supabase/SKILL.md +57 -10
- package/skills/better-supabase/references/plugins.md +7 -11
- package/skills/better-supabase/references/troubleshooting.md +24 -4
- package/skills/better-supabase-api/SKILL.md +66 -18
- package/skills/better-supabase-api/references/adapters.md +44 -26
- package/skills/better-supabase-auth/SKILL.md +146 -0
- package/skills/better-supabase-auth/references/permdock.md +55 -0
- package/skills/better-supabase-testing/SKILL.md +58 -4
- package/dist/claims-CoTobNC-.js +0 -25
package/dist/realtime/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { n as dbError } from "../errors-
|
|
2
|
-
import { i as ok, n as err, t as AsyncResult } from "../result-
|
|
3
|
-
import { i as tenantClaimPaths } from "../claims-
|
|
4
|
-
import { n as liveQuery, r as liveTopic, t as liveCount } from "../live-
|
|
5
|
-
import { s as toApp } from "../shared-
|
|
6
|
-
import { a as sqlIdent, i as slug, o as sqlString, r as parseTemplate, t as permdockCheck } from "../permdock-sql-
|
|
1
|
+
import { n as dbError } from "../errors-L0xK41y_.js";
|
|
2
|
+
import { i as ok, n as err, t as AsyncResult } from "../result-Bj6yt33D.js";
|
|
3
|
+
import { i as tenantClaimPaths } from "../claims-BAf6c7is.js";
|
|
4
|
+
import { n as liveQuery, r as liveTopic, t as liveCount } from "../live-CdJBOc4f.js";
|
|
5
|
+
import { s as toApp } from "../shared-DnoCdMDr.js";
|
|
6
|
+
import { a as sqlIdent, i as slug, o as sqlString, r as parseTemplate, t as permdockCheck } from "../permdock-sql-C4KFzLc9.js";
|
|
7
7
|
import { REALTIME_SUBSCRIBE_STATES } from "@supabase/supabase-js";
|
|
8
8
|
//#region src/realtime/topic.ts
|
|
9
9
|
const VALUE = /^[\w.@+=-]+$/;
|
|
@@ -119,8 +119,8 @@ function defineTopic(template, options = {}) {
|
|
|
119
119
|
}
|
|
120
120
|
return `${lines.join("\n")}\n`;
|
|
121
121
|
},
|
|
122
|
-
triggerSql(
|
|
123
|
-
const meta =
|
|
122
|
+
triggerSql(betterSupabase, table, trigger) {
|
|
123
|
+
const meta = betterSupabase.meta.tables[table];
|
|
124
124
|
if (!meta) throw new TypeError(`defineTopic: unknown table "${table}"`);
|
|
125
125
|
const values = trigger.values;
|
|
126
126
|
const parts = template.split(/(\{[^}]+\})/).filter(Boolean).map((part) => {
|
|
@@ -236,13 +236,14 @@ function defineTopic(template, options = {}) {
|
|
|
236
236
|
const topic = topicOf(values);
|
|
237
237
|
const checked = await validate(schemas[event], payload);
|
|
238
238
|
if (!checked.ok) return err(dbError("validation", `Invalid payload for ${event}`, { issues: checked.issues }));
|
|
239
|
-
|
|
239
|
+
const open = client.getChannels().some((existing) => existing.topic === `realtime:${topic}`);
|
|
240
|
+
if (isPrivate && !open) await client.realtime.setAuth();
|
|
240
241
|
const channel = client.channel(topic, { config: { private: isPrivate } });
|
|
241
242
|
try {
|
|
242
243
|
const response = await channel.httpSend(event, checked.value);
|
|
243
244
|
return response.success ? ok(void 0) : err(sendError(response.status, response.error));
|
|
244
245
|
} finally {
|
|
245
|
-
await client.removeChannel(channel);
|
|
246
|
+
if (!open) await client.removeChannel(channel);
|
|
246
247
|
}
|
|
247
248
|
});
|
|
248
249
|
}
|
|
@@ -252,8 +253,8 @@ function defineTopic(template, options = {}) {
|
|
|
252
253
|
* Reads a `realtime.broadcast_changes` message as an app-cased row change.
|
|
253
254
|
* Returns `null` for messages about other tables.
|
|
254
255
|
*/
|
|
255
|
-
function rowChange(
|
|
256
|
-
const meta =
|
|
256
|
+
function rowChange(betterSupabase, table, message) {
|
|
257
|
+
const meta = betterSupabase.meta.tables[table];
|
|
257
258
|
const payload = message.payload;
|
|
258
259
|
if (!meta || !payload || payload.table !== meta.name || payload.schema && payload.schema !== meta.schema) return null;
|
|
259
260
|
const operation = payload.operation ?? message.event;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { m as StoredSession } from "./resolve-
|
|
2
|
-
import "./view-3Po_PVlr.js";
|
|
3
|
-
import "./entitlements-DvJXoI02.js";
|
|
1
|
+
import { m as StoredSession } from "./resolve-BAqQcwMW.js";
|
|
4
2
|
//#region src/auth/refresh.d.ts
|
|
5
3
|
interface RefreshOptions {
|
|
6
4
|
readonly url: string;
|
|
@@ -14,6 +12,11 @@ interface RefreshOptions {
|
|
|
14
12
|
readonly secretKey: string;
|
|
15
13
|
};
|
|
16
14
|
readonly fetch?: typeof fetch;
|
|
15
|
+
/**
|
|
16
|
+
* Milliseconds before a refresh counts as a network failure (the session is
|
|
17
|
+
* kept). 5000 by default.
|
|
18
|
+
*/
|
|
19
|
+
readonly timeoutMs?: number;
|
|
17
20
|
/** Epoch milliseconds, the unit JWT `exp` and `iat` math needs. */
|
|
18
21
|
readonly now?: () => number;
|
|
19
22
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { p as DbError } from "./result-
|
|
2
|
-
import { f as Logger, l as RefreshEvent } from "./events-
|
|
3
|
-
import { h as RequestContext } from "./plugin-
|
|
4
|
-
import { t as StandardSchemaV1 } from "./standard-
|
|
1
|
+
import { p as DbError } from "./result-CqEsbgYs.js";
|
|
2
|
+
import { f as Logger, l as RefreshEvent } from "./events-B21UOhvP.js";
|
|
3
|
+
import { h as RequestContext } from "./plugin-bNhvJwRh.js";
|
|
4
|
+
import { t as StandardSchemaV1 } from "./standard-BsaCYQJc.js";
|
|
5
5
|
import { t as BetterSupabaseEnv } from "./index-Dy6kuF0P.js";
|
|
6
6
|
import { CookieOptions } from "@supabase/ssr";
|
|
7
7
|
import { JWTClaims, SupabaseEnv, UserClaims } from "@supabase/server";
|
|
@@ -46,9 +46,9 @@ declare function applyCookieWrites(cookies: readonly CookieRecord[], writes: rea
|
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/auth/resolve.d.ts
|
|
48
48
|
/**
|
|
49
|
-
* Who is calling. `C` is the claims type from `
|
|
49
|
+
* Who is calling. `C` is the claims type from `betterSupabase.claims(schema)`; the
|
|
50
50
|
* verified payload keeps every JWT claim and adds the schema's output. `P`
|
|
51
|
-
* is the `user_metadata` type from `
|
|
51
|
+
* is the `user_metadata` type from `betterSupabase.userMetadata(schema)`.
|
|
52
52
|
*/
|
|
53
53
|
type AuthState<C = unknown, P = unknown> = {
|
|
54
54
|
readonly kind: "user";
|
|
@@ -59,7 +59,7 @@ type AuthState<C = unknown, P = unknown> = {
|
|
|
59
59
|
/** Seconds since epoch, from the token's `exp`. */
|
|
60
60
|
readonly expiresAt: number | null;
|
|
61
61
|
/**
|
|
62
|
-
* `user_metadata` parsed by `
|
|
62
|
+
* `user_metadata` parsed by `betterSupabase.userMetadata(schema)`; `undefined`
|
|
63
63
|
* without a schema or when the metadata fails it. Users can write this
|
|
64
64
|
* data themselves: use it for display, never for access.
|
|
65
65
|
*/
|
|
@@ -101,8 +101,13 @@ interface ResolveAuthOptions {
|
|
|
101
101
|
readonly env: BetterSupabaseEnv;
|
|
102
102
|
/** Allow refreshing an expiring cookie session. Only where cookies can be written (proxy). */
|
|
103
103
|
readonly refresh?: boolean;
|
|
104
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* With `refresh`, refresh when the token expires within this many seconds.
|
|
106
|
+
* Defaults to 60. Without `refresh` a token stays valid until its `exp`.
|
|
107
|
+
*/
|
|
105
108
|
readonly leeway?: number;
|
|
109
|
+
/** Milliseconds before a refresh counts as a network failure. Defaults to 5000. */
|
|
110
|
+
readonly refreshTimeoutMs?: number;
|
|
106
111
|
/**
|
|
107
112
|
* Accept `sb_secret_` keys (`apikey` header) as service callers: `true` for
|
|
108
113
|
* any configured key, or the key names allowed (`['cron']`). Defaults to false.
|
|
@@ -117,12 +122,12 @@ interface ResolveAuthOptions {
|
|
|
117
122
|
/** Inline JWKS instead of fetching `env.jwksUrl` (tests, air-gapped). */
|
|
118
123
|
readonly jwks?: SupabaseEnv["jwks"];
|
|
119
124
|
/**
|
|
120
|
-
* Validates the verified claims (`
|
|
125
|
+
* Validates the verified claims (`betterSupabase.claims(schema)` sets it). A failure
|
|
121
126
|
* resolves to `{ kind: 'invalid', reason: 'claims' }`.
|
|
122
127
|
*/
|
|
123
128
|
readonly claims?: StandardSchemaV1;
|
|
124
129
|
/**
|
|
125
|
-
* Parses `user_metadata` into `profile` (`
|
|
130
|
+
* Parses `user_metadata` into `profile` (`betterSupabase.userMetadata(schema)` sets
|
|
126
131
|
* it). A failure leaves `profile` undefined and warns once; the session
|
|
127
132
|
* stays valid.
|
|
128
133
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { N as TableKey, P as TableMeta, R as Update, g as Insert, n as AnyModels, t as AnyFunctions } from "./types-DONv9SHN.js";
|
|
2
|
-
import { b as ValidationIssue, o as Result } from "./result-
|
|
3
|
-
import { t as BetterSupabase, xt as SelectArg } from "./define-
|
|
4
|
-
import {
|
|
5
|
-
import { i as ResourceOperation } from "./index-
|
|
2
|
+
import { b as ValidationIssue, o as Result } from "./result-CqEsbgYs.js";
|
|
3
|
+
import { t as BetterSupabase, xt as SelectArg } from "./define-CRS2MRV3.js";
|
|
4
|
+
import { i as RespondOptions } from "./respond-DgIbdrt8.js";
|
|
5
|
+
import { i as ResourceOperation } from "./index-BhHEC04r.js";
|
|
6
6
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
7
7
|
//#region src/server/resource.d.ts
|
|
8
8
|
/** What a resource needs from `defineListQuery`. */
|
|
@@ -69,6 +69,6 @@ interface ResourceHandler {
|
|
|
69
69
|
* `createOpenApi`: `GET/POST /<table>` and `GET/PATCH/DELETE /<table>/{id}`.
|
|
70
70
|
* Shared by the HTTP adapters and the MCP table tools.
|
|
71
71
|
*/
|
|
72
|
-
declare function defineResource<M extends AnyModels, D, F extends AnyFunctions, E, T extends TableKey<M>>(
|
|
72
|
+
declare function defineResource<M extends AnyModels, D, F extends AnyFunctions, E, T extends TableKey<M>>(betterSupabase: BetterSupabase<M, D, F, E>, table: T, options?: ResourceRouteOptions<M, T>): ResourceHandler;
|
|
73
73
|
//#endregion
|
|
74
74
|
export { ResourceRouteOptions as a, ResourcePagination as i, ResourceInput as n, defineResource as o, ResourceList as r, ResourceHandler as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as dbError } from "./errors-
|
|
2
|
-
import { i as ok, n as err } from "./result-
|
|
3
|
-
import { t as validate } from "./standard-
|
|
4
|
-
import {
|
|
1
|
+
import { n as dbError } from "./errors-L0xK41y_.js";
|
|
2
|
+
import { i as ok, n as err } from "./result-Bj6yt33D.js";
|
|
3
|
+
import { t as validate } from "./standard-BRyBGTtp.js";
|
|
4
|
+
import { s as respond } from "./server-BA75B0mT.js";
|
|
5
5
|
//#region src/server/resource.ts
|
|
6
6
|
const INTEGER = /* @__PURE__ */ new Set([
|
|
7
7
|
"int2",
|
|
@@ -82,8 +82,8 @@ function notAllowed(allowed) {
|
|
|
82
82
|
* `createOpenApi`: `GET/POST /<table>` and `GET/PATCH/DELETE /<table>/{id}`.
|
|
83
83
|
* Shared by the HTTP adapters and the MCP table tools.
|
|
84
84
|
*/
|
|
85
|
-
function defineResource(
|
|
86
|
-
const meta =
|
|
85
|
+
function defineResource(betterSupabase, table, options = {}) {
|
|
86
|
+
const meta = betterSupabase.meta.tables[table];
|
|
87
87
|
if (!meta) throw new TypeError(`Unknown table "${table}"`);
|
|
88
88
|
if (options.list && options.list.table !== table) throw new TypeError(`The list query is for "${options.list.table}", not "${table}"`);
|
|
89
89
|
const operations = options.operations ?? (meta.kind === "view" ? ["list", "get"] : [
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { n as AnyModels, t as AnyFunctions } from "./types-DONv9SHN.js";
|
|
2
|
-
import { p as DbError, t as AsyncResult } from "./result-
|
|
3
|
-
import { I as StatsRecorder, P as DbStats, t as BetterSupabase, y as Db } from "./define-
|
|
4
|
-
import { h as RequestContext } from "./plugin-
|
|
2
|
+
import { p as DbError, t as AsyncResult } from "./result-CqEsbgYs.js";
|
|
3
|
+
import { I as StatsRecorder, P as DbStats, t as BetterSupabase, y as Db } from "./define-CRS2MRV3.js";
|
|
4
|
+
import { h as RequestContext } from "./plugin-bNhvJwRh.js";
|
|
5
5
|
import { t as BetterSupabaseEnv } from "./index-Dy6kuF0P.js";
|
|
6
|
-
import { o as SqlClaims,
|
|
7
|
-
import { o as ImpersonationOptions, t as Aal } from "./mfa-
|
|
8
|
-
import { a as ResolveAuthOptions, r as AuthState, t as AuthResolution } from "./resolve-
|
|
9
|
-
import { n as Bucket } from "./index-
|
|
6
|
+
import { o as SqlClaims, t as BetterPostgres } from "./pool-DFwEQwmN.js";
|
|
7
|
+
import { o as ImpersonationOptions, t as Aal } from "./mfa-C7MNQ1Ob.js";
|
|
8
|
+
import { a as ResolveAuthOptions, r as AuthState, t as AuthResolution } from "./resolve-BAqQcwMW.js";
|
|
9
|
+
import { n as Bucket } from "./index-528Ugmyp.js";
|
|
10
10
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
11
11
|
//#region src/server/delete-account.d.ts
|
|
12
12
|
interface DeleteAccountOptions {
|
|
@@ -40,7 +40,7 @@ interface ReplicaState {
|
|
|
40
40
|
readonly pinned: boolean;
|
|
41
41
|
/** This context wrote through its repositories (or called `pin()`). */
|
|
42
42
|
readonly wrote: boolean;
|
|
43
|
-
/** Sends later reads to the primary, e.g. after a write through `$client` or
|
|
43
|
+
/** Sends later reads to the primary, e.g. after a write through `$client` or `ctx.sql`. */
|
|
44
44
|
pin(): void;
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
@@ -49,8 +49,14 @@ interface ServerOptions {
|
|
|
49
49
|
/** Defaults to `loadEnv()`. */
|
|
50
50
|
readonly env?: BetterSupabaseEnv;
|
|
51
51
|
/** Enables `ctx.sql` and `actingAs()`. */
|
|
52
|
-
readonly postgres?:
|
|
52
|
+
readonly postgres?: BetterPostgres;
|
|
53
53
|
readonly auth?: Omit<ResolveAuthOptions, "env">;
|
|
54
|
+
/**
|
|
55
|
+
* Fetch the JWKS when the server is created, so the first request verifies
|
|
56
|
+
* its token without waiting for it. Applies when `env.jwksUrl` is set and
|
|
57
|
+
* `auth.jwks` is not. Defaults to true, except under `NODE_ENV=test`.
|
|
58
|
+
*/
|
|
59
|
+
readonly prefetchJwks?: boolean;
|
|
54
60
|
/**
|
|
55
61
|
* Extra headers on the caller's PostgREST requests, readable in Postgres as
|
|
56
62
|
* `current_setting('request.headers')` (channel, request id, client IP for audit).
|
|
@@ -98,6 +104,11 @@ interface BetterServer<M extends AnyModels, F extends AnyFunctions, E, C = unkno
|
|
|
98
104
|
* cookie session only with `refresh: true`; send `ctx.resolution.apply(response)`.
|
|
99
105
|
*/
|
|
100
106
|
context(request: Request, options?: ContextOptions): Promise<ServerContext<M, F, E, C, P>>;
|
|
107
|
+
/**
|
|
108
|
+
* The context for a resolution from `resolve(request)`, so one resolution
|
|
109
|
+
* can back several contexts without verifying the token again.
|
|
110
|
+
*/
|
|
111
|
+
contextFromResolution(resolution: AuthResolution<C, P>, request: Request, options?: Omit<ContextOptions, "refresh">): ServerContext<M, F, E, C, P>;
|
|
101
112
|
/** The context for an auth state resolved elsewhere (the proxy, a queue message). */
|
|
102
113
|
contextFor(auth: AuthState<C, P>, options?: ContextOptions): ServerContext<M, F, E, C, P>;
|
|
103
114
|
/** A stateless supabase-js client for an auth state, optionally with extra request headers. */
|
|
@@ -121,9 +132,9 @@ interface BetterServer<M extends AnyModels, F extends AnyFunctions, E, C = unkno
|
|
|
121
132
|
}
|
|
122
133
|
/**
|
|
123
134
|
* Server-side entry point for APIs, jobs, MCP servers and framework
|
|
124
|
-
* adapters. Holds the secrets; `
|
|
135
|
+
* adapters. Holds the secrets; `betterSupabase` stays isomorphic.
|
|
125
136
|
*/
|
|
126
|
-
declare function createServer<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(
|
|
137
|
+
declare function createServer<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(betterSupabase: BetterSupabase<M, D, F, E, C, P>, options?: ServerOptions): BetterServer<M, F, E, C, P>;
|
|
127
138
|
//#endregion
|
|
128
139
|
//#region src/server/respond.d.ts
|
|
129
140
|
type AuthKind = AuthState["kind"];
|
|
@@ -142,6 +153,14 @@ interface GuardOptions {
|
|
|
142
153
|
*/
|
|
143
154
|
readonly scopes?: readonly string[];
|
|
144
155
|
}
|
|
156
|
+
/** Guard options for middleware and fetch handlers that resolve the caller themselves. */
|
|
157
|
+
interface MiddlewareOptions extends GuardOptions {
|
|
158
|
+
/**
|
|
159
|
+
* Refresh an expired cookie session and send the new cookies. Only for
|
|
160
|
+
* routes browsers call with cookies; bearer tokens never refresh.
|
|
161
|
+
*/
|
|
162
|
+
readonly refresh?: boolean;
|
|
163
|
+
}
|
|
145
164
|
/** `undefined` when `auth` may pass; otherwise the 401/403 error to send. */
|
|
146
165
|
declare function guard(auth: AuthState, allow?: GuardOptions["allow"], aal?: Aal, scopes?: readonly string[]): DbError | undefined;
|
|
147
166
|
declare function isResult(value: unknown): value is {
|
|
@@ -172,4 +191,4 @@ interface RespondOptions {
|
|
|
172
191
|
/** A handler's value as a response: `Response` as is, data as JSON, errors as Problem Details. */
|
|
173
192
|
declare function respond(run: () => unknown, options?: RespondOptions): Promise<Response>;
|
|
174
193
|
//#endregion
|
|
175
|
-
export {
|
|
194
|
+
export { DeleteAccountOptions as _, Settled as a, respond as c, ContextOptions as d, ServerContext as f, ReplicaState as g, PRIMARY_COOKIE as h, RespondOptions as i, settle as l, createServer as m, GuardOptions as n, guard as o, ServerOptions as p, MiddlewareOptions as r, isResult as s, AuthKind as t, BetterServer as u, DeleteAccountResult as v };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as dbError, r as dbErrorOf, t as DbException } from "./errors-
|
|
1
|
+
import { n as dbError, r as dbErrorOf, t as DbException } from "./errors-L0xK41y_.js";
|
|
2
2
|
//#region src/core/result.ts
|
|
3
3
|
const throwMappers = /* @__PURE__ */ new WeakMap();
|
|
4
4
|
function inherit(from, next) {
|
|
@@ -49,7 +49,7 @@ var AsyncResult = class AsyncResult {
|
|
|
49
49
|
/**
|
|
50
50
|
* Resolves with the data, or rejects with a `DbException`. Pass `factory`
|
|
51
51
|
* to throw your own error instead, e.g. an HTTP error for your framework;
|
|
52
|
-
* without it, the mapper from `
|
|
52
|
+
* without it, the mapper from `betterSupabase.mapError()` applies when one is set.
|
|
53
53
|
*/
|
|
54
54
|
async orThrow(factory) {
|
|
55
55
|
const result = await this.#promise;
|
|
@@ -124,7 +124,7 @@ type ErrorMapper = (raw: RawDbError, fallback: DbError) => DbError | undefined;
|
|
|
124
124
|
declare function mapDbError(raw: RawDbError, mappers?: readonly ErrorMapper[]): DbError;
|
|
125
125
|
//#endregion
|
|
126
126
|
//#region src/core/result.d.ts
|
|
127
|
-
/** Turns a `DbError` into the error `.orThrow()` throws; set with `
|
|
127
|
+
/** Turns a `DbError` into the error `.orThrow()` throws; set with `betterSupabase.mapError()`. */
|
|
128
128
|
type ThrowMapper = (error: DbError) => unknown;
|
|
129
129
|
type Ok<T> = {
|
|
130
130
|
readonly ok: true;
|
|
@@ -153,7 +153,7 @@ declare class AsyncResult<T> implements PromiseLike<Result<T>> {
|
|
|
153
153
|
/**
|
|
154
154
|
* Resolves with the data, or rejects with a `DbException`. Pass `factory`
|
|
155
155
|
* to throw your own error instead, e.g. an HTTP error for your framework;
|
|
156
|
-
* without it, the mapper from `
|
|
156
|
+
* without it, the mapper from `betterSupabase.mapError()` applies when one is set.
|
|
157
157
|
*/
|
|
158
158
|
orThrow(factory?: (error: DbError) => unknown): Promise<T>;
|
|
159
159
|
/** Resolves with the data, or `fallback` when the result is an error. */
|
|
@@ -178,7 +178,7 @@ interface BetterResultApi<T, E> {
|
|
|
178
178
|
/**
|
|
179
179
|
* Builds a better-result value with the `Result` namespace you pass, so
|
|
180
180
|
* better-result stays your dependency. Errors go through `mapError`, or, for a
|
|
181
|
-
* result from a `db` of `
|
|
181
|
+
* result from a `db` of `betterSupabase.mapError(fn)`, through `fn` (typed `unknown`).
|
|
182
182
|
* The value is a real `Ok`/`Err`: cast it (`as Result<T, E>`) for its
|
|
183
183
|
* methods in types; the cast itself is not checked against `T`.
|
|
184
184
|
*
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
|
|
2
|
-
import { p as DbError } from "../result-
|
|
3
|
-
import { t as BetterSupabase, y as Db } from "../define-
|
|
4
|
-
import { h as RequestContext } from "../plugin-
|
|
5
|
-
import { a as checkAal, c as impersonatorOf, i as amrOf, n as AmrEntry, o as ImpersonationOptions, r as aalOf, s as Impersonator, t as Aal } from "../mfa-
|
|
6
|
-
import { _ as readSession, a as ResolveAuthOptions, b as writeSession, c as clientIp, d as CookieOptions, f as CookieRecord, i as InvalidReason, l as resolveAuth, m as StoredSession, n as AuthResolver, o as ResolvedState, p as CookieWrite, r as AuthState, s as authContext, t as AuthResolution, u as AUTH_CACHE_HEADERS, y as sessionCookieName } from "../resolve-
|
|
7
|
-
import { _ as
|
|
8
|
-
import { a as ResourceRouteOptions, i as ResourcePagination, n as ResourceInput, o as defineResource, r as ResourceList, t as ResourceHandler } from "../resource-
|
|
9
|
-
import { a as ProblemResponseOptions, c as problemResponse, i as ProblemOptions, l as toProblem, n as PROBLEM_TYPE_BASE, o as fromProblem, r as ProblemDetails, s as isProblem, t as PROBLEM_CONTENT_TYPE } from "../problem-
|
|
10
|
-
import { a as SessionDelegation, i as SessionActor, n as toSession, r as ActClaim, t as AuthSession } from "../view-
|
|
11
|
-
import { n as RefreshOutcome, r as refreshSession } from "../
|
|
2
|
+
import { p as DbError } from "../result-CqEsbgYs.js";
|
|
3
|
+
import { t as BetterSupabase, y as Db } from "../define-CRS2MRV3.js";
|
|
4
|
+
import { h as RequestContext } from "../plugin-bNhvJwRh.js";
|
|
5
|
+
import { a as checkAal, c as impersonatorOf, i as amrOf, n as AmrEntry, o as ImpersonationOptions, r as aalOf, s as Impersonator, t as Aal } from "../mfa-C7MNQ1Ob.js";
|
|
6
|
+
import { _ as readSession, a as ResolveAuthOptions, b as writeSession, c as clientIp, d as CookieOptions, f as CookieRecord, i as InvalidReason, l as resolveAuth, m as StoredSession, n as AuthResolver, o as ResolvedState, p as CookieWrite, r as AuthState, s as authContext, t as AuthResolution, u as AUTH_CACHE_HEADERS, y as sessionCookieName } from "../resolve-BAqQcwMW.js";
|
|
7
|
+
import { _ as DeleteAccountOptions, a as Settled, c as respond, d as ContextOptions, f as ServerContext, g as ReplicaState, h as PRIMARY_COOKIE, i as RespondOptions, l as settle, m as createServer, n as GuardOptions, o as guard, p as ServerOptions, r as MiddlewareOptions, s as isResult, t as AuthKind, u as BetterServer, v as DeleteAccountResult } from "../respond-DgIbdrt8.js";
|
|
8
|
+
import { a as ResourceRouteOptions, i as ResourcePagination, n as ResourceInput, o as defineResource, r as ResourceList, t as ResourceHandler } from "../resource-BJcaIfxi.js";
|
|
9
|
+
import { a as ProblemResponseOptions, c as problemResponse, i as ProblemOptions, l as toProblem, n as PROBLEM_TYPE_BASE, o as fromProblem, r as ProblemDetails, s as isProblem, t as PROBLEM_CONTENT_TYPE } from "../problem-U8UEKccv.js";
|
|
10
|
+
import { a as SessionDelegation, i as SessionActor, n as toSession, r as ActClaim, t as AuthSession } from "../view-B64o81wp.js";
|
|
11
|
+
import { n as RefreshOutcome, r as refreshSession } from "../refresh-BwYkyNOQ.js";
|
|
12
12
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
13
13
|
import { Middleware } from "@supabase/middleware";
|
|
14
14
|
import { AuthMode, JWTClaims, UserClaims } from "@supabase/server";
|
|
@@ -27,17 +27,17 @@ export declare function contextFromSupabase(ctx: SupabaseAuthContext): RequestCo
|
|
|
27
27
|
* `ctx.supabase` (the caller-scoped client from `withSupabase`).
|
|
28
28
|
*
|
|
29
29
|
* ```ts
|
|
30
|
-
* pipeline([withSupabase({ auth: 'user' }), withBetterSupabase(
|
|
30
|
+
* pipeline([withSupabase({ auth: 'user' }), withBetterSupabase(betterSupabase)()], (req, ctx) => ...)
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
|
-
export declare function withBetterSupabase<M extends AnyModels, D, F extends AnyFunctions, E>(
|
|
33
|
+
export declare function withBetterSupabase<M extends AnyModels, D, F extends AnyFunctions, E>(betterSupabase: BetterSupabase<M, D, F, E>): Middleware<"db", void, SupabaseAuthContext & {
|
|
34
34
|
readonly supabase: SupabaseClient;
|
|
35
35
|
}, Db<M, F, E, SupabaseClient>>;
|
|
36
36
|
/**
|
|
37
37
|
* Entry contributing `ctx.sql`: repositories over `ctx.postgres` from
|
|
38
38
|
* `withPostgresClient`, which runs each query as the caller with RLS.
|
|
39
39
|
*/
|
|
40
|
-
export declare function withBetterPostgres<M extends AnyModels, D, F extends AnyFunctions, E>(
|
|
40
|
+
export declare function withBetterPostgres<M extends AnyModels, D, F extends AnyFunctions, E>(betterSupabase: BetterSupabase<M, D, F, E>): Middleware<"sql", void, SupabaseAuthContext & {
|
|
41
41
|
readonly postgres: PostgresApi;
|
|
42
42
|
}, Db<M, F, E, undefined>>;
|
|
43
43
|
//#endregion
|
|
@@ -64,4 +64,4 @@ export declare function checkSession(sql: SessionLookup, auth: {
|
|
|
64
64
|
readonly kind: string;
|
|
65
65
|
}): Promise<DbError | undefined>;
|
|
66
66
|
//#endregion
|
|
67
|
-
export { AUTH_CACHE_HEADERS, type Aal, type ActClaim, type AmrEntry, type AuthKind, type AuthResolution, type AuthResolver, type AuthSession, type AuthState, type BetterServer, type ContextOptions, type CookieOptions, type CookieRecord, type CookieWrite, type DeleteAccountOptions, type DeleteAccountResult, type GuardOptions, type ImpersonationOptions, type Impersonator, type InvalidReason, PRIMARY_COOKIE, PROBLEM_CONTENT_TYPE, PROBLEM_TYPE_BASE, type ProblemDetails, type ProblemOptions, type ProblemResponseOptions, type RefreshOutcome, type ReplicaState, type ResolveAuthOptions, type ResolvedState, type ResourceHandler, type ResourceInput, type ResourceList, type ResourcePagination, type ResourceRouteOptions, type RespondOptions, type ServerContext, type ServerOptions, type SessionActor, type SessionDelegation, type SessionLookup, type Settled, type StoredSession, type SupabaseAuthContext, aalOf, amrOf, authContext, checkAal, clientIp, createServer, defineResource, fromProblem, guard, impersonatorOf, isProblem, isResult, problemResponse, readSession, refreshSession, resolveAuth, respond, sessionCookieName, settle, toProblem, toSession, writeSession };
|
|
67
|
+
export { AUTH_CACHE_HEADERS, type Aal, type ActClaim, type AmrEntry, type AuthKind, type AuthResolution, type AuthResolver, type AuthSession, type AuthState, type BetterServer, type ContextOptions, type CookieOptions, type CookieRecord, type CookieWrite, type DeleteAccountOptions, type DeleteAccountResult, type GuardOptions, type ImpersonationOptions, type Impersonator, type InvalidReason, type MiddlewareOptions, PRIMARY_COOKIE, PROBLEM_CONTENT_TYPE, PROBLEM_TYPE_BASE, type ProblemDetails, type ProblemOptions, type ProblemResponseOptions, type RefreshOutcome, type ReplicaState, type ResolveAuthOptions, type ResolvedState, type ResourceHandler, type ResourceInput, type ResourceList, type ResourcePagination, type ResourceRouteOptions, type RespondOptions, type ServerContext, type ServerOptions, type SessionActor, type SessionDelegation, type SessionLookup, type Settled, type StoredSession, type SupabaseAuthContext, aalOf, amrOf, authContext, checkAal, clientIp, createServer, defineResource, fromProblem, guard, impersonatorOf, isProblem, isResult, problemResponse, readSession, refreshSession, resolveAuth, respond, sessionCookieName, settle, toProblem, toSession, writeSession };
|
package/dist/server/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { n as dbError } from "../errors-
|
|
2
|
-
import { a as problemResponse, i as isProblem, n as PROBLEM_TYPE_BASE, o as toProblem, r as fromProblem, t as PROBLEM_CONTENT_TYPE } from "../problem-
|
|
3
|
-
import { a as
|
|
4
|
-
import { a as clientIp,
|
|
5
|
-
import { n as postgresExecutor } from "../executor-
|
|
6
|
-
import { t as toSession } from "../view-
|
|
7
|
-
import { t as defineResource } from "../resource-
|
|
1
|
+
import { n as dbError } from "../errors-L0xK41y_.js";
|
|
2
|
+
import { a as problemResponse, i as isProblem, n as PROBLEM_TYPE_BASE, o as toProblem, r as fromProblem, t as PROBLEM_CONTENT_TYPE } from "../problem-BihjPR0t.js";
|
|
3
|
+
import { a as guard, c as settle, o as isResult, s as respond, t as createServer, u as PRIMARY_COOKIE } from "../server-BA75B0mT.js";
|
|
4
|
+
import { a as clientIp, c as AUTH_CACHE_HEADERS, d as readSession, h as refreshSession, i as authContext, m as writeSession, n as amrOf, p as sessionCookieName, r as checkAal, s as resolveAuth, t as aalOf, y as impersonatorOf } from "../mfa-byfjruR9.js";
|
|
5
|
+
import { n as postgresExecutor } from "../executor-CVt6dD0B.js";
|
|
6
|
+
import { t as toSession } from "../view-BrJI2LKp.js";
|
|
7
|
+
import { t as defineResource } from "../resource-My_y-Syh.js";
|
|
8
8
|
import { defineMiddleware } from "@supabase/middleware";
|
|
9
9
|
//#region src/server/middleware.ts
|
|
10
10
|
/** Repository context from a `@supabase/server` context. */
|
|
@@ -50,23 +50,23 @@ function contextFromSupabase(ctx) {
|
|
|
50
50
|
* `ctx.supabase` (the caller-scoped client from `withSupabase`).
|
|
51
51
|
*
|
|
52
52
|
* ```ts
|
|
53
|
-
* pipeline([withSupabase({ auth: 'user' }), withBetterSupabase(
|
|
53
|
+
* pipeline([withSupabase({ auth: 'user' }), withBetterSupabase(betterSupabase)()], (req, ctx) => ...)
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
|
-
function withBetterSupabase(
|
|
56
|
+
function withBetterSupabase(betterSupabase) {
|
|
57
57
|
return defineMiddleware({
|
|
58
58
|
key: "db",
|
|
59
|
-
run: (_config) => (_req, ctx) => Promise.resolve({ db:
|
|
59
|
+
run: (_config) => (_req, ctx) => Promise.resolve({ db: betterSupabase.connect(ctx.supabase, contextFromSupabase(ctx)) })
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Entry contributing `ctx.sql`: repositories over `ctx.postgres` from
|
|
64
64
|
* `withPostgresClient`, which runs each query as the caller with RLS.
|
|
65
65
|
*/
|
|
66
|
-
function withBetterPostgres(
|
|
66
|
+
function withBetterPostgres(betterSupabase) {
|
|
67
67
|
return defineMiddleware({
|
|
68
68
|
key: "sql",
|
|
69
|
-
run: (_config) => (_req, ctx) => Promise.resolve({ sql:
|
|
69
|
+
run: (_config) => (_req, ctx) => Promise.resolve({ sql: betterSupabase.connect(postgresExecutor(ctx.postgres), contextFromSupabase(ctx)) })
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
//#endregion
|