{"version":3,"file":"merge.cjs","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  \"app\",\n  \"plugins\",\n  \"shared\",\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 { plugins: _ignoredParentPlugins, ...parentWithoutPlugins } = parent;\n  const merged = bosConfigMerger(child, parentWithoutPlugins) 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  } else {\n    delete (merged as Record<string, unknown>).plugins;\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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"}