@devdogsuga/devtools 0.1.26 → 0.1.28

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
@@ -1,130 +0,0 @@
1
- /** `pnpm devtools emails [template…] [--format html,text] [--out dir]`. */
2
- import { mkdir, writeFile } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
- import { dirname, resolve } from "node:path";
5
- import { isTTY, log, multiselect, note, text as askText } from "@clack/prompts";
6
- import { positionals } from "../args.js";
7
- import { explain, explainError, unwrap, UsageError } from "../ui.js";
8
- import { EMAIL_FIXTURES } from "./fixtures.js";
9
- import { loadEmail } from "../repo/source.js";
10
- export const EMAIL_FORMATS = ["html", "text"];
11
- export const EMAIL_NAMES = Object.keys(EMAIL_FIXTURES);
12
- function flagValue(argv, flag) {
13
- const index = argv.indexOf(flag);
14
- if (index === -1)
15
- return undefined;
16
- const value = argv[index + 1];
17
- return value && !value.startsWith("--") ? value : undefined;
18
- }
19
- function expandHome(value) {
20
- return value === "~" || value.startsWith("~/")
21
- ? resolve(homedir(), value.slice(2))
22
- : value;
23
- }
24
- export function parseEmailArgs(argv, cwd) {
25
- const requested = positionals(argv);
26
- const unknown = requested.filter((name) => name !== "*" && !EMAIL_NAMES.includes(name));
27
- if (unknown.length) {
28
- return new Error(`No email template called ${unknown.join(", ")}. Try ${EMAIL_NAMES.join(", ")}, or *.`);
29
- }
30
- const rawFormats = (flagValue(argv, "--format") ?? "html")
31
- .split(",")
32
- .map((value) => value.trim())
33
- .filter(Boolean);
34
- const badFormats = rawFormats.filter((value) => !EMAIL_FORMATS.includes(value));
35
- if (badFormats.length)
36
- return new Error(`Unknown format ${badFormats.join(", ")}. Try html or text.`);
37
- const names = requested.includes("*")
38
- ? [...EMAIL_NAMES]
39
- : requested;
40
- return {
41
- names,
42
- formats: [...new Set(rawFormats)],
43
- out: resolve(cwd, expandHome(flagValue(argv, "--out") ?? "email-previews")),
44
- noOutput: argv.includes("--dry-run") || argv.includes("--no-output"),
45
- };
46
- }
47
- async function interactive(options) {
48
- if (options.names.length)
49
- return options;
50
- if (!isTTY(process.stdout)) {
51
- throw new UsageError("No terminal to choose templates. Name one, or pass * for all.");
52
- }
53
- const names = unwrap(await multiselect({
54
- message: "Which emails?",
55
- options: EMAIL_NAMES.map((name) => ({ value: name, label: name })),
56
- initialValues: [...EMAIL_NAMES],
57
- required: true,
58
- }));
59
- const formats = unwrap(await multiselect({
60
- message: "Which formats?",
61
- options: [
62
- { value: "html", label: "HTML", hint: "open in a browser" },
63
- { value: "text", label: "Plain text", hint: "the email alternative" },
64
- ],
65
- initialValues: ["html"],
66
- required: true,
67
- }));
68
- const entered = unwrap(await askText({
69
- message: "Where should these go?",
70
- placeholder: "./email-previews",
71
- defaultValue: "./email-previews",
72
- })).trim();
73
- return {
74
- ...options,
75
- names,
76
- formats,
77
- out: resolve(process.cwd(), expandHome(entered || "email-previews")),
78
- };
79
- }
80
- export function destination(out, name, format) {
81
- return resolve(out, `${name}.${format === "text" ? "txt" : "html"}`);
82
- }
83
- export async function generateEmails(options) {
84
- // Dynamic so every unrelated devtools command can still run before the
85
- // generated email package has been built on a fresh checkout.
86
- const { render } = await loadEmail();
87
- const written = [];
88
- for (const name of options.names) {
89
- const email = render(name, EMAIL_FIXTURES[name]);
90
- for (const format of options.formats) {
91
- const file = destination(options.out, name, format);
92
- await mkdir(dirname(file), { recursive: true });
93
- await writeFile(file, format === "html" ? email.html : email.text);
94
- written.push(file);
95
- }
96
- }
97
- return written;
98
- }
99
- export async function runEmails(argv) {
100
- const parsed = parseEmailArgs(argv, process.cwd());
101
- if (parsed instanceof Error) {
102
- explain("Could not read that.", parsed.message, [
103
- "pnpm devtools emails",
104
- "pnpm devtools emails '*' --format html,text --out ~/emails",
105
- ]);
106
- process.exitCode = 1;
107
- return;
108
- }
109
- try {
110
- const options = await interactive(parsed);
111
- if (options.noOutput) {
112
- const { render } = await loadEmail();
113
- note(options.names
114
- .map((name) => `${name}\n ${render(name, EMAIL_FIXTURES[name]).subject}\n ${options.formats.map((f) => destination(options.out, name, f)).join("\n ")}`)
115
- .join("\n"), `${options.names.length} email template${options.names.length === 1 ? "" : "s"}`);
116
- return;
117
- }
118
- const written = await generateEmails(options);
119
- for (const file of written)
120
- log.success(file);
121
- log.info(`${written.length} preview file${written.length === 1 ? "" : "s"} written.`);
122
- }
123
- catch (err) {
124
- explainError("Could not render those emails.", err, [
125
- "Build the email package with `pnpm --filter @devdogsuga/email build`.",
126
- "Then try `pnpm devtools emails '*' --out ~/emails`.",
127
- ]);
128
- process.exitCode = 1;
129
- }
130
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Type-only stand-in for `@devdogsuga/email`'s generated `Templates` map.
3
- *
4
- * `@devdogsuga/email` is `"private": true` and never published — it stays
5
- * in DevDogsUGA and devtools resolves it FROM the repo at runtime
6
- * (`repo/source.ts`'s `loadEmail()`). Backstage has no copy to pull the
7
- * real generated type from, so `fixtures.ts`'s `satisfies` check is
8
- * loosened to "every template's fixture is a flat string-prop object"
9
- * instead of the real per-template exact prop shape. A prop typo here is
10
- * caught at runtime (the real `render()` throws on a missing required
11
- * prop), not at Backstage's typecheck time.
12
- */
13
- export type EmailTemplates = Record<string, Record<string, string>>;
@@ -1 +0,0 @@
1
- export {};
@@ -1,20 +0,0 @@
1
- /**
2
- * Credible, non-sensitive values for local previews.
3
- *
4
- * `satisfies` is the maintenance mechanism: a new template or prop cannot be
5
- * shipped without deciding what its preview should say.
6
- */
7
- export declare const EMAIL_FIXTURES: {
8
- JoinRequest: {
9
- leadName: string;
10
- applicantName: string;
11
- teamName: string;
12
- reviewUrl: string;
13
- };
14
- TeamInvite: {
15
- inviteeName: string;
16
- teamName: string;
17
- leadName: string;
18
- acceptUrl: string;
19
- };
20
- };
@@ -1,20 +0,0 @@
1
- /**
2
- * Credible, non-sensitive values for local previews.
3
- *
4
- * `satisfies` is the maintenance mechanism: a new template or prop cannot be
5
- * shipped without deciding what its preview should say.
6
- */
7
- export const EMAIL_FIXTURES = {
8
- JoinRequest: {
9
- leadName: "Jordan",
10
- applicantName: "Avery",
11
- teamName: "Byte Bulldogs",
12
- reviewUrl: "https://devdogsuga.org/teams/requests",
13
- },
14
- TeamInvite: {
15
- inviteeName: "Avery",
16
- teamName: "Byte Bulldogs",
17
- leadName: "Jordan",
18
- acceptUrl: "https://devdogsuga.org/teams/requests",
19
- },
20
- };
@@ -1,206 +0,0 @@
1
- /**
2
- * Four-way drift detection: local `.env`, Bitwarden, GitHub, Cloudflare.
3
- *
4
- * Each store answers a different question, and only one of them answers it
5
- * completely:
6
- *
7
- * | Store | Exposes | So it can be checked for |
8
- * |--------------------|-------------------|--------------------------|
9
- * | local .env | names AND values | value drift |
10
- * | Bitwarden | names AND values | value drift (the truth) |
11
- * | GitHub *secrets* | names + updatedAt | presence, staleness |
12
- * | GitHub *variables* | names AND values | value drift |
13
- * | Cloudflare | names only | presence, orphans |
14
- *
15
- * That asymmetry is the whole design. A GitHub *secret* is write-only, so a
16
- * clean audit of one does not mean "the values match". It means "nothing
17
- * detectable is wrong", and the report says which is which rather than implying
18
- * the stronger claim.
19
- *
20
- * Timestamps are what rescue that row from being presence-only. Bitwarden
21
- * reports a `revisionDate` per secret and `gh secret list` reports `updatedAt`,
22
- * so "was GitHub updated after Bitwarden last changed?" IS answerable. That is
23
- * the failure this design actually has: a credential rotated in Bitwarden and
24
- * never propagated, which a name-only check calls healthy right up until the old
25
- * one is revoked.
26
- *
27
- * ⚠️ GitHub *variables* are the one downstream store that needs none of that
28
- * reasoning: `gh variable list` returns the value, so the public per-environment
29
- * keys are compared exactly the way the local file is. Staleness is deliberately
30
- * NOT applied to them: a timestamp is a proxy for a comparison that could not
31
- * be made, and using the proxy where the real answer is available would report a
32
- * variable re-pushed with an identical value as drift. The security plan's §3.6
33
- * limitation ("names only… a changed value is undetectable") now holds for the
34
- * secret row alone.
35
- *
36
- * ⚠️ One scope sits outside that table altogether: the repository's OWN
37
- * variables. Push writes environment-scoped values only, and an environment
38
- * variable SHADOWS a repository one of the same name, so a repository-level
39
- * copy of a managed key is read by nothing, drifts forever, and becomes the
40
- * live value on the day somebody deletes the environment copy. It is checked
41
- * here for the same reason §3.6's orphans are: nothing manages it, so nothing
42
- * else in this system would ever mention it.
43
- */
44
- export type Severity = "error" | "warning" | "info";
45
- export interface Finding {
46
- key: string;
47
- severity: Severity;
48
- /** Where the problem is, for grouping. */
49
- store: "local" | "github" | "cloudflare";
50
- summary: string;
51
- }
52
- export interface BwsEntry {
53
- value: string;
54
- /** ISO 8601. Absent when the CLI did not report one. */
55
- revisionDate?: string;
56
- }
57
- export interface GithubEntry {
58
- /** Which GitHub environment this copy sits in. */
59
- environment: string;
60
- name: string;
61
- /** ISO 8601. */
62
- updatedAt?: string;
63
- }
64
- /**
65
- * A GitHub environment *variable*: the same shape plus the thing that makes
66
- * variables worth having, a readable value.
67
- */
68
- export interface GithubVariableEntry extends GithubEntry {
69
- value: string;
70
- }
71
- export interface AuditInput {
72
- /** Active assignments in the local `.env`. */
73
- local: Map<string, string>;
74
- /** Keys present in the local `.env` but commented out. */
75
- localCommented?: Set<string>;
76
- /** The source of truth. */
77
- bws: Map<string, BwsEntry>;
78
- /**
79
- * Every GitHub copy found, across every environment this project feeds.
80
- *
81
- * A list rather than a map keyed by name, because one Bitwarden project can
82
- * feed two GitHub environments and the interesting case is a key appearing in
83
- * BOTH, which a name-keyed map would quietly collapse to one.
84
- */
85
- github: GithubEntry[];
86
- /**
87
- * Every GitHub environment *variable* found, across the same environments.
88
- *
89
- * Separate from `github` rather than merged with a discriminator, because
90
- * GitHub genuinely allows one name to exist as both a secret and a variable
91
- * in one environment and resolves the ambiguity by never telling you. See
92
- * `deploy/write-env.ts`, which refuses that case outright. Two lists can
93
- * represent it; one list keyed by name cannot, and would silently pick a
94
- * winner.
95
- */
96
- githubVariables?: GithubVariableEntry[];
97
- /**
98
- * Keys that belong in the variable store rather than the secret store, from
99
- * `variableKeys()` in the registry.
100
- *
101
- * Drives two things at once: which store a key is compared against, and the
102
- * pair of misplacement errors. Absent, every key is treated as a secret,
103
- * which is the pre-variables behaviour.
104
- */
105
- variables?: ReadonlySet<string>;
106
- /**
107
- * Where a key is supposed to live: the PRIMARY environment, the one whose
108
- * absence is an error. `null` means "nowhere in this environment", which is
109
- * ordinary rather than wrong.
110
- */
111
- route: (key: string) => string | null;
112
- /**
113
- * Whether a copy found in some OTHER environment is legitimate.
114
- *
115
- * Separate from `route` because the two questions stopped having the same
116
- * answer when `production-apply` became a superset of `production`: an
117
- * ordinary production key is now pushed to both, so "not where `route` says"
118
- * no longer means "misplaced". Folding them back together reports every
119
- * correctly-pushed copy as a stray to delete, and a reviewer who deletes 46
120
- * of those learns to skim the stray finding, which is the one finding that
121
- * catches an apply-tier credential sitting in the unreviewed environment.
122
- *
123
- * Defaults to the old behaviour (`only where route says`), so a caller that
124
- * has not thought about fan-out gets the strict answer rather than a
125
- * permissive one.
126
- */
127
- accepted?: (key: string, environment: string) => boolean;
128
- /** Worker name → secret names on it. Values are unreadable. */
129
- cloudflare?: Map<string, Set<string>>;
130
- /** Keys that legitimately live outside Bitwarden: the non-secrets. */
131
- ignore?: ReadonlySet<string>;
132
- /**
133
- * Credentials that must never be stored remotely at all.
134
- *
135
- * Distinct from `ignore`, and the opposite of it: an ignored key is expected
136
- * somewhere else, whereas one of these appearing in ANY remote store is a
137
- * finding. Being in the local `.env` and nowhere else is the correct state,
138
- * so the ordinary "not in Bitwarden" warning must not fire for them.
139
- */
140
- neverStore?: ReadonlySet<string>;
141
- /**
142
- * Credentials signed at deploy time, whose only copy is on the deploy target.
143
- *
144
- * The third category, and it sits between the other two rather than beside
145
- * them: unlike `ignore` it is a secret, and unlike `neverStore` there is one
146
- * remote store it BELONGS in. A key like this is signed at deploy time and
147
- * written straight to the Worker, so being on Cloudflare and nowhere else is
148
- * exactly right. `SANDBOX_PROXY_TOKEN` is still declared this way in
149
- * `apps/sandbox/env.ts` — nothing mints it any more, since the deploy that
150
- * did was removed with the sandbox integration it authenticated, but the
151
- * declaration is what keeps this category from reading "on production-
152
- * sandbox, not in Bitwarden" the day someone does fill it in by hand.
153
- *
154
- * Without this set the Cloudflare pass below reports a key like that as an
155
- * orphan, which the plan doc's §3.6 prune path then offers to delete.
156
- * Marking it the other obvious way, as `never-store`, inverts the error into
157
- * "must NEVER be a Worker secret" and is just as wrong in the opposite
158
- * direction.
159
- */
160
- minted?: ReadonlySet<string>;
161
- /**
162
- * Every key the registry knows. When provided, a local key outside it is
163
- * reported as UNDECLARED: its own category, not folded into drift, because
164
- * the fix is different: drift wants a push or a pull, an undeclared key
165
- * wants a `define()` in the owning manifest (or the line removed). It is
166
- * also why `env push` skipped it, and the audit should say so rather
167
- * than let "not in Bitwarden" imply pushing would help.
168
- */
169
- declared?: ReadonlySet<string>;
170
- /**
171
- * The repository's OWN variables, the scope with no environment, or the fact
172
- * that they could not be listed.
173
- *
174
- * A union rather than a list, because three states have to stay apart and a
175
- * list can only hold two of them: nobody looked (absent: the pre-existing
176
- * behaviour, no claim either way), looked and saw these names
177
- * (`readable`), and TRIED TO LOOK AND COULD NOT (`readable: false`). Listing
178
- * repository variables can fail where listing environment ones succeeds, so
179
- * the third state is ordinary rather than exotic, and collapsing it into an
180
- * empty list is precisely the bug this check was added to catch, one scope
181
- * up: a report that says "checked, nothing found" about a check that never
182
- * ran.
183
- *
184
- * Names only. See `GhRepositoryVariable` for why the value is deliberately
185
- * not fetched.
186
- */
187
- repositoryVariables?: RepositoryVariableScan;
188
- }
189
- /**
190
- * What `gh variable list` (no `--env`) returned, or why it returned nothing.
191
- *
192
- * `reason` is carried rather than dropped because the two failures need
193
- * different next steps and look identical from a finding that omits it: a
194
- * missing permission is fixed by an admin, an unauthenticated CLI by the person
195
- * reading the report.
196
- */
197
- export type RepositoryVariableScan = {
198
- readable: true;
199
- names: readonly string[];
200
- } | {
201
- readable: false;
202
- reason: string;
203
- };
204
- export declare function audit(input: AuditInput): Finding[];
205
- export declare function hasErrors(findings: Finding[]): boolean;
206
- export declare function renderFindings(findings: Finding[]): string;