{"version":3,"file":"merge.mjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import { createDefu } from \"defu\";\nimport type { BosConfigInput, ExtendsConfig } from \"./types\";\n\nexport const BOS_CONFIG_ORDER = [\n  \"extends\",\n  \"account\",\n  \"domain\",\n  \"title\",\n  \"description\",\n  \"testnet\",\n  \"staging\",\n  \"repository\",\n  \"ci\",\n  \"app\",\n  \"plugins\",\n] as const;\n\nexport type BosConfigFieldName = (typeof BOS_CONFIG_ORDER)[number];\n\nexport type BosEnv = \"development\" | \"production\" | \"staging\";\n\nexport interface ResolvedConfigMeta {\n  env: BosEnv;\n  resolvedAt: string;\n  extendsChain: string[];\n  source?: string;\n}\n\nconst ARRAY_UNION_KEYS = new Set([\"secrets\"]);\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n  return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction unionArrays(a: unknown, b: unknown): unknown[] | undefined {\n  const aArr = Array.isArray(a) ? a : [];\n  const bArr = Array.isArray(b) ? b : [];\n  if (aArr.length === 0 && bArr.length === 0) return undefined;\n  const seen = new Set<string>();\n  const result: unknown[] = [];\n  for (const item of [...aArr, ...bArr]) {\n    if (typeof item === \"string\") {\n      if (seen.has(item)) continue;\n      seen.add(item);\n    }\n    result.push(item);\n  }\n  return result;\n}\n\nfunction cleanNullSentinels(obj: Record<string, unknown>): Record<string, unknown> {\n  const out: Record<string, unknown> = {};\n  for (const [key, value] of Object.entries(obj)) {\n    if (value === null || value === undefined) continue;\n    if (isPlainObject(value)) {\n      const cleaned = cleanNullSentinels(value);\n      if (Object.keys(cleaned).length > 0) {\n        out[key] = cleaned;\n      }\n    } else {\n      out[key] = value;\n    }\n  }\n  return out;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst bosConfigMerger = createDefu((obj: any, key: any, value: any): boolean | undefined => {\n  if (obj[key] === null) return true;\n  if (value === null) {\n    obj[key] = null;\n    return true;\n  }\n  if (Array.isArray(obj[key]) && Array.isArray(value)) {\n    if (ARRAY_UNION_KEYS.has(key)) {\n      obj[key] = unionArrays(obj[key], value);\n    } else {\n      obj[key] = value;\n    }\n    return true;\n  }\n  return false;\n});\n\nexport function resolveExtendsRef(\n  extendsField: string | ExtendsConfig | undefined,\n  env: BosEnv,\n): string | undefined {\n  if (!extendsField) return undefined;\n  if (typeof extendsField === \"string\") return extendsField;\n  return extendsField[env] ?? extendsField.production ?? Object.values(extendsField).find(Boolean);\n}\n\nexport function mergeBosConfigWithExtends(\n  parent: BosConfigInput,\n  child: BosConfigInput,\n): BosConfigInput {\n  const merged = bosConfigMerger(child, parent) as BosConfigInput;\n\n  if (child.plugins !== undefined && isPlainObject(child.plugins)) {\n    (merged as Record<string, unknown>).plugins = cleanNullSentinels(\n      child.plugins as Record<string, unknown>,\n    );\n  }\n\n  const mergedRecord = merged as Record<string, unknown>;\n\n  if (isPlainObject(mergedRecord.app)) {\n    for (const entryVal of Object.values(mergedRecord.app as Record<string, unknown>)) {\n      if (!isPlainObject(entryVal)) continue;\n      for (const secretKey of ARRAY_UNION_KEYS) {\n        if (Array.isArray(entryVal[secretKey])) {\n          entryVal[secretKey] =\n            (unionArrays(entryVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n              Boolean,\n            ) ?? entryVal[secretKey];\n        }\n      }\n    }\n  }\n\n  if (isPlainObject(mergedRecord.plugins)) {\n    for (const pluginVal of Object.values(mergedRecord.plugins as Record<string, unknown>)) {\n      if (!isPlainObject(pluginVal)) continue;\n      for (const secretKey of ARRAY_UNION_KEYS) {\n        if (Array.isArray(pluginVal[secretKey])) {\n          pluginVal[secretKey] =\n            (unionArrays(pluginVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n              Boolean,\n            ) ?? pluginVal[secretKey];\n        }\n      }\n    }\n  }\n\n  return rebuildOrderedConfig(mergedRecord) as BosConfigInput;\n}\n\nexport function mergeBosConfigWithTemplate(\n  local: BosConfigInput,\n  template: BosConfigInput,\n): BosConfigInput {\n  const merged = mergeJsonValuesPreservingLocalOrder(local, template) as BosConfigInput;\n  return rebuildOrderedConfig(merged as Record<string, unknown>) as BosConfigInput;\n}\n\nfunction mergeJsonValuesPreservingLocalOrder(local: unknown, template: unknown): unknown {\n  if (isPlainObject(local) && isPlainObject(template)) {\n    const merged: Record<string, unknown> = {};\n    for (const key of Object.keys(local)) {\n      merged[key] = mergeJsonValuesPreservingLocalOrder(\n        local[key],\n        (template as Record<string, unknown>)[key],\n      );\n    }\n    for (const key of Object.keys(template as Record<string, unknown>)) {\n      if (!(key in merged)) {\n        merged[key] = (template as Record<string, unknown>)[key];\n      }\n    }\n    return merged;\n  }\n  return local ?? template;\n}\n\nexport function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T {\n  const ordered: Record<string, unknown> = {};\n\n  for (const key of BOS_CONFIG_ORDER) {\n    if (key in config) {\n      ordered[key] = config[key];\n    }\n  }\n\n  for (const key of Object.keys(config)) {\n    if (!BOS_CONFIG_ORDER.includes(key as BosConfigFieldName)) {\n      ordered[key] = config[key];\n    }\n  }\n\n  return ordered as T;\n}\n\nexport { bosConfigMerger };\n"],"mappings":"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"}