@devdogsuga/devtools 0.1.50 → 0.1.51-next.527
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 +81 -7
- package/bin/with-env.mjs +17 -0
- package/dist/app-registry-BoAnenqC.js +38 -0
- package/dist/{cli-t3gWyy3w.js → cli-D4LUUxUR.js} +2286 -718
- package/dist/launch.js +23 -11
- package/dist/load-Cp8AMERM.js +640 -0
- package/dist/load-Czhayrsk.js +2 -0
- package/dist/{menu-DYQ-Dd-O.js → menu-DjE4EZKO.js} +475 -178
- package/dist/{failure-log-DOt6E3hS.js → root-CsocfxnK.js} +104 -5
- package/dist/session-CmDxHEMn.js +2 -0
- package/dist/session-rbONwFvY.js +431 -0
- package/dist/{stack-C_50FafG.js → stack-7Lbym6Wq.js} +2 -3
- package/dist/stack-7qA2UP9g.js +2 -0
- package/dist/supabase-project-BoJKMCxL.js +766 -0
- package/dist/{telemetry-C07-ioHm.js → telemetry-BKFDtzXI.js} +8 -9
- package/dist/telemetry.js +1 -1
- package/dist/with-env.js +370 -0
- package/dist/worker-env-CAFmquhW.js +36 -0
- package/dist/wrangler-env-BrlNe4pm.js +34 -0
- package/env.ts +1 -1
- package/package.json +11 -20
- package/dist/local-env-BuHU5Ckd.js +0 -357
- package/dist/local-env-MqH18s-N.js +0 -2
- package/dist/peers-DvUH5VxX.js +0 -354
- package/dist/root-CjPWNkBu.js +0 -99
- package/dist/stack-o1Kml3V0.js +0 -2
- package/dist/supabase-project-C93xRFkN.js +0 -436
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as listContainerNames, i as foreignStackProjectId, n as containerPrefix, o as readProjectId, p as supabaseCapture, r as foreignStackMessage, t as STACK_API_PORT } from "./supabase-project-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { d as JSON_FLAG, f as SCOPES, g as positionals, m as createCatalog, p as YES, r as findRepoRoot, s as suppressFailureLog } from "./root-CsocfxnK.js";
|
|
2
|
+
import { E as resolveLayout, _ as loadEnvLoad, a as listContainerNames, g as loadEnv, i as foreignStackProjectId, n as containerPrefix, o as readProjectId, p as supabaseCapture, r as foreignStackMessage, t as STACK_API_PORT, x as devdogsugaRootOrNull } from "./supabase-project-BoJKMCxL.js";
|
|
3
|
+
import { c as reportDevtoolsError, f as isNonInteractive } from "./telemetry-BKFDtzXI.js";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { z } from "zod";
|
|
6
7
|
import { cancel, confirm, isCancel, log, note, select, text } from "@clack/prompts";
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
8
8
|
import { writeFile } from "node:fs/promises";
|
|
9
|
-
import { join } from "node:path";
|
|
10
9
|
import { execFileSync } from "node:child_process";
|
|
11
10
|
//#region ../cli-core/src/help.ts
|
|
12
11
|
/**
|
|
@@ -479,6 +478,67 @@ const jobsCommand = {
|
|
|
479
478
|
]
|
|
480
479
|
};
|
|
481
480
|
//#endregion
|
|
481
|
+
//#region src/moderation/catalog.ts
|
|
482
|
+
const TO = {
|
|
483
|
+
flag: "--to",
|
|
484
|
+
value: "<url>",
|
|
485
|
+
summary: "The receiver's URL. Defaults to the local Edge Function, http://127.0.0.1:54321/functions/v1/platform-moderation."
|
|
486
|
+
};
|
|
487
|
+
const SECRET = {
|
|
488
|
+
flag: "--secret",
|
|
489
|
+
value: "<whsec_...>",
|
|
490
|
+
summary: "The signing secret the receiver verifies with. Defaults to $PLATFORM_MODERATION_SECRET, then the well-known sample secret."
|
|
491
|
+
};
|
|
492
|
+
const moderationCommand = {
|
|
493
|
+
name: "moderation",
|
|
494
|
+
title: "Test a Report & Block receiver",
|
|
495
|
+
summary: "Send signed moderation messages to your app's receiver.",
|
|
496
|
+
hint: "send one action, or run the conformance samples",
|
|
497
|
+
subcommands: [{
|
|
498
|
+
name: "send",
|
|
499
|
+
title: "Send one signed action",
|
|
500
|
+
summary: "Send one signed moderation action to a receiver.",
|
|
501
|
+
hint: "like a moderator's decision, without the platform",
|
|
502
|
+
envFree: true,
|
|
503
|
+
noEnv: true,
|
|
504
|
+
options: [
|
|
505
|
+
TO,
|
|
506
|
+
SECRET,
|
|
507
|
+
{
|
|
508
|
+
flag: "--content-type",
|
|
509
|
+
value: "<type>",
|
|
510
|
+
summary: "For content.* actions. Defaults to session."
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
flag: "--content-ref",
|
|
514
|
+
value: "<id>",
|
|
515
|
+
summary: "For content.* actions: your row's id. Defaults to a sample id."
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
flag: "--user",
|
|
519
|
+
value: "<uuid>",
|
|
520
|
+
summary: "For user.* actions: the platform user id. Defaults to a sample id."
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
flag: "--replay",
|
|
524
|
+
summary: "Send the same message twice. The second must be ignored."
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
flag: "--stale",
|
|
528
|
+
summary: "Date the decision a minute in the past, as a late retry would. A receiver that has seen a newer decision must ignore it."
|
|
529
|
+
}
|
|
530
|
+
]
|
|
531
|
+
}, {
|
|
532
|
+
name: "samples",
|
|
533
|
+
title: "Run the conformance samples",
|
|
534
|
+
summary: "Send the conformance sequence and check each answer.",
|
|
535
|
+
hint: "pass or fail",
|
|
536
|
+
envFree: true,
|
|
537
|
+
noEnv: true,
|
|
538
|
+
options: [TO, SECRET]
|
|
539
|
+
}]
|
|
540
|
+
};
|
|
541
|
+
//#endregion
|
|
482
542
|
//#region src/roles/catalog.ts
|
|
483
543
|
/**
|
|
484
544
|
* `roles`' place in the command tree: declaration only, nothing here runs.
|
|
@@ -542,122 +602,6 @@ const oauthCommand = {
|
|
|
542
602
|
}
|
|
543
603
|
]
|
|
544
604
|
};
|
|
545
|
-
//#endregion
|
|
546
|
-
//#region src/passthrough/catalog.ts
|
|
547
|
-
const supabaseCommand = {
|
|
548
|
-
name: "supabase",
|
|
549
|
-
dryRun: "handled",
|
|
550
|
-
summary: "Run the Supabase CLI against the session's tier.",
|
|
551
|
-
hint: "fills in --db-url / --project-ref; the rest is yours",
|
|
552
|
-
surface: "cli-only"
|
|
553
|
-
};
|
|
554
|
-
const wranglerCommand = {
|
|
555
|
-
name: "wrangler",
|
|
556
|
-
dryRun: "handled",
|
|
557
|
-
summary: "Run Wrangler with the session's env.",
|
|
558
|
-
hint: "everything after the name goes to wrangler",
|
|
559
|
-
surface: "cli-only"
|
|
560
|
-
};
|
|
561
|
-
const drizzleKitCommand = {
|
|
562
|
-
name: "drizzle-kit",
|
|
563
|
-
dryRun: "handled",
|
|
564
|
-
summary: "Run drizzle-kit with the session's env.",
|
|
565
|
-
hint: "run it from the app that owns the config",
|
|
566
|
-
surface: "cli-only"
|
|
567
|
-
};
|
|
568
|
-
const psqlCommand = {
|
|
569
|
-
name: "psql",
|
|
570
|
-
dryRun: "handled",
|
|
571
|
-
summary: "Run psql against the session's database.",
|
|
572
|
-
hint: "connects with the tier's DB_URL unless you say otherwise",
|
|
573
|
-
surface: "cli-only"
|
|
574
|
-
};
|
|
575
|
-
//#endregion
|
|
576
|
-
//#region src/preset/catalog.ts
|
|
577
|
-
/**
|
|
578
|
-
* The Supabase jobs' places in the command tree: declaration only, nothing
|
|
579
|
-
* here runs. The handlers live in `commands.ts` beside it.
|
|
580
|
-
*
|
|
581
|
-
* Each sequences several tool calls (or asks a question between them) and
|
|
582
|
-
* prints the commands it ran. They were once subcommands of `preset`; now each
|
|
583
|
-
* is a top-level command under its own name, so the menu's first screen can
|
|
584
|
-
* offer them directly. `preset` is refused with the new name (`cli.ts`'s
|
|
585
|
-
* `RETIRED`).
|
|
586
|
-
*/
|
|
587
|
-
const restartStackCommand = {
|
|
588
|
-
name: "restart-stack",
|
|
589
|
-
title: "Restart local Supabase",
|
|
590
|
-
dryRun: "handled",
|
|
591
|
-
summary: "Restart the local Supabase stack.",
|
|
592
|
-
hint: "supabase stop, then start; picks up config.toml",
|
|
593
|
-
scope: "machine",
|
|
594
|
-
when: "instance-running"
|
|
595
|
-
};
|
|
596
|
-
const newMigrationCommand = {
|
|
597
|
-
name: "new-migration",
|
|
598
|
-
title: "Create a new migration",
|
|
599
|
-
dryRun: "handled",
|
|
600
|
-
summary: "Create an empty migration for an app's schema.",
|
|
601
|
-
hint: "supabase migration new <schema>_<description>",
|
|
602
|
-
scope: "repo",
|
|
603
|
-
options: [{
|
|
604
|
-
flag: "--app",
|
|
605
|
-
value: "<slug>",
|
|
606
|
-
summary: "Whose schema. Asked for when absent."
|
|
607
|
-
}]
|
|
608
|
-
};
|
|
609
|
-
const applyMigrationsCommand = {
|
|
610
|
-
name: "apply-migrations",
|
|
611
|
-
title: "Apply migrations to the database",
|
|
612
|
-
dryRun: "handled",
|
|
613
|
-
summary: "Apply new migrations to the session's database.",
|
|
614
|
-
hint: "supabase db push, then offers types:db",
|
|
615
|
-
scope: "endpoint",
|
|
616
|
-
options: [YES]
|
|
617
|
-
};
|
|
618
|
-
const pushConfigCommand = {
|
|
619
|
-
name: "push-config",
|
|
620
|
-
title: "Push Supabase settings",
|
|
621
|
-
dryRun: "handled",
|
|
622
|
-
summary: "Push config.toml to the session's hosted project.",
|
|
623
|
-
hint: "shows the diff first; the local stack reads it on restart",
|
|
624
|
-
scope: "endpoint",
|
|
625
|
-
options: [YES]
|
|
626
|
-
};
|
|
627
|
-
//#endregion
|
|
628
|
-
//#region src/run/catalog.ts
|
|
629
|
-
/**
|
|
630
|
-
* The three flags every `run` task takes.
|
|
631
|
-
*
|
|
632
|
-
* None carries a `prompt`, and that is the point rather than an omission.
|
|
633
|
-
* `run` opens a multiselect of the apps defining the task, so a wizard that
|
|
634
|
-
* asked "every package?", "which filter?" and "which tier?" up front would
|
|
635
|
-
* ask the same question three times and let the answers disagree. `--tier`
|
|
636
|
-
* is promptless for a different reason than `--filter`/`--all`, though: it is
|
|
637
|
-
* not a question `run` asks itself elsewhere (unlike `jobs run`'s own
|
|
638
|
-
* `--tier`, which opens a live picker) — it is a scripting input with no
|
|
639
|
-
* wizard equivalent, the same category `--json` sits in. `--help` still
|
|
640
|
-
* documents all three, which is where someone scripting this will look.
|
|
641
|
-
*
|
|
642
|
-
* Same reasoning as `VAULT_TARGET` above: the command owns the question, so the
|
|
643
|
-
* tree declares the flag and stays quiet.
|
|
644
|
-
*/
|
|
645
|
-
const RUN_OPTIONS = [
|
|
646
|
-
{
|
|
647
|
-
flag: "--filter",
|
|
648
|
-
value: "<pkg>",
|
|
649
|
-
summary: "Limit to a package. pnpm's own flag; skips the question."
|
|
650
|
-
},
|
|
651
|
-
{
|
|
652
|
-
flag: "--all",
|
|
653
|
-
summary: "Every package, unfiltered, with nothing asked."
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
flag: "--tier",
|
|
657
|
-
value: "<t>",
|
|
658
|
-
summary: "Load a tier's env into the run. CLI-only; never prompted."
|
|
659
|
-
}
|
|
660
|
-
];
|
|
661
605
|
const catalog = createCatalog({
|
|
662
606
|
usage: "pnpm devtools",
|
|
663
607
|
commonTasks: [
|
|
@@ -685,56 +629,52 @@ const catalog = createCatalog({
|
|
|
685
629
|
hint: "pnpm -F <package> run <script>"
|
|
686
630
|
},
|
|
687
631
|
oauthCommand,
|
|
688
|
-
doctorCommand
|
|
689
|
-
{
|
|
690
|
-
name: "run",
|
|
691
|
-
dryRun: "handled",
|
|
692
|
-
summary: "Run a pnpm workspace task, asking which apps first.",
|
|
693
|
-
hint: "deprecated: pnpm -r run <task>",
|
|
694
|
-
surface: "cli-only",
|
|
695
|
-
deprecated: "Use `pnpm -r run <task>` for every package or `pnpm -F <app> <task>` for one. It still runs, and each use is reported so it can be removed (TASK-404).",
|
|
696
|
-
subcommands: [
|
|
697
|
-
{
|
|
698
|
-
name: "build",
|
|
699
|
-
summary: "Compile every package an app needs.",
|
|
700
|
-
options: RUN_OPTIONS
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
name: "dev",
|
|
704
|
-
summary: "Start the development servers.",
|
|
705
|
-
options: RUN_OPTIONS
|
|
706
|
-
},
|
|
707
|
-
{
|
|
708
|
-
name: "typecheck",
|
|
709
|
-
summary: "Run tsc across the workspace.",
|
|
710
|
-
options: RUN_OPTIONS
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
name: "lint",
|
|
714
|
-
summary: "Run ESLint across the workspace.",
|
|
715
|
-
options: RUN_OPTIONS
|
|
716
|
-
},
|
|
717
|
-
{
|
|
718
|
-
name: "lint:fix",
|
|
719
|
-
summary: "Run ESLint and write what it can fix.",
|
|
720
|
-
options: RUN_OPTIONS
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
name: "test",
|
|
724
|
-
summary: "Run the unit tests.",
|
|
725
|
-
options: RUN_OPTIONS
|
|
726
|
-
}
|
|
727
|
-
]
|
|
728
|
-
}
|
|
632
|
+
doctorCommand
|
|
729
633
|
]
|
|
730
634
|
},
|
|
731
635
|
{
|
|
732
636
|
title: "Supabase",
|
|
733
637
|
commands: [
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
638
|
+
{
|
|
639
|
+
name: "restart-stack",
|
|
640
|
+
title: "Restart local Supabase",
|
|
641
|
+
dryRun: "handled",
|
|
642
|
+
summary: "Restart the local Supabase stack.",
|
|
643
|
+
hint: "supabase stop, then start; picks up config.toml",
|
|
644
|
+
scope: "machine",
|
|
645
|
+
when: "instance-running"
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
name: "new-migration",
|
|
649
|
+
title: "Create a new migration",
|
|
650
|
+
dryRun: "handled",
|
|
651
|
+
summary: "Create an empty migration for an app's schema.",
|
|
652
|
+
hint: "supabase migration new <schema>_<description>",
|
|
653
|
+
scope: "repo",
|
|
654
|
+
options: [{
|
|
655
|
+
flag: "--app",
|
|
656
|
+
value: "<slug>",
|
|
657
|
+
summary: "Whose schema. Asked for when absent."
|
|
658
|
+
}]
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
name: "apply-migrations",
|
|
662
|
+
title: "Apply migrations to the database",
|
|
663
|
+
dryRun: "handled",
|
|
664
|
+
summary: "Apply new migrations to the session's database.",
|
|
665
|
+
hint: "supabase db push, then offers types:db",
|
|
666
|
+
scope: "endpoint",
|
|
667
|
+
options: [YES]
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
name: "push-config",
|
|
671
|
+
title: "Push Supabase settings",
|
|
672
|
+
dryRun: "handled",
|
|
673
|
+
summary: "Push config.toml to the session's hosted project.",
|
|
674
|
+
hint: "shows the diff first; the local stack reads it on restart",
|
|
675
|
+
scope: "endpoint",
|
|
676
|
+
options: [YES]
|
|
677
|
+
}
|
|
738
678
|
]
|
|
739
679
|
},
|
|
740
680
|
{
|
|
@@ -743,15 +683,43 @@ const catalog = createCatalog({
|
|
|
743
683
|
},
|
|
744
684
|
{
|
|
745
685
|
title: "Configuration & integrations",
|
|
746
|
-
commands: [
|
|
686
|
+
commands: [
|
|
687
|
+
envCommand,
|
|
688
|
+
rolesCommand,
|
|
689
|
+
moderationCommand
|
|
690
|
+
]
|
|
747
691
|
},
|
|
748
692
|
{
|
|
749
693
|
title: "The real tools",
|
|
750
694
|
commands: [
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
695
|
+
{
|
|
696
|
+
name: "supabase",
|
|
697
|
+
dryRun: "handled",
|
|
698
|
+
summary: "Run the Supabase CLI against the session's tier.",
|
|
699
|
+
hint: "fills in --db-url / --project-ref; the rest is yours",
|
|
700
|
+
surface: "cli-only"
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
name: "wrangler",
|
|
704
|
+
dryRun: "handled",
|
|
705
|
+
summary: "Run Wrangler with the session's env.",
|
|
706
|
+
hint: "everything after the name goes to wrangler",
|
|
707
|
+
surface: "cli-only"
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
name: "drizzle-kit",
|
|
711
|
+
dryRun: "handled",
|
|
712
|
+
summary: "Run drizzle-kit with the session's env.",
|
|
713
|
+
hint: "run it from the app that owns the config",
|
|
714
|
+
surface: "cli-only"
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
name: "psql",
|
|
718
|
+
dryRun: "handled",
|
|
719
|
+
summary: "Run psql against the session's database.",
|
|
720
|
+
hint: "connects with the tier's DB_URL unless you say otherwise",
|
|
721
|
+
surface: "cli-only"
|
|
722
|
+
}
|
|
755
723
|
]
|
|
756
724
|
},
|
|
757
725
|
{
|
|
@@ -761,6 +729,335 @@ const catalog = createCatalog({
|
|
|
761
729
|
]
|
|
762
730
|
});
|
|
763
731
|
//#endregion
|
|
732
|
+
//#region src/product/catalog.ts
|
|
733
|
+
/**
|
|
734
|
+
* The command tree devtools shows inside a product repository (one with a
|
|
735
|
+
* `devtools.config.json`): declaration only, nothing here runs. The handlers
|
|
736
|
+
* are in `commands.ts`.
|
|
737
|
+
*
|
|
738
|
+
* Smaller than the platform's tree on purpose. A product has no tiers, no env
|
|
739
|
+
* registry and no workspace of Workers, so everything about those is absent
|
|
740
|
+
* rather than present and refusing.
|
|
741
|
+
*/
|
|
742
|
+
const TARGET_OPTIONS = [{
|
|
743
|
+
flag: "--local",
|
|
744
|
+
summary: "Use the Docker stack on this machine."
|
|
745
|
+
}, {
|
|
746
|
+
flag: "--hosted",
|
|
747
|
+
summary: "Use your own hosted project (from .env.tooling)."
|
|
748
|
+
}];
|
|
749
|
+
const productSetupCommand = {
|
|
750
|
+
name: "setup",
|
|
751
|
+
title: "Set up this checkout",
|
|
752
|
+
summary: "Your own database (Docker or a hosted project), the schema, the types and sign in.",
|
|
753
|
+
hint: "run this first",
|
|
754
|
+
envFree: true,
|
|
755
|
+
options: [
|
|
756
|
+
...TARGET_OPTIONS,
|
|
757
|
+
{
|
|
758
|
+
flag: "--download-types",
|
|
759
|
+
summary: "Fetch the types CI built for your schema instead of generating them."
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
flag: "--skip-oauth",
|
|
763
|
+
summary: "Leave \"Sign in with DevDogs\" for later (`devtools oauth`)."
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
flag: "--yes",
|
|
767
|
+
summary: "Allow the schema sync to drop things in your database."
|
|
768
|
+
}
|
|
769
|
+
]
|
|
770
|
+
};
|
|
771
|
+
const productDoctorCommand = {
|
|
772
|
+
name: "doctor",
|
|
773
|
+
title: "Check my setup",
|
|
774
|
+
summary: "Docker, the pinned Supabase CLI, .env, the database and the types.",
|
|
775
|
+
dryRun: "read-only",
|
|
776
|
+
envFree: true
|
|
777
|
+
};
|
|
778
|
+
const productDbCommand = {
|
|
779
|
+
name: "db",
|
|
780
|
+
summary: "Keep your development database and generated types in step with supabase/schemas.",
|
|
781
|
+
envFree: true,
|
|
782
|
+
subcommands: [
|
|
783
|
+
{
|
|
784
|
+
name: "sync",
|
|
785
|
+
title: "Apply the schema to my database",
|
|
786
|
+
summary: "Diff supabase/schemas against what your database has had applied, and apply the difference.",
|
|
787
|
+
envFree: true,
|
|
788
|
+
dryRun: "handled",
|
|
789
|
+
options: [
|
|
790
|
+
...TARGET_OPTIONS,
|
|
791
|
+
{
|
|
792
|
+
flag: "--rebuild",
|
|
793
|
+
summary: "Reset the local database and start over from the schema, then register sign in again."
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
flag: "--yes",
|
|
797
|
+
summary: "Allow changes that drop tables or columns."
|
|
798
|
+
}
|
|
799
|
+
]
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
name: "types",
|
|
803
|
+
title: "Generate the database types",
|
|
804
|
+
summary: "Generate (or download) the types for the current schema. Never committed.",
|
|
805
|
+
envFree: true,
|
|
806
|
+
dryRun: "handled",
|
|
807
|
+
options: [
|
|
808
|
+
...TARGET_OPTIONS,
|
|
809
|
+
{
|
|
810
|
+
flag: "--download",
|
|
811
|
+
summary: "Download the types CI built for this exact schema, with no database needed."
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
flag: "--pack",
|
|
815
|
+
value: "<file>",
|
|
816
|
+
summary: "Also pack the generated output into a .tgz release asset (CI)."
|
|
817
|
+
}
|
|
818
|
+
]
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
name: "hash",
|
|
822
|
+
summary: "Print the schema hash that names the generated types.",
|
|
823
|
+
surface: "cli-only",
|
|
824
|
+
envFree: true,
|
|
825
|
+
dryRun: "read-only"
|
|
826
|
+
}
|
|
827
|
+
]
|
|
828
|
+
};
|
|
829
|
+
const productDevDataCommand = {
|
|
830
|
+
name: "dev-data",
|
|
831
|
+
title: "Fill my database with sample data",
|
|
832
|
+
summary: "Run the product's dev-data SQL for the member who signed in, in one transaction.",
|
|
833
|
+
hint: "after your first sign in",
|
|
834
|
+
envFree: true,
|
|
835
|
+
dryRun: "handled",
|
|
836
|
+
options: [...TARGET_OPTIONS, {
|
|
837
|
+
flag: "--member",
|
|
838
|
+
value: "<email|id>",
|
|
839
|
+
summary: "Seed for this user instead of the latest DevDogs sign in."
|
|
840
|
+
}]
|
|
841
|
+
};
|
|
842
|
+
const productCatalog = createCatalog({
|
|
843
|
+
usage: "devtools",
|
|
844
|
+
commonTasks: [
|
|
845
|
+
["setup", "Prepare this checkout"],
|
|
846
|
+
["db sync", "Apply supabase/schemas to your database"],
|
|
847
|
+
["db types --download", "Get the generated types without a database"],
|
|
848
|
+
["dev-data", "Fill your database with sample data"],
|
|
849
|
+
["doctor", "Check the setup"]
|
|
850
|
+
],
|
|
851
|
+
groups: [
|
|
852
|
+
{
|
|
853
|
+
title: "Get started",
|
|
854
|
+
commands: [
|
|
855
|
+
productSetupCommand,
|
|
856
|
+
productDoctorCommand,
|
|
857
|
+
oauthCommand
|
|
858
|
+
]
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
title: "Database",
|
|
862
|
+
commands: [productDbCommand, productDevDataCommand]
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
title: "Report & Block",
|
|
866
|
+
commands: [moderationCommand]
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
title: "The real tools",
|
|
870
|
+
commands: [{
|
|
871
|
+
name: "supabase",
|
|
872
|
+
summary: "The Supabase CLI at the version this product is pinned to.",
|
|
873
|
+
hint: "start, status, stop, ...",
|
|
874
|
+
surface: "cli-only",
|
|
875
|
+
envFree: true,
|
|
876
|
+
dryRun: "handled"
|
|
877
|
+
}]
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
title: "CI",
|
|
881
|
+
commands: [{
|
|
882
|
+
name: "check",
|
|
883
|
+
summary: "Structural checks over the checkout, for CI.",
|
|
884
|
+
surface: "cli-only",
|
|
885
|
+
envFree: true,
|
|
886
|
+
noEnv: true,
|
|
887
|
+
dryRun: "read-only",
|
|
888
|
+
subcommands: [{
|
|
889
|
+
name: "env-example",
|
|
890
|
+
summary: ".env.example holds public values only: it ships in the app.",
|
|
891
|
+
surface: "cli-only",
|
|
892
|
+
envFree: true,
|
|
893
|
+
noEnv: true
|
|
894
|
+
}]
|
|
895
|
+
}, completionsCommand]
|
|
896
|
+
}
|
|
897
|
+
]
|
|
898
|
+
});
|
|
899
|
+
//#endregion
|
|
900
|
+
//#region src/active.ts
|
|
901
|
+
let product = false;
|
|
902
|
+
/** Switches to the product tree. Called by `launch()`; tests reset it. */
|
|
903
|
+
function setProductMode(on) {
|
|
904
|
+
product = on;
|
|
905
|
+
}
|
|
906
|
+
function isProductMode() {
|
|
907
|
+
return product;
|
|
908
|
+
}
|
|
909
|
+
function activeCatalog() {
|
|
910
|
+
return product ? productCatalog : catalog;
|
|
911
|
+
}
|
|
912
|
+
//#endregion
|
|
913
|
+
//#region src/product/config.ts
|
|
914
|
+
/**
|
|
915
|
+
* `devtools.config.json`: what makes a repository a product devtools knows how
|
|
916
|
+
* to set up (DogDays, DogPack, and any future one).
|
|
917
|
+
*
|
|
918
|
+
* The file sits at the repo root. Its presence is what switches devtools into
|
|
919
|
+
* product mode: a smaller command set (`setup`, `db sync`, `db types`,
|
|
920
|
+
* `dev-data`, `oauth`) that needs no tiers, no env registry and no workspace.
|
|
921
|
+
* The platform repository has no such file and keeps the full CLI.
|
|
922
|
+
*
|
|
923
|
+
* Nothing in the file is secret. Secrets live in `.env.tooling`, which is
|
|
924
|
+
* git-ignored; the public configuration the app reads at run time lives in
|
|
925
|
+
* `.env`, written from `.env.example`.
|
|
926
|
+
*/
|
|
927
|
+
const CONFIG_FILE = "devtools.config.json";
|
|
928
|
+
const relativePath = z.string().min(1).refine((p) => !p.startsWith("/") && !p.includes(".."), { message: "must be a relative path inside the repository" });
|
|
929
|
+
const typesSchema = z.discriminatedUnion("kind", [z.object({
|
|
930
|
+
kind: z.literal("typescript"),
|
|
931
|
+
/** The generated `.ts` file. Git-ignored. */
|
|
932
|
+
out: relativePath
|
|
933
|
+
}), z.object({
|
|
934
|
+
kind: z.literal("supadart"),
|
|
935
|
+
/** The directory supadart writes models to. Git-ignored. */
|
|
936
|
+
out: relativePath,
|
|
937
|
+
/** supadart's own config, whose `output` must match `out`. */
|
|
938
|
+
config: relativePath.default("supadart.yaml")
|
|
939
|
+
})]);
|
|
940
|
+
const productConfigSchema = z.object({
|
|
941
|
+
$schema: z.string().optional(),
|
|
942
|
+
/** Lowercase id: `dogdays`. Names the generated-types assets. */
|
|
943
|
+
product: z.string().regex(/^[a-z][a-z0-9-]*$/),
|
|
944
|
+
/** Display name: `DogDays`. */
|
|
945
|
+
name: z.string().min(1),
|
|
946
|
+
supabase: z.object({
|
|
947
|
+
/** The directory holding `config.toml` and `schemas/`. */
|
|
948
|
+
dir: relativePath.default("supabase"),
|
|
949
|
+
/** The Supabase CLI version this product is built with. Part of the types hash. */
|
|
950
|
+
version: z.string().regex(/^\d+\.\d+\.\d+$/, "an exact version, 2.117.0"),
|
|
951
|
+
/** Schemas that hold the product's tables, for type generation. */
|
|
952
|
+
schemas: z.array(z.string().min(1)).min(1)
|
|
953
|
+
}),
|
|
954
|
+
env: z.object({
|
|
955
|
+
/** The committed manifest: public keys only. */
|
|
956
|
+
example: relativePath.default(".env.example"),
|
|
957
|
+
/** What the app reads. Git-ignored. */
|
|
958
|
+
file: relativePath.default(".env"),
|
|
959
|
+
/** Secrets the tooling needs (`DB_URL`, `SECRET_KEY`). Git-ignored. */
|
|
960
|
+
tooling: relativePath.default(".env.tooling"),
|
|
961
|
+
/** Which `.env` keys receive the project's URL and publishable key. */
|
|
962
|
+
keys: z.object({
|
|
963
|
+
url: z.string().default("SUPABASE_URL"),
|
|
964
|
+
publishableKey: z.string().default("SUPABASE_PUBLISHABLE_KEY")
|
|
965
|
+
}).default({
|
|
966
|
+
url: "SUPABASE_URL",
|
|
967
|
+
publishableKey: "SUPABASE_PUBLISHABLE_KEY"
|
|
968
|
+
})
|
|
969
|
+
}).default({
|
|
970
|
+
example: ".env.example",
|
|
971
|
+
file: ".env",
|
|
972
|
+
tooling: ".env.tooling",
|
|
973
|
+
keys: {
|
|
974
|
+
url: "SUPABASE_URL",
|
|
975
|
+
publishableKey: "SUPABASE_PUBLISHABLE_KEY"
|
|
976
|
+
}
|
|
977
|
+
}),
|
|
978
|
+
types: typesSchema,
|
|
979
|
+
/** Where CI publishes generated types, for `db types --download`. */
|
|
980
|
+
generatedTypes: z.object({
|
|
981
|
+
/** `DevDogsUGA/DogDays`. */
|
|
982
|
+
repo: z.string().regex(/^[\w.-]+\/[\w.-]+$/),
|
|
983
|
+
tag: z.string().default("generated-types")
|
|
984
|
+
}).optional(),
|
|
985
|
+
oauth: z.object({
|
|
986
|
+
/**
|
|
987
|
+
* App return addresses to allow on the project (`dev.dogpack://login-callback`,
|
|
988
|
+
* `http://localhost:3000/auth/callback`). Added to `uri_allow_list` on a
|
|
989
|
+
* hosted project; for local Docker they belong in `supabase/config.toml`.
|
|
990
|
+
*/
|
|
991
|
+
redirectUris: z.array(z.string()).default([]) }).default({ redirectUris: [] }),
|
|
992
|
+
/** `dev-data`: a SQL file that fills a development database. */
|
|
993
|
+
devData: z.object({
|
|
994
|
+
file: relativePath,
|
|
995
|
+
/** One line for `--help`. */
|
|
996
|
+
summary: z.string().optional()
|
|
997
|
+
}).optional(),
|
|
998
|
+
/** What `setup` prints as the last step. */
|
|
999
|
+
runHint: z.string().optional(),
|
|
1000
|
+
/** Databases `dev-data` must never touch. */
|
|
1001
|
+
production: z.object({
|
|
1002
|
+
/** Hosted project refs and vanity names: `dogpack`, `abcdefghij`. */
|
|
1003
|
+
projects: z.array(z.string()).default([]) }).default({ projects: [] })
|
|
1004
|
+
});
|
|
1005
|
+
var ProductConfigError = class extends Error {
|
|
1006
|
+
file;
|
|
1007
|
+
constructor(message, file) {
|
|
1008
|
+
super(message);
|
|
1009
|
+
this.file = file;
|
|
1010
|
+
this.name = "ProductConfigError";
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
/** The directory holding `devtools.config.json`, searching upward from `from`. */
|
|
1014
|
+
function findProductRoot(from = process.cwd()) {
|
|
1015
|
+
let dir = resolve(from);
|
|
1016
|
+
for (;;) {
|
|
1017
|
+
if (existsSync(join(dir, "devtools.config.json"))) return dir;
|
|
1018
|
+
const parent = dirname(dir);
|
|
1019
|
+
if (parent === dir) return null;
|
|
1020
|
+
dir = parent;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
function parseProductConfig(text, file) {
|
|
1024
|
+
let json;
|
|
1025
|
+
try {
|
|
1026
|
+
json = JSON.parse(text);
|
|
1027
|
+
} catch (err) {
|
|
1028
|
+
throw new ProductConfigError(`${file} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`, file);
|
|
1029
|
+
}
|
|
1030
|
+
const result = productConfigSchema.safeParse(json);
|
|
1031
|
+
if (!result.success) throw new ProductConfigError(`${file} is not valid:\n${result.error.issues.map((i) => ` ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n")}`, file);
|
|
1032
|
+
return result.data;
|
|
1033
|
+
}
|
|
1034
|
+
function loadProductConfig(root) {
|
|
1035
|
+
const file = join(root, CONFIG_FILE);
|
|
1036
|
+
return parseProductConfig(readFileSync(file, "utf8"), file);
|
|
1037
|
+
}
|
|
1038
|
+
function productAt(root) {
|
|
1039
|
+
const config = loadProductConfig(root);
|
|
1040
|
+
const supabaseDir = join(root, config.supabase.dir);
|
|
1041
|
+
return {
|
|
1042
|
+
root,
|
|
1043
|
+
config,
|
|
1044
|
+
paths: {
|
|
1045
|
+
supabaseDir,
|
|
1046
|
+
schemasDir: join(supabaseDir, "schemas"),
|
|
1047
|
+
migrationsDir: join(supabaseDir, "migrations"),
|
|
1048
|
+
envExample: join(root, config.env.example),
|
|
1049
|
+
envFile: join(root, config.env.file),
|
|
1050
|
+
toolingFile: join(root, config.env.tooling),
|
|
1051
|
+
stateFile: join(root, ".devtools", "state.json")
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
/** The product the working directory belongs to, or null outside one. */
|
|
1056
|
+
function currentProduct(from = process.cwd()) {
|
|
1057
|
+
const root = findProductRoot(from);
|
|
1058
|
+
return root === null ? null : productAt(root);
|
|
1059
|
+
}
|
|
1060
|
+
//#endregion
|
|
764
1061
|
//#region ../cli-core/src/db/generated-env.ts
|
|
765
1062
|
/**
|
|
766
1063
|
* Regenerating `.env.generated` from a Supabase stack that is ALREADY
|
|
@@ -772,7 +1069,7 @@ const catalog = createCatalog({
|
|
|
772
1069
|
* except `.env.generated` (the connection block `startLocalStack`,
|
|
773
1070
|
* the CLI's `db/stack.ts`, writes) only ever lands in the workspace that actually ran
|
|
774
1071
|
* it. Left alone, this checkout sees port 54321 listening with the file
|
|
775
|
-
* missing, and
|
|
1072
|
+
* missing, and `env-runtime/load`'s `selectEnvFiles` either warns (an
|
|
776
1073
|
* unqualified session) or throws `LocalStackOfflineError` (an explicit
|
|
777
1074
|
* `development:local` one) — both of which used to mean the contributor
|
|
778
1075
|
* ran `supabase status -o env > .env.generated` by hand.
|
|
@@ -793,9 +1090,9 @@ const catalog = createCatalog({
|
|
|
793
1090
|
* otherwise just repeat "nothing is listening" advice for a port that very
|
|
794
1091
|
* much has something listening on it.
|
|
795
1092
|
*/
|
|
796
|
-
/** The file this module writes and checks — see
|
|
1093
|
+
/** The file this module writes and checks — see `env-runtime/load`'s
|
|
797
1094
|
* `GENERATED_FILE`, duplicated as a literal rather than imported so this
|
|
798
|
-
* module never has to load the `@devdogsuga/env` peer just for one
|
|
1095
|
+
* module never has to load the `@devdogsuga/sdk/env` peer just for one
|
|
799
1096
|
* constant. Keep in sync if that constant's value ever changes. */
|
|
800
1097
|
const GENERATED_FILE = ".env.generated";
|
|
801
1098
|
/**
|
|
@@ -846,7 +1143,7 @@ async function ensureGeneratedEnvFile(tier, devDatabase, deps) {
|
|
|
846
1143
|
* The production wiring for `EnsureGeneratedEnvDeps`, shared by
|
|
847
1144
|
* `../env-entry.ts` (via `realEnvEntryDeps`) and `../stack.ts`'s `db
|
|
848
1145
|
* status`. `probeLocalStack` is passed in rather than resolved here because
|
|
849
|
-
* both callers already hold the loaded
|
|
1146
|
+
* both callers already hold the loaded `env-runtime/load` peer their
|
|
850
1147
|
* own dispatch needs anyway — resolving it again here would mean two
|
|
851
1148
|
* different copies of the same optional peer in play.
|
|
852
1149
|
*/
|
|
@@ -1681,4 +1978,4 @@ async function runMenu(catalog, dispatch, env = probeEnvironment(), options = {}
|
|
|
1681
1978
|
return dispatch(chosen.argv);
|
|
1682
1979
|
}
|
|
1683
1980
|
//#endregion
|
|
1684
|
-
export {
|
|
1981
|
+
export { renderCommandList as A, currentProduct as C, setProductMode as D, isProductMode as E, catalog as O, ProductConfigError as S, activeCatalog as T, bail as _, recordEnteredTier as a, explainError as b, describeEnvironment as c, resolveDbConnection as d, sessionIsLocal as f, setMenuEnvHook as g, realEnvEntryDeps as h, beginInvocation as i, renderHelp as j, helpPath as k, probeEnvironment as l, enterSessionEnvironment as m, bareGroupStartPath as n, recordResolved as o, sessionLabel as p, runMenu as r, reproducibleCommand as s, bareGroupStartFlags as t, nonEmpty as u, errorMessage as v, findProductRoot as w, unwrap as x, explain as y };
|