@playfast/reform-remote 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +16 -16
  2. package/dist/client.d.ts +33 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +80 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/index.d.ts +11 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +11 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/memory.d.ts +7 -0
  11. package/dist/memory.d.ts.map +1 -0
  12. package/dist/memory.js +21 -0
  13. package/dist/memory.js.map +1 -0
  14. package/dist/react.d.ts +15 -0
  15. package/dist/react.d.ts.map +1 -0
  16. package/dist/react.js +11 -0
  17. package/dist/react.js.map +1 -0
  18. package/dist/server.d.ts +16 -0
  19. package/dist/server.d.ts.map +1 -0
  20. package/dist/server.js +56 -0
  21. package/dist/server.js.map +1 -0
  22. package/dist/transport.d.ts +53 -0
  23. package/dist/transport.d.ts.map +1 -0
  24. package/dist/transport.js +171 -0
  25. package/dist/transport.js.map +1 -0
  26. package/dist/wireNode.d.ts +24 -0
  27. package/dist/wireNode.d.ts.map +1 -0
  28. package/dist/wireNode.js +80 -0
  29. package/dist/wireNode.js.map +1 -0
  30. package/dist/wireRender.d.ts +20 -0
  31. package/dist/wireRender.d.ts.map +1 -0
  32. package/dist/wireRender.js +97 -0
  33. package/dist/wireRender.js.map +1 -0
  34. package/package.json +34 -22
  35. package/src/client-keyed-slot.test.ts +19 -6
  36. package/src/client-remount.test.ts +14 -5
  37. package/src/client.ts +74 -41
  38. package/src/counterFixtures.ts +142 -0
  39. package/src/fixtures.ts +10 -247
  40. package/src/listFixtures.ts +174 -0
  41. package/src/optionalFixtures.ts +64 -0
  42. package/src/react.ts +1 -6
  43. package/src/remote-view.typecheck.ts +3 -2
  44. package/src/server.test.ts +26 -12
  45. package/src/server.ts +150 -211
  46. package/src/slottedFixtures.ts +120 -0
  47. package/src/transport.test.ts +2 -1
  48. package/src/transport.ts +88 -24
  49. package/src/wireNode.ts +153 -0
  50. package/src/wireRender.ts +235 -0
  51. package/src/connect.ts +0 -44
  52. package/src/contract.ts +0 -3
  53. package/src/remote-server.ts +0 -109
  54. package/src/transport.types.ts +0 -29
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAErD,OAAO,EAAiB,IAAI,EAAiC,MAAM,2BAA2B,CAAA;AAC9F,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EACL,cAAc,GAIf,MAAM,UAAU,CAAA;AA8BjB,MAAM,mBAAmB,GAAG,EAAE,CAAA;AAO9B,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,OAAqB,EAAiB,EAAE;IAC5D,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IACpC,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;IACtC,oFAAoF;IACpF,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IAChC,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAiB,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACtE,MAAM,IAAI,GAAG,GAAkB,EAAE;QAC/B,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACvC,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAA;YACnB,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAA;QAC7B,CAAC;QACD,oEAAoE;QACpE,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAC3B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;YAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;YAC9C,CAAC;QACH,CAAC,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,QAAQ,CACb,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;YAC9B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,MAAM,CAAC,KAAK,GAAG,KAAK,CAAA;gBACpB,KAAK,IAAI,EAAE,CAAA;YACb,CAAC;QACH,CAAC,CAAC,CACH,CACF,CACF,CAAA;QACD,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjC,OAAO,GAAG,CAAA;IACZ,CAAC,CAAA;IACD,2FAA2F;IAC3F,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAA4B,EAAE,CAAA;IACnE,MAAM,aAAa,GAAG,GAAS,EAAE;QAC/B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACpD,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAC1B,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,IAAI,CACpC,MAAM,CAAC,QAAQ,CACb,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,QAAQ,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;YAC9B,KAAK,IAAI,EAAE,CAAA;QACb,CAAC,CAAC,CACH,CACF,CACF,CAAA;QACD,QAAQ,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACrC,CAAC,CAAA;IACD,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACtC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAA;QAClC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;QAC1C,OAAO,CAAC,KAAK,GAAG,IAAI,CAAA;QACpB,aAAa,EAAE,CAAA;IACjB,CAAC,CAAA;IACD,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE;QAC1C,KAAK,MAAM,CAAC,UAAU,CACpB,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACvE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CACtC,CACF,CAAA;IACH,CAAC,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;IACjD,OAAO;QACL,KAAK;QACL,OAAO,EAAE,KAAK,IAAmB,EAAE;YACjC,GAAG,EAAE,CAAA;YACL,SAAS,EAAE,CAAA;YACX,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;YACvD,CAAC;YACD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;QACxB,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAiBD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAA2B,EAAuB,EAAE;IAC9E,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IACzB,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;IACtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAiD,CAAA;IACxE,MAAM,SAAS,GAAG,CAAC,OAAsB,EAAQ,EAAE;QACjD,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;IACnD,CAAC,CAAA;IAED,kFAAkF;IAClF,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IAChC,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAiB,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACtE,MAAM,IAAI,GAAG,GAAkB,EAAE;QAC/B,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACvC,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAA;YACnB,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAA;QAC7B,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAC3B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;YAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;YACzC,CAAC;QACH,CAAC,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,QAAQ,CACb,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;YAC9B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,MAAM,CAAC,KAAK,GAAG,KAAK,CAAA;gBACpB,KAAK,IAAI,EAAE,CAAA;YACb,CAAC;QACH,CAAC,CAAC,CACH,CACF,CACF,CAAA;QACD,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjC,OAAO,GAAG,CAAA;IACZ,CAAC,CAAA;IAED,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAA4B,EAAE,CAAA;IACnE,MAAM,aAAa,GAAG,GAAS,EAAE;QAC/B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACpD,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAC1B,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,IAAI,CACpC,MAAM,CAAC,QAAQ,CACb,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,QAAQ,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;YAC9B,KAAK,IAAI,EAAE,CAAA;QACb,CAAC,CAAC,CACH,CACF,CACF,CAAA;QACD,QAAQ,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACrC,CAAC,CAAA;IAED,qEAAqE;IACrE,MAAM,KAAK,GAAG,CAAC,KAAK,IAAmB,EAAE;QACvC,MAAM,MAAM,CAAC,MAAM,EAAE,CAAA;QACrB,OAAO,CAAC,KAAK,GAAG,IAAI,CAAA;QACpB,aAAa,EAAE,CAAA;IACjB,CAAC,CAAC,EAAE,CAAA;IAEJ,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;IAEjD,MAAM,SAAS,GAAG,CAChB,SAAwD,EACpC,EAAE;QACtB,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE;YAC1C,KAAK,MAAM,CAAC,UAAU,CACpB,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACvE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CACtC,CACF,CAAA;QACH,CAAC,CAAC,CAAA;QACF,oFAAoF;QACpF,KAAK,MAAM,CAAC,UAAU,CACpB,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAC9B,MAAM,CAAC,QAAQ,CACb,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;YAChE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACxB,CAAC,CAAC,CACH,CACF,CACF,CAAA;QACD,OAAO;YACL,MAAM,EAAE,GAAS,EAAE;gBACjB,GAAG,EAAE,CAAA;gBACL,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAC3B,CAAC;SACF,CAAA;IACH,CAAC,CAAA;IAED,OAAO;QACL,SAAS;QACT,OAAO,EAAE,KAAK,IAAmB,EAAE;YACjC,SAAS,EAAE,CAAA;YACX,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;YACvD,CAAC;YACD,OAAO,CAAC,KAAK,EAAE,CAAA;YACf,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;QACxB,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAcD,MAAM,CAAC,MAAM,OAAO,GAAG,CAA2B,OAA0B,EAAiB,EAAE;IAC7F,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IACpC,MAAM,KAAK,GAAuB,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;IAC9C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAA;IACvC,MAAM,MAAM,GAAoB;QAC9B,KAAK;QACL,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;KACjF,CAAA;IACD,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE;QAC1C,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CACpC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EACzC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,EACtE,KAAK,CAAC,UAAU,CACjB,CAAA;QACD,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IACF,OAAO;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;QAC9C,SAAS,EAAE,CAAC,QAAQ,EAAE,EAAE;YACtB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACvB,OAAO,GAAG,EAAE,CAAC,KAAK,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAC9C,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG;KACb,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { Effect, Option, type ParseResult } from 'effect';
2
+ import { type SlotFill, type Structure, type Trigger, type UiContract } from '@playfast/reform';
3
+ import { type AnyComposition, type AnySlot, type AnySlotChild, type TriggerRegistryApi, type WireNode } from '@playfast/reform/internal';
4
+ export declare const SETTLE_DRAIN = 30;
5
+ export declare const settleDrain: Effect.Effect<void>;
6
+ export declare const eventsOf: (structure: Structure<UiContract>) => Record<string, Trigger<unknown>>;
7
+ export interface Mounted {
8
+ readonly comp: AnyComposition;
9
+ readonly props: unknown;
10
+ readonly id: string;
11
+ readonly parentId: Option.Option<string>;
12
+ readonly slot: Option.Option<string>;
13
+ readonly childIndex: number;
14
+ readonly key: Option.Option<string>;
15
+ }
16
+ export declare const childComposition: (child: AnySlotChild) => AnyComposition;
17
+ export declare const isRecord: (candidate: unknown) => candidate is Record<string, unknown>;
18
+ export declare const handlesOf: (node: WireNode) => ReadonlyArray<string>;
19
+ export declare const noRegister: TriggerRegistryApi['register'];
20
+ export declare const toWireNodeFromStructure: (mounted: Mounted, structure: Structure<UiContract>, register: TriggerRegistryApi['register']) => Effect.Effect<WireNode, ParseResult.ParseError>;
21
+ export declare const isSlotFill: (candidate: unknown) => candidate is SlotFill<unknown>;
22
+ export declare const structureFills: (structure: Structure<UiContract>) => Record<string, SlotFill<unknown>>;
23
+ export declare const slotsOf: (comp: AnyComposition) => Record<string, AnySlot>;
24
+ //# sourceMappingURL=wireNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wireNode.d.ts","sourceRoot":"","sources":["../src/wireNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAE,MAAM,EAAE,KAAK,WAAW,EAAyB,MAAM,QAAQ,CAAA;AAC9F,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,kBAAkB,EAEvB,KAAK,QAAQ,EAEd,MAAM,2BAA2B,CAAA;AAElC,eAAO,MAAM,YAAY,KAAK,CAAA;AAC9B,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAwD,CAAA;AAGpG,eAAO,MAAM,QAAQ,GAAI,WAAW,SAAS,CAAC,UAAU,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAIvF,CAAA;AAEJ,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;CACpC;AAED,eAAO,MAAM,gBAAgB,GAAI,OAAO,YAAY,KAAG,cAKtD,CAAA;AAED,eAAO,MAAM,QAAQ,GAAI,WAAW,OAAO,KAAG,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACC,CAAA;AAElF,eAAO,MAAM,SAAS,GAAI,MAAM,QAAQ,KAAG,aAAa,CAAC,MAAM,CACa,CAAA;AAG5E,eAAO,MAAM,UAAU,EAAE,kBAAkB,CAAC,UAAU,CAAqB,CAAA;AAE3E,eAAO,MAAM,uBAAuB,EAAE,CACpC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,EAChC,QAAQ,EAAE,kBAAkB,CAAC,UAAU,CAAC,KACrC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,UAAU,CA6DjD,CAAA;AAGF,eAAO,MAAM,UAAU,GAAI,WAAW,OAAO,KAAG,SAAS,IAAI,QAAQ,CAAC,OAAO,CAIW,CAAA;AAExF,eAAO,MAAM,cAAc,GAAI,WAAW,SAAS,CAAC,UAAU,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAM/F,CAAA;AAEH,eAAO,MAAM,OAAO,GAAI,MAAM,cAAc,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAejE,CAAA"}
@@ -0,0 +1,80 @@
1
+ import { Array as Arr, Effect, Option, Record as Rec, Schema } from 'effect';
2
+ import { isFeatureBinding, isSlot, } from '@playfast/reform';
3
+ import {} from '@playfast/reform/internal';
4
+ export const SETTLE_DRAIN = 30;
5
+ export const settleDrain = Effect.yieldNow().pipe(Effect.repeatN(SETTLE_DRAIN));
6
+ // The erased event map's never values remain assignable to Trigger<unknown> without a cast.
7
+ export const eventsOf = (structure) => Option.match(Option.fromNullable(structure.events), {
8
+ onNone: () => ({}),
9
+ onSome: (events) => events,
10
+ });
11
+ export const childComposition = (child) => {
12
+ if (!isFeatureBinding(child)) {
13
+ return child;
14
+ }
15
+ return child.capture((binding) => binding.composition);
16
+ };
17
+ export const isRecord = (candidate) => typeof candidate === 'object' && candidate !== null && !Array.isArray(candidate);
18
+ export const handlesOf = (node) => node.props.flatMap((prop) => (prop._tag === 'Event' ? [prop.handle] : []));
19
+ // One-shot renders still emit deterministic handles but have no client registry.
20
+ export const noRegister = () => Effect.void;
21
+ export const toWireNodeFromStructure = Effect.fn('toWireNodeFromStructure')(function* (mounted, structure, register) {
22
+ const manifest = mounted.comp.manifest.ui.manifest;
23
+ const name = manifest.name;
24
+ // The manifest stores schema AST only; reconstruct an unknown-safe schema at this wire seam.
25
+ const encodedProps = yield* Option.match(Option.fromNullable(manifest.props), {
26
+ onNone: () => Effect.succeed({}),
27
+ onSome: (reflection) => Schema.encodeUnknown(Schema.make(reflection.ast))(structure.props).pipe(Effect.flatMap((encoded) => {
28
+ if (isRecord(encoded)) {
29
+ return Effect.succeed(encoded);
30
+ }
31
+ return Effect.dieMessage(`reform-remote: ${manifest.name} props did not encode to a record`);
32
+ })),
33
+ });
34
+ const dataProps = Rec.toEntries(encodedProps).map(([propName, propValue]) => ({
35
+ _tag: 'Data',
36
+ name: propName,
37
+ value: propValue,
38
+ }));
39
+ const eventSchemas = Option.fromNullable(manifest.events);
40
+ const events = eventsOf(structure);
41
+ const eventProps = yield* Effect.forEach(Rec.toEntries(events), ([eventName, trigger]) => Effect.gen(function* () {
42
+ const schema = Option.flatMap(eventSchemas, (schemas) => Rec.get(schemas, eventName));
43
+ if (Option.isNone(schema)) {
44
+ return Option.none();
45
+ }
46
+ const handle = `${mounted.id}:${eventName}`;
47
+ yield* register(handle, trigger, Schema.make(schema.value.ast));
48
+ return Option.some({
49
+ _tag: 'Event',
50
+ name: eventName,
51
+ handle,
52
+ });
53
+ })).pipe(Effect.map(Arr.getSomes));
54
+ return {
55
+ id: mounted.id,
56
+ name,
57
+ parentId: Option.getOrNull(mounted.parentId),
58
+ childIndex: mounted.childIndex,
59
+ slot: Option.getOrNull(mounted.slot),
60
+ key: Option.getOrNull(mounted.key),
61
+ props: [...dataProps, ...eventProps],
62
+ };
63
+ });
64
+ // Structure erases per-slot fill types; recover SlotFill structurally without `as`.
65
+ export const isSlotFill = (candidate) => typeof candidate === 'object' &&
66
+ candidate !== null &&
67
+ '_tag' in candidate &&
68
+ (candidate._tag === 'Each' || candidate._tag === 'One' || candidate._tag === 'Absent');
69
+ export const structureFills = (structure) => Rec.fromEntries(Rec.toEntries(structure.slots).flatMap(([slotName, fillValue]) => isSlotFill(fillValue) ? [[slotName, fillValue]] : []));
70
+ export const slotsOf = (comp) => comp.capture((exact) => {
71
+ if (!('slots' in exact.manifest)) {
72
+ return {};
73
+ }
74
+ const candidate = exact.manifest.slots;
75
+ if (!isRecord(candidate)) {
76
+ return {};
77
+ }
78
+ return Rec.fromEntries(Rec.toEntries(candidate).flatMap(([name, slotDefinition]) => isSlot(slotDefinition) ? [[name, slotDefinition]] : []));
79
+ });
80
+ //# sourceMappingURL=wireNode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wireNode.js","sourceRoot":"","sources":["../src/wireNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,GAAG,EAAE,MAAM,EAAE,MAAM,EAAoB,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC9F,OAAO,EACL,gBAAgB,EAChB,MAAM,GAKP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAQN,MAAM,2BAA2B,CAAA;AAElC,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,CAAA;AAC9B,MAAM,CAAC,MAAM,WAAW,GAAwB,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAA;AAEpG,4FAA4F;AAC5F,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAgC,EAAoC,EAAE,CAC7F,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE;IAClD,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;IAClB,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM;CAC3B,CAAC,CAAA;AAYJ,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAmB,EAAkB,EAAE;IACtE,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAiB,CAAC,OAAO,EAAkB,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;AACxF,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAkB,EAAwC,EAAE,CACnF,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;AAElF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,IAAc,EAAyB,EAAE,CACjE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAE5E,iFAAiF;AACjF,MAAM,CAAC,MAAM,UAAU,GAAmC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAA;AAE3E,MAAM,CAAC,MAAM,uBAAuB,GAImB,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CACzF,QAAQ,CAAC,EACT,OAAgB,EAChB,SAAgC,EAChC,QAAwC;IAExC,MAAM,QAAQ,GAAe,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAA;IAC9D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAA;IAE1B,6FAA6F;IAC7F,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;QAC5E,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,CACrB,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAmB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CACvF,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACzB,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACtB,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;YAChC,CAAC;YACD,OAAO,MAAM,CAAC,UAAU,CACtB,kBAAkB,QAAQ,CAAC,IAAI,mCAAmC,CACnE,CAAA;QACH,CAAC,CAAC,CACH;KACJ,CAAC,CAAA;IACF,MAAM,SAAS,GAA4B,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,GAAG,CACxE,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAY,EAAE,CAAC,CAAC;QACpC,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,SAAS;KACjB,CAAC,CACH,CAAA;IAED,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IACzD,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAA;IAClC,MAAM,UAAU,GAA4B,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAC/D,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,EACrB,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CACvB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAA;QACrF,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO,MAAM,CAAC,IAAI,EAAY,CAAA;QAChC,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,IAAI,SAAS,EAAE,CAAA;QAC3C,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAmB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QACjF,OAAO,MAAM,CAAC,IAAI,CAAW;YAC3B,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,SAAS;YACf,MAAM;SACP,CAAC,CAAA;IACJ,CAAC,CAAC,CACL,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEhC,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI;QACJ,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC5C,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;QACpC,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC;QAClC,KAAK,EAAE,CAAC,GAAG,SAAS,EAAE,GAAG,UAAU,CAAC;KACrC,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,SAAkB,EAAkC,EAAE,CAC/E,OAAO,SAAS,KAAK,QAAQ;IAC7B,SAAS,KAAK,IAAI;IAClB,MAAM,IAAI,SAAS;IACnB,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,IAAI,KAAK,KAAK,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;AAExF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,SAAgC,EAAqC,EAAE,CACpG,GAAG,CAAC,WAAW,CACb,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CACpC,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAuD,EAAE,CAC7E,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CACvD,CACF,CAAA;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAoB,EAA2B,EAAE,CACvE,IAAI,CAAC,OAAO,CAA0B,CAAC,KAAK,EAAE,EAAE;IAC9C,IAAI,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,SAAS,GAAY,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAA;IAC/C,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,OAAO,GAAG,CAAC,WAAW,CACpB,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO,CAC9B,CAAC,CAAC,IAAI,EAAE,cAAc,CAAC,EAA6C,EAAE,CACpE,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CACzD,CACF,CAAA;AACH,CAAC,CAAC,CAAA"}
@@ -0,0 +1,20 @@
1
+ import { Context, Effect, type ParseResult } from 'effect';
2
+ import { type SlotFill, type Structure, type UiContract } from '@playfast/reform';
3
+ import { type AnyComposition, type AnyScene, type AnySlot, type AnySlotChild, type TriggerRegistryApi, type WireNode, type WireTree } from '@playfast/reform/internal';
4
+ import { type Mounted } from './wireNode.js';
5
+ export interface RenderSceneToWireOptions {
6
+ readonly register: TriggerRegistryApi['register'];
7
+ }
8
+ export declare const serviceFromContext: <Services, Identifier, Service>(context: Context.Context<Services>, tag: Context.Tag<Identifier, Service>, missing: string) => Effect.Effect<Service, never, never>;
9
+ export declare const capturedSlotProvider: <Services>(context: Context.Context<Services>, slotDefinition: AnySlot) => Effect.Effect<AnySlotChild, never, never>;
10
+ export declare const renderCapturedComposition: <Services>(context: Context.Context<Services>, composition: AnyComposition, props: unknown) => Effect.Effect<Structure<UiContract>, never, never>;
11
+ export declare const enqueueStructureSlot: (parent: Mounted, slotName: string, child: AnyComposition, fill: SlotFill<unknown>) => ReadonlyArray<Mounted>;
12
+ export type SlotProvider<Services> = (slotDefinition: AnySlot) => Effect.Effect<AnySlotChild, never, Services>;
13
+ export declare const collect: <Services>(root: AnyComposition, provideSlot: SlotProvider<Services>) => Effect.Effect<Map<AnySlot, AnyComposition>, never, Services>;
14
+ export type CompositionRenderer<Services> = (composition: AnyComposition, props: unknown) => Effect.Effect<Structure<UiContract>, never, Services>;
15
+ export interface RenderLevel {
16
+ <Services>(frontier: ReadonlyArray<Mounted>, bindings: ReadonlyMap<AnySlot, AnyComposition>, register: TriggerRegistryApi['register'], render: CompositionRenderer<Services>): Effect.Effect<ReadonlyArray<WireNode>, ParseResult.ParseError, Services>;
17
+ }
18
+ export declare const renderLevel: RenderLevel;
19
+ export declare const renderSceneWith: <SlotServices, CompositionServices>(scene: AnyScene, options: RenderSceneToWireOptions | undefined, provideSlot: SlotProvider<SlotServices>, render: CompositionRenderer<CompositionServices>) => Effect.Effect<WireTree, ParseResult.ParseError, SlotServices | CompositionServices>;
20
+ //# sourceMappingURL=wireRender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wireRender.d.ts","sourceRoot":"","sources":["../src/wireRender.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ import { Context, Effect, Match, Option, Record as Rec } from 'effect';
2
+ import { Composition, isStructure, } from '@playfast/reform';
3
+ import {} from '@playfast/reform/internal';
4
+ import { childComposition, noRegister, slotsOf, structureFills, toWireNodeFromStructure, } from './wireNode.js';
5
+ export const serviceFromContext = (context, tag, missing) => Option.match(Context.getOption(context, tag), {
6
+ onNone: () => Effect.dieMessage(missing),
7
+ onSome: Effect.succeed,
8
+ });
9
+ export const capturedSlotProvider = (context, slotDefinition) => slotDefinition.capture((exact) => serviceFromContext(context, exact.provider, 'reform-remote: scene does not provide a declared slot'));
10
+ export const renderCapturedComposition = (context, composition, props) => composition.capture((exact) => Option.match(exact.parseProps(props), {
11
+ onNone: () => Effect.dieMessage(`reform-remote: invalid props for composition ${composition.manifest.name}`),
12
+ onSome: (parsed) => serviceFromContext(context, exact.tag, `reform-remote: scene does not provide composition ${composition.manifest.name}`).pipe(Effect.flatMap((service) => Composition.render(service, {
13
+ props: parsed,
14
+ tracker: { add: () => { } },
15
+ })), Effect.flatMap((frame) => {
16
+ if (isStructure(frame)) {
17
+ return Effect.succeed(frame);
18
+ }
19
+ return Effect.dieMessage(`reform-remote: composition ${composition.manifest.name} did not return a Structure`);
20
+ })),
21
+ }));
22
+ export const enqueueStructureSlot = (parent, slotName, child, fill) => Match.value(fill).pipe(Match.tag('Each', (each) => each.items.map((entry, index) => ({
23
+ comp: child,
24
+ props: entry.props,
25
+ id: `${parent.id}.${slotName}.${index}`,
26
+ parentId: Option.some(parent.id),
27
+ slot: Option.some(slotName),
28
+ childIndex: index,
29
+ key: Option.fromNullable(entry.key),
30
+ }))), Match.tag('One', (single) => [
31
+ {
32
+ comp: child,
33
+ props: single.props,
34
+ id: `${parent.id}.${slotName}.0`,
35
+ parentId: Option.some(parent.id),
36
+ slot: Option.some(slotName),
37
+ childIndex: 0,
38
+ key: Option.none(),
39
+ },
40
+ ]), Match.tag('Absent', () => []), Match.exhaustive);
41
+ export const collect = Effect.fn('collect')(function* (root, provideSlot) {
42
+ const bindings = new Map();
43
+ // Explicit type keeps the recursive reference cast-free.
44
+ const walk = Effect.fn('walk')(function* (comp) {
45
+ yield* Effect.forEach(Rec.values(slotsOf(comp)), (slotClass) => Effect.gen(function* () {
46
+ if (bindings.has(slotClass)) {
47
+ return;
48
+ }
49
+ const child = childComposition(yield* provideSlot(slotClass));
50
+ bindings.set(slotClass, child);
51
+ yield* walk(child);
52
+ }));
53
+ });
54
+ yield* walk(root);
55
+ return bindings;
56
+ });
57
+ export const renderLevel = Effect.fn('renderLevel')(function* (frontier, bindings, register, render) {
58
+ if (frontier.length === 0) {
59
+ return [];
60
+ }
61
+ const rendered = yield* Effect.forEach(frontier, (mounted) => Effect.gen(function* () {
62
+ const frame = yield* render(mounted.comp, mounted.props);
63
+ const node = yield* toWireNodeFromStructure(mounted, frame, register);
64
+ const fills = structureFills(frame);
65
+ const children = Rec.toEntries(slotsOf(mounted.comp)).flatMap(([slotName, slotClass]) => {
66
+ const child = bindings.get(slotClass);
67
+ if (child === undefined) {
68
+ return [];
69
+ }
70
+ const fill = fills[slotName];
71
+ if (fill === undefined) {
72
+ return [];
73
+ }
74
+ return enqueueStructureSlot(mounted, slotName, child, fill);
75
+ });
76
+ return { node, children };
77
+ }));
78
+ const nodes = rendered.map((entry) => entry.node);
79
+ const next = rendered.flatMap((entry) => entry.children);
80
+ const rest = yield* renderLevel(next, bindings, register, render);
81
+ return [...nodes, ...rest];
82
+ });
83
+ export const renderSceneWith = Effect.fn('renderSceneWith')(function* (scene, options, provideSlot, render) {
84
+ const register = options?.register ?? noRegister;
85
+ const bindings = yield* collect(scene.composition, provideSlot);
86
+ const root = {
87
+ comp: scene.composition,
88
+ props: {},
89
+ id: '0',
90
+ parentId: Option.none(),
91
+ slot: Option.none(),
92
+ childIndex: 0,
93
+ key: Option.none(),
94
+ };
95
+ return yield* renderLevel([root], bindings, register, render);
96
+ });
97
+ //# sourceMappingURL=wireRender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wireRender.js","sourceRoot":"","sources":["../src/wireRender.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,52 +1,64 @@
1
1
  {
2
2
  "name": "@playfast/reform-remote",
3
- "playbook": "./playbook",
4
- "version": "1.0.2",
5
- "type": "module",
3
+ "version": "1.2.0",
6
4
  "description": "Run a reform scene's logic on the server and stream its rendered UI to a thin client over any duplex transport.",
7
5
  "keywords": [
8
- "reform",
9
6
  "effect",
7
+ "react",
8
+ "reform",
10
9
  "remote",
11
10
  "server-driven-ui",
12
- "transport",
13
- "react"
11
+ "transport"
14
12
  ],
13
+ "bugs": {
14
+ "url": "https://github.com/playfast/reform/issues"
15
+ },
15
16
  "license": "MIT",
16
17
  "repository": {
17
18
  "type": "git",
18
19
  "url": "https://github.com/playfast/reform.git",
19
20
  "directory": "packages/reform-remote"
20
21
  },
21
- "bugs": {
22
- "url": "https://github.com/playfast/reform/issues"
23
- },
24
- "sideEffects": false,
25
- "exports": {
26
- "./package.json": "./package.json",
27
- ".": "./src/index.ts",
28
- "./*": "./src/*.ts"
29
- },
30
22
  "files": [
23
+ "dist",
31
24
  "src",
32
25
  "README.md"
33
26
  ],
27
+ "type": "module",
28
+ "sideEffects": false,
29
+ "main": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ "./package.json": "./package.json",
33
+ ".": {
34
+ "playfast-src": "./src/index.ts",
35
+ "types": "./dist/index.d.ts",
36
+ "default": "./dist/index.js"
37
+ },
38
+ "./*": {
39
+ "playfast-src": "./src/*.ts",
40
+ "types": "./dist/*.d.ts",
41
+ "default": "./dist/*.js"
42
+ }
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
34
47
  "scripts": {
35
48
  "clean": "rm -rf dist .tsbuildinfo",
36
49
  "check": "tsc --noEmit",
37
- "build": "tsc -p tsconfig.build.json",
50
+ "build": "rm -rf dist .tsbuildinfo && tsc -p tsconfig.build.json && bun ../../scripts/fix-esm-extensions.ts dist",
38
51
  "test": "vitest run",
39
52
  "test:watch": "vitest",
40
53
  "coverage": "vitest run --coverage",
41
54
  "lint": "oxlint src",
42
- "lint:fix": "oxlint --fix src"
55
+ "lint:fix": "oxlint --fix src",
56
+ "prepack": "bun run build"
43
57
  },
44
58
  "peerDependencies": {
59
+ "@playfast/reform": "*",
45
60
  "effect": "*",
46
- "react": "^19.0.0",
47
- "@playfast/reform": "*"
61
+ "react": "^19.0.0"
48
62
  },
49
- "publishConfig": {
50
- "access": "public"
51
- }
63
+ "playbook": "./playbook"
52
64
  }
@@ -4,13 +4,18 @@ import { createRoot } from 'react-dom/client'
4
4
  import { expect, test } from 'vitest'
5
5
  import { Schema as S } from 'effect'
6
6
  import { Composition, slot, Ui, ui } from '@playfast/reform'
7
- import type { WireNode } from '@playfast/reform'
7
+ import type { WireNode } from '@playfast/reform/internal'
8
8
  import { remoteViews, renderWireTree, type RemoteViews } from './client'
9
9
 
10
10
  class ItemUi extends ui('Item', { props: S.Struct({ label: S.String }) }) {}
11
- class ItemComp extends Composition.make('Item', { title: 'Item', ui: ItemUi }) {}
12
- class ItemSlot extends slot('Item')<typeof ItemComp>() {}
13
- class ListUi extends ui('List')<{ props: { mode: string }; slots: { Item: ItemSlot } }>() {}
11
+
12
+ class ItemComp extends Composition.make('Item', { title: 'Item', ui: ItemUi })<ItemComp>() {}
13
+
14
+ class ItemSlot extends slot('Item')<ItemSlot, typeof ItemComp>() {}
15
+ class ListUi extends ui('List', {
16
+ props: S.Struct({ mode: S.String }),
17
+ slots: { Item: ItemSlot },
18
+ }) {}
14
19
 
15
20
  const node = (over: Partial<WireNode> & Pick<WireNode, 'id' | 'name'>): WireNode => ({
16
21
  parentId: null,
@@ -77,8 +82,16 @@ test('a keyed slot renders ONE matching child per call site, not the whole list'
77
82
  createElement(
78
83
  'div',
79
84
  null,
80
- createElement('section', { 'data-section': 'one' }, createElement(slots.Item, { slotKey: 'a' })),
81
- createElement('section', { 'data-section': 'two' }, createElement(slots.Item, { slotKey: 'b' })),
85
+ createElement(
86
+ 'section',
87
+ { 'data-section': 'one' },
88
+ createElement(slots.Item, { slotKey: 'a' }),
89
+ ),
90
+ createElement(
91
+ 'section',
92
+ { 'data-section': 'two' },
93
+ createElement(slots.Item, { slotKey: 'b' }),
94
+ ),
82
95
  ),
83
96
  )
84
97
  const container = await renderWith(ListView, tree)
@@ -5,16 +5,21 @@ import { createRoot } from 'react-dom/client'
5
5
  import { expect, test } from 'vitest'
6
6
  import { Schema as S } from 'effect'
7
7
  import { Composition, slot, Ui, ui } from '@playfast/reform'
8
- import type { WireNode } from '@playfast/reform'
8
+ import type { WireNode } from '@playfast/reform/internal'
9
9
  import type { ServerMessage } from './transport'
10
10
  import { connect } from './transport'
11
11
  import { inMemoryTransportPair } from './memory'
12
12
  import { remoteViews } from './client'
13
13
 
14
14
  class ChildUi extends ui('Child', { props: S.Struct({}) }) {}
15
- class ChildComp extends Composition.make('Child', { title: 'Child', ui: ChildUi }) {}
16
- class MainSlot extends slot('Main')<typeof ChildComp>() {}
17
- class HostUi extends ui('Host')<{ props: { count: number }; slots: { Main: MainSlot } }>() {}
15
+
16
+ class ChildComp extends Composition.make('Child', { title: 'Child', ui: ChildUi })<ChildComp>() {}
17
+
18
+ class MainSlot extends slot('Main')<MainSlot, typeof ChildComp>() {}
19
+ class HostUi extends ui('Host', {
20
+ props: S.Struct({ count: S.Number }),
21
+ slots: { Main: MainSlot },
22
+ }) {}
18
23
 
19
24
  const node = (over: Partial<WireNode> & Pick<WireNode, 'id' | 'name'>): WireNode => ({
20
25
  parentId: null,
@@ -84,7 +89,11 @@ test('a background patch on the PARENT keeps its slot child mounted (local useSt
84
89
  patches: [
85
90
  {
86
91
  _tag: 'Upsert',
87
- node: node({ id: '0', name: 'Host', props: [{ _tag: 'Data', name: 'count', value: 7 }] }),
92
+ node: node({
93
+ id: '0',
94
+ name: 'Host',
95
+ props: [{ _tag: 'Data', name: 'count', value: 7 }],
96
+ }),
88
97
  },
89
98
  ],
90
99
  })
package/src/client.ts CHANGED
@@ -1,19 +1,15 @@
1
1
  import { createElement, Fragment, type ReactNode, useRef } from 'react'
2
- import { Option, Record as Rec, Schema } from 'effect'
2
+ import { Effect, Record as Rec, Schema } from 'effect'
3
+ import { type MadeView, Ui, type UiContract, UiViewContract, type ViewImpl } from '@playfast/reform'
3
4
  import {
4
- type MadeView,
5
- Ui,
6
- type UiClass,
7
- UiViewContract,
5
+ type AnyMadeView,
6
+ type AnyUi,
7
+ type UiReflection,
8
+ type WiredUiManifest,
8
9
  Wire,
9
10
  type WireNode,
10
11
  type WireTree,
11
- } from '@playfast/reform'
12
-
13
- type AnyValue = Schema.Schema.Type<Schema.Schema.Any>
14
-
15
- // Variance escape (like Schema<any, any>): heterogeneous contract-typed views can't share one element type.
16
- type AnyMadeView = MadeView<AnyValue>
12
+ } from '@playfast/reform/internal'
17
13
 
18
14
  export interface SlotPropsExternalApi {
19
15
  readonly slotKey?: string
@@ -28,40 +24,74 @@ export type RemoteView = (
28
24
  export interface RegisteredRemoteView {
29
25
  readonly name: string
30
26
  readonly view: RemoteView
31
- readonly propsSchema: Option.Option<Schema.Schema<Record<string, unknown>, unknown>>
32
27
  }
33
28
 
34
29
  type ViewRegistry = Readonly<Record<string, RegisteredRemoteView>>
35
30
 
36
- // Phantom brand of C (function never called) so remoteViews can brand cast-free with type-only C.
37
31
  const ViewSetContract: unique symbol = Symbol.for('reform-remote/view-set-contract')
38
32
 
39
33
  export type RemoteViewSet<C extends RemoteContract> = ViewRegistry & {
40
- readonly [ViewSetContract]: (contract: C) => void
34
+ readonly [ViewSetContract]: (contract: C) => C
41
35
  }
42
36
 
43
- export type RemoteContract = Readonly<Record<string, UiClass<AnyValue>>>
37
+ type RemoteUi = UiReflection<UiContract, string> & AnyUi & { readonly manifest: WiredUiManifest }
38
+ export type RemoteContract = Readonly<Record<string, RemoteUi>>
44
39
 
45
- export { remoteContract } from './contract'
40
+ export const remoteContract = <const C extends RemoteContract>(contract: C): C => contract
46
41
 
42
+ type RemoteViewFor<U extends RemoteUi> =
43
+ U extends UiReflection<infer Contract, infer Name> ? MadeView<Contract, Name, U> : never
47
44
  export type RemoteViews<C extends RemoteContract> = {
48
- readonly [K in keyof C]: MadeView<Ui.Contract<C[K]>>
45
+ readonly [K in keyof C]: RemoteViewFor<C[K]>
46
+ }
47
+
48
+ type RemoteSlots = Record<string, (slotProps?: SlotPropsExternalApi) => ReactNode>
49
+ type RemoteEvents = Record<string, (payload: unknown) => void>
50
+
51
+ function bindSlots<C extends Ui.UiContract>(slots: RemoteSlots): Parameters<ViewImpl<C>>[1]
52
+ function bindSlots(slots: RemoteSlots): unknown {
53
+ return slots
49
54
  }
50
55
 
56
+ function bindEvents<C extends Ui.UiContract>(events: RemoteEvents): Parameters<ViewImpl<C>>[2]
57
+ function bindEvents(events: RemoteEvents): unknown {
58
+ return events
59
+ }
60
+
61
+ const registerView = (view: AnyMadeView): RegisteredRemoteView =>
62
+ view.capture(
63
+ <C extends Ui.UiContract, N extends string, U extends Ui.AnyUi>(exact: MadeView<C, N, U>) => {
64
+ const contract = exact[UiViewContract]
65
+ const propsReflection = contract.manifest.props
66
+ if (propsReflection === undefined) {
67
+ return Effect.runSync(
68
+ Effect.dieMessage(
69
+ `reform-remote: view ${contract.manifest.name} needs a wire props schema`,
70
+ ),
71
+ )
72
+ }
73
+ const propsSchema = Schema.make<C['props'], unknown>(propsReflection.ast)
74
+ return {
75
+ name: contract.manifest.name,
76
+ view: (encoded, slots, events) =>
77
+ exact(
78
+ Schema.decodeUnknownSync(propsSchema)(encoded),
79
+ bindSlots<C>(slots),
80
+ bindEvents<C>(events),
81
+ ),
82
+ }
83
+ },
84
+ )
85
+
51
86
  export const remoteViews = <C extends RemoteContract>(views: RemoteViews<C>): RemoteViewSet<C> => {
52
87
  const madeViews: ReadonlyArray<AnyMadeView> = Object.values(views)
53
88
  const byName: ViewRegistry = Rec.fromEntries(
54
89
  madeViews.map((view): readonly [string, RegisteredRemoteView] => {
55
- const viewContract = view[UiViewContract]
56
- const entry: RegisteredRemoteView = {
57
- name: viewContract.manifest.name,
58
- view,
59
- propsSchema: Option.fromNullable(viewContract.manifest.props),
60
- }
90
+ const entry = registerView(view)
61
91
  return [entry.name, entry]
62
92
  }),
63
93
  )
64
- return { ...byName, [ViewSetContract]: (_contract: C): void => {} }
94
+ return { ...byName, [ViewSetContract]: (contract: C): C => contract }
65
95
  }
66
96
 
67
97
  export interface ClientConfig<C extends RemoteContract> {
@@ -81,10 +111,10 @@ interface WireNodeViewProps {
81
111
  }
82
112
 
83
113
  const WireNodeView = ({ node, tree, config }: WireNodeViewProps): ReactNode => {
84
- // latest mutates each render so stable slot closures always see current tree.
114
+ // Stable slot closures read current inputs through this ref.
85
115
  const latest = useRef({ node, tree, config })
86
116
  latest.current = { node, tree, config }
87
- // Cache slot components by name: fresh closures remount the slot subtree and reset descendant hooks.
117
+ // Fresh slot component types remount descendants and reset their hooks.
88
118
  const slotCache = useRef<Record<string, () => ReactNode>>({})
89
119
 
90
120
  const registered = config.views[node.name]
@@ -93,21 +123,22 @@ const WireNodeView = ({ node, tree, config }: WireNodeViewProps): ReactNode => {
93
123
  }
94
124
 
95
125
  const encoded: Record<string, unknown> = Rec.fromEntries(
96
- node.props.flatMap((prop) => (prop._tag === 'Data' ? [[prop.name, prop.value] as const] : [])),
97
- )
98
- const events: Record<string, (payload: unknown) => void> = Rec.fromEntries(
99
- node.props.flatMap((prop) =>
100
- prop._tag === 'Event'
101
- ? [[prop.name, (payload: unknown) => config.invoke(prop.handle, payload)] as const]
102
- : [],
126
+ node.props.flatMap(
127
+ (prop): ReadonlyArray<readonly [string, unknown]> =>
128
+ prop._tag === 'Data' ? [[prop.name, prop.value]] : [],
103
129
  ),
104
130
  )
105
- const props: Record<string, unknown> = Option.match(registered.propsSchema, {
106
- onNone: () => encoded,
107
- onSome: (schema) => Schema.decodeUnknownSync(schema)(encoded),
108
- })
131
+ const eventEntries = node.props.flatMap(
132
+ (prop): ReadonlyArray<readonly [string, (payload: unknown) => void]> => {
133
+ if (prop._tag !== 'Event') {
134
+ return []
135
+ }
136
+ return [[prop.name, (payload: unknown) => config.invoke(prop.handle, payload)]]
137
+ },
138
+ )
139
+ const events: Record<string, (payload: unknown) => void> = Rec.fromEntries(eventEntries)
109
140
 
110
- // slotKey one child by wire key (list-per-item); omit all children of the slot.
141
+ // slotKey selects one wire child; omitting it renders every child in the slot.
111
142
  const slotFor = (slotName: string): ((slotProps?: SlotPropsExternalApi) => ReactNode) => {
112
143
  const cached = slotCache.current[slotName]
113
144
  if (cached !== undefined) {
@@ -136,13 +167,15 @@ const WireNodeView = ({ node, tree, config }: WireNodeViewProps): ReactNode => {
136
167
  return stable
137
168
  }
138
169
 
139
- // Lazy by name: empty slots yield a no-op component, never undefined (invalid element type).
170
+ // Empty slots must still produce a callable component, not an invalid undefined element type.
140
171
  const slots: Record<string, (slotProps?: SlotPropsExternalApi) => ReactNode> = new Proxy(
141
172
  Object.create(null),
142
- { get: (_target, key) => (typeof key === 'string' ? slotFor(key) : undefined) },
173
+ {
174
+ get: (_target, key) => (typeof key === 'string' ? slotFor(key) : undefined),
175
+ },
143
176
  )
144
177
 
145
- return registered.view(props, slots, events)
178
+ return registered.view(encoded, slots, events)
146
179
  }
147
180
 
148
181
  export const renderWireTree = <C extends RemoteContract>(