everything-dev 1.17.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/init.cjs +165 -84
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +14 -2
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +14 -2
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +164 -85
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/prompts.cjs +12 -14
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +12 -14
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/timing.cjs +21 -1
- package/dist/cli/timing.cjs.map +1 -1
- package/dist/cli/timing.mjs +21 -1
- package/dist/cli/timing.mjs.map +1 -1
- package/dist/cli/upgrade.cjs +114 -49
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs +114 -49
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/contract.cjs +1 -4
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +4 -10
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +4 -10
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.meta.cjs +5 -5
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +9 -9
- package/dist/contract.meta.d.mts +9 -9
- package/dist/contract.meta.mjs +5 -5
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +1 -4
- package/dist/contract.mjs.map +1 -1
- package/dist/plugin.cjs +83 -119
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +3 -6
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +3 -6
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +84 -120
- package/dist/plugin.mjs.map +1 -1
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/package.json +5 -5
- package/src/cli/init.ts +224 -162
- package/src/cli/prompts.ts +17 -22
- package/src/cli/timing.ts +27 -0
- package/src/cli/upgrade.ts +173 -56
- package/src/contract.meta.ts +6 -8
- package/src/contract.ts +1 -4
- package/src/plugin.ts +189 -209
package/src/cli/init.ts
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
loadManifestNormalizationSpec,
|
|
21
21
|
normalizePackageManifestsInTree,
|
|
22
22
|
} from "../internal/manifest-normalizer";
|
|
23
|
-
import type { BosConfig } from "../types";
|
|
23
|
+
import type { BosConfig, BosConfigInput } from "../types";
|
|
24
24
|
import { isPathExcluded } from "../utils/path-match";
|
|
25
25
|
import { saveBosConfig } from "../utils/save-config";
|
|
26
26
|
import { writeSnapshot } from "./snapshot";
|
|
@@ -51,12 +51,25 @@ export async function resolveSourceDir(opts: {
|
|
|
51
51
|
|
|
52
52
|
const parentConfig = await fetchParentConfig(opts.extendsAccount, opts.extendsGateway);
|
|
53
53
|
|
|
54
|
-
if (
|
|
55
|
-
|
|
54
|
+
if (parentConfig.repository) {
|
|
55
|
+
const { dir: sourceDir, cleanup } = await downloadTarball(parentConfig.repository);
|
|
56
|
+
return { sourceDir, parentConfig, cleanup };
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
const
|
|
59
|
-
|
|
59
|
+
const chainResult = await resolveRepositoryViaExtendsChain(
|
|
60
|
+
opts.extendsAccount,
|
|
61
|
+
opts.extendsGateway,
|
|
62
|
+
);
|
|
63
|
+
if (chainResult?.repository) {
|
|
64
|
+
const { dir: sourceDir, cleanup } = await downloadTarball(chainResult.repository);
|
|
65
|
+
return { sourceDir, parentConfig: chainResult.config, cleanup };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
sourceDir: "",
|
|
70
|
+
parentConfig,
|
|
71
|
+
cleanup: async () => {},
|
|
72
|
+
};
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
export async function readTemplatekeep(sourceDir: string): Promise<string[]> {
|
|
@@ -80,6 +93,37 @@ export async function fetchParentConfig(
|
|
|
80
93
|
return fetchBosConfigFromFastKv<BosConfig>(bosUrl);
|
|
81
94
|
}
|
|
82
95
|
|
|
96
|
+
export async function resolveRepositoryViaExtendsChain(
|
|
97
|
+
extendsAccount: string,
|
|
98
|
+
extendsGateway: string,
|
|
99
|
+
visited = new Set<string>(),
|
|
100
|
+
): Promise<{ repository: string; config: BosConfig } | null> {
|
|
101
|
+
const key = `bos://${extendsAccount}/${extendsGateway}`;
|
|
102
|
+
if (visited.has(key)) return null;
|
|
103
|
+
visited.add(key);
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const config = await fetchParentConfig(extendsAccount, extendsGateway);
|
|
107
|
+
if (config.repository) {
|
|
108
|
+
return { repository: config.repository, config };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const extendsRef = config.extends;
|
|
112
|
+
if (extendsRef && typeof extendsRef === "string") {
|
|
113
|
+
const normalized = extendsRef.startsWith("bos://") ? extendsRef : `bos://${extendsRef}`;
|
|
114
|
+
const match = normalized.match(/^bos:\/\/([^/]+)\/(.+)$/);
|
|
115
|
+
if (match) {
|
|
116
|
+
const result = await resolveRepositoryViaExtendsChain(match[1], match[2], visited);
|
|
117
|
+
if (result) return result;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return null;
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
83
127
|
export async function downloadTarball(
|
|
84
128
|
repoUrl: string,
|
|
85
129
|
): Promise<{ dir: string; cleanup: () => Promise<void> }> {
|
|
@@ -286,89 +330,21 @@ export async function personalizeConfig(
|
|
|
286
330
|
}
|
|
287
331
|
}
|
|
288
332
|
|
|
289
|
-
|
|
290
|
-
const
|
|
333
|
+
for (const pluginKey of Object.keys(plugins)) {
|
|
334
|
+
const plugin = plugins[pluginKey];
|
|
335
|
+
let pluginObj: Record<string, unknown>;
|
|
291
336
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
} else if (plugin && typeof plugin === "object") {
|
|
300
|
-
pluginObj = { ...(plugin as Record<string, unknown>) };
|
|
301
|
-
} else {
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (
|
|
306
|
-
pluginObj.development &&
|
|
307
|
-
typeof pluginObj.development === "string" &&
|
|
308
|
-
pluginObj.development.startsWith("local:")
|
|
309
|
-
) {
|
|
310
|
-
const pluginDir = join(destination, pluginObj.development.slice("local:".length));
|
|
311
|
-
const pluginConfigPath = join(pluginDir, "bos.config.json");
|
|
312
|
-
|
|
313
|
-
if (existsSync(pluginConfigPath)) {
|
|
314
|
-
try {
|
|
315
|
-
const pluginConfig = JSON.parse(readFileSync(pluginConfigPath, "utf-8")) as Record<
|
|
316
|
-
string,
|
|
317
|
-
unknown
|
|
318
|
-
>;
|
|
319
|
-
const normalizedConfig = ensureLocalPluginProviderConfig(
|
|
320
|
-
pluginConfig,
|
|
321
|
-
pluginKey,
|
|
322
|
-
`${pluginKey}.${opts.domain ?? parentDomain}`,
|
|
323
|
-
pluginObj,
|
|
324
|
-
opts.pluginRoutes,
|
|
325
|
-
);
|
|
326
|
-
if (stripProviderProductionFields(normalizedConfig)) {
|
|
327
|
-
writeFileSync(pluginConfigPath, `${JSON.stringify(normalizedConfig, null, 2)}\n`);
|
|
328
|
-
}
|
|
329
|
-
} catch {}
|
|
330
|
-
} else if (existsSync(pluginDir)) {
|
|
331
|
-
const pluginConfig = ensureLocalPluginProviderConfig(
|
|
332
|
-
{},
|
|
333
|
-
pluginKey,
|
|
334
|
-
`${pluginKey}.${opts.domain ?? parentDomain}`,
|
|
335
|
-
pluginObj,
|
|
336
|
-
opts.pluginRoutes,
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
mkdirSync(pluginDir, { recursive: true });
|
|
340
|
-
writeFileSync(pluginConfigPath, `${JSON.stringify(pluginConfig, null, 2)}\n`);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
plugins[pluginKey] = buildCleanPluginEntry(pluginObj);
|
|
344
|
-
} else {
|
|
345
|
-
delete pluginObj.production;
|
|
346
|
-
delete pluginObj.integrity;
|
|
347
|
-
delete pluginObj.sidebar;
|
|
348
|
-
delete pluginObj.routes;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
} else {
|
|
352
|
-
for (const pluginKey of Object.keys(plugins)) {
|
|
353
|
-
const pluginDir = resolve(
|
|
354
|
-
destination,
|
|
355
|
-
(plugins[pluginKey] as Record<string, unknown>)?.development
|
|
356
|
-
?.toString()
|
|
357
|
-
?.slice("local:".length) ?? "",
|
|
358
|
-
);
|
|
359
|
-
const pluginConfigPath = join(pluginDir, "bos.config.json");
|
|
360
|
-
if (!existsSync(pluginConfigPath)) continue;
|
|
361
|
-
|
|
362
|
-
try {
|
|
363
|
-
const pluginConfig = JSON.parse(readFileSync(pluginConfigPath, "utf-8")) as Record<
|
|
364
|
-
string,
|
|
365
|
-
unknown
|
|
366
|
-
>;
|
|
367
|
-
if (stripProviderProductionFields(pluginConfig)) {
|
|
368
|
-
writeFileSync(pluginConfigPath, `${JSON.stringify(pluginConfig, null, 2)}\n`);
|
|
369
|
-
}
|
|
370
|
-
} catch {}
|
|
337
|
+
if (typeof plugin === "string") {
|
|
338
|
+
pluginObj = { extends: plugin };
|
|
339
|
+
plugins[pluginKey] = pluginObj;
|
|
340
|
+
} else if (plugin && typeof plugin === "object") {
|
|
341
|
+
pluginObj = { ...(plugin as Record<string, unknown>) };
|
|
342
|
+
} else {
|
|
343
|
+
continue;
|
|
371
344
|
}
|
|
345
|
+
|
|
346
|
+
delete pluginObj.production;
|
|
347
|
+
delete pluginObj.integrity;
|
|
372
348
|
}
|
|
373
349
|
|
|
374
350
|
if (Object.keys(plugins).length === 0) {
|
|
@@ -506,82 +482,6 @@ export async function personalizeConfig(
|
|
|
506
482
|
}
|
|
507
483
|
}
|
|
508
484
|
|
|
509
|
-
function stripProviderProductionFields(config: Record<string, unknown>): boolean {
|
|
510
|
-
let changed = false;
|
|
511
|
-
|
|
512
|
-
if ("extends" in config) {
|
|
513
|
-
delete config.extends;
|
|
514
|
-
changed = true;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (config.plugins && typeof config.plugins === "object") {
|
|
518
|
-
const pluginEntries = config.plugins as Record<string, unknown>;
|
|
519
|
-
for (const entryKey of Object.keys(pluginEntries)) {
|
|
520
|
-
const entry = pluginEntries[entryKey];
|
|
521
|
-
if (!entry || typeof entry !== "object") continue;
|
|
522
|
-
const normalizedEntry = entry as Record<string, unknown>;
|
|
523
|
-
if ("production" in normalizedEntry) {
|
|
524
|
-
delete normalizedEntry.production;
|
|
525
|
-
changed = true;
|
|
526
|
-
}
|
|
527
|
-
if ("integrity" in normalizedEntry) {
|
|
528
|
-
delete normalizedEntry.integrity;
|
|
529
|
-
changed = true;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
return changed;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
function ensureLocalPluginProviderConfig(
|
|
538
|
-
pluginConfig: Record<string, unknown>,
|
|
539
|
-
pluginKey: string,
|
|
540
|
-
domain: string,
|
|
541
|
-
pluginObj: Record<string, unknown>,
|
|
542
|
-
pluginRoutes?: Record<string, string[]>,
|
|
543
|
-
): Record<string, unknown> {
|
|
544
|
-
pluginConfig.domain = domain;
|
|
545
|
-
if (!pluginConfig.plugins || typeof pluginConfig.plugins !== "object") {
|
|
546
|
-
pluginConfig.plugins = {};
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const plugins = pluginConfig.plugins as Record<string, unknown>;
|
|
550
|
-
if (!plugins[pluginKey] || typeof plugins[pluginKey] !== "object") {
|
|
551
|
-
plugins[pluginKey] = {};
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
const pluginEntry = plugins[pluginKey] as Record<string, unknown>;
|
|
555
|
-
if (!pluginEntry.name) {
|
|
556
|
-
pluginEntry.name = pluginKey;
|
|
557
|
-
}
|
|
558
|
-
if (!pluginEntry.development) {
|
|
559
|
-
pluginEntry.development = "local:.";
|
|
560
|
-
}
|
|
561
|
-
if (pluginRoutes?.[pluginKey]) {
|
|
562
|
-
pluginEntry.routes = pluginRoutes[pluginKey];
|
|
563
|
-
}
|
|
564
|
-
if (pluginObj.sidebar) {
|
|
565
|
-
pluginEntry.sidebar = pluginObj.sidebar;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
return pluginConfig;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
function buildCleanPluginEntry(pluginObj: Record<string, unknown>): Record<string, unknown> {
|
|
572
|
-
const cleanEntry: Record<string, unknown> = { development: pluginObj.development };
|
|
573
|
-
if (pluginObj.extends) {
|
|
574
|
-
cleanEntry.extends = pluginObj.extends;
|
|
575
|
-
}
|
|
576
|
-
if (pluginObj.secrets) {
|
|
577
|
-
cleanEntry.secrets = pluginObj.secrets;
|
|
578
|
-
}
|
|
579
|
-
if (pluginObj.variables) {
|
|
580
|
-
cleanEntry.variables = pluginObj.variables;
|
|
581
|
-
}
|
|
582
|
-
return cleanEntry;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
485
|
function generateAuthTypesTemplate(): string {
|
|
586
486
|
return `import type { Auth } from "better-auth";
|
|
587
487
|
export type { Auth } from "better-auth";
|
|
@@ -650,6 +550,121 @@ const WORKSPACE_LOCAL_PATHS: Record<string, string> = {
|
|
|
650
550
|
"every-plugin": "packages/every-plugin",
|
|
651
551
|
};
|
|
652
552
|
|
|
553
|
+
export async function scaffoldMinimalProject(
|
|
554
|
+
destination: string,
|
|
555
|
+
parentConfig: BosConfigInput,
|
|
556
|
+
opts: {
|
|
557
|
+
extendsAccount: string;
|
|
558
|
+
extendsGateway: string;
|
|
559
|
+
account?: string;
|
|
560
|
+
domain?: string;
|
|
561
|
+
plugins?: string[];
|
|
562
|
+
withHost?: boolean;
|
|
563
|
+
},
|
|
564
|
+
): Promise<number> {
|
|
565
|
+
mkdirSync(destination, { recursive: true });
|
|
566
|
+
|
|
567
|
+
const config: Record<string, unknown> = {
|
|
568
|
+
extends: `bos://${opts.extendsAccount}/${opts.extendsGateway}`,
|
|
569
|
+
account: opts.account || opts.extendsAccount,
|
|
570
|
+
...(opts.domain ? { domain: opts.domain } : {}),
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
if (parentConfig.app && typeof parentConfig.app === "object") {
|
|
574
|
+
const app: Record<string, unknown> = {};
|
|
575
|
+
const parentApp = parentConfig.app as Record<string, Record<string, unknown>>;
|
|
576
|
+
|
|
577
|
+
if (parentApp.host) {
|
|
578
|
+
app.host = { ...parentApp.host };
|
|
579
|
+
const host = app.host as Record<string, unknown>;
|
|
580
|
+
delete host.production;
|
|
581
|
+
delete host.integrity;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (parentApp.ui) {
|
|
585
|
+
app.ui = { ...parentApp.ui };
|
|
586
|
+
const ui = app.ui as Record<string, unknown>;
|
|
587
|
+
delete ui.production;
|
|
588
|
+
delete ui.integrity;
|
|
589
|
+
delete ui.ssr;
|
|
590
|
+
delete ui.ssrIntegrity;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (parentApp.api) {
|
|
594
|
+
app.api = { ...parentApp.api };
|
|
595
|
+
const api = app.api as Record<string, unknown>;
|
|
596
|
+
delete api.production;
|
|
597
|
+
delete api.integrity;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (parentApp.auth) {
|
|
601
|
+
app.auth = { ...parentApp.auth };
|
|
602
|
+
const auth = app.auth as Record<string, unknown>;
|
|
603
|
+
delete auth.production;
|
|
604
|
+
delete auth.integrity;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
config.app = app;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (opts.plugins && opts.plugins.length > 0 && parentConfig.plugins) {
|
|
611
|
+
const plugins: Record<string, unknown> = {};
|
|
612
|
+
for (const key of opts.plugins) {
|
|
613
|
+
const parentPlugin = (parentConfig.plugins as Record<string, unknown>)?.[key];
|
|
614
|
+
if (parentPlugin) {
|
|
615
|
+
if (typeof parentPlugin === "string") {
|
|
616
|
+
plugins[key] = { extends: parentPlugin };
|
|
617
|
+
} else {
|
|
618
|
+
const pluginCopy = { ...(parentPlugin as Record<string, unknown>) };
|
|
619
|
+
delete pluginCopy.production;
|
|
620
|
+
delete pluginCopy.integrity;
|
|
621
|
+
plugins[key] = pluginCopy;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
config.plugins = plugins;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
await saveBosConfig(destination, config);
|
|
629
|
+
|
|
630
|
+
const pkg: Record<string, unknown> = {
|
|
631
|
+
name: opts.domain || opts.extendsGateway,
|
|
632
|
+
private: true,
|
|
633
|
+
type: "module",
|
|
634
|
+
scripts: {
|
|
635
|
+
dev: "node_modules/.bin/bos dev --host remote",
|
|
636
|
+
"dev:ui": "node_modules/.bin/bos dev --ui local --api remote",
|
|
637
|
+
"dev:api": "node_modules/.bin/bos dev --ui remote --api local",
|
|
638
|
+
build: "node_modules/.bin/bos build",
|
|
639
|
+
deploy: "node_modules/.bin/bos build --deploy",
|
|
640
|
+
publish: "node_modules/.bin/bos publish",
|
|
641
|
+
start: "node_modules/.bin/bos start",
|
|
642
|
+
typecheck: "node_modules/.bin/bos types gen && tsc --noEmit",
|
|
643
|
+
postinstall: "node_modules/.bin/bos types gen || true",
|
|
644
|
+
"types:gen": "node_modules/.bin/bos types gen",
|
|
645
|
+
},
|
|
646
|
+
dependencies: {
|
|
647
|
+
"everything-dev": "catalog:",
|
|
648
|
+
"every-plugin": "catalog:",
|
|
649
|
+
},
|
|
650
|
+
devDependencies: {},
|
|
651
|
+
workspaces: {
|
|
652
|
+
packages: [],
|
|
653
|
+
catalog: {},
|
|
654
|
+
},
|
|
655
|
+
};
|
|
656
|
+
writeFileSync(join(destination, "package.json"), `${JSON.stringify(pkg, null, 2)}\n`);
|
|
657
|
+
|
|
658
|
+
const envExample = generateEnvExample(parentConfig);
|
|
659
|
+
if (envExample) {
|
|
660
|
+
writeFileSync(join(destination, ".env.example"), envExample);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
writeFileSync(join(destination, ".gitignore"), generateGitignore());
|
|
664
|
+
|
|
665
|
+
return 4;
|
|
666
|
+
}
|
|
667
|
+
|
|
653
668
|
async function resolveWorkspaceRefs(
|
|
654
669
|
destination: string,
|
|
655
670
|
options?: { localOverrides?: boolean; sourceDir?: string },
|
|
@@ -801,3 +816,50 @@ export async function generateDatabaseMigrations(destination: string): Promise<v
|
|
|
801
816
|
export async function execCommand(command: string, args: string[], cwd?: string): Promise<void> {
|
|
802
817
|
await execa(command, args, { cwd, stdio: "pipe" });
|
|
803
818
|
}
|
|
819
|
+
|
|
820
|
+
function generateEnvExample(config: BosConfigInput): string {
|
|
821
|
+
const lines: string[] = ["# Environment variables"];
|
|
822
|
+
const collectSecrets = (obj: Record<string, unknown>, prefix = ""): void => {
|
|
823
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
824
|
+
if (key === "secrets" && Array.isArray(value)) {
|
|
825
|
+
for (const secret of value) {
|
|
826
|
+
if (typeof secret === "string") {
|
|
827
|
+
lines.push(`${secret}=`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
} else if (key === "variables" && isPlainObject(value)) {
|
|
831
|
+
for (const [varKey, varVal] of Object.entries(value as Record<string, unknown>)) {
|
|
832
|
+
if (typeof varVal === "string") {
|
|
833
|
+
lines.push(`${varKey}=${varVal}`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
} else if (isPlainObject(value) && key !== "extends") {
|
|
837
|
+
collectSecrets(value as Record<string, unknown>, `${prefix}${key}.`);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
if (config.app && typeof config.app === "object") {
|
|
843
|
+
collectSecrets(config.app as Record<string, unknown>);
|
|
844
|
+
}
|
|
845
|
+
if (config.plugins && typeof config.plugins === "object") {
|
|
846
|
+
collectSecrets(config.plugins as Record<string, unknown>);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
lines.push("BETTER_AUTH_SECRET=generate-a-secret-here");
|
|
850
|
+
return `${lines.join("\n")}\n`;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
854
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function generateGitignore(): string {
|
|
858
|
+
return `node_modules/
|
|
859
|
+
dist/
|
|
860
|
+
.env
|
|
861
|
+
.bos/
|
|
862
|
+
*.gen.ts
|
|
863
|
+
*.gen.tsx
|
|
864
|
+
`;
|
|
865
|
+
}
|
package/src/cli/prompts.ts
CHANGED
|
@@ -2,12 +2,13 @@ import process from "node:process";
|
|
|
2
2
|
import * as p from "@clack/prompts";
|
|
3
3
|
|
|
4
4
|
function parseExtendsRef(ref: string): { account: string; gateway: string } | null {
|
|
5
|
-
const
|
|
5
|
+
const normalized = ref.startsWith("bos://") ? ref : `bos://${ref}`;
|
|
6
|
+
const match = normalized.match(/^bos:\/\/([^/]+)\/(.+)$/);
|
|
6
7
|
if (!match) return null;
|
|
7
8
|
return { account: match[1], gateway: match[2] };
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
function
|
|
11
|
+
function deriveAccountFromExtends(domain: string, extendsAccount: string): string {
|
|
11
12
|
const firstSegment = domain.split(".")[0];
|
|
12
13
|
if (!firstSegment) return "";
|
|
13
14
|
const suffix = extendsAccount.includes(".")
|
|
@@ -17,8 +18,6 @@ function deriveAccountFromDomain(domain: string, extendsAccount: string): string
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export async function promptInitOptions(input: {
|
|
20
|
-
extendsAccount?: string;
|
|
21
|
-
extendsGateway?: string;
|
|
22
21
|
extends?: string;
|
|
23
22
|
directory?: string;
|
|
24
23
|
account?: string;
|
|
@@ -37,40 +36,36 @@ export async function promptInitOptions(input: {
|
|
|
37
36
|
}> {
|
|
38
37
|
p.intro("Let's build an app...");
|
|
39
38
|
|
|
40
|
-
const domain =
|
|
41
|
-
input.domain ??
|
|
42
|
-
((await p.text({
|
|
43
|
-
message: "Starting with a domain?",
|
|
44
|
-
placeholder: "no",
|
|
45
|
-
})) as string);
|
|
46
|
-
|
|
47
|
-
if (p.isCancel(domain)) process.exit(0);
|
|
48
|
-
|
|
49
|
-
const extendsPlaceholder = "bos://dev.everything.near/everything.dev";
|
|
50
39
|
const extendsInput =
|
|
51
40
|
input.extends ??
|
|
52
41
|
((await p.text({
|
|
53
42
|
message: "Extending an existing app?",
|
|
54
|
-
placeholder:
|
|
43
|
+
placeholder: "bos://dev.everything.near/everything.dev",
|
|
55
44
|
})) as string);
|
|
56
45
|
|
|
57
46
|
if (p.isCancel(extendsInput)) process.exit(0);
|
|
58
47
|
|
|
59
|
-
let extendsAccount =
|
|
60
|
-
let extendsGateway =
|
|
48
|
+
let extendsAccount = "dev.everything.near";
|
|
49
|
+
let extendsGateway = "everything.dev";
|
|
61
50
|
|
|
62
51
|
if (extendsInput) {
|
|
63
52
|
const parsed = parseExtendsRef(extendsInput);
|
|
64
53
|
if (parsed) {
|
|
65
|
-
extendsAccount =
|
|
66
|
-
extendsGateway =
|
|
54
|
+
extendsAccount = parsed.account;
|
|
55
|
+
extendsGateway = parsed.gateway;
|
|
67
56
|
}
|
|
68
57
|
}
|
|
69
58
|
|
|
70
|
-
|
|
71
|
-
|
|
59
|
+
const domain =
|
|
60
|
+
input.domain ??
|
|
61
|
+
((await p.text({
|
|
62
|
+
message: "Starting with a domain?",
|
|
63
|
+
placeholder: "no",
|
|
64
|
+
})) as string);
|
|
65
|
+
|
|
66
|
+
if (p.isCancel(domain)) process.exit(0);
|
|
72
67
|
|
|
73
|
-
const accountDefault = domain ?
|
|
68
|
+
const accountDefault = domain ? deriveAccountFromExtends(domain, extendsAccount) : "";
|
|
74
69
|
const account =
|
|
75
70
|
input.account ??
|
|
76
71
|
((await p.text({
|
package/src/cli/timing.ts
CHANGED
|
@@ -1,13 +1,40 @@
|
|
|
1
|
+
import type { spinner as clackSpinner } from "@clack/prompts";
|
|
2
|
+
|
|
3
|
+
type Spinner = ReturnType<typeof clackSpinner>;
|
|
4
|
+
|
|
1
5
|
export interface PhaseTiming {
|
|
2
6
|
name: string;
|
|
3
7
|
durationMs: number;
|
|
4
8
|
}
|
|
5
9
|
|
|
10
|
+
const PHASE_LABELS: Record<string, string> = {
|
|
11
|
+
"parent config": "Fetching parent config...",
|
|
12
|
+
"template source": "Resolving template source...",
|
|
13
|
+
"scaffold project": "Creating project scaffold...",
|
|
14
|
+
"copy files": "Copying template files...",
|
|
15
|
+
"personalize config": "Personalizing config...",
|
|
16
|
+
"write snapshot": "Writing snapshot...",
|
|
17
|
+
"resolve config": "Resolving config...",
|
|
18
|
+
"generate env/docker": "Generating environment config...",
|
|
19
|
+
"create env file": "Creating .env file...",
|
|
20
|
+
"install dependencies": "Installing dependencies...",
|
|
21
|
+
"generate types": "Generating types...",
|
|
22
|
+
"generate migrations": "Generating database migrations...",
|
|
23
|
+
"generate code artifacts": "Generating code artifacts...",
|
|
24
|
+
"docker compose up": "Starting Docker services...",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function phaseLabel(name: string): string {
|
|
28
|
+
return PHASE_LABELS[name] ?? name;
|
|
29
|
+
}
|
|
30
|
+
|
|
6
31
|
export async function timePhase<T>(
|
|
7
32
|
timings: PhaseTiming[],
|
|
8
33
|
name: string,
|
|
9
34
|
fn: () => Promise<T>,
|
|
35
|
+
spinner?: Spinner,
|
|
10
36
|
): Promise<T> {
|
|
37
|
+
spinner?.message(phaseLabel(name));
|
|
11
38
|
const startedAt = Date.now();
|
|
12
39
|
try {
|
|
13
40
|
return await fn();
|