@assistant-ui/tap 0.6.1 → 0.7.1

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 (213) hide show
  1. package/README.md +9 -6
  2. package/dist/core/ResourceFiber.d.ts +5 -5
  3. package/dist/core/ResourceFiber.d.ts.map +1 -1
  4. package/dist/core/ResourceFiber.js +26 -18
  5. package/dist/core/ResourceFiber.js.map +1 -1
  6. package/dist/core/createTapRoot.d.ts +9 -0
  7. package/dist/core/createTapRoot.d.ts.map +1 -0
  8. package/dist/core/createTapRoot.js +27 -0
  9. package/dist/core/createTapRoot.js.map +1 -0
  10. package/dist/core/helpers/commit.d.ts +1 -1
  11. package/dist/core/helpers/commit.d.ts.map +1 -1
  12. package/dist/core/helpers/commit.js +6 -1
  13. package/dist/core/helpers/commit.js.map +1 -1
  14. package/dist/core/helpers/execution-context.d.ts +4 -5
  15. package/dist/core/helpers/execution-context.d.ts.map +1 -1
  16. package/dist/core/helpers/execution-context.js +1 -7
  17. package/dist/core/helpers/execution-context.js.map +1 -1
  18. package/dist/core/helpers/root.d.ts +3 -2
  19. package/dist/core/helpers/root.d.ts.map +1 -1
  20. package/dist/core/helpers/root.js +19 -15
  21. package/dist/core/helpers/root.js.map +1 -1
  22. package/dist/core/react-dispatcher.d.ts.map +1 -1
  23. package/dist/core/react-dispatcher.js +14 -14
  24. package/dist/core/react-dispatcher.js.map +1 -1
  25. package/dist/core/resource.d.ts +2 -4
  26. package/dist/core/resource.d.ts.map +1 -1
  27. package/dist/core/resource.js +5 -10
  28. package/dist/core/resource.js.map +1 -1
  29. package/dist/core/scheduler.d.ts +2 -2
  30. package/dist/core/scheduler.d.ts.map +1 -1
  31. package/dist/core/scheduler.js +2 -2
  32. package/dist/core/scheduler.js.map +1 -1
  33. package/dist/core/types.d.ts +27 -25
  34. package/dist/core/types.d.ts.map +1 -1
  35. package/dist/hooks/useResource.d.ts +2 -2
  36. package/dist/hooks/useResource.d.ts.map +1 -1
  37. package/dist/hooks/useResource.js +14 -20
  38. package/dist/hooks/useResource.js.map +1 -1
  39. package/dist/hooks/useResources.d.ts +1 -1
  40. package/dist/hooks/useResources.d.ts.map +1 -1
  41. package/dist/hooks/useResources.js +18 -27
  42. package/dist/hooks/useResources.js.map +1 -1
  43. package/dist/hooks/useTapHost.d.ts +21 -0
  44. package/dist/hooks/useTapHost.d.ts.map +1 -0
  45. package/dist/hooks/useTapHost.js +30 -0
  46. package/dist/hooks/useTapHost.js.map +1 -0
  47. package/dist/hooks/useTapRoot.d.ts +18 -0
  48. package/dist/hooks/useTapRoot.d.ts.map +1 -0
  49. package/dist/hooks/useTapRoot.js +77 -0
  50. package/dist/hooks/useTapRoot.js.map +1 -0
  51. package/dist/hooks/utils/depsShallowEqual.d.ts.map +1 -1
  52. package/dist/hooks/utils/depsShallowEqual.js +5 -2
  53. package/dist/hooks/utils/depsShallowEqual.js.map +1 -1
  54. package/dist/hooks/utils/useCell.d.ts +2 -2
  55. package/dist/hooks/utils/useCell.d.ts.map +1 -1
  56. package/dist/hooks/utils/useCell.js.map +1 -1
  57. package/dist/hooks/utils/useDevStrictMode.d.ts +5 -0
  58. package/dist/hooks/utils/useDevStrictMode.d.ts.map +1 -0
  59. package/dist/hooks/utils/useDevStrictMode.js +25 -0
  60. package/dist/hooks/utils/useDevStrictMode.js.map +1 -0
  61. package/dist/hooks/utils/useRenderMemo.d.ts +5 -0
  62. package/dist/hooks/utils/useRenderMemo.d.ts.map +1 -0
  63. package/dist/hooks/utils/useRenderMemo.js +25 -0
  64. package/dist/hooks/utils/useRenderMemo.js.map +1 -0
  65. package/dist/hooks/utils/useResourceFiberHostUtils.d.ts +10 -0
  66. package/dist/hooks/utils/useResourceFiberHostUtils.d.ts.map +1 -0
  67. package/dist/hooks/utils/useResourceFiberHostUtils.js +46 -0
  68. package/dist/hooks/utils/useResourceFiberHostUtils.js.map +1 -0
  69. package/dist/index.d.ts +7 -4
  70. package/dist/index.js +7 -4
  71. package/dist/{hooks → react-hooks}/index.d.ts +6 -6
  72. package/dist/{hooks → react-hooks}/index.js +5 -5
  73. package/dist/{hooks → react-hooks}/use.d.ts +1 -1
  74. package/dist/{hooks → react-hooks}/use.d.ts.map +1 -1
  75. package/dist/{hooks → react-hooks}/use.js +1 -1
  76. package/dist/react-hooks/use.js.map +1 -0
  77. package/dist/{hooks → react-hooks}/useCallback.d.ts +1 -1
  78. package/dist/react-hooks/useCallback.d.ts.map +1 -0
  79. package/dist/{hooks → react-hooks}/useCallback.js +1 -1
  80. package/dist/react-hooks/useCallback.js.map +1 -0
  81. package/dist/{hooks → react-hooks}/useEffect.d.ts +1 -1
  82. package/dist/react-hooks/useEffect.d.ts.map +1 -0
  83. package/dist/react-hooks/useEffect.js +35 -0
  84. package/dist/react-hooks/useEffect.js.map +1 -0
  85. package/dist/{hooks → react-hooks}/useEffectEvent.d.ts +1 -1
  86. package/dist/react-hooks/useEffectEvent.d.ts.map +1 -0
  87. package/dist/{hooks → react-hooks}/useEffectEvent.js +2 -2
  88. package/dist/react-hooks/useEffectEvent.js.map +1 -0
  89. package/dist/{hooks → react-hooks}/useMemo.d.ts +1 -1
  90. package/dist/react-hooks/useMemo.d.ts.map +1 -0
  91. package/dist/{hooks → react-hooks}/useMemo.js +3 -3
  92. package/dist/react-hooks/useMemo.js.map +1 -0
  93. package/dist/{hooks → react-hooks}/useMemoCache.d.ts +1 -1
  94. package/dist/react-hooks/useMemoCache.d.ts.map +1 -0
  95. package/dist/{hooks → react-hooks}/useMemoCache.js +1 -1
  96. package/dist/react-hooks/useMemoCache.js.map +1 -0
  97. package/dist/react-hooks/useReducer.d.ts +9 -0
  98. package/dist/react-hooks/useReducer.d.ts.map +1 -0
  99. package/dist/react-hooks/useReducer.js +120 -0
  100. package/dist/react-hooks/useReducer.js.map +1 -0
  101. package/dist/{hooks → react-hooks}/useRef.d.ts +1 -1
  102. package/dist/react-hooks/useRef.d.ts.map +1 -0
  103. package/dist/{hooks → react-hooks}/useRef.js +1 -1
  104. package/dist/react-hooks/useRef.js.map +1 -0
  105. package/dist/{hooks → react-hooks}/useState.d.ts +1 -1
  106. package/dist/react-hooks/useState.d.ts.map +1 -0
  107. package/dist/{hooks → react-hooks}/useState.js +3 -3
  108. package/dist/react-hooks/useState.js.map +1 -0
  109. package/dist/react-shim/index.js +11 -11
  110. package/dist/react-shim/index.js.map +1 -1
  111. package/package.json +1 -1
  112. package/src/__tests__/basic/resourceHandle.test.ts +32 -22
  113. package/src/__tests__/basic/tapEffect.basic.test.ts +8 -8
  114. package/src/__tests__/basic/tapReducer.basic.test.ts +16 -14
  115. package/src/__tests__/basic/tapResources.basic.test.ts +19 -16
  116. package/src/__tests__/basic/tapState.basic.test.ts +11 -11
  117. package/src/__tests__/bench/hosts.bench.tsx +124 -0
  118. package/src/__tests__/bench/tree.bench.tsx +166 -0
  119. package/src/__tests__/errors/errors.effect-errors.test.ts +12 -13
  120. package/src/__tests__/errors/errors.render-errors.test.ts +65 -22
  121. package/src/__tests__/lifecycle/lifecycle.dependencies.test.ts +19 -19
  122. package/src/__tests__/lifecycle/lifecycle.mount-unmount.test.ts +14 -14
  123. package/src/__tests__/parity/describeParity.tsx +217 -0
  124. package/src/__tests__/parity/parity.adversarial.test.tsx +375 -0
  125. package/src/__tests__/parity/parity.basics.test.tsx +281 -0
  126. package/src/__tests__/parity/parity.divergences.test.tsx +208 -0
  127. package/src/__tests__/parity/parity.smoke.test.tsx +43 -0
  128. package/src/__tests__/react/concurrent-mode.test.tsx +10 -6
  129. package/src/__tests__/react/concurrent-pending-updates.test.tsx +351 -0
  130. package/src/__tests__/react/concurrent-render-phase.test.tsx +350 -0
  131. package/src/__tests__/react/react-shim.test.tsx +1 -1
  132. package/src/__tests__/react/useResource.test.tsx +41 -26
  133. package/src/__tests__/react/useTapHost.test.tsx +233 -0
  134. package/src/__tests__/react-dispatcher.test.ts +4 -4
  135. package/src/__tests__/rules/rules.hook-count.test.ts +21 -21
  136. package/src/__tests__/rules/rules.hook-order.test.ts +17 -17
  137. package/src/__tests__/strictmode/strictmode-parity.test.tsx +420 -0
  138. package/src/__tests__/strictmode/strictmode.test.ts +39 -209
  139. package/src/__tests__/test-utils.ts +33 -23
  140. package/src/core/ResourceFiber.ts +43 -35
  141. package/src/core/createTapRoot.ts +45 -0
  142. package/src/core/helpers/commit.ts +12 -2
  143. package/src/core/helpers/execution-context.ts +4 -13
  144. package/src/core/helpers/root.ts +24 -12
  145. package/src/core/react-dispatcher.ts +10 -9
  146. package/src/core/resource.ts +5 -20
  147. package/src/core/scheduler.ts +1 -1
  148. package/src/core/types.ts +27 -21
  149. package/src/hooks/useResource.ts +18 -27
  150. package/src/hooks/useResources.ts +18 -42
  151. package/src/hooks/useTapHost.ts +60 -0
  152. package/src/hooks/useTapRoot.ts +135 -0
  153. package/src/hooks/utils/depsShallowEqual.ts +12 -2
  154. package/src/hooks/utils/useCell.ts +2 -2
  155. package/src/hooks/utils/useDevStrictMode.ts +34 -0
  156. package/src/hooks/utils/useRenderMemo.ts +27 -0
  157. package/src/hooks/utils/useResourceFiberHostUtils.ts +61 -0
  158. package/src/index.ts +6 -3
  159. package/src/{hooks → react-hooks}/index.ts +4 -4
  160. package/src/react-hooks/useEffect.ts +58 -0
  161. package/src/{hooks → react-hooks}/useMemo.ts +1 -1
  162. package/src/react-hooks/useReducer.ts +254 -0
  163. package/src/{hooks → react-hooks}/useState.ts +2 -2
  164. package/src/react-shim/index.ts +1 -1
  165. package/dist/core/createResourceRoot.d.ts +0 -11
  166. package/dist/core/createResourceRoot.d.ts.map +0 -1
  167. package/dist/core/createResourceRoot.js +0 -31
  168. package/dist/core/createResourceRoot.js.map +0 -1
  169. package/dist/core/helpers/callResourceFn.d.ts +0 -1
  170. package/dist/core/helpers/callResourceFn.js +0 -19
  171. package/dist/core/helpers/callResourceFn.js.map +0 -1
  172. package/dist/hooks/use.js.map +0 -1
  173. package/dist/hooks/useCallback.d.ts.map +0 -1
  174. package/dist/hooks/useCallback.js.map +0 -1
  175. package/dist/hooks/useEffect.d.ts.map +0 -1
  176. package/dist/hooks/useEffect.js +0 -40
  177. package/dist/hooks/useEffect.js.map +0 -1
  178. package/dist/hooks/useEffectEvent.d.ts.map +0 -1
  179. package/dist/hooks/useEffectEvent.js.map +0 -1
  180. package/dist/hooks/useMemo.d.ts.map +0 -1
  181. package/dist/hooks/useMemo.js.map +0 -1
  182. package/dist/hooks/useMemoCache.d.ts.map +0 -1
  183. package/dist/hooks/useMemoCache.js.map +0 -1
  184. package/dist/hooks/useReducer.d.ts +0 -21
  185. package/dist/hooks/useReducer.d.ts.map +0 -1
  186. package/dist/hooks/useReducer.js +0 -81
  187. package/dist/hooks/useReducer.js.map +0 -1
  188. package/dist/hooks/useRef.d.ts.map +0 -1
  189. package/dist/hooks/useRef.js.map +0 -1
  190. package/dist/hooks/useResourceRoot.d.ts +0 -20
  191. package/dist/hooks/useResourceRoot.d.ts.map +0 -1
  192. package/dist/hooks/useResourceRoot.js +0 -77
  193. package/dist/hooks/useResourceRoot.js.map +0 -1
  194. package/dist/hooks/useState.d.ts.map +0 -1
  195. package/dist/hooks/useState.js.map +0 -1
  196. package/dist/react/hooks.d.ts +0 -25
  197. package/dist/react/hooks.d.ts.map +0 -1
  198. package/dist/react/hooks.js +0 -69
  199. package/dist/react/hooks.js.map +0 -1
  200. package/src/__tests__/strictmode/react-strictmode-behavior.test.tsx +0 -920
  201. package/src/__tests__/strictmode/react-strictmode-rerender-sources.test.tsx +0 -488
  202. package/src/__tests__/strictmode/tap-strictmode-rerender-sources.test.ts +0 -687
  203. package/src/core/createResourceRoot.ts +0 -53
  204. package/src/core/helpers/callResourceFn.ts +0 -21
  205. package/src/hooks/useEffect.ts +0 -72
  206. package/src/hooks/useReducer.ts +0 -160
  207. package/src/hooks/useResourceRoot.ts +0 -130
  208. package/src/react/hooks.ts +0 -112
  209. /package/src/{hooks → react-hooks}/use.ts +0 -0
  210. /package/src/{hooks → react-hooks}/useCallback.ts +0 -0
  211. /package/src/{hooks → react-hooks}/useEffectEvent.ts +0 -0
  212. /package/src/{hooks → react-hooks}/useMemoCache.ts +0 -0
  213. /package/src/{hooks → react-hooks}/useRef.ts +0 -0
package/README.md CHANGED
@@ -18,10 +18,10 @@ npm install @assistant-ui/tap
18
18
  ## Usage
19
19
 
20
20
  ```typescript
21
- import { resource, createResourceRoot } from "@assistant-ui/tap";
21
+ import { resource, createTapRoot, useResource } from "@assistant-ui/tap";
22
22
  import { useState, useEffect } from "react";
23
23
 
24
- const Counter = resource(function Counter({ incrementBy = 1 }: { incrementBy?: number }) {
24
+ const useCounter = ({ incrementBy = 1 }: { incrementBy?: number }) => {
25
25
  const [count, setCount] = useState(0);
26
26
 
27
27
  useEffect(() => {
@@ -32,10 +32,13 @@ const Counter = resource(function Counter({ incrementBy = 1 }: { incrementBy?: n
32
32
  count,
33
33
  increment: () => setCount((c) => c + incrementBy),
34
34
  };
35
- });
35
+ };
36
+
37
+ const Counter = resource(useCounter);
36
38
 
37
- const root = createResourceRoot();
38
- const counter = root.render(Counter({ incrementBy: 2 }));
39
+ const counter = createTapRoot(function CounterRoot() {
40
+ return useResource(Counter({ incrementBy: 2 }));
41
+ });
39
42
 
40
43
  const unsubscribe = counter.subscribe(() => {
41
44
  console.log("counter updated:", counter.getValue().count);
@@ -57,7 +60,7 @@ function CounterButton() {
57
60
 
58
61
  ## Hooks
59
62
 
60
- Inside a resource you use React's hooks (`useState`, `useEffect`, `useMemo`, `useCallback`, `useRef`, `use`, ...) imported from `"react"`. tap adds `useResource` / `useResources` / `useResourceRoot` for composition and `createResourceContext` / `withContextProvider` for context.
63
+ Inside a resource you use React's hooks (`useState`, `useEffect`, `useMemo`, `useCallback`, `useRef`, `use`, ...) imported from `"react"`. tap adds `useResource` / `useResources` / `useTapRoot` for composition and `createResourceContext` / `withContextProvider` for context.
61
64
 
62
65
  Full API reference at [assistant-ui.com/tap/docs](https://www.assistant-ui.com/tap/docs).
63
66
 
@@ -1,10 +1,10 @@
1
- import { RenderResult, Resource, ResourceFiber, ResourceFiberRoot } from "./types.js";
1
+ import { RenderResult, ResourceFiber, ResourceFiberRoot } from "./types.js";
2
2
 
3
3
  //#region src/core/ResourceFiber.d.ts
4
- declare function createResourceFiber<R, P>(type: Resource<R, P>, root: ResourceFiberRoot, markDirty?: (() => void) | undefined, strictMode?: "root" | "child" | null): ResourceFiber<R, P>;
5
- declare function unmountResourceFiber<R, P>(fiber: ResourceFiber<R, P>): void;
6
- declare function renderResourceFiber<R, P>(fiber: ResourceFiber<R, P>, props: P): RenderResult;
7
- declare function commitResourceFiber<R, P>(fiber: ResourceFiber<R, P>, result: RenderResult): void;
4
+ declare function createResourceFiber<R, A extends readonly unknown[]>(hook: (...args: A) => R, root: ResourceFiberRoot, markDirty: (() => void) | undefined, strictMode: "root" | "child" | null): ResourceFiber<R, A>;
5
+ declare function unmountResourceFiber<R, A extends readonly unknown[]>(fiber: ResourceFiber<R, A>): void;
6
+ declare function renderResourceFiber<R, A extends readonly unknown[]>(fiber: ResourceFiber<R, A>, args: Readonly<A>): RenderResult;
7
+ declare function commitResourceFiber<R, A extends readonly unknown[]>(fiber: ResourceFiber<R, A>, result: RenderResult): void;
8
8
  //#endregion
9
9
  export { commitResourceFiber, createResourceFiber, renderResourceFiber, unmountResourceFiber };
10
10
  //# sourceMappingURL=ResourceFiber.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceFiber.d.ts","names":[],"sources":["../../src/core/ResourceFiber.ts"],"mappings":";;;iBAegB,mBAAA,OACd,IAAA,EAAM,QAAA,CAAS,CAAA,EAAG,CAAA,GAClB,IAAA,EAAM,iBAAA,EACN,SAAA,6BACA,UAAA,6BACC,aAAA,CAAc,CAAA,EAAG,CAAA;AAAA,iBAeJ,oBAAA,OAA2B,KAAA,EAAO,aAAA,CAAc,CAAA,EAAG,CAAA;AAAA,iBAQnD,mBAAA,OACd,KAAA,EAAO,aAAA,CAAc,CAAA,EAAG,CAAA,GACxB,KAAA,EAAO,CAAA,GACN,YAAA;AAAA,iBAqBa,mBAAA,OACd,KAAA,EAAO,aAAA,CAAc,CAAA,EAAG,CAAA,GACxB,MAAA,EAAQ,YAAA"}
1
+ {"version":3,"file":"ResourceFiber.d.ts","names":[],"sources":["../../src/core/ResourceFiber.ts"],"mappings":";;;iBAMgB,mBAAA,kCACd,IAAA,MAAU,IAAA,EAAM,CAAA,KAAM,CAAA,EACtB,IAAA,EAAM,iBAAA,EACN,SAAA,4BACA,UAAA,4BACC,aAAA,CAAc,CAAA,EAAG,CAAA;AAAA,iBAgBJ,oBAAA,kCACd,KAAA,EAAO,aAAA,CAAc,CAAA,EAAG,CAAA;AAAA,iBASV,mBAAA,kCACd,KAAA,EAAO,aAAA,CAAc,CAAA,EAAG,CAAA,GACxB,IAAA,EAAM,QAAA,CAAS,CAAA,IACd,YAAA;AAAA,iBAmCa,mBAAA,kCACd,KAAA,EAAO,aAAA,CAAc,CAAA,EAAG,CAAA,GACxB,MAAA,EAAQ,YAAA"}
@@ -1,16 +1,16 @@
1
- import { callResourceFn } from "./helpers/callResourceFn.js";
2
1
  import { cleanupAllEffects, commitAllEffects } from "./helpers/commit.js";
2
+ import { withResourceFiber } from "./helpers/execution-context.js";
3
3
  import { isDevelopment } from "./helpers/env.js";
4
- import { getDevStrictMode, withResourceFiber } from "./helpers/execution-context.js";
5
4
  import { withReactDispatcher } from "./react-dispatcher.js";
6
5
  //#region src/core/ResourceFiber.ts
7
- function createResourceFiber(type, root, markDirty = void 0, strictMode = getDevStrictMode(false)) {
6
+ function createResourceFiber(hook, root, markDirty = void 0, strictMode) {
8
7
  return {
9
- type,
8
+ hook,
10
9
  root,
11
10
  markDirty,
12
11
  devStrictMode: strictMode,
13
12
  cells: [],
13
+ renderPendingCells: null,
14
14
  currentIndex: 0,
15
15
  renderContext: void 0,
16
16
  isFirstRender: true,
@@ -23,20 +23,28 @@ function unmountResourceFiber(fiber) {
23
23
  fiber.isMounted = false;
24
24
  cleanupAllEffects(fiber);
25
25
  }
26
- function renderResourceFiber(fiber, props) {
27
- const result = {
28
- effectTasks: [],
29
- props,
30
- output: void 0
31
- };
32
- withResourceFiber(fiber, () => {
33
- fiber.renderContext = result;
34
- try {
35
- result.output = withReactDispatcher(() => callResourceFn(fiber.type, props));
36
- } finally {
37
- fiber.renderContext = void 0;
38
- }
39
- });
26
+ function renderResourceFiber(fiber, args) {
27
+ if (fiber.renderPendingCells !== null) {
28
+ for (const cell of fiber.renderPendingCells) cell.renderQueue = null;
29
+ fiber.renderPendingCells.clear();
30
+ }
31
+ let passes = 0;
32
+ let result;
33
+ do {
34
+ if (++passes > 25) throw new Error("Too many re-renders. tap limits the number of renders to prevent an infinite loop.");
35
+ result = {
36
+ effectTasks: [],
37
+ value: void 0
38
+ };
39
+ withResourceFiber(fiber, () => {
40
+ fiber.renderContext = result;
41
+ try {
42
+ result.value = withReactDispatcher(() => fiber.hook(...args));
43
+ } finally {
44
+ fiber.renderContext = void 0;
45
+ }
46
+ });
47
+ } while ((fiber.renderPendingCells?.size ?? 0) > 0);
40
48
  return result;
41
49
  }
42
50
  function commitResourceFiber(fiber, result) {
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceFiber.js","names":[],"sources":["../../src/core/ResourceFiber.ts"],"sourcesContent":["import type {\n ResourceFiber,\n RenderResult,\n Resource,\n ResourceFiberRoot,\n} from \"./types\";\nimport { commitAllEffects, cleanupAllEffects } from \"./helpers/commit\";\nimport {\n getDevStrictMode,\n withResourceFiber,\n} from \"./helpers/execution-context\";\nimport { callResourceFn } from \"./helpers/callResourceFn\";\nimport { withReactDispatcher } from \"./react-dispatcher\";\nimport { isDevelopment } from \"./helpers/env\";\n\nexport function createResourceFiber<R, P>(\n type: Resource<R, P>,\n root: ResourceFiberRoot,\n markDirty: (() => void) | undefined = undefined,\n strictMode: \"root\" | \"child\" | null = getDevStrictMode(false),\n): ResourceFiber<R, P> {\n return {\n type,\n root,\n markDirty,\n devStrictMode: strictMode,\n cells: [],\n currentIndex: 0,\n renderContext: undefined,\n isFirstRender: true,\n isMounted: false,\n isNeverMounted: true,\n };\n}\n\nexport function unmountResourceFiber<R, P>(fiber: ResourceFiber<R, P>): void {\n if (!fiber.isMounted)\n throw new Error(\"Tried to unmount a fiber that is already unmounted\");\n\n fiber.isMounted = false;\n cleanupAllEffects(fiber);\n}\n\nexport function renderResourceFiber<R, P>(\n fiber: ResourceFiber<R, P>,\n props: P,\n): RenderResult {\n const result = {\n effectTasks: [],\n props,\n output: undefined as R | undefined,\n };\n\n withResourceFiber(fiber, () => {\n fiber.renderContext = result;\n try {\n result.output = withReactDispatcher(() =>\n callResourceFn(fiber.type, props),\n );\n } finally {\n fiber.renderContext = undefined;\n }\n });\n\n return result;\n}\n\nexport function commitResourceFiber<R, P>(\n fiber: ResourceFiber<R, P>,\n result: RenderResult,\n): void {\n fiber.isMounted = true;\n\n if (isDevelopment && fiber.isNeverMounted && fiber.devStrictMode === \"root\") {\n fiber.isNeverMounted = false;\n\n commitAllEffects(result);\n cleanupAllEffects(fiber);\n }\n\n fiber.isNeverMounted = false;\n commitAllEffects(result);\n}\n"],"mappings":";;;;;;AAeA,SAAgB,oBACd,MACA,MACA,YAAsC,KAAA,GACtC,aAAsC,iBAAiB,KAAK,GACvC;CACrB,OAAO;EACL;EACA;EACA;EACA,eAAe;EACf,OAAO,CAAC;EACR,cAAc;EACd,eAAe,KAAA;EACf,eAAe;EACf,WAAW;EACX,gBAAgB;CAClB;AACF;AAEA,SAAgB,qBAA2B,OAAkC;CAC3E,IAAI,CAAC,MAAM,WACT,MAAM,IAAI,MAAM,oDAAoD;CAEtE,MAAM,YAAY;CAClB,kBAAkB,KAAK;AACzB;AAEA,SAAgB,oBACd,OACA,OACc;CACd,MAAM,SAAS;EACb,aAAa,CAAC;EACd;EACA,QAAQ,KAAA;CACV;CAEA,kBAAkB,aAAa;EAC7B,MAAM,gBAAgB;EACtB,IAAI;GACF,OAAO,SAAS,0BACd,eAAe,MAAM,MAAM,KAAK,CAClC;EACF,UAAU;GACR,MAAM,gBAAgB,KAAA;EACxB;CACF,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,oBACd,OACA,QACM;CACN,MAAM,YAAY;CAElB,IAAI,iBAAiB,MAAM,kBAAkB,MAAM,kBAAkB,QAAQ;EAC3E,MAAM,iBAAiB;EAEvB,iBAAiB,MAAM;EACvB,kBAAkB,KAAK;CACzB;CAEA,MAAM,iBAAiB;CACvB,iBAAiB,MAAM;AACzB"}
1
+ {"version":3,"file":"ResourceFiber.js","names":[],"sources":["../../src/core/ResourceFiber.ts"],"sourcesContent":["import type { ResourceFiber, RenderResult, ResourceFiberRoot } from \"./types\";\nimport { commitAllEffects, cleanupAllEffects } from \"./helpers/commit\";\nimport { withResourceFiber } from \"./helpers/execution-context\";\nimport { withReactDispatcher } from \"./react-dispatcher\";\nimport { isDevelopment } from \"./helpers/env\";\n\nexport function createResourceFiber<R, A extends readonly unknown[]>(\n hook: (...args: A) => R,\n root: ResourceFiberRoot,\n markDirty: (() => void) | undefined = undefined,\n strictMode: \"root\" | \"child\" | null,\n): ResourceFiber<R, A> {\n return {\n hook,\n root,\n markDirty,\n devStrictMode: strictMode,\n cells: [],\n renderPendingCells: null,\n currentIndex: 0,\n renderContext: undefined,\n isFirstRender: true,\n isMounted: false,\n isNeverMounted: true,\n };\n}\n\nexport function unmountResourceFiber<R, A extends readonly unknown[]>(\n fiber: ResourceFiber<R, A>,\n): void {\n if (!fiber.isMounted)\n throw new Error(\"Tried to unmount a fiber that is already unmounted\");\n\n fiber.isMounted = false;\n cleanupAllEffects(fiber);\n}\n\nexport function renderResourceFiber<R, A extends readonly unknown[]>(\n fiber: ResourceFiber<R, A>,\n args: Readonly<A>,\n): RenderResult {\n // Discard render-phase actions left by a previous render\n if (fiber.renderPendingCells !== null) {\n for (const cell of fiber.renderPendingCells) cell.renderQueue = null;\n fiber.renderPendingCells.clear();\n }\n\n let passes = 0;\n let result: RenderResult;\n do {\n if (++passes > 25) {\n throw new Error(\n \"Too many re-renders. tap limits the number of renders to prevent \" +\n \"an infinite loop.\",\n );\n }\n\n result = {\n effectTasks: [],\n value: undefined as R | undefined,\n };\n\n withResourceFiber(fiber, () => {\n fiber.renderContext = result;\n try {\n result.value = withReactDispatcher(() => fiber.hook(...args));\n } finally {\n fiber.renderContext = undefined;\n }\n });\n } while ((fiber.renderPendingCells?.size ?? 0) > 0);\n\n return result;\n}\n\nexport function commitResourceFiber<R, A extends readonly unknown[]>(\n fiber: ResourceFiber<R, A>,\n result: RenderResult,\n): void {\n fiber.isMounted = true;\n\n if (isDevelopment && fiber.isNeverMounted && fiber.devStrictMode === \"root\") {\n fiber.isNeverMounted = false;\n\n commitAllEffects(result);\n cleanupAllEffects(fiber);\n }\n\n fiber.isNeverMounted = false;\n commitAllEffects(result);\n}\n"],"mappings":";;;;;AAMA,SAAgB,oBACd,MACA,MACA,YAAsC,KAAA,GACtC,YACqB;CACrB,OAAO;EACL;EACA;EACA;EACA,eAAe;EACf,OAAO,CAAC;EACR,oBAAoB;EACpB,cAAc;EACd,eAAe,KAAA;EACf,eAAe;EACf,WAAW;EACX,gBAAgB;CAClB;AACF;AAEA,SAAgB,qBACd,OACM;CACN,IAAI,CAAC,MAAM,WACT,MAAM,IAAI,MAAM,oDAAoD;CAEtE,MAAM,YAAY;CAClB,kBAAkB,KAAK;AACzB;AAEA,SAAgB,oBACd,OACA,MACc;CAEd,IAAI,MAAM,uBAAuB,MAAM;EACrC,KAAK,MAAM,QAAQ,MAAM,oBAAoB,KAAK,cAAc;EAChE,MAAM,mBAAmB,MAAM;CACjC;CAEA,IAAI,SAAS;CACb,IAAI;CACJ,GAAG;EACD,IAAI,EAAE,SAAS,IACb,MAAM,IAAI,MACR,oFAEF;EAGF,SAAS;GACP,aAAa,CAAC;GACd,OAAO,KAAA;EACT;EAEA,kBAAkB,aAAa;GAC7B,MAAM,gBAAgB;GACtB,IAAI;IACF,OAAO,QAAQ,0BAA0B,MAAM,KAAK,GAAG,IAAI,CAAC;GAC9D,UAAU;IACR,MAAM,gBAAgB,KAAA;GACxB;EACF,CAAC;CACH,UAAU,MAAM,oBAAoB,QAAQ,KAAK;CAEjD,OAAO;AACT;AAEA,SAAgB,oBACd,OACA,QACM;CACN,MAAM,YAAY;CAElB,IAAI,iBAAiB,MAAM,kBAAkB,MAAM,kBAAkB,QAAQ;EAC3E,MAAM,iBAAiB;EAEvB,iBAAiB,MAAM;EACvB,kBAAkB,KAAK;CACzB;CAEA,MAAM,iBAAiB;CACvB,iBAAiB,MAAM;AACzB"}
@@ -0,0 +1,9 @@
1
+ import { useTapRoot } from "../hooks/useTapRoot.js";
2
+
3
+ //#region src/core/createTapRoot.d.ts
4
+ declare const createTapRoot: <R>(render: () => R) => useTapRoot.Root<R> & {
5
+ unmount: () => void;
6
+ };
7
+ //#endregion
8
+ export { createTapRoot };
9
+ //# sourceMappingURL=createTapRoot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTapRoot.d.ts","names":[],"sources":["../../src/core/createTapRoot.ts"],"mappings":";;;cAWa,aAAA,MACX,MAAA,QAAc,CAAA,KACb,UAAA,CAAW,IAAA,CAAK,CAAA;EAAO,OAAA;AAAA"}
@@ -0,0 +1,27 @@
1
+ import { isDevelopment } from "./helpers/env.js";
2
+ import { createResourceFiberRoot } from "./helpers/root.js";
3
+ import { commitResourceFiber, createResourceFiber, renderResourceFiber, unmountResourceFiber } from "./ResourceFiber.js";
4
+ import { UpdateScheduler, flushTapSync } from "./scheduler.js";
5
+ import { useTapRoot } from "../hooks/useTapRoot.js";
6
+ //#region src/core/createTapRoot.ts
7
+ const createTapRoot = (render) => {
8
+ const fiber = createResourceFiber(useTapRoot, createResourceFiberRoot((callback) => {
9
+ new UpdateScheduler(() => {
10
+ if (callback()) throw new Error("Unexpected rerender of createTapRoot outer fiber");
11
+ return false;
12
+ }).markDirty();
13
+ }), void 0, isDevelopment ? "root" : null);
14
+ if (isDevelopment && fiber.devStrictMode === "root") renderResourceFiber(fiber, [render]);
15
+ const rendered = renderResourceFiber(fiber, [render]);
16
+ flushTapSync(() => commitResourceFiber(fiber, rendered));
17
+ return {
18
+ ...rendered.value,
19
+ unmount: () => {
20
+ unmountResourceFiber(fiber);
21
+ }
22
+ };
23
+ };
24
+ //#endregion
25
+ export { createTapRoot };
26
+
27
+ //# sourceMappingURL=createTapRoot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTapRoot.js","names":[],"sources":["../../src/core/createTapRoot.ts"],"sourcesContent":["import {\n createResourceFiber,\n unmountResourceFiber,\n renderResourceFiber,\n commitResourceFiber,\n} from \"./ResourceFiber\";\nimport { useTapRoot } from \"../hooks/useTapRoot\";\nimport { isDevelopment } from \"./helpers/env\";\nimport { flushTapSync, UpdateScheduler } from \"./scheduler\";\nimport { createResourceFiberRoot } from \"./helpers/root\";\n\nexport const createTapRoot = <R>(\n render: () => R,\n): useTapRoot.Root<R> & { unmount: () => void } => {\n const fiber = createResourceFiber(\n useTapRoot,\n createResourceFiberRoot((callback) => {\n new UpdateScheduler(() => {\n if (callback()) {\n throw new Error(\"Unexpected rerender of createTapRoot outer fiber\");\n }\n return false;\n }).markDirty();\n }),\n undefined,\n isDevelopment ? \"root\" : null,\n );\n\n // In strict mode, render twice to detect side effects\n if (isDevelopment && fiber.devStrictMode === \"root\") {\n void renderResourceFiber(fiber, [render]);\n }\n\n const rendered = renderResourceFiber(fiber, [render]);\n flushTapSync(() => commitResourceFiber(fiber, rendered));\n\n const root = rendered.value as useTapRoot.Root<R>;\n\n return {\n ...root,\n unmount: () => {\n unmountResourceFiber(fiber);\n },\n };\n};\n"],"mappings":";;;;;;AAWA,MAAa,iBACX,WACiD;CACjD,MAAM,QAAQ,oBACZ,YACA,yBAAyB,aAAa;EACpC,IAAI,sBAAsB;GACxB,IAAI,SAAS,GACX,MAAM,IAAI,MAAM,kDAAkD;GAEpE,OAAO;EACT,CAAC,CAAC,CAAC,UAAU;CACf,CAAC,GACD,KAAA,GACA,gBAAgB,SAAS,IAC3B;CAGA,IAAI,iBAAiB,MAAM,kBAAkB,QAC3C,oBAAyB,OAAO,CAAC,MAAM,CAAC;CAG1C,MAAM,WAAW,oBAAoB,OAAO,CAAC,MAAM,CAAC;CACpD,mBAAmB,oBAAoB,OAAO,QAAQ,CAAC;CAIvD,OAAO;EACL,GAHW,SAAS;EAIpB,eAAe;GACb,qBAAqB,KAAK;EAC5B;CACF;AACF"}
@@ -2,7 +2,7 @@ import { RenderResult, ResourceFiber } from "../types.js";
2
2
 
3
3
  //#region src/core/helpers/commit.d.ts
4
4
  declare function commitAllEffects(renderResult: RenderResult): void;
5
- declare function cleanupAllEffects<R, P>(executionContext: ResourceFiber<R, P>): void;
5
+ declare function cleanupAllEffects<R, A extends readonly unknown[]>(executionContext: ResourceFiber<R, A>): void;
6
6
  //#endregion
7
7
  export { cleanupAllEffects, commitAllEffects };
8
8
  //# sourceMappingURL=commit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commit.d.ts","names":[],"sources":["../../../src/core/helpers/commit.ts"],"mappings":";;;iBAEgB,gBAAA,CAAiB,YAA0B,EAAZ,YAAY;AAAA,iBAuB3C,iBAAA,OAAwB,gBAAA,EAAkB,aAAA,CAAc,CAAA,EAAG,CAAA"}
1
+ {"version":3,"file":"commit.d.ts","names":[],"sources":["../../../src/core/helpers/commit.ts"],"mappings":";;;iBAEgB,gBAAA,CAAiB,YAA0B,EAAZ,YAAY;AAAA,iBA+B3C,iBAAA,kCACd,gBAAA,EAAkB,aAAA,CAAc,CAAA,EAAG,CAAA"}
@@ -2,7 +2,12 @@
2
2
  function commitAllEffects(renderResult) {
3
3
  const errors = [];
4
4
  for (const task of renderResult.effectTasks) try {
5
- task();
5
+ task.cleanup();
6
+ } catch (error) {
7
+ errors.push(error);
8
+ }
9
+ for (const task of renderResult.effectTasks) try {
10
+ task.setup();
6
11
  } catch (error) {
7
12
  errors.push(error);
8
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"commit.js","names":[],"sources":["../../../src/core/helpers/commit.ts"],"sourcesContent":["import type { ResourceFiber, RenderResult } from \"../types\";\n\nexport function commitAllEffects(renderResult: RenderResult): void {\n const errors: unknown[] = [];\n\n for (const task of renderResult.effectTasks) {\n try {\n task();\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length > 0) {\n if (errors.length === 1) {\n throw errors[0];\n } else {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors, \"Errors during commit\");\n }\n }\n}\n\nexport function cleanupAllEffects<R, P>(executionContext: ResourceFiber<R, P>) {\n const errors: unknown[] = [];\n for (const cell of executionContext.cells) {\n if (cell?.type === \"effect\") {\n cell.deps = null; // Reset deps so effect runs again on next mount\n\n if (cell.cleanup) {\n try {\n cell.cleanup?.();\n } catch (e) {\n errors.push(e);\n } finally {\n cell.cleanup = undefined;\n }\n }\n }\n }\n if (errors.length > 0) {\n if (errors.length === 1) {\n throw errors[0];\n } else {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors, \"Errors during cleanup\");\n }\n }\n}\n"],"mappings":";AAEA,SAAgB,iBAAiB,cAAkC;CACjE,MAAM,SAAoB,CAAC;CAE3B,KAAK,MAAM,QAAQ,aAAa,aAC9B,IAAI;EACF,KAAK;CACP,SAAS,OAAO;EACd,OAAO,KAAK,KAAK;CACnB;CAGF,IAAI,OAAO,SAAS,GAClB,IAAI,OAAO,WAAW,GACpB,MAAM,OAAO;MACR;EACL,KAAK,MAAM,SAAS,QAClB,QAAQ,MAAM,KAAK;EAErB,MAAM,IAAI,eAAe,QAAQ,sBAAsB;CACzD;AAEJ;AAEA,SAAgB,kBAAwB,kBAAuC;CAC7E,MAAM,SAAoB,CAAC;CAC3B,KAAK,MAAM,QAAQ,iBAAiB,OAClC,IAAI,MAAM,SAAS,UAAU;EAC3B,KAAK,OAAO;EAEZ,IAAI,KAAK,SACP,IAAI;GACF,KAAK,UAAU;EACjB,SAAS,GAAG;GACV,OAAO,KAAK,CAAC;EACf,UAAU;GACR,KAAK,UAAU,KAAA;EACjB;CAEJ;CAEF,IAAI,OAAO,SAAS,GAClB,IAAI,OAAO,WAAW,GACpB,MAAM,OAAO;MACR;EACL,KAAK,MAAM,SAAS,QAClB,QAAQ,MAAM,KAAK;EAErB,MAAM,IAAI,eAAe,QAAQ,uBAAuB;CAC1D;AAEJ"}
1
+ {"version":3,"file":"commit.js","names":[],"sources":["../../../src/core/helpers/commit.ts"],"sourcesContent":["import type { ResourceFiber, RenderResult } from \"../types\";\n\nexport function commitAllEffects(renderResult: RenderResult): void {\n const errors: unknown[] = [];\n\n for (const task of renderResult.effectTasks) {\n try {\n task.cleanup();\n } catch (error) {\n errors.push(error);\n }\n }\n\n for (const task of renderResult.effectTasks) {\n try {\n task.setup();\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length > 0) {\n if (errors.length === 1) {\n throw errors[0];\n } else {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors, \"Errors during commit\");\n }\n }\n}\n\nexport function cleanupAllEffects<R, A extends readonly unknown[]>(\n executionContext: ResourceFiber<R, A>,\n) {\n const errors: unknown[] = [];\n for (const cell of executionContext.cells) {\n if (cell?.type === \"effect\") {\n cell.deps = null; // Reset deps so effect runs again on next mount\n\n if (cell.cleanup) {\n try {\n cell.cleanup?.();\n } catch (e) {\n errors.push(e);\n } finally {\n cell.cleanup = undefined;\n }\n }\n }\n }\n if (errors.length > 0) {\n if (errors.length === 1) {\n throw errors[0];\n } else {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors, \"Errors during cleanup\");\n }\n }\n}\n"],"mappings":";AAEA,SAAgB,iBAAiB,cAAkC;CACjE,MAAM,SAAoB,CAAC;CAE3B,KAAK,MAAM,QAAQ,aAAa,aAC9B,IAAI;EACF,KAAK,QAAQ;CACf,SAAS,OAAO;EACd,OAAO,KAAK,KAAK;CACnB;CAGF,KAAK,MAAM,QAAQ,aAAa,aAC9B,IAAI;EACF,KAAK,MAAM;CACb,SAAS,OAAO;EACd,OAAO,KAAK,KAAK;CACnB;CAGF,IAAI,OAAO,SAAS,GAClB,IAAI,OAAO,WAAW,GACpB,MAAM,OAAO;MACR;EACL,KAAK,MAAM,SAAS,QAClB,QAAQ,MAAM,KAAK;EAErB,MAAM,IAAI,eAAe,QAAQ,sBAAsB;CACzD;AAEJ;AAEA,SAAgB,kBACd,kBACA;CACA,MAAM,SAAoB,CAAC;CAC3B,KAAK,MAAM,QAAQ,iBAAiB,OAClC,IAAI,MAAM,SAAS,UAAU;EAC3B,KAAK,OAAO;EAEZ,IAAI,KAAK,SACP,IAAI;GACF,KAAK,UAAU;EACjB,SAAS,GAAG;GACV,OAAO,KAAK,CAAC;EACf,UAAU;GACR,KAAK,UAAU,KAAA;EACjB;CAEJ;CAEF,IAAI,OAAO,SAAS,GAClB,IAAI,OAAO,WAAW,GACpB,MAAM,OAAO;MACR;EACL,KAAK,MAAM,SAAS,QAClB,QAAQ,MAAM,KAAK;EAErB,MAAM,IAAI,eAAe,QAAQ,uBAAuB;CAC1D;AAEJ"}
@@ -1,10 +1,9 @@
1
1
  import { ResourceFiber } from "../types.js";
2
2
 
3
3
  //#region src/core/helpers/execution-context.d.ts
4
- declare function withResourceFiber<R, P>(fiber: ResourceFiber<R, P>, fn: () => void): void;
5
- declare function getCurrentResourceFiber(): ResourceFiber<unknown, unknown>;
6
- declare function peekResourceFiber(): ResourceFiber<unknown, unknown> | null;
7
- declare function getDevStrictMode(enable: boolean): "root" | "child" | null;
4
+ declare function withResourceFiber<R, A extends readonly unknown[]>(fiber: ResourceFiber<R, A>, fn: () => void): void;
5
+ declare function getCurrentResourceFiber(): ResourceFiber<unknown>;
6
+ declare function peekResourceFiber(): ResourceFiber<unknown> | null;
8
7
  //#endregion
9
- export { getCurrentResourceFiber, getDevStrictMode, peekResourceFiber, withResourceFiber };
8
+ export { getCurrentResourceFiber, peekResourceFiber, withResourceFiber };
10
9
  //# sourceMappingURL=execution-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context.d.ts","names":[],"sources":["../../../src/core/helpers/execution-context.ts"],"mappings":";;;iBAKgB,iBAAA,OACd,KAAA,EAAO,aAAA,CAAc,CAAA,EAAG,CAAA,GACxB,EAAA;AAAA,iBAsBc,uBAAA,IAA2B,aAAa;AAAA,iBAOxC,iBAAA,IAAqB,aAAa;AAAA,iBAIlC,gBAAA,CAAiB,MAAe"}
1
+ {"version":3,"file":"execution-context.d.ts","names":[],"sources":["../../../src/core/helpers/execution-context.ts"],"mappings":";;;iBAIgB,iBAAA,kCACd,KAAA,EAAO,aAAA,CAAc,CAAA,EAAG,CAAA,GACxB,EAAA;AAAA,iBAsBc,uBAAA,IAA2B,aAAa;AAAA,iBAOxC,iBAAA,IAAqB,aAAa"}
@@ -1,4 +1,3 @@
1
- import { isDevelopment } from "./env.js";
2
1
  //#region src/core/helpers/execution-context.ts
3
2
  let currentResourceFiber = null;
4
3
  function withResourceFiber(fiber, fn) {
@@ -20,12 +19,7 @@ function getCurrentResourceFiber() {
20
19
  function peekResourceFiber() {
21
20
  return currentResourceFiber;
22
21
  }
23
- function getDevStrictMode(enable) {
24
- if (!isDevelopment) return null;
25
- if (currentResourceFiber?.devStrictMode) return currentResourceFiber.isFirstRender ? "child" : "root";
26
- return enable ? "root" : null;
27
- }
28
22
  //#endregion
29
- export { getCurrentResourceFiber, getDevStrictMode, peekResourceFiber, withResourceFiber };
23
+ export { getCurrentResourceFiber, peekResourceFiber, withResourceFiber };
30
24
 
31
25
  //# sourceMappingURL=execution-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context.js","names":[],"sources":["../../../src/core/helpers/execution-context.ts"],"sourcesContent":["import { isDevelopment } from \"./env\";\nimport type { ResourceFiber } from \"../types\";\n\nlet currentResourceFiber: ResourceFiber<any, any> | null = null;\n\nexport function withResourceFiber<R, P>(\n fiber: ResourceFiber<R, P>,\n fn: () => void,\n): void {\n fiber.currentIndex = 0;\n\n const previousContext = currentResourceFiber;\n currentResourceFiber = fiber;\n try {\n fn();\n\n fiber.isFirstRender = false;\n\n // ensure hook count matches\n if (fiber.cells.length !== fiber.currentIndex) {\n throw new Error(\n `Rendered ${fiber.currentIndex} hooks but expected ${fiber.cells.length}. ` +\n \"Hooks must be called in the exact same order in every render.\",\n );\n }\n } finally {\n currentResourceFiber = previousContext;\n }\n}\nexport function getCurrentResourceFiber(): ResourceFiber<unknown, unknown> {\n if (!currentResourceFiber) {\n throw new Error(\"No resource fiber available\");\n }\n return currentResourceFiber;\n}\n\nexport function peekResourceFiber(): ResourceFiber<unknown, unknown> | null {\n return currentResourceFiber;\n}\n\nexport function getDevStrictMode(enable: boolean) {\n if (!isDevelopment) return null;\n if (currentResourceFiber?.devStrictMode)\n return currentResourceFiber.isFirstRender ? \"child\" : \"root\";\n\n return enable ? \"root\" : null;\n}\n"],"mappings":";;AAGA,IAAI,uBAAuD;AAE3D,SAAgB,kBACd,OACA,IACM;CACN,MAAM,eAAe;CAErB,MAAM,kBAAkB;CACxB,uBAAuB;CACvB,IAAI;EACF,GAAG;EAEH,MAAM,gBAAgB;EAGtB,IAAI,MAAM,MAAM,WAAW,MAAM,cAC/B,MAAM,IAAI,MACR,YAAY,MAAM,aAAa,sBAAsB,MAAM,MAAM,OAAO,gEAE1E;CAEJ,UAAU;EACR,uBAAuB;CACzB;AACF;AACA,SAAgB,0BAA2D;CACzE,IAAI,CAAC,sBACH,MAAM,IAAI,MAAM,6BAA6B;CAE/C,OAAO;AACT;AAEA,SAAgB,oBAA4D;CAC1E,OAAO;AACT;AAEA,SAAgB,iBAAiB,QAAiB;CAChD,IAAI,CAAC,eAAe,OAAO;CAC3B,IAAI,sBAAsB,eACxB,OAAO,qBAAqB,gBAAgB,UAAU;CAExD,OAAO,SAAS,SAAS;AAC3B"}
1
+ {"version":3,"file":"execution-context.js","names":[],"sources":["../../../src/core/helpers/execution-context.ts"],"sourcesContent":["import type { ResourceFiber } from \"../types\";\n\nlet currentResourceFiber: ResourceFiber<any, any> | null = null;\n\nexport function withResourceFiber<R, A extends readonly unknown[]>(\n fiber: ResourceFiber<R, A>,\n fn: () => void,\n): void {\n fiber.currentIndex = 0;\n\n const previousContext = currentResourceFiber;\n currentResourceFiber = fiber;\n try {\n fn();\n\n fiber.isFirstRender = false;\n\n // ensure hook count matches\n if (fiber.cells.length !== fiber.currentIndex) {\n throw new Error(\n `Rendered ${fiber.currentIndex} hooks but expected ${fiber.cells.length}. ` +\n \"Hooks must be called in the exact same order in every render.\",\n );\n }\n } finally {\n currentResourceFiber = previousContext;\n }\n}\nexport function getCurrentResourceFiber(): ResourceFiber<unknown> {\n if (!currentResourceFiber) {\n throw new Error(\"No resource fiber available\");\n }\n return currentResourceFiber;\n}\n\nexport function peekResourceFiber(): ResourceFiber<unknown> | null {\n return currentResourceFiber;\n}\n"],"mappings":";AAEA,IAAI,uBAAuD;AAE3D,SAAgB,kBACd,OACA,IACM;CACN,MAAM,eAAe;CAErB,MAAM,kBAAkB;CACxB,uBAAuB;CACvB,IAAI;EACF,GAAG;EAEH,MAAM,gBAAgB;EAGtB,IAAI,MAAM,MAAM,WAAW,MAAM,cAC/B,MAAM,IAAI,MACR,YAAY,MAAM,aAAa,sBAAsB,MAAM,MAAM,OAAO,gEAE1E;CAEJ,UAAU;EACR,uBAAuB;CACzB;AACF;AACA,SAAgB,0BAAkD;CAChE,IAAI,CAAC,sBACH,MAAM,IAAI,MAAM,6BAA6B;CAE/C,OAAO;AACT;AAEA,SAAgB,oBAAmD;CACjE,OAAO;AACT"}
@@ -1,12 +1,13 @@
1
- import { Cell, ResourceFiber, ResourceFiberRoot } from "../types.js";
1
+ import { Cell, ChangelogRecord, ResourceFiber, ResourceFiberRoot } from "../types.js";
2
2
 
3
3
  //#region src/core/helpers/root.d.ts
4
4
  declare const createResourceFiberRoot: (dispatchUpdate: (cb: () => boolean) => void) => ResourceFiberRoot;
5
5
  declare const commitRoot: (root: ResourceFiberRoot) => void;
6
6
  declare const setRootVersion: (root: ResourceFiberRoot, version: number) => void;
7
+ declare const applyChangelogRecord: (record: ChangelogRecord) => void;
7
8
  declare const markCellDirty: (fiber: ResourceFiber<any, any>, cell: Cell & {
8
9
  type: "reducer";
9
10
  }) => void;
10
11
  //#endregion
11
- export { commitRoot, createResourceFiberRoot, markCellDirty, setRootVersion };
12
+ export { applyChangelogRecord, commitRoot, createResourceFiberRoot, markCellDirty, setRootVersion };
12
13
  //# sourceMappingURL=root.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"root.d.ts","names":[],"sources":["../../../src/core/helpers/root.ts"],"mappings":";;;cAEa,uBAAA,GACX,cAAA,GAAiB,EAAA,6BAChB,iBAQF;AAAA,cAEY,UAAA,GAAc,IAAuB,EAAjB,iBAAiB;AAAA,cAWrC,cAAA,GACX,IAAA,EAAM,iBAAiB,EACvB,OAAA;AAAA,cA8BW,aAAA,GACX,KAAA,EAAO,aAAA,YACP,IAAA,EAAM,IAAI;EAAK,IAAA;AAAA"}
1
+ {"version":3,"file":"root.d.ts","names":[],"sources":["../../../src/core/helpers/root.ts"],"mappings":";;;cAOa,uBAAA,GACX,cAAA,GAAiB,EAAA,6BAChB,iBAQF;AAAA,cAEY,UAAA,GAAc,IAAuB,EAAjB,iBAAiB;AAAA,cASrC,cAAA,GACX,IAAA,EAAM,iBAAiB,EACvB,OAAA;AAAA,cAgCW,oBAAA,GAAwB,MAAuB,EAAf,eAAe;AAAA,cAQ/C,aAAA,GACX,KAAA,EAAO,aAAA,YACP,IAAA,EAAM,IAAI;EAAK,IAAA;AAAA"}
@@ -5,46 +5,50 @@ const createResourceFiberRoot = (dispatchUpdate) => {
5
5
  committedVersion: 0,
6
6
  dispatchUpdate,
7
7
  changelog: [],
8
- dirtyCells: []
8
+ dirtyCells: /* @__PURE__ */ new Set()
9
9
  };
10
10
  };
11
11
  const commitRoot = (root) => {
12
- for (const cell of root.dirtyCells) {
13
- cell.dirty = false;
14
- cell.queue.clear();
15
- cell.current = cell.workInProgress;
16
- }
12
+ for (const cell of root.dirtyCells) cell.current = cell.workInProgress;
17
13
  root.committedVersion = root.version;
18
14
  root.changelog.length = 0;
19
- root.dirtyCells.length = 0;
15
+ root.dirtyCells.clear();
20
16
  };
21
17
  const setRootVersion = (root, version) => {
22
18
  const rollback = root.version > version;
23
19
  root.version = version;
24
20
  if (rollback) {
25
21
  for (const cell of root.dirtyCells) {
26
- cell.dirty = false;
27
- cell.queue.clear();
22
+ if (cell.queue !== null) {
23
+ for (const record of cell.queue) record.queued = false;
24
+ cell.queue = null;
25
+ }
28
26
  cell.workInProgress = cell.current;
29
27
  }
30
- root.dirtyCells.length = 0;
28
+ root.dirtyCells.clear();
31
29
  if (version === root.committedVersion) root.changelog.length = 0;
32
30
  else {
33
31
  if (root.committedVersion > version) throw new Error("Version is less than committed version");
34
32
  while (root.committedVersion + root.changelog.length > version) root.changelog.pop();
35
- root.changelog.forEach((apply) => apply());
33
+ root.changelog.forEach(applyChangelogRecord);
36
34
  commitRoot(root);
37
35
  }
38
36
  }
39
37
  };
38
+ const applyChangelogRecord = (record) => {
39
+ markCellDirty(record.fiber, record.cell);
40
+ if (!record.queued) {
41
+ record.queued = true;
42
+ (record.cell.queue ??= []).push(record);
43
+ }
44
+ };
40
45
  const markCellDirty = (fiber, cell) => {
41
- if (!cell.dirty) {
42
- cell.dirty = true;
46
+ if (!fiber.root.dirtyCells.has(cell)) {
43
47
  fiber.markDirty?.();
44
- fiber.root.dirtyCells.push(cell);
48
+ fiber.root.dirtyCells.add(cell);
45
49
  }
46
50
  };
47
51
  //#endregion
48
- export { commitRoot, createResourceFiberRoot, markCellDirty, setRootVersion };
52
+ export { applyChangelogRecord, commitRoot, createResourceFiberRoot, markCellDirty, setRootVersion };
49
53
 
50
54
  //# sourceMappingURL=root.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"root.js","names":[],"sources":["../../../src/core/helpers/root.ts"],"sourcesContent":["import type { Cell, ResourceFiber, ResourceFiberRoot } from \"../types\";\n\nexport const createResourceFiberRoot = (\n dispatchUpdate: (cb: () => boolean) => void,\n): ResourceFiberRoot => {\n return {\n version: 0,\n committedVersion: 0,\n dispatchUpdate,\n changelog: [],\n dirtyCells: [],\n };\n};\n\nexport const commitRoot = (root: ResourceFiberRoot): void => {\n for (const cell of root.dirtyCells) {\n cell.dirty = false;\n cell.queue.clear();\n cell.current = cell.workInProgress;\n }\n root.committedVersion = root.version;\n root.changelog.length = 0;\n root.dirtyCells.length = 0;\n};\n\nexport const setRootVersion = (\n root: ResourceFiberRoot,\n version: number,\n): void => {\n const rollback = root.version > version;\n root.version = version;\n if (rollback) {\n for (const cell of root.dirtyCells) {\n cell.dirty = false;\n cell.queue.clear();\n cell.workInProgress = cell.current;\n }\n root.dirtyCells.length = 0;\n\n if (version === root.committedVersion) {\n root.changelog.length = 0;\n } else {\n // commit happened without a useEffect update (offscreen API)\n\n if (root.committedVersion > version)\n throw new Error(\"Version is less than committed version\");\n\n while (root.committedVersion + root.changelog.length > version) {\n root.changelog.pop();\n }\n\n root.changelog.forEach((apply) => apply());\n commitRoot(root);\n }\n }\n};\n\nexport const markCellDirty = (\n fiber: ResourceFiber<any, any>,\n cell: Cell & { type: \"reducer\" },\n): void => {\n if (!cell.dirty) {\n cell.dirty = true;\n fiber.markDirty?.();\n fiber.root.dirtyCells.push(cell);\n }\n};\n"],"mappings":";AAEA,MAAa,2BACX,mBACsB;CACtB,OAAO;EACL,SAAS;EACT,kBAAkB;EAClB;EACA,WAAW,CAAC;EACZ,YAAY,CAAC;CACf;AACF;AAEA,MAAa,cAAc,SAAkC;CAC3D,KAAK,MAAM,QAAQ,KAAK,YAAY;EAClC,KAAK,QAAQ;EACb,KAAK,MAAM,MAAM;EACjB,KAAK,UAAU,KAAK;CACtB;CACA,KAAK,mBAAmB,KAAK;CAC7B,KAAK,UAAU,SAAS;CACxB,KAAK,WAAW,SAAS;AAC3B;AAEA,MAAa,kBACX,MACA,YACS;CACT,MAAM,WAAW,KAAK,UAAU;CAChC,KAAK,UAAU;CACf,IAAI,UAAU;EACZ,KAAK,MAAM,QAAQ,KAAK,YAAY;GAClC,KAAK,QAAQ;GACb,KAAK,MAAM,MAAM;GACjB,KAAK,iBAAiB,KAAK;EAC7B;EACA,KAAK,WAAW,SAAS;EAEzB,IAAI,YAAY,KAAK,kBACnB,KAAK,UAAU,SAAS;OACnB;GAGL,IAAI,KAAK,mBAAmB,SAC1B,MAAM,IAAI,MAAM,wCAAwC;GAE1D,OAAO,KAAK,mBAAmB,KAAK,UAAU,SAAS,SACrD,KAAK,UAAU,IAAI;GAGrB,KAAK,UAAU,SAAS,UAAU,MAAM,CAAC;GACzC,WAAW,IAAI;EACjB;CACF;AACF;AAEA,MAAa,iBACX,OACA,SACS;CACT,IAAI,CAAC,KAAK,OAAO;EACf,KAAK,QAAQ;EACb,MAAM,YAAY;EAClB,MAAM,KAAK,WAAW,KAAK,IAAI;CACjC;AACF"}
1
+ {"version":3,"file":"root.js","names":[],"sources":["../../../src/core/helpers/root.ts"],"sourcesContent":["import type {\n Cell,\n ChangelogRecord,\n ResourceFiber,\n ResourceFiberRoot,\n} from \"../types\";\n\nexport const createResourceFiberRoot = (\n dispatchUpdate: (cb: () => boolean) => void,\n): ResourceFiberRoot => {\n return {\n version: 0,\n committedVersion: 0,\n dispatchUpdate,\n changelog: [],\n dirtyCells: new Set(),\n };\n};\n\nexport const commitRoot = (root: ResourceFiberRoot): void => {\n for (const cell of root.dirtyCells) {\n cell.current = cell.workInProgress;\n }\n root.committedVersion = root.version;\n root.changelog.length = 0;\n root.dirtyCells.clear();\n};\n\nexport const setRootVersion = (\n root: ResourceFiberRoot,\n version: number,\n): void => {\n const rollback = root.version > version;\n root.version = version;\n if (rollback) {\n for (const cell of root.dirtyCells) {\n if (cell.queue !== null) {\n for (const record of cell.queue) record.queued = false;\n cell.queue = null;\n }\n cell.workInProgress = cell.current;\n }\n root.dirtyCells.clear();\n\n if (version === root.committedVersion) {\n root.changelog.length = 0;\n } else {\n // commit happened without a useEffect update (offscreen API)\n\n if (root.committedVersion > version)\n throw new Error(\"Version is less than committed version\");\n\n while (root.committedVersion + root.changelog.length > version) {\n root.changelog.pop();\n }\n\n root.changelog.forEach(applyChangelogRecord);\n commitRoot(root);\n }\n }\n};\n\nexport const applyChangelogRecord = (record: ChangelogRecord): void => {\n markCellDirty(record.fiber, record.cell);\n if (!record.queued) {\n record.queued = true;\n (record.cell.queue ??= []).push(record);\n }\n};\n\nexport const markCellDirty = (\n fiber: ResourceFiber<any, any>,\n cell: Cell & { type: \"reducer\" },\n): void => {\n if (!fiber.root.dirtyCells.has(cell)) {\n fiber.markDirty?.();\n fiber.root.dirtyCells.add(cell);\n }\n};\n"],"mappings":";AAOA,MAAa,2BACX,mBACsB;CACtB,OAAO;EACL,SAAS;EACT,kBAAkB;EAClB;EACA,WAAW,CAAC;EACZ,4BAAY,IAAI,IAAI;CACtB;AACF;AAEA,MAAa,cAAc,SAAkC;CAC3D,KAAK,MAAM,QAAQ,KAAK,YACtB,KAAK,UAAU,KAAK;CAEtB,KAAK,mBAAmB,KAAK;CAC7B,KAAK,UAAU,SAAS;CACxB,KAAK,WAAW,MAAM;AACxB;AAEA,MAAa,kBACX,MACA,YACS;CACT,MAAM,WAAW,KAAK,UAAU;CAChC,KAAK,UAAU;CACf,IAAI,UAAU;EACZ,KAAK,MAAM,QAAQ,KAAK,YAAY;GAClC,IAAI,KAAK,UAAU,MAAM;IACvB,KAAK,MAAM,UAAU,KAAK,OAAO,OAAO,SAAS;IACjD,KAAK,QAAQ;GACf;GACA,KAAK,iBAAiB,KAAK;EAC7B;EACA,KAAK,WAAW,MAAM;EAEtB,IAAI,YAAY,KAAK,kBACnB,KAAK,UAAU,SAAS;OACnB;GAGL,IAAI,KAAK,mBAAmB,SAC1B,MAAM,IAAI,MAAM,wCAAwC;GAE1D,OAAO,KAAK,mBAAmB,KAAK,UAAU,SAAS,SACrD,KAAK,UAAU,IAAI;GAGrB,KAAK,UAAU,QAAQ,oBAAoB;GAC3C,WAAW,IAAI;EACjB;CACF;AACF;AAEA,MAAa,wBAAwB,WAAkC;CACrE,cAAc,OAAO,OAAO,OAAO,IAAI;CACvC,IAAI,CAAC,OAAO,QAAQ;EAClB,OAAO,SAAS;EAChB,CAAC,OAAO,KAAK,UAAU,CAAC,EAAA,CAAG,KAAK,MAAM;CACxC;AACF;AAEA,MAAa,iBACX,OACA,SACS;CACT,IAAI,CAAC,MAAM,KAAK,WAAW,IAAI,IAAI,GAAG;EACpC,MAAM,YAAY;EAClB,MAAM,KAAK,WAAW,IAAI,IAAI;CAChC;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"react-dispatcher.d.ts","names":[],"sources":["../../src/core/react-dispatcher.ts"],"mappings":";;AAmEA;;;;;;iBAAgB,mBAAA,IAAuB,MAAA,QAAc,CAAA,GAAI,CAAC"}
1
+ {"version":3,"file":"react-dispatcher.d.ts","names":[],"sources":["../../src/core/react-dispatcher.ts"],"mappings":";;AAoEA;;;;;;iBAAgB,mBAAA,IAAuB,MAAA,QAAc,CAAA,GAAI,CAAC"}
@@ -1,12 +1,12 @@
1
- import { useReducer as useReducer$1 } from "../hooks/useReducer.js";
2
- import { useState as useState$1 } from "../hooks/useState.js";
3
- import { useRef as useRef$1 } from "../hooks/useRef.js";
4
- import { useMemo as useMemo$1 } from "../hooks/useMemo.js";
5
- import { useCallback } from "../hooks/useCallback.js";
6
- import { useEffect } from "../hooks/useEffect.js";
7
- import { useEffectEvent } from "../hooks/useEffectEvent.js";
8
- import { use } from "../hooks/use.js";
9
- import { useMemoCache } from "../hooks/useMemoCache.js";
1
+ import { useReducer as useReducer$1 } from "../react-hooks/useReducer.js";
2
+ import { useState as useState$1 } from "../react-hooks/useState.js";
3
+ import { useRef as useRef$1 } from "../react-hooks/useRef.js";
4
+ import { useMemo as useMemo$1 } from "../react-hooks/useMemo.js";
5
+ import { useCallback as useCallback$1 } from "../react-hooks/useCallback.js";
6
+ import { useEffect as useEffect$1 } from "../react-hooks/useEffect.js";
7
+ import { useEffectEvent as useEffectEvent$1 } from "../react-hooks/useEffectEvent.js";
8
+ import { use } from "../react-hooks/use.js";
9
+ import { useMemoCache } from "../react-hooks/useMemoCache.js";
10
10
  import React from "react";
11
11
  //#region src/core/react-dispatcher.ts
12
12
  const tapDispatcher = {
@@ -14,11 +14,11 @@ const tapDispatcher = {
14
14
  useReducer: useReducer$1,
15
15
  useRef: useRef$1,
16
16
  useMemo: useMemo$1,
17
- useCallback,
18
- useEffect,
19
- useLayoutEffect: useEffect,
20
- useInsertionEffect: useEffect,
21
- useEffectEvent,
17
+ useCallback: useCallback$1,
18
+ useEffect: useEffect$1,
19
+ useLayoutEffect: useEffect$1,
20
+ useInsertionEffect: useEffect$1,
21
+ useEffectEvent: useEffectEvent$1,
22
22
  useContext: use,
23
23
  use,
24
24
  useMemoCache
@@ -1 +1 @@
1
- {"version":3,"file":"react-dispatcher.js","names":[],"sources":["../../src/core/react-dispatcher.ts"],"sourcesContent":["import React from \"react\";\nimport { useState } from \"../hooks/useState\";\nimport { useReducer } from \"../hooks/useReducer\";\nimport { useRef } from \"../hooks/useRef\";\nimport { useMemo } from \"../hooks/useMemo\";\nimport { useCallback } from \"../hooks/useCallback\";\nimport { useEffect } from \"../hooks/useEffect\";\nimport { useEffectEvent } from \"../hooks/useEffectEvent\";\nimport { use } from \"../hooks/use\";\nimport { useMemoCache } from \"../hooks/useMemoCache\";\n\n// The dispatcher React reads while a resource renders, so hooks imported from\n// \"react\" route to tap with no build step. Hooks tap has no equivalent for are\n// intentionally absent: calling one throws, which is the intended \"unsupported\n// in a resource\" signal.\nconst tapDispatcher = {\n useState,\n useReducer,\n useRef,\n useMemo,\n useCallback,\n useEffect,\n useLayoutEffect: useEffect,\n useInsertionEffect: useEffect,\n useEffectEvent,\n useContext: use,\n use,\n useMemoCache,\n};\n\n// React's live dispatcher slot differs by version: React 19 exposes it as `H` on\n// the client internals object; React 18 as `ReactCurrentDispatcher.current`.\nconst ReactRuntime = React as any;\nconst internals =\n ReactRuntime.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ??\n ReactRuntime.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n\nconst slot: { current: unknown } | null =\n internals == null\n ? null\n : \"H\" in internals\n ? {\n get current() {\n return internals.H;\n },\n set current(d) {\n internals.H = d;\n },\n }\n : \"ReactCurrentDispatcher\" in internals\n ? {\n get current() {\n return internals.ReactCurrentDispatcher.current;\n },\n set current(d) {\n internals.ReactCurrentDispatcher.current = d;\n },\n }\n : null;\n\n/**\n * Runs a resource body with tap's React dispatcher installed, so real React\n * hooks called inside it (`import { useState } from \"react\"`) route to tap, then\n * restores the previous dispatcher. If React's internal dispatcher slot can't be\n * found (an unsupported React version), the body runs unchanged and `react`\n * hooks inside it keep throwing React's \"invalid hook call\".\n */\nexport function withReactDispatcher<T>(render: () => T): T {\n if (!slot) return render();\n\n const previous = slot.current;\n slot.current = tapDispatcher;\n try {\n return render();\n } finally {\n slot.current = previous;\n }\n}\n"],"mappings":";;;;;;;;;;;AAeA,MAAM,gBAAgB;CACpB,UAAA;CACA,YAAA;CACA,QAAA;CACA,SAAA;CACA;CACA;CACA,iBAAiB;CACjB,oBAAoB;CACpB;CACA,YAAY;CACZ;CACA;AACF;AAIA,MAAM,eAAe;AACrB,MAAM,YACJ,aAAa,mEACb,aAAa;AAEf,MAAM,OACJ,aAAa,OACT,OACA,OAAO,YACL;CACE,IAAI,UAAU;EACZ,OAAO,UAAU;CACnB;CACA,IAAI,QAAQ,GAAG;EACb,UAAU,IAAI;CAChB;AACF,IACA,4BAA4B,YAC1B;CACE,IAAI,UAAU;EACZ,OAAO,UAAU,uBAAuB;CAC1C;CACA,IAAI,QAAQ,GAAG;EACb,UAAU,uBAAuB,UAAU;CAC7C;AACF,IACA;;;;;;;;AASV,SAAgB,oBAAuB,QAAoB;CACzD,IAAI,CAAC,MAAM,OAAO,OAAO;CAEzB,MAAM,WAAW,KAAK;CACtB,KAAK,UAAU;CACf,IAAI;EACF,OAAO,OAAO;CAChB,UAAU;EACR,KAAK,UAAU;CACjB;AACF"}
1
+ {"version":3,"file":"react-dispatcher.js","names":["useEffect"],"sources":["../../src/core/react-dispatcher.ts"],"sourcesContent":["import React from \"react\";\n\nimport { useState } from \"../react-hooks/useState\";\nimport { useReducer } from \"../react-hooks/useReducer\";\nimport { useRef } from \"../react-hooks/useRef\";\nimport { useMemo } from \"../react-hooks/useMemo\";\nimport { useCallback } from \"../react-hooks/useCallback\";\nimport { useEffect } from \"../react-hooks/useEffect\";\nimport { useEffectEvent } from \"../react-hooks/useEffectEvent\";\nimport { use } from \"../react-hooks/use\";\nimport { useMemoCache } from \"../react-hooks/useMemoCache\";\n\n// The dispatcher React reads while a resource renders, so hooks imported from\n// \"react\" route to tap with no build step. Hooks tap has no equivalent for are\n// intentionally absent: calling one throws, which is the intended \"unsupported\n// in a resource\" signal.\nconst tapDispatcher = {\n useState,\n useReducer,\n useRef,\n useMemo,\n useCallback,\n useEffect,\n useLayoutEffect: useEffect,\n useInsertionEffect: useEffect,\n useEffectEvent,\n useContext: use,\n use,\n useMemoCache,\n};\n\n// React's live dispatcher slot differs by version: React 19 exposes it as `H` on\n// the client internals object; React 18 as `ReactCurrentDispatcher.current`.\nconst ReactRuntime = React as any;\nconst internals =\n ReactRuntime.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ??\n ReactRuntime.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n\nconst slot: { current: unknown } | null =\n internals == null\n ? null\n : \"H\" in internals\n ? {\n get current() {\n return internals.H;\n },\n set current(d) {\n internals.H = d;\n },\n }\n : \"ReactCurrentDispatcher\" in internals\n ? {\n get current() {\n return internals.ReactCurrentDispatcher.current;\n },\n set current(d) {\n internals.ReactCurrentDispatcher.current = d;\n },\n }\n : null;\n\n/**\n * Runs a resource body with tap's React dispatcher installed, so real React\n * hooks called inside it (`import { useState } from \"react\"`) route to tap, then\n * restores the previous dispatcher. If React's internal dispatcher slot can't be\n * found (an unsupported React version), the body runs unchanged and `react`\n * hooks inside it keep throwing React's \"invalid hook call\".\n */\nexport function withReactDispatcher<T>(render: () => T): T {\n if (!slot) return render();\n\n const previous = slot.current;\n slot.current = tapDispatcher;\n try {\n return render();\n } finally {\n slot.current = previous;\n }\n}\n"],"mappings":";;;;;;;;;;;AAgBA,MAAM,gBAAgB;CACpB,UAAA;CACA,YAAA;CACA,QAAA;CACA,SAAA;CACA,aAAA;CACA,WAAA;CACA,iBAAiBA;CACjB,oBAAoBA;CACpB,gBAAA;CACA,YAAY;CACZ;CACA;AACF;AAIA,MAAM,eAAe;AACrB,MAAM,YACJ,aAAa,mEACb,aAAa;AAEf,MAAM,OACJ,aAAa,OACT,OACA,OAAO,YACL;CACE,IAAI,UAAU;EACZ,OAAO,UAAU;CACnB;CACA,IAAI,QAAQ,GAAG;EACb,UAAU,IAAI;CAChB;AACF,IACA,4BAA4B,YAC1B;CACE,IAAI,UAAU;EACZ,OAAO,UAAU,uBAAuB;CAC1C;CACA,IAAI,QAAQ,GAAG;EACb,UAAU,uBAAuB,UAAU;CAC7C;AACF,IACA;;;;;;;;AASV,SAAgB,oBAAuB,QAAoB;CACzD,IAAI,CAAC,MAAM,OAAO,OAAO;CAEzB,MAAM,WAAW,KAAK;CACtB,KAAK,UAAU;CACf,IAAI;EACF,OAAO,OAAO;CAChB,UAAU;EACR,KAAK,UAAU;CACjB;AACF"}
@@ -1,9 +1,7 @@
1
- import { ResourceElement } from "./types.js";
1
+ import { Resource } from "./types.js";
2
2
 
3
3
  //#region src/core/resource.d.ts
4
- declare function resource<R>(fn: () => R): () => ResourceElement<R, undefined>;
5
- declare function resource<R, P>(fn: (props: P) => R): (props: P) => ResourceElement<R, P>;
6
- declare function resource<R, P>(fn: (props?: P) => R): (props?: P) => ResourceElement<R, P | undefined>;
4
+ declare function resource<R, A extends readonly unknown[]>(hook: (...args: A) => R): Resource<R, A>;
7
5
  //#endregion
8
6
  export { resource };
9
7
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","names":[],"sources":["../../src/core/resource.ts"],"mappings":";;;iBAGgB,QAAA,IAAY,EAAA,QAAU,CAAA,SAAU,eAAA,CAAgB,CAAA;AAAA,iBAChD,QAAA,OACd,EAAA,GAAK,KAAA,EAAO,CAAA,KAAM,CAAA,IAChB,KAAA,EAAO,CAAA,KAAM,eAAA,CAAgB,CAAA,EAAG,CAAA;AAAA,iBACpB,QAAA,OACd,EAAA,GAAK,KAAA,GAAQ,CAAA,KAAM,CAAA,IACjB,KAAA,GAAQ,CAAA,KAAM,eAAA,CAAgB,CAAA,EAAG,CAAA"}
1
+ {"version":3,"file":"resource.d.ts","names":[],"sources":["../../src/core/resource.ts"],"mappings":";;;iBAEgB,QAAA,kCACd,IAAA,MAAU,IAAA,EAAM,CAAA,KAAM,CAAA,GACrB,QAAA,CAAS,CAAA,EAAG,CAAA"}
@@ -1,14 +1,9 @@
1
- import { fnSymbol } from "./helpers/callResourceFn.js";
2
1
  //#region src/core/resource.ts
3
- function resource(fn) {
4
- const type = (props) => {
5
- return {
6
- type,
7
- props
8
- };
9
- };
10
- type[fnSymbol] = fn;
11
- return type;
2
+ function resource(hook) {
3
+ return (...args) => ({
4
+ hook,
5
+ args
6
+ });
12
7
  }
13
8
  //#endregion
14
9
  export { resource };
@@ -1 +1 @@
1
- {"version":3,"file":"resource.js","names":[],"sources":["../../src/core/resource.ts"],"sourcesContent":["import type { ResourceElement } from \"./types\";\nimport { fnSymbol } from \"./helpers/callResourceFn\";\n\nexport function resource<R>(fn: () => R): () => ResourceElement<R, undefined>;\nexport function resource<R, P>(\n fn: (props: P) => R,\n): (props: P) => ResourceElement<R, P>;\nexport function resource<R, P>(\n fn: (props?: P) => R,\n): (props?: P) => ResourceElement<R, P | undefined>;\nexport function resource<R, P = undefined>(fn: (props: P) => R) {\n const type = (props?: P) => {\n return {\n type,\n props: props!,\n } satisfies ResourceElement<R, P>;\n };\n\n type[fnSymbol] = fn;\n\n return type;\n}\n"],"mappings":";;AAUA,SAAgB,SAA2B,IAAqB;CAC9D,MAAM,QAAQ,UAAc;EAC1B,OAAO;GACL;GACO;EACT;CACF;CAEA,KAAK,YAAY;CAEjB,OAAO;AACT"}
1
+ {"version":3,"file":"resource.js","names":[],"sources":["../../src/core/resource.ts"],"sourcesContent":["import type { Resource, ResourceElement } from \"./types\";\n\nexport function resource<R, A extends readonly unknown[]>(\n hook: (...args: A) => R,\n): Resource<R, A> {\n return (...args: A): ResourceElement<R, A> => ({ hook, args });\n}\n"],"mappings":";AAEA,SAAgB,SACd,MACgB;CAChB,QAAQ,GAAG,UAAoC;EAAE;EAAM;CAAK;AAC9D"}
@@ -8,7 +8,7 @@ declare class UpdateScheduler {
8
8
  markDirty(): void;
9
9
  runTask(): void;
10
10
  }
11
- declare const flushResourcesSync: <T>(callback: () => T) => T;
11
+ declare const flushTapSync: <T>(callback: () => T) => T;
12
12
  //#endregion
13
- export { UpdateScheduler, flushResourcesSync };
13
+ export { UpdateScheduler, flushTapSync };
14
14
  //# sourceMappingURL=scheduler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.d.ts","names":[],"sources":["../../src/core/scheduler.ts"],"mappings":";KAAK,IAAA;AAAA,cAaQ,eAAA;EAAA,iBAGkB,KAAA;EAAA,QAFrB,QAAA;cAEqB,KAAA,EAAO,IAAI;EAAA,IAEpC,OAAA;EAIJ,SAAA;EAOA,OAAA;AAAA;AAAA,cAiEW,kBAAA,MAAyB,QAAA,QAAgB,CAAA,KAAI,CAezD"}
1
+ {"version":3,"file":"scheduler.d.ts","names":[],"sources":["../../src/core/scheduler.ts"],"mappings":";KAAK,IAAA;AAAA,cAaQ,eAAA;EAAA,iBAGkB,KAAA;EAAA,QAFrB,QAAA;cAEqB,KAAA,EAAO,IAAI;EAAA,IAEpC,OAAA;EAIJ,SAAA;EAOA,OAAA;AAAA;AAAA,cAiEW,YAAA,MAAmB,QAAA,QAAgB,CAAA,KAAI,CAenD"}
@@ -61,7 +61,7 @@ const scheduleMacrotask = (() => {
61
61
  }
62
62
  return () => setTimeout(flushScheduled, 0);
63
63
  })();
64
- const flushResourcesSync = (callback) => {
64
+ const flushTapSync = (callback) => {
65
65
  const prev = flushState;
66
66
  flushState = {
67
67
  schedulers: /* @__PURE__ */ new Set([]),
@@ -76,6 +76,6 @@ const flushResourcesSync = (callback) => {
76
76
  }
77
77
  };
78
78
  //#endregion
79
- export { UpdateScheduler, flushResourcesSync };
79
+ export { UpdateScheduler, flushTapSync };
80
80
 
81
81
  //# sourceMappingURL=scheduler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.js","names":[],"sources":["../../src/core/scheduler.ts"],"sourcesContent":["type Task = () => void;\n\ntype GlobalFlushState = {\n schedulers: Set<UpdateScheduler>;\n isScheduled: boolean;\n};\n\nconst MAX_FLUSH_LIMIT = 50;\nlet flushState: GlobalFlushState = {\n schedulers: new Set([]),\n isScheduled: false,\n};\n\nexport class UpdateScheduler {\n private _isDirty = false;\n\n constructor(private readonly _task: Task) {}\n\n get isDirty() {\n return this._isDirty;\n }\n\n markDirty() {\n this._isDirty = true;\n\n flushState.schedulers.add(this);\n scheduleFlush();\n }\n\n runTask() {\n this._isDirty = false;\n this._task();\n }\n}\n\nconst scheduleFlush = () => {\n if (flushState.isScheduled) return;\n flushState.isScheduled = true;\n scheduleMacrotask();\n};\n\nconst flushScheduled = () => {\n try {\n const errors = [];\n let flushDepth = 0;\n\n for (const scheduler of flushState.schedulers) {\n flushState.schedulers.delete(scheduler);\n if (!scheduler.isDirty) continue;\n\n flushDepth++;\n\n if (flushDepth > MAX_FLUSH_LIMIT) {\n throw new Error(\n `Maximum update depth exceeded. This can happen when a resource ` +\n `repeatedly calls setState inside useEffect.`,\n );\n }\n\n try {\n scheduler.runTask();\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length > 0) {\n if (errors.length === 1) {\n throw errors[0];\n } else {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors, \"Errors occurred during flushSync\");\n }\n }\n } finally {\n flushState.schedulers.clear();\n flushState.isScheduled = false;\n }\n};\n\n// Use MessageChannel to schedule flushes as macrotasks (like React's scheduler).\n// This allows more state updates to batch into a single re-render.\nconst scheduleMacrotask = (() => {\n if (typeof MessageChannel !== \"undefined\") {\n const channel = new MessageChannel();\n channel.port1.onmessage = flushScheduled;\n return () => channel.port2.postMessage(null);\n }\n // Fallback for environments without MessageChannel\n return () => setTimeout(flushScheduled, 0);\n})();\n\nexport const flushResourcesSync = <T>(callback: () => T): T => {\n const prev = flushState;\n flushState = {\n schedulers: new Set([]),\n isScheduled: true,\n };\n\n try {\n const result = callback();\n flushScheduled();\n\n return result;\n } finally {\n flushState = prev;\n }\n};\n"],"mappings":";AAOA,MAAM,kBAAkB;AACxB,IAAI,aAA+B;CACjC,4BAAY,IAAI,IAAI,CAAC,CAAC;CACtB,aAAa;AACf;AAEA,IAAa,kBAAb,MAA6B;CAGE;CAF7B,WAAmB;CAEnB,YAAY,OAA8B;EAAb,KAAA,QAAA;CAAc;CAE3C,IAAI,UAAU;EACZ,OAAO,KAAK;CACd;CAEA,YAAY;EACV,KAAK,WAAW;EAEhB,WAAW,WAAW,IAAI,IAAI;EAC9B,cAAc;CAChB;CAEA,UAAU;EACR,KAAK,WAAW;EAChB,KAAK,MAAM;CACb;AACF;AAEA,MAAM,sBAAsB;CAC1B,IAAI,WAAW,aAAa;CAC5B,WAAW,cAAc;CACzB,kBAAkB;AACpB;AAEA,MAAM,uBAAuB;CAC3B,IAAI;EACF,MAAM,SAAS,CAAC;EAChB,IAAI,aAAa;EAEjB,KAAK,MAAM,aAAa,WAAW,YAAY;GAC7C,WAAW,WAAW,OAAO,SAAS;GACtC,IAAI,CAAC,UAAU,SAAS;GAExB;GAEA,IAAI,aAAa,iBACf,MAAM,IAAI,MACR,4GAEF;GAGF,IAAI;IACF,UAAU,QAAQ;GACpB,SAAS,OAAO;IACd,OAAO,KAAK,KAAK;GACnB;EACF;EAEA,IAAI,OAAO,SAAS,GAClB,IAAI,OAAO,WAAW,GACpB,MAAM,OAAO;OACR;GACL,KAAK,MAAM,SAAS,QAClB,QAAQ,MAAM,KAAK;GAErB,MAAM,IAAI,eAAe,QAAQ,kCAAkC;EACrE;CAEJ,UAAU;EACR,WAAW,WAAW,MAAM;EAC5B,WAAW,cAAc;CAC3B;AACF;AAIA,MAAM,2BAA2B;CAC/B,IAAI,OAAO,mBAAmB,aAAa;EACzC,MAAM,UAAU,IAAI,eAAe;EACnC,QAAQ,MAAM,YAAY;EAC1B,aAAa,QAAQ,MAAM,YAAY,IAAI;CAC7C;CAEA,aAAa,WAAW,gBAAgB,CAAC;AAC3C,EAAA,CAAG;AAEH,MAAa,sBAAyB,aAAyB;CAC7D,MAAM,OAAO;CACb,aAAa;EACX,4BAAY,IAAI,IAAI,CAAC,CAAC;EACtB,aAAa;CACf;CAEA,IAAI;EACF,MAAM,SAAS,SAAS;EACxB,eAAe;EAEf,OAAO;CACT,UAAU;EACR,aAAa;CACf;AACF"}
1
+ {"version":3,"file":"scheduler.js","names":[],"sources":["../../src/core/scheduler.ts"],"sourcesContent":["type Task = () => void;\n\ntype GlobalFlushState = {\n schedulers: Set<UpdateScheduler>;\n isScheduled: boolean;\n};\n\nconst MAX_FLUSH_LIMIT = 50;\nlet flushState: GlobalFlushState = {\n schedulers: new Set([]),\n isScheduled: false,\n};\n\nexport class UpdateScheduler {\n private _isDirty = false;\n\n constructor(private readonly _task: Task) {}\n\n get isDirty() {\n return this._isDirty;\n }\n\n markDirty() {\n this._isDirty = true;\n\n flushState.schedulers.add(this);\n scheduleFlush();\n }\n\n runTask() {\n this._isDirty = false;\n this._task();\n }\n}\n\nconst scheduleFlush = () => {\n if (flushState.isScheduled) return;\n flushState.isScheduled = true;\n scheduleMacrotask();\n};\n\nconst flushScheduled = () => {\n try {\n const errors = [];\n let flushDepth = 0;\n\n for (const scheduler of flushState.schedulers) {\n flushState.schedulers.delete(scheduler);\n if (!scheduler.isDirty) continue;\n\n flushDepth++;\n\n if (flushDepth > MAX_FLUSH_LIMIT) {\n throw new Error(\n `Maximum update depth exceeded. This can happen when a resource ` +\n `repeatedly calls setState inside useEffect.`,\n );\n }\n\n try {\n scheduler.runTask();\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length > 0) {\n if (errors.length === 1) {\n throw errors[0];\n } else {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors, \"Errors occurred during flushSync\");\n }\n }\n } finally {\n flushState.schedulers.clear();\n flushState.isScheduled = false;\n }\n};\n\n// Use MessageChannel to schedule flushes as macrotasks (like React's scheduler).\n// This allows more state updates to batch into a single re-render.\nconst scheduleMacrotask = (() => {\n if (typeof MessageChannel !== \"undefined\") {\n const channel = new MessageChannel();\n channel.port1.onmessage = flushScheduled;\n return () => channel.port2.postMessage(null);\n }\n // Fallback for environments without MessageChannel\n return () => setTimeout(flushScheduled, 0);\n})();\n\nexport const flushTapSync = <T>(callback: () => T): T => {\n const prev = flushState;\n flushState = {\n schedulers: new Set([]),\n isScheduled: true,\n };\n\n try {\n const result = callback();\n flushScheduled();\n\n return result;\n } finally {\n flushState = prev;\n }\n};\n"],"mappings":";AAOA,MAAM,kBAAkB;AACxB,IAAI,aAA+B;CACjC,4BAAY,IAAI,IAAI,CAAC,CAAC;CACtB,aAAa;AACf;AAEA,IAAa,kBAAb,MAA6B;CAGE;CAF7B,WAAmB;CAEnB,YAAY,OAA8B;EAAb,KAAA,QAAA;CAAc;CAE3C,IAAI,UAAU;EACZ,OAAO,KAAK;CACd;CAEA,YAAY;EACV,KAAK,WAAW;EAEhB,WAAW,WAAW,IAAI,IAAI;EAC9B,cAAc;CAChB;CAEA,UAAU;EACR,KAAK,WAAW;EAChB,KAAK,MAAM;CACb;AACF;AAEA,MAAM,sBAAsB;CAC1B,IAAI,WAAW,aAAa;CAC5B,WAAW,cAAc;CACzB,kBAAkB;AACpB;AAEA,MAAM,uBAAuB;CAC3B,IAAI;EACF,MAAM,SAAS,CAAC;EAChB,IAAI,aAAa;EAEjB,KAAK,MAAM,aAAa,WAAW,YAAY;GAC7C,WAAW,WAAW,OAAO,SAAS;GACtC,IAAI,CAAC,UAAU,SAAS;GAExB;GAEA,IAAI,aAAa,iBACf,MAAM,IAAI,MACR,4GAEF;GAGF,IAAI;IACF,UAAU,QAAQ;GACpB,SAAS,OAAO;IACd,OAAO,KAAK,KAAK;GACnB;EACF;EAEA,IAAI,OAAO,SAAS,GAClB,IAAI,OAAO,WAAW,GACpB,MAAM,OAAO;OACR;GACL,KAAK,MAAM,SAAS,QAClB,QAAQ,MAAM,KAAK;GAErB,MAAM,IAAI,eAAe,QAAQ,kCAAkC;EACrE;CAEJ,UAAU;EACR,WAAW,WAAW,MAAM;EAC5B,WAAW,cAAc;CAC3B;AACF;AAIA,MAAM,2BAA2B;CAC/B,IAAI,OAAO,mBAAmB,aAAa;EACzC,MAAM,UAAU,IAAI,eAAe;EACnC,QAAQ,MAAM,YAAY;EAC1B,aAAa,QAAQ,MAAM,YAAY,IAAI;CAC7C;CAEA,aAAa,WAAW,gBAAgB,CAAC;AAC3C,EAAA,CAAG;AAEH,MAAa,gBAAmB,aAAyB;CACvD,MAAM,OAAO;CACb,aAAa;EACX,4BAAY,IAAI,IAAI,CAAC,CAAC;EACtB,aAAa;CACf;CAEA,IAAI;EACF,MAAM,SAAS,SAAS;EACxB,eAAe;EAEf,OAAO;CACT,UAAU;EACR,aAAa;CACf;AACF"}