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.
Files changed (114) hide show
  1. package/dist/cli/infra.cjs +1 -1
  2. package/dist/cli/infra.mjs +1 -1
  3. package/dist/cli/init.cjs +34 -9
  4. package/dist/cli/init.cjs.map +1 -1
  5. package/dist/cli/init.d.cts +2 -1
  6. package/dist/cli/init.d.cts.map +1 -1
  7. package/dist/cli/init.d.mts +2 -1
  8. package/dist/cli/init.d.mts.map +1 -1
  9. package/dist/cli/init.mjs +34 -9
  10. package/dist/cli/init.mjs.map +1 -1
  11. package/dist/cli/prompts.cjs +28 -24
  12. package/dist/cli/prompts.cjs.map +1 -1
  13. package/dist/cli/prompts.mjs +27 -24
  14. package/dist/cli/prompts.mjs.map +1 -1
  15. package/dist/cli/sync.cjs +40 -3
  16. package/dist/cli/sync.cjs.map +1 -1
  17. package/dist/cli/sync.mjs +40 -3
  18. package/dist/cli/sync.mjs.map +1 -1
  19. package/dist/cli.cjs +187 -12
  20. package/dist/cli.cjs.map +1 -1
  21. package/dist/cli.mjs +186 -11
  22. package/dist/cli.mjs.map +1 -1
  23. package/dist/config.cjs +1 -0
  24. package/dist/config.cjs.map +1 -1
  25. package/dist/config.d.cts.map +1 -1
  26. package/dist/config.d.mts.map +1 -1
  27. package/dist/config.mjs +1 -0
  28. package/dist/config.mjs.map +1 -1
  29. package/dist/contract.cjs +1 -1
  30. package/dist/contract.cjs.map +1 -1
  31. package/dist/contract.d.cts +38 -34
  32. package/dist/contract.d.cts.map +1 -1
  33. package/dist/contract.d.mts +38 -34
  34. package/dist/contract.d.mts.map +1 -1
  35. package/dist/contract.mjs +1 -0
  36. package/dist/contract.mjs.map +1 -1
  37. package/dist/dev-session.cjs +0 -1
  38. package/dist/dev-session.mjs +1 -1
  39. package/dist/index.cjs +0 -2
  40. package/dist/index.d.cts +2 -2
  41. package/dist/index.d.mts +2 -2
  42. package/dist/index.mjs +0 -1
  43. package/dist/near-cli.cjs +1 -1
  44. package/dist/near-cli.mjs +1 -1
  45. package/dist/orchestrator.cjs +1 -1
  46. package/dist/orchestrator.mjs +1 -1
  47. package/dist/plugin.cjs +183 -151
  48. package/dist/plugin.cjs.map +1 -1
  49. package/dist/plugin.d.cts +67 -34
  50. package/dist/plugin.d.cts.map +1 -1
  51. package/dist/plugin.d.mts +66 -34
  52. package/dist/plugin.d.mts.map +1 -1
  53. package/dist/plugin.mjs +173 -142
  54. package/dist/plugin.mjs.map +1 -1
  55. package/dist/service-descriptor.d.cts +34 -0
  56. package/dist/service-descriptor.d.cts.map +1 -0
  57. package/dist/service-descriptor.d.mts +36 -0
  58. package/dist/service-descriptor.d.mts.map +1 -0
  59. package/dist/types.d.cts +2 -2
  60. package/dist/types.d.mts +2 -2
  61. package/dist/utils/run.cjs +9 -20
  62. package/dist/utils/run.cjs.map +1 -1
  63. package/dist/utils/run.mjs +9 -20
  64. package/dist/utils/run.mjs.map +1 -1
  65. package/package.json +2 -2
  66. package/src/api-contract.ts +0 -623
  67. package/src/app.ts +0 -193
  68. package/src/cli/catalog.ts +0 -49
  69. package/src/cli/framework-version.ts +0 -61
  70. package/src/cli/help.ts +0 -13
  71. package/src/cli/infra.ts +0 -190
  72. package/src/cli/init.ts +0 -1145
  73. package/src/cli/parse.ts +0 -147
  74. package/src/cli/prompts.ts +0 -135
  75. package/src/cli/snapshot.ts +0 -46
  76. package/src/cli/status.ts +0 -99
  77. package/src/cli/sync.ts +0 -429
  78. package/src/cli/timing.ts +0 -63
  79. package/src/cli/upgrade.ts +0 -869
  80. package/src/cli.ts +0 -516
  81. package/src/components/dev-view.tsx +0 -352
  82. package/src/components/streaming-view.ts +0 -177
  83. package/src/config.ts +0 -893
  84. package/src/contract.meta.ts +0 -140
  85. package/src/contract.ts +0 -326
  86. package/src/dev-logs.ts +0 -92
  87. package/src/dev-session.ts +0 -283
  88. package/src/fastkv.ts +0 -181
  89. package/src/index.ts +0 -8
  90. package/src/integrity.ts +0 -138
  91. package/src/internal/manifest-normalizer.ts +0 -290
  92. package/src/merge.ts +0 -187
  93. package/src/mf.ts +0 -147
  94. package/src/near-cli.ts +0 -259
  95. package/src/network.ts +0 -3
  96. package/src/orchestrator.ts +0 -493
  97. package/src/plugin.ts +0 -1799
  98. package/src/sdk.ts +0 -14
  99. package/src/service-descriptor.ts +0 -281
  100. package/src/shared.ts +0 -249
  101. package/src/sidebar.ts +0 -140
  102. package/src/types.ts +0 -330
  103. package/src/ui/head.ts +0 -83
  104. package/src/ui/index.ts +0 -5
  105. package/src/ui/metadata.ts +0 -95
  106. package/src/ui/router.ts +0 -88
  107. package/src/ui/runtime.ts +0 -42
  108. package/src/ui/types.ts +0 -65
  109. package/src/utils/banner.ts +0 -21
  110. package/src/utils/linkify.ts +0 -11
  111. package/src/utils/path-match.ts +0 -16
  112. package/src/utils/run.ts +0 -31
  113. package/src/utils/save-config.ts +0 -20
  114. 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
- }
@@ -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
- }