Created
February 22, 2026 19:15
-
-
Save mikesol/c6df8b2670023508d81b0a45afe4d396 to your computer and use it in GitHub Desktop.
Spike: ExtractKinds + StrictPlugin for ctor-kind consistency enforcement
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Spike: enforce ctor-kind consistency at the Plugin type level. | |
| * | |
| * ExtractKinds<T> recursively walks a type tree and collects all CExpr | |
| * kind strings — through functions, builder chains, nested objects, | |
| * closure returns, and callback parameter types. | |
| * | |
| * StrictPlugin uses this to reject plugins where ctors produce CExpr | |
| * nodes with kinds not declared in the `kinds` map. | |
| * | |
| * Tested against all patterns in the codebase: | |
| * - Simple arity-N (num/add) | |
| * - Builder chains (error/try...catch) | |
| * - Closure-returning (st/let → {get, set}) | |
| * - Nested objects (redis: {get, set}) | |
| * | |
| * Known gap: side-effecting ctors (st/let creates st/let via push to | |
| * effects array, not via return type) — can't be seen by type system. | |
| */ | |
| import type { CExpr } from "@mvfm/core"; | |
| import type { KindSpec, Interpreter, TraitDef } from "@mvfm/core"; | |
| // ─── ExtractKinds ─────────────────────────────────────────────────── | |
| type ExtractKinds<T> = | |
| T extends CExpr<any, infer K extends string, any> | |
| ? K | |
| : T extends (...args: any[]) => infer R | |
| ? ExtractKinds<R> | |
| : T extends Record<string, unknown> | |
| ? { [P in keyof T]: ExtractKinds<T[P]> }[keyof T] | |
| : never; | |
| // ─── StrictPlugin ─────────────────────────────────────────────────── | |
| interface StrictPlugin< | |
| Name extends string = string, | |
| Ctors = any, | |
| Kinds extends Record<string, KindSpec<any, any>> = any, | |
| Traits extends Record<string, TraitDef<any, any>> = any, | |
| Lifts extends Record<string, string> = any, | |
| > { | |
| readonly name: Name; | |
| readonly ctors: [ExtractKinds<Ctors>] extends [keyof Kinds] ? Ctors : never; | |
| readonly kinds: Kinds; | |
| readonly traits: Traits; | |
| readonly lifts: Lifts; | |
| readonly defaultInterpreter?: () => Interpreter; | |
| readonly shapes?: Record<string, unknown>; | |
| } | |
| // ─── Example: passes ──────────────────────────────────────────────── | |
| // const good = { | |
| // name: "test", | |
| // ctors: { | |
| // add: <A, B>(a: A, b: B): CExpr<unknown, "test/add", [A, B]> => | |
| // makeCExpr("test/add", [a, b]), | |
| // }, | |
| // kinds: { | |
| // "test/add": { inputs: [0, 0], output: 0 } as KindSpec<[number, number], number>, | |
| // }, | |
| // traits: {}, | |
| // lifts: {}, | |
| // } satisfies StrictPlugin; | |
| // ─── Example: fails (unregistered kind) ───────────────────────────── | |
| // const bad = { | |
| // name: "test", | |
| // ctors: { | |
| // add: <A, B>(a: A, b: B): CExpr<unknown, "test/add", [A, B]> => | |
| // makeCExpr("test/add", [a, b]), | |
| // sub: <A, B>(a: A, b: B): CExpr<unknown, "test/sub", [A, B]> => | |
| // makeCExpr("test/sub", [a, b]), | |
| // }, | |
| // kinds: { | |
| // "test/add": { inputs: [0, 0], output: 0 } as KindSpec<[number, number], number>, | |
| // // "test/sub" NOT registered — `satisfies StrictPlugin` fails! | |
| // }, | |
| // traits: {}, | |
| // lifts: {}, | |
| // } satisfies StrictPlugin; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment