clawfire 0.6.8 → 0.6.10
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/dist/cli.js +1 -1
- package/dist/{dev-server-ESMIOA6C.js → dev-server-FNRFWJD3.js} +189 -16
- package/dist/dev.cjs +189 -16
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.js +189 -16
- package/dist/dev.js.map +1 -1
- package/package.json +1 -1
package/dist/dev.js
CHANGED
|
@@ -3330,7 +3330,7 @@ var FirebaseSetup = class {
|
|
|
3330
3330
|
return { success: true, message: `Web app "${displayName}" selected.` };
|
|
3331
3331
|
}
|
|
3332
3332
|
// ─── Deploy ─────────────────────────────────────────────────────────
|
|
3333
|
-
async deployHosting() {
|
|
3333
|
+
async deployHosting(targets = "hosting") {
|
|
3334
3334
|
const firebaseJsonPath = resolve5(this.projectDir, "firebase.json");
|
|
3335
3335
|
if (!existsSync6(firebaseJsonPath)) {
|
|
3336
3336
|
return { success: false, message: "firebase.json not found. Enable hosting first." };
|
|
@@ -3343,12 +3343,12 @@ var FirebaseSetup = class {
|
|
|
3343
3343
|
} catch {
|
|
3344
3344
|
return { success: false, message: "Invalid firebase.json." };
|
|
3345
3345
|
}
|
|
3346
|
+
const timeoutMs = targets.includes("functions") ? 3e5 : 12e4;
|
|
3346
3347
|
try {
|
|
3347
3348
|
const output = await this.execTimeout(
|
|
3348
3349
|
"firebase",
|
|
3349
|
-
["deploy", "--only",
|
|
3350
|
-
|
|
3351
|
-
// 2 min timeout
|
|
3350
|
+
["deploy", "--only", targets, "--json"],
|
|
3351
|
+
timeoutMs
|
|
3352
3352
|
);
|
|
3353
3353
|
let url = "";
|
|
3354
3354
|
try {
|
|
@@ -3371,12 +3371,147 @@ var FirebaseSetup = class {
|
|
|
3371
3371
|
url = `https://${state.projectId}.web.app`;
|
|
3372
3372
|
}
|
|
3373
3373
|
}
|
|
3374
|
-
|
|
3374
|
+
const label = targets.includes("functions") ? "Hosting + Functions" : "Hosting";
|
|
3375
|
+
return { success: true, url: url || void 0, message: `${label} deployed successfully!` };
|
|
3375
3376
|
} catch (err) {
|
|
3376
3377
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
3377
3378
|
return { success: false, message: `Deploy failed: ${msg}` };
|
|
3378
3379
|
}
|
|
3379
3380
|
}
|
|
3381
|
+
// ─── Full Deploy Pipeline ────────────────────────────────────────────
|
|
3382
|
+
/**
|
|
3383
|
+
* Sync environment variables to functions/.env for production.
|
|
3384
|
+
* Copies project root .env vars + injects CLAWFIRE_FIREBASE_CONFIG.
|
|
3385
|
+
*/
|
|
3386
|
+
syncEnvToFunctions(firebaseConfig) {
|
|
3387
|
+
const functionsDir = resolve5(this.projectDir, "functions");
|
|
3388
|
+
if (!existsSync6(functionsDir)) {
|
|
3389
|
+
return { success: false, message: "functions/ directory not found.", count: 0 };
|
|
3390
|
+
}
|
|
3391
|
+
const lines = [];
|
|
3392
|
+
const rootEnvPath = resolve5(this.projectDir, ".env");
|
|
3393
|
+
if (existsSync6(rootEnvPath)) {
|
|
3394
|
+
const content = readFileSync4(rootEnvPath, "utf-8");
|
|
3395
|
+
for (const rawLine of content.split("\n")) {
|
|
3396
|
+
let trimmed = rawLine.trim();
|
|
3397
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
3398
|
+
if (trimmed.startsWith("export ")) {
|
|
3399
|
+
trimmed = trimmed.slice(7);
|
|
3400
|
+
}
|
|
3401
|
+
const eqIdx = trimmed.indexOf("=");
|
|
3402
|
+
if (eqIdx === -1) continue;
|
|
3403
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
3404
|
+
if (key === "NPM_TOKEN" || key === "NODE_ENV") continue;
|
|
3405
|
+
lines.push(trimmed);
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {
|
|
3409
|
+
const filtered = lines.filter((l) => !l.startsWith("CLAWFIRE_FIREBASE_CONFIG="));
|
|
3410
|
+
filtered.push(`CLAWFIRE_FIREBASE_CONFIG=${JSON.stringify(firebaseConfig)}`);
|
|
3411
|
+
lines.length = 0;
|
|
3412
|
+
lines.push(...filtered);
|
|
3413
|
+
}
|
|
3414
|
+
const functionsEnvPath = resolve5(functionsDir, ".env");
|
|
3415
|
+
writeFileSync3(functionsEnvPath, lines.join("\n") + "\n", "utf-8");
|
|
3416
|
+
return { success: true, message: `Synced ${lines.length} env vars to functions/.env`, count: lines.length };
|
|
3417
|
+
}
|
|
3418
|
+
/**
|
|
3419
|
+
* Ensure firebase.json is fully configured for deployment:
|
|
3420
|
+
* - functions config exists
|
|
3421
|
+
* - hosting has cleanUrls
|
|
3422
|
+
* - hosting has API rewrite (/api/** → api function) before catch-all
|
|
3423
|
+
*
|
|
3424
|
+
* Fixes existing configs created by older versions.
|
|
3425
|
+
*/
|
|
3426
|
+
ensureDeployConfig() {
|
|
3427
|
+
const firebaseJsonPath = resolve5(this.projectDir, "firebase.json");
|
|
3428
|
+
if (!existsSync6(firebaseJsonPath)) {
|
|
3429
|
+
return { success: false, message: "firebase.json not found.", changes: [] };
|
|
3430
|
+
}
|
|
3431
|
+
try {
|
|
3432
|
+
const config = JSON.parse(readFileSync4(firebaseJsonPath, "utf-8"));
|
|
3433
|
+
const changes = [];
|
|
3434
|
+
if (!config.functions) {
|
|
3435
|
+
config.functions = {
|
|
3436
|
+
source: "functions",
|
|
3437
|
+
runtime: "nodejs20",
|
|
3438
|
+
codebase: "clawfire"
|
|
3439
|
+
};
|
|
3440
|
+
changes.push("Added functions config");
|
|
3441
|
+
}
|
|
3442
|
+
if (config.hosting && !config.hosting.cleanUrls) {
|
|
3443
|
+
config.hosting.cleanUrls = true;
|
|
3444
|
+
changes.push("Added cleanUrls");
|
|
3445
|
+
}
|
|
3446
|
+
if (config.hosting) {
|
|
3447
|
+
if (!config.hosting.rewrites) {
|
|
3448
|
+
config.hosting.rewrites = [];
|
|
3449
|
+
}
|
|
3450
|
+
const rewrites = config.hosting.rewrites;
|
|
3451
|
+
const hasApiRewrite = rewrites.some(
|
|
3452
|
+
(r) => r.source === "/api/**" && r.function === "api"
|
|
3453
|
+
);
|
|
3454
|
+
if (!hasApiRewrite) {
|
|
3455
|
+
const apiRewrite = { source: "/api/**", function: "api" };
|
|
3456
|
+
const catchAllIndex = rewrites.findIndex((r) => r.source === "**");
|
|
3457
|
+
if (catchAllIndex >= 0) {
|
|
3458
|
+
rewrites.splice(catchAllIndex, 0, apiRewrite);
|
|
3459
|
+
} else {
|
|
3460
|
+
rewrites.push(apiRewrite);
|
|
3461
|
+
rewrites.push({ source: "**", destination: "/index.html" });
|
|
3462
|
+
}
|
|
3463
|
+
changes.push("Added API rewrite (/api/** \u2192 Cloud Function)");
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
if (changes.length > 0) {
|
|
3467
|
+
writeFileSync3(firebaseJsonPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
3468
|
+
}
|
|
3469
|
+
return {
|
|
3470
|
+
success: true,
|
|
3471
|
+
message: changes.length > 0 ? changes.join(", ") : "Deploy config up to date.",
|
|
3472
|
+
changes
|
|
3473
|
+
};
|
|
3474
|
+
} catch {
|
|
3475
|
+
return { success: false, message: "Invalid firebase.json.", changes: [] };
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
/**
|
|
3479
|
+
* Install dependencies and build functions for deployment.
|
|
3480
|
+
*/
|
|
3481
|
+
async buildFunctions() {
|
|
3482
|
+
const functionsDir = resolve5(this.projectDir, "functions");
|
|
3483
|
+
if (!existsSync6(functionsDir)) {
|
|
3484
|
+
return { success: false, message: "functions/ directory not found." };
|
|
3485
|
+
}
|
|
3486
|
+
const pkgPath = resolve5(functionsDir, "package.json");
|
|
3487
|
+
if (!existsSync6(pkgPath)) {
|
|
3488
|
+
return { success: false, message: "functions/package.json not found." };
|
|
3489
|
+
}
|
|
3490
|
+
const nodeModulesPath = resolve5(functionsDir, "node_modules");
|
|
3491
|
+
if (!existsSync6(nodeModulesPath)) {
|
|
3492
|
+
try {
|
|
3493
|
+
await this.execTimeout("npm", ["install"], 12e4, functionsDir);
|
|
3494
|
+
} catch (err) {
|
|
3495
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
3496
|
+
return { success: false, message: `npm install in functions/ failed: ${msg}` };
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
try {
|
|
3500
|
+
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
3501
|
+
if (pkg.scripts?.build) {
|
|
3502
|
+
await this.execTimeout("npm", ["run", "build"], 6e4, functionsDir);
|
|
3503
|
+
} else {
|
|
3504
|
+
const tsconfigPath = resolve5(functionsDir, "tsconfig.json");
|
|
3505
|
+
if (existsSync6(tsconfigPath)) {
|
|
3506
|
+
await this.execTimeout("npx", ["tsc"], 6e4, functionsDir);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
return { success: true, message: "Functions built successfully." };
|
|
3510
|
+
} catch (err) {
|
|
3511
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
3512
|
+
return { success: false, message: `Functions build failed: ${msg}` };
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3380
3515
|
// ─── Service Enable ────────────────────────────────────────────────
|
|
3381
3516
|
enableService(service) {
|
|
3382
3517
|
const firebaseJsonPath = resolve5(this.projectDir, "firebase.json");
|
|
@@ -3625,9 +3760,9 @@ https://console.developers.google.com/apis/api/firestore.googleapis.com/overview
|
|
|
3625
3760
|
}
|
|
3626
3761
|
}
|
|
3627
3762
|
// ─── Helpers ───────────────────────────────────────────────────────
|
|
3628
|
-
execTimeout(command, args, timeoutMs) {
|
|
3763
|
+
execTimeout(command, args, timeoutMs, cwd) {
|
|
3629
3764
|
return new Promise((resolve7, reject) => {
|
|
3630
|
-
const proc = execFile2(command, args, { cwd: this.projectDir, timeout: timeoutMs }, (err, stdout, stderr) => {
|
|
3765
|
+
const proc = execFile2(command, args, { cwd: cwd || this.projectDir, timeout: timeoutMs }, (err, stdout, stderr) => {
|
|
3631
3766
|
if (err) {
|
|
3632
3767
|
const detail = stderr?.trim() || stdout?.trim() || "";
|
|
3633
3768
|
const enriched = new Error(`${err.message}${detail ? "\n" + detail : ""}`);
|
|
@@ -4790,22 +4925,27 @@ ${liveReloadScript}
|
|
|
4790
4925
|
if (url.pathname === "/__dev/deploy/hosting" && req.method === "POST") {
|
|
4791
4926
|
(async () => {
|
|
4792
4927
|
try {
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4928
|
+
const steps = [];
|
|
4929
|
+
const functionsDir = resolve6(this.options.projectDir, "functions");
|
|
4930
|
+
const hasFunctions = existsSync7(functionsDir);
|
|
4931
|
+
const projectConfig = this.readProjectConfig();
|
|
4932
|
+
const firebaseConfig = {};
|
|
4933
|
+
for (const field of projectConfig.fields) {
|
|
4934
|
+
if (!field.isPlaceholder) {
|
|
4935
|
+
firebaseConfig[field.key] = field.value;
|
|
4800
4936
|
}
|
|
4937
|
+
}
|
|
4938
|
+
if (this.pageCompiler.isActive()) {
|
|
4801
4939
|
const configScript = Object.keys(firebaseConfig).length > 0 ? `
|
|
4802
4940
|
<script>window.__CLAWFIRE_CONFIG__=${JSON.stringify(firebaseConfig)};</script>` : "";
|
|
4803
4941
|
const routerScript = generateProductionRouterScript();
|
|
4804
4942
|
const scriptToInject = configScript + routerScript;
|
|
4805
4943
|
const buildResult = this.pageCompiler.buildForProduction(this.publicDir, scriptToInject);
|
|
4806
4944
|
console.log(` \x1B[32m\u2713\x1B[0m Built ${buildResult.pages.length} pages for production`);
|
|
4945
|
+
steps.push(`${buildResult.pages.length} pages built`);
|
|
4807
4946
|
if (configScript) {
|
|
4808
4947
|
console.log(` \x1B[32m\u2713\x1B[0m Firebase config injected (projectId: ${firebaseConfig.projectId || "?"})`);
|
|
4948
|
+
steps.push("Firebase config injected");
|
|
4809
4949
|
}
|
|
4810
4950
|
if (buildResult.errors.length > 0) {
|
|
4811
4951
|
for (const err of buildResult.errors) {
|
|
@@ -4813,9 +4953,42 @@ ${liveReloadScript}
|
|
|
4813
4953
|
}
|
|
4814
4954
|
}
|
|
4815
4955
|
}
|
|
4816
|
-
const
|
|
4956
|
+
const deployConfig = this.firebaseSetup.ensureDeployConfig();
|
|
4957
|
+
if (deployConfig.success && deployConfig.changes.length > 0) {
|
|
4958
|
+
for (const change of deployConfig.changes) {
|
|
4959
|
+
console.log(` \x1B[32m\u2713\x1B[0m firebase.json: ${change}`);
|
|
4960
|
+
}
|
|
4961
|
+
steps.push(`firebase.json updated (${deployConfig.changes.length} fixes)`);
|
|
4962
|
+
}
|
|
4963
|
+
let deployFunctions = false;
|
|
4964
|
+
if (hasFunctions) {
|
|
4965
|
+
const syncResult = this.firebaseSetup.syncEnvToFunctions(firebaseConfig);
|
|
4966
|
+
if (syncResult.success) {
|
|
4967
|
+
console.log(` \x1B[32m\u2713\x1B[0m ${syncResult.message}`);
|
|
4968
|
+
steps.push(`${syncResult.count} env vars synced`);
|
|
4969
|
+
} else {
|
|
4970
|
+
console.log(` \x1B[33m\u26A0\x1B[0m Env sync: ${syncResult.message}`);
|
|
4971
|
+
}
|
|
4972
|
+
console.log(" Building functions...");
|
|
4973
|
+
const buildResult = await this.firebaseSetup.buildFunctions();
|
|
4974
|
+
if (buildResult.success) {
|
|
4975
|
+
console.log(` \x1B[32m\u2713\x1B[0m ${buildResult.message}`);
|
|
4976
|
+
steps.push("Functions built");
|
|
4977
|
+
deployFunctions = true;
|
|
4978
|
+
} else {
|
|
4979
|
+
console.log(` \x1B[31m\u2717\x1B[0m ${buildResult.message}`);
|
|
4980
|
+
console.log(" \x1B[33m\u26A0\x1B[0m Deploying hosting only (functions build failed)");
|
|
4981
|
+
steps.push("Functions build failed \u2014 hosting only");
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
const targets = deployFunctions ? "hosting,functions" : "hosting";
|
|
4985
|
+
console.log(` Deploying ${targets}...`);
|
|
4986
|
+
const result = await this.firebaseSetup.deployHosting(targets);
|
|
4817
4987
|
clearFirebaseStatusCache();
|
|
4818
|
-
sendJson(
|
|
4988
|
+
sendJson({
|
|
4989
|
+
...result,
|
|
4990
|
+
message: result.message + (steps.length > 0 ? ` (${steps.join(", ")})` : "")
|
|
4991
|
+
});
|
|
4819
4992
|
} catch (err) {
|
|
4820
4993
|
sendJson({ success: false, message: err instanceof Error ? err.message : "Failed" }, 500);
|
|
4821
4994
|
}
|