{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/builders/apiContracts/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { promisify } from 'node:util';\n\nimport {\n  canonicalContractJson,\n  DOOM_API_CONTRACT_FILE,\n  parseApiContract,\n  type DoomCompiledContracts,\n  type DoomPackageContract,\n} from '@agimon-ai/doompi-core/api-contracts';\nimport type { DoomServerBundle } from '@agimon-ai/doompi-core/server-facet';\n\nimport { compileExtensionModule, extensionModuleManifestPath } from '../../compiler';\n\nconst execute = promisify(execFile);\nexport const CONTRACT_MANIFEST_SUFFIX = '#contracts';\n\n/** Contracts have a separate entry graph so exporting never imports a server facet. */\nexport async function compileApiContracts(input: {\n  descriptor: DoomServerBundle;\n  packageRoots: ReadonlyMap<string, string>;\n  repositoryRoot: string;\n  outputDirectory: string;\n  cacheDirectory: string;\n  sharedCacheDirectory?: string;\n}): Promise<{\n  contracts: { file: string; sha256: string };\n  compilerManifests: Record<string, string>;\n  gaps: string[];\n}> {\n  fs.mkdirSync(input.outputDirectory, { recursive: true });\n  const compiled: DoomCompiledContracts = {\n    version: 1,\n    generation: input.descriptor.generation,\n    fingerprint: input.descriptor.fingerprint,\n    packages: [],\n  };\n  const compilerManifests: Record<string, string> = {};\n  const gaps: string[] = [];\n  for (const [index, facet] of input.descriptor.entries.entries()) {\n    const root = input.packageRoots.get(facet.packageName)!;\n    const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as {\n      version?: string;\n      doompiServer?: { contracts?: { entry?: string; dist?: string } };\n    };\n    const entry: DoomPackageContract = {\n      packageName: facet.packageName,\n      packageVersion: manifest.version ?? 'unknown',\n      scopes: facet.scopes,\n      owners: facet.owners,\n    };\n    compiled.packages.push(entry);\n    try {\n      const declaration = manifest.doompiServer?.contracts;\n      if (!declaration) throw new Error('No doompiServer.contracts declaration');\n      for (const value of [declaration.entry, declaration.dist]) {\n        if (typeof value !== 'string' || !value.startsWith('./') || value.includes('..') || /[\\\\%?#]/u.test(value))\n          throw new Error('Contract entry and dist must be contained package-relative files');\n        if (value === declaration.dist) {\n          const relative = path.relative(fs.realpathSync(root), fs.realpathSync(path.join(root, value)));\n          if (relative.startsWith('..') || path.isAbsolute(relative)) throw new Error('Contract entry escapes package');\n        }\n      }\n      const built = path.resolve(root, declaration.dist!);\n      const options = {\n        repositoryRoot: input.repositoryRoot,\n        sharedCacheDirectory: input.sharedCacheDirectory,\n        outputDirectory: path.join(input.outputDirectory, 'contracts', String(index)),\n        outputName: 'contract',\n      };\n      const module = await compileExtensionModule(built, input.cacheDirectory, options);\n      compilerManifests[`${facet.packageName}${CONTRACT_MANIFEST_SUFFIX}`] = extensionModuleManifestPath(\n        built,\n        input.cacheDirectory,\n        options,\n      );\n      entry.module = `./${path.relative(input.outputDirectory, module).split(path.sep).join('/')}`;\n      // A bounded separate process keeps module caches, timers and errors out of sync.\n      const { stdout } = await execute(\n        process.execPath,\n        [\n          '--input-type=module',\n          '-e',\n          'try { const m = await import(process.argv[1]); process.stdout.write(JSON.stringify(m.default, (_key, value) => { if (typeof value === \"function\" || typeof value === \"bigint\" || typeof value === \"undefined\" || (typeof value === \"number\" && !Number.isFinite(value))) throw new Error(\"Contract contains non-JSON data\"); return value; })); } catch (error) { process.stderr.write(error instanceof Error ? `${error.name}: ${error.message}` : String(error)); process.exitCode = 1; }',\n          pathToFileURL(module).href,\n        ],\n        { timeout: 10_000, maxBuffer: 16 * 1024 * 1024 },\n      );\n      entry.contract = parseApiContract(JSON.parse(stdout));\n    } catch (error) {\n      entry.problem = (\n        error !== null &&\n        typeof error === 'object' &&\n        'stderr' in error &&\n        typeof error.stderr === 'string' &&\n        error.stderr.trim()\n          ? error.stderr.trim()\n          : error instanceof Error\n            ? error.message\n            : String(error)\n      ).slice(0, 1000);\n      gaps.push(`${facet.packageName}: ${entry.problem}`);\n    }\n  }\n  const bytes = canonicalContractJson(compiled);\n  fs.writeFileSync(path.join(input.outputDirectory, DOOM_API_CONTRACT_FILE), bytes);\n  return {\n    contracts: { file: `./${DOOM_API_CONTRACT_FILE}`, sha256: crypto.createHash('sha256').update(bytes).digest('hex') },\n    compilerManifests,\n    gaps,\n  };\n}\n"],"mappings":";;;;;;;;;AAkBA,MAAM,UAAU,UAAU,QAAQ;AAClC,MAAa,2BAA2B;;AAGxC,eAAsB,oBAAoB,OAWvC;CACD,GAAG,UAAU,MAAM,iBAAiB,EAAE,WAAW,KAAK,CAAC;CACvD,MAAM,WAAkC;EACtC,SAAS;EACT,YAAY,MAAM,WAAW;EAC7B,aAAa,MAAM,WAAW;EAC9B,UAAU,CAAC;CACb;CACA,MAAM,oBAA4C,CAAC;CACnD,MAAM,OAAiB,CAAC;CACxB,KAAK,MAAM,CAAC,OAAO,UAAU,MAAM,WAAW,QAAQ,QAAQ,GAAG;EAC/D,MAAM,OAAO,MAAM,aAAa,IAAI,MAAM,WAAW;EACrD,MAAM,WAAW,KAAK,MAAM,GAAG,aAAa,KAAK,KAAK,MAAM,cAAc,GAAG,MAAM,CAAC;EAIpF,MAAM,QAA6B;GACjC,aAAa,MAAM;GACnB,gBAAgB,SAAS,WAAW;GACpC,QAAQ,MAAM;GACd,QAAQ,MAAM;EAChB;EACA,SAAS,SAAS,KAAK,KAAK;EAC5B,IAAI;GACF,MAAM,cAAc,SAAS,cAAc;GAC3C,IAAI,CAAC,aAAa,MAAM,IAAI,MAAM,uCAAuC;GACzE,KAAK,MAAM,SAAS,CAAC,YAAY,OAAO,YAAY,IAAI,GAAG;IACzD,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,WAAW,IAAI,KAAK,MAAM,SAAS,IAAI,KAAK,WAAW,KAAK,KAAK,GACvG,MAAM,IAAI,MAAM,kEAAkE;IACpF,IAAI,UAAU,YAAY,MAAM;KAC9B,MAAM,WAAW,KAAK,SAAS,GAAG,aAAa,IAAI,GAAG,GAAG,aAAa,KAAK,KAAK,MAAM,KAAK,CAAC,CAAC;KAC7F,IAAI,SAAS,WAAW,IAAI,KAAK,KAAK,WAAW,QAAQ,GAAG,MAAM,IAAI,MAAM,gCAAgC;IAC9G;GACF;GACA,MAAM,QAAQ,KAAK,QAAQ,MAAM,YAAY,IAAK;GAClD,MAAM,UAAU;IACd,gBAAgB,MAAM;IACtB,sBAAsB,MAAM;IAC5B,iBAAiB,KAAK,KAAK,MAAM,iBAAiB,aAAa,OAAO,KAAK,CAAC;IAC5E,YAAY;GACd;GACA,MAAM,SAAS,MAAM,uBAAuB,OAAO,MAAM,gBAAgB,OAAO;GAChF,kBAAkB,GAAG,MAAM,cAAc,8BAA8B,4BACrE,OACA,MAAM,gBACN,OACF;GACA,MAAM,SAAS,KAAK,KAAK,SAAS,MAAM,iBAAiB,MAAM,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;GAEzF,MAAM,EAAE,WAAW,MAAM,QACvB,QAAQ,UACR;IACE;IACA;IACA;IACA,cAAc,MAAM,CAAC,CAAC;GACxB,GACA;IAAE,SAAS;IAAQ,WAAW;GAAiB,CACjD;GACA,MAAM,WAAW,iBAAiB,KAAK,MAAM,MAAM,CAAC;EACtD,SAAS,OAAO;GACd,MAAM,WACJ,UAAU,QACV,OAAO,UAAU,YACjB,YAAY,SACZ,OAAO,MAAM,WAAW,YACxB,MAAM,OAAO,KAAK,IACd,MAAM,OAAO,KAAK,IAClB,iBAAiB,QACf,MAAM,UACN,OAAO,KAAK,EAAA,CAClB,MAAM,GAAG,GAAI;GACf,KAAK,KAAK,GAAG,MAAM,YAAY,IAAI,MAAM,SAAS;EACpD;CACF;CACA,MAAM,QAAQ,sBAAsB,QAAQ;CAC5C,GAAG,cAAc,KAAK,KAAK,MAAM,iBAAiB,sBAAsB,GAAG,KAAK;CAChF,OAAO;EACL,WAAW;GAAE,MAAM,KAAK;GAA0B,QAAQ,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;EAAE;EAClH;EACA;CACF;AACF"}