everything-dev 1.16.1 → 1.16.3
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/api-contract.cjs +16 -5
- package/dist/api-contract.cjs.map +1 -1
- package/dist/api-contract.mjs +16 -5
- package/dist/api-contract.mjs.map +1 -1
- package/dist/cli/infra.cjs +127 -0
- package/dist/cli/infra.cjs.map +1 -0
- package/dist/cli/infra.mjs +124 -0
- package/dist/cli/infra.mjs.map +1 -0
- package/dist/cli/init.cjs +9 -0
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +2 -1
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +2 -1
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +9 -1
- package/dist/cli/init.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/timing.cjs +30 -0
- package/dist/cli/timing.cjs.map +1 -0
- package/dist/cli/timing.mjs +27 -0
- package/dist/cli/timing.mjs.map +1 -0
- package/dist/cli/upgrade.cjs +66 -47
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs +66 -47
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/cli.cjs +9 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +9 -0
- package/dist/cli.mjs.map +1 -1
- package/dist/components/dev-view.cjs +1 -1
- package/dist/components/dev-view.mjs +1 -1
- package/dist/components/streaming-view.cjs +1 -1
- package/dist/components/streaming-view.mjs +1 -1
- package/dist/contract.cjs +7 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +22 -1
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +22 -1
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +7 -1
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-session.cjs +5 -3
- package/dist/dev-session.cjs.map +1 -1
- package/dist/dev-session.mjs +3 -3
- package/dist/dev-session.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/near-cli.cjs +1 -1
- package/dist/near-cli.mjs +1 -1
- package/dist/orchestrator.cjs +1 -1
- package/dist/orchestrator.mjs +1 -1
- package/dist/plugin.cjs +56 -39
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +8 -0
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +8 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +54 -37
- package/dist/plugin.mjs.map +1 -1
- package/package.json +1 -1
- package/src/api-contract.ts +21 -3
- package/src/cli/infra.ts +190 -0
- package/src/cli/init.ts +4 -0
- package/src/cli/sync.ts +9 -0
- package/src/cli/timing.ts +36 -0
- package/src/cli/upgrade.ts +82 -53
- package/src/cli.ts +15 -0
- package/src/contract.ts +8 -0
- package/src/dev-session.ts +1 -1
- package/src/plugin.ts +98 -67
package/src/cli/infra.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
|
+
import type { RuntimeConfig } from "../types";
|
|
6
|
+
|
|
7
|
+
const POSTGRES_USER = "everythingdev";
|
|
8
|
+
const POSTGRES_PASSWORD = "everythingdev";
|
|
9
|
+
const API_DATABASE_SECRET = "API_DATABASE_URL";
|
|
10
|
+
const AUTH_DATABASE_SECRET = "AUTH_DATABASE_URL";
|
|
11
|
+
const BASE_DATABASE_PORT = 5434;
|
|
12
|
+
|
|
13
|
+
interface DatabaseSecretConfig {
|
|
14
|
+
secret: string;
|
|
15
|
+
slug: string;
|
|
16
|
+
port: number;
|
|
17
|
+
serviceName: string;
|
|
18
|
+
databaseName: string;
|
|
19
|
+
volumeName: string;
|
|
20
|
+
url: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function uniqueSecrets(values: Array<string | undefined>): string[] {
|
|
24
|
+
const secrets: string[] = [];
|
|
25
|
+
const seen = new Set<string>();
|
|
26
|
+
|
|
27
|
+
for (const value of values) {
|
|
28
|
+
if (!value || seen.has(value)) continue;
|
|
29
|
+
seen.add(value);
|
|
30
|
+
secrets.push(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return secrets;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getRuntimeSecrets(runtimeConfig: RuntimeConfig): string[] {
|
|
37
|
+
const pluginSecrets = Object.values(runtimeConfig.plugins ?? {}).flatMap(
|
|
38
|
+
(plugin) => plugin.secrets ?? [],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return uniqueSecrets([
|
|
42
|
+
API_DATABASE_SECRET,
|
|
43
|
+
AUTH_DATABASE_SECRET,
|
|
44
|
+
...(runtimeConfig.api.secrets ?? []),
|
|
45
|
+
...(runtimeConfig.auth?.secrets ?? []),
|
|
46
|
+
...pluginSecrets,
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeDatabaseSlug(secret: string): string {
|
|
51
|
+
return secret.replace(/_DATABASE_URL$/, "").toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildDatabaseConfigs(secrets: string[]): DatabaseSecretConfig[] {
|
|
55
|
+
const databaseSecrets = uniqueSecrets(
|
|
56
|
+
secrets.filter((secret) => secret.endsWith("_DATABASE_URL")),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const additionalSecrets = databaseSecrets
|
|
60
|
+
.filter((secret) => secret !== API_DATABASE_SECRET && secret !== AUTH_DATABASE_SECRET)
|
|
61
|
+
.sort((a, b) => a.localeCompare(b));
|
|
62
|
+
|
|
63
|
+
const orderedSecrets = [API_DATABASE_SECRET, AUTH_DATABASE_SECRET, ...additionalSecrets];
|
|
64
|
+
|
|
65
|
+
return orderedSecrets.map((secret, index) => {
|
|
66
|
+
const slug = normalizeDatabaseSlug(secret);
|
|
67
|
+
const port =
|
|
68
|
+
secret === API_DATABASE_SECRET
|
|
69
|
+
? 5432
|
|
70
|
+
: secret === AUTH_DATABASE_SECRET
|
|
71
|
+
? 5433
|
|
72
|
+
: BASE_DATABASE_PORT + index - 2;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
secret,
|
|
76
|
+
slug,
|
|
77
|
+
port,
|
|
78
|
+
serviceName: `postgres-${slug.replace(/_/g, "-")}`,
|
|
79
|
+
databaseName: `${slug}_db`,
|
|
80
|
+
volumeName: `postgres_${slug}_data`,
|
|
81
|
+
url: `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${slug}_db`,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function defaultSecretValue(
|
|
87
|
+
secret: string,
|
|
88
|
+
databases: Map<string, DatabaseSecretConfig>,
|
|
89
|
+
options: { forExample: boolean },
|
|
90
|
+
): string {
|
|
91
|
+
if (secret === "BETTER_AUTH_SECRET") {
|
|
92
|
+
return options.forExample ? "" : randomBytes(32).toString("base64url");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (secret === "CORS_ORIGIN") {
|
|
96
|
+
return "http://localhost:3000";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return databases.get(secret)?.url ?? "";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function renderEnvFile(
|
|
103
|
+
secrets: string[],
|
|
104
|
+
databases: DatabaseSecretConfig[],
|
|
105
|
+
options: { forExample: boolean },
|
|
106
|
+
): string {
|
|
107
|
+
const databaseMap = new Map(databases.map((entry) => [entry.secret, entry]));
|
|
108
|
+
const lines = [
|
|
109
|
+
"# Generated from configured bos secrets",
|
|
110
|
+
"# Update values as needed for your local environment",
|
|
111
|
+
"",
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
for (const secret of secrets) {
|
|
115
|
+
lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, options)}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return `${lines.join("\n")}\n`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function renderDockerCompose(databases: DatabaseSecretConfig[]): string {
|
|
122
|
+
const lines = [
|
|
123
|
+
"x-pg-common: &pg-common",
|
|
124
|
+
" image: postgres:17-alpine",
|
|
125
|
+
" environment: &pg-env",
|
|
126
|
+
` POSTGRES_USER: ${POSTGRES_USER}`,
|
|
127
|
+
` POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}`,
|
|
128
|
+
" healthcheck:",
|
|
129
|
+
' test: ["CMD-SHELL", "pg_isready -U everythingdev"]',
|
|
130
|
+
" interval: 3s",
|
|
131
|
+
" timeout: 3s",
|
|
132
|
+
" retries: 5",
|
|
133
|
+
"",
|
|
134
|
+
"services:",
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
for (const database of databases) {
|
|
138
|
+
lines.push(` ${database.serviceName}:`);
|
|
139
|
+
lines.push(" <<: *pg-common");
|
|
140
|
+
lines.push(" environment:");
|
|
141
|
+
lines.push(" <<: *pg-env");
|
|
142
|
+
lines.push(` POSTGRES_DB: ${database.databaseName}`);
|
|
143
|
+
lines.push(" ports:");
|
|
144
|
+
lines.push(` - "${database.port}:5432"`);
|
|
145
|
+
lines.push(" volumes:");
|
|
146
|
+
lines.push(` - ${database.volumeName}:/var/lib/postgresql/data`);
|
|
147
|
+
lines.push("");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
lines.push("volumes:");
|
|
151
|
+
for (const database of databases) {
|
|
152
|
+
lines.push(` ${database.volumeName}:`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return `${lines.join("\n")}\n`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function writeGeneratedInfra(configDir: string, runtimeConfig: RuntimeConfig): string[] {
|
|
159
|
+
const secrets = getRuntimeSecrets(runtimeConfig);
|
|
160
|
+
const databases = buildDatabaseConfigs(secrets);
|
|
161
|
+
const envExamplePath = join(configDir, ".env.example");
|
|
162
|
+
const dockerComposePath = join(configDir, "docker-compose.yml");
|
|
163
|
+
|
|
164
|
+
writeFileSync(envExamplePath, renderEnvFile(secrets, databases, { forExample: true }));
|
|
165
|
+
writeFileSync(dockerComposePath, renderDockerCompose(databases));
|
|
166
|
+
|
|
167
|
+
return secrets;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function ensureEnvFile(configDir: string): void {
|
|
171
|
+
const envPath = join(configDir, ".env");
|
|
172
|
+
const examplePath = join(configDir, ".env.example");
|
|
173
|
+
|
|
174
|
+
if (existsSync(envPath) || !existsSync(examplePath)) return;
|
|
175
|
+
|
|
176
|
+
const content = readFileSync(examplePath, "utf-8");
|
|
177
|
+
const lines = content.split("\n");
|
|
178
|
+
const secret = randomBytes(32).toString("base64url");
|
|
179
|
+
const updated = lines
|
|
180
|
+
.map((line) => {
|
|
181
|
+
if (/^BETTER_AUTH_SECRET=/.test(line)) {
|
|
182
|
+
return `BETTER_AUTH_SECRET=${secret}`;
|
|
183
|
+
}
|
|
184
|
+
return line;
|
|
185
|
+
})
|
|
186
|
+
.join("\n");
|
|
187
|
+
|
|
188
|
+
writeFileSync(envPath, updated);
|
|
189
|
+
p.log.info("Created .env from generated .env.example with generated BETTER_AUTH_SECRET");
|
|
190
|
+
}
|
package/src/cli/init.ts
CHANGED
|
@@ -609,6 +609,10 @@ export async function runTypesGen(destination: string): Promise<void> {
|
|
|
609
609
|
await execCommand("node_modules/.bin/bos", ["types", "gen"], destination);
|
|
610
610
|
}
|
|
611
611
|
|
|
612
|
+
export async function runDockerComposeUp(destination: string): Promise<void> {
|
|
613
|
+
await execCommand("docker", ["compose", "up", "-d", "--wait"], destination);
|
|
614
|
+
}
|
|
615
|
+
|
|
612
616
|
const WORKSPACE_LOCAL_PATHS: Record<string, string> = {
|
|
613
617
|
"everything-dev": "packages/everything-dev",
|
|
614
618
|
"every-plugin": "packages/every-plugin",
|
package/src/cli/sync.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "node:fs";
|
|
10
10
|
import { dirname, join } from "node:path";
|
|
11
11
|
import { glob } from "glob";
|
|
12
|
+
import { loadConfig } from "../config";
|
|
12
13
|
import type { SyncOptions, SyncResult } from "../contract";
|
|
13
14
|
import {
|
|
14
15
|
isPlainObject as isPlainObjectFromMerge,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
} from "../merge";
|
|
18
19
|
import type { BosPluginRef } from "../types";
|
|
19
20
|
import { isPathExcluded } from "../utils/path-match";
|
|
21
|
+
import { writeGeneratedInfra } from "./infra";
|
|
20
22
|
import {
|
|
21
23
|
personalizeConfig,
|
|
22
24
|
readTemplatekeep,
|
|
@@ -27,10 +29,12 @@ import {
|
|
|
27
29
|
import { readSnapshot, writeSnapshot } from "./snapshot";
|
|
28
30
|
|
|
29
31
|
const FRAMEWORK_OWNED_SYNC_FILES = new Set([
|
|
32
|
+
".env.example",
|
|
30
33
|
".gitignore",
|
|
31
34
|
"biome.json",
|
|
32
35
|
"bos.config.json",
|
|
33
36
|
"package.json",
|
|
37
|
+
"docker-compose.yml",
|
|
34
38
|
".github/renovate.json",
|
|
35
39
|
".github/workflows/ci.yml",
|
|
36
40
|
".github/workflows/release-sync.yml",
|
|
@@ -494,6 +498,11 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr
|
|
|
494
498
|
mode: "sync",
|
|
495
499
|
});
|
|
496
500
|
|
|
501
|
+
const syncedConfig = await loadConfig({ cwd: projectDir });
|
|
502
|
+
if (syncedConfig?.runtime) {
|
|
503
|
+
writeGeneratedInfra(projectDir, syncedConfig.runtime);
|
|
504
|
+
}
|
|
505
|
+
|
|
497
506
|
if (!options.noInstall) {
|
|
498
507
|
await runBunInstall(projectDir);
|
|
499
508
|
await runTypesGen(projectDir);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface PhaseTiming {
|
|
2
|
+
name: string;
|
|
3
|
+
durationMs: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export async function timePhase<T>(
|
|
7
|
+
timings: PhaseTiming[],
|
|
8
|
+
name: string,
|
|
9
|
+
fn: () => Promise<T>,
|
|
10
|
+
): Promise<T> {
|
|
11
|
+
const startedAt = Date.now();
|
|
12
|
+
try {
|
|
13
|
+
return await fn();
|
|
14
|
+
} finally {
|
|
15
|
+
timings.push({ name, durationMs: Date.now() - startedAt });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function sumPhaseDurations(timings: PhaseTiming[]): number {
|
|
20
|
+
return timings.reduce((total, timing) => total + timing.durationMs, 0);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function formatDuration(durationMs: number): string {
|
|
24
|
+
if (durationMs < 1000) {
|
|
25
|
+
return `${durationMs}ms`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (durationMs < 60_000) {
|
|
29
|
+
const seconds = durationMs / 1000;
|
|
30
|
+
return `${seconds.toFixed(seconds >= 10 ? 0 : 1)}s`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const minutes = Math.floor(durationMs / 60_000);
|
|
34
|
+
const seconds = Math.round((durationMs % 60_000) / 1000);
|
|
35
|
+
return `${minutes}m ${seconds}s`;
|
|
36
|
+
}
|
package/src/cli/upgrade.ts
CHANGED
|
@@ -3,12 +3,13 @@ import { join } from "node:path";
|
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
import { glob } from "glob";
|
|
6
|
-
import type { UpgradeOptions, UpgradeResult } from "../contract";
|
|
6
|
+
import type { PhaseTiming, UpgradeOptions, UpgradeResult } from "../contract";
|
|
7
7
|
import { resolveExtendsRef } from "../merge";
|
|
8
8
|
import { saveBosConfig } from "../utils/save-config";
|
|
9
9
|
import { readInstalledFrameworkVersion } from "./framework-version";
|
|
10
10
|
import { fetchParentConfig, runBunInstall, runTypesGen } from "./init";
|
|
11
11
|
import { syncTemplate } from "./sync";
|
|
12
|
+
import { timePhase } from "./timing";
|
|
12
13
|
|
|
13
14
|
const FRAMEWORK_PACKAGES = ["everything-dev", "every-plugin"];
|
|
14
15
|
|
|
@@ -333,38 +334,52 @@ export async function upgradeTemplate(
|
|
|
333
334
|
projectDir: string,
|
|
334
335
|
options: UpgradeOptions,
|
|
335
336
|
): Promise<UpgradeResult> {
|
|
337
|
+
const timings: PhaseTiming[] = [];
|
|
336
338
|
const pkgPath = join(projectDir, "package.json");
|
|
337
339
|
if (!existsSync(pkgPath)) {
|
|
338
340
|
return {
|
|
339
341
|
status: "error",
|
|
340
342
|
packages: [],
|
|
343
|
+
timings,
|
|
341
344
|
error: "No package.json found in current directory",
|
|
342
345
|
};
|
|
343
346
|
}
|
|
344
347
|
|
|
345
|
-
const
|
|
348
|
+
const { packages, catalogVersionUpdates } = await timePhase(
|
|
349
|
+
timings,
|
|
350
|
+
"check package versions",
|
|
351
|
+
async () => {
|
|
352
|
+
const nextPackages: UpgradeResult["packages"] = [];
|
|
346
353
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
354
|
+
for (const name of FRAMEWORK_PACKAGES) {
|
|
355
|
+
const installed = readInstalledVersion(projectDir, name);
|
|
356
|
+
const latest = await fetchLatestNpmVersion(name);
|
|
350
357
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
358
|
+
if (!latest) {
|
|
359
|
+
nextPackages.push({ name, from: installed, to: installed ?? "unknown" });
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
355
362
|
|
|
356
|
-
|
|
357
|
-
|
|
363
|
+
nextPackages.push({ name, from: installed, to: latest });
|
|
364
|
+
}
|
|
358
365
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
366
|
+
const nextCatalogVersionUpdates: Array<{
|
|
367
|
+
name: string;
|
|
368
|
+
from: string | undefined;
|
|
369
|
+
to: string;
|
|
370
|
+
}> = [];
|
|
371
|
+
for (const name of CATALOG_TOOL_PACKAGES) {
|
|
372
|
+
const installed = readInstalledVersion(projectDir, name);
|
|
373
|
+
if (!installed) continue;
|
|
374
|
+
const latest = await fetchLatestNpmVersion(name);
|
|
375
|
+
if (!latest) continue;
|
|
376
|
+
if (installed === latest) continue;
|
|
377
|
+
nextCatalogVersionUpdates.push({ name, from: installed, to: latest });
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return { packages: nextPackages, catalogVersionUpdates: nextCatalogVersionUpdates };
|
|
381
|
+
},
|
|
382
|
+
);
|
|
368
383
|
|
|
369
384
|
const hasFrameworkUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);
|
|
370
385
|
const hasCatalogUpdates = catalogVersionUpdates.length > 0;
|
|
@@ -372,7 +387,11 @@ export async function upgradeTemplate(
|
|
|
372
387
|
|
|
373
388
|
if (options.dryRun) {
|
|
374
389
|
let changelogUrl: string | undefined;
|
|
375
|
-
const pluginOptions = options.noSync
|
|
390
|
+
const pluginOptions = options.noSync
|
|
391
|
+
? null
|
|
392
|
+
: await timePhase(timings, "discover parent plugins", () =>
|
|
393
|
+
loadParentPluginOptions(projectDir),
|
|
394
|
+
);
|
|
376
395
|
if (hasUpdates) {
|
|
377
396
|
const configPath = join(projectDir, "bos.config.json");
|
|
378
397
|
let parentConfig: Record<string, unknown> | null = null;
|
|
@@ -394,59 +413,68 @@ export async function upgradeTemplate(
|
|
|
394
413
|
...catalogVersionUpdates.map((u) => ({ name: u.name, from: u.from, to: u.to })),
|
|
395
414
|
],
|
|
396
415
|
availablePlugins: pluginOptions?.newPluginKeys,
|
|
416
|
+
timings,
|
|
397
417
|
changelogUrl,
|
|
398
418
|
};
|
|
399
419
|
}
|
|
400
420
|
|
|
401
|
-
|
|
402
|
-
if (pkg.from !== undefined && pkg.from !== pkg.to) {
|
|
403
|
-
updateRootPackageVersion(projectDir, pkg.name, pkg.to);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
for (const update of catalogVersionUpdates) {
|
|
408
|
-
updateRootCatalogVersion(projectDir, update.name, update.to);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);
|
|
412
|
-
for (const pkgPath of workspacePkgPaths) {
|
|
421
|
+
await timePhase(timings, "apply package updates", async () => {
|
|
413
422
|
for (const pkg of packages) {
|
|
414
423
|
if (pkg.from !== undefined && pkg.from !== pkg.to) {
|
|
415
|
-
|
|
424
|
+
updateRootPackageVersion(projectDir, pkg.name, pkg.to);
|
|
416
425
|
}
|
|
417
426
|
}
|
|
427
|
+
|
|
418
428
|
for (const update of catalogVersionUpdates) {
|
|
419
|
-
|
|
429
|
+
updateRootCatalogVersion(projectDir, update.name, update.to);
|
|
420
430
|
}
|
|
421
|
-
|
|
431
|
+
|
|
432
|
+
const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);
|
|
433
|
+
for (const pkgPath of workspacePkgPaths) {
|
|
434
|
+
for (const pkg of packages) {
|
|
435
|
+
if (pkg.from !== undefined && pkg.from !== pkg.to) {
|
|
436
|
+
updateWorkspacePackageRefInFile(pkgPath, pkg.name);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
for (const update of catalogVersionUpdates) {
|
|
440
|
+
updateWorkspacePackageRefInFile(pkgPath, update.name);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
});
|
|
422
444
|
|
|
423
445
|
let syncResult: UpgradeResult["sync"];
|
|
424
446
|
let addedPlugins: string[] = [];
|
|
425
447
|
if (!options.noSync) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
syncResult = await syncTemplate(projectDir, {
|
|
431
|
-
dryRun: false,
|
|
432
|
-
force: options.force,
|
|
433
|
-
noInstall: true,
|
|
448
|
+
addedPlugins = await timePhase(timings, "discover parent plugins", async () => {
|
|
449
|
+
if (options.dryRun) return [];
|
|
450
|
+
return addSelectedParentPlugins(projectDir);
|
|
434
451
|
});
|
|
452
|
+
|
|
453
|
+
syncResult = await timePhase(timings, "sync template", () =>
|
|
454
|
+
syncTemplate(projectDir, {
|
|
455
|
+
dryRun: false,
|
|
456
|
+
force: options.force,
|
|
457
|
+
noInstall: true,
|
|
458
|
+
}),
|
|
459
|
+
);
|
|
435
460
|
}
|
|
436
461
|
|
|
437
462
|
if ((hasUpdates || addedPlugins.length > 0) && !options.noInstall) {
|
|
438
|
-
await runBunInstall(projectDir);
|
|
439
|
-
await runTypesGen(projectDir);
|
|
463
|
+
await timePhase(timings, "install dependencies", () => runBunInstall(projectDir));
|
|
464
|
+
await timePhase(timings, "generate types", () => runTypesGen(projectDir));
|
|
440
465
|
}
|
|
441
466
|
|
|
442
|
-
const migratedFiles = await
|
|
443
|
-
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
467
|
+
const migratedFiles = await timePhase(timings, "clean obsolete files", async () => {
|
|
468
|
+
const nextMigratedFiles = await rewriteLegacyUiImports(projectDir);
|
|
469
|
+
for (const file of OBSOLETE_FILES) {
|
|
470
|
+
const filePath = join(projectDir, file);
|
|
471
|
+
if (existsSync(filePath)) {
|
|
472
|
+
rmSync(filePath);
|
|
473
|
+
nextMigratedFiles.push(file);
|
|
474
|
+
}
|
|
448
475
|
}
|
|
449
|
-
|
|
476
|
+
return nextMigratedFiles;
|
|
477
|
+
});
|
|
450
478
|
|
|
451
479
|
let changelogUrl: string | undefined;
|
|
452
480
|
const mainPkg = packages.find((p) => p.name === "everything-dev");
|
|
@@ -470,6 +498,7 @@ export async function upgradeTemplate(
|
|
|
470
498
|
sync: syncResult,
|
|
471
499
|
migrated: migratedFiles.length > 0 ? migratedFiles : undefined,
|
|
472
500
|
selectedPlugins: addedPlugins.length > 0 ? addedPlugins : undefined,
|
|
501
|
+
timings,
|
|
473
502
|
changelogUrl,
|
|
474
503
|
};
|
|
475
504
|
}
|
package/src/cli.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { findCommandDescriptor } from "./cli/catalog";
|
|
3
3
|
import { printHelp } from "./cli/help";
|
|
4
4
|
import { parseCommandInput } from "./cli/parse";
|
|
5
|
+
import { formatDuration, sumPhaseDurations } from "./cli/timing";
|
|
5
6
|
import { findConfigPath } from "./config";
|
|
6
7
|
import bosPlugin from "./plugin";
|
|
7
8
|
import { createPluginRuntime } from "./sdk";
|
|
@@ -50,6 +51,18 @@ function normalizeVersion(v: string): string {
|
|
|
50
51
|
return v.replace(/^[\^~>=v]+/, "").trim();
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
function printTimingSummary(timings: Array<{ name: string; durationMs: number }> | undefined) {
|
|
55
|
+
if (!timings || timings.length === 0) return;
|
|
56
|
+
|
|
57
|
+
console.log(` ${colors.dim("Timings:")}`);
|
|
58
|
+
for (const timing of timings) {
|
|
59
|
+
console.log(` ${colors.dim(timing.name.padEnd(22))} ${formatDuration(timing.durationMs)}`);
|
|
60
|
+
}
|
|
61
|
+
console.log(
|
|
62
|
+
` ${colors.dim("total".padEnd(22))} ${formatDuration(sumPhaseDurations(timings))}`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
async function warnIfOutdated(client: any, command: string): Promise<void> {
|
|
54
67
|
if (!["dev", "build", "start"].includes(command)) return;
|
|
55
68
|
|
|
@@ -152,6 +165,7 @@ async function main() {
|
|
|
152
165
|
if (result.plugins && result.plugins.length > 0)
|
|
153
166
|
console.log(` ${colors.dim("Plugins:")} ${result.plugins.join(", ")}`);
|
|
154
167
|
console.log(` ${colors.dim("Files copied:")} ${result.filesCopied}`);
|
|
168
|
+
printTimingSummary(result.timings);
|
|
155
169
|
console.log();
|
|
156
170
|
console.log(colors.dim(" Next steps:"));
|
|
157
171
|
console.log(colors.dim(` cd ${result.directory}`));
|
|
@@ -246,6 +260,7 @@ async function main() {
|
|
|
246
260
|
if (result.selectedPlugins && result.selectedPlugins.length > 0) {
|
|
247
261
|
console.log(` ${colors.dim("Added plugins:")} ${result.selectedPlugins.join(", ")}`);
|
|
248
262
|
}
|
|
263
|
+
printTimingSummary(result.timings);
|
|
249
264
|
if (result.sync) {
|
|
250
265
|
const sync = result.sync;
|
|
251
266
|
if (sync.updated.length > 0) {
|
package/src/contract.ts
CHANGED
|
@@ -156,6 +156,11 @@ export const InitOptionsSchema = z.object({
|
|
|
156
156
|
noInstall: z.boolean().default(false),
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
+
export const PhaseTimingSchema = z.object({
|
|
160
|
+
name: z.string(),
|
|
161
|
+
durationMs: z.number(),
|
|
162
|
+
});
|
|
163
|
+
|
|
159
164
|
export const InitResultSchema = z.object({
|
|
160
165
|
status: z.enum(["initialized", "error"]),
|
|
161
166
|
directory: z.string(),
|
|
@@ -166,6 +171,7 @@ export const InitResultSchema = z.object({
|
|
|
166
171
|
extends: z.string(),
|
|
167
172
|
plugins: z.array(z.string()).optional(),
|
|
168
173
|
filesCopied: z.number(),
|
|
174
|
+
timings: z.array(PhaseTimingSchema).optional(),
|
|
169
175
|
error: z.string().optional(),
|
|
170
176
|
});
|
|
171
177
|
|
|
@@ -203,6 +209,7 @@ export const UpgradeResultSchema = z.object({
|
|
|
203
209
|
migrated: z.array(z.string()).optional(),
|
|
204
210
|
availablePlugins: z.array(z.string()).optional(),
|
|
205
211
|
selectedPlugins: z.array(z.string()).optional(),
|
|
212
|
+
timings: z.array(PhaseTimingSchema).optional(),
|
|
206
213
|
changelogUrl: z.string().optional(),
|
|
207
214
|
error: z.string().optional(),
|
|
208
215
|
});
|
|
@@ -307,6 +314,7 @@ export type KeyPublishOptions = z.infer<typeof KeyPublishOptionsSchema>;
|
|
|
307
314
|
export type KeyPublishResult = z.infer<typeof KeyPublishResultSchema>;
|
|
308
315
|
export type InitOptions = z.infer<typeof InitOptionsSchema>;
|
|
309
316
|
export type InitResult = z.infer<typeof InitResultSchema>;
|
|
317
|
+
export type PhaseTiming = z.infer<typeof PhaseTimingSchema>;
|
|
310
318
|
export type SyncOptions = z.infer<typeof SyncOptionsSchema>;
|
|
311
319
|
export type SyncResult = z.infer<typeof SyncResultSchema>;
|
|
312
320
|
export type UpgradeOptions = z.infer<typeof UpgradeOptionsSchema>;
|
package/src/dev-session.ts
CHANGED