better-supabase 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/README.md +2 -0
  2. package/dist/channel-FQW-CnGR.d.ts +70 -0
  3. package/dist/cli/bin.js +1 -1
  4. package/dist/cli/codemod-DqV7612m.js +388 -0
  5. package/dist/cli/{config-D9R0o1LH.js → config-CPVpXP5I.js} +78 -8
  6. package/dist/cli/{config-SSKSTUl2.js → config-dtoekM0v.js} +1 -1
  7. package/dist/cli/{read-sets-CMa29t9a.js → diff-SjkO5rNr.js} +1 -220
  8. package/dist/cli/{dist-CFgGknmZ.js → dist-DBPdlPYK.js} +4 -4
  9. package/dist/cli/{dist-B1MvLv-M.js → dist-DreZ24Sz.js} +1 -1
  10. package/dist/cli/{dist-Du7VnEUG.js → dist-xn4wlboc.js} +112 -1
  11. package/dist/cli/{doctor-BOym3mbL.js → doctor-L7Z8mYTx.js} +531 -50
  12. package/dist/cli/{env-BZ0uy8XK.js → env-DmKv_G4A.js} +1 -1
  13. package/dist/cli/gen-BJc9vdyu.js +2 -0
  14. package/dist/cli/{gen-Ccn_p90L.js → gen-C2osRcr5.js} +283 -87
  15. package/dist/cli/index.d.ts +1 -1
  16. package/dist/cli/index.js +4 -4
  17. package/dist/cli/{init-ClnxRYBF.js → init-Saf5-R9T.js} +2 -2
  18. package/dist/cli/{introspect-DxqfkVIv.js → introspect--H7oGmcd.js} +2 -2
  19. package/dist/cli/{openapi-8WHjxiTJ.js → openapi-Cp5Fyta-.js} +1 -1
  20. package/dist/cli/read-sets-BfMQcKy8.js +289 -0
  21. package/dist/cli/{run-CP20LKk_.js → run-BKR2W_79.js} +16 -12
  22. package/dist/cli/{seed-5J6eu_Xg.js → seed-CUlqWh-8.js} +1 -1
  23. package/dist/cli/sql-DzPbfngx.js +348 -0
  24. package/dist/cli/{version-5mW_8sY1.js → version-BDJuO0UR.js} +1 -1
  25. package/dist/client/index.d.ts +1 -1
  26. package/dist/client/index.js +3 -10
  27. package/dist/clone-BfgmLU90.js +21 -0
  28. package/dist/{compiler-DB3aSFOA.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
  29. package/dist/config/index.d.ts +4 -4
  30. package/dist/config/index.js +4 -4
  31. package/dist/{config-CbvFmGD8.js → config-DreERlKo.js} +1 -1
  32. package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
  33. package/dist/{define-CRS2MRV3.d.ts → define-Bp_BZa5h.d.ts} +11 -7
  34. package/dist/{define-Bh0AqTik.js → define-cNKzqwL_.js} +202 -16
  35. package/dist/edge/index.d.ts +16 -5
  36. package/dist/edge/index.js +10 -7
  37. package/dist/{entitlements-Dm-FHpNH.d.ts → entitlements-DUxU4oHe.d.ts} +1 -1
  38. package/dist/env/index.d.ts +2 -2
  39. package/dist/env/index.js +34 -1
  40. package/dist/{errors-L0xK41y_.js → errors-DtwXa6b8.js} +1 -0
  41. package/dist/events/index.d.ts +3 -2
  42. package/dist/events/index.js +65 -12
  43. package/dist/events-aIO7vsIk.d.ts +230 -0
  44. package/dist/{result-CqEsbgYs.d.ts → executor-BunPOO-k.d.ts} +200 -1
  45. package/dist/{executor-CZ7ImRiD.d.ts → executor-Cghdstwu.d.ts} +1 -2
  46. package/dist/{executor-CVt6dD0B.js → executor-XycESQPH.js} +3 -3
  47. package/dist/hono/index.d.ts +17 -6
  48. package/dist/hono/index.js +40 -5
  49. package/dist/impersonation-VfaGc4Q_.js +41 -0
  50. package/dist/{index-BBdvo-5d.d.ts → index-BGdgBPdf.d.ts} +49 -6
  51. package/dist/{index-BhHEC04r.d.ts → index-C7f8cOBM.d.ts} +2 -2
  52. package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
  53. package/dist/{index-D5IfUIDh.d.ts → index-DOESKG27.d.ts} +33 -8
  54. package/dist/{index-DFmOpBGC.d.ts → index-DgILJRXj.d.ts} +35 -9
  55. package/dist/{index-RlLXd8dL.d.ts → index-DmXSxbLi.d.ts} +4 -4
  56. package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
  57. package/dist/{index-528Ugmyp.d.ts → index-SaTsEfGP.d.ts} +76 -8
  58. package/dist/{index-BAy4tsAP.d.ts → index-fag8VJZ2.d.ts} +3 -4
  59. package/dist/index.d.ts +30 -13
  60. package/dist/index.js +10 -9
  61. package/dist/jobs/index.d.ts +147 -101
  62. package/dist/jobs/index.js +744 -237
  63. package/dist/{json-schema-DP6V7cbr.js → json-schema-m1fiwKgN.js} +22 -1
  64. package/dist/kit-events-CTFDfSwb.js +60 -0
  65. package/dist/kit-transport-B82k1djG.d.ts +33 -0
  66. package/dist/kit-transport-DJlHQUBU.js +55 -0
  67. package/dist/{config-BpxDfnbg.js → kits-CGLLrNPd.js} +37 -9
  68. package/dist/list/index.d.ts +1 -1
  69. package/dist/{live-BHakSWWo.d.ts → live-D9W2M5aO.d.ts} +6 -5
  70. package/dist/mcp/index.d.ts +25 -8
  71. package/dist/mcp/index.js +23 -14
  72. package/dist/{mfa-C7MNQ1Ob.d.ts → mfa-CusDgPaD.d.ts} +5 -1
  73. package/dist/next/index.d.ts +31 -10
  74. package/dist/next/index.js +136 -27
  75. package/dist/notifications/index.d.ts +162 -0
  76. package/dist/notifications/index.js +264 -0
  77. package/dist/openapi/index.d.ts +1 -1
  78. package/dist/openapi/index.js +2 -2
  79. package/dist/orgs/index.d.ts +132 -0
  80. package/dist/orgs/index.js +245 -0
  81. package/dist/orpc/index.d.ts +28 -8
  82. package/dist/orpc/index.js +16 -3
  83. package/dist/otel/index.d.ts +21 -2
  84. package/dist/otel/index.js +50 -9
  85. package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
  86. package/dist/permdock-sql-DgOfeixi.js +51 -0
  87. package/dist/plugin-DwARMuQp.js +59 -0
  88. package/dist/{plugin-bNhvJwRh.d.ts → plugin-o144f322.d.ts} +17 -5
  89. package/dist/plugins/actor/index.d.ts +5 -2
  90. package/dist/plugins/actor/index.js +22 -8
  91. package/dist/plugins/rules/index.d.ts +2 -2
  92. package/dist/plugins/rules/index.js +15 -10
  93. package/dist/plugins/soft-delete/index.d.ts +3 -3
  94. package/dist/plugins/soft-delete/index.js +14 -5
  95. package/dist/plugins/tenant/index.d.ts +2 -2
  96. package/dist/plugins/tenant/index.js +38 -20
  97. package/dist/plugins/timestamps/index.d.ts +1 -1
  98. package/dist/plugins/timestamps/index.js +4 -3
  99. package/dist/plugins/validation/index.d.ts +2 -2
  100. package/dist/plugins/validation/index.js +26 -6
  101. package/dist/{pool-DFwEQwmN.d.ts → pool-C9voM4Kw.d.ts} +17 -6
  102. package/dist/postgres/index.d.ts +5 -5
  103. package/dist/postgres/index.js +20 -11
  104. package/dist/{postgrest-Bc-0Xw62.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
  105. package/dist/{problem-BihjPR0t.js → problem-BW1KhzSG.js} +2 -1
  106. package/dist/{problem-U8UEKccv.d.ts → problem-Blxe8S31.d.ts} +1 -1
  107. package/dist/query/index.d.ts +2 -2
  108. package/dist/query/index.js +3 -3
  109. package/dist/{query-BMMSWbI7.js → query-CCbo44lG.js} +2 -2
  110. package/dist/queue-BYUCjr59.d.ts +252 -0
  111. package/dist/react/index.d.ts +8 -7
  112. package/dist/react/index.js +4 -3
  113. package/dist/react/server.d.ts +14 -3
  114. package/dist/react/server.js +10 -2
  115. package/dist/react/session.d.ts +7 -1
  116. package/dist/react/session.js +9 -1
  117. package/dist/{react-CQHMaESR.js → react-C_s7kiNz.js} +103 -11
  118. package/dist/{read-set-FWK8qQ58.js → read-set-DQFt1N_Y.js} +22 -1
  119. package/dist/realtime/index.d.ts +3 -3
  120. package/dist/realtime/index.js +5 -4
  121. package/dist/{refresh-BwYkyNOQ.d.ts → refresh-BTrQ_ZTp.d.ts} +1 -1
  122. package/dist/{resolve-BAqQcwMW.d.ts → resolve-C15TJq77.d.ts} +11 -5
  123. package/dist/{resource-My_y-Syh.js → resource-CRxxwkQq.js} +22 -7
  124. package/dist/{resource-BJcaIfxi.d.ts → resource-D8iqWMr-.d.ts} +5 -5
  125. package/dist/respond-D2OQl62-.d.ts +371 -0
  126. package/dist/{result-Bj6yt33D.js → result-BKiliPY7.js} +1 -1
  127. package/dist/server/index.d.ts +14 -12
  128. package/dist/server/index.js +11 -17
  129. package/dist/{server-BA75B0mT.js → server-Csx2sRjg.js} +251 -34
  130. package/dist/{shared-DnoCdMDr.js → shared-D1dRUrAg.js} +16 -2
  131. package/dist/signers-Cc6r98SW.d.ts +162 -0
  132. package/dist/{simplify-Cqvfr9dj.js → simplify-_0oUKqCd.js} +4 -115
  133. package/dist/sql/index.d.ts +247 -10
  134. package/dist/sql/index.js +8590 -779
  135. package/dist/{sql-B34HU2DI.js → sql-Coh6n4cC.js} +9 -2
  136. package/dist/ssr/index.d.ts +4 -4
  137. package/dist/ssr/index.js +1 -2
  138. package/dist/{standard-BRyBGTtp.js → standard-D7r6dMUu.js} +6 -3
  139. package/dist/{standard-BsaCYQJc.d.ts → standard-J4YgMOLm.d.ts} +1 -1
  140. package/dist/{stats-C_YxA2r1.js → stats-Bqob3WqT.js} +4 -4
  141. package/dist/storage/index.d.ts +4 -4
  142. package/dist/storage/index.js +155 -26
  143. package/dist/support-DT-OhSxD.d.ts +88 -0
  144. package/dist/support-Dk6vdmEw.js +291 -0
  145. package/dist/support-view-Da5peKFg.d.ts +17 -0
  146. package/dist/support-view-g4YLBeqX.js +18 -0
  147. package/dist/{permdock-sql-C4KFzLc9.js → template-TxrGI5jR.js} +2 -31
  148. package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
  149. package/dist/{temporal-required-CBNHT6ni.js → temporal-required-CAt4Dc9Z.js} +2 -2
  150. package/dist/testing/index.d.ts +100 -10
  151. package/dist/testing/index.js +336 -8
  152. package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
  153. package/dist/user-change-CHFEUro1.js +49 -0
  154. package/dist/{webhooks-Bh_Jf_-l.js → verify-Ce49ETPz.js} +5 -5
  155. package/dist/{version-Bmf7_FzM.js → version-DM_Roffb.js} +1 -1
  156. package/dist/{view-B64o81wp.d.ts → view-BEf0diC5.d.ts} +8 -3
  157. package/dist/{mfa-byfjruR9.js → view-Bq64ki8-.js} +95 -38
  158. package/dist/webhooks/index.d.ts +135 -4
  159. package/dist/webhooks/index.js +588 -2
  160. package/dist/wire-D2dT86vi.js +35 -0
  161. package/package.json +16 -8
  162. package/schemas/config-v1.json +131 -1
  163. package/schemas/doctor-report-v1.json +10 -0
  164. package/skills/better-supabase/SKILL.md +9 -6
  165. package/skills/better-supabase/references/plugins.md +6 -3
  166. package/skills/better-supabase/references/troubleshooting.md +1 -0
  167. package/skills/better-supabase-api/SKILL.md +9 -2
  168. package/skills/better-supabase-api/references/adapters.md +11 -8
  169. package/skills/better-supabase-auth/SKILL.md +3 -0
  170. package/skills/better-supabase-auth/references/permdock.md +56 -11
  171. package/dist/cli/gen-D5cLCULx.js +0 -2
  172. package/dist/cli/sql-D4VbqmXi.js +0 -199
  173. package/dist/events-B21UOhvP.d.ts +0 -84
  174. package/dist/executor-B5IoUxLE.d.ts +0 -195
  175. package/dist/invalidate-CgxmRamI.js +0 -24
  176. package/dist/permdock-sql-DImJULY-.d.ts +0 -17
  177. package/dist/plugin-BqR6wKMB.js +0 -23
  178. package/dist/respond-DgIbdrt8.d.ts +0 -194
  179. package/dist/view-BrJI2LKp.js +0 -45
@@ -0,0 +1,348 @@
1
+ import { r as defineCliCommand } from "./command-CK14fP91.js";
2
+ import { r as writeIfChanged, t as display } from "./io-k41_z-jT.js";
3
+ import { i as plain, r as painter } from "./style-C0NFdJRL.js";
4
+ import { f as readSupabaseToml, p as schemaPaths, u as migrationCommand } from "./config-CPVpXP5I.js";
5
+ import { t as fileDiff } from "./diff-SjkO5rNr.js";
6
+ import { o as permdockSource, r as entitlementsMode, s as readPermdock, t as compiledReadSets } from "./read-sets-BfMQcKy8.js";
7
+ import { dirname, join, relative, resolve, sep } from "node:path";
8
+ import { existsSync } from "node:fs";
9
+ import { readFile, readdir } from "node:fs/promises";
10
+ import { SQL_MODULES, contractSignature, customContracts, kitFileVersion, kitLayout, moduleBody, renderKit, resolveModules, sameKitFile, upgradePlan } from "better-supabase/sql";
11
+ //#region src/cli/commands/sql.ts
12
+ const SQL_ARGS = {
13
+ action: {
14
+ type: "positional",
15
+ required: false,
16
+ description: "list (modules and whether they are installed), add <module...>, sync, upgrade, data (a migration with the rows a schema diff skips) or print <module>"
17
+ },
18
+ check: {
19
+ type: "boolean",
20
+ description: "With sync: fail when a file is stale. With upgrade: fail when a module is behind"
21
+ },
22
+ "tests-dir": {
23
+ type: "string",
24
+ description: "Where the pgtap module goes. Defaults to sql.testsDir",
25
+ valueHint: "dir"
26
+ },
27
+ "dry-run": {
28
+ type: "boolean",
29
+ description: "Show what would be written"
30
+ },
31
+ force: {
32
+ type: "boolean",
33
+ description: "Write tenant even though a permdock.config.ts is present"
34
+ }
35
+ };
36
+ const USAGE = "Run `better-supabase sql --help` for the actions.";
37
+ /** Modules that fill a claim PermDock's hook also writes (`memberships`). */
38
+ const PERMDOCK_OWNED = /* @__PURE__ */ new Set(["tenant"]);
39
+ /**
40
+ * PermDock's helpers for the `entitlements` module. When `names` resolve to
41
+ * that module, an invalid PermDock mode (no manifest, a scope it lacks, an
42
+ * unknown id type) throws instead of falling back to the tenant module.
43
+ * Without `names` (`sql list`) it only describes the modules, so it doesn't.
44
+ */
45
+ async function permdockFor(config, names) {
46
+ const mode = entitlementsMode(config, await readPermdock(config.root, config.permdock));
47
+ switch (mode.kind) {
48
+ case "tenant": return;
49
+ case "permdock": return mode.permdock;
50
+ case "invalid":
51
+ if (names === void 0 || !resolveModules(names, {}).some((module) => module.name === "entitlements")) return void 0;
52
+ throw new TypeError(mode.problem);
53
+ default: return mode;
54
+ }
55
+ }
56
+ async function layout(config, args, names) {
57
+ return kitLayout(config, args["tests-dir"], [], await permdockFor(config, names));
58
+ }
59
+ /** The layout, with `config.readSets` compiled when `names` includes `read-sets`. */
60
+ async function layoutFor(config, args, names) {
61
+ const permdock = await permdockFor(config, names);
62
+ const needsReadSets = resolveModules(names, { ...permdock ? { permdock } : {} }).some((module) => module.name === "read-sets");
63
+ return kitLayout(config, args["tests-dir"], needsReadSets ? await compiledReadSets(config) : [], permdock);
64
+ }
65
+ /**
66
+ * The migrations folder next to the `config.toml` found walking up from
67
+ * `sql.dir`, relative to the root; `supabase/migrations` without one. The
68
+ * walk stops where `sql.dir` and the root meet, so a `sql.dir` outside the
69
+ * project (a shared stack in a monorepo) finds that stack's folder.
70
+ */
71
+ function migrationsDir(config) {
72
+ const root = resolve(config.root);
73
+ let dir = resolve(root, config.sql.dir);
74
+ let stop = root;
75
+ while (dir !== stop && !dir.startsWith(stop + sep) && dirname(stop) !== stop) stop = dirname(stop);
76
+ for (;;) {
77
+ if (existsSync(join(dir, "config.toml"))) return relative(root, join(dir, "migrations")).replaceAll("\\", "/");
78
+ if (dir === stop || dirname(dir) === dir) break;
79
+ dir = dirname(dir);
80
+ }
81
+ return "supabase/migrations";
82
+ }
83
+ /** `YYYYMMDDHHMMSS` in UTC, the Supabase CLI's migration prefix. */
84
+ const migrationStamp = (now) => now.toISOString().replaceAll(/[-:T]/g, "").slice(0, 14);
85
+ /**
86
+ * A stamp for `now`, or one second after the newest migration when that one
87
+ * has the same stamp or a later one: the Supabase CLI keys migrations by
88
+ * stamp, and the data has to run after the schema.
89
+ */
90
+ function stampAfter(migrations, now) {
91
+ const newest = migrations.map((name) => /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})_/.exec(name)).filter((match) => match !== null).map((match) => Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]), Number(match[4]), Number(match[5]), Number(match[6]))).reduce((max, time) => Math.max(max, time), Number.NEGATIVE_INFINITY);
92
+ const at = Math.floor(now.getTime() / 1e3) * 1e3;
93
+ return migrationStamp(new Date(Math.max(at, newest + 1e3)));
94
+ }
95
+ async function write(config, args, names, kit) {
96
+ const lines = [];
97
+ const dryRun = args["dry-run"] === true;
98
+ let data = false;
99
+ for (const file of renderKit(names, kit)) {
100
+ const path = resolve(config.root, file.path);
101
+ const shown = display(config.root, file.path);
102
+ if (dryRun) {
103
+ lines.push(`Would write ${shown} (${file.module})`);
104
+ continue;
105
+ }
106
+ const wrote = await writeIfChanged(path, file.contents);
107
+ if (wrote && file.kind === "data") data = true;
108
+ lines.push(`${wrote ? "Wrote" : "Unchanged"} ${shown} (${file.module})`);
109
+ }
110
+ if (data) lines.push("", DATA_NEXT);
111
+ return lines;
112
+ }
113
+ const DATA_NEXT = "The better-supabase-data files hold rows and settings a schema diff skips: after the schema migration, run `better-supabase sql data`.";
114
+ const DATA_SUFFIX = "_better_supabase_kit_data.sql";
115
+ /**
116
+ * Writes the data files of `sql.kit` into one migration, meant to run after
117
+ * the schema migration that creates their tables. Every statement is
118
+ * idempotent, so the migration carries all of them, not only the changes.
119
+ */
120
+ async function dataMigration(config, args) {
121
+ if (config.sql.kit.length === 0) return {
122
+ code: 0,
123
+ output: "sql.kit is empty; nothing to write."
124
+ };
125
+ const files = renderKit(config.sql.kit, await layoutFor(config, args, config.sql.kit)).filter((file) => file.kind === "data");
126
+ const contents = `-- better-supabase sql data: the rows and settings of ${files.map((file) => file.module).join(", ")}, which a schema diff skips.\n\n${files.map((file) => file.contents.trim()).join("\n\n")}\n`;
127
+ const dir = migrationsDir(config);
128
+ const existing = await readdir(resolve(config.root, dir)).catch(() => []);
129
+ const latest = existing.toSorted().findLast((name) => name.endsWith(DATA_SUFFIX));
130
+ if (latest !== void 0 && await readFile(resolve(config.root, dir, latest), "utf8") === contents) return {
131
+ code: 0,
132
+ output: `${dir}/${latest} already has these rows; nothing to write.`
133
+ };
134
+ const path = `${dir}/${stampAfter(existing, /* @__PURE__ */ new Date())}${DATA_SUFFIX}`;
135
+ if (args["dry-run"] === true) return {
136
+ code: 0,
137
+ output: `Would write ${path}`
138
+ };
139
+ await writeIfChanged(resolve(config.root, path), contents);
140
+ return {
141
+ code: 0,
142
+ output: `Wrote ${path}`
143
+ };
144
+ }
145
+ /** A note when migra's `schema_paths` is set and misses kit files, which `supabase db diff` would then skip. */
146
+ async function unlistedKitFiles(config, names, kit) {
147
+ const order = await schemaPaths(config.root, await readSupabaseToml(config.root));
148
+ if (!order.configured) return [];
149
+ const unlisted = new Set(order.unlisted);
150
+ const listed = new Set(order.files.filter((path) => !unlisted.has(path)));
151
+ const dir = `${config.sql.dir.replace(/\/$/, "")}/`;
152
+ const missing = renderKit(names, kit).filter((file) => file.kind !== "data").map((file) => file.path).filter((path) => path.startsWith(dir) && !listed.has(path));
153
+ if (missing.length === 0) return [];
154
+ return [
155
+ "",
156
+ "supabase/config.toml sets [db.migrations] schema_paths, and no entry matches these files, so `supabase db diff` skips them.",
157
+ "Add them before the schemas that call their functions:",
158
+ ...missing.map((path) => ` "./${path.startsWith("supabase/") ? path.slice(9) : path}",`)
159
+ ];
160
+ }
161
+ /**
162
+ * Rewrites the kit files of modules behind the current version, and writes
163
+ * their upgrade steps (renames, backfills) as a migration that runs before
164
+ * the one the schema diff creates.
165
+ */
166
+ async function upgrade(config, args, paint) {
167
+ if (config.sql.kit.length === 0) return {
168
+ code: 0,
169
+ output: "sql.kit is empty; nothing to upgrade."
170
+ };
171
+ const kit = await layoutFor(config, args, config.sql.kit);
172
+ const files = renderKit(config.sql.kit, kit);
173
+ const installed = [];
174
+ const stale = [];
175
+ const diffs = [];
176
+ for (const file of files) {
177
+ if (file.kind === "data") continue;
178
+ const current = await readFile(resolve(config.root, file.path), "utf8").catch(() => void 0);
179
+ if (current === void 0) continue;
180
+ installed.push({
181
+ module: file.module,
182
+ version: kitFileVersion(current)?.version ?? 1
183
+ });
184
+ if (!sameKitFile(current, file.contents)) {
185
+ stale.push(file);
186
+ diffs.push(fileDiff(display(config.root, file.path), current, file.contents, paint));
187
+ }
188
+ }
189
+ const plan = upgradePlan(installed, kit);
190
+ const behind = plan.map((entry) => `${entry.module} is at version ${String(entry.from)}; the current version is ${String(entry.to)}`);
191
+ if (args.check === true) {
192
+ if (plan.length === 0 && stale.length === 0) return {
193
+ code: 0,
194
+ output: "SQL kit modules are at their current versions."
195
+ };
196
+ return {
197
+ code: 1,
198
+ output: diffs.join("\n\n"),
199
+ error: [
200
+ ...behind,
201
+ ...stale.length > 0 ? [`Out of date: ${stale.map((file) => display(config.root, file.path)).join(", ")}.`] : [],
202
+ "Run `better-supabase sql upgrade`."
203
+ ].join("\n")
204
+ };
205
+ }
206
+ if (plan.length === 0 && stale.length === 0) return {
207
+ code: 0,
208
+ output: "SQL kit modules are at their current versions."
209
+ };
210
+ const lines = [];
211
+ const steps = plan.flatMap((entry) => entry.steps.filter((step) => step.sql !== "").map((step) => `-- ${entry.module}: version ${String(step.from)} to ${String(step.from + 1)}. ${step.description}\n${step.sql}`));
212
+ const dryRun = args["dry-run"] === true;
213
+ if (steps.length > 0) {
214
+ const path = `${migrationsDir(config)}/${migrationStamp(/* @__PURE__ */ new Date())}_better_supabase_kit_upgrade.sql`;
215
+ const contents = `-- better-supabase sql upgrade: steps that run before the schema diff.\n\n${steps.join("\n\n")}\n`;
216
+ if (dryRun) lines.push(`Would write ${path}`);
217
+ else {
218
+ await writeIfChanged(resolve(config.root, path), contents);
219
+ lines.push(`Wrote ${path}`);
220
+ }
221
+ }
222
+ for (const entry of plan) lines.push(`${entry.module}: version ${String(entry.from)} to ${String(entry.to)}`, ...entry.steps.map((step) => ` ${step.description}`));
223
+ lines.push(...await write(config, args, config.sql.kit, kit), "", `Then create a migration: ${migrationCommand(await readSupabaseToml(config.root), "better_supabase_kit")}`);
224
+ return {
225
+ code: 0,
226
+ output: lines.join("\n")
227
+ };
228
+ }
229
+ async function runSql(config, args, paint = plain) {
230
+ const [action, ...names] = args._;
231
+ switch (action) {
232
+ case "list": {
233
+ const kit = await layout(config, args);
234
+ const files = new Map(renderKit(Object.keys(SQL_MODULES), kit).filter((file) => file.kind !== "data").map((file) => [file.module, file.path]));
235
+ return {
236
+ code: 0,
237
+ output: `${Object.values(SQL_MODULES).map((module) => {
238
+ const path = files.get(module.name);
239
+ const installed = path !== void 0 && existsSync(resolve(config.root, path));
240
+ const tracked = config.sql.kit.includes(module.name);
241
+ const mark = path === void 0 ? "◇" : installed ? tracked ? "●" : "○" : " ";
242
+ const requires = kit.permdock && module.permdockRequires ? module.permdockRequires : module.requires;
243
+ const needs = requires.length > 0 ? ` (needs ${requires.join(", ")})` : "";
244
+ return `${mark} ${module.name.padEnd(15)} ${module.description}${needs}`;
245
+ }).join("\n")}\n\n● installed and in sql.kit ○ installed, not in sql.kit ◇ custom mode (the app implements it)`
246
+ };
247
+ }
248
+ case "add": {
249
+ if (names.length === 0) return {
250
+ code: 2,
251
+ error: `Name at least one module.\n${USAGE}`
252
+ };
253
+ const unknown = names.filter((name) => !(name in SQL_MODULES));
254
+ if (unknown.length > 0) return {
255
+ code: 2,
256
+ error: `Unknown module ${unknown.join(", ")}. Available: ${Object.keys(SQL_MODULES).join(", ")}`
257
+ };
258
+ const project = await readPermdock(config.root, config.permdock);
259
+ const permdock = project ? permdockSource(project) : void 0;
260
+ const hookModules = names.filter((name) => PERMDOCK_OWNED.has(name));
261
+ if (permdock && hookModules.length > 0 && args.force !== true) return {
262
+ code: 1,
263
+ error: [
264
+ `${permdock} is present, so PermDock owns the access token hook and the memberships claim.`,
265
+ `${hookModules.join(" and ")} would add a second source for them. Use \`permdock supabase hook generate\` instead,`,
266
+ "or pass --force to write the modules anyway."
267
+ ].join("\n")
268
+ };
269
+ const kit = await layoutFor(config, args, names);
270
+ const lines = await write(config, args, names, kit);
271
+ const pulledIn = resolveModules(names, kit).map((module) => module.name).filter((name) => PERMDOCK_OWNED.has(name) && !names.includes(name));
272
+ if (permdock && pulledIn.length > 0) lines.push("", `${pulledIn.join(" and ")} came along as a dependency: its memberships table backs has_org_role() and has_entitlement().`, `PermDock's hook still owns the memberships claim, so don't call better_supabase.membership_claims from a hook.`, "List better_supabase.memberships as a PermDock membership source if both should agree.");
273
+ const untracked = resolveModules(names, kit).map((module) => module.name).filter((name) => !config.sql.kit.includes(name));
274
+ if (untracked.length > 0) lines.push("", `Add them to your config so \`sql sync --check\` keeps them current:`, ` sql: { kit: [${[...config.sql.kit, ...untracked].map((name) => `'${name}'`).join(", ")}] }`);
275
+ if (args["dry-run"] !== true) lines.push(...await unlistedKitFiles(config, names, kit));
276
+ lines.push("", `Then create a migration: ${migrationCommand(await readSupabaseToml(config.root), "better_supabase_kit")}`);
277
+ return {
278
+ code: 0,
279
+ output: lines.join("\n")
280
+ };
281
+ }
282
+ case "sync": {
283
+ if (config.sql.kit.length === 0) return {
284
+ code: 0,
285
+ output: "sql.kit is empty; nothing to sync."
286
+ };
287
+ if (args.check !== true) return {
288
+ code: 0,
289
+ output: (await write(config, args, config.sql.kit, await layoutFor(config, args, config.sql.kit))).join("\n")
290
+ };
291
+ const stale = [];
292
+ const diffs = [];
293
+ for (const file of renderKit(config.sql.kit, await layoutFor(config, args, config.sql.kit))) {
294
+ const current = await readFile(resolve(config.root, file.path), "utf8").catch(() => void 0);
295
+ if (!sameKitFile(current, file.contents)) {
296
+ const shown = display(config.root, file.path);
297
+ stale.push(shown);
298
+ diffs.push(fileDiff(shown, current, file.contents, paint));
299
+ }
300
+ }
301
+ return stale.length === 0 ? {
302
+ code: 0,
303
+ output: "SQL kit files are up to date."
304
+ } : {
305
+ code: 1,
306
+ output: diffs.join("\n\n"),
307
+ error: `Out of date: ${stale.join(", ")}. Run \`better-supabase sql sync\`.`
308
+ };
309
+ }
310
+ case "upgrade": return upgrade(config, args, paint);
311
+ case "data": return dataMigration(config, args);
312
+ case "print": {
313
+ const [name] = names;
314
+ const module = name ? SQL_MODULES[name] : void 0;
315
+ if (!module) return {
316
+ code: 2,
317
+ error: `Name one module: ${Object.keys(SQL_MODULES).join(", ")}`
318
+ };
319
+ const body = moduleBody(module.name, await layout(config, args, [module.name]));
320
+ if (body === void 0) return {
321
+ code: 0,
322
+ output: `-- kits.${module.name} is in custom mode: the app writes these functions.\n${customContracts([module.name], await layout(config, args, [module.name])).flatMap((contract) => contract.functions.map((fn) => `-- ${contract.schema}.${fn.name}(${contractSignature(fn, contract.idType)}) returns ${fn.returns.replaceAll("{id}", contract.idType)}`)).join("\n")}`
323
+ };
324
+ return {
325
+ code: 0,
326
+ output: body
327
+ };
328
+ }
329
+ case void 0: return {
330
+ code: 2,
331
+ error: `Name an action.\n${USAGE}`
332
+ };
333
+ default: return {
334
+ code: 2,
335
+ error: `Unknown sql action "${action}".\n${USAGE}`
336
+ };
337
+ }
338
+ }
339
+ const sqlCommand = defineCliCommand({
340
+ meta: {
341
+ name: "sql",
342
+ description: "Lists, adds, syncs, upgrades and prints SQL kit modules, and writes their data migration"
343
+ },
344
+ args: SQL_ARGS,
345
+ run: (args, { config, io }) => runSql(config, args, painter(io.color))
346
+ });
347
+ //#endregion
348
+ export { sqlCommand };
@@ -1,4 +1,4 @@
1
1
  //#region src/cli/version.ts
2
- const VERSION = "0.4.0";
2
+ const VERSION = "0.5.0";
3
3
  //#endregion
4
4
  export { VERSION as t };
@@ -1,2 +1,2 @@
1
- import { a as ClientOptions, i as ClientAuth, n as AuthUser, o as createClient, r as BetterClient, t as AuthSnapshot } from "../index-RlLXd8dL.js";
1
+ import { a as ClientOptions, i as ClientAuth, n as AuthUser, o as createClient, r as BetterClient, t as AuthSnapshot } from "../index-DmXSxbLi.js";
2
2
  export { AuthSnapshot, AuthUser, BetterClient, ClientAuth, ClientOptions, createClient };
@@ -1,6 +1,7 @@
1
1
  import { n as decodeJwtPayload } from "../base64-D2gsioCY.js";
2
+ import { r as userContext } from "../impersonation-VfaGc4Q_.js";
2
3
  import { EnvValidationError, parseEnv } from "../env/index.js";
3
- import { t as createQueries } from "../query-BMMSWbI7.js";
4
+ import { t as createQueries } from "../query-CCbo44lG.js";
4
5
  import { createBrowserClient } from "@supabase/ssr";
5
6
  import { createClient as createClient$1 } from "@supabase/supabase-js";
6
7
  //#region src/client/index.ts
@@ -37,15 +38,7 @@ function contextOf(snapshot) {
37
38
  },
38
39
  claims: { role: "anon" }
39
40
  };
40
- return {
41
- actor: {
42
- id: snapshot.user.id,
43
- kind: "user",
44
- ...snapshot.user.role ? { role: snapshot.user.role } : {},
45
- ...snapshot.user.email ? { email: snapshot.user.email } : {}
46
- },
47
- claims: snapshot.claims
48
- };
41
+ return userContext(snapshot.user, snapshot.claims);
49
42
  }
50
43
  function clientFor(options) {
51
44
  if (options.client) return options.client;
@@ -0,0 +1,21 @@
1
+ //#region src/core/clone.ts
2
+ function isPlainObject(value) {
3
+ if (typeof value !== "object" || value === null) return false;
4
+ const proto = Object.getPrototypeOf(value);
5
+ return proto === Object.prototype || proto === null;
6
+ }
7
+ /**
8
+ * Copies plain objects and arrays so hooks and listeners cannot change the
9
+ * caller's result (invariant 5). Temporal values are immutable and stay shared.
10
+ */
11
+ function cloneValue(value) {
12
+ if (Array.isArray(value)) return value.map(cloneValue);
13
+ if (isPlainObject(value)) {
14
+ const copy = {};
15
+ for (const [key, item] of Object.entries(value)) copy[key] = cloneValue(item);
16
+ return copy;
17
+ }
18
+ return value;
19
+ }
20
+ //#endregion
21
+ export { isPlainObject as n, cloneValue as t };
@@ -1,5 +1,5 @@
1
- import { g as Operation } from "./executor-B5IoUxLE.js";
2
- import { i as PostgrestPlan } from "./postgrest-Bc-0Xw62.js";
1
+ import { U as Operation } from "./executor-BunPOO-k.js";
2
+ import { i as PostgrestPlan } from "./postgrest-CruYKAvV.js";
3
3
  //#region src/core/compiler.d.ts
4
4
  /**
5
5
  * Turns an IR operation into something a backend runs. An `Executor` for a
@@ -1,4 +1,4 @@
1
- import { a as Casing } from "../types-DONv9SHN.js";
2
- import { $ as GeneratorMetadata, A as VectorSearchConfig, B as CatalogFunction, C as SoftDeleteConfig, D as TenantConfig, E as TableConfig, F as CatalogCheck, G as CatalogTable, H as CatalogGrant, I as CatalogColumn, J as ExtrasFunction, K as CatalogTrigger, L as CatalogColumnGrant, M as resolveConfig, N as resolveJsonSchema, O as TimestampsConfig, P as Catalog, Q as ForeignKeyAction, R as CatalogEnum, S as SeedConfig, T as SqlConfig, U as CatalogIndex, V as CatalogFunctionArg, W as CatalogPolicy, X as ExtrasHookFunction, Y as ExtrasHook, Z as ExtrasTable, _ as PluginFlagsConfig, a as ClaimsConfig, b as ResolvedConfig, c as EntitlementsConfig, d as Generator, et as Snapshot, f as GeneratorInput, g as PermdockPathsConfig, h as OpenApiConfig, i as CONFIG_SCHEMA_URL, j as defineConfig, k as VectorDistance, l as ExposeConfig, m as JsonTypeConfig, n as BetterSupabaseConfig, nt as SnapshotExtras, o as CodecsConfig, p as JsonSchemaSource, q as CatalogUnique, r as BucketConfig, s as DoctorConfig, t as ActorConfig, tt as SnapshotBucket, u as GeneratedFile, v as Privilege, w as SourceConfig, x as ResolvedExpose, y as RealtimeConfig, z as CatalogForeignKey } from "../config-C1CYcm6o.js";
3
- import { a as JsonSchemaGeneratorOptions, c as DEFAULT_CLAIMS, i as valibot, l as tenantClaimPaths, n as zod, o as buildJsonSchema, r as ValibotGeneratorOptions, s as jsonSchema, t as ZodGeneratorOptions } from "../index-Cf-D2jk1.js";
4
- export { type ActorConfig, type BetterSupabaseConfig, type BucketConfig, CONFIG_SCHEMA_URL, type Casing, type Catalog, type CatalogCheck, type CatalogColumn, type CatalogColumnGrant, type CatalogEnum, type CatalogForeignKey, type CatalogFunction, type CatalogFunctionArg, type CatalogGrant, type CatalogIndex, type CatalogPolicy, type CatalogTable, type CatalogTrigger, type CatalogUnique, type ClaimsConfig, type CodecsConfig, DEFAULT_CLAIMS, type DoctorConfig, type EntitlementsConfig, type ExposeConfig, type ExtrasFunction, type ExtrasHook, type ExtrasHookFunction, type ExtrasTable, type ForeignKeyAction, type GeneratedFile, type Generator, type GeneratorInput, type GeneratorMetadata, type JsonSchemaGeneratorOptions, type JsonSchemaSource, type JsonTypeConfig, type OpenApiConfig, type PermdockPathsConfig, type PluginFlagsConfig, type Privilege, type RealtimeConfig, type ResolvedConfig, type ResolvedExpose, type SeedConfig, type Snapshot, type SnapshotBucket, type SnapshotExtras, type SoftDeleteConfig, type SourceConfig, type SqlConfig, type TableConfig, type TenantConfig, type TimestampsConfig, type ValibotGeneratorOptions, type VectorDistance, type VectorSearchConfig, type ZodGeneratorOptions, buildJsonSchema, defineConfig, jsonSchema, resolveConfig, resolveJsonSchema, tenantClaimPaths, valibot, zod };
1
+ import { o as Casing } from "../types-Dnf_wRDz.js";
2
+ import { $ as GeneratorMetadata, A as VectorSearchConfig, B as CatalogFunction, C as SoftDeleteConfig, D as TenantConfig, E as TableConfig, F as CatalogCheck, G as CatalogTable, H as CatalogGrant, I as CatalogColumn, J as ExtrasFunction, K as CatalogTrigger, L as CatalogColumnGrant, M as resolveConfig, N as resolveJsonSchema, O as TimestampsConfig, P as Catalog, Q as ForeignKeyAction, R as CatalogEnum, S as SeedConfig, T as SqlConfig, U as CatalogIndex, V as CatalogFunctionArg, W as CatalogPolicy, X as ExtrasHookFunction, Y as ExtrasHook, Z as ExtrasTable, _ as PluginFlagsConfig, a as ClaimsConfig, at as DEFAULT_ACTIVE_TENANT, b as ResolvedConfig, c as EntitlementsConfig, ct as KitsConfig, d as Generator, et as Snapshot, f as GeneratorInput, g as PermdockPathsConfig, h as OpenApiConfig, i as CONFIG_SCHEMA_URL, it as ActiveTenantSource, j as defineConfig, k as VectorDistance, l as ExposeConfig, m as JsonTypeConfig, n as BetterSupabaseConfig, nt as SnapshotExtras, o as CodecsConfig, ot as KitMode, p as JsonSchemaSource, q as CatalogUnique, r as BucketConfig, rt as AccessKitConfig, s as DoctorConfig, st as KitModuleConfig, t as ActorConfig, tt as SnapshotBucket, u as GeneratedFile, v as Privilege, w as SourceConfig, x as ResolvedExpose, y as RealtimeConfig, z as CatalogForeignKey } from "../config-T_ejwyd3.js";
3
+ import { a as JsonSchemaGeneratorOptions, c as DEFAULT_CLAIMS, i as valibot, l as tenantClaimPaths, n as zod, o as buildJsonSchema, r as ValibotGeneratorOptions, s as jsonSchema, t as ZodGeneratorOptions } from "../index-Dxn2a5cw.js";
4
+ export { type AccessKitConfig, type ActiveTenantSource, type ActorConfig, type BetterSupabaseConfig, type BucketConfig, CONFIG_SCHEMA_URL, type Casing, type Catalog, type CatalogCheck, type CatalogColumn, type CatalogColumnGrant, type CatalogEnum, type CatalogForeignKey, type CatalogFunction, type CatalogFunctionArg, type CatalogGrant, type CatalogIndex, type CatalogPolicy, type CatalogTable, type CatalogTrigger, type CatalogUnique, type ClaimsConfig, type CodecsConfig, DEFAULT_ACTIVE_TENANT, DEFAULT_CLAIMS, type DoctorConfig, type EntitlementsConfig, type ExposeConfig, type ExtrasFunction, type ExtrasHook, type ExtrasHookFunction, type ExtrasTable, type ForeignKeyAction, type GeneratedFile, type Generator, type GeneratorInput, type GeneratorMetadata, type JsonSchemaGeneratorOptions, type JsonSchemaSource, type JsonTypeConfig, type KitMode, type KitModuleConfig, type KitsConfig, type OpenApiConfig, type PermdockPathsConfig, type PluginFlagsConfig, type Privilege, type RealtimeConfig, type ResolvedConfig, type ResolvedExpose, type SeedConfig, type Snapshot, type SnapshotBucket, type SnapshotExtras, type SoftDeleteConfig, type SourceConfig, type SqlConfig, type TableConfig, type TenantConfig, type TimestampsConfig, type ValibotGeneratorOptions, type VectorDistance, type VectorSearchConfig, type ZodGeneratorOptions, buildJsonSchema, defineConfig, jsonSchema, resolveConfig, resolveJsonSchema, tenantClaimPaths, valibot, zod };
@@ -1,5 +1,5 @@
1
1
  import { i as tenantClaimPaths, t as DEFAULT_CLAIMS } from "../claims-BAf6c7is.js";
2
- import { i as resolveJsonSchema, n as defineConfig, r as resolveConfig, t as CONFIG_SCHEMA_URL } from "../config-BpxDfnbg.js";
3
- import { n as jsonSchema, t as buildJsonSchema } from "../json-schema-DP6V7cbr.js";
4
- import { n as valibot, t as zod } from "../config-CbvFmGD8.js";
5
- export { CONFIG_SCHEMA_URL, DEFAULT_CLAIMS, buildJsonSchema, defineConfig, jsonSchema, resolveConfig, resolveJsonSchema, tenantClaimPaths, valibot, zod };
2
+ import { a as resolveConfig, i as defineConfig, o as resolveJsonSchema, r as CONFIG_SCHEMA_URL, t as DEFAULT_ACTIVE_TENANT } from "../kits-CGLLrNPd.js";
3
+ import { n as jsonSchema, t as buildJsonSchema } from "../json-schema-m1fiwKgN.js";
4
+ import { n as valibot, t as zod } from "../config-DreERlKo.js";
5
+ export { CONFIG_SCHEMA_URL, DEFAULT_ACTIVE_TENANT, DEFAULT_CLAIMS, buildJsonSchema, defineConfig, jsonSchema, resolveConfig, resolveJsonSchema, tenantClaimPaths, valibot, zod };
@@ -1,4 +1,4 @@
1
- import { a as parseImport, c as schemaName, d as variantsFor, i as fieldsFor, l as siblingPath, o as propertyKey, r as HEADER, s as rowType, u as tableEntries } from "./json-schema-DP6V7cbr.js";
1
+ import { a as parseImport, c as schemaName, d as variantsFor, i as fieldsFor, l as siblingPath, o as propertyKey, r as HEADER, s as rowType, u as tableEntries } from "./json-schema-m1fiwKgN.js";
2
2
  //#region src/generators/valibot.ts
3
3
  function scalar$1(kind) {
4
4
  switch (kind.kind) {