create-100x-mobile 0.5.1 → 0.5.3
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 +0 -0
- package/dist/commands/new/steps.js +74 -3
- package/dist/commands/new.js +53 -10
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -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");
|
|
@@ -328,6 +329,7 @@ async function promptClerkSetup(options) {
|
|
|
328
329
|
async function promptInstantSetup(options, authMode) {
|
|
329
330
|
let appId = options.instantAppId?.trim() ?? "";
|
|
330
331
|
if ((0, scaffold_1.isMagicCodeInstantAuthMode)(authMode)) {
|
|
332
|
+
let adminToken = "";
|
|
331
333
|
if (options.interactive) {
|
|
332
334
|
prompts_1.log.info("");
|
|
333
335
|
prompts_1.log.info(picocolors_1.default.bold((0, scaffold_1.isCloudflareInstantAuthMode)(authMode)
|
|
@@ -335,7 +337,7 @@ async function promptInstantSetup(options, authMode) {
|
|
|
335
337
|
: "InstantDB Magic Code Setup"));
|
|
336
338
|
prompts_1.log.info(picocolors_1.default.dim("Create an app id with: npx instant-cli init-without-files --title my-app"));
|
|
337
339
|
if ((0, scaffold_1.isCloudflareInstantAuthMode)(authMode)) {
|
|
338
|
-
prompts_1.log.info(picocolors_1.default.dim("Cloudflare Worker, R2, and D1 files will be generated
|
|
340
|
+
prompts_1.log.info(picocolors_1.default.dim("Cloudflare Worker, R2, and D1 files will be generated and deployed automatically."));
|
|
339
341
|
}
|
|
340
342
|
if (!appId) {
|
|
341
343
|
prompts_1.log.info(picocolors_1.default.dim("Press Enter to skip and configure later."));
|
|
@@ -348,8 +350,21 @@ async function promptInstantSetup(options, authMode) {
|
|
|
348
350
|
appId = appIdInput.trim();
|
|
349
351
|
}
|
|
350
352
|
}
|
|
353
|
+
if ((0, scaffold_1.isCloudflareInstantAuthMode)(authMode) && appId) {
|
|
354
|
+
prompts_1.log.info("");
|
|
355
|
+
prompts_1.log.info(picocolors_1.default.dim("The admin token lets the Cloudflare Worker sync upload metadata to InstantDB."));
|
|
356
|
+
prompts_1.log.info(picocolors_1.default.dim("Get it from: https://instantdb.com/dash → Your App → Admin Tokens"));
|
|
357
|
+
const adminTokenInput = await (0, prompts_1.text)({
|
|
358
|
+
message: "InstantDB Admin Token",
|
|
359
|
+
placeholder: "your-instant-admin-token",
|
|
360
|
+
defaultValue: "",
|
|
361
|
+
});
|
|
362
|
+
if (!(0, prompts_1.isCancel)(adminTokenInput) && adminTokenInput) {
|
|
363
|
+
adminToken = adminTokenInput.trim();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
351
366
|
}
|
|
352
|
-
return { authMode, appId };
|
|
367
|
+
return { authMode, appId, adminToken };
|
|
353
368
|
}
|
|
354
369
|
let clerkPublishableKey = options.clerkPublishableKey?.trim() ?? "";
|
|
355
370
|
let clerkClientName = options.instantClerkClientName?.trim() || "clerk";
|
|
@@ -444,7 +459,8 @@ async function writeInstantEnvironment(projectDir, instant) {
|
|
|
444
459
|
}
|
|
445
460
|
if ((0, scaffold_1.isCloudflareInstantAuthMode)(instant.authMode)) {
|
|
446
461
|
envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "INSTANT_APP_ID", instant.appId);
|
|
447
|
-
|
|
462
|
+
const adminToken = "adminToken" in instant ? instant.adminToken : "";
|
|
463
|
+
envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "INSTANT_ADMIN_TOKEN", adminToken);
|
|
448
464
|
envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "ALLOWED_ORIGINS", "http://localhost:3000,http://localhost:8080,http://localhost:8081,http://localhost:19006");
|
|
449
465
|
envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "MAX_UPLOAD_BYTES", "26214400");
|
|
450
466
|
envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "USER_STORAGE_LIMIT_BYTES", "524288000");
|
|
@@ -524,6 +540,61 @@ async function setConvexClerkEnv(projectDir, clerkDomain) {
|
|
|
524
540
|
return false;
|
|
525
541
|
}
|
|
526
542
|
}
|
|
543
|
+
async function setupCloudflare(projectDir) {
|
|
544
|
+
const result = {
|
|
545
|
+
configured: false,
|
|
546
|
+
loggedIn: false,
|
|
547
|
+
deployed: false,
|
|
548
|
+
workerUrl: "",
|
|
549
|
+
};
|
|
550
|
+
// Step 1: cloudflare:configure (alchemy configure)
|
|
551
|
+
try {
|
|
552
|
+
await (0, run_1.run)("bun", ["run", "cloudflare:configure"], { cwd: projectDir });
|
|
553
|
+
result.configured = true;
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
prompts_1.log.warn(`Cloudflare configure failed: ${toErrorMessage(error)}`);
|
|
557
|
+
prompts_1.log.info(picocolors_1.default.dim("Run manually: bun run cloudflare:configure"));
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
// Step 2: cloudflare:login (alchemy login)
|
|
561
|
+
try {
|
|
562
|
+
await (0, run_1.run)("bun", ["run", "cloudflare:login"], { cwd: projectDir });
|
|
563
|
+
result.loggedIn = true;
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
prompts_1.log.warn(`Cloudflare login failed: ${toErrorMessage(error)}`);
|
|
567
|
+
prompts_1.log.info(picocolors_1.default.dim("Run manually: bun run cloudflare:login"));
|
|
568
|
+
return result;
|
|
569
|
+
}
|
|
570
|
+
// Step 3: cloudflare:deploy (alchemy deploy) — capture output for worker URL
|
|
571
|
+
try {
|
|
572
|
+
const deployOutput = await (0, run_1.runCapture)("bun", ["run", "cloudflare:deploy"], { cwd: projectDir });
|
|
573
|
+
result.deployed = true;
|
|
574
|
+
// Try to extract the storageWorkerUrl from the deploy output
|
|
575
|
+
const urlMatch = deployOutput.match(/storageWorkerUrl[:\s]*["']?(https?:\/\/[^\s"']+)["']?/i);
|
|
576
|
+
if (urlMatch?.[1]) {
|
|
577
|
+
result.workerUrl = urlMatch[1];
|
|
578
|
+
// Write the worker URL to .env.local
|
|
579
|
+
const envLocalPath = (0, node_path_1.join)(projectDir, ".env.local");
|
|
580
|
+
let envContents = "";
|
|
581
|
+
try {
|
|
582
|
+
envContents = await (0, fs_1.readTextFile)(envLocalPath);
|
|
583
|
+
}
|
|
584
|
+
catch {
|
|
585
|
+
// .env.local may not exist yet
|
|
586
|
+
}
|
|
587
|
+
envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "EXPO_PUBLIC_STORAGE_WORKER_URL", result.workerUrl);
|
|
588
|
+
await (0, fs_1.writeTextFile)(envLocalPath, envContents);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
catch (error) {
|
|
592
|
+
prompts_1.log.warn(`Cloudflare deploy failed: ${toErrorMessage(error)}`);
|
|
593
|
+
prompts_1.log.info(picocolors_1.default.dim("Run manually: bun run cloudflare:deploy"));
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
527
598
|
async function initializeGit(projectDir) {
|
|
528
599
|
try {
|
|
529
600
|
await (0, run_1.run)("git", ["init"], { cwd: projectDir });
|
package/dist/commands/new.js
CHANGED
|
@@ -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!
|
|
385
|
-
:
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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.
|
|
3
|
+
"version": "0.5.3",
|
|
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"
|