create-100x-mobile 0.5.0 → 0.5.2

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.
@@ -18,6 +18,7 @@ exports.writeInstantEnvironment = writeInstantEnvironment;
18
18
  exports.initializeConvex = initializeConvex;
19
19
  exports.ensureExpoPublicConvexUrl = ensureExpoPublicConvexUrl;
20
20
  exports.setConvexClerkEnv = setConvexClerkEnv;
21
+ exports.setupCloudflare = setupCloudflare;
21
22
  exports.initializeGit = initializeGit;
22
23
  exports.runHealthChecks = runHealthChecks;
23
24
  const node_path_1 = require("node:path");
@@ -524,6 +525,61 @@ async function setConvexClerkEnv(projectDir, clerkDomain) {
524
525
  return false;
525
526
  }
526
527
  }
528
+ async function setupCloudflare(projectDir) {
529
+ const result = {
530
+ configured: false,
531
+ loggedIn: false,
532
+ deployed: false,
533
+ workerUrl: "",
534
+ };
535
+ // Step 1: cloudflare:configure (alchemy configure)
536
+ try {
537
+ await (0, run_1.run)("bun", ["run", "cloudflare:configure"], { cwd: projectDir });
538
+ result.configured = true;
539
+ }
540
+ catch (error) {
541
+ prompts_1.log.warn(`Cloudflare configure failed: ${toErrorMessage(error)}`);
542
+ prompts_1.log.info(picocolors_1.default.dim("Run manually: bun run cloudflare:configure"));
543
+ return result;
544
+ }
545
+ // Step 2: cloudflare:login (alchemy login)
546
+ try {
547
+ await (0, run_1.run)("bun", ["run", "cloudflare:login"], { cwd: projectDir });
548
+ result.loggedIn = true;
549
+ }
550
+ catch (error) {
551
+ prompts_1.log.warn(`Cloudflare login failed: ${toErrorMessage(error)}`);
552
+ prompts_1.log.info(picocolors_1.default.dim("Run manually: bun run cloudflare:login"));
553
+ return result;
554
+ }
555
+ // Step 3: cloudflare:deploy (alchemy deploy) — capture output for worker URL
556
+ try {
557
+ const deployOutput = await (0, run_1.runCapture)("bun", ["run", "cloudflare:deploy"], { cwd: projectDir });
558
+ result.deployed = true;
559
+ // Try to extract the storageWorkerUrl from the deploy output
560
+ const urlMatch = deployOutput.match(/storageWorkerUrl[:\s]*["']?(https?:\/\/[^\s"']+)["']?/i);
561
+ if (urlMatch?.[1]) {
562
+ result.workerUrl = urlMatch[1];
563
+ // Write the worker URL to .env.local
564
+ const envLocalPath = (0, node_path_1.join)(projectDir, ".env.local");
565
+ let envContents = "";
566
+ try {
567
+ envContents = await (0, fs_1.readTextFile)(envLocalPath);
568
+ }
569
+ catch {
570
+ // .env.local may not exist yet
571
+ }
572
+ envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "EXPO_PUBLIC_STORAGE_WORKER_URL", result.workerUrl);
573
+ await (0, fs_1.writeTextFile)(envLocalPath, envContents);
574
+ }
575
+ }
576
+ catch (error) {
577
+ prompts_1.log.warn(`Cloudflare deploy failed: ${toErrorMessage(error)}`);
578
+ prompts_1.log.info(picocolors_1.default.dim("Run manually: bun run cloudflare:deploy"));
579
+ return result;
580
+ }
581
+ return result;
582
+ }
527
583
  async function initializeGit(projectDir) {
528
584
  try {
529
585
  await (0, run_1.run)("git", ["init"], { cwd: projectDir });
@@ -257,9 +257,11 @@ async function cmdNew(rawArgs) {
257
257
  prompts_1.log.info(picocolors_1.default.dim(`Client name: ${instant.clerkClientName}`));
258
258
  prompts_1.log.info(picocolors_1.default.dim(`Clerk publishable key: ${instant.clerkPublishableKey}`));
259
259
  }
260
+ const isCloudflareMode = backend === "instantdb" && (0, scaffold_1.isCloudflareInstantAuthMode)(instantAuthMode);
260
261
  const totalSteps = 1 + // project generation
261
262
  (options.installDependencies ? 2 : 0) +
262
263
  (backend === "convex" ? 2 + (clerk.domain ? 1 : 0) : 1) +
264
+ (isCloudflareMode && options.installDependencies ? 1 : 0) + // cloudflare setup
263
265
  (options.initializeGit ? 1 : 0) +
264
266
  1; // health check
265
267
  let currentStep = 0;
@@ -319,6 +321,7 @@ async function cmdNew(rawArgs) {
319
321
  else {
320
322
  prompts_1.log.info(picocolors_1.default.dim("Skipping dependency installation (--no-install)."));
321
323
  }
324
+ let cloudflareSetup = null;
322
325
  if (backend === "convex") {
323
326
  currentStep++;
324
327
  prompts_1.log.step(`Setting up Convex ${stepLabel()}`);
@@ -348,6 +351,23 @@ async function cmdNew(rawArgs) {
348
351
  }
349
352
  }
350
353
  }
354
+ if (isCloudflareMode && options.installDependencies) {
355
+ currentStep++;
356
+ prompts_1.log.step(`Setting up Cloudflare storage ${stepLabel()}`);
357
+ cloudflareSetup = await (0, steps_1.setupCloudflare)(projectDir);
358
+ if (cloudflareSetup.deployed) {
359
+ if (cloudflareSetup.workerUrl) {
360
+ prompts_1.log.success(`Cloudflare deployed. Worker URL: ${picocolors_1.default.cyan(cloudflareSetup.workerUrl)}`);
361
+ }
362
+ else {
363
+ prompts_1.log.success("Cloudflare deployed.");
364
+ prompts_1.log.info(picocolors_1.default.dim("Add the storageWorkerUrl to EXPO_PUBLIC_STORAGE_WORKER_URL in .env.local."));
365
+ }
366
+ }
367
+ else {
368
+ prompts_1.log.warn("Cloudflare setup did not complete fully.");
369
+ }
370
+ }
351
371
  if (options.initializeGit) {
352
372
  currentStep++;
353
373
  prompts_1.log.step(`Initializing git repository ${stepLabel()}`);
@@ -372,17 +392,19 @@ async function cmdNew(rawArgs) {
372
392
  }
373
393
  prompts_1.log.info("");
374
394
  if (backend === "instantdb") {
375
- const isCloudflareMode = instant?.authMode && (0, scaffold_1.isCloudflareInstantAuthMode)(instant.authMode);
376
395
  const isMagicCodeMode = instant?.authMode && (0, scaffold_1.isMagicCodeInstantAuthMode)(instant.authMode);
377
396
  const instantReady = isMagicCodeMode
378
397
  ? Boolean(instant?.appId)
379
398
  : Boolean(instant?.authMode === "clerk" &&
380
399
  instant.appId &&
381
400
  instant.clerkPublishableKey);
401
+ const cloudflareFullyDeployed = cloudflareSetup?.deployed && cloudflareSetup?.workerUrl;
382
402
  if (instantReady) {
383
- prompts_1.log.success(picocolors_1.default.bold(picocolors_1.default.green(isCloudflareMode
384
- ? "Your app is ready! Deploy Cloudflare storage next."
385
- : "Your app is ready!")));
403
+ prompts_1.log.success(picocolors_1.default.bold(picocolors_1.default.green(isCloudflareMode && cloudflareFullyDeployed
404
+ ? "Your app is ready! Cloudflare storage deployed."
405
+ : isCloudflareMode
406
+ ? "Your app is ready! Deploy Cloudflare storage next."
407
+ : "Your app is ready!")));
386
408
  }
387
409
  else if (isMagicCodeMode) {
388
410
  prompts_1.log.success(picocolors_1.default.bold(picocolors_1.default.green(isCloudflareMode
@@ -402,15 +424,36 @@ async function cmdNew(rawArgs) {
402
424
  prompts_1.log.info(` ${picocolors_1.default.cyan("bun install")} ${picocolors_1.default.dim("# or npm install")}`);
403
425
  prompts_1.log.info(` ${picocolors_1.default.cyan("bunx expo start")}`);
404
426
  }
405
- if (isCloudflareMode) {
427
+ if (isCloudflareMode && !cloudflareFullyDeployed) {
406
428
  prompts_1.log.info("");
407
- prompts_1.log.info(picocolors_1.default.dim(" Cloudflare setup:"));
408
- prompts_1.log.info(picocolors_1.default.dim(" INSTANT_APP_ID is written to .env.local for Alchemy deploys."));
409
- prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:configure")}`);
410
- prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:login")}`);
411
- prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:deploy")}`);
429
+ if (!cloudflareSetup || !cloudflareSetup.configured) {
430
+ // Nothing ran show all steps
431
+ prompts_1.log.info(picocolors_1.default.dim(" Cloudflare setup (run manually):"));
432
+ prompts_1.log.info(picocolors_1.default.dim(" INSTANT_APP_ID is written to .env.local for Alchemy deploys."));
433
+ prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:configure")}`);
434
+ prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:login")}`);
435
+ prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:deploy")}`);
436
+ }
437
+ else if (!cloudflareSetup.loggedIn) {
438
+ prompts_1.log.info(picocolors_1.default.dim(" Cloudflare setup (continue from login):"));
439
+ prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:login")}`);
440
+ prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:deploy")}`);
441
+ }
442
+ else if (!cloudflareSetup.deployed) {
443
+ prompts_1.log.info(picocolors_1.default.dim(" Cloudflare setup (deploy remaining):"));
444
+ prompts_1.log.info(` ${picocolors_1.default.cyan("bun run cloudflare:deploy")}`);
445
+ }
446
+ else {
447
+ // deployed but worker URL not extracted
448
+ prompts_1.log.info(picocolors_1.default.dim(" Cloudflare deployed but worker URL not detected."));
449
+ }
412
450
  prompts_1.log.info(picocolors_1.default.dim(" Add the printed storageWorkerUrl to EXPO_PUBLIC_STORAGE_WORKER_URL in .env.local."));
413
451
  }
452
+ else if (isCloudflareMode && cloudflareFullyDeployed) {
453
+ prompts_1.log.info("");
454
+ prompts_1.log.info(` ${picocolors_1.default.green("✓")} Cloudflare Worker URL: ${picocolors_1.default.cyan(cloudflareSetup.workerUrl)}`);
455
+ prompts_1.log.info(picocolors_1.default.dim(" EXPO_PUBLIC_STORAGE_WORKER_URL set in .env.local."));
456
+ }
414
457
  if (isMagicCodeMode) {
415
458
  if (!instantReady) {
416
459
  prompts_1.log.info("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-100x-mobile",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Scaffold a full-stack mobile app with Expo + Convex + Clerk in seconds",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -13,7 +13,7 @@
13
13
  "typecheck": "tsc --noEmit",
14
14
  "test": "vitest run",
15
15
  "check": "npm run typecheck && npm test && npm pack --dry-run",
16
- "prepublishOnly": "npm run check"
16
+ "prepublishOnly": "npm run build && npm run check"
17
17
  },
18
18
  "files": [
19
19
  "dist"