@donotdev/cli 0.0.19 → 0.0.21
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 +31 -0
- package/dependencies-matrix.json +205 -50
- package/dist/bin/commands/agent-setup.js +2 -2
- package/dist/bin/commands/build.js +6 -6
- package/dist/bin/commands/bump.js +495 -70
- package/dist/bin/commands/cacheout.js +6 -6
- package/dist/bin/commands/coach.js +6 -6
- package/dist/bin/commands/create-app.js +24 -16
- package/dist/bin/commands/create-project.js +114 -18
- package/dist/bin/commands/db.js +142136 -0
- package/dist/bin/commands/deploy.js +354 -126
- package/dist/bin/commands/dev.js +6 -6
- package/dist/bin/commands/doctor.js +140 -33
- package/dist/bin/commands/emu.js +6 -6
- package/dist/bin/commands/format.js +6 -6
- package/dist/bin/commands/get-demo.js +11 -6
- package/dist/bin/commands/make-admin.js +14210 -13770
- package/dist/bin/commands/preview.js +6 -6
- package/dist/bin/commands/seed.js +142426 -0
- package/dist/bin/commands/setup-cicd.js +8904 -0
- package/dist/bin/commands/setup.js +259 -212
- package/dist/bin/commands/staging.js +361 -127
- package/dist/bin/commands/sync-secrets.js +55 -33
- package/dist/bin/commands/type-check.js +16 -10
- package/dist/bin/commands/wai.js +6 -6
- package/dist/bin/dndev.js +194 -188
- package/dist/bin/donotdev.js +139 -189
- package/dist/index.js +468 -144
- package/package.json +1 -1
- package/templates/app-demo/.env.example +1 -0
- package/templates/{root-consumer → app-demo}/entities/ExampleEntity.ts.example +15 -9
- package/templates/app-demo/index.html.example +1 -1
- package/templates/app-demo/public/apple-touch-icon.png.example +0 -0
- package/templates/app-demo/public/favicon.svg.example +1 -0
- package/templates/app-demo/public/icon-192x192.png.example +0 -0
- package/templates/app-demo/public/icon-512x512.png.example +0 -0
- package/templates/app-demo/src/App.tsx.example +3 -1
- package/templates/app-demo/src/config/app.ts.example +1 -0
- package/templates/app-demo/src/entities/booking.ts.example +75 -0
- package/templates/app-demo/src/entities/onboarding.ts.example +160 -0
- package/templates/app-demo/src/entities/product.ts.example +12 -0
- package/templates/app-demo/src/entities/quote.ts.example +70 -0
- package/templates/app-demo/src/pages/ChangelogPage.tsx.example +28 -1
- package/templates/app-demo/src/pages/ConditionalFormPage.tsx.example +88 -0
- package/templates/app-demo/src/pages/DashboardPage.tsx.example +2 -0
- package/templates/app-demo/src/pages/HomePage.tsx.example +355 -2
- package/templates/app-demo/src/pages/OnboardingPage.tsx.example +47 -0
- package/templates/app-demo/src/pages/PricingPage.tsx.example +28 -1
- package/templates/app-demo/src/pages/ProductsPage.tsx.example +2 -0
- package/templates/app-demo/src/pages/ProfilePage.tsx.example +2 -0
- package/templates/app-demo/src/pages/SettingsPage.tsx.example +2 -0
- package/templates/app-demo/src/pages/ShowcaseDetailPage.tsx.example +22 -16
- package/templates/app-demo/src/pages/ShowcasePage.tsx.example +3 -1
- package/templates/app-demo/src/pages/components/ComponentRenderer.tsx.example +147 -51
- package/templates/app-demo/src/pages/components/ComponentsData.tsx.example +103 -21
- package/templates/app-demo/src/pages/components/componentConfig.ts.example +139 -59
- package/templates/app-demo/src/pages/legal/LegalPage.tsx.example +12 -1
- package/templates/app-demo/src/pages/legal/PrivacyPage.tsx.example +10 -1
- package/templates/app-demo/src/pages/legal/TermsPage.tsx.example +10 -1
- package/templates/app-demo/src/themes.css.example +289 -77
- package/templates/app-demo/stats.html.example +4949 -0
- package/templates/app-dndev/index.html.example +164 -0
- package/templates/app-dndev/public/logo.svg.example +1 -0
- package/templates/app-dndev/public/manifest.json.example +10 -0
- package/templates/app-dndev/src/App.tsx.example +35 -0
- package/templates/app-dndev/src/components/CockpitLayout.css.example +181 -0
- package/templates/app-dndev/src/components/CockpitLayout.tsx.example +209 -0
- package/templates/app-dndev/src/components/Kanban.css.example +385 -0
- package/templates/app-dndev/src/components/ModeToggle.tsx.example +32 -0
- package/templates/app-dndev/src/components/OverlaySlot.tsx.example +68 -0
- package/templates/app-dndev/src/components/TerminalPanel.css.example +228 -0
- package/templates/app-dndev/src/components/TerminalPanel.tsx.example +714 -0
- package/templates/app-dndev/src/components/markdown-prose.css.example +49 -0
- package/templates/app-dndev/src/components/phases/CaptainLog.tsx.example +107 -0
- package/templates/app-dndev/src/components/phases/ContextTabs.tsx.example +352 -0
- package/templates/app-dndev/src/components/phases/PhaseCard.tsx.example +126 -0
- package/templates/app-dndev/src/components/phases/PhaseDetail.tsx.example +147 -0
- package/templates/app-dndev/src/components/phases/ReviewPanel.tsx.example +115 -0
- package/templates/app-dndev/src/components/phases/phaseData.ts.example +366 -0
- package/templates/app-dndev/src/config/app.ts.example +103 -0
- package/templates/app-dndev/src/config/commands.ts.example +171 -0
- package/templates/app-dndev/src/config/legal.ts.example +170 -0
- package/templates/app-dndev/src/config/providers.ts.example +7 -0
- package/templates/app-dndev/src/globals.css.example +10 -0
- package/templates/app-dndev/src/hooks/useDndevFile.ts.example +144 -0
- package/templates/app-dndev/src/main.tsx.example +21 -0
- package/templates/app-dndev/src/pages/BoardPage.tsx.example +640 -0
- package/templates/app-dndev/src/pages/GrillPage.tsx.example +658 -0
- package/templates/app-dndev/src/pages/HomePage.tsx.example +347 -0
- package/templates/app-dndev/src/pages/NotFoundPage.tsx.example +33 -0
- package/templates/app-dndev/src/pages/PhasesPage.tsx.example +137 -0
- package/templates/app-dndev/src/pages/SettingsPage.tsx.example +64 -0
- package/templates/app-dndev/src/pages/legal/LegalNoticePage.tsx.example +75 -0
- package/templates/app-dndev/src/pages/legal/PrivacyPage.tsx.example +69 -0
- package/templates/app-dndev/src/pages/legal/TermsPage.tsx.example +71 -0
- package/templates/app-dndev/src/stores/dndevStore.ts.example +386 -0
- package/templates/app-dndev/src/themes.css.example +161 -0
- package/templates/app-dndev/terminal-sidecar.cjs.example +341 -0
- package/templates/app-dndev/tsconfig.json.example +9 -0
- package/templates/app-dndev/vite.config.ts.example +24 -0
- package/templates/app-vite/index.html.example +1 -1
- package/templates/functions-supabase/supabase/functions/.env.example +0 -2
- package/templates/root-consumer/.claude/commands/grill.md.example +86 -8
- package/templates/root-consumer/.dndev.secrets.example +32 -0
- package/templates/root-consumer/.gitignore.example +3 -0
- package/templates/root-consumer/AI.md.example +4 -0
- package/templates/root-consumer/entities/index.ts.example +2 -5
- package/templates/root-consumer/guides/dndev/COMPONENTS_ATOMIC.md.example +4 -0
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +23 -20
- package/templates/root-consumer/guides/dndev/INDEX.md.example +1 -0
- package/templates/root-consumer/guides/dndev/SETUP_BILLING.md.example +3 -7
- package/templates/root-consumer/guides/dndev/SETUP_CICD.md.example +115 -0
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +41 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +13 -18
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +17 -12
- package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +185 -251
- package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +26 -8
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +66 -49
- package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +6 -5
- package/templates/root-consumer/guides/wai-way/blueprints/2_entities.md.example +9 -9
- package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +1 -1
- package/templates/root-consumer/guides/wai-way/blueprints/4_configure.md.example +7 -6
- package/templates/root-consumer/guides/wai-way/context_map.json.example +51 -20
- package/templates/root-consumer/guides/wai-way/hld_template.md.example +138 -0
- package/templates/root-consumer/guides/wai-way/lld_template.md.example +103 -0
- package/templates/root-consumer/guides/wai-way/prd_template.md.example +140 -0
- /package/templates/{root-consumer → app-demo}/entities/Contact.ts.example +0 -0
- /package/templates/{root-consumer → app-demo}/entities/demo.ts.example +0 -0
|
@@ -7077,7 +7077,7 @@ var init_PathResolver = __esm({
|
|
|
7077
7077
|
}
|
|
7078
7078
|
const detectedFormat = this._detectFormat(filePath, format);
|
|
7079
7079
|
let writeContent;
|
|
7080
|
-
if (
|
|
7080
|
+
if (Buffer.isBuffer(content)) {
|
|
7081
7081
|
writeContent = content;
|
|
7082
7082
|
} else if (detectedFormat === "json" && typeof content === "object") {
|
|
7083
7083
|
writeContent = JSON.stringify(content, null, 2);
|
|
@@ -7086,7 +7086,7 @@ var init_PathResolver = __esm({
|
|
|
7086
7086
|
}
|
|
7087
7087
|
try {
|
|
7088
7088
|
return await safeExecuteAsync(async () => {
|
|
7089
|
-
if (
|
|
7089
|
+
if (Buffer.isBuffer(writeContent)) {
|
|
7090
7090
|
await fs.promises.writeFile(normalizedPath, writeContent);
|
|
7091
7091
|
} else {
|
|
7092
7092
|
await fs.promises.writeFile(normalizedPath, writeContent, "utf8");
|
|
@@ -7138,7 +7138,7 @@ var init_PathResolver = __esm({
|
|
|
7138
7138
|
}
|
|
7139
7139
|
const detectedFormat = this._detectFormat(filePath, format);
|
|
7140
7140
|
let writeContent;
|
|
7141
|
-
if (
|
|
7141
|
+
if (Buffer.isBuffer(content)) {
|
|
7142
7142
|
writeContent = content;
|
|
7143
7143
|
} else if (detectedFormat === "json" && typeof content === "object") {
|
|
7144
7144
|
writeContent = JSON.stringify(content, null, 2);
|
|
@@ -7146,7 +7146,7 @@ var init_PathResolver = __esm({
|
|
|
7146
7146
|
writeContent = String(content);
|
|
7147
7147
|
}
|
|
7148
7148
|
try {
|
|
7149
|
-
if (
|
|
7149
|
+
if (Buffer.isBuffer(writeContent)) {
|
|
7150
7150
|
fs.writeFileSync(normalizedPath, writeContent);
|
|
7151
7151
|
} else {
|
|
7152
7152
|
fs.writeFileSync(normalizedPath, writeContent, "utf8");
|
|
@@ -8034,7 +8034,7 @@ var init_typed_file_operations = __esm({
|
|
|
8034
8034
|
});
|
|
8035
8035
|
|
|
8036
8036
|
// packages/tooling/src/bundler/utils.ts
|
|
8037
|
-
import { Buffer
|
|
8037
|
+
import { Buffer } from "node:buffer";
|
|
8038
8038
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
8039
8039
|
import { createRequire as createRequire3 } from "node:module";
|
|
8040
8040
|
import { dirname as dirname3, resolve as resolve3 } from "node:path";
|
|
@@ -8051,7 +8051,7 @@ var init_utils = __esm({
|
|
|
8051
8051
|
globalThis.require = require2;
|
|
8052
8052
|
globalThis.__filename = __filename;
|
|
8053
8053
|
globalThis.__dirname = __dirname;
|
|
8054
|
-
globalThis.Buffer =
|
|
8054
|
+
globalThis.Buffer = Buffer;
|
|
8055
8055
|
globalThis.process = process;
|
|
8056
8056
|
if (typeof global === "undefined") {
|
|
8057
8057
|
globalThis.global = globalThis;
|
|
@@ -8251,7 +8251,28 @@ var init_cli_input = __esm({
|
|
|
8251
8251
|
}
|
|
8252
8252
|
});
|
|
8253
8253
|
|
|
8254
|
-
// packages/tooling/src/
|
|
8254
|
+
// packages/tooling/src/utils/secrets-resolver.ts
|
|
8255
|
+
function parseEnvFile(filePath) {
|
|
8256
|
+
if (!pathExists(filePath)) return {};
|
|
8257
|
+
const content = readSync(filePath, { format: "text" });
|
|
8258
|
+
if (typeof content !== "string" || !content) return {};
|
|
8259
|
+
const result = {};
|
|
8260
|
+
for (const line of content.split(/\r?\n/)) {
|
|
8261
|
+
const trimmed = line.trim();
|
|
8262
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
8263
|
+
const eqIdx = trimmed.indexOf("=");
|
|
8264
|
+
if (eqIdx === -1) continue;
|
|
8265
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
8266
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
8267
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
8268
|
+
value = value.slice(1, -1);
|
|
8269
|
+
}
|
|
8270
|
+
if (key && value) {
|
|
8271
|
+
result[key] = value;
|
|
8272
|
+
}
|
|
8273
|
+
}
|
|
8274
|
+
return result;
|
|
8275
|
+
}
|
|
8255
8276
|
function readEnvVar(filePath, varName) {
|
|
8256
8277
|
if (!pathExists(filePath)) return null;
|
|
8257
8278
|
const content = readSync(filePath, { format: "text" });
|
|
@@ -8260,16 +8281,93 @@ function readEnvVar(filePath, varName) {
|
|
|
8260
8281
|
const trimmed = line.trim();
|
|
8261
8282
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
8262
8283
|
if (trimmed.startsWith(`${varName}=`)) {
|
|
8263
|
-
|
|
8284
|
+
let val = trimmed.substring(`${varName}=`.length).trim();
|
|
8264
8285
|
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
8265
|
-
|
|
8286
|
+
val = val.slice(1, -1);
|
|
8266
8287
|
}
|
|
8267
8288
|
return val || null;
|
|
8268
8289
|
}
|
|
8269
8290
|
}
|
|
8270
8291
|
return null;
|
|
8271
8292
|
}
|
|
8272
|
-
function
|
|
8293
|
+
function loadDndevSecrets(projectRoot) {
|
|
8294
|
+
const secretsPath = joinPath(projectRoot, ".dndev.secrets");
|
|
8295
|
+
if (cachedSecretsPath === secretsPath && cachedSecrets) {
|
|
8296
|
+
return cachedSecrets;
|
|
8297
|
+
}
|
|
8298
|
+
cachedSecrets = parseEnvFile(secretsPath);
|
|
8299
|
+
cachedSecretsPath = secretsPath;
|
|
8300
|
+
return cachedSecrets;
|
|
8301
|
+
}
|
|
8302
|
+
function resolveSecret(name, projectRoot, opts) {
|
|
8303
|
+
const appDir = opts?.appDir ?? projectRoot;
|
|
8304
|
+
const envValue = process.env[name];
|
|
8305
|
+
if (envValue) {
|
|
8306
|
+
return { value: envValue, source: "process.env" };
|
|
8307
|
+
}
|
|
8308
|
+
const secrets = loadDndevSecrets(projectRoot);
|
|
8309
|
+
if (secrets[name]) {
|
|
8310
|
+
return { value: secrets[name], source: ".dndev.secrets" };
|
|
8311
|
+
}
|
|
8312
|
+
if (name === "SUPABASE_SECRET_KEY" && secrets["SUPABASE_SERVICE_ROLE_KEY"]) {
|
|
8313
|
+
return {
|
|
8314
|
+
value: secrets["SUPABASE_SERVICE_ROLE_KEY"],
|
|
8315
|
+
source: ".dndev.secrets"
|
|
8316
|
+
};
|
|
8317
|
+
}
|
|
8318
|
+
const legacyPaths = LEGACY_PATHS[name];
|
|
8319
|
+
if (legacyPaths) {
|
|
8320
|
+
for (const relPath of legacyPaths) {
|
|
8321
|
+
const fullPath = joinPath(appDir, relPath);
|
|
8322
|
+
const value = readEnvVar(fullPath, name);
|
|
8323
|
+
if (value) {
|
|
8324
|
+
if (!opts?.silent && !warnedLegacy.has(name)) {
|
|
8325
|
+
warnedLegacy.add(name);
|
|
8326
|
+
log.warn(
|
|
8327
|
+
`${name} found in ${relPath} (legacy). Move it to .dndev.secrets at project root.`
|
|
8328
|
+
);
|
|
8329
|
+
}
|
|
8330
|
+
return { value, source: "legacy", legacyPath: relPath };
|
|
8331
|
+
}
|
|
8332
|
+
if (name === "SUPABASE_SECRET_KEY") {
|
|
8333
|
+
const aliasValue = readEnvVar(fullPath, "SUPABASE_SERVICE_ROLE_KEY");
|
|
8334
|
+
if (aliasValue) {
|
|
8335
|
+
if (!opts?.silent && !warnedLegacy.has(name)) {
|
|
8336
|
+
warnedLegacy.add(name);
|
|
8337
|
+
log.warn(
|
|
8338
|
+
`${name} found in ${relPath} (legacy). Move it to .dndev.secrets at project root.`
|
|
8339
|
+
);
|
|
8340
|
+
}
|
|
8341
|
+
return { value: aliasValue, source: "legacy", legacyPath: relPath };
|
|
8342
|
+
}
|
|
8343
|
+
}
|
|
8344
|
+
}
|
|
8345
|
+
}
|
|
8346
|
+
return null;
|
|
8347
|
+
}
|
|
8348
|
+
var LEGACY_PATHS, warnedLegacy, cachedSecretsPath, cachedSecrets;
|
|
8349
|
+
var init_secrets_resolver = __esm({
|
|
8350
|
+
"packages/tooling/src/utils/secrets-resolver.ts"() {
|
|
8351
|
+
"use strict";
|
|
8352
|
+
init_utils();
|
|
8353
|
+
init_pathResolver();
|
|
8354
|
+
init_cli_output();
|
|
8355
|
+
LEGACY_PATHS = {
|
|
8356
|
+
VERCEL_TOKEN: [".env.local"],
|
|
8357
|
+
SUPABASE_SECRET_KEY: ["supabase/functions/.env", "functions/.env"],
|
|
8358
|
+
SUPABASE_DB_URL: ["supabase/functions/.env", "functions/.env"],
|
|
8359
|
+
SUPABASE_ACCESS_TOKEN: ["supabase/functions/.env", "functions/.env"],
|
|
8360
|
+
STRIPE_SECRET_KEY: ["supabase/functions/.env", "functions/.env"],
|
|
8361
|
+
STRIPE_WEBHOOK_SECRET: ["supabase/functions/.env", "functions/.env"]
|
|
8362
|
+
};
|
|
8363
|
+
warnedLegacy = /* @__PURE__ */ new Set();
|
|
8364
|
+
cachedSecretsPath = null;
|
|
8365
|
+
cachedSecrets = null;
|
|
8366
|
+
}
|
|
8367
|
+
});
|
|
8368
|
+
|
|
8369
|
+
// packages/tooling/src/cli/setup/vercel-token.ts
|
|
8370
|
+
function resolvePerAppVar(appDir, varName) {
|
|
8273
8371
|
if (process.env[varName]) return process.env[varName];
|
|
8274
8372
|
const fromLocal = readEnvVar(joinPath(appDir, ".env.local"), varName);
|
|
8275
8373
|
if (fromLocal) return fromLocal;
|
|
@@ -8277,10 +8375,11 @@ function resolveVercelVar(appDir, varName) {
|
|
|
8277
8375
|
if (fromEnv) return fromEnv;
|
|
8278
8376
|
return null;
|
|
8279
8377
|
}
|
|
8280
|
-
function resolveVercelCredentials(appDir) {
|
|
8281
|
-
const
|
|
8282
|
-
const
|
|
8283
|
-
const
|
|
8378
|
+
function resolveVercelCredentials(appDir, projectRoot) {
|
|
8379
|
+
const root = projectRoot ?? process.cwd();
|
|
8380
|
+
const token = resolveSecret("VERCEL_TOKEN", root, { appDir })?.value ?? null;
|
|
8381
|
+
const orgId = resolvePerAppVar(appDir, "VERCEL_ORG_ID");
|
|
8382
|
+
const projectId = resolvePerAppVar(appDir, "VERCEL_PROJECT_ID");
|
|
8284
8383
|
const missing = [];
|
|
8285
8384
|
if (!token) missing.push("VERCEL_TOKEN");
|
|
8286
8385
|
if (!orgId) missing.push("VERCEL_ORG_ID");
|
|
@@ -8294,6 +8393,7 @@ var init_vercel_token = __esm({
|
|
|
8294
8393
|
"packages/tooling/src/cli/setup/vercel-token.ts"() {
|
|
8295
8394
|
"use strict";
|
|
8296
8395
|
init_utils();
|
|
8396
|
+
init_secrets_resolver();
|
|
8297
8397
|
init_pathResolver();
|
|
8298
8398
|
}
|
|
8299
8399
|
});
|
|
@@ -8912,40 +9012,15 @@ var init_firebase = __esm({
|
|
|
8912
9012
|
});
|
|
8913
9013
|
|
|
8914
9014
|
// packages/tooling/src/utils/supabase-management.ts
|
|
8915
|
-
import { execSync
|
|
8916
|
-
function
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
function detectDbUrl(appDir) {
|
|
8923
|
-
const envPaths = [
|
|
8924
|
-
joinPath(appDir, "supabase", "functions", ".env"),
|
|
8925
|
-
joinPath(appDir, "functions", ".env"),
|
|
8926
|
-
joinPath(appDir, ".env.local"),
|
|
8927
|
-
joinPath(appDir, ".env")
|
|
8928
|
-
];
|
|
8929
|
-
for (const envPath of envPaths) {
|
|
8930
|
-
if (!pathExists(envPath)) continue;
|
|
8931
|
-
try {
|
|
8932
|
-
const content = readSync(envPath);
|
|
8933
|
-
if (!content) continue;
|
|
8934
|
-
for (const line of content.split("\n")) {
|
|
8935
|
-
const trimmed = line.trim();
|
|
8936
|
-
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
8937
|
-
const eqIdx = trimmed.indexOf("=");
|
|
8938
|
-
const key = trimmed.slice(0, eqIdx).trim();
|
|
8939
|
-
const value = stripQuotes(trimmed.slice(eqIdx + 1).trim());
|
|
8940
|
-
if (key === "SUPABASE_DB_URL" && value) {
|
|
8941
|
-
return value;
|
|
8942
|
-
}
|
|
8943
|
-
}
|
|
8944
|
-
} catch {
|
|
8945
|
-
continue;
|
|
8946
|
-
}
|
|
9015
|
+
import { execSync } from "node:child_process";
|
|
9016
|
+
function extractProjectRef(supabaseUrl) {
|
|
9017
|
+
const match = supabaseUrl.match(/https?:\/\/([a-z0-9]+)\.supabase\.co/);
|
|
9018
|
+
if (!match?.[1]) {
|
|
9019
|
+
throw new Error(
|
|
9020
|
+
`Cannot extract project ref from URL: ${supabaseUrl}. Expected format: https://<ref>.supabase.co`
|
|
9021
|
+
);
|
|
8947
9022
|
}
|
|
8948
|
-
return
|
|
9023
|
+
return match[1];
|
|
8949
9024
|
}
|
|
8950
9025
|
async function cleanupOldEntityMigrations(supabaseDir, latestFile) {
|
|
8951
9026
|
const migrationsDir = joinPath(supabaseDir, "migrations");
|
|
@@ -8959,29 +9034,6 @@ async function cleanupOldEntityMigrations(supabaseDir, latestFile) {
|
|
|
8959
9034
|
const latestBasename = getBasename(latestFile);
|
|
8960
9035
|
const toRemove = oldFiles.filter((f) => getBasename(f) !== latestBasename);
|
|
8961
9036
|
if (toRemove.length === 0) return;
|
|
8962
|
-
const timestamps = [];
|
|
8963
|
-
for (const f of toRemove) {
|
|
8964
|
-
const match = getBasename(f).match(/^(\d{14})_/);
|
|
8965
|
-
if (match?.[1]) timestamps.push(match[1]);
|
|
8966
|
-
}
|
|
8967
|
-
if (timestamps.length > 0) {
|
|
8968
|
-
const cmd = getSupabaseCmd();
|
|
8969
|
-
const { dirname: dirname4 } = await import("node:path");
|
|
8970
|
-
const projectRoot = dirname4(supabaseDir);
|
|
8971
|
-
try {
|
|
8972
|
-
execSync(
|
|
8973
|
-
`${cmd} migration repair --status reverted ${timestamps.join(" ")}`,
|
|
8974
|
-
{ cwd: projectRoot, stdio: "pipe", env: { ...process.env } }
|
|
8975
|
-
);
|
|
8976
|
-
log.info(
|
|
8977
|
-
`Repaired ${timestamps.length} old entity migration(s) in remote`
|
|
8978
|
-
);
|
|
8979
|
-
} catch (error2) {
|
|
8980
|
-
log.debug(
|
|
8981
|
-
`Migration repair skipped: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
8982
|
-
);
|
|
8983
|
-
}
|
|
8984
|
-
}
|
|
8985
9037
|
for (const f of toRemove) {
|
|
8986
9038
|
try {
|
|
8987
9039
|
removeSync(f);
|
|
@@ -8991,7 +9043,7 @@ async function cleanupOldEntityMigrations(supabaseDir, latestFile) {
|
|
|
8991
9043
|
log.info(`Cleaned up ${toRemove.length} old entity migration file(s)`);
|
|
8992
9044
|
}
|
|
8993
9045
|
async function executeMigration(options) {
|
|
8994
|
-
const { supabaseDir } = options;
|
|
9046
|
+
const { supabaseDir, supabaseUrl, accessToken } = options;
|
|
8995
9047
|
if (!pathExists(supabaseDir)) {
|
|
8996
9048
|
throw new Error(`Supabase directory not found: ${supabaseDir}`);
|
|
8997
9049
|
}
|
|
@@ -8999,54 +9051,30 @@ async function executeMigration(options) {
|
|
|
8999
9051
|
if (!pathExists(migrationsDir)) {
|
|
9000
9052
|
throw new Error(`Migrations directory not found: ${migrationsDir}`);
|
|
9001
9053
|
}
|
|
9002
|
-
const
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
"
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
env: { ...process.env }
|
|
9021
|
-
});
|
|
9022
|
-
if (result.status !== 0) {
|
|
9023
|
-
const stderr = result.stderr?.toString() || "";
|
|
9024
|
-
throw new Error(stderr || `Process exited with code ${result.status}`);
|
|
9025
|
-
}
|
|
9026
|
-
} catch (error2) {
|
|
9027
|
-
const raw = error2 instanceof Error ? error2.message : String(error2);
|
|
9028
|
-
const sanitized = raw.replace(
|
|
9029
|
-
/postgresql:\/\/[^\s"']*/g,
|
|
9030
|
-
"postgresql://***"
|
|
9031
|
-
);
|
|
9054
|
+
const sqlFiles = readdirSync2(migrationsDir).filter((f) => f.endsWith(".sql")).sort().map((f) => joinPath(migrationsDir, f));
|
|
9055
|
+
if (sqlFiles.length === 0) {
|
|
9056
|
+
throw new Error(`No .sql files found in ${migrationsDir}`);
|
|
9057
|
+
}
|
|
9058
|
+
const sql = sqlFiles.map((f) => readSync(f)).filter(Boolean).join("\n\n");
|
|
9059
|
+
const projectRef = extractProjectRef(supabaseUrl);
|
|
9060
|
+
const mgmtUrl = `https://api.supabase.com/v1/projects/${projectRef}/database/query`;
|
|
9061
|
+
const response = await fetch(mgmtUrl, {
|
|
9062
|
+
method: "POST",
|
|
9063
|
+
headers: {
|
|
9064
|
+
"Content-Type": "application/json",
|
|
9065
|
+
Authorization: `Bearer ${accessToken}`
|
|
9066
|
+
},
|
|
9067
|
+
body: JSON.stringify({ query: sql })
|
|
9068
|
+
});
|
|
9069
|
+
if (!response.ok) {
|
|
9070
|
+
const body = await response.text();
|
|
9071
|
+
const safe = body.replace(/sbp_[^\s"']*/g, "***").replace(/sb_secret_[^\s"']*/g, "***");
|
|
9032
9072
|
throw new Error(
|
|
9033
|
-
`
|
|
9034
|
-
|
|
9073
|
+
`Supabase Management API error (HTTP ${response.status}): ${safe}
|
|
9074
|
+
Ensure SUPABASE_ACCESS_TOKEN is valid. Get one from: https://supabase.com/dashboard/account/tokens`
|
|
9035
9075
|
);
|
|
9036
9076
|
}
|
|
9037
|
-
|
|
9038
|
-
function getSupabaseCmd() {
|
|
9039
|
-
try {
|
|
9040
|
-
execSync("supabase --version", { stdio: "pipe" });
|
|
9041
|
-
return "supabase";
|
|
9042
|
-
} catch {
|
|
9043
|
-
try {
|
|
9044
|
-
execSync("bunx supabase --version", { stdio: "pipe" });
|
|
9045
|
-
return "bunx supabase";
|
|
9046
|
-
} catch {
|
|
9047
|
-
return "npx supabase";
|
|
9048
|
-
}
|
|
9049
|
-
}
|
|
9077
|
+
log.info("Migrations executed via Supabase Management API");
|
|
9050
9078
|
}
|
|
9051
9079
|
var init_supabase_management = __esm({
|
|
9052
9080
|
"packages/tooling/src/utils/supabase-management.ts"() {
|
|
@@ -9501,7 +9529,7 @@ var init_base_generator = __esm({
|
|
|
9501
9529
|
async isBinaryFile(filePath) {
|
|
9502
9530
|
try {
|
|
9503
9531
|
const fd = await openFile(filePath, "r");
|
|
9504
|
-
const buffer =
|
|
9532
|
+
const buffer = Buffer.alloc(4096);
|
|
9505
9533
|
await fd.read(buffer, 0, 4096, 0);
|
|
9506
9534
|
await fd.close();
|
|
9507
9535
|
for (let i = 0; i < buffer.length; i++) {
|
|
@@ -9660,6 +9688,9 @@ function roleToSqlCondition(role) {
|
|
|
9660
9688
|
case "super":
|
|
9661
9689
|
return "(auth.jwt()->'app_metadata'->>'role') = 'super'";
|
|
9662
9690
|
default:
|
|
9691
|
+
log.warn(
|
|
9692
|
+
`Unknown access role "${role}" \u2014 defaulting to deny (false). Use guest|user|admin|super.`
|
|
9693
|
+
);
|
|
9663
9694
|
return "false";
|
|
9664
9695
|
}
|
|
9665
9696
|
}
|
|
@@ -9667,7 +9698,20 @@ function camelToSnake(str) {
|
|
|
9667
9698
|
return str.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
9668
9699
|
}
|
|
9669
9700
|
function fieldTypeToSql(fieldType) {
|
|
9670
|
-
return FIELD_TYPE_TO_SQL[fieldType] ??
|
|
9701
|
+
return FIELD_TYPE_TO_SQL[fieldType] ?? null;
|
|
9702
|
+
}
|
|
9703
|
+
function inferSqlTypeFromValidation(validation) {
|
|
9704
|
+
if (!validation) return "text";
|
|
9705
|
+
if (validation.min !== void 0 || validation.max !== void 0)
|
|
9706
|
+
return "numeric";
|
|
9707
|
+
if (validation.minLength !== void 0 || validation.maxLength !== void 0)
|
|
9708
|
+
return "text";
|
|
9709
|
+
if (Array.isArray(validation.options) && validation.options.length > 0) {
|
|
9710
|
+
const first = validation.options[0];
|
|
9711
|
+
if (first && typeof first === "object" && typeof first.value === "number")
|
|
9712
|
+
return "numeric";
|
|
9713
|
+
}
|
|
9714
|
+
return "text";
|
|
9671
9715
|
}
|
|
9672
9716
|
function schemaRequiresJsonb(schema) {
|
|
9673
9717
|
if (!schema || typeof schema !== "object") return false;
|
|
@@ -9838,7 +9882,6 @@ var init_sql_generator = __esm({
|
|
|
9838
9882
|
const tableName = this.safeTableName(entity.collection);
|
|
9839
9883
|
const columns = [];
|
|
9840
9884
|
columns.push("id uuid PRIMARY KEY DEFAULT gen_random_uuid()");
|
|
9841
|
-
columns.push("user_id uuid NOT NULL");
|
|
9842
9885
|
columns.push("created_at timestamptz NOT NULL DEFAULT now()");
|
|
9843
9886
|
columns.push("updated_at timestamptz NOT NULL DEFAULT now()");
|
|
9844
9887
|
columns.push("created_by_id uuid");
|
|
@@ -9854,14 +9897,23 @@ var init_sql_generator = __esm({
|
|
|
9854
9897
|
if (schema && schemaRequiresJsonb(schema)) {
|
|
9855
9898
|
sqlType = "jsonb";
|
|
9856
9899
|
} else {
|
|
9857
|
-
sqlType = fieldTypeToSql(field.type || "text");
|
|
9900
|
+
sqlType = fieldTypeToSql(field.type || "text") ?? inferSqlTypeFromValidation(field.validation);
|
|
9858
9901
|
}
|
|
9859
9902
|
columns.push(`${colName} ${sqlType}`);
|
|
9860
9903
|
}
|
|
9861
9904
|
const columnList = columns.join(",\n ");
|
|
9862
|
-
|
|
9905
|
+
const createSql = `CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
9863
9906
|
${columnList}
|
|
9864
9907
|
);`;
|
|
9908
|
+
const alterDefaults = [
|
|
9909
|
+
`ALTER TABLE ${tableName} ALTER COLUMN id SET DEFAULT gen_random_uuid();`,
|
|
9910
|
+
`ALTER TABLE ${tableName} ALTER COLUMN created_at SET DEFAULT now();`,
|
|
9911
|
+
`ALTER TABLE ${tableName} ALTER COLUMN updated_at SET DEFAULT now();`,
|
|
9912
|
+
`ALTER TABLE ${tableName} ALTER COLUMN status SET DEFAULT 'available';`
|
|
9913
|
+
].join("\n");
|
|
9914
|
+
return `${createSql}
|
|
9915
|
+
|
|
9916
|
+
${alterDefaults}`;
|
|
9865
9917
|
}
|
|
9866
9918
|
buildRlsSql(tableName, access) {
|
|
9867
9919
|
const quoted = this.safeTableName(tableName);
|
|
@@ -10148,29 +10200,9 @@ function detectPublicConfig(appDir, framework) {
|
|
|
10148
10200
|
}
|
|
10149
10201
|
return null;
|
|
10150
10202
|
}
|
|
10151
|
-
function detectSecretKey(appDir) {
|
|
10152
|
-
const
|
|
10153
|
-
|
|
10154
|
-
joinPath(appDir, "functions", ".env")
|
|
10155
|
-
];
|
|
10156
|
-
for (const functionsEnvPath of candidates) {
|
|
10157
|
-
if (!pathExists(functionsEnvPath)) continue;
|
|
10158
|
-
const envContentRaw = readSync(functionsEnvPath, { format: "text" });
|
|
10159
|
-
const envContent = typeof envContentRaw === "string" ? envContentRaw : "";
|
|
10160
|
-
const lines = envContent.split("\n");
|
|
10161
|
-
for (const line of lines) {
|
|
10162
|
-
const trimmed = line.trim();
|
|
10163
|
-
if (trimmed.startsWith("SUPABASE_SECRET_KEY=") || trimmed.startsWith("SUPABASE_SERVICE_ROLE_KEY=")) {
|
|
10164
|
-
const match = trimmed.match(
|
|
10165
|
-
/^SUPABASE_(?:SECRET|SERVICE_ROLE)_KEY=(.+)$/
|
|
10166
|
-
);
|
|
10167
|
-
if (match && match[1]) {
|
|
10168
|
-
return match[1].trim();
|
|
10169
|
-
}
|
|
10170
|
-
}
|
|
10171
|
-
}
|
|
10172
|
-
}
|
|
10173
|
-
return null;
|
|
10203
|
+
function detectSecretKey(appDir, projectRoot) {
|
|
10204
|
+
const root = projectRoot ?? process.cwd();
|
|
10205
|
+
return resolveSecret("SUPABASE_SECRET_KEY", root, { appDir })?.value ?? null;
|
|
10174
10206
|
}
|
|
10175
10207
|
async function generateCrudFunction(supabaseDir, projectRoot) {
|
|
10176
10208
|
const templatesRoot = getTemplatesRoot();
|
|
@@ -10254,26 +10286,23 @@ async function main3(options = {}) {
|
|
|
10254
10286
|
const supabaseUrl = existingConfig.url;
|
|
10255
10287
|
log.success(`Supabase URL: ${supabaseUrl}`);
|
|
10256
10288
|
const secretKey = detectSecretKey(appDir);
|
|
10257
|
-
const
|
|
10289
|
+
const accessToken = resolveSecret("SUPABASE_ACCESS_TOKEN", projectRoot, { appDir })?.value ?? null;
|
|
10258
10290
|
if (secretKey) log.success("Secret key detected");
|
|
10259
|
-
else log.error("Missing SUPABASE_SECRET_KEY
|
|
10260
|
-
if (
|
|
10261
|
-
else log.error("Missing
|
|
10262
|
-
if (!secretKey || !
|
|
10263
|
-
const functionsEnvPath = joinPath(supabaseDir, "functions", ".env");
|
|
10291
|
+
else log.error("Missing SUPABASE_SECRET_KEY");
|
|
10292
|
+
if (accessToken) log.success("Access token detected");
|
|
10293
|
+
else log.error("Missing SUPABASE_ACCESS_TOKEN");
|
|
10294
|
+
if (!secretKey || !accessToken) {
|
|
10264
10295
|
const missing = [];
|
|
10265
10296
|
if (!secretKey) missing.push(" SUPABASE_SECRET_KEY=your-service-role-key");
|
|
10266
|
-
if (!
|
|
10297
|
+
if (!accessToken)
|
|
10267
10298
|
missing.push(
|
|
10268
|
-
"
|
|
10299
|
+
" SUPABASE_ACCESS_TOKEN=sbp_... (from https://supabase.com/dashboard/account/tokens)"
|
|
10269
10300
|
);
|
|
10270
10301
|
Me(
|
|
10271
10302
|
[
|
|
10272
|
-
|
|
10303
|
+
"Fill in .dndev.secrets at project root:",
|
|
10273
10304
|
...missing,
|
|
10274
10305
|
"",
|
|
10275
|
-
"Get these from: https://supabase.com/dashboard > Settings > Database",
|
|
10276
|
-
"",
|
|
10277
10306
|
"Then re-run: dndev setup supabase"
|
|
10278
10307
|
].join("\n"),
|
|
10279
10308
|
"Missing Credentials"
|
|
@@ -10340,7 +10369,7 @@ async function main3(options = {}) {
|
|
|
10340
10369
|
const s = Y2();
|
|
10341
10370
|
s.start("Pushing migrations...");
|
|
10342
10371
|
try {
|
|
10343
|
-
await executeMigration({ supabaseDir,
|
|
10372
|
+
await executeMigration({ supabaseDir, supabaseUrl, accessToken });
|
|
10344
10373
|
s.stop("Migrations pushed successfully");
|
|
10345
10374
|
migrationsPushed = true;
|
|
10346
10375
|
} catch (error2) {
|
|
@@ -10359,10 +10388,13 @@ async function main3(options = {}) {
|
|
|
10359
10388
|
const steps = [];
|
|
10360
10389
|
steps.push("\u2705 Credentials validated from .env");
|
|
10361
10390
|
if (secretKey) steps.push("\u2705 Secret key detected");
|
|
10362
|
-
else steps.push("\u274C Secret key missing
|
|
10391
|
+
else steps.push("\u274C Secret key missing");
|
|
10392
|
+
if (accessToken) steps.push("\u2705 Access token detected");
|
|
10393
|
+
else steps.push("\u274C Access token missing \u2014 cannot push migrations");
|
|
10363
10394
|
if (sqlGenerated) steps.push("\u2705 Entity SQL migrations generated");
|
|
10364
|
-
if (migrationsPushed) steps.push("\u2705 Migrations pushed
|
|
10365
|
-
else if (
|
|
10395
|
+
if (migrationsPushed) steps.push("\u2705 Migrations pushed via Management API");
|
|
10396
|
+
else if (accessToken)
|
|
10397
|
+
steps.push("\u274C Migration push failed (see error above)");
|
|
10366
10398
|
const hasErrors = steps.some((s) => s.startsWith("\u274C"));
|
|
10367
10399
|
const title = hasErrors ? "Setup Incomplete" : "Setup Complete";
|
|
10368
10400
|
Me(
|
|
@@ -10393,6 +10425,7 @@ var init_supabase = __esm({
|
|
|
10393
10425
|
init_pathResolver();
|
|
10394
10426
|
init_app_selector();
|
|
10395
10427
|
init_supabase_management();
|
|
10428
|
+
init_secrets_resolver();
|
|
10396
10429
|
init_error_handling();
|
|
10397
10430
|
init_types();
|
|
10398
10431
|
supabase_default = main3;
|
|
@@ -10416,15 +10449,17 @@ var init_supabase = __esm({
|
|
|
10416
10449
|
return { provider: "supabase", steps, overallStatus: "failed" };
|
|
10417
10450
|
}
|
|
10418
10451
|
const secretKey = detectSecretKey(ctx.appDir);
|
|
10419
|
-
const
|
|
10452
|
+
const accessToken = resolveSecret("SUPABASE_ACCESS_TOKEN", ctx.projectRoot, {
|
|
10453
|
+
appDir: ctx.appDir
|
|
10454
|
+
})?.value ?? null;
|
|
10420
10455
|
const missingSecrets = [];
|
|
10421
10456
|
if (!secretKey) missingSecrets.push("SUPABASE_SECRET_KEY");
|
|
10422
|
-
if (!
|
|
10457
|
+
if (!accessToken) missingSecrets.push("SUPABASE_ACCESS_TOKEN");
|
|
10423
10458
|
if (missingSecrets.length > 0) {
|
|
10424
10459
|
steps.push({
|
|
10425
10460
|
name: "Credentials",
|
|
10426
10461
|
status: "failed",
|
|
10427
|
-
message: `Missing in
|
|
10462
|
+
message: `Missing in .dndev.secrets: ${missingSecrets.join(", ")}`
|
|
10428
10463
|
});
|
|
10429
10464
|
return { provider: "supabase", steps, overallStatus: "failed" };
|
|
10430
10465
|
}
|
|
@@ -10488,7 +10523,11 @@ var init_supabase = __esm({
|
|
|
10488
10523
|
const s = Y2();
|
|
10489
10524
|
s.start("Pushing migrations...");
|
|
10490
10525
|
try {
|
|
10491
|
-
await executeMigration({
|
|
10526
|
+
await executeMigration({
|
|
10527
|
+
supabaseDir,
|
|
10528
|
+
supabaseUrl: existingConfig.url,
|
|
10529
|
+
accessToken
|
|
10530
|
+
});
|
|
10492
10531
|
s.stop("Migrations pushed");
|
|
10493
10532
|
steps.push({
|
|
10494
10533
|
name: "DB migrations",
|
|
@@ -10597,6 +10636,7 @@ init_cross_app_detection();
|
|
|
10597
10636
|
init_utils();
|
|
10598
10637
|
init_pathResolver();
|
|
10599
10638
|
init_cli_output();
|
|
10639
|
+
init_secrets_resolver();
|
|
10600
10640
|
var FIREBASE_VARS = [
|
|
10601
10641
|
{
|
|
10602
10642
|
provider: "Firebase",
|
|
@@ -10635,32 +10675,28 @@ var SUPABASE_SECRET_VARS = [
|
|
|
10635
10675
|
{
|
|
10636
10676
|
provider: "Supabase",
|
|
10637
10677
|
varName: "SUPABASE_SECRET_KEY",
|
|
10638
|
-
envFile: "
|
|
10678
|
+
envFile: ".dndev.secrets",
|
|
10679
|
+
isSecret: true
|
|
10639
10680
|
},
|
|
10640
10681
|
{
|
|
10641
10682
|
provider: "Supabase",
|
|
10642
|
-
varName: "
|
|
10643
|
-
envFile: "
|
|
10683
|
+
varName: "SUPABASE_ACCESS_TOKEN",
|
|
10684
|
+
envFile: ".dndev.secrets",
|
|
10685
|
+
isSecret: true
|
|
10644
10686
|
}
|
|
10645
10687
|
];
|
|
10646
|
-
var
|
|
10647
|
-
{
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
|
|
10652
|
-
if (!pathExists(envPath)) return null;
|
|
10653
|
-
const content = readSync(envPath, { format: "text" });
|
|
10654
|
-
if (typeof content !== "string") return null;
|
|
10655
|
-
for (const line of content.split(/\r?\n/)) {
|
|
10656
|
-
const trimmed = line.trim();
|
|
10657
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
10658
|
-
if (trimmed.startsWith(`${varName}=`)) {
|
|
10659
|
-
return trimmed.substring(`${varName}=`.length).trim();
|
|
10660
|
-
}
|
|
10688
|
+
var VERCEL_SECRET_VARS = [
|
|
10689
|
+
{
|
|
10690
|
+
provider: "Vercel",
|
|
10691
|
+
varName: "VERCEL_TOKEN",
|
|
10692
|
+
envFile: ".dndev.secrets",
|
|
10693
|
+
isSecret: true
|
|
10661
10694
|
}
|
|
10662
|
-
|
|
10663
|
-
|
|
10695
|
+
];
|
|
10696
|
+
var VERCEL_APP_VARS = [
|
|
10697
|
+
{ provider: "Vercel", varName: "VERCEL_ORG_ID", envFile: ".env" },
|
|
10698
|
+
{ provider: "Vercel", varName: "VERCEL_PROJECT_ID", envFile: ".env" }
|
|
10699
|
+
];
|
|
10664
10700
|
function isProviderRelevant(ctx, provider) {
|
|
10665
10701
|
switch (provider) {
|
|
10666
10702
|
case "Firebase":
|
|
@@ -10683,32 +10719,33 @@ function validateRequiredEnvVars(ctx) {
|
|
|
10683
10719
|
}
|
|
10684
10720
|
if (isProviderRelevant(ctx, "Supabase")) {
|
|
10685
10721
|
requirements.push(...SUPABASE_VARS);
|
|
10686
|
-
|
|
10687
|
-
ctx.appDir,
|
|
10688
|
-
"supabase",
|
|
10689
|
-
"functions",
|
|
10690
|
-
".env"
|
|
10691
|
-
);
|
|
10692
|
-
const altFunctionsEnv = joinPath(ctx.appDir, "functions", ".env");
|
|
10693
|
-
if (pathExists(joinPath(ctx.appDir, "supabase", "functions")) || pathExists(sbFunctionsEnv)) {
|
|
10694
|
-
requirements.push(...SUPABASE_SECRET_VARS);
|
|
10695
|
-
} else if (pathExists(altFunctionsEnv)) {
|
|
10696
|
-
requirements.push({
|
|
10697
|
-
provider: "Supabase",
|
|
10698
|
-
varName: "SUPABASE_SECRET_KEY",
|
|
10699
|
-
envFile: "functions/.env"
|
|
10700
|
-
});
|
|
10701
|
-
}
|
|
10722
|
+
requirements.push(...SUPABASE_SECRET_VARS);
|
|
10702
10723
|
}
|
|
10703
10724
|
if (isProviderRelevant(ctx, "Vercel")) {
|
|
10704
|
-
requirements.push(...
|
|
10725
|
+
requirements.push(...VERCEL_SECRET_VARS);
|
|
10726
|
+
requirements.push(...VERCEL_APP_VARS);
|
|
10705
10727
|
}
|
|
10706
10728
|
for (const req of requirements) {
|
|
10707
10729
|
const actualVarName = req.needsPrefix ? `${prefix}${req.varName}` : req.varName;
|
|
10730
|
+
if (req.isSecret) {
|
|
10731
|
+
const resolved = resolveSecret(req.varName, ctx.projectRoot, {
|
|
10732
|
+
appDir: ctx.appDir,
|
|
10733
|
+
silent: true
|
|
10734
|
+
});
|
|
10735
|
+
if (!resolved) {
|
|
10736
|
+
missing.push({
|
|
10737
|
+
provider: req.provider,
|
|
10738
|
+
varName: req.varName,
|
|
10739
|
+
envFile: req.envFile,
|
|
10740
|
+
status: "missing"
|
|
10741
|
+
});
|
|
10742
|
+
}
|
|
10743
|
+
continue;
|
|
10744
|
+
}
|
|
10708
10745
|
const envFilePath = joinPath(ctx.appDir, req.envFile);
|
|
10709
10746
|
if (req.varName === "SUPABASE_PUBLIC_KEY") {
|
|
10710
|
-
const pkValue =
|
|
10711
|
-
const anonValue =
|
|
10747
|
+
const pkValue = readEnvVar(envFilePath, actualVarName);
|
|
10748
|
+
const anonValue = readEnvVar(envFilePath, `${prefix}SUPABASE_ANON_KEY`);
|
|
10712
10749
|
if ((!pkValue || pkValue === "") && (!anonValue || anonValue === "")) {
|
|
10713
10750
|
missing.push({
|
|
10714
10751
|
provider: req.provider,
|
|
@@ -10719,20 +10756,30 @@ function validateRequiredEnvVars(ctx) {
|
|
|
10719
10756
|
}
|
|
10720
10757
|
continue;
|
|
10721
10758
|
}
|
|
10722
|
-
|
|
10759
|
+
if (req.varName === "VERCEL_ORG_ID" || req.varName === "VERCEL_PROJECT_ID") {
|
|
10760
|
+
const fromEnv = readEnvVar(envFilePath, actualVarName);
|
|
10761
|
+
const fromLocal = readEnvVar(
|
|
10762
|
+
joinPath(ctx.appDir, ".env.local"),
|
|
10763
|
+
actualVarName
|
|
10764
|
+
);
|
|
10765
|
+
const fromProcessEnv = process.env[actualVarName];
|
|
10766
|
+
if (!fromEnv && !fromLocal && !fromProcessEnv) {
|
|
10767
|
+
missing.push({
|
|
10768
|
+
provider: req.provider,
|
|
10769
|
+
varName: actualVarName,
|
|
10770
|
+
envFile: req.envFile,
|
|
10771
|
+
status: pathExists(envFilePath) ? "missing" : "missing"
|
|
10772
|
+
});
|
|
10773
|
+
}
|
|
10774
|
+
continue;
|
|
10775
|
+
}
|
|
10776
|
+
const value = readEnvVar(envFilePath, actualVarName);
|
|
10723
10777
|
if (value === null) {
|
|
10724
10778
|
missing.push({
|
|
10725
10779
|
provider: req.provider,
|
|
10726
10780
|
varName: actualVarName,
|
|
10727
10781
|
envFile: req.envFile,
|
|
10728
|
-
status: pathExists(envFilePath) ? "missing" : "
|
|
10729
|
-
});
|
|
10730
|
-
} else if (value === "") {
|
|
10731
|
-
missing.push({
|
|
10732
|
-
provider: req.provider,
|
|
10733
|
-
varName: actualVarName,
|
|
10734
|
-
envFile: req.envFile,
|
|
10735
|
-
status: "empty"
|
|
10782
|
+
status: pathExists(envFilePath) ? "missing" : "no_env_file"
|
|
10736
10783
|
});
|
|
10737
10784
|
}
|
|
10738
10785
|
}
|
|
@@ -10764,7 +10811,7 @@ function printAllValidationResults(results, multiApp) {
|
|
|
10764
10811
|
}
|
|
10765
10812
|
for (const { appName, m: m2 } of allMissing) {
|
|
10766
10813
|
const prefix = multiApp ? `${appName}: ` : "";
|
|
10767
|
-
const detail = m2.status === "empty" ? `empty in ${m2.envFile}` : `missing \u2014 ${m2.envFile}`;
|
|
10814
|
+
const detail = m2.status === "empty" ? `empty in ${m2.envFile}` : m2.status === "no_env_file" ? `env file not found: ${m2.envFile}` : `missing \u2014 ${m2.envFile}`;
|
|
10768
10815
|
log.error(` ${prefix}${m2.varName} (${detail})`);
|
|
10769
10816
|
}
|
|
10770
10817
|
log.warn(`Run 'dndev coach' to see where to get them.`);
|