@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.
- package/README.md +72 -11
- package/bin/devtools.mjs +4 -4
- package/dist/cli-CZKkNHK5.js +6619 -0
- package/dist/failure-log-BBPGejQv.js +256 -0
- package/dist/launch.js +267 -281
- package/dist/local-env-D8HC2JK_.js +351 -0
- package/dist/local-env-DrBCXqD8.js +2 -0
- package/dist/menu-CQBsQRgH.js +1684 -0
- package/dist/peer-redirect-hooks.js +15 -0
- package/dist/peers-RBplwovA.js +229 -0
- package/dist/root-C2OmZDwD.js +94 -0
- package/dist/stack-Bx-55oi_.js +2 -0
- package/dist/stack-BynCgRVS.js +181 -0
- package/dist/supabase-project-0knM0mqd.js +426 -0
- package/dist/telemetry-C4foD8MD.js +249 -0
- package/dist/telemetry.js +2 -148
- package/package.json +10 -29
- package/bin/devtools-ci-bare.mjs +0 -48
- package/bin/devtools-ci.mjs +0 -31
- package/dist/args.d.ts +0 -26
- package/dist/args.js +0 -78
- package/dist/bws/bw.d.ts +0 -18
- package/dist/bws/bw.js +0 -70
- package/dist/bws/client.d.ts +0 -43
- package/dist/bws/client.js +0 -363
- package/dist/bws/environments.d.ts +0 -96
- package/dist/bws/environments.js +0 -111
- package/dist/bws/pace.d.ts +0 -25
- package/dist/bws/pace.js +0 -43
- package/dist/bws/retry.d.ts +0 -12
- package/dist/bws/retry.js +0 -39
- package/dist/bws/store.d.ts +0 -1
- package/dist/bws/store.js +0 -32
- package/dist/bws/token.d.ts +0 -31
- package/dist/bws/token.js +0 -88
- package/dist/bws/vault.d.ts +0 -29
- package/dist/bws/vault.js +0 -200
- package/dist/catalog.d.ts +0 -57
- package/dist/catalog.js +0 -57
- package/dist/cf/build.d.ts +0 -13
- package/dist/cf/build.js +0 -65
- package/dist/cf/commands.d.ts +0 -1
- package/dist/cf/commands.js +0 -172
- package/dist/cf/local-env.d.ts +0 -58
- package/dist/cf/local-env.js +0 -177
- package/dist/ci.d.ts +0 -1
- package/dist/ci.js +0 -328
- package/dist/cli.d.ts +0 -11
- package/dist/cli.js +0 -1094
- package/dist/commands.d.ts +0 -261
- package/dist/commands.js +0 -1318
- package/dist/completions.d.ts +0 -4
- package/dist/completions.js +0 -110
- package/dist/cron/commands.d.ts +0 -47
- package/dist/cron/commands.js +0 -372
- package/dist/cron/discovery.d.ts +0 -55
- package/dist/cron/discovery.js +0 -85
- package/dist/cron/schema.d.ts +0 -45
- package/dist/cron/schema.js +0 -35
- package/dist/db/config-push.d.ts +0 -8
- package/dist/db/config-push.js +0 -11
- package/dist/db/connection.d.ts +0 -79
- package/dist/db/connection.js +0 -114
- package/dist/db/exec.d.ts +0 -1
- package/dist/db/exec.js +0 -11
- package/dist/db/generate-types.d.ts +0 -1
- package/dist/db/generate-types.js +0 -4
- package/dist/db/generated-env.d.ts +0 -69
- package/dist/db/generated-env.js +0 -113
- package/dist/db/introspect.d.ts +0 -1
- package/dist/db/introspect.js +0 -122
- package/dist/db/local-env.d.ts +0 -65
- package/dist/db/local-env.js +0 -23
- package/dist/db/new-migration.d.ts +0 -8
- package/dist/db/new-migration.js +0 -69
- package/dist/db/pooler.d.ts +0 -23
- package/dist/db/pooler.js +0 -55
- package/dist/db/run.d.ts +0 -68
- package/dist/db/run.js +0 -156
- package/dist/db/seed-buckets.d.ts +0 -2
- package/dist/db/seed-buckets.js +0 -4
- package/dist/db/seed-production.d.ts +0 -8
- package/dist/db/seed-production.js +0 -79
- package/dist/db/seed-roles.d.ts +0 -7
- package/dist/db/seed-roles.js +0 -33
- package/dist/db/session-refresh.d.ts +0 -43
- package/dist/db/session-refresh.js +0 -40
- package/dist/deploy/migrations.d.ts +0 -20
- package/dist/deploy/migrations.js +0 -68
- package/dist/deploy/orphans.d.ts +0 -20
- package/dist/deploy/orphans.js +0 -177
- package/dist/deploy/preflight.d.ts +0 -53
- package/dist/deploy/preflight.js +0 -189
- package/dist/deploy/report.d.ts +0 -26
- package/dist/deploy/report.js +0 -70
- package/dist/deploy/require-planner.d.ts +0 -2
- package/dist/deploy/require-planner.js +0 -60
- package/dist/deploy/require-token.d.ts +0 -15
- package/dist/deploy/require-token.js +0 -78
- package/dist/deploy/secrets-file.d.ts +0 -17
- package/dist/deploy/secrets-file.js +0 -166
- package/dist/deploy/write-env.d.ts +0 -40
- package/dist/deploy/write-env.js +0 -395
- package/dist/docs/index-pages.d.ts +0 -38
- package/dist/docs/index-pages.js +0 -186
- package/dist/emails/commands.d.ts +0 -15
- package/dist/emails/commands.js +0 -130
- package/dist/emails/email-types.d.ts +0 -13
- package/dist/emails/email-types.js +0 -1
- package/dist/emails/fixtures.d.ts +0 -20
- package/dist/emails/fixtures.js +0 -20
- package/dist/env/audit.d.ts +0 -206
- package/dist/env/audit.js +0 -436
- package/dist/env/cloudflare.d.ts +0 -21
- package/dist/env/cloudflare.js +0 -61
- package/dist/env/commands.d.ts +0 -99
- package/dist/env/commands.js +0 -613
- package/dist/env/discovery.d.ts +0 -15
- package/dist/env/discovery.js +0 -239
- package/dist/env/document.d.ts +0 -119
- package/dist/env/document.js +0 -323
- package/dist/env/example.d.ts +0 -68
- package/dist/env/example.js +0 -605
- package/dist/env/selection.d.ts +0 -105
- package/dist/env/selection.js +0 -228
- package/dist/env-entry.d.ts +0 -49
- package/dist/env-entry.js +0 -204
- package/dist/environment-doctor.d.ts +0 -45
- package/dist/environment-doctor.js +0 -429
- package/dist/environment.d.ts +0 -92
- package/dist/environment.js +0 -185
- package/dist/fingerprint.d.ts +0 -10
- package/dist/fingerprint.js +0 -18
- package/dist/gen/campus-map.d.ts +0 -1
- package/dist/gen/campus-map.js +0 -473
- package/dist/gen/commands.d.ts +0 -1
- package/dist/gen/commands.js +0 -25
- package/dist/gen/hypno.d.ts +0 -1
- package/dist/gen/hypno.js +0 -134
- package/dist/gh/client.d.ts +0 -80
- package/dist/gh/client.js +0 -259
- package/dist/gh/environments.d.ts +0 -83
- package/dist/gh/environments.js +0 -178
- package/dist/gh/rulesets/actors.d.ts +0 -14
- package/dist/gh/rulesets/actors.js +0 -69
- package/dist/gh/rulesets/api.d.ts +0 -15
- package/dist/gh/rulesets/api.js +0 -104
- package/dist/gh/rulesets/commands.d.ts +0 -3
- package/dist/gh/rulesets/commands.js +0 -203
- package/dist/gh/rulesets/desired.d.ts +0 -112
- package/dist/gh/rulesets/desired.js +0 -271
- package/dist/gh/rulesets/diff.d.ts +0 -65
- package/dist/gh/rulesets/diff.js +0 -170
- package/dist/gh/rulesets/types.d.ts +0 -76
- package/dist/gh/rulesets/types.js +0 -13
- package/dist/gh/settings/api.d.ts +0 -40
- package/dist/gh/settings/api.js +0 -180
- package/dist/gh/settings/commands.d.ts +0 -1
- package/dist/gh/settings/commands.js +0 -254
- package/dist/gh/settings/desired.d.ts +0 -53
- package/dist/gh/settings/desired.js +0 -43
- package/dist/gh/settings/diff.d.ts +0 -57
- package/dist/gh/settings/diff.js +0 -161
- package/dist/gh/settings/types.d.ts +0 -92
- package/dist/gh/settings/types.js +0 -11
- package/dist/gh/settings/workflows.d.ts +0 -41
- package/dist/gh/settings/workflows.js +0 -128
- package/dist/grantRoot.d.ts +0 -42
- package/dist/grantRoot.js +0 -89
- package/dist/help.d.ts +0 -18
- package/dist/help.js +0 -169
- package/dist/images/commands.d.ts +0 -32
- package/dist/images/commands.js +0 -231
- package/dist/images/events.d.ts +0 -47
- package/dist/images/events.js +0 -116
- package/dist/images/graphics.d.ts +0 -70
- package/dist/images/graphics.js +0 -234
- package/dist/images/open-graph-types.d.ts +0 -75
- package/dist/images/open-graph-types.js +0 -1
- package/dist/images/prompts.d.ts +0 -34
- package/dist/images/prompts.js +0 -100
- package/dist/images/render.d.ts +0 -21
- package/dist/images/render.js +0 -61
- package/dist/images/select.d.ts +0 -64
- package/dist/images/select.js +0 -96
- package/dist/instance.d.ts +0 -69
- package/dist/instance.js +0 -102
- package/dist/invocation.d.ts +0 -49
- package/dist/invocation.js +0 -78
- package/dist/launch-ci.d.ts +0 -1
- package/dist/launch-ci.js +0 -90
- package/dist/launch.d.ts +0 -18
- package/dist/menu.d.ts +0 -43
- package/dist/menu.js +0 -300
- package/dist/moderation.d.ts +0 -70
- package/dist/moderation.js +0 -128
- package/dist/oauth/browser.d.ts +0 -1
- package/dist/oauth/browser.js +0 -13
- package/dist/oauth/config.d.ts +0 -24
- package/dist/oauth/config.js +0 -24
- package/dist/oauth/connect-url.d.ts +0 -17
- package/dist/oauth/connect-url.js +0 -23
- package/dist/oauth/db.d.ts +0 -37
- package/dist/oauth/db.js +0 -108
- package/dist/oauth/device.d.ts +0 -93
- package/dist/oauth/device.js +0 -198
- package/dist/oauth/discovery.d.ts +0 -28
- package/dist/oauth/discovery.js +0 -54
- package/dist/oauth/env-file.d.ts +0 -6
- package/dist/oauth/env-file.js +0 -35
- package/dist/oauth/exchange.d.ts +0 -43
- package/dist/oauth/exchange.js +0 -93
- package/dist/oauth/label.d.ts +0 -1
- package/dist/oauth/label.js +0 -25
- package/dist/oauth/loopback.d.ts +0 -52
- package/dist/oauth/loopback.js +0 -168
- package/dist/oauth/pkce.d.ts +0 -6
- package/dist/oauth/pkce.js +0 -34
- package/dist/oauth/target.d.ts +0 -54
- package/dist/oauth/target.js +0 -102
- package/dist/oauth/transport.d.ts +0 -41
- package/dist/oauth/transport.js +0 -32
- package/dist/oauth/wizard.d.ts +0 -40
- package/dist/oauth/wizard.js +0 -578
- package/dist/persona.d.ts +0 -18
- package/dist/persona.js +0 -221
- package/dist/pick.d.ts +0 -6
- package/dist/pick.js +0 -64
- package/dist/pipes.d.ts +0 -1
- package/dist/pipes.js +0 -25
- package/dist/planner/checks.d.ts +0 -26
- package/dist/planner/checks.js +0 -104
- package/dist/planner/commands.d.ts +0 -10
- package/dist/planner/commands.js +0 -367
- package/dist/planner/db.d.ts +0 -15
- package/dist/planner/db.js +0 -33
- package/dist/planner/role.d.ts +0 -33
- package/dist/planner/role.js +0 -68
- package/dist/repo/peer-redirect-hooks.d.ts +0 -16
- package/dist/repo/peer-redirect-hooks.js +0 -10
- package/dist/repo/peer-redirect.d.ts +0 -4
- package/dist/repo/peer-redirect.js +0 -26
- package/dist/repo/peers.d.ts +0 -39
- package/dist/repo/peers.js +0 -139
- package/dist/repo/resolve.d.ts +0 -41
- package/dist/repo/resolve.js +0 -152
- package/dist/repo/root.d.ts +0 -23
- package/dist/repo/root.js +0 -91
- package/dist/repo/source.d.ts +0 -14
- package/dist/repo/source.js +0 -54
- package/dist/repo/supabase-project.d.ts +0 -60
- package/dist/repo/supabase-project.js +0 -124
- package/dist/repo/tsx-loader.d.ts +0 -6
- package/dist/repo/tsx-loader.js +0 -41
- package/dist/run/pick.d.ts +0 -90
- package/dist/run/pick.js +0 -664
- package/dist/setup.d.ts +0 -1
- package/dist/setup.js +0 -280
- package/dist/stack.d.ts +0 -48
- package/dist/stack.js +0 -403
- package/dist/telemetry.d.ts +0 -59
- package/dist/tier.d.ts +0 -40
- package/dist/tier.js +0 -96
- package/dist/ui.d.ts +0 -40
- package/dist/ui.js +0 -71
- package/dist/version.d.ts +0 -7
- package/dist/version.js +0 -17
- package/dist/workers.d.ts +0 -5
- package/dist/workers.js +0 -33
- package/dist/workflows/commands.d.ts +0 -54
- package/dist/workflows/commands.js +0 -763
package/dist/menu.js
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The wizard: `pnpm devtools` with no arguments.
|
|
3
|
-
*
|
|
4
|
-
* ## The one property worth protecting
|
|
5
|
-
*
|
|
6
|
-
* **It builds an argv and hands it to the CLI's own dispatcher.** It does not
|
|
7
|
-
* call command functions directly. That is what makes "the menu covers every
|
|
8
|
-
* interactive command" structural instead of aspirational. The menu it replaced held a
|
|
9
|
-
* hand-written list of ten entries beside a CLI that had grown to sixteen
|
|
10
|
-
* top-level commands and thirty-one subcommands, so `env`, `planner` and
|
|
11
|
-
* `docs index` were reachable only by someone who already knew their names.
|
|
12
|
-
* A contributor who does not know a command name is the entire audience for
|
|
13
|
-
* this file.
|
|
14
|
-
*
|
|
15
|
-
* Walking `commands.ts` means an interactive command added there is in the
|
|
16
|
-
* menu the same day, with its options. Commands marked `cli-only` and deploy
|
|
17
|
-
* commands in the separate `devtools-ci` bin do not appear here.
|
|
18
|
-
*/
|
|
19
|
-
import { confirm, note, select, text } from "@clack/prompts";
|
|
20
|
-
import { positionals } from "./args.js";
|
|
21
|
-
import { findCommand, GROUPS, SCOPES, } from "./commands.js";
|
|
22
|
-
import { takeMenuEnvHook } from "./env-entry.js";
|
|
23
|
-
import { blockedBecause, describeEnvironment, isOffered, probeEnvironment, } from "./environment.js";
|
|
24
|
-
import { beginInvocation, recordEnteredTier } from "./invocation.js";
|
|
25
|
-
import { unwrap } from "./ui.js";
|
|
26
|
-
/** Chosen when a submenu should return to the screen above it. */
|
|
27
|
-
const BACK = Symbol("back");
|
|
28
|
-
const BACK_OPTION = { value: BACK, label: "← Back" };
|
|
29
|
-
// ── What this machine is offered ─────────────────────────────────────────────
|
|
30
|
-
/**
|
|
31
|
-
* The commands at one level that are worth showing right now.
|
|
32
|
-
*
|
|
33
|
-
* `when` is the only thing that removes an entry, and it removes very few;
|
|
34
|
-
* see `isOffered`. Everything else stays on screen and explains itself
|
|
35
|
-
* through `hintFor` below, because the menu's job is to help someone who does
|
|
36
|
-
* not know a command's name, and it cannot do that for a command it declined
|
|
37
|
-
* to draw.
|
|
38
|
-
*/
|
|
39
|
-
function offered(nodes, env) {
|
|
40
|
-
return nodes.filter((node) => node.surface !== "cli-only" && isOffered(node, env));
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* The line beside a name, with whatever is standing in this command's way.
|
|
44
|
-
*
|
|
45
|
-
* The reader learns a command needs something standing in its way *before*
|
|
46
|
-
* choosing it, instead of after a spinner and a connection error — `db
|
|
47
|
-
* start`'s `when: "instance-stopped"` is the one still in the tree today. The
|
|
48
|
-
* command stays selectable when it merely `needs` rather than requires the
|
|
49
|
-
* condition to be shown at all: the check is a probe, the probe can be
|
|
50
|
-
* wrong, and the command's own failure message is the authority on whether
|
|
51
|
-
* it can run.
|
|
52
|
-
*/
|
|
53
|
-
function hintFor(node, env) {
|
|
54
|
-
const base = node.hint ?? node.summary;
|
|
55
|
-
const blocked = blockedBecause(node, env);
|
|
56
|
-
const said = blocked ? `${base} — ${blocked}` : base;
|
|
57
|
-
// The scope leads, because in the one group that has scopes it is the thing
|
|
58
|
-
// the reader is actually choosing between: `restart` and `reset` sit four
|
|
59
|
-
// lines apart and act on different layers.
|
|
60
|
-
return node.scope ? `${SCOPES[node.scope].menu} · ${said}` : said;
|
|
61
|
-
}
|
|
62
|
-
// ── Screens ──────────────────────────────────────────────────────────────────
|
|
63
|
-
async function pickGroup(env) {
|
|
64
|
-
// A group whose every command is hidden has nothing behind its door, so the
|
|
65
|
-
// door is not drawn. No group in the tree can empty out today; this is here
|
|
66
|
-
// so that a later `when` cannot leave a dead entry on the first screen.
|
|
67
|
-
const groups = GROUPS.filter((group) => offered(group.commands, env).length > 0);
|
|
68
|
-
const choice = unwrap(await select({
|
|
69
|
-
message: "What would you like to do?",
|
|
70
|
-
options: [
|
|
71
|
-
...groups.map((group) => ({
|
|
72
|
-
value: group,
|
|
73
|
-
label: group.title,
|
|
74
|
-
// The group's own commands, so the first screen says what is behind
|
|
75
|
-
// each door rather than making the reader open every one to find out.
|
|
76
|
-
hint: offered(group.commands, env)
|
|
77
|
-
.map((command) => command.name)
|
|
78
|
-
.join(", "),
|
|
79
|
-
})),
|
|
80
|
-
{ value: null, label: "Quit" },
|
|
81
|
-
],
|
|
82
|
-
}));
|
|
83
|
-
return choice;
|
|
84
|
-
}
|
|
85
|
-
async function pickCommand(group, env) {
|
|
86
|
-
const commands = offered(group.commands, env);
|
|
87
|
-
// A group with one command has nothing to choose; asking would be a screen
|
|
88
|
-
// whose only real option is the one already implied by the group's title.
|
|
89
|
-
if (commands.length === 1)
|
|
90
|
-
return commands[0];
|
|
91
|
-
return unwrap(await select({
|
|
92
|
-
message: `${group.title}:`,
|
|
93
|
-
options: [
|
|
94
|
-
...commands.map((command) => ({
|
|
95
|
-
value: command,
|
|
96
|
-
label: command.name,
|
|
97
|
-
hint: hintFor(command, env),
|
|
98
|
-
})),
|
|
99
|
-
BACK_OPTION,
|
|
100
|
-
],
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
async function pickSubcommand(node, env) {
|
|
104
|
-
return unwrap(await select({
|
|
105
|
-
message: `${node.name}:`,
|
|
106
|
-
options: [
|
|
107
|
-
...offered(node.subcommands ?? [], env).map((child) => ({
|
|
108
|
-
value: child,
|
|
109
|
-
label: child.name,
|
|
110
|
-
hint: hintFor(child, env),
|
|
111
|
-
})),
|
|
112
|
-
BACK_OPTION,
|
|
113
|
-
],
|
|
114
|
-
}));
|
|
115
|
-
}
|
|
116
|
-
// ── Options ──────────────────────────────────────────────────────────────────
|
|
117
|
-
/**
|
|
118
|
-
* Asks for one option, returning the argv fragment it contributes.
|
|
119
|
-
*
|
|
120
|
-
* An empty array is a real answer, not a failure: a declined confirm adds no
|
|
121
|
-
* flag, and a blank optional text means "let the command decide", which is
|
|
122
|
-
* how `--db-url` falls back to `.env.production`.
|
|
123
|
-
*/
|
|
124
|
-
async function askOption(option) {
|
|
125
|
-
const prompt = option.prompt;
|
|
126
|
-
if (!prompt)
|
|
127
|
-
return [];
|
|
128
|
-
if (prompt.kind === "confirm") {
|
|
129
|
-
// Yes adds the flag, always. `commands.ts` phrases every message so that
|
|
130
|
-
// this needs no per-option inversion.
|
|
131
|
-
const yes = unwrap(await confirm({ message: prompt.message, initialValue: prompt.initial }));
|
|
132
|
-
return yes ? [option.flag] : [];
|
|
133
|
-
}
|
|
134
|
-
if (prompt.kind === "text") {
|
|
135
|
-
const answer = unwrap(await text({
|
|
136
|
-
message: prompt.message,
|
|
137
|
-
placeholder: prompt.placeholder,
|
|
138
|
-
defaultValue: "",
|
|
139
|
-
})).trim();
|
|
140
|
-
return answer ? [option.flag, answer] : [];
|
|
141
|
-
}
|
|
142
|
-
const choice = unwrap(await select({
|
|
143
|
-
message: prompt.message,
|
|
144
|
-
options: prompt.choices.map((c) => ({
|
|
145
|
-
value: c.value,
|
|
146
|
-
label: c.label ?? c.value,
|
|
147
|
-
hint: c.hint,
|
|
148
|
-
})),
|
|
149
|
-
}));
|
|
150
|
-
return [option.flag, choice];
|
|
151
|
-
}
|
|
152
|
-
async function askOptions(node) {
|
|
153
|
-
const argv = [];
|
|
154
|
-
for (const option of node.options ?? []) {
|
|
155
|
-
argv.push(...(await askOption(option)));
|
|
156
|
-
}
|
|
157
|
-
return argv;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Descends from `start` through its subcommand screens to a leaf, then asks
|
|
161
|
-
* that leaf's options.
|
|
162
|
-
*
|
|
163
|
-
* Shared by both entries into a walk: the top-of-tree loop below, which calls
|
|
164
|
-
* this once a group and its first command are chosen, and a resumed walk
|
|
165
|
-
* (`walk`'s `startPath` branch), which calls it directly on a node the caller
|
|
166
|
-
* already picked by argv. `while` rather than a single step because the tree
|
|
167
|
-
* is two deep today and this does not care. The condition counts the OFFERED
|
|
168
|
-
* children rather than all of them, so a node whose every subcommand is
|
|
169
|
-
* hidden is treated as the leaf it has become instead of opening a screen
|
|
170
|
-
* holding only "Back".
|
|
171
|
-
*/
|
|
172
|
-
async function descendFrom(start, pathNames, env) {
|
|
173
|
-
let node = start;
|
|
174
|
-
const path = [...pathNames];
|
|
175
|
-
while (offered(node.subcommands ?? [], env).length > 0) {
|
|
176
|
-
const child = await pickSubcommand(node, env);
|
|
177
|
-
if (child === BACK)
|
|
178
|
-
return BACK;
|
|
179
|
-
node = child;
|
|
180
|
-
path.push(node.name);
|
|
181
|
-
}
|
|
182
|
-
return { node, argv: [...path, ...(await askOptions(node))] };
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Walks the tree to a leaf and its options, either from the top or resumed at
|
|
186
|
-
* a known group.
|
|
187
|
-
*
|
|
188
|
-
* `startPath`, when given, is a path to a group node from `bareGroupStartPath`
|
|
189
|
-
* — `["db"]` for `devtools db` — and lands here instead of at `pickGroup`.
|
|
190
|
-
* There is no group screen above a resumed node (the caller already named it
|
|
191
|
-
* by typing `db`), so BACK on its first subcommand screen has nowhere to
|
|
192
|
-
* return to but out, unlike BACK from the top of the tree, which returns to
|
|
193
|
-
* `pickGroup`.
|
|
194
|
-
*/
|
|
195
|
-
async function walk(env, startPath) {
|
|
196
|
-
if (startPath) {
|
|
197
|
-
const start = findCommand(startPath);
|
|
198
|
-
if (!start)
|
|
199
|
-
return null; // defensive: the caller guarantees a group node
|
|
200
|
-
const chosen = await descendFrom(start, [...startPath], env);
|
|
201
|
-
return chosen === BACK ? null : chosen;
|
|
202
|
-
}
|
|
203
|
-
for (;;) {
|
|
204
|
-
const group = await pickGroup(env);
|
|
205
|
-
if (!group)
|
|
206
|
-
return null;
|
|
207
|
-
const first = await pickCommand(group, env);
|
|
208
|
-
if (first === BACK)
|
|
209
|
-
continue;
|
|
210
|
-
const chosen = await descendFrom(first, [first.name], env);
|
|
211
|
-
if (chosen === BACK)
|
|
212
|
-
continue;
|
|
213
|
-
return chosen;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
// ── Resuming at a node ───────────────────────────────────────────────────────
|
|
217
|
-
/**
|
|
218
|
-
* The command path when argv names a group with subcommands but no subcommand
|
|
219
|
-
* token — what `devtools db` and `devtools db seed` are. Returns null for a
|
|
220
|
-
* leaf command, an unknown token, or a bare invocation (which the no-argument
|
|
221
|
-
* wizard already covers). The caller resumes the wizard at this node when
|
|
222
|
-
* stdin is a TTY; a non-TTY caller keeps the dispatcher's "which of …?" exit.
|
|
223
|
-
*/
|
|
224
|
-
export function bareGroupStartPath(argv) {
|
|
225
|
-
const path = positionals(argv);
|
|
226
|
-
if (path.length === 0)
|
|
227
|
-
return null;
|
|
228
|
-
const node = findCommand(path);
|
|
229
|
-
if (!node || (node.subcommands ?? []).length === 0)
|
|
230
|
-
return null;
|
|
231
|
-
return path;
|
|
232
|
-
}
|
|
233
|
-
// ── Entry ────────────────────────────────────────────────────────────────────
|
|
234
|
-
/**
|
|
235
|
-
* Runs the wizard, returning the `outro()` line its command earned.
|
|
236
|
-
*
|
|
237
|
-
* `dispatch` is the CLI's own argv handler, injected rather than imported so
|
|
238
|
-
* that `cli.ts` keeps a single definition of what each command does and the
|
|
239
|
-
* tests can watch what a walk produces without running it. Its return value
|
|
240
|
-
* passes straight through, the closing line or `null` for a failure already
|
|
241
|
-
* explained, so a command reached from the menu signs off exactly as it does
|
|
242
|
-
* from the command line.
|
|
243
|
-
*
|
|
244
|
-
* `options.startPath`, from `bareGroupStartPath`, skips straight to that
|
|
245
|
-
* node's subcommand screen — see `walk`'s `startPath` branch.
|
|
246
|
-
*
|
|
247
|
-
* The deploy tier is NOT asked here any more. `src/launch.ts` resolves it
|
|
248
|
-
* before `cli.ts` — and therefore this module — is even imported, and
|
|
249
|
-
* exports `DEPLOY_ENV`/`DEV_DB` right away, so by the time the wizard opens
|
|
250
|
-
* `process.env` already names the session exactly as if `--tier` had been
|
|
251
|
-
* typed. Recording it for the "run it directly next time" line only needs to
|
|
252
|
-
* read `process.env.DEPLOY_ENV` back.
|
|
253
|
-
*
|
|
254
|
-
* Entering that tier — loading its `.env.<tier>` file onto `process.env` —
|
|
255
|
-
* is what's deferred: `launch.ts` registers a hook (`env-entry.ts`'s
|
|
256
|
-
* `setMenuEnvHook`) instead of entering up front, so whatever changed while
|
|
257
|
-
* the reader was still walking the menu (the local stack coming up in
|
|
258
|
-
* another terminal, `.env.generated` being rewritten) is picked up rather
|
|
259
|
-
* than missed. `takeMenuEnvHook()` below runs it right before the chosen
|
|
260
|
-
* command dispatches — the ONE place in a menu walk an env value is ever
|
|
261
|
-
* actually read. `undefined` (no hook registered — a typed command entered
|
|
262
|
-
* already, or a test drives `runMenu` directly) just dispatches straight
|
|
263
|
-
* through, unchanged from before this existed.
|
|
264
|
-
*/
|
|
265
|
-
export async function runMenu(dispatch, env = probeEnvironment(), options = {}) {
|
|
266
|
-
// Before the first question, not after a failure. Three lines saying what
|
|
267
|
-
// this machine currently is explain why the database commands below are
|
|
268
|
-
// flagged, and cost one glance on a healthy machine. Injected rather than
|
|
269
|
-
// probed inside `walk` so the tests can drive a machine they describe
|
|
270
|
-
// instead of the one they happen to run on.
|
|
271
|
-
note(describeEnvironment(env), "This machine");
|
|
272
|
-
const chosen = await walk(env, options.startPath);
|
|
273
|
-
// Quitting is not a failure, but it has nothing to announce either.
|
|
274
|
-
if (!chosen)
|
|
275
|
-
return null;
|
|
276
|
-
// Every step here was a prompt, so the built argv is the reproducible
|
|
277
|
-
// command — a runner that prompts further (a bare `workflows run`) appends
|
|
278
|
-
// the rest through `recordResolved`, and the entered tier rides along as the
|
|
279
|
-
// `--tier` flag `recordEnteredTier` adds.
|
|
280
|
-
beginInvocation(chosen.argv, true);
|
|
281
|
-
// A development session that answered the "which development database?"
|
|
282
|
-
// question records the QUALIFIED selector, so the "run it directly next
|
|
283
|
-
// time" line reproduces the whole session — a bare `development` would
|
|
284
|
-
// re-ask (or refuse, non-interactively) on a machine whose `.env` names a
|
|
285
|
-
// remote DB_URL.
|
|
286
|
-
const enteredTier = process.env.DEPLOY_ENV ?? "development";
|
|
287
|
-
const devDb = process.env.DEV_DB;
|
|
288
|
-
recordEnteredTier(enteredTier === "development" && (devDb === "local" || devDb === "remote")
|
|
289
|
-
? `development:${devDb}`
|
|
290
|
-
: enteredTier);
|
|
291
|
-
// The deferred entry, if `launch.ts` registered one — see this function's
|
|
292
|
-
// header. Read-and-cleared in one call so a nested launcher (this same
|
|
293
|
-
// command re-dispatching through its own `launch()`, e.g. `db start`'s
|
|
294
|
-
// offline-stack offer) never inherits a hook meant for this walk.
|
|
295
|
-
const enterEnvironment = takeMenuEnvHook();
|
|
296
|
-
if (enterEnvironment) {
|
|
297
|
-
return enterEnvironment(chosen.argv, () => dispatch(chosen.argv));
|
|
298
|
-
}
|
|
299
|
-
return dispatch(chosen.argv);
|
|
300
|
-
}
|
package/dist/moderation.d.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Does my app's moderation integration actually work?
|
|
3
|
-
*
|
|
4
|
-
* `conformance()` asks the database what it derived from your schema: is the
|
|
5
|
-
* table addressable, can an author be attributed, does quarantine have a column
|
|
6
|
-
* to write to, can a client still write that column. All of it is answerable
|
|
7
|
-
* from the catalog, and `platform.conformance_check()` does the answering.
|
|
8
|
-
*
|
|
9
|
-
* The end-to-end round trip that used to live here (`quarantineRoundTrip`,
|
|
10
|
-
* `moderation roundtrip`) is gone: the app repo's own CI covers that path —
|
|
11
|
-
* file, quarantine, and check the freeze — more thoroughly than a devtools
|
|
12
|
-
* command driven by hand ever did, and keeping two implementations of the
|
|
13
|
-
* same assertion around was a drift risk with no offsetting benefit.
|
|
14
|
-
*
|
|
15
|
-
* Both `conformance()` and `catalog.ts`'s `readCatalog()` need to run as a
|
|
16
|
-
* moderator, subject to the same RLS and `canModerate` checks a real one
|
|
17
|
-
* would hit, rather than as `postgres` bypassing them. `withTemporaryModerator`
|
|
18
|
-
* below is what supplies that account: a throwaway user, created through the
|
|
19
|
-
* admin API and deleted again in a `finally`, rather than the seeded
|
|
20
|
-
* `moderator@devdogs.test` persona this package used to sign in as — that
|
|
21
|
-
* persona came from `supabase/seed/development/02_moderation.sql`, which only
|
|
22
|
-
* ever ran on a local reset and is going away along with the rest of
|
|
23
|
-
* `seed/development/` (see `persona.ts`'s header for the replacement).
|
|
24
|
-
*/
|
|
25
|
-
import { type DevtoolsClient, type Instance } from "./instance.js";
|
|
26
|
-
import type { CheckResult } from "./ui.js";
|
|
27
|
-
interface ConformanceType {
|
|
28
|
-
contentType: string;
|
|
29
|
-
/** Schema-qualified, e.g. `platform.profile`. */
|
|
30
|
-
tableName: string;
|
|
31
|
-
checks: CheckResult[];
|
|
32
|
-
}
|
|
33
|
-
/** Runs `platform.conformance_check()` as the given moderator client. */
|
|
34
|
-
export declare function conformance(client: DevtoolsClient, appSlug: string): Promise<ConformanceType[]>;
|
|
35
|
-
/**
|
|
36
|
-
* The role every devtools-granted moderator holds — `moderation check`'s
|
|
37
|
-
* throwaway account and `persona moderator`'s longer-lived one alike, so
|
|
38
|
-
* both resolve to the SAME role row rather than each accumulating their own.
|
|
39
|
-
*
|
|
40
|
-
* Reuses the id `supabase/seed/development/02_moderation.sql` used to seed
|
|
41
|
-
* this same role under, on the theory that an existing local database that
|
|
42
|
-
* still has that seeded row should be upserted into agreement rather than
|
|
43
|
-
* given a second, redundant "Moderator" role.
|
|
44
|
-
*/
|
|
45
|
-
export declare const MODERATOR_ROLE_ID = "00000000-0000-4000-9000-000000000001";
|
|
46
|
-
/**
|
|
47
|
-
* Creates (or updates) the Moderator role definition, and returns its id.
|
|
48
|
-
*
|
|
49
|
-
* `canModerate: true` and nothing else — not Root, not any of the other
|
|
50
|
-
* `roles` permission columns — mirroring exactly what the seed persona held.
|
|
51
|
-
* Idempotent (`upsert`) so repeated calls, from either caller above, never
|
|
52
|
-
* race each other into two rows or fail on the second one.
|
|
53
|
-
*/
|
|
54
|
-
export declare function ensureModeratorRole(admin: DevtoolsClient): Promise<string>;
|
|
55
|
-
/** Grants the Moderator role to `userId`, creating the role definition first
|
|
56
|
-
* if this is the first account to need it. */
|
|
57
|
-
export declare function grantModerator(admin: DevtoolsClient, userId: string): Promise<void>;
|
|
58
|
-
/**
|
|
59
|
-
* Runs `fn` with a client signed in as a throwaway moderator, created for
|
|
60
|
-
* this call and deleted again in a `finally` whether or not `fn` throws.
|
|
61
|
-
*
|
|
62
|
-
* The admin API's `createUser` bypasses the app's `before_user_created`
|
|
63
|
-
* uga.edu hook (that hook only runs on the sign-up/OTP paths a real user
|
|
64
|
-
* takes, not the service-role admin API), which is why a `@persona.test`
|
|
65
|
-
* address works here at all — see `persona.ts`'s header for the same note
|
|
66
|
-
* in more detail, since that is the command a contributor is more likely to
|
|
67
|
-
* read first.
|
|
68
|
-
*/
|
|
69
|
-
export declare function withTemporaryModerator<T>(instance: Instance, fn: (client: DevtoolsClient) => Promise<T>): Promise<T>;
|
|
70
|
-
export {};
|
package/dist/moderation.js
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Does my app's moderation integration actually work?
|
|
3
|
-
*
|
|
4
|
-
* `conformance()` asks the database what it derived from your schema: is the
|
|
5
|
-
* table addressable, can an author be attributed, does quarantine have a column
|
|
6
|
-
* to write to, can a client still write that column. All of it is answerable
|
|
7
|
-
* from the catalog, and `platform.conformance_check()` does the answering.
|
|
8
|
-
*
|
|
9
|
-
* The end-to-end round trip that used to live here (`quarantineRoundTrip`,
|
|
10
|
-
* `moderation roundtrip`) is gone: the app repo's own CI covers that path —
|
|
11
|
-
* file, quarantine, and check the freeze — more thoroughly than a devtools
|
|
12
|
-
* command driven by hand ever did, and keeping two implementations of the
|
|
13
|
-
* same assertion around was a drift risk with no offsetting benefit.
|
|
14
|
-
*
|
|
15
|
-
* Both `conformance()` and `catalog.ts`'s `readCatalog()` need to run as a
|
|
16
|
-
* moderator, subject to the same RLS and `canModerate` checks a real one
|
|
17
|
-
* would hit, rather than as `postgres` bypassing them. `withTemporaryModerator`
|
|
18
|
-
* below is what supplies that account: a throwaway user, created through the
|
|
19
|
-
* admin API and deleted again in a `finally`, rather than the seeded
|
|
20
|
-
* `moderator@devdogs.test` persona this package used to sign in as — that
|
|
21
|
-
* persona came from `supabase/seed/development/02_moderation.sql`, which only
|
|
22
|
-
* ever ran on a local reset and is going away along with the rest of
|
|
23
|
-
* `seed/development/` (see `persona.ts`'s header for the replacement).
|
|
24
|
-
*/
|
|
25
|
-
import { adminClient, signedInClient, } from "./instance.js";
|
|
26
|
-
/** Runs `platform.conformance_check()` as the given moderator client. */
|
|
27
|
-
export async function conformance(client, appSlug) {
|
|
28
|
-
const response = await client.rpc("conformance_check", {
|
|
29
|
-
app_slug: appSlug,
|
|
30
|
-
});
|
|
31
|
-
if (response.error)
|
|
32
|
-
throw new Error(response.error.message);
|
|
33
|
-
// The generated types do not describe this function's return shape.
|
|
34
|
-
const data = response.data;
|
|
35
|
-
return (data ?? []);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* The role every devtools-granted moderator holds — `moderation check`'s
|
|
39
|
-
* throwaway account and `persona moderator`'s longer-lived one alike, so
|
|
40
|
-
* both resolve to the SAME role row rather than each accumulating their own.
|
|
41
|
-
*
|
|
42
|
-
* Reuses the id `supabase/seed/development/02_moderation.sql` used to seed
|
|
43
|
-
* this same role under, on the theory that an existing local database that
|
|
44
|
-
* still has that seeded row should be upserted into agreement rather than
|
|
45
|
-
* given a second, redundant "Moderator" role.
|
|
46
|
-
*/
|
|
47
|
-
export const MODERATOR_ROLE_ID = "00000000-0000-4000-9000-000000000001";
|
|
48
|
-
/**
|
|
49
|
-
* Creates (or updates) the Moderator role definition, and returns its id.
|
|
50
|
-
*
|
|
51
|
-
* `canModerate: true` and nothing else — not Root, not any of the other
|
|
52
|
-
* `roles` permission columns — mirroring exactly what the seed persona held.
|
|
53
|
-
* Idempotent (`upsert`) so repeated calls, from either caller above, never
|
|
54
|
-
* race each other into two rows or fail on the second one.
|
|
55
|
-
*/
|
|
56
|
-
export async function ensureModeratorRole(admin) {
|
|
57
|
-
const { error } = await admin.from("roles").upsert({
|
|
58
|
-
id: MODERATOR_ROLE_ID,
|
|
59
|
-
title: "Moderator",
|
|
60
|
-
description: "Created by `pnpm devtools`: works the report queue, and nothing else.",
|
|
61
|
-
roleType: "custom",
|
|
62
|
-
rank: 5000,
|
|
63
|
-
canModerate: true,
|
|
64
|
-
});
|
|
65
|
-
if (error) {
|
|
66
|
-
throw new Error(`Could not create the Moderator role: ${error.message}`);
|
|
67
|
-
}
|
|
68
|
-
return MODERATOR_ROLE_ID;
|
|
69
|
-
}
|
|
70
|
-
/** Grants the Moderator role to `userId`, creating the role definition first
|
|
71
|
-
* if this is the first account to need it. */
|
|
72
|
-
export async function grantModerator(admin, userId) {
|
|
73
|
-
const roleId = await ensureModeratorRole(admin);
|
|
74
|
-
const { error } = await admin.from("userRoles").insert({ userId, roleId });
|
|
75
|
-
if (error) {
|
|
76
|
-
throw new Error(`Could not grant the Moderator role: ${error.message}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/** An email that unambiguously belongs to devtools, not a real account —
|
|
80
|
-
* see `persona.ts`'s header for the `@persona.test` convention this shares. */
|
|
81
|
-
function temporaryModeratorEmail() {
|
|
82
|
-
return `devtools-moderator-${crypto.randomUUID().slice(0, 8)}@persona.test`;
|
|
83
|
-
}
|
|
84
|
-
/** A password nobody needs to remember — this account lives for the
|
|
85
|
-
* duration of one command. */
|
|
86
|
-
function randomPassword() {
|
|
87
|
-
return crypto.randomUUID() + crypto.randomUUID();
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Runs `fn` with a client signed in as a throwaway moderator, created for
|
|
91
|
-
* this call and deleted again in a `finally` whether or not `fn` throws.
|
|
92
|
-
*
|
|
93
|
-
* The admin API's `createUser` bypasses the app's `before_user_created`
|
|
94
|
-
* uga.edu hook (that hook only runs on the sign-up/OTP paths a real user
|
|
95
|
-
* takes, not the service-role admin API), which is why a `@persona.test`
|
|
96
|
-
* address works here at all — see `persona.ts`'s header for the same note
|
|
97
|
-
* in more detail, since that is the command a contributor is more likely to
|
|
98
|
-
* read first.
|
|
99
|
-
*/
|
|
100
|
-
export async function withTemporaryModerator(instance, fn) {
|
|
101
|
-
const admin = adminClient(instance);
|
|
102
|
-
const email = temporaryModeratorEmail();
|
|
103
|
-
const password = randomPassword();
|
|
104
|
-
let userId = null;
|
|
105
|
-
try {
|
|
106
|
-
const { data, error } = await admin.auth.admin.createUser({
|
|
107
|
-
email,
|
|
108
|
-
password,
|
|
109
|
-
email_confirm: true,
|
|
110
|
-
user_metadata: { createdBy: "devtools moderation check" },
|
|
111
|
-
});
|
|
112
|
-
if (error || !data.user) {
|
|
113
|
-
throw new Error(`Could not create a temporary moderator: ${error?.message}`);
|
|
114
|
-
}
|
|
115
|
-
userId = data.user.id;
|
|
116
|
-
await grantModerator(admin, userId);
|
|
117
|
-
const client = await signedInClient(instance, email, password);
|
|
118
|
-
return await fn(client);
|
|
119
|
-
}
|
|
120
|
-
finally {
|
|
121
|
-
// `userRoles.userId` cascades on delete (see the migration that defines
|
|
122
|
-
// it), so deleting the auth user is the whole teardown — no separate
|
|
123
|
-
// `userRoles` row to clean up, and the Moderator role DEFINITION is left
|
|
124
|
-
// in place for the next caller, same as `ensureModeratorRole` intends.
|
|
125
|
-
if (userId)
|
|
126
|
-
await admin.auth.admin.deleteUser(userId);
|
|
127
|
-
}
|
|
128
|
-
}
|
package/dist/oauth/browser.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function openBrowser(url: string): void;
|
package/dist/oauth/browser.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/** Opens `url` in the user's default browser, cross-platform. */
|
|
2
|
-
import { execFile } from "node:child_process";
|
|
3
|
-
export function openBrowser(url) {
|
|
4
|
-
if (process.platform === "win32") {
|
|
5
|
-
execFile("cmd", ["/c", "start", "", url]);
|
|
6
|
-
}
|
|
7
|
-
else if (process.platform === "darwin") {
|
|
8
|
-
execFile("open", [url]);
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
execFile("xdg-open", [url]);
|
|
12
|
-
}
|
|
13
|
-
}
|
package/dist/oauth/config.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/** Default Supabase project that hosts the "Sign in with DevDogs" OAuth server. */
|
|
2
|
-
export declare const DEFAULT_API_URL = "https://api.devdogsuga.org";
|
|
3
|
-
/**
|
|
4
|
-
* DevDogs website — human-facing links, and (since TASK-351) the origin
|
|
5
|
-
* that serves the one-click connect flow's `/tools/oauth/connect` and
|
|
6
|
-
* `/tools/oauth/connect/exchange` endpoints. Overridable via `--platform-url`
|
|
7
|
-
* / `DEVDOGS_PLATFORM_URL` for local testing against a platform dev server —
|
|
8
|
-
* see `commands.ts`'s `oauth` node and `wizard.ts`.
|
|
9
|
-
*/
|
|
10
|
-
export declare const WEBSITE_URL = "https://devdogsuga.org";
|
|
11
|
-
/**
|
|
12
|
-
* Custom OAuth/OIDC provider identifier. Supabase requires custom provider
|
|
13
|
-
* identifiers to be prefixed with "custom:".
|
|
14
|
-
*/
|
|
15
|
-
export declare const PROVIDER_IDENTIFIER = "custom:devdogsuga";
|
|
16
|
-
export declare const PROVIDER_NAME = "DevDogs";
|
|
17
|
-
export declare const PROVIDER_SCOPES: string[];
|
|
18
|
-
export declare const ENV_KEYS: {
|
|
19
|
-
readonly baseUrl: "OAUTH_BASE_URL";
|
|
20
|
-
readonly providerName: "OAUTH_PROVIDER_NAME";
|
|
21
|
-
readonly clientId: "OAUTH_CLIENT_ID";
|
|
22
|
-
readonly clientSecret: "OAUTH_CLIENT_SECRET";
|
|
23
|
-
readonly platformUrl: "DEVDOGS_PLATFORM_URL";
|
|
24
|
-
};
|
package/dist/oauth/config.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/** Default Supabase project that hosts the "Sign in with DevDogs" OAuth server. */
|
|
2
|
-
export const DEFAULT_API_URL = "https://api.devdogsuga.org";
|
|
3
|
-
/**
|
|
4
|
-
* DevDogs website — human-facing links, and (since TASK-351) the origin
|
|
5
|
-
* that serves the one-click connect flow's `/tools/oauth/connect` and
|
|
6
|
-
* `/tools/oauth/connect/exchange` endpoints. Overridable via `--platform-url`
|
|
7
|
-
* / `DEVDOGS_PLATFORM_URL` for local testing against a platform dev server —
|
|
8
|
-
* see `commands.ts`'s `oauth` node and `wizard.ts`.
|
|
9
|
-
*/
|
|
10
|
-
export const WEBSITE_URL = "https://devdogsuga.org";
|
|
11
|
-
/**
|
|
12
|
-
* Custom OAuth/OIDC provider identifier. Supabase requires custom provider
|
|
13
|
-
* identifiers to be prefixed with "custom:".
|
|
14
|
-
*/
|
|
15
|
-
export const PROVIDER_IDENTIFIER = "custom:devdogsuga";
|
|
16
|
-
export const PROVIDER_NAME = "DevDogs";
|
|
17
|
-
export const PROVIDER_SCOPES = ["openid", "email", "profile"];
|
|
18
|
-
export const ENV_KEYS = {
|
|
19
|
-
baseUrl: "OAUTH_BASE_URL",
|
|
20
|
-
providerName: "OAUTH_PROVIDER_NAME",
|
|
21
|
-
clientId: "OAUTH_CLIENT_ID",
|
|
22
|
-
clientSecret: "OAUTH_CLIENT_SECRET",
|
|
23
|
-
platformUrl: "DEVDOGS_PLATFORM_URL",
|
|
24
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds the platform's `/tools/oauth/connect` URL — the page a browser
|
|
3
|
-
* opens to approve a `devtools oauth` connection. See this repo's launch
|
|
4
|
-
* instructions for the fixed wire contract: `redirect_uri`, `code_challenge`
|
|
5
|
-
* (base64url SHA-256 of the PKCE verifier, no padding — `pkce.ts`),
|
|
6
|
-
* `code_challenge_method=S256`, `state`, `label` (1–100 characters), and
|
|
7
|
-
* `callback_uri` (the TARGET Supabase project's own `/auth/v1/callback` —
|
|
8
|
-
* NOT this loopback listener's URL; that is `redirect_uri`).
|
|
9
|
-
*/
|
|
10
|
-
export declare function buildConnectUrl({ platformUrl, redirectUri, codeChallenge, state, label, callbackUri, }: {
|
|
11
|
-
platformUrl: string;
|
|
12
|
-
redirectUri: string;
|
|
13
|
-
codeChallenge: string;
|
|
14
|
-
state: string;
|
|
15
|
-
label: string;
|
|
16
|
-
callbackUri: string;
|
|
17
|
-
}): string;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds the platform's `/tools/oauth/connect` URL — the page a browser
|
|
3
|
-
* opens to approve a `devtools oauth` connection. See this repo's launch
|
|
4
|
-
* instructions for the fixed wire contract: `redirect_uri`, `code_challenge`
|
|
5
|
-
* (base64url SHA-256 of the PKCE verifier, no padding — `pkce.ts`),
|
|
6
|
-
* `code_challenge_method=S256`, `state`, `label` (1–100 characters), and
|
|
7
|
-
* `callback_uri` (the TARGET Supabase project's own `/auth/v1/callback` —
|
|
8
|
-
* NOT this loopback listener's URL; that is `redirect_uri`).
|
|
9
|
-
*/
|
|
10
|
-
export function buildConnectUrl({ platformUrl, redirectUri, codeChallenge, state, label, callbackUri, }) {
|
|
11
|
-
const url = new URL(`${platformUrl.replace(/\/+$/, "")}/tools/oauth/connect`);
|
|
12
|
-
url.searchParams.set("redirect_uri", redirectUri);
|
|
13
|
-
url.searchParams.set("code_challenge", codeChallenge);
|
|
14
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
15
|
-
url.searchParams.set("state", state);
|
|
16
|
-
// The wire contract caps this at 100 characters; truncated here rather
|
|
17
|
-
// than left for the platform to reject, since the label is a cosmetic
|
|
18
|
-
// default (project name / directory name — see `label.ts`), not something
|
|
19
|
-
// worth failing the whole connect flow over.
|
|
20
|
-
url.searchParams.set("label", label.slice(0, 100));
|
|
21
|
-
url.searchParams.set("callback_uri", callbackUri);
|
|
22
|
-
return url.toString();
|
|
23
|
-
}
|
package/dist/oauth/db.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export type LocalSupabaseConfig = {
|
|
2
|
-
apiUrl: string;
|
|
3
|
-
serviceRoleKey: string;
|
|
4
|
-
};
|
|
5
|
-
/**
|
|
6
|
-
* Runs `supabase status -o env` in `cwd` and extracts the local API URL and
|
|
7
|
-
* service role key. Throws a descriptive error if Supabase isn't running or
|
|
8
|
-
* the CLI isn't installed.
|
|
9
|
-
*
|
|
10
|
-
* Two invocations are tried, because this wizard runs in two different worlds:
|
|
11
|
-
* a sibling project, where the Supabase CLI is normally installed globally, and
|
|
12
|
-
* this monorepo, where it is a workspace devDependency and not on PATH at all.
|
|
13
|
-
* A global install wins, since `pnpm exec` in a non-pnpm project would fail for
|
|
14
|
-
* reasons that have nothing to do with Supabase.
|
|
15
|
-
*/
|
|
16
|
-
export declare function detectLocalSupabase(cwd: string): LocalSupabaseConfig;
|
|
17
|
-
/**
|
|
18
|
-
* Looks up a custom auth provider on a local Supabase instance through the
|
|
19
|
-
* Admin SDK, and reports whether it exists along with its name and issuer.
|
|
20
|
-
*
|
|
21
|
-
* A 404 means it does not exist yet. Any other error is real and rethrown.
|
|
22
|
-
*/
|
|
23
|
-
export declare function checkProvider({ apiUrl, serviceRoleKey }: LocalSupabaseConfig, identifier: string): Promise<{
|
|
24
|
-
exists: boolean;
|
|
25
|
-
name?: string;
|
|
26
|
-
issuer?: string;
|
|
27
|
-
}>;
|
|
28
|
-
export declare function upsertDevDogsProvider({ apiUrl, serviceRoleKey }: LocalSupabaseConfig, { identifier, name, clientId, clientSecret, issuer, }: {
|
|
29
|
-
identifier: string;
|
|
30
|
-
name: string;
|
|
31
|
-
clientId: string;
|
|
32
|
-
clientSecret: string;
|
|
33
|
-
issuer: string;
|
|
34
|
-
}): Promise<{
|
|
35
|
-
identifier: string;
|
|
36
|
-
issuer: string;
|
|
37
|
-
}>;
|