@donotdev/cli 0.0.20 → 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 +86 -19
- package/dist/bin/commands/agent-setup.js +2 -2
- package/dist/bin/commands/build.js +6 -6
- package/dist/bin/commands/bump.js +491 -69
- package/dist/bin/commands/cacheout.js +6 -6
- package/dist/bin/commands/coach.js +6 -6
- package/dist/bin/commands/create-app.js +23 -15
- package/dist/bin/commands/create-project.js +101 -16
- package/dist/bin/commands/db.js +142136 -0
- package/dist/bin/commands/deploy.js +336 -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 +256 -212
- package/dist/bin/commands/staging.js +343 -127
- package/dist/bin/commands/sync-secrets.js +55 -33
- package/dist/bin/commands/type-check.js +6 -6
- package/dist/bin/commands/wai.js +6 -6
- package/dist/bin/dndev.js +76 -11
- package/dist/bin/donotdev.js +21 -12
- package/dist/index.js +437 -142
- 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-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-next/src/locales/home_en.json.example +6 -6
- package/templates/app-vite/index.html.example +1 -1
- package/templates/app-vite/src/locales/home_en.json.example +6 -6
- 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/dndev/advanced/COOKIE_REFERENCE.md.example +252 -252
- package/templates/root-consumer/guides/dndev/advanced/VERSION_CONTROL.md.example +174 -174
- 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++) {
|
|
@@ -9670,7 +9698,20 @@ function camelToSnake(str) {
|
|
|
9670
9698
|
return str.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
9671
9699
|
}
|
|
9672
9700
|
function fieldTypeToSql(fieldType) {
|
|
9673
|
-
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";
|
|
9674
9715
|
}
|
|
9675
9716
|
function schemaRequiresJsonb(schema) {
|
|
9676
9717
|
if (!schema || typeof schema !== "object") return false;
|
|
@@ -9841,7 +9882,6 @@ var init_sql_generator = __esm({
|
|
|
9841
9882
|
const tableName = this.safeTableName(entity.collection);
|
|
9842
9883
|
const columns = [];
|
|
9843
9884
|
columns.push("id uuid PRIMARY KEY DEFAULT gen_random_uuid()");
|
|
9844
|
-
columns.push("user_id uuid NOT NULL");
|
|
9845
9885
|
columns.push("created_at timestamptz NOT NULL DEFAULT now()");
|
|
9846
9886
|
columns.push("updated_at timestamptz NOT NULL DEFAULT now()");
|
|
9847
9887
|
columns.push("created_by_id uuid");
|
|
@@ -9857,14 +9897,23 @@ var init_sql_generator = __esm({
|
|
|
9857
9897
|
if (schema && schemaRequiresJsonb(schema)) {
|
|
9858
9898
|
sqlType = "jsonb";
|
|
9859
9899
|
} else {
|
|
9860
|
-
sqlType = fieldTypeToSql(field.type || "text");
|
|
9900
|
+
sqlType = fieldTypeToSql(field.type || "text") ?? inferSqlTypeFromValidation(field.validation);
|
|
9861
9901
|
}
|
|
9862
9902
|
columns.push(`${colName} ${sqlType}`);
|
|
9863
9903
|
}
|
|
9864
9904
|
const columnList = columns.join(",\n ");
|
|
9865
|
-
|
|
9905
|
+
const createSql = `CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
9866
9906
|
${columnList}
|
|
9867
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}`;
|
|
9868
9917
|
}
|
|
9869
9918
|
buildRlsSql(tableName, access) {
|
|
9870
9919
|
const quoted = this.safeTableName(tableName);
|
|
@@ -10151,29 +10200,9 @@ function detectPublicConfig(appDir, framework) {
|
|
|
10151
10200
|
}
|
|
10152
10201
|
return null;
|
|
10153
10202
|
}
|
|
10154
|
-
function detectSecretKey(appDir) {
|
|
10155
|
-
const
|
|
10156
|
-
|
|
10157
|
-
joinPath(appDir, "functions", ".env")
|
|
10158
|
-
];
|
|
10159
|
-
for (const functionsEnvPath of candidates) {
|
|
10160
|
-
if (!pathExists(functionsEnvPath)) continue;
|
|
10161
|
-
const envContentRaw = readSync(functionsEnvPath, { format: "text" });
|
|
10162
|
-
const envContent = typeof envContentRaw === "string" ? envContentRaw : "";
|
|
10163
|
-
const lines = envContent.split("\n");
|
|
10164
|
-
for (const line of lines) {
|
|
10165
|
-
const trimmed = line.trim();
|
|
10166
|
-
if (trimmed.startsWith("SUPABASE_SECRET_KEY=") || trimmed.startsWith("SUPABASE_SERVICE_ROLE_KEY=")) {
|
|
10167
|
-
const match = trimmed.match(
|
|
10168
|
-
/^SUPABASE_(?:SECRET|SERVICE_ROLE)_KEY=(.+)$/
|
|
10169
|
-
);
|
|
10170
|
-
if (match && match[1]) {
|
|
10171
|
-
return match[1].trim();
|
|
10172
|
-
}
|
|
10173
|
-
}
|
|
10174
|
-
}
|
|
10175
|
-
}
|
|
10176
|
-
return null;
|
|
10203
|
+
function detectSecretKey(appDir, projectRoot) {
|
|
10204
|
+
const root = projectRoot ?? process.cwd();
|
|
10205
|
+
return resolveSecret("SUPABASE_SECRET_KEY", root, { appDir })?.value ?? null;
|
|
10177
10206
|
}
|
|
10178
10207
|
async function generateCrudFunction(supabaseDir, projectRoot) {
|
|
10179
10208
|
const templatesRoot = getTemplatesRoot();
|
|
@@ -10257,26 +10286,23 @@ async function main3(options = {}) {
|
|
|
10257
10286
|
const supabaseUrl = existingConfig.url;
|
|
10258
10287
|
log.success(`Supabase URL: ${supabaseUrl}`);
|
|
10259
10288
|
const secretKey = detectSecretKey(appDir);
|
|
10260
|
-
const
|
|
10289
|
+
const accessToken = resolveSecret("SUPABASE_ACCESS_TOKEN", projectRoot, { appDir })?.value ?? null;
|
|
10261
10290
|
if (secretKey) log.success("Secret key detected");
|
|
10262
|
-
else log.error("Missing SUPABASE_SECRET_KEY
|
|
10263
|
-
if (
|
|
10264
|
-
else log.error("Missing
|
|
10265
|
-
if (!secretKey || !
|
|
10266
|
-
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) {
|
|
10267
10295
|
const missing = [];
|
|
10268
10296
|
if (!secretKey) missing.push(" SUPABASE_SECRET_KEY=your-service-role-key");
|
|
10269
|
-
if (!
|
|
10297
|
+
if (!accessToken)
|
|
10270
10298
|
missing.push(
|
|
10271
|
-
"
|
|
10299
|
+
" SUPABASE_ACCESS_TOKEN=sbp_... (from https://supabase.com/dashboard/account/tokens)"
|
|
10272
10300
|
);
|
|
10273
10301
|
Me(
|
|
10274
10302
|
[
|
|
10275
|
-
|
|
10303
|
+
"Fill in .dndev.secrets at project root:",
|
|
10276
10304
|
...missing,
|
|
10277
10305
|
"",
|
|
10278
|
-
"Get these from: https://supabase.com/dashboard > Settings > Database",
|
|
10279
|
-
"",
|
|
10280
10306
|
"Then re-run: dndev setup supabase"
|
|
10281
10307
|
].join("\n"),
|
|
10282
10308
|
"Missing Credentials"
|
|
@@ -10343,7 +10369,7 @@ async function main3(options = {}) {
|
|
|
10343
10369
|
const s = Y2();
|
|
10344
10370
|
s.start("Pushing migrations...");
|
|
10345
10371
|
try {
|
|
10346
|
-
await executeMigration({ supabaseDir,
|
|
10372
|
+
await executeMigration({ supabaseDir, supabaseUrl, accessToken });
|
|
10347
10373
|
s.stop("Migrations pushed successfully");
|
|
10348
10374
|
migrationsPushed = true;
|
|
10349
10375
|
} catch (error2) {
|
|
@@ -10362,10 +10388,13 @@ async function main3(options = {}) {
|
|
|
10362
10388
|
const steps = [];
|
|
10363
10389
|
steps.push("\u2705 Credentials validated from .env");
|
|
10364
10390
|
if (secretKey) steps.push("\u2705 Secret key detected");
|
|
10365
|
-
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");
|
|
10366
10394
|
if (sqlGenerated) steps.push("\u2705 Entity SQL migrations generated");
|
|
10367
|
-
if (migrationsPushed) steps.push("\u2705 Migrations pushed
|
|
10368
|
-
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)");
|
|
10369
10398
|
const hasErrors = steps.some((s) => s.startsWith("\u274C"));
|
|
10370
10399
|
const title = hasErrors ? "Setup Incomplete" : "Setup Complete";
|
|
10371
10400
|
Me(
|
|
@@ -10396,6 +10425,7 @@ var init_supabase = __esm({
|
|
|
10396
10425
|
init_pathResolver();
|
|
10397
10426
|
init_app_selector();
|
|
10398
10427
|
init_supabase_management();
|
|
10428
|
+
init_secrets_resolver();
|
|
10399
10429
|
init_error_handling();
|
|
10400
10430
|
init_types();
|
|
10401
10431
|
supabase_default = main3;
|
|
@@ -10419,15 +10449,17 @@ var init_supabase = __esm({
|
|
|
10419
10449
|
return { provider: "supabase", steps, overallStatus: "failed" };
|
|
10420
10450
|
}
|
|
10421
10451
|
const secretKey = detectSecretKey(ctx.appDir);
|
|
10422
|
-
const
|
|
10452
|
+
const accessToken = resolveSecret("SUPABASE_ACCESS_TOKEN", ctx.projectRoot, {
|
|
10453
|
+
appDir: ctx.appDir
|
|
10454
|
+
})?.value ?? null;
|
|
10423
10455
|
const missingSecrets = [];
|
|
10424
10456
|
if (!secretKey) missingSecrets.push("SUPABASE_SECRET_KEY");
|
|
10425
|
-
if (!
|
|
10457
|
+
if (!accessToken) missingSecrets.push("SUPABASE_ACCESS_TOKEN");
|
|
10426
10458
|
if (missingSecrets.length > 0) {
|
|
10427
10459
|
steps.push({
|
|
10428
10460
|
name: "Credentials",
|
|
10429
10461
|
status: "failed",
|
|
10430
|
-
message: `Missing in
|
|
10462
|
+
message: `Missing in .dndev.secrets: ${missingSecrets.join(", ")}`
|
|
10431
10463
|
});
|
|
10432
10464
|
return { provider: "supabase", steps, overallStatus: "failed" };
|
|
10433
10465
|
}
|
|
@@ -10491,7 +10523,11 @@ var init_supabase = __esm({
|
|
|
10491
10523
|
const s = Y2();
|
|
10492
10524
|
s.start("Pushing migrations...");
|
|
10493
10525
|
try {
|
|
10494
|
-
await executeMigration({
|
|
10526
|
+
await executeMigration({
|
|
10527
|
+
supabaseDir,
|
|
10528
|
+
supabaseUrl: existingConfig.url,
|
|
10529
|
+
accessToken
|
|
10530
|
+
});
|
|
10495
10531
|
s.stop("Migrations pushed");
|
|
10496
10532
|
steps.push({
|
|
10497
10533
|
name: "DB migrations",
|
|
@@ -10600,6 +10636,7 @@ init_cross_app_detection();
|
|
|
10600
10636
|
init_utils();
|
|
10601
10637
|
init_pathResolver();
|
|
10602
10638
|
init_cli_output();
|
|
10639
|
+
init_secrets_resolver();
|
|
10603
10640
|
var FIREBASE_VARS = [
|
|
10604
10641
|
{
|
|
10605
10642
|
provider: "Firebase",
|
|
@@ -10638,32 +10675,28 @@ var SUPABASE_SECRET_VARS = [
|
|
|
10638
10675
|
{
|
|
10639
10676
|
provider: "Supabase",
|
|
10640
10677
|
varName: "SUPABASE_SECRET_KEY",
|
|
10641
|
-
envFile: "
|
|
10678
|
+
envFile: ".dndev.secrets",
|
|
10679
|
+
isSecret: true
|
|
10642
10680
|
},
|
|
10643
10681
|
{
|
|
10644
10682
|
provider: "Supabase",
|
|
10645
|
-
varName: "
|
|
10646
|
-
envFile: "
|
|
10683
|
+
varName: "SUPABASE_ACCESS_TOKEN",
|
|
10684
|
+
envFile: ".dndev.secrets",
|
|
10685
|
+
isSecret: true
|
|
10647
10686
|
}
|
|
10648
10687
|
];
|
|
10649
|
-
var
|
|
10650
|
-
{
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
if (!pathExists(envPath)) return null;
|
|
10656
|
-
const content = readSync(envPath, { format: "text" });
|
|
10657
|
-
if (typeof content !== "string") return null;
|
|
10658
|
-
for (const line of content.split(/\r?\n/)) {
|
|
10659
|
-
const trimmed = line.trim();
|
|
10660
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
10661
|
-
if (trimmed.startsWith(`${varName}=`)) {
|
|
10662
|
-
return trimmed.substring(`${varName}=`.length).trim();
|
|
10663
|
-
}
|
|
10688
|
+
var VERCEL_SECRET_VARS = [
|
|
10689
|
+
{
|
|
10690
|
+
provider: "Vercel",
|
|
10691
|
+
varName: "VERCEL_TOKEN",
|
|
10692
|
+
envFile: ".dndev.secrets",
|
|
10693
|
+
isSecret: true
|
|
10664
10694
|
}
|
|
10665
|
-
|
|
10666
|
-
|
|
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
|
+
];
|
|
10667
10700
|
function isProviderRelevant(ctx, provider) {
|
|
10668
10701
|
switch (provider) {
|
|
10669
10702
|
case "Firebase":
|
|
@@ -10686,32 +10719,33 @@ function validateRequiredEnvVars(ctx) {
|
|
|
10686
10719
|
}
|
|
10687
10720
|
if (isProviderRelevant(ctx, "Supabase")) {
|
|
10688
10721
|
requirements.push(...SUPABASE_VARS);
|
|
10689
|
-
|
|
10690
|
-
ctx.appDir,
|
|
10691
|
-
"supabase",
|
|
10692
|
-
"functions",
|
|
10693
|
-
".env"
|
|
10694
|
-
);
|
|
10695
|
-
const altFunctionsEnv = joinPath(ctx.appDir, "functions", ".env");
|
|
10696
|
-
if (pathExists(joinPath(ctx.appDir, "supabase", "functions")) || pathExists(sbFunctionsEnv)) {
|
|
10697
|
-
requirements.push(...SUPABASE_SECRET_VARS);
|
|
10698
|
-
} else if (pathExists(altFunctionsEnv)) {
|
|
10699
|
-
requirements.push({
|
|
10700
|
-
provider: "Supabase",
|
|
10701
|
-
varName: "SUPABASE_SECRET_KEY",
|
|
10702
|
-
envFile: "functions/.env"
|
|
10703
|
-
});
|
|
10704
|
-
}
|
|
10722
|
+
requirements.push(...SUPABASE_SECRET_VARS);
|
|
10705
10723
|
}
|
|
10706
10724
|
if (isProviderRelevant(ctx, "Vercel")) {
|
|
10707
|
-
requirements.push(...
|
|
10725
|
+
requirements.push(...VERCEL_SECRET_VARS);
|
|
10726
|
+
requirements.push(...VERCEL_APP_VARS);
|
|
10708
10727
|
}
|
|
10709
10728
|
for (const req of requirements) {
|
|
10710
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
|
+
}
|
|
10711
10745
|
const envFilePath = joinPath(ctx.appDir, req.envFile);
|
|
10712
10746
|
if (req.varName === "SUPABASE_PUBLIC_KEY") {
|
|
10713
|
-
const pkValue =
|
|
10714
|
-
const anonValue =
|
|
10747
|
+
const pkValue = readEnvVar(envFilePath, actualVarName);
|
|
10748
|
+
const anonValue = readEnvVar(envFilePath, `${prefix}SUPABASE_ANON_KEY`);
|
|
10715
10749
|
if ((!pkValue || pkValue === "") && (!anonValue || anonValue === "")) {
|
|
10716
10750
|
missing.push({
|
|
10717
10751
|
provider: req.provider,
|
|
@@ -10722,20 +10756,30 @@ function validateRequiredEnvVars(ctx) {
|
|
|
10722
10756
|
}
|
|
10723
10757
|
continue;
|
|
10724
10758
|
}
|
|
10725
|
-
|
|
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);
|
|
10726
10777
|
if (value === null) {
|
|
10727
10778
|
missing.push({
|
|
10728
10779
|
provider: req.provider,
|
|
10729
10780
|
varName: actualVarName,
|
|
10730
10781
|
envFile: req.envFile,
|
|
10731
|
-
status: pathExists(envFilePath) ? "missing" : "
|
|
10732
|
-
});
|
|
10733
|
-
} else if (value === "") {
|
|
10734
|
-
missing.push({
|
|
10735
|
-
provider: req.provider,
|
|
10736
|
-
varName: actualVarName,
|
|
10737
|
-
envFile: req.envFile,
|
|
10738
|
-
status: "empty"
|
|
10782
|
+
status: pathExists(envFilePath) ? "missing" : "no_env_file"
|
|
10739
10783
|
});
|
|
10740
10784
|
}
|
|
10741
10785
|
}
|
|
@@ -10767,7 +10811,7 @@ function printAllValidationResults(results, multiApp) {
|
|
|
10767
10811
|
}
|
|
10768
10812
|
for (const { appName, m: m2 } of allMissing) {
|
|
10769
10813
|
const prefix = multiApp ? `${appName}: ` : "";
|
|
10770
|
-
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}`;
|
|
10771
10815
|
log.error(` ${prefix}${m2.varName} (${detail})`);
|
|
10772
10816
|
}
|
|
10773
10817
|
log.warn(`Run 'dndev coach' to see where to get them.`);
|