{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/discovery/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAG7B,MAAM,eAAe,CAAC;AAEvB,eAAO,MAAM,iCAAiC,IAAI,CAAC;AACnD,eAAO,MAAM,mCAAmC,QAAc,CAAC;AAC/D,eAAO,MAAM,wCAAwC,KAAK,CAAC;AAC3D,eAAO,MAAM,qCAAqC,MAAM,CAAC;AACzD,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AAEpE,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,CAAC,CAAC;IACjB,eAAe,EAAE,gBAAgB,EAAE,CAAC;IACpC,cAAc,EAAE,wBAAwB,EAAE,CAAC;CAC3C;AAgBD,mEAAmE;AACnE,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,CAkClF;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAErE;AAED,mFAAmF;AACnF,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA6B9F","sourcesContent":["import { realpath } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport type { EvoPaths } from \"../paths.ts\";\nimport { readRegularFileNoFollow } from \"../secure-file.ts\";\nimport { canonicalJson } from \"../storage.ts\";\nimport {\n\ttype EvoRawFileSource,\n\ttype EvoTrustedRegistrySigner,\n\tparseEvoRawFileSource,\n\tvalidateTrustedEvoRegistrySigners,\n} from \"./registry.ts\";\n\nexport const EVO_PACK_DISCOVERY_CONFIG_VERSION = 1;\nexport const EVO_PACK_DISCOVERY_CONFIG_MAX_BYTES = 1024 * 1024;\nexport const EVO_PACK_DISCOVERY_CONFIG_MAX_REGISTRIES = 64;\nexport const EVO_PACK_DISCOVERY_CONFIG_MAX_SIGNERS = 256;\nexport const EVO_PACK_DISCOVERY_CONFIG_FILE = \"pack-discovery.json\";\n\nexport interface EvoPackDiscoveryConfig {\n\tschemaVersion: 1;\n\tregistrySources: EvoRawFileSource[];\n\ttrustedSigners: EvoTrustedRegistrySigner[];\n}\n\nfunction asRecord(value: unknown, label: string): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${label} must be an object`);\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction exactKeys(record: Record<string, unknown>, allowed: readonly string[], label: string): void {\n\tconst allowedKeys = new Set(allowed);\n\tfor (const key of Object.keys(record)) {\n\t\tif (!allowedKeys.has(key)) throw new Error(`${label} has unknown key: ${key}`);\n\t}\n}\n\n/** Parse the strict local v1 registry/trust-root configuration. */\nexport function parseEvoPackDiscoveryConfig(value: unknown): EvoPackDiscoveryConfig {\n\tconst record = asRecord(value, \"pack discovery config\");\n\texactKeys(record, [\"schemaVersion\", \"registrySources\", \"trustedSigners\"], \"pack discovery config\");\n\tif (record.schemaVersion !== EVO_PACK_DISCOVERY_CONFIG_VERSION) {\n\t\tthrow new Error(`pack discovery config schemaVersion must be ${EVO_PACK_DISCOVERY_CONFIG_VERSION}`);\n\t}\n\tif (!Array.isArray(record.registrySources) || record.registrySources.length === 0) {\n\t\tthrow new Error(\"pack discovery config registrySources must be a non-empty array\");\n\t}\n\tif (record.registrySources.length > EVO_PACK_DISCOVERY_CONFIG_MAX_REGISTRIES) {\n\t\tthrow new Error(`pack discovery config exceeds ${EVO_PACK_DISCOVERY_CONFIG_MAX_REGISTRIES} registry sources`);\n\t}\n\tconst registrySources = record.registrySources.map((source) => parseEvoRawFileSource(source));\n\tconst sourceKeys = registrySources.map((source) => canonicalJson(source));\n\tif (new Set(sourceKeys).size !== sourceKeys.length) {\n\t\tthrow new Error(\"pack discovery config registrySources must not contain duplicates\");\n\t}\n\n\tif (!Array.isArray(record.trustedSigners)) {\n\t\tthrow new Error(\"pack discovery config trustedSigners must be an array\");\n\t}\n\tif (record.trustedSigners.length > EVO_PACK_DISCOVERY_CONFIG_MAX_SIGNERS) {\n\t\tthrow new Error(`pack discovery config exceeds ${EVO_PACK_DISCOVERY_CONFIG_MAX_SIGNERS} trusted signers`);\n\t}\n\tconst trustedSigners = record.trustedSigners.map((value, index): EvoTrustedRegistrySigner => {\n\t\tconst signer = asRecord(value, `pack discovery config trustedSigners[${index}]`);\n\t\texactKeys(signer, [\"id\", \"publicKeyPem\"], `pack discovery config trustedSigners[${index}]`);\n\t\treturn {\n\t\t\tid: typeof signer.id === \"string\" ? signer.id : \"\",\n\t\t\tpublicKeyPem: typeof signer.publicKeyPem === \"string\" ? signer.publicKeyPem : \"\",\n\t\t};\n\t});\n\tvalidateTrustedEvoRegistrySigners(trustedSigners);\n\treturn { schemaVersion: EVO_PACK_DISCOVERY_CONFIG_VERSION, registrySources, trustedSigners };\n}\n\nexport function getEvoPackDiscoveryConfigPath(paths: EvoPaths): string {\n\treturn join(paths.registry, EVO_PACK_DISCOVERY_CONFIG_FILE);\n}\n\n/** Read one regular, bounded local discovery config without following symlinks. */\nexport async function readEvoPackDiscoveryConfig(path: string): Promise<EvoPackDiscoveryConfig> {\n\tlet bytes: Buffer;\n\ttry {\n\t\tconst absolutePath = resolve(path);\n\t\tconst canonicalParent = await realpath(dirname(absolutePath));\n\t\tbytes = await readRegularFileNoFollow(\n\t\t\tjoin(canonicalParent, basename(absolutePath)),\n\t\t\t`pack discovery config ${path}`,\n\t\t\tEVO_PACK_DISCOVERY_CONFIG_MAX_BYTES,\n\t\t);\n\t} catch (error) {\n\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") {\n\t\t\tthrow new Error(`pack discovery config not found: ${path}`);\n\t\t}\n\t\tthrow error;\n\t}\n\tlet text: string;\n\ttry {\n\t\ttext = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n\t} catch {\n\t\tthrow new Error(`pack discovery config is not valid UTF-8: ${path}`);\n\t}\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(text) as unknown;\n\t} catch {\n\t\tthrow new Error(`pack discovery config is not valid JSON: ${path}`);\n\t}\n\treturn parseEvoPackDiscoveryConfig(value);\n}\n"]}