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.
Files changed (211) hide show
  1. package/README.md +9 -6
  2. package/bin/better-supabase.js +4 -0
  3. package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
  4. package/dist/channel-FQW-CnGR.d.ts +70 -0
  5. package/dist/claims-BAf6c7is.js +48 -0
  6. package/dist/cli/_format-Cax5EsM9.js +101 -0
  7. package/dist/cli/bin.d.ts +1 -0
  8. package/dist/cli/bin.js +48 -0
  9. package/dist/cli/codemod-DqV7612m.js +388 -0
  10. package/dist/cli/command-CK14fP91.js +502 -0
  11. package/dist/cli/compare-BmIWVEui.js +9 -0
  12. package/dist/cli/config-CPVpXP5I.js +1965 -0
  13. package/dist/cli/config-dtoekM0v.js +2 -0
  14. package/dist/cli/db-DOBPt660.js +52 -0
  15. package/dist/cli/diff-SjkO5rNr.js +752 -0
  16. package/dist/cli/dist-DBPdlPYK.js +1112 -0
  17. package/dist/cli/dist-DreZ24Sz.js +129 -0
  18. package/dist/cli/dist-xn4wlboc.js +732 -0
  19. package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
  20. package/dist/cli/env-DmKv_G4A.js +108 -0
  21. package/dist/cli/env-jIFUADLY.js +511 -0
  22. package/dist/cli/errors-4v4DTtda.js +56 -0
  23. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  24. package/dist/cli/gen-BJc9vdyu.js +2 -0
  25. package/dist/cli/gen-C2osRcr5.js +2204 -0
  26. package/dist/cli/index.d.ts +493 -0
  27. package/dist/cli/index.js +184 -0
  28. package/dist/cli/init-Saf5-R9T.js +550 -0
  29. package/dist/cli/introspect--H7oGmcd.js +81 -0
  30. package/dist/cli/io-k41_z-jT.js +24 -0
  31. package/dist/cli/json5-ChRycquj.js +787 -0
  32. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  33. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  34. package/dist/cli/keys-ByKHnvP9.js +83 -0
  35. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  36. package/dist/cli/openapi-Cp5Fyta-.js +66 -0
  37. package/dist/cli/project-B_GeXyHh.js +70 -0
  38. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  39. package/dist/cli/prompts-BTYQGunc.js +2 -0
  40. package/dist/cli/read-sets-BfMQcKy8.js +289 -0
  41. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  42. package/dist/cli/run-BKR2W_79.js +351 -0
  43. package/dist/cli/seed-CUlqWh-8.js +98 -0
  44. package/dist/cli/skills-LJJIhLFR.js +137 -0
  45. package/dist/cli/sql-DzPbfngx.js +348 -0
  46. package/dist/cli/style-C0NFdJRL.js +29 -0
  47. package/dist/cli/toml-DB-MNnFs.js +424 -0
  48. package/dist/cli/version-BDJuO0UR.js +4 -0
  49. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  50. package/dist/client/index.d.ts +2 -2
  51. package/dist/client/index.js +15 -22
  52. package/dist/clone-BfgmLU90.js +21 -0
  53. package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
  54. package/dist/config/index.d.ts +4 -4
  55. package/dist/config/index.js +5 -5
  56. package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
  57. package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
  58. package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
  59. package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
  60. package/dist/edge/index.d.ts +20 -13
  61. package/dist/edge/index.js +55 -26
  62. package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
  63. package/dist/env/index.d.ts +2 -2
  64. package/dist/env/index.js +34 -1
  65. package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
  66. package/dist/events/index.d.ts +3 -2
  67. package/dist/events/index.js +69 -16
  68. package/dist/events-aIO7vsIk.d.ts +230 -0
  69. package/dist/executor-BunPOO-k.d.ts +402 -0
  70. package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
  71. package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
  72. package/dist/hono/index.d.ts +27 -23
  73. package/dist/hono/index.js +46 -11
  74. package/dist/impersonation-VfaGc4Q_.js +41 -0
  75. package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
  76. package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
  77. package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
  78. package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
  79. package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
  80. package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
  81. package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
  82. package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
  83. package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
  84. package/dist/index.d.ts +34 -17
  85. package/dist/index.js +15 -14
  86. package/dist/jobs/index.d.ts +147 -95
  87. package/dist/jobs/index.js +773 -250
  88. package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
  89. package/dist/kit-events-CTFDfSwb.js +60 -0
  90. package/dist/kit-transport-B82k1djG.d.ts +33 -0
  91. package/dist/kit-transport-DJlHQUBU.js +55 -0
  92. package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
  93. package/dist/list/index.d.ts +1 -1
  94. package/dist/list/index.js +3 -3
  95. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  96. package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
  97. package/dist/lookup-BaP5bI27.js +18 -0
  98. package/dist/mcp/index.d.ts +39 -22
  99. package/dist/mcp/index.js +39 -25
  100. package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
  101. package/dist/next/image/index.js +6 -3
  102. package/dist/next/index.d.ts +60 -36
  103. package/dist/next/index.js +167 -50
  104. package/dist/notifications/index.d.ts +162 -0
  105. package/dist/notifications/index.js +264 -0
  106. package/dist/openapi/index.d.ts +1 -1
  107. package/dist/openapi/index.js +6 -6
  108. package/dist/orgs/index.d.ts +132 -0
  109. package/dist/orgs/index.js +245 -0
  110. package/dist/orpc/index.d.ts +37 -20
  111. package/dist/orpc/index.js +24 -11
  112. package/dist/otel/index.d.ts +24 -5
  113. package/dist/otel/index.js +124 -64
  114. package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
  115. package/dist/permdock-sql-DgOfeixi.js +51 -0
  116. package/dist/plugin-DwARMuQp.js +59 -0
  117. package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
  118. package/dist/plugins/actor/index.d.ts +5 -2
  119. package/dist/plugins/actor/index.js +22 -8
  120. package/dist/plugins/rules/index.d.ts +2 -2
  121. package/dist/plugins/rules/index.js +52 -17
  122. package/dist/plugins/soft-delete/index.d.ts +3 -3
  123. package/dist/plugins/soft-delete/index.js +14 -5
  124. package/dist/plugins/tenant/index.d.ts +3 -3
  125. package/dist/plugins/tenant/index.js +45 -21
  126. package/dist/plugins/timestamps/index.d.ts +1 -1
  127. package/dist/plugins/timestamps/index.js +4 -3
  128. package/dist/plugins/validation/index.d.ts +2 -2
  129. package/dist/plugins/validation/index.js +26 -6
  130. package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
  131. package/dist/postgres/index.d.ts +5 -5
  132. package/dist/postgres/index.js +20 -10
  133. package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
  134. package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
  135. package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
  136. package/dist/query/index.d.ts +2 -2
  137. package/dist/query/index.js +3 -3
  138. package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
  139. package/dist/queue-BYUCjr59.d.ts +252 -0
  140. package/dist/react/index.d.ts +8 -7
  141. package/dist/react/index.js +4 -3
  142. package/dist/react/server.d.ts +16 -5
  143. package/dist/react/server.js +12 -4
  144. package/dist/react/session.d.ts +10 -4
  145. package/dist/react/session.js +11 -3
  146. package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
  147. package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
  148. package/dist/realtime/index.d.ts +3 -3
  149. package/dist/realtime/index.js +14 -12
  150. package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
  151. package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
  152. package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
  153. package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
  154. package/dist/respond-D2OQl62-.d.ts +371 -0
  155. package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
  156. package/dist/server/index.d.ts +17 -15
  157. package/dist/server/index.js +16 -22
  158. package/dist/server-Csx2sRjg.js +655 -0
  159. package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
  160. package/dist/signers-Cc6r98SW.d.ts +162 -0
  161. package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
  162. package/dist/sql/index.d.ts +247 -10
  163. package/dist/sql/index.js +8591 -780
  164. package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
  165. package/dist/ssr/index.d.ts +4 -4
  166. package/dist/ssr/index.js +1 -2
  167. package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
  168. package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
  169. package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
  170. package/dist/storage/index.d.ts +4 -4
  171. package/dist/storage/index.js +174 -27
  172. package/dist/support-DT-OhSxD.d.ts +88 -0
  173. package/dist/support-Dk6vdmEw.js +291 -0
  174. package/dist/support-view-Da5peKFg.d.ts +17 -0
  175. package/dist/support-view-g4YLBeqX.js +18 -0
  176. package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
  177. package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
  178. package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
  179. package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
  180. package/dist/testing/index.d.ts +113 -24
  181. package/dist/testing/index.js +375 -47
  182. package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
  183. package/dist/user-change-CHFEUro1.js +49 -0
  184. package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
  185. package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
  186. package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
  187. package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
  188. package/dist/webhooks/index.d.ts +136 -5
  189. package/dist/webhooks/index.js +588 -2
  190. package/dist/wire-D2dT86vi.js +35 -0
  191. package/package.json +50 -10
  192. package/schemas/config-v1.json +131 -1
  193. package/schemas/doctor-report-v1.json +10 -0
  194. package/skills/better-supabase/SKILL.md +60 -10
  195. package/skills/better-supabase/references/plugins.md +13 -14
  196. package/skills/better-supabase/references/troubleshooting.md +25 -4
  197. package/skills/better-supabase-api/SKILL.md +74 -19
  198. package/skills/better-supabase-api/references/adapters.md +48 -27
  199. package/skills/better-supabase-auth/SKILL.md +149 -0
  200. package/skills/better-supabase-auth/references/permdock.md +100 -0
  201. package/skills/better-supabase-testing/SKILL.md +58 -4
  202. package/dist/claims-CoTobNC-.js +0 -25
  203. package/dist/events-Do8jXh4D.d.ts +0 -84
  204. package/dist/executor-DynmIWhq.d.ts +0 -195
  205. package/dist/invalidate-DbxuL2jL.js +0 -18
  206. package/dist/permdock-sql-DImJULY-.d.ts +0 -17
  207. package/dist/plugin-BqR6wKMB.js +0 -23
  208. package/dist/respond-C44EZAJo.js +0 -399
  209. package/dist/respond-DAkPIdTH.d.ts +0 -175
  210. package/dist/result-c2OYgCV2.d.ts +0 -203
  211. 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 };
@@ -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-qriRM9cZ.js";
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 };
@@ -1,6 +1,6 @@
1
- import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-CGt1ADix.js";
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--aOXAGfU.js";
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(sb, {
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(sb, options) {
182
+ function createOpenApi(betterSupabase, options) {
183
183
  const defs = buildJsonSchema({
184
- meta: sb.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 = sb.meta.tables[key];
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 };