import { createElement } from 'react'; type Renderer = ( ui: React.ReactElement, options?: RO, ) => RR; export class Kit { static create( render: Renderer, component: React.ComponentType

, defaultProps: () => P, ) { return new Kit(render, component, defaultProps); } static withRender(render: Renderer) { return { create(component: React.ComponentType, defaultProps: () => CP) { return Kit.create(render, component, defaultProps); }, }; } private elements?: (rr: RR) => E; private fire?: (e: E) => F; private async?: (e: E) => A; protected constructor( private render: Renderer, private component: React.ComponentType

, private defaultProps: () => P, ) {} setElements(elements: (renderResult: RR) => NE) { const kit = new Kit(this.render, this.component, this.defaultProps) as Kit< RR, RO, P, NE, F, A >; kit.elements = elements; kit.fire = this.fire; kit.async = this.async; return kit; } setFire(fire: (renderResult: E) => NF) { const kit = new Kit(this.render, this.component, this.defaultProps) as Kit< RR, RO, P, E, NF, A >; kit.elements = this.elements; kit.fire = fire; kit.async = this.async; return kit; } setAsync(async: (e: E) => NA) { const kit = new Kit(this.render, this.component, this.defaultProps) as Kit< RR, RO, P, E, F, NA >; kit.elements = this.elements; kit.fire = this.fire; kit.async = async; return kit; } run(overrides: Partial

= {}, renderOptions?: RO) { const props = { ...this.defaultProps(), ...overrides }; const queries = this.render( createElement(this.component, props), renderOptions, ); const elements = this.elements?.(queries) ?? ({} as E); const fire = this.fire?.(elements) ?? ({} as F); const waitFor = this.async?.(elements) ?? ({} as A); return new RunInstance(props, queries, elements, fire, waitFor); } } class RunInstance { constructor( public props: P, public queries: Q, public elements: E, public fire: F, public waitFor: A, ) {} }