everything-dev 1.20.0 → 1.21.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 +138 -74
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +9 -5
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +9 -5
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +138 -75
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/parse.cjs +9 -0
- package/dist/cli/parse.cjs.map +1 -1
- package/dist/cli/parse.mjs +9 -0
- package/dist/cli/parse.mjs.map +1 -1
- package/dist/cli/prompts.cjs +44 -13
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +44 -13
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/sync.cjs +6 -0
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +6 -0
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli.cjs +3 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +3 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/contract.cjs +9 -1
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +37 -8
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +37 -8
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.meta.cjs +2 -2
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +3 -3
- package/dist/contract.meta.d.mts +3 -3
- package/dist/contract.meta.mjs +2 -2
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +9 -2
- package/dist/contract.mjs.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/plugin.cjs +24 -12
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +16 -5
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +16 -5
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +25 -13
- package/dist/plugin.mjs.map +1 -1
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/package.json +1 -1
- package/src/cli/init.ts +215 -89
- package/src/cli/parse.ts +17 -0
- package/src/cli/prompts.ts +45 -28
- package/src/cli/sync.ts +1 -0
- package/src/cli.ts +8 -1
- package/src/contract.meta.ts +6 -2
- package/src/contract.ts +5 -1
- package/src/plugin.ts +41 -18
package/src/cli/init.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
15
15
|
import { pipeline } from "node:stream/promises";
|
|
16
16
|
import { execa } from "execa";
|
|
17
17
|
import { glob } from "glob";
|
|
18
|
+
import type { OverrideSection } from "../contract";
|
|
18
19
|
import { fetchBosConfigFromFastKv } from "../fastkv";
|
|
19
20
|
import {
|
|
20
21
|
loadManifestNormalizationSpec,
|
|
@@ -27,6 +28,15 @@ import { writeSnapshot } from "./snapshot";
|
|
|
27
28
|
|
|
28
29
|
const require = createRequire(import.meta.url);
|
|
29
30
|
|
|
31
|
+
const _DEFAULT_OVERRIDES: OverrideSection[] = ["ui", "api"];
|
|
32
|
+
|
|
33
|
+
const OVERRIDE_WORKSPACE_MAP: Record<OverrideSection, string[]> = {
|
|
34
|
+
ui: ["ui"],
|
|
35
|
+
api: ["api"],
|
|
36
|
+
host: ["host"],
|
|
37
|
+
plugins: [],
|
|
38
|
+
};
|
|
39
|
+
|
|
30
40
|
interface SourceResult {
|
|
31
41
|
sourceDir: string;
|
|
32
42
|
parentConfig: BosConfig;
|
|
@@ -124,6 +134,32 @@ export async function resolveRepositoryViaExtendsChain(
|
|
|
124
134
|
}
|
|
125
135
|
}
|
|
126
136
|
|
|
137
|
+
export async function detectGitRemoteUrl(directory: string): Promise<string | undefined> {
|
|
138
|
+
try {
|
|
139
|
+
const { stdout } = await execa("git", ["remote", "get-url", "origin"], {
|
|
140
|
+
cwd: directory,
|
|
141
|
+
stdio: "pipe",
|
|
142
|
+
});
|
|
143
|
+
const url = stdout.trim();
|
|
144
|
+
if (!url) return undefined;
|
|
145
|
+
return normalizeGitUrl(url);
|
|
146
|
+
} catch {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function normalizeGitUrl(url: string): string | undefined {
|
|
152
|
+
const sshMatch = url.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
153
|
+
if (sshMatch) {
|
|
154
|
+
return `https://github.com/${sshMatch[1]}/${sshMatch[2]}`;
|
|
155
|
+
}
|
|
156
|
+
const httpsMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/);
|
|
157
|
+
if (httpsMatch) {
|
|
158
|
+
return `https://github.com/${httpsMatch[1]}/${httpsMatch[2]}`;
|
|
159
|
+
}
|
|
160
|
+
return url.endsWith(".git") ? url.slice(0, -4) : url;
|
|
161
|
+
}
|
|
162
|
+
|
|
127
163
|
export async function downloadTarball(
|
|
128
164
|
repoUrl: string,
|
|
129
165
|
): Promise<{ dir: string; cleanup: () => Promise<void> }> {
|
|
@@ -191,19 +227,51 @@ function parseGitHubUrl(url: string): { owner: string; repo: string; branch: str
|
|
|
191
227
|
return null;
|
|
192
228
|
}
|
|
193
229
|
|
|
230
|
+
function filterPatternsByOverrides(
|
|
231
|
+
patterns: string[],
|
|
232
|
+
overrides: OverrideSection[],
|
|
233
|
+
_plugins?: string[],
|
|
234
|
+
): string[] {
|
|
235
|
+
const has = (section: OverrideSection) => overrides.includes(section);
|
|
236
|
+
let filtered = [...patterns];
|
|
237
|
+
|
|
238
|
+
if (!has("host")) {
|
|
239
|
+
filtered = filtered.filter((p) => !p.startsWith("host/") && p !== "host/**");
|
|
240
|
+
}
|
|
241
|
+
if (!has("ui")) {
|
|
242
|
+
filtered = filtered.filter((p) => !p.startsWith("ui/") && p !== "ui/**");
|
|
243
|
+
}
|
|
244
|
+
if (!has("api")) {
|
|
245
|
+
filtered = filtered.filter((p) => !p.startsWith("api/") && p !== "api/**");
|
|
246
|
+
}
|
|
247
|
+
if (!has("plugins")) {
|
|
248
|
+
filtered = filtered.filter((p) => !p.startsWith("plugins/") && p !== "plugins/**");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return filtered;
|
|
252
|
+
}
|
|
253
|
+
|
|
194
254
|
export async function copyFilteredFiles(
|
|
195
255
|
sourceDir: string,
|
|
196
256
|
destination: string,
|
|
197
257
|
patterns: string[],
|
|
198
|
-
options: {
|
|
258
|
+
options: {
|
|
259
|
+
overrides: OverrideSection[];
|
|
260
|
+
plugins?: string[];
|
|
261
|
+
pluginRoutes?: Record<string, string[]>;
|
|
262
|
+
},
|
|
199
263
|
): Promise<number> {
|
|
200
264
|
if (patterns.length === 0) {
|
|
201
265
|
return 0;
|
|
202
266
|
}
|
|
203
267
|
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
268
|
+
const has = (section: OverrideSection) => options.overrides.includes(section);
|
|
269
|
+
|
|
270
|
+
const effectivePatterns = filterPatternsByOverrides(patterns, options.overrides, options.plugins);
|
|
271
|
+
|
|
272
|
+
if (has("host") && !effectivePatterns.some((p) => p.startsWith("host/") || p === "host/**")) {
|
|
273
|
+
effectivePatterns.push("host/**");
|
|
274
|
+
}
|
|
207
275
|
|
|
208
276
|
const excludedRoutePatterns: string[] = [];
|
|
209
277
|
if (options.pluginRoutes) {
|
|
@@ -276,6 +344,13 @@ export async function copyFilteredFiles(
|
|
|
276
344
|
return count;
|
|
277
345
|
}
|
|
278
346
|
|
|
347
|
+
function stripProductionFields(entry: Record<string, unknown>): void {
|
|
348
|
+
delete entry.production;
|
|
349
|
+
delete entry.integrity;
|
|
350
|
+
delete entry.ssr;
|
|
351
|
+
delete entry.ssrIntegrity;
|
|
352
|
+
}
|
|
353
|
+
|
|
279
354
|
export async function personalizeConfig(
|
|
280
355
|
destination: string,
|
|
281
356
|
opts: {
|
|
@@ -284,13 +359,16 @@ export async function personalizeConfig(
|
|
|
284
359
|
account?: string;
|
|
285
360
|
domain?: string;
|
|
286
361
|
plugins?: string[];
|
|
362
|
+
overrides: OverrideSection[];
|
|
287
363
|
pluginRoutes?: Record<string, string[]>;
|
|
288
364
|
workspaceOpts?: { localOverrides?: boolean; sourceDir?: string };
|
|
289
365
|
mode?: "init" | "sync";
|
|
290
|
-
|
|
366
|
+
repository?: string;
|
|
291
367
|
},
|
|
292
368
|
): Promise<void> {
|
|
293
369
|
const isInit = opts.mode !== "sync";
|
|
370
|
+
const has = (section: OverrideSection) => opts.overrides.includes(section);
|
|
371
|
+
|
|
294
372
|
const configPath = join(destination, "bos.config.json");
|
|
295
373
|
if (existsSync(configPath)) {
|
|
296
374
|
const config = JSON.parse(readFileSync(configPath, "utf-8")) as Record<string, unknown>;
|
|
@@ -303,53 +381,62 @@ export async function personalizeConfig(
|
|
|
303
381
|
if (opts.domain) {
|
|
304
382
|
config.domain = opts.domain;
|
|
305
383
|
}
|
|
384
|
+
if (opts.repository) {
|
|
385
|
+
config.repository = opts.repository;
|
|
386
|
+
}
|
|
306
387
|
|
|
307
388
|
if (isInit && config.app && typeof config.app === "object") {
|
|
308
389
|
const app = config.app as Record<string, unknown>;
|
|
309
390
|
|
|
310
391
|
for (const entryKey of Object.keys(app)) {
|
|
392
|
+
if (
|
|
393
|
+
!has(entryKey as OverrideSection) &&
|
|
394
|
+
(entryKey === "host" || entryKey === "ui" || entryKey === "api" || entryKey === "auth")
|
|
395
|
+
) {
|
|
396
|
+
delete app[entryKey];
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
311
399
|
const entry = app[entryKey];
|
|
312
400
|
if (entry && typeof entry === "object") {
|
|
313
|
-
|
|
314
|
-
delete e.production;
|
|
315
|
-
delete e.integrity;
|
|
316
|
-
delete e.ssr;
|
|
317
|
-
delete e.ssrIntegrity;
|
|
401
|
+
stripProductionFields(entry as Record<string, unknown>);
|
|
318
402
|
}
|
|
319
403
|
}
|
|
320
404
|
}
|
|
321
405
|
|
|
322
|
-
if (
|
|
323
|
-
|
|
406
|
+
if (has("plugins")) {
|
|
407
|
+
if (config.plugins && typeof config.plugins === "object") {
|
|
408
|
+
const plugins = config.plugins as Record<string, unknown>;
|
|
324
409
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
410
|
+
if (opts.plugins !== undefined) {
|
|
411
|
+
for (const pluginKey of Object.keys(plugins)) {
|
|
412
|
+
if (!opts.plugins.includes(pluginKey)) {
|
|
413
|
+
delete plugins[pluginKey];
|
|
414
|
+
}
|
|
329
415
|
}
|
|
330
416
|
}
|
|
331
|
-
}
|
|
332
417
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
418
|
+
for (const pluginKey of Object.keys(plugins)) {
|
|
419
|
+
const plugin = plugins[pluginKey];
|
|
420
|
+
let pluginObj: Record<string, unknown>;
|
|
421
|
+
|
|
422
|
+
if (typeof plugin === "string") {
|
|
423
|
+
pluginObj = { extends: plugin };
|
|
424
|
+
plugins[pluginKey] = pluginObj;
|
|
425
|
+
} else if (plugin && typeof plugin === "object") {
|
|
426
|
+
pluginObj = { ...(plugin as Record<string, unknown>) };
|
|
427
|
+
} else {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
336
430
|
|
|
337
|
-
|
|
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;
|
|
431
|
+
stripProductionFields(pluginObj);
|
|
344
432
|
}
|
|
345
433
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (Object.keys(plugins).length === 0) {
|
|
351
|
-
config.plugins = {};
|
|
434
|
+
if (Object.keys(plugins).length === 0) {
|
|
435
|
+
config.plugins = {};
|
|
436
|
+
}
|
|
352
437
|
}
|
|
438
|
+
} else {
|
|
439
|
+
delete config.plugins;
|
|
353
440
|
}
|
|
354
441
|
|
|
355
442
|
await saveBosConfig(destination, config);
|
|
@@ -364,10 +451,11 @@ export async function personalizeConfig(
|
|
|
364
451
|
if (Array.isArray(ws.packages)) {
|
|
365
452
|
ws.packages = ws.packages.filter((p: string) => {
|
|
366
453
|
if (p.startsWith("packages/")) return false;
|
|
367
|
-
if (p === "host") return
|
|
368
|
-
if (p === "plugins/*") return (opts.plugins?.length ?? 0) > 0;
|
|
454
|
+
if (p === "host") return has("host");
|
|
455
|
+
if (p === "plugins/*") return has("plugins") && (opts.plugins?.length ?? 0) > 0;
|
|
369
456
|
const pluginMatch = p.match(/^plugins\/([^/]+)/);
|
|
370
|
-
if (pluginMatch)
|
|
457
|
+
if (pluginMatch)
|
|
458
|
+
return has("plugins") && (opts.plugins?.includes(pluginMatch[1]) ?? true);
|
|
371
459
|
return true;
|
|
372
460
|
});
|
|
373
461
|
}
|
|
@@ -395,7 +483,7 @@ export async function personalizeConfig(
|
|
|
395
483
|
scripts.typecheck = scripts.typecheck
|
|
396
484
|
.replace("bun run types:gen && ", "")
|
|
397
485
|
.replace(/bun run --cwd packages\/everything-dev typecheck & ?/, "");
|
|
398
|
-
if (!
|
|
486
|
+
if (!has("host")) {
|
|
399
487
|
scripts.typecheck = scripts.typecheck.replace(/bun run --cwd host tsc --noEmit & ?/, "");
|
|
400
488
|
}
|
|
401
489
|
}
|
|
@@ -451,27 +539,34 @@ export async function personalizeConfig(
|
|
|
451
539
|
|
|
452
540
|
await resolveWorkspaceRefs(destination, opts.workspaceOpts);
|
|
453
541
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
542
|
+
if (has("ui")) {
|
|
543
|
+
const genContractPath = join(destination, "ui", "src", "lib", "api-types.gen.ts");
|
|
544
|
+
if (!existsSync(genContractPath)) {
|
|
545
|
+
mkdirSync(dirname(genContractPath), { recursive: true });
|
|
546
|
+
writeFileSync(genContractPath, `export type ApiContract = Record<string, never>;\n`);
|
|
547
|
+
}
|
|
458
548
|
}
|
|
459
549
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
550
|
+
if (has("api")) {
|
|
551
|
+
const pluginsClientGenPath = join(destination, "api", "src", "lib", "plugins-types.gen.ts");
|
|
552
|
+
if (!existsSync(pluginsClientGenPath)) {
|
|
553
|
+
mkdirSync(dirname(pluginsClientGenPath), { recursive: true });
|
|
554
|
+
writeFileSync(
|
|
555
|
+
pluginsClientGenPath,
|
|
556
|
+
`import type { ContractRouterClient, AnyContractRouter } from "@orpc/contract";\ntype ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\nexport type PluginsClient = Record<string, never>;\n`,
|
|
557
|
+
);
|
|
558
|
+
}
|
|
467
559
|
}
|
|
468
560
|
|
|
469
561
|
const authTypesContent = generateAuthTypesTemplate();
|
|
470
|
-
const authTypesPaths = [
|
|
471
|
-
|
|
472
|
-
join(destination, "
|
|
473
|
-
|
|
474
|
-
if (
|
|
562
|
+
const authTypesPaths: string[] = [];
|
|
563
|
+
if (has("ui")) {
|
|
564
|
+
authTypesPaths.push(join(destination, "ui", "src", "lib", "auth-types.gen.ts"));
|
|
565
|
+
}
|
|
566
|
+
if (has("api")) {
|
|
567
|
+
authTypesPaths.push(join(destination, "api", "src", "lib", "auth-types.gen.ts"));
|
|
568
|
+
}
|
|
569
|
+
if (has("host") && existsSync(join(destination, "host", "src"))) {
|
|
475
570
|
authTypesPaths.push(join(destination, "host", "src", "lib", "auth-types.gen.ts"));
|
|
476
571
|
}
|
|
477
572
|
for (const authTypesGenPath of authTypesPaths) {
|
|
@@ -559,55 +654,51 @@ export async function scaffoldMinimalProject(
|
|
|
559
654
|
account?: string;
|
|
560
655
|
domain?: string;
|
|
561
656
|
plugins?: string[];
|
|
562
|
-
|
|
657
|
+
overrides: OverrideSection[];
|
|
658
|
+
repository?: string;
|
|
563
659
|
},
|
|
564
660
|
): Promise<number> {
|
|
565
661
|
mkdirSync(destination, { recursive: true });
|
|
566
662
|
|
|
663
|
+
const has = (section: OverrideSection) => opts.overrides.includes(section);
|
|
664
|
+
|
|
567
665
|
const config: Record<string, unknown> = {
|
|
568
666
|
extends: `bos://${opts.extendsAccount}/${opts.extendsGateway}`,
|
|
569
667
|
account: opts.account || opts.extendsAccount,
|
|
570
668
|
...(opts.domain ? { domain: opts.domain } : {}),
|
|
669
|
+
...(opts.repository ? { repository: opts.repository } : {}),
|
|
571
670
|
};
|
|
572
671
|
|
|
573
672
|
if (parentConfig.app && typeof parentConfig.app === "object") {
|
|
574
673
|
const app: Record<string, unknown> = {};
|
|
575
674
|
const parentApp = parentConfig.app as Record<string, Record<string, unknown>>;
|
|
576
675
|
|
|
577
|
-
if (parentApp.host) {
|
|
676
|
+
if (has("host") && parentApp.host) {
|
|
578
677
|
app.host = { ...parentApp.host };
|
|
579
|
-
|
|
580
|
-
delete host.production;
|
|
581
|
-
delete host.integrity;
|
|
678
|
+
stripProductionFields(app.host as Record<string, unknown>);
|
|
582
679
|
}
|
|
583
680
|
|
|
584
|
-
if (parentApp.ui) {
|
|
681
|
+
if (has("ui") && parentApp.ui) {
|
|
585
682
|
app.ui = { ...parentApp.ui };
|
|
586
|
-
|
|
587
|
-
delete ui.production;
|
|
588
|
-
delete ui.integrity;
|
|
589
|
-
delete ui.ssr;
|
|
590
|
-
delete ui.ssrIntegrity;
|
|
683
|
+
stripProductionFields(app.ui as Record<string, unknown>);
|
|
591
684
|
}
|
|
592
685
|
|
|
593
|
-
if (parentApp.api) {
|
|
686
|
+
if (has("api") && parentApp.api) {
|
|
594
687
|
app.api = { ...parentApp.api };
|
|
595
|
-
|
|
596
|
-
delete api.production;
|
|
597
|
-
delete api.integrity;
|
|
688
|
+
stripProductionFields(app.api as Record<string, unknown>);
|
|
598
689
|
}
|
|
599
690
|
|
|
600
|
-
if (parentApp.auth) {
|
|
691
|
+
if (has("plugins") && parentApp.auth) {
|
|
601
692
|
app.auth = { ...parentApp.auth };
|
|
602
|
-
|
|
603
|
-
delete auth.production;
|
|
604
|
-
delete auth.integrity;
|
|
693
|
+
stripProductionFields(app.auth as Record<string, unknown>);
|
|
605
694
|
}
|
|
606
695
|
|
|
607
|
-
|
|
696
|
+
if (Object.keys(app).length > 0) {
|
|
697
|
+
config.app = app;
|
|
698
|
+
}
|
|
608
699
|
}
|
|
609
700
|
|
|
610
|
-
if (opts.plugins && opts.plugins.length > 0 && parentConfig.plugins) {
|
|
701
|
+
if (has("plugins") && opts.plugins && opts.plugins.length > 0 && parentConfig.plugins) {
|
|
611
702
|
const plugins: Record<string, unknown> = {};
|
|
612
703
|
for (const key of opts.plugins) {
|
|
613
704
|
const parentPlugin = (parentConfig.plugins as Record<string, unknown>)?.[key];
|
|
@@ -616,8 +707,7 @@ export async function scaffoldMinimalProject(
|
|
|
616
707
|
plugins[key] = { extends: parentPlugin };
|
|
617
708
|
} else {
|
|
618
709
|
const pluginCopy = { ...(parentPlugin as Record<string, unknown>) };
|
|
619
|
-
|
|
620
|
-
delete pluginCopy.integrity;
|
|
710
|
+
stripProductionFields(pluginCopy);
|
|
621
711
|
plugins[key] = pluginCopy;
|
|
622
712
|
}
|
|
623
713
|
}
|
|
@@ -627,6 +717,16 @@ export async function scaffoldMinimalProject(
|
|
|
627
717
|
|
|
628
718
|
await saveBosConfig(destination, config);
|
|
629
719
|
|
|
720
|
+
const workspacePackages: string[] = [];
|
|
721
|
+
for (const section of opts.overrides) {
|
|
722
|
+
workspacePackages.push(...OVERRIDE_WORKSPACE_MAP[section]);
|
|
723
|
+
}
|
|
724
|
+
if (has("plugins") && opts.plugins) {
|
|
725
|
+
for (const plugin of opts.plugins) {
|
|
726
|
+
workspacePackages.push(`plugins/${plugin}`);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
630
730
|
const pkg: Record<string, unknown> = {
|
|
631
731
|
name: opts.domain || opts.extendsGateway,
|
|
632
732
|
private: true,
|
|
@@ -649,13 +749,13 @@ export async function scaffoldMinimalProject(
|
|
|
649
749
|
},
|
|
650
750
|
devDependencies: {},
|
|
651
751
|
workspaces: {
|
|
652
|
-
packages:
|
|
752
|
+
packages: workspacePackages,
|
|
653
753
|
catalog: {},
|
|
654
754
|
},
|
|
655
755
|
};
|
|
656
756
|
writeFileSync(join(destination, "package.json"), `${JSON.stringify(pkg, null, 2)}\n`);
|
|
657
757
|
|
|
658
|
-
const envExample = generateEnvExample(parentConfig);
|
|
758
|
+
const envExample = generateEnvExample(parentConfig, opts.overrides);
|
|
659
759
|
if (envExample) {
|
|
660
760
|
writeFileSync(join(destination, ".env.example"), envExample);
|
|
661
761
|
}
|
|
@@ -714,11 +814,18 @@ export async function writeInitSnapshot(
|
|
|
714
814
|
extendsGateway: string,
|
|
715
815
|
sourceDir: string,
|
|
716
816
|
patterns: string[],
|
|
717
|
-
options: {
|
|
817
|
+
options: {
|
|
818
|
+
overrides: OverrideSection[];
|
|
819
|
+
plugins?: string[];
|
|
820
|
+
pluginRoutes?: Record<string, string[]>;
|
|
821
|
+
},
|
|
718
822
|
): Promise<void> {
|
|
719
|
-
const effectivePatterns = options.
|
|
720
|
-
|
|
721
|
-
|
|
823
|
+
const effectivePatterns = filterPatternsByOverrides(patterns, options.overrides, options.plugins);
|
|
824
|
+
|
|
825
|
+
const has = (section: OverrideSection) => options.overrides.includes(section);
|
|
826
|
+
if (has("host") && !effectivePatterns.some((p) => p.startsWith("host/") || p === "host/**")) {
|
|
827
|
+
effectivePatterns.push("host/**");
|
|
828
|
+
}
|
|
722
829
|
|
|
723
830
|
const excludedRoutePatterns: string[] = [];
|
|
724
831
|
if (options.pluginRoutes) {
|
|
@@ -817,10 +924,17 @@ export async function execCommand(command: string, args: string[], cwd?: string)
|
|
|
817
924
|
await execa(command, args, { cwd, stdio: "pipe" });
|
|
818
925
|
}
|
|
819
926
|
|
|
820
|
-
function generateEnvExample(config: BosConfigInput): string {
|
|
927
|
+
function generateEnvExample(config: BosConfigInput, overrides: OverrideSection[]): string {
|
|
928
|
+
const has = (section: OverrideSection) => overrides.includes(section);
|
|
929
|
+
|
|
821
930
|
const lines: string[] = ["# Environment variables"];
|
|
822
|
-
const collectSecrets = (
|
|
931
|
+
const collectSecrets = (
|
|
932
|
+
obj: Record<string, unknown>,
|
|
933
|
+
includeSection: boolean,
|
|
934
|
+
prefix = "",
|
|
935
|
+
): void => {
|
|
823
936
|
for (const [key, value] of Object.entries(obj)) {
|
|
937
|
+
if (!includeSection) continue;
|
|
824
938
|
if (key === "secrets" && Array.isArray(value)) {
|
|
825
939
|
for (const secret of value) {
|
|
826
940
|
if (typeof secret === "string") {
|
|
@@ -834,16 +948,28 @@ function generateEnvExample(config: BosConfigInput): string {
|
|
|
834
948
|
}
|
|
835
949
|
}
|
|
836
950
|
} else if (isPlainObject(value) && key !== "extends") {
|
|
837
|
-
collectSecrets(value as Record<string, unknown>, `${prefix}${key}.`);
|
|
951
|
+
collectSecrets(value as Record<string, unknown>, includeSection, `${prefix}${key}.`);
|
|
838
952
|
}
|
|
839
953
|
}
|
|
840
954
|
};
|
|
841
955
|
|
|
842
956
|
if (config.app && typeof config.app === "object") {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
collectSecrets(
|
|
957
|
+
const app = config.app as Record<string, unknown>;
|
|
958
|
+
collectSecrets(app, has("host"), "host.");
|
|
959
|
+
collectSecrets(app, has("ui"), "ui.");
|
|
960
|
+
collectSecrets(app, has("api"), "api.");
|
|
961
|
+
collectSecrets(app, has("plugins"), "auth.");
|
|
962
|
+
}
|
|
963
|
+
if (has("plugins") && config.plugins && typeof config.plugins === "object") {
|
|
964
|
+
for (const [pluginKey, pluginVal] of Object.entries(
|
|
965
|
+
config.plugins as Record<string, unknown>,
|
|
966
|
+
)) {
|
|
967
|
+
if (isPlainObject(pluginVal)) {
|
|
968
|
+
collectSecrets(pluginVal as Record<string, unknown>, true);
|
|
969
|
+
} else if (typeof pluginVal === "string") {
|
|
970
|
+
lines.push(`# Plugin '${pluginKey}' extends ${pluginVal}`);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
847
973
|
}
|
|
848
974
|
|
|
849
975
|
lines.push("BETTER_AUTH_SECRET=generate-a-secret-here");
|
package/src/cli/parse.ts
CHANGED
|
@@ -29,6 +29,10 @@ function isBooleanSchema(schema: SchemaLike): boolean {
|
|
|
29
29
|
return unwrap(schema)._def?.type === "boolean";
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function isArraySchema(schema: SchemaLike): boolean {
|
|
33
|
+
return unwrap(schema)._def?.type === "array";
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
function coerceValue(raw: string, schema: SchemaLike): unknown {
|
|
33
37
|
const inner = unwrap(schema);
|
|
34
38
|
switch (inner._def?.type) {
|
|
@@ -100,6 +104,19 @@ export function parseCommandInput(descriptor: CommandDescriptor, argv: string[])
|
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
const next = inline ?? argv[i + 1];
|
|
107
|
+
|
|
108
|
+
if (isArraySchema(fieldSchema)) {
|
|
109
|
+
if (next === undefined || next.startsWith("--")) {
|
|
110
|
+
throw new Error(`Missing value for ${flag}`);
|
|
111
|
+
}
|
|
112
|
+
input[fieldName] = next
|
|
113
|
+
.split(",")
|
|
114
|
+
.map((s: string) => s.trim())
|
|
115
|
+
.filter(Boolean);
|
|
116
|
+
if (!inline) i += 1;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
103
120
|
if (next === undefined || next.startsWith("--")) {
|
|
104
121
|
throw new Error(`Missing value for ${flag}`);
|
|
105
122
|
}
|
package/src/cli/prompts.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import * as p from "@clack/prompts";
|
|
3
|
+
import type { OverrideSection } from "../contract";
|
|
3
4
|
|
|
4
5
|
function parseExtendsRef(ref: string): { account: string; gateway: string } | null {
|
|
5
6
|
const normalized = ref.startsWith("bos://") ? ref : `bos://${ref}`;
|
|
@@ -17,13 +18,20 @@ function deriveAccountFromExtends(domain: string, extendsAccount: string): strin
|
|
|
17
18
|
return `${firstSegment}.${suffix}`;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
const OVERRIDE_OPTIONS: { value: OverrideSection; label: string; hint: string }[] = [
|
|
22
|
+
{ value: "ui", label: "ui", hint: "Override UI with local source" },
|
|
23
|
+
{ value: "api", label: "api", hint: "Override API with local source" },
|
|
24
|
+
{ value: "host", label: "host", hint: "Override host with local source" },
|
|
25
|
+
{ value: "plugins", label: "plugins", hint: "Override selected plugins with local source" },
|
|
26
|
+
];
|
|
27
|
+
|
|
20
28
|
export async function promptInitOptions(input: {
|
|
21
29
|
extends?: string;
|
|
22
30
|
directory?: string;
|
|
23
31
|
account?: string;
|
|
24
32
|
domain?: string;
|
|
25
33
|
plugins?: string[];
|
|
26
|
-
|
|
34
|
+
overrides?: OverrideSection[];
|
|
27
35
|
parentPluginKeys?: string[];
|
|
28
36
|
}): Promise<{
|
|
29
37
|
extendsAccount: string;
|
|
@@ -32,7 +40,7 @@ export async function promptInitOptions(input: {
|
|
|
32
40
|
account?: string;
|
|
33
41
|
domain?: string;
|
|
34
42
|
plugins: string[];
|
|
35
|
-
|
|
43
|
+
overrides: OverrideSection[];
|
|
36
44
|
}> {
|
|
37
45
|
p.intro("Let's build an app...");
|
|
38
46
|
|
|
@@ -78,34 +86,43 @@ export async function promptInitOptions(input: {
|
|
|
78
86
|
|
|
79
87
|
const directory = input.directory || domain || extendsGateway;
|
|
80
88
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
const overrides =
|
|
90
|
+
input.overrides ??
|
|
91
|
+
((await p.multiselect({
|
|
92
|
+
message: "Which sections to override locally?",
|
|
93
|
+
options: OVERRIDE_OPTIONS,
|
|
94
|
+
initialValues: ["ui", "api"] as OverrideSection[],
|
|
95
|
+
required: false,
|
|
96
|
+
})) as OverrideSection[]);
|
|
97
|
+
|
|
98
|
+
if (p.isCancel(overrides)) process.exit(0);
|
|
99
|
+
|
|
100
|
+
let plugins: string[] = [];
|
|
101
|
+
if (overrides.includes("plugins")) {
|
|
102
|
+
const parentPlugins = input.parentPluginKeys ?? [];
|
|
103
|
+
const pluginOptions =
|
|
104
|
+
parentPlugins.length > 0 ? parentPlugins.map((key) => ({ value: key, label: key })) : [];
|
|
105
|
+
|
|
106
|
+
plugins =
|
|
107
|
+
input.plugins ??
|
|
108
|
+
(pluginOptions.length > 0
|
|
109
|
+
? ((await p.multiselect({
|
|
110
|
+
message: "Select plugins to include:",
|
|
111
|
+
options: pluginOptions,
|
|
112
|
+
required: false,
|
|
113
|
+
})) as string[])
|
|
114
|
+
: []);
|
|
115
|
+
|
|
116
|
+
if (p.isCancel(plugins)) process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const go = await p.confirm({
|
|
120
|
+
message: "GO!",
|
|
121
|
+
initialValue: true,
|
|
122
|
+
});
|
|
104
123
|
|
|
105
124
|
if (p.isCancel(go) || !go) process.exit(0);
|
|
106
125
|
|
|
107
|
-
const withHost = input.withHost ?? false;
|
|
108
|
-
|
|
109
126
|
return {
|
|
110
127
|
extendsAccount,
|
|
111
128
|
extendsGateway,
|
|
@@ -113,6 +130,6 @@ export async function promptInitOptions(input: {
|
|
|
113
130
|
account: account || undefined,
|
|
114
131
|
domain: domain || undefined,
|
|
115
132
|
plugins,
|
|
116
|
-
|
|
133
|
+
overrides,
|
|
117
134
|
};
|
|
118
135
|
}
|
package/src/cli/sync.ts
CHANGED
package/src/cli.ts
CHANGED
|
@@ -70,9 +70,14 @@ async function warnIfOutdated(client: any, command: string): Promise<void> {
|
|
|
70
70
|
const status = await client.status();
|
|
71
71
|
if (status.status === "error" || !status.packages) return;
|
|
72
72
|
|
|
73
|
+
const frameworkPackages = ["everything-dev", "every-plugin"];
|
|
74
|
+
|
|
73
75
|
const outdated = status.packages.filter(
|
|
74
76
|
(p: { name: string; installed?: string; latest?: string }) =>
|
|
75
|
-
p.installed &&
|
|
77
|
+
p.installed &&
|
|
78
|
+
p.latest &&
|
|
79
|
+
normalizeVersion(p.installed) !== normalizeVersion(p.latest) &&
|
|
80
|
+
frameworkPackages.includes(p.name),
|
|
76
81
|
);
|
|
77
82
|
|
|
78
83
|
if (outdated.length === 0) return;
|
|
@@ -162,6 +167,8 @@ async function main() {
|
|
|
162
167
|
console.log(` ${colors.dim("Directory:")} ${result.directory}`);
|
|
163
168
|
if (result.account) console.log(` ${colors.dim("Account:")} ${result.account}`);
|
|
164
169
|
if (result.domain) console.log(` ${colors.dim("Domain:")} ${result.domain}`);
|
|
170
|
+
if (result.overrides && result.overrides.length > 0)
|
|
171
|
+
console.log(` ${colors.dim("Overrides:")} ${result.overrides.join(", ")}`);
|
|
165
172
|
if (result.plugins && result.plugins.length > 0)
|
|
166
173
|
console.log(` ${colors.dim("Plugins:")} ${result.plugins.join(", ")}`);
|
|
167
174
|
console.log(` ${colors.dim("Files copied:")} ${result.filesCopied}`);
|