@donotdev/cli 0.0.16 → 0.0.18
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/dependencies-matrix.json +38 -26
- package/dist/bin/commands/bump.js +9 -2
- package/dist/bin/commands/create-app.js +185 -81
- package/dist/bin/commands/create-project.js +186 -85
- package/dist/bin/commands/deploy.js +51 -20
- package/dist/bin/commands/doctor.js +249 -56
- package/dist/bin/commands/emu.js +18 -20
- package/dist/bin/commands/make-admin.js +30 -10
- package/dist/bin/commands/setup.js +512 -122
- package/dist/bin/commands/type-check.d.ts.map +1 -1
- package/dist/bin/commands/type-check.js +7 -3
- package/dist/bin/commands/type-check.js.map +1 -1
- package/dist/bin/dndev.js +9 -6
- package/dist/bin/donotdev.js +35 -20
- package/dist/index.js +262 -129
- package/package.json +1 -1
- package/templates/root-consumer/.claude/commands/brainstorm.md.example +15 -1
- package/templates/root-consumer/.claude/commands/build.md.example +24 -2
- package/templates/root-consumer/.claude/commands/design.md.example +17 -0
- package/templates/root-consumer/.claude/commands/polish.md.example +17 -0
- package/templates/root-consumer/AI.md.example +50 -18
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +6 -6
- package/templates/root-consumer/guides/dndev/INDEX.md.example +2 -2
- package/templates/root-consumer/guides/dndev/SETUP_AUTH.md.example +13 -6
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +149 -1086
- package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +68 -16
- package/templates/root-consumer/guides/dndev/SETUP_FUNCTIONS.md.example +6 -111
- package/templates/root-consumer/guides/dndev/SETUP_PAGES.md.example +64 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +123 -32
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +108 -91
- package/templates/root-consumer/guides/dndev/advanced/EMULATORS.md.example +2 -2
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +1 -1
- package/dist/bin/commands/firebase-setup.d.ts +0 -6
- package/dist/bin/commands/firebase-setup.d.ts.map +0 -1
- package/dist/bin/commands/firebase-setup.js +0 -7
- package/dist/bin/commands/firebase-setup.js.map +0 -1
- package/dist/bin/commands/supabase-setup.d.ts +0 -6
- package/dist/bin/commands/supabase-setup.d.ts.map +0 -1
- package/dist/bin/commands/supabase-setup.js +0 -7
- package/dist/bin/commands/supabase-setup.js.map +0 -1
- package/templates/functions-firebase/functions-firebase/README.md.example +0 -123
- package/templates/functions-firebase/functions-firebase/build.mjs.example +0 -5
- package/templates/functions-firebase/functions-firebase/src/auth/getCustomClaims.ts.example +0 -19
- package/templates/functions-firebase/functions-firebase/src/auth/getUserAuthStatus.ts.example +0 -21
- package/templates/functions-firebase/functions-firebase/src/auth/index.ts.example +0 -11
- package/templates/functions-firebase/functions-firebase/src/auth/removeCustomClaims.ts.example +0 -21
- package/templates/functions-firebase/functions-firebase/src/auth/setCustomClaims.ts.example +0 -21
- package/templates/functions-firebase/functions-firebase/src/billing/handleStripeWebhook.ts.example +0 -24
- package/templates/functions-firebase/functions-firebase/src/billing/index.ts.example +0 -10
- package/templates/functions-firebase/functions-firebase/src/billing/processPaymentSuccess.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/billing/refreshSubscriptionStatus.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/index.ts.example +0 -39
- package/templates/functions-firebase/functions-firebase/src/oauth/checkGitHubAccess.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/disconnect.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/exchangeToken.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/getConnections.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/grantGitHubAccess.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/index.ts.example +0 -17
- package/templates/functions-firebase/functions-firebase/src/oauth/refreshToken.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/revokeGitHubAccess.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/tsconfig.json.example +0 -21
- package/templates/functions-vercel/functions-vercel/README.md.example +0 -116
- package/templates/functions-vercel/functions-vercel/build.mjs.example +0 -52
- package/templates/functions-vercel/functions-vercel/src/api/auth/getCustomClaims.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/auth/getUserAuthStatus.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/auth/removeCustomClaims.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/auth/setCustomClaims.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/billing/handleStripeWebhook.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/billing/processPaymentSuccess.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/billing/refreshSubscriptionStatus.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/createEntity.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/deleteEntity.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/getEntity.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/listEntities.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/updateEntity.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/checkGitHubAccess.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/disconnect.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/exchangeToken.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/getConnections.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/grantGitHubAccess.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/refreshToken.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/revokeGitHubAccess.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/tsconfig.json.example +0 -21
- package/templates/functions-vercel/functions-vercel/vercel.json.example +0 -14
- package/templates/github/github/workflows/firebase-deploy.yml.example +0 -79
- /package/templates/functions-firebase/{functions-firebase/.env.example.example → .env.example} +0 -0
- /package/templates/functions-vercel/{functions-vercel/.env.example.example → .env.example} +0 -0
|
@@ -7951,7 +7951,11 @@ var init_check_env = __esm({
|
|
|
7951
7951
|
const envPath = joinPath(ctx.appDir, ".env");
|
|
7952
7952
|
const examplePath = joinPath(ctx.appDir, ".env.example");
|
|
7953
7953
|
if (!pathExists(envPath)) {
|
|
7954
|
-
results.push({
|
|
7954
|
+
results.push({
|
|
7955
|
+
name: ".env file",
|
|
7956
|
+
status: "fail",
|
|
7957
|
+
message: "Not found \u2014 run dndev setup"
|
|
7958
|
+
});
|
|
7955
7959
|
return results;
|
|
7956
7960
|
}
|
|
7957
7961
|
results.push({ name: ".env file", status: "pass", message: "Found" });
|
|
@@ -7999,9 +8003,17 @@ var init_check_env = __esm({
|
|
|
7999
8003
|
return /^\.env(\*|\..*)?$/.test(trimmed);
|
|
8000
8004
|
});
|
|
8001
8005
|
if (hasEnvIgnore) {
|
|
8002
|
-
results.push({
|
|
8006
|
+
results.push({
|
|
8007
|
+
name: ".gitignore",
|
|
8008
|
+
status: "pass",
|
|
8009
|
+
message: ".env is gitignored"
|
|
8010
|
+
});
|
|
8003
8011
|
} else {
|
|
8004
|
-
results.push({
|
|
8012
|
+
results.push({
|
|
8013
|
+
name: ".gitignore",
|
|
8014
|
+
status: "warn",
|
|
8015
|
+
message: ".env might not be gitignored \u2014 verify manually"
|
|
8016
|
+
});
|
|
8005
8017
|
}
|
|
8006
8018
|
}
|
|
8007
8019
|
}
|
|
@@ -8038,12 +8050,24 @@ var init_check_firebase = __esm({
|
|
|
8038
8050
|
timeout: 1e4
|
|
8039
8051
|
});
|
|
8040
8052
|
if (result.status === 0) {
|
|
8041
|
-
results.push({
|
|
8053
|
+
results.push({
|
|
8054
|
+
name: "Firebase CLI",
|
|
8055
|
+
status: "pass",
|
|
8056
|
+
message: result.stdout.trim()
|
|
8057
|
+
});
|
|
8042
8058
|
} else {
|
|
8043
|
-
results.push({
|
|
8059
|
+
results.push({
|
|
8060
|
+
name: "Firebase CLI",
|
|
8061
|
+
status: "fail",
|
|
8062
|
+
message: "Not installed"
|
|
8063
|
+
});
|
|
8044
8064
|
}
|
|
8045
8065
|
} catch {
|
|
8046
|
-
results.push({
|
|
8066
|
+
results.push({
|
|
8067
|
+
name: "Firebase CLI",
|
|
8068
|
+
status: "fail",
|
|
8069
|
+
message: "Not installed"
|
|
8070
|
+
});
|
|
8047
8071
|
}
|
|
8048
8072
|
const rcPath = joinPath(ctx.projectRoot, ".firebaserc");
|
|
8049
8073
|
if (pathExists(rcPath)) {
|
|
@@ -8054,15 +8078,31 @@ var init_check_firebase = __esm({
|
|
|
8054
8078
|
const projects = rc?.projects;
|
|
8055
8079
|
const projectId = projects && typeof projects === "object" ? projects.default : void 0;
|
|
8056
8080
|
if (projectId && typeof projectId === "string") {
|
|
8057
|
-
results.push({
|
|
8081
|
+
results.push({
|
|
8082
|
+
name: ".firebaserc",
|
|
8083
|
+
status: "pass",
|
|
8084
|
+
message: `Project: ${projectId}`
|
|
8085
|
+
});
|
|
8058
8086
|
} else {
|
|
8059
|
-
results.push({
|
|
8087
|
+
results.push({
|
|
8088
|
+
name: ".firebaserc",
|
|
8089
|
+
status: "warn",
|
|
8090
|
+
message: "No default project set"
|
|
8091
|
+
});
|
|
8060
8092
|
}
|
|
8061
8093
|
} catch {
|
|
8062
|
-
results.push({
|
|
8094
|
+
results.push({
|
|
8095
|
+
name: ".firebaserc",
|
|
8096
|
+
status: "warn",
|
|
8097
|
+
message: "Invalid JSON"
|
|
8098
|
+
});
|
|
8063
8099
|
}
|
|
8064
8100
|
} else {
|
|
8065
|
-
results.push({
|
|
8101
|
+
results.push({
|
|
8102
|
+
name: ".firebaserc",
|
|
8103
|
+
status: "fail",
|
|
8104
|
+
message: "Missing \u2014 run dndev setup firebase"
|
|
8105
|
+
});
|
|
8066
8106
|
}
|
|
8067
8107
|
const framework = ctx.app?.framework ?? "vite";
|
|
8068
8108
|
const prefix = framework === "nextjs" ? "NEXT_PUBLIC_" : "VITE_";
|
|
@@ -8070,16 +8110,34 @@ var init_check_firebase = __esm({
|
|
|
8070
8110
|
if (pathExists(envPath)) {
|
|
8071
8111
|
const content = readSync(envPath, { format: "text" });
|
|
8072
8112
|
if (typeof content === "string") {
|
|
8073
|
-
const requiredKeys = [
|
|
8074
|
-
|
|
8113
|
+
const requiredKeys = [
|
|
8114
|
+
"FIREBASE_API_KEY",
|
|
8115
|
+
"FIREBASE_PROJECT_ID",
|
|
8116
|
+
"FIREBASE_AUTH_DOMAIN"
|
|
8117
|
+
];
|
|
8118
|
+
const missing = requiredKeys.filter(
|
|
8119
|
+
(key) => !content.includes(`${prefix}${key}=`)
|
|
8120
|
+
);
|
|
8075
8121
|
if (missing.length === 0) {
|
|
8076
|
-
results.push({
|
|
8122
|
+
results.push({
|
|
8123
|
+
name: "Firebase .env",
|
|
8124
|
+
status: "pass",
|
|
8125
|
+
message: "All required keys present"
|
|
8126
|
+
});
|
|
8077
8127
|
} else {
|
|
8078
|
-
results.push({
|
|
8128
|
+
results.push({
|
|
8129
|
+
name: "Firebase .env",
|
|
8130
|
+
status: "fail",
|
|
8131
|
+
message: `Missing: ${missing.join(", ")}`
|
|
8132
|
+
});
|
|
8079
8133
|
}
|
|
8080
8134
|
}
|
|
8081
8135
|
} else {
|
|
8082
|
-
results.push({
|
|
8136
|
+
results.push({
|
|
8137
|
+
name: "Firebase .env",
|
|
8138
|
+
status: "fail",
|
|
8139
|
+
message: ".env file not found"
|
|
8140
|
+
});
|
|
8083
8141
|
}
|
|
8084
8142
|
const saKeyPaths = [
|
|
8085
8143
|
joinPath(ctx.appDir, "service-account-key.json"),
|
|
@@ -8087,9 +8145,17 @@ var init_check_firebase = __esm({
|
|
|
8087
8145
|
];
|
|
8088
8146
|
const saFound = saKeyPaths.some((p2) => pathExists(p2));
|
|
8089
8147
|
if (saFound) {
|
|
8090
|
-
results.push({
|
|
8148
|
+
results.push({
|
|
8149
|
+
name: "Service account key",
|
|
8150
|
+
status: "pass",
|
|
8151
|
+
message: "Found"
|
|
8152
|
+
});
|
|
8091
8153
|
} else {
|
|
8092
|
-
results.push({
|
|
8154
|
+
results.push({
|
|
8155
|
+
name: "Service account key",
|
|
8156
|
+
status: "warn",
|
|
8157
|
+
message: "Not found (needed for deploy/emu)"
|
|
8158
|
+
});
|
|
8093
8159
|
}
|
|
8094
8160
|
return results;
|
|
8095
8161
|
}
|
|
@@ -8147,42 +8213,82 @@ var init_check_supabase = __esm({
|
|
|
8147
8213
|
timeout: 1e4
|
|
8148
8214
|
});
|
|
8149
8215
|
if (result.status === 0) {
|
|
8150
|
-
results.push({
|
|
8216
|
+
results.push({
|
|
8217
|
+
name: "Supabase CLI",
|
|
8218
|
+
status: "pass",
|
|
8219
|
+
message: result.stdout.trim()
|
|
8220
|
+
});
|
|
8151
8221
|
} else {
|
|
8152
|
-
results.push({
|
|
8222
|
+
results.push({
|
|
8223
|
+
name: "Supabase CLI",
|
|
8224
|
+
status: "warn",
|
|
8225
|
+
message: "Not installed (needed for migrations)"
|
|
8226
|
+
});
|
|
8153
8227
|
}
|
|
8154
8228
|
} catch {
|
|
8155
|
-
results.push({
|
|
8229
|
+
results.push({
|
|
8230
|
+
name: "Supabase CLI",
|
|
8231
|
+
status: "warn",
|
|
8232
|
+
message: "Not installed"
|
|
8233
|
+
});
|
|
8156
8234
|
}
|
|
8157
8235
|
const configPath = joinPath(ctx.appDir, "supabase", "config.toml");
|
|
8158
8236
|
if (pathExists(configPath)) {
|
|
8159
8237
|
results.push({ name: "config.toml", status: "pass", message: "Found" });
|
|
8160
8238
|
} else {
|
|
8161
|
-
results.push({
|
|
8239
|
+
results.push({
|
|
8240
|
+
name: "config.toml",
|
|
8241
|
+
status: "warn",
|
|
8242
|
+
message: "Missing (expected at supabase/config.toml)"
|
|
8243
|
+
});
|
|
8162
8244
|
}
|
|
8163
8245
|
const envPath = joinPath(ctx.appDir, ".env");
|
|
8164
8246
|
if (pathExists(envPath)) {
|
|
8165
8247
|
const content = readSync(envPath, { format: "text" });
|
|
8166
8248
|
if (typeof content === "string") {
|
|
8167
|
-
const urlMatch = content.match(
|
|
8249
|
+
const urlMatch = content.match(
|
|
8250
|
+
new RegExp(`${prefix}SUPABASE_URL=(.+)`)
|
|
8251
|
+
);
|
|
8168
8252
|
const url = urlMatch?.[1]?.trim();
|
|
8169
8253
|
if (url && isValidSupabaseUrl(url)) {
|
|
8170
8254
|
results.push({ name: "Supabase URL", status: "pass", message: url });
|
|
8171
8255
|
} else if (url) {
|
|
8172
|
-
results.push({
|
|
8256
|
+
results.push({
|
|
8257
|
+
name: "Supabase URL",
|
|
8258
|
+
status: "fail",
|
|
8259
|
+
message: "Invalid URL format"
|
|
8260
|
+
});
|
|
8173
8261
|
} else {
|
|
8174
|
-
results.push({
|
|
8262
|
+
results.push({
|
|
8263
|
+
name: "Supabase URL",
|
|
8264
|
+
status: "fail",
|
|
8265
|
+
message: "Missing in .env"
|
|
8266
|
+
});
|
|
8175
8267
|
}
|
|
8176
|
-
const keyMatch = content.match(
|
|
8268
|
+
const keyMatch = content.match(
|
|
8269
|
+
new RegExp(`${prefix}SUPABASE_(?:PUBLIC_KEY|ANON_KEY)=(.+)`)
|
|
8270
|
+
);
|
|
8177
8271
|
const key = keyMatch?.[1]?.trim();
|
|
8178
8272
|
if (key && isValidPublicKey(key)) {
|
|
8179
|
-
results.push({
|
|
8273
|
+
results.push({
|
|
8274
|
+
name: "Supabase public key",
|
|
8275
|
+
status: "pass",
|
|
8276
|
+
message: "Present"
|
|
8277
|
+
});
|
|
8180
8278
|
} else {
|
|
8181
|
-
results.push({
|
|
8279
|
+
results.push({
|
|
8280
|
+
name: "Supabase public key",
|
|
8281
|
+
status: "fail",
|
|
8282
|
+
message: "Missing in .env"
|
|
8283
|
+
});
|
|
8182
8284
|
}
|
|
8183
8285
|
}
|
|
8184
8286
|
} else {
|
|
8185
|
-
results.push({
|
|
8287
|
+
results.push({
|
|
8288
|
+
name: "Supabase .env",
|
|
8289
|
+
status: "fail",
|
|
8290
|
+
message: ".env file not found"
|
|
8291
|
+
});
|
|
8186
8292
|
}
|
|
8187
8293
|
const functionsEnvCandidates = [
|
|
8188
8294
|
joinPath(ctx.appDir, "supabase", "functions", ".env"),
|
|
@@ -8194,12 +8300,20 @@ var init_check_supabase = __esm({
|
|
|
8194
8300
|
const content = readSync(fenvPath, { format: "text" });
|
|
8195
8301
|
if (typeof content === "string" && content.match(/SUPABASE_(?:SECRET|SERVICE_ROLE)_KEY=.+/)) {
|
|
8196
8302
|
secretFound = true;
|
|
8197
|
-
results.push({
|
|
8303
|
+
results.push({
|
|
8304
|
+
name: "Secret key",
|
|
8305
|
+
status: "pass",
|
|
8306
|
+
message: `Found in ${fenvPath.split("/").pop()}`
|
|
8307
|
+
});
|
|
8198
8308
|
break;
|
|
8199
8309
|
}
|
|
8200
8310
|
}
|
|
8201
8311
|
if (!secretFound) {
|
|
8202
|
-
results.push({
|
|
8312
|
+
results.push({
|
|
8313
|
+
name: "Secret key",
|
|
8314
|
+
status: "warn",
|
|
8315
|
+
message: "Not found (needed for migrations + deploy)"
|
|
8316
|
+
});
|
|
8203
8317
|
}
|
|
8204
8318
|
return results;
|
|
8205
8319
|
}
|
|
@@ -8257,7 +8371,8 @@ var init_check_stripe = __esm({
|
|
|
8257
8371
|
const envPath = joinPath(ctx.appDir, ".env");
|
|
8258
8372
|
if (pathExists(envPath)) {
|
|
8259
8373
|
const content = readSync(envPath, { format: "text" });
|
|
8260
|
-
if (typeof content === "string" && content.includes("STRIPE_PUBLISHABLE_KEY"))
|
|
8374
|
+
if (typeof content === "string" && content.includes("STRIPE_PUBLISHABLE_KEY"))
|
|
8375
|
+
return true;
|
|
8261
8376
|
}
|
|
8262
8377
|
return false;
|
|
8263
8378
|
},
|
|
@@ -8272,12 +8387,24 @@ var init_check_stripe = __esm({
|
|
|
8272
8387
|
if (pk) {
|
|
8273
8388
|
if (pk.startsWith("pk_test_") || pk.startsWith("pk_live_")) {
|
|
8274
8389
|
pkMode = pk.startsWith("pk_test_") ? "test" : "live";
|
|
8275
|
-
results.push({
|
|
8390
|
+
results.push({
|
|
8391
|
+
name: "Publishable key",
|
|
8392
|
+
status: "pass",
|
|
8393
|
+
message: `Valid (${pkMode} mode)`
|
|
8394
|
+
});
|
|
8276
8395
|
} else {
|
|
8277
|
-
results.push({
|
|
8396
|
+
results.push({
|
|
8397
|
+
name: "Publishable key",
|
|
8398
|
+
status: "fail",
|
|
8399
|
+
message: "Invalid format \u2014 must start with pk_test_ or pk_live_"
|
|
8400
|
+
});
|
|
8278
8401
|
}
|
|
8279
8402
|
} else {
|
|
8280
|
-
results.push({
|
|
8403
|
+
results.push({
|
|
8404
|
+
name: "Publishable key",
|
|
8405
|
+
status: "fail",
|
|
8406
|
+
message: "Missing in .env"
|
|
8407
|
+
});
|
|
8281
8408
|
}
|
|
8282
8409
|
let skMode = null;
|
|
8283
8410
|
if (functionsEnvPath) {
|
|
@@ -8285,33 +8412,69 @@ var init_check_stripe = __esm({
|
|
|
8285
8412
|
if (sk) {
|
|
8286
8413
|
if (sk.startsWith("sk_test_") || sk.startsWith("sk_live_") || sk.startsWith("rk_test_") || sk.startsWith("rk_live_")) {
|
|
8287
8414
|
skMode = sk.includes("_test_") ? "test" : "live";
|
|
8288
|
-
results.push({
|
|
8415
|
+
results.push({
|
|
8416
|
+
name: "Secret key",
|
|
8417
|
+
status: "pass",
|
|
8418
|
+
message: `Valid (${skMode} mode)`
|
|
8419
|
+
});
|
|
8289
8420
|
} else {
|
|
8290
|
-
results.push({
|
|
8421
|
+
results.push({
|
|
8422
|
+
name: "Secret key",
|
|
8423
|
+
status: "fail",
|
|
8424
|
+
message: "STRIPE_SECRET_KEY has invalid format"
|
|
8425
|
+
});
|
|
8291
8426
|
}
|
|
8292
8427
|
} else {
|
|
8293
|
-
results.push({
|
|
8428
|
+
results.push({
|
|
8429
|
+
name: "Secret key",
|
|
8430
|
+
status: "warn",
|
|
8431
|
+
message: "Not found in functions/.env"
|
|
8432
|
+
});
|
|
8294
8433
|
}
|
|
8295
8434
|
} else {
|
|
8296
|
-
results.push({
|
|
8435
|
+
results.push({
|
|
8436
|
+
name: "Secret key",
|
|
8437
|
+
status: "warn",
|
|
8438
|
+
message: "No functions/.env found"
|
|
8439
|
+
});
|
|
8297
8440
|
}
|
|
8298
8441
|
if (functionsEnvPath) {
|
|
8299
8442
|
const wh = readEnvValue(functionsEnvPath, "STRIPE_WEBHOOK_SECRET");
|
|
8300
8443
|
if (wh) {
|
|
8301
8444
|
if (wh.startsWith("whsec_")) {
|
|
8302
|
-
results.push({
|
|
8445
|
+
results.push({
|
|
8446
|
+
name: "Webhook secret",
|
|
8447
|
+
status: "pass",
|
|
8448
|
+
message: "Valid format"
|
|
8449
|
+
});
|
|
8303
8450
|
} else {
|
|
8304
|
-
results.push({
|
|
8451
|
+
results.push({
|
|
8452
|
+
name: "Webhook secret",
|
|
8453
|
+
status: "fail",
|
|
8454
|
+
message: "STRIPE_WEBHOOK_SECRET has invalid format"
|
|
8455
|
+
});
|
|
8305
8456
|
}
|
|
8306
8457
|
} else {
|
|
8307
|
-
results.push({
|
|
8458
|
+
results.push({
|
|
8459
|
+
name: "Webhook secret",
|
|
8460
|
+
status: "warn",
|
|
8461
|
+
message: "Not found \u2014 webhooks won't verify"
|
|
8462
|
+
});
|
|
8308
8463
|
}
|
|
8309
8464
|
}
|
|
8310
8465
|
if (pkMode && skMode) {
|
|
8311
8466
|
if (pkMode !== skMode) {
|
|
8312
|
-
results.push({
|
|
8467
|
+
results.push({
|
|
8468
|
+
name: "Key mode",
|
|
8469
|
+
status: "warn",
|
|
8470
|
+
message: `Publishable key is ${pkMode} but secret key is ${skMode}`
|
|
8471
|
+
});
|
|
8313
8472
|
} else {
|
|
8314
|
-
results.push({
|
|
8473
|
+
results.push({
|
|
8474
|
+
name: "Key mode",
|
|
8475
|
+
status: "pass",
|
|
8476
|
+
message: `Both keys are ${pkMode}`
|
|
8477
|
+
});
|
|
8315
8478
|
}
|
|
8316
8479
|
}
|
|
8317
8480
|
return results;
|
|
@@ -8338,13 +8501,18 @@ var init_check_auth = __esm({
|
|
|
8338
8501
|
const pkgPath = joinPath(ctx.appDir, "package.json");
|
|
8339
8502
|
if (pathExists(pkgPath)) {
|
|
8340
8503
|
const content = readSync(pkgPath, { format: "text" });
|
|
8341
|
-
if (typeof content === "string" && content.includes("@donotdev/auth"))
|
|
8504
|
+
if (typeof content === "string" && content.includes("@donotdev/auth"))
|
|
8505
|
+
return true;
|
|
8342
8506
|
}
|
|
8343
8507
|
return false;
|
|
8344
8508
|
},
|
|
8345
8509
|
async run(ctx) {
|
|
8346
8510
|
const results = [];
|
|
8347
|
-
results.push({
|
|
8511
|
+
results.push({
|
|
8512
|
+
name: "@donotdev/auth",
|
|
8513
|
+
status: "pass",
|
|
8514
|
+
message: "Installed"
|
|
8515
|
+
});
|
|
8348
8516
|
const candidates = [
|
|
8349
8517
|
joinPath(ctx.appDir, "src", "providers.ts"),
|
|
8350
8518
|
joinPath(ctx.appDir, "src", "providers.tsx"),
|
|
@@ -8361,10 +8529,14 @@ var init_check_auth = __esm({
|
|
|
8361
8529
|
const content = readSync(providerFile, { format: "text" });
|
|
8362
8530
|
if (typeof content === "string") {
|
|
8363
8531
|
const providers = [];
|
|
8364
|
-
if (content.includes("email") || content.includes("Email"))
|
|
8365
|
-
|
|
8366
|
-
if (content.includes("
|
|
8367
|
-
|
|
8532
|
+
if (content.includes("email") || content.includes("Email"))
|
|
8533
|
+
providers.push("email");
|
|
8534
|
+
if (content.includes("google") || content.includes("Google"))
|
|
8535
|
+
providers.push("google");
|
|
8536
|
+
if (content.includes("github") || content.includes("GitHub"))
|
|
8537
|
+
providers.push("github");
|
|
8538
|
+
if (content.includes("apple") || content.includes("Apple"))
|
|
8539
|
+
providers.push("apple");
|
|
8368
8540
|
if (providers.length > 0) {
|
|
8369
8541
|
results.push({
|
|
8370
8542
|
name: "Auth providers",
|
|
@@ -8374,7 +8546,11 @@ var init_check_auth = __esm({
|
|
|
8374
8546
|
}
|
|
8375
8547
|
}
|
|
8376
8548
|
} else {
|
|
8377
|
-
results.push({
|
|
8549
|
+
results.push({
|
|
8550
|
+
name: "Providers config",
|
|
8551
|
+
status: "warn",
|
|
8552
|
+
message: "No providers.ts found \u2014 auth may not work"
|
|
8553
|
+
});
|
|
8378
8554
|
}
|
|
8379
8555
|
const hasFirebase = pathExists(joinPath(ctx.projectRoot, ".firebaserc"));
|
|
8380
8556
|
const hasSupabase = pathExists(joinPath(ctx.appDir, "supabase"));
|
|
@@ -8393,7 +8569,11 @@ var init_check_auth = __esm({
|
|
|
8393
8569
|
detail: "Run dndev setup auth for setup coaching"
|
|
8394
8570
|
});
|
|
8395
8571
|
} else {
|
|
8396
|
-
results.push({
|
|
8572
|
+
results.push({
|
|
8573
|
+
name: "Backend",
|
|
8574
|
+
status: "warn",
|
|
8575
|
+
message: "No backend detected for auth"
|
|
8576
|
+
});
|
|
8397
8577
|
}
|
|
8398
8578
|
return results;
|
|
8399
8579
|
}
|
|
@@ -8442,7 +8622,9 @@ async function main(options = {}) {
|
|
|
8442
8622
|
let hasFail = false;
|
|
8443
8623
|
const registry = options.check ? CHECK_REGISTRY.filter((e2) => e2.id === options.check) : CHECK_REGISTRY;
|
|
8444
8624
|
if (options.check && registry.length === 0) {
|
|
8445
|
-
log.error(
|
|
8625
|
+
log.error(
|
|
8626
|
+
`Unknown check: ${options.check}. Available: ${CHECK_REGISTRY.map((e2) => e2.id).join(", ")}`
|
|
8627
|
+
);
|
|
8446
8628
|
Se("Doctor aborted.");
|
|
8447
8629
|
return 1;
|
|
8448
8630
|
}
|
|
@@ -8476,7 +8658,9 @@ async function main(options = {}) {
|
|
|
8476
8658
|
if (warnings > 0) summaryParts.push(`${warnings} warning(s)`);
|
|
8477
8659
|
if (fails > 0) summaryParts.push(`${fails} failure(s)`);
|
|
8478
8660
|
const exitCode = hasFail ? 1 : 0;
|
|
8479
|
-
Se(
|
|
8661
|
+
Se(
|
|
8662
|
+
hasFail ? `Health check failed: ${summaryParts.join(", ")}` : `All clear: ${summaryParts.join(", ")}`
|
|
8663
|
+
);
|
|
8480
8664
|
return exitCode;
|
|
8481
8665
|
}
|
|
8482
8666
|
var CHECK_REGISTRY, STATUS_ICONS;
|
|
@@ -8489,9 +8673,18 @@ var init_doctor = __esm({
|
|
|
8489
8673
|
init_pathResolver();
|
|
8490
8674
|
CHECK_REGISTRY = [
|
|
8491
8675
|
{ id: "env", load: async () => (await Promise.resolve().then(() => (init_check_env(), check_env_exports))).envCheck },
|
|
8492
|
-
{
|
|
8493
|
-
|
|
8494
|
-
|
|
8676
|
+
{
|
|
8677
|
+
id: "firebase",
|
|
8678
|
+
load: async () => (await Promise.resolve().then(() => (init_check_firebase(), check_firebase_exports))).firebaseCheck
|
|
8679
|
+
},
|
|
8680
|
+
{
|
|
8681
|
+
id: "supabase",
|
|
8682
|
+
load: async () => (await Promise.resolve().then(() => (init_check_supabase(), check_supabase_exports))).supabaseCheck
|
|
8683
|
+
},
|
|
8684
|
+
{
|
|
8685
|
+
id: "stripe",
|
|
8686
|
+
load: async () => (await Promise.resolve().then(() => (init_check_stripe(), check_stripe_exports))).stripeCheck
|
|
8687
|
+
},
|
|
8495
8688
|
{ id: "auth", load: async () => (await Promise.resolve().then(() => (init_check_auth(), check_auth_exports))).authCheck }
|
|
8496
8689
|
];
|
|
8497
8690
|
STATUS_ICONS = {
|
package/dist/bin/commands/emu.js
CHANGED
|
@@ -8413,10 +8413,7 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
|
|
|
8413
8413
|
// packages/tooling/src/apps/emu-supabase.ts
|
|
8414
8414
|
init_utils();
|
|
8415
8415
|
init_cli_output();
|
|
8416
|
-
import {
|
|
8417
|
-
spawn as spawn2,
|
|
8418
|
-
execSync as execSync3
|
|
8419
|
-
} from "node:child_process";
|
|
8416
|
+
import { spawn as spawn2, execSync as execSync3 } from "node:child_process";
|
|
8420
8417
|
import { platform as platform2 } from "node:os";
|
|
8421
8418
|
function isDockerRunning() {
|
|
8422
8419
|
try {
|
|
@@ -8466,7 +8463,10 @@ async function startSupabase(app, _projectRoot) {
|
|
|
8466
8463
|
if (!childProcess.pid) return;
|
|
8467
8464
|
if (isWindows) {
|
|
8468
8465
|
try {
|
|
8469
|
-
execSync3(`taskkill /F /T /PID ${childProcess.pid}`, {
|
|
8466
|
+
execSync3(`taskkill /F /T /PID ${childProcess.pid}`, {
|
|
8467
|
+
stdio: "ignore",
|
|
8468
|
+
timeout: 2e3
|
|
8469
|
+
});
|
|
8470
8470
|
} catch {
|
|
8471
8471
|
}
|
|
8472
8472
|
} else {
|
|
@@ -8496,10 +8496,7 @@ async function startSupabase(app, _projectRoot) {
|
|
|
8496
8496
|
// packages/tooling/src/apps/emu-vercel.ts
|
|
8497
8497
|
init_utils();
|
|
8498
8498
|
init_cli_output();
|
|
8499
|
-
import {
|
|
8500
|
-
spawn as spawn3,
|
|
8501
|
-
execSync as execSync4
|
|
8502
|
-
} from "node:child_process";
|
|
8499
|
+
import { spawn as spawn3, execSync as execSync4 } from "node:child_process";
|
|
8503
8500
|
import { platform as platform3 } from "node:os";
|
|
8504
8501
|
async function startVercel(app, _projectRoot) {
|
|
8505
8502
|
log.info(`Starting Vercel dev server for ${app.name}...
|
|
@@ -8512,20 +8509,19 @@ async function startVercel(app, _projectRoot) {
|
|
|
8512
8509
|
return 1;
|
|
8513
8510
|
}
|
|
8514
8511
|
const isWindows = platform3() === "win32";
|
|
8515
|
-
const childProcess = spawn3(
|
|
8516
|
-
"
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
cwd: app.path,
|
|
8521
|
-
shell: isWindows
|
|
8522
|
-
}
|
|
8523
|
-
);
|
|
8512
|
+
const childProcess = spawn3("vercel", ["dev"], {
|
|
8513
|
+
stdio: "inherit",
|
|
8514
|
+
cwd: app.path,
|
|
8515
|
+
shell: isWindows
|
|
8516
|
+
});
|
|
8524
8517
|
const cleanup = () => {
|
|
8525
8518
|
if (!childProcess.pid) return;
|
|
8526
8519
|
if (isWindows) {
|
|
8527
8520
|
try {
|
|
8528
|
-
execSync4(`taskkill /F /T /PID ${childProcess.pid}`, {
|
|
8521
|
+
execSync4(`taskkill /F /T /PID ${childProcess.pid}`, {
|
|
8522
|
+
stdio: "ignore",
|
|
8523
|
+
timeout: 2e3
|
|
8524
|
+
});
|
|
8529
8525
|
} catch {
|
|
8530
8526
|
}
|
|
8531
8527
|
} else {
|
|
@@ -8572,7 +8568,9 @@ async function main(options) {
|
|
|
8572
8568
|
switch (app.platform) {
|
|
8573
8569
|
case "firebase": {
|
|
8574
8570
|
if (!app.hasFunctions) {
|
|
8575
|
-
log.error(
|
|
8571
|
+
log.error(
|
|
8572
|
+
`App "${app.name}" has Firebase config but no functions directory.`
|
|
8573
|
+
);
|
|
8576
8574
|
log.error('Use "dndev dev" for apps without backend functions.\n');
|
|
8577
8575
|
return 1;
|
|
8578
8576
|
}
|
|
@@ -77121,7 +77121,10 @@ function getSupabaseConfig(appDir) {
|
|
|
77121
77121
|
}
|
|
77122
77122
|
async function makeSupabaseAdmin(appDir, userIdOrEmail, role) {
|
|
77123
77123
|
const config = getSupabaseConfig(appDir);
|
|
77124
|
-
if (!config)
|
|
77124
|
+
if (!config)
|
|
77125
|
+
throw new Error(
|
|
77126
|
+
"Supabase config not found. Set SUPABASE_URL and SUPABASE_SECRET_KEY in that app's functions/.env"
|
|
77127
|
+
);
|
|
77125
77128
|
M2.info(`Connecting to Supabase (${config.path})...`);
|
|
77126
77129
|
const supabase = createClient(config.url, config.key, {
|
|
77127
77130
|
auth: {
|
|
@@ -77137,7 +77140,10 @@ async function makeSupabaseAdmin(appDir, userIdOrEmail, role) {
|
|
|
77137
77140
|
const perPage = 50;
|
|
77138
77141
|
let user;
|
|
77139
77142
|
while (true) {
|
|
77140
|
-
const { data: data2, error: error3 } = await supabase.auth.admin.listUsers({
|
|
77143
|
+
const { data: data2, error: error3 } = await supabase.auth.admin.listUsers({
|
|
77144
|
+
page,
|
|
77145
|
+
perPage
|
|
77146
|
+
});
|
|
77141
77147
|
if (error3) throw error3;
|
|
77142
77148
|
user = data2.users.find((u2) => u2.email === userIdOrEmail);
|
|
77143
77149
|
if (user) break;
|
|
@@ -77145,7 +77151,9 @@ async function makeSupabaseAdmin(appDir, userIdOrEmail, role) {
|
|
|
77145
77151
|
page += 1;
|
|
77146
77152
|
}
|
|
77147
77153
|
if (!user) {
|
|
77148
|
-
throw new Error(
|
|
77154
|
+
throw new Error(
|
|
77155
|
+
`User ${userIdOrEmail} not found (use UUID if you know it)`
|
|
77156
|
+
);
|
|
77149
77157
|
}
|
|
77150
77158
|
uid = user.id;
|
|
77151
77159
|
M2.info(`Found user ID: ${uid}`);
|
|
@@ -77161,7 +77169,9 @@ async function makeSupabaseAdmin(appDir, userIdOrEmail, role) {
|
|
|
77161
77169
|
if (error2) throw error2;
|
|
77162
77170
|
const email = data?.user?.email ?? uid;
|
|
77163
77171
|
M2.success(`User ${email} is now ${role}!`);
|
|
77164
|
-
M2.info(
|
|
77172
|
+
M2.info(
|
|
77173
|
+
"Supabase: app_metadata updated. JWT will reflect this on next refresh/signin."
|
|
77174
|
+
);
|
|
77165
77175
|
}
|
|
77166
77176
|
function initFirebase(appDir, projectId) {
|
|
77167
77177
|
if (getApps().length > 0) {
|
|
@@ -77175,9 +77185,13 @@ function initFirebase(appDir, projectId) {
|
|
|
77175
77185
|
];
|
|
77176
77186
|
const serviceAccountPath = process.env.FIREBASE_SERVICE_ACCOUNT || possiblePaths.find((p2) => pathExists(p2));
|
|
77177
77187
|
if (!serviceAccountPath || !pathExists(serviceAccountPath)) {
|
|
77178
|
-
throw new Error(
|
|
77188
|
+
throw new Error(
|
|
77189
|
+
"Service account key not found. Set FIREBASE_SERVICE_ACCOUNT or place service-account-key.json in that app root or app/functions/."
|
|
77190
|
+
);
|
|
77179
77191
|
}
|
|
77180
|
-
const serviceAccount = readSync(serviceAccountPath, {
|
|
77192
|
+
const serviceAccount = readSync(serviceAccountPath, {
|
|
77193
|
+
format: "json"
|
|
77194
|
+
});
|
|
77181
77195
|
const targetProjectId = projectId || serviceAccount.project_id;
|
|
77182
77196
|
initializeApp({
|
|
77183
77197
|
credential: cert(serviceAccount),
|
|
@@ -77200,10 +77214,15 @@ async function makeFirebaseAdmin(auth, userId, projectId, role = "admin") {
|
|
|
77200
77214
|
isSuper
|
|
77201
77215
|
};
|
|
77202
77216
|
await auth.setCustomUserClaims(userId, newClaims);
|
|
77203
|
-
M2.success(
|
|
77204
|
-
|
|
77217
|
+
M2.success(
|
|
77218
|
+
`User ${user.email || userId} now has role '${role}' in project ${projectId}`
|
|
77219
|
+
);
|
|
77220
|
+
M2.info(
|
|
77221
|
+
"Note: User must sign out and sign in again for changes to take effect."
|
|
77222
|
+
);
|
|
77205
77223
|
} catch (error2) {
|
|
77206
|
-
if (error2.code === "auth/user-not-found")
|
|
77224
|
+
if (error2.code === "auth/user-not-found")
|
|
77225
|
+
throw new Error(`User with ID ${userId} not found in Firebase Auth`);
|
|
77207
77226
|
throw error2;
|
|
77208
77227
|
}
|
|
77209
77228
|
}
|
|
@@ -77235,7 +77254,8 @@ async function main(args = []) {
|
|
|
77235
77254
|
message: `Enter user Email or UUID to assign role '${role}'`,
|
|
77236
77255
|
placeholder: "user@example.com or uuid",
|
|
77237
77256
|
validate: (value) => {
|
|
77238
|
-
if (!value || value.trim().length === 0)
|
|
77257
|
+
if (!value || value.trim().length === 0)
|
|
77258
|
+
return "User Identifier is required";
|
|
77239
77259
|
return;
|
|
77240
77260
|
}
|
|
77241
77261
|
});
|