clawfire 0.6.7 → 0.6.9

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/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", "hosting", "--json"],
3350
- 12e4
3351
- // 2 min timeout
3350
+ ["deploy", "--only", targets, "--json"],
3351
+ timeoutMs
3352
3352
  );
3353
3353
  let url = "";
3354
3354
  try {
@@ -3371,12 +3371,111 @@ var FirebaseSetup = class {
3371
3371
  url = `https://${state.projectId}.web.app`;
3372
3372
  }
3373
3373
  }
3374
- return { success: true, url: url || void 0, message: "Hosting deployed successfully!" };
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 has functions configuration.
3420
+ */
3421
+ ensureFunctionsConfig() {
3422
+ const firebaseJsonPath = resolve5(this.projectDir, "firebase.json");
3423
+ if (!existsSync6(firebaseJsonPath)) {
3424
+ return { success: false, message: "firebase.json not found." };
3425
+ }
3426
+ try {
3427
+ const config = JSON.parse(readFileSync4(firebaseJsonPath, "utf-8"));
3428
+ if (!config.functions) {
3429
+ config.functions = {
3430
+ source: "functions",
3431
+ runtime: "nodejs20",
3432
+ codebase: "clawfire"
3433
+ };
3434
+ writeFileSync3(firebaseJsonPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
3435
+ return { success: true, message: "Functions config added to firebase.json." };
3436
+ }
3437
+ return { success: true, message: "Functions config already present." };
3438
+ } catch {
3439
+ return { success: false, message: "Invalid firebase.json." };
3440
+ }
3441
+ }
3442
+ /**
3443
+ * Install dependencies and build functions for deployment.
3444
+ */
3445
+ async buildFunctions() {
3446
+ const functionsDir = resolve5(this.projectDir, "functions");
3447
+ if (!existsSync6(functionsDir)) {
3448
+ return { success: false, message: "functions/ directory not found." };
3449
+ }
3450
+ const pkgPath = resolve5(functionsDir, "package.json");
3451
+ if (!existsSync6(pkgPath)) {
3452
+ return { success: false, message: "functions/package.json not found." };
3453
+ }
3454
+ const nodeModulesPath = resolve5(functionsDir, "node_modules");
3455
+ if (!existsSync6(nodeModulesPath)) {
3456
+ try {
3457
+ await this.execTimeout("npm", ["install"], 12e4, functionsDir);
3458
+ } catch (err) {
3459
+ const msg = err instanceof Error ? err.message : "Unknown error";
3460
+ return { success: false, message: `npm install in functions/ failed: ${msg}` };
3461
+ }
3462
+ }
3463
+ try {
3464
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
3465
+ if (pkg.scripts?.build) {
3466
+ await this.execTimeout("npm", ["run", "build"], 6e4, functionsDir);
3467
+ } else {
3468
+ const tsconfigPath = resolve5(functionsDir, "tsconfig.json");
3469
+ if (existsSync6(tsconfigPath)) {
3470
+ await this.execTimeout("npx", ["tsc"], 6e4, functionsDir);
3471
+ }
3472
+ }
3473
+ return { success: true, message: "Functions built successfully." };
3474
+ } catch (err) {
3475
+ const msg = err instanceof Error ? err.message : "Unknown error";
3476
+ return { success: false, message: `Functions build failed: ${msg}` };
3477
+ }
3478
+ }
3380
3479
  // ─── Service Enable ────────────────────────────────────────────────
3381
3480
  enableService(service) {
3382
3481
  const firebaseJsonPath = resolve5(this.projectDir, "firebase.json");
@@ -3625,9 +3724,9 @@ https://console.developers.google.com/apis/api/firestore.googleapis.com/overview
3625
3724
  }
3626
3725
  }
3627
3726
  // ─── Helpers ───────────────────────────────────────────────────────
3628
- execTimeout(command, args, timeoutMs) {
3727
+ execTimeout(command, args, timeoutMs, cwd) {
3629
3728
  return new Promise((resolve7, reject) => {
3630
- const proc = execFile2(command, args, { cwd: this.projectDir, timeout: timeoutMs }, (err, stdout, stderr) => {
3729
+ const proc = execFile2(command, args, { cwd: cwd || this.projectDir, timeout: timeoutMs }, (err, stdout, stderr) => {
3631
3730
  if (err) {
3632
3731
  const detail = stderr?.trim() || stdout?.trim() || "";
3633
3732
  const enriched = new Error(`${err.message}${detail ? "\n" + detail : ""}`);
@@ -4790,19 +4889,67 @@ ${liveReloadScript}
4790
4889
  if (url.pathname === "/__dev/deploy/hosting" && req.method === "POST") {
4791
4890
  (async () => {
4792
4891
  try {
4892
+ const steps = [];
4893
+ const functionsDir = resolve6(this.options.projectDir, "functions");
4894
+ const hasFunctions = existsSync7(functionsDir);
4895
+ const projectConfig = this.readProjectConfig();
4896
+ const firebaseConfig = {};
4897
+ for (const field of projectConfig.fields) {
4898
+ if (!field.isPlaceholder) {
4899
+ firebaseConfig[field.key] = field.value;
4900
+ }
4901
+ }
4793
4902
  if (this.pageCompiler.isActive()) {
4903
+ const configScript = Object.keys(firebaseConfig).length > 0 ? `
4904
+ <script>window.__CLAWFIRE_CONFIG__=${JSON.stringify(firebaseConfig)};</script>` : "";
4794
4905
  const routerScript = generateProductionRouterScript();
4795
- const buildResult = this.pageCompiler.buildForProduction(this.publicDir, routerScript);
4906
+ const scriptToInject = configScript + routerScript;
4907
+ const buildResult = this.pageCompiler.buildForProduction(this.publicDir, scriptToInject);
4796
4908
  console.log(` \x1B[32m\u2713\x1B[0m Built ${buildResult.pages.length} pages for production`);
4909
+ steps.push(`${buildResult.pages.length} pages built`);
4910
+ if (configScript) {
4911
+ console.log(` \x1B[32m\u2713\x1B[0m Firebase config injected (projectId: ${firebaseConfig.projectId || "?"})`);
4912
+ steps.push("Firebase config injected");
4913
+ }
4797
4914
  if (buildResult.errors.length > 0) {
4798
4915
  for (const err of buildResult.errors) {
4799
4916
  console.log(` \x1B[31m\u2717\x1B[0m ${err}`);
4800
4917
  }
4801
4918
  }
4802
4919
  }
4803
- const result = await this.firebaseSetup.deployHosting();
4920
+ let deployFunctions = false;
4921
+ if (hasFunctions) {
4922
+ const syncResult = this.firebaseSetup.syncEnvToFunctions(firebaseConfig);
4923
+ if (syncResult.success) {
4924
+ console.log(` \x1B[32m\u2713\x1B[0m ${syncResult.message}`);
4925
+ steps.push(`${syncResult.count} env vars synced`);
4926
+ } else {
4927
+ console.log(` \x1B[33m\u26A0\x1B[0m Env sync: ${syncResult.message}`);
4928
+ }
4929
+ const configResult = this.firebaseSetup.ensureFunctionsConfig();
4930
+ if (configResult.success) {
4931
+ console.log(` \x1B[32m\u2713\x1B[0m ${configResult.message}`);
4932
+ }
4933
+ console.log(" Building functions...");
4934
+ const buildResult = await this.firebaseSetup.buildFunctions();
4935
+ if (buildResult.success) {
4936
+ console.log(` \x1B[32m\u2713\x1B[0m ${buildResult.message}`);
4937
+ steps.push("Functions built");
4938
+ deployFunctions = true;
4939
+ } else {
4940
+ console.log(` \x1B[31m\u2717\x1B[0m ${buildResult.message}`);
4941
+ console.log(" \x1B[33m\u26A0\x1B[0m Deploying hosting only (functions build failed)");
4942
+ steps.push("Functions build failed \u2014 hosting only");
4943
+ }
4944
+ }
4945
+ const targets = deployFunctions ? "hosting,functions" : "hosting";
4946
+ console.log(` Deploying ${targets}...`);
4947
+ const result = await this.firebaseSetup.deployHosting(targets);
4804
4948
  clearFirebaseStatusCache();
4805
- sendJson(result);
4949
+ sendJson({
4950
+ ...result,
4951
+ message: result.message + (steps.length > 0 ? ` (${steps.join(", ")})` : "")
4952
+ });
4806
4953
  } catch (err) {
4807
4954
  sendJson({ success: false, message: err instanceof Error ? err.message : "Failed" }, 500);
4808
4955
  }