@techstream/quark-create-app 1.6.0 → 1.8.0

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/README.md CHANGED
@@ -42,6 +42,67 @@ Aliases:
42
42
  - `create-quark-app`
43
43
  - `quark-update`
44
44
 
45
+ ## Usage with Flags
46
+
47
+ The CLI supports non-interactive mode with custom options for automation and CI/CD workflows.
48
+
49
+ ### Non-Interactive Mode
50
+
51
+ Skip all interactive prompts and use defaults:
52
+
53
+ ```bash
54
+ # Create project without prompts
55
+ npx @techstream/quark-create-app my-app --no-prompts
56
+ ```
57
+
58
+ ### Custom Features
59
+
60
+ Specify which optional packages to include (default: `ui,jobs`):
61
+
62
+ ```bash
63
+ # Only include UI package
64
+ npx @techstream/quark-create-app my-app --no-prompts --features ui
65
+
66
+ # Include both UI and Jobs
67
+ npx @techstream/quark-create-app my-app --no-prompts --features ui,jobs
68
+
69
+ # Minimal setup (no optional packages)
70
+ npx @techstream/quark-create-app my-app --no-prompts --features ""
71
+ ```
72
+
73
+ ### Skip Installation Steps
74
+
75
+ Create the project structure without running package installation:
76
+
77
+ ```bash
78
+ # Create project but skip pnpm install
79
+ npx @techstream/quark-create-app my-app --no-prompts --skip-install
80
+
81
+ # Useful for CI/CD where you'll install dependencies separately
82
+ ```
83
+
84
+ ### Docker Cleanup
85
+
86
+ Control whether to remove Docker volumes from previous cleanup:
87
+
88
+ ```bash
89
+ # Keep Docker working directories (useful in CI/CD)
90
+ npx @techstream/quark-create-app my-app --no-prompts --skip-docker
91
+ ```
92
+
93
+ ### Complete Example: Full Automation
94
+
95
+ ```bash
96
+ # Create, install, and setup everything automatically
97
+ npx @techstream/quark-create-app my-app \
98
+ --no-prompts \
99
+ --features ui,jobs \
100
+ && cd my-app \
101
+ && docker compose up -d \
102
+ && pnpm db:migrate \
103
+ && pnpm dev
104
+ ```
105
+
45
106
  ## Common Tasks
46
107
 
47
108
  - **Update Quark packages**: `quark-update` or `pnpm update @techstream/quark-*`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techstream/quark-create-app",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "quark-create-app": "src/index.js",
@@ -33,7 +33,10 @@
33
33
  "test": "node test-cli.js",
34
34
  "test:build": "node test-build.js",
35
35
  "test:e2e": "node test-e2e.js",
36
+ "test:e2e:full": "node test-e2e-full.js",
36
37
  "test:integration": "node test-integration.js",
37
- "test:all": "node test-all.js"
38
+ "test:flags": "node --test test-flags.js",
39
+ "test:all": "node test-all.js",
40
+ "check:perf": "node scripts/check-e2e-perf.js"
38
41
  }
39
42
  }
package/src/index.js CHANGED
@@ -254,7 +254,15 @@ function validateProjectName(name) {
254
254
 
255
255
  program
256
256
  .argument("<project-name>", "Name of the project to create")
257
- .action(async (projectName) => {
257
+ .option(
258
+ "--no-prompts",
259
+ "Skip interactive prompts and use default/provided values",
260
+ )
261
+ .option(
262
+ "--features <features>",
263
+ "Comma-separated list of optional features to include (ui,jobs)",
264
+ )
265
+ .action(async (projectName, options) => {
258
266
  console.log(
259
267
  chalk.blue.bold(
260
268
  `\n\uD83D\uDE80 Creating your new Quark project: ${projectName}\n`,
@@ -314,16 +322,25 @@ program
314
322
 
315
323
  // Check if directory already exists
316
324
  if (await fs.pathExists(targetDir)) {
317
- const { overwrite } = await prompts({
318
- type: "confirm",
319
- name: "overwrite",
320
- message: `Directory "${projectName}" already exists. Remove it and recreate?`,
321
- initial: false,
322
- });
325
+ if (!options.prompts) {
326
+ // In non-interactive mode, automatically remove existing directory
327
+ console.log(
328
+ chalk.yellow(
329
+ ` Directory "${projectName}" already exists. Removing... (non-interactive mode)`,
330
+ ),
331
+ );
332
+ } else {
333
+ const { overwrite } = await prompts({
334
+ type: "confirm",
335
+ name: "overwrite",
336
+ message: `Directory "${projectName}" already exists. Remove it and recreate?`,
337
+ initial: false,
338
+ });
323
339
 
324
- if (!overwrite) {
325
- console.log(chalk.yellow("Aborted."));
326
- process.exit(1);
340
+ if (!overwrite) {
341
+ console.log(chalk.yellow("Aborted."));
342
+ process.exit(1);
343
+ }
327
344
  }
328
345
 
329
346
  // Stop any running Docker containers for this project
@@ -379,35 +396,72 @@ program
379
396
  }
380
397
 
381
398
  // Step 5: Ask which optional features to eject
382
- console.log(chalk.cyan("\n 🎯 Configuring optional features...\n"));
383
- const response = await prompts([
384
- {
385
- type: "multiselect",
386
- name: "features",
387
- message: "Which optional packages would you like to include?",
388
- instructions: false,
389
- choices: [
390
- {
391
- title: "UI Components (packages/ui)",
392
- value: "ui",
393
- selected: true,
394
- },
395
- {
396
- title: "Job Definitions (packages/jobs)",
397
- value: "jobs",
398
- selected: true,
399
- },
400
- ],
401
- },
402
- ]);
399
+ let features;
400
+ if (!options.prompts && options.features) {
401
+ // Parse features from CLI flag
402
+ console.log(chalk.cyan("\n 🎯 Configuring optional features..."));
403
+ const validFeatures = ["ui", "jobs"];
404
+ features = options.features
405
+ .split(",")
406
+ .map((f) => f.trim())
407
+ .filter((f) => f.length > 0);
408
+
409
+ // Validate features
410
+ const invalidFeatures = features.filter(
411
+ (f) => !validFeatures.includes(f),
412
+ );
413
+ if (invalidFeatures.length > 0) {
414
+ throw new Error(
415
+ `Invalid features: ${invalidFeatures.join(", ")}. Valid options are: ${validFeatures.join(", ")}`,
416
+ );
417
+ }
403
418
 
404
- const { features } = response;
419
+ console.log(
420
+ chalk.green(
421
+ ` Selected features: ${features.join(", ") || "none"} (non-interactive mode)`,
422
+ ),
423
+ );
424
+ } else if (!options.prompts) {
425
+ // Use defaults when --no-prompts is set without --features
426
+ console.log(chalk.cyan("\n 🎯 Configuring optional features..."));
427
+ features = ["ui", "jobs"]; // Default to both
428
+ console.log(
429
+ chalk.green(
430
+ ` Using default features: ${features.join(", ")} (non-interactive mode)`,
431
+ ),
432
+ );
433
+ } else {
434
+ // Interactive prompt
435
+ console.log(chalk.cyan("\n 🎯 Configuring optional features...\n"));
436
+ const response = await prompts([
437
+ {
438
+ type: "multiselect",
439
+ name: "features",
440
+ message: "Which optional packages would you like to include?",
441
+ instructions: false,
442
+ choices: [
443
+ {
444
+ title: "UI Components (packages/ui)",
445
+ value: "ui",
446
+ selected: true,
447
+ },
448
+ {
449
+ title: "Job Definitions (packages/jobs)",
450
+ value: "jobs",
451
+ selected: true,
452
+ },
453
+ ],
454
+ },
455
+ ]);
405
456
 
406
- // Handle prompt cancellation (Ctrl+C)
407
- if (!features) {
408
- console.log(chalk.yellow("\n\u26A0\uFE0F Setup cancelled."));
409
- await fs.remove(targetDir);
410
- process.exit(0);
457
+ features = response.features;
458
+
459
+ // Handle prompt cancellation (Ctrl+C)
460
+ if (!features) {
461
+ console.log(chalk.yellow("\n\u26A0\uFE0F Setup cancelled."));
462
+ await fs.remove(targetDir);
463
+ process.exit(0);
464
+ }
411
465
  }
412
466
 
413
467
  // Step 6: Copy selected optional packages
@@ -484,11 +538,11 @@ program
484
538
  # Generate strong passwords with: openssl rand -base64 32
485
539
  POSTGRES_HOST=localhost
486
540
  POSTGRES_PORT=5432
487
- POSTGRES_USER=quark_user
541
+ POSTGRES_USER=${scope}_user
488
542
  POSTGRES_PASSWORD=CHANGE_ME_TO_STRONG_PASSWORD
489
543
  POSTGRES_DB=${scope}_dev
490
544
  # Optional: Set DATABASE_URL to override the dynamic construction above
491
- # DATABASE_URL="postgresql://quark_user:CHANGE_ME_TO_STRONG_PASSWORD@localhost:5432/${scope}_dev?schema=public"
545
+ # DATABASE_URL="postgresql://${scope}_user:CHANGE_ME_TO_STRONG_PASSWORD@localhost:5432/${scope}_dev?schema=public"
492
546
 
493
547
  # --- Redis Configuration ---
494
548
  REDIS_HOST=localhost
@@ -501,8 +555,6 @@ REDIS_PORT=6379
501
555
  MAIL_HOST=localhost
502
556
  MAIL_SMTP_PORT=1025
503
557
  MAIL_UI_PORT=8025
504
- # Optional: Set MAIL_SMTP_URL to override the dynamic construction above
505
- # MAIL_SMTP_URL="smtp://localhost:1025"
506
558
 
507
559
  # Production SMTP: Set these instead of MAIL_* when using a real SMTP relay
508
560
  # SMTP_HOST=smtp.example.com
@@ -525,6 +577,10 @@ MAIL_UI_PORT=8025
525
577
  # In production, set this to your real domain:
526
578
  # APP_URL=https://yourdomain.com
527
579
 
580
+ # --- Application Identity ---
581
+ # APP_NAME is used in metadata, emails, and page titles.
582
+ APP_NAME=${projectName}
583
+
528
584
  # --- NextAuth Configuration ---
529
585
  # ⚠️ CRITICAL: Generate a secure secret with: openssl rand -base64 32
530
586
  # This secret is used to encrypt JWT tokens and session data
@@ -617,7 +673,7 @@ STORAGE_PROVIDER=local
617
673
  const envContent = `# --- Database Configuration ---
618
674
  POSTGRES_HOST=localhost
619
675
  POSTGRES_PORT=${postgresPort}
620
- POSTGRES_USER=quark_user
676
+ POSTGRES_USER=${scope}_user
621
677
  POSTGRES_PASSWORD=${dbPassword}
622
678
  POSTGRES_DB=${scope}_dev
623
679
 
@@ -630,6 +686,9 @@ MAIL_HOST=localhost
630
686
  MAIL_SMTP_PORT=${mailSmtpPort}
631
687
  MAIL_UI_PORT=${mailUiPort}
632
688
 
689
+ # --- Application Identity ---
690
+ APP_NAME=${projectName}
691
+
633
692
  # --- NextAuth Configuration ---
634
693
  NEXTAUTH_SECRET=${nextAuthSecret}
635
694
 
@@ -1,5 +1,9 @@
1
1
  /** @type {import('next').NextConfig} */
2
2
  const nextConfig = {
3
+ // Required for Railway deployment — produces a self-contained build
4
+ // at .next/standalone that can run without node_modules.
5
+ output: "standalone",
6
+
3
7
  // Support workspace package resolution (including @techstream/quark-db which uses
4
8
  // the Prisma driver-adapter pattern — pure JS, no native engine binary)
5
9
  transpilePackages: [
@@ -16,18 +16,18 @@
16
16
  "@techstream/quark-db": "workspace:*",
17
17
  "@techstream/quark-jobs": "workspace:*",
18
18
  "@techstream/quark-ui": "workspace:*",
19
- "@prisma/client": "^7.3.0",
19
+ "@prisma/client": "^7.4.0",
20
20
  "next": "16.1.6",
21
21
  "next-auth": "5.0.0-beta.30",
22
22
  "pg": "^8.18.0",
23
- "react": "19.2.0",
24
- "react-dom": "19.2.0",
23
+ "react": "19.2.4",
24
+ "react-dom": "19.2.4",
25
25
  "zod": "^4.3.6"
26
26
  },
27
27
  "devDependencies": {
28
- "@tailwindcss/postcss": "^4.1.18",
29
- "@types/node": "^20.19.33",
30
- "tailwindcss": "^4.1.18",
28
+ "@tailwindcss/postcss": "^4.2.0",
29
+ "@types/node": "^25.2.3",
30
+ "tailwindcss": "^4.2.0",
31
31
  "@techstream/quark-config": "workspace:*"
32
32
  }
33
33
  }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://railway.com/railway.schema.json",
3
+ "build": {
4
+ "builder": "RAILPACK",
5
+ "buildCommand": "pnpm install --frozen-lockfile && pnpm db:generate && pnpm build",
6
+ "watchPatterns": ["apps/web/**", "packages/**"]
7
+ },
8
+ "deploy": {
9
+ "releaseCommand": "pnpm --filter @techstream/quark-db exec prisma migrate deploy",
10
+ "startCommand": "node apps/web/.next/standalone/server.js",
11
+ "healthcheckPath": "/api/health",
12
+ "healthcheckTimeout": 30,
13
+ "restartPolicyType": "ON_FAILURE",
14
+ "restartPolicyMaxRetries": 5
15
+ }
16
+ }