UNPKG

@beignet/core

Version:

Core framework primitives for Beignet

98 lines (90 loc) 2.71 kB
/** * PortsBuilder - Mutable builder for composing ports during app initialization * * This builder is a small composition helper for tests and custom bootstrapping * code that wants to assemble ports incrementally. */ /** * A mutable builder around a ports object. * Used during app composition and provider registration. */ export interface PortsBuilder<Ports> { /** * The current ports object being built. * This is mutated internally when extend/replace are called. */ ports: Ports; /** * Extend ports with a new key. If the key already exists, it's overwritten. * * Returns the same builder instance with an extended type: * Ports & { [K in key]: Value } */ extend<K extends string, V>( key: K, value: V, ): PortsBuilder<Ports & { [P in K]: V }>; /** * Replace an existing key. Does not change the type, but updates the runtime value. * Returns the same builder instance. */ replace<K extends keyof Ports>(key: K, value: Ports[K]): PortsBuilder<Ports>; } /** * Create a new PortsBuilder from an initial ports object. * * The builder wraps a mutable object internally and provides type-safe methods * to extend or replace ports. This is used during application composition * helpers that want to modify ports before passing them to a server. * * @example * ```ts * const initialPorts = definePorts({ db: dbAdapter }); * const builder = createPortsBuilder(initialPorts); * * // Provider extends with cache * builder.extend("cache", cacheAdapter); * * // Final ports includes both db and cache * const finalPorts = builder.ports; * ``` */ export function createPortsBuilder<Ports>( initialPorts: Ports, ): PortsBuilder<Ports> { // Keep a mutable object internally // biome-ignore lint/suspicious/noExplicitAny: internal mutable state needs any to accept arbitrary port extensions const state: { ports: any } = { ports: { ...initialPorts }, }; const builder: PortsBuilder<Ports> = { get ports() { // Could freeze in dev, but keep simple return state.ports as Ports; }, extend<K extends string, V>( key: K, value: V, ): PortsBuilder<Ports & { [P in K]: V }> { state.ports[key] = value; return builder as unknown as PortsBuilder<Ports & { [P in K]: V }>; }, replace<K extends keyof Ports>( key: K, value: Ports[K], ): PortsBuilder<Ports> { state.ports[key] = value; return builder; }, }; return builder; } /** * Extract the Ports type from a PortsBuilder * * @example * ```ts * type MyPorts = PortsOf<typeof builder>; * ``` */ export type PortsOf<PB> = PB extends PortsBuilder<infer P> ? P : never;