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
|
@@ -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 = {
|
|
@@ -173,15 +173,15 @@ function pageSchema(row, cursor) {
|
|
|
173
173
|
* schemes and Problem Details errors.
|
|
174
174
|
*
|
|
175
175
|
* ```ts
|
|
176
|
-
* const doc = createOpenApi(
|
|
176
|
+
* const doc = createOpenApi(betterSupabase, {
|
|
177
177
|
* info: { title: 'CRM API', version: '1.0.0' },
|
|
178
178
|
* resources: { customers: { list: customerList }, notes: { operations: ['list', 'get'] } },
|
|
179
179
|
* });
|
|
180
180
|
* ```
|
|
181
181
|
*/
|
|
182
|
-
function createOpenApi(
|
|
182
|
+
function createOpenApi(betterSupabase, options) {
|
|
183
183
|
const defs = buildJsonSchema({
|
|
184
|
-
meta:
|
|
184
|
+
meta: betterSupabase.meta,
|
|
185
185
|
config: { json: options.json ?? {} }
|
|
186
186
|
})["$defs"];
|
|
187
187
|
const schemas = { Problem: PROBLEM_SCHEMA };
|
|
@@ -190,7 +190,7 @@ function createOpenApi(sb, options) {
|
|
|
190
190
|
const base = (options.basePath ?? "").replace(/\/$/, "");
|
|
191
191
|
const security = options.security ?? ["bearer"];
|
|
192
192
|
for (const [key, raw] of Object.entries(options.resources)) {
|
|
193
|
-
const table =
|
|
193
|
+
const table = betterSupabase.meta.tables[key];
|
|
194
194
|
if (!table) throw new TypeError(`createOpenApi: unknown table "${key}"`);
|
|
195
195
|
const resource = raw === true ? {} : raw;
|
|
196
196
|
const operations = resource.operations ?? (table.kind === "view" ? ["list", "get"] : [
|
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
//#region src/orgs/orgs.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Organization columns by database name: `name`, `slug` and the columns in
|
|
9
|
+
* `kits.organizations.options.attributes`. Other keys are ignored.
|
|
10
|
+
*/
|
|
11
|
+
interface OrgAttributes {
|
|
12
|
+
readonly name?: string;
|
|
13
|
+
readonly slug?: string;
|
|
14
|
+
readonly [column: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
interface CreateOrgOptions {
|
|
17
|
+
/** The owner, when the transport runs as the service role. */
|
|
18
|
+
readonly ownerId?: string;
|
|
19
|
+
}
|
|
20
|
+
type SlugProblem = "invalid" | "reserved" | "taken";
|
|
21
|
+
interface InviteRequest {
|
|
22
|
+
/** `null` invites to the platform (`kits.access.model: 'catalog'`). */
|
|
23
|
+
readonly organizationId: string | null;
|
|
24
|
+
readonly email: string;
|
|
25
|
+
/** A role name, or a role id or key under the catalog model. */
|
|
26
|
+
readonly role: string;
|
|
27
|
+
/** A Postgres interval such as `'3 days'`. Defaults to `options.validFor`. */
|
|
28
|
+
readonly validFor?: string;
|
|
29
|
+
/** Profile fields to fill in on sign-up. */
|
|
30
|
+
readonly prefill?: Readonly<Record<string, unknown>>;
|
|
31
|
+
}
|
|
32
|
+
interface Invitation {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly organizationId: string | null;
|
|
35
|
+
readonly email: string;
|
|
36
|
+
readonly role: string;
|
|
37
|
+
readonly expiresAt: Temporal.Instant;
|
|
38
|
+
readonly invitedBy?: string;
|
|
39
|
+
readonly prefill: Readonly<Record<string, unknown>>;
|
|
40
|
+
}
|
|
41
|
+
/** What `onInvite` receives: the place to send the email. */
|
|
42
|
+
interface InvitationSent {
|
|
43
|
+
readonly invitation: Invitation;
|
|
44
|
+
/** Put it in the accept link. It is never stored in plain text by default. */
|
|
45
|
+
readonly token: string;
|
|
46
|
+
readonly resent: boolean;
|
|
47
|
+
}
|
|
48
|
+
type InvitationStatus = "pending" | "accepted" | "declined" | "revoked" | "expired";
|
|
49
|
+
interface InvitationPreview {
|
|
50
|
+
readonly status: InvitationStatus;
|
|
51
|
+
readonly email: string;
|
|
52
|
+
readonly role: string;
|
|
53
|
+
readonly organizationId: string | null;
|
|
54
|
+
readonly expiresAt: Temporal.Instant;
|
|
55
|
+
/** The organization columns in `options.previewColumns`. */
|
|
56
|
+
readonly organization: Readonly<Record<string, unknown>> | null;
|
|
57
|
+
readonly prefill: Readonly<Record<string, unknown>>;
|
|
58
|
+
}
|
|
59
|
+
interface SwitchResult {
|
|
60
|
+
readonly organizationId: string;
|
|
61
|
+
/** The active tenant is a claim: refresh the session to get it. */
|
|
62
|
+
readonly refresh: boolean;
|
|
63
|
+
}
|
|
64
|
+
interface OrgsOptions {
|
|
65
|
+
/** `sqlTransport(postgres.asUser(claims))` or `rpcTransport(supabase)`. */
|
|
66
|
+
readonly transport: KitTransport;
|
|
67
|
+
/**
|
|
68
|
+
* The schema holding the functions: `kits.<module>.schema` with
|
|
69
|
+
* `sqlTransport`, the exposed API schema of wrappers with `rpcTransport`.
|
|
70
|
+
* One schema for both modules, or one per module. Defaults to
|
|
71
|
+
* `better_supabase`.
|
|
72
|
+
*/
|
|
73
|
+
readonly schema?: string | {
|
|
74
|
+
readonly organizations?: string;
|
|
75
|
+
readonly invitations?: string;
|
|
76
|
+
};
|
|
77
|
+
/** `betterSupabase.events`, to emit `org.*` and `invitation.*` kit events. */
|
|
78
|
+
readonly events?: EventHub;
|
|
79
|
+
/** The user acting, for event metadata. */
|
|
80
|
+
readonly actorId?: string;
|
|
81
|
+
readonly context?: RequestContext;
|
|
82
|
+
/**
|
|
83
|
+
* Runs before an invitation is created, after the database checks the
|
|
84
|
+
* caller may invite. Return `false` to refuse (seat limits, plans).
|
|
85
|
+
*/
|
|
86
|
+
readonly canInvite?: (request: InviteRequest) => boolean | Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Runs after an invitation is created or resent, to send the email. When
|
|
89
|
+
* it throws, the call returns an error and the invitation stays open:
|
|
90
|
+
* call `resendInvitation` to retry.
|
|
91
|
+
*/
|
|
92
|
+
readonly onInvite?: (sent: InvitationSent) => void | Promise<void>;
|
|
93
|
+
/** Run before the built-in mapping, as in `betterSupabase.mapError()`. */
|
|
94
|
+
readonly errorMappers?: readonly ErrorMapper[];
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The `organizations` and `invitations` SQL kit modules as typed calls. Each
|
|
98
|
+
* method returns an `AsyncResult`; database errors carry the kit's error
|
|
99
|
+
* code (`ORG_FORBIDDEN`, `INVITATION_INVALID`) as `hint`.
|
|
100
|
+
*/
|
|
101
|
+
interface Orgs {
|
|
102
|
+
create(attributes: OrgAttributes, options?: CreateOrgOptions): AsyncResult<{
|
|
103
|
+
readonly id: string;
|
|
104
|
+
}>;
|
|
105
|
+
update(organizationId: string, attributes: OrgAttributes): AsyncResult<true>;
|
|
106
|
+
/** `false` when it did not exist (or was already soft-deleted). */
|
|
107
|
+
delete(organizationId: string): AsyncResult<boolean>;
|
|
108
|
+
/** Why a slug can't be used, or `undefined` when it can. */
|
|
109
|
+
slugProblem(slug: string, exceptOrganizationId?: string): AsyncResult<SlugProblem | undefined>;
|
|
110
|
+
updateMemberRole(organizationId: string, userId: string, role: string): AsyncResult<true>;
|
|
111
|
+
removeMember(organizationId: string, userId: string): AsyncResult<true>;
|
|
112
|
+
leave(organizationId: string): AsyncResult<true>;
|
|
113
|
+
/** `formerRole` defaults to `options.formerOwnerRole`. */
|
|
114
|
+
transferOwnership(organizationId: string, newOwnerId: string, formerRole?: string): AsyncResult<true>;
|
|
115
|
+
markUsed(organizationId: string): AsyncResult<boolean>;
|
|
116
|
+
switch(organizationId: string): AsyncResult<SwitchResult>;
|
|
117
|
+
invite(request: InviteRequest): AsyncResult<InvitationSent>;
|
|
118
|
+
resendInvitation(invitationId: string, validFor?: string): AsyncResult<InvitationSent>;
|
|
119
|
+
/** `false` when it was not open. */
|
|
120
|
+
revokeInvitation(invitationId: string): AsyncResult<boolean>;
|
|
121
|
+
declineInvitation(token: string): AsyncResult<boolean>;
|
|
122
|
+
/** `undefined` for an unknown token. Callable without a session. */
|
|
123
|
+
previewInvitation(token: string): AsyncResult<InvitationPreview | undefined>;
|
|
124
|
+
/** Returns the organization joined, `null` for a platform invitation. */
|
|
125
|
+
acceptInvitation(token: string): AsyncResult<{
|
|
126
|
+
readonly organizationId: string | null;
|
|
127
|
+
}>;
|
|
128
|
+
}
|
|
129
|
+
/** Calls the `organizations` and `invitations` kit functions. */
|
|
130
|
+
export declare function createOrgs(options: OrgsOptions): Orgs;
|
|
131
|
+
//#endregion
|
|
132
|
+
export { type CreateOrgOptions, type Invitation, type InvitationPreview, type InvitationSent, type InvitationStatus, type InviteRequest, type KitTransport, type OrgAttributes, type Orgs, type OrgsOptions, type RpcClient, type SlugProblem, type SwitchResult, rpcTransport, sqlTransport };
|
|
@@ -0,0 +1,245 @@
|
|
|
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 { n as emitKitEvent } from "../kit-events-CTFDfSwb.js";
|
|
5
|
+
import { n as rpcTransport, r as sqlTransport, t as rawError } from "../kit-transport-DJlHQUBU.js";
|
|
6
|
+
//#region src/orgs/orgs.ts
|
|
7
|
+
const DEFAULT_SCHEMA = "better_supabase";
|
|
8
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
const text = (value) => typeof value === "string" ? value : String(value);
|
|
10
|
+
const optionalText = (value) => value === null || value === void 0 ? null : text(value);
|
|
11
|
+
function invitationFrom(row) {
|
|
12
|
+
const invitedBy = optionalText(row["invited_by"]);
|
|
13
|
+
return {
|
|
14
|
+
id: text(row["id"]),
|
|
15
|
+
organizationId: optionalText(row["tenant"]),
|
|
16
|
+
email: text(row["email"]),
|
|
17
|
+
role: text(row["role"]),
|
|
18
|
+
expiresAt: temporal().Instant.from(text(row["expires_at"])),
|
|
19
|
+
...invitedBy === null ? {} : { invitedBy },
|
|
20
|
+
prefill: isRecord(row["prefill"]) ? row["prefill"] : {}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const STATUSES = /* @__PURE__ */ new Set([
|
|
24
|
+
"pending",
|
|
25
|
+
"accepted",
|
|
26
|
+
"declined",
|
|
27
|
+
"revoked",
|
|
28
|
+
"expired"
|
|
29
|
+
]);
|
|
30
|
+
function isStatus(value) {
|
|
31
|
+
return typeof value === "string" && STATUSES.has(value);
|
|
32
|
+
}
|
|
33
|
+
function previewFrom(row) {
|
|
34
|
+
const status = row["status"];
|
|
35
|
+
if (!isStatus(status)) throw new TypeError(`Unknown invitation status ${String(status)}`);
|
|
36
|
+
return {
|
|
37
|
+
status,
|
|
38
|
+
email: text(row["email"]),
|
|
39
|
+
role: text(row["role"]),
|
|
40
|
+
organizationId: optionalText(row["tenant"]),
|
|
41
|
+
expiresAt: temporal().Instant.from(text(row["expires_at"])),
|
|
42
|
+
organization: isRecord(row["organization"]) ? row["organization"] : null,
|
|
43
|
+
prefill: isRecord(row["prefill"]) ? row["prefill"] : {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function recordOf(value, fn) {
|
|
47
|
+
if (!isRecord(value)) throw new TypeError(`${fn} returned ${JSON.stringify(value)}`);
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
/** Calls the `organizations` and `invitations` kit functions. */
|
|
51
|
+
function createOrgs(options) {
|
|
52
|
+
const { transport } = options;
|
|
53
|
+
const schemaOf = (module) => typeof options.schema === "string" ? options.schema : options.schema?.[module] ?? DEFAULT_SCHEMA;
|
|
54
|
+
const mappers = options.errorMappers ?? [];
|
|
55
|
+
function run(module, fn, args, then) {
|
|
56
|
+
return AsyncResult.from(async () => {
|
|
57
|
+
let value;
|
|
58
|
+
try {
|
|
59
|
+
value = await transport.call(schemaOf(module), fn, args);
|
|
60
|
+
} catch (cause) {
|
|
61
|
+
const raw = rawError(cause);
|
|
62
|
+
return err(raw ? mapDbError(raw, mappers) : toDbError(cause));
|
|
63
|
+
}
|
|
64
|
+
return ok(await then(value));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function emit(type, data, subject, tenant) {
|
|
68
|
+
if (!options.events) return;
|
|
69
|
+
emitKitEvent(options.events, type, data, {
|
|
70
|
+
subject,
|
|
71
|
+
...tenant ? { tenant } : {},
|
|
72
|
+
...options.actorId ? { actorId: options.actorId } : {},
|
|
73
|
+
...options.context ? { context: options.context } : {}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const orgEvent = (type, data) => {
|
|
77
|
+
emit(type, data, `organizations/${data.organizationId}`, data.organizationId);
|
|
78
|
+
};
|
|
79
|
+
const invitationEvent = (type, data) => {
|
|
80
|
+
emit(type, data, `invitations/${data.invitationId}`, data.organizationId ?? null);
|
|
81
|
+
};
|
|
82
|
+
const sent = async (value, fn, resent) => {
|
|
83
|
+
const row = recordOf(value, fn);
|
|
84
|
+
const invitation = invitationFrom(row);
|
|
85
|
+
const result = {
|
|
86
|
+
invitation,
|
|
87
|
+
token: text(row["token"]),
|
|
88
|
+
resent
|
|
89
|
+
};
|
|
90
|
+
invitationEvent(resent ? "invitation.resent" : "invitation.created", {
|
|
91
|
+
invitationId: invitation.id,
|
|
92
|
+
...invitation.organizationId === null ? {} : { organizationId: invitation.organizationId },
|
|
93
|
+
email: invitation.email,
|
|
94
|
+
role: invitation.role
|
|
95
|
+
});
|
|
96
|
+
await options.onInvite?.(result);
|
|
97
|
+
return result;
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
create(attributes, createOptions) {
|
|
101
|
+
return run("organizations", "create_organization", { attrs: {
|
|
102
|
+
...attributes,
|
|
103
|
+
...createOptions?.ownerId ? { owner_id: createOptions.ownerId } : {}
|
|
104
|
+
} }, (value) => {
|
|
105
|
+
const id = text(value);
|
|
106
|
+
orgEvent("org.created", {
|
|
107
|
+
organizationId: id,
|
|
108
|
+
...createOptions?.ownerId ?? options.actorId ? { userId: createOptions?.ownerId ?? options.actorId } : {}
|
|
109
|
+
});
|
|
110
|
+
return { id };
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
update(organizationId, attributes) {
|
|
114
|
+
return run("organizations", "update_organization", {
|
|
115
|
+
org: organizationId,
|
|
116
|
+
attrs: attributes
|
|
117
|
+
}, () => {
|
|
118
|
+
orgEvent("org.updated", { organizationId });
|
|
119
|
+
return true;
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
delete(organizationId) {
|
|
123
|
+
return run("organizations", "delete_organization", { org: organizationId }, (value) => {
|
|
124
|
+
if (value === true) orgEvent("org.deleted", { organizationId });
|
|
125
|
+
return value === true;
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
slugProblem(slug, exceptOrganizationId) {
|
|
129
|
+
return run("organizations", "organization_slug_problem", {
|
|
130
|
+
value: slug,
|
|
131
|
+
except_org: exceptOrganizationId
|
|
132
|
+
}, (value) => {
|
|
133
|
+
if (value === null) return;
|
|
134
|
+
if (value === "invalid" || value === "reserved" || value === "taken") return value;
|
|
135
|
+
throw new TypeError(`Unknown slug problem ${String(value)}`);
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
updateMemberRole(organizationId, userId, role) {
|
|
139
|
+
return run("organizations", "update_member_role", {
|
|
140
|
+
org: organizationId,
|
|
141
|
+
member: userId,
|
|
142
|
+
role
|
|
143
|
+
}, () => {
|
|
144
|
+
orgEvent("org.role_changed", {
|
|
145
|
+
organizationId,
|
|
146
|
+
userId,
|
|
147
|
+
role
|
|
148
|
+
});
|
|
149
|
+
return true;
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
removeMember(organizationId, userId) {
|
|
153
|
+
return run("organizations", "remove_member", {
|
|
154
|
+
org: organizationId,
|
|
155
|
+
member: userId
|
|
156
|
+
}, () => {
|
|
157
|
+
orgEvent("org.member_removed", {
|
|
158
|
+
organizationId,
|
|
159
|
+
userId
|
|
160
|
+
});
|
|
161
|
+
return true;
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
leave(organizationId) {
|
|
165
|
+
return run("organizations", "leave_organization", { org: organizationId }, () => {
|
|
166
|
+
orgEvent("org.member_left", {
|
|
167
|
+
organizationId,
|
|
168
|
+
...options.actorId ? { userId: options.actorId } : {}
|
|
169
|
+
});
|
|
170
|
+
return true;
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
transferOwnership(organizationId, newOwnerId, formerRole) {
|
|
174
|
+
return run("organizations", "transfer_ownership", {
|
|
175
|
+
org: organizationId,
|
|
176
|
+
new_owner: newOwnerId,
|
|
177
|
+
former_role: formerRole
|
|
178
|
+
}, () => {
|
|
179
|
+
orgEvent("org.ownership_transferred", {
|
|
180
|
+
organizationId,
|
|
181
|
+
userId: newOwnerId
|
|
182
|
+
});
|
|
183
|
+
return true;
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
markUsed(organizationId) {
|
|
187
|
+
return run("organizations", "mark_used", { org: organizationId }, (value) => value === true);
|
|
188
|
+
},
|
|
189
|
+
switch(organizationId) {
|
|
190
|
+
return run("organizations", "switch_organization", { org: organizationId }, (value) => {
|
|
191
|
+
const row = recordOf(value, "switch_organization");
|
|
192
|
+
orgEvent("org.switched", {
|
|
193
|
+
organizationId,
|
|
194
|
+
...options.actorId ? { userId: options.actorId } : {}
|
|
195
|
+
});
|
|
196
|
+
return {
|
|
197
|
+
organizationId: text(row["organization_id"]),
|
|
198
|
+
refresh: row["refresh"] === true
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
invite(request) {
|
|
203
|
+
return AsyncResult.from(async () => {
|
|
204
|
+
if (options.canInvite && !await options.canInvite(request)) return err(dbError("forbidden", "The invitation was refused", { hint: "INVITATION_FORBIDDEN" }));
|
|
205
|
+
return run("invitations", "invite_member", {
|
|
206
|
+
tenant: request.organizationId,
|
|
207
|
+
invitee_email: request.email,
|
|
208
|
+
invitee_role: request.role,
|
|
209
|
+
valid_for: request.validFor,
|
|
210
|
+
prefill: request.prefill
|
|
211
|
+
}, (value) => sent(value, "invite_member", false));
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
resendInvitation(invitationId, validFor) {
|
|
215
|
+
return run("invitations", "resend_invitation", {
|
|
216
|
+
invitation_id: invitationId,
|
|
217
|
+
valid_for: validFor
|
|
218
|
+
}, (value) => sent(value, "resend_invitation", true));
|
|
219
|
+
},
|
|
220
|
+
revokeInvitation(invitationId) {
|
|
221
|
+
return run("invitations", "revoke_invitation", { invitation_id: invitationId }, (value) => {
|
|
222
|
+
if (value === true) invitationEvent("invitation.revoked", { invitationId });
|
|
223
|
+
return value === true;
|
|
224
|
+
});
|
|
225
|
+
},
|
|
226
|
+
declineInvitation(token) {
|
|
227
|
+
return run("invitations", "decline_invitation", { token }, (value) => value === true);
|
|
228
|
+
},
|
|
229
|
+
previewInvitation(token) {
|
|
230
|
+
return run("invitations", "invitation_preview", { token }, (value) => value === null ? void 0 : previewFrom(recordOf(value, "invitation_preview")));
|
|
231
|
+
},
|
|
232
|
+
acceptInvitation(token) {
|
|
233
|
+
return run("invitations", "accept_invitation", { token }, (value) => {
|
|
234
|
+
const organizationId = optionalText(value);
|
|
235
|
+
if (organizationId !== null) orgEvent("org.member_added", {
|
|
236
|
+
organizationId,
|
|
237
|
+
...options.actorId ? { userId: options.actorId } : {}
|
|
238
|
+
});
|
|
239
|
+
return { organizationId };
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
export { createOrgs, rpcTransport, sqlTransport };
|