UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

1 lines • 17.1 kB
{"version":3,"file":"resolve.cjs","names":["AppDescriptorSchema","mergeBosConfigWithExtends"],"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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"}