everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 15.9 kB
Source Map (JSON)
{"version":3,"file":"shared-deps.cjs","names":["isPlainObject","SharedDepMapSchema","rebuildOrderedConfig"],"sources":["../src/shared-deps.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { type BosEnv, isPlainObject, type ResolvedConfigMeta, rebuildOrderedConfig } from \"./merge\";\nimport { type SharedDepConfig, SharedDepMapSchema } from \"./types\";\n\ninterface PackageJson {\n workspaces?: {\n packages?: string[];\n catalog?: Record<string, string>;\n };\n}\n\ninterface SharedDepRef {\n source: string;\n config: SharedDepConfig;\n}\n\ntype SharedDepsConfig = Record<string, unknown>;\n\ninterface NormalizedSharedDepConfig {\n version: string;\n requiredVersion: string | false;\n singleton: boolean;\n eager: boolean;\n strictVersion: boolean;\n shareScope: string;\n}\n\nexport interface ResolvedSharedDep {\n name: string;\n version: string;\n requiredVersion: string;\n shareScope: string;\n singleton: boolean;\n eager: boolean;\n strictVersion: boolean;\n sources: string[];\n}\n\nexport interface ResolvedSharedDeps {\n deps: Record<string, ResolvedSharedDep>;\n fingerprintSha256: string;\n}\n\nexport interface SharedDepsSyncResult {\n mode: \"catalog->bos\" | \"bos->catalog\";\n hostMode: \"local\" | \"remote\";\n bosConfigChanged: boolean;\n catalogChanged: boolean;\n generatedChanged: boolean;\n resolved: ResolvedSharedDeps;\n}\n\nfunction sha256(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction extractSemverExact(input: unknown): string | null {\n if (typeof input !== \"string\") return null;\n const match = input.match(/\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?/);\n return match ? match[0] : null;\n}\n\nfunction caretRange(version: string): string {\n return `^${version}`;\n}\n\nfunction stableDepsObject(\n deps: Record<string, ResolvedSharedDep>,\n): Record<string, ResolvedSharedDep> {\n const keys = Object.keys(deps).sort((a, b) => a.localeCompare(b));\n const out: Record<string, ResolvedSharedDep> = {};\n for (const key of keys) {\n out[key] = deps[key]!;\n }\n return out;\n}\n\nfunction normalizeSharedDepConfig(config: SharedDepConfig): NormalizedSharedDepConfig {\n return {\n version: config.version,\n requiredVersion: config.requiredVersion ?? false,\n singleton: config.singleton ?? false,\n eager: config.eager ?? false,\n strictVersion: config.strictVersion ?? false,\n shareScope: config.shareScope ?? \"default\",\n };\n}\n\nfunction getObject(value: unknown): Record<string, unknown> | undefined {\n return isPlainObject(value) ? value : undefined;\n}\n\nfunction getSharedDepsMap(\n value: unknown,\n source: string,\n): Record<string, SharedDepConfig> | undefined {\n if (value === undefined) return undefined;\n const parsed = SharedDepMapSchema.safeParse(value);\n if (!parsed.success) {\n throw new Error(`Invalid shared dependency map at ${source}`);\n }\n return value as Record<string, SharedDepConfig>;\n}\n\nfunction writeFileIfChanged(filePath: string, nextContent: string): boolean {\n try {\n const current = readFileSync(filePath, \"utf-8\");\n if (current === nextContent) return false;\n } catch {\n // file does not exist yet\n }\n\n writeFileSync(filePath, nextContent);\n return true;\n}\n\nfunction fingerprintResolved(deps: Record<string, ResolvedSharedDep>): string {\n return sha256(JSON.stringify(stableDepsObject(deps)));\n}\n\nfunction isSameSharedDepConfig(a: SharedDepConfig, b: SharedDepConfig): boolean {\n const left = normalizeSharedDepConfig(a);\n const right = normalizeSharedDepConfig(b);\n\n return (\n left.version === right.version &&\n left.requiredVersion === right.requiredVersion &&\n left.singleton === right.singleton &&\n left.eager === right.eager &&\n left.strictVersion === right.strictVersion &&\n left.shareScope === right.shareScope\n );\n}\n\nfunction collectSharedDepRefs(bosConfig: SharedDepsConfig): Record<string, SharedDepRef[]> {\n const refs = new Map<string, SharedDepRef[]>();\n\n const app = getObject(bosConfig.app);\n const appUi = getObject(app?.ui);\n const appApi = getObject(app?.api);\n const appAuth = getObject(app?.auth);\n const plugins = getObject(bosConfig.plugins);\n\n if (appUi && \"shared\" in appUi) {\n throw new Error(\n \"app.ui.shared is no longer supported. Move shared deps to app.api.shared, app.auth.shared, or plugins.*.shared.\",\n );\n }\n\n const append = (source: string, shared: Record<string, SharedDepConfig> | undefined) => {\n if (!shared) return;\n\n for (const [name, config] of Object.entries(shared)) {\n const existing = refs.get(name);\n if (!existing) {\n refs.set(name, [{ source, config }]);\n continue;\n }\n\n if (!isSameSharedDepConfig(existing[0]!.config, config)) {\n const previous = existing.map((ref) => ref.source).join(\", \");\n throw new Error(\n `Conflicting shared dependency \"${name}\" between ${previous} and ${source}`,\n );\n }\n\n existing.push({ source, config });\n }\n };\n\n append(\"app.api\", getSharedDepsMap(appApi?.shared, \"app.api.shared\"));\n append(\"app.auth\", getSharedDepsMap(appAuth?.shared, \"app.auth.shared\"));\n\n for (const [pluginId, plugin] of Object.entries(plugins ?? {})) {\n const pluginRecord = getObject(plugin);\n if (!pluginRecord) continue;\n\n const pluginApp = getObject(pluginRecord.app);\n const pluginUi = getObject(pluginApp?.ui);\n if (pluginUi && \"shared\" in pluginUi) {\n throw new Error(\n `app.ui.shared is no longer supported in plugins.${pluginId}. Move shared deps to app.api.shared, app.auth.shared, or plugins.*.shared.`,\n );\n }\n append(\n `plugins.${pluginId}`,\n getSharedDepsMap(pluginRecord.shared, `plugins.${pluginId}.shared`),\n );\n }\n\n return Object.fromEntries(refs);\n}\n\nexport async function syncResolvedSharedDeps(opts: {\n configDir: string;\n hostMode: \"local\" | \"remote\";\n bosConfig?: SharedDepsConfig;\n env?: BosEnv;\n extendsChain?: string[];\n}): Promise<SharedDepsSyncResult> {\n const bosConfigPath = join(opts.configDir, \"bos.config.json\");\n const resolvedConfigPath = join(opts.configDir, \".bos\", \"bos.resolved-config.json\");\n const packageJsonPath = join(opts.configDir, \"package.json\");\n const generatedPath = join(opts.configDir, \".bos\", \"generated\", \"shared-deps.json\");\n\n const bosConfig: unknown = opts.bosConfig ?? JSON.parse(readFileSync(bosConfigPath, \"utf-8\"));\n if (!isPlainObject(bosConfig)) {\n throw new Error(\"bos.config.json must be an object\");\n }\n\n const pkgJson = existsSync(packageJsonPath)\n ? (JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as PackageJson)\n : {};\n\n const originalBos = JSON.stringify(bosConfig);\n const originalPkg = JSON.stringify(pkgJson);\n\n const mode = opts.hostMode === \"local\" ? \"catalog->bos\" : \"bos->catalog\";\n const refsByName = collectSharedDepRefs(bosConfig);\n const catalog = pkgJson.workspaces?.catalog ?? {};\n\n const resolvedDeps: Record<string, ResolvedSharedDep> = {};\n\n for (const [name, refs] of Object.entries(refsByName)) {\n const first = refs[0];\n if (!first) continue;\n\n const exactFromConfig =\n extractSemverExact(first.config.version) ?? extractSemverExact(first.config.requiredVersion);\n const exactFromCatalog = extractSemverExact(catalog[name]);\n const version =\n mode === \"catalog->bos\"\n ? (exactFromCatalog ?? exactFromConfig)\n : (exactFromConfig ?? exactFromCatalog);\n\n if (!version) {\n const sources = refs.map((ref) => ref.source).join(\", \");\n throw new Error(\n `Could not resolve exact version for shared dependency \"${name}\" from ${sources}`,\n );\n }\n\n if (mode === \"catalog->bos\" && exactFromCatalog === null && exactFromConfig) {\n catalog[name] = exactFromConfig;\n }\n\n if (mode === \"bos->catalog\" && catalog[name] !== version) {\n catalog[name] = version;\n }\n\n for (const ref of refs) {\n ref.config.version = version;\n ref.config.requiredVersion = caretRange(version);\n ref.config.shareScope = ref.config.shareScope ?? \"default\";\n }\n\n resolvedDeps[name] = {\n name,\n version,\n requiredVersion: caretRange(version),\n shareScope: first.config.shareScope ?? \"default\",\n singleton: first.config.singleton ?? false,\n eager: first.config.eager ?? false,\n strictVersion: first.config.strictVersion ?? false,\n sources: refs.map((ref) => ref.source).sort((a, b) => a.localeCompare(b)),\n };\n }\n\n if (!pkgJson.workspaces) {\n pkgJson.workspaces = { packages: [], catalog: {} };\n }\n pkgJson.workspaces.catalog = catalog;\n\n const nextBos = JSON.stringify(bosConfig);\n const nextPkg = JSON.stringify(pkgJson);\n const bosConfigChanged = nextBos !== originalBos;\n const catalogChanged = nextPkg !== originalPkg;\n\n if (bosConfigChanged) {\n const resolvedDir = dirname(resolvedConfigPath);\n if (!existsSync(resolvedDir)) {\n mkdirSync(resolvedDir, { recursive: true });\n }\n\n const ordered = rebuildOrderedConfig(bosConfig);\n const meta: ResolvedConfigMeta = {\n env: opts.env ?? \"development\",\n resolvedAt: new Date().toISOString(),\n extendsChain: opts.extendsChain ?? [],\n source: \"shared-sync\",\n };\n const resolvedOutput = {\n _resolved: meta,\n ...ordered,\n };\n\n writeFileIfChanged(resolvedConfigPath, `${JSON.stringify(resolvedOutput, null, 2)}\\n`);\n }\n\n if (catalogChanged) {\n writeFileIfChanged(packageJsonPath, `${JSON.stringify(pkgJson, null, 2)}\\n`);\n }\n\n const stableResolvedDeps = stableDepsObject(resolvedDeps);\n const resolved: ResolvedSharedDeps = {\n deps: stableResolvedDeps,\n fingerprintSha256: fingerprintResolved(stableResolvedDeps),\n };\n\n const nextGenerated = {\n schemaVersion: 1,\n kind: \"everything-dev/shared-deps\",\n generatedAt: new Date().toISOString(),\n deps: stableResolvedDeps,\n fingerprintSha256: resolved.fingerprintSha256,\n inputs: {\n mode,\n hostMode: opts.hostMode,\n },\n };\n\n let prevFingerprint: string | null = null;\n try {\n const prev = JSON.parse(readFileSync(generatedPath, \"utf-8\"));\n prevFingerprint = prev?.fingerprintSha256 ?? null;\n } catch {\n // ignore\n }\n\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, `${JSON.stringify(nextGenerated, null, 2)}\\n`);\n\n const generatedChanged = prevFingerprint !== nextGenerated.fingerprintSha256;\n\n return {\n mode,\n hostMode: opts.hostMode,\n bosConfigChanged,\n catalogChanged,\n generatedChanged,\n resolved,\n };\n}\n"],"mappings":"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"}