{"version":3,"file":"resolve.mjs","names":[],"sources":["../../src/descriptor/resolve.ts"],"sourcesContent":["import { mergeBosConfigWithExtends } from \"../merge\";\nimport type { BosConfigInput } from \"../types\";\nimport type { AppDescriptor, AppRegistry, AttachmentRef, PipelineFields } from \"./schema\";\nimport { AppDescriptorSchema } from \"./schema\";\n\n/**\n * Pipeline-owned fields per app entry, keyed by app name. Authored\n * descriptors never carry deployment state — the deploy map resolves local\n * refs to URLs (today extracted verbatim from bos.config.json; tomorrow\n * written back by the deploy pipeline).\n */\nexport interface AppDeployEntry {\n  app?: Partial<Record<\"host\" | \"ui\" | \"api\" | \"auth\", PipelineFields>>;\n  plugins?: Record<string, PipelineFields>;\n}\nexport type DeployMap = Record<string, AppDeployEntry>;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nconst devRef = (ref: { path?: string; development?: string }): string | undefined =>\n  ref.path ? `local:${ref.path}` : ref.development;\n\nfunction pick<T extends object, K extends keyof T>(source: T, keys: K[]): Partial<T> {\n  const out: Partial<T> = {};\n  for (const key of keys) {\n    if (source[key] !== undefined) out[key] = source[key];\n  }\n  return out;\n}\n\nexport const ATTACHMENT_FIELDS = [\n  \"name\",\n  \"proxy\",\n  \"variables\",\n  \"secrets\",\n  \"routes\",\n  \"shared\",\n  \"connectSrc\",\n  \"dependsOn\",\n  \"version\",\n] as const;\n\n/** Fields the attachment slots carry beyond the shared attachment set. */\nexport const SLOT_INPUT_FIELDS = [\"development\", \"extends\", \"integrity\"] as const;\n\n/** Fields a config-entry may carry that map onto an attachment's slot-free set. */\nconst ENTRY_ONLY_FIELDS = [\"name\", \"extends\"] as const;\n\nconst LOCAL_PREFIX = \"local:\";\n\nfunction stripLocal(development: string | undefined): string | undefined {\n  return development?.startsWith(LOCAL_PREFIX) ? development.slice(LOCAL_PREFIX.length) : undefined;\n}\n\nfunction attachmentToEntry(attachment: AttachmentRef): Record<string, unknown> {\n  const development = devRef(attachment);\n  return {\n    ...(attachment.extends ? { extends: attachment.extends } : {}),\n    ...(development ? { development } : {}),\n    ...pick(attachment, [...ATTACHMENT_FIELDS]),\n    ...(attachment.ui\n      ? {\n          ui: {\n            ...(attachment.ui.name ? { name: attachment.ui.name } : {}),\n            ...(attachment.ui.path\n              ? { development: `local:${attachment.ui.path}` }\n              : attachment.ui.development\n                ? { development: attachment.ui.development }\n                : {}),\n            ...(attachment.ui.integrity ? { integrity: attachment.ui.integrity } : {}),\n            ...(attachment.ui.pin ? { pin: attachment.ui.pin } : {}),\n          },\n        }\n      : {}),\n  };\n}\n\n/** Descriptor → authoring-shape `BosConfigInput` (local refs only, no pipeline state). */\nexport function toConfigInput(descriptor: AppDescriptor): BosConfigInput {\n  const input: Record<string, unknown> = {\n    ...pick(descriptor, [\n      \"account\",\n      \"domain\",\n      \"title\",\n      \"description\",\n      \"repository\",\n      \"testnet\",\n      \"staging\",\n      \"ci\",\n      \"cdn\",\n      \"publish\",\n    ]),\n  };\n\n  const app: Record<string, unknown> = {};\n  if (descriptor.host) {\n    const development = devRef(descriptor.host);\n    if (development) app.host = { development, ...pick(descriptor.host, [\"secrets\"]) };\n  }\n  if (descriptor.api) {\n    const development = devRef(descriptor.api);\n    if (development) {\n      app.api = {\n        development,\n        ...pick(descriptor.api, [\"proxy\", \"variables\", \"secrets\", \"routes\", \"shared\", \"dependsOn\"]),\n      };\n    }\n  }\n  if (descriptor.ui) {\n    const development = devRef(descriptor.ui);\n    if (development) app.ui = { development };\n  }\n  if (descriptor.auth) app.auth = attachmentToEntry(descriptor.auth);\n  if (Object.keys(app).length > 0) input.app = app;\n\n  if (descriptor.plugins) {\n    const plugins: Record<string, unknown> = {};\n    for (const [key, attachment] of Object.entries(descriptor.plugins)) {\n      const entry = attachmentToEntry(attachment);\n      // today's authored shape omits a name identical to the registry key\n      if (entry.name === key) delete entry.name;\n      plugins[key] = entry;\n    }\n    input.plugins = plugins;\n  }\n\n  return input as BosConfigInput;\n}\n\nfunction applyPipeline(target: Record<string, unknown>, fields: PipelineFields): void {\n  for (const [key, value] of Object.entries(fields)) {\n    if (value !== undefined) target[key] = value;\n  }\n}\n\nfunction applyDeployMap(config: Record<string, unknown>, entry: AppDeployEntry | undefined): void {\n  if (!entry) return;\n\n  if (entry.app && isPlainObject(config.app)) {\n    for (const [slot, fields] of Object.entries(entry.app)) {\n      const target = config.app[slot];\n      if (fields && isPlainObject(target)) applyPipeline(target, fields);\n    }\n  }\n  if (entry.plugins && isPlainObject(config.plugins)) {\n    for (const [key, fields] of Object.entries(entry.plugins)) {\n      const target = config.plugins[key];\n      if (fields && isPlainObject(target)) applyPipeline(target, fields);\n    }\n  }\n}\n\n/**\n * Flatten one app against the registry: `extends` inherits the base App's\n * composition, the child's entries win (depth-1 — chains are deferred,\n * wayfinder ticket 08). The consumed `extends` key is dropped from the\n * output; the deploy map injects pipeline state.\n *\n * `extends` accepts a registry name or an imported App descriptor value —\n * an imported parent is an inlined parent: same materialization, no fetch.\n */\nexport function resolveApp(\n  name: string,\n  registry: AppRegistry,\n  deployMap: DeployMap = {},\n  seen: Set<string> = new Set(),\n): BosConfigInput {\n  if (seen.has(name)) throw new Error(`Circular extends detected: ${name}`);\n  const raw = registry[name];\n  if (!raw) throw new Error(`App \"${name}\" not found in registry`);\n  const descriptor = AppDescriptorSchema.parse(raw);\n\n  let parentInput: BosConfigInput | undefined;\n  if (descriptor.extends) {\n    if (typeof descriptor.extends === \"string\") {\n      const parentRaw = registry[descriptor.extends];\n      if (!parentRaw) {\n        throw new Error(`extends target \"${descriptor.extends}\" not found in registry`);\n      }\n      parentInput = flattenParent(parentRaw);\n    } else {\n      parentInput = flattenParent(descriptor.extends);\n    }\n  }\n\n  const childInput = toConfigInput(descriptor);\n  const merged = parentInput ? mergeBosConfigWithExtends(parentInput, childInput) : childInput;\n\n  const resolved = { ...merged } as Record<string, unknown>;\n  delete resolved.extends;\n  applyDeployMap(resolved, deployMap[name]);\n  return resolved as BosConfigInput;\n}\n\nfunction flattenParent(parentRaw: unknown): BosConfigInput {\n  const parentDescriptor = AppDescriptorSchema.parse(parentRaw);\n  if (parentDescriptor.extends) {\n    const parentName =\n      typeof parentDescriptor.extends === \"string\"\n        ? parentDescriptor.extends\n        : (parentDescriptor.extends as { name?: string }).name;\n    throw new Error(\n      `extends chains deeper than one level are not supported yet (\"${parentDescriptor.name}\" extends \"${parentName ?? \"?\"}\") — wayfinder ticket 08`,\n    );\n  }\n  return toConfigInput(parentDescriptor);\n}\n\n/**\n * Dev-time overlay — merged child-wins over a resolved config by the dev\n * runner (ports, local-vs-remote sources, seeding). Overlays are never\n * published: they exist only in `bos.dev.ts` and apply when the environment\n * is development.\n */\nexport function applyDevOverlay(\n  resolved: BosConfigInput,\n  overlay: Partial<AppDescriptor>,\n): BosConfigInput {\n  return mergeBosConfigWithExtends(resolved, toConfigInput(overlay as AppDescriptor));\n}\n\n/** Resolve every app in the registry. */\nexport function resolveApps(\n  registry: AppRegistry,\n  deployMap: DeployMap = {},\n): Record<string, BosConfigInput> {\n  return Object.fromEntries(\n    Object.keys(registry).map((name) => [name, resolveApp(name, registry, deployMap)]),\n  );\n}\n\n/**\n * The inverse of `toConfigInput` for the scaffold surface: an authoring-shape\n * config (as personalizeConfig computes it) back into the authored\n * descriptor form a child's `bos.app.ts` embeds. Driven by the same field\n * maps as `toConfigInput` so the two directions cannot drift; pipeline state\n * (production URLs, integrity) is dropped — it is deploy-map territory,\n * never authored.\n */\nexport function configInputToDescriptor(input: BosConfigInput): AppDescriptor {\n  const source = input as Record<string, unknown>;\n  const descriptor: Record<string, unknown> = {\n    name: (input.domain ?? input.account ?? \"child.app\") as string,\n  };\n  for (const field of [\n    \"extends\",\n    \"account\",\n    \"domain\",\n    \"title\",\n    \"description\",\n    \"repository\",\n    \"testnet\",\n    \"staging\",\n    \"starter\",\n    \"ci\",\n    \"cdn\",\n    \"publish\",\n  ] as const) {\n    if (source[field] !== undefined) descriptor[field] = source[field];\n  }\n\n  const app = (input.app ?? {}) as Record<string, Record<string, unknown>>;\n  const slot = (key: \"host\" | \"ui\" | \"api\"): Record<string, unknown> | undefined => {\n    const entry = app[key];\n    if (!entry?.development) return undefined;\n    const out: Record<string, unknown> = { path: stripLocal(entry.development as string) };\n    for (const field of ATTACHMENT_FIELDS) {\n      if (field === \"name\") continue;\n      if (entry[field] !== undefined) out[field] = entry[field];\n    }\n    return out;\n  };\n  for (const key of [\"host\", \"ui\", \"api\"] as const) {\n    const value = slot(key);\n    if (value) descriptor[key] = value;\n  }\n\n  const auth = app.auth;\n  if (auth?.development) descriptor.auth = entryToAttachment(\"auth\", auth);\n\n  const plugins: Record<string, unknown> = {};\n  for (const [key, raw] of Object.entries((input.plugins ?? {}) as Record<string, unknown>)) {\n    if (typeof raw === \"string\") {\n      plugins[key] = { name: key, extends: raw };\n    } else if (raw && typeof raw === \"object\") {\n      plugins[key] = entryToAttachment(key, raw as Record<string, unknown>);\n    }\n  }\n  if (Object.keys(plugins).length > 0) descriptor.plugins = plugins;\n\n  return AppDescriptorSchema.parse(descriptor);\n}\n\n/** Config-entry → authored attachment: name/extends + the shared field map. */\nfunction entryToAttachment(key: string, entry: Record<string, unknown>): Record<string, unknown> {\n  const out: Record<string, unknown> = {};\n  for (const field of ENTRY_ONLY_FIELDS) {\n    const value = entry[field];\n    if (value !== undefined) out[field] = field === \"name\" ? value : value;\n    else if (field === \"name\") out.name = key;\n  }\n  const development = entry.development as string | undefined;\n  const path = stripLocal(development);\n  if (path !== undefined) out.path = path;\n  for (const field of ATTACHMENT_FIELDS) {\n    if (field === \"name\") continue;\n    if (entry[field] !== undefined) out[field] = entry[field];\n  }\n  const ui = entry.ui as Record<string, unknown> | undefined;\n  if (ui && typeof ui === \"object\") {\n    const uiPath = stripLocal(ui.development as string | undefined);\n    out.ui = {\n      ...(typeof ui.name === \"string\" ? { name: ui.name } : {}),\n      ...(uiPath !== undefined ? { path: uiPath } : {}),\n      ...(ui.integrity !== undefined ? { integrity: ui.integrity } : {}),\n      ...(ui.pin !== undefined ? { pin: ui.pin } : {}),\n    };\n  }\n  return out;\n}\n"],"mappings":"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"}