create-better-t-stack 3.19.4 → 3.19.5
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 +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-mMd-eO_p.mjs → src-C8vduSK8.mjs} +356 -342
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { _ as DatabaseSetupError, a as VirtualFileSystem, b as UserCancelledError, c as create, d as docs, f as generate, g as CompatibilityError, h as CLIError, i as TEMPLATE_COUNT, l as createBtsCli, m as sponsors, n as GeneratorError, o as add, p as router, r as Result, s as builder, t as EMBEDDED_TEMPLATES, u as createVirtual, v as DirectoryConflictError, x as ValidationError, y as ProjectCreationError } from "./src-
|
|
2
|
+
import { _ as DatabaseSetupError, a as VirtualFileSystem, b as UserCancelledError, c as create, d as docs, f as generate, g as CompatibilityError, h as CLIError, i as TEMPLATE_COUNT, l as createBtsCli, m as sponsors, n as GeneratorError, o as add, p as router, r as Result, s as builder, t as EMBEDDED_TEMPLATES, u as createVirtual, v as DirectoryConflictError, x as ValidationError, y as ProjectCreationError } from "./src-C8vduSK8.mjs";
|
|
3
3
|
|
|
4
4
|
export { CLIError, CompatibilityError, DatabaseSetupError, DirectoryConflictError, EMBEDDED_TEMPLATES, GeneratorError, ProjectCreationError, Result, TEMPLATE_COUNT, UserCancelledError, ValidationError, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generate, router, sponsors };
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as __reExport } from "./chunk-CHc3S52W.mjs";
|
|
3
|
-
import { autocompleteMultiselect, cancel, confirm, group, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
|
|
4
3
|
import { createRouterClient, os } from "@orpc/server";
|
|
5
4
|
import { Result, Result as Result$1, TaggedError } from "better-result";
|
|
6
|
-
import pc from "picocolors";
|
|
7
5
|
import { createCli } from "trpc-cli";
|
|
8
6
|
import z from "zod";
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
7
|
+
import { autocompleteMultiselect, cancel, confirm, group, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
|
|
8
|
+
import pc from "picocolors";
|
|
9
|
+
import envPaths from "env-paths";
|
|
11
10
|
import fs from "fs-extra";
|
|
12
11
|
import path from "node:path";
|
|
13
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, GeneratorError, GeneratorError as GeneratorError$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, dependencyVersionMap, generate, generate as generate$1, generateReproducibleCommand, processAddonTemplates, processAddonsDeps } from "@better-t-stack/template-generator";
|
|
14
14
|
import consola, { consola as consola$1 } from "consola";
|
|
15
|
+
import gradient from "gradient-string";
|
|
16
|
+
import { $, execa } from "execa";
|
|
17
|
+
import { writeTree } from "@better-t-stack/template-generator/fs-writer";
|
|
15
18
|
import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
|
|
16
19
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
17
20
|
import { applyEdits, modify, parse } from "jsonc-parser";
|
|
18
|
-
import gradient from "gradient-string";
|
|
19
|
-
import { $, execa } from "execa";
|
|
20
|
-
import envPaths from "env-paths";
|
|
21
21
|
import { format } from "oxfmt";
|
|
22
22
|
import os$1 from "node:os";
|
|
23
23
|
|
|
@@ -94,25 +94,6 @@ const ADDON_COMPATIBILITY = {
|
|
|
94
94
|
none: []
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
-
//#endregion
|
|
98
|
-
//#region src/types.ts
|
|
99
|
-
var types_exports = {};
|
|
100
|
-
import * as import__better_t_stack_types from "@better-t-stack/types";
|
|
101
|
-
__reExport(types_exports, import__better_t_stack_types);
|
|
102
|
-
|
|
103
|
-
//#endregion
|
|
104
|
-
//#region src/utils/compatibility.ts
|
|
105
|
-
const WEB_FRAMEWORKS = [
|
|
106
|
-
"tanstack-router",
|
|
107
|
-
"react-router",
|
|
108
|
-
"tanstack-start",
|
|
109
|
-
"next",
|
|
110
|
-
"nuxt",
|
|
111
|
-
"svelte",
|
|
112
|
-
"solid",
|
|
113
|
-
"astro"
|
|
114
|
-
];
|
|
115
|
-
|
|
116
97
|
//#endregion
|
|
117
98
|
//#region src/utils/errors.ts
|
|
118
99
|
/**
|
|
@@ -202,6 +183,344 @@ function displayError(error) {
|
|
|
202
183
|
else consola.error(pc.red(error.message));
|
|
203
184
|
}
|
|
204
185
|
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/utils/get-latest-cli-version.ts
|
|
188
|
+
function getLatestCLIVersionResult() {
|
|
189
|
+
const packageJsonPath = path.join(PKG_ROOT, "package.json");
|
|
190
|
+
return Result.try({
|
|
191
|
+
try: () => {
|
|
192
|
+
return fs.readJSONSync(packageJsonPath).version;
|
|
193
|
+
},
|
|
194
|
+
catch: (e) => new CLIError({
|
|
195
|
+
message: `Failed to read CLI version from package.json: ${e instanceof Error ? e.message : String(e)}`,
|
|
196
|
+
cause: e
|
|
197
|
+
})
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function getLatestCLIVersion() {
|
|
201
|
+
return getLatestCLIVersionResult().unwrapOr("1.0.0");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/utils/project-history.ts
|
|
206
|
+
const paths = envPaths("better-t-stack", { suffix: "" });
|
|
207
|
+
const HISTORY_FILE = "history.json";
|
|
208
|
+
var HistoryError = class extends TaggedError("HistoryError")() {};
|
|
209
|
+
function getHistoryDir() {
|
|
210
|
+
return paths.data;
|
|
211
|
+
}
|
|
212
|
+
function getHistoryPath() {
|
|
213
|
+
return path.join(paths.data, HISTORY_FILE);
|
|
214
|
+
}
|
|
215
|
+
function generateId() {
|
|
216
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
217
|
+
}
|
|
218
|
+
function emptyHistory() {
|
|
219
|
+
return {
|
|
220
|
+
version: 1,
|
|
221
|
+
entries: []
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async function ensureHistoryDir() {
|
|
225
|
+
return Result.tryPromise({
|
|
226
|
+
try: async () => {
|
|
227
|
+
await fs.ensureDir(getHistoryDir());
|
|
228
|
+
},
|
|
229
|
+
catch: (e) => new HistoryError({
|
|
230
|
+
message: `Failed to create history directory: ${e instanceof Error ? e.message : String(e)}`,
|
|
231
|
+
cause: e
|
|
232
|
+
})
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function readHistory() {
|
|
236
|
+
const historyPath = getHistoryPath();
|
|
237
|
+
const existsResult = await Result.tryPromise({
|
|
238
|
+
try: async () => await fs.pathExists(historyPath),
|
|
239
|
+
catch: (e) => new HistoryError({
|
|
240
|
+
message: `Failed to check history file: ${e instanceof Error ? e.message : String(e)}`,
|
|
241
|
+
cause: e
|
|
242
|
+
})
|
|
243
|
+
});
|
|
244
|
+
if (existsResult.isErr()) return existsResult;
|
|
245
|
+
if (!existsResult.value) return Result.ok(emptyHistory());
|
|
246
|
+
const readResult = await Result.tryPromise({
|
|
247
|
+
try: async () => await fs.readJson(historyPath),
|
|
248
|
+
catch: (e) => new HistoryError({
|
|
249
|
+
message: `Failed to read history file: ${e instanceof Error ? e.message : String(e)}`,
|
|
250
|
+
cause: e
|
|
251
|
+
})
|
|
252
|
+
});
|
|
253
|
+
if (readResult.isErr()) return Result.ok(emptyHistory());
|
|
254
|
+
return Result.ok(readResult.value);
|
|
255
|
+
}
|
|
256
|
+
async function writeHistory(history) {
|
|
257
|
+
const ensureDirResult = await ensureHistoryDir();
|
|
258
|
+
if (ensureDirResult.isErr()) return ensureDirResult;
|
|
259
|
+
return Result.tryPromise({
|
|
260
|
+
try: async () => {
|
|
261
|
+
await fs.writeJson(getHistoryPath(), history, { spaces: 2 });
|
|
262
|
+
},
|
|
263
|
+
catch: (e) => new HistoryError({
|
|
264
|
+
message: `Failed to write history file: ${e instanceof Error ? e.message : String(e)}`,
|
|
265
|
+
cause: e
|
|
266
|
+
})
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
async function addToHistory(config, reproducibleCommand) {
|
|
270
|
+
const historyResult = await readHistory();
|
|
271
|
+
if (historyResult.isErr()) return historyResult;
|
|
272
|
+
const history = historyResult.value;
|
|
273
|
+
const entry = {
|
|
274
|
+
id: generateId(),
|
|
275
|
+
projectName: config.projectName,
|
|
276
|
+
projectDir: config.projectDir,
|
|
277
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
278
|
+
stack: {
|
|
279
|
+
frontend: config.frontend,
|
|
280
|
+
backend: config.backend,
|
|
281
|
+
database: config.database,
|
|
282
|
+
orm: config.orm,
|
|
283
|
+
runtime: config.runtime,
|
|
284
|
+
auth: config.auth,
|
|
285
|
+
payments: config.payments,
|
|
286
|
+
api: config.api,
|
|
287
|
+
addons: config.addons,
|
|
288
|
+
examples: config.examples,
|
|
289
|
+
dbSetup: config.dbSetup,
|
|
290
|
+
packageManager: config.packageManager
|
|
291
|
+
},
|
|
292
|
+
cliVersion: getLatestCLIVersion(),
|
|
293
|
+
reproducibleCommand
|
|
294
|
+
};
|
|
295
|
+
history.entries.unshift(entry);
|
|
296
|
+
if (history.entries.length > 100) history.entries = history.entries.slice(0, 100);
|
|
297
|
+
return await writeHistory(history);
|
|
298
|
+
}
|
|
299
|
+
async function getHistory(limit = 10) {
|
|
300
|
+
const historyResult = await readHistory();
|
|
301
|
+
if (historyResult.isErr()) return historyResult;
|
|
302
|
+
return Result.ok(historyResult.value.entries.slice(0, limit));
|
|
303
|
+
}
|
|
304
|
+
async function clearHistory() {
|
|
305
|
+
const historyPath = getHistoryPath();
|
|
306
|
+
return Result.tryPromise({
|
|
307
|
+
try: async () => {
|
|
308
|
+
if (await fs.pathExists(historyPath)) await fs.remove(historyPath);
|
|
309
|
+
},
|
|
310
|
+
catch: (e) => new HistoryError({
|
|
311
|
+
message: `Failed to clear history: ${e instanceof Error ? e.message : String(e)}`,
|
|
312
|
+
cause: e
|
|
313
|
+
})
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/utils/render-title.ts
|
|
319
|
+
const TITLE_TEXT = `
|
|
320
|
+
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
321
|
+
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
|
|
322
|
+
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
|
|
323
|
+
██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
|
|
324
|
+
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
|
|
325
|
+
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
|
326
|
+
|
|
327
|
+
████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
|
|
328
|
+
╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
|
329
|
+
██║ ███████╗ ██║ ███████║██║ █████╔╝
|
|
330
|
+
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
|
|
331
|
+
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
|
332
|
+
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
333
|
+
`;
|
|
334
|
+
const catppuccinTheme = {
|
|
335
|
+
pink: "#F5C2E7",
|
|
336
|
+
mauve: "#CBA6F7",
|
|
337
|
+
red: "#F38BA8",
|
|
338
|
+
maroon: "#E78284",
|
|
339
|
+
peach: "#FAB387",
|
|
340
|
+
yellow: "#F9E2AF",
|
|
341
|
+
green: "#A6E3A1",
|
|
342
|
+
teal: "#94E2D5",
|
|
343
|
+
sky: "#89DCEB",
|
|
344
|
+
sapphire: "#74C7EC",
|
|
345
|
+
lavender: "#B4BEFE"
|
|
346
|
+
};
|
|
347
|
+
const renderTitle = () => {
|
|
348
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
349
|
+
const titleLines = TITLE_TEXT.split("\n");
|
|
350
|
+
if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(catppuccinTheme)).multiline(`Better T Stack`));
|
|
351
|
+
else console.log(gradient(Object.values(catppuccinTheme)).multiline(TITLE_TEXT));
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region src/commands/history.ts
|
|
356
|
+
function formatStackSummary(entry) {
|
|
357
|
+
const parts = [];
|
|
358
|
+
if (entry.stack.frontend.length > 0 && !entry.stack.frontend.includes("none")) parts.push(entry.stack.frontend.join(", "));
|
|
359
|
+
if (entry.stack.backend && entry.stack.backend !== "none") parts.push(entry.stack.backend);
|
|
360
|
+
if (entry.stack.database && entry.stack.database !== "none") parts.push(entry.stack.database);
|
|
361
|
+
if (entry.stack.orm && entry.stack.orm !== "none") parts.push(entry.stack.orm);
|
|
362
|
+
return parts.length > 0 ? parts.join(" + ") : "minimal";
|
|
363
|
+
}
|
|
364
|
+
function formatDate(isoString) {
|
|
365
|
+
return new Date(isoString).toLocaleDateString("en-US", {
|
|
366
|
+
year: "numeric",
|
|
367
|
+
month: "short",
|
|
368
|
+
day: "numeric",
|
|
369
|
+
hour: "2-digit",
|
|
370
|
+
minute: "2-digit"
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
async function historyHandler(input) {
|
|
374
|
+
if (input.clear) {
|
|
375
|
+
const clearResult = await clearHistory();
|
|
376
|
+
if (clearResult.isErr()) {
|
|
377
|
+
log.warn(pc.yellow(clearResult.error.message));
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
log.success(pc.green("Project history cleared."));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const historyResult = await getHistory(input.limit);
|
|
384
|
+
if (historyResult.isErr()) {
|
|
385
|
+
log.warn(pc.yellow(historyResult.error.message));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const entries = historyResult.value;
|
|
389
|
+
if (entries.length === 0) {
|
|
390
|
+
log.info(pc.dim("No projects in history yet."));
|
|
391
|
+
log.info(pc.dim("Create a project with: create-better-t-stack my-app"));
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (input.json) {
|
|
395
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
renderTitle();
|
|
399
|
+
intro(pc.magenta(`Project History (${entries.length} entries)`));
|
|
400
|
+
for (const [index, entry] of entries.entries()) {
|
|
401
|
+
const num = pc.dim(`${index + 1}.`);
|
|
402
|
+
const name = pc.cyan(pc.bold(entry.projectName));
|
|
403
|
+
const stack = pc.dim(formatStackSummary(entry));
|
|
404
|
+
log.message(`${num} ${name}`);
|
|
405
|
+
log.message(` ${pc.dim("Created:")} ${formatDate(entry.createdAt)}`);
|
|
406
|
+
log.message(` ${pc.dim("Path:")} ${entry.projectDir}`);
|
|
407
|
+
log.message(` ${pc.dim("Stack:")} ${stack}`);
|
|
408
|
+
log.message(` ${pc.dim("Command:")} ${pc.dim(entry.reproducibleCommand)}`);
|
|
409
|
+
log.message("");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region src/utils/open-url.ts
|
|
415
|
+
async function openUrl(url) {
|
|
416
|
+
const platform = process.platform;
|
|
417
|
+
if (platform === "darwin") {
|
|
418
|
+
await $({ stdio: "ignore" })`open ${url}`;
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (platform === "win32") {
|
|
422
|
+
const escapedUrl = url.replace(/&/g, "^&");
|
|
423
|
+
await $({ stdio: "ignore" })`cmd /c start "" ${escapedUrl}`;
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
await $({ stdio: "ignore" })`xdg-open ${url}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/utils/sponsors.ts
|
|
431
|
+
const SPONSORS_JSON_URL = "https://sponsors.better-t-stack.dev/sponsors.json";
|
|
432
|
+
async function fetchSponsors(url = SPONSORS_JSON_URL) {
|
|
433
|
+
const s = spinner();
|
|
434
|
+
s.start("Fetching sponsors…");
|
|
435
|
+
const response = await fetch(url);
|
|
436
|
+
if (!response.ok) {
|
|
437
|
+
s.stop(pc.red(`Failed to fetch sponsors: ${response.statusText}`));
|
|
438
|
+
throw new Error(`Failed to fetch sponsors: ${response.statusText}`);
|
|
439
|
+
}
|
|
440
|
+
const sponsors = await response.json();
|
|
441
|
+
s.stop("Sponsors fetched successfully!");
|
|
442
|
+
return sponsors;
|
|
443
|
+
}
|
|
444
|
+
function displaySponsors(sponsors) {
|
|
445
|
+
const { total_sponsors } = sponsors.summary;
|
|
446
|
+
if (total_sponsors === 0) {
|
|
447
|
+
log.info("No sponsors found. You can be the first one! ✨");
|
|
448
|
+
outro(pc.cyan("Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor."));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
displaySponsorsBox(sponsors);
|
|
452
|
+
if (total_sponsors - sponsors.specialSponsors.length > 0) log.message(pc.blue(`+${total_sponsors - sponsors.specialSponsors.length} more amazing sponsors.\n`));
|
|
453
|
+
outro(pc.magenta("Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor."));
|
|
454
|
+
}
|
|
455
|
+
function displaySponsorsBox(sponsors) {
|
|
456
|
+
if (sponsors.specialSponsors.length === 0) return;
|
|
457
|
+
let output = `${pc.bold(pc.cyan("-> Special Sponsors"))}\n\n`;
|
|
458
|
+
sponsors.specialSponsors.forEach((sponsor, idx) => {
|
|
459
|
+
const displayName = sponsor.name ?? sponsor.githubId;
|
|
460
|
+
const tier = sponsor.tierName ? ` ${pc.yellow(`(${sponsor.tierName})`)}` : "";
|
|
461
|
+
output += `${pc.green(`• ${displayName}`)}${tier}\n`;
|
|
462
|
+
output += ` ${pc.dim("GitHub:")} https://github.com/${sponsor.githubId}\n`;
|
|
463
|
+
const website = sponsor.websiteUrl ?? sponsor.githubUrl;
|
|
464
|
+
if (website) output += ` ${pc.dim("Website:")} ${website}\n`;
|
|
465
|
+
if (idx < sponsors.specialSponsors.length - 1) output += "\n";
|
|
466
|
+
});
|
|
467
|
+
consola$1.box(output);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/commands/meta.ts
|
|
472
|
+
const DOCS_URL = "https://better-t-stack.dev/docs";
|
|
473
|
+
const BUILDER_URL = "https://better-t-stack.dev/new";
|
|
474
|
+
async function openExternalUrl(url, successMessage) {
|
|
475
|
+
if ((await Result.tryPromise({
|
|
476
|
+
try: () => openUrl(url),
|
|
477
|
+
catch: () => null
|
|
478
|
+
})).isOk()) log.success(pc.blue(successMessage));
|
|
479
|
+
else log.message(`Please visit ${url}`);
|
|
480
|
+
}
|
|
481
|
+
async function showSponsorsCommand() {
|
|
482
|
+
const result = await Result.tryPromise({
|
|
483
|
+
try: async () => {
|
|
484
|
+
renderTitle();
|
|
485
|
+
intro(pc.magenta("Better-T-Stack Sponsors"));
|
|
486
|
+
displaySponsors(await fetchSponsors());
|
|
487
|
+
},
|
|
488
|
+
catch: (error) => new CLIError({
|
|
489
|
+
message: error instanceof Error ? error.message : "Failed to display sponsors",
|
|
490
|
+
cause: error
|
|
491
|
+
})
|
|
492
|
+
});
|
|
493
|
+
if (result.isErr()) {
|
|
494
|
+
displayError(result.error);
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
async function openDocsCommand() {
|
|
499
|
+
await openExternalUrl(DOCS_URL, "Opened docs in your default browser.");
|
|
500
|
+
}
|
|
501
|
+
async function openBuilderCommand() {
|
|
502
|
+
await openExternalUrl(BUILDER_URL, "Opened builder in your default browser.");
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/types.ts
|
|
507
|
+
var types_exports = {};
|
|
508
|
+
import * as import__better_t_stack_types from "@better-t-stack/types";
|
|
509
|
+
__reExport(types_exports, import__better_t_stack_types);
|
|
510
|
+
|
|
511
|
+
//#endregion
|
|
512
|
+
//#region src/utils/compatibility.ts
|
|
513
|
+
const WEB_FRAMEWORKS = [
|
|
514
|
+
"tanstack-router",
|
|
515
|
+
"react-router",
|
|
516
|
+
"tanstack-start",
|
|
517
|
+
"next",
|
|
518
|
+
"nuxt",
|
|
519
|
+
"svelte",
|
|
520
|
+
"solid",
|
|
521
|
+
"astro"
|
|
522
|
+
];
|
|
523
|
+
|
|
205
524
|
//#endregion
|
|
206
525
|
//#region src/utils/compatibility-rules.ts
|
|
207
526
|
function validationErr$1(message) {
|
|
@@ -845,43 +1164,6 @@ async function updateBtsConfig(projectDir, updates) {
|
|
|
845
1164
|
} catch {}
|
|
846
1165
|
}
|
|
847
1166
|
|
|
848
|
-
//#endregion
|
|
849
|
-
//#region src/utils/render-title.ts
|
|
850
|
-
const TITLE_TEXT = `
|
|
851
|
-
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
852
|
-
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
|
|
853
|
-
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
|
|
854
|
-
██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
|
|
855
|
-
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
|
|
856
|
-
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
|
857
|
-
|
|
858
|
-
████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
|
|
859
|
-
╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
|
860
|
-
██║ ███████╗ ██║ ███████║██║ █████╔╝
|
|
861
|
-
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
|
|
862
|
-
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
|
863
|
-
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
864
|
-
`;
|
|
865
|
-
const catppuccinTheme = {
|
|
866
|
-
pink: "#F5C2E7",
|
|
867
|
-
mauve: "#CBA6F7",
|
|
868
|
-
red: "#F38BA8",
|
|
869
|
-
maroon: "#E78284",
|
|
870
|
-
peach: "#FAB387",
|
|
871
|
-
yellow: "#F9E2AF",
|
|
872
|
-
green: "#A6E3A1",
|
|
873
|
-
teal: "#94E2D5",
|
|
874
|
-
sky: "#89DCEB",
|
|
875
|
-
sapphire: "#74C7EC",
|
|
876
|
-
lavender: "#B4BEFE"
|
|
877
|
-
};
|
|
878
|
-
const renderTitle = () => {
|
|
879
|
-
const terminalWidth = process.stdout.columns || 80;
|
|
880
|
-
const titleLines = TITLE_TEXT.split("\n");
|
|
881
|
-
if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(catppuccinTheme)).multiline(`Better T Stack`));
|
|
882
|
-
else console.log(gradient(Object.values(catppuccinTheme)).multiline(TITLE_TEXT));
|
|
883
|
-
};
|
|
884
|
-
|
|
885
1167
|
//#endregion
|
|
886
1168
|
//#region src/utils/add-package-deps.ts
|
|
887
1169
|
const addPackageDependency = async (opts) => {
|
|
@@ -3041,24 +3323,6 @@ async function getProjectName(initialName) {
|
|
|
3041
3323
|
return projectPath;
|
|
3042
3324
|
}
|
|
3043
3325
|
|
|
3044
|
-
//#endregion
|
|
3045
|
-
//#region src/utils/get-latest-cli-version.ts
|
|
3046
|
-
function getLatestCLIVersionResult() {
|
|
3047
|
-
const packageJsonPath = path.join(PKG_ROOT, "package.json");
|
|
3048
|
-
return Result.try({
|
|
3049
|
-
try: () => {
|
|
3050
|
-
return fs.readJSONSync(packageJsonPath).version;
|
|
3051
|
-
},
|
|
3052
|
-
catch: (e) => new CLIError({
|
|
3053
|
-
message: `Failed to read CLI version from package.json: ${e instanceof Error ? e.message : String(e)}`,
|
|
3054
|
-
cause: e
|
|
3055
|
-
})
|
|
3056
|
-
});
|
|
3057
|
-
}
|
|
3058
|
-
function getLatestCLIVersion() {
|
|
3059
|
-
return getLatestCLIVersionResult().unwrapOr("1.0.0");
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
3326
|
//#endregion
|
|
3063
3327
|
//#region src/utils/telemetry.ts
|
|
3064
3328
|
/**
|
|
@@ -3233,119 +3497,6 @@ async function setupProjectDirectory(finalPathInput, shouldClearDirectory) {
|
|
|
3233
3497
|
};
|
|
3234
3498
|
}
|
|
3235
3499
|
|
|
3236
|
-
//#endregion
|
|
3237
|
-
//#region src/utils/project-history.ts
|
|
3238
|
-
const paths = envPaths("better-t-stack", { suffix: "" });
|
|
3239
|
-
const HISTORY_FILE = "history.json";
|
|
3240
|
-
var HistoryError = class extends TaggedError("HistoryError")() {};
|
|
3241
|
-
function getHistoryDir() {
|
|
3242
|
-
return paths.data;
|
|
3243
|
-
}
|
|
3244
|
-
function getHistoryPath() {
|
|
3245
|
-
return path.join(paths.data, HISTORY_FILE);
|
|
3246
|
-
}
|
|
3247
|
-
function generateId() {
|
|
3248
|
-
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
3249
|
-
}
|
|
3250
|
-
function emptyHistory() {
|
|
3251
|
-
return {
|
|
3252
|
-
version: 1,
|
|
3253
|
-
entries: []
|
|
3254
|
-
};
|
|
3255
|
-
}
|
|
3256
|
-
async function ensureHistoryDir() {
|
|
3257
|
-
return Result.tryPromise({
|
|
3258
|
-
try: async () => {
|
|
3259
|
-
await fs.ensureDir(getHistoryDir());
|
|
3260
|
-
},
|
|
3261
|
-
catch: (e) => new HistoryError({
|
|
3262
|
-
message: `Failed to create history directory: ${e instanceof Error ? e.message : String(e)}`,
|
|
3263
|
-
cause: e
|
|
3264
|
-
})
|
|
3265
|
-
});
|
|
3266
|
-
}
|
|
3267
|
-
async function readHistory() {
|
|
3268
|
-
const historyPath = getHistoryPath();
|
|
3269
|
-
const existsResult = await Result.tryPromise({
|
|
3270
|
-
try: async () => await fs.pathExists(historyPath),
|
|
3271
|
-
catch: (e) => new HistoryError({
|
|
3272
|
-
message: `Failed to check history file: ${e instanceof Error ? e.message : String(e)}`,
|
|
3273
|
-
cause: e
|
|
3274
|
-
})
|
|
3275
|
-
});
|
|
3276
|
-
if (existsResult.isErr()) return existsResult;
|
|
3277
|
-
if (!existsResult.value) return Result.ok(emptyHistory());
|
|
3278
|
-
const readResult = await Result.tryPromise({
|
|
3279
|
-
try: async () => await fs.readJson(historyPath),
|
|
3280
|
-
catch: (e) => new HistoryError({
|
|
3281
|
-
message: `Failed to read history file: ${e instanceof Error ? e.message : String(e)}`,
|
|
3282
|
-
cause: e
|
|
3283
|
-
})
|
|
3284
|
-
});
|
|
3285
|
-
if (readResult.isErr()) return Result.ok(emptyHistory());
|
|
3286
|
-
return Result.ok(readResult.value);
|
|
3287
|
-
}
|
|
3288
|
-
async function writeHistory(history) {
|
|
3289
|
-
const ensureDirResult = await ensureHistoryDir();
|
|
3290
|
-
if (ensureDirResult.isErr()) return ensureDirResult;
|
|
3291
|
-
return Result.tryPromise({
|
|
3292
|
-
try: async () => {
|
|
3293
|
-
await fs.writeJson(getHistoryPath(), history, { spaces: 2 });
|
|
3294
|
-
},
|
|
3295
|
-
catch: (e) => new HistoryError({
|
|
3296
|
-
message: `Failed to write history file: ${e instanceof Error ? e.message : String(e)}`,
|
|
3297
|
-
cause: e
|
|
3298
|
-
})
|
|
3299
|
-
});
|
|
3300
|
-
}
|
|
3301
|
-
async function addToHistory(config, reproducibleCommand) {
|
|
3302
|
-
const historyResult = await readHistory();
|
|
3303
|
-
if (historyResult.isErr()) return historyResult;
|
|
3304
|
-
const history = historyResult.value;
|
|
3305
|
-
const entry = {
|
|
3306
|
-
id: generateId(),
|
|
3307
|
-
projectName: config.projectName,
|
|
3308
|
-
projectDir: config.projectDir,
|
|
3309
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3310
|
-
stack: {
|
|
3311
|
-
frontend: config.frontend,
|
|
3312
|
-
backend: config.backend,
|
|
3313
|
-
database: config.database,
|
|
3314
|
-
orm: config.orm,
|
|
3315
|
-
runtime: config.runtime,
|
|
3316
|
-
auth: config.auth,
|
|
3317
|
-
payments: config.payments,
|
|
3318
|
-
api: config.api,
|
|
3319
|
-
addons: config.addons,
|
|
3320
|
-
examples: config.examples,
|
|
3321
|
-
dbSetup: config.dbSetup,
|
|
3322
|
-
packageManager: config.packageManager
|
|
3323
|
-
},
|
|
3324
|
-
cliVersion: getLatestCLIVersion(),
|
|
3325
|
-
reproducibleCommand
|
|
3326
|
-
};
|
|
3327
|
-
history.entries.unshift(entry);
|
|
3328
|
-
if (history.entries.length > 100) history.entries = history.entries.slice(0, 100);
|
|
3329
|
-
return await writeHistory(history);
|
|
3330
|
-
}
|
|
3331
|
-
async function getHistory(limit = 10) {
|
|
3332
|
-
const historyResult = await readHistory();
|
|
3333
|
-
if (historyResult.isErr()) return historyResult;
|
|
3334
|
-
return Result.ok(historyResult.value.entries.slice(0, limit));
|
|
3335
|
-
}
|
|
3336
|
-
async function clearHistory() {
|
|
3337
|
-
const historyPath = getHistoryPath();
|
|
3338
|
-
return Result.tryPromise({
|
|
3339
|
-
try: async () => {
|
|
3340
|
-
if (await fs.pathExists(historyPath)) await fs.remove(historyPath);
|
|
3341
|
-
},
|
|
3342
|
-
catch: (e) => new HistoryError({
|
|
3343
|
-
message: `Failed to clear history: ${e instanceof Error ? e.message : String(e)}`,
|
|
3344
|
-
cause: e
|
|
3345
|
-
})
|
|
3346
|
-
});
|
|
3347
|
-
}
|
|
3348
|
-
|
|
3349
3500
|
//#endregion
|
|
3350
3501
|
//#region src/utils/project-name-validation.ts
|
|
3351
3502
|
function validateProjectName(name) {
|
|
@@ -5072,11 +5223,12 @@ async function displayPostInstallInstructions(config) {
|
|
|
5072
5223
|
"solid"
|
|
5073
5224
|
].includes(f));
|
|
5074
5225
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
5075
|
-
const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
|
|
5076
|
-
const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
|
|
5077
5226
|
const hasReactRouter = frontend?.includes("react-router");
|
|
5078
5227
|
const hasSvelte = frontend?.includes("svelte");
|
|
5079
5228
|
const webPort = hasReactRouter || hasSvelte ? "5173" : "3001";
|
|
5229
|
+
const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
|
|
5230
|
+
const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
|
|
5231
|
+
const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
|
|
5080
5232
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
5081
5233
|
let stepCounter = 2;
|
|
5082
5234
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
|
|
@@ -5116,6 +5268,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
5116
5268
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
5117
5269
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
5118
5270
|
if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
|
|
5271
|
+
if (betterAuthConvexInstructions) output += `\n${betterAuthConvexInstructions.trim()}\n`;
|
|
5119
5272
|
if (polarInstructions) output += `\n${polarInstructions.trim()}\n`;
|
|
5120
5273
|
if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
|
|
5121
5274
|
if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
|
|
@@ -5199,6 +5352,10 @@ function getBunWebNativeWarning() {
|
|
|
5199
5352
|
function getClerkInstructions() {
|
|
5200
5353
|
return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
|
|
5201
5354
|
}
|
|
5355
|
+
function getBetterAuthConvexInstructions(hasWeb, webPort, packageManager) {
|
|
5356
|
+
const cmd = packageManager === "npm" ? "npx" : packageManager;
|
|
5357
|
+
return `${pc.bold("Better Auth + Convex Setup:")}\n${pc.cyan("•")} Set environment variables from ${pc.white("packages/backend")}:\n${pc.white(" cd packages/backend")}\n${pc.white(` ${cmd} convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)`)}\n` + (hasWeb ? `${pc.white(` ${cmd} convex env set SITE_URL http://localhost:${webPort}`)}\n` : "");
|
|
5358
|
+
}
|
|
5202
5359
|
function getPolarInstructions(backend) {
|
|
5203
5360
|
const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
5204
5361
|
return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in ${envPath}`;
|
|
@@ -5560,121 +5717,8 @@ async function handleDirectoryConflictProgrammatically(currentPathInput, strateg
|
|
|
5560
5717
|
}
|
|
5561
5718
|
}
|
|
5562
5719
|
|
|
5563
|
-
//#endregion
|
|
5564
|
-
//#region src/utils/open-url.ts
|
|
5565
|
-
async function openUrl(url) {
|
|
5566
|
-
const platform = process.platform;
|
|
5567
|
-
if ((await Result.tryPromise({
|
|
5568
|
-
try: async () => {
|
|
5569
|
-
if (platform === "darwin") await $({ stdio: "ignore" })`open ${url}`;
|
|
5570
|
-
else if (platform === "win32") {
|
|
5571
|
-
const escapedUrl = url.replace(/&/g, "^&");
|
|
5572
|
-
await $({ stdio: "ignore" })`cmd /c start "" ${escapedUrl}`;
|
|
5573
|
-
} else await $({ stdio: "ignore" })`xdg-open ${url}`;
|
|
5574
|
-
},
|
|
5575
|
-
catch: () => void 0
|
|
5576
|
-
})).isErr()) log.message(`Please open ${url} in your browser.`);
|
|
5577
|
-
}
|
|
5578
|
-
|
|
5579
|
-
//#endregion
|
|
5580
|
-
//#region src/utils/sponsors.ts
|
|
5581
|
-
const SPONSORS_JSON_URL = "https://sponsors.better-t-stack.dev/sponsors.json";
|
|
5582
|
-
async function fetchSponsors(url = SPONSORS_JSON_URL) {
|
|
5583
|
-
const s = spinner();
|
|
5584
|
-
s.start("Fetching sponsors…");
|
|
5585
|
-
const response = await fetch(url);
|
|
5586
|
-
if (!response.ok) {
|
|
5587
|
-
s.stop(pc.red(`Failed to fetch sponsors: ${response.statusText}`));
|
|
5588
|
-
throw new Error(`Failed to fetch sponsors: ${response.statusText}`);
|
|
5589
|
-
}
|
|
5590
|
-
const sponsors = await response.json();
|
|
5591
|
-
s.stop("Sponsors fetched successfully!");
|
|
5592
|
-
return sponsors;
|
|
5593
|
-
}
|
|
5594
|
-
function displaySponsors(sponsors) {
|
|
5595
|
-
const { total_sponsors } = sponsors.summary;
|
|
5596
|
-
if (total_sponsors === 0) {
|
|
5597
|
-
log.info("No sponsors found. You can be the first one! ✨");
|
|
5598
|
-
outro(pc.cyan("Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor."));
|
|
5599
|
-
return;
|
|
5600
|
-
}
|
|
5601
|
-
displaySponsorsBox(sponsors);
|
|
5602
|
-
if (total_sponsors - sponsors.specialSponsors.length > 0) log.message(pc.blue(`+${total_sponsors - sponsors.specialSponsors.length} more amazing sponsors.\n`));
|
|
5603
|
-
outro(pc.magenta("Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor."));
|
|
5604
|
-
}
|
|
5605
|
-
function displaySponsorsBox(sponsors) {
|
|
5606
|
-
if (sponsors.specialSponsors.length === 0) return;
|
|
5607
|
-
let output = `${pc.bold(pc.cyan("-> Special Sponsors"))}\n\n`;
|
|
5608
|
-
sponsors.specialSponsors.forEach((sponsor, idx) => {
|
|
5609
|
-
const displayName = sponsor.name ?? sponsor.githubId;
|
|
5610
|
-
const tier = sponsor.tierName ? ` ${pc.yellow(`(${sponsor.tierName})`)}` : "";
|
|
5611
|
-
output += `${pc.green(`• ${displayName}`)}${tier}\n`;
|
|
5612
|
-
output += ` ${pc.dim("GitHub:")} https://github.com/${sponsor.githubId}\n`;
|
|
5613
|
-
const website = sponsor.websiteUrl ?? sponsor.githubUrl;
|
|
5614
|
-
if (website) output += ` ${pc.dim("Website:")} ${website}\n`;
|
|
5615
|
-
if (idx < sponsors.specialSponsors.length - 1) output += "\n";
|
|
5616
|
-
});
|
|
5617
|
-
consola$1.box(output);
|
|
5618
|
-
}
|
|
5619
|
-
|
|
5620
5720
|
//#endregion
|
|
5621
5721
|
//#region src/index.ts
|
|
5622
|
-
function formatStackSummary(entry) {
|
|
5623
|
-
const parts = [];
|
|
5624
|
-
if (entry.stack.frontend.length > 0 && !entry.stack.frontend.includes("none")) parts.push(entry.stack.frontend.join(", "));
|
|
5625
|
-
if (entry.stack.backend && entry.stack.backend !== "none") parts.push(entry.stack.backend);
|
|
5626
|
-
if (entry.stack.database && entry.stack.database !== "none") parts.push(entry.stack.database);
|
|
5627
|
-
if (entry.stack.orm && entry.stack.orm !== "none") parts.push(entry.stack.orm);
|
|
5628
|
-
return parts.length > 0 ? parts.join(" + ") : "minimal";
|
|
5629
|
-
}
|
|
5630
|
-
function formatDate(isoString) {
|
|
5631
|
-
return new Date(isoString).toLocaleDateString("en-US", {
|
|
5632
|
-
year: "numeric",
|
|
5633
|
-
month: "short",
|
|
5634
|
-
day: "numeric",
|
|
5635
|
-
hour: "2-digit",
|
|
5636
|
-
minute: "2-digit"
|
|
5637
|
-
});
|
|
5638
|
-
}
|
|
5639
|
-
async function historyHandler(input) {
|
|
5640
|
-
if (input.clear) {
|
|
5641
|
-
const clearResult = await clearHistory();
|
|
5642
|
-
if (clearResult.isErr()) {
|
|
5643
|
-
log.warn(pc.yellow(clearResult.error.message));
|
|
5644
|
-
return;
|
|
5645
|
-
}
|
|
5646
|
-
log.success(pc.green("Project history cleared."));
|
|
5647
|
-
return;
|
|
5648
|
-
}
|
|
5649
|
-
const historyResult = await getHistory(input.limit);
|
|
5650
|
-
if (historyResult.isErr()) {
|
|
5651
|
-
log.warn(pc.yellow(historyResult.error.message));
|
|
5652
|
-
return;
|
|
5653
|
-
}
|
|
5654
|
-
const entries = historyResult.value;
|
|
5655
|
-
if (entries.length === 0) {
|
|
5656
|
-
log.info(pc.dim("No projects in history yet."));
|
|
5657
|
-
log.info(pc.dim("Create a project with: create-better-t-stack my-app"));
|
|
5658
|
-
return;
|
|
5659
|
-
}
|
|
5660
|
-
if (input.json) {
|
|
5661
|
-
console.log(JSON.stringify(entries, null, 2));
|
|
5662
|
-
return;
|
|
5663
|
-
}
|
|
5664
|
-
renderTitle();
|
|
5665
|
-
intro(pc.magenta(`Project History (${entries.length} entries)`));
|
|
5666
|
-
for (const [index, entry] of entries.entries()) {
|
|
5667
|
-
const num = pc.dim(`${index + 1}.`);
|
|
5668
|
-
const name = pc.cyan(pc.bold(entry.projectName));
|
|
5669
|
-
const stack = pc.dim(formatStackSummary(entry));
|
|
5670
|
-
log.message(`${num} ${name}`);
|
|
5671
|
-
log.message(` ${pc.dim("Created:")} ${formatDate(entry.createdAt)}`);
|
|
5672
|
-
log.message(` ${pc.dim("Path:")} ${entry.projectDir}`);
|
|
5673
|
-
log.message(` ${pc.dim("Stack:")} ${stack}`);
|
|
5674
|
-
log.message(` ${pc.dim("Command:")} ${pc.dim(entry.reproducibleCommand)}`);
|
|
5675
|
-
log.message("");
|
|
5676
|
-
}
|
|
5677
|
-
}
|
|
5678
5722
|
const router = os.router({
|
|
5679
5723
|
create: os.meta({
|
|
5680
5724
|
description: "Create a new Better-T-Stack project",
|
|
@@ -5713,39 +5757,9 @@ const router = os.router({
|
|
|
5713
5757
|
});
|
|
5714
5758
|
if (options.verbose) return result;
|
|
5715
5759
|
}),
|
|
5716
|
-
sponsors: os.meta({ description: "Show Better-T-Stack sponsors" }).handler(
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
renderTitle();
|
|
5720
|
-
intro(pc.magenta("Better-T-Stack Sponsors"));
|
|
5721
|
-
displaySponsors(await fetchSponsors());
|
|
5722
|
-
},
|
|
5723
|
-
catch: (e) => new CLIError({
|
|
5724
|
-
message: e instanceof Error ? e.message : "Failed to display sponsors",
|
|
5725
|
-
cause: e
|
|
5726
|
-
})
|
|
5727
|
-
});
|
|
5728
|
-
if (result.isErr()) {
|
|
5729
|
-
displayError(result.error);
|
|
5730
|
-
process.exit(1);
|
|
5731
|
-
}
|
|
5732
|
-
}),
|
|
5733
|
-
docs: os.meta({ description: "Open Better-T-Stack documentation" }).handler(async () => {
|
|
5734
|
-
const DOCS_URL = "https://better-t-stack.dev/docs";
|
|
5735
|
-
if ((await Result.tryPromise({
|
|
5736
|
-
try: () => openUrl(DOCS_URL),
|
|
5737
|
-
catch: () => null
|
|
5738
|
-
})).isOk()) log.success(pc.blue("Opened docs in your default browser."));
|
|
5739
|
-
else log.message(`Please visit ${DOCS_URL}`);
|
|
5740
|
-
}),
|
|
5741
|
-
builder: os.meta({ description: "Open the web-based stack builder" }).handler(async () => {
|
|
5742
|
-
const BUILDER_URL = "https://better-t-stack.dev/new";
|
|
5743
|
-
if ((await Result.tryPromise({
|
|
5744
|
-
try: () => openUrl(BUILDER_URL),
|
|
5745
|
-
catch: () => null
|
|
5746
|
-
})).isOk()) log.success(pc.blue("Opened builder in your default browser."));
|
|
5747
|
-
else log.message(`Please visit ${BUILDER_URL}`);
|
|
5748
|
-
}),
|
|
5760
|
+
sponsors: os.meta({ description: "Show Better-T-Stack sponsors" }).handler(showSponsorsCommand),
|
|
5761
|
+
docs: os.meta({ description: "Open Better-T-Stack documentation" }).handler(openDocsCommand),
|
|
5762
|
+
builder: os.meta({ description: "Open the web-based stack builder" }).handler(openBuilderCommand),
|
|
5749
5763
|
add: os.meta({ description: "Add addons to an existing Better-T-Stack project" }).input(z.object({
|
|
5750
5764
|
addons: z.array(types_exports.AddonsSchema).optional().describe("Addons to add"),
|
|
5751
5765
|
install: z.boolean().optional().default(false).describe("Install dependencies after adding"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.19.
|
|
3
|
+
"version": "3.19.5",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"better-auth",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"prepublishOnly": "npm run build"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@better-t-stack/template-generator": "^3.19.
|
|
74
|
-
"@better-t-stack/types": "^3.19.
|
|
73
|
+
"@better-t-stack/template-generator": "^3.19.5",
|
|
74
|
+
"@better-t-stack/types": "^3.19.5",
|
|
75
75
|
"@clack/core": "^1.0.0",
|
|
76
76
|
"@clack/prompts": "^1.0.0",
|
|
77
77
|
"@orpc/server": "^1.13.4",
|