create-ts-saas 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -17,7 +17,7 @@ import { pipeline } from "node:stream/promises";
17
17
  import unzipper from "unzipper";
18
18
  //#region src/lib/version.ts
19
19
  function getCliVersion() {
20
- return "0.1.2";
20
+ return "0.1.4";
21
21
  }
22
22
  function getCliPlatform() {
23
23
  return `${process.platform}-${process.arch}`;
@@ -36,13 +36,15 @@ var ApiError = class extends Error {
36
36
  };
37
37
  function parseApiErrorBody(text) {
38
38
  try {
39
- const data = JSON.parse(text);
39
+ const raw = JSON.parse(text);
40
+ if (raw === null || typeof raw !== "object") return null;
41
+ const data = raw;
40
42
  if (typeof data.code === "string" && typeof data.message === "string") return {
41
43
  code: data.code,
42
44
  message: data.message,
43
45
  retryable: Boolean(data.retryable)
44
46
  };
45
- const nested = data.error;
47
+ const nested = typeof data.error === "object" && data.error !== null ? data.error : void 0;
46
48
  if (nested && typeof nested.code === "string" && typeof nested.message === "string") return {
47
49
  code: nested.code,
48
50
  message: nested.message,
@@ -81,7 +83,9 @@ function createApiClient(options) {
81
83
  throw new ApiError(body?.code ?? (res.status === 401 ? "AUTH_INVALID" : "REQUEST_FAILED"), body?.message ?? res.statusText, body?.retryable ?? false, res.status, minCli ?? void 0);
82
84
  }
83
85
  if (!text) return {};
84
- return JSON.parse(text);
86
+ const json = JSON.parse(text);
87
+ if (json !== null && typeof json === "object" && "success" in json && "data" in json && json.success === true) return json.data;
88
+ return json;
85
89
  }
86
90
  async function stream(url) {
87
91
  const headers = new Headers();
@@ -136,8 +140,8 @@ function createApiClient(options) {
136
140
  };
137
141
  }
138
142
  //#endregion
139
- //#region src/constants.ts
140
- const DEFAULT_API_BASE_URL = "https://api.ts-saas.com/api/v1";
143
+ //#region src/constant.ts
144
+ const DEFAULT_API_BASE_URL = "http://localhost:8787/api/v1";
141
145
  const CONFIG_DIR_NAME = "ts-saas-cli";
142
146
  const CONFIG_FILE_NAME = "config.json";
143
147
  const KEYTAR_SERVICE_NAME = "create-ts-saas";
@@ -162,13 +166,13 @@ async function loadConfig() {
162
166
  if (await fs.pathExists(configPath)) {
163
167
  const data = await fs.readJson(configPath);
164
168
  return {
165
- apiBaseUrl: data.apiBaseUrl ?? fromEnv.apiBaseUrl ?? "https://api.ts-saas.com/api/v1",
169
+ apiBaseUrl: data.apiBaseUrl ?? fromEnv.apiBaseUrl ?? "http://localhost:8787/api/v1",
166
170
  disableTelemetry: data.disableTelemetry ?? fromEnv.disableTelemetry ?? false
167
171
  };
168
172
  }
169
173
  } catch {}
170
174
  return {
171
- apiBaseUrl: fromEnv.apiBaseUrl ?? "https://api.ts-saas.com/api/v1",
175
+ apiBaseUrl: fromEnv.apiBaseUrl ?? "http://localhost:8787/api/v1",
172
176
  disableTelemetry: fromEnv.disableTelemetry ?? false
173
177
  };
174
178
  }
@@ -366,8 +370,9 @@ async function loginWithDeviceFlow(api) {
366
370
  await setStoredSession(session);
367
371
  return session;
368
372
  } catch (error) {
369
- const e = error;
370
- if (e.code === "AUTH_PENDING" || e.retryable) continue;
373
+ const code = error !== null && typeof error === "object" && "code" in error ? error.code : void 0;
374
+ const retryable = error !== null && typeof error === "object" && "retryable" in error ? error.retryable : false;
375
+ if (code === "AUTH_PENDING" || retryable) continue;
371
376
  throw error;
372
377
  }
373
378
  }
@@ -464,7 +469,7 @@ async function promptTemplate(templates) {
464
469
  message: "Select template",
465
470
  options: templates.map((t) => ({
466
471
  value: t.slug,
467
- label: `${t.name} (${t.slug})`,
472
+ label: t.slug,
468
473
  hint: `latest ${t.latestVersion}`
469
474
  }))
470
475
  });
@@ -474,23 +479,6 @@ async function promptTemplate(templates) {
474
479
  return chosen;
475
480
  }
476
481
  //#endregion
477
- //#region src/prompts/variant.ts
478
- async function promptVariant(template) {
479
- const first = template.variants[0];
480
- if (!first) throw new Error(`Template "${template.slug}" has no variants.`);
481
- const value = await select({
482
- message: "Select variant",
483
- options: template.variants.map((v) => ({
484
- value: v.slug,
485
- label: v.name,
486
- hint: v.slug
487
- })),
488
- initialValue: first.slug
489
- });
490
- if (isCancel(value) || typeof value !== "string") throw new Error("Operation cancelled.");
491
- return value;
492
- }
493
- //#endregion
494
482
  //#region src/utils/get-package-manager.ts
495
483
  function getUserPackageManager() {
496
484
  const userAgent = process.env.npm_config_user_agent;
@@ -505,7 +493,7 @@ function isNetworkError(error) {
505
493
  if (!(error instanceof Error)) return false;
506
494
  const cause = error.cause;
507
495
  if (cause instanceof Error) {
508
- const code = cause.code;
496
+ const code = "code" in cause ? cause.code : void 0;
509
497
  if (code === "ECONNREFUSED" || code === "ENOTFOUND" || code === "ETIMEDOUT" || code === "ECONNRESET") return true;
510
498
  }
511
499
  return error.message === "fetch failed";
@@ -523,11 +511,11 @@ function handleCliError(error) {
523
511
  }
524
512
  if (error instanceof Error) {
525
513
  log.error(pc.red(error.message || "Something went wrong"));
526
- if (DEBUG_FLAG) console.error(error);
514
+ console.error(error);
527
515
  process.exit(1);
528
516
  }
529
- log.error(pc.red("Something went wrong"));
530
- if (DEBUG_FLAG) console.error(error);
517
+ console.error("Unhandled non-Error:", error);
518
+ log.error(pc.red(typeof error === "string" ? error : "Something went wrong"));
531
519
  process.exit(1);
532
520
  }
533
521
  function getExitCode(error) {
@@ -607,14 +595,10 @@ async function initCommand(options) {
607
595
  log.error(pc.red("Selected template not found."));
608
596
  process.exit(1);
609
597
  }
610
- const selectedVariant = options.variant && selectedTemplate.variants.some((variant) => variant.slug === options.variant) ? options.variant : await promptVariant(selectedTemplate);
611
598
  const s = spinner();
612
599
  s.start("Authorizing template download...");
613
600
  try {
614
- const authz = await api.authorizeInit({
615
- templateSlug: selectedTemplate.slug,
616
- variantSlug: selectedVariant
617
- });
601
+ const authz = await api.authorizeInit({ templateSlug: selectedTemplate.slug });
618
602
  s.message("Downloading template...");
619
603
  await streamAndExtractTemplate(await api.stream(authz.downloadUrl), projectDir);
620
604
  await writeWatermark(projectDir, authz.watermark);
@@ -724,7 +708,6 @@ const router = os.router({
724
708
  dir: z.string().optional(),
725
709
  yes: z.boolean().optional().default(false),
726
710
  template: z.string().optional(),
727
- variant: z.string().optional(),
728
711
  packageManager: z.enum([
729
712
  "npm",
730
713
  "pnpm",
@@ -743,7 +726,6 @@ const router = os.router({
743
726
  dir: z.string().optional(),
744
727
  yes: z.boolean().optional().default(false),
745
728
  template: z.string().optional(),
746
- variant: z.string().optional(),
747
729
  packageManager: z.enum([
748
730
  "npm",
749
731
  "pnpm",
package/dist/index.d.mts CHANGED
@@ -7,7 +7,6 @@ declare const router: {
7
7
  dir: z.ZodOptional<z.ZodString>;
8
8
  yes: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
9
9
  template: z.ZodOptional<z.ZodString>;
10
- variant: z.ZodOptional<z.ZodString>;
11
10
  packageManager: z.ZodOptional<z.ZodEnum<{
12
11
  npm: "npm";
13
12
  pnpm: "pnpm";
@@ -20,7 +19,6 @@ declare const router: {
20
19
  dir: z.ZodOptional<z.ZodString>;
21
20
  yes: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
22
21
  template: z.ZodOptional<z.ZodString>;
23
- variant: z.ZodOptional<z.ZodString>;
24
22
  packageManager: z.ZodOptional<z.ZodEnum<{
25
23
  npm: "npm";
26
24
  pnpm: "pnpm";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;cAaa,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4ES,MAAA,CAAA,GAAM,OAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;cAaa,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4ES,MAAA,CAAA,GAAM,OAAA"}
package/dist/index.mjs CHANGED
@@ -16,7 +16,7 @@ import { pipeline } from "node:stream/promises";
16
16
  import unzipper from "unzipper";
17
17
  //#region src/lib/version.ts
18
18
  function getCliVersion() {
19
- return "0.1.2";
19
+ return "0.1.4";
20
20
  }
21
21
  function getCliPlatform() {
22
22
  return `${process.platform}-${process.arch}`;
@@ -35,13 +35,15 @@ var ApiError = class extends Error {
35
35
  };
36
36
  function parseApiErrorBody(text) {
37
37
  try {
38
- const data = JSON.parse(text);
38
+ const raw = JSON.parse(text);
39
+ if (raw === null || typeof raw !== "object") return null;
40
+ const data = raw;
39
41
  if (typeof data.code === "string" && typeof data.message === "string") return {
40
42
  code: data.code,
41
43
  message: data.message,
42
44
  retryable: Boolean(data.retryable)
43
45
  };
44
- const nested = data.error;
46
+ const nested = typeof data.error === "object" && data.error !== null ? data.error : void 0;
45
47
  if (nested && typeof nested.code === "string" && typeof nested.message === "string") return {
46
48
  code: nested.code,
47
49
  message: nested.message,
@@ -80,7 +82,9 @@ function createApiClient(options) {
80
82
  throw new ApiError(body?.code ?? (res.status === 401 ? "AUTH_INVALID" : "REQUEST_FAILED"), body?.message ?? res.statusText, body?.retryable ?? false, res.status, minCli ?? void 0);
81
83
  }
82
84
  if (!text) return {};
83
- return JSON.parse(text);
85
+ const json = JSON.parse(text);
86
+ if (json !== null && typeof json === "object" && "success" in json && "data" in json && json.success === true) return json.data;
87
+ return json;
84
88
  }
85
89
  async function stream(url) {
86
90
  const headers = new Headers();
@@ -135,8 +139,8 @@ function createApiClient(options) {
135
139
  };
136
140
  }
137
141
  //#endregion
138
- //#region src/constants.ts
139
- const DEFAULT_API_BASE_URL = "https://api.ts-saas.com/api/v1";
142
+ //#region src/constant.ts
143
+ const DEFAULT_API_BASE_URL = "http://localhost:8787/api/v1";
140
144
  const CONFIG_DIR_NAME = "ts-saas-cli";
141
145
  const CONFIG_FILE_NAME = "config.json";
142
146
  const KEYTAR_SERVICE_NAME = "create-ts-saas";
@@ -161,13 +165,13 @@ async function loadConfig() {
161
165
  if (await fs.pathExists(configPath)) {
162
166
  const data = await fs.readJson(configPath);
163
167
  return {
164
- apiBaseUrl: data.apiBaseUrl ?? fromEnv.apiBaseUrl ?? "https://api.ts-saas.com/api/v1",
168
+ apiBaseUrl: data.apiBaseUrl ?? fromEnv.apiBaseUrl ?? "http://localhost:8787/api/v1",
165
169
  disableTelemetry: data.disableTelemetry ?? fromEnv.disableTelemetry ?? false
166
170
  };
167
171
  }
168
172
  } catch {}
169
173
  return {
170
- apiBaseUrl: fromEnv.apiBaseUrl ?? "https://api.ts-saas.com/api/v1",
174
+ apiBaseUrl: fromEnv.apiBaseUrl ?? "http://localhost:8787/api/v1",
171
175
  disableTelemetry: fromEnv.disableTelemetry ?? false
172
176
  };
173
177
  }
@@ -365,8 +369,9 @@ async function loginWithDeviceFlow(api) {
365
369
  await setStoredSession(session);
366
370
  return session;
367
371
  } catch (error) {
368
- const e = error;
369
- if (e.code === "AUTH_PENDING" || e.retryable) continue;
372
+ const code = error !== null && typeof error === "object" && "code" in error ? error.code : void 0;
373
+ const retryable = error !== null && typeof error === "object" && "retryable" in error ? error.retryable : false;
374
+ if (code === "AUTH_PENDING" || retryable) continue;
370
375
  throw error;
371
376
  }
372
377
  }
@@ -463,7 +468,7 @@ async function promptTemplate(templates) {
463
468
  message: "Select template",
464
469
  options: templates.map((t) => ({
465
470
  value: t.slug,
466
- label: `${t.name} (${t.slug})`,
471
+ label: t.slug,
467
472
  hint: `latest ${t.latestVersion}`
468
473
  }))
469
474
  });
@@ -473,23 +478,6 @@ async function promptTemplate(templates) {
473
478
  return chosen;
474
479
  }
475
480
  //#endregion
476
- //#region src/prompts/variant.ts
477
- async function promptVariant(template) {
478
- const first = template.variants[0];
479
- if (!first) throw new Error(`Template "${template.slug}" has no variants.`);
480
- const value = await select({
481
- message: "Select variant",
482
- options: template.variants.map((v) => ({
483
- value: v.slug,
484
- label: v.name,
485
- hint: v.slug
486
- })),
487
- initialValue: first.slug
488
- });
489
- if (isCancel(value) || typeof value !== "string") throw new Error("Operation cancelled.");
490
- return value;
491
- }
492
- //#endregion
493
481
  //#region src/utils/get-package-manager.ts
494
482
  function getUserPackageManager() {
495
483
  const userAgent = process.env.npm_config_user_agent;
@@ -504,7 +492,7 @@ function isNetworkError(error) {
504
492
  if (!(error instanceof Error)) return false;
505
493
  const cause = error.cause;
506
494
  if (cause instanceof Error) {
507
- const code = cause.code;
495
+ const code = "code" in cause ? cause.code : void 0;
508
496
  if (code === "ECONNREFUSED" || code === "ENOTFOUND" || code === "ETIMEDOUT" || code === "ECONNRESET") return true;
509
497
  }
510
498
  return error.message === "fetch failed";
@@ -522,11 +510,11 @@ function handleCliError(error) {
522
510
  }
523
511
  if (error instanceof Error) {
524
512
  log.error(pc.red(error.message || "Something went wrong"));
525
- if (DEBUG_FLAG) console.error(error);
513
+ console.error(error);
526
514
  process.exit(1);
527
515
  }
528
- log.error(pc.red("Something went wrong"));
529
- if (DEBUG_FLAG) console.error(error);
516
+ console.error("Unhandled non-Error:", error);
517
+ log.error(pc.red(typeof error === "string" ? error : "Something went wrong"));
530
518
  process.exit(1);
531
519
  }
532
520
  function getExitCode(error) {
@@ -606,14 +594,10 @@ async function initCommand(options) {
606
594
  log.error(pc.red("Selected template not found."));
607
595
  process.exit(1);
608
596
  }
609
- const selectedVariant = options.variant && selectedTemplate.variants.some((variant) => variant.slug === options.variant) ? options.variant : await promptVariant(selectedTemplate);
610
597
  const s = spinner();
611
598
  s.start("Authorizing template download...");
612
599
  try {
613
- const authz = await api.authorizeInit({
614
- templateSlug: selectedTemplate.slug,
615
- variantSlug: selectedVariant
616
- });
600
+ const authz = await api.authorizeInit({ templateSlug: selectedTemplate.slug });
617
601
  s.message("Downloading template...");
618
602
  await streamAndExtractTemplate(await api.stream(authz.downloadUrl), projectDir);
619
603
  await writeWatermark(projectDir, authz.watermark);
@@ -723,7 +707,6 @@ const router = os.router({
723
707
  dir: z.string().optional(),
724
708
  yes: z.boolean().optional().default(false),
725
709
  template: z.string().optional(),
726
- variant: z.string().optional(),
727
710
  packageManager: z.enum([
728
711
  "npm",
729
712
  "pnpm",
@@ -742,7 +725,6 @@ const router = os.router({
742
725
  dir: z.string().optional(),
743
726
  yes: z.boolean().optional().default(false),
744
727
  template: z.string().optional(),
745
- variant: z.string().optional(),
746
728
  packageManager: z.enum([
747
729
  "npm",
748
730
  "pnpm",
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["os","CONSTANTS.CONFIG_DIR_NAME","CONSTANTS.CONFIG_FILE_NAME","CONSTANTS.DEFAULT_API_BASE_URL"],"sources":["../src/lib/version.ts","../src/lib/api.ts","../src/constants.ts","../src/lib/config.ts","../src/lib/storage.ts","../src/commands/doctor.ts","../src/utils/open-url.ts","../src/lib/auth.ts","../src/lib/scaffolder.ts","../src/prompts/install.ts","../src/prompts/package-manager.ts","../src/prompts/project-name.ts","../src/prompts/template.ts","../src/prompts/variant.ts","../src/utils/get-package-manager.ts","../src/utils/handle-cli-error.ts","../src/utils/project-directory.ts","../src/commands/init.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/upgrade.ts","../src/commands/whoami.ts","../src/index.ts"],"sourcesContent":["import path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nimport fs from \"fs-extra\"\n\nexport function getCliVersion(): string {\n if (typeof process.env.CLI_VERSION === \"string\" && process.env.CLI_VERSION.length > 0) {\n return process.env.CLI_VERSION\n }\n try {\n const dir = path.dirname(fileURLToPath(import.meta.url))\n const packageJsonPath = path.join(dir, \"..\", \"..\", \"package.json\")\n\n const pkg = fs.readJSONSync(packageJsonPath) as { version?: string }\n return pkg.version ?? \"0.0.0\"\n } catch {\n return \"0.0.0\"\n }\n}\n\nexport function getCliPlatform(): string {\n return `${process.platform}-${process.arch}`\n}\n","import semver from \"semver\"\n\nimport { getCliPlatform, getCliVersion } from \"./version\"\n\nimport type { InitAuthorization, TemplateSummary } from \"../types\"\nimport type { PaidCliConfig } from \"./config\"\n\nexport class ApiError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n public readonly retryable = false,\n public readonly status?: number,\n public readonly minCliVersion?: string,\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\nexport interface ApiClientOptions {\n config: PaidCliConfig\n accessToken?: string | null\n deviceId?: string | null\n}\n\nfunction parseApiErrorBody(text: string): { code: string; message: string; retryable?: boolean } | null {\n try {\n const data = JSON.parse(text) as Record<string, unknown>\n if (typeof data.code === \"string\" && typeof data.message === \"string\") {\n return {\n code: data.code,\n message: data.message,\n retryable: Boolean(data.retryable),\n }\n }\n\n const nested = data.error as Record<string, unknown> | undefined\n if (nested && typeof nested.code === \"string\" && typeof nested.message === \"string\") {\n return {\n code: nested.code,\n message: nested.message,\n retryable: Boolean(data.retryable),\n }\n }\n } catch {\n // noop\n }\n return null\n}\n\nexport function createApiClient(options: ApiClientOptions) {\n const { config, accessToken, deviceId } = options\n const baseUrl = config.apiBaseUrl.replace(/\\/$/, \"\")\n const cliVersion = getCliVersion()\n const cliPlatform = getCliPlatform()\n\n async function request<T>(path: string, init: RequestInit = {}): Promise<T> {\n const url = `${baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`\n const headers = new Headers(init.headers)\n headers.set(\"Accept\", \"application/json\")\n headers.set(\"x-cli-version\", cliVersion)\n headers.set(\"x-cli-platform\", cliPlatform)\n if (deviceId) headers.set(\"x-cli-device-id\", deviceId)\n if (accessToken) headers.set(\"Authorization\", `Bearer ${accessToken}`)\n\n let res: Response\n try {\n res = await fetch(url, { ...init, headers })\n } catch {\n throw new ApiError(\"NETWORK_ERROR\", \"Network error — could not connect to the server.\", true)\n }\n const minCli = res.headers.get(\"x-min-supported-cli\")\n if (minCli && semver.valid(cliVersion) && semver.valid(minCli) && semver.lt(cliVersion, minCli)) {\n throw new ApiError(\n \"UPGRADE_REQUIRED\",\n `CLI version ${cliVersion} is below minimum supported ${minCli}. Please upgrade.`,\n false,\n res.status,\n minCli,\n )\n }\n\n const text = await res.text()\n if (!res.ok) {\n const body = parseApiErrorBody(text)\n throw new ApiError(\n body?.code ?? (res.status === 401 ? \"AUTH_INVALID\" : \"REQUEST_FAILED\"),\n body?.message ?? res.statusText,\n body?.retryable ?? false,\n res.status,\n minCli ?? undefined,\n )\n }\n\n if (!text) return {} as T\n\n return JSON.parse(text) as T\n }\n\n async function stream(url: string): Promise<Response> {\n const headers = new Headers()\n headers.set(\"x-cli-version\", cliVersion)\n headers.set(\"x-cli-platform\", cliPlatform)\n if (deviceId) headers.set(\"x-cli-device-id\", deviceId)\n if (accessToken) headers.set(\"Authorization\", `Bearer ${accessToken}`)\n let res: Response\n try {\n res = await fetch(url, { headers })\n } catch {\n throw new ApiError(\"NETWORK_ERROR\", \"Network error — could not connect to the server.\", true)\n }\n if (!res.ok) {\n const text = await res.text()\n const body = parseApiErrorBody(text)\n throw new ApiError(\n body?.code ?? \"DOWNLOAD_FAILED\",\n body?.message ?? \"Template download failed\",\n false,\n res.status,\n )\n }\n return res\n }\n\n return {\n baseUrl,\n request,\n stream,\n deviceInit: () =>\n request<{\n deviceCode: string\n userCode: string\n verificationUrl: string\n expiresIn: number\n interval: number\n }>(\"/cli/device/init\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({}),\n }),\n devicePoll: (deviceCode: string) =>\n request<{ accessToken: string; refreshToken: string; expiresIn: number; deviceId: string }>(\n \"/cli/device/poll\",\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ deviceCode }),\n },\n ),\n refresh: (refreshToken: string) =>\n request<{ accessToken: string; refreshToken: string; expiresIn: number }>(\"/cli/auth/refresh\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken }),\n }),\n logoutDevice: (refreshToken: string) =>\n request<{ success: boolean }>(\"/cli/device/logout\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken }),\n }),\n me: () =>\n request<{\n email: string\n plan: string\n templates: string[]\n activeDevices: number\n lastInitAt: string | null\n }>(\"/cli/me\"),\n getTemplates: () => request<{ templates: TemplateSummary[] }>(\"/cli/templates\"),\n authorizeInit: (input: { templateSlug: string; variantSlug: string }) =>\n request<InitAuthorization>(\"/cli/init/authorize\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n }),\n getLatestVersion: () => request<{ version: string; minSupportedVersion: string }>(\"/cli/latest-version\"),\n }\n}\n\nexport type ApiClient = ReturnType<typeof createApiClient>\n","export const DEFAULT_API_BASE_URL = \"https://api.ts-saas.com/api/v1\"\nexport const CONFIG_DIR_NAME = \"ts-saas-cli\"\nexport const CONFIG_FILE_NAME = \"config.json\"\nexport const KEYTAR_SERVICE_NAME = \"create-ts-saas\"\nexport const KEYTAR_ACCOUNT_NAME = \"default\"\n","import os from \"node:os\"\nimport path from \"node:path\"\n\nimport fs from \"fs-extra\"\n\nimport * as CONSTANTS from \"../constants\"\n\nexport interface PaidCliConfig {\n apiBaseUrl: string\n disableTelemetry: boolean\n}\n\nfunction getConfigDir() {\n const home = os.homedir()\n const configHome = process.env.XDG_CONFIG_HOME ?? path.join(home, \".config\")\n return path.join(configHome, CONSTANTS.CONFIG_DIR_NAME)\n}\n\nexport function getConfigPath() {\n return path.join(getConfigDir(), CONSTANTS.CONFIG_FILE_NAME)\n}\n\nexport async function loadConfig(): Promise<PaidCliConfig> {\n const fromEnv = {\n apiBaseUrl: CONSTANTS.DEFAULT_API_BASE_URL,\n disableTelemetry:\n process.env.TS_SAAS_CLI_DISABLE_TELEMETRY === \"1\" ||\n process.env.TS_SAAS_CLI_DISABLE_TELEMETRY === \"true\",\n }\n const configPath = getConfigPath()\n try {\n if (await fs.pathExists(configPath)) {\n const data = (await fs.readJson(configPath)) as Partial<PaidCliConfig>\n return {\n apiBaseUrl: data.apiBaseUrl ?? fromEnv.apiBaseUrl ?? CONSTANTS.DEFAULT_API_BASE_URL,\n disableTelemetry: data.disableTelemetry ?? fromEnv.disableTelemetry ?? false,\n }\n }\n } catch {\n // ignore bad config\n }\n return {\n apiBaseUrl: fromEnv.apiBaseUrl ?? CONSTANTS.DEFAULT_API_BASE_URL,\n disableTelemetry: fromEnv.disableTelemetry ?? false,\n }\n}\n\nexport async function saveConfig(config: Partial<PaidCliConfig>): Promise<void> {\n const dir = getConfigDir()\n await fs.ensureDir(dir)\n const next = { ...(await loadConfig()), ...config }\n await fs.writeJson(getConfigPath(), next, { spaces: 2 })\n}\n","import path from \"node:path\"\n\nimport fs from \"fs-extra\"\n\nimport { KEYTAR_ACCOUNT_NAME, KEYTAR_SERVICE_NAME } from \"../constants\"\nimport { getConfigPath } from \"./config\"\n\nimport type { CliSession } from \"../types\"\n\nconst FALLBACK_FILE = \"session.json\"\ntype KeytarModule = {\n getPassword: (service: string, account: string) => Promise<string | null>\n setPassword: (service: string, account: string, password: string) => Promise<void>\n deletePassword: (service: string, account: string) => Promise<boolean>\n}\n\nlet cachedKeytar: KeytarModule | null | undefined\n\nfunction getFallbackSessionPath() {\n return path.join(path.dirname(getConfigPath()), FALLBACK_FILE)\n}\n\nexport async function getStoredSession(): Promise<CliSession | null> {\n const keytar = await getKeytar()\n try {\n const value = keytar ? await keytar.getPassword(KEYTAR_SERVICE_NAME, KEYTAR_ACCOUNT_NAME) : null\n if (!value) return await getFallbackSession()\n\n const parsed = JSON.parse(value) as CliSession\n if (\n typeof parsed.accessToken !== \"string\" ||\n typeof parsed.refreshToken !== \"string\" ||\n typeof parsed.deviceId !== \"string\" ||\n typeof parsed.expiresAt !== \"number\"\n ) {\n return null\n }\n return parsed\n } catch {\n return await getFallbackSession()\n }\n}\n\nexport async function setStoredSession(session: CliSession): Promise<void> {\n const serialized = JSON.stringify(session)\n const keytar = await getKeytar()\n try {\n if (keytar) {\n await keytar.setPassword(KEYTAR_SERVICE_NAME, KEYTAR_ACCOUNT_NAME, serialized)\n return\n }\n await setFallbackSession(session)\n } catch {\n await setFallbackSession(session)\n }\n}\n\nexport async function clearStoredSession(): Promise<void> {\n const keytar = await getKeytar()\n try {\n if (keytar) {\n await keytar.deletePassword(KEYTAR_SERVICE_NAME, KEYTAR_ACCOUNT_NAME)\n }\n } catch {\n // noop\n }\n try {\n await fs.remove(getFallbackSessionPath())\n } catch {\n // noop\n }\n}\n\nasync function getFallbackSession(): Promise<CliSession | null> {\n const filePath = getFallbackSessionPath()\n try {\n if (!(await fs.pathExists(filePath))) return null\n\n const value = (await fs.readJson(filePath)) as CliSession\n if (\n typeof value.accessToken !== \"string\" ||\n typeof value.refreshToken !== \"string\" ||\n typeof value.deviceId !== \"string\" ||\n typeof value.expiresAt !== \"number\"\n ) {\n return null\n }\n return value\n } catch {\n return null\n }\n}\n\nasync function setFallbackSession(session: CliSession): Promise<void> {\n await fs.ensureDir(path.dirname(getFallbackSessionPath()))\n await fs.writeJson(getFallbackSessionPath(), session, { spaces: 0 })\n try {\n await fs.chmod(getFallbackSessionPath(), 0o600)\n } catch {\n // noop\n }\n}\n\nasync function getKeytar(): Promise<KeytarModule | null> {\n if (cachedKeytar !== undefined) return cachedKeytar\n try {\n const imported = (await import(\"keytar\")) as unknown as { default?: KeytarModule } & KeytarModule\n cachedKeytar = imported.default ?? imported\n return cachedKeytar\n } catch {\n cachedKeytar = null\n return null\n }\n}\n","import { consola } from \"consola\"\nimport { $ } from \"execa\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { loadConfig } from \"../lib/config\"\nimport { getStoredSession } from \"../lib/storage\"\nimport { getCliVersion } from \"../lib/version\"\n\nexport async function doctorCommand(): Promise<void> {\n consola.log(pc.bold(\"Environment checks\\n\"))\n const checks: Array<{ name: string; status: \"ok\" | \"fail\"; detail: string }> = []\n\n checks.push({\n name: \"Node.js\",\n status: Number(process.versions.node.split(\".\")[0]) >= 20 ? \"ok\" : \"fail\",\n detail: process.version,\n })\n\n try {\n const result = await $`pnpm --version`\n checks.push({ name: \"pnpm\", status: \"ok\", detail: result.stdout.trim() })\n } catch {\n checks.push({ name: \"pnpm\", status: \"fail\", detail: \"not found\" })\n }\n\n try {\n await $`git --version`\n checks.push({ name: \"git\", status: \"ok\", detail: \"installed\" })\n } catch {\n checks.push({ name: \"git\", status: \"fail\", detail: \"not found\" })\n }\n\n const config = await loadConfig()\n const session = await getStoredSession()\n checks.push({\n name: \"session\",\n status: session ? \"ok\" : \"fail\",\n detail: session ? \"present\" : \"missing\",\n })\n\n try {\n const api = createApiClient({\n config,\n accessToken: session?.accessToken ?? null,\n deviceId: session?.deviceId ?? null,\n })\n await api.getLatestVersion()\n checks.push({ name: \"api\", status: \"ok\", detail: config.apiBaseUrl })\n } catch {\n checks.push({ name: \"api\", status: \"fail\", detail: \"unreachable\" })\n }\n\n for (const check of checks) {\n const icon = check.status === \"ok\" ? pc.green(\"✓\") : pc.red(\"✗\")\n consola.log(`${icon} ${check.name}: ${check.detail}`)\n }\n consola.log(`\\nCLI version: ${getCliVersion()}`)\n}\n","import open from \"open\"\n\nexport async function openUrl(url: string): Promise<void> {\n try {\n await open(url)\n } catch {\n // ignore and let user open manually\n }\n}\n","import { note } from \"@clack/prompts\"\n\nimport { openUrl } from \"../utils/open-url\"\nimport { clearStoredSession, getStoredSession, setStoredSession } from \"./storage\"\n\nimport type { CliSession } from \"../types\"\nimport type { ApiClient } from \"./api\"\n\nexport async function getValidSession(): Promise<CliSession | null> {\n const session = await getStoredSession()\n if (!session) return null\n const bufferMs = 60_000\n if (session.expiresAt <= Date.now() + bufferMs) return null\n return session\n}\n\nexport async function ensureSession(apiFactory: (session?: CliSession) => ApiClient): Promise<CliSession> {\n const valid = await getValidSession()\n if (valid) return valid\n\n const current = await getStoredSession()\n if (current?.refreshToken) {\n try {\n const api = apiFactory(current)\n const refreshed = await api.refresh(current.refreshToken)\n const next: CliSession = {\n accessToken: refreshed.accessToken,\n refreshToken: refreshed.refreshToken,\n deviceId: current.deviceId,\n expiresAt: Date.now() + refreshed.expiresIn * 1000,\n }\n await setStoredSession(next)\n return next\n } catch {\n await clearStoredSession()\n }\n }\n\n return await loginWithDeviceFlow(apiFactory())\n}\n\nexport async function loginWithDeviceFlow(api: ApiClient): Promise<CliSession> {\n const init = await api.deviceInit()\n const verificationUrlWithCode = `${init.verificationUrl}${init.verificationUrl.includes(\"?\") ? \"&\" : \"?\"}code=${encodeURIComponent(init.userCode)}`\n note(\n `Open ${verificationUrlWithCode} and confirm code ${init.userCode}\\nCode expires in ${Math.floor(init.expiresIn / 60)} minutes.`,\n \"Device Login\",\n )\n void openUrl(verificationUrlWithCode)\n\n const deadline = Date.now() + init.expiresIn * 1000\n while (Date.now() < deadline) {\n // eslint-disable-next-line no-await-in-loop\n await new Promise<void>((resolve) => {\n setTimeout(resolve, Math.max(init.interval, 3) * 1000)\n })\n try {\n // eslint-disable-next-line no-await-in-loop\n const token = await api.devicePoll(init.deviceCode)\n const session: CliSession = {\n accessToken: token.accessToken,\n refreshToken: token.refreshToken,\n deviceId: token.deviceId,\n expiresAt: Date.now() + token.expiresIn * 1000,\n }\n // eslint-disable-next-line no-await-in-loop\n await setStoredSession(session)\n return session\n } catch (error) {\n const e = error as { code?: string; retryable?: boolean }\n if (e.code === \"AUTH_PENDING\" || e.retryable) continue\n throw error\n }\n }\n throw new Error(\"Device code expired. Run login again.\")\n}\n\nexport async function logout(apiFactory: (session?: CliSession) => ApiClient): Promise<void> {\n const session = await getStoredSession()\n if (session?.refreshToken) {\n try {\n const api = apiFactory(session)\n await api.logoutDevice(session.refreshToken)\n } catch {\n // ignore remote failure and clear locally\n }\n }\n await clearStoredSession()\n}\n","import { createWriteStream } from \"node:fs\"\nimport path from \"node:path\"\nimport { Readable } from \"node:stream\"\nimport { pipeline } from \"node:stream/promises\"\n\nimport fs from \"fs-extra\"\nimport unzipper from \"unzipper\"\n\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\"\n\nconst IGNORED_ROOT_ENTRIES = new Set([\"__MACOSX\"])\n\nexport async function streamAndExtractTemplate(\n downloadResponse: Response,\n projectDir: string,\n): Promise<void> {\n if (!downloadResponse.body) throw new Error(\"Template response body is empty.\")\n\n await fs.ensureDir(projectDir)\n const tempDir = path.join(projectDir, \"..\", `.create-ts-saas-tmp-${Date.now()}`)\n const extractDir = path.join(tempDir, \"extracted\")\n await fs.ensureDir(tempDir)\n await fs.ensureDir(extractDir)\n const zipPath = path.join(tempDir, \"template.zip\")\n\n // 1. Stream zip to disk\n const writable = createWriteStream(zipPath)\n\n const nodeStream = Readable.fromWeb(downloadResponse.body as NodeReadableStream)\n await pipeline(nodeStream, writable)\n\n // 2. Extract to a staging directory\n const archive = await unzipper.Open.file(zipPath)\n await archive.extract({ path: extractDir })\n\n // 3. Determine the root folder inside the zip\n // Filter out junk entries like __MACOSX\n const topLevelEntries = await fs.readdir(extractDir)\n const meaningfulEntries = topLevelEntries.filter((name) => !IGNORED_ROOT_ENTRIES.has(name))\n\n if (meaningfulEntries.length === 1 && meaningfulEntries[0]) {\n const singleRoot = path.join(extractDir, meaningfulEntries[0])\n const stat = await fs.stat(singleRoot)\n if (stat.isDirectory()) {\n // Strip the root folder — move its contents directly into projectDir\n const children = await fs.readdir(singleRoot)\n for (const child of children) {\n // eslint-disable-next-line no-await-in-loop\n await fs.move(path.join(singleRoot, child), path.join(projectDir, child), {\n overwrite: true,\n })\n }\n } else {\n // Single file at root (unlikely but handle it)\n await fs.move(singleRoot, path.join(projectDir, meaningfulEntries[0]), { overwrite: true })\n }\n } else {\n // Multiple meaningful entries at root — move them all into projectDir\n for (const entry of meaningfulEntries) {\n // eslint-disable-next-line no-await-in-loop\n await fs.move(path.join(extractDir, entry), path.join(projectDir, entry), {\n overwrite: true,\n })\n }\n }\n\n // 4. Cleanup temp files\n await fs.remove(tempDir).catch(() => {})\n}\n\nexport async function writeWatermark(projectDir: string, payload: Record<string, unknown>): Promise<void> {\n const pkgJsonPath = path.join(projectDir, \"package.json\")\n\n if (await fs.pathExists(pkgJsonPath)) {\n const pkgJson = (await fs.readJson(pkgJsonPath)) as Record<string, unknown>\n pkgJson[\"create-ts-saas\"] = {\n wId: payload.watermarkId ?? payload.issuanceId,\n }\n await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 })\n }\n}\n","import { confirm, isCancel } from \"@clack/prompts\"\n\nexport async function promptInstall(defaultValue = true): Promise<boolean> {\n const value = await confirm({\n message: \"Install dependencies now?\",\n initialValue: defaultValue,\n })\n if (isCancel(value)) throw new Error(\"Operation cancelled.\")\n return value\n}\n","import { isCancel, select } from \"@clack/prompts\"\n\nimport type { PackageManager } from \"../types\"\n\nexport async function promptPackageManager(initialValue: PackageManager): Promise<PackageManager> {\n const value = await select<PackageManager>({\n message: \"Select package manager\",\n options: [\n { value: \"pnpm\", label: \"pnpm\" },\n { value: \"npm\", label: \"npm\" },\n { value: \"bun\", label: \"bun\" },\n ],\n initialValue,\n })\n if (isCancel(value)) throw new Error(\"Operation cancelled.\")\n return value\n}\n","import { isCancel, text } from \"@clack/prompts\"\n\nexport async function promptProjectName(initialValue = \"my-saas-app\"): Promise<string> {\n const value = await text({\n message: \"Project name\",\n initialValue,\n validate: (input) => (input?.trim() ? undefined : \"Project name is required\"),\n })\n if (isCancel(value) || typeof value !== \"string\") {\n throw new Error(\"Operation cancelled.\")\n }\n return value.trim()\n}\n","import { isCancel, select } from \"@clack/prompts\"\n\nimport type { TemplateSummary } from \"../types\"\n\nexport async function promptTemplate(templates: TemplateSummary[]): Promise<TemplateSummary> {\n const value = await select<string>({\n message: \"Select template\",\n options: templates.map((t) => ({\n value: t.slug,\n label: `${t.name} (${t.slug})`,\n hint: `latest ${t.latestVersion}`,\n })),\n })\n if (isCancel(value) || typeof value !== \"string\") {\n throw new Error(\"Operation cancelled.\")\n }\n const chosen = templates.find((t) => t.slug === value)\n if (!chosen) throw new Error(`Unknown template \"${value}\".`)\n return chosen\n}\n","import { isCancel, select } from \"@clack/prompts\"\n\nimport type { TemplateSummary } from \"../types\"\n\nexport async function promptVariant(template: TemplateSummary): Promise<string> {\n const first = template.variants[0]\n if (!first) throw new Error(`Template \"${template.slug}\" has no variants.`)\n const value = await select<string>({\n message: \"Select variant\",\n options: template.variants.map((v) => ({ value: v.slug, label: v.name, hint: v.slug })),\n initialValue: first.slug,\n })\n if (isCancel(value) || typeof value !== \"string\") {\n throw new Error(\"Operation cancelled.\")\n }\n return value\n}\n","import type { PackageManager } from \"../types\"\n\nexport function getUserPackageManager(): PackageManager {\n const userAgent = process.env.npm_config_user_agent\n if (userAgent?.startsWith(\"pnpm\")) return \"pnpm\"\n if (userAgent?.startsWith(\"bun\")) return \"bun\"\n return \"npm\"\n}\n","import { log } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { ApiError } from \"../lib/api\"\n\nconst DEBUG_FLAG = process.env.TS_SAAS_CLI_DEBUG === \"1\" || process.env.TS_SAAS_CLI_DEBUG === \"true\"\n\nfunction isNetworkError(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n\n const cause = (error as { cause?: unknown }).cause\n if (cause instanceof Error) {\n const code = (cause as { code?: string }).code\n if (code === \"ECONNREFUSED\" || code === \"ENOTFOUND\" || code === \"ETIMEDOUT\" || code === \"ECONNRESET\") {\n return true\n }\n }\n return error.message === \"fetch failed\"\n}\n\nexport function handleCliError(error: unknown): never {\n if (isNetworkError(error)) {\n log.error(pc.red(\"Network error — could not connect to the server.\"))\n if (DEBUG_FLAG) {\n console.error(error)\n }\n process.exit(1)\n }\n\n if (error instanceof ApiError) {\n log.error(pc.red(error.message))\n if (DEBUG_FLAG) {\n console.error(error)\n }\n process.exit(getExitCode(error))\n }\n\n if (error instanceof Error) {\n log.error(pc.red(error.message || \"Something went wrong\"))\n if (DEBUG_FLAG) {\n console.error(error)\n }\n process.exit(1)\n }\n\n log.error(pc.red(\"Something went wrong\"))\n if (DEBUG_FLAG) {\n console.error(error)\n }\n process.exit(1)\n}\n\nfunction getExitCode(error: ApiError): number {\n switch (error.code) {\n case \"AUTH_EXPIRED\":\n case \"AUTH_INVALID\":\n return 2\n case \"PLAN_FORBIDDEN\":\n return 3\n case \"UPGRADE_REQUIRED\":\n return 4\n default:\n return error.status && error.status >= 500 ? 1 : 1\n }\n}\n","import path from \"node:path\"\n\nimport { isCancel, log, select } from \"@clack/prompts\"\nimport fs from \"fs-extra\"\nimport pc from \"picocolors\"\n\nexport async function resolveProjectDirectory(\n initialInput: string,\n): Promise<{ projectDir: string; projectName: string }> {\n const current = initialInput\n while (true) {\n const fullPath = path.resolve(process.cwd(), current)\n // eslint-disable-next-line no-await-in-loop\n const exists = await fs.pathExists(fullPath)\n // eslint-disable-next-line no-await-in-loop\n const hasContent = exists && (await fs.readdir(fullPath)).length > 0\n if (!hasContent) {\n // eslint-disable-next-line no-await-in-loop\n await fs.ensureDir(fullPath)\n return {\n projectDir: fullPath,\n projectName: path.basename(fullPath),\n }\n }\n\n log.warn(`Directory \"${pc.yellow(current)}\" already exists and is not empty.`)\n // eslint-disable-next-line no-await-in-loop\n const action = await select<\"merge\" | \"overwrite\" | \"cancel\">({\n message: \"Choose how to proceed\",\n options: [\n { value: \"merge\", label: \"Merge into existing directory\" },\n { value: \"overwrite\", label: \"Overwrite directory contents\" },\n { value: \"cancel\", label: \"Cancel\" },\n ],\n initialValue: \"merge\",\n })\n if (isCancel(action) || action === \"cancel\") {\n throw new Error(\"Operation cancelled.\")\n }\n if (action === \"merge\") {\n return { projectDir: fullPath, projectName: path.basename(fullPath) }\n }\n // eslint-disable-next-line no-await-in-loop\n await fs.emptyDir(fullPath)\n return { projectDir: fullPath, projectName: path.basename(fullPath) }\n }\n}\n","import path from \"node:path\"\n\nimport { confirm, intro, log, note, spinner } from \"@clack/prompts\"\nimport { $ } from \"execa\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { ensureSession } from \"../lib/auth\"\nimport { loadConfig } from \"../lib/config\"\nimport { streamAndExtractTemplate, writeWatermark } from \"../lib/scaffolder\"\nimport { promptInstall } from \"../prompts/install\"\nimport { promptPackageManager } from \"../prompts/package-manager\"\nimport { promptProjectName } from \"../prompts/project-name\"\nimport { promptTemplate } from \"../prompts/template\"\nimport { promptVariant } from \"../prompts/variant\"\nimport { getUserPackageManager } from \"../utils/get-package-manager\"\nimport { handleCliError } from \"../utils/handle-cli-error\"\nimport { resolveProjectDirectory } from \"../utils/project-directory\"\n\nimport type { PackageManager } from \"../types\"\n\nexport interface InitOptions {\n projectName?: string | undefined\n dir?: string | undefined\n yes?: boolean | undefined\n template?: string | undefined\n variant?: string | undefined\n packageManager?: PackageManager | undefined\n install?: boolean | undefined\n git?: boolean | undefined\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n intro(pc.magenta(\"Initialize your saas...\"))\n\n try {\n const config = await loadConfig()\n const session = await ensureSession((current) =>\n createApiClient({\n config,\n accessToken: current?.accessToken ?? null,\n deviceId: current?.deviceId ?? null,\n }),\n )\n\n const api = createApiClient({\n config,\n accessToken: session.accessToken,\n deviceId: session.deviceId,\n })\n\n const projectInput = options.projectName || options.dir || (await promptProjectName())\n const { projectDir, projectName } = await resolveProjectDirectory(projectInput)\n\n const templates = (await api.getTemplates()).templates\n if (!templates.length) throw new Error(\"No templates available for this account.\")\n\n const selectedTemplate =\n options.template && templates.find((template) => template.slug === options.template)\n ? templates.find((template) => template.slug === options.template)\n : await promptTemplate(templates)\n if (!selectedTemplate) {\n log.error(pc.red(\"Selected template not found.\"))\n process.exit(1)\n }\n\n const selectedVariant =\n options.variant && selectedTemplate.variants.some((variant) => variant.slug === options.variant)\n ? options.variant\n : await promptVariant(selectedTemplate)\n\n const s = spinner()\n s.start(\"Authorizing template download...\")\n try {\n const authz = await api.authorizeInit({\n templateSlug: selectedTemplate.slug,\n variantSlug: selectedVariant,\n })\n s.message(\"Downloading template...\")\n const response = await api.stream(authz.downloadUrl)\n await streamAndExtractTemplate(response, projectDir)\n await writeWatermark(projectDir, authz.watermark)\n s.stop(\"Template ready.\")\n } catch (error) {\n s.stop(pc.red(\"Failed to scaffold template.\"))\n throw error\n }\n\n const packageManager = options.packageManager ?? (await promptPackageManager(getUserPackageManager()))\n const runInstall = options.install ?? (options.yes ? true : await promptInstall(true))\n if (runInstall) {\n s.start(`Running ${packageManager} install...`)\n await $({ cwd: projectDir, stderr: \"inherit\" })`${packageManager} install`\n s.stop(\"Dependencies installed.\")\n }\n\n const shouldInitGit =\n options.git ?? (options.yes ? true : await confirm({ message: \"Initialize git?\", initialValue: true }))\n if (shouldInitGit) {\n try {\n await $({ cwd: projectDir, stderr: \"ignore\", stdout: \"ignore\" })`git init`\n } catch {\n log.warn(\"Git init failed. Continuing without repository setup.\")\n }\n }\n\n log.success(pc.green(`Project created at ${projectDir}`))\n note(\n [\n `cd ${path.basename(projectDir)}`,\n ``,\n `Copy ${pc.cyan(\".env.example\")} → ${pc.cyan(\".env\")} and fill in your values`,\n `Run ${pc.cyan(`${packageManager} db:migrate`)} to set up the database`,\n `Run ${pc.cyan(`${packageManager} dev`)} to start`,\n ].join(\"\\n\"),\n `Next steps (${projectName})`,\n )\n } catch (error) {\n handleCliError(error)\n }\n}\n","import { intro, log } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { loginWithDeviceFlow } from \"../lib/auth\"\nimport { loadConfig } from \"../lib/config\"\nimport { handleCliError } from \"../utils/handle-cli-error\"\n\nexport async function loginCommand(): Promise<void> {\n intro(pc.magenta(\"Login to ts-saas\"))\n try {\n const config = await loadConfig()\n await loginWithDeviceFlow(createApiClient({ config }))\n log.success(pc.green(\"Logged in successfully.\"))\n } catch (error) {\n handleCliError(error)\n }\n}\n","import { intro, log } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { logout } from \"../lib/auth\"\nimport { loadConfig } from \"../lib/config\"\n\nexport async function logoutCommand(): Promise<void> {\n intro(pc.magenta(\"Logout from create-ts-saas\"))\n const config = await loadConfig()\n await logout((session) =>\n createApiClient({\n config,\n accessToken: session?.accessToken ?? null,\n deviceId: session?.deviceId ?? null,\n }),\n )\n log.success(pc.green(\"Logged out and local session cleared.\"))\n}\n","import { intro, log } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { loadConfig } from \"../lib/config\"\nimport { getCliVersion } from \"../lib/version\"\n\nexport async function upgradeCommand(): Promise<void> {\n intro(pc.magenta(\"Checking for updates\"))\n const config = await loadConfig()\n const api = createApiClient({ config })\n const latest = await api.getLatestVersion()\n const current = getCliVersion()\n if (latest.version !== current) {\n log.warn(\n pc.yellow(\n `Update available: current ${current}, latest ${latest.version}. Run: npx create-ts-saas@latest`,\n ),\n )\n return\n }\n log.success(pc.green(`You are on latest version (${current}).`))\n}\n","import { intro, log, note } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { ensureSession } from \"../lib/auth\"\nimport { loadConfig } from \"../lib/config\"\nimport { handleCliError } from \"../utils/handle-cli-error\"\n\nexport async function whoamiCommand(): Promise<void> {\n intro(pc.magenta(\"Current CLI account\"))\n try {\n const config = await loadConfig()\n const session = await ensureSession((current) =>\n createApiClient({\n config,\n accessToken: current?.accessToken ?? null,\n deviceId: current?.deviceId ?? null,\n }),\n )\n const api = createApiClient({\n config,\n accessToken: session.accessToken,\n deviceId: session.deviceId,\n })\n const data = await api.me()\n note(\n `Email: ${data.email}\\nPlan: ${data.plan}\\nActive devices: ${data.activeDevices}\\nTemplates: ${data.templates.join(\", \") || \"none\"}`,\n \"Account\",\n )\n log.success(pc.green(\"Account lookup completed.\"))\n } catch (error) {\n handleCliError(error)\n }\n}\n","import { os } from \"@orpc/server\"\nimport { createCli } from \"trpc-cli\"\nimport { z } from \"zod\"\n\nimport { doctorCommand } from \"./commands/doctor\"\nimport { initCommand } from \"./commands/init\"\nimport { loginCommand } from \"./commands/login\"\nimport { logoutCommand } from \"./commands/logout\"\nimport { upgradeCommand } from \"./commands/upgrade\"\nimport { whoamiCommand } from \"./commands/whoami\"\nimport { getCliVersion } from \"./lib/version\"\nimport { handleCliError } from \"./utils/handle-cli-error\"\n\nexport const router = os.router({\n create: os\n .meta({\n description: \"Create a new project from your paid SaaS template\",\n default: true,\n negateBooleans: true,\n })\n .input(\n z.tuple([\n z.string().optional(),\n z.object({\n dir: z.string().optional(),\n yes: z.boolean().optional().default(false),\n template: z.string().optional(),\n variant: z.string().optional(),\n packageManager: z.enum([\"npm\", \"pnpm\", \"bun\"]).optional(),\n install: z.boolean().optional(),\n git: z.boolean().optional(),\n }),\n ]),\n )\n .handler(async ({ input }) => {\n const [projectName, options] = input\n const payload = projectName ? { ...options, projectName } : options\n await initCommand({\n ...payload,\n })\n }),\n init: os\n .meta({ description: \"Alias for create command\" })\n .input(\n z.tuple([\n z.string().optional(),\n z.object({\n dir: z.string().optional(),\n yes: z.boolean().optional().default(false),\n template: z.string().optional(),\n variant: z.string().optional(),\n packageManager: z.enum([\"npm\", \"pnpm\", \"bun\"]).optional(),\n install: z.boolean().optional(),\n git: z.boolean().optional(),\n }),\n ]),\n )\n .handler(async ({ input }) => {\n const [projectName, options] = input\n const payload = projectName ? { ...options, projectName } : options\n await initCommand({\n ...payload,\n })\n }),\n login: os.meta({ description: \"Login with device flow\" }).handler(async () => {\n await loginCommand()\n }),\n logout: os.meta({ description: \"Logout and clear local auth session\" }).handler(async () => {\n await logoutCommand()\n }),\n doctor: os.meta({ description: \"Run local environment checks\" }).handler(async () => {\n await doctorCommand()\n }),\n whoami: os.meta({ description: \"Show authenticated account details\" }).handler(async () => {\n await whoamiCommand()\n }),\n upgrade: os.meta({ description: \"Check CLI version status\" }).handler(async () => {\n await upgradeCommand()\n }),\n})\n\nfunction createCliApp() {\n return createCli({\n router,\n name: \"create-ts-saas\",\n version: getCliVersion(),\n })\n}\n\nexport async function runCli() {\n try {\n await createCliApp().run()\n } catch (error) {\n handleCliError(error)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["os","CONSTANTS.CONFIG_DIR_NAME","CONSTANTS.CONFIG_FILE_NAME","CONSTANTS.DEFAULT_API_BASE_URL"],"sources":["../src/lib/version.ts","../src/lib/api.ts","../src/constant.ts","../src/lib/config.ts","../src/lib/storage.ts","../src/commands/doctor.ts","../src/utils/open-url.ts","../src/lib/auth.ts","../src/lib/scaffolder.ts","../src/prompts/install.ts","../src/prompts/package-manager.ts","../src/prompts/project-name.ts","../src/prompts/template.ts","../src/utils/get-package-manager.ts","../src/utils/handle-cli-error.ts","../src/utils/project-directory.ts","../src/commands/init.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/upgrade.ts","../src/commands/whoami.ts","../src/index.ts"],"sourcesContent":["import path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nimport fs from \"fs-extra\"\n\nexport function getCliVersion(): string {\n if (typeof process.env.CLI_VERSION === \"string\" && process.env.CLI_VERSION.length > 0) {\n return process.env.CLI_VERSION\n }\n try {\n const dir = path.dirname(fileURLToPath(import.meta.url))\n const packageJsonPath = path.join(dir, \"..\", \"..\", \"package.json\")\n\n const pkg: unknown = fs.readJSONSync(packageJsonPath)\n const version = pkg !== null && typeof pkg === \"object\" && \"version\" in pkg ? pkg.version : undefined\n return typeof version === \"string\" ? version : \"0.0.0\"\n } catch {\n return \"0.0.0\"\n }\n}\n\nexport function getCliPlatform(): string {\n return `${process.platform}-${process.arch}`\n}\n","import semver from \"semver\"\n\nimport { getCliPlatform, getCliVersion } from \"./version\"\n\nimport type { InitAuthorization, TemplateSummary } from \"../types\"\nimport type { PaidCliConfig } from \"./config\"\n\nexport class ApiError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n public readonly retryable = false,\n public readonly status?: number,\n public readonly minCliVersion?: string,\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\ninterface ApiClientOptions {\n config: PaidCliConfig\n accessToken?: string | null\n deviceId?: string | null\n}\n\nfunction parseApiErrorBody(text: string): { code: string; message: string; retryable?: boolean } | null {\n try {\n const raw: unknown = JSON.parse(text)\n if (raw === null || typeof raw !== \"object\") return null\n const data = raw as Record<string, unknown>\n if (typeof data.code === \"string\" && typeof data.message === \"string\") {\n return {\n code: data.code,\n message: data.message,\n retryable: Boolean(data.retryable),\n }\n }\n\n const nested =\n typeof data.error === \"object\" && data.error !== null\n ? (data.error as Record<string, unknown>)\n : undefined\n if (nested && typeof nested.code === \"string\" && typeof nested.message === \"string\") {\n return {\n code: nested.code,\n message: nested.message,\n retryable: Boolean(data.retryable),\n }\n }\n } catch {\n // noop\n }\n return null\n}\n\nexport function createApiClient(options: ApiClientOptions) {\n const { config, accessToken, deviceId } = options\n const baseUrl = config.apiBaseUrl.replace(/\\/$/, \"\")\n const cliVersion = getCliVersion()\n const cliPlatform = getCliPlatform()\n\n async function request<T>(path: string, init: RequestInit = {}): Promise<T> {\n const url = `${baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`\n const headers = new Headers(init.headers)\n headers.set(\"Accept\", \"application/json\")\n headers.set(\"x-cli-version\", cliVersion)\n headers.set(\"x-cli-platform\", cliPlatform)\n if (deviceId) headers.set(\"x-cli-device-id\", deviceId)\n if (accessToken) headers.set(\"Authorization\", `Bearer ${accessToken}`)\n\n let res: Response\n try {\n res = await fetch(url, { ...init, headers })\n } catch {\n throw new ApiError(\"NETWORK_ERROR\", \"Network error — could not connect to the server.\", true)\n }\n const minCli = res.headers.get(\"x-min-supported-cli\")\n if (minCli && semver.valid(cliVersion) && semver.valid(minCli) && semver.lt(cliVersion, minCli)) {\n throw new ApiError(\n \"UPGRADE_REQUIRED\",\n `CLI version ${cliVersion} is below minimum supported ${minCli}. Please upgrade.`,\n false,\n res.status,\n minCli,\n )\n }\n\n const text = await res.text()\n if (!res.ok) {\n const body = parseApiErrorBody(text)\n throw new ApiError(\n body?.code ?? (res.status === 401 ? \"AUTH_INVALID\" : \"REQUEST_FAILED\"),\n body?.message ?? res.statusText,\n body?.retryable ?? false,\n res.status,\n minCli ?? undefined,\n )\n }\n\n if (!text) return {} as unknown as T\n\n const json = JSON.parse(text) as unknown\n if (\n json !== null &&\n typeof json === \"object\" &&\n \"success\" in json &&\n \"data\" in json &&\n (json as Record<string, unknown>).success === true\n ) {\n return (json as Record<string, unknown>).data as T\n }\n return json as T\n }\n\n async function stream(url: string): Promise<Response> {\n const headers = new Headers()\n headers.set(\"x-cli-version\", cliVersion)\n headers.set(\"x-cli-platform\", cliPlatform)\n if (deviceId) headers.set(\"x-cli-device-id\", deviceId)\n if (accessToken) headers.set(\"Authorization\", `Bearer ${accessToken}`)\n let res: Response\n try {\n res = await fetch(url, { headers })\n } catch {\n throw new ApiError(\"NETWORK_ERROR\", \"Network error — could not connect to the server.\", true)\n }\n if (!res.ok) {\n const text = await res.text()\n const body = parseApiErrorBody(text)\n throw new ApiError(\n body?.code ?? \"DOWNLOAD_FAILED\",\n body?.message ?? \"Template download failed\",\n false,\n res.status,\n )\n }\n return res\n }\n\n return {\n baseUrl,\n request,\n stream,\n deviceInit: () =>\n request<{\n deviceCode: string\n userCode: string\n verificationUrl: string\n expiresIn: number\n interval: number\n }>(\"/cli/device/init\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({}),\n }),\n devicePoll: (deviceCode: string) =>\n request<{ accessToken: string; refreshToken: string; expiresIn: number; deviceId: string }>(\n \"/cli/device/poll\",\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ deviceCode }),\n },\n ),\n refresh: (refreshToken: string) =>\n request<{ accessToken: string; refreshToken: string; expiresIn: number }>(\"/cli/auth/refresh\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken }),\n }),\n logoutDevice: (refreshToken: string) =>\n request<{ success: boolean }>(\"/cli/device/logout\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken }),\n }),\n me: () =>\n request<{\n email: string\n plan: string\n templates: string[]\n activeDevices: number\n lastInitAt: string | null\n }>(\"/cli/me\"),\n getTemplates: () => request<{ templates: TemplateSummary[] }>(\"/cli/templates\"),\n authorizeInit: (input: { templateSlug: string }) =>\n request<InitAuthorization>(\"/cli/init/authorize\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n }),\n getLatestVersion: () => request<{ version: string; minSupportedVersion: string }>(\"/cli/latest-version\"),\n }\n}\n\nexport type ApiClient = ReturnType<typeof createApiClient>\n","export const DEFAULT_API_BASE_URL = \"http://localhost:8787/api/v1\"\n// export const DEFAULT_API_BASE_URL = \"https://dev.api.ts-saas.com/api/v1\"\nexport const CONFIG_DIR_NAME = \"ts-saas-cli\"\nexport const CONFIG_FILE_NAME = \"config.json\"\nexport const KEYTAR_SERVICE_NAME = \"create-ts-saas\"\nexport const KEYTAR_ACCOUNT_NAME = \"default\"\n","import os from \"node:os\"\nimport path from \"node:path\"\n\nimport fs from \"fs-extra\"\n\nimport * as CONSTANTS from \"../constant\"\n\nexport interface PaidCliConfig {\n apiBaseUrl: string\n disableTelemetry: boolean\n}\n\nfunction getConfigDir() {\n const home = os.homedir()\n const configHome = process.env.XDG_CONFIG_HOME ?? path.join(home, \".config\")\n return path.join(configHome, CONSTANTS.CONFIG_DIR_NAME)\n}\n\nexport function getConfigPath() {\n return path.join(getConfigDir(), CONSTANTS.CONFIG_FILE_NAME)\n}\n\nexport async function loadConfig(): Promise<PaidCliConfig> {\n const fromEnv = {\n apiBaseUrl: CONSTANTS.DEFAULT_API_BASE_URL,\n disableTelemetry:\n process.env.TS_SAAS_CLI_DISABLE_TELEMETRY === \"1\" ||\n process.env.TS_SAAS_CLI_DISABLE_TELEMETRY === \"true\",\n }\n const configPath = getConfigPath()\n try {\n if (await fs.pathExists(configPath)) {\n const raw: unknown = await fs.readJson(configPath)\n const data = raw as Partial<PaidCliConfig>\n return {\n apiBaseUrl: data.apiBaseUrl ?? fromEnv.apiBaseUrl ?? CONSTANTS.DEFAULT_API_BASE_URL,\n disableTelemetry: data.disableTelemetry ?? fromEnv.disableTelemetry ?? false,\n }\n }\n } catch {\n // ignore bad config\n }\n return {\n apiBaseUrl: fromEnv.apiBaseUrl ?? CONSTANTS.DEFAULT_API_BASE_URL,\n disableTelemetry: fromEnv.disableTelemetry ?? false,\n }\n}\n","import path from \"node:path\"\n\nimport fs from \"fs-extra\"\n\nimport { KEYTAR_ACCOUNT_NAME, KEYTAR_SERVICE_NAME } from \"../constant\"\nimport { getConfigPath } from \"./config\"\n\nimport type { CliSession } from \"../types\"\n\nconst FALLBACK_FILE = \"session.json\"\ntype KeytarModule = {\n getPassword: (service: string, account: string) => Promise<string | null>\n setPassword: (service: string, account: string, password: string) => Promise<void>\n deletePassword: (service: string, account: string) => Promise<boolean>\n}\n\nlet cachedKeytar: KeytarModule | null | undefined\n\nfunction getFallbackSessionPath() {\n return path.join(path.dirname(getConfigPath()), FALLBACK_FILE)\n}\n\nexport async function getStoredSession(): Promise<CliSession | null> {\n const keytar = await getKeytar()\n try {\n const value = keytar ? await keytar.getPassword(KEYTAR_SERVICE_NAME, KEYTAR_ACCOUNT_NAME) : null\n if (!value) return await getFallbackSession()\n\n const parsed: CliSession = JSON.parse(value)\n if (\n typeof parsed.accessToken !== \"string\" ||\n typeof parsed.refreshToken !== \"string\" ||\n typeof parsed.deviceId !== \"string\" ||\n typeof parsed.expiresAt !== \"number\"\n ) {\n return null\n }\n return parsed\n } catch {\n return await getFallbackSession()\n }\n}\n\nexport async function setStoredSession(session: CliSession): Promise<void> {\n const serialized = JSON.stringify(session)\n const keytar = await getKeytar()\n try {\n if (keytar) {\n await keytar.setPassword(KEYTAR_SERVICE_NAME, KEYTAR_ACCOUNT_NAME, serialized)\n return\n }\n await setFallbackSession(session)\n } catch {\n await setFallbackSession(session)\n }\n}\n\nexport async function clearStoredSession(): Promise<void> {\n const keytar = await getKeytar()\n try {\n if (keytar) {\n await keytar.deletePassword(KEYTAR_SERVICE_NAME, KEYTAR_ACCOUNT_NAME)\n }\n } catch {\n // noop\n }\n try {\n await fs.remove(getFallbackSessionPath())\n } catch {\n // noop\n }\n}\n\nasync function getFallbackSession(): Promise<CliSession | null> {\n const filePath = getFallbackSessionPath()\n try {\n if (!(await fs.pathExists(filePath))) return null\n\n const raw: unknown = await fs.readJson(filePath)\n const value = raw as CliSession\n if (\n typeof value.accessToken !== \"string\" ||\n typeof value.refreshToken !== \"string\" ||\n typeof value.deviceId !== \"string\" ||\n typeof value.expiresAt !== \"number\"\n ) {\n return null\n }\n return value\n } catch {\n return null\n }\n}\n\nasync function setFallbackSession(session: CliSession): Promise<void> {\n await fs.ensureDir(path.dirname(getFallbackSessionPath()))\n await fs.writeJson(getFallbackSessionPath(), session, { spaces: 0 })\n try {\n await fs.chmod(getFallbackSessionPath(), 0o600)\n } catch {\n // noop\n }\n}\n\nasync function getKeytar(): Promise<KeytarModule | null> {\n if (cachedKeytar !== undefined) return cachedKeytar\n try {\n const imported = (await import(\"keytar\")) as unknown as { default?: KeytarModule } & KeytarModule\n cachedKeytar = imported.default ?? imported\n return cachedKeytar\n } catch {\n cachedKeytar = null\n return null\n }\n}\n","import { consola } from \"consola\"\nimport { $ } from \"execa\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { loadConfig } from \"../lib/config\"\nimport { getStoredSession } from \"../lib/storage\"\nimport { getCliVersion } from \"../lib/version\"\n\nexport async function doctorCommand(): Promise<void> {\n consola.log(pc.bold(\"Environment checks\\n\"))\n const checks: Array<{ name: string; status: \"ok\" | \"fail\"; detail: string }> = []\n\n checks.push({\n name: \"Node.js\",\n status: Number(process.versions.node.split(\".\")[0]) >= 20 ? \"ok\" : \"fail\",\n detail: process.version,\n })\n\n try {\n const result = await $`pnpm --version`\n checks.push({ name: \"pnpm\", status: \"ok\", detail: result.stdout.trim() })\n } catch {\n checks.push({ name: \"pnpm\", status: \"fail\", detail: \"not found\" })\n }\n\n try {\n await $`git --version`\n checks.push({ name: \"git\", status: \"ok\", detail: \"installed\" })\n } catch {\n checks.push({ name: \"git\", status: \"fail\", detail: \"not found\" })\n }\n\n const config = await loadConfig()\n const session = await getStoredSession()\n checks.push({\n name: \"session\",\n status: session ? \"ok\" : \"fail\",\n detail: session ? \"present\" : \"missing\",\n })\n\n try {\n const api = createApiClient({\n config,\n accessToken: session?.accessToken ?? null,\n deviceId: session?.deviceId ?? null,\n })\n await api.getLatestVersion()\n checks.push({ name: \"api\", status: \"ok\", detail: config.apiBaseUrl })\n } catch {\n checks.push({ name: \"api\", status: \"fail\", detail: \"unreachable\" })\n }\n\n for (const check of checks) {\n const icon = check.status === \"ok\" ? pc.green(\"✓\") : pc.red(\"✗\")\n consola.log(`${icon} ${check.name}: ${check.detail}`)\n }\n consola.log(`\\nCLI version: ${getCliVersion()}`)\n}\n","import open from \"open\"\n\nexport async function openUrl(url: string): Promise<void> {\n try {\n await open(url)\n } catch {\n // ignore and let user open manually\n }\n}\n","import { note } from \"@clack/prompts\"\n\nimport { openUrl } from \"../utils/open-url\"\nimport { clearStoredSession, getStoredSession, setStoredSession } from \"./storage\"\n\nimport type { CliSession } from \"../types\"\nimport type { ApiClient } from \"./api\"\n\nasync function getValidSession(): Promise<CliSession | null> {\n const session = await getStoredSession()\n if (!session) return null\n const bufferMs = 60_000\n if (session.expiresAt <= Date.now() + bufferMs) return null\n return session\n}\n\nexport async function ensureSession(apiFactory: (session?: CliSession) => ApiClient): Promise<CliSession> {\n const valid = await getValidSession()\n if (valid) return valid\n\n const current = await getStoredSession()\n if (current?.refreshToken) {\n try {\n const api = apiFactory(current)\n const refreshed = await api.refresh(current.refreshToken)\n const next: CliSession = {\n accessToken: refreshed.accessToken,\n refreshToken: refreshed.refreshToken,\n deviceId: current.deviceId,\n expiresAt: Date.now() + refreshed.expiresIn * 1000,\n }\n await setStoredSession(next)\n return next\n } catch {\n await clearStoredSession()\n }\n }\n\n return await loginWithDeviceFlow(apiFactory())\n}\n\nexport async function loginWithDeviceFlow(api: ApiClient): Promise<CliSession> {\n const init = await api.deviceInit()\n const verificationUrlWithCode = `${init.verificationUrl}${init.verificationUrl.includes(\"?\") ? \"&\" : \"?\"}code=${encodeURIComponent(init.userCode)}`\n note(\n `Open ${verificationUrlWithCode} and confirm code ${init.userCode}\\nCode expires in ${Math.floor(init.expiresIn / 60)} minutes.`,\n \"Device Login\",\n )\n void openUrl(verificationUrlWithCode)\n\n const deadline = Date.now() + init.expiresIn * 1000\n while (Date.now() < deadline) {\n // eslint-disable-next-line no-await-in-loop\n await new Promise<void>((resolve) => {\n setTimeout(resolve, Math.max(init.interval, 3) * 1000)\n })\n try {\n // eslint-disable-next-line no-await-in-loop\n const token = await api.devicePoll(init.deviceCode)\n const session: CliSession = {\n accessToken: token.accessToken,\n refreshToken: token.refreshToken,\n deviceId: token.deviceId,\n expiresAt: Date.now() + token.expiresIn * 1000,\n }\n // eslint-disable-next-line no-await-in-loop\n await setStoredSession(session)\n return session\n } catch (error) {\n const code = error !== null && typeof error === \"object\" && \"code\" in error ? error.code : undefined\n const retryable =\n error !== null && typeof error === \"object\" && \"retryable\" in error ? error.retryable : false\n if (code === \"AUTH_PENDING\" || retryable) continue\n throw error\n }\n }\n throw new Error(\"Device code expired. Run login again.\")\n}\n\nexport async function logout(apiFactory: (session?: CliSession) => ApiClient): Promise<void> {\n const session = await getStoredSession()\n if (session?.refreshToken) {\n try {\n const api = apiFactory(session)\n await api.logoutDevice(session.refreshToken)\n } catch {\n // ignore remote failure and clear locally\n }\n }\n await clearStoredSession()\n}\n","import { createWriteStream } from \"node:fs\"\nimport path from \"node:path\"\nimport { Readable } from \"node:stream\"\nimport { pipeline } from \"node:stream/promises\"\n\nimport fs from \"fs-extra\"\nimport unzipper from \"unzipper\"\n\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\"\n\nconst IGNORED_ROOT_ENTRIES = new Set([\"__MACOSX\"])\n\nexport async function streamAndExtractTemplate(\n downloadResponse: Response,\n projectDir: string,\n): Promise<void> {\n if (!downloadResponse.body) throw new Error(\"Template response body is empty.\")\n\n await fs.ensureDir(projectDir)\n const tempDir = path.join(projectDir, \"..\", `.create-ts-saas-tmp-${Date.now()}`)\n const extractDir = path.join(tempDir, \"extracted\")\n await fs.ensureDir(tempDir)\n await fs.ensureDir(extractDir)\n const zipPath = path.join(tempDir, \"template.zip\")\n\n // 1. Stream zip to disk\n const writable = createWriteStream(zipPath)\n\n const nodeStream = Readable.fromWeb(downloadResponse.body as unknown as NodeReadableStream)\n await pipeline(nodeStream, writable)\n\n // 2. Extract to a staging directory\n const archive = await unzipper.Open.file(zipPath)\n await archive.extract({ path: extractDir })\n\n // 3. Determine the root folder inside the zip\n // Filter out junk entries like __MACOSX\n const topLevelEntries = await fs.readdir(extractDir)\n const meaningfulEntries = topLevelEntries.filter((name) => !IGNORED_ROOT_ENTRIES.has(name))\n\n if (meaningfulEntries.length === 1 && meaningfulEntries[0]) {\n const singleRoot = path.join(extractDir, meaningfulEntries[0])\n const stat = await fs.stat(singleRoot)\n if (stat.isDirectory()) {\n // Strip the root folder — move its contents directly into projectDir\n const children = await fs.readdir(singleRoot)\n for (const child of children) {\n // eslint-disable-next-line no-await-in-loop\n await fs.move(path.join(singleRoot, child), path.join(projectDir, child), {\n overwrite: true,\n })\n }\n } else {\n // Single file at root (unlikely but handle it)\n await fs.move(singleRoot, path.join(projectDir, meaningfulEntries[0]), { overwrite: true })\n }\n } else {\n // Multiple meaningful entries at root — move them all into projectDir\n for (const entry of meaningfulEntries) {\n // eslint-disable-next-line no-await-in-loop\n await fs.move(path.join(extractDir, entry), path.join(projectDir, entry), {\n overwrite: true,\n })\n }\n }\n\n // 4. Cleanup temp files\n await fs.remove(tempDir).catch(() => {})\n}\n\nexport async function writeWatermark(projectDir: string, payload: Record<string, unknown>): Promise<void> {\n const pkgJsonPath = path.join(projectDir, \"package.json\")\n\n if (await fs.pathExists(pkgJsonPath)) {\n const pkgJson = (await fs.readJson(pkgJsonPath)) as unknown as Record<string, unknown>\n pkgJson[\"create-ts-saas\"] = {\n wId: payload.watermarkId ?? payload.issuanceId,\n }\n await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 })\n }\n}\n","import { confirm, isCancel } from \"@clack/prompts\"\n\nexport async function promptInstall(defaultValue = true): Promise<boolean> {\n const value = await confirm({\n message: \"Install dependencies now?\",\n initialValue: defaultValue,\n })\n if (isCancel(value)) throw new Error(\"Operation cancelled.\")\n return value\n}\n","import { isCancel, select } from \"@clack/prompts\"\n\nimport type { PackageManager } from \"../types\"\n\nexport async function promptPackageManager(initialValue: PackageManager): Promise<PackageManager> {\n const value = await select<PackageManager>({\n message: \"Select package manager\",\n options: [\n { value: \"pnpm\", label: \"pnpm\" },\n { value: \"npm\", label: \"npm\" },\n { value: \"bun\", label: \"bun\" },\n ],\n initialValue,\n })\n if (isCancel(value)) throw new Error(\"Operation cancelled.\")\n return value\n}\n","import { isCancel, text } from \"@clack/prompts\"\n\nexport async function promptProjectName(initialValue = \"my-saas-app\"): Promise<string> {\n const value = await text({\n message: \"Project name\",\n initialValue,\n validate: (input) => (input?.trim() ? undefined : \"Project name is required\"),\n })\n if (isCancel(value) || typeof value !== \"string\") {\n throw new Error(\"Operation cancelled.\")\n }\n return value.trim()\n}\n","import { isCancel, select } from \"@clack/prompts\"\n\nimport type { TemplateSummary } from \"../types\"\n\nexport async function promptTemplate(templates: TemplateSummary[]): Promise<TemplateSummary> {\n const value = await select<string>({\n message: \"Select template\",\n options: templates.map((t) => ({\n value: t.slug,\n label: t.slug,\n hint: `latest ${t.latestVersion}`,\n })),\n })\n if (isCancel(value) || typeof value !== \"string\") {\n throw new Error(\"Operation cancelled.\")\n }\n const chosen = templates.find((t) => t.slug === value)\n if (!chosen) throw new Error(`Unknown template \"${value}\".`)\n return chosen\n}\n","import type { PackageManager } from \"../types\"\n\nexport function getUserPackageManager(): PackageManager {\n const userAgent = process.env.npm_config_user_agent\n if (userAgent?.startsWith(\"pnpm\")) return \"pnpm\"\n if (userAgent?.startsWith(\"bun\")) return \"bun\"\n return \"npm\"\n}\n","import { log } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { ApiError } from \"../lib/api\"\n\nconst DEBUG_FLAG = process.env.TS_SAAS_CLI_DEBUG === \"1\" || process.env.TS_SAAS_CLI_DEBUG === \"true\"\n\nfunction isNetworkError(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n\n const cause = (error as { cause?: unknown }).cause\n if (cause instanceof Error) {\n const code = \"code\" in cause ? cause.code : undefined\n if (code === \"ECONNREFUSED\" || code === \"ENOTFOUND\" || code === \"ETIMEDOUT\" || code === \"ECONNRESET\") {\n return true\n }\n }\n return error.message === \"fetch failed\"\n}\n\nexport function handleCliError(error: unknown): never {\n if (isNetworkError(error)) {\n log.error(pc.red(\"Network error — could not connect to the server.\"))\n if (DEBUG_FLAG) {\n console.error(error)\n }\n process.exit(1)\n }\n\n if (error instanceof ApiError) {\n log.error(pc.red(error.message))\n if (DEBUG_FLAG) {\n console.error(error)\n }\n process.exit(getExitCode(error))\n }\n\n if (error instanceof Error) {\n log.error(pc.red(error.message || \"Something went wrong\"))\n console.error(error)\n process.exit(1)\n }\n\n console.error(\"Unhandled non-Error:\", error)\n log.error(pc.red(typeof error === \"string\" ? error : \"Something went wrong\"))\n process.exit(1)\n}\n\nfunction getExitCode(error: ApiError): number {\n switch (error.code) {\n case \"AUTH_EXPIRED\":\n case \"AUTH_INVALID\":\n return 2\n case \"PLAN_FORBIDDEN\":\n return 3\n case \"UPGRADE_REQUIRED\":\n return 4\n default:\n return error.status && error.status >= 500 ? 1 : 1\n }\n}\n","import path from \"node:path\"\n\nimport { isCancel, log, select } from \"@clack/prompts\"\nimport fs from \"fs-extra\"\nimport pc from \"picocolors\"\n\nexport async function resolveProjectDirectory(\n initialInput: string,\n): Promise<{ projectDir: string; projectName: string }> {\n const current = initialInput\n while (true) {\n const fullPath = path.resolve(process.cwd(), current)\n // eslint-disable-next-line no-await-in-loop\n const exists = await fs.pathExists(fullPath)\n // eslint-disable-next-line no-await-in-loop\n const hasContent = exists && (await fs.readdir(fullPath)).length > 0\n if (!hasContent) {\n // eslint-disable-next-line no-await-in-loop\n await fs.ensureDir(fullPath)\n return {\n projectDir: fullPath,\n projectName: path.basename(fullPath),\n }\n }\n\n log.warn(`Directory \"${pc.yellow(current)}\" already exists and is not empty.`)\n // eslint-disable-next-line no-await-in-loop\n const action = await select<\"merge\" | \"overwrite\" | \"cancel\">({\n message: \"Choose how to proceed\",\n options: [\n { value: \"merge\", label: \"Merge into existing directory\" },\n { value: \"overwrite\", label: \"Overwrite directory contents\" },\n { value: \"cancel\", label: \"Cancel\" },\n ],\n initialValue: \"merge\",\n })\n if (isCancel(action) || action === \"cancel\") {\n throw new Error(\"Operation cancelled.\")\n }\n if (action === \"merge\") {\n return { projectDir: fullPath, projectName: path.basename(fullPath) }\n }\n // eslint-disable-next-line no-await-in-loop\n await fs.emptyDir(fullPath)\n return { projectDir: fullPath, projectName: path.basename(fullPath) }\n }\n}\n","import path from \"node:path\"\n\nimport { confirm, intro, log, note, spinner } from \"@clack/prompts\"\nimport { $ } from \"execa\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { ensureSession } from \"../lib/auth\"\nimport { loadConfig } from \"../lib/config\"\nimport { streamAndExtractTemplate, writeWatermark } from \"../lib/scaffolder\"\nimport { promptInstall } from \"../prompts/install\"\nimport { promptPackageManager } from \"../prompts/package-manager\"\nimport { promptProjectName } from \"../prompts/project-name\"\nimport { promptTemplate } from \"../prompts/template\"\nimport { getUserPackageManager } from \"../utils/get-package-manager\"\nimport { handleCliError } from \"../utils/handle-cli-error\"\nimport { resolveProjectDirectory } from \"../utils/project-directory\"\n\nimport type { PackageManager } from \"../types\"\n\ninterface InitOptions {\n projectName?: string | undefined\n dir?: string | undefined\n yes?: boolean | undefined\n template?: string | undefined\n packageManager?: PackageManager | undefined\n install?: boolean | undefined\n git?: boolean | undefined\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n intro(pc.magenta(\"Initialize your saas...\"))\n\n try {\n const config = await loadConfig()\n const session = await ensureSession((current) =>\n createApiClient({\n config,\n accessToken: current?.accessToken ?? null,\n deviceId: current?.deviceId ?? null,\n }),\n )\n\n const api = createApiClient({\n config,\n accessToken: session.accessToken,\n deviceId: session.deviceId,\n })\n\n const projectInput = options.projectName || options.dir || (await promptProjectName())\n const { projectDir, projectName } = await resolveProjectDirectory(projectInput)\n\n const templates = (await api.getTemplates()).templates\n if (!templates.length) throw new Error(\"No templates available for this account.\")\n\n const selectedTemplate =\n options.template && templates.find((template) => template.slug === options.template)\n ? templates.find((template) => template.slug === options.template)\n : await promptTemplate(templates)\n if (!selectedTemplate) {\n log.error(pc.red(\"Selected template not found.\"))\n process.exit(1)\n }\n\n const s = spinner()\n s.start(\"Authorizing template download...\")\n try {\n const authz = await api.authorizeInit({\n templateSlug: selectedTemplate.slug,\n })\n s.message(\"Downloading template...\")\n const response = await api.stream(authz.downloadUrl)\n await streamAndExtractTemplate(response, projectDir)\n await writeWatermark(projectDir, authz.watermark)\n s.stop(\"Template ready.\")\n } catch (error) {\n s.stop(pc.red(\"Failed to scaffold template.\"))\n throw error\n }\n\n const packageManager = options.packageManager ?? (await promptPackageManager(getUserPackageManager()))\n const runInstall = options.install ?? (options.yes ? true : await promptInstall(true))\n if (runInstall) {\n s.start(`Running ${packageManager} install...`)\n await $({ cwd: projectDir, stderr: \"inherit\" })`${packageManager} install`\n s.stop(\"Dependencies installed.\")\n }\n\n const shouldInitGit =\n options.git ?? (options.yes ? true : await confirm({ message: \"Initialize git?\", initialValue: true }))\n if (shouldInitGit) {\n try {\n await $({ cwd: projectDir, stderr: \"ignore\", stdout: \"ignore\" })`git init`\n } catch {\n log.warn(\"Git init failed. Continuing without repository setup.\")\n }\n }\n\n log.success(pc.green(`Project created at ${projectDir}`))\n note(\n [\n `cd ${path.basename(projectDir)}`,\n ``,\n `Copy ${pc.cyan(\".env.example\")} → ${pc.cyan(\".env\")} and fill in your values`,\n `Run ${pc.cyan(`${packageManager} db:migrate`)} to set up the database`,\n `Run ${pc.cyan(`${packageManager} dev`)} to start`,\n ].join(\"\\n\"),\n `Next steps (${projectName})`,\n )\n } catch (error) {\n handleCliError(error)\n }\n}\n","import { intro, log } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { loginWithDeviceFlow } from \"../lib/auth\"\nimport { loadConfig } from \"../lib/config\"\nimport { handleCliError } from \"../utils/handle-cli-error\"\n\nexport async function loginCommand(): Promise<void> {\n intro(pc.magenta(\"Login to ts-saas\"))\n try {\n const config = await loadConfig()\n await loginWithDeviceFlow(createApiClient({ config }))\n log.success(pc.green(\"Logged in successfully.\"))\n } catch (error) {\n handleCliError(error)\n }\n}\n","import { intro, log } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { logout } from \"../lib/auth\"\nimport { loadConfig } from \"../lib/config\"\n\nexport async function logoutCommand(): Promise<void> {\n intro(pc.magenta(\"Logout from create-ts-saas\"))\n const config = await loadConfig()\n await logout((session) =>\n createApiClient({\n config,\n accessToken: session?.accessToken ?? null,\n deviceId: session?.deviceId ?? null,\n }),\n )\n log.success(pc.green(\"Logged out and local session cleared.\"))\n}\n","import { intro, log } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { loadConfig } from \"../lib/config\"\nimport { getCliVersion } from \"../lib/version\"\n\nexport async function upgradeCommand(): Promise<void> {\n intro(pc.magenta(\"Checking for updates\"))\n const config = await loadConfig()\n const api = createApiClient({ config })\n const latest = await api.getLatestVersion()\n const current = getCliVersion()\n if (latest.version !== current) {\n log.warn(\n pc.yellow(\n `Update available: current ${current}, latest ${latest.version}. Run: npx create-ts-saas@latest`,\n ),\n )\n return\n }\n log.success(pc.green(`You are on latest version (${current}).`))\n}\n","import { intro, log, note } from \"@clack/prompts\"\nimport pc from \"picocolors\"\n\nimport { createApiClient } from \"../lib/api\"\nimport { ensureSession } from \"../lib/auth\"\nimport { loadConfig } from \"../lib/config\"\nimport { handleCliError } from \"../utils/handle-cli-error\"\n\nexport async function whoamiCommand(): Promise<void> {\n intro(pc.magenta(\"Current CLI account\"))\n try {\n const config = await loadConfig()\n const session = await ensureSession((current) =>\n createApiClient({\n config,\n accessToken: current?.accessToken ?? null,\n deviceId: current?.deviceId ?? null,\n }),\n )\n const api = createApiClient({\n config,\n accessToken: session.accessToken,\n deviceId: session.deviceId,\n })\n const data = await api.me()\n note(\n `Email: ${data.email}\\nPlan: ${data.plan}\\nActive devices: ${data.activeDevices}\\nTemplates: ${data.templates.join(\", \") || \"none\"}`,\n \"Account\",\n )\n log.success(pc.green(\"Account lookup completed.\"))\n } catch (error) {\n handleCliError(error)\n }\n}\n","import { os } from \"@orpc/server\"\nimport { createCli } from \"trpc-cli\"\nimport { z } from \"zod\"\n\nimport { doctorCommand } from \"./commands/doctor\"\nimport { initCommand } from \"./commands/init\"\nimport { loginCommand } from \"./commands/login\"\nimport { logoutCommand } from \"./commands/logout\"\nimport { upgradeCommand } from \"./commands/upgrade\"\nimport { whoamiCommand } from \"./commands/whoami\"\nimport { getCliVersion } from \"./lib/version\"\nimport { handleCliError } from \"./utils/handle-cli-error\"\n\nexport const router = os.router({\n create: os\n .meta({\n description: \"Create a new project from your paid SaaS template\",\n default: true,\n negateBooleans: true,\n })\n .input(\n z.tuple([\n z.string().optional(),\n z.object({\n dir: z.string().optional(),\n yes: z.boolean().optional().default(false),\n template: z.string().optional(),\n\n packageManager: z.enum([\"npm\", \"pnpm\", \"bun\"]).optional(),\n install: z.boolean().optional(),\n git: z.boolean().optional(),\n }),\n ]),\n )\n .handler(async ({ input }) => {\n const [projectName, options] = input\n const payload = projectName ? { ...options, projectName } : options\n await initCommand({\n ...payload,\n })\n }),\n init: os\n .meta({ description: \"Alias for create command\" })\n .input(\n z.tuple([\n z.string().optional(),\n z.object({\n dir: z.string().optional(),\n yes: z.boolean().optional().default(false),\n template: z.string().optional(),\n\n packageManager: z.enum([\"npm\", \"pnpm\", \"bun\"]).optional(),\n install: z.boolean().optional(),\n git: z.boolean().optional(),\n }),\n ]),\n )\n .handler(async ({ input }) => {\n const [projectName, options] = input\n const payload = projectName ? { ...options, projectName } : options\n await initCommand({\n ...payload,\n })\n }),\n login: os.meta({ description: \"Login with device flow\" }).handler(async () => {\n await loginCommand()\n }),\n logout: os.meta({ description: \"Logout and clear local auth session\" }).handler(async () => {\n await logoutCommand()\n }),\n doctor: os.meta({ description: \"Run local environment checks\" }).handler(async () => {\n await doctorCommand()\n }),\n whoami: os.meta({ description: \"Show authenticated account details\" }).handler(async () => {\n await whoamiCommand()\n }),\n upgrade: os.meta({ description: \"Check CLI version status\" }).handler(async () => {\n await upgradeCommand()\n }),\n})\n\nfunction createCliApp() {\n return createCli({\n router,\n name: \"create-ts-saas\",\n version: getCliVersion(),\n })\n}\n\nexport async function runCli() {\n try {\n await createCliApp().run()\n } catch (error) {\n handleCliError(error)\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ts-saas",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Paid secure CLI for initializing your SaaS boilerplate",
5
5
  "homepage": "https://ts-saas.com/",
6
6
  "license": "UNLICENSED",
@@ -28,7 +28,7 @@
28
28
  "start": "node dist/cli.mjs",
29
29
  "start:dev": "tsx src/cli.ts",
30
30
  "typecheck": "tsc --noEmit",
31
- "check": "oxlint --fix --fix-suggestions && oxfmt --write",
31
+ "check": "oxlint --fix --fix-suggestions --type-aware --type-check && oxfmt --write",
32
32
  "clean": "rimraf dist node_modules .turbo tsconfig.tsbuildinfo my-saas-app",
33
33
  "doctor": "knip",
34
34
  "i18n": "paraglide-js compile --project ../../packages/i18n/project.inlang --outdir ./src/paraglide",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@clack/prompts": "1.2.0",
39
- "@orpc/server": "1.13.13",
39
+ "@orpc/server": "1.13.14",
40
40
  "consola": "3.4.2",
41
41
  "execa": "9.6.1",
42
42
  "fs-extra": "11.3.4",
@@ -50,14 +50,9 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/fs-extra": "11.0.4",
53
- "@types/node": "25.5.2",
54
53
  "@types/semver": "7.7.1",
55
54
  "@types/unzipper": "0.10.11",
56
55
  "knip": "catalog:",
57
- "oxfmt": "catalog:",
58
- "oxlint": "catalog:",
59
- "tsdown": "0.21.7",
60
- "tsx": "4.21.0",
61
- "typescript": "catalog:"
56
+ "tsx": "4.21.0"
62
57
  }
63
58
  }