everything-dev 1.51.5 → 1.51.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/dist/_virtual/_rolldown/runtime.cjs +0 -2
- package/dist/api-contract.cjs +0 -1
- package/dist/api-contract.cjs.map +1 -1
- package/dist/api-contract.mjs.map +1 -1
- package/dist/app.cjs +1 -1
- package/dist/app.cjs.map +1 -1
- package/dist/app.mjs +1 -1
- package/dist/app.mjs.map +1 -1
- package/dist/build.cjs +16 -22
- package/dist/build.cjs.map +1 -1
- package/dist/build.mjs +16 -22
- package/dist/build.mjs.map +1 -1
- package/dist/cli/catalog.cjs.map +1 -1
- package/dist/cli/catalog.mjs.map +1 -1
- package/dist/cli/db-doctor.cjs +1 -2
- package/dist/cli/db-doctor.cjs.map +1 -1
- package/dist/cli/db-doctor.mjs +1 -1
- package/dist/cli/db-doctor.mjs.map +1 -1
- package/dist/cli/db-repair.cjs +1 -2
- package/dist/cli/db-repair.cjs.map +1 -1
- package/dist/cli/db-repair.mjs +1 -1
- package/dist/cli/db-repair.mjs.map +1 -1
- package/dist/cli/db-studio.cjs.map +1 -1
- package/dist/cli/db-studio.mjs.map +1 -1
- package/dist/cli/framework-version.cjs +2 -1
- package/dist/cli/framework-version.cjs.map +1 -1
- package/dist/cli/framework-version.mjs +1 -1
- package/dist/cli/framework-version.mjs.map +1 -1
- package/dist/cli/help.cjs.map +1 -1
- package/dist/cli/help.mjs.map +1 -1
- package/dist/cli/infra.cjs +4 -0
- package/dist/cli/infra.cjs.map +1 -1
- package/dist/cli/infra.mjs +1 -1
- package/dist/cli/infra.mjs.map +1 -1
- package/dist/cli/init.cjs +3 -4
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +0 -1
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +0 -1
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +3 -3
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/parse.cjs.map +1 -1
- package/dist/cli/parse.mjs.map +1 -1
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/snapshot.cjs +0 -1
- package/dist/cli/snapshot.cjs.map +1 -1
- package/dist/cli/snapshot.mjs.map +1 -1
- package/dist/cli/status.cjs +0 -1
- package/dist/cli/status.cjs.map +1 -1
- package/dist/cli/status.mjs.map +1 -1
- package/dist/cli/sync.cjs +2 -2
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +2 -2
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli/timing.cjs.map +1 -1
- package/dist/cli/timing.mjs.map +1 -1
- package/dist/cli/upgrade.cjs +3 -0
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs +1 -1
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/code-artifacts.cjs +0 -1
- package/dist/code-artifacts.cjs.map +1 -1
- package/dist/code-artifacts.mjs.map +1 -1
- package/dist/components/dev-view.cjs +0 -1
- package/dist/components/dev-view.cjs.map +1 -1
- package/dist/components/dev-view.mjs.map +1 -1
- package/dist/components/streaming-view.cjs.map +1 -1
- package/dist/components/streaming-view.mjs.map +1 -1
- package/dist/config.cjs +0 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +0 -1
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +0 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +42 -44
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +42 -44
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts.map +1 -1
- package/dist/contract.meta.d.mts.map +1 -1
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs.map +1 -1
- package/dist/db.cjs +0 -1
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts.map +1 -1
- package/dist/db.d.mts.map +1 -1
- package/dist/db.mjs.map +1 -1
- package/dist/dev-logs.cjs +2 -1
- package/dist/dev-logs.cjs.map +1 -1
- package/dist/dev-logs.mjs +1 -1
- package/dist/dev-logs.mjs.map +1 -1
- package/dist/dev-session.cjs +3 -1
- package/dist/dev-session.cjs.map +1 -1
- package/dist/dev-session.mjs +3 -2
- package/dist/dev-session.mjs.map +1 -1
- package/dist/fastkv.cjs +2 -7
- package/dist/fastkv.cjs.map +1 -1
- package/dist/fastkv.d.cts.map +1 -1
- package/dist/fastkv.d.mts.map +1 -1
- package/dist/fastkv.mjs +2 -7
- package/dist/fastkv.mjs.map +1 -1
- package/dist/http-client.cjs +4 -1
- package/dist/http-client.cjs.map +1 -1
- package/dist/http-client.mjs +1 -1
- package/dist/http-client.mjs.map +1 -1
- package/dist/index.cjs +0 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/infra/planner.cjs +5 -1
- package/dist/infra/planner.cjs.map +1 -1
- package/dist/infra/planner.mjs +1 -1
- package/dist/infra/planner.mjs.map +1 -1
- package/dist/infra/preflight.cjs +1 -2
- package/dist/infra/preflight.cjs.map +1 -1
- package/dist/infra/preflight.mjs +1 -1
- package/dist/infra/preflight.mjs.map +1 -1
- package/dist/infra/types.cjs +0 -1
- package/dist/infra/types.cjs.map +1 -1
- package/dist/infra/types.mjs.map +1 -1
- package/dist/integrity.cjs +0 -1
- package/dist/integrity.cjs.map +1 -1
- package/dist/integrity.d.cts.map +1 -1
- package/dist/integrity.d.mts.map +1 -1
- package/dist/integrity.mjs.map +1 -1
- package/dist/internal/manifest-normalizer.cjs +2 -2
- package/dist/internal/manifest-normalizer.cjs.map +1 -1
- package/dist/internal/manifest-normalizer.mjs +2 -2
- package/dist/internal/manifest-normalizer.mjs.map +1 -1
- package/dist/merge.cjs +1 -2
- package/dist/merge.cjs.map +1 -1
- package/dist/merge.d.cts.map +1 -1
- package/dist/merge.d.mts.map +1 -1
- package/dist/merge.mjs +1 -1
- package/dist/merge.mjs.map +1 -1
- package/dist/mf.cjs +0 -1
- package/dist/mf.cjs.map +1 -1
- package/dist/mf.d.cts +0 -1
- package/dist/mf.d.cts.map +1 -1
- package/dist/mf.d.mts +0 -1
- package/dist/mf.d.mts.map +1 -1
- package/dist/mf.mjs.map +1 -1
- package/dist/near-cli.cjs +3 -1
- package/dist/near-cli.cjs.map +1 -1
- package/dist/near-cli.mjs +1 -1
- package/dist/near-cli.mjs.map +1 -1
- package/dist/network.cjs.map +1 -1
- package/dist/network.mjs.map +1 -1
- package/dist/orchestrator.cjs +5 -4
- package/dist/orchestrator.cjs.map +1 -1
- package/dist/orchestrator.mjs +5 -3
- package/dist/orchestrator.mjs.map +1 -1
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +43 -46
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +43 -46
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/process-registry.cjs +4 -1
- package/dist/process-registry.cjs.map +1 -1
- package/dist/process-registry.mjs +1 -1
- package/dist/process-registry.mjs.map +1 -1
- package/dist/publish.cjs +1 -0
- package/dist/publish.cjs.map +1 -1
- package/dist/publish.mjs +1 -1
- package/dist/publish.mjs.map +1 -1
- package/dist/sdk.cjs +0 -1
- package/dist/service-descriptor.cjs +0 -1
- package/dist/service-descriptor.cjs.map +1 -1
- package/dist/service-descriptor.d.cts +1 -0
- package/dist/service-descriptor.d.cts.map +1 -1
- package/dist/service-descriptor.d.mts +0 -1
- package/dist/service-descriptor.d.mts.map +1 -1
- package/dist/service-descriptor.mjs.map +1 -1
- package/dist/shared-deps.cjs +0 -1
- package/dist/shared-deps.cjs.map +1 -1
- package/dist/shared-deps.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +0 -1
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +0 -1
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/ui/head.cjs.map +1 -1
- package/dist/ui/head.d.cts +0 -1
- package/dist/ui/head.d.cts.map +1 -1
- package/dist/ui/head.d.mts +0 -1
- package/dist/ui/head.d.mts.map +1 -1
- package/dist/ui/head.mjs.map +1 -1
- package/dist/ui/index.d.cts +1 -1
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/metadata.cjs +1 -1
- package/dist/ui/metadata.cjs.map +1 -1
- package/dist/ui/metadata.d.cts.map +1 -1
- package/dist/ui/metadata.d.mts.map +1 -1
- package/dist/ui/metadata.mjs +1 -1
- package/dist/ui/metadata.mjs.map +1 -1
- package/dist/ui/router.cjs.map +1 -1
- package/dist/ui/router.d.cts +0 -1
- package/dist/ui/router.d.cts.map +1 -1
- package/dist/ui/router.d.mts +0 -1
- package/dist/ui/router.d.mts.map +1 -1
- package/dist/ui/router.mjs.map +1 -1
- package/dist/ui/runtime.cjs.map +1 -1
- package/dist/ui/runtime.d.cts +0 -1
- package/dist/ui/runtime.d.cts.map +1 -1
- package/dist/ui/runtime.d.mts +0 -1
- package/dist/ui/runtime.d.mts.map +1 -1
- package/dist/ui/runtime.mjs.map +1 -1
- package/dist/ui/types.d.cts +0 -1
- package/dist/ui/types.d.cts.map +1 -1
- package/dist/ui/types.d.mts +0 -1
- package/dist/ui/types.d.mts.map +1 -1
- package/dist/utils/banner.cjs +0 -1
- package/dist/utils/banner.cjs.map +1 -1
- package/dist/utils/banner.mjs.map +1 -1
- package/dist/utils/linkify.cjs.map +1 -1
- package/dist/utils/linkify.mjs.map +1 -1
- package/dist/utils/run.cjs +0 -1
- package/dist/utils/run.cjs.map +1 -1
- package/dist/utils/run.mjs.map +1 -1
- package/dist/utils/save-config.cjs +0 -1
- package/dist/utils/save-config.cjs.map +1 -1
- package/dist/utils/save-config.mjs.map +1 -1
- package/dist/utils/string.cjs.map +1 -1
- package/dist/utils/string.mjs.map +1 -1
- package/dist/utils/theme.cjs.map +1 -1
- package/dist/utils/theme.mjs.map +1 -1
- package/package.json +4 -3
- package/dist/_virtual/_rolldown/runtime.mjs +0 -32
package/dist/infra/preflight.cjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
2
1
|
let effect = require("effect");
|
|
3
2
|
let node_net = require("node:net");
|
|
4
3
|
|
|
@@ -45,7 +44,7 @@ function checkPgConnection(url) {
|
|
|
45
44
|
return effect.Effect.gen(function* () {
|
|
46
45
|
if (!(yield* checkTcpReachable(new URL(url).hostname, Number(new URL(url).port) || 5432, 4e3))) return false;
|
|
47
46
|
try {
|
|
48
|
-
const { Pool } = yield* effect.Effect.promise(() =>
|
|
47
|
+
const { Pool } = yield* effect.Effect.promise(() => import("pg"));
|
|
49
48
|
const pool = new Pool({
|
|
50
49
|
connectionString: url,
|
|
51
50
|
connectionTimeoutMillis: 3e3
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preflight.cjs","names":["Effect"],"sources":["../../src/infra/preflight.ts"],"sourcesContent":["import { createConnection } from \"node:net\";\nimport { Effect } from \"effect\";\n\nexport interface PreflightTarget {\n secret: string;\n host: string;\n port: number;\n kind: \"postgres\" | \"redis\";\n url: string;\n}\n\nexport interface PreflightFailure {\n secret: string;\n host: string;\n port: number;\n error: string;\n}\n\nfunction parseLocalUrl(url: string): { host: string; port: number } | null {\n try {\n const match = url.match(/:\\/\\/([^:/]+):(\\d+)/);\n if (!match) return null;\n const host = match[1];\n const port = Number.parseInt(match[2], 10);\n if (Number.isNaN(port)) return null;\n if (host !== \"localhost\" && host !== \"127.0.0.1\") return null;\n return { host, port };\n } catch {\n return null;\n }\n}\n\nfunction checkTcpReachable(host: string, port: number, timeoutMs = 2000): Effect.Effect<boolean> {\n return Effect.async<boolean>((resume) => {\n const socket = createConnection({ host, port });\n const timer = setTimeout(() => {\n socket.destroy();\n resume(Effect.succeed(false));\n }, timeoutMs);\n\n socket.once(\"connect\", () => {\n clearTimeout(timer);\n socket.destroy();\n resume(Effect.succeed(true));\n });\n\n socket.once(\"error\", () => {\n clearTimeout(timer);\n socket.destroy();\n resume(Effect.succeed(false));\n });\n });\n}\n\nfunction checkPgConnection(url: string): Effect.Effect<boolean> {\n return Effect.gen(function* () {\n const reachable = yield* checkTcpReachable(\n new URL(url).hostname,\n Number(new URL(url).port) || 5432,\n 4000,\n );\n if (!reachable) return false;\n\n try {\n const { Pool } = yield* Effect.promise(() => import(\"pg\"));\n const pool = new Pool({ connectionString: url, connectionTimeoutMillis: 3000 });\n const client = yield* Effect.promise(() => pool.connect().then((c) => ({ client: c, pool })));\n try {\n yield* Effect.promise(() => client.client.query(\"SELECT 1\"));\n return true;\n } finally {\n client.client.release();\n yield* Effect.promise(() => client.pool.end().catch(() => {}));\n }\n } catch {\n return false;\n }\n });\n}\n\nfunction preflightTargetsFromEnv(env: Record<string, string>): PreflightTarget[] {\n const targets: PreflightTarget[] = [];\n for (const [secret, value] of Object.entries(env)) {\n if (!value) continue;\n if (secret.endsWith(\"_DATABASE_URL\")) {\n const parsed = parseLocalUrl(value);\n if (parsed) {\n targets.push({\n secret,\n host: parsed.host,\n port: parsed.port,\n kind: \"postgres\",\n url: value,\n });\n }\n } else if (secret.endsWith(\"_REDIS_URL\")) {\n const parsed = parseLocalUrl(value);\n if (parsed) {\n targets.push({ secret, host: parsed.host, port: parsed.port, kind: \"redis\", url: value });\n }\n }\n }\n return targets;\n}\n\nexport function preflightLocalInfra(\n env: Record<string, string>,\n overrides?: Record<string, string>,\n): Effect.Effect<PreflightFailure[], never> {\n const merged = overrides ? { ...env, ...overrides } : env;\n const targets = preflightTargetsFromEnv(merged);\n if (targets.length === 0) return Effect.succeed([]);\n\n return Effect.forEach(\n targets,\n (target) =>\n Effect.gen(function* () {\n if (target.kind === \"postgres\") {\n const ok = yield* checkPgConnection(target.url);\n if (ok) return null;\n const tcpOk = yield* checkTcpReachable(target.host, target.port);\n const pluginContext = target.secret.endsWith(\"_DATABASE_URL\")\n ? ` The plugin for ${target.secret} runs inside the local host process, so this DB must be reachable. Run \\`docker compose up -d --wait\\` to start local Postgres/Redis.`\n : \"\";\n if (tcpOk) {\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} at ${target.host}:${target.port} is reachable but Postgres connection failed. Check credentials and database name.${pluginContext}`,\n } satisfies PreflightFailure;\n }\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} points to ${target.host}:${target.port} but nothing is listening.${pluginContext}`,\n } satisfies PreflightFailure;\n }\n\n const reachable = yield* checkTcpReachable(target.host, target.port);\n if (reachable) return null;\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} points to ${target.host}:${target.port} but nothing is listening`,\n } satisfies PreflightFailure;\n }),\n { concurrency: \"unbounded\" },\n ).pipe(Effect.map((results) => results.filter((r): r is PreflightFailure => r !== null)));\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"preflight.cjs","names":["Effect"],"sources":["../../src/infra/preflight.ts"],"sourcesContent":["import { createConnection } from \"node:net\";\nimport { Effect } from \"effect\";\n\nexport interface PreflightTarget {\n secret: string;\n host: string;\n port: number;\n kind: \"postgres\" | \"redis\";\n url: string;\n}\n\nexport interface PreflightFailure {\n secret: string;\n host: string;\n port: number;\n error: string;\n}\n\nfunction parseLocalUrl(url: string): { host: string; port: number } | null {\n try {\n const match = url.match(/:\\/\\/([^:/]+):(\\d+)/);\n if (!match) return null;\n const host = match[1];\n const port = Number.parseInt(match[2], 10);\n if (Number.isNaN(port)) return null;\n if (host !== \"localhost\" && host !== \"127.0.0.1\") return null;\n return { host, port };\n } catch {\n return null;\n }\n}\n\nfunction checkTcpReachable(host: string, port: number, timeoutMs = 2000): Effect.Effect<boolean> {\n return Effect.async<boolean>((resume) => {\n const socket = createConnection({ host, port });\n const timer = setTimeout(() => {\n socket.destroy();\n resume(Effect.succeed(false));\n }, timeoutMs);\n\n socket.once(\"connect\", () => {\n clearTimeout(timer);\n socket.destroy();\n resume(Effect.succeed(true));\n });\n\n socket.once(\"error\", () => {\n clearTimeout(timer);\n socket.destroy();\n resume(Effect.succeed(false));\n });\n });\n}\n\nfunction checkPgConnection(url: string): Effect.Effect<boolean> {\n return Effect.gen(function* () {\n const reachable = yield* checkTcpReachable(\n new URL(url).hostname,\n Number(new URL(url).port) || 5432,\n 4000,\n );\n if (!reachable) return false;\n\n try {\n const { Pool } = yield* Effect.promise(() => import(\"pg\"));\n const pool = new Pool({ connectionString: url, connectionTimeoutMillis: 3000 });\n const client = yield* Effect.promise(() => pool.connect().then((c) => ({ client: c, pool })));\n try {\n yield* Effect.promise(() => client.client.query(\"SELECT 1\"));\n return true;\n } finally {\n client.client.release();\n yield* Effect.promise(() => client.pool.end().catch(() => {}));\n }\n } catch {\n return false;\n }\n });\n}\n\nfunction preflightTargetsFromEnv(env: Record<string, string>): PreflightTarget[] {\n const targets: PreflightTarget[] = [];\n for (const [secret, value] of Object.entries(env)) {\n if (!value) continue;\n if (secret.endsWith(\"_DATABASE_URL\")) {\n const parsed = parseLocalUrl(value);\n if (parsed) {\n targets.push({\n secret,\n host: parsed.host,\n port: parsed.port,\n kind: \"postgres\",\n url: value,\n });\n }\n } else if (secret.endsWith(\"_REDIS_URL\")) {\n const parsed = parseLocalUrl(value);\n if (parsed) {\n targets.push({ secret, host: parsed.host, port: parsed.port, kind: \"redis\", url: value });\n }\n }\n }\n return targets;\n}\n\nexport function preflightLocalInfra(\n env: Record<string, string>,\n overrides?: Record<string, string>,\n): Effect.Effect<PreflightFailure[], never> {\n const merged = overrides ? { ...env, ...overrides } : env;\n const targets = preflightTargetsFromEnv(merged);\n if (targets.length === 0) return Effect.succeed([]);\n\n return Effect.forEach(\n targets,\n (target) =>\n Effect.gen(function* () {\n if (target.kind === \"postgres\") {\n const ok = yield* checkPgConnection(target.url);\n if (ok) return null;\n const tcpOk = yield* checkTcpReachable(target.host, target.port);\n const pluginContext = target.secret.endsWith(\"_DATABASE_URL\")\n ? ` The plugin for ${target.secret} runs inside the local host process, so this DB must be reachable. Run \\`docker compose up -d --wait\\` to start local Postgres/Redis.`\n : \"\";\n if (tcpOk) {\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} at ${target.host}:${target.port} is reachable but Postgres connection failed. Check credentials and database name.${pluginContext}`,\n } satisfies PreflightFailure;\n }\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} points to ${target.host}:${target.port} but nothing is listening.${pluginContext}`,\n } satisfies PreflightFailure;\n }\n\n const reachable = yield* checkTcpReachable(target.host, target.port);\n if (reachable) return null;\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} points to ${target.host}:${target.port} but nothing is listening`,\n } satisfies PreflightFailure;\n }),\n { concurrency: \"unbounded\" },\n ).pipe(Effect.map((results) => results.filter((r): r is PreflightFailure => r !== null)));\n}\n"],"mappings":"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"}
|
package/dist/infra/preflight.mjs
CHANGED
|
@@ -44,7 +44,7 @@ function checkPgConnection(url) {
|
|
|
44
44
|
return Effect.gen(function* () {
|
|
45
45
|
if (!(yield* checkTcpReachable(new URL(url).hostname, Number(new URL(url).port) || 5432, 4e3))) return false;
|
|
46
46
|
try {
|
|
47
|
-
const { Pool } = yield* Effect.promise(() => import("
|
|
47
|
+
const { Pool } = yield* Effect.promise(() => import("pg"));
|
|
48
48
|
const pool = new Pool({
|
|
49
49
|
connectionString: url,
|
|
50
50
|
connectionTimeoutMillis: 3e3
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preflight.mjs","names":[],"sources":["../../src/infra/preflight.ts"],"sourcesContent":["import { createConnection } from \"node:net\";\nimport { Effect } from \"effect\";\n\nexport interface PreflightTarget {\n secret: string;\n host: string;\n port: number;\n kind: \"postgres\" | \"redis\";\n url: string;\n}\n\nexport interface PreflightFailure {\n secret: string;\n host: string;\n port: number;\n error: string;\n}\n\nfunction parseLocalUrl(url: string): { host: string; port: number } | null {\n try {\n const match = url.match(/:\\/\\/([^:/]+):(\\d+)/);\n if (!match) return null;\n const host = match[1];\n const port = Number.parseInt(match[2], 10);\n if (Number.isNaN(port)) return null;\n if (host !== \"localhost\" && host !== \"127.0.0.1\") return null;\n return { host, port };\n } catch {\n return null;\n }\n}\n\nfunction checkTcpReachable(host: string, port: number, timeoutMs = 2000): Effect.Effect<boolean> {\n return Effect.async<boolean>((resume) => {\n const socket = createConnection({ host, port });\n const timer = setTimeout(() => {\n socket.destroy();\n resume(Effect.succeed(false));\n }, timeoutMs);\n\n socket.once(\"connect\", () => {\n clearTimeout(timer);\n socket.destroy();\n resume(Effect.succeed(true));\n });\n\n socket.once(\"error\", () => {\n clearTimeout(timer);\n socket.destroy();\n resume(Effect.succeed(false));\n });\n });\n}\n\nfunction checkPgConnection(url: string): Effect.Effect<boolean> {\n return Effect.gen(function* () {\n const reachable = yield* checkTcpReachable(\n new URL(url).hostname,\n Number(new URL(url).port) || 5432,\n 4000,\n );\n if (!reachable) return false;\n\n try {\n const { Pool } = yield* Effect.promise(() => import(\"pg\"));\n const pool = new Pool({ connectionString: url, connectionTimeoutMillis: 3000 });\n const client = yield* Effect.promise(() => pool.connect().then((c) => ({ client: c, pool })));\n try {\n yield* Effect.promise(() => client.client.query(\"SELECT 1\"));\n return true;\n } finally {\n client.client.release();\n yield* Effect.promise(() => client.pool.end().catch(() => {}));\n }\n } catch {\n return false;\n }\n });\n}\n\nfunction preflightTargetsFromEnv(env: Record<string, string>): PreflightTarget[] {\n const targets: PreflightTarget[] = [];\n for (const [secret, value] of Object.entries(env)) {\n if (!value) continue;\n if (secret.endsWith(\"_DATABASE_URL\")) {\n const parsed = parseLocalUrl(value);\n if (parsed) {\n targets.push({\n secret,\n host: parsed.host,\n port: parsed.port,\n kind: \"postgres\",\n url: value,\n });\n }\n } else if (secret.endsWith(\"_REDIS_URL\")) {\n const parsed = parseLocalUrl(value);\n if (parsed) {\n targets.push({ secret, host: parsed.host, port: parsed.port, kind: \"redis\", url: value });\n }\n }\n }\n return targets;\n}\n\nexport function preflightLocalInfra(\n env: Record<string, string>,\n overrides?: Record<string, string>,\n): Effect.Effect<PreflightFailure[], never> {\n const merged = overrides ? { ...env, ...overrides } : env;\n const targets = preflightTargetsFromEnv(merged);\n if (targets.length === 0) return Effect.succeed([]);\n\n return Effect.forEach(\n targets,\n (target) =>\n Effect.gen(function* () {\n if (target.kind === \"postgres\") {\n const ok = yield* checkPgConnection(target.url);\n if (ok) return null;\n const tcpOk = yield* checkTcpReachable(target.host, target.port);\n const pluginContext = target.secret.endsWith(\"_DATABASE_URL\")\n ? ` The plugin for ${target.secret} runs inside the local host process, so this DB must be reachable. Run \\`docker compose up -d --wait\\` to start local Postgres/Redis.`\n : \"\";\n if (tcpOk) {\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} at ${target.host}:${target.port} is reachable but Postgres connection failed. Check credentials and database name.${pluginContext}`,\n } satisfies PreflightFailure;\n }\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} points to ${target.host}:${target.port} but nothing is listening.${pluginContext}`,\n } satisfies PreflightFailure;\n }\n\n const reachable = yield* checkTcpReachable(target.host, target.port);\n if (reachable) return null;\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} points to ${target.host}:${target.port} but nothing is listening`,\n } satisfies PreflightFailure;\n }),\n { concurrency: \"unbounded\" },\n ).pipe(Effect.map((results) => results.filter((r): r is PreflightFailure => r !== null)));\n}\n"],"mappings":";;;;AAkBA,SAAS,cAAc,KAAoD;
|
|
1
|
+
{"version":3,"file":"preflight.mjs","names":[],"sources":["../../src/infra/preflight.ts"],"sourcesContent":["import { createConnection } from \"node:net\";\nimport { Effect } from \"effect\";\n\nexport interface PreflightTarget {\n secret: string;\n host: string;\n port: number;\n kind: \"postgres\" | \"redis\";\n url: string;\n}\n\nexport interface PreflightFailure {\n secret: string;\n host: string;\n port: number;\n error: string;\n}\n\nfunction parseLocalUrl(url: string): { host: string; port: number } | null {\n try {\n const match = url.match(/:\\/\\/([^:/]+):(\\d+)/);\n if (!match) return null;\n const host = match[1];\n const port = Number.parseInt(match[2], 10);\n if (Number.isNaN(port)) return null;\n if (host !== \"localhost\" && host !== \"127.0.0.1\") return null;\n return { host, port };\n } catch {\n return null;\n }\n}\n\nfunction checkTcpReachable(host: string, port: number, timeoutMs = 2000): Effect.Effect<boolean> {\n return Effect.async<boolean>((resume) => {\n const socket = createConnection({ host, port });\n const timer = setTimeout(() => {\n socket.destroy();\n resume(Effect.succeed(false));\n }, timeoutMs);\n\n socket.once(\"connect\", () => {\n clearTimeout(timer);\n socket.destroy();\n resume(Effect.succeed(true));\n });\n\n socket.once(\"error\", () => {\n clearTimeout(timer);\n socket.destroy();\n resume(Effect.succeed(false));\n });\n });\n}\n\nfunction checkPgConnection(url: string): Effect.Effect<boolean> {\n return Effect.gen(function* () {\n const reachable = yield* checkTcpReachable(\n new URL(url).hostname,\n Number(new URL(url).port) || 5432,\n 4000,\n );\n if (!reachable) return false;\n\n try {\n const { Pool } = yield* Effect.promise(() => import(\"pg\"));\n const pool = new Pool({ connectionString: url, connectionTimeoutMillis: 3000 });\n const client = yield* Effect.promise(() => pool.connect().then((c) => ({ client: c, pool })));\n try {\n yield* Effect.promise(() => client.client.query(\"SELECT 1\"));\n return true;\n } finally {\n client.client.release();\n yield* Effect.promise(() => client.pool.end().catch(() => {}));\n }\n } catch {\n return false;\n }\n });\n}\n\nfunction preflightTargetsFromEnv(env: Record<string, string>): PreflightTarget[] {\n const targets: PreflightTarget[] = [];\n for (const [secret, value] of Object.entries(env)) {\n if (!value) continue;\n if (secret.endsWith(\"_DATABASE_URL\")) {\n const parsed = parseLocalUrl(value);\n if (parsed) {\n targets.push({\n secret,\n host: parsed.host,\n port: parsed.port,\n kind: \"postgres\",\n url: value,\n });\n }\n } else if (secret.endsWith(\"_REDIS_URL\")) {\n const parsed = parseLocalUrl(value);\n if (parsed) {\n targets.push({ secret, host: parsed.host, port: parsed.port, kind: \"redis\", url: value });\n }\n }\n }\n return targets;\n}\n\nexport function preflightLocalInfra(\n env: Record<string, string>,\n overrides?: Record<string, string>,\n): Effect.Effect<PreflightFailure[], never> {\n const merged = overrides ? { ...env, ...overrides } : env;\n const targets = preflightTargetsFromEnv(merged);\n if (targets.length === 0) return Effect.succeed([]);\n\n return Effect.forEach(\n targets,\n (target) =>\n Effect.gen(function* () {\n if (target.kind === \"postgres\") {\n const ok = yield* checkPgConnection(target.url);\n if (ok) return null;\n const tcpOk = yield* checkTcpReachable(target.host, target.port);\n const pluginContext = target.secret.endsWith(\"_DATABASE_URL\")\n ? ` The plugin for ${target.secret} runs inside the local host process, so this DB must be reachable. Run \\`docker compose up -d --wait\\` to start local Postgres/Redis.`\n : \"\";\n if (tcpOk) {\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} at ${target.host}:${target.port} is reachable but Postgres connection failed. Check credentials and database name.${pluginContext}`,\n } satisfies PreflightFailure;\n }\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} points to ${target.host}:${target.port} but nothing is listening.${pluginContext}`,\n } satisfies PreflightFailure;\n }\n\n const reachable = yield* checkTcpReachable(target.host, target.port);\n if (reachable) return null;\n return {\n secret: target.secret,\n host: target.host,\n port: target.port,\n error: `${target.secret} points to ${target.host}:${target.port} but nothing is listening`,\n } satisfies PreflightFailure;\n }),\n { concurrency: \"unbounded\" },\n ).pipe(Effect.map((results) => results.filter((r): r is PreflightFailure => r !== null)));\n}\n"],"mappings":"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"}
|
package/dist/infra/types.cjs
CHANGED
package/dist/infra/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs","names":["Data"],"sources":["../../src/infra/types.ts"],"sourcesContent":["import { Data } from \"effect\";\nimport type { AppOrchestrator } from \"../service-descriptor\";\nimport type { RuntimeConfig } from \"../types\";\n\nexport interface CliPorts {\n host?: number;\n api?: number;\n auth?: number;\n ui?: number;\n uiSsr?: number;\n pluginsStart?: number;\n plugins?: Record<string, { api?: number; ui?: number }>;\n}\n\nexport interface ResolvedPorts {\n host?: number;\n api?: number;\n auth?: number;\n ui?: number;\n uiSsr?: number;\n plugins: Record<string, { api?: number; ui?: number }>;\n postgres: Record<string, number>;\n redis: Record<string, number>;\n}\n\nexport interface RuntimeLaunchSpec {\n port?: number;\n hostUrl?: string;\n corsOrigin?: string;\n trustedOrigins?: string[];\n env: Record<string, string>;\n runtimeConfig: RuntimeConfig;\n}\n\nexport interface ClaimRecord {\n resourceKey: string;\n pid: number;\n configDir: string;\n ports: Record<string, number>;\n startedAt: number;\n}\n\nexport interface InfraPlan {\n workspaceKey: string;\n cliPorts: CliPorts;\n resolvedPorts: ResolvedPorts;\n runtimeConfig: RuntimeConfig;\n launch: RuntimeLaunchSpec;\n description: string;\n serviceDescriptors: Map<string, ServiceDescriptorPlan>;\n envGenerated: Record<string, string>;\n composeModel: ComposeModelPlan;\n claims: ClaimRecord[];\n orchestrator: AppOrchestrator;\n}\n\nexport interface ServiceDescriptorPlan {\n key: string;\n source: \"local\" | \"remote\";\n url: string;\n port?: number;\n localPath?: string;\n}\n\nexport interface ComposeModelPlan {\n databases: DatabasePlan[];\n redis: RedisPlan[];\n}\n\nexport interface DatabasePlan {\n secret: string;\n slug: string;\n port: number;\n dbName: string;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport interface RedisPlan {\n secret: string;\n slug: string;\n port: number;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport type InfraPhase =\n | \"resolve-config\"\n | \"allocate-services\"\n | \"allocate-databases\"\n | \"claim\"\n | \"materialize-env\"\n | \"materialize-compose\"\n | \"launch\";\n\nexport class InfraError extends Data.TaggedError(\"InfraError\")<{\n phase: InfraPhase;\n message: string;\n cause?: unknown;\n}> {}\n\nexport interface InfraInput {\n configDir: string;\n bosConfig: RuntimeConfig;\n cli: {\n port?: number;\n apiPort?: number;\n authPort?: number;\n uiPort?: number;\n pluginPortStart?: number;\n plugins?: Record<string, { api?: number; ui?: number }>;\n ssr?: boolean;\n proxy?: boolean;\n hostSource?: \"local\" | \"remote\";\n uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n interactive?: boolean;\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.cjs","names":["Data"],"sources":["../../src/infra/types.ts"],"sourcesContent":["import { Data } from \"effect\";\nimport type { AppOrchestrator } from \"../service-descriptor\";\nimport type { RuntimeConfig } from \"../types\";\n\nexport interface CliPorts {\n host?: number;\n api?: number;\n auth?: number;\n ui?: number;\n uiSsr?: number;\n pluginsStart?: number;\n plugins?: Record<string, { api?: number; ui?: number }>;\n}\n\nexport interface ResolvedPorts {\n host?: number;\n api?: number;\n auth?: number;\n ui?: number;\n uiSsr?: number;\n plugins: Record<string, { api?: number; ui?: number }>;\n postgres: Record<string, number>;\n redis: Record<string, number>;\n}\n\nexport interface RuntimeLaunchSpec {\n port?: number;\n hostUrl?: string;\n corsOrigin?: string;\n trustedOrigins?: string[];\n env: Record<string, string>;\n runtimeConfig: RuntimeConfig;\n}\n\nexport interface ClaimRecord {\n resourceKey: string;\n pid: number;\n configDir: string;\n ports: Record<string, number>;\n startedAt: number;\n}\n\nexport interface InfraPlan {\n workspaceKey: string;\n cliPorts: CliPorts;\n resolvedPorts: ResolvedPorts;\n runtimeConfig: RuntimeConfig;\n launch: RuntimeLaunchSpec;\n description: string;\n serviceDescriptors: Map<string, ServiceDescriptorPlan>;\n envGenerated: Record<string, string>;\n composeModel: ComposeModelPlan;\n claims: ClaimRecord[];\n orchestrator: AppOrchestrator;\n}\n\nexport interface ServiceDescriptorPlan {\n key: string;\n source: \"local\" | \"remote\";\n url: string;\n port?: number;\n localPath?: string;\n}\n\nexport interface ComposeModelPlan {\n databases: DatabasePlan[];\n redis: RedisPlan[];\n}\n\nexport interface DatabasePlan {\n secret: string;\n slug: string;\n port: number;\n dbName: string;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport interface RedisPlan {\n secret: string;\n slug: string;\n port: number;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport type InfraPhase =\n | \"resolve-config\"\n | \"allocate-services\"\n | \"allocate-databases\"\n | \"claim\"\n | \"materialize-env\"\n | \"materialize-compose\"\n | \"launch\";\n\nexport class InfraError extends Data.TaggedError(\"InfraError\")<{\n phase: InfraPhase;\n message: string;\n cause?: unknown;\n}> {}\n\nexport interface InfraInput {\n configDir: string;\n bosConfig: RuntimeConfig;\n cli: {\n port?: number;\n apiPort?: number;\n authPort?: number;\n uiPort?: number;\n pluginPortStart?: number;\n plugins?: Record<string, { api?: number; ui?: number }>;\n ssr?: boolean;\n proxy?: boolean;\n hostSource?: \"local\" | \"remote\";\n uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n interactive?: boolean;\n };\n}\n"],"mappings":";;;AAiGA,IAAa,aAAb,cAAgCA,YAAK,YAAY,YAAY,CAAC,CAI3D,CAAC"}
|
package/dist/infra/types.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","names":[],"sources":["../../src/infra/types.ts"],"sourcesContent":["import { Data } from \"effect\";\nimport type { AppOrchestrator } from \"../service-descriptor\";\nimport type { RuntimeConfig } from \"../types\";\n\nexport interface CliPorts {\n host?: number;\n api?: number;\n auth?: number;\n ui?: number;\n uiSsr?: number;\n pluginsStart?: number;\n plugins?: Record<string, { api?: number; ui?: number }>;\n}\n\nexport interface ResolvedPorts {\n host?: number;\n api?: number;\n auth?: number;\n ui?: number;\n uiSsr?: number;\n plugins: Record<string, { api?: number; ui?: number }>;\n postgres: Record<string, number>;\n redis: Record<string, number>;\n}\n\nexport interface RuntimeLaunchSpec {\n port?: number;\n hostUrl?: string;\n corsOrigin?: string;\n trustedOrigins?: string[];\n env: Record<string, string>;\n runtimeConfig: RuntimeConfig;\n}\n\nexport interface ClaimRecord {\n resourceKey: string;\n pid: number;\n configDir: string;\n ports: Record<string, number>;\n startedAt: number;\n}\n\nexport interface InfraPlan {\n workspaceKey: string;\n cliPorts: CliPorts;\n resolvedPorts: ResolvedPorts;\n runtimeConfig: RuntimeConfig;\n launch: RuntimeLaunchSpec;\n description: string;\n serviceDescriptors: Map<string, ServiceDescriptorPlan>;\n envGenerated: Record<string, string>;\n composeModel: ComposeModelPlan;\n claims: ClaimRecord[];\n orchestrator: AppOrchestrator;\n}\n\nexport interface ServiceDescriptorPlan {\n key: string;\n source: \"local\" | \"remote\";\n url: string;\n port?: number;\n localPath?: string;\n}\n\nexport interface ComposeModelPlan {\n databases: DatabasePlan[];\n redis: RedisPlan[];\n}\n\nexport interface DatabasePlan {\n secret: string;\n slug: string;\n port: number;\n dbName: string;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport interface RedisPlan {\n secret: string;\n slug: string;\n port: number;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport type InfraPhase =\n | \"resolve-config\"\n | \"allocate-services\"\n | \"allocate-databases\"\n | \"claim\"\n | \"materialize-env\"\n | \"materialize-compose\"\n | \"launch\";\n\nexport class InfraError extends Data.TaggedError(\"InfraError\")<{\n phase: InfraPhase;\n message: string;\n cause?: unknown;\n}> {}\n\nexport interface InfraInput {\n configDir: string;\n bosConfig: RuntimeConfig;\n cli: {\n port?: number;\n apiPort?: number;\n authPort?: number;\n uiPort?: number;\n pluginPortStart?: number;\n plugins?: Record<string, { api?: number; ui?: number }>;\n ssr?: boolean;\n proxy?: boolean;\n hostSource?: \"local\" | \"remote\";\n uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n interactive?: boolean;\n };\n}\n"],"mappings":";;;AAiGA,IAAa,aAAb,cAAgC,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"types.mjs","names":[],"sources":["../../src/infra/types.ts"],"sourcesContent":["import { Data } from \"effect\";\nimport type { AppOrchestrator } from \"../service-descriptor\";\nimport type { RuntimeConfig } from \"../types\";\n\nexport interface CliPorts {\n host?: number;\n api?: number;\n auth?: number;\n ui?: number;\n uiSsr?: number;\n pluginsStart?: number;\n plugins?: Record<string, { api?: number; ui?: number }>;\n}\n\nexport interface ResolvedPorts {\n host?: number;\n api?: number;\n auth?: number;\n ui?: number;\n uiSsr?: number;\n plugins: Record<string, { api?: number; ui?: number }>;\n postgres: Record<string, number>;\n redis: Record<string, number>;\n}\n\nexport interface RuntimeLaunchSpec {\n port?: number;\n hostUrl?: string;\n corsOrigin?: string;\n trustedOrigins?: string[];\n env: Record<string, string>;\n runtimeConfig: RuntimeConfig;\n}\n\nexport interface ClaimRecord {\n resourceKey: string;\n pid: number;\n configDir: string;\n ports: Record<string, number>;\n startedAt: number;\n}\n\nexport interface InfraPlan {\n workspaceKey: string;\n cliPorts: CliPorts;\n resolvedPorts: ResolvedPorts;\n runtimeConfig: RuntimeConfig;\n launch: RuntimeLaunchSpec;\n description: string;\n serviceDescriptors: Map<string, ServiceDescriptorPlan>;\n envGenerated: Record<string, string>;\n composeModel: ComposeModelPlan;\n claims: ClaimRecord[];\n orchestrator: AppOrchestrator;\n}\n\nexport interface ServiceDescriptorPlan {\n key: string;\n source: \"local\" | \"remote\";\n url: string;\n port?: number;\n localPath?: string;\n}\n\nexport interface ComposeModelPlan {\n databases: DatabasePlan[];\n redis: RedisPlan[];\n}\n\nexport interface DatabasePlan {\n secret: string;\n slug: string;\n port: number;\n dbName: string;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport interface RedisPlan {\n secret: string;\n slug: string;\n port: number;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport type InfraPhase =\n | \"resolve-config\"\n | \"allocate-services\"\n | \"allocate-databases\"\n | \"claim\"\n | \"materialize-env\"\n | \"materialize-compose\"\n | \"launch\";\n\nexport class InfraError extends Data.TaggedError(\"InfraError\")<{\n phase: InfraPhase;\n message: string;\n cause?: unknown;\n}> {}\n\nexport interface InfraInput {\n configDir: string;\n bosConfig: RuntimeConfig;\n cli: {\n port?: number;\n apiPort?: number;\n authPort?: number;\n uiPort?: number;\n pluginPortStart?: number;\n plugins?: Record<string, { api?: number; ui?: number }>;\n ssr?: boolean;\n proxy?: boolean;\n hostSource?: \"local\" | \"remote\";\n uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n interactive?: boolean;\n };\n}\n"],"mappings":";;;AAiGA,IAAa,aAAb,cAAgC,KAAK,YAAY,YAAY,CAAC,CAI3D,CAAC"}
|
package/dist/integrity.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
3
2
|
const require_http_client = require('./http-client.cjs');
|
|
4
3
|
const require_fastkv = require('./fastkv.cjs');
|
|
5
4
|
let node_fs = require("node:fs");
|
package/dist/integrity.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.cjs","names":["fetchResponse","fetchBosConfigFromFastKv"],"sources":["../src/integrity.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fetchBosConfigFromFastKv } from \"./fastkv\";\nimport { fetchResponse } from \"./http-client\";\n\nconst DEFAULT_MAX_SRI_RESPONSE_BYTES = 20 * 1024 * 1024;\n\ninterface SriUrlOptions {\n resolveEntryUrl?: boolean;\n maxBytes?: number;\n}\n\nexport function computeSriHash(content: string | Buffer): string {\n return `sha384-${createHash(\"sha384\").update(content).digest(\"base64\")}`;\n}\n\nfunction resolveSriTargetUrl(url: string, options?: SriUrlOptions): string {\n return options?.resolveEntryUrl === false ? url : resolveEntryUrl(url);\n}\n\nfunction getMaxSriResponseBytes(options?: SriUrlOptions): number {\n return options?.maxBytes ?? DEFAULT_MAX_SRI_RESPONSE_BYTES;\n}\n\nasync function computeSriHashFromResponse(\n response: Response,\n url: string,\n options?: SriUrlOptions,\n): Promise<string> {\n const maxBytes = getMaxSriResponseBytes(options);\n const contentLengthHeader = response.headers.get(\"content-length\");\n\n if (contentLengthHeader) {\n const contentLength = Number(contentLengthHeader);\n if (Number.isFinite(contentLength) && contentLength > maxBytes) {\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${contentLength})`,\n );\n }\n }\n\n if (!response.body) {\n throw new Error(`[SRI] Missing response body for ${url}`);\n }\n\n const hash = createHash(\"sha384\");\n const reader = response.body.getReader();\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${totalBytes})`,\n );\n }\n\n hash.update(value);\n }\n\n return `sha384-${hash.digest(\"base64\")}`;\n}\n\nexport async function computeSriHashForUrl(\n url: string,\n options?: SriUrlOptions,\n): Promise<string | null> {\n try {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);\n return null;\n }\n return await computeSriHashFromResponse(response, entryUrl, options);\n } catch (error) {\n console.warn(\n `[SRI] Error computing integrity for ${url}:`,\n error instanceof Error ? error.message : error,\n );\n return null;\n }\n}\n\nexport function resolveEntryUrl(url: string): string {\n if (url.endsWith(\"/remoteEntry.js\")) return url;\n if (url.endsWith(\"/mf-manifest.json\"))\n return `${url.replace(/\\/mf-manifest\\.json$/, \"\")}/remoteEntry.js`;\n return `${url.replace(/\\/$/, \"\")}/remoteEntry.js`;\n}\n\nexport async function verifySriForUrl(\n url: string,\n expectedIntegrity: string,\n options?: SriUrlOptions,\n): Promise<void> {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);\n return;\n }\n\n const computed = await computeSriHashFromResponse(response, entryUrl, options);\n\n if (computed !== expectedIntegrity) {\n throw new Error(\n `[SRI] Integrity check failed for ${entryUrl}\\n Expected: ${expectedIntegrity}\\n Computed: ${computed}`,\n );\n }\n}\n\nexport class IntegrityRegistry {\n private hashes = new Map<string, string>();\n\n register(url: string, integrity: string): void {\n this.hashes.set(url, integrity);\n }\n\n registerEntry(baseUrl: string, integrity: string): void {\n this.hashes.set(resolveEntryUrl(baseUrl), integrity);\n }\n\n get(url: string): string | undefined {\n return this.hashes.get(url);\n }\n\n has(url: string): boolean {\n return this.hashes.has(url);\n }\n\n entries(): IterableIterator<[string, string]> {\n return this.hashes.entries();\n }\n}\n\nfunction extractIntegrityHashes(config: Record<string, unknown>): Map<string, string> {\n const hashes = new Map<string, string>();\n const app = config.app as Record<string, Record<string, unknown>> | undefined;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n\n if (app) {\n for (const [, entry] of Object.entries(app)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n if (plugins) {\n for (const [, entry] of Object.entries(plugins)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n return hashes;\n}\n\nexport async function verifyConfigAgainstChain(\n localConfig: Record<string, unknown>,\n bosUrl: string,\n): Promise<{ verified: boolean; mismatches: string[] }> {\n const mismatches: string[] = [];\n\n let chainConfig: Record<string, unknown>;\n try {\n chainConfig = await fetchBosConfigFromFastKv<Record<string, unknown>>(bosUrl);\n } catch (error) {\n console.warn(\n `[Attestation] Failed to fetch on-chain config: ${error instanceof Error ? error.message : String(error)}`,\n );\n return { verified: false, mismatches: [\"chain-fetch-failed\"] };\n }\n\n const localHashes = extractIntegrityHashes(localConfig);\n const chainHashes = extractIntegrityHashes(chainConfig);\n\n for (const [url, chainHash] of chainHashes) {\n const localHash = localHashes.get(url);\n if (localHash && localHash !== chainHash) {\n mismatches.push(url);\n console.error(\n `[Attestation] Integrity mismatch for ${url}\\n Local: ${localHash}\\n Chain: ${chainHash}`,\n );\n }\n }\n\n if (mismatches.length === 0 && localHashes.size > 0) {\n console.log(\n `[Attestation] Local config verified against on-chain anchor (${localHashes.size} entries checked)`,\n );\n }\n\n return { verified: mismatches.length === 0, mismatches };\n}\n\nexport interface DeployResultEntry {\n url: string;\n integrity?: string;\n urlField: string;\n integrityField?: string;\n}\n\nfunction setNestedPath(obj: Record<string, unknown>, dottedPath: string, value: unknown): void {\n const keys = dottedPath.split(\".\");\n let current = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i]!;\n if (current[key] === undefined || current[key] === null || typeof current[key] !== \"object\") {\n current[key] = {};\n }\n current = current[key] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]!] = value;\n}\n\nfunction deleteNestedPath(obj: Record<string, unknown>, dottedPath: string): void {\n const keys = dottedPath.split(\".\");\n let current = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i]!;\n if (current[key] === undefined || typeof current[key] !== \"object\") return;\n current = current[key] as Record<string, unknown>;\n }\n delete current[keys[keys.length - 1]!];\n}\n\nexport function reportDeployResult(opts: {\n url: string;\n integrity?: string | null;\n bosConfigPath: string;\n urlField: string;\n integrityField?: string;\n}): void {\n console.log(\n `[BOS_DEPLOY] url=${opts.url} urlField=${opts.urlField} integrityField=${opts.integrityField ?? \"\"} integrity=${opts.integrity ?? \"\"}`,\n );\n\n try {\n const config = JSON.parse(readFileSync(opts.bosConfigPath, \"utf8\")) as Record<string, unknown>;\n setNestedPath(config, opts.urlField, opts.url);\n if (opts.integrityField) {\n if (opts.integrity) {\n setNestedPath(config, opts.integrityField, opts.integrity);\n } else {\n deleteNestedPath(config, opts.integrityField);\n }\n }\n writeFileSync(opts.bosConfigPath, `${JSON.stringify(config, null, 2)}\\n`);\n console.log(` ✅ Updated bos.config.json: ${opts.urlField}`);\n if (opts.integrityField && opts.integrity) {\n console.log(` ✅ Updated bos.config.json: ${opts.integrityField}`);\n }\n } catch (err) {\n console.error(\" ❌ Failed to update bos.config.json:\", (err as Error).message);\n }\n}\n\nexport function parseDeployLines(output: string): DeployResultEntry[] {\n const results: DeployResultEntry[] = [];\n for (const line of output.split(\"\\n\")) {\n if (!line.includes(\"[BOS_DEPLOY]\")) continue;\n const urlMatch = line.match(/url=(\\S+)/);\n const urlFieldMatch = line.match(/urlField=(\\S+)/);\n if (!urlMatch || !urlFieldMatch) continue;\n const integrityFieldMatch = line.match(/integrityField=(\\S+)/);\n const integrityMatch = line.match(/integrity=(\\S+)/);\n results.push({\n url: urlMatch[1],\n urlField: urlFieldMatch[1],\n integrityField: integrityFieldMatch?.[1] || undefined,\n integrity: integrityMatch?.[1] || undefined,\n });\n }\n return results;\n}\n\nexport function applyDeployResults(\n config: Record<string, unknown>,\n results: DeployResultEntry[],\n): Record<string, unknown> {\n const merged = structuredClone(config);\n for (const result of results) {\n setNestedPath(merged, result.urlField, result.url);\n if (result.integrityField) {\n if (result.integrity) {\n setNestedPath(merged, result.integrityField, result.integrity);\n } else {\n deleteNestedPath(merged, result.integrityField);\n }\n }\n }\n return merged;\n}\n\nexport function findPluginKey(bosConfigPath: string, pluginDir: string): string | null {\n const config = JSON.parse(readFileSync(bosConfigPath, \"utf8\")) as Record<string, unknown>;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n if (!plugins) return null;\n const configRoot = join(bosConfigPath, \"..\");\n const normalizedPluginDir = pluginDir.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n for (const [key, plugin] of Object.entries(plugins)) {\n const dev = plugin?.development;\n if (typeof dev !== \"string\" || !dev.startsWith(\"local:\")) continue;\n const resolved = join(configRoot, dev.slice(\"local:\".length))\n .replace(/\\\\/g, \"/\")\n .replace(/\\/+$/, \"\");\n if (resolved === normalizedPluginDir) return key;\n }\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integrity.cjs","names":["fetchResponse","fetchBosConfigFromFastKv"],"sources":["../src/integrity.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fetchBosConfigFromFastKv } from \"./fastkv\";\nimport { fetchResponse } from \"./http-client\";\n\nconst DEFAULT_MAX_SRI_RESPONSE_BYTES = 20 * 1024 * 1024;\n\ninterface SriUrlOptions {\n resolveEntryUrl?: boolean;\n maxBytes?: number;\n}\n\nexport function computeSriHash(content: string | Buffer): string {\n return `sha384-${createHash(\"sha384\").update(content).digest(\"base64\")}`;\n}\n\nfunction resolveSriTargetUrl(url: string, options?: SriUrlOptions): string {\n return options?.resolveEntryUrl === false ? url : resolveEntryUrl(url);\n}\n\nfunction getMaxSriResponseBytes(options?: SriUrlOptions): number {\n return options?.maxBytes ?? DEFAULT_MAX_SRI_RESPONSE_BYTES;\n}\n\nasync function computeSriHashFromResponse(\n response: Response,\n url: string,\n options?: SriUrlOptions,\n): Promise<string> {\n const maxBytes = getMaxSriResponseBytes(options);\n const contentLengthHeader = response.headers.get(\"content-length\");\n\n if (contentLengthHeader) {\n const contentLength = Number(contentLengthHeader);\n if (Number.isFinite(contentLength) && contentLength > maxBytes) {\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${contentLength})`,\n );\n }\n }\n\n if (!response.body) {\n throw new Error(`[SRI] Missing response body for ${url}`);\n }\n\n const hash = createHash(\"sha384\");\n const reader = response.body.getReader();\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${totalBytes})`,\n );\n }\n\n hash.update(value);\n }\n\n return `sha384-${hash.digest(\"base64\")}`;\n}\n\nexport async function computeSriHashForUrl(\n url: string,\n options?: SriUrlOptions,\n): Promise<string | null> {\n try {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);\n return null;\n }\n return await computeSriHashFromResponse(response, entryUrl, options);\n } catch (error) {\n console.warn(\n `[SRI] Error computing integrity for ${url}:`,\n error instanceof Error ? error.message : error,\n );\n return null;\n }\n}\n\nexport function resolveEntryUrl(url: string): string {\n if (url.endsWith(\"/remoteEntry.js\")) return url;\n if (url.endsWith(\"/mf-manifest.json\"))\n return `${url.replace(/\\/mf-manifest\\.json$/, \"\")}/remoteEntry.js`;\n return `${url.replace(/\\/$/, \"\")}/remoteEntry.js`;\n}\n\nexport async function verifySriForUrl(\n url: string,\n expectedIntegrity: string,\n options?: SriUrlOptions,\n): Promise<void> {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);\n return;\n }\n\n const computed = await computeSriHashFromResponse(response, entryUrl, options);\n\n if (computed !== expectedIntegrity) {\n throw new Error(\n `[SRI] Integrity check failed for ${entryUrl}\\n Expected: ${expectedIntegrity}\\n Computed: ${computed}`,\n );\n }\n}\n\nexport class IntegrityRegistry {\n private hashes = new Map<string, string>();\n\n register(url: string, integrity: string): void {\n this.hashes.set(url, integrity);\n }\n\n registerEntry(baseUrl: string, integrity: string): void {\n this.hashes.set(resolveEntryUrl(baseUrl), integrity);\n }\n\n get(url: string): string | undefined {\n return this.hashes.get(url);\n }\n\n has(url: string): boolean {\n return this.hashes.has(url);\n }\n\n entries(): IterableIterator<[string, string]> {\n return this.hashes.entries();\n }\n}\n\nfunction extractIntegrityHashes(config: Record<string, unknown>): Map<string, string> {\n const hashes = new Map<string, string>();\n const app = config.app as Record<string, Record<string, unknown>> | undefined;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n\n if (app) {\n for (const [, entry] of Object.entries(app)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n if (plugins) {\n for (const [, entry] of Object.entries(plugins)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n return hashes;\n}\n\nexport async function verifyConfigAgainstChain(\n localConfig: Record<string, unknown>,\n bosUrl: string,\n): Promise<{ verified: boolean; mismatches: string[] }> {\n const mismatches: string[] = [];\n\n let chainConfig: Record<string, unknown>;\n try {\n chainConfig = await fetchBosConfigFromFastKv<Record<string, unknown>>(bosUrl);\n } catch (error) {\n console.warn(\n `[Attestation] Failed to fetch on-chain config: ${error instanceof Error ? error.message : String(error)}`,\n );\n return { verified: false, mismatches: [\"chain-fetch-failed\"] };\n }\n\n const localHashes = extractIntegrityHashes(localConfig);\n const chainHashes = extractIntegrityHashes(chainConfig);\n\n for (const [url, chainHash] of chainHashes) {\n const localHash = localHashes.get(url);\n if (localHash && localHash !== chainHash) {\n mismatches.push(url);\n console.error(\n `[Attestation] Integrity mismatch for ${url}\\n Local: ${localHash}\\n Chain: ${chainHash}`,\n );\n }\n }\n\n if (mismatches.length === 0 && localHashes.size > 0) {\n console.log(\n `[Attestation] Local config verified against on-chain anchor (${localHashes.size} entries checked)`,\n );\n }\n\n return { verified: mismatches.length === 0, mismatches };\n}\n\nexport interface DeployResultEntry {\n url: string;\n integrity?: string;\n urlField: string;\n integrityField?: string;\n}\n\nfunction setNestedPath(obj: Record<string, unknown>, dottedPath: string, value: unknown): void {\n const keys = dottedPath.split(\".\");\n let current = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i]!;\n if (current[key] === undefined || current[key] === null || typeof current[key] !== \"object\") {\n current[key] = {};\n }\n current = current[key] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]!] = value;\n}\n\nfunction deleteNestedPath(obj: Record<string, unknown>, dottedPath: string): void {\n const keys = dottedPath.split(\".\");\n let current = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i]!;\n if (current[key] === undefined || typeof current[key] !== \"object\") return;\n current = current[key] as Record<string, unknown>;\n }\n delete current[keys[keys.length - 1]!];\n}\n\nexport function reportDeployResult(opts: {\n url: string;\n integrity?: string | null;\n bosConfigPath: string;\n urlField: string;\n integrityField?: string;\n}): void {\n console.log(\n `[BOS_DEPLOY] url=${opts.url} urlField=${opts.urlField} integrityField=${opts.integrityField ?? \"\"} integrity=${opts.integrity ?? \"\"}`,\n );\n\n try {\n const config = JSON.parse(readFileSync(opts.bosConfigPath, \"utf8\")) as Record<string, unknown>;\n setNestedPath(config, opts.urlField, opts.url);\n if (opts.integrityField) {\n if (opts.integrity) {\n setNestedPath(config, opts.integrityField, opts.integrity);\n } else {\n deleteNestedPath(config, opts.integrityField);\n }\n }\n writeFileSync(opts.bosConfigPath, `${JSON.stringify(config, null, 2)}\\n`);\n console.log(` ✅ Updated bos.config.json: ${opts.urlField}`);\n if (opts.integrityField && opts.integrity) {\n console.log(` ✅ Updated bos.config.json: ${opts.integrityField}`);\n }\n } catch (err) {\n console.error(\" ❌ Failed to update bos.config.json:\", (err as Error).message);\n }\n}\n\nexport function parseDeployLines(output: string): DeployResultEntry[] {\n const results: DeployResultEntry[] = [];\n for (const line of output.split(\"\\n\")) {\n if (!line.includes(\"[BOS_DEPLOY]\")) continue;\n const urlMatch = line.match(/url=(\\S+)/);\n const urlFieldMatch = line.match(/urlField=(\\S+)/);\n if (!urlMatch || !urlFieldMatch) continue;\n const integrityFieldMatch = line.match(/integrityField=(\\S+)/);\n const integrityMatch = line.match(/integrity=(\\S+)/);\n results.push({\n url: urlMatch[1],\n urlField: urlFieldMatch[1],\n integrityField: integrityFieldMatch?.[1] || undefined,\n integrity: integrityMatch?.[1] || undefined,\n });\n }\n return results;\n}\n\nexport function applyDeployResults(\n config: Record<string, unknown>,\n results: DeployResultEntry[],\n): Record<string, unknown> {\n const merged = structuredClone(config);\n for (const result of results) {\n setNestedPath(merged, result.urlField, result.url);\n if (result.integrityField) {\n if (result.integrity) {\n setNestedPath(merged, result.integrityField, result.integrity);\n } else {\n deleteNestedPath(merged, result.integrityField);\n }\n }\n }\n return merged;\n}\n\nexport function findPluginKey(bosConfigPath: string, pluginDir: string): string | null {\n const config = JSON.parse(readFileSync(bosConfigPath, \"utf8\")) as Record<string, unknown>;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n if (!plugins) return null;\n const configRoot = join(bosConfigPath, \"..\");\n const normalizedPluginDir = pluginDir.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n for (const [key, plugin] of Object.entries(plugins)) {\n const dev = plugin?.development;\n if (typeof dev !== \"string\" || !dev.startsWith(\"local:\")) continue;\n const resolved = join(configRoot, dev.slice(\"local:\".length))\n .replace(/\\\\/g, \"/\")\n .replace(/\\/+$/, \"\");\n if (resolved === normalizedPluginDir) return key;\n }\n return null;\n}\n"],"mappings":"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"}
|
package/dist/integrity.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.d.cts","names":[],"sources":["../src/integrity.ts"],"mappings":";UAQU
|
|
1
|
+
{"version":3,"file":"integrity.d.cts","names":[],"sources":["../src/integrity.ts"],"mappings":";UAQU;EACR;EACA;;iBAGc,eAAe,kBAAkB;iBAyD3B,qBACpB,aACA,UAAU,gBACT;iBAmBa,gBAAgB;iBAOV,gBACpB,aACA,2BACA,UAAU,gBACT;cAkBU;UACH;EAER,SAAS,aAAa;EAItB,cAAc,iBAAiB;EAI/B,IAAI;EAIJ,IAAI;EAIJ,WAAW;;iBA6BS,yBACpB,aAAa,yBACb,iBACC;EAAU;EAAmB;;UAmCf;EACf;EACA;EACA;EACA;;iBA2Bc,mBAAmB;EACjC;EACA;EACA;EACA;EACA;;iBA0Bc,iBAAiB,iBAAiB;iBAmBlC,mBACd,QAAQ,yBACR,SAAS,sBACR;iBAea,cAAc,uBAAuB"}
|
package/dist/integrity.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.d.mts","names":[],"sources":["../src/integrity.ts"],"mappings":";UAQU
|
|
1
|
+
{"version":3,"file":"integrity.d.mts","names":[],"sources":["../src/integrity.ts"],"mappings":";UAQU;EACR;EACA;;iBAGc,eAAe,kBAAkB;iBAyD3B,qBACpB,aACA,UAAU,gBACT;iBAmBa,gBAAgB;iBAOV,gBACpB,aACA,2BACA,UAAU,gBACT;cAkBU;UACH;EAER,SAAS,aAAa;EAItB,cAAc,iBAAiB;EAI/B,IAAI;EAIJ,IAAI;EAIJ,WAAW;;iBA6BS,yBACpB,aAAa,yBACb,iBACC;EAAU;EAAmB;;UAmCf;EACf;EACA;EACA;EACA;;iBA2Bc,mBAAmB;EACjC;EACA;EACA;EACA;EACA;;iBA0Bc,iBAAiB,iBAAiB;iBAmBlC,mBACd,QAAQ,yBACR,SAAS,sBACR;iBAea,cAAc,uBAAuB"}
|
package/dist/integrity.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.mjs","names":[],"sources":["../src/integrity.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fetchBosConfigFromFastKv } from \"./fastkv\";\nimport { fetchResponse } from \"./http-client\";\n\nconst DEFAULT_MAX_SRI_RESPONSE_BYTES = 20 * 1024 * 1024;\n\ninterface SriUrlOptions {\n resolveEntryUrl?: boolean;\n maxBytes?: number;\n}\n\nexport function computeSriHash(content: string | Buffer): string {\n return `sha384-${createHash(\"sha384\").update(content).digest(\"base64\")}`;\n}\n\nfunction resolveSriTargetUrl(url: string, options?: SriUrlOptions): string {\n return options?.resolveEntryUrl === false ? url : resolveEntryUrl(url);\n}\n\nfunction getMaxSriResponseBytes(options?: SriUrlOptions): number {\n return options?.maxBytes ?? DEFAULT_MAX_SRI_RESPONSE_BYTES;\n}\n\nasync function computeSriHashFromResponse(\n response: Response,\n url: string,\n options?: SriUrlOptions,\n): Promise<string> {\n const maxBytes = getMaxSriResponseBytes(options);\n const contentLengthHeader = response.headers.get(\"content-length\");\n\n if (contentLengthHeader) {\n const contentLength = Number(contentLengthHeader);\n if (Number.isFinite(contentLength) && contentLength > maxBytes) {\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${contentLength})`,\n );\n }\n }\n\n if (!response.body) {\n throw new Error(`[SRI] Missing response body for ${url}`);\n }\n\n const hash = createHash(\"sha384\");\n const reader = response.body.getReader();\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${totalBytes})`,\n );\n }\n\n hash.update(value);\n }\n\n return `sha384-${hash.digest(\"base64\")}`;\n}\n\nexport async function computeSriHashForUrl(\n url: string,\n options?: SriUrlOptions,\n): Promise<string | null> {\n try {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);\n return null;\n }\n return await computeSriHashFromResponse(response, entryUrl, options);\n } catch (error) {\n console.warn(\n `[SRI] Error computing integrity for ${url}:`,\n error instanceof Error ? error.message : error,\n );\n return null;\n }\n}\n\nexport function resolveEntryUrl(url: string): string {\n if (url.endsWith(\"/remoteEntry.js\")) return url;\n if (url.endsWith(\"/mf-manifest.json\"))\n return `${url.replace(/\\/mf-manifest\\.json$/, \"\")}/remoteEntry.js`;\n return `${url.replace(/\\/$/, \"\")}/remoteEntry.js`;\n}\n\nexport async function verifySriForUrl(\n url: string,\n expectedIntegrity: string,\n options?: SriUrlOptions,\n): Promise<void> {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);\n return;\n }\n\n const computed = await computeSriHashFromResponse(response, entryUrl, options);\n\n if (computed !== expectedIntegrity) {\n throw new Error(\n `[SRI] Integrity check failed for ${entryUrl}\\n Expected: ${expectedIntegrity}\\n Computed: ${computed}`,\n );\n }\n}\n\nexport class IntegrityRegistry {\n private hashes = new Map<string, string>();\n\n register(url: string, integrity: string): void {\n this.hashes.set(url, integrity);\n }\n\n registerEntry(baseUrl: string, integrity: string): void {\n this.hashes.set(resolveEntryUrl(baseUrl), integrity);\n }\n\n get(url: string): string | undefined {\n return this.hashes.get(url);\n }\n\n has(url: string): boolean {\n return this.hashes.has(url);\n }\n\n entries(): IterableIterator<[string, string]> {\n return this.hashes.entries();\n }\n}\n\nfunction extractIntegrityHashes(config: Record<string, unknown>): Map<string, string> {\n const hashes = new Map<string, string>();\n const app = config.app as Record<string, Record<string, unknown>> | undefined;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n\n if (app) {\n for (const [, entry] of Object.entries(app)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n if (plugins) {\n for (const [, entry] of Object.entries(plugins)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n return hashes;\n}\n\nexport async function verifyConfigAgainstChain(\n localConfig: Record<string, unknown>,\n bosUrl: string,\n): Promise<{ verified: boolean; mismatches: string[] }> {\n const mismatches: string[] = [];\n\n let chainConfig: Record<string, unknown>;\n try {\n chainConfig = await fetchBosConfigFromFastKv<Record<string, unknown>>(bosUrl);\n } catch (error) {\n console.warn(\n `[Attestation] Failed to fetch on-chain config: ${error instanceof Error ? error.message : String(error)}`,\n );\n return { verified: false, mismatches: [\"chain-fetch-failed\"] };\n }\n\n const localHashes = extractIntegrityHashes(localConfig);\n const chainHashes = extractIntegrityHashes(chainConfig);\n\n for (const [url, chainHash] of chainHashes) {\n const localHash = localHashes.get(url);\n if (localHash && localHash !== chainHash) {\n mismatches.push(url);\n console.error(\n `[Attestation] Integrity mismatch for ${url}\\n Local: ${localHash}\\n Chain: ${chainHash}`,\n );\n }\n }\n\n if (mismatches.length === 0 && localHashes.size > 0) {\n console.log(\n `[Attestation] Local config verified against on-chain anchor (${localHashes.size} entries checked)`,\n );\n }\n\n return { verified: mismatches.length === 0, mismatches };\n}\n\nexport interface DeployResultEntry {\n url: string;\n integrity?: string;\n urlField: string;\n integrityField?: string;\n}\n\nfunction setNestedPath(obj: Record<string, unknown>, dottedPath: string, value: unknown): void {\n const keys = dottedPath.split(\".\");\n let current = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i]!;\n if (current[key] === undefined || current[key] === null || typeof current[key] !== \"object\") {\n current[key] = {};\n }\n current = current[key] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]!] = value;\n}\n\nfunction deleteNestedPath(obj: Record<string, unknown>, dottedPath: string): void {\n const keys = dottedPath.split(\".\");\n let current = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i]!;\n if (current[key] === undefined || typeof current[key] !== \"object\") return;\n current = current[key] as Record<string, unknown>;\n }\n delete current[keys[keys.length - 1]!];\n}\n\nexport function reportDeployResult(opts: {\n url: string;\n integrity?: string | null;\n bosConfigPath: string;\n urlField: string;\n integrityField?: string;\n}): void {\n console.log(\n `[BOS_DEPLOY] url=${opts.url} urlField=${opts.urlField} integrityField=${opts.integrityField ?? \"\"} integrity=${opts.integrity ?? \"\"}`,\n );\n\n try {\n const config = JSON.parse(readFileSync(opts.bosConfigPath, \"utf8\")) as Record<string, unknown>;\n setNestedPath(config, opts.urlField, opts.url);\n if (opts.integrityField) {\n if (opts.integrity) {\n setNestedPath(config, opts.integrityField, opts.integrity);\n } else {\n deleteNestedPath(config, opts.integrityField);\n }\n }\n writeFileSync(opts.bosConfigPath, `${JSON.stringify(config, null, 2)}\\n`);\n console.log(` ✅ Updated bos.config.json: ${opts.urlField}`);\n if (opts.integrityField && opts.integrity) {\n console.log(` ✅ Updated bos.config.json: ${opts.integrityField}`);\n }\n } catch (err) {\n console.error(\" ❌ Failed to update bos.config.json:\", (err as Error).message);\n }\n}\n\nexport function parseDeployLines(output: string): DeployResultEntry[] {\n const results: DeployResultEntry[] = [];\n for (const line of output.split(\"\\n\")) {\n if (!line.includes(\"[BOS_DEPLOY]\")) continue;\n const urlMatch = line.match(/url=(\\S+)/);\n const urlFieldMatch = line.match(/urlField=(\\S+)/);\n if (!urlMatch || !urlFieldMatch) continue;\n const integrityFieldMatch = line.match(/integrityField=(\\S+)/);\n const integrityMatch = line.match(/integrity=(\\S+)/);\n results.push({\n url: urlMatch[1],\n urlField: urlFieldMatch[1],\n integrityField: integrityFieldMatch?.[1] || undefined,\n integrity: integrityMatch?.[1] || undefined,\n });\n }\n return results;\n}\n\nexport function applyDeployResults(\n config: Record<string, unknown>,\n results: DeployResultEntry[],\n): Record<string, unknown> {\n const merged = structuredClone(config);\n for (const result of results) {\n setNestedPath(merged, result.urlField, result.url);\n if (result.integrityField) {\n if (result.integrity) {\n setNestedPath(merged, result.integrityField, result.integrity);\n } else {\n deleteNestedPath(merged, result.integrityField);\n }\n }\n }\n return merged;\n}\n\nexport function findPluginKey(bosConfigPath: string, pluginDir: string): string | null {\n const config = JSON.parse(readFileSync(bosConfigPath, \"utf8\")) as Record<string, unknown>;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n if (!plugins) return null;\n const configRoot = join(bosConfigPath, \"..\");\n const normalizedPluginDir = pluginDir.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n for (const [key, plugin] of Object.entries(plugins)) {\n const dev = plugin?.development;\n if (typeof dev !== \"string\" || !dev.startsWith(\"local:\")) continue;\n const resolved = join(configRoot, dev.slice(\"local:\".length))\n .replace(/\\\\/g, \"/\")\n .replace(/\\/+$/, \"\");\n if (resolved === normalizedPluginDir) return key;\n }\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integrity.mjs","names":[],"sources":["../src/integrity.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fetchBosConfigFromFastKv } from \"./fastkv\";\nimport { fetchResponse } from \"./http-client\";\n\nconst DEFAULT_MAX_SRI_RESPONSE_BYTES = 20 * 1024 * 1024;\n\ninterface SriUrlOptions {\n resolveEntryUrl?: boolean;\n maxBytes?: number;\n}\n\nexport function computeSriHash(content: string | Buffer): string {\n return `sha384-${createHash(\"sha384\").update(content).digest(\"base64\")}`;\n}\n\nfunction resolveSriTargetUrl(url: string, options?: SriUrlOptions): string {\n return options?.resolveEntryUrl === false ? url : resolveEntryUrl(url);\n}\n\nfunction getMaxSriResponseBytes(options?: SriUrlOptions): number {\n return options?.maxBytes ?? DEFAULT_MAX_SRI_RESPONSE_BYTES;\n}\n\nasync function computeSriHashFromResponse(\n response: Response,\n url: string,\n options?: SriUrlOptions,\n): Promise<string> {\n const maxBytes = getMaxSriResponseBytes(options);\n const contentLengthHeader = response.headers.get(\"content-length\");\n\n if (contentLengthHeader) {\n const contentLength = Number(contentLengthHeader);\n if (Number.isFinite(contentLength) && contentLength > maxBytes) {\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${contentLength})`,\n );\n }\n }\n\n if (!response.body) {\n throw new Error(`[SRI] Missing response body for ${url}`);\n }\n\n const hash = createHash(\"sha384\");\n const reader = response.body.getReader();\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${totalBytes})`,\n );\n }\n\n hash.update(value);\n }\n\n return `sha384-${hash.digest(\"base64\")}`;\n}\n\nexport async function computeSriHashForUrl(\n url: string,\n options?: SriUrlOptions,\n): Promise<string | null> {\n try {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);\n return null;\n }\n return await computeSriHashFromResponse(response, entryUrl, options);\n } catch (error) {\n console.warn(\n `[SRI] Error computing integrity for ${url}:`,\n error instanceof Error ? error.message : error,\n );\n return null;\n }\n}\n\nexport function resolveEntryUrl(url: string): string {\n if (url.endsWith(\"/remoteEntry.js\")) return url;\n if (url.endsWith(\"/mf-manifest.json\"))\n return `${url.replace(/\\/mf-manifest\\.json$/, \"\")}/remoteEntry.js`;\n return `${url.replace(/\\/$/, \"\")}/remoteEntry.js`;\n}\n\nexport async function verifySriForUrl(\n url: string,\n expectedIntegrity: string,\n options?: SriUrlOptions,\n): Promise<void> {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);\n return;\n }\n\n const computed = await computeSriHashFromResponse(response, entryUrl, options);\n\n if (computed !== expectedIntegrity) {\n throw new Error(\n `[SRI] Integrity check failed for ${entryUrl}\\n Expected: ${expectedIntegrity}\\n Computed: ${computed}`,\n );\n }\n}\n\nexport class IntegrityRegistry {\n private hashes = new Map<string, string>();\n\n register(url: string, integrity: string): void {\n this.hashes.set(url, integrity);\n }\n\n registerEntry(baseUrl: string, integrity: string): void {\n this.hashes.set(resolveEntryUrl(baseUrl), integrity);\n }\n\n get(url: string): string | undefined {\n return this.hashes.get(url);\n }\n\n has(url: string): boolean {\n return this.hashes.has(url);\n }\n\n entries(): IterableIterator<[string, string]> {\n return this.hashes.entries();\n }\n}\n\nfunction extractIntegrityHashes(config: Record<string, unknown>): Map<string, string> {\n const hashes = new Map<string, string>();\n const app = config.app as Record<string, Record<string, unknown>> | undefined;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n\n if (app) {\n for (const [, entry] of Object.entries(app)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n if (plugins) {\n for (const [, entry] of Object.entries(plugins)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n return hashes;\n}\n\nexport async function verifyConfigAgainstChain(\n localConfig: Record<string, unknown>,\n bosUrl: string,\n): Promise<{ verified: boolean; mismatches: string[] }> {\n const mismatches: string[] = [];\n\n let chainConfig: Record<string, unknown>;\n try {\n chainConfig = await fetchBosConfigFromFastKv<Record<string, unknown>>(bosUrl);\n } catch (error) {\n console.warn(\n `[Attestation] Failed to fetch on-chain config: ${error instanceof Error ? error.message : String(error)}`,\n );\n return { verified: false, mismatches: [\"chain-fetch-failed\"] };\n }\n\n const localHashes = extractIntegrityHashes(localConfig);\n const chainHashes = extractIntegrityHashes(chainConfig);\n\n for (const [url, chainHash] of chainHashes) {\n const localHash = localHashes.get(url);\n if (localHash && localHash !== chainHash) {\n mismatches.push(url);\n console.error(\n `[Attestation] Integrity mismatch for ${url}\\n Local: ${localHash}\\n Chain: ${chainHash}`,\n );\n }\n }\n\n if (mismatches.length === 0 && localHashes.size > 0) {\n console.log(\n `[Attestation] Local config verified against on-chain anchor (${localHashes.size} entries checked)`,\n );\n }\n\n return { verified: mismatches.length === 0, mismatches };\n}\n\nexport interface DeployResultEntry {\n url: string;\n integrity?: string;\n urlField: string;\n integrityField?: string;\n}\n\nfunction setNestedPath(obj: Record<string, unknown>, dottedPath: string, value: unknown): void {\n const keys = dottedPath.split(\".\");\n let current = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i]!;\n if (current[key] === undefined || current[key] === null || typeof current[key] !== \"object\") {\n current[key] = {};\n }\n current = current[key] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]!] = value;\n}\n\nfunction deleteNestedPath(obj: Record<string, unknown>, dottedPath: string): void {\n const keys = dottedPath.split(\".\");\n let current = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i]!;\n if (current[key] === undefined || typeof current[key] !== \"object\") return;\n current = current[key] as Record<string, unknown>;\n }\n delete current[keys[keys.length - 1]!];\n}\n\nexport function reportDeployResult(opts: {\n url: string;\n integrity?: string | null;\n bosConfigPath: string;\n urlField: string;\n integrityField?: string;\n}): void {\n console.log(\n `[BOS_DEPLOY] url=${opts.url} urlField=${opts.urlField} integrityField=${opts.integrityField ?? \"\"} integrity=${opts.integrity ?? \"\"}`,\n );\n\n try {\n const config = JSON.parse(readFileSync(opts.bosConfigPath, \"utf8\")) as Record<string, unknown>;\n setNestedPath(config, opts.urlField, opts.url);\n if (opts.integrityField) {\n if (opts.integrity) {\n setNestedPath(config, opts.integrityField, opts.integrity);\n } else {\n deleteNestedPath(config, opts.integrityField);\n }\n }\n writeFileSync(opts.bosConfigPath, `${JSON.stringify(config, null, 2)}\\n`);\n console.log(` ✅ Updated bos.config.json: ${opts.urlField}`);\n if (opts.integrityField && opts.integrity) {\n console.log(` ✅ Updated bos.config.json: ${opts.integrityField}`);\n }\n } catch (err) {\n console.error(\" ❌ Failed to update bos.config.json:\", (err as Error).message);\n }\n}\n\nexport function parseDeployLines(output: string): DeployResultEntry[] {\n const results: DeployResultEntry[] = [];\n for (const line of output.split(\"\\n\")) {\n if (!line.includes(\"[BOS_DEPLOY]\")) continue;\n const urlMatch = line.match(/url=(\\S+)/);\n const urlFieldMatch = line.match(/urlField=(\\S+)/);\n if (!urlMatch || !urlFieldMatch) continue;\n const integrityFieldMatch = line.match(/integrityField=(\\S+)/);\n const integrityMatch = line.match(/integrity=(\\S+)/);\n results.push({\n url: urlMatch[1],\n urlField: urlFieldMatch[1],\n integrityField: integrityFieldMatch?.[1] || undefined,\n integrity: integrityMatch?.[1] || undefined,\n });\n }\n return results;\n}\n\nexport function applyDeployResults(\n config: Record<string, unknown>,\n results: DeployResultEntry[],\n): Record<string, unknown> {\n const merged = structuredClone(config);\n for (const result of results) {\n setNestedPath(merged, result.urlField, result.url);\n if (result.integrityField) {\n if (result.integrity) {\n setNestedPath(merged, result.integrityField, result.integrity);\n } else {\n deleteNestedPath(merged, result.integrityField);\n }\n }\n }\n return merged;\n}\n\nexport function findPluginKey(bosConfigPath: string, pluginDir: string): string | null {\n const config = JSON.parse(readFileSync(bosConfigPath, \"utf8\")) as Record<string, unknown>;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n if (!plugins) return null;\n const configRoot = join(bosConfigPath, \"..\");\n const normalizedPluginDir = pluginDir.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n for (const [key, plugin] of Object.entries(plugins)) {\n const dev = plugin?.development;\n if (typeof dev !== \"string\" || !dev.startsWith(\"local:\")) continue;\n const resolved = join(configRoot, dev.slice(\"local:\".length))\n .replace(/\\\\/g, \"/\")\n .replace(/\\/+$/, \"\");\n if (resolved === normalizedPluginDir) return key;\n }\n return null;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
2
1
|
let node_fs = require("node:fs");
|
|
3
2
|
let node_path = require("node:path");
|
|
4
3
|
let glob = require("glob");
|
|
@@ -45,7 +44,7 @@ function loadManifestNormalizationSpec(sourceRootDir) {
|
|
|
45
44
|
};
|
|
46
45
|
}
|
|
47
46
|
function normalizeDependencyMap(map, spec, options) {
|
|
48
|
-
const catalogPackages = new Set([...FRAMEWORK_PACKAGES, ...CATALOG_TOOL_PACKAGES]);
|
|
47
|
+
const catalogPackages = /* @__PURE__ */ new Set([...FRAMEWORK_PACKAGES, ...CATALOG_TOOL_PACKAGES]);
|
|
49
48
|
let modified = false;
|
|
50
49
|
for (const [name, version] of Object.entries(map)) {
|
|
51
50
|
if (options.preserveCatalogRefs && catalogPackages.has(name)) {
|
|
@@ -156,5 +155,6 @@ async function normalizePackageManifestsInTree(opts) {
|
|
|
156
155
|
|
|
157
156
|
//#endregion
|
|
158
157
|
exports.loadManifestNormalizationSpec = loadManifestNormalizationSpec;
|
|
158
|
+
exports.normalizePackageManifest = normalizePackageManifest;
|
|
159
159
|
exports.normalizePackageManifestsInTree = normalizePackageManifestsInTree;
|
|
160
160
|
//# sourceMappingURL=manifest-normalizer.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-normalizer.cjs","names":[],"sources":["../../src/internal/manifest-normalizer.ts"],"sourcesContent":["import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport { glob } from \"glob\";\n\nconst FRAMEWORK_PACKAGES = [\"every-plugin\", \"everything-dev\"] as const;\n\nconst CATALOG_TOOL_PACKAGES = [\n \"@rspack/core\",\n \"@rspack/cli\",\n \"@rsbuild/core\",\n \"@rsbuild/plugin-react\",\n \"@module-federation/enhanced\",\n \"@module-federation/node\",\n \"@module-federation/rsbuild-plugin\",\n \"@module-federation/runtime-core\",\n \"@module-federation/sdk\",\n \"@module-federation/dts-plugin\",\n] as const;\n\ntype PackageJson = Record<string, unknown>;\n\ntype NormalizationSpec = {\n rootCatalog: Record<string, string>;\n frameworkVersions: Record<string, string>;\n};\n\ntype NormalizeManifestOptions = {\n resolveCatalogRefs: boolean;\n preserveCatalogRefs?: boolean;\n excludeFrameworkWorkspaces?: boolean;\n removeWorkspaceDeps?: string[];\n removeWorkspaces?: boolean;\n removePublishScripts?: boolean;\n};\n\nexport type NormalizeTreeOptions = NormalizeManifestOptions & {\n sourceRootDir: string;\n targetDir: string;\n};\n\nfunction readJson<T>(filePath: string): T {\n return JSON.parse(readFileSync(filePath, \"utf-8\")) as T;\n}\n\nfunction extractExactVersion(input: string | undefined): string | null {\n if (!input) return null;\n const match = input.match(/\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?/);\n return match ? match[0] : null;\n}\n\nfunction writeJson(filePath: string, value: PackageJson) {\n writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport function loadManifestNormalizationSpec(sourceRootDir: string): NormalizationSpec {\n const rootPackage = readJson<PackageJson>(join(sourceRootDir, \"package.json\"));\n const rootCatalog = {\n ...(((rootPackage.workspaces as { catalog?: Record<string, string> } | undefined)?.catalog ??\n {}) as Record<string, string>),\n };\n const frameworkVersions: Record<string, string> = {};\n\n for (const packageName of FRAMEWORK_PACKAGES) {\n const sourcePackagePath = join(sourceRootDir, \"packages\", packageName, \"package.json\");\n const localPackagePath = join(import.meta.dirname, \"..\", \"..\", packageName, \"package.json\");\n const packageVersion = existsSync(localPackagePath)\n ? readJson<{ version: string }>(localPackagePath).version\n : existsSync(sourcePackagePath)\n ? readJson<{ version: string }>(sourcePackagePath).version\n : extractExactVersion(rootCatalog[packageName]);\n\n if (!packageVersion) {\n throw new Error(`Could not resolve version for ${packageName}`);\n }\n\n frameworkVersions[packageName] = packageVersion;\n rootCatalog[packageName] = `^${packageVersion}`;\n }\n\n return { rootCatalog, frameworkVersions };\n}\n\nfunction normalizeDependencyMap(\n map: Record<string, string>,\n spec: NormalizationSpec,\n options: NormalizeManifestOptions,\n) {\n const catalogPackages = new Set<string>([...FRAMEWORK_PACKAGES, ...CATALOG_TOOL_PACKAGES]);\n\n let modified = false;\n\n for (const [name, version] of Object.entries(map)) {\n if (options.preserveCatalogRefs && catalogPackages.has(name)) {\n if (version !== \"catalog:\") {\n map[name] = \"catalog:\";\n modified = true;\n }\n continue;\n }\n\n if (version === \"workspace:*\") {\n const frameworkVersion = spec.frameworkVersions[name];\n if (frameworkVersion) {\n map[name] = `^${frameworkVersion}`;\n modified = true;\n continue;\n }\n\n if (options.removeWorkspaceDeps?.includes(name)) {\n delete map[name];\n modified = true;\n }\n continue;\n }\n\n if (options.resolveCatalogRefs && version.startsWith(\"catalog:\")) {\n const resolved = spec.rootCatalog[name];\n if (resolved) {\n map[name] = resolved;\n modified = true;\n }\n }\n }\n\n return modified;\n}\n\nexport function normalizePackageManifest(\n pkg: PackageJson,\n spec: NormalizationSpec,\n options: NormalizeManifestOptions,\n) {\n let modified = false;\n\n for (const depField of [\"dependencies\", \"devDependencies\", \"peerDependencies\"]) {\n const deps = pkg[depField];\n if (!deps || typeof deps !== \"object\") continue;\n if (normalizeDependencyMap(deps as Record<string, string>, spec, options)) {\n modified = true;\n }\n }\n\n if (pkg.workspaces && typeof pkg.workspaces === \"object\") {\n const workspaces = pkg.workspaces as {\n packages?: string[];\n catalog?: Record<string, string>;\n };\n\n if (options.excludeFrameworkWorkspaces && Array.isArray(workspaces.packages)) {\n const nextPackages = workspaces.packages.filter(\n (entry) => !FRAMEWORK_PACKAGES.some((name) => entry === `packages/${name}`),\n );\n if (nextPackages.length !== workspaces.packages.length) {\n workspaces.packages = nextPackages;\n modified = true;\n }\n }\n\n if (workspaces.catalog && typeof workspaces.catalog === \"object\") {\n for (const [name, version] of Object.entries(workspaces.catalog)) {\n const resolved = spec.rootCatalog[name];\n if (resolved && resolved !== version) {\n workspaces.catalog[name] = resolved;\n modified = true;\n continue;\n }\n\n if (version === \"workspace:*\" && spec.frameworkVersions[name]) {\n workspaces.catalog[name] = `^${spec.frameworkVersions[name]}`;\n modified = true;\n }\n }\n }\n }\n\n if (options.removeWorkspaces && \"workspaces\" in pkg) {\n delete pkg.workspaces;\n modified = true;\n }\n\n if (options.removePublishScripts && pkg.scripts && typeof pkg.scripts === \"object\") {\n const scripts = pkg.scripts as Record<string, string>;\n let scriptsModified = false;\n for (const key of [\"prepublishOnly\", \"prepack\", \"prepare\", \"postpack\"]) {\n if (key in scripts) {\n delete scripts[key];\n scriptsModified = true;\n }\n }\n if (scriptsModified) {\n modified = true;\n if (Object.keys(scripts).length === 0) {\n delete pkg.scripts;\n }\n }\n }\n\n return modified;\n}\n\nexport async function normalizePackageManifestsInTree(opts: NormalizeTreeOptions) {\n const spec = loadManifestNormalizationSpec(opts.sourceRootDir);\n const files = await glob(\"**/package.json\", {\n cwd: opts.targetDir,\n nodir: true,\n dot: false,\n absolute: true,\n ignore: [\"**/node_modules/**\"],\n });\n\n const updatedFiles: string[] = [];\n\n for (const filePath of files) {\n const pkg = readJson<PackageJson>(filePath);\n if (normalizePackageManifest(pkg, spec, opts)) {\n writeJson(filePath, pkg);\n updatedFiles.push(filePath);\n }\n }\n\n return updatedFiles;\n}\n\nfunction shouldCopyPackageFile(sourceDir: string, filePath: string) {\n const relPath = relative(sourceDir, filePath);\n if (!relPath) return true;\n const segments = relPath.split(sep);\n return !segments.includes(\"node_modules\") && !segments.includes(\"tests\");\n}\n\nfunction stripDevelopmentExports(pkg: PackageJson) {\n const exports = pkg.exports;\n if (!exports || typeof exports !== \"object\") return;\n\n for (const key of Object.keys(exports as Record<string, unknown>)) {\n const entry = (exports as Record<string, unknown>)[key];\n if (entry && typeof entry === \"object\") {\n delete (entry as Record<string, unknown>).development;\n }\n }\n}\n\nexport function stageReleasePackage(opts: {\n repoRoot: string;\n packageName: string;\n outDir: string;\n}) {\n const sourceDir = join(opts.repoRoot, \"packages\", opts.packageName);\n\n rmSync(opts.outDir, { recursive: true, force: true });\n mkdirSync(dirname(opts.outDir), { recursive: true });\n cpSync(sourceDir, opts.outDir, {\n recursive: true,\n filter: (filePath) => shouldCopyPackageFile(sourceDir, filePath),\n });\n rmSync(join(opts.outDir, \"tests\"), { recursive: true, force: true });\n\n const packageJsonPath = join(opts.outDir, \"package.json\");\n const spec = loadManifestNormalizationSpec(opts.repoRoot);\n const pkg = readJson<PackageJson>(packageJsonPath);\n\n normalizePackageManifest(pkg, spec, {\n resolveCatalogRefs: true,\n preserveCatalogRefs: false,\n removeWorkspaces: true,\n removePublishScripts: true,\n });\n\n stripDevelopmentExports(pkg);\n\n writeJson(packageJsonPath, pkg);\n}\n\nexport function stageReleasePackages(opts: {\n repoRoot: string;\n outDir: string;\n packageNames?: string[];\n}) {\n const packageNames = opts.packageNames ?? [...FRAMEWORK_PACKAGES];\n rmSync(opts.outDir, { recursive: true, force: true });\n mkdirSync(opts.outDir, { recursive: true });\n\n for (const packageName of packageNames) {\n stageReleasePackage({\n repoRoot: opts.repoRoot,\n packageName,\n outDir: join(opts.outDir, packageName),\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"manifest-normalizer.cjs","names":[],"sources":["../../src/internal/manifest-normalizer.ts"],"sourcesContent":["import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport { glob } from \"glob\";\n\nconst FRAMEWORK_PACKAGES = [\"every-plugin\", \"everything-dev\"] as const;\n\nconst CATALOG_TOOL_PACKAGES = [\n \"@rspack/core\",\n \"@rspack/cli\",\n \"@rsbuild/core\",\n \"@rsbuild/plugin-react\",\n \"@module-federation/enhanced\",\n \"@module-federation/node\",\n \"@module-federation/rsbuild-plugin\",\n \"@module-federation/runtime-core\",\n \"@module-federation/sdk\",\n \"@module-federation/dts-plugin\",\n] as const;\n\ntype PackageJson = Record<string, unknown>;\n\ntype NormalizationSpec = {\n rootCatalog: Record<string, string>;\n frameworkVersions: Record<string, string>;\n};\n\ntype NormalizeManifestOptions = {\n resolveCatalogRefs: boolean;\n preserveCatalogRefs?: boolean;\n excludeFrameworkWorkspaces?: boolean;\n removeWorkspaceDeps?: string[];\n removeWorkspaces?: boolean;\n removePublishScripts?: boolean;\n};\n\nexport type NormalizeTreeOptions = NormalizeManifestOptions & {\n sourceRootDir: string;\n targetDir: string;\n};\n\nfunction readJson<T>(filePath: string): T {\n return JSON.parse(readFileSync(filePath, \"utf-8\")) as T;\n}\n\nfunction extractExactVersion(input: string | undefined): string | null {\n if (!input) return null;\n const match = input.match(/\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?/);\n return match ? match[0] : null;\n}\n\nfunction writeJson(filePath: string, value: PackageJson) {\n writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport function loadManifestNormalizationSpec(sourceRootDir: string): NormalizationSpec {\n const rootPackage = readJson<PackageJson>(join(sourceRootDir, \"package.json\"));\n const rootCatalog = {\n ...(((rootPackage.workspaces as { catalog?: Record<string, string> } | undefined)?.catalog ??\n {}) as Record<string, string>),\n };\n const frameworkVersions: Record<string, string> = {};\n\n for (const packageName of FRAMEWORK_PACKAGES) {\n const sourcePackagePath = join(sourceRootDir, \"packages\", packageName, \"package.json\");\n const localPackagePath = join(import.meta.dirname, \"..\", \"..\", packageName, \"package.json\");\n const packageVersion = existsSync(localPackagePath)\n ? readJson<{ version: string }>(localPackagePath).version\n : existsSync(sourcePackagePath)\n ? readJson<{ version: string }>(sourcePackagePath).version\n : extractExactVersion(rootCatalog[packageName]);\n\n if (!packageVersion) {\n throw new Error(`Could not resolve version for ${packageName}`);\n }\n\n frameworkVersions[packageName] = packageVersion;\n rootCatalog[packageName] = `^${packageVersion}`;\n }\n\n return { rootCatalog, frameworkVersions };\n}\n\nfunction normalizeDependencyMap(\n map: Record<string, string>,\n spec: NormalizationSpec,\n options: NormalizeManifestOptions,\n) {\n const catalogPackages = new Set<string>([...FRAMEWORK_PACKAGES, ...CATALOG_TOOL_PACKAGES]);\n\n let modified = false;\n\n for (const [name, version] of Object.entries(map)) {\n if (options.preserveCatalogRefs && catalogPackages.has(name)) {\n if (version !== \"catalog:\") {\n map[name] = \"catalog:\";\n modified = true;\n }\n continue;\n }\n\n if (version === \"workspace:*\") {\n const frameworkVersion = spec.frameworkVersions[name];\n if (frameworkVersion) {\n map[name] = `^${frameworkVersion}`;\n modified = true;\n continue;\n }\n\n if (options.removeWorkspaceDeps?.includes(name)) {\n delete map[name];\n modified = true;\n }\n continue;\n }\n\n if (options.resolveCatalogRefs && version.startsWith(\"catalog:\")) {\n const resolved = spec.rootCatalog[name];\n if (resolved) {\n map[name] = resolved;\n modified = true;\n }\n }\n }\n\n return modified;\n}\n\nexport function normalizePackageManifest(\n pkg: PackageJson,\n spec: NormalizationSpec,\n options: NormalizeManifestOptions,\n) {\n let modified = false;\n\n for (const depField of [\"dependencies\", \"devDependencies\", \"peerDependencies\"]) {\n const deps = pkg[depField];\n if (!deps || typeof deps !== \"object\") continue;\n if (normalizeDependencyMap(deps as Record<string, string>, spec, options)) {\n modified = true;\n }\n }\n\n if (pkg.workspaces && typeof pkg.workspaces === \"object\") {\n const workspaces = pkg.workspaces as {\n packages?: string[];\n catalog?: Record<string, string>;\n };\n\n if (options.excludeFrameworkWorkspaces && Array.isArray(workspaces.packages)) {\n const nextPackages = workspaces.packages.filter(\n (entry) => !FRAMEWORK_PACKAGES.some((name) => entry === `packages/${name}`),\n );\n if (nextPackages.length !== workspaces.packages.length) {\n workspaces.packages = nextPackages;\n modified = true;\n }\n }\n\n if (workspaces.catalog && typeof workspaces.catalog === \"object\") {\n for (const [name, version] of Object.entries(workspaces.catalog)) {\n const resolved = spec.rootCatalog[name];\n if (resolved && resolved !== version) {\n workspaces.catalog[name] = resolved;\n modified = true;\n continue;\n }\n\n if (version === \"workspace:*\" && spec.frameworkVersions[name]) {\n workspaces.catalog[name] = `^${spec.frameworkVersions[name]}`;\n modified = true;\n }\n }\n }\n }\n\n if (options.removeWorkspaces && \"workspaces\" in pkg) {\n delete pkg.workspaces;\n modified = true;\n }\n\n if (options.removePublishScripts && pkg.scripts && typeof pkg.scripts === \"object\") {\n const scripts = pkg.scripts as Record<string, string>;\n let scriptsModified = false;\n for (const key of [\"prepublishOnly\", \"prepack\", \"prepare\", \"postpack\"]) {\n if (key in scripts) {\n delete scripts[key];\n scriptsModified = true;\n }\n }\n if (scriptsModified) {\n modified = true;\n if (Object.keys(scripts).length === 0) {\n delete pkg.scripts;\n }\n }\n }\n\n return modified;\n}\n\nexport async function normalizePackageManifestsInTree(opts: NormalizeTreeOptions) {\n const spec = loadManifestNormalizationSpec(opts.sourceRootDir);\n const files = await glob(\"**/package.json\", {\n cwd: opts.targetDir,\n nodir: true,\n dot: false,\n absolute: true,\n ignore: [\"**/node_modules/**\"],\n });\n\n const updatedFiles: string[] = [];\n\n for (const filePath of files) {\n const pkg = readJson<PackageJson>(filePath);\n if (normalizePackageManifest(pkg, spec, opts)) {\n writeJson(filePath, pkg);\n updatedFiles.push(filePath);\n }\n }\n\n return updatedFiles;\n}\n\nfunction shouldCopyPackageFile(sourceDir: string, filePath: string) {\n const relPath = relative(sourceDir, filePath);\n if (!relPath) return true;\n const segments = relPath.split(sep);\n return !segments.includes(\"node_modules\") && !segments.includes(\"tests\");\n}\n\nfunction stripDevelopmentExports(pkg: PackageJson) {\n const exports = pkg.exports;\n if (!exports || typeof exports !== \"object\") return;\n\n for (const key of Object.keys(exports as Record<string, unknown>)) {\n const entry = (exports as Record<string, unknown>)[key];\n if (entry && typeof entry === \"object\") {\n delete (entry as Record<string, unknown>).development;\n }\n }\n}\n\nexport function stageReleasePackage(opts: {\n repoRoot: string;\n packageName: string;\n outDir: string;\n}) {\n const sourceDir = join(opts.repoRoot, \"packages\", opts.packageName);\n\n rmSync(opts.outDir, { recursive: true, force: true });\n mkdirSync(dirname(opts.outDir), { recursive: true });\n cpSync(sourceDir, opts.outDir, {\n recursive: true,\n filter: (filePath) => shouldCopyPackageFile(sourceDir, filePath),\n });\n rmSync(join(opts.outDir, \"tests\"), { recursive: true, force: true });\n\n const packageJsonPath = join(opts.outDir, \"package.json\");\n const spec = loadManifestNormalizationSpec(opts.repoRoot);\n const pkg = readJson<PackageJson>(packageJsonPath);\n\n normalizePackageManifest(pkg, spec, {\n resolveCatalogRefs: true,\n preserveCatalogRefs: false,\n removeWorkspaces: true,\n removePublishScripts: true,\n });\n\n stripDevelopmentExports(pkg);\n\n writeJson(packageJsonPath, pkg);\n}\n\nexport function stageReleasePackages(opts: {\n repoRoot: string;\n outDir: string;\n packageNames?: string[];\n}) {\n const packageNames = opts.packageNames ?? [...FRAMEWORK_PACKAGES];\n rmSync(opts.outDir, { recursive: true, force: true });\n mkdirSync(opts.outDir, { recursive: true });\n\n for (const packageName of packageNames) {\n stageReleasePackage({\n repoRoot: opts.repoRoot,\n packageName,\n outDir: join(opts.outDir, packageName),\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -44,7 +44,7 @@ function loadManifestNormalizationSpec(sourceRootDir) {
|
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
function normalizeDependencyMap(map, spec, options) {
|
|
47
|
-
const catalogPackages = new Set([...FRAMEWORK_PACKAGES, ...CATALOG_TOOL_PACKAGES]);
|
|
47
|
+
const catalogPackages = /* @__PURE__ */ new Set([...FRAMEWORK_PACKAGES, ...CATALOG_TOOL_PACKAGES]);
|
|
48
48
|
let modified = false;
|
|
49
49
|
for (const [name, version] of Object.entries(map)) {
|
|
50
50
|
if (options.preserveCatalogRefs && catalogPackages.has(name)) {
|
|
@@ -154,5 +154,5 @@ async function normalizePackageManifestsInTree(opts) {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
//#endregion
|
|
157
|
-
export { loadManifestNormalizationSpec, normalizePackageManifestsInTree };
|
|
157
|
+
export { loadManifestNormalizationSpec, normalizePackageManifest, normalizePackageManifestsInTree };
|
|
158
158
|
//# sourceMappingURL=manifest-normalizer.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-normalizer.mjs","names":[],"sources":["../../src/internal/manifest-normalizer.ts"],"sourcesContent":["import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport { glob } from \"glob\";\n\nconst FRAMEWORK_PACKAGES = [\"every-plugin\", \"everything-dev\"] as const;\n\nconst CATALOG_TOOL_PACKAGES = [\n \"@rspack/core\",\n \"@rspack/cli\",\n \"@rsbuild/core\",\n \"@rsbuild/plugin-react\",\n \"@module-federation/enhanced\",\n \"@module-federation/node\",\n \"@module-federation/rsbuild-plugin\",\n \"@module-federation/runtime-core\",\n \"@module-federation/sdk\",\n \"@module-federation/dts-plugin\",\n] as const;\n\ntype PackageJson = Record<string, unknown>;\n\ntype NormalizationSpec = {\n rootCatalog: Record<string, string>;\n frameworkVersions: Record<string, string>;\n};\n\ntype NormalizeManifestOptions = {\n resolveCatalogRefs: boolean;\n preserveCatalogRefs?: boolean;\n excludeFrameworkWorkspaces?: boolean;\n removeWorkspaceDeps?: string[];\n removeWorkspaces?: boolean;\n removePublishScripts?: boolean;\n};\n\nexport type NormalizeTreeOptions = NormalizeManifestOptions & {\n sourceRootDir: string;\n targetDir: string;\n};\n\nfunction readJson<T>(filePath: string): T {\n return JSON.parse(readFileSync(filePath, \"utf-8\")) as T;\n}\n\nfunction extractExactVersion(input: string | undefined): string | null {\n if (!input) return null;\n const match = input.match(/\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?/);\n return match ? match[0] : null;\n}\n\nfunction writeJson(filePath: string, value: PackageJson) {\n writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport function loadManifestNormalizationSpec(sourceRootDir: string): NormalizationSpec {\n const rootPackage = readJson<PackageJson>(join(sourceRootDir, \"package.json\"));\n const rootCatalog = {\n ...(((rootPackage.workspaces as { catalog?: Record<string, string> } | undefined)?.catalog ??\n {}) as Record<string, string>),\n };\n const frameworkVersions: Record<string, string> = {};\n\n for (const packageName of FRAMEWORK_PACKAGES) {\n const sourcePackagePath = join(sourceRootDir, \"packages\", packageName, \"package.json\");\n const localPackagePath = join(import.meta.dirname, \"..\", \"..\", packageName, \"package.json\");\n const packageVersion = existsSync(localPackagePath)\n ? readJson<{ version: string }>(localPackagePath).version\n : existsSync(sourcePackagePath)\n ? readJson<{ version: string }>(sourcePackagePath).version\n : extractExactVersion(rootCatalog[packageName]);\n\n if (!packageVersion) {\n throw new Error(`Could not resolve version for ${packageName}`);\n }\n\n frameworkVersions[packageName] = packageVersion;\n rootCatalog[packageName] = `^${packageVersion}`;\n }\n\n return { rootCatalog, frameworkVersions };\n}\n\nfunction normalizeDependencyMap(\n map: Record<string, string>,\n spec: NormalizationSpec,\n options: NormalizeManifestOptions,\n) {\n const catalogPackages = new Set<string>([...FRAMEWORK_PACKAGES, ...CATALOG_TOOL_PACKAGES]);\n\n let modified = false;\n\n for (const [name, version] of Object.entries(map)) {\n if (options.preserveCatalogRefs && catalogPackages.has(name)) {\n if (version !== \"catalog:\") {\n map[name] = \"catalog:\";\n modified = true;\n }\n continue;\n }\n\n if (version === \"workspace:*\") {\n const frameworkVersion = spec.frameworkVersions[name];\n if (frameworkVersion) {\n map[name] = `^${frameworkVersion}`;\n modified = true;\n continue;\n }\n\n if (options.removeWorkspaceDeps?.includes(name)) {\n delete map[name];\n modified = true;\n }\n continue;\n }\n\n if (options.resolveCatalogRefs && version.startsWith(\"catalog:\")) {\n const resolved = spec.rootCatalog[name];\n if (resolved) {\n map[name] = resolved;\n modified = true;\n }\n }\n }\n\n return modified;\n}\n\nexport function normalizePackageManifest(\n pkg: PackageJson,\n spec: NormalizationSpec,\n options: NormalizeManifestOptions,\n) {\n let modified = false;\n\n for (const depField of [\"dependencies\", \"devDependencies\", \"peerDependencies\"]) {\n const deps = pkg[depField];\n if (!deps || typeof deps !== \"object\") continue;\n if (normalizeDependencyMap(deps as Record<string, string>, spec, options)) {\n modified = true;\n }\n }\n\n if (pkg.workspaces && typeof pkg.workspaces === \"object\") {\n const workspaces = pkg.workspaces as {\n packages?: string[];\n catalog?: Record<string, string>;\n };\n\n if (options.excludeFrameworkWorkspaces && Array.isArray(workspaces.packages)) {\n const nextPackages = workspaces.packages.filter(\n (entry) => !FRAMEWORK_PACKAGES.some((name) => entry === `packages/${name}`),\n );\n if (nextPackages.length !== workspaces.packages.length) {\n workspaces.packages = nextPackages;\n modified = true;\n }\n }\n\n if (workspaces.catalog && typeof workspaces.catalog === \"object\") {\n for (const [name, version] of Object.entries(workspaces.catalog)) {\n const resolved = spec.rootCatalog[name];\n if (resolved && resolved !== version) {\n workspaces.catalog[name] = resolved;\n modified = true;\n continue;\n }\n\n if (version === \"workspace:*\" && spec.frameworkVersions[name]) {\n workspaces.catalog[name] = `^${spec.frameworkVersions[name]}`;\n modified = true;\n }\n }\n }\n }\n\n if (options.removeWorkspaces && \"workspaces\" in pkg) {\n delete pkg.workspaces;\n modified = true;\n }\n\n if (options.removePublishScripts && pkg.scripts && typeof pkg.scripts === \"object\") {\n const scripts = pkg.scripts as Record<string, string>;\n let scriptsModified = false;\n for (const key of [\"prepublishOnly\", \"prepack\", \"prepare\", \"postpack\"]) {\n if (key in scripts) {\n delete scripts[key];\n scriptsModified = true;\n }\n }\n if (scriptsModified) {\n modified = true;\n if (Object.keys(scripts).length === 0) {\n delete pkg.scripts;\n }\n }\n }\n\n return modified;\n}\n\nexport async function normalizePackageManifestsInTree(opts: NormalizeTreeOptions) {\n const spec = loadManifestNormalizationSpec(opts.sourceRootDir);\n const files = await glob(\"**/package.json\", {\n cwd: opts.targetDir,\n nodir: true,\n dot: false,\n absolute: true,\n ignore: [\"**/node_modules/**\"],\n });\n\n const updatedFiles: string[] = [];\n\n for (const filePath of files) {\n const pkg = readJson<PackageJson>(filePath);\n if (normalizePackageManifest(pkg, spec, opts)) {\n writeJson(filePath, pkg);\n updatedFiles.push(filePath);\n }\n }\n\n return updatedFiles;\n}\n\nfunction shouldCopyPackageFile(sourceDir: string, filePath: string) {\n const relPath = relative(sourceDir, filePath);\n if (!relPath) return true;\n const segments = relPath.split(sep);\n return !segments.includes(\"node_modules\") && !segments.includes(\"tests\");\n}\n\nfunction stripDevelopmentExports(pkg: PackageJson) {\n const exports = pkg.exports;\n if (!exports || typeof exports !== \"object\") return;\n\n for (const key of Object.keys(exports as Record<string, unknown>)) {\n const entry = (exports as Record<string, unknown>)[key];\n if (entry && typeof entry === \"object\") {\n delete (entry as Record<string, unknown>).development;\n }\n }\n}\n\nexport function stageReleasePackage(opts: {\n repoRoot: string;\n packageName: string;\n outDir: string;\n}) {\n const sourceDir = join(opts.repoRoot, \"packages\", opts.packageName);\n\n rmSync(opts.outDir, { recursive: true, force: true });\n mkdirSync(dirname(opts.outDir), { recursive: true });\n cpSync(sourceDir, opts.outDir, {\n recursive: true,\n filter: (filePath) => shouldCopyPackageFile(sourceDir, filePath),\n });\n rmSync(join(opts.outDir, \"tests\"), { recursive: true, force: true });\n\n const packageJsonPath = join(opts.outDir, \"package.json\");\n const spec = loadManifestNormalizationSpec(opts.repoRoot);\n const pkg = readJson<PackageJson>(packageJsonPath);\n\n normalizePackageManifest(pkg, spec, {\n resolveCatalogRefs: true,\n preserveCatalogRefs: false,\n removeWorkspaces: true,\n removePublishScripts: true,\n });\n\n stripDevelopmentExports(pkg);\n\n writeJson(packageJsonPath, pkg);\n}\n\nexport function stageReleasePackages(opts: {\n repoRoot: string;\n outDir: string;\n packageNames?: string[];\n}) {\n const packageNames = opts.packageNames ?? [...FRAMEWORK_PACKAGES];\n rmSync(opts.outDir, { recursive: true, force: true });\n mkdirSync(opts.outDir, { recursive: true });\n\n for (const packageName of packageNames) {\n stageReleasePackage({\n repoRoot: opts.repoRoot,\n packageName,\n outDir: join(opts.outDir, packageName),\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"manifest-normalizer.mjs","names":[],"sources":["../../src/internal/manifest-normalizer.ts"],"sourcesContent":["import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport { glob } from \"glob\";\n\nconst FRAMEWORK_PACKAGES = [\"every-plugin\", \"everything-dev\"] as const;\n\nconst CATALOG_TOOL_PACKAGES = [\n \"@rspack/core\",\n \"@rspack/cli\",\n \"@rsbuild/core\",\n \"@rsbuild/plugin-react\",\n \"@module-federation/enhanced\",\n \"@module-federation/node\",\n \"@module-federation/rsbuild-plugin\",\n \"@module-federation/runtime-core\",\n \"@module-federation/sdk\",\n \"@module-federation/dts-plugin\",\n] as const;\n\ntype PackageJson = Record<string, unknown>;\n\ntype NormalizationSpec = {\n rootCatalog: Record<string, string>;\n frameworkVersions: Record<string, string>;\n};\n\ntype NormalizeManifestOptions = {\n resolveCatalogRefs: boolean;\n preserveCatalogRefs?: boolean;\n excludeFrameworkWorkspaces?: boolean;\n removeWorkspaceDeps?: string[];\n removeWorkspaces?: boolean;\n removePublishScripts?: boolean;\n};\n\nexport type NormalizeTreeOptions = NormalizeManifestOptions & {\n sourceRootDir: string;\n targetDir: string;\n};\n\nfunction readJson<T>(filePath: string): T {\n return JSON.parse(readFileSync(filePath, \"utf-8\")) as T;\n}\n\nfunction extractExactVersion(input: string | undefined): string | null {\n if (!input) return null;\n const match = input.match(/\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?/);\n return match ? match[0] : null;\n}\n\nfunction writeJson(filePath: string, value: PackageJson) {\n writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport function loadManifestNormalizationSpec(sourceRootDir: string): NormalizationSpec {\n const rootPackage = readJson<PackageJson>(join(sourceRootDir, \"package.json\"));\n const rootCatalog = {\n ...(((rootPackage.workspaces as { catalog?: Record<string, string> } | undefined)?.catalog ??\n {}) as Record<string, string>),\n };\n const frameworkVersions: Record<string, string> = {};\n\n for (const packageName of FRAMEWORK_PACKAGES) {\n const sourcePackagePath = join(sourceRootDir, \"packages\", packageName, \"package.json\");\n const localPackagePath = join(import.meta.dirname, \"..\", \"..\", packageName, \"package.json\");\n const packageVersion = existsSync(localPackagePath)\n ? readJson<{ version: string }>(localPackagePath).version\n : existsSync(sourcePackagePath)\n ? readJson<{ version: string }>(sourcePackagePath).version\n : extractExactVersion(rootCatalog[packageName]);\n\n if (!packageVersion) {\n throw new Error(`Could not resolve version for ${packageName}`);\n }\n\n frameworkVersions[packageName] = packageVersion;\n rootCatalog[packageName] = `^${packageVersion}`;\n }\n\n return { rootCatalog, frameworkVersions };\n}\n\nfunction normalizeDependencyMap(\n map: Record<string, string>,\n spec: NormalizationSpec,\n options: NormalizeManifestOptions,\n) {\n const catalogPackages = new Set<string>([...FRAMEWORK_PACKAGES, ...CATALOG_TOOL_PACKAGES]);\n\n let modified = false;\n\n for (const [name, version] of Object.entries(map)) {\n if (options.preserveCatalogRefs && catalogPackages.has(name)) {\n if (version !== \"catalog:\") {\n map[name] = \"catalog:\";\n modified = true;\n }\n continue;\n }\n\n if (version === \"workspace:*\") {\n const frameworkVersion = spec.frameworkVersions[name];\n if (frameworkVersion) {\n map[name] = `^${frameworkVersion}`;\n modified = true;\n continue;\n }\n\n if (options.removeWorkspaceDeps?.includes(name)) {\n delete map[name];\n modified = true;\n }\n continue;\n }\n\n if (options.resolveCatalogRefs && version.startsWith(\"catalog:\")) {\n const resolved = spec.rootCatalog[name];\n if (resolved) {\n map[name] = resolved;\n modified = true;\n }\n }\n }\n\n return modified;\n}\n\nexport function normalizePackageManifest(\n pkg: PackageJson,\n spec: NormalizationSpec,\n options: NormalizeManifestOptions,\n) {\n let modified = false;\n\n for (const depField of [\"dependencies\", \"devDependencies\", \"peerDependencies\"]) {\n const deps = pkg[depField];\n if (!deps || typeof deps !== \"object\") continue;\n if (normalizeDependencyMap(deps as Record<string, string>, spec, options)) {\n modified = true;\n }\n }\n\n if (pkg.workspaces && typeof pkg.workspaces === \"object\") {\n const workspaces = pkg.workspaces as {\n packages?: string[];\n catalog?: Record<string, string>;\n };\n\n if (options.excludeFrameworkWorkspaces && Array.isArray(workspaces.packages)) {\n const nextPackages = workspaces.packages.filter(\n (entry) => !FRAMEWORK_PACKAGES.some((name) => entry === `packages/${name}`),\n );\n if (nextPackages.length !== workspaces.packages.length) {\n workspaces.packages = nextPackages;\n modified = true;\n }\n }\n\n if (workspaces.catalog && typeof workspaces.catalog === \"object\") {\n for (const [name, version] of Object.entries(workspaces.catalog)) {\n const resolved = spec.rootCatalog[name];\n if (resolved && resolved !== version) {\n workspaces.catalog[name] = resolved;\n modified = true;\n continue;\n }\n\n if (version === \"workspace:*\" && spec.frameworkVersions[name]) {\n workspaces.catalog[name] = `^${spec.frameworkVersions[name]}`;\n modified = true;\n }\n }\n }\n }\n\n if (options.removeWorkspaces && \"workspaces\" in pkg) {\n delete pkg.workspaces;\n modified = true;\n }\n\n if (options.removePublishScripts && pkg.scripts && typeof pkg.scripts === \"object\") {\n const scripts = pkg.scripts as Record<string, string>;\n let scriptsModified = false;\n for (const key of [\"prepublishOnly\", \"prepack\", \"prepare\", \"postpack\"]) {\n if (key in scripts) {\n delete scripts[key];\n scriptsModified = true;\n }\n }\n if (scriptsModified) {\n modified = true;\n if (Object.keys(scripts).length === 0) {\n delete pkg.scripts;\n }\n }\n }\n\n return modified;\n}\n\nexport async function normalizePackageManifestsInTree(opts: NormalizeTreeOptions) {\n const spec = loadManifestNormalizationSpec(opts.sourceRootDir);\n const files = await glob(\"**/package.json\", {\n cwd: opts.targetDir,\n nodir: true,\n dot: false,\n absolute: true,\n ignore: [\"**/node_modules/**\"],\n });\n\n const updatedFiles: string[] = [];\n\n for (const filePath of files) {\n const pkg = readJson<PackageJson>(filePath);\n if (normalizePackageManifest(pkg, spec, opts)) {\n writeJson(filePath, pkg);\n updatedFiles.push(filePath);\n }\n }\n\n return updatedFiles;\n}\n\nfunction shouldCopyPackageFile(sourceDir: string, filePath: string) {\n const relPath = relative(sourceDir, filePath);\n if (!relPath) return true;\n const segments = relPath.split(sep);\n return !segments.includes(\"node_modules\") && !segments.includes(\"tests\");\n}\n\nfunction stripDevelopmentExports(pkg: PackageJson) {\n const exports = pkg.exports;\n if (!exports || typeof exports !== \"object\") return;\n\n for (const key of Object.keys(exports as Record<string, unknown>)) {\n const entry = (exports as Record<string, unknown>)[key];\n if (entry && typeof entry === \"object\") {\n delete (entry as Record<string, unknown>).development;\n }\n }\n}\n\nexport function stageReleasePackage(opts: {\n repoRoot: string;\n packageName: string;\n outDir: string;\n}) {\n const sourceDir = join(opts.repoRoot, \"packages\", opts.packageName);\n\n rmSync(opts.outDir, { recursive: true, force: true });\n mkdirSync(dirname(opts.outDir), { recursive: true });\n cpSync(sourceDir, opts.outDir, {\n recursive: true,\n filter: (filePath) => shouldCopyPackageFile(sourceDir, filePath),\n });\n rmSync(join(opts.outDir, \"tests\"), { recursive: true, force: true });\n\n const packageJsonPath = join(opts.outDir, \"package.json\");\n const spec = loadManifestNormalizationSpec(opts.repoRoot);\n const pkg = readJson<PackageJson>(packageJsonPath);\n\n normalizePackageManifest(pkg, spec, {\n resolveCatalogRefs: true,\n preserveCatalogRefs: false,\n removeWorkspaces: true,\n removePublishScripts: true,\n });\n\n stripDevelopmentExports(pkg);\n\n writeJson(packageJsonPath, pkg);\n}\n\nexport function stageReleasePackages(opts: {\n repoRoot: string;\n outDir: string;\n packageNames?: string[];\n}) {\n const packageNames = opts.packageNames ?? [...FRAMEWORK_PACKAGES];\n rmSync(opts.outDir, { recursive: true, force: true });\n mkdirSync(opts.outDir, { recursive: true });\n\n for (const packageName of packageNames) {\n stageReleasePackage({\n repoRoot: opts.repoRoot,\n packageName,\n outDir: join(opts.outDir, packageName),\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/merge.cjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
1
|
let defu = require("defu");
|
|
3
2
|
|
|
4
3
|
//#region src/merge.ts
|
|
@@ -15,7 +14,7 @@ const BOS_CONFIG_ORDER = [
|
|
|
15
14
|
"app",
|
|
16
15
|
"plugins"
|
|
17
16
|
];
|
|
18
|
-
const ARRAY_UNION_KEYS = new Set(["secrets"]);
|
|
17
|
+
const ARRAY_UNION_KEYS = /* @__PURE__ */ new Set(["secrets"]);
|
|
19
18
|
function isPlainObject(value) {
|
|
20
19
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
21
20
|
}
|
package/dist/merge.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.cjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import { createDefu } from \"defu\";\nimport type { BosConfigInput, ExtendsConfig } from \"./types\";\n\nexport const BOS_CONFIG_ORDER = [\n \"extends\",\n \"account\",\n \"domain\",\n \"title\",\n \"description\",\n \"testnet\",\n \"staging\",\n \"repository\",\n \"ci\",\n \"app\",\n \"plugins\",\n] as const;\n\nexport type BosConfigFieldName = (typeof BOS_CONFIG_ORDER)[number];\n\nexport type BosEnv = \"development\" | \"production\" | \"staging\";\n\nexport interface ResolvedConfigMeta {\n env: BosEnv;\n resolvedAt: string;\n extendsChain: string[];\n source?: string;\n}\n\nconst ARRAY_UNION_KEYS = new Set([\"secrets\"]);\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction unionArrays(a: unknown, b: unknown): unknown[] | undefined {\n const aArr = Array.isArray(a) ? a : [];\n const bArr = Array.isArray(b) ? b : [];\n if (aArr.length === 0 && bArr.length === 0) return undefined;\n const seen = new Set<string>();\n const result: unknown[] = [];\n for (const item of [...aArr, ...bArr]) {\n if (typeof item === \"string\") {\n if (seen.has(item)) continue;\n seen.add(item);\n }\n result.push(item);\n }\n return result;\n}\n\nfunction cleanNullSentinels(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value === null || value === undefined) continue;\n if (isPlainObject(value)) {\n const cleaned = cleanNullSentinels(value);\n if (Object.keys(cleaned).length > 0) {\n out[key] = cleaned;\n }\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst bosConfigMerger = createDefu((obj: any, key: any, value: any): boolean | undefined => {\n if (obj[key] === null) return true;\n if (value === null) {\n obj[key] = null;\n return true;\n }\n if (Array.isArray(obj[key]) && Array.isArray(value)) {\n if (ARRAY_UNION_KEYS.has(key)) {\n obj[key] = unionArrays(obj[key], value);\n } else {\n obj[key] = value;\n }\n return true;\n }\n return false;\n});\n\nexport function resolveExtendsRef(\n extendsField: string | ExtendsConfig | undefined,\n env: BosEnv,\n): string | undefined {\n if (!extendsField) return undefined;\n if (typeof extendsField === \"string\") return extendsField;\n return extendsField[env] ?? extendsField.production ?? Object.values(extendsField).find(Boolean);\n}\n\nexport function mergeBosConfigWithExtends(\n parent: BosConfigInput,\n child: BosConfigInput,\n): BosConfigInput {\n const merged = bosConfigMerger(child, parent) as BosConfigInput;\n\n if (child.plugins !== undefined && isPlainObject(child.plugins)) {\n (merged as Record<string, unknown>).plugins = cleanNullSentinels(\n child.plugins as Record<string, unknown>,\n );\n }\n\n const mergedRecord = merged as Record<string, unknown>;\n\n if (isPlainObject(mergedRecord.app)) {\n for (const entryVal of Object.values(mergedRecord.app as Record<string, unknown>)) {\n if (!isPlainObject(entryVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(entryVal[secretKey])) {\n entryVal[secretKey] =\n (unionArrays(entryVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? entryVal[secretKey];\n }\n }\n }\n }\n\n if (isPlainObject(mergedRecord.plugins)) {\n for (const pluginVal of Object.values(mergedRecord.plugins as Record<string, unknown>)) {\n if (!isPlainObject(pluginVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(pluginVal[secretKey])) {\n pluginVal[secretKey] =\n (unionArrays(pluginVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? pluginVal[secretKey];\n }\n }\n }\n }\n\n return rebuildOrderedConfig(mergedRecord) as BosConfigInput;\n}\n\nexport function mergeBosConfigWithTemplate(\n local: BosConfigInput,\n template: BosConfigInput,\n): BosConfigInput {\n const merged = mergeJsonValuesPreservingLocalOrder(local, template) as BosConfigInput;\n return rebuildOrderedConfig(merged as Record<string, unknown>) as BosConfigInput;\n}\n\nfunction mergeJsonValuesPreservingLocalOrder(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValuesPreservingLocalOrder(\n local[key],\n (template as Record<string, unknown>)[key],\n );\n }\n for (const key of Object.keys(template as Record<string, unknown>)) {\n if (!(key in merged)) {\n merged[key] = (template as Record<string, unknown>)[key];\n }\n }\n return merged;\n }\n return local ?? template;\n}\n\nexport function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T {\n const ordered: Record<string, unknown> = {};\n\n for (const key of BOS_CONFIG_ORDER) {\n if (key in config) {\n ordered[key] = config[key];\n }\n }\n\n for (const key of Object.keys(config)) {\n if (!BOS_CONFIG_ORDER.includes(key as BosConfigFieldName)) {\n ordered[key] = config[key];\n }\n }\n\n return ordered as T;\n}\n\nexport { bosConfigMerger };\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"merge.cjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import { createDefu } from \"defu\";\nimport type { BosConfigInput, ExtendsConfig } from \"./types\";\n\nexport const BOS_CONFIG_ORDER = [\n \"extends\",\n \"account\",\n \"domain\",\n \"title\",\n \"description\",\n \"testnet\",\n \"staging\",\n \"repository\",\n \"ci\",\n \"app\",\n \"plugins\",\n] as const;\n\nexport type BosConfigFieldName = (typeof BOS_CONFIG_ORDER)[number];\n\nexport type BosEnv = \"development\" | \"production\" | \"staging\";\n\nexport interface ResolvedConfigMeta {\n env: BosEnv;\n resolvedAt: string;\n extendsChain: string[];\n source?: string;\n}\n\nconst ARRAY_UNION_KEYS = new Set([\"secrets\"]);\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction unionArrays(a: unknown, b: unknown): unknown[] | undefined {\n const aArr = Array.isArray(a) ? a : [];\n const bArr = Array.isArray(b) ? b : [];\n if (aArr.length === 0 && bArr.length === 0) return undefined;\n const seen = new Set<string>();\n const result: unknown[] = [];\n for (const item of [...aArr, ...bArr]) {\n if (typeof item === \"string\") {\n if (seen.has(item)) continue;\n seen.add(item);\n }\n result.push(item);\n }\n return result;\n}\n\nfunction cleanNullSentinels(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value === null || value === undefined) continue;\n if (isPlainObject(value)) {\n const cleaned = cleanNullSentinels(value);\n if (Object.keys(cleaned).length > 0) {\n out[key] = cleaned;\n }\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst bosConfigMerger = createDefu((obj: any, key: any, value: any): boolean | undefined => {\n if (obj[key] === null) return true;\n if (value === null) {\n obj[key] = null;\n return true;\n }\n if (Array.isArray(obj[key]) && Array.isArray(value)) {\n if (ARRAY_UNION_KEYS.has(key)) {\n obj[key] = unionArrays(obj[key], value);\n } else {\n obj[key] = value;\n }\n return true;\n }\n return false;\n});\n\nexport function resolveExtendsRef(\n extendsField: string | ExtendsConfig | undefined,\n env: BosEnv,\n): string | undefined {\n if (!extendsField) return undefined;\n if (typeof extendsField === \"string\") return extendsField;\n return extendsField[env] ?? extendsField.production ?? Object.values(extendsField).find(Boolean);\n}\n\nexport function mergeBosConfigWithExtends(\n parent: BosConfigInput,\n child: BosConfigInput,\n): BosConfigInput {\n const merged = bosConfigMerger(child, parent) as BosConfigInput;\n\n if (child.plugins !== undefined && isPlainObject(child.plugins)) {\n (merged as Record<string, unknown>).plugins = cleanNullSentinels(\n child.plugins as Record<string, unknown>,\n );\n }\n\n const mergedRecord = merged as Record<string, unknown>;\n\n if (isPlainObject(mergedRecord.app)) {\n for (const entryVal of Object.values(mergedRecord.app as Record<string, unknown>)) {\n if (!isPlainObject(entryVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(entryVal[secretKey])) {\n entryVal[secretKey] =\n (unionArrays(entryVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? entryVal[secretKey];\n }\n }\n }\n }\n\n if (isPlainObject(mergedRecord.plugins)) {\n for (const pluginVal of Object.values(mergedRecord.plugins as Record<string, unknown>)) {\n if (!isPlainObject(pluginVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(pluginVal[secretKey])) {\n pluginVal[secretKey] =\n (unionArrays(pluginVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? pluginVal[secretKey];\n }\n }\n }\n }\n\n return rebuildOrderedConfig(mergedRecord) as BosConfigInput;\n}\n\nexport function mergeBosConfigWithTemplate(\n local: BosConfigInput,\n template: BosConfigInput,\n): BosConfigInput {\n const merged = mergeJsonValuesPreservingLocalOrder(local, template) as BosConfigInput;\n return rebuildOrderedConfig(merged as Record<string, unknown>) as BosConfigInput;\n}\n\nfunction mergeJsonValuesPreservingLocalOrder(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValuesPreservingLocalOrder(\n local[key],\n (template as Record<string, unknown>)[key],\n );\n }\n for (const key of Object.keys(template as Record<string, unknown>)) {\n if (!(key in merged)) {\n merged[key] = (template as Record<string, unknown>)[key];\n }\n }\n return merged;\n }\n return local ?? template;\n}\n\nexport function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T {\n const ordered: Record<string, unknown> = {};\n\n for (const key of BOS_CONFIG_ORDER) {\n if (key in config) {\n ordered[key] = config[key];\n }\n }\n\n for (const key of Object.keys(config)) {\n if (!BOS_CONFIG_ORDER.includes(key as BosConfigFieldName)) {\n ordered[key] = config[key];\n }\n }\n\n return ordered as T;\n}\n\nexport { bosConfigMerger };\n"],"mappings":"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"}
|
package/dist/merge.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.d.cts","names":[],"sources":["../src/merge.ts"],"mappings":";;cAGa
|
|
1
|
+
{"version":3,"file":"merge.d.cts","names":[],"sources":["../src/merge.ts"],"mappings":";;cAGa;KAgBD;iBAiEI,kBACd,uBAAuB,2BACvB,KAAK;iBAOS,0BACd,QAAQ,gBACR,OAAO,iBACN;iBAqEa,qBAAqB,UAAU,yBAAyB,QAAQ,IAAI"}
|