{"version":3,"file":"shared.mjs","names":[],"sources":["../src/shared.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { type BosEnv, type ResolvedConfigMeta, rebuildOrderedConfig } from \"./merge\";\nimport type { BosConfig, SharedDepConfig } from \"./types\";\nimport { BosConfigSchema } from \"./types\";\n\ninterface PackageJson {\n  name?: string;\n  private?: boolean;\n  version?: string;\n  workspaces?: {\n    packages?: string[];\n    catalog?: Record<string, string>;\n  };\n  dependencies?: Record<string, string>;\n  devDependencies?: Record<string, string>;\n  scripts?: Record<string, string>;\n}\n\nexport interface SharedUiResolvedDep {\n  name: string;\n  version: string;\n  requiredVersion: string;\n  shareScope: string;\n  singleton: boolean;\n  eager: boolean;\n  strictVersion: boolean;\n}\n\nexport interface SharedUiResolved {\n  deps: Record<string, SharedUiResolvedDep>;\n  fingerprintSha256: string;\n}\n\nexport interface SharedSyncResult {\n  mode: \"catalog->bos\" | \"bos->catalog\";\n  hostMode: \"local\" | \"remote\";\n  bosConfigChanged: boolean;\n  catalogChanged: boolean;\n  generatedChanged: boolean;\n  resolved: SharedUiResolved;\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, SharedUiResolvedDep>,\n): Record<string, SharedUiResolvedDep> {\n  const keys = Object.keys(deps).sort((a, b) => a.localeCompare(b));\n  const out: Record<string, SharedUiResolvedDep> = {};\n  for (const k of keys) out[k] = deps[k]!;\n  return out;\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    // ignore\n  }\n\n  writeFileSync(filePath, nextContent);\n  return true;\n}\n\nfunction fingerprintResolved(deps: Record<string, SharedUiResolvedDep>): string {\n  const stable = stableDepsObject(deps);\n  return sha256(JSON.stringify(stable));\n}\n\nfunction getSharedUiDeps(bosConfig: BosConfig): Record<string, SharedDepConfig> {\n  const shared = bosConfig.shared ?? {};\n  const ui = shared.ui ?? {};\n  return ui;\n}\n\nexport async function syncAndGenerateSharedUi(opts: {\n  configDir: string;\n  hostMode: \"local\" | \"remote\";\n  bosConfig?: BosConfig;\n  env?: BosEnv;\n  extendsChain?: string[];\n}): Promise<SharedSyncResult> {\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-ui.json\");\n\n  let bosConfig: BosConfig;\n  if (opts.bosConfig) {\n    bosConfig = opts.bosConfig;\n  } else {\n    const raw = JSON.parse(readFileSync(bosConfigPath, \"utf-8\")) as Record<string, unknown>;\n    bosConfig = BosConfigSchema.parse(raw);\n  }\n  let pkgJson: PackageJson = {};\n  try {\n    pkgJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as PackageJson;\n  } catch {\n    // package.json might not exist\n  }\n\n  const originalBos = JSON.stringify(bosConfig);\n  const originalPkg = JSON.stringify(pkgJson);\n\n  const catalog = pkgJson?.workspaces?.catalog ?? {};\n  const sharedUi = getSharedUiDeps(bosConfig);\n\n  const mode = opts.hostMode === \"local\" ? \"catalog->bos\" : \"bos->catalog\";\n\n  if (mode === \"catalog->bos\") {\n    for (const [name, cfg] of Object.entries(sharedUi)) {\n      const dep = cfg as SharedDepConfig;\n      const version =\n        catalog[name] ?? extractSemverExact(dep.version) ?? extractSemverExact(dep.requiredVersion);\n      if (!version) continue;\n      dep.version = version;\n      dep.requiredVersion = caretRange(version);\n      dep.shareScope = dep.shareScope ?? \"default\";\n    }\n  } else {\n    for (const [name, cfg] of Object.entries(sharedUi)) {\n      const dep = cfg as SharedDepConfig;\n      const version = extractSemverExact(dep.version) ?? extractSemverExact(dep.requiredVersion);\n      if (!version) continue;\n      dep.version = version;\n      dep.requiredVersion = caretRange(version);\n      dep.shareScope = dep.shareScope ?? \"default\";\n      if (catalog[name] !== version) {\n        catalog[name] = version;\n      }\n    }\n    if (!pkgJson.workspaces) pkgJson.workspaces = { packages: [], catalog: {} };\n    pkgJson.workspaces.catalog = catalog;\n  }\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    if (mode === \"catalog->bos\") {\n      const resolvedDir = dirname(resolvedConfigPath);\n      if (!existsSync(resolvedDir)) {\n        mkdirSync(resolvedDir, { recursive: true });\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      writeFileIfChanged(resolvedConfigPath, `${JSON.stringify(resolvedOutput, null, 2)}\\n`);\n    } else {\n      writeFileIfChanged(bosConfigPath, `${JSON.stringify(bosConfig, null, 2)}\\n`);\n    }\n  }\n  if (catalogChanged) {\n    writeFileIfChanged(packageJsonPath, `${JSON.stringify(pkgJson, null, 2)}\\n`);\n  }\n\n  const resolvedDeps: Record<string, SharedUiResolvedDep> = {};\n  for (const [name, cfg] of Object.entries(getSharedUiDeps(bosConfig))) {\n    const version =\n      catalog[name] ?? extractSemverExact(cfg.version) ?? extractSemverExact(cfg.requiredVersion);\n    if (!version) continue;\n    resolvedDeps[name] = {\n      name,\n      version,\n      requiredVersion: caretRange(version),\n      shareScope: cfg.shareScope ?? \"default\",\n      singleton: cfg.singleton ?? false,\n      eager: cfg.eager ?? false,\n      strictVersion: cfg.strictVersion ?? false,\n    };\n  }\n\n  const stableResolvedDeps = stableDepsObject(resolvedDeps);\n  const resolved: SharedUiResolved = {\n    deps: stableResolvedDeps,\n    fingerprintSha256: fingerprintResolved(stableResolvedDeps),\n  };\n\n  const nextGenerated = {\n    schemaVersion: 1,\n    kind: \"everything-dev/shared-ui\",\n    generatedAt: new Date().toISOString(),\n    ui: {\n      deps: stableResolvedDeps,\n      fingerprintSha256: resolved.fingerprintSha256,\n    },\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?.ui?.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.ui.fingerprintSha256;\n\n  return {\n    mode,\n    hostMode: opts.hostMode,\n    bosConfigChanged,\n    catalogChanged,\n    generatedChanged,\n    resolved,\n  };\n}\n\nexport function loadGeneratedSharedUi(configDir: string): SharedUiResolved | null {\n  const generatedPath = join(configDir, \".bos\", \"generated\", \"shared-ui.json\");\n  try {\n    const content = JSON.parse(readFileSync(generatedPath, \"utf-8\"));\n    return content?.ui ?? null;\n  } catch {\n    return null;\n  }\n}\n"],"mappings":"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"}