everything-dev 1.27.0 → 1.28.1
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/infra.cjs +1 -1
- package/dist/cli/infra.mjs +1 -1
- package/dist/cli/init.cjs +34 -9
- 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 +34 -9
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/prompts.cjs +28 -24
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +27 -24
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/sync.cjs +40 -3
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +40 -3
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli.cjs +187 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +186 -11
- package/dist/cli.mjs.map +1 -1
- package/dist/config.cjs +1 -0
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +1 -0
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +1 -1
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +38 -34
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +38 -34
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +1 -0
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-session.cjs +0 -1
- package/dist/dev-session.mjs +1 -1
- package/dist/index.cjs +0 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +0 -1
- 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 +183 -151
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +67 -34
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +66 -34
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +173 -142
- package/dist/plugin.mjs.map +1 -1
- package/dist/service-descriptor.d.cts +34 -0
- package/dist/service-descriptor.d.cts.map +1 -0
- package/dist/service-descriptor.d.mts +36 -0
- package/dist/service-descriptor.d.mts.map +1 -0
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/dist/utils/run.cjs +9 -20
- package/dist/utils/run.cjs.map +1 -1
- package/dist/utils/run.mjs +9 -20
- package/dist/utils/run.mjs.map +1 -1
- package/package.json +2 -2
- package/src/api-contract.ts +0 -623
- package/src/app.ts +0 -193
- package/src/cli/catalog.ts +0 -49
- package/src/cli/framework-version.ts +0 -61
- package/src/cli/help.ts +0 -13
- package/src/cli/infra.ts +0 -190
- package/src/cli/init.ts +0 -1145
- package/src/cli/parse.ts +0 -147
- package/src/cli/prompts.ts +0 -135
- package/src/cli/snapshot.ts +0 -46
- package/src/cli/status.ts +0 -99
- package/src/cli/sync.ts +0 -429
- package/src/cli/timing.ts +0 -63
- package/src/cli/upgrade.ts +0 -869
- package/src/cli.ts +0 -516
- package/src/components/dev-view.tsx +0 -352
- package/src/components/streaming-view.ts +0 -177
- package/src/config.ts +0 -893
- package/src/contract.meta.ts +0 -140
- package/src/contract.ts +0 -326
- package/src/dev-logs.ts +0 -92
- package/src/dev-session.ts +0 -283
- package/src/fastkv.ts +0 -181
- package/src/index.ts +0 -8
- package/src/integrity.ts +0 -138
- package/src/internal/manifest-normalizer.ts +0 -290
- package/src/merge.ts +0 -187
- package/src/mf.ts +0 -147
- package/src/near-cli.ts +0 -259
- package/src/network.ts +0 -3
- package/src/orchestrator.ts +0 -493
- package/src/plugin.ts +0 -1799
- package/src/sdk.ts +0 -14
- package/src/service-descriptor.ts +0 -281
- package/src/shared.ts +0 -249
- package/src/sidebar.ts +0 -140
- package/src/types.ts +0 -330
- package/src/ui/head.ts +0 -83
- package/src/ui/index.ts +0 -5
- package/src/ui/metadata.ts +0 -95
- package/src/ui/router.ts +0 -88
- package/src/ui/runtime.ts +0 -42
- package/src/ui/types.ts +0 -65
- package/src/utils/banner.ts +0 -21
- package/src/utils/linkify.ts +0 -11
- package/src/utils/path-match.ts +0 -16
- package/src/utils/run.ts +0 -31
- package/src/utils/save-config.ts +0 -20
- package/src/utils/theme.ts +0 -39
package/src/app.ts
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { createConnection } from "node:net";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
buildRuntimeConfig as configBuildRuntimeConfig,
|
|
6
|
-
getProjectRoot,
|
|
7
|
-
resolveLocalDevelopmentPath,
|
|
8
|
-
} from "./config";
|
|
9
|
-
import type { AppOrchestrator } from "./service-descriptor";
|
|
10
|
-
import type { BosConfig, RuntimeConfig, RuntimePluginConfig } from "./types";
|
|
11
|
-
|
|
12
|
-
export type { AppOrchestrator };
|
|
13
|
-
|
|
14
|
-
const DEFAULT_HOST_PORT = 3000;
|
|
15
|
-
const DEFAULT_API_PORT = 3001;
|
|
16
|
-
const DEFAULT_AUTH_PORT = 3002;
|
|
17
|
-
const DEFAULT_UI_PORT = 3003;
|
|
18
|
-
const DEFAULT_PLUGIN_PORT_START = 3010;
|
|
19
|
-
|
|
20
|
-
export function detectLocalPackages(
|
|
21
|
-
bosConfig?: BosConfig,
|
|
22
|
-
runtimeConfig?: RuntimeConfig,
|
|
23
|
-
): string[] {
|
|
24
|
-
const packages: string[] = [];
|
|
25
|
-
const configDir = getProjectRoot();
|
|
26
|
-
|
|
27
|
-
const uiLocalPath =
|
|
28
|
-
runtimeConfig?.ui.localPath ??
|
|
29
|
-
resolveLocalDevelopmentPath(bosConfig?.app.ui.development, configDir);
|
|
30
|
-
if (uiLocalPath && existsSync(join(uiLocalPath, "package.json"))) {
|
|
31
|
-
packages.push("ui");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const apiLocalPath =
|
|
35
|
-
runtimeConfig?.api.localPath ??
|
|
36
|
-
resolveLocalDevelopmentPath(bosConfig?.app.api.development, configDir);
|
|
37
|
-
if (apiLocalPath && existsSync(join(apiLocalPath, "package.json"))) {
|
|
38
|
-
packages.push("api");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const hostLocalPath =
|
|
42
|
-
runtimeConfig?.host?.localPath ??
|
|
43
|
-
resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);
|
|
44
|
-
if (hostLocalPath && existsSync(join(hostLocalPath, "package.json"))) {
|
|
45
|
-
packages.push("host");
|
|
46
|
-
} else if (existsSync(join(configDir, "host", "package.json"))) {
|
|
47
|
-
packages.push("host");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) {
|
|
51
|
-
if (pluginConfig.localPath && existsSync(join(pluginConfig.localPath, "package.json"))) {
|
|
52
|
-
packages.push(`plugin:${pluginId}`);
|
|
53
|
-
}
|
|
54
|
-
if (pluginConfig.ui?.localPath && existsSync(join(pluginConfig.ui.localPath, "package.json"))) {
|
|
55
|
-
packages.push(`plugin-ui:${pluginId}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const authLocalPath =
|
|
60
|
-
runtimeConfig?.auth?.localPath ??
|
|
61
|
-
resolveLocalDevelopmentPath(bosConfig?.app.auth?.development, configDir);
|
|
62
|
-
if (authLocalPath && existsSync(join(authLocalPath, "package.json"))) {
|
|
63
|
-
packages.push("auth");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return packages;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function buildRuntimeConfig(
|
|
70
|
-
bosConfig: BosConfig,
|
|
71
|
-
options: {
|
|
72
|
-
hostSource?: "local" | "remote";
|
|
73
|
-
uiSource?: "local" | "remote";
|
|
74
|
-
apiSource?: "local" | "remote";
|
|
75
|
-
authSource?: "local" | "remote";
|
|
76
|
-
proxy?: string;
|
|
77
|
-
env?: "development" | "production";
|
|
78
|
-
plugins?: Record<string, RuntimePluginConfig>;
|
|
79
|
-
},
|
|
80
|
-
): RuntimeConfig {
|
|
81
|
-
return configBuildRuntimeConfig(bosConfig, getProjectRoot(), options.env ?? "development", {
|
|
82
|
-
hostSource: options.hostSource,
|
|
83
|
-
uiSource: options.uiSource,
|
|
84
|
-
apiSource: options.apiSource,
|
|
85
|
-
authSource: options.authSource,
|
|
86
|
-
proxy: options.proxy,
|
|
87
|
-
plugins: options.plugins,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function probeTcpOpen(port: number, timeoutMs = 250): Promise<boolean> {
|
|
92
|
-
return new Promise((resolve) => {
|
|
93
|
-
const socket = createConnection({ host: "127.0.0.1", port });
|
|
94
|
-
const timer = setTimeout(() => {
|
|
95
|
-
socket.destroy();
|
|
96
|
-
resolve(false);
|
|
97
|
-
}, timeoutMs);
|
|
98
|
-
|
|
99
|
-
socket.once("connect", () => {
|
|
100
|
-
clearTimeout(timer);
|
|
101
|
-
socket.destroy();
|
|
102
|
-
resolve(true);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
socket.once("error", () => {
|
|
106
|
-
clearTimeout(timer);
|
|
107
|
-
resolve(false);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async function pickAvailablePort(preferred: number, usedPorts: Set<number>): Promise<number> {
|
|
113
|
-
let port = preferred;
|
|
114
|
-
while (usedPorts.has(port) || (await probeTcpOpen(port))) {
|
|
115
|
-
port += 1;
|
|
116
|
-
}
|
|
117
|
-
usedPorts.add(port);
|
|
118
|
-
return port;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function withLocalRuntimeUrl<
|
|
122
|
-
T extends { url: string; entry: string; port?: number; localPath?: string },
|
|
123
|
-
>(entry: T, port: number): T {
|
|
124
|
-
const url = `http://localhost:${port}`;
|
|
125
|
-
return {
|
|
126
|
-
...entry,
|
|
127
|
-
url,
|
|
128
|
-
entry: `${url}/mf-manifest.json`,
|
|
129
|
-
port,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export async function prepareDevelopmentRuntimeConfig(
|
|
134
|
-
runtimeConfig: RuntimeConfig,
|
|
135
|
-
options?: { hostPort?: number; ssr?: boolean },
|
|
136
|
-
): Promise<RuntimeConfig> {
|
|
137
|
-
const usedPorts = new Set<number>();
|
|
138
|
-
const hostPort = await pickAvailablePort(options?.hostPort ?? DEFAULT_HOST_PORT, usedPorts);
|
|
139
|
-
|
|
140
|
-
const next: RuntimeConfig = {
|
|
141
|
-
...runtimeConfig,
|
|
142
|
-
host: { ...runtimeConfig.host, url: `http://localhost:${hostPort}`, port: hostPort },
|
|
143
|
-
ui: { ...runtimeConfig.ui },
|
|
144
|
-
api: { ...runtimeConfig.api },
|
|
145
|
-
auth: runtimeConfig.auth ? { ...runtimeConfig.auth } : undefined,
|
|
146
|
-
plugins: runtimeConfig.plugins ? { ...runtimeConfig.plugins } : undefined,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
if (next.api.source === "local" && next.api.localPath) {
|
|
150
|
-
const apiPort = await pickAvailablePort(next.api.port ?? DEFAULT_API_PORT, usedPorts);
|
|
151
|
-
next.api = withLocalRuntimeUrl(next.api, apiPort);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (next.auth?.source === "local" && next.auth.localPath) {
|
|
155
|
-
const authPort = await pickAvailablePort(next.auth.port ?? DEFAULT_AUTH_PORT, usedPorts);
|
|
156
|
-
next.auth = withLocalRuntimeUrl(next.auth, authPort);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (next.ui.source === "local" && next.ui.localPath) {
|
|
160
|
-
const uiPort = await pickAvailablePort(next.ui.port ?? DEFAULT_UI_PORT, usedPorts);
|
|
161
|
-
next.ui = withLocalRuntimeUrl(next.ui, uiPort);
|
|
162
|
-
if (options?.ssr) {
|
|
163
|
-
const ssrPort = await pickAvailablePort(uiPort + 1, usedPorts);
|
|
164
|
-
next.ui.ssrUrl = `http://localhost:${ssrPort}`;
|
|
165
|
-
} else {
|
|
166
|
-
next.ui.ssrUrl = undefined;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (next.plugins) {
|
|
171
|
-
const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));
|
|
172
|
-
let pluginBasePort = DEFAULT_PLUGIN_PORT_START;
|
|
173
|
-
|
|
174
|
-
for (const [pluginId, plugin] of entries) {
|
|
175
|
-
if (plugin.source === "local" && plugin.localPath) {
|
|
176
|
-
const pluginPort = await pickAvailablePort(plugin.port ?? pluginBasePort, usedPorts);
|
|
177
|
-
next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);
|
|
178
|
-
pluginBasePort = pluginPort + 1;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (plugin.ui?.source === "local" && plugin.ui.localPath) {
|
|
182
|
-
const uiPort = await pickAvailablePort(plugin.ui.port ?? pluginBasePort, usedPorts);
|
|
183
|
-
next.plugins[pluginId] = {
|
|
184
|
-
...next.plugins[pluginId]!,
|
|
185
|
-
ui: withLocalRuntimeUrl(plugin.ui, uiPort),
|
|
186
|
-
};
|
|
187
|
-
pluginBasePort = uiPort + 1;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return next;
|
|
193
|
-
}
|
package/src/cli/catalog.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { bosContract } from "../contract";
|
|
2
|
-
import { type CliCommandMeta, cliCommandMeta } from "../contract.meta";
|
|
3
|
-
|
|
4
|
-
export type CommandDescriptor = {
|
|
5
|
-
key: keyof typeof bosContract;
|
|
6
|
-
commandPath: string[];
|
|
7
|
-
summary: string;
|
|
8
|
-
meta: CliCommandMeta;
|
|
9
|
-
procedure: (typeof bosContract)[keyof typeof bosContract];
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
function splitCamelCase(value: string): string[] {
|
|
13
|
-
return value
|
|
14
|
-
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
15
|
-
.split(/\s+/)
|
|
16
|
-
.map((part) => part.trim())
|
|
17
|
-
.filter(Boolean)
|
|
18
|
-
.map((part) => part.toLowerCase());
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const commandCatalog: CommandDescriptor[] = (
|
|
22
|
-
Object.entries(bosContract) as Array<
|
|
23
|
-
[keyof typeof bosContract, (typeof bosContract)[keyof typeof bosContract]]
|
|
24
|
-
>
|
|
25
|
-
).map(([key, procedure]) => {
|
|
26
|
-
const meta = cliCommandMeta[key as keyof typeof cliCommandMeta] ?? {
|
|
27
|
-
summary: splitCamelCase(String(key)).join(" "),
|
|
28
|
-
};
|
|
29
|
-
return {
|
|
30
|
-
key,
|
|
31
|
-
commandPath: meta.commandPath ?? splitCamelCase(String(key)),
|
|
32
|
-
summary: meta.summary,
|
|
33
|
-
meta,
|
|
34
|
-
procedure,
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
export function findCommandDescriptor(
|
|
39
|
-
args: string[],
|
|
40
|
-
): { descriptor: CommandDescriptor; consumed: number } | null {
|
|
41
|
-
const sorted = [...commandCatalog].sort((a, b) => b.commandPath.length - a.commandPath.length);
|
|
42
|
-
for (const descriptor of sorted) {
|
|
43
|
-
const parts = args.slice(0, descriptor.commandPath.length).map((part) => part.toLowerCase());
|
|
44
|
-
if (parts.join(" ") === descriptor.commandPath.join(" ")) {
|
|
45
|
-
return { descriptor, consumed: descriptor.commandPath.length };
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
function stripVersionPrefix(version: string): string {
|
|
5
|
-
return version.replace(/^[\^~>=]+/, "");
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function readRootCatalogVersion(
|
|
9
|
-
projectDir: string,
|
|
10
|
-
packageName: string,
|
|
11
|
-
): string | undefined {
|
|
12
|
-
const pkgPath = join(projectDir, "package.json");
|
|
13
|
-
if (!existsSync(pkgPath)) return undefined;
|
|
14
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as {
|
|
15
|
-
workspaces?: { catalog?: Record<string, string> };
|
|
16
|
-
};
|
|
17
|
-
const version = pkg.workspaces?.catalog?.[packageName];
|
|
18
|
-
return version ? stripVersionPrefix(version) : undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function readNodeModulesVersion(
|
|
22
|
-
projectDir: string,
|
|
23
|
-
packageName: string,
|
|
24
|
-
): string | undefined {
|
|
25
|
-
const pkgPath = join(projectDir, "node_modules", packageName, "package.json");
|
|
26
|
-
if (!existsSync(pkgPath)) return undefined;
|
|
27
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { version?: string };
|
|
28
|
-
return pkg.version;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function readInstalledFrameworkVersion(
|
|
32
|
-
projectDir: string,
|
|
33
|
-
packageName: string,
|
|
34
|
-
): string | undefined {
|
|
35
|
-
const pkgPath = join(projectDir, "package.json");
|
|
36
|
-
if (!existsSync(pkgPath)) return undefined;
|
|
37
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as Record<string, unknown>;
|
|
38
|
-
const deps = (pkg.dependencies ?? {}) as Record<string, string>;
|
|
39
|
-
const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;
|
|
40
|
-
const version = deps[packageName] || devDeps[packageName];
|
|
41
|
-
|
|
42
|
-
if (!version) {
|
|
43
|
-
return (
|
|
44
|
-
readRootCatalogVersion(projectDir, packageName) ??
|
|
45
|
-
readNodeModulesVersion(projectDir, packageName)
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (version.startsWith("catalog:")) {
|
|
50
|
-
return (
|
|
51
|
-
readRootCatalogVersion(projectDir, packageName) ??
|
|
52
|
-
readNodeModulesVersion(projectDir, packageName)
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (version.startsWith("workspace:") || version.startsWith("file:")) {
|
|
57
|
-
return readNodeModulesVersion(projectDir, packageName);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return stripVersionPrefix(version);
|
|
61
|
-
}
|
package/src/cli/help.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { commandCatalog } from "./catalog";
|
|
2
|
-
|
|
3
|
-
export function printHelp() {
|
|
4
|
-
process.stdout.write(`everything-dev commands\n\n`);
|
|
5
|
-
|
|
6
|
-
for (const command of commandCatalog) {
|
|
7
|
-
process.stdout.write(` everything-dev ${command.commandPath.join(" ")}`);
|
|
8
|
-
process.stdout.write(command.meta.longRunning ? " (long running)" : "");
|
|
9
|
-
process.stdout.write(`\n ${command.summary}\n`);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
process.stdout.write(`\n'bos' is an alias for 'everything-dev'.\n`);
|
|
13
|
-
}
|
package/src/cli/infra.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
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
|
-
}
|