everything-dev 2.0.0-rc.5 → 2.0.0-rc.7
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/README.md +2 -2
- package/dist/build.cjs +1 -1
- package/dist/build.mjs +1 -1
- package/dist/bundle-cache.cjs +1 -1
- package/dist/bundle-cache.cjs.map +1 -1
- package/dist/bundle-cache.mjs +1 -1
- package/dist/bundle-cache.mjs.map +1 -1
- package/dist/cli/infra.cjs +4 -2
- package/dist/cli/infra.cjs.map +1 -1
- package/dist/cli/infra.mjs +4 -2
- package/dist/cli/infra.mjs.map +1 -1
- package/dist/cli.cjs +2 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/deploy.cjs +111 -1
- package/dist/commands/deploy.cjs.map +1 -1
- package/dist/commands/deploy.mjs +112 -2
- package/dist/commands/deploy.mjs.map +1 -1
- package/dist/commands/smoke.cjs +41 -0
- package/dist/commands/smoke.cjs.map +1 -0
- package/dist/commands/smoke.mjs +41 -0
- package/dist/commands/smoke.mjs.map +1 -0
- package/dist/commands/upgrade.cjs +2 -2
- package/dist/commands/upgrade.cjs.map +1 -1
- package/dist/commands/upgrade.mjs +1 -1
- package/dist/commands/upgrade.mjs.map +1 -1
- package/dist/contract.cjs +27 -1
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +65 -1
- package/dist/contract.d.mts +65 -1
- package/dist/contract.meta.cjs +13 -1
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +23 -1
- package/dist/contract.meta.d.mts +23 -1
- package/dist/contract.meta.mjs +13 -1
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +26 -2
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-log-pipeline.cjs +6 -0
- package/dist/dev-log-pipeline.cjs.map +1 -1
- package/dist/dev-log-pipeline.mjs +6 -0
- package/dist/dev-log-pipeline.mjs.map +1 -1
- package/dist/dev-program.cjs +21 -5
- package/dist/dev-program.cjs.map +1 -1
- package/dist/dev-program.mjs +21 -6
- package/dist/dev-program.mjs.map +1 -1
- package/dist/dev-session.cjs +1 -0
- package/dist/dev-session.cjs.map +1 -1
- package/dist/dev-session.mjs +1 -0
- package/dist/dev-session.mjs.map +1 -1
- package/dist/fastkv.cjs +21 -3
- package/dist/fastkv.cjs.map +1 -1
- package/dist/fastkv.d.cts +14 -1
- package/dist/fastkv.d.mts +14 -1
- package/dist/fastkv.mjs +21 -5
- package/dist/fastkv.mjs.map +1 -1
- package/dist/{mf.cjs → federation.cjs} +6 -13
- package/dist/federation.cjs.map +1 -0
- package/dist/{mf.d.cts → federation.d.cts} +2 -2
- package/dist/{mf.d.mts → federation.d.mts} +2 -2
- package/dist/{mf.mjs → federation.mjs} +6 -13
- package/dist/federation.mjs.map +1 -0
- package/dist/http-client.cjs +70 -53
- package/dist/http-client.cjs.map +1 -1
- package/dist/http-client.mjs +70 -54
- package/dist/http-client.mjs.map +1 -1
- package/dist/image-deploy.cjs +9 -0
- package/dist/image-deploy.cjs.map +1 -1
- package/dist/image-deploy.mjs +9 -1
- package/dist/image-deploy.mjs.map +1 -1
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/orchestrator.cjs +60 -52
- package/dist/orchestrator.cjs.map +1 -1
- package/dist/orchestrator.mjs +60 -52
- package/dist/orchestrator.mjs.map +1 -1
- package/dist/plugin.cjs +2 -0
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +24 -0
- package/dist/plugin.d.mts +24 -0
- package/dist/plugin.mjs +2 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/process-registry.cjs +1 -1
- package/dist/process-registry.mjs +1 -1
- package/dist/publish.cjs +3 -3
- package/dist/publish.cjs.map +1 -1
- package/dist/publish.mjs +4 -4
- package/dist/publish.mjs.map +1 -1
- package/dist/resolver.cjs +293 -0
- package/dist/resolver.cjs.map +1 -0
- package/dist/resolver.d.cts +99 -0
- package/dist/resolver.d.mts +99 -0
- package/dist/resolver.mjs +284 -0
- package/dist/resolver.mjs.map +1 -0
- package/dist/smoke.cjs +262 -0
- package/dist/smoke.cjs.map +1 -0
- package/dist/smoke.mjs +260 -0
- package/dist/smoke.mjs.map +1 -0
- package/dist/version-manifest-resolve.cjs +3 -3
- package/dist/version-manifest-resolve.cjs.map +1 -1
- package/dist/version-manifest-resolve.mjs +1 -1
- package/dist/version-manifest-resolve.mjs.map +1 -1
- package/dist/workspace.cjs +1 -1
- package/dist/workspace.mjs +1 -1
- package/package.json +9 -9
- package/dist/bundle-fs-resolve.cjs +0 -201
- package/dist/bundle-fs-resolve.cjs.map +0 -1
- package/dist/bundle-fs-resolve.d.cts +0 -82
- package/dist/bundle-fs-resolve.d.mts +0 -82
- package/dist/bundle-fs-resolve.mjs +0 -194
- package/dist/bundle-fs-resolve.mjs.map +0 -1
- package/dist/bundle-path.cjs +0 -48
- package/dist/bundle-path.cjs.map +0 -1
- package/dist/bundle-path.mjs +0 -45
- package/dist/bundle-path.mjs.map +0 -1
- package/dist/mf.cjs.map +0 -1
- package/dist/mf.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A consolidated product package for building Module Federation apps with oRPC API
|
|
|
7
7
|
- `everything-dev/types` - Zod schemas and TypeScript types
|
|
8
8
|
- `everything-dev/config` - Config loading and resolution
|
|
9
9
|
- `everything-dev/shared` - Shared dependency policy generation
|
|
10
|
-
- `everything-dev/
|
|
10
|
+
- `everything-dev/federation` - Module Federation runtime utilities
|
|
11
11
|
- `everything-dev/plugin` - Plugin authoring (re-exports from every-plugin)
|
|
12
12
|
- `everything-dev/api` - API plugin loading and router stitching
|
|
13
13
|
- `everything-dev/host` - Host server orchestration
|
|
@@ -42,7 +42,7 @@ everything-dev/shared (generate shared deps policy)
|
|
|
42
42
|
↓
|
|
43
43
|
everything-dev/host (orchestrate)
|
|
44
44
|
├── everything-dev/api (load API plugins via MF)
|
|
45
|
-
└── everything-dev/
|
|
45
|
+
└── everything-dev/federation (load UI remote via MF)
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
## MF Manifest Standard
|
package/dist/build.cjs
CHANGED
|
@@ -6,9 +6,9 @@ const require_timing = require('./cli/timing.cjs');
|
|
|
6
6
|
const require_workspace = require('./workspace.cjs');
|
|
7
7
|
const require_shared_deps = require('./shared-deps.cjs');
|
|
8
8
|
const require_string = require('./utils/string.cjs');
|
|
9
|
+
let node_fs_promises = require("node:fs/promises");
|
|
9
10
|
let node_process = require("node:process");
|
|
10
11
|
node_process = require_runtime.__toESM(node_process, 1);
|
|
11
|
-
let node_fs_promises = require("node:fs/promises");
|
|
12
12
|
|
|
13
13
|
//#region src/build.ts
|
|
14
14
|
const buildCommands = {
|
package/dist/build.mjs
CHANGED
|
@@ -5,8 +5,8 @@ import { formatDuration } from "./cli/timing.mjs";
|
|
|
5
5
|
import { ensureFreshDeps, findWorkspaceRoot } from "./workspace.mjs";
|
|
6
6
|
import { syncResolvedSharedDeps } from "./shared-deps.mjs";
|
|
7
7
|
import { padRight } from "./utils/string.mjs";
|
|
8
|
-
import process from "node:process";
|
|
9
8
|
import { access, readFile } from "node:fs/promises";
|
|
9
|
+
import process from "node:process";
|
|
10
10
|
|
|
11
11
|
//#region src/build.ts
|
|
12
12
|
const buildCommands = {
|
package/dist/bundle-cache.cjs
CHANGED
|
@@ -34,7 +34,7 @@ function bundleCachePath(url, input) {
|
|
|
34
34
|
const match = /^\/bundles\/([^/]+)\/([^/]+)\/(.+)$/.exec(parsed.pathname);
|
|
35
35
|
if (!match) return null;
|
|
36
36
|
const [, account, gateway, rest] = match;
|
|
37
|
-
if (!rest || rest.endsWith("/")) return null;
|
|
37
|
+
if (!account || !gateway || !rest || rest.endsWith("/")) return null;
|
|
38
38
|
let decoded;
|
|
39
39
|
try {
|
|
40
40
|
decoded = decodeURIComponent(rest);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-cache.cjs","names":["path","readFile","mkdir","createHash","writeFile","rename"],"sources":["../src/bundle-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Bundle disk cache (ADR 0011 amendment — foreign-namespace resilience):\n * the child tier loads base workspaces from foreign origins; this cache\n * keeps the last-known-good bytes so an origin outage degrades to stale\n * serving instead of a hard failure. One cache root, two entrances: the\n * host's `/bundles/*` proxy handler (browser traffic) and the CLI fetch\n * adapter (boot-time outbound). Keyed exactly like the staged layout —\n * `<account>/<gateway>/<rest>` — but a separate root from BOS_BUNDLE_DIR:\n * cached bytes are a resilience artifact, never a deployment (plan 043 —\n * never point a runtime's BOS_BUNDLE_DIR at another runtime's layout).\n */\n\nexport function bundleCacheRoot(input?: { cacheDir?: string }): string {\n return path.resolve(input?.cacheDir ?? process.env.BOS_BUNDLE_CACHE_DIR ?? joinDefault());\n}\n\nfunction joinDefault(): string {\n return path.join(process.cwd(), \".bos\", \"bundle-cache\");\n}\n\nexport function bundleCachePath(url: string, input?: { cacheDir?: string }): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n const match = /^\\/bundles\\/([^/]+)\\/([^/]+)\\/(.+)$/.exec(parsed.pathname);\n if (!match) return null;\n const [, account, gateway, rest] = match;\n if (!rest || rest.endsWith(\"/\")) return null;\n let decoded: string;\n try {\n decoded = decodeURIComponent(rest);\n } catch {\n return null;\n }\n const root = path.resolve(bundleCacheRoot(input));\n const filePath = path.resolve(root, account, gateway, decoded);\n if (!filePath.startsWith(root + path.sep)) return null;\n return filePath;\n}\n\nexport async function readBundleCache(\n url: string,\n input?: { cacheDir?: string },\n): Promise<Uint8Array | null> {\n const filePath = bundleCachePath(url, input);\n if (!filePath) return null;\n try {\n return new Uint8Array(await readFile(filePath));\n } catch {\n return null;\n }\n}\n\nexport async function writeBundleCache(\n url: string,\n bytes: Uint8Array,\n input?: { cacheDir?: string },\n): Promise<void> {\n const filePath = bundleCachePath(url, input);\n if (!filePath) return;\n try {\n await mkdir(path.dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${process.pid}.${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 8)}.tmp`;\n await writeFile(tmp, bytes);\n await rename(tmp, filePath);\n } catch {\n // cache writes are best-effort — never fail the serving path\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,OAAuC;CACrE,OAAOA,kBAAK,QAAQ,OAAO,YAAY,QAAQ,IAAI,wBAAwB,YAAY,CAAC;AAC1F;AAEA,SAAS,cAAsB;CAC7B,OAAOA,kBAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,cAAc;AACxD;AAEA,SAAgB,gBAAgB,KAAa,OAA8C;CACzF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS,OAAO;CACxE,MAAM,QAAQ,sCAAsC,KAAK,OAAO,QAAQ;CACxE,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,GAAG,SAAS,SAAS,QAAQ;CACnC,IAAI,CAAC,QAAQ,KAAK,SAAS,GAAG,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"bundle-cache.cjs","names":["path","readFile","mkdir","createHash","writeFile","rename"],"sources":["../src/bundle-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Bundle disk cache (ADR 0011 amendment — foreign-namespace resilience):\n * the child tier loads base workspaces from foreign origins; this cache\n * keeps the last-known-good bytes so an origin outage degrades to stale\n * serving instead of a hard failure. One cache root, two entrances: the\n * host's `/bundles/*` proxy handler (browser traffic) and the CLI fetch\n * adapter (boot-time outbound). Keyed exactly like the staged layout —\n * `<account>/<gateway>/<rest>` — but a separate root from BOS_BUNDLE_DIR:\n * cached bytes are a resilience artifact, never a deployment (plan 043 —\n * never point a runtime's BOS_BUNDLE_DIR at another runtime's layout).\n */\n\nexport function bundleCacheRoot(input?: { cacheDir?: string }): string {\n return path.resolve(input?.cacheDir ?? process.env.BOS_BUNDLE_CACHE_DIR ?? joinDefault());\n}\n\nfunction joinDefault(): string {\n return path.join(process.cwd(), \".bos\", \"bundle-cache\");\n}\n\nexport function bundleCachePath(url: string, input?: { cacheDir?: string }): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n const match = /^\\/bundles\\/([^/]+)\\/([^/]+)\\/(.+)$/.exec(parsed.pathname);\n if (!match) return null;\n const [, account, gateway, rest] = match;\n if (!account || !gateway || !rest || rest.endsWith(\"/\")) return null;\n let decoded: string;\n try {\n decoded = decodeURIComponent(rest);\n } catch {\n return null;\n }\n const root = path.resolve(bundleCacheRoot(input));\n const filePath = path.resolve(root, account, gateway, decoded);\n if (!filePath.startsWith(root + path.sep)) return null;\n return filePath;\n}\n\nexport async function readBundleCache(\n url: string,\n input?: { cacheDir?: string },\n): Promise<Uint8Array | null> {\n const filePath = bundleCachePath(url, input);\n if (!filePath) return null;\n try {\n return new Uint8Array(await readFile(filePath));\n } catch {\n return null;\n }\n}\n\nexport async function writeBundleCache(\n url: string,\n bytes: Uint8Array,\n input?: { cacheDir?: string },\n): Promise<void> {\n const filePath = bundleCachePath(url, input);\n if (!filePath) return;\n try {\n await mkdir(path.dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${process.pid}.${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 8)}.tmp`;\n await writeFile(tmp, bytes);\n await rename(tmp, filePath);\n } catch {\n // cache writes are best-effort — never fail the serving path\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,OAAuC;CACrE,OAAOA,kBAAK,QAAQ,OAAO,YAAY,QAAQ,IAAI,wBAAwB,YAAY,CAAC;AAC1F;AAEA,SAAS,cAAsB;CAC7B,OAAOA,kBAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,cAAc;AACxD;AAEA,SAAgB,gBAAgB,KAAa,OAA8C;CACzF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS,OAAO;CACxE,MAAM,QAAQ,sCAAsC,KAAK,OAAO,QAAQ;CACxE,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,GAAG,SAAS,SAAS,QAAQ;CACnC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,QAAQ,KAAK,SAAS,GAAG,GAAG,OAAO;CAChE,IAAI;CACJ,IAAI;EACF,UAAU,mBAAmB,IAAI;CACnC,QAAQ;EACN,OAAO;CACT;CACA,MAAM,OAAOA,kBAAK,QAAQ,gBAAgB,KAAK,CAAC;CAChD,MAAM,WAAWA,kBAAK,QAAQ,MAAM,SAAS,SAAS,OAAO;CAC7D,IAAI,CAAC,SAAS,WAAW,OAAOA,kBAAK,GAAG,GAAG,OAAO;CAClD,OAAO;AACT;AAEA,eAAsB,gBACpB,KACA,OAC4B;CAC5B,MAAM,WAAW,gBAAgB,KAAK,KAAK;CAC3C,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI;EACF,OAAO,IAAI,WAAW,UAAMC,2BAAS,QAAQ,CAAC;CAChD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAsB,iBACpB,KACA,OACA,OACe;CACf,MAAM,WAAW,gBAAgB,KAAK,KAAK;CAC3C,IAAI,CAAC,UAAU;CACf,IAAI;EACF,UAAMC,wBAAMF,kBAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EACvD,MAAM,MAAM,GAAG,SAAS,GAAG,QAAQ,IAAI,OAAGG,wBAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;EACvG,UAAMC,4BAAU,KAAK,KAAK;EAC1B,UAAMC,yBAAO,KAAK,QAAQ;CAC5B,QAAQ,CAER;AACF"}
|
package/dist/bundle-cache.mjs
CHANGED
|
@@ -31,7 +31,7 @@ function bundleCachePath(url, input) {
|
|
|
31
31
|
const match = /^\/bundles\/([^/]+)\/([^/]+)\/(.+)$/.exec(parsed.pathname);
|
|
32
32
|
if (!match) return null;
|
|
33
33
|
const [, account, gateway, rest] = match;
|
|
34
|
-
if (!rest || rest.endsWith("/")) return null;
|
|
34
|
+
if (!account || !gateway || !rest || rest.endsWith("/")) return null;
|
|
35
35
|
let decoded;
|
|
36
36
|
try {
|
|
37
37
|
decoded = decodeURIComponent(rest);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-cache.mjs","names":[],"sources":["../src/bundle-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Bundle disk cache (ADR 0011 amendment — foreign-namespace resilience):\n * the child tier loads base workspaces from foreign origins; this cache\n * keeps the last-known-good bytes so an origin outage degrades to stale\n * serving instead of a hard failure. One cache root, two entrances: the\n * host's `/bundles/*` proxy handler (browser traffic) and the CLI fetch\n * adapter (boot-time outbound). Keyed exactly like the staged layout —\n * `<account>/<gateway>/<rest>` — but a separate root from BOS_BUNDLE_DIR:\n * cached bytes are a resilience artifact, never a deployment (plan 043 —\n * never point a runtime's BOS_BUNDLE_DIR at another runtime's layout).\n */\n\nexport function bundleCacheRoot(input?: { cacheDir?: string }): string {\n return path.resolve(input?.cacheDir ?? process.env.BOS_BUNDLE_CACHE_DIR ?? joinDefault());\n}\n\nfunction joinDefault(): string {\n return path.join(process.cwd(), \".bos\", \"bundle-cache\");\n}\n\nexport function bundleCachePath(url: string, input?: { cacheDir?: string }): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n const match = /^\\/bundles\\/([^/]+)\\/([^/]+)\\/(.+)$/.exec(parsed.pathname);\n if (!match) return null;\n const [, account, gateway, rest] = match;\n if (!rest || rest.endsWith(\"/\")) return null;\n let decoded: string;\n try {\n decoded = decodeURIComponent(rest);\n } catch {\n return null;\n }\n const root = path.resolve(bundleCacheRoot(input));\n const filePath = path.resolve(root, account, gateway, decoded);\n if (!filePath.startsWith(root + path.sep)) return null;\n return filePath;\n}\n\nexport async function readBundleCache(\n url: string,\n input?: { cacheDir?: string },\n): Promise<Uint8Array | null> {\n const filePath = bundleCachePath(url, input);\n if (!filePath) return null;\n try {\n return new Uint8Array(await readFile(filePath));\n } catch {\n return null;\n }\n}\n\nexport async function writeBundleCache(\n url: string,\n bytes: Uint8Array,\n input?: { cacheDir?: string },\n): Promise<void> {\n const filePath = bundleCachePath(url, input);\n if (!filePath) return;\n try {\n await mkdir(path.dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${process.pid}.${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 8)}.tmp`;\n await writeFile(tmp, bytes);\n await rename(tmp, filePath);\n } catch {\n // cache writes are best-effort — never fail the serving path\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,OAAuC;CACrE,OAAO,KAAK,QAAQ,OAAO,YAAY,QAAQ,IAAI,wBAAwB,YAAY,CAAC;AAC1F;AAEA,SAAS,cAAsB;CAC7B,OAAO,KAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,cAAc;AACxD;AAEA,SAAgB,gBAAgB,KAAa,OAA8C;CACzF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS,OAAO;CACxE,MAAM,QAAQ,sCAAsC,KAAK,OAAO,QAAQ;CACxE,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,GAAG,SAAS,SAAS,QAAQ;CACnC,IAAI,CAAC,QAAQ,KAAK,SAAS,GAAG,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"bundle-cache.mjs","names":[],"sources":["../src/bundle-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Bundle disk cache (ADR 0011 amendment — foreign-namespace resilience):\n * the child tier loads base workspaces from foreign origins; this cache\n * keeps the last-known-good bytes so an origin outage degrades to stale\n * serving instead of a hard failure. One cache root, two entrances: the\n * host's `/bundles/*` proxy handler (browser traffic) and the CLI fetch\n * adapter (boot-time outbound). Keyed exactly like the staged layout —\n * `<account>/<gateway>/<rest>` — but a separate root from BOS_BUNDLE_DIR:\n * cached bytes are a resilience artifact, never a deployment (plan 043 —\n * never point a runtime's BOS_BUNDLE_DIR at another runtime's layout).\n */\n\nexport function bundleCacheRoot(input?: { cacheDir?: string }): string {\n return path.resolve(input?.cacheDir ?? process.env.BOS_BUNDLE_CACHE_DIR ?? joinDefault());\n}\n\nfunction joinDefault(): string {\n return path.join(process.cwd(), \".bos\", \"bundle-cache\");\n}\n\nexport function bundleCachePath(url: string, input?: { cacheDir?: string }): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n const match = /^\\/bundles\\/([^/]+)\\/([^/]+)\\/(.+)$/.exec(parsed.pathname);\n if (!match) return null;\n const [, account, gateway, rest] = match;\n if (!account || !gateway || !rest || rest.endsWith(\"/\")) return null;\n let decoded: string;\n try {\n decoded = decodeURIComponent(rest);\n } catch {\n return null;\n }\n const root = path.resolve(bundleCacheRoot(input));\n const filePath = path.resolve(root, account, gateway, decoded);\n if (!filePath.startsWith(root + path.sep)) return null;\n return filePath;\n}\n\nexport async function readBundleCache(\n url: string,\n input?: { cacheDir?: string },\n): Promise<Uint8Array | null> {\n const filePath = bundleCachePath(url, input);\n if (!filePath) return null;\n try {\n return new Uint8Array(await readFile(filePath));\n } catch {\n return null;\n }\n}\n\nexport async function writeBundleCache(\n url: string,\n bytes: Uint8Array,\n input?: { cacheDir?: string },\n): Promise<void> {\n const filePath = bundleCachePath(url, input);\n if (!filePath) return;\n try {\n await mkdir(path.dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${process.pid}.${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 8)}.tmp`;\n await writeFile(tmp, bytes);\n await rename(tmp, filePath);\n } catch {\n // cache writes are best-effort — never fail the serving path\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,OAAuC;CACrE,OAAO,KAAK,QAAQ,OAAO,YAAY,QAAQ,IAAI,wBAAwB,YAAY,CAAC;AAC1F;AAEA,SAAS,cAAsB;CAC7B,OAAO,KAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,cAAc;AACxD;AAEA,SAAgB,gBAAgB,KAAa,OAA8C;CACzF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS,OAAO;CACxE,MAAM,QAAQ,sCAAsC,KAAK,OAAO,QAAQ;CACxE,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,GAAG,SAAS,SAAS,QAAQ;CACnC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,QAAQ,KAAK,SAAS,GAAG,GAAG,OAAO;CAChE,IAAI;CACJ,IAAI;EACF,UAAU,mBAAmB,IAAI;CACnC,QAAQ;EACN,OAAO;CACT;CACA,MAAM,OAAO,KAAK,QAAQ,gBAAgB,KAAK,CAAC;CAChD,MAAM,WAAW,KAAK,QAAQ,MAAM,SAAS,SAAS,OAAO;CAC7D,IAAI,CAAC,SAAS,WAAW,OAAO,KAAK,GAAG,GAAG,OAAO;CAClD,OAAO;AACT;AAEA,eAAsB,gBACpB,KACA,OAC4B;CAC5B,MAAM,WAAW,gBAAgB,KAAK,KAAK;CAC3C,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI;EACF,OAAO,IAAI,WAAW,MAAM,SAAS,QAAQ,CAAC;CAChD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAsB,iBACpB,KACA,OACA,OACe;CACf,MAAM,WAAW,gBAAgB,KAAK,KAAK;CAC3C,IAAI,CAAC,UAAU;CACf,IAAI;EACF,MAAM,MAAM,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EACvD,MAAM,MAAM,GAAG,SAAS,GAAG,QAAQ,IAAI,GAAG,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;EACvG,MAAM,UAAU,KAAK,KAAK;EAC1B,MAAM,OAAO,KAAK,QAAQ;CAC5B,QAAQ,CAER;AACF"}
|
package/dist/cli/infra.cjs
CHANGED
|
@@ -20,6 +20,8 @@ const DEV_API_DB_PORT = 5432;
|
|
|
20
20
|
const DEV_AUTH_DB_PORT = 5433;
|
|
21
21
|
const TEST_API_DB_PORT = 5434;
|
|
22
22
|
const TEST_AUTH_DB_PORT = 5435;
|
|
23
|
+
const CI_POSTGRES_IMAGE = "ghcr.io/nearbuilders/mirror-postgres:17-alpine";
|
|
24
|
+
const CI_REDIS_IMAGE = "ghcr.io/nearbuilders/mirror-redis:7-alpine";
|
|
23
25
|
function uniqueSecrets(values) {
|
|
24
26
|
const secrets = [];
|
|
25
27
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -189,7 +191,7 @@ function buildCiInfraPlan(runtimeConfig, options = {}) {
|
|
|
189
191
|
services.push({
|
|
190
192
|
key: db.slug,
|
|
191
193
|
slug: db.slug,
|
|
192
|
-
image:
|
|
194
|
+
image: CI_POSTGRES_IMAGE,
|
|
193
195
|
env: {
|
|
194
196
|
POSTGRES_USER,
|
|
195
197
|
POSTGRES_PASSWORD,
|
|
@@ -213,7 +215,7 @@ function buildCiInfraPlan(runtimeConfig, options = {}) {
|
|
|
213
215
|
for (const r of redisConfigs) services.push({
|
|
214
216
|
key: r.slug,
|
|
215
217
|
slug: r.slug,
|
|
216
|
-
image:
|
|
218
|
+
image: CI_REDIS_IMAGE,
|
|
217
219
|
env: {},
|
|
218
220
|
ports: [`${r.port}:6379`],
|
|
219
221
|
healthcheck: {
|
package/dist/cli/infra.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"infra.cjs","names":["join","existsSync","readFileSync","dirname","randomBytes"],"sources":["../../src/cli/infra.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { RuntimeConfig } from \"../types\";\n\nconst POSTGRES_USER = \"everythingdev\";\nconst POSTGRES_PASSWORD = \"everythingdev\";\nconst AUTH_DATABASE_SECRET = \"AUTH_DATABASE_URL\";\nconst HOST_SECRET = \"CORS_ORIGIN\";\nconst REDIS_PORT = 6379;\nconst TEST_AUTH_SECRET = \"regression-test-secret-do-not-use-in-production\";\nconst VESTIGIAL_SECRETS = new Set([\n \"NEAR_RELAYER_PRIVATE_KEY_MAINNET\",\n \"NEAR_RELAYER_PRIVATE_KEY_TESTNET\",\n]);\n\n/**\n * Fixed local-development database conventions. The committed\n * docker-compose.yml provisions exactly these services/ports — nothing is\n * derived from runtime config. Every non-auth `*_DATABASE_URL` secret shares\n * the api database (schema-isolated per plugin); auth gets its own.\n */\nconst DEV_API_DB_PORT = 5432;\nconst DEV_AUTH_DB_PORT = 5433;\nconst TEST_API_DB_PORT = 5434;\nconst TEST_AUTH_DB_PORT = 5435;\n\nexport interface DatabaseConvention {\n secret: string;\n slug: string;\n port: number;\n databaseName: string;\n url: string;\n}\n\nexport interface RedisConvention {\n secret: string;\n slug: string;\n port: number;\n url: string;\n}\n\nexport interface SecretGroup {\n section: string;\n secrets: string[];\n}\n\ninterface DevPortState {\n host?: number;\n api?: number;\n ui?: number;\n auth?: number;\n pluginPortStart?: number;\n}\n\nexport type { DevPortState };\n\nexport interface PortState {\n devPorts?: DevPortState;\n}\n\nfunction uniqueSecrets(values: Array<string | undefined>): string[] {\n const secrets: string[] = [];\n const seen = new Set<string>();\n\n for (const value of values) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n secrets.push(value);\n }\n\n return secrets;\n}\n\nexport function loadPortState(configDir?: string): PortState {\n if (!configDir) return {};\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n if (!existsSync(statePath)) return {};\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Partial<PortState>;\n return { devPorts: raw.devPorts };\n } catch {\n return {};\n }\n}\n\nexport function savePortState(configDir: string, state: PortState): void {\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n mkdirSync(dirname(statePath), { recursive: true });\n writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\nexport function normalizeDatabaseSlug(secret: string): string {\n return secret.replace(/_DATABASE_URL$/, \"\").toLowerCase();\n}\n\nfunction normalizeRedisSlug(secret: string): string {\n return secret.replace(/_REDIS_URL$/, \"\").toLowerCase();\n}\n\nfunction databaseUrl(port: number, databaseName: string): string {\n return `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${databaseName}`;\n}\n\nexport function buildConventionalDatabases(secrets: string[]): DatabaseConvention[] {\n const databaseSecrets = uniqueSecrets(\n secrets.filter((secret) => secret.endsWith(\"_DATABASE_URL\")),\n );\n\n return databaseSecrets\n .sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b)))\n .map((secret) => {\n const slug = normalizeDatabaseSlug(secret);\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? DEV_AUTH_DB_PORT : DEV_API_DB_PORT;\n const databaseName = isAuth ? \"auth_db\" : \"api_db\";\n return { secret, slug, port, databaseName, url: databaseUrl(port, databaseName) };\n });\n}\n\nexport function buildConventionalRedis(secrets: string[]): RedisConvention[] {\n const redisSecrets = uniqueSecrets(secrets.filter((secret) => secret.endsWith(\"_REDIS_URL\")));\n\n return redisSecrets\n .sort((a, b) => normalizeRedisSlug(a).localeCompare(normalizeRedisSlug(b)))\n .map((secret) => ({\n secret,\n slug: normalizeRedisSlug(secret),\n port: REDIS_PORT,\n url: `redis://localhost:${REDIS_PORT}`,\n }));\n}\n\nfunction testDatabaseUrl(secret: string): string {\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? TEST_AUTH_DB_PORT : TEST_API_DB_PORT;\n const databaseName = isAuth ? \"auth_test_db\" : \"api_test_db\";\n return databaseUrl(port, databaseName);\n}\n\nexport function getSecretGroups(runtimeConfig: RuntimeConfig): SecretGroup[] {\n const groups: SecretGroup[] = [];\n const seen = new Set<string>();\n\n const addGroup = (section: string, secrets: string[]) => {\n const filtered = secrets.filter((s) => {\n if (VESTIGIAL_SECRETS.has(s)) return false;\n if (seen.has(s)) return false;\n seen.add(s);\n return true;\n });\n if (filtered.length > 0) {\n groups.push({ section, secrets: filtered });\n }\n };\n\n addGroup(\"app.host\", uniqueSecrets([...(runtimeConfig.host.secrets ?? []), HOST_SECRET]));\n\n addGroup(\"app.api\", uniqueSecrets(runtimeConfig.api.secrets ?? []));\n\n if (runtimeConfig.auth) {\n addGroup(\"app.auth\", uniqueSecrets(runtimeConfig.auth.secrets ?? []));\n }\n\n if (runtimeConfig.plugins) {\n for (const [pluginKey, plugin] of Object.entries(runtimeConfig.plugins)) {\n if (plugin.secrets && plugin.secrets.length > 0) {\n addGroup(`plugins.${pluginKey}`, plugin.secrets);\n }\n }\n }\n\n return groups;\n}\n\nfunction extractPortFromUrl(url: string): string | null {\n const match = url.match(/:(\\d{4,5})(?:\\/|$)/);\n return match?.[1] ?? null;\n}\n\nfunction resolveDevHostPort(runtimeConfig: RuntimeConfig): number {\n if (typeof runtimeConfig.host?.port === \"number\") return runtimeConfig.host.port;\n const fromUrl = runtimeConfig.host?.url ? extractPortFromUrl(runtimeConfig.host.url) : null;\n if (fromUrl) {\n const parsed = Number.parseInt(fromUrl, 10);\n if (!Number.isNaN(parsed)) return parsed;\n }\n return 3000;\n}\n\nexport { resolveDevHostPort };\n\nfunction defaultSecretValue(\n secret: string,\n databases: Map<string, DatabaseConvention>,\n redisConfigs: Map<string, RedisConvention>,\n options: { forExample: boolean; devHostPort?: number },\n): string {\n if (secret === \"BETTER_AUTH_SECRET\") {\n return options.forExample ? \"\" : randomBytes(32).toString(\"base64url\");\n }\n\n if (secret === \"CORS_ORIGIN\") {\n if (options.forExample) {\n return \"http://localhost:3000\";\n }\n if (typeof options.devHostPort === \"number\") {\n return `http://localhost:${options.devHostPort}`;\n }\n return \"http://localhost:3000\";\n }\n\n return databases.get(secret)?.url ?? redisConfigs.get(secret)?.url ?? \"\";\n}\n\n/**\n * Plugin `*_DATABASE_URL` secrets share the API database (the platform's\n * two-database contract), so they are not rendered as template entries —\n * the host falls back to `API_DATABASE_URL` when they are unset.\n */\nfunction isFallbackDatabaseSecret(secret: string): boolean {\n return (\n secret.endsWith(\"_DATABASE_URL\") &&\n secret !== \"API_DATABASE_URL\" &&\n secret !== \"AUTH_DATABASE_URL\"\n );\n}\n\nfunction renderEnvFile(\n groups: SecretGroup[],\n options: { forExample: boolean; devHostPort?: number },\n): string {\n const rendered = groups\n .map((group) => ({\n section: group.section,\n secrets: group.secrets.filter((secret) => !isFallbackDatabaseSecret(secret)),\n }))\n .filter((group) => group.secrets.length > 0);\n const allSecrets = rendered.flatMap((group) => group.secrets);\n const databaseMap = new Map(\n buildConventionalDatabases(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const redisMap = new Map(\n buildConventionalRedis(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const lines: string[] = [\n \"# Generated from configured bos secrets\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n\n for (const group of rendered) {\n lines.push(`# ${group.section}`);\n for (const secret of group.secrets) {\n lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, redisMap, options)}`);\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvFile };\n\nfunction renderEnvTestFile(groups: SecretGroup[]): string {\n const lines: string[] = [\n \"# Generated test environment — loaded by test suites instead of .env\",\n \"# Test databases run on dedicated *-test docker-compose services\",\n \"\",\n ];\n\n for (const group of groups) {\n const entries: string[] = [];\n for (const secret of group.secrets) {\n if (secret === \"BETTER_AUTH_SECRET\") {\n entries.push(`${secret}=${TEST_AUTH_SECRET}`);\n } else if (secret.endsWith(\"_DATABASE_URL\")) {\n entries.push(`${secret}=${testDatabaseUrl(secret)}`);\n }\n }\n if (entries.length > 0) {\n lines.push(`# ${group.section}`);\n lines.push(...entries);\n lines.push(\"\");\n }\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvTestFile };\n\nexport interface CiServiceSpec {\n key: string;\n slug: string;\n image: string;\n env: Record<string, string>;\n ports: string[];\n healthcheck?: { test: string[]; interval: string; timeout: string; retries: number };\n volumes: string[];\n database?: { user: string; password: string; name: string };\n}\n\nexport interface CiInfraPlan {\n account: string;\n gateway: string;\n project: string;\n env: Record<string, string>;\n services: CiServiceSpec[];\n generatedAt: string;\n}\n\nexport function buildCiInfraPlan(\n runtimeConfig: RuntimeConfig,\n options: { hostPortOverride?: number } = {},\n): CiInfraPlan {\n const allSecrets = collectAllSecrets(runtimeConfig);\n const databases = buildConventionalDatabases(allSecrets);\n const redisConfigs = buildConventionalRedis(allSecrets);\n\n const hostPort =\n options.hostPortOverride ??\n (Number.isFinite(Number(process.env.BOS_CI_HOST_PORT))\n ? Number(process.env.BOS_CI_HOST_PORT)\n : undefined) ??\n resolveDevHostPort(runtimeConfig);\n const env: Record<string, string> = {};\n for (const db of databases) env[db.secret] = db.url;\n for (const r of redisConfigs) env[r.secret] = r.url;\n\n env.CORS_ORIGIN = `http://127.0.0.1:${hostPort}`;\n if (!env.BETTER_AUTH_SECRET) env.BETTER_AUTH_SECRET = \"\";\n\n const services: CiServiceSpec[] = [];\n const seenPorts = new Set<number>();\n\n for (const db of databases) {\n if (seenPorts.has(db.port)) continue;\n seenPorts.add(db.port);\n services.push({\n key: db.slug,\n slug: db.slug,\n image: \"public.ecr.aws/docker/library/postgres:17-alpine\",\n env: {\n POSTGRES_USER: POSTGRES_USER,\n POSTGRES_PASSWORD: POSTGRES_PASSWORD,\n POSTGRES_DB: db.databaseName,\n },\n ports: [`${db.port}:5432`],\n healthcheck: {\n test: [\"CMD-SHELL\", `pg_isready -U ${POSTGRES_USER} -d ${db.databaseName}`],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${db.slug}_data:/var/lib/postgresql/data`],\n database: { user: POSTGRES_USER, password: POSTGRES_PASSWORD, name: db.databaseName },\n });\n }\n\n for (const r of redisConfigs) {\n services.push({\n key: r.slug,\n slug: r.slug,\n image: \"public.ecr.aws/docker/library/redis:7-alpine\",\n env: {},\n ports: [`${r.port}:6379`],\n healthcheck: {\n test: [\"CMD\", \"redis-cli\", \"ping\"],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${r.slug}_data:/data`],\n });\n }\n\n return {\n account: runtimeConfig.account,\n gateway: runtimeConfig.domain ?? runtimeConfig.account,\n project: runtimeConfig.account,\n env,\n services,\n generatedAt: new Date().toISOString(),\n };\n}\n\nfunction collectAllSecrets(runtimeConfig: RuntimeConfig): string[] {\n const all: string[] = [];\n all.push(...(runtimeConfig.host.secrets ?? []));\n all.push(...(runtimeConfig.api.secrets ?? []));\n if (runtimeConfig.auth) all.push(...(runtimeConfig.auth.secrets ?? []));\n if (runtimeConfig.plugins) {\n for (const plugin of Object.values(runtimeConfig.plugins)) {\n if (plugin.secrets) all.push(...plugin.secrets);\n }\n }\n return all.filter((s) => !VESTIGIAL_SECRETS.has(s));\n}\n"],"mappings":";;;;;AAKA,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAC1B,MAAM,uBAAuB;AAC7B,MAAM,cAAc;AACpB,MAAM,aAAa;AACnB,MAAM,mBAAmB;AACzB,MAAM,oCAAoB,IAAI,IAAI,CAChC,oCACA,kCACF,CAAC;;;;;;;AAQD,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AACzB,MAAM,oBAAoB;AAoC1B,SAAS,cAAc,QAA6C;CAClE,MAAM,UAAoB,CAAC;CAC3B,MAAM,uBAAO,IAAI,IAAY;CAE7B,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,CAAC,SAAS,KAAK,IAAI,KAAK,GAAG;EAC/B,KAAK,IAAI,KAAK;EACd,QAAQ,KAAK,KAAK;CACpB;CAEA,OAAO;AACT;AAEA,SAAgB,cAAc,WAA+B;CAC3D,IAAI,CAAC,WAAW,OAAO,CAAC;CACxB,MAAM,gBAAYA,gBAAK,WAAW,QAAQ,kBAAkB;CAC5D,IAAI,KAACC,oBAAW,SAAS,GAAG,OAAO,CAAC;CACpC,IAAI;EAEF,OAAO,EAAE,UADG,KAAK,UAAMC,sBAAa,WAAW,OAAO,CACjC,CAAC,CAAC,SAAS;CAClC,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAgB,cAAc,WAAmB,OAAwB;CACvE,MAAM,gBAAYF,gBAAK,WAAW,QAAQ,kBAAkB;CAC5D,2BAAUG,mBAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,2BAAc,WAAW,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,GAAG;AAChE;AAEA,SAAgB,sBAAsB,QAAwB;CAC5D,OAAO,OAAO,QAAQ,kBAAkB,EAAE,CAAC,CAAC,YAAY;AAC1D;AAEA,SAAS,mBAAmB,QAAwB;CAClD,OAAO,OAAO,QAAQ,eAAe,EAAE,CAAC,CAAC,YAAY;AACvD;AAEA,SAAS,YAAY,MAAc,cAA8B;CAC/D,OAAO,cAAc,cAAc,GAAG,kBAAkB,aAAa,KAAK,GAAG;AAC/E;AAEA,SAAgB,2BAA2B,SAAyC;CAKlF,OAJwB,cACtB,QAAQ,QAAQ,WAAW,OAAO,SAAS,eAAe,CAAC,CAGxC,CAAC,CACnB,MAAM,GAAG,MAAM,sBAAsB,CAAC,CAAC,CAAC,cAAc,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAChF,KAAK,WAAW;EACf,MAAM,OAAO,sBAAsB,MAAM;EACzC,MAAM,SAAS,WAAW;EAC1B,MAAM,OAAO,SAAS,mBAAmB;EACzC,MAAM,eAAe,SAAS,YAAY;EAC1C,OAAO;GAAE;GAAQ;GAAM;GAAM;GAAc,KAAK,YAAY,MAAM,YAAY;EAAE;CAClF,CAAC;AACL;AAEA,SAAgB,uBAAuB,SAAsC;CAG3E,OAFqB,cAAc,QAAQ,QAAQ,WAAW,OAAO,SAAS,YAAY,CAAC,CAEzE,CAAC,CAChB,MAAM,GAAG,MAAM,mBAAmB,CAAC,CAAC,CAAC,cAAc,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAC1E,KAAK,YAAY;EAChB;EACA,MAAM,mBAAmB,MAAM;EAC/B,MAAM;EACN,KAAK,qBAAqB;CAC5B,EAAE;AACN;AAEA,SAAS,gBAAgB,QAAwB;CAC/C,MAAM,SAAS,WAAW;CAG1B,OAAO,YAFM,SAAS,oBAAoB,kBACrB,SAAS,iBAAiB,aACV;AACvC;AAEA,SAAgB,gBAAgB,eAA6C;CAC3E,MAAM,SAAwB,CAAC;CAC/B,MAAM,uBAAO,IAAI,IAAY;CAE7B,MAAM,YAAY,SAAiB,YAAsB;EACvD,MAAM,WAAW,QAAQ,QAAQ,MAAM;GACrC,IAAI,kBAAkB,IAAI,CAAC,GAAG,OAAO;GACrC,IAAI,KAAK,IAAI,CAAC,GAAG,OAAO;GACxB,KAAK,IAAI,CAAC;GACV,OAAO;EACT,CAAC;EACD,IAAI,SAAS,SAAS,GACpB,OAAO,KAAK;GAAE;GAAS,SAAS;EAAS,CAAC;CAE9C;CAEA,SAAS,YAAY,cAAc,CAAC,GAAI,cAAc,KAAK,WAAW,CAAC,GAAI,WAAW,CAAC,CAAC;CAExF,SAAS,WAAW,cAAc,cAAc,IAAI,WAAW,CAAC,CAAC,CAAC;CAElE,IAAI,cAAc,MAChB,SAAS,YAAY,cAAc,cAAc,KAAK,WAAW,CAAC,CAAC,CAAC;CAGtE,IAAI,cAAc,SAChB;OAAK,MAAM,CAAC,WAAW,WAAW,OAAO,QAAQ,cAAc,OAAO,GACpE,IAAI,OAAO,WAAW,OAAO,QAAQ,SAAS,GAC5C,SAAS,WAAW,aAAa,OAAO,OAAO;CAEnD;CAGF,OAAO;AACT;AAEA,SAAS,mBAAmB,KAA4B;CAEtD,OADc,IAAI,MAAM,oBACb,CAAC,GAAG,MAAM;AACvB;AAEA,SAAS,mBAAmB,eAAsC;CAChE,IAAI,OAAO,cAAc,MAAM,SAAS,UAAU,OAAO,cAAc,KAAK;CAC5E,MAAM,UAAU,cAAc,MAAM,MAAM,mBAAmB,cAAc,KAAK,GAAG,IAAI;CACvF,IAAI,SAAS;EACX,MAAM,SAAS,OAAO,SAAS,SAAS,EAAE;EAC1C,IAAI,CAAC,OAAO,MAAM,MAAM,GAAG,OAAO;CACpC;CACA,OAAO;AACT;AAIA,SAAS,mBACP,QACA,WACA,cACA,SACQ;CACR,IAAI,WAAW,sBACb,OAAO,QAAQ,aAAa,SAAKC,yBAAY,EAAE,CAAC,CAAC,SAAS,WAAW;CAGvE,IAAI,WAAW,eAAe;EAC5B,IAAI,QAAQ,YACV,OAAO;EAET,IAAI,OAAO,QAAQ,gBAAgB,UACjC,OAAO,oBAAoB,QAAQ;EAErC,OAAO;CACT;CAEA,OAAO,UAAU,IAAI,MAAM,CAAC,EAAE,OAAO,aAAa,IAAI,MAAM,CAAC,EAAE,OAAO;AACxE;;;;;;AAOA,SAAS,yBAAyB,QAAyB;CACzD,OACE,OAAO,SAAS,eAAe,KAC/B,WAAW,sBACX,WAAW;AAEf;AAEA,SAAS,cACP,QACA,SACQ;CACR,MAAM,WAAW,OACd,KAAK,WAAW;EACf,SAAS,MAAM;EACf,SAAS,MAAM,QAAQ,QAAQ,WAAW,CAAC,yBAAyB,MAAM,CAAC;CAC7E,EAAE,CAAC,CACF,QAAQ,UAAU,MAAM,QAAQ,SAAS,CAAC;CAC7C,MAAM,aAAa,SAAS,SAAS,UAAU,MAAM,OAAO;CAC5D,MAAM,cAAc,IAAI,IACtB,2BAA2B,UAAU,CAAC,CAAC,KAAK,UAAU,CAAC,MAAM,QAAQ,KAAK,CAAC,CAC7E;CACA,MAAM,WAAW,IAAI,IACnB,uBAAuB,UAAU,CAAC,CAAC,KAAK,UAAU,CAAC,MAAM,QAAQ,KAAK,CAAC,CACzE;CACA,MAAM,QAAkB;EACtB;EACA;EACA;CACF;CAEA,KAAK,MAAM,SAAS,UAAU;EAC5B,MAAM,KAAK,KAAK,MAAM,SAAS;EAC/B,KAAK,MAAM,UAAU,MAAM,SACzB,MAAM,KAAK,GAAG,OAAO,GAAG,mBAAmB,QAAQ,aAAa,UAAU,OAAO,GAAG;EAEtF,MAAM,KAAK,EAAE;CACf;CAEA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AAIA,SAAS,kBAAkB,QAA+B;CACxD,MAAM,QAAkB;EACtB;EACA;EACA;CACF;CAEA,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,UAAoB,CAAC;EAC3B,KAAK,MAAM,UAAU,MAAM,SACzB,IAAI,WAAW,sBACb,QAAQ,KAAK,GAAG,OAAO,GAAG,kBAAkB;OACvC,IAAI,OAAO,SAAS,eAAe,GACxC,QAAQ,KAAK,GAAG,OAAO,GAAG,gBAAgB,MAAM,GAAG;EAGvD,IAAI,QAAQ,SAAS,GAAG;GACtB,MAAM,KAAK,KAAK,MAAM,SAAS;GAC/B,MAAM,KAAK,GAAG,OAAO;GACrB,MAAM,KAAK,EAAE;EACf;CACF;CAEA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AAwBA,SAAgB,iBACd,eACA,UAAyC,CAAC,GAC7B;CACb,MAAM,aAAa,kBAAkB,aAAa;CAClD,MAAM,YAAY,2BAA2B,UAAU;CACvD,MAAM,eAAe,uBAAuB,UAAU;CAEtD,MAAM,WACJ,QAAQ,qBACP,OAAO,SAAS,OAAO,QAAQ,IAAI,gBAAgB,CAAC,IACjD,OAAO,QAAQ,IAAI,gBAAgB,IACnC,WACJ,mBAAmB,aAAa;CAClC,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,MAAM,WAAW,IAAI,GAAG,UAAU,GAAG;CAChD,KAAK,MAAM,KAAK,cAAc,IAAI,EAAE,UAAU,EAAE;CAEhD,IAAI,cAAc,oBAAoB;CACtC,IAAI,CAAC,IAAI,oBAAoB,IAAI,qBAAqB;CAEtD,MAAM,WAA4B,CAAC;CACnC,MAAM,4BAAY,IAAI,IAAY;CAElC,KAAK,MAAM,MAAM,WAAW;EAC1B,IAAI,UAAU,IAAI,GAAG,IAAI,GAAG;EAC5B,UAAU,IAAI,GAAG,IAAI;EACrB,SAAS,KAAK;GACZ,KAAK,GAAG;GACR,MAAM,GAAG;GACT,OAAO;GACP,KAAK;IACY;IACI;IACnB,aAAa,GAAG;GAClB;GACA,OAAO,CAAC,GAAG,GAAG,KAAK,MAAM;GACzB,aAAa;IACX,MAAM,CAAC,aAAa,iBAAiB,cAAc,MAAM,GAAG,cAAc;IAC1E,UAAU;IACV,SAAS;IACT,SAAS;GACX;GACA,SAAS,CAAC,GAAG,GAAG,KAAK,+BAA+B;GACpD,UAAU;IAAE,MAAM;IAAe,UAAU;IAAmB,MAAM,GAAG;GAAa;EACtF,CAAC;CACH;CAEA,KAAK,MAAM,KAAK,cACd,SAAS,KAAK;EACZ,KAAK,EAAE;EACP,MAAM,EAAE;EACR,OAAO;EACP,KAAK,CAAC;EACN,OAAO,CAAC,GAAG,EAAE,KAAK,MAAM;EACxB,aAAa;GACX,MAAM;IAAC;IAAO;IAAa;GAAM;GACjC,UAAU;GACV,SAAS;GACT,SAAS;EACX;EACA,SAAS,CAAC,GAAG,EAAE,KAAK,YAAY;CAClC,CAAC;CAGH,OAAO;EACL,SAAS,cAAc;EACvB,SAAS,cAAc,UAAU,cAAc;EAC/C,SAAS,cAAc;EACvB;EACA;EACA,8BAAa,IAAI,KAAK,EAAC,CAAC,YAAY;CACtC;AACF;AAEA,SAAS,kBAAkB,eAAwC;CACjE,MAAM,MAAgB,CAAC;CACvB,IAAI,KAAK,GAAI,cAAc,KAAK,WAAW,CAAC,CAAE;CAC9C,IAAI,KAAK,GAAI,cAAc,IAAI,WAAW,CAAC,CAAE;CAC7C,IAAI,cAAc,MAAM,IAAI,KAAK,GAAI,cAAc,KAAK,WAAW,CAAC,CAAE;CACtE,IAAI,cAAc,SAChB;OAAK,MAAM,UAAU,OAAO,OAAO,cAAc,OAAO,GACtD,IAAI,OAAO,SAAS,IAAI,KAAK,GAAG,OAAO,OAAO;CAChD;CAEF,OAAO,IAAI,QAAQ,MAAM,CAAC,kBAAkB,IAAI,CAAC,CAAC;AACpD"}
|
|
1
|
+
{"version":3,"file":"infra.cjs","names":["join","existsSync","readFileSync","dirname","randomBytes"],"sources":["../../src/cli/infra.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { RuntimeConfig } from \"../types\";\n\nconst POSTGRES_USER = \"everythingdev\";\nconst POSTGRES_PASSWORD = \"everythingdev\";\nconst AUTH_DATABASE_SECRET = \"AUTH_DATABASE_URL\";\nconst HOST_SECRET = \"CORS_ORIGIN\";\nconst REDIS_PORT = 6379;\nconst TEST_AUTH_SECRET = \"regression-test-secret-do-not-use-in-production\";\nconst VESTIGIAL_SECRETS = new Set([\n \"NEAR_RELAYER_PRIVATE_KEY_MAINNET\",\n \"NEAR_RELAYER_PRIVATE_KEY_TESTNET\",\n]);\n\n/**\n * Fixed local-development database conventions. The committed\n * docker-compose.yml provisions exactly these services/ports — nothing is\n * derived from runtime config. Every non-auth `*_DATABASE_URL` secret shares\n * the api database (schema-isolated per plugin); auth gets its own.\n */\nconst DEV_API_DB_PORT = 5432;\nconst DEV_AUTH_DB_PORT = 5433;\nconst TEST_API_DB_PORT = 5434;\nconst TEST_AUTH_DB_PORT = 5435;\n\n// The org's GHCR mirror of the upstream library images (refreshed by\n// .github/workflows/mirror-images.yml) — Docker Hub and ECR Public both\n// rate-limit shared CI egress IPs.\nconst CI_POSTGRES_IMAGE = \"ghcr.io/nearbuilders/mirror-postgres:17-alpine\";\nconst CI_REDIS_IMAGE = \"ghcr.io/nearbuilders/mirror-redis:7-alpine\";\n\nexport interface DatabaseConvention {\n secret: string;\n slug: string;\n port: number;\n databaseName: string;\n url: string;\n}\n\nexport interface RedisConvention {\n secret: string;\n slug: string;\n port: number;\n url: string;\n}\n\nexport interface SecretGroup {\n section: string;\n secrets: string[];\n}\n\ninterface DevPortState {\n host?: number;\n api?: number;\n ui?: number;\n auth?: number;\n pluginPortStart?: number;\n}\n\nexport type { DevPortState };\n\nexport interface PortState {\n devPorts?: DevPortState;\n}\n\nfunction uniqueSecrets(values: Array<string | undefined>): string[] {\n const secrets: string[] = [];\n const seen = new Set<string>();\n\n for (const value of values) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n secrets.push(value);\n }\n\n return secrets;\n}\n\nexport function loadPortState(configDir?: string): PortState {\n if (!configDir) return {};\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n if (!existsSync(statePath)) return {};\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Partial<PortState>;\n return { devPorts: raw.devPorts };\n } catch {\n return {};\n }\n}\n\nexport function savePortState(configDir: string, state: PortState): void {\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n mkdirSync(dirname(statePath), { recursive: true });\n writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\nexport function normalizeDatabaseSlug(secret: string): string {\n return secret.replace(/_DATABASE_URL$/, \"\").toLowerCase();\n}\n\nfunction normalizeRedisSlug(secret: string): string {\n return secret.replace(/_REDIS_URL$/, \"\").toLowerCase();\n}\n\nfunction databaseUrl(port: number, databaseName: string): string {\n return `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${databaseName}`;\n}\n\nexport function buildConventionalDatabases(secrets: string[]): DatabaseConvention[] {\n const databaseSecrets = uniqueSecrets(\n secrets.filter((secret) => secret.endsWith(\"_DATABASE_URL\")),\n );\n\n return databaseSecrets\n .sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b)))\n .map((secret) => {\n const slug = normalizeDatabaseSlug(secret);\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? DEV_AUTH_DB_PORT : DEV_API_DB_PORT;\n const databaseName = isAuth ? \"auth_db\" : \"api_db\";\n return { secret, slug, port, databaseName, url: databaseUrl(port, databaseName) };\n });\n}\n\nexport function buildConventionalRedis(secrets: string[]): RedisConvention[] {\n const redisSecrets = uniqueSecrets(secrets.filter((secret) => secret.endsWith(\"_REDIS_URL\")));\n\n return redisSecrets\n .sort((a, b) => normalizeRedisSlug(a).localeCompare(normalizeRedisSlug(b)))\n .map((secret) => ({\n secret,\n slug: normalizeRedisSlug(secret),\n port: REDIS_PORT,\n url: `redis://localhost:${REDIS_PORT}`,\n }));\n}\n\nfunction testDatabaseUrl(secret: string): string {\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? TEST_AUTH_DB_PORT : TEST_API_DB_PORT;\n const databaseName = isAuth ? \"auth_test_db\" : \"api_test_db\";\n return databaseUrl(port, databaseName);\n}\n\nexport function getSecretGroups(runtimeConfig: RuntimeConfig): SecretGroup[] {\n const groups: SecretGroup[] = [];\n const seen = new Set<string>();\n\n const addGroup = (section: string, secrets: string[]) => {\n const filtered = secrets.filter((s) => {\n if (VESTIGIAL_SECRETS.has(s)) return false;\n if (seen.has(s)) return false;\n seen.add(s);\n return true;\n });\n if (filtered.length > 0) {\n groups.push({ section, secrets: filtered });\n }\n };\n\n addGroup(\"app.host\", uniqueSecrets([...(runtimeConfig.host.secrets ?? []), HOST_SECRET]));\n\n addGroup(\"app.api\", uniqueSecrets(runtimeConfig.api.secrets ?? []));\n\n if (runtimeConfig.auth) {\n addGroup(\"app.auth\", uniqueSecrets(runtimeConfig.auth.secrets ?? []));\n }\n\n if (runtimeConfig.plugins) {\n for (const [pluginKey, plugin] of Object.entries(runtimeConfig.plugins)) {\n if (plugin.secrets && plugin.secrets.length > 0) {\n addGroup(`plugins.${pluginKey}`, plugin.secrets);\n }\n }\n }\n\n return groups;\n}\n\nfunction extractPortFromUrl(url: string): string | null {\n const match = url.match(/:(\\d{4,5})(?:\\/|$)/);\n return match?.[1] ?? null;\n}\n\nfunction resolveDevHostPort(runtimeConfig: RuntimeConfig): number {\n if (typeof runtimeConfig.host?.port === \"number\") return runtimeConfig.host.port;\n const fromUrl = runtimeConfig.host?.url ? extractPortFromUrl(runtimeConfig.host.url) : null;\n if (fromUrl) {\n const parsed = Number.parseInt(fromUrl, 10);\n if (!Number.isNaN(parsed)) return parsed;\n }\n return 3000;\n}\n\nexport { resolveDevHostPort };\n\nfunction defaultSecretValue(\n secret: string,\n databases: Map<string, DatabaseConvention>,\n redisConfigs: Map<string, RedisConvention>,\n options: { forExample: boolean; devHostPort?: number },\n): string {\n if (secret === \"BETTER_AUTH_SECRET\") {\n return options.forExample ? \"\" : randomBytes(32).toString(\"base64url\");\n }\n\n if (secret === \"CORS_ORIGIN\") {\n if (options.forExample) {\n return \"http://localhost:3000\";\n }\n if (typeof options.devHostPort === \"number\") {\n return `http://localhost:${options.devHostPort}`;\n }\n return \"http://localhost:3000\";\n }\n\n return databases.get(secret)?.url ?? redisConfigs.get(secret)?.url ?? \"\";\n}\n\n/**\n * Plugin `*_DATABASE_URL` secrets share the API database (the platform's\n * two-database contract), so they are not rendered as template entries —\n * the host falls back to `API_DATABASE_URL` when they are unset.\n */\nfunction isFallbackDatabaseSecret(secret: string): boolean {\n return (\n secret.endsWith(\"_DATABASE_URL\") &&\n secret !== \"API_DATABASE_URL\" &&\n secret !== \"AUTH_DATABASE_URL\"\n );\n}\n\nfunction renderEnvFile(\n groups: SecretGroup[],\n options: { forExample: boolean; devHostPort?: number },\n): string {\n const rendered = groups\n .map((group) => ({\n section: group.section,\n secrets: group.secrets.filter((secret) => !isFallbackDatabaseSecret(secret)),\n }))\n .filter((group) => group.secrets.length > 0);\n const allSecrets = rendered.flatMap((group) => group.secrets);\n const databaseMap = new Map(\n buildConventionalDatabases(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const redisMap = new Map(\n buildConventionalRedis(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const lines: string[] = [\n \"# Generated from configured bos secrets\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n\n for (const group of rendered) {\n lines.push(`# ${group.section}`);\n for (const secret of group.secrets) {\n lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, redisMap, options)}`);\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvFile };\n\nfunction renderEnvTestFile(groups: SecretGroup[]): string {\n const lines: string[] = [\n \"# Generated test environment — loaded by test suites instead of .env\",\n \"# Test databases run on dedicated *-test docker-compose services\",\n \"\",\n ];\n\n for (const group of groups) {\n const entries: string[] = [];\n for (const secret of group.secrets) {\n if (secret === \"BETTER_AUTH_SECRET\") {\n entries.push(`${secret}=${TEST_AUTH_SECRET}`);\n } else if (secret.endsWith(\"_DATABASE_URL\")) {\n entries.push(`${secret}=${testDatabaseUrl(secret)}`);\n }\n }\n if (entries.length > 0) {\n lines.push(`# ${group.section}`);\n lines.push(...entries);\n lines.push(\"\");\n }\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvTestFile };\n\nexport interface CiServiceSpec {\n key: string;\n slug: string;\n image: string;\n env: Record<string, string>;\n ports: string[];\n healthcheck?: { test: string[]; interval: string; timeout: string; retries: number };\n volumes: string[];\n database?: { user: string; password: string; name: string };\n}\n\nexport interface CiInfraPlan {\n account: string;\n gateway: string;\n project: string;\n env: Record<string, string>;\n services: CiServiceSpec[];\n generatedAt: string;\n}\n\nexport function buildCiInfraPlan(\n runtimeConfig: RuntimeConfig,\n options: { hostPortOverride?: number } = {},\n): CiInfraPlan {\n const allSecrets = collectAllSecrets(runtimeConfig);\n const databases = buildConventionalDatabases(allSecrets);\n const redisConfigs = buildConventionalRedis(allSecrets);\n\n const hostPort =\n options.hostPortOverride ??\n (Number.isFinite(Number(process.env.BOS_CI_HOST_PORT))\n ? Number(process.env.BOS_CI_HOST_PORT)\n : undefined) ??\n resolveDevHostPort(runtimeConfig);\n const env: Record<string, string> = {};\n for (const db of databases) env[db.secret] = db.url;\n for (const r of redisConfigs) env[r.secret] = r.url;\n\n env.CORS_ORIGIN = `http://127.0.0.1:${hostPort}`;\n if (!env.BETTER_AUTH_SECRET) env.BETTER_AUTH_SECRET = \"\";\n\n const services: CiServiceSpec[] = [];\n const seenPorts = new Set<number>();\n\n for (const db of databases) {\n if (seenPorts.has(db.port)) continue;\n seenPorts.add(db.port);\n services.push({\n key: db.slug,\n slug: db.slug,\n image: CI_POSTGRES_IMAGE,\n env: {\n POSTGRES_USER: POSTGRES_USER,\n POSTGRES_PASSWORD: POSTGRES_PASSWORD,\n POSTGRES_DB: db.databaseName,\n },\n ports: [`${db.port}:5432`],\n healthcheck: {\n test: [\"CMD-SHELL\", `pg_isready -U ${POSTGRES_USER} -d ${db.databaseName}`],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${db.slug}_data:/var/lib/postgresql/data`],\n database: { user: POSTGRES_USER, password: POSTGRES_PASSWORD, name: db.databaseName },\n });\n }\n\n for (const r of redisConfigs) {\n services.push({\n key: r.slug,\n slug: r.slug,\n image: CI_REDIS_IMAGE,\n env: {},\n ports: [`${r.port}:6379`],\n healthcheck: {\n test: [\"CMD\", \"redis-cli\", \"ping\"],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${r.slug}_data:/data`],\n });\n }\n\n return {\n account: runtimeConfig.account,\n gateway: runtimeConfig.domain ?? runtimeConfig.account,\n project: runtimeConfig.account,\n env,\n services,\n generatedAt: new Date().toISOString(),\n };\n}\n\nfunction collectAllSecrets(runtimeConfig: RuntimeConfig): string[] {\n const all: string[] = [];\n all.push(...(runtimeConfig.host.secrets ?? []));\n all.push(...(runtimeConfig.api.secrets ?? []));\n if (runtimeConfig.auth) all.push(...(runtimeConfig.auth.secrets ?? []));\n if (runtimeConfig.plugins) {\n for (const plugin of Object.values(runtimeConfig.plugins)) {\n if (plugin.secrets) all.push(...plugin.secrets);\n }\n }\n return all.filter((s) => !VESTIGIAL_SECRETS.has(s));\n}\n"],"mappings":"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"}
|
package/dist/cli/infra.mjs
CHANGED
|
@@ -20,6 +20,8 @@ const DEV_API_DB_PORT = 5432;
|
|
|
20
20
|
const DEV_AUTH_DB_PORT = 5433;
|
|
21
21
|
const TEST_API_DB_PORT = 5434;
|
|
22
22
|
const TEST_AUTH_DB_PORT = 5435;
|
|
23
|
+
const CI_POSTGRES_IMAGE = "ghcr.io/nearbuilders/mirror-postgres:17-alpine";
|
|
24
|
+
const CI_REDIS_IMAGE = "ghcr.io/nearbuilders/mirror-redis:7-alpine";
|
|
23
25
|
function uniqueSecrets(values) {
|
|
24
26
|
const secrets = [];
|
|
25
27
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -189,7 +191,7 @@ function buildCiInfraPlan(runtimeConfig, options = {}) {
|
|
|
189
191
|
services.push({
|
|
190
192
|
key: db.slug,
|
|
191
193
|
slug: db.slug,
|
|
192
|
-
image:
|
|
194
|
+
image: CI_POSTGRES_IMAGE,
|
|
193
195
|
env: {
|
|
194
196
|
POSTGRES_USER,
|
|
195
197
|
POSTGRES_PASSWORD,
|
|
@@ -213,7 +215,7 @@ function buildCiInfraPlan(runtimeConfig, options = {}) {
|
|
|
213
215
|
for (const r of redisConfigs) services.push({
|
|
214
216
|
key: r.slug,
|
|
215
217
|
slug: r.slug,
|
|
216
|
-
image:
|
|
218
|
+
image: CI_REDIS_IMAGE,
|
|
217
219
|
env: {},
|
|
218
220
|
ports: [`${r.port}:6379`],
|
|
219
221
|
healthcheck: {
|
package/dist/cli/infra.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"infra.mjs","names":[],"sources":["../../src/cli/infra.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { RuntimeConfig } from \"../types\";\n\nconst POSTGRES_USER = \"everythingdev\";\nconst POSTGRES_PASSWORD = \"everythingdev\";\nconst AUTH_DATABASE_SECRET = \"AUTH_DATABASE_URL\";\nconst HOST_SECRET = \"CORS_ORIGIN\";\nconst REDIS_PORT = 6379;\nconst TEST_AUTH_SECRET = \"regression-test-secret-do-not-use-in-production\";\nconst VESTIGIAL_SECRETS = new Set([\n \"NEAR_RELAYER_PRIVATE_KEY_MAINNET\",\n \"NEAR_RELAYER_PRIVATE_KEY_TESTNET\",\n]);\n\n/**\n * Fixed local-development database conventions. The committed\n * docker-compose.yml provisions exactly these services/ports — nothing is\n * derived from runtime config. Every non-auth `*_DATABASE_URL` secret shares\n * the api database (schema-isolated per plugin); auth gets its own.\n */\nconst DEV_API_DB_PORT = 5432;\nconst DEV_AUTH_DB_PORT = 5433;\nconst TEST_API_DB_PORT = 5434;\nconst TEST_AUTH_DB_PORT = 5435;\n\nexport interface DatabaseConvention {\n secret: string;\n slug: string;\n port: number;\n databaseName: string;\n url: string;\n}\n\nexport interface RedisConvention {\n secret: string;\n slug: string;\n port: number;\n url: string;\n}\n\nexport interface SecretGroup {\n section: string;\n secrets: string[];\n}\n\ninterface DevPortState {\n host?: number;\n api?: number;\n ui?: number;\n auth?: number;\n pluginPortStart?: number;\n}\n\nexport type { DevPortState };\n\nexport interface PortState {\n devPorts?: DevPortState;\n}\n\nfunction uniqueSecrets(values: Array<string | undefined>): string[] {\n const secrets: string[] = [];\n const seen = new Set<string>();\n\n for (const value of values) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n secrets.push(value);\n }\n\n return secrets;\n}\n\nexport function loadPortState(configDir?: string): PortState {\n if (!configDir) return {};\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n if (!existsSync(statePath)) return {};\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Partial<PortState>;\n return { devPorts: raw.devPorts };\n } catch {\n return {};\n }\n}\n\nexport function savePortState(configDir: string, state: PortState): void {\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n mkdirSync(dirname(statePath), { recursive: true });\n writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\nexport function normalizeDatabaseSlug(secret: string): string {\n return secret.replace(/_DATABASE_URL$/, \"\").toLowerCase();\n}\n\nfunction normalizeRedisSlug(secret: string): string {\n return secret.replace(/_REDIS_URL$/, \"\").toLowerCase();\n}\n\nfunction databaseUrl(port: number, databaseName: string): string {\n return `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${databaseName}`;\n}\n\nexport function buildConventionalDatabases(secrets: string[]): DatabaseConvention[] {\n const databaseSecrets = uniqueSecrets(\n secrets.filter((secret) => secret.endsWith(\"_DATABASE_URL\")),\n );\n\n return databaseSecrets\n .sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b)))\n .map((secret) => {\n const slug = normalizeDatabaseSlug(secret);\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? DEV_AUTH_DB_PORT : DEV_API_DB_PORT;\n const databaseName = isAuth ? \"auth_db\" : \"api_db\";\n return { secret, slug, port, databaseName, url: databaseUrl(port, databaseName) };\n });\n}\n\nexport function buildConventionalRedis(secrets: string[]): RedisConvention[] {\n const redisSecrets = uniqueSecrets(secrets.filter((secret) => secret.endsWith(\"_REDIS_URL\")));\n\n return redisSecrets\n .sort((a, b) => normalizeRedisSlug(a).localeCompare(normalizeRedisSlug(b)))\n .map((secret) => ({\n secret,\n slug: normalizeRedisSlug(secret),\n port: REDIS_PORT,\n url: `redis://localhost:${REDIS_PORT}`,\n }));\n}\n\nfunction testDatabaseUrl(secret: string): string {\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? TEST_AUTH_DB_PORT : TEST_API_DB_PORT;\n const databaseName = isAuth ? \"auth_test_db\" : \"api_test_db\";\n return databaseUrl(port, databaseName);\n}\n\nexport function getSecretGroups(runtimeConfig: RuntimeConfig): SecretGroup[] {\n const groups: SecretGroup[] = [];\n const seen = new Set<string>();\n\n const addGroup = (section: string, secrets: string[]) => {\n const filtered = secrets.filter((s) => {\n if (VESTIGIAL_SECRETS.has(s)) return false;\n if (seen.has(s)) return false;\n seen.add(s);\n return true;\n });\n if (filtered.length > 0) {\n groups.push({ section, secrets: filtered });\n }\n };\n\n addGroup(\"app.host\", uniqueSecrets([...(runtimeConfig.host.secrets ?? []), HOST_SECRET]));\n\n addGroup(\"app.api\", uniqueSecrets(runtimeConfig.api.secrets ?? []));\n\n if (runtimeConfig.auth) {\n addGroup(\"app.auth\", uniqueSecrets(runtimeConfig.auth.secrets ?? []));\n }\n\n if (runtimeConfig.plugins) {\n for (const [pluginKey, plugin] of Object.entries(runtimeConfig.plugins)) {\n if (plugin.secrets && plugin.secrets.length > 0) {\n addGroup(`plugins.${pluginKey}`, plugin.secrets);\n }\n }\n }\n\n return groups;\n}\n\nfunction extractPortFromUrl(url: string): string | null {\n const match = url.match(/:(\\d{4,5})(?:\\/|$)/);\n return match?.[1] ?? null;\n}\n\nfunction resolveDevHostPort(runtimeConfig: RuntimeConfig): number {\n if (typeof runtimeConfig.host?.port === \"number\") return runtimeConfig.host.port;\n const fromUrl = runtimeConfig.host?.url ? extractPortFromUrl(runtimeConfig.host.url) : null;\n if (fromUrl) {\n const parsed = Number.parseInt(fromUrl, 10);\n if (!Number.isNaN(parsed)) return parsed;\n }\n return 3000;\n}\n\nexport { resolveDevHostPort };\n\nfunction defaultSecretValue(\n secret: string,\n databases: Map<string, DatabaseConvention>,\n redisConfigs: Map<string, RedisConvention>,\n options: { forExample: boolean; devHostPort?: number },\n): string {\n if (secret === \"BETTER_AUTH_SECRET\") {\n return options.forExample ? \"\" : randomBytes(32).toString(\"base64url\");\n }\n\n if (secret === \"CORS_ORIGIN\") {\n if (options.forExample) {\n return \"http://localhost:3000\";\n }\n if (typeof options.devHostPort === \"number\") {\n return `http://localhost:${options.devHostPort}`;\n }\n return \"http://localhost:3000\";\n }\n\n return databases.get(secret)?.url ?? redisConfigs.get(secret)?.url ?? \"\";\n}\n\n/**\n * Plugin `*_DATABASE_URL` secrets share the API database (the platform's\n * two-database contract), so they are not rendered as template entries —\n * the host falls back to `API_DATABASE_URL` when they are unset.\n */\nfunction isFallbackDatabaseSecret(secret: string): boolean {\n return (\n secret.endsWith(\"_DATABASE_URL\") &&\n secret !== \"API_DATABASE_URL\" &&\n secret !== \"AUTH_DATABASE_URL\"\n );\n}\n\nfunction renderEnvFile(\n groups: SecretGroup[],\n options: { forExample: boolean; devHostPort?: number },\n): string {\n const rendered = groups\n .map((group) => ({\n section: group.section,\n secrets: group.secrets.filter((secret) => !isFallbackDatabaseSecret(secret)),\n }))\n .filter((group) => group.secrets.length > 0);\n const allSecrets = rendered.flatMap((group) => group.secrets);\n const databaseMap = new Map(\n buildConventionalDatabases(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const redisMap = new Map(\n buildConventionalRedis(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const lines: string[] = [\n \"# Generated from configured bos secrets\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n\n for (const group of rendered) {\n lines.push(`# ${group.section}`);\n for (const secret of group.secrets) {\n lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, redisMap, options)}`);\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvFile };\n\nfunction renderEnvTestFile(groups: SecretGroup[]): string {\n const lines: string[] = [\n \"# Generated test environment — loaded by test suites instead of .env\",\n \"# Test databases run on dedicated *-test docker-compose services\",\n \"\",\n ];\n\n for (const group of groups) {\n const entries: string[] = [];\n for (const secret of group.secrets) {\n if (secret === \"BETTER_AUTH_SECRET\") {\n entries.push(`${secret}=${TEST_AUTH_SECRET}`);\n } else if (secret.endsWith(\"_DATABASE_URL\")) {\n entries.push(`${secret}=${testDatabaseUrl(secret)}`);\n }\n }\n if (entries.length > 0) {\n lines.push(`# ${group.section}`);\n lines.push(...entries);\n lines.push(\"\");\n }\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvTestFile };\n\nexport interface CiServiceSpec {\n key: string;\n slug: string;\n image: string;\n env: Record<string, string>;\n ports: string[];\n healthcheck?: { test: string[]; interval: string; timeout: string; retries: number };\n volumes: string[];\n database?: { user: string; password: string; name: string };\n}\n\nexport interface CiInfraPlan {\n account: string;\n gateway: string;\n project: string;\n env: Record<string, string>;\n services: CiServiceSpec[];\n generatedAt: string;\n}\n\nexport function buildCiInfraPlan(\n runtimeConfig: RuntimeConfig,\n options: { hostPortOverride?: number } = {},\n): CiInfraPlan {\n const allSecrets = collectAllSecrets(runtimeConfig);\n const databases = buildConventionalDatabases(allSecrets);\n const redisConfigs = buildConventionalRedis(allSecrets);\n\n const hostPort =\n options.hostPortOverride ??\n (Number.isFinite(Number(process.env.BOS_CI_HOST_PORT))\n ? Number(process.env.BOS_CI_HOST_PORT)\n : undefined) ??\n resolveDevHostPort(runtimeConfig);\n const env: Record<string, string> = {};\n for (const db of databases) env[db.secret] = db.url;\n for (const r of redisConfigs) env[r.secret] = r.url;\n\n env.CORS_ORIGIN = `http://127.0.0.1:${hostPort}`;\n if (!env.BETTER_AUTH_SECRET) env.BETTER_AUTH_SECRET = \"\";\n\n const services: CiServiceSpec[] = [];\n const seenPorts = new Set<number>();\n\n for (const db of databases) {\n if (seenPorts.has(db.port)) continue;\n seenPorts.add(db.port);\n services.push({\n key: db.slug,\n slug: db.slug,\n image: \"public.ecr.aws/docker/library/postgres:17-alpine\",\n env: {\n POSTGRES_USER: POSTGRES_USER,\n POSTGRES_PASSWORD: POSTGRES_PASSWORD,\n POSTGRES_DB: db.databaseName,\n },\n ports: [`${db.port}:5432`],\n healthcheck: {\n test: [\"CMD-SHELL\", `pg_isready -U ${POSTGRES_USER} -d ${db.databaseName}`],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${db.slug}_data:/var/lib/postgresql/data`],\n database: { user: POSTGRES_USER, password: POSTGRES_PASSWORD, name: db.databaseName },\n });\n }\n\n for (const r of redisConfigs) {\n services.push({\n key: r.slug,\n slug: r.slug,\n image: \"public.ecr.aws/docker/library/redis:7-alpine\",\n env: {},\n ports: [`${r.port}:6379`],\n healthcheck: {\n test: [\"CMD\", \"redis-cli\", \"ping\"],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${r.slug}_data:/data`],\n });\n }\n\n return {\n account: runtimeConfig.account,\n gateway: runtimeConfig.domain ?? runtimeConfig.account,\n project: runtimeConfig.account,\n env,\n services,\n generatedAt: new Date().toISOString(),\n };\n}\n\nfunction collectAllSecrets(runtimeConfig: RuntimeConfig): string[] {\n const all: string[] = [];\n all.push(...(runtimeConfig.host.secrets ?? []));\n all.push(...(runtimeConfig.api.secrets ?? []));\n if (runtimeConfig.auth) all.push(...(runtimeConfig.auth.secrets ?? []));\n if (runtimeConfig.plugins) {\n for (const plugin of Object.values(runtimeConfig.plugins)) {\n if (plugin.secrets) all.push(...plugin.secrets);\n }\n }\n return all.filter((s) => !VESTIGIAL_SECRETS.has(s));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"infra.mjs","names":[],"sources":["../../src/cli/infra.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { RuntimeConfig } from \"../types\";\n\nconst POSTGRES_USER = \"everythingdev\";\nconst POSTGRES_PASSWORD = \"everythingdev\";\nconst AUTH_DATABASE_SECRET = \"AUTH_DATABASE_URL\";\nconst HOST_SECRET = \"CORS_ORIGIN\";\nconst REDIS_PORT = 6379;\nconst TEST_AUTH_SECRET = \"regression-test-secret-do-not-use-in-production\";\nconst VESTIGIAL_SECRETS = new Set([\n \"NEAR_RELAYER_PRIVATE_KEY_MAINNET\",\n \"NEAR_RELAYER_PRIVATE_KEY_TESTNET\",\n]);\n\n/**\n * Fixed local-development database conventions. The committed\n * docker-compose.yml provisions exactly these services/ports — nothing is\n * derived from runtime config. Every non-auth `*_DATABASE_URL` secret shares\n * the api database (schema-isolated per plugin); auth gets its own.\n */\nconst DEV_API_DB_PORT = 5432;\nconst DEV_AUTH_DB_PORT = 5433;\nconst TEST_API_DB_PORT = 5434;\nconst TEST_AUTH_DB_PORT = 5435;\n\n// The org's GHCR mirror of the upstream library images (refreshed by\n// .github/workflows/mirror-images.yml) — Docker Hub and ECR Public both\n// rate-limit shared CI egress IPs.\nconst CI_POSTGRES_IMAGE = \"ghcr.io/nearbuilders/mirror-postgres:17-alpine\";\nconst CI_REDIS_IMAGE = \"ghcr.io/nearbuilders/mirror-redis:7-alpine\";\n\nexport interface DatabaseConvention {\n secret: string;\n slug: string;\n port: number;\n databaseName: string;\n url: string;\n}\n\nexport interface RedisConvention {\n secret: string;\n slug: string;\n port: number;\n url: string;\n}\n\nexport interface SecretGroup {\n section: string;\n secrets: string[];\n}\n\ninterface DevPortState {\n host?: number;\n api?: number;\n ui?: number;\n auth?: number;\n pluginPortStart?: number;\n}\n\nexport type { DevPortState };\n\nexport interface PortState {\n devPorts?: DevPortState;\n}\n\nfunction uniqueSecrets(values: Array<string | undefined>): string[] {\n const secrets: string[] = [];\n const seen = new Set<string>();\n\n for (const value of values) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n secrets.push(value);\n }\n\n return secrets;\n}\n\nexport function loadPortState(configDir?: string): PortState {\n if (!configDir) return {};\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n if (!existsSync(statePath)) return {};\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Partial<PortState>;\n return { devPorts: raw.devPorts };\n } catch {\n return {};\n }\n}\n\nexport function savePortState(configDir: string, state: PortState): void {\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n mkdirSync(dirname(statePath), { recursive: true });\n writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\nexport function normalizeDatabaseSlug(secret: string): string {\n return secret.replace(/_DATABASE_URL$/, \"\").toLowerCase();\n}\n\nfunction normalizeRedisSlug(secret: string): string {\n return secret.replace(/_REDIS_URL$/, \"\").toLowerCase();\n}\n\nfunction databaseUrl(port: number, databaseName: string): string {\n return `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${databaseName}`;\n}\n\nexport function buildConventionalDatabases(secrets: string[]): DatabaseConvention[] {\n const databaseSecrets = uniqueSecrets(\n secrets.filter((secret) => secret.endsWith(\"_DATABASE_URL\")),\n );\n\n return databaseSecrets\n .sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b)))\n .map((secret) => {\n const slug = normalizeDatabaseSlug(secret);\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? DEV_AUTH_DB_PORT : DEV_API_DB_PORT;\n const databaseName = isAuth ? \"auth_db\" : \"api_db\";\n return { secret, slug, port, databaseName, url: databaseUrl(port, databaseName) };\n });\n}\n\nexport function buildConventionalRedis(secrets: string[]): RedisConvention[] {\n const redisSecrets = uniqueSecrets(secrets.filter((secret) => secret.endsWith(\"_REDIS_URL\")));\n\n return redisSecrets\n .sort((a, b) => normalizeRedisSlug(a).localeCompare(normalizeRedisSlug(b)))\n .map((secret) => ({\n secret,\n slug: normalizeRedisSlug(secret),\n port: REDIS_PORT,\n url: `redis://localhost:${REDIS_PORT}`,\n }));\n}\n\nfunction testDatabaseUrl(secret: string): string {\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? TEST_AUTH_DB_PORT : TEST_API_DB_PORT;\n const databaseName = isAuth ? \"auth_test_db\" : \"api_test_db\";\n return databaseUrl(port, databaseName);\n}\n\nexport function getSecretGroups(runtimeConfig: RuntimeConfig): SecretGroup[] {\n const groups: SecretGroup[] = [];\n const seen = new Set<string>();\n\n const addGroup = (section: string, secrets: string[]) => {\n const filtered = secrets.filter((s) => {\n if (VESTIGIAL_SECRETS.has(s)) return false;\n if (seen.has(s)) return false;\n seen.add(s);\n return true;\n });\n if (filtered.length > 0) {\n groups.push({ section, secrets: filtered });\n }\n };\n\n addGroup(\"app.host\", uniqueSecrets([...(runtimeConfig.host.secrets ?? []), HOST_SECRET]));\n\n addGroup(\"app.api\", uniqueSecrets(runtimeConfig.api.secrets ?? []));\n\n if (runtimeConfig.auth) {\n addGroup(\"app.auth\", uniqueSecrets(runtimeConfig.auth.secrets ?? []));\n }\n\n if (runtimeConfig.plugins) {\n for (const [pluginKey, plugin] of Object.entries(runtimeConfig.plugins)) {\n if (plugin.secrets && plugin.secrets.length > 0) {\n addGroup(`plugins.${pluginKey}`, plugin.secrets);\n }\n }\n }\n\n return groups;\n}\n\nfunction extractPortFromUrl(url: string): string | null {\n const match = url.match(/:(\\d{4,5})(?:\\/|$)/);\n return match?.[1] ?? null;\n}\n\nfunction resolveDevHostPort(runtimeConfig: RuntimeConfig): number {\n if (typeof runtimeConfig.host?.port === \"number\") return runtimeConfig.host.port;\n const fromUrl = runtimeConfig.host?.url ? extractPortFromUrl(runtimeConfig.host.url) : null;\n if (fromUrl) {\n const parsed = Number.parseInt(fromUrl, 10);\n if (!Number.isNaN(parsed)) return parsed;\n }\n return 3000;\n}\n\nexport { resolveDevHostPort };\n\nfunction defaultSecretValue(\n secret: string,\n databases: Map<string, DatabaseConvention>,\n redisConfigs: Map<string, RedisConvention>,\n options: { forExample: boolean; devHostPort?: number },\n): string {\n if (secret === \"BETTER_AUTH_SECRET\") {\n return options.forExample ? \"\" : randomBytes(32).toString(\"base64url\");\n }\n\n if (secret === \"CORS_ORIGIN\") {\n if (options.forExample) {\n return \"http://localhost:3000\";\n }\n if (typeof options.devHostPort === \"number\") {\n return `http://localhost:${options.devHostPort}`;\n }\n return \"http://localhost:3000\";\n }\n\n return databases.get(secret)?.url ?? redisConfigs.get(secret)?.url ?? \"\";\n}\n\n/**\n * Plugin `*_DATABASE_URL` secrets share the API database (the platform's\n * two-database contract), so they are not rendered as template entries —\n * the host falls back to `API_DATABASE_URL` when they are unset.\n */\nfunction isFallbackDatabaseSecret(secret: string): boolean {\n return (\n secret.endsWith(\"_DATABASE_URL\") &&\n secret !== \"API_DATABASE_URL\" &&\n secret !== \"AUTH_DATABASE_URL\"\n );\n}\n\nfunction renderEnvFile(\n groups: SecretGroup[],\n options: { forExample: boolean; devHostPort?: number },\n): string {\n const rendered = groups\n .map((group) => ({\n section: group.section,\n secrets: group.secrets.filter((secret) => !isFallbackDatabaseSecret(secret)),\n }))\n .filter((group) => group.secrets.length > 0);\n const allSecrets = rendered.flatMap((group) => group.secrets);\n const databaseMap = new Map(\n buildConventionalDatabases(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const redisMap = new Map(\n buildConventionalRedis(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const lines: string[] = [\n \"# Generated from configured bos secrets\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n\n for (const group of rendered) {\n lines.push(`# ${group.section}`);\n for (const secret of group.secrets) {\n lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, redisMap, options)}`);\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvFile };\n\nfunction renderEnvTestFile(groups: SecretGroup[]): string {\n const lines: string[] = [\n \"# Generated test environment — loaded by test suites instead of .env\",\n \"# Test databases run on dedicated *-test docker-compose services\",\n \"\",\n ];\n\n for (const group of groups) {\n const entries: string[] = [];\n for (const secret of group.secrets) {\n if (secret === \"BETTER_AUTH_SECRET\") {\n entries.push(`${secret}=${TEST_AUTH_SECRET}`);\n } else if (secret.endsWith(\"_DATABASE_URL\")) {\n entries.push(`${secret}=${testDatabaseUrl(secret)}`);\n }\n }\n if (entries.length > 0) {\n lines.push(`# ${group.section}`);\n lines.push(...entries);\n lines.push(\"\");\n }\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvTestFile };\n\nexport interface CiServiceSpec {\n key: string;\n slug: string;\n image: string;\n env: Record<string, string>;\n ports: string[];\n healthcheck?: { test: string[]; interval: string; timeout: string; retries: number };\n volumes: string[];\n database?: { user: string; password: string; name: string };\n}\n\nexport interface CiInfraPlan {\n account: string;\n gateway: string;\n project: string;\n env: Record<string, string>;\n services: CiServiceSpec[];\n generatedAt: string;\n}\n\nexport function buildCiInfraPlan(\n runtimeConfig: RuntimeConfig,\n options: { hostPortOverride?: number } = {},\n): CiInfraPlan {\n const allSecrets = collectAllSecrets(runtimeConfig);\n const databases = buildConventionalDatabases(allSecrets);\n const redisConfigs = buildConventionalRedis(allSecrets);\n\n const hostPort =\n options.hostPortOverride ??\n (Number.isFinite(Number(process.env.BOS_CI_HOST_PORT))\n ? Number(process.env.BOS_CI_HOST_PORT)\n : undefined) ??\n resolveDevHostPort(runtimeConfig);\n const env: Record<string, string> = {};\n for (const db of databases) env[db.secret] = db.url;\n for (const r of redisConfigs) env[r.secret] = r.url;\n\n env.CORS_ORIGIN = `http://127.0.0.1:${hostPort}`;\n if (!env.BETTER_AUTH_SECRET) env.BETTER_AUTH_SECRET = \"\";\n\n const services: CiServiceSpec[] = [];\n const seenPorts = new Set<number>();\n\n for (const db of databases) {\n if (seenPorts.has(db.port)) continue;\n seenPorts.add(db.port);\n services.push({\n key: db.slug,\n slug: db.slug,\n image: CI_POSTGRES_IMAGE,\n env: {\n POSTGRES_USER: POSTGRES_USER,\n POSTGRES_PASSWORD: POSTGRES_PASSWORD,\n POSTGRES_DB: db.databaseName,\n },\n ports: [`${db.port}:5432`],\n healthcheck: {\n test: [\"CMD-SHELL\", `pg_isready -U ${POSTGRES_USER} -d ${db.databaseName}`],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${db.slug}_data:/var/lib/postgresql/data`],\n database: { user: POSTGRES_USER, password: POSTGRES_PASSWORD, name: db.databaseName },\n });\n }\n\n for (const r of redisConfigs) {\n services.push({\n key: r.slug,\n slug: r.slug,\n image: CI_REDIS_IMAGE,\n env: {},\n ports: [`${r.port}:6379`],\n healthcheck: {\n test: [\"CMD\", \"redis-cli\", \"ping\"],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${r.slug}_data:/data`],\n });\n }\n\n return {\n account: runtimeConfig.account,\n gateway: runtimeConfig.domain ?? runtimeConfig.account,\n project: runtimeConfig.account,\n env,\n services,\n generatedAt: new Date().toISOString(),\n };\n}\n\nfunction collectAllSecrets(runtimeConfig: RuntimeConfig): string[] {\n const all: string[] = [];\n all.push(...(runtimeConfig.host.secrets ?? []));\n all.push(...(runtimeConfig.api.secrets ?? []));\n if (runtimeConfig.auth) all.push(...(runtimeConfig.auth.secrets ?? []));\n if (runtimeConfig.plugins) {\n for (const plugin of Object.values(runtimeConfig.plugins)) {\n if (plugin.secrets) all.push(...plugin.secrets);\n }\n }\n return all.filter((s) => !VESTIGIAL_SECRETS.has(s));\n}\n"],"mappings":"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"}
|
package/dist/cli.cjs
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
3
3
|
const require_fastkv = require('./fastkv.cjs');
|
|
4
4
|
const require_network = require('./network.cjs');
|
|
5
|
+
const require_resolver = require('./resolver.cjs');
|
|
5
6
|
const require_config = require('./config.cjs');
|
|
6
7
|
const require_theme = require('./utils/theme.cjs');
|
|
7
8
|
const require_docker = require('./infra/docker.cjs');
|
|
8
|
-
const require_bundle_fs_resolve = require('./bundle-fs-resolve.cjs');
|
|
9
9
|
const require_timing = require('./cli/timing.cjs');
|
|
10
10
|
const require_near_cli = require('./near-cli.cjs');
|
|
11
11
|
const require_progress = require('./progress.cjs');
|
|
@@ -145,7 +145,7 @@ async function main() {
|
|
|
145
145
|
const bootInput = parsedInput;
|
|
146
146
|
const cwdConfigPath = require_config.findConfigPath();
|
|
147
147
|
const configPath = bootInput.configPath ?? cwdConfigPath;
|
|
148
|
-
|
|
148
|
+
require_resolver.installBundleFetchFromEnv({ configPath });
|
|
149
149
|
const projectDir = cwdConfigPath ? (0, node_path.dirname)(cwdConfigPath) : void 0;
|
|
150
150
|
const edResolved = projectDir ? require_framework_version.resolveFrameworkPackage(projectDir, "everything-dev") : void 0;
|
|
151
151
|
const displayVersion = edResolved?.installedVersion ? `${edResolved.installedVersion}${edResolved.isLinked ? " (linked)" : ""}` : void 0;
|