import { RuntimeLinearBrowseCommands } from './runtime-linear-browse-commands' import { RuntimeLinearCommandBase } from './runtime-linear-command-base' import { RuntimeLinearCommands } from './runtime-linear-connection-commands' type PublicMethods = Pick export type RuntimeLinearCommandSurface = PublicMethods type LinearFacadeInstance = { linearCommands: RuntimeLinearCommands } type LinearMethodBag = Record unknown> const delegators = new WeakSet() const receiverByCommands = new WeakMap() function collectMethodNames( instancePrototype: RuntimeLinearBrowseCommands, stopAt: RuntimeLinearBrowseCommands | null ): Set { const names = new Set() let prototype: object | null = instancePrototype while (prototype && prototype !== Object.prototype && prototype !== stopAt) { for (const name of Object.getOwnPropertyNames(prototype)) { if (name !== 'constructor') { names.add(name) } } prototype = Object.getPrototypeOf(prototype) } return names } // Preserve facade overrides/spies on reentrant this calls through the chain. function overrideAwareReceiver( facade: LinearFacadeInstance, commands: LinearMethodBag, surfaceNames: ReadonlySet ): LinearMethodBag { const cached = receiverByCommands.get(commands) if (cached) { return cached } const receiver = new Proxy(commands, { get(target, property, proxyReceiver) { if (typeof property === 'string' && surfaceNames.has(property)) { const override = (facade as Record)[property] if (typeof override === 'function' && !delegators.has(override)) { return override.bind(facade) } } // oxlint-disable-next-line anti-slop/no-reflect-get -- Proxy `get` trap: raw string|symbol pass-through; the receiver stays the target on purpose. return Reflect.get(target, property, proxyReceiver) } }) receiverByCommands.set(commands, receiver) return receiver } export function installRuntimeLinearCommandSurface(target: LinearFacadeInstance): void { const names = collectMethodNames( RuntimeLinearCommands.prototype, RuntimeLinearCommandBase.prototype ) for (const name of collectMethodNames(RuntimeLinearBrowseCommands.prototype, null)) { names.add(name) } for (const name of names) { const method = { [name](this: LinearFacadeInstance, ...args: unknown[]): unknown { const commands = this.linearCommands as unknown as LinearMethodBag return commands[name].call(overrideAwareReceiver(this, commands, names), ...args) } }[name] delegators.add(method) Object.defineProperty(target, name, { configurable: true, enumerable: false, writable: true, value: method }) } }