@devdogsuga/devtools 0.1.25 → 0.1.27

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 (271) hide show
  1. package/README.md +72 -11
  2. package/bin/devtools.mjs +4 -4
  3. package/dist/cli-CZKkNHK5.js +6619 -0
  4. package/dist/failure-log-BBPGejQv.js +256 -0
  5. package/dist/launch.js +267 -281
  6. package/dist/local-env-D8HC2JK_.js +351 -0
  7. package/dist/local-env-DrBCXqD8.js +2 -0
  8. package/dist/menu-CQBsQRgH.js +1684 -0
  9. package/dist/peer-redirect-hooks.js +15 -0
  10. package/dist/peers-RBplwovA.js +229 -0
  11. package/dist/root-C2OmZDwD.js +94 -0
  12. package/dist/stack-Bx-55oi_.js +2 -0
  13. package/dist/stack-BynCgRVS.js +181 -0
  14. package/dist/supabase-project-0knM0mqd.js +426 -0
  15. package/dist/telemetry-C4foD8MD.js +249 -0
  16. package/dist/telemetry.js +2 -148
  17. package/package.json +10 -29
  18. package/bin/devtools-ci-bare.mjs +0 -48
  19. package/bin/devtools-ci.mjs +0 -31
  20. package/dist/args.d.ts +0 -26
  21. package/dist/args.js +0 -78
  22. package/dist/bws/bw.d.ts +0 -18
  23. package/dist/bws/bw.js +0 -70
  24. package/dist/bws/client.d.ts +0 -43
  25. package/dist/bws/client.js +0 -363
  26. package/dist/bws/environments.d.ts +0 -96
  27. package/dist/bws/environments.js +0 -111
  28. package/dist/bws/pace.d.ts +0 -25
  29. package/dist/bws/pace.js +0 -43
  30. package/dist/bws/retry.d.ts +0 -12
  31. package/dist/bws/retry.js +0 -39
  32. package/dist/bws/store.d.ts +0 -1
  33. package/dist/bws/store.js +0 -32
  34. package/dist/bws/token.d.ts +0 -31
  35. package/dist/bws/token.js +0 -88
  36. package/dist/bws/vault.d.ts +0 -29
  37. package/dist/bws/vault.js +0 -200
  38. package/dist/catalog.d.ts +0 -57
  39. package/dist/catalog.js +0 -57
  40. package/dist/cf/build.d.ts +0 -13
  41. package/dist/cf/build.js +0 -65
  42. package/dist/cf/commands.d.ts +0 -1
  43. package/dist/cf/commands.js +0 -172
  44. package/dist/cf/local-env.d.ts +0 -58
  45. package/dist/cf/local-env.js +0 -177
  46. package/dist/ci.d.ts +0 -1
  47. package/dist/ci.js +0 -328
  48. package/dist/cli.d.ts +0 -11
  49. package/dist/cli.js +0 -1094
  50. package/dist/commands.d.ts +0 -261
  51. package/dist/commands.js +0 -1318
  52. package/dist/completions.d.ts +0 -4
  53. package/dist/completions.js +0 -110
  54. package/dist/cron/commands.d.ts +0 -47
  55. package/dist/cron/commands.js +0 -372
  56. package/dist/cron/discovery.d.ts +0 -55
  57. package/dist/cron/discovery.js +0 -85
  58. package/dist/cron/schema.d.ts +0 -45
  59. package/dist/cron/schema.js +0 -35
  60. package/dist/db/config-push.d.ts +0 -8
  61. package/dist/db/config-push.js +0 -11
  62. package/dist/db/connection.d.ts +0 -79
  63. package/dist/db/connection.js +0 -114
  64. package/dist/db/exec.d.ts +0 -1
  65. package/dist/db/exec.js +0 -11
  66. package/dist/db/generate-types.d.ts +0 -1
  67. package/dist/db/generate-types.js +0 -4
  68. package/dist/db/generated-env.d.ts +0 -69
  69. package/dist/db/generated-env.js +0 -113
  70. package/dist/db/introspect.d.ts +0 -1
  71. package/dist/db/introspect.js +0 -122
  72. package/dist/db/local-env.d.ts +0 -65
  73. package/dist/db/local-env.js +0 -23
  74. package/dist/db/new-migration.d.ts +0 -8
  75. package/dist/db/new-migration.js +0 -69
  76. package/dist/db/pooler.d.ts +0 -23
  77. package/dist/db/pooler.js +0 -55
  78. package/dist/db/run.d.ts +0 -68
  79. package/dist/db/run.js +0 -156
  80. package/dist/db/seed-buckets.d.ts +0 -2
  81. package/dist/db/seed-buckets.js +0 -4
  82. package/dist/db/seed-production.d.ts +0 -8
  83. package/dist/db/seed-production.js +0 -79
  84. package/dist/db/seed-roles.d.ts +0 -7
  85. package/dist/db/seed-roles.js +0 -33
  86. package/dist/db/session-refresh.d.ts +0 -43
  87. package/dist/db/session-refresh.js +0 -40
  88. package/dist/deploy/migrations.d.ts +0 -20
  89. package/dist/deploy/migrations.js +0 -68
  90. package/dist/deploy/orphans.d.ts +0 -20
  91. package/dist/deploy/orphans.js +0 -177
  92. package/dist/deploy/preflight.d.ts +0 -53
  93. package/dist/deploy/preflight.js +0 -189
  94. package/dist/deploy/report.d.ts +0 -26
  95. package/dist/deploy/report.js +0 -70
  96. package/dist/deploy/require-planner.d.ts +0 -2
  97. package/dist/deploy/require-planner.js +0 -60
  98. package/dist/deploy/require-token.d.ts +0 -15
  99. package/dist/deploy/require-token.js +0 -78
  100. package/dist/deploy/secrets-file.d.ts +0 -17
  101. package/dist/deploy/secrets-file.js +0 -166
  102. package/dist/deploy/write-env.d.ts +0 -40
  103. package/dist/deploy/write-env.js +0 -395
  104. package/dist/docs/index-pages.d.ts +0 -38
  105. package/dist/docs/index-pages.js +0 -186
  106. package/dist/emails/commands.d.ts +0 -15
  107. package/dist/emails/commands.js +0 -130
  108. package/dist/emails/email-types.d.ts +0 -13
  109. package/dist/emails/email-types.js +0 -1
  110. package/dist/emails/fixtures.d.ts +0 -20
  111. package/dist/emails/fixtures.js +0 -20
  112. package/dist/env/audit.d.ts +0 -206
  113. package/dist/env/audit.js +0 -436
  114. package/dist/env/cloudflare.d.ts +0 -21
  115. package/dist/env/cloudflare.js +0 -61
  116. package/dist/env/commands.d.ts +0 -99
  117. package/dist/env/commands.js +0 -613
  118. package/dist/env/discovery.d.ts +0 -15
  119. package/dist/env/discovery.js +0 -239
  120. package/dist/env/document.d.ts +0 -119
  121. package/dist/env/document.js +0 -323
  122. package/dist/env/example.d.ts +0 -68
  123. package/dist/env/example.js +0 -605
  124. package/dist/env/selection.d.ts +0 -105
  125. package/dist/env/selection.js +0 -228
  126. package/dist/env-entry.d.ts +0 -49
  127. package/dist/env-entry.js +0 -204
  128. package/dist/environment-doctor.d.ts +0 -45
  129. package/dist/environment-doctor.js +0 -429
  130. package/dist/environment.d.ts +0 -92
  131. package/dist/environment.js +0 -185
  132. package/dist/fingerprint.d.ts +0 -10
  133. package/dist/fingerprint.js +0 -18
  134. package/dist/gen/campus-map.d.ts +0 -1
  135. package/dist/gen/campus-map.js +0 -473
  136. package/dist/gen/commands.d.ts +0 -1
  137. package/dist/gen/commands.js +0 -25
  138. package/dist/gen/hypno.d.ts +0 -1
  139. package/dist/gen/hypno.js +0 -134
  140. package/dist/gh/client.d.ts +0 -80
  141. package/dist/gh/client.js +0 -259
  142. package/dist/gh/environments.d.ts +0 -83
  143. package/dist/gh/environments.js +0 -178
  144. package/dist/gh/rulesets/actors.d.ts +0 -14
  145. package/dist/gh/rulesets/actors.js +0 -69
  146. package/dist/gh/rulesets/api.d.ts +0 -15
  147. package/dist/gh/rulesets/api.js +0 -104
  148. package/dist/gh/rulesets/commands.d.ts +0 -3
  149. package/dist/gh/rulesets/commands.js +0 -203
  150. package/dist/gh/rulesets/desired.d.ts +0 -112
  151. package/dist/gh/rulesets/desired.js +0 -271
  152. package/dist/gh/rulesets/diff.d.ts +0 -65
  153. package/dist/gh/rulesets/diff.js +0 -170
  154. package/dist/gh/rulesets/types.d.ts +0 -76
  155. package/dist/gh/rulesets/types.js +0 -13
  156. package/dist/gh/settings/api.d.ts +0 -40
  157. package/dist/gh/settings/api.js +0 -180
  158. package/dist/gh/settings/commands.d.ts +0 -1
  159. package/dist/gh/settings/commands.js +0 -254
  160. package/dist/gh/settings/desired.d.ts +0 -53
  161. package/dist/gh/settings/desired.js +0 -43
  162. package/dist/gh/settings/diff.d.ts +0 -57
  163. package/dist/gh/settings/diff.js +0 -161
  164. package/dist/gh/settings/types.d.ts +0 -92
  165. package/dist/gh/settings/types.js +0 -11
  166. package/dist/gh/settings/workflows.d.ts +0 -41
  167. package/dist/gh/settings/workflows.js +0 -128
  168. package/dist/grantRoot.d.ts +0 -42
  169. package/dist/grantRoot.js +0 -89
  170. package/dist/help.d.ts +0 -18
  171. package/dist/help.js +0 -169
  172. package/dist/images/commands.d.ts +0 -32
  173. package/dist/images/commands.js +0 -231
  174. package/dist/images/events.d.ts +0 -47
  175. package/dist/images/events.js +0 -116
  176. package/dist/images/graphics.d.ts +0 -70
  177. package/dist/images/graphics.js +0 -234
  178. package/dist/images/open-graph-types.d.ts +0 -75
  179. package/dist/images/open-graph-types.js +0 -1
  180. package/dist/images/prompts.d.ts +0 -34
  181. package/dist/images/prompts.js +0 -100
  182. package/dist/images/render.d.ts +0 -21
  183. package/dist/images/render.js +0 -61
  184. package/dist/images/select.d.ts +0 -64
  185. package/dist/images/select.js +0 -96
  186. package/dist/instance.d.ts +0 -69
  187. package/dist/instance.js +0 -102
  188. package/dist/invocation.d.ts +0 -49
  189. package/dist/invocation.js +0 -78
  190. package/dist/launch-ci.d.ts +0 -1
  191. package/dist/launch-ci.js +0 -90
  192. package/dist/launch.d.ts +0 -18
  193. package/dist/menu.d.ts +0 -43
  194. package/dist/menu.js +0 -300
  195. package/dist/moderation.d.ts +0 -70
  196. package/dist/moderation.js +0 -128
  197. package/dist/oauth/browser.d.ts +0 -1
  198. package/dist/oauth/browser.js +0 -13
  199. package/dist/oauth/config.d.ts +0 -24
  200. package/dist/oauth/config.js +0 -24
  201. package/dist/oauth/connect-url.d.ts +0 -17
  202. package/dist/oauth/connect-url.js +0 -23
  203. package/dist/oauth/db.d.ts +0 -37
  204. package/dist/oauth/db.js +0 -108
  205. package/dist/oauth/device.d.ts +0 -93
  206. package/dist/oauth/device.js +0 -198
  207. package/dist/oauth/discovery.d.ts +0 -28
  208. package/dist/oauth/discovery.js +0 -54
  209. package/dist/oauth/env-file.d.ts +0 -6
  210. package/dist/oauth/env-file.js +0 -35
  211. package/dist/oauth/exchange.d.ts +0 -43
  212. package/dist/oauth/exchange.js +0 -93
  213. package/dist/oauth/label.d.ts +0 -1
  214. package/dist/oauth/label.js +0 -25
  215. package/dist/oauth/loopback.d.ts +0 -52
  216. package/dist/oauth/loopback.js +0 -168
  217. package/dist/oauth/pkce.d.ts +0 -6
  218. package/dist/oauth/pkce.js +0 -34
  219. package/dist/oauth/target.d.ts +0 -54
  220. package/dist/oauth/target.js +0 -102
  221. package/dist/oauth/transport.d.ts +0 -41
  222. package/dist/oauth/transport.js +0 -32
  223. package/dist/oauth/wizard.d.ts +0 -40
  224. package/dist/oauth/wizard.js +0 -578
  225. package/dist/persona.d.ts +0 -18
  226. package/dist/persona.js +0 -221
  227. package/dist/pick.d.ts +0 -6
  228. package/dist/pick.js +0 -64
  229. package/dist/pipes.d.ts +0 -1
  230. package/dist/pipes.js +0 -25
  231. package/dist/planner/checks.d.ts +0 -26
  232. package/dist/planner/checks.js +0 -104
  233. package/dist/planner/commands.d.ts +0 -10
  234. package/dist/planner/commands.js +0 -367
  235. package/dist/planner/db.d.ts +0 -15
  236. package/dist/planner/db.js +0 -33
  237. package/dist/planner/role.d.ts +0 -33
  238. package/dist/planner/role.js +0 -68
  239. package/dist/repo/peer-redirect-hooks.d.ts +0 -16
  240. package/dist/repo/peer-redirect-hooks.js +0 -10
  241. package/dist/repo/peer-redirect.d.ts +0 -4
  242. package/dist/repo/peer-redirect.js +0 -26
  243. package/dist/repo/peers.d.ts +0 -39
  244. package/dist/repo/peers.js +0 -139
  245. package/dist/repo/resolve.d.ts +0 -41
  246. package/dist/repo/resolve.js +0 -152
  247. package/dist/repo/root.d.ts +0 -23
  248. package/dist/repo/root.js +0 -91
  249. package/dist/repo/source.d.ts +0 -14
  250. package/dist/repo/source.js +0 -54
  251. package/dist/repo/supabase-project.d.ts +0 -60
  252. package/dist/repo/supabase-project.js +0 -124
  253. package/dist/repo/tsx-loader.d.ts +0 -6
  254. package/dist/repo/tsx-loader.js +0 -41
  255. package/dist/run/pick.d.ts +0 -90
  256. package/dist/run/pick.js +0 -664
  257. package/dist/setup.d.ts +0 -1
  258. package/dist/setup.js +0 -280
  259. package/dist/stack.d.ts +0 -48
  260. package/dist/stack.js +0 -403
  261. package/dist/telemetry.d.ts +0 -59
  262. package/dist/tier.d.ts +0 -40
  263. package/dist/tier.js +0 -96
  264. package/dist/ui.d.ts +0 -40
  265. package/dist/ui.js +0 -71
  266. package/dist/version.d.ts +0 -7
  267. package/dist/version.js +0 -17
  268. package/dist/workers.d.ts +0 -5
  269. package/dist/workers.js +0 -33
  270. package/dist/workflows/commands.d.ts +0 -54
  271. package/dist/workflows/commands.js +0 -763
package/dist/persona.js DELETED
@@ -1,221 +0,0 @@
1
- /**
2
- * `persona <member|moderator>` / `persona --clean` — throwaway accounts for
3
- * a development database.
4
- *
5
- * These replace the seeded `member@devdogs.test` / `author@devdogs.test` /
6
- * `moderator@devdogs.test` personas that used to come from
7
- * `supabase/seed/development/02_moderation.sql`. That file — and the rest of
8
- * `supabase/seed/development/` — is going away in the app repo, so a fresh
9
- * `db reset` no longer leaves a signed-in account to switch to; this is what
10
- * creates one on demand instead.
11
- *
12
- * Every account this creates lives at `<kind>-<8 hex>@persona.test`: a
13
- * domain that cannot collide with a real UGA address, and — not
14
- * coincidentally — the same reason it can exist here at all. `.createUser`
15
- * goes through GoTrue's ADMIN API, which inserts the row directly rather
16
- * than running the app's `before_user_created` Auth Hook (the one that
17
- * requires a uga.edu address on the sign-up/OTP paths a real member takes).
18
- * The admin API was never wired to that hook, so a `@persona.test` address
19
- * sails through here uncontested.
20
- *
21
- * `--clean` deletes every account this command created — matched on BOTH the
22
- * `@persona.test` domain AND a `user_metadata.createdBy` marker, never the
23
- * domain alone. A shared development database can carry `@persona.test`
24
- * accounts this command did not create — CI's own fixtures, most likely, or
25
- * a `moderation check` run whose `withTemporaryModerator` teardown never
26
- * reached its `finally` because the process was killed. The marker is what
27
- * keeps `--clean` from reaching past accounts it is not responsible for.
28
- */
29
- import { randomUUID } from "node:crypto";
30
- import { confirm, log, note, select } from "@clack/prompts";
31
- import { adminClient, resolveInstance } from "./instance.js";
32
- import { grantModerator } from "./moderation.js";
33
- import { explain, unwrap } from "./ui.js";
34
- export const PERSONA_KINDS = ["member", "moderator"];
35
- export function isPersonaKind(value) {
36
- return PERSONA_KINDS.includes(value);
37
- }
38
- /** Every account this command creates carries this in `user_metadata`, and
39
- * `--clean` refuses to delete anything without it — see the header. */
40
- const CREATED_BY = "devtools persona";
41
- function personaEmail(kind) {
42
- return `${kind}-${randomUUID().slice(0, 8)}@persona.test`;
43
- }
44
- /** Nobody needs to remember this — it is printed once, for whoever asked. */
45
- function randomPassword() {
46
- return randomUUID() + randomUUID();
47
- }
48
- /**
49
- * Refuses outside development — a staging or production database has real
50
- * members on it, and this command exists to populate one that does not.
51
- * Exported so `cli.ts`'s `moderation check` (also development-only now, for
52
- * the same reason) shares the exact refusal wording.
53
- */
54
- export function refuseUnlessDevelopment(connection, label) {
55
- if (connection.tier === "development")
56
- return false;
57
- process.stderr.write(`${label}: only runs against a development database — this session ` +
58
- `targets ${connection.tier}. Relaunch with --tier development:local ` +
59
- "or development:remote.\n");
60
- return true;
61
- }
62
- /**
63
- * Creates the account and its profile — `userId` and `preferredName` are the
64
- * only two columns `platform.profile` requires (see the migration; every
65
- * other column is nullable or defaulted), which is why this needs nothing
66
- * from `02_moderation.sql`'s richer INSERT beyond the shape of the two rows
67
- * it wrote. Grants the Moderator role too, for `kind === "moderator"`.
68
- */
69
- async function createPersona(instance, kind) {
70
- const admin = adminClient(instance);
71
- const email = personaEmail(kind);
72
- const password = randomPassword();
73
- const preferredName = kind === "moderator" ? "Devtools Moderator" : "Devtools Member";
74
- const { data, error } = await admin.auth.admin.createUser({
75
- email,
76
- password,
77
- email_confirm: true,
78
- user_metadata: { createdBy: CREATED_BY },
79
- });
80
- if (error || !data.user) {
81
- throw new Error(`Could not create the account: ${error?.message}`);
82
- }
83
- const userId = data.user.id;
84
- const { error: profileErr } = await admin
85
- .from("profile")
86
- .insert({ userId, preferredName });
87
- if (profileErr) {
88
- throw new Error(`Could not create a profile: ${profileErr.message}`);
89
- }
90
- if (kind === "moderator")
91
- await grantModerator(admin, userId);
92
- return { email, password, userId, preferredName };
93
- }
94
- /** Any OTHER profile on this database, for a moderator persona to report —
95
- * `null` when this is the only account around, which the caller reports
96
- * rather than guessing at a subject that does not exist. */
97
- async function findOtherProfile(instance, excludeUserId) {
98
- const { data, error } = await adminClient(instance)
99
- .from("profile")
100
- .select("userId, preferredName")
101
- .neq("userId", excludeUserId)
102
- .limit(1)
103
- .maybeSingle();
104
- if (error)
105
- throw new Error(`Could not read platform.profile: ${error.message}`);
106
- return data;
107
- }
108
- /**
109
- * Files one open report, mirroring the "one open report" `02_moderation.sql`
110
- * used to seed — inserted directly rather than through
111
- * `platform.file_report()`, the same reasoning that seed's own comment gave:
112
- * neither a seed nor this command has a session to attribute the RPC call to.
113
- */
114
- async function fileSampleReport(instance, reporterId, subjectId, subjectName) {
115
- const admin = adminClient(instance);
116
- const { data: app, error: appErr } = await admin
117
- .from("apps")
118
- .select("id")
119
- .eq("slug", "platform")
120
- .single();
121
- if (appErr || !app) {
122
- throw new Error(`Could not find the platform app row: ${appErr?.message}`);
123
- }
124
- const { error } = await admin.from("reports").insert({
125
- appId: app.id,
126
- reporterUserId: reporterId,
127
- reportedUserId: subjectId,
128
- contentType: "profile",
129
- contentRef: subjectId,
130
- contentSnapshot: subjectName,
131
- description: "Filed by `pnpm devtools persona` so the moderation queue has something in it.",
132
- reason: "spam",
133
- });
134
- if (error) {
135
- throw new Error(`Could not file the sample report: ${error.message}`);
136
- }
137
- }
138
- async function runClean(instance) {
139
- const admin = adminClient(instance);
140
- const { data, error } = await admin.auth.admin.listUsers();
141
- if (error)
142
- throw new Error(`Could not list accounts: ${error.message}`);
143
- // Both conditions — see the header on why the domain alone is not enough.
144
- const targets = data.users.filter((u) => u.email?.endsWith("@persona.test") &&
145
- u.user_metadata?.createdBy === CREATED_BY);
146
- if (targets.length === 0) {
147
- log.info("No `pnpm devtools persona`-created accounts to clean up.");
148
- return;
149
- }
150
- // Deleting the auth user cascades to its profile, its Moderator role grant
151
- // (if any) and any reports it filed or was reported in — every FK a
152
- // persona account touches is `on delete cascade` — so there is nothing
153
- // else to clean up per account.
154
- for (const user of targets) {
155
- await admin.auth.admin.deleteUser(user.id);
156
- log.success(`Deleted ${user.email}.`);
157
- }
158
- log.info(`Deleted ${targets.length} account${targets.length === 1 ? "" : "s"}.`);
159
- }
160
- export async function runPersona(argv) {
161
- const clean = argv.includes("--clean");
162
- const positional = argv.find((arg) => !arg.startsWith("--"));
163
- if (clean && positional) {
164
- explain("Two answers to one question.", "--clean deletes every account this command made; a kind creates a new one.", ["pnpm devtools persona member", "pnpm devtools persona --clean"]);
165
- process.exitCode = 1;
166
- return;
167
- }
168
- const resolved = await resolveInstance({ label: "devtools persona" });
169
- if (!resolved) {
170
- process.exitCode = 1;
171
- return;
172
- }
173
- const { connection, instance } = resolved;
174
- if (refuseUnlessDevelopment(connection, "devtools persona")) {
175
- process.exitCode = 1;
176
- return;
177
- }
178
- if (clean) {
179
- await runClean(instance);
180
- return;
181
- }
182
- let kind;
183
- if (positional) {
184
- if (!isPersonaKind(positional)) {
185
- explain(`"${positional}" is not a persona kind.`, "", [
186
- "pnpm devtools persona member",
187
- "pnpm devtools persona moderator",
188
- ]);
189
- process.exitCode = 1;
190
- return;
191
- }
192
- kind = positional;
193
- }
194
- else {
195
- kind = unwrap(await select({
196
- message: "Which kind of persona?",
197
- options: PERSONA_KINDS.map((value) => ({ value, label: value })),
198
- }));
199
- }
200
- const persona = await createPersona(instance, kind);
201
- note(`Email: ${persona.email}\nPassword: ${persona.password}`, `${kind} persona created`);
202
- const fileReport = unwrap(await confirm({
203
- message: "File a sample report against a member profile, so the moderation queue isn't empty?",
204
- initialValue: kind === "moderator",
205
- }));
206
- if (!fileReport)
207
- return;
208
- // A member persona reports itself — there is no separate reporter account
209
- // on offer here, and nothing downstream cares who filed it, only that the
210
- // queue has one open report to practice resolving. A moderator persona
211
- // reports whatever OTHER profile already exists.
212
- const subject = kind === "member"
213
- ? { userId: persona.userId, preferredName: persona.preferredName }
214
- : await findOtherProfile(instance, persona.userId);
215
- if (!subject) {
216
- log.warn("No other account to report yet — run `pnpm devtools persona member` first.");
217
- return;
218
- }
219
- await fileSampleReport(instance, persona.userId, subject.userId, subject.preferredName);
220
- log.success("Filed a sample report. Resolve it from /console/moderation.");
221
- }
package/dist/pick.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import type { VaultTarget } from "@devdogsuga/env";
2
- /**
3
- * `null` means the failure has already been explained; set an exit code and
4
- * stop. It is not the same as a cancel, which exits the process outright.
5
- */
6
- export declare function resolveVaultTarget(given: string | undefined, message: string): Promise<VaultTarget | null>;
package/dist/pick.js DELETED
@@ -1,64 +0,0 @@
1
- /**
2
- * Choosing a target when the command line did not name one.
3
- *
4
- * `--target` is optional, and when it is absent this asks rather than guessing.
5
- * There is no safe default to fall back to, because the targets differ
6
- * precisely in how much damage picking the wrong one does.
7
- *
8
- * Three properties the prompt is built around:
9
- *
10
- * * Production is never the highlighted option. The list is ordered least-
11
- * to most-dangerous (that order lives in the target table), so a reflexive
12
- * Enter selects `preflight`.
13
- * * It refuses to prompt where nobody can answer. A prompt on a
14
- * non-interactive stdin hangs until the job times out, which reads as a
15
- * broken tool rather than a missing argument.
16
- * * `development` is refused by name, not by silence. It is a real target
17
- * with a file, and it is the one target with no Bitwarden project, so
18
- * `pull`/`push`/`audit` cannot do anything with it. Letting it fall into
19
- * "not a target" would say something false; the message says which fact
20
- * rules it out.
21
- */
22
- import { select } from "@clack/prompts";
23
- import { loadEnv } from "./repo/peers.js";
24
- import { noVaultProjectHints, NoVaultProjectError, } from "./bws/environments.js";
25
- import { explain, unwrap } from "./ui.js";
26
- /** Short enough to sit beside the name; the specs' summaries are paragraphs. */
27
- const VAULT_HINTS = {
28
- preflight: "read-only credentials for the pre-promotion checks",
29
- staging: "the everyday one — safe to overwrite",
30
- production: "⚠️ the live values",
31
- };
32
- /**
33
- * `null` means the failure has already been explained; set an exit code and
34
- * stop. It is not the same as a cancel, which exits the process outright.
35
- */
36
- export async function resolveVaultTarget(given, message) {
37
- const { ENV_TARGETS, VAULT_TARGETS, isEnvTarget, isVaultTarget } = await loadEnv();
38
- if (given !== undefined) {
39
- if (isVaultTarget(given))
40
- return given;
41
- if (isEnvTarget(given)) {
42
- explain(new NoVaultProjectError(given).message, "", noVaultProjectHints());
43
- return null;
44
- }
45
- explain(`"${given}" is not a target.`, "", [
46
- `Try one of: ${ENV_TARGETS.join(", ")}`,
47
- `Only ${VAULT_TARGETS.join(", ")} have a Bitwarden project.`,
48
- ]);
49
- return null;
50
- }
51
- if (!process.stdin.isTTY) {
52
- explain("No target given, and there is nobody here to ask.", "", [
53
- `Pass --target: ${VAULT_TARGETS.join(" | ")}`,
54
- ]);
55
- return null;
56
- }
57
- return unwrap(await select({
58
- message,
59
- options: VAULT_TARGETS.map((value) => ({
60
- value,
61
- hint: VAULT_HINTS[value],
62
- })),
63
- }));
64
- }
package/dist/pipes.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare function ignoreClosedPipes(): void;
package/dist/pipes.js DELETED
@@ -1,25 +0,0 @@
1
- /**
2
- * Keeps a closed stdout/stderr from crashing devtools.
3
- *
4
- * When whatever reads devtools' output goes away first (`devtools … | head`,
5
- * or a wrapper that stopped listening), the next write fails with EPIPE. The
6
- * stream emits that as an `'error'` event nobody handles, so Node throws it as
7
- * an uncaught exception and Sentry files it as a fatal. Nobody is left to read
8
- * the output, so dropping it is the right outcome. Any other stream error is
9
- * rethrown and crashes as it did before.
10
- *
11
- * Idempotent, because `launch`, `launchCi` and `ci.ts`'s `main` can all run in
12
- * one process.
13
- */
14
- let installed = false;
15
- export function ignoreClosedPipes() {
16
- if (installed)
17
- return;
18
- installed = true;
19
- for (const stream of [process.stdout, process.stderr]) {
20
- stream.on("error", (err) => {
21
- if (err.code !== "EPIPE")
22
- throw err;
23
- });
24
- }
25
- }
@@ -1,26 +0,0 @@
1
- import type { PlannerDb } from "./db.js";
2
- /** Constants, so `PlannerDb.run` never sees a composed string. */
3
- export declare const CHECK_IDENTITY = "select current_user as who";
4
- /**
5
- * `platform.profile` because it exists in every tier and holds real member
6
- * data in production: the exact rows §3.5 refuses to put behind `main`.
7
- */
8
- export declare const CHECK_OVERREACH = "select * from platform.profile limit 1";
9
- export declare const CHECK_MIGRATIONS = "select count(*)::int as n from supabase_migrations.schema_migrations";
10
- export interface PlannerVerdict {
11
- ok: boolean;
12
- /** One line per check, in the order run; the report either way. */
13
- lines: string[];
14
- /** Set when `ok` is false: what to go and fix. */
15
- problem?: string;
16
- }
17
- /**
18
- * Runs the three checks over an OPEN connection; never closes it.
19
- *
20
- * Any error from check 2 counts as "cannot read": the interesting distinction
21
- * is readable versus not, and a role that cannot even enumerate the schema
22
- * (the validated state) throws just as loudly as one denied on the table.
23
- */
24
- export declare function checkPlanner(db: PlannerDb): Promise<PlannerVerdict>;
25
- /** Postgres 3F000 (invalid_schema_name), with a text fallback. */
26
- export declare function isMissingSchema(error: unknown): boolean;
@@ -1,104 +0,0 @@
1
- /**
2
- * The three questions that decide whether a connection IS the planner:
3
- *
4
- * 1. Who did Postgres authenticate? (`current_user`)
5
- * 2. Can it read an application schema? (it must not)
6
- * 3. Can it read the migrations table? (it must, the dry run is next)
7
- *
8
- * Shared between `deploy require-planner` (which turns a bad answer into a
9
- * red CI job) and `planner create` / `planner reset-password` (which run the
10
- * same checks over a fresh connection before calling the credential done),
11
- * so "validated working" means the same thing in both places.
12
- *
13
- * ⚠️ Check 2 exists because nothing else can catch the mistake it looks for.
14
- * The preflight `DB_URL` is a value somebody composes by hand, the registry
15
- * cannot verify it, and a full-access connection string pasted there works
16
- * identically: the dry run succeeds, `env push` uploads it, `env audit` sees
17
- * no drift. Reading a `platform.*` table and EXPECTING the permission error
18
- * is the only observable difference between the right credential and the
19
- * wrong one.
20
- */
21
- import { PLANNER_ROLE } from "./role.js";
22
- /** Constants, so `PlannerDb.run` never sees a composed string. */
23
- export const CHECK_IDENTITY = "select current_user as who";
24
- /**
25
- * `platform.profile` because it exists in every tier and holds real member
26
- * data in production: the exact rows §3.5 refuses to put behind `main`.
27
- */
28
- export const CHECK_OVERREACH = "select * from platform.profile limit 1";
29
- export const CHECK_MIGRATIONS = "select count(*)::int as n from supabase_migrations.schema_migrations";
30
- /**
31
- * Runs the three checks over an OPEN connection; never closes it.
32
- *
33
- * Any error from check 2 counts as "cannot read": the interesting distinction
34
- * is readable versus not, and a role that cannot even enumerate the schema
35
- * (the validated state) throws just as loudly as one denied on the table.
36
- */
37
- export async function checkPlanner(db) {
38
- const lines = [];
39
- const [identity] = await db.run(CHECK_IDENTITY);
40
- const who = typeof identity?.who === "string" ? identity.who : "";
41
- if (who !== PLANNER_ROLE) {
42
- return {
43
- ok: false,
44
- lines,
45
- problem: `the connection authenticated as "${who}", not ${PLANNER_ROLE} — ` +
46
- "this is a full-privilege connection string where the planner role " +
47
- "belongs.",
48
- };
49
- }
50
- lines.push(`authenticated as ${PLANNER_ROLE}`);
51
- let overreach = false;
52
- try {
53
- await db.run(CHECK_OVERREACH);
54
- overreach = true;
55
- }
56
- catch {
57
- lines.push("cannot read platform.* (permission denied, as it must be)");
58
- }
59
- if (overreach) {
60
- return {
61
- ok: false,
62
- lines,
63
- problem: `${PLANNER_ROLE} can read platform.profile. The role has grants ` +
64
- "beyond the validated pair — drop and re-create it, then re-push " +
65
- "the preflight target.",
66
- };
67
- }
68
- try {
69
- const [row] = await db.run(CHECK_MIGRATIONS);
70
- lines.push(`can read supabase_migrations.schema_migrations (${String(row?.n)} rows)`);
71
- }
72
- catch (error) {
73
- // Two causes share this symptom, and the fix for one is a GRANT while
74
- // the fix for the other is initializing the database's migration
75
- // history, so the message has to say which. 3F000 is
76
- // invalid_schema_name; the text match covers drivers that drop the code.
77
- if (isMissingSchema(error)) {
78
- return {
79
- ok: false,
80
- lines,
81
- problem: "the supabase_migrations schema does not exist on this database. " +
82
- "The Supabase CLI creates it the first time it records migration " +
83
- "history there (`supabase db push`, or `supabase migration " +
84
- "repair` when baselining an existing database) — until then " +
85
- "there is nothing for the planner to read and nothing for the " +
86
- "dry run to plan against. This is not a grants problem.",
87
- };
88
- }
89
- return {
90
- ok: false,
91
- lines,
92
- problem: `${PLANNER_ROLE} cannot read supabase_migrations.schema_migrations, ` +
93
- "so the migration dry run would fail right after this. Re-apply the " +
94
- "two grants (see planner/role.ts).",
95
- };
96
- }
97
- return { ok: true, lines };
98
- }
99
- /** Postgres 3F000 (invalid_schema_name), with a text fallback. */
100
- export function isMissingSchema(error) {
101
- const e = error;
102
- return (e.code === "3F000" ||
103
- /schema "?supabase_migrations"? does not exist/i.test(e.message ?? ""));
104
- }
@@ -1,10 +0,0 @@
1
- import { type Connect } from "./db.js";
2
- export interface PlannerOptions {
3
- /** Privileged connection override for the bootstrap case. */
4
- dbUrl?: string;
5
- connect?: Connect;
6
- }
7
- export declare function runPlannerStatus(options?: PlannerOptions): Promise<void>;
8
- export declare function runPlannerCreate(options?: PlannerOptions): Promise<void>;
9
- export declare function runPlannerDrop(options?: PlannerOptions): Promise<void>;
10
- export declare function runPlannerResetPassword(options?: PlannerOptions): Promise<void>;