better-supabase 0.2.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.
Files changed (190) hide show
  1. package/README.md +37 -33
  2. package/dist/base64-D2gsioCY.js +42 -0
  3. package/dist/claims-BAf6c7is.js +48 -0
  4. package/dist/cli/_format-Cax5EsM9.js +101 -0
  5. package/dist/cli/bin.js +39 -6
  6. package/dist/cli/command-CK14fP91.js +502 -0
  7. package/dist/cli/compare-BmIWVEui.js +9 -0
  8. package/dist/cli/config-D9R0o1LH.js +1895 -0
  9. package/dist/cli/config-SSKSTUl2.js +2 -0
  10. package/dist/cli/db-DOBPt660.js +52 -0
  11. package/dist/cli/dist-B1MvLv-M.js +129 -0
  12. package/dist/cli/dist-CFgGknmZ.js +1112 -0
  13. package/dist/cli/dist-Du7VnEUG.js +621 -0
  14. package/dist/cli/doctor-BOym3mbL.js +2382 -0
  15. package/dist/cli/env-BZ0uy8XK.js +108 -0
  16. package/dist/cli/env-jIFUADLY.js +511 -0
  17. package/dist/cli/errors-4v4DTtda.js +56 -0
  18. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  19. package/dist/cli/gen-Ccn_p90L.js +2008 -0
  20. package/dist/cli/gen-D5cLCULx.js +2 -0
  21. package/dist/cli/index.d.ts +295 -28
  22. package/dist/cli/index.js +184 -3
  23. package/dist/cli/init-ClnxRYBF.js +550 -0
  24. package/dist/cli/introspect-DxqfkVIv.js +81 -0
  25. package/dist/cli/io-k41_z-jT.js +24 -0
  26. package/dist/cli/json5-ChRycquj.js +787 -0
  27. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  28. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  29. package/dist/cli/keys-ByKHnvP9.js +83 -0
  30. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  31. package/dist/cli/openapi-8WHjxiTJ.js +66 -0
  32. package/dist/cli/project-B_GeXyHh.js +70 -0
  33. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  34. package/dist/cli/prompts-BTYQGunc.js +2 -0
  35. package/dist/cli/read-sets-CMa29t9a.js +971 -0
  36. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  37. package/dist/cli/run-CP20LKk_.js +347 -0
  38. package/dist/cli/seed-5J6eu_Xg.js +98 -0
  39. package/dist/cli/skills-LJJIhLFR.js +137 -0
  40. package/dist/cli/sql-D4VbqmXi.js +199 -0
  41. package/dist/cli/style-C0NFdJRL.js +29 -0
  42. package/dist/cli/toml-DB-MNnFs.js +424 -0
  43. package/dist/{version-Cqq1m3UY.js → cli/version-5mW_8sY1.js} +1 -1
  44. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  45. package/dist/client/index.d.ts +2 -2
  46. package/dist/client/index.js +15 -25
  47. package/dist/{compiler-CZ9nIuqy.d.ts → compiler-DB3aSFOA.d.ts} +2 -2
  48. package/dist/config/index.d.ts +4 -3
  49. package/dist/config/index.js +5 -3
  50. package/dist/config-BpxDfnbg.js +129 -0
  51. package/dist/{index-DOsjFvMf.d.ts → config-C1CYcm6o.d.ts} +49 -49
  52. package/dist/{config-ClDXmVtY.js → config-CbvFmGD8.js} +14 -137
  53. package/dist/{define-BXMYD9AY.js → define-Bh0AqTik.js} +221 -120
  54. package/dist/{define-e3zI4_4g.d.ts → define-CRS2MRV3.d.ts} +36 -31
  55. package/dist/edge/index.d.ts +9 -13
  56. package/dist/edge/index.js +51 -25
  57. package/dist/{entitlements-Dng-ctuT.d.ts → entitlements-Dm-FHpNH.d.ts} +2 -2
  58. package/dist/env/index.js +203 -1
  59. package/dist/{errors-Bl3rkOzo.js → errors-L0xK41y_.js} +1 -1
  60. package/dist/events/index.d.ts +1 -1
  61. package/dist/events/index.js +139 -1
  62. package/dist/{events-DOt3nSO-.d.ts → events-B21UOhvP.d.ts} +4 -4
  63. package/dist/{executor-CGdNQ27g.d.ts → executor-B5IoUxLE.d.ts} +2 -2
  64. package/dist/{executor-DeppvirK.js → executor-CVt6dD0B.js} +9 -4
  65. package/dist/{executor-BYXQ9AGn.d.ts → executor-CZ7ImRiD.d.ts} +2 -2
  66. package/dist/hono/index.d.ts +17 -24
  67. package/dist/hono/index.js +11 -11
  68. package/dist/{index-DuP14VHo.d.ts → index-528Ugmyp.d.ts} +30 -10
  69. package/dist/{index-Bw1bwtC0.d.ts → index-BAy4tsAP.d.ts} +28 -15
  70. package/dist/{index-CBDufH1M.d.ts → index-BBdvo-5d.d.ts} +24 -26
  71. package/dist/{index-DbxFr7XG.d.ts → index-BhHEC04r.d.ts} +9 -4
  72. package/dist/index-Cf-D2jk1.d.ts +50 -0
  73. package/dist/{index-Ciu5Xlml.d.ts → index-D5IfUIDh.d.ts} +6 -5
  74. package/dist/{index-K-3WM2Hp.d.ts → index-DFmOpBGC.d.ts} +13 -13
  75. package/dist/{index-D4v7sncu.d.ts → index-RlLXd8dL.d.ts} +13 -13
  76. package/dist/index.d.ts +17 -17
  77. package/dist/index.js +23 -14
  78. package/dist/{invalidate-DbxuL2jL.js → invalidate-CgxmRamI.js} +7 -1
  79. package/dist/jobs/index.d.ts +33 -26
  80. package/dist/jobs/index.js +44 -22
  81. package/dist/{json-schema-BpYqkCKf.js → json-schema-DP6V7cbr.js} +8 -3
  82. package/dist/lint/index.js +1 -1
  83. package/dist/list/index.d.ts +2 -2
  84. package/dist/list/index.js +131 -96
  85. package/dist/{live-DJyZMD6w.d.ts → live-BHakSWWo.d.ts} +26 -16
  86. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  87. package/dist/lookup-BaP5bI27.js +18 -0
  88. package/dist/mcp/index.d.ts +34 -27
  89. package/dist/mcp/index.js +59 -25
  90. package/dist/{mfa-TVia554c.d.ts → mfa-C7MNQ1Ob.d.ts} +1 -1
  91. package/dist/{mfa-CpLfJSde.js → mfa-byfjruR9.js} +190 -46
  92. package/dist/next/image/index.js +6 -3
  93. package/dist/next/index.d.ts +64 -48
  94. package/dist/next/index.js +63 -50
  95. package/dist/openapi/index.d.ts +1 -1
  96. package/dist/openapi/index.js +61 -38
  97. package/dist/orpc/index.d.ts +18 -21
  98. package/dist/orpc/index.js +12 -12
  99. package/dist/otel/index.d.ts +5 -5
  100. package/dist/otel/index.js +84 -63
  101. package/dist/{permdock-sql-CghlHPuH.js → permdock-sql-C4KFzLc9.js} +12 -7
  102. package/dist/permdock-sql-DImJULY-.d.ts +17 -0
  103. package/dist/{plugin-C4TR9J0Z.d.ts → plugin-bNhvJwRh.d.ts} +5 -4
  104. package/dist/plugins/actor/index.d.ts +1 -1
  105. package/dist/plugins/actor/index.js +1 -1
  106. package/dist/plugins/rules/index.d.ts +2 -2
  107. package/dist/plugins/rules/index.js +46 -16
  108. package/dist/plugins/soft-delete/index.d.ts +3 -3
  109. package/dist/plugins/soft-delete/index.js +4 -4
  110. package/dist/plugins/tenant/index.d.ts +3 -3
  111. package/dist/plugins/tenant/index.js +10 -4
  112. package/dist/plugins/timestamps/index.d.ts +1 -1
  113. package/dist/plugins/timestamps/index.js +2 -2
  114. package/dist/plugins/validation/index.d.ts +2 -2
  115. package/dist/plugins/validation/index.js +3 -3
  116. package/dist/pool-DFwEQwmN.d.ts +78 -0
  117. package/dist/postgres/index.d.ts +10 -5
  118. package/dist/postgres/index.js +37 -16
  119. package/dist/{postgrest-cAEQ_IQN.d.ts → postgrest-Bc-0Xw62.d.ts} +1 -1
  120. package/dist/{problem-BTkTtRV9.js → problem-BihjPR0t.js} +11 -5
  121. package/dist/{problem-DlgB0lYl.d.ts → problem-U8UEKccv.d.ts} +6 -4
  122. package/dist/query/index.d.ts +2 -2
  123. package/dist/query/index.js +2 -2
  124. package/dist/{query-ChvDbsDE.js → query-BMMSWbI7.js} +19 -19
  125. package/dist/react/index.d.ts +7 -7
  126. package/dist/react/index.js +2 -2
  127. package/dist/react/server.d.ts +6 -6
  128. package/dist/react/server.js +3 -3
  129. package/dist/react/session.d.ts +21 -2
  130. package/dist/react/session.js +19 -1
  131. package/dist/{react-C83BdBGt.js → react-CQHMaESR.js} +59 -55
  132. package/dist/{read-set-4lI3W_gZ.js → read-set-FWK8qQ58.js} +51 -16
  133. package/dist/realtime/index.d.ts +4 -3
  134. package/dist/realtime/index.js +19 -17
  135. package/dist/{index-CG69s15v.d.ts → refresh-BwYkyNOQ.d.ts} +7 -3
  136. package/dist/{resolve-CjHILk2w.d.ts → resolve-BAqQcwMW.d.ts} +17 -11
  137. package/dist/{resource-CgZa3YG_.d.ts → resource-BJcaIfxi.d.ts} +17 -8
  138. package/dist/{resource-BHEWmQJK.js → resource-My_y-Syh.js} +43 -21
  139. package/dist/{respond-Dne1ARSa.d.ts → respond-DgIbdrt8.d.ts} +39 -14
  140. package/dist/{result-DWXatkd6.js → result-Bj6yt33D.js} +2 -2
  141. package/dist/{result-CTkCZ6JA.d.ts → result-CqEsbgYs.d.ts} +16 -11
  142. package/dist/server/index.d.ts +38 -13
  143. package/dist/server/index.js +43 -14
  144. package/dist/{respond-DMcbQwfN.js → server-BA75B0mT.js} +151 -96
  145. package/dist/{shared-_6-uPL7E.js → shared-DnoCdMDr.js} +4 -3
  146. package/dist/{simplify-bwJpLnaS.js → simplify-Cqvfr9dj.js} +76 -39
  147. package/dist/sql/index.d.ts +139 -0
  148. package/dist/sql/index.js +1983 -0
  149. package/dist/{sql-DTjA1LPj.js → sql-B34HU2DI.js} +74 -17
  150. package/dist/ssr/index.d.ts +4 -4
  151. package/dist/ssr/index.js +2 -2
  152. package/dist/{standard-FUi3Z3-7.js → standard-BRyBGTtp.js} +2 -2
  153. package/dist/{standard-CyhRzctb.d.ts → standard-BsaCYQJc.d.ts} +1 -1
  154. package/dist/{stats-C5W-rBfR.js → stats-C_YxA2r1.js} +40 -14
  155. package/dist/storage/index.d.ts +4 -3
  156. package/dist/storage/index.js +548 -1
  157. package/dist/{tables-rZxRxNPW.js → tables-Cb-3_VWc.js} +19 -5
  158. package/dist/temporal-BwshQk0W.js +27 -0
  159. package/dist/temporal-required-CBNHT6ni.js +19 -0
  160. package/dist/testing/index.d.ts +25 -24
  161. package/dist/testing/index.js +165 -66
  162. package/dist/{types-C83v5SQz.d.ts → types-DONv9SHN.d.ts} +14 -11
  163. package/dist/version-Bmf7_FzM.js +4 -0
  164. package/dist/{view-D1s0wa0T.d.ts → view-B64o81wp.d.ts} +36 -6
  165. package/dist/{view-aOYizTeE.js → view-BrJI2LKp.js} +11 -2
  166. package/dist/webhooks/index.d.ts +19 -17
  167. package/dist/webhooks/index.js +1 -1
  168. package/dist/{webhooks-DMipjhSP.js → webhooks-Bh_Jf_-l.js} +45 -24
  169. package/package.json +50 -22
  170. package/schemas/config-v1.json +31 -1
  171. package/schemas/doctor-report-v1.json +17 -1
  172. package/schemas/snapshot-v2.json +35 -0
  173. package/skills/better-supabase/SKILL.md +77 -18
  174. package/skills/better-supabase/references/plugins.md +21 -22
  175. package/skills/better-supabase/references/troubleshooting.md +38 -18
  176. package/skills/better-supabase-api/SKILL.md +66 -18
  177. package/skills/better-supabase-api/references/adapters.md +64 -41
  178. package/skills/better-supabase-auth/SKILL.md +146 -0
  179. package/skills/better-supabase-auth/references/permdock.md +55 -0
  180. package/skills/better-supabase-testing/SKILL.md +68 -11
  181. package/dist/casing-da0uRTqt.js +0 -14
  182. package/dist/claims-CoTobNC-.js +0 -25
  183. package/dist/cli-DrSgDOQC.js +0 -6646
  184. package/dist/env-BEToab0I.js +0 -204
  185. package/dist/events-BuzTgn2D.js +0 -135
  186. package/dist/pool-R2m_DkXi.d.ts +0 -37
  187. package/dist/seed-CzecODcJ.js +0 -101
  188. package/dist/session-C3LxnQ0e.d.ts +0 -22
  189. package/dist/session-CqEwzVup.js +0 -20
  190. package/dist/storage-DuGn38fr.js +0 -527
@@ -1,10 +1,12 @@
1
- import { c as mapDbError, n as dbError, t as DbException } from "../errors-Bl3rkOzo.js";
2
- import { i as ok, n as err, o as toDbError, t as AsyncResult } from "../result-DWXatkd6.js";
3
- import { t as validate } from "../standard-FUi3Z3-7.js";
4
- import { a as problemResponse } from "../problem-BTkTtRV9.js";
5
- import { t as fromPgError } from "../executor-DeppvirK.js";
6
- import { c as verifyWebhook } from "../webhooks-DMipjhSP.js";
7
- //#region src/jobs/index.ts
1
+ import { c as mapDbError, n as dbError, t as DbException } from "../errors-L0xK41y_.js";
2
+ import { n as nowInstant } from "../temporal-BwshQk0W.js";
3
+ import { t as temporal } from "../temporal-required-CBNHT6ni.js";
4
+ import { i as ok, n as err, o as toDbError, t as AsyncResult } from "../result-Bj6yt33D.js";
5
+ import { t as validate } from "../standard-BRyBGTtp.js";
6
+ import { a as problemResponse } from "../problem-BihjPR0t.js";
7
+ import { t as fromPgError } from "../executor-CVt6dD0B.js";
8
+ import { c as verifyWebhook } from "../webhooks-Bh_Jf_-l.js";
9
+ //#region src/jobs/jobs.ts
8
10
  function run(fn) {
9
11
  return AsyncResult.from(async () => {
10
12
  try {
@@ -22,7 +24,10 @@ function workerId() {
22
24
  return `worker-${crypto.randomUUID().slice(0, 8)}`;
23
25
  }
24
26
  const sleep = (ms, signal) => new Promise((resolve) => {
25
- if (signal?.aborted) return resolve();
27
+ if (signal?.aborted) {
28
+ resolve();
29
+ return;
30
+ }
26
31
  const timer = setTimeout(done, ms);
27
32
  function done() {
28
33
  clearTimeout(timer);
@@ -149,7 +154,8 @@ function postgrestTransport(client) {
149
154
  unschedule: () => sqlOnly("unschedule")
150
155
  };
151
156
  }
152
- const toDate = (value) => value instanceof Date ? value : new Date(value);
157
+ /** pgmq and the inbox return `timestamptz` as text over RPC and as `Date` from `pg`. */
158
+ const toInstant = (value) => value instanceof Date ? temporal().Instant.fromEpochMilliseconds(value.getTime()) : temporal().Instant.from(value);
153
159
  function toJob(queue, row) {
154
160
  const message = row.message ?? {};
155
161
  return {
@@ -158,8 +164,8 @@ function toJob(queue, row) {
158
164
  payload: message.payload,
159
165
  attempts: row.attempts,
160
166
  maxAttempts: message.max_attempts ?? 5,
161
- enqueuedAt: toDate(row.enqueued_at),
162
- visibleUntil: toDate(row.visible_until),
167
+ enqueuedAt: toInstant(row.enqueued_at),
168
+ visibleUntil: toInstant(row.visible_until),
163
169
  lastError: message.last_error ?? null
164
170
  };
165
171
  }
@@ -194,12 +200,15 @@ function createJobs(source, queues) {
194
200
  schemaOf(queue);
195
201
  return (await transport.read(queue, claimOptions.lease ?? 300, claimOptions.batch ?? 1)).map((row) => toJob(queue, row));
196
202
  });
203
+ /**
204
+ * Extends the lease of every job in `held` at half the lease, so jobs
205
+ * waiting their turn in a claimed batch don't expire before they run.
206
+ */
207
+ const heartbeat = (held, lease) => transport.name === "sql" ? setInterval(() => {
208
+ for (const job of held) run(() => transport.extend(job, lease));
209
+ }, Math.max(1e3, lease * 1e3 / 2)) : void 0;
197
210
  const handle = async (job, handler, workOptions) => {
198
211
  const controller = new AbortController();
199
- const lease = workOptions.lease ?? 300;
200
- const heartbeat = transport.name === "sql" ? setInterval(() => {
201
- run(() => transport.extend(job, lease));
202
- }, Math.max(1e3, lease * 1e3 / 2)) : void 0;
203
212
  try {
204
213
  const payload = await validate(schemaOf(job.queue), job.payload, "payload");
205
214
  if (!payload.ok) {
@@ -219,15 +228,17 @@ function createJobs(source, queues) {
219
228
  await fail(job, cause);
220
229
  workOptions.onError?.(typeof cause === "object" && cause !== null && "kind" in cause ? cause : toDbError(cause), job);
221
230
  return false;
222
- } finally {
223
- clearInterval(heartbeat);
224
231
  }
225
232
  };
226
233
  const loop = async (queue, handler, workOptions, forever) => {
227
234
  const concurrency = Math.max(1, workOptions.concurrency ?? 1);
235
+ const lease = workOptions.lease ?? 300;
236
+ const firstWait = workOptions.pollInterval ?? 1e3;
237
+ const maxWait = Math.max(firstWait, workOptions.maxPollInterval ?? 3e4);
228
238
  let succeeded = 0;
229
239
  let failed = 0;
230
240
  const lanes = Array.from({ length: concurrency }, async () => {
241
+ let wait = firstWait;
231
242
  while (!workOptions.signal?.aborted) {
232
243
  const claimed = await claim(queue, {
233
244
  ...workOptions,
@@ -236,11 +247,22 @@ function createJobs(source, queues) {
236
247
  if (!claimed.ok) throw new TypeError(claimed.error.message, { cause: claimed.error });
237
248
  if (claimed.data.length === 0) {
238
249
  if (!forever) return;
239
- await sleep(workOptions.pollInterval ?? 1e3, workOptions.signal);
250
+ await sleep(wait, workOptions.signal);
251
+ wait = Math.min(maxWait, wait * 2);
240
252
  continue;
241
253
  }
242
- for (const job of claimed.data) if (await handle(job, handler, workOptions)) succeeded += 1;
243
- else failed += 1;
254
+ wait = firstWait;
255
+ const held = new Set(claimed.data);
256
+ const timer = heartbeat(held, lease);
257
+ try {
258
+ for (const job of claimed.data) {
259
+ if (await handle(job, handler, workOptions)) succeeded += 1;
260
+ else failed += 1;
261
+ held.delete(job);
262
+ }
263
+ } finally {
264
+ clearInterval(timer);
265
+ }
244
266
  }
245
267
  });
246
268
  await Promise.all(lanes);
@@ -254,7 +276,7 @@ function createJobs(source, queues) {
254
276
  return AsyncResult.from(async () => {
255
277
  const valid = await validate(schemaOf(queue), payload, "payload");
256
278
  if (!valid.ok) return valid;
257
- const delay = enqueueOptions.delay ?? (enqueueOptions.runAt ? Math.max(0, Math.ceil((enqueueOptions.runAt.getTime() - Date.now()) / 1e3)) : 0);
279
+ const delay = enqueueOptions.delay ?? (enqueueOptions.runAt ? Math.max(0, Math.ceil((enqueueOptions.runAt.epochMilliseconds - nowInstant().epochMilliseconds) / 1e3)) : 0);
258
280
  return run(() => transport.send(queue, valid.data, delay, enqueueOptions.maxAttempts ?? 5, enqueueOptions.dedupeKey));
259
281
  });
260
282
  },
@@ -435,7 +457,7 @@ function createInbox(sql, options) {
435
457
  payload: row.payload,
436
458
  headers: row.headers,
437
459
  attempts: row.attempts,
438
- receivedAt: toDate(row.received_at)
460
+ receivedAt: toInstant(row.received_at)
439
461
  };
440
462
  try {
441
463
  const outcome = await handler(message);
@@ -19,7 +19,8 @@ function scalarKind(column) {
19
19
  values: column.enum
20
20
  };
21
21
  if (column.json) return { kind: "json" };
22
- if (column.codec === "date") return { kind: "dateObject" };
22
+ if (column.codec === "instant") return { kind: "instant" };
23
+ if (column.codec === "plainDateTime") return { kind: "plainDateTime" };
23
24
  if (column.codec === "bigint") return { kind: "bigint" };
24
25
  if (column.codec === "string") return { kind: "string" };
25
26
  if (column.type === "uuid") return { kind: "uuid" };
@@ -64,7 +65,7 @@ function variantsFor(table) {
64
65
  ];
65
66
  }
66
67
  function tableEntries(input) {
67
- return Object.entries(input.meta.tables).sort(([a], [b]) => a.localeCompare(b));
68
+ return Object.entries(input.meta.tables).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
68
69
  }
69
70
  function propertyKey(name) {
70
71
  return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
@@ -107,10 +108,14 @@ function scalar(kind) {
107
108
  type: "string",
108
109
  format: "date"
109
110
  };
110
- case "dateObject": return {
111
+ case "instant": return {
111
112
  type: "string",
112
113
  format: "date-time"
113
114
  };
115
+ case "plainDateTime": return {
116
+ type: "string",
117
+ pattern: "^\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?$"
118
+ };
114
119
  case "bigint": return {
115
120
  type: "string",
116
121
  pattern: "^-?\\d+$"
@@ -16,7 +16,7 @@ function receiverName(node) {
16
16
  return receiver.property.name;
17
17
  }
18
18
  /** Matches `order_items`, `orderItems` and `public.order_items` alike. */
19
- const tableKey = (name) => (name.split(".").at(-1) ?? name).replace(/_/g, "").toLowerCase();
19
+ const tableKey = (name) => (name.split(".").at(-1) ?? name).replaceAll("_", "").toLowerCase();
20
20
  function keyName(property) {
21
21
  if (property.computed) return void 0;
22
22
  if (property.key.type === "Identifier") return property.key.name;
@@ -1,2 +1,2 @@
1
- import { a as ListPage, c as ListQuery, d as ListSearchParams, f as OpenApiParameter, h as defineListQuery, i as ListExtra, l as ListQueryConfig, m as UNSET, n as FacetInfo, o as ListParseResult, p as ParserSpec, r as ListDefinition, s as ListParsers, t as FacetCounts, u as ListQueryInput } from "../index-Bw1bwtC0.js";
2
- export { FacetCounts, FacetInfo, ListDefinition, ListExtra, ListPage, ListParseResult, ListParsers, ListQuery, ListQueryConfig, ListQueryInput, ListSearchParams, OpenApiParameter, ParserSpec, UNSET, defineListQuery };
1
+ import { a as ListPage, c as ListParsers, d as ListQueryInput, f as ListSearchParams, g as defineListQuery, h as UNSET, i as ListExtra, l as ListQuery, m as ParserSpec, n as FacetInfo, o as ListPagination, p as OpenApiParameter, r as ListDefinition, s as ListParseResult, t as FacetCounts, u as ListQueryConfig } from "../index-BAy4tsAP.js";
2
+ export { type FacetCounts, type FacetInfo, type ListDefinition, type ListExtra, type ListPage, type ListPagination, type ListParseResult, type ListParsers, type ListQuery, type ListQueryConfig, type ListQueryInput, type ListSearchParams, type OpenApiParameter, type ParserSpec, UNSET, defineListQuery };
@@ -1,10 +1,11 @@
1
- //#region src/list/index.ts
1
+ //#region src/list/list-query.ts
2
2
  /** Facet value meaning "not set": filters with `is null`. */
3
3
  const UNSET = "__unset__";
4
4
  const RESERVED = /* @__PURE__ */ new Set([
5
5
  "q",
6
6
  "sort",
7
7
  "page",
8
+ "after",
8
9
  "size"
9
10
  ]);
10
11
  function first(value) {
@@ -13,8 +14,9 @@ function first(value) {
13
14
  function all(value) {
14
15
  return (typeof value === "string" ? [value] : [...value ?? []]).flatMap((entry) => entry.split(",")).map((entry) => entry.trim()).filter(Boolean);
15
16
  }
17
+ const isParamValue = (value) => value === void 0 || typeof value === "string" || Array.isArray(value) && value.every((entry) => typeof entry === "string");
16
18
  function isSearchParams(value) {
17
- return value instanceof URLSearchParams || !("facets" in value && typeof value.facets === "object");
19
+ return value instanceof URLSearchParams || !("facets" in value && typeof value.facets === "object") && Object.values(value).every(isParamValue);
18
20
  }
19
21
  function readRecord(input) {
20
22
  if (!(input instanceof URLSearchParams)) return input;
@@ -33,7 +35,7 @@ function toInt(raw) {
33
35
  * OpenAPI parameters and a JSON Schema derived from the same definition.
34
36
  *
35
37
  * ```ts
36
- * export const customerList = defineListQuery(sb, 'customers', {
38
+ * export const customerList = defineListQuery(betterSupabase, 'customers', {
37
39
  * search: ['name', 'kvk'],
38
40
  * facets: { status: 'status' },
39
41
  * sorts: { name: { name: 'asc' }, newest: [{ createdAt: 'desc' }, { id: 'asc' }] },
@@ -41,12 +43,13 @@ function toInt(raw) {
41
43
  * });
42
44
  * ```
43
45
  */
44
- function defineListQuery(sb, table, config) {
45
- const meta = sb.meta.tables[table];
46
+ function defineListQuery(betterSupabase, table, config) {
47
+ const meta = betterSupabase.meta.tables[table];
46
48
  if (!meta) throw new TypeError(`defineListQuery: unknown table "${table}"`);
47
49
  const pageSize = config.pageSize ?? 50;
48
50
  const maxPageSize = config.maxPageSize ?? 200;
49
51
  const maxSearch = config.maxSearchLength ?? 200;
52
+ const cursor = config.pagination === "cursor";
50
53
  const sortKeys = Object.keys(config.sorts);
51
54
  const facetEntries = Object.entries(config.facets ?? {});
52
55
  for (const [key, column] of facetEntries) {
@@ -96,6 +99,18 @@ function defineListQuery(sb, table, config) {
96
99
  message: "Must be a whole number of at least 1",
97
100
  path: ["page"]
98
101
  });
102
+ else if (cursor && page !== 1) issues.push({
103
+ message: "This list pages with `after`, not `page`",
104
+ path: ["page"]
105
+ });
106
+ let after;
107
+ if (raw.after !== void 0 && raw.after !== "") {
108
+ if (cursor && typeof raw.after === "string") after = raw.after;
109
+ else issues.push({
110
+ message: cursor ? "Must be text" : "This list pages with `page`, not `after`",
111
+ path: ["after"]
112
+ });
113
+ }
99
114
  const size = raw.size === void 0 || raw.size === "" ? pageSize : toInt(raw.size);
100
115
  if (size === void 0 || !Number.isInteger(size) || size < 1 || size > maxPageSize) issues.push({
101
116
  message: `Must be a whole number from 1 to ${String(maxPageSize)}`,
@@ -121,6 +136,7 @@ function defineListQuery(sb, table, config) {
121
136
  ...q ? { q } : {},
122
137
  sort,
123
138
  page: page ?? 1,
139
+ ...after ? { after } : {},
124
140
  size: size ?? pageSize,
125
141
  facets: facetValues
126
142
  }
@@ -143,6 +159,7 @@ function defineListQuery(sb, table, config) {
143
159
  q: first(record["q"]),
144
160
  sort: first(record["sort"]),
145
161
  page: first(record["page"]),
162
+ after: first(record["after"]),
146
163
  size: first(record["size"]),
147
164
  facets: facetValues
148
165
  });
@@ -160,6 +177,7 @@ function defineListQuery(sb, table, config) {
160
177
  q: typed.q,
161
178
  sort: typed.sort,
162
179
  page: typed.page,
180
+ after: typed.after,
163
181
  size: typed.size,
164
182
  facets: rawFacets
165
183
  });
@@ -195,12 +213,15 @@ function defineListQuery(sb, table, config) {
195
213
  }
196
214
  function args(query) {
197
215
  const filter = where(query);
216
+ const paging = cursor ? { after: query.after ?? null } : {
217
+ page: query.page,
218
+ count: config.count ?? "exact"
219
+ };
198
220
  return {
199
221
  ...filter ? { where: filter } : {},
200
222
  orderBy: config.sorts[query.sort],
201
- page: query.page,
202
223
  size: query.size,
203
- count: config.count ?? "exact"
224
+ ...paging
204
225
  };
205
226
  }
206
227
  /** Whether a group's value passes the facet's selected values. */
@@ -249,6 +270,10 @@ function defineListQuery(sb, table, config) {
249
270
  serialize: (value) => value
250
271
  },
251
272
  page: intParser,
273
+ after: {
274
+ parse: (value) => value || null,
275
+ serialize: (value) => value
276
+ },
252
277
  size: intParser,
253
278
  ...Object.fromEntries(facets.map((facet) => [facet.key, listParser]))
254
279
  };
@@ -261,8 +286,102 @@ function defineListQuery(sb, table, config) {
261
286
  uniqueItems: true
262
287
  });
263
288
  const searchDescription = config.search ? Array.isArray(config.search) ? `Case-insensitive search in ${config.search.join(", ")}.` : "Full-text search (web search syntax)." : void 0;
289
+ const openapi = [
290
+ ...searchDescription ? [{
291
+ name: "q",
292
+ in: "query",
293
+ required: false,
294
+ description: searchDescription,
295
+ schema: {
296
+ type: "string",
297
+ maxLength: maxSearch
298
+ }
299
+ }] : [],
300
+ {
301
+ name: "sort",
302
+ in: "query",
303
+ required: false,
304
+ description: `Ordering. Default \`${config.defaultSort}\`.`,
305
+ schema: {
306
+ type: "string",
307
+ enum: sortKeys,
308
+ default: config.defaultSort
309
+ }
310
+ },
311
+ cursor ? {
312
+ name: "after",
313
+ in: "query",
314
+ required: false,
315
+ description: "`nextCursor` of the previous page. Leave it out for the first page.",
316
+ schema: { type: "string" }
317
+ } : {
318
+ name: "page",
319
+ in: "query",
320
+ required: false,
321
+ description: "1-based page number.",
322
+ schema: {
323
+ type: "integer",
324
+ minimum: 1,
325
+ default: 1
326
+ }
327
+ },
328
+ {
329
+ name: "size",
330
+ in: "query",
331
+ required: false,
332
+ description: "Page size.",
333
+ schema: {
334
+ type: "integer",
335
+ minimum: 1,
336
+ maximum: maxPageSize,
337
+ default: pageSize
338
+ }
339
+ },
340
+ ...facets.map((facet) => ({
341
+ name: facet.key,
342
+ in: "query",
343
+ required: false,
344
+ description: `Filter on ${facet.column}. Comma-separated; any value matches.${facet.nullable ? ` \`${UNSET}\` matches empty.` : ""}`,
345
+ schema: facetSchema(facet),
346
+ style: "form",
347
+ explode: false
348
+ }))
349
+ ];
350
+ const jsonSchema = {
351
+ type: "object",
352
+ additionalProperties: false,
353
+ properties: {
354
+ ...searchDescription ? { q: {
355
+ type: "string",
356
+ maxLength: maxSearch,
357
+ description: searchDescription
358
+ } } : {},
359
+ sort: {
360
+ type: "string",
361
+ enum: sortKeys,
362
+ default: config.defaultSort
363
+ },
364
+ ...cursor ? { after: { type: "string" } } : { page: {
365
+ type: "integer",
366
+ minimum: 1,
367
+ default: 1
368
+ } },
369
+ size: {
370
+ type: "integer",
371
+ minimum: 1,
372
+ maximum: maxPageSize,
373
+ default: pageSize
374
+ },
375
+ ...facets.length > 0 ? { facets: {
376
+ type: "object",
377
+ additionalProperties: false,
378
+ properties: Object.fromEntries(facets.map((facet) => [facet.key, facetSchema(facet)]))
379
+ } } : {}
380
+ }
381
+ };
264
382
  return {
265
383
  table,
384
+ pagination: config.pagination ?? "offset",
266
385
  defaults,
267
386
  facets,
268
387
  parse,
@@ -279,7 +398,8 @@ function defineListQuery(sb, table, config) {
279
398
  if (query.q) params.set("q", query.q);
280
399
  if (query.sort && query.sort !== config.defaultSort) params.set("sort", query.sort);
281
400
  if (query.page && query.page !== 1) params.set("page", String(query.page));
282
- if (query.size && query.size !== pageSize) params.set("size", String(query.size));
401
+ if (query.after) params.set("after", query.after);
402
+ if (query.size !== void 0 && query.size > 0 && query.size !== pageSize) params.set("size", String(query.size));
283
403
  for (const facet of facets) {
284
404
  const values = query.facets?.[facet.key];
285
405
  if (values && values.length > 0) params.set(facet.key, values.join(","));
@@ -297,7 +417,7 @@ function defineListQuery(sb, table, config) {
297
417
  const page = repository.paginate({
298
418
  ...extra,
299
419
  ...base,
300
- count: count ?? base["count"],
420
+ ...cursor ? {} : { count: count ?? base["count"] },
301
421
  ...combined ? { where: combined } : {}
302
422
  });
303
423
  if (!config.facetCounts) return page;
@@ -317,93 +437,8 @@ function defineListQuery(sb, table, config) {
317
437
  facetCounts: marginals(rows, query)
318
438
  })));
319
439
  },
320
- openapi: [
321
- ...searchDescription ? [{
322
- name: "q",
323
- in: "query",
324
- required: false,
325
- description: searchDescription,
326
- schema: {
327
- type: "string",
328
- maxLength: maxSearch
329
- }
330
- }] : [],
331
- {
332
- name: "sort",
333
- in: "query",
334
- required: false,
335
- description: `Ordering. Default \`${config.defaultSort}\`.`,
336
- schema: {
337
- type: "string",
338
- enum: sortKeys,
339
- default: config.defaultSort
340
- }
341
- },
342
- {
343
- name: "page",
344
- in: "query",
345
- required: false,
346
- description: "1-based page number.",
347
- schema: {
348
- type: "integer",
349
- minimum: 1,
350
- default: 1
351
- }
352
- },
353
- {
354
- name: "size",
355
- in: "query",
356
- required: false,
357
- description: "Page size.",
358
- schema: {
359
- type: "integer",
360
- minimum: 1,
361
- maximum: maxPageSize,
362
- default: pageSize
363
- }
364
- },
365
- ...facets.map((facet) => ({
366
- name: facet.key,
367
- in: "query",
368
- required: false,
369
- description: `Filter on ${facet.column}. Comma-separated; any value matches.${facet.nullable ? ` \`${UNSET}\` matches empty.` : ""}`,
370
- schema: facetSchema(facet),
371
- style: "form",
372
- explode: false
373
- }))
374
- ],
375
- jsonSchema: {
376
- type: "object",
377
- additionalProperties: false,
378
- properties: {
379
- ...searchDescription ? { q: {
380
- type: "string",
381
- maxLength: maxSearch,
382
- description: searchDescription
383
- } } : {},
384
- sort: {
385
- type: "string",
386
- enum: sortKeys,
387
- default: config.defaultSort
388
- },
389
- page: {
390
- type: "integer",
391
- minimum: 1,
392
- default: 1
393
- },
394
- size: {
395
- type: "integer",
396
- minimum: 1,
397
- maximum: maxPageSize,
398
- default: pageSize
399
- },
400
- ...facets.length > 0 ? { facets: {
401
- type: "object",
402
- additionalProperties: false,
403
- properties: Object.fromEntries(facets.map((facet) => [facet.key, facetSchema(facet)]))
404
- } } : {}
405
- }
406
- }
440
+ openapi,
441
+ jsonSchema
407
442
  };
408
443
  }
409
444
  //#endregion
@@ -1,10 +1,11 @@
1
- import { A as SchemaMeta, N as TableKey, O as Row, n as AnyModels, t as AnyFunctions, x as PermdockTopicPolicy } from "./types-C83v5SQz.js";
2
- import { b as ValidationIssue, o as Result, p as DbError, t as AsyncResult } from "./result-CTkCZ6JA.js";
3
- import { Z as QuerySpec, t as BetterSupabase } from "./define-e3zI4_4g.js";
1
+ import { A as SchemaMeta, N as TableKey, O as Row, n as AnyModels, t as AnyFunctions, x as PermdockTopicPolicy } from "./types-DONv9SHN.js";
2
+ import { b as ValidationIssue, o as Result, p as DbError, t as AsyncResult } from "./result-CqEsbgYs.js";
3
+ import { Z as QuerySpec, t as BetterSupabase } from "./define-CRS2MRV3.js";
4
+ import { t as PermdockCatalog } from "./permdock-sql-DImJULY-.js";
4
5
  import { n as TemplateValues, t as TemplateParams } from "./template-mbvlysOo.js";
5
6
  import { RealtimeChannel, SupabaseClient } from "@supabase/supabase-js";
6
7
  import { StandardSchemaV1 } from "@standard-schema/spec";
7
- //#region src/realtime/index.d.ts
8
+ //#region src/realtime/topic.d.ts
8
9
  type EventSchemas = Readonly<Record<string, StandardSchemaV1>>;
9
10
  interface TopicOptions<E extends EventSchemas> {
10
11
  /** Name used for generated policies and functions. Defaults to the template's literal parts. */
@@ -34,9 +35,15 @@ interface TopicOptions<E extends EventSchemas> {
34
35
  * The helpers check role and scope only. Use this just for permissions
35
36
  * whose grants have no row conditions beyond the scope: for a permission
36
37
  * with row conditions (e.g. `authorId = principal.id`) every member of the
37
- * scope may join. Leave those to the policies `permdock rls generate` writes.
38
+ * scope could join. Pass `catalog` to refuse those keys here;
39
+ * `better-supabase doctor` (BS214) refuses them from the catalog file.
38
40
  */
39
41
  readonly permdock?: PermdockTopicPolicy;
42
+ /**
43
+ * PermDock's `permissions.catalog.json`. With it, a `permdock` policy
44
+ * naming a permission with `rowConditions: true` throws.
45
+ */
46
+ readonly catalog?: PermdockCatalog;
40
47
  /** Let clients broadcast on the topic, not only receive. Defaults to `false`. */
41
48
  readonly send?: boolean;
42
49
  /** Also authorize presence. Defaults to `false`. */
@@ -71,13 +78,16 @@ interface Subscription extends Disposable, AsyncDisposable {
71
78
  readonly ready: Promise<void>;
72
79
  unsubscribe(): Promise<void>;
73
80
  }
74
- type RealtimeClient = Pick<SupabaseClient, "channel" | "removeChannel" | "realtime">;
81
+ type RealtimeClient = Pick<SupabaseClient, "channel" | "getChannels" | "removeChannel" | "realtime">;
75
82
  interface TriggerOptions<M extends AnyModels, T extends keyof M, P extends string> {
76
83
  /** Column that fills each placeholder. */
77
84
  readonly values: { readonly [K in TemplateParams<P>]: Extract<keyof Row<M, T>, string>; };
78
85
  /** Defaults to all three. */
79
86
  readonly events?: readonly ("insert" | "update" | "delete")[];
80
- /** Schema for the generated function. Defaults to `public`. */
87
+ /**
88
+ * Schema for the generated function. Defaults to `better_supabase`, which
89
+ * the Data API does not expose.
90
+ */
81
91
  readonly functionSchema?: string;
82
92
  }
83
93
  interface RowChange<R> {
@@ -96,7 +106,7 @@ interface Topic<P extends string, E extends EventSchemas> {
96
106
  /** `realtime.messages` policies for this topic. Idempotent. */
97
107
  sql(): string;
98
108
  /** A trigger that broadcasts row changes (`realtime.broadcast_changes`) to this topic. */
99
- triggerSql<M extends AnyModels, D, F extends AnyFunctions, X, T extends TableKey<M>>(sb: BetterSupabase<M, D, F, X>, table: T, options: TriggerOptions<M, T, P>): string;
109
+ triggerSql<M extends AnyModels, D, F extends AnyFunctions, X, T extends TableKey<M>>(betterSupabase: BetterSupabase<M, D, F, X>, table: T, options: TriggerOptions<M, T, P>): string;
100
110
  subscribe(client: RealtimeClient, values: TemplateValues<P>, handlers: TopicHandlers<E>, options?: SubscribeOptions): Subscription;
101
111
  /** Sends over HTTP, without joining the channel. */
102
112
  send<K extends TopicEvent<E>>(client: RealtimeClient, values: TemplateValues<P>, event: K, payload: TopicPayload<E, K>): AsyncResult<void>;
@@ -117,10 +127,10 @@ declare function defineTopic<const P extends string, const E extends EventSchema
117
127
  * Reads a `realtime.broadcast_changes` message as an app-cased row change.
118
128
  * Returns `null` for messages about other tables.
119
129
  */
120
- declare function rowChange<M extends AnyModels, D, F extends AnyFunctions, X, T extends TableKey<M>>(sb: BetterSupabase<M, D, F, X>, table: T, message: TopicMessage): RowChange<Row<M, T>> | null;
130
+ declare function rowChange<M extends AnyModels, D, F extends AnyFunctions, X, T extends TableKey<M>>(betterSupabase: BetterSupabase<M, D, F, X>, table: T, message: TopicMessage): RowChange<Row<M, T>> | null;
121
131
  //#endregion
122
132
  //#region src/realtime/live.d.ts
123
- /** What `liveQuery` needs from `sb`: metadata and the tables a spec reads. */
133
+ /** What `liveQuery` needs from `betterSupabase`: metadata and the tables a spec reads. */
124
134
  interface LiveSource {
125
135
  readonly meta: SchemaMeta;
126
136
  tablesOf(spec: QuerySpec): string[];
@@ -144,7 +154,7 @@ interface LiveSubscription extends Disposable, AsyncDisposable {
144
154
  }
145
155
  /**
146
156
  * A count from the server plus the spec to keep it live on the client:
147
- * `next.liveCount(spec)` makes one, `useLiveCount(seed)` reads it. `count`
157
+ * `bs.liveCount(spec)` makes one, `useLiveCount(seed)` reads it. `count`
148
158
  * is `null` when the server read failed; the client then fetches it.
149
159
  */
150
160
  interface LiveCountSeed<T extends string = string> {
@@ -160,11 +170,11 @@ declare function liveTopic(meta: SchemaMeta, table: string, tenant?: string): st
160
170
  * table, so any number of them can run at once.
161
171
  *
162
172
  * ```ts
163
- * using live = liveQuery(sb, supabase, spec, { onChange: () => refetch() });
173
+ * using live = liveQuery(betterSupabase, supabase, spec, { onChange: () => refetch() });
164
174
  * ```
165
175
  */
166
- declare function liveQuery(sb: LiveSource, client: RealtimeClient, spec: QuerySpec | readonly string[], options: LiveQueryOptions): LiveSubscription;
167
- /** What `liveCount` runs the spec with: a `db` from `sb.connect()` or `createBrowser()`. */
176
+ declare function liveQuery(betterSupabase: LiveSource, client: RealtimeClient, spec: QuerySpec | readonly string[], options: LiveQueryOptions): LiveSubscription;
177
+ /** What `liveCount` runs the spec with: a `db` from `betterSupabase.connect()` or `createClient()`. */
168
178
  interface CountRunner {
169
179
  $run(spec: QuerySpec): PromiseLike<Result<unknown>>;
170
180
  }
@@ -181,11 +191,11 @@ interface LiveCountOptions extends Omit<LiveQueryOptions, "onChange"> {
181
191
  * Responses that arrive out of order are dropped.
182
192
  *
183
193
  * ```ts
184
- * using live = liveCount(sb, supabase, db, sb.spec.notes.count(), {
194
+ * using live = liveCount(betterSupabase, supabase, db, betterSupabase.spec.notes.count(), {
185
195
  * onCount: (count) => render(count),
186
196
  * });
187
197
  * ```
188
198
  */
189
- declare function liveCount(sb: LiveSource, client: RealtimeClient, db: CountRunner, spec: QuerySpec<string, "count", number>, options: LiveCountOptions): LiveSubscription;
199
+ declare function liveCount(betterSupabase: LiveSource, client: RealtimeClient, db: CountRunner, spec: QuerySpec<string, "count", number>, options: LiveCountOptions): LiveSubscription;
190
200
  //#endregion
191
201
  export { defineTopic as C, TriggerOptions as S, TopicEvent as _, LiveSource as a, TopicOptions as b, liveQuery as c, RealtimeClient as d, RowChange as f, Topic as g, SubscriptionStatus as h, LiveQueryOptions as i, liveTopic as l, Subscription as m, LiveCountOptions as n, LiveSubscription as o, SubscribeOptions as p, LiveCountSeed as r, liveCount as s, CountRunner as t, EventSchemas as u, TopicHandlers as v, rowChange as w, TopicPayload as x, TopicMessage as y };