{"version":3,"file":"config.mjs","names":[],"sources":["../../../../../src/services/scaffold/upgrade-audit/checks/config.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport { Finding, KNOWN_APP_SLOT_KEYS, KNOWN_CONFIG_KEYS, KNOWN_PLUGIN_KEYS } from \"../domain\";\nimport { fileExists, readText } from \"../fs\";\n\ninterface BosConfig {\n  readonly extends?: unknown;\n  readonly account?: unknown;\n  readonly domain?: unknown;\n  readonly app?: unknown;\n  readonly plugins?: unknown;\n  readonly [key: string]: unknown;\n}\n\nconst parseJson = (source: string): Effect.Effect<BosConfig, Error> =>\n  Effect.try({\n    try: () => JSON.parse(source) as BosConfig,\n    catch: (error) => new Error(`bos.config.json is not valid JSON: ${String(error)}`),\n  });\n\nconst checkAppSlot = (slotPath: string, slot: unknown): Finding[] => {\n  if (slot === null || typeof slot !== \"object\" || Array.isArray(slot)) {\n    return [];\n  }\n  const findings: Finding[] = [];\n  for (const key of Object.keys(slot as Record<string, unknown>)) {\n    if (!KNOWN_APP_SLOT_KEYS.includes(key as (typeof KNOWN_APP_SLOT_KEYS)[number])) {\n      findings.push(\n        new Finding({\n          check: \"config\",\n          path: slotPath,\n          severity: \"warn\",\n          message: `unknown app slot key \"${key}\" — not mappable to the v2 surface`,\n        }),\n      );\n    }\n  }\n  return findings;\n};\n\nconst checkPlugins = (plugins: unknown): Finding[] => {\n  if (plugins === null || typeof plugins !== \"object\" || Array.isArray(plugins)) {\n    return [];\n  }\n  const findings: Finding[] = [];\n  for (const [name, entry] of Object.entries(plugins as Record<string, unknown>)) {\n    if (typeof entry !== \"object\" || entry === null) continue;\n    for (const key of Object.keys(entry as Record<string, unknown>)) {\n      if (!KNOWN_PLUGIN_KEYS.includes(key as (typeof KNOWN_PLUGIN_KEYS)[number])) {\n        findings.push(\n          new Finding({\n            check: \"config\",\n            path: `plugins.${name}`,\n            severity: \"warn\",\n            message: `unknown plugin key \"${key}\" — not mappable to the v2 surface`,\n          }),\n        );\n      }\n    }\n  }\n  return findings;\n};\n\nexport const checkConfig = (projectDir: string): Effect.Effect<Finding[], Error> =>\n  Effect.gen(function* () {\n    const exists = yield* fileExists(projectDir, \"bos.config.json\");\n    if (!exists) {\n      return [\n        new Finding({\n          check: \"config\",\n          path: \"bos.config.json\",\n          severity: \"blocker\",\n          message: \"bos.config.json not found\",\n        }),\n      ];\n    }\n\n    const config = yield* readText(projectDir, \"bos.config.json\").pipe(Effect.flatMap(parseJson));\n    const findings: Finding[] = [];\n\n    for (const key of Object.keys(config)) {\n      if (!KNOWN_CONFIG_KEYS.includes(key as (typeof KNOWN_CONFIG_KEYS)[number])) {\n        findings.push(\n          new Finding({\n            check: \"config\",\n            path: \"bos.config.json\",\n            severity: \"warn\",\n            message: `unknown top-level key \"${key}\" — not mappable to the v2 surface`,\n          }),\n        );\n      }\n    }\n\n    if (typeof config.extends !== \"string\" || !config.extends.startsWith(\"bos://\")) {\n      findings.push(\n        new Finding({\n          check: \"config\",\n          path: \"bos.config.json\",\n          severity: \"blocker\",\n          message: '\"extends\" must be a bos:// ref — child apps upgrade through the extends chain',\n        }),\n      );\n    }\n    if (typeof config.account !== \"string\" || config.account.length === 0) {\n      findings.push(\n        new Finding({\n          check: \"config\",\n          path: \"bos.config.json\",\n          severity: \"blocker\",\n          message: '\"account\" is required',\n        }),\n      );\n    }\n    if (typeof config.domain !== \"string\" || config.domain.length === 0) {\n      findings.push(\n        new Finding({\n          check: \"config\",\n          path: \"bos.config.json\",\n          severity: \"blocker\",\n          message: '\"domain\" is required',\n        }),\n      );\n    }\n\n    const app = config.app;\n    if (app !== null && typeof app === \"object\" && !Array.isArray(app)) {\n      for (const [slot, value] of Object.entries(app as Record<string, unknown>)) {\n        findings.push(...checkAppSlot(`app.${slot}`, value));\n      }\n    }\n    findings.push(...checkPlugins(config.plugins));\n\n    return findings;\n  });\n"],"mappings":";;;;;AAaA,MAAM,aAAa,WACjB,OAAO,IAAI;CACT,WAAW,KAAK,MAAM,MAAM;CAC5B,QAAQ,0BAAU,IAAI,MAAM,sCAAsC,OAAO,KAAK,GAAG;AACnF,CAAC;AAEH,MAAM,gBAAgB,UAAkB,SAA6B;CACnE,IAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GACjE,OAAO,CAAC;CAEV,MAAM,WAAsB,CAAC;CAC7B,KAAK,MAAM,OAAO,OAAO,KAAK,IAA+B,GAC3D,IAAI,CAAC,oBAAoB,SAAS,GAA2C,GAC3E,SAAS,KACP,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS,yBAAyB,IAAI;CACxC,CAAC,CACH;CAGJ,OAAO;AACT;AAEA,MAAM,gBAAgB,YAAgC;CACpD,IAAI,YAAY,QAAQ,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,GAC1E,OAAO,CAAC;CAEV,MAAM,WAAsB,CAAC;CAC7B,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAkC,GAAG;EAC9E,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EACjD,KAAK,MAAM,OAAO,OAAO,KAAK,KAAgC,GAC5D,IAAI,CAAC,kBAAkB,SAAS,GAAyC,GACvE,SAAS,KACP,IAAI,QAAQ;GACV,OAAO;GACP,MAAM,WAAW;GACjB,UAAU;GACV,SAAS,uBAAuB,IAAI;EACtC,CAAC,CACH;CAGN;CACA,OAAO;AACT;AAEA,MAAa,eAAe,eAC1B,OAAO,IAAI,aAAa;CAEtB,IAAI,EAAC,OADiB,WAAW,YAAY,iBAAiB,IAE5D,OAAO,CACL,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS;CACX,CAAC,CACH;CAGF,MAAM,SAAS,OAAO,SAAS,YAAY,iBAAiB,CAAC,CAAC,KAAK,OAAO,QAAQ,SAAS,CAAC;CAC5F,MAAM,WAAsB,CAAC;CAE7B,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,IAAI,CAAC,kBAAkB,SAAS,GAAyC,GACvE,SAAS,KACP,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS,0BAA0B,IAAI;CACzC,CAAC,CACH;CAIJ,IAAI,OAAO,OAAO,YAAY,YAAY,CAAC,OAAO,QAAQ,WAAW,QAAQ,GAC3E,SAAS,KACP,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS;CACX,CAAC,CACH;CAEF,IAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,WAAW,GAClE,SAAS,KACP,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS;CACX,CAAC,CACH;CAEF,IAAI,OAAO,OAAO,WAAW,YAAY,OAAO,OAAO,WAAW,GAChE,SAAS,KACP,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS;CACX,CAAC,CACH;CAGF,MAAM,MAAM,OAAO;CACnB,IAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,GAC/D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,GAA8B,GACvE,SAAS,KAAK,GAAG,aAAa,OAAO,QAAQ,KAAK,CAAC;CAGvD,SAAS,KAAK,GAAG,aAAa,OAAO,OAAO,CAAC;CAE7C,OAAO;AACT,CAAC"}