@cellajs/create-cella 0.2.1 → 0.2.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.
@@ -9,7 +9,7 @@
9
9
  * NOTE: This file is NOT compiled or imported — it is copied and interpolated
10
10
  * as plain text. The import path below is relative to `shared/config/` (its destination).
11
11
  */
12
- import type { BaseAuthStrategies, BaseOAuthProviders, ConfigMode, RequiredConfig, S3Config } from '../src/builder/types';
12
+ import type { ConfigMode, RequiredConfig, S3ConfigInput } from '../src/config-builder/types';
13
13
 
14
14
  // Re-export for external consumers
15
15
  export { roles, hierarchy } from './hierarchy-config';
@@ -20,22 +20,19 @@ export const config = {
20
20
  * ENTITY DATA MODEL
21
21
  ******************************************************************************/
22
22
 
23
- /** All entity types in the app - must match hierarchy.allTypes. Explicit tuple for Drizzle compatibility. */
23
+ /** All entity types in the app - must match hierarchy.allTypes. */
24
24
  entityTypes: ['user', 'organization', 'attachment', 'page'] as const,
25
25
 
26
- /** Context entities with memberships - must match hierarchy.contextTypes. Explicit tuple for Drizzle compatibility. */
26
+ /** Context entities with memberships - must match hierarchy.contextTypes. */
27
27
  contextEntityTypes: ['organization'] as const,
28
-
29
- /** Product/content entities - must match hierarchy.productTypes. Explicit tuple for Drizzle compatibility. */
28
+
29
+ /** Product/content entities - must match hierarchy.productTypes. */
30
30
  productEntityTypes: ['attachment', 'page'] as const,
31
31
 
32
32
  /**
33
- * Parentless product entities (no organization_id) - must match hierarchy.parentlessProductTypes.
34
- * Explicit tuple required for Drizzle compatibility. Compile-time validated in shared/index.ts.
33
+ * Product entity types tracked for seen/unseen counts.
34
+ * Unseen counts are grouped by the parent context entity of each tracked type.
35
35
  */
36
- parentlessProductEntityTypes: ['page'] as const,
37
-
38
- /** Product entity types tracked for seen/unseen counts. Must be org-scoped (not parentless). */
39
36
  seenTrackedEntityTypes: ['attachment'] as const,
40
37
 
41
38
  /** Maps entity types to their ID column names - must match entityTypes */
@@ -52,12 +49,18 @@ export const config = {
52
49
  /** Resource types that are not entities but have activities logged */
53
50
  resourceTypes: ['request', 'membership', 'inactive_membership', 'tenant'] as const,
54
51
 
52
+ /**
53
+ * Entity embeddings: declares which entities are embedded as ID arrays inside
54
+ * other entities. Forks extend when adding new embedding relationships.
55
+ */
56
+ entityEmbeddings: [] as readonly { readonly embeddedEntity: string; readonly hostEntity: string; readonly hostColumn: string }[],
57
+
55
58
  /**
56
59
  * User menu structure of context entities with optional nested subentities.
57
60
  * If subentityType is set, the table must include `${entity}Id` foreign key.
58
61
  */
59
62
  menuStructure: [
60
- { entityType: 'organization',subentityType: null} as const,
63
+ { entityType: 'organization', subentityType: null } as const,
61
64
  ],
62
65
 
63
66
  /** Default restrictions for tenants (entity quotas and rate limits) */
@@ -72,17 +75,10 @@ export const config = {
72
75
  },
73
76
  } as const,
74
77
 
75
- /** Public tenant for platform-wide content (pages, docs, etc.) */
76
- publicTenant: { id: 'public', name: 'Public' },
77
-
78
78
  /******************************************************************************
79
79
  * SYSTEM ROLES
80
80
  ******************************************************************************/
81
-
82
- /**
83
- * System-wide roles stored in DB.
84
- * Must include 'admin' for system administration access.
85
- */
81
+
86
82
  systemRoles: ['admin'] as const,
87
83
 
88
84
  /******************************************************************************
@@ -99,7 +95,7 @@ export const config = {
99
95
  description: '__project_name__ — powered by Cella.',
100
96
  /** SEO keywords for search engines */
101
97
  keywords:
102
- 'starter kit, fullstack, monorepo, typescript, hono, honojs, drizzle, shadcn, react, postgres, pwa, offline, instant§ updates, realtime data, sync engine',
98
+ 'starter kit, fullstack, monorepo, typescript, hono, honojs, drizzle, baseui, react, postgres, pwa, offline, instant updates, realtime data, sync engine',
103
99
 
104
100
  /******************************************************************************
105
101
  * URLS & ENDPOINTS
@@ -111,9 +107,13 @@ export const config = {
111
107
  backendUrl: 'https://api.__project_slug__.example.com',
112
108
  /** OAuth callback base URL */
113
109
  backendAuthUrl: 'https://api.__project_slug__.example.com/auth',
110
+ /** Yjs realtime relay URL */
111
+ yjsUrl: 'wss://yjs.__project_slug__.example.com',
112
+ /** AI service base URL */
113
+ aiUrl: 'https://ai.__project_slug__.example.com',
114
114
 
115
115
  /** About page URL */
116
- aboutUrl: 'https://__project_slug__.example.com/about',
116
+ aboutUrl: '/about',
117
117
  /** Status page URL for uptime monitoring */
118
118
  statusUrl: 'https://status.__project_slug__.example.com',
119
119
  /** Canonical production URL */
@@ -128,23 +128,19 @@ export const config = {
128
128
  * EMAIL
129
129
  ******************************************************************************/
130
130
 
131
- /** Email address for user support inquiries */
132
- supportEmail: 'support@__project_slug__.example.com',
133
-
134
131
  /** From address for system notifications */
135
132
  senderEmail: 'notifications@__project_slug__.example.com',
136
-
137
- /** Receive security warnings */
133
+ /** Email address for user support inquiries */
134
+ supportEmail: 'support@__project_slug__.example.com',
135
+ /** Receive security warnings */
138
136
  securityEmail: 'security@__project_slug__.example.com',
139
137
 
140
138
  /******************************************************************************
141
139
  * MODE & FLAGS
142
140
  ******************************************************************************/
143
-
141
+
144
142
  /** Runtime mode - overridden per environment file */
145
143
  mode: 'development' as ConfigMode,
146
- /** Enable debug logging and dev tools */
147
- debug: false,
148
144
  /** Enable maintenance mode (blocks all requests) */
149
145
  maintenance: false,
150
146
  /** Cookie version - increment when changing cookie structure to invalidate old cookies */
@@ -160,13 +156,19 @@ export const config = {
160
156
  */
161
157
  has: {
162
158
  /** Progressive Web App support for preloading static assets and offline support */
163
- pwa: true,
159
+ pwa: true as boolean,
164
160
  /** Allow users to sign up. If false, the app is by invitation only */
165
- registrationEnabled: true,
161
+ selfRegistration: false as boolean,
166
162
  /** Suggest a waitlist for unknown emails when sign up is disabled */
167
- waitlist: true,
163
+ waitlist: false as boolean,
168
164
  /** S3 fully configured - if false, files will be stored in local browser (IndexedDB) */
169
- uploadEnabled: false,
165
+ uploadEnabled: false as boolean,
166
+ /** Customer support chat widget (Gleap). Unrelated to the AI module. */
167
+ chatSupport: false as boolean,
168
+ /** Yjs realtime collaboration */
169
+ yjs: false as boolean,
170
+ /** AI capability layer: tool registry + model runner + MCP endpoint. */
171
+ ai: false as boolean,
170
172
  },
171
173
 
172
174
  /******************************************************************************
@@ -177,13 +179,13 @@ export const config = {
177
179
  * Enabled authentication strategies.
178
180
  * TOTP can only be used as MFA fallback with passkey as primary.
179
181
  */
180
- enabledAuthStrategies: ['password', 'passkey', 'totp'] satisfies BaseAuthStrategies[],
182
+ enabledAuthStrategies: ['passkey', 'oauth', 'totp', 'magic'] as const,
181
183
 
182
184
  /** Enabled OAuth providers - currently supports: github, google, microsoft */
183
- enabledOAuthProviders: [] satisfies BaseOAuthProviders[],
185
+ enabledOAuthProviders: ['github'] as const,
184
186
 
185
187
  /** Token types used for verification flows */
186
- tokenTypes: ['email-verification', 'oauth-verification', 'password-reset', 'invitation', 'confirm-mfa'] as const,
188
+ tokenTypes: ['email-verification', 'oauth-verification', 'invitation', 'confirm-mfa', 'magic'] as const,
187
189
 
188
190
  /** TOTP configuration for MFA */
189
191
  totpConfig: {
@@ -199,12 +201,8 @@ export const config = {
199
201
  /** API version prefix for endpoints */
200
202
  apiVersion: 'v1',
201
203
  /** API documentation description shown in Scalar */
202
- apiDescription: `⚠️ ATTENTION: PRERELEASE!
203
- This API is organized into modules based on logical domains (e.g. \`auth\`, \`organizations\`, \`memberships\`).
204
- Each module includes a set of endpoints that expose functionality related to a specific resource or cross resource logic.
205
-
206
- The documentation is generated from source code using \`zod\` schemas, converted into OpenAPI via \`zod-openapi\` and served through the \`hono\` framework.`,
207
-
204
+ apiDescription: `⚠️ ATTENTION: PRERELEASE!
205
+ This API is organized into modules based on logical domains.`,
208
206
 
209
207
  /******************************************************************************
210
208
  * REQUEST LIMITS
@@ -221,7 +219,7 @@ export const config = {
221
219
  organizations: 40,
222
220
  requests: 40,
223
221
  attachments: 40,
224
- pages: 40,
222
+ pages: 100,
225
223
  pendingMemberships: 20,
226
224
  },
227
225
 
@@ -236,23 +234,11 @@ export const config = {
236
234
  * STORAGE & UPLOADS (S3)
237
235
  ******************************************************************************/
238
236
 
239
- /** S3-compatible storage configuration */
237
+ /** S3-compatible storage configuration. Only region and host are required; the rest is derived from the slug. */
240
238
  s3: {
241
- /** Prefix to namespace files when sharing a bucket across apps or envs */
242
- bucketPrefix: '__project_slug__',
243
- /** Public bucket name for publicly accessible files */
244
- publicBucket: '',
245
- /** Private bucket name for authenticated-only files */
246
- privateBucket: '',
247
- /** S3 region identifier */
248
239
  region: '',
249
- /** S3 host endpoint */
250
240
  host: '',
251
- /** CDN URL for private bucket (signed URLs) */
252
- privateCDNUrl: '',
253
- /** CDN URL for public bucket */
254
- publicCDNUrl: '',
255
- } satisfies S3Config,
241
+ } as S3ConfigInput,
256
242
 
257
243
  /** Upload template IDs for Transloadit processing pipelines */
258
244
  uploadTemplateIds: ['avatar', 'cover', 'attachment'] as const,
@@ -275,30 +261,20 @@ export const config = {
275
261
  * Controls which attachments are cached locally for offline access.
276
262
  */
277
263
  localBlobStorage: {
278
- enabled: true, // Enable local blob caching
279
- maxFileSize: 10 * 1024 * 1024, // 10MB - files larger than this are not cached locally
280
- maxTotalSize: 100 * 1024 * 1024, // 100MB - total cache size, LRU eviction when exceeded
281
- allowedContentTypes: [] as string[], // Empty = all types allowed
282
- excludedContentTypes: ['video/*'] as string[], // Excluded types (takes precedence over allowed)
283
- downloadConcurrency: 2, // Max concurrent background downloads
284
- uploadRetryAttempts: 3, // Max retry attempts for failed uploads
285
- uploadRetryDelays: [60000, 300000, 900000] as const, // Retry delays in ms (1min, 5min, 15min)
264
+ enabled: true,
265
+ maxFileSize: 10 * 1024 * 1024,
266
+ maxTotalSize: 100 * 1024 * 1024,
267
+ allowedContentTypes: [] as string[],
268
+ excludedContentTypes: ['video/*'] as string[],
269
+ downloadConcurrency: 2,
270
+ uploadRetryAttempts: 3,
271
+ uploadRetryDelays: [60000, 300000, 900000] as const,
286
272
  },
287
273
 
288
274
  /******************************************************************************
289
275
  * THIRD-PARTY SERVICES
290
276
  ******************************************************************************/
291
277
 
292
- /** Paddle client token for payments */
293
- paddleToken: '',
294
- /** Paddle price IDs for subscription products */
295
- paddlePriceIds: {
296
- donate: '',
297
- },
298
- /** Sentry DSN for error tracking */
299
- sentryDsn: '',
300
- /** Upload source maps to Sentry on build */
301
- sentSentrySourceMaps: false,
302
278
  /** Gleap token for customer support widget */
303
279
  gleapToken: '',
304
280
  /** Google Maps API key */
@@ -320,9 +296,7 @@ export const config = {
320
296
  sidebarWidthCollapsed: '4rem',
321
297
  sheetPanelWidth: '20rem',
322
298
  },
323
- colors: {
324
- rose: '#e11d48',
325
- },
299
+ colors: {},
326
300
  strokeWidth: 1.5,
327
301
  screenSizes: {
328
302
  xs: '420px',
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import pc from "picocolors";
12
12
  // package.json
13
13
  var package_default = {
14
14
  name: "@cellajs/create-cella",
15
- version: "0.2.1",
15
+ version: "0.2.2",
16
16
  private: false,
17
17
  license: "MIT",
18
18
  description: "Cella is a TypeScript template to create web apps with sync and offline capabilities.",
@@ -54,7 +54,7 @@ var package_default = {
54
54
  lint: "biome check .",
55
55
  "lint:fix": "biome check --write .",
56
56
  test: "vitest run",
57
- "test:release": "pnpm run build && vitest run tests/release-artifact.test.ts",
57
+ "test:release": "pnpm run build && vitest run tests/release-artifact.test.ts tests/release-smoke.test.ts",
58
58
  "test:watch": "vitest"
59
59
  },
60
60
  dependencies: {
@@ -101,7 +101,7 @@ var TO_COPY = {
101
101
  "./frontend/.env.example": "./frontend/.env",
102
102
  "./info/QUICKSTART.md": "README.md"
103
103
  };
104
- var PLACEHOLDER_CONFIG = "./cli/create-cella/configs/default-config.ts.template";
104
+ var PLACEHOLDER_CONFIG = "configs/default-config.ts.template";
105
105
  async function generateEnvFromExample(examplePath, replacements) {
106
106
  const { readFile: readFile2 } = await import("fs/promises");
107
107
  let content;
@@ -115,16 +115,12 @@ async function generateEnvFromExample(examplePath, replacements) {
115
115
  return match;
116
116
  });
117
117
  }
118
- function getRootEnvReplacements(slug, portOffset) {
119
- return {
120
- PROJECT_SLUG: slug,
121
- DB_PORT: String(5432 + portOffset),
122
- DB_TEST_PORT: String(5434 + portOffset)
123
- };
124
- }
125
- function getBackendEnvReplacements(adminEmail, portOffset) {
118
+ function getBackendEnvReplacements(slug, adminEmail, portOffset) {
126
119
  const db = 5432 + portOffset;
127
120
  return {
121
+ PROJECT_SLUG: slug,
122
+ DB_PORT: String(db),
123
+ DB_TEST_PORT: String(5434 + portOffset),
128
124
  DATABASE_URL: `postgres://runtime_role:dev_password@0.0.0.0:${db}/postgres`,
129
125
  DATABASE_ADMIN_URL: `postgres://postgres:postgres@0.0.0.0:${db}/postgres`,
130
126
  DATABASE_CDC_URL: `postgres://admin_role:dev_password@0.0.0.0:${db}/postgres`,
@@ -135,12 +131,11 @@ function getBackendEnvReplacements(adminEmail, portOffset) {
135
131
  function generateEnvConfigs(slug, name, portOffset) {
136
132
  const fe = 3e3 + portOffset;
137
133
  const be = 4e3 + portOffset;
138
- const header = "import type { DeepPartial } from '../src/builder/types';\nimport type _default from './config.default';\n";
134
+ const header = "import type { DeepPartial } from '../src/config-builder/types';\nimport type _default from './config.default';\n";
139
135
  const envs = {
140
136
  development: {
141
137
  props: {
142
138
  slug: `${slug}-development`,
143
- debug: false,
144
139
  domain: "",
145
140
  frontendUrl: `http://localhost:${fe}`,
146
141
  backendUrl: `http://localhost:${be}`,
@@ -150,7 +145,6 @@ function generateEnvConfigs(slug, name, portOffset) {
150
145
  staging: {
151
146
  props: {
152
147
  slug: `${slug}-staging`,
153
- debug: false,
154
148
  domain: `${slug}.example.com`,
155
149
  frontendUrl: `https://staging.${slug}.example.com`,
156
150
  backendUrl: `https://api-staging.${slug}.example.com`,
@@ -167,7 +161,6 @@ function generateEnvConfigs(slug, name, portOffset) {
167
161
  test: {
168
162
  imports: "import development from './config.development';\n",
169
163
  props: {
170
- debug: false,
171
164
  domain: "",
172
165
  frontendUrl: "=development.frontendUrl",
173
166
  backendUrl: "=development.backendUrl",
@@ -223,7 +216,7 @@ async function gitAddAll(repoPath) {
223
216
  return runGitCommand(["add", "."], repoPath);
224
217
  }
225
218
  async function gitCommit(repoPath, message) {
226
- return runGitCommand(["commit", "-m", message], repoPath);
219
+ return runGitCommand(["commit", "--quiet", "--no-verify", "-m", message], repoPath);
227
220
  }
228
221
  async function gitBranch(repoPath, branchName) {
229
222
  return runGitCommand(["branch", branchName], repoPath);
@@ -286,10 +279,20 @@ function validateProjectName(name) {
286
279
  }
287
280
 
288
281
  // src/modules/cli/commands.ts
282
+ function parsePortOffset(value) {
283
+ const parsed = Number(value);
284
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 490) {
285
+ throw new InvalidArgumentError("Port offset must be an integer between 0 and 490");
286
+ }
287
+ if (parsed % 10 !== 0) {
288
+ throw new InvalidArgumentError("Port offset must be a multiple of 10");
289
+ }
290
+ return parsed;
291
+ }
289
292
  var directory = null;
290
293
  var newBranchName = null;
291
294
  var packageManager = "pnpm";
292
- var command = new Command(NAME).version(VERSION, "-v, --version", `output the current version of ${NAME}`).argument("[directory]", "the directory name for the new project").usage("[directory] [options]").helpOption("-h, --help", "display this help message").option("--template <path>", "use a custom template (local path or github:user/repo)").action((name) => {
295
+ var command = new Command(NAME).version(VERSION, "-v, --version", `output the current version of ${NAME}`).argument("[directory]", "the directory name for the new project").usage("[directory] [options]").helpOption("-h, --help", "display this help message").option("--template <path>", "use a custom template (local path or github:user/repo)").option("--port-offset <number>", "set the port offset (0-490 in steps of 10)", parsePortOffset).option("--admin-email <email>", "set the admin email for the initial seed user").action((name) => {
293
296
  const trimmedName = typeof name === "string" ? name.trim() : name;
294
297
  if (trimmedName) {
295
298
  const validation = validateProjectName(basename(resolve(trimmedName)));
@@ -353,8 +356,21 @@ function showSuccess(projectName, _targetFolder, relativePath, needsCd, packageM
353
356
  // src/utils/clean-template.ts
354
357
  import fs from "fs/promises";
355
358
  import path from "path";
359
+ import { fileURLToPath } from "url";
356
360
  import pc3 from "picocolors";
357
361
  var warningMark = pc3.yellow("\u26A0");
362
+ async function resolvePlaceholderConfigPath() {
363
+ const here = path.dirname(fileURLToPath(import.meta.url));
364
+ const candidates = [path.resolve(here, "../..", PLACEHOLDER_CONFIG), path.resolve(here, "..", PLACEHOLDER_CONFIG)];
365
+ for (const candidate of candidates) {
366
+ try {
367
+ await fs.access(candidate);
368
+ return candidate;
369
+ } catch {
370
+ }
371
+ }
372
+ return candidates[0];
373
+ }
358
374
  async function cleanTemplate({
359
375
  targetFolder,
360
376
  projectName,
@@ -374,14 +390,7 @@ async function cleanTemplate({
374
390
  await copyFile(srcAbsolutePath, destAbsolutePath);
375
391
  }
376
392
  await applyPlaceholderConfig(targetFolder, projectName);
377
- const rootReplacements = getRootEnvReplacements(projectName, portOffset);
378
- const rootEnv = await generateEnvFromExample(path.resolve(targetFolder, ".env.example"), rootReplacements);
379
- await fs.writeFile(
380
- path.resolve(targetFolder, ".env"),
381
- rootEnv ?? Object.entries(rootReplacements).map(([k, v]) => `${k}=${v}`).join("\n"),
382
- "utf8"
383
- );
384
- const backendReplacements = getBackendEnvReplacements(adminEmail, portOffset);
393
+ const backendReplacements = getBackendEnvReplacements(projectName, adminEmail, portOffset);
385
394
  const backendEnv = await generateEnvFromExample(
386
395
  path.resolve(targetFolder, "backend/.env.example"),
387
396
  backendReplacements
@@ -447,7 +456,7 @@ ${warningMark} Source file "${src}" does not exist > Skip copy`);
447
456
  }
448
457
  async function applyPlaceholderConfig(targetFolder, projectName) {
449
458
  const displayName = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
450
- const src = path.resolve(targetFolder, PLACEHOLDER_CONFIG);
459
+ const src = await resolvePlaceholderConfigPath();
451
460
  const dest = path.resolve(targetFolder, "./shared/config/config.default.ts");
452
461
  try {
453
462
  let content = await fs.readFile(src, "utf8");
@@ -554,6 +563,9 @@ async function generate(packageManager2) {
554
563
  function isLocalPath(path2) {
555
564
  return path2.startsWith("/") || path2.startsWith("./") || path2.startsWith("../");
556
565
  }
566
+ function shouldSkipStep(name) {
567
+ return process.env[`CREATE_CELLA_SKIP_${name.toUpperCase()}`] === "true";
568
+ }
557
569
  async function create({
558
570
  projectName,
559
571
  targetFolder,
@@ -597,12 +609,16 @@ async function create({
597
609
  progress.step("cleaning template");
598
610
  const displayName = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
599
611
  await cleanTemplate({ targetFolder, projectName, displayName, portOffset, adminEmail });
600
- progress.step("installing dependencies");
601
- await install(packageManager2);
602
- progress.step("generating migrations");
603
- await generate(packageManager2);
612
+ if (!shouldSkipStep("install")) {
613
+ progress.step("installing dependencies");
614
+ await install(packageManager2);
615
+ }
616
+ if (!shouldSkipStep("generate")) {
617
+ progress.step("generating migrations");
618
+ await generate(packageManager2);
619
+ }
604
620
  const gitFolderPath = join(targetFolder, ".git");
605
- if (!existsSync(gitFolderPath)) {
621
+ if (!shouldSkipStep("git") && !existsSync(gitFolderPath)) {
606
622
  progress.step("initializing git");
607
623
  await gitInit(targetFolder);
608
624
  await gitAddAll(targetFolder);
@@ -613,8 +629,10 @@ async function create({
613
629
  await gitCheckout(targetFolder, newBranchName2);
614
630
  }
615
631
  }
616
- progress.step("adding upstream remote");
617
- await addRemote({ targetFolder, silent: true });
632
+ if (!shouldSkipStep("remote")) {
633
+ progress.step("adding upstream remote");
634
+ await addRemote({ targetFolder, silent: true });
635
+ }
618
636
  progress.done(`created ${projectName}`);
619
637
  } catch (error) {
620
638
  progress.fail(error instanceof Error ? error.message : String(error));
@@ -730,8 +748,8 @@ async function main() {
730
748
  process.exit(1);
731
749
  }
732
750
  }
733
- const portOffset = await promptPortOffset(targetFolder, promptTheme, promptContext);
734
- const adminEmail = await input(
751
+ const portOffset = cli.options.portOffset ?? await promptPortOffset(targetFolder, promptTheme, promptContext);
752
+ const adminEmail = cli.options.adminEmail ?? await input(
735
753
  {
736
754
  message: "Admin email for initial seed user",
737
755
  default: `admin@${projectName}.com`,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-cella-cli.ts","../src/constants.ts","../package.json","../src/create.ts","../src/utils/git/command.ts","../src/add-remote.ts","../src/modules/cli/commands.ts","../src/utils/validate-project-name.ts","../src/modules/cli/display.ts","../src/utils/clean-template.ts","../src/utils/progress.ts","../src/utils/run-package-manager-command.ts","../src/utils/detect-used-ports.ts","../src/utils/extract-package-json-version-from-uri.ts","../src/utils/is-empty-directory.ts"],"sourcesContent":["#!/usr/bin/env tsx\n\nimport { existsSync } from 'node:fs';\nimport { basename, resolve } from 'node:path';\nimport { input, select } from '@inquirer/prompts';\nimport pc from 'picocolors';\nimport { TEMPLATE_URL } from '#/constants';\nimport { create } from '#/create';\nimport { type CreateOptions, cli, showWelcome } from '#/modules/cli';\nimport { detectUsedPorts, findNextOffset } from '#/utils/detect-used-ports';\nimport { extractPackageJsonVersionFromUri } from '#/utils/extract-package-json-version-from-uri';\nimport { isEmptyDirectory } from '#/utils/is-empty-directory';\nimport { validateProjectName } from '#/utils/validate-project-name';\n\nasync function main(): Promise<void> {\n // Get the latest version of the template\n const templateVersion = await extractPackageJsonVersionFromUri(TEMPLATE_URL);\n\n // Display CLI welcome banner\n showWelcome(templateVersion);\n\n // Shared theme to clear prompts after answering\n const promptTheme = { prefix: '', style: { answer: (text: string) => text } };\n const promptContext = { clearPromptOnDone: true };\n\n // Prompt for project name if not provided\n if (!cli.directory) {\n cli.directory = await input(\n {\n message: 'Enter your project name',\n default: 'my-cella-app',\n theme: promptTheme,\n validate: (name) => {\n const validation = validateProjectName(basename(resolve(name)));\n return validation.valid ? true : `Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`;\n },\n },\n promptContext,\n );\n }\n\n // Default to creating a 'development' working branch\n if (!cli.newBranchName) {\n cli.newBranchName = 'development';\n }\n\n const targetFolder = resolve(cli.directory);\n const projectName = basename(targetFolder)?.toLowerCase() || 'project';\n\n // Check if the target folder exists and is not empty\n if (existsSync(targetFolder) && !(await isEmptyDirectory(targetFolder))) {\n const dirName = cli.directory === '.' ? 'Current directory' : `Target directory \"${targetFolder}\"`;\n const message = `${dirName} is not empty. Please choose how you would like to proceed:`;\n\n const action = await select(\n {\n message,\n theme: promptTheme,\n choices: [\n { name: 'Cancel and exit', value: 'cancel' },\n { name: 'Ignore existing files and continue', value: 'ignore' },\n ],\n },\n promptContext,\n );\n\n if (action === 'cancel') {\n process.exit(1);\n }\n }\n\n // Scan sibling directories and prompt for port offset\n const portOffset = await promptPortOffset(targetFolder, promptTheme, promptContext);\n\n // Prompt for admin email\n const adminEmail = await input(\n {\n message: 'Admin email for initial seed user',\n default: `admin@${projectName}.com`,\n theme: promptTheme,\n },\n promptContext,\n );\n\n // Proceed with the project creation\n const createOptions: CreateOptions = {\n projectName,\n targetFolder,\n newBranchName: cli.newBranchName,\n packageManager: cli.packageManager,\n templateUrl: cli.options.template,\n portOffset,\n adminEmail,\n };\n\n await create(createOptions);\n}\n\nmain().catch(console.error);\n\n/** Format an offset as a port overview string, e.g. \"10 → :3010 / :4010 / :5442\" */\nfunction formatOffset(o: number, suffix = ''): string {\n return `${o} → :${3000 + o} / :${4000 + o} / :${5432 + o}${suffix}`;\n}\n\n/** Scan sibling forks and prompt the user to pick a port offset */\nasync function promptPortOffset(targetFolder: string, theme: object, context: object): Promise<number> {\n const usedPorts = await detectUsedPorts(targetFolder);\n const suggested = findNextOffset(usedPorts);\n\n if (usedPorts.length > 0) {\n console.info(pc.dim('\\nDetected cella forks in sibling directories:'));\n for (const p of usedPorts) {\n console.info(pc.dim(` ${p.project}: frontend :${p.frontend}, backend :${p.backend} (offset ${p.offset})`));\n }\n console.info();\n }\n\n const presets = [0, 10, 20, 30].filter((o) => o !== suggested);\n const choice = await select(\n {\n message: 'Port offset (avoids conflicts with sibling forks)',\n theme,\n choices: [\n ...(suggested > 0 ? [{ name: formatOffset(suggested, ' (suggested)'), value: suggested }] : []),\n { name: formatOffset(0, ' (default)'), value: 0 },\n ...presets.map((o) => ({ name: formatOffset(o), value: o })),\n { name: 'Custom offset', value: -1 },\n ],\n },\n context,\n );\n\n if (choice !== -1) return choice;\n\n const custom = await input(\n {\n message: 'Enter custom offset (0-490, multiples of 10)',\n default: String(suggested),\n theme,\n validate: (val) => {\n const n = Number(val);\n if (Number.isNaN(n) || n < 0 || n > 490) return 'Must be between 0 and 490';\n if (n % 10 !== 0) return 'Must be a multiple of 10';\n return true;\n },\n },\n context,\n );\n return Number(custom);\n}\n","import pc from 'picocolors';\nimport packageJson from '../package.json' with { type: 'json' };\n\n/** Name of this CLI tool */\nexport const NAME = 'create cella';\n\n/** Thin line divider for console output (60 chars wide) */\nexport const DIVIDER = '─'.repeat(60);\n\n/** URL of the template repository */\nexport const TEMPLATE_URL = 'github:cellajs/cella';\n\n/** URL to the repository */\nexport const CELLA_REMOTE_URL = 'git@github.com:cellajs/cella.git';\n\n/** Export details from package.json */\nexport const DESCRIPTION: string = packageJson.description;\nexport const VERSION: string = packageJson.version;\nexport const AUTHOR: string = packageJson.author;\nexport const WEBSITE: string = packageJson.homepage;\nexport const GITHUB: string = packageJson.repository.url;\n\nexport function getHeaderLine(templateVersion?: string): string {\n const leftText = `⧈ ${NAME} · v${VERSION} · cella v${templateVersion}`;\n const rightText = packageJson.homepage.replace('https://', '');\n const left = `${pc.cyan(`⧈ ${NAME}`)} ${pc.dim(`· v${VERSION} · cella v${templateVersion}`)}`;\n const right = pc.cyan(rightText);\n const padding = Math.max(1, 60 - leftText.length - rightText.length);\n return `${left}${' '.repeat(padding)}${right}`;\n}\n\n// Files or folders to be removed from the template after downloading\nexport const TO_REMOVE: string[] = ['./cli/create', './info/QUICKSTART.md'];\n\n// Specific folder contents to be cleaned out from the template\nexport const TO_CLEAN: string[] = ['./backend/drizzle'];\n\n// Files to copy/paste after downloading\nexport const TO_COPY: Record<string, string> = {\n './frontend/.env.example': './frontend/.env',\n './info/QUICKSTART.md': 'README.md',\n};\n\n/**\n * Placeholder config template that replaces `shared/config/config.default.ts` in new forks.\n * Contains `__project_name__` and `__project_slug__` tokens interpolated at create time.\n */\nexport const PLACEHOLDER_CONFIG = './cli/create-cella/configs/default-config.ts.template';\n\n/**\n * Read a `.env.example` file and apply key=value replacements.\n * Comments and unmatched keys are preserved as-is.\n * Returns null if the file doesn't exist (caller should generate from scratch).\n */\nexport async function generateEnvFromExample(\n examplePath: string,\n replacements: Record<string, string>,\n): Promise<string | null> {\n const { readFile } = await import('node:fs/promises');\n let content: string;\n try {\n content = await readFile(examplePath, 'utf8');\n } catch {\n return null;\n }\n\n return content.replace(/^([A-Z_][A-Z0-9_]*)=(.*)$/gm, (match, key, _value) => {\n if (key in replacements) return `${key}=${replacements[key]}`;\n return match;\n });\n}\n\n/** Replacement map for root `.env` */\nexport function getRootEnvReplacements(slug: string, portOffset: number): Record<string, string> {\n return {\n PROJECT_SLUG: slug,\n DB_PORT: String(5432 + portOffset),\n DB_TEST_PORT: String(5434 + portOffset),\n };\n}\n\n/** Replacement map for `backend/.env` */\nexport function getBackendEnvReplacements(adminEmail: string, portOffset: number): Record<string, string> {\n const db = 5432 + portOffset;\n return {\n DATABASE_URL: `postgres://runtime_role:dev_password@0.0.0.0:${db}/postgres`,\n DATABASE_ADMIN_URL: `postgres://postgres:postgres@0.0.0.0:${db}/postgres`,\n DATABASE_CDC_URL: `postgres://admin_role:dev_password@0.0.0.0:${db}/postgres`,\n ADMIN_EMAIL: adminEmail,\n PORT: String(4000 + portOffset),\n };\n}\n\n/**\n * Generate env config files with project-specific values.\n * All configs go through the same data-driven loop.\n * Values prefixed with '=' are emitted as raw TS expressions (not quoted).\n */\nexport function generateEnvConfigs(slug: string, name: string, portOffset: number): Record<string, string> {\n const fe = 3000 + portOffset;\n const be = 4000 + portOffset;\n\n const header =\n \"import type { DeepPartial } from '../src/builder/types';\\nimport type _default from './config.default';\\n\";\n\n // Per-environment specs: optional imports + object props (= prefix → raw TS expression)\n const envs: Record<string, { imports?: string; props: Record<string, string | boolean> }> = {\n development: {\n props: {\n slug: `${slug}-development`,\n debug: false,\n domain: '',\n frontendUrl: `http://localhost:${fe}`,\n backendUrl: `http://localhost:${be}`,\n backendAuthUrl: `http://localhost:${be}/auth`,\n },\n },\n staging: {\n props: {\n slug: `${slug}-staging`,\n debug: false,\n domain: `${slug}.example.com`,\n frontendUrl: `https://staging.${slug}.example.com`,\n backendUrl: `https://api-staging.${slug}.example.com`,\n backendAuthUrl: `https://api-staging.${slug}.example.com/auth`,\n },\n },\n tunnel: {\n props: {\n frontendUrl: `https://localhost:${fe}`,\n backendUrl: `https://${slug}.ngrok.dev`,\n backendAuthUrl: `https://${slug}.ngrok.dev/auth`,\n },\n },\n test: {\n imports: \"import development from './config.development';\\n\",\n props: {\n debug: false,\n domain: '',\n frontendUrl: '=development.frontendUrl',\n backendUrl: '=development.backendUrl',\n backendAuthUrl: '=development.backendAuthUrl',\n },\n },\n production: { props: { maintenance: false } },\n };\n\n // Serialize value: '=' prefix → raw TS expression, boolean → literal, string → quoted\n const lit = (v: string | boolean) => {\n if (typeof v === 'boolean') return String(v);\n if (v.startsWith('=')) return v.slice(1);\n return `'${v}'`;\n };\n\n const result: Record<string, string> = {};\n\n for (const [mode, { imports = '', props }] of Object.entries(envs)) {\n const nameEntry = mode !== 'production' ? ` name: '${name} ${mode.toUpperCase()}',\\n` : '';\n const body = Object.entries(props)\n .map(([k, v]) => ` ${k}: ${lit(v)},`)\n .join('\\n');\n result[`./shared/config/config.${mode}.ts`] =\n `${imports}${header}\\nexport default {\\n mode: '${mode}',\\n${nameEntry}${body}\\n} satisfies DeepPartial<typeof _default>;\\n`;\n }\n\n return result;\n}\n","{\n \"name\": \"@cellajs/create-cella\",\n \"version\": \"0.2.1\",\n \"private\": false,\n \"license\": \"MIT\",\n \"description\": \"Cella is a TypeScript template to create web apps with sync and offline capabilities.\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/cellajs/cella\",\n \"directory\": \"cli/create-cella\"\n },\n \"homepage\": \"https://cellajs.com\",\n \"author\": \"CellaJS <info@cellajs.com>\",\n \"engines\": {\n \"node\": \"24.x\"\n },\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"configs\",\n \"dist\",\n \"index.js\",\n \"README.md\"\n ],\n \"imports\": {\n \"#/*\": \"./src/*\"\n },\n \"bin\": {\n \"create-cella\": \"index.js\"\n },\n \"scripts\": {\n \"start\": \"tsx ./src/create-cella-cli.ts\",\n \"clean\": \"rimraf ./dist\",\n \"build\": \"tsup\",\n \"test-build\": \"pnpm run build && node index.js\",\n \"prepublishOnly\": \"pnpm run test:release\",\n \"check\": \"pnpm ts && pnpm biome check --write .\",\n \"ts\": \"tsgo --pretty\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"test\": \"vitest run\",\n \"test:release\": \"pnpm run build && vitest run tests/release-artifact.test.ts\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@inquirer/prompts\": \"^8.5.2\",\n \"axios\": \"^1.17.0\",\n \"commander\": \"^15.0.0\",\n \"giget\": \"^3.1.2\",\n \"nano-spawn\": \"^2.0.0\",\n \"ora\": \"^9.3.0\",\n \"picocolors\": \"^1.1.1\",\n \"validate-npm-package-name\": \"^8.0.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"catalog:\",\n \"@typescript/native-preview\": \"catalog:\",\n \"@types/validate-npm-package-name\": \"^4.0.2\",\n \"tsup\": \"catalog:\",\n \"tsx\": \"catalog:\",\n \"vitest\": \"catalog:\"\n }\n}\n","import { existsSync } from 'node:fs';\nimport { cp, mkdir } from 'node:fs/promises';\nimport { join, relative, resolve } from 'node:path';\nimport { downloadTemplate } from 'giget';\nimport { addRemote } from '#/add-remote';\nimport { TEMPLATE_URL } from '#/constants';\nimport { type CreateOptions, showSuccess } from '#/modules/cli';\nimport { cleanTemplate } from '#/utils/clean-template';\nimport { gitAddAll, gitBranch, gitCheckout, gitCommit, gitInit } from '#/utils/git';\nimport { createProgress } from '#/utils/progress';\nimport { generate, install } from '#/utils/run-package-manager-command';\n\n/** Check if a path is a local directory */\nfunction isLocalPath(path: string): boolean {\n return path.startsWith('/') || path.startsWith('./') || path.startsWith('../');\n}\n\nexport async function create({\n projectName,\n targetFolder,\n newBranchName,\n packageManager,\n templateUrl,\n portOffset,\n adminEmail,\n silent = false,\n}: CreateOptions): Promise<void> {\n // Save the original working directory\n const originalCwd = process.cwd();\n\n // Use custom template or default\n const template = templateUrl || TEMPLATE_URL;\n const isLocalTemplate = templateUrl && isLocalPath(templateUrl);\n\n const progress = createProgress('creating project', silent);\n\n try {\n // Create the target folder\n progress.step('creating project folder');\n await mkdir(targetFolder, { recursive: true });\n process.chdir(targetFolder);\n\n // Download or copy the template\n if (isLocalTemplate) {\n progress.step('copying local template');\n const sourcePath = resolve(originalCwd, templateUrl);\n\n // Check if target is inside source (would cause EINVAL)\n if (targetFolder.startsWith(sourcePath)) {\n throw new Error(\n 'Cannot create project inside the template directory.\\n' +\n ` Run from outside: cd ~ && pnpm create @cellajs/cella ${projectName} --template ${templateUrl}`,\n );\n }\n\n await cp(sourcePath, targetFolder, {\n recursive: true,\n filter: (src) => !src.includes('node_modules') && !src.includes('.git'),\n });\n } else {\n progress.step('downloading cella template');\n await downloadTemplate(template, {\n cwd: process.cwd(),\n dir: targetFolder,\n force: true,\n provider: 'github',\n });\n }\n\n // Clean the template, generate configs\n progress.step('cleaning template');\n const displayName = projectName.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n await cleanTemplate({ targetFolder, projectName, displayName, portOffset, adminEmail });\n\n // Install dependencies\n progress.step('installing dependencies');\n await install(packageManager);\n\n // Generate SQL files\n progress.step('generating migrations');\n await generate(packageManager);\n\n // Initialize git repository\n const gitFolderPath = join(targetFolder, '.git');\n if (!existsSync(gitFolderPath)) {\n progress.step('initializing git');\n await gitInit(targetFolder);\n await gitAddAll(targetFolder);\n await gitCommit(targetFolder, 'Initial commit');\n\n if (newBranchName) {\n progress.step(`creating branch '${newBranchName}'`);\n await gitBranch(targetFolder, newBranchName);\n await gitCheckout(targetFolder, newBranchName);\n }\n }\n\n // Add upstream remote\n progress.step('adding upstream remote');\n await addRemote({ targetFolder, silent: true });\n\n // Done\n progress.done(`created ${projectName}`);\n } catch (error) {\n progress.fail(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n\n // Check if the working directory needs to be changed\n const needsCd = originalCwd !== targetFolder;\n const relativePath = relative(originalCwd, targetFolder);\n\n // Display final success message\n showSuccess(projectName, targetFolder, relativePath, needsCd, packageManager);\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Executes a Git command in a specific repository path and returns the trimmed stdout.\n * Uses execFile for safety (no shell injection) aligned with sync package pattern.\n *\n * @param args - The Git command arguments to execute (e.g., ['status'], ['checkout', 'branch-name']).\n * @param repoPath - The path to the Git repository.\n * @param options - Optional settings for command execution.\n * @returns The stdout of the Git command, trimmed of leading and trailing whitespace.\n */\nexport async function runGitCommand(\n args: string[],\n repoPath: string,\n options: { skipEditor?: boolean; maxBuffer?: number } = {},\n): Promise<string> {\n const gitArgs = repoPath ? ['-C', repoPath, ...args] : args;\n\n const env = {\n ...process.env,\n ...(options.skipEditor ? { GIT_EDITOR: 'true' } : {}),\n };\n\n // Default to 10MB buffer for typical outputs\n const maxBuffer = options.maxBuffer ?? 10 * 1024 * 1024;\n\n const { stdout } = await execFileAsync('git', gitArgs, { env, maxBuffer });\n\n return stdout.trim();\n}\n\n/**\n * Initializes a new Git repository in the specified directory.\n */\nexport async function gitInit(repoPath: string): Promise<string> {\n return runGitCommand(['init'], repoPath);\n}\n\n/**\n * Stages all files in the repository.\n */\nexport async function gitAddAll(repoPath: string): Promise<string> {\n return runGitCommand(['add', '.'], repoPath);\n}\n\n/**\n * Creates a commit with the specified message.\n */\nexport async function gitCommit(repoPath: string, message: string): Promise<string> {\n return runGitCommand(['commit', '-m', message], repoPath);\n}\n\n/**\n * Creates a new branch with the specified name.\n */\nexport async function gitBranch(repoPath: string, branchName: string): Promise<string> {\n return runGitCommand(['branch', branchName], repoPath);\n}\n\n/**\n * Checks out the specified branch.\n */\nexport async function gitCheckout(repoPath: string, branchName: string): Promise<string> {\n return runGitCommand(['checkout', branchName], repoPath);\n}\n\n/**\n * Gets the URL of a remote.\n */\nexport async function gitRemoteGetUrl(repoPath: string, remoteName: string): Promise<string> {\n return runGitCommand(['remote', 'get-url', remoteName], repoPath);\n}\n\n/**\n * Adds a new remote.\n */\nexport async function gitRemoteAdd(repoPath: string, remoteName: string, remoteUrl: string): Promise<string> {\n return runGitCommand(['remote', 'add', remoteName, remoteUrl], repoPath);\n}\n\n/**\n * Removes a remote.\n */\nexport async function gitRemoteRemove(repoPath: string, remoteName: string): Promise<string> {\n return runGitCommand(['remote', 'remove', remoteName], repoPath);\n}\n","import { CELLA_REMOTE_URL } from '#/constants';\nimport type { AddRemoteOptions } from '#/modules/cli';\nimport { gitRemoteAdd, gitRemoteGetUrl, gitRemoteRemove } from '#/utils/git';\n\n/**\n * Adds or updates the upstream remote for the Cella template.\n * @param options - Configuration options\n * @param options.silent - If true, don't throw on failure (used when called from progress tracker)\n */\nexport async function addRemote({\n targetFolder,\n remoteUrl = CELLA_REMOTE_URL,\n remoteName = 'upstream',\n silent = false,\n}: AddRemoteOptions): Promise<void> {\n try {\n // Check if the remote exists\n let remote: string | null = null;\n\n try {\n remote = await gitRemoteGetUrl(targetFolder, remoteName);\n } catch {\n // If the remote doesn't exist, it throws a fatal error\n remote = null;\n }\n\n // Add or update the remote if it doesn't exist or differs from `remoteUrl`\n if (!remote) {\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n } else if (remote !== remoteUrl) {\n // Remove existing remote and set the new URL\n await gitRemoteRemove(targetFolder, remoteName);\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n }\n } catch (error) {\n if (!silent) {\n throw error;\n }\n }\n}\n","import { basename, resolve } from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { NAME, VERSION } from '#/constants';\nimport { validateProjectName } from '#/utils/validate-project-name';\nimport type { CLIConfig, CLIOptions } from './types';\n\n// Initialize CLI variables\nlet directory: string | null = null;\nconst newBranchName: string | null = null;\nconst packageManager = 'pnpm';\n\n/**\n * Defines the root CLI command using Commander.\n * This command accepts CLI options and validates user input.\n */\nexport const command = new Command(NAME)\n .version(VERSION, '-v, --version', `output the current version of ${NAME}`)\n .argument('[directory]', 'the directory name for the new project')\n .usage('[directory] [options]')\n .helpOption('-h, --help', 'display this help message')\n .option('--template <path>', 'use a custom template (local path or github:user/repo)')\n .action((name: string) => {\n const trimmedName = typeof name === 'string' ? name.trim() : name;\n\n if (trimmedName) {\n const validation = validateProjectName(basename(resolve(trimmedName)));\n\n if (!validation.valid) {\n throw new InvalidArgumentError(`Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`);\n }\n\n directory = trimmedName;\n }\n })\n .parse();\n\n// Gather the CLI options and arguments\nconst options: CLIOptions = command.opts<CLIOptions>();\n\n/**\n * Runs the CLI and returns the parsed configuration.\n * This function parses command line arguments and returns the CLI config.\n */\nexport function runCli(): CLIConfig {\n return {\n options,\n args: command.args,\n directory,\n newBranchName,\n packageManager,\n };\n}\n\n// Export CLI configuration for direct import\nexport const cli: CLIConfig = runCli();\n","import validate from 'validate-npm-package-name';\n\ninterface ValidationResult {\n valid: boolean;\n problems?: string[];\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const nameValidation = validate(name);\n\n if (nameValidation.validForNewPackages) {\n return { valid: true };\n }\n\n return {\n valid: false,\n problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])],\n };\n}\n","import pc from 'picocolors';\nimport { DESCRIPTION, DIVIDER, getHeaderLine } from '#/constants';\n\n/** ASCII art logo for the CLI welcome screen. */\nfunction showAscii(): void {\n console.info(pc.cyan(' _ _ '));\n console.info(pc.cyan('▒▓█████▓▒ ___ ___| | | __ _ '));\n console.info(pc.cyan('▒▓█ █▓▒ / __/ _ \\\\ | |/ _` | '));\n console.info(pc.cyan('▒▓█ █▓▒ | (_| __/ | | (_| | '));\n console.info(pc.cyan('▒▓█████▓▒ \\\\___\\\\___|_|_|\\\\__,_| '));\n}\n\n/**\n * Displays the compact CLI welcome header.\n * @param templateVersion - The version of the cella template being used\n */\nexport function showWelcome(templateVersion: string): void {\n console.info();\n showAscii();\n console.info();\n console.info(pc.dim(DESCRIPTION));\n console.info();\n console.info(getHeaderLine(templateVersion));\n console.info(DIVIDER);\n}\n\n/**\n * Displays the final success message after project creation.\n */\nexport function showSuccess(\n projectName: string,\n _targetFolder: string,\n relativePath: string,\n needsCd: boolean,\n packageManager: string,\n): void {\n console.info(DIVIDER);\n console.info();\n\n // Navigation instruction\n if (needsCd) {\n console.info(`${pc.green('→')} cd ${pc.cyan(relativePath)}`);\n console.info();\n }\n\n // Quick start options\n console.info(`${pc.green('→')} ${pc.cyan(`${packageManager} quick`)} ${pc.gray('(pglite, no docker)')}`);\n console.info();\n console.info(pc.gray('or, for full setup:'));\n console.info();\n console.info(\n `${pc.green('→')} ${pc.cyan(`${packageManager} docker`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} seed`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} dev`)}`,\n );\n console.info();\n\n // Credentials\n console.info(`sign in: ${pc.gray('admin-test@cellajs.com / 12345678')}`);\n console.info();\n console.info(`enjoy building ${pc.green(projectName)} with cella!`);\n console.info();\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport pc from 'picocolors';\nimport {\n generateEnvConfigs,\n generateEnvFromExample,\n getBackendEnvReplacements,\n getRootEnvReplacements,\n PLACEHOLDER_CONFIG,\n TO_CLEAN,\n TO_COPY,\n TO_REMOVE,\n} from '#/constants';\n\nconst warningMark = pc.yellow('⚠');\n\n/**\n * Cleans the specified template by removing designated folders and files.\n * @param params - Parameters containing the target folder, project name, and optional extra edits.\n */\nexport async function cleanTemplate({\n targetFolder,\n projectName,\n displayName,\n portOffset = 0,\n adminEmail = `admin@${projectName}.example.com`,\n}: {\n targetFolder: string;\n projectName: string;\n displayName: string;\n portOffset?: number;\n adminEmail?: string;\n}): Promise<void> {\n // Change the current working directory to targetFolder if not already set\n if (process.cwd() !== targetFolder) {\n process.chdir(targetFolder);\n }\n\n return new Promise<void>((resolve, reject) => {\n (async () => {\n try {\n // Copy specified files\n for (const [src, dest] of Object.entries(TO_COPY)) {\n const srcAbsolutePath = path.resolve(targetFolder, src);\n const destAbsolutePath = path.resolve(targetFolder, dest);\n await copyFile(srcAbsolutePath, destAbsolutePath);\n }\n\n // Replace config.default.ts with interpolated placeholder config\n await applyPlaceholderConfig(targetFolder, projectName);\n\n // Generate root .env from .env.example (single source of truth for ports)\n const rootReplacements = getRootEnvReplacements(projectName, portOffset);\n const rootEnv = await generateEnvFromExample(path.resolve(targetFolder, '.env.example'), rootReplacements);\n await fs.writeFile(\n path.resolve(targetFolder, '.env'),\n rootEnv ??\n Object.entries(rootReplacements)\n .map(([k, v]) => `${k}=${v}`)\n .join('\\n'),\n 'utf8',\n );\n\n // Generate backend .env from backend/.env.example\n const backendReplacements = getBackendEnvReplacements(adminEmail, portOffset);\n const backendEnv = await generateEnvFromExample(\n path.resolve(targetFolder, 'backend/.env.example'),\n backendReplacements,\n );\n if (backendEnv) {\n await fs.writeFile(path.resolve(targetFolder, 'backend/.env'), backendEnv, 'utf8');\n }\n\n // Generate minimal env config files with project values and ports baked in\n const envConfigs = generateEnvConfigs(projectName, displayName, portOffset);\n await Promise.all(\n Object.entries(envConfigs).map(([filePath, content]) =>\n fs.writeFile(path.resolve(targetFolder, filePath), content, 'utf8'),\n ),\n );\n\n // Clean specified folder contents\n await Promise.all(\n TO_CLEAN.map((folderPath) => {\n const absolutePath = path.resolve(targetFolder, folderPath);\n return removeFolderContents(absolutePath);\n }),\n );\n\n // Remove specified files and folders\n await Promise.all(\n TO_REMOVE.map((filePath) => {\n const absolutePath = path.resolve(targetFolder, filePath);\n return removeFileOrFolder(absolutePath);\n }),\n );\n\n resolve();\n } catch (err) {\n reject(`Error during the cleaning process: ${err}`);\n }\n })();\n });\n}\n\n/**\n * Removes all contents within a specified folder.\n * @param folderPath - The path of the folder to clean.\n */\nexport async function removeFolderContents(folderPath: string): Promise<void> {\n // List all files in the folder\n const files = await fs.readdir(folderPath);\n\n await Promise.all(\n files.map(async (file) => {\n const filePath = path.join(folderPath, file);\n\n // Get the file or folder statistics\n const stat = await fs.lstat(filePath);\n\n // If it's a directory, remove it and all its contents\n if (stat.isDirectory()) {\n await fs.rm(filePath, { recursive: true, force: true });\n } else {\n // If it's a file, remove it\n await fs.rm(filePath);\n }\n }),\n );\n}\n\n/**\n * Removes a specified file or folder.\n * @param pathToRemove - The path to the file or folder to remove.\n */\nexport async function removeFileOrFolder(pathToRemove: string): Promise<void> {\n await fs.rm(pathToRemove, { recursive: true, force: true });\n}\n\n/**\n * Helper function to copy files if the source exists.\n * @param src - The source file path.\n * @param dest - The destination file path.\n */\nexport async function copyFile(src: string, dest: string): Promise<void> {\n try {\n // Check if the source file exists\n await fs.access(src);\n\n // Ensure the destination directory exists\n await fs.mkdir(path.dirname(dest), { recursive: true });\n\n // Copy the file\n await fs.copyFile(src, dest);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Source file \"${src}\" does not exist > Skip copy`);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Read the placeholder config template, interpolate project tokens, and\n * write it as `shared/config/config.default.ts` — replacing the original.\n */\nasync function applyPlaceholderConfig(targetFolder: string, projectName: string): Promise<void> {\n const displayName = projectName.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n\n const src = path.resolve(targetFolder, PLACEHOLDER_CONFIG);\n const dest = path.resolve(targetFolder, './shared/config/config.default.ts');\n\n try {\n let content = await fs.readFile(src, 'utf8');\n content = content.replaceAll('__project_name__', displayName);\n content = content.replaceAll('__project_slug__', projectName);\n await fs.writeFile(dest, content, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Placeholder config \"${src}\" not found > Skip`);\n } else {\n throw err;\n }\n }\n}\n","/**\n * Progress Tracker Utility\n *\n * Provides compact progress tracking for CLI operations.\n * Uses a single spinner that updates in place.\n */\n\nimport ora, { type Ora } from 'ora';\nimport pc from 'picocolors';\n\n/** Global reference to the currently active spinner */\nlet activeSpinner: Ora | null = null;\n\n/** Pause the active spinner (for interactive prompts) */\nexport function pauseSpinner(): void {\n if (activeSpinner) {\n activeSpinner.stop();\n }\n}\n\n/** Resume the active spinner after a prompt */\nexport function resumeSpinner(): void {\n if (activeSpinner) {\n activeSpinner.start();\n }\n}\n\nexport interface ProgressTracker {\n /** Update the current step (shown as spinner text) */\n step: (message: string) => void;\n /** Mark the tracker as complete with final message */\n done: (message: string) => void;\n /** Mark the tracker as failed with error message */\n fail: (message: string) => void;\n /** Wrap an async operation to auto-fail on error */\n wrap: <T>(fn: () => Promise<T>) => Promise<T>;\n}\n\n/**\n * Creates a progress tracker for a multi-step operation.\n * Shows a single spinner that updates in place.\n *\n * @param title - The initial title for the spinner\n * @returns A ProgressTracker instance\n *\n * @example\n * const progress = createProgress('creating project');\n * progress.step('downloading template');\n * progress.step('installing dependencies');\n * progress.done('project created');\n */\nexport function createProgress(title: string, silent = false): ProgressTracker {\n const completedSteps: string[] = [];\n\n const spinner = ora({\n text: title,\n isSilent: silent,\n });\n\n activeSpinner = spinner;\n spinner.start();\n\n // Helper to log (respects silent mode)\n const log = (msg: string) => {\n if (!silent) console.info(msg);\n };\n\n return {\n step: (message: string) => {\n // Complete previous step with green check if there was one\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n }\n completedSteps.push(message);\n spinner.text = message;\n spinner.start();\n },\n\n done: (message: string) => {\n // Complete the last step with green check\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n } else {\n spinner.stop();\n }\n activeSpinner = null;\n if (message) {\n log('');\n log(`${pc.green('✓')} ${message}`);\n }\n },\n\n fail: (message: string) => {\n spinner.stop();\n activeSpinner = null;\n log(`${pc.red('✗')} ${pc.red(message)}`);\n },\n\n wrap: async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n // Stop spinner and show tree-style breakdown on failure\n spinner.stop();\n activeSpinner = null;\n log(pc.cyan(`\\n${title}`));\n for (const step of completedSteps) {\n log(pc.gray(` ├─ ${step}`));\n }\n log(pc.red(` └─ ✗ ${errorMessage}`));\n throw error;\n }\n },\n };\n}\n","import spawn from 'nano-spawn';\n\n/**\n * Executes a command using the specified package manager (e.g., pnpm).\n *\n * @param packageManager - The package manager to use (e.g., 'pnpm').\n * @param args - The arguments to pass to the package manager command.\n * @param env - Additional environment variables to set during command execution.\n * @returns A promise that resolves if the command executes successfully; otherwise, it rejects with an error message.\n */\nexport async function runPackageManagerCommand(\n packageManager: string,\n args: string[],\n env: Record<string, string> = {},\n): Promise<void> {\n try {\n await spawn(packageManager, args, {\n env: { ...process.env, ...env },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error) {\n const err = error as { stdout?: string; stderr?: string };\n throw new Error(`\"${packageManager} ${args.join(' ')}\" failed ${err.stdout || ''} ${err.stderr || ''}`);\n }\n}\n\n/**\n * Installs dependencies using the specified package manager.\n *\n * @param packageManager - The package manager to use for installation (e.g., 'pnpm').\n * @returns A promise that resolves if the installation completes successfully; otherwise, it rejects with an error.\n */\nexport async function install(packageManager: string): Promise<void> {\n return runPackageManagerCommand(packageManager, ['install'], {\n NODE_ENV: 'development',\n });\n}\n\n/**\n * Generates SQL files using the specified package manager.\n *\n * @param packageManager - The package manager to use for generation (e.g., 'pnpm').\n * @returns A promise that resolves if the generation completes successfully; otherwise, it rejects with an error.\n */\nexport async function generate(packageManager: string): Promise<void> {\n return runPackageManagerCommand(packageManager, ['generate'], {\n NODE_ENV: 'development',\n });\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\ninterface UsedPorts {\n project: string;\n frontend: number;\n backend: number;\n offset: number;\n}\n\n/**\n * Scan sibling directories for existing cella forks and extract their dev ports.\n * Looks for `shared/config/config.development.ts` to identify cella-based projects.\n */\nexport async function detectUsedPorts(targetFolder: string): Promise<UsedPorts[]> {\n const parentDir = dirname(targetFolder);\n const used: UsedPorts[] = [];\n\n let siblings: string[];\n try {\n siblings = await readdir(parentDir);\n } catch {\n return used;\n }\n\n for (const name of siblings) {\n const configPath = join(parentDir, name, 'shared/config/config.development.ts');\n try {\n const content = await readFile(configPath, 'utf8');\n const feMatch = content.match(/frontendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n const beMatch = content.match(/backendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n if (feMatch && beMatch) {\n const frontend = Number(feMatch[1]);\n const backend = Number(beMatch[1]);\n used.push({\n project: name,\n frontend,\n backend,\n offset: frontend - 3000,\n });\n }\n } catch {\n // Not a cella fork, skip\n }\n }\n\n return used;\n}\n\n/** Find the next available offset (in steps of 10) that doesn't collide with existing forks. */\nexport function findNextOffset(usedPorts: UsedPorts[]): number {\n const usedOffsets = new Set(usedPorts.map((p) => p.offset));\n for (let offset = 0; offset <= 490; offset += 10) {\n if (!usedOffsets.has(offset)) return offset;\n }\n return 0;\n}\n","import axios from 'axios';\n\n/**\n * Retrieves the version from the package.json file of a GitHub repository.\n * If the package.json file is not found or an error occurs, it returns 'unknown'.\n *\n * @param repositoryUrl {string} - The GitHub repository URL in the format 'github:user/repo'.\n * @param branch {string} - The branch name (defaults to 'main').\n * @returns {Promise<string>} - The version from the package.json file.\n */\nexport async function extractPackageJsonVersionFromUri(repositoryUrl: string, branch = 'main'): Promise<string> {\n // Extract owner and repo from the URL\n const [owner, repo] = repositoryUrl.replace('github:', '').split('/');\n\n // Construct the URL for the package.json file in the provided branch\n const packageJsonUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/package.json`;\n\n try {\n // Fetch the package.json file\n const response = await axios.get(packageJsonUrl);\n const packageJson = response.data;\n\n // Return the version from the package.json, or 'unknown' if not found\n return packageJson.version || 'unknown';\n } catch (error) {\n // If there's an error (file not found, etc.), return 'unknown'\n return 'unknown';\n }\n}\n","import { readdir } from 'node:fs/promises';\n\n/**\n * Checks if a directory is empty or only contains a .git directory.\n *\n * @param path - The path of the directory to check.\n * @returns Resolves to true if the directory is empty or contains only a .git folder, false otherwise.\n * @throws Throws an error if the path is not a directory or if there's an issue reading the directory.\n */\nexport async function isEmptyDirectory(path: string): Promise<boolean> {\n const files = await readdir(path);\n\n // Check if directory is empty or contains only the .git directory\n return files.length === 0 || (files.length === 1 && files[0] === '.git');\n}\n"],"mappings":"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","names":["existsSync","basename","resolve","pc","readFile","resolve","options","pc","pc","packageManager","pc","pc","resolve","pc","pc","packageManager","path","newBranchName","packageManager","resolve","join","readdir","path","basename","resolve","existsSync","pc"]}
1
+ {"version":3,"sources":["../src/create-cella-cli.ts","../src/constants.ts","../package.json","../src/create.ts","../src/utils/git/command.ts","../src/add-remote.ts","../src/modules/cli/commands.ts","../src/utils/validate-project-name.ts","../src/modules/cli/display.ts","../src/utils/clean-template.ts","../src/utils/progress.ts","../src/utils/run-package-manager-command.ts","../src/utils/detect-used-ports.ts","../src/utils/extract-package-json-version-from-uri.ts","../src/utils/is-empty-directory.ts"],"sourcesContent":["#!/usr/bin/env tsx\n\nimport { existsSync } from 'node:fs';\nimport { basename, resolve } from 'node:path';\nimport { input, select } from '@inquirer/prompts';\nimport pc from 'picocolors';\nimport { TEMPLATE_URL } from '#/constants';\nimport { create } from '#/create';\nimport { type CreateOptions, cli, showWelcome } from '#/modules/cli';\nimport { detectUsedPorts, findNextOffset } from '#/utils/detect-used-ports';\nimport { extractPackageJsonVersionFromUri } from '#/utils/extract-package-json-version-from-uri';\nimport { isEmptyDirectory } from '#/utils/is-empty-directory';\nimport { validateProjectName } from '#/utils/validate-project-name';\n\nasync function main(): Promise<void> {\n // Get the latest version of the template\n const templateVersion = await extractPackageJsonVersionFromUri(TEMPLATE_URL);\n\n // Display CLI welcome banner\n showWelcome(templateVersion);\n\n // Shared theme to clear prompts after answering\n const promptTheme = { prefix: '', style: { answer: (text: string) => text } };\n const promptContext = { clearPromptOnDone: true };\n\n // Prompt for project name if not provided\n if (!cli.directory) {\n cli.directory = await input(\n {\n message: 'Enter your project name',\n default: 'my-cella-app',\n theme: promptTheme,\n validate: (name) => {\n const validation = validateProjectName(basename(resolve(name)));\n return validation.valid ? true : `Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`;\n },\n },\n promptContext,\n );\n }\n\n // Default to creating a 'development' working branch\n if (!cli.newBranchName) {\n cli.newBranchName = 'development';\n }\n\n const targetFolder = resolve(cli.directory);\n const projectName = basename(targetFolder)?.toLowerCase() || 'project';\n\n // Check if the target folder exists and is not empty\n if (existsSync(targetFolder) && !(await isEmptyDirectory(targetFolder))) {\n const dirName = cli.directory === '.' ? 'Current directory' : `Target directory \"${targetFolder}\"`;\n const message = `${dirName} is not empty. Please choose how you would like to proceed:`;\n\n const action = await select(\n {\n message,\n theme: promptTheme,\n choices: [\n { name: 'Cancel and exit', value: 'cancel' },\n { name: 'Ignore existing files and continue', value: 'ignore' },\n ],\n },\n promptContext,\n );\n\n if (action === 'cancel') {\n process.exit(1);\n }\n }\n\n // Scan sibling directories and prompt for port offset\n const portOffset = cli.options.portOffset ?? (await promptPortOffset(targetFolder, promptTheme, promptContext));\n\n // Prompt for admin email\n const adminEmail =\n cli.options.adminEmail ??\n (await input(\n {\n message: 'Admin email for initial seed user',\n default: `admin@${projectName}.com`,\n theme: promptTheme,\n },\n promptContext,\n ));\n\n // Proceed with the project creation\n const createOptions: CreateOptions = {\n projectName,\n targetFolder,\n newBranchName: cli.newBranchName,\n packageManager: cli.packageManager,\n templateUrl: cli.options.template,\n portOffset,\n adminEmail,\n };\n\n await create(createOptions);\n}\n\nmain().catch(console.error);\n\n/** Format an offset as a port overview string, e.g. \"10 → :3010 / :4010 / :5442\" */\nfunction formatOffset(o: number, suffix = ''): string {\n return `${o} → :${3000 + o} / :${4000 + o} / :${5432 + o}${suffix}`;\n}\n\n/** Scan sibling forks and prompt the user to pick a port offset */\nasync function promptPortOffset(targetFolder: string, theme: object, context: object): Promise<number> {\n const usedPorts = await detectUsedPorts(targetFolder);\n const suggested = findNextOffset(usedPorts);\n\n if (usedPorts.length > 0) {\n console.info(pc.dim('\\nDetected cella forks in sibling directories:'));\n for (const p of usedPorts) {\n console.info(pc.dim(` ${p.project}: frontend :${p.frontend}, backend :${p.backend} (offset ${p.offset})`));\n }\n console.info();\n }\n\n const presets = [0, 10, 20, 30].filter((o) => o !== suggested);\n const choice = await select(\n {\n message: 'Port offset (avoids conflicts with sibling forks)',\n theme,\n choices: [\n ...(suggested > 0 ? [{ name: formatOffset(suggested, ' (suggested)'), value: suggested }] : []),\n { name: formatOffset(0, ' (default)'), value: 0 },\n ...presets.map((o) => ({ name: formatOffset(o), value: o })),\n { name: 'Custom offset', value: -1 },\n ],\n },\n context,\n );\n\n if (choice !== -1) return choice;\n\n const custom = await input(\n {\n message: 'Enter custom offset (0-490, multiples of 10)',\n default: String(suggested),\n theme,\n validate: (val) => {\n const n = Number(val);\n if (Number.isNaN(n) || n < 0 || n > 490) return 'Must be between 0 and 490';\n if (n % 10 !== 0) return 'Must be a multiple of 10';\n return true;\n },\n },\n context,\n );\n return Number(custom);\n}\n","import pc from 'picocolors';\nimport packageJson from '../package.json' with { type: 'json' };\n\n/** Name of this CLI tool */\nexport const NAME = 'create cella';\n\n/** Thin line divider for console output (60 chars wide) */\nexport const DIVIDER = '─'.repeat(60);\n\n/** URL of the template repository */\nexport const TEMPLATE_URL = 'github:cellajs/cella';\n\n/** URL to the repository */\nexport const CELLA_REMOTE_URL = 'git@github.com:cellajs/cella.git';\n\n/** Export details from package.json */\nexport const DESCRIPTION: string = packageJson.description;\nexport const VERSION: string = packageJson.version;\nexport const AUTHOR: string = packageJson.author;\nexport const WEBSITE: string = packageJson.homepage;\nexport const GITHUB: string = packageJson.repository.url;\n\nexport function getHeaderLine(templateVersion?: string): string {\n const leftText = `⧈ ${NAME} · v${VERSION} · cella v${templateVersion}`;\n const rightText = packageJson.homepage.replace('https://', '');\n const left = `${pc.cyan(`⧈ ${NAME}`)} ${pc.dim(`· v${VERSION} · cella v${templateVersion}`)}`;\n const right = pc.cyan(rightText);\n const padding = Math.max(1, 60 - leftText.length - rightText.length);\n return `${left}${' '.repeat(padding)}${right}`;\n}\n\n// Files or folders to be removed from the template after downloading\nexport const TO_REMOVE: string[] = ['./cli/create', './info/QUICKSTART.md'];\n\n// Specific folder contents to be cleaned out from the template\nexport const TO_CLEAN: string[] = ['./backend/drizzle'];\n\n// Files to copy/paste after downloading\nexport const TO_COPY: Record<string, string> = {\n './frontend/.env.example': './frontend/.env',\n './info/QUICKSTART.md': 'README.md',\n};\n\n/**\n * Placeholder config template that replaces `shared/config/config.default.ts` in new forks.\n * Ships inside the create-cella package (see package.json `files`) and is resolved relative\n * to the package — NOT the downloaded fork. Contains `__project_name__` and `__project_slug__`\n * tokens interpolated at create time.\n */\nexport const PLACEHOLDER_CONFIG = 'configs/default-config.ts.template';\n\n/**\n * Read a `.env.example` file and apply key=value replacements.\n * Comments and unmatched keys are preserved as-is.\n * Returns null if the file doesn't exist (caller should generate from scratch).\n */\nexport async function generateEnvFromExample(\n examplePath: string,\n replacements: Record<string, string>,\n): Promise<string | null> {\n const { readFile } = await import('node:fs/promises');\n let content: string;\n try {\n content = await readFile(examplePath, 'utf8');\n } catch {\n return null;\n }\n\n return content.replace(/^([A-Z_][A-Z0-9_]*)=(.*)$/gm, (match, key, _value) => {\n if (key in replacements) return `${key}=${replacements[key]}`;\n return match;\n });\n}\n\n/**\n * Replacement map for `backend/.env`.\n * The backend `.env` is the single source of truth for the project slug, database\n * ports (consumed by `backend/compose.yaml`), connection URLs, admin email and API port.\n */\nexport function getBackendEnvReplacements(\n slug: string,\n adminEmail: string,\n portOffset: number,\n): Record<string, string> {\n const db = 5432 + portOffset;\n return {\n PROJECT_SLUG: slug,\n DB_PORT: String(db),\n DB_TEST_PORT: String(5434 + portOffset),\n DATABASE_URL: `postgres://runtime_role:dev_password@0.0.0.0:${db}/postgres`,\n DATABASE_ADMIN_URL: `postgres://postgres:postgres@0.0.0.0:${db}/postgres`,\n DATABASE_CDC_URL: `postgres://admin_role:dev_password@0.0.0.0:${db}/postgres`,\n ADMIN_EMAIL: adminEmail,\n PORT: String(4000 + portOffset),\n };\n}\n\n/**\n * Generate env config files with project-specific values.\n * All configs go through the same data-driven loop.\n * Values prefixed with '=' are emitted as raw TS expressions (not quoted).\n */\nexport function generateEnvConfigs(slug: string, name: string, portOffset: number): Record<string, string> {\n const fe = 3000 + portOffset;\n const be = 4000 + portOffset;\n\n const header =\n \"import type { DeepPartial } from '../src/config-builder/types';\\nimport type _default from './config.default';\\n\";\n\n // Per-environment specs: optional imports + object props (= prefix → raw TS expression)\n const envs: Record<string, { imports?: string; props: Record<string, string | boolean> }> = {\n development: {\n props: {\n slug: `${slug}-development`,\n domain: '',\n frontendUrl: `http://localhost:${fe}`,\n backendUrl: `http://localhost:${be}`,\n backendAuthUrl: `http://localhost:${be}/auth`,\n },\n },\n staging: {\n props: {\n slug: `${slug}-staging`,\n domain: `${slug}.example.com`,\n frontendUrl: `https://staging.${slug}.example.com`,\n backendUrl: `https://api-staging.${slug}.example.com`,\n backendAuthUrl: `https://api-staging.${slug}.example.com/auth`,\n },\n },\n tunnel: {\n props: {\n frontendUrl: `https://localhost:${fe}`,\n backendUrl: `https://${slug}.ngrok.dev`,\n backendAuthUrl: `https://${slug}.ngrok.dev/auth`,\n },\n },\n test: {\n imports: \"import development from './config.development';\\n\",\n props: {\n domain: '',\n frontendUrl: '=development.frontendUrl',\n backendUrl: '=development.backendUrl',\n backendAuthUrl: '=development.backendAuthUrl',\n },\n },\n production: { props: { maintenance: false } },\n };\n\n // Serialize value: '=' prefix → raw TS expression, boolean → literal, string → quoted\n const lit = (v: string | boolean) => {\n if (typeof v === 'boolean') return String(v);\n if (v.startsWith('=')) return v.slice(1);\n return `'${v}'`;\n };\n\n const result: Record<string, string> = {};\n\n for (const [mode, { imports = '', props }] of Object.entries(envs)) {\n const nameEntry = mode !== 'production' ? ` name: '${name} ${mode.toUpperCase()}',\\n` : '';\n const body = Object.entries(props)\n .map(([k, v]) => ` ${k}: ${lit(v)},`)\n .join('\\n');\n result[`./shared/config/config.${mode}.ts`] =\n `${imports}${header}\\nexport default {\\n mode: '${mode}',\\n${nameEntry}${body}\\n} satisfies DeepPartial<typeof _default>;\\n`;\n }\n\n return result;\n}\n","{\n \"name\": \"@cellajs/create-cella\",\n \"version\": \"0.2.2\",\n \"private\": false,\n \"license\": \"MIT\",\n \"description\": \"Cella is a TypeScript template to create web apps with sync and offline capabilities.\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/cellajs/cella\",\n \"directory\": \"cli/create-cella\"\n },\n \"homepage\": \"https://cellajs.com\",\n \"author\": \"CellaJS <info@cellajs.com>\",\n \"engines\": {\n \"node\": \"24.x\"\n },\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"configs\",\n \"dist\",\n \"index.js\",\n \"README.md\"\n ],\n \"imports\": {\n \"#/*\": \"./src/*\"\n },\n \"bin\": {\n \"create-cella\": \"index.js\"\n },\n \"scripts\": {\n \"start\": \"tsx ./src/create-cella-cli.ts\",\n \"clean\": \"rimraf ./dist\",\n \"build\": \"tsup\",\n \"test-build\": \"pnpm run build && node index.js\",\n \"prepublishOnly\": \"pnpm run test:release\",\n \"check\": \"pnpm ts && pnpm biome check --write .\",\n \"ts\": \"tsgo --pretty\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"test\": \"vitest run\",\n \"test:release\": \"pnpm run build && vitest run tests/release-artifact.test.ts tests/release-smoke.test.ts\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@inquirer/prompts\": \"^8.5.2\",\n \"axios\": \"^1.17.0\",\n \"commander\": \"^15.0.0\",\n \"giget\": \"^3.1.2\",\n \"nano-spawn\": \"^2.0.0\",\n \"ora\": \"^9.3.0\",\n \"picocolors\": \"^1.1.1\",\n \"validate-npm-package-name\": \"^8.0.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"catalog:\",\n \"@typescript/native-preview\": \"catalog:\",\n \"@types/validate-npm-package-name\": \"^4.0.2\",\n \"tsup\": \"catalog:\",\n \"tsx\": \"catalog:\",\n \"vitest\": \"catalog:\"\n }\n}\n","import { existsSync } from 'node:fs';\nimport { cp, mkdir } from 'node:fs/promises';\nimport { join, relative, resolve } from 'node:path';\nimport { downloadTemplate } from 'giget';\nimport { addRemote } from '#/add-remote';\nimport { TEMPLATE_URL } from '#/constants';\nimport { type CreateOptions, showSuccess } from '#/modules/cli';\nimport { cleanTemplate } from '#/utils/clean-template';\nimport { gitAddAll, gitBranch, gitCheckout, gitCommit, gitInit } from '#/utils/git';\nimport { createProgress } from '#/utils/progress';\nimport { generate, install } from '#/utils/run-package-manager-command';\n\n/** Check if a path is a local directory */\nfunction isLocalPath(path: string): boolean {\n return path.startsWith('/') || path.startsWith('./') || path.startsWith('../');\n}\n\nfunction shouldSkipStep(name: 'install' | 'generate' | 'git' | 'remote'): boolean {\n return process.env[`CREATE_CELLA_SKIP_${name.toUpperCase()}`] === 'true';\n}\n\nexport async function create({\n projectName,\n targetFolder,\n newBranchName,\n packageManager,\n templateUrl,\n portOffset,\n adminEmail,\n silent = false,\n}: CreateOptions): Promise<void> {\n // Save the original working directory\n const originalCwd = process.cwd();\n\n // Use custom template or default\n const template = templateUrl || TEMPLATE_URL;\n const isLocalTemplate = templateUrl && isLocalPath(templateUrl);\n\n const progress = createProgress('creating project', silent);\n\n try {\n // Create the target folder\n progress.step('creating project folder');\n await mkdir(targetFolder, { recursive: true });\n process.chdir(targetFolder);\n\n // Download or copy the template\n if (isLocalTemplate) {\n progress.step('copying local template');\n const sourcePath = resolve(originalCwd, templateUrl);\n\n // Check if target is inside source (would cause EINVAL)\n if (targetFolder.startsWith(sourcePath)) {\n throw new Error(\n 'Cannot create project inside the template directory.\\n' +\n ` Run from outside: cd ~ && pnpm create @cellajs/cella ${projectName} --template ${templateUrl}`,\n );\n }\n\n await cp(sourcePath, targetFolder, {\n recursive: true,\n filter: (src) => !src.includes('node_modules') && !src.includes('.git'),\n });\n } else {\n progress.step('downloading cella template');\n await downloadTemplate(template, {\n cwd: process.cwd(),\n dir: targetFolder,\n force: true,\n provider: 'github',\n });\n }\n\n // Clean the template, generate configs\n progress.step('cleaning template');\n const displayName = projectName.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n await cleanTemplate({ targetFolder, projectName, displayName, portOffset, adminEmail });\n\n // Install dependencies\n if (!shouldSkipStep('install')) {\n progress.step('installing dependencies');\n await install(packageManager);\n }\n\n // Generate SQL files\n if (!shouldSkipStep('generate')) {\n progress.step('generating migrations');\n await generate(packageManager);\n }\n\n // Initialize git repository\n const gitFolderPath = join(targetFolder, '.git');\n if (!shouldSkipStep('git') && !existsSync(gitFolderPath)) {\n progress.step('initializing git');\n await gitInit(targetFolder);\n await gitAddAll(targetFolder);\n await gitCommit(targetFolder, 'Initial commit');\n\n if (newBranchName) {\n progress.step(`creating branch '${newBranchName}'`);\n await gitBranch(targetFolder, newBranchName);\n await gitCheckout(targetFolder, newBranchName);\n }\n }\n\n // Add upstream remote\n if (!shouldSkipStep('remote')) {\n progress.step('adding upstream remote');\n await addRemote({ targetFolder, silent: true });\n }\n\n // Done\n progress.done(`created ${projectName}`);\n } catch (error) {\n progress.fail(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n\n // Check if the working directory needs to be changed\n const needsCd = originalCwd !== targetFolder;\n const relativePath = relative(originalCwd, targetFolder);\n\n // Display final success message\n showSuccess(projectName, targetFolder, relativePath, needsCd, packageManager);\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Executes a Git command in a specific repository path and returns the trimmed stdout.\n * Uses execFile for safety (no shell injection) aligned with sync package pattern.\n *\n * @param args - The Git command arguments to execute (e.g., ['status'], ['checkout', 'branch-name']).\n * @param repoPath - The path to the Git repository.\n * @param options - Optional settings for command execution.\n * @returns The stdout of the Git command, trimmed of leading and trailing whitespace.\n */\nexport async function runGitCommand(\n args: string[],\n repoPath: string,\n options: { skipEditor?: boolean; maxBuffer?: number } = {},\n): Promise<string> {\n const gitArgs = repoPath ? ['-C', repoPath, ...args] : args;\n\n const env = {\n ...process.env,\n ...(options.skipEditor ? { GIT_EDITOR: 'true' } : {}),\n };\n\n // Default to 10MB buffer for typical outputs\n const maxBuffer = options.maxBuffer ?? 10 * 1024 * 1024;\n\n const { stdout } = await execFileAsync('git', gitArgs, { env, maxBuffer });\n\n return stdout.trim();\n}\n\n/**\n * Initializes a new Git repository in the specified directory.\n */\nexport async function gitInit(repoPath: string): Promise<string> {\n return runGitCommand(['init'], repoPath);\n}\n\n/**\n * Stages all files in the repository.\n */\nexport async function gitAddAll(repoPath: string): Promise<string> {\n return runGitCommand(['add', '.'], repoPath);\n}\n\n/**\n * Creates a commit with the specified message.\n */\nexport async function gitCommit(repoPath: string, message: string): Promise<string> {\n return runGitCommand(['commit', '--quiet', '--no-verify', '-m', message], repoPath);\n}\n\n/**\n * Creates a new branch with the specified name.\n */\nexport async function gitBranch(repoPath: string, branchName: string): Promise<string> {\n return runGitCommand(['branch', branchName], repoPath);\n}\n\n/**\n * Checks out the specified branch.\n */\nexport async function gitCheckout(repoPath: string, branchName: string): Promise<string> {\n return runGitCommand(['checkout', branchName], repoPath);\n}\n\n/**\n * Gets the URL of a remote.\n */\nexport async function gitRemoteGetUrl(repoPath: string, remoteName: string): Promise<string> {\n return runGitCommand(['remote', 'get-url', remoteName], repoPath);\n}\n\n/**\n * Adds a new remote.\n */\nexport async function gitRemoteAdd(repoPath: string, remoteName: string, remoteUrl: string): Promise<string> {\n return runGitCommand(['remote', 'add', remoteName, remoteUrl], repoPath);\n}\n\n/**\n * Removes a remote.\n */\nexport async function gitRemoteRemove(repoPath: string, remoteName: string): Promise<string> {\n return runGitCommand(['remote', 'remove', remoteName], repoPath);\n}\n","import { CELLA_REMOTE_URL } from '#/constants';\nimport type { AddRemoteOptions } from '#/modules/cli';\nimport { gitRemoteAdd, gitRemoteGetUrl, gitRemoteRemove } from '#/utils/git';\n\n/**\n * Adds or updates the upstream remote for the Cella template.\n * @param options - Configuration options\n * @param options.silent - If true, don't throw on failure (used when called from progress tracker)\n */\nexport async function addRemote({\n targetFolder,\n remoteUrl = CELLA_REMOTE_URL,\n remoteName = 'upstream',\n silent = false,\n}: AddRemoteOptions): Promise<void> {\n try {\n // Check if the remote exists\n let remote: string | null = null;\n\n try {\n remote = await gitRemoteGetUrl(targetFolder, remoteName);\n } catch {\n // If the remote doesn't exist, it throws a fatal error\n remote = null;\n }\n\n // Add or update the remote if it doesn't exist or differs from `remoteUrl`\n if (!remote) {\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n } else if (remote !== remoteUrl) {\n // Remove existing remote and set the new URL\n await gitRemoteRemove(targetFolder, remoteName);\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n }\n } catch (error) {\n if (!silent) {\n throw error;\n }\n }\n}\n","import { basename, resolve } from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { NAME, VERSION } from '#/constants';\nimport { validateProjectName } from '#/utils/validate-project-name';\nimport type { CLIConfig, CLIOptions } from './types';\n\nfunction parsePortOffset(value: string): number {\n const parsed = Number(value);\n\n if (!Number.isInteger(parsed) || parsed < 0 || parsed > 490) {\n throw new InvalidArgumentError('Port offset must be an integer between 0 and 490');\n }\n\n if (parsed % 10 !== 0) {\n throw new InvalidArgumentError('Port offset must be a multiple of 10');\n }\n\n return parsed;\n}\n\n// Initialize CLI variables\nlet directory: string | null = null;\nconst newBranchName: string | null = null;\nconst packageManager = 'pnpm';\n\n/**\n * Defines the root CLI command using Commander.\n * This command accepts CLI options and validates user input.\n */\nexport const command = new Command(NAME)\n .version(VERSION, '-v, --version', `output the current version of ${NAME}`)\n .argument('[directory]', 'the directory name for the new project')\n .usage('[directory] [options]')\n .helpOption('-h, --help', 'display this help message')\n .option('--template <path>', 'use a custom template (local path or github:user/repo)')\n .option('--port-offset <number>', 'set the port offset (0-490 in steps of 10)', parsePortOffset)\n .option('--admin-email <email>', 'set the admin email for the initial seed user')\n .action((name: string) => {\n const trimmedName = typeof name === 'string' ? name.trim() : name;\n\n if (trimmedName) {\n const validation = validateProjectName(basename(resolve(trimmedName)));\n\n if (!validation.valid) {\n throw new InvalidArgumentError(`Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`);\n }\n\n directory = trimmedName;\n }\n })\n .parse();\n\n// Gather the CLI options and arguments\nconst options: CLIOptions = command.opts<CLIOptions>();\n\n/**\n * Runs the CLI and returns the parsed configuration.\n * This function parses command line arguments and returns the CLI config.\n */\nexport function runCli(): CLIConfig {\n return {\n options,\n args: command.args,\n directory,\n newBranchName,\n packageManager,\n };\n}\n\n// Export CLI configuration for direct import\nexport const cli: CLIConfig = runCli();\n","import validate from 'validate-npm-package-name';\n\ninterface ValidationResult {\n valid: boolean;\n problems?: string[];\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const nameValidation = validate(name);\n\n if (nameValidation.validForNewPackages) {\n return { valid: true };\n }\n\n return {\n valid: false,\n problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])],\n };\n}\n","import pc from 'picocolors';\nimport { DESCRIPTION, DIVIDER, getHeaderLine } from '#/constants';\n\n/** ASCII art logo for the CLI welcome screen. */\nfunction showAscii(): void {\n console.info(pc.cyan(' _ _ '));\n console.info(pc.cyan('▒▓█████▓▒ ___ ___| | | __ _ '));\n console.info(pc.cyan('▒▓█ █▓▒ / __/ _ \\\\ | |/ _` | '));\n console.info(pc.cyan('▒▓█ █▓▒ | (_| __/ | | (_| | '));\n console.info(pc.cyan('▒▓█████▓▒ \\\\___\\\\___|_|_|\\\\__,_| '));\n}\n\n/**\n * Displays the compact CLI welcome header.\n * @param templateVersion - The version of the cella template being used\n */\nexport function showWelcome(templateVersion: string): void {\n console.info();\n showAscii();\n console.info();\n console.info(pc.dim(DESCRIPTION));\n console.info();\n console.info(getHeaderLine(templateVersion));\n console.info(DIVIDER);\n}\n\n/**\n * Displays the final success message after project creation.\n */\nexport function showSuccess(\n projectName: string,\n _targetFolder: string,\n relativePath: string,\n needsCd: boolean,\n packageManager: string,\n): void {\n console.info(DIVIDER);\n console.info();\n\n // Navigation instruction\n if (needsCd) {\n console.info(`${pc.green('→')} cd ${pc.cyan(relativePath)}`);\n console.info();\n }\n\n // Quick start options\n console.info(`${pc.green('→')} ${pc.cyan(`${packageManager} quick`)} ${pc.gray('(pglite, no docker)')}`);\n console.info();\n console.info(pc.gray('or, for full setup:'));\n console.info();\n console.info(\n `${pc.green('→')} ${pc.cyan(`${packageManager} docker`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} seed`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} dev`)}`,\n );\n console.info();\n\n // Credentials\n console.info(`sign in: ${pc.gray('admin-test@cellajs.com / 12345678')}`);\n console.info();\n console.info(`enjoy building ${pc.green(projectName)} with cella!`);\n console.info();\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport pc from 'picocolors';\nimport {\n generateEnvConfigs,\n generateEnvFromExample,\n getBackendEnvReplacements,\n PLACEHOLDER_CONFIG,\n TO_CLEAN,\n TO_COPY,\n TO_REMOVE,\n} from '#/constants';\n\nconst warningMark = pc.yellow('⚠');\n\n/**\n * Resolve the placeholder config template that ships inside the create-cella package.\n *\n * The template is the CLI's own asset (listed in package.json `files`), NOT part of the\n * downloaded fork. It must therefore be read relative to this module, which lives at:\n * - dev (tsx): `<pkg>/src/utils/clean-template.ts` → template at `../../<PLACEHOLDER_CONFIG>`\n * - bundled: `<pkg>/dist/index.js` → template at `../<PLACEHOLDER_CONFIG>`\n */\nasync function resolvePlaceholderConfigPath(): Promise<string> {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [path.resolve(here, '../..', PLACEHOLDER_CONFIG), path.resolve(here, '..', PLACEHOLDER_CONFIG)];\n for (const candidate of candidates) {\n try {\n await fs.access(candidate);\n return candidate;\n } catch {\n // try next candidate\n }\n }\n // Fall back to the first candidate so the caller surfaces a clear ENOENT.\n return candidates[0];\n}\n\n/**\n * Cleans the specified template by removing designated folders and files.\n * @param params - Parameters containing the target folder, project name, and optional extra edits.\n */\nexport async function cleanTemplate({\n targetFolder,\n projectName,\n displayName,\n portOffset = 0,\n adminEmail = `admin@${projectName}.example.com`,\n}: {\n targetFolder: string;\n projectName: string;\n displayName: string;\n portOffset?: number;\n adminEmail?: string;\n}): Promise<void> {\n // Change the current working directory to targetFolder if not already set\n if (process.cwd() !== targetFolder) {\n process.chdir(targetFolder);\n }\n\n return new Promise<void>((resolve, reject) => {\n (async () => {\n try {\n // Copy specified files\n for (const [src, dest] of Object.entries(TO_COPY)) {\n const srcAbsolutePath = path.resolve(targetFolder, src);\n const destAbsolutePath = path.resolve(targetFolder, dest);\n await copyFile(srcAbsolutePath, destAbsolutePath);\n }\n\n // Replace config.default.ts with interpolated placeholder config\n await applyPlaceholderConfig(targetFolder, projectName);\n\n // Generate backend .env from backend/.env.example.\n // The backend .env is the single source of truth for the project slug and DB ports\n // (consumed by backend/compose.yaml). There is no root .env.\n const backendReplacements = getBackendEnvReplacements(projectName, adminEmail, portOffset);\n const backendEnv = await generateEnvFromExample(\n path.resolve(targetFolder, 'backend/.env.example'),\n backendReplacements,\n );\n if (backendEnv) {\n await fs.writeFile(path.resolve(targetFolder, 'backend/.env'), backendEnv, 'utf8');\n }\n\n // Generate minimal env config files with project values and ports baked in\n const envConfigs = generateEnvConfigs(projectName, displayName, portOffset);\n await Promise.all(\n Object.entries(envConfigs).map(([filePath, content]) =>\n fs.writeFile(path.resolve(targetFolder, filePath), content, 'utf8'),\n ),\n );\n\n // Clean specified folder contents\n await Promise.all(\n TO_CLEAN.map((folderPath) => {\n const absolutePath = path.resolve(targetFolder, folderPath);\n return removeFolderContents(absolutePath);\n }),\n );\n\n // Remove specified files and folders\n await Promise.all(\n TO_REMOVE.map((filePath) => {\n const absolutePath = path.resolve(targetFolder, filePath);\n return removeFileOrFolder(absolutePath);\n }),\n );\n\n resolve();\n } catch (err) {\n reject(`Error during the cleaning process: ${err}`);\n }\n })();\n });\n}\n\n/**\n * Removes all contents within a specified folder.\n * @param folderPath - The path of the folder to clean.\n */\nexport async function removeFolderContents(folderPath: string): Promise<void> {\n // List all files in the folder\n const files = await fs.readdir(folderPath);\n\n await Promise.all(\n files.map(async (file) => {\n const filePath = path.join(folderPath, file);\n\n // Get the file or folder statistics\n const stat = await fs.lstat(filePath);\n\n // If it's a directory, remove it and all its contents\n if (stat.isDirectory()) {\n await fs.rm(filePath, { recursive: true, force: true });\n } else {\n // If it's a file, remove it\n await fs.rm(filePath);\n }\n }),\n );\n}\n\n/**\n * Removes a specified file or folder.\n * @param pathToRemove - The path to the file or folder to remove.\n */\nexport async function removeFileOrFolder(pathToRemove: string): Promise<void> {\n await fs.rm(pathToRemove, { recursive: true, force: true });\n}\n\n/**\n * Helper function to copy files if the source exists.\n * @param src - The source file path.\n * @param dest - The destination file path.\n */\nexport async function copyFile(src: string, dest: string): Promise<void> {\n try {\n // Check if the source file exists\n await fs.access(src);\n\n // Ensure the destination directory exists\n await fs.mkdir(path.dirname(dest), { recursive: true });\n\n // Copy the file\n await fs.copyFile(src, dest);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Source file \"${src}\" does not exist > Skip copy`);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Read the placeholder config template, interpolate project tokens, and\n * write it as `shared/config/config.default.ts` — replacing the original.\n */\nasync function applyPlaceholderConfig(targetFolder: string, projectName: string): Promise<void> {\n const displayName = projectName.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n\n const src = await resolvePlaceholderConfigPath();\n const dest = path.resolve(targetFolder, './shared/config/config.default.ts');\n\n try {\n let content = await fs.readFile(src, 'utf8');\n content = content.replaceAll('__project_name__', displayName);\n content = content.replaceAll('__project_slug__', projectName);\n await fs.writeFile(dest, content, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Placeholder config \"${src}\" not found > Skip`);\n } else {\n throw err;\n }\n }\n}\n","/**\n * Progress Tracker Utility\n *\n * Provides compact progress tracking for CLI operations.\n * Uses a single spinner that updates in place.\n */\n\nimport ora, { type Ora } from 'ora';\nimport pc from 'picocolors';\n\n/** Global reference to the currently active spinner */\nlet activeSpinner: Ora | null = null;\n\n/** Pause the active spinner (for interactive prompts) */\nexport function pauseSpinner(): void {\n if (activeSpinner) {\n activeSpinner.stop();\n }\n}\n\n/** Resume the active spinner after a prompt */\nexport function resumeSpinner(): void {\n if (activeSpinner) {\n activeSpinner.start();\n }\n}\n\nexport interface ProgressTracker {\n /** Update the current step (shown as spinner text) */\n step: (message: string) => void;\n /** Mark the tracker as complete with final message */\n done: (message: string) => void;\n /** Mark the tracker as failed with error message */\n fail: (message: string) => void;\n /** Wrap an async operation to auto-fail on error */\n wrap: <T>(fn: () => Promise<T>) => Promise<T>;\n}\n\n/**\n * Creates a progress tracker for a multi-step operation.\n * Shows a single spinner that updates in place.\n *\n * @param title - The initial title for the spinner\n * @returns A ProgressTracker instance\n *\n * @example\n * const progress = createProgress('creating project');\n * progress.step('downloading template');\n * progress.step('installing dependencies');\n * progress.done('project created');\n */\nexport function createProgress(title: string, silent = false): ProgressTracker {\n const completedSteps: string[] = [];\n\n const spinner = ora({\n text: title,\n isSilent: silent,\n });\n\n activeSpinner = spinner;\n spinner.start();\n\n // Helper to log (respects silent mode)\n const log = (msg: string) => {\n if (!silent) console.info(msg);\n };\n\n return {\n step: (message: string) => {\n // Complete previous step with green check if there was one\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n }\n completedSteps.push(message);\n spinner.text = message;\n spinner.start();\n },\n\n done: (message: string) => {\n // Complete the last step with green check\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n } else {\n spinner.stop();\n }\n activeSpinner = null;\n if (message) {\n log('');\n log(`${pc.green('✓')} ${message}`);\n }\n },\n\n fail: (message: string) => {\n spinner.stop();\n activeSpinner = null;\n log(`${pc.red('✗')} ${pc.red(message)}`);\n },\n\n wrap: async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n // Stop spinner and show tree-style breakdown on failure\n spinner.stop();\n activeSpinner = null;\n log(pc.cyan(`\\n${title}`));\n for (const step of completedSteps) {\n log(pc.gray(` ├─ ${step}`));\n }\n log(pc.red(` └─ ✗ ${errorMessage}`));\n throw error;\n }\n },\n };\n}\n","import spawn from 'nano-spawn';\n\n/**\n * Executes a command using the specified package manager (e.g., pnpm).\n *\n * @param packageManager - The package manager to use (e.g., 'pnpm').\n * @param args - The arguments to pass to the package manager command.\n * @param env - Additional environment variables to set during command execution.\n * @returns A promise that resolves if the command executes successfully; otherwise, it rejects with an error message.\n */\nexport async function runPackageManagerCommand(\n packageManager: string,\n args: string[],\n env: Record<string, string> = {},\n): Promise<void> {\n try {\n await spawn(packageManager, args, {\n env: { ...process.env, ...env },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error) {\n const err = error as { stdout?: string; stderr?: string };\n throw new Error(`\"${packageManager} ${args.join(' ')}\" failed ${err.stdout || ''} ${err.stderr || ''}`);\n }\n}\n\n/**\n * Installs dependencies using the specified package manager.\n *\n * @param packageManager - The package manager to use for installation (e.g., 'pnpm').\n * @returns A promise that resolves if the installation completes successfully; otherwise, it rejects with an error.\n */\nexport async function install(packageManager: string): Promise<void> {\n return runPackageManagerCommand(packageManager, ['install'], {\n NODE_ENV: 'development',\n });\n}\n\n/**\n * Generates SQL files using the specified package manager.\n *\n * @param packageManager - The package manager to use for generation (e.g., 'pnpm').\n * @returns A promise that resolves if the generation completes successfully; otherwise, it rejects with an error.\n */\nexport async function generate(packageManager: string): Promise<void> {\n return runPackageManagerCommand(packageManager, ['generate'], {\n NODE_ENV: 'development',\n });\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\ninterface UsedPorts {\n project: string;\n frontend: number;\n backend: number;\n offset: number;\n}\n\n/**\n * Scan sibling directories for existing cella forks and extract their dev ports.\n * Looks for `shared/config/config.development.ts` to identify cella-based projects.\n */\nexport async function detectUsedPorts(targetFolder: string): Promise<UsedPorts[]> {\n const parentDir = dirname(targetFolder);\n const used: UsedPorts[] = [];\n\n let siblings: string[];\n try {\n siblings = await readdir(parentDir);\n } catch {\n return used;\n }\n\n for (const name of siblings) {\n const configPath = join(parentDir, name, 'shared/config/config.development.ts');\n try {\n const content = await readFile(configPath, 'utf8');\n const feMatch = content.match(/frontendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n const beMatch = content.match(/backendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n if (feMatch && beMatch) {\n const frontend = Number(feMatch[1]);\n const backend = Number(beMatch[1]);\n used.push({\n project: name,\n frontend,\n backend,\n offset: frontend - 3000,\n });\n }\n } catch {\n // Not a cella fork, skip\n }\n }\n\n return used;\n}\n\n/** Find the next available offset (in steps of 10) that doesn't collide with existing forks. */\nexport function findNextOffset(usedPorts: UsedPorts[]): number {\n const usedOffsets = new Set(usedPorts.map((p) => p.offset));\n for (let offset = 0; offset <= 490; offset += 10) {\n if (!usedOffsets.has(offset)) return offset;\n }\n return 0;\n}\n","import axios from 'axios';\n\n/**\n * Retrieves the version from the package.json file of a GitHub repository.\n * If the package.json file is not found or an error occurs, it returns 'unknown'.\n *\n * @param repositoryUrl {string} - The GitHub repository URL in the format 'github:user/repo'.\n * @param branch {string} - The branch name (defaults to 'main').\n * @returns {Promise<string>} - The version from the package.json file.\n */\nexport async function extractPackageJsonVersionFromUri(repositoryUrl: string, branch = 'main'): Promise<string> {\n // Extract owner and repo from the URL\n const [owner, repo] = repositoryUrl.replace('github:', '').split('/');\n\n // Construct the URL for the package.json file in the provided branch\n const packageJsonUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/package.json`;\n\n try {\n // Fetch the package.json file\n const response = await axios.get(packageJsonUrl);\n const packageJson = response.data;\n\n // Return the version from the package.json, or 'unknown' if not found\n return packageJson.version || 'unknown';\n } catch (error) {\n // If there's an error (file not found, etc.), return 'unknown'\n return 'unknown';\n }\n}\n","import { readdir } from 'node:fs/promises';\n\n/**\n * Checks if a directory is empty or only contains a .git directory.\n *\n * @param path - The path of the directory to check.\n * @returns Resolves to true if the directory is empty or contains only a .git folder, false otherwise.\n * @throws Throws an error if the path is not a directory or if there's an issue reading the directory.\n */\nexport async function isEmptyDirectory(path: string): Promise<boolean> {\n const files = await readdir(path);\n\n // Check if directory is empty or contains only the .git directory\n return files.length === 0 || (files.length === 1 && files[0] === '.git');\n}\n"],"mappings":"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","names":["existsSync","basename","resolve","pc","readFile","resolve","options","pc","pc","packageManager","pc","pc","resolve","pc","pc","packageManager","path","newBranchName","packageManager","resolve","join","readdir","path","basename","resolve","existsSync","pc"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cellajs/create-cella",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "Cella is a TypeScript template to create web apps with sync and offline capabilities.",
@@ -42,7 +42,7 @@
42
42
  "lint": "biome check .",
43
43
  "lint:fix": "biome check --write .",
44
44
  "test": "vitest run",
45
- "test:release": "pnpm run build && vitest run tests/release-artifact.test.ts",
45
+ "test:release": "pnpm run build && vitest run tests/release-artifact.test.ts tests/release-smoke.test.ts",
46
46
  "test:watch": "vitest"
47
47
  },
48
48
  "dependencies": {