@assistant-ui/tap 0.5.11 → 0.5.13

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 (125) hide show
  1. package/README.md +21 -364
  2. package/dist/core/ResourceFiber.d.ts +9 -5
  3. package/dist/core/ResourceFiber.d.ts.map +1 -1
  4. package/dist/core/ResourceFiber.js +47 -45
  5. package/dist/core/ResourceFiber.js.map +1 -1
  6. package/dist/core/context.d.ts +7 -5
  7. package/dist/core/context.d.ts.map +1 -1
  8. package/dist/core/context.js +16 -15
  9. package/dist/core/context.js.map +1 -1
  10. package/dist/core/createResourceRoot.d.ts +8 -4
  11. package/dist/core/createResourceRoot.d.ts.map +1 -1
  12. package/dist/core/createResourceRoot.js +25 -26
  13. package/dist/core/createResourceRoot.js.map +1 -1
  14. package/dist/core/helpers/callResourceFn.d.ts +1 -2
  15. package/dist/core/helpers/callResourceFn.js +15 -13
  16. package/dist/core/helpers/callResourceFn.js.map +1 -1
  17. package/dist/core/helpers/commit.d.ts +7 -3
  18. package/dist/core/helpers/commit.d.ts.map +1 -1
  19. package/dist/core/helpers/commit.js +33 -50
  20. package/dist/core/helpers/commit.js.map +1 -1
  21. package/dist/core/helpers/env.d.ts +4 -1
  22. package/dist/core/helpers/env.d.ts.map +1 -1
  23. package/dist/core/helpers/env.js +5 -2
  24. package/dist/core/helpers/env.js.map +1 -1
  25. package/dist/core/helpers/execution-context.d.ts +8 -4
  26. package/dist/core/helpers/execution-context.d.ts.map +1 -1
  27. package/dist/core/helpers/execution-context.js +22 -27
  28. package/dist/core/helpers/execution-context.js.map +1 -1
  29. package/dist/core/helpers/root.d.ts +10 -6
  30. package/dist/core/helpers/root.d.ts.map +1 -1
  31. package/dist/core/helpers/root.js +45 -48
  32. package/dist/core/helpers/root.js.map +1 -1
  33. package/dist/core/resource.d.ts +8 -4
  34. package/dist/core/resource.d.ts.map +1 -1
  35. package/dist/core/resource.js +13 -9
  36. package/dist/core/resource.js.map +1 -1
  37. package/dist/core/scheduler.d.ts +11 -9
  38. package/dist/core/scheduler.d.ts.map +1 -1
  39. package/dist/core/scheduler.js +70 -83
  40. package/dist/core/scheduler.js.map +1 -1
  41. package/dist/core/types.d.ts +56 -55
  42. package/dist/core/types.d.ts.map +1 -1
  43. package/dist/core/types.js +0 -2
  44. package/dist/core/withKey.d.ts +6 -2
  45. package/dist/core/withKey.d.ts.map +1 -1
  46. package/dist/core/withKey.js +9 -2
  47. package/dist/core/withKey.js.map +1 -1
  48. package/dist/hooks/tap-callback.d.ts +4 -1
  49. package/dist/hooks/tap-callback.d.ts.map +1 -1
  50. package/dist/hooks/tap-callback.js +6 -3
  51. package/dist/hooks/tap-callback.js.map +1 -1
  52. package/dist/hooks/tap-const.d.ts +4 -1
  53. package/dist/hooks/tap-const.d.ts.map +1 -1
  54. package/dist/hooks/tap-const.js +7 -3
  55. package/dist/hooks/tap-const.js.map +1 -1
  56. package/dist/hooks/tap-effect-event.d.ts +4 -1
  57. package/dist/hooks/tap-effect-event.d.ts.map +1 -1
  58. package/dist/hooks/tap-effect-event.js +31 -29
  59. package/dist/hooks/tap-effect-event.js.map +1 -1
  60. package/dist/hooks/tap-effect.d.ts +8 -5
  61. package/dist/hooks/tap-effect.d.ts.map +1 -1
  62. package/dist/hooks/tap-effect.js +35 -45
  63. package/dist/hooks/tap-effect.js.map +1 -1
  64. package/dist/hooks/tap-memo.d.ts +4 -1
  65. package/dist/hooks/tap-memo.d.ts.map +1 -1
  66. package/dist/hooks/tap-memo.js +17 -13
  67. package/dist/hooks/tap-memo.js.map +1 -1
  68. package/dist/hooks/tap-reducer.d.ts +7 -5
  69. package/dist/hooks/tap-reducer.d.ts.map +1 -1
  70. package/dist/hooks/tap-reducer.js +70 -76
  71. package/dist/hooks/tap-reducer.js.map +1 -1
  72. package/dist/hooks/tap-ref.d.ts +9 -6
  73. package/dist/hooks/tap-ref.d.ts.map +1 -1
  74. package/dist/hooks/tap-ref.js +7 -5
  75. package/dist/hooks/tap-ref.js.map +1 -1
  76. package/dist/hooks/tap-resource.d.ts +7 -3
  77. package/dist/hooks/tap-resource.d.ts.map +1 -1
  78. package/dist/hooks/tap-resource.js +31 -22
  79. package/dist/hooks/tap-resource.js.map +1 -1
  80. package/dist/hooks/tap-resources.d.ts +6 -2
  81. package/dist/hooks/tap-resources.d.ts.map +1 -1
  82. package/dist/hooks/tap-resources.js +74 -96
  83. package/dist/hooks/tap-resources.js.map +1 -1
  84. package/dist/hooks/tap-state.d.ts +7 -7
  85. package/dist/hooks/tap-state.d.ts.map +1 -1
  86. package/dist/hooks/tap-state.js +7 -5
  87. package/dist/hooks/tap-state.js.map +1 -1
  88. package/dist/hooks/utils/depsShallowEqual.d.ts +4 -1
  89. package/dist/hooks/utils/depsShallowEqual.d.ts.map +1 -1
  90. package/dist/hooks/utils/depsShallowEqual.js +8 -8
  91. package/dist/hooks/utils/depsShallowEqual.js.map +1 -1
  92. package/dist/hooks/utils/tapHook.d.ts +8 -4
  93. package/dist/hooks/utils/tapHook.d.ts.map +1 -1
  94. package/dist/hooks/utils/tapHook.js +17 -20
  95. package/dist/hooks/utils/tapHook.js.map +1 -1
  96. package/dist/index.d.ts +18 -18
  97. package/dist/index.js +17 -23
  98. package/dist/react/index.d.ts +2 -2
  99. package/dist/react/index.js +2 -2
  100. package/dist/react/use-resource.d.ts +6 -2
  101. package/dist/react/use-resource.d.ts.map +1 -1
  102. package/dist/react/use-resource.js +41 -36
  103. package/dist/react/use-resource.js.map +1 -1
  104. package/dist/tapResourceRoot.d.ts +18 -14
  105. package/dist/tapResourceRoot.d.ts.map +1 -1
  106. package/dist/tapResourceRoot.js +68 -77
  107. package/dist/tapResourceRoot.js.map +1 -1
  108. package/package.json +8 -8
  109. package/src/__tests__/basic/tapEffect.basic.test.ts +4 -1
  110. package/src/__tests__/errors/errors.effect-errors.test.ts +3 -4
  111. package/src/__tests__/lifecycle/lifecycle.dependencies.test.ts +1 -1
  112. package/src/__tests__/strictmode/react-strictmode-behavior.test.tsx +1 -0
  113. package/src/__tests__/strictmode/react-strictmode-rerender-sources.test.tsx +17 -5
  114. package/src/__tests__/strictmode/tap-strictmode-rerender-sources.test.ts +1 -0
  115. package/src/core/types.ts +1 -2
  116. package/src/hooks/tap-callback.ts +1 -1
  117. package/src/hooks/tap-resource.ts +2 -2
  118. package/src/hooks/tap-resources.ts +1 -1
  119. package/src/react/use-resource.ts +2 -2
  120. package/dist/core/helpers/callResourceFn.d.ts.map +0 -1
  121. package/dist/core/types.js.map +0 -1
  122. package/dist/index.d.ts.map +0 -1
  123. package/dist/index.js.map +0 -1
  124. package/dist/react/index.d.ts.map +0 -1
  125. package/dist/react/index.js.map +0 -1
@@ -1,16 +1,20 @@
1
- import type { ResourceElement } from "./core/types.js";
2
- export declare namespace tapResourceRoot {
3
- type Unsubscribe = () => void;
4
- interface SubscribableResource<TState> {
5
- /**
6
- * Get the current state of the store.
7
- */
8
- getValue(): TState;
9
- /**
10
- * Subscribe to the store.
11
- */
12
- subscribe(listener: () => void): Unsubscribe;
13
- }
1
+ import { ResourceElement } from "./core/types.js";
2
+
3
+ //#region src/tapResourceRoot.d.ts
4
+ declare namespace tapResourceRoot {
5
+ type Unsubscribe = () => void;
6
+ interface SubscribableResource<TState> {
7
+ /**
8
+ * Get the current state of the store.
9
+ */
10
+ getValue(): TState;
11
+ /**
12
+ * Subscribe to the store.
13
+ */
14
+ subscribe(listener: () => void): Unsubscribe;
15
+ }
14
16
  }
15
- export declare const tapResourceRoot: <TState>(element: ResourceElement<TState>) => tapResourceRoot.SubscribableResource<TState>;
17
+ declare const tapResourceRoot: <TState>(element: ResourceElement<TState>) => tapResourceRoot.SubscribableResource<TState>;
18
+ //#endregion
19
+ export { tapResourceRoot };
16
20
  //# sourceMappingURL=tapResourceRoot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tapResourceRoot.d.ts","sourceRoot":"","sources":["../src/tapResourceRoot.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAgB,eAAe,EAAE,wBAAqB;AAQlE,yBAAiB,eAAe,CAAC;IAC/B,KAAY,WAAW,GAAG,MAAM,IAAI,CAAC;IAErC,UAAiB,oBAAoB,CAAC,MAAM;QAC1C;;WAEG;QACH,QAAQ,IAAI,MAAM,CAAC;QAEnB;;WAEG;QACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;KAC9C;CACF;AAID,eAAO,MAAM,eAAe,GAAI,MAAM,EACpC,SAAS,eAAe,CAAC,MAAM,CAAC,KAC/B,eAAe,CAAC,oBAAoB,CAAC,MAAM,CA4F7C,CAAC"}
1
+ {"version":3,"file":"tapResourceRoot.d.ts","names":[],"sources":["../src/tapResourceRoot.ts"],"mappings":";;;kBAoBiB,eAAA;EAAA,KACH,WAAA;EAAA,UAEK,oBAAA;IAHa;;;IAO5B,QAAA,IAAY,MAAA;IAJG;;;IASf,SAAA,CAAU,QAAA,eAAuB,WAAW;EAAA;AAAA;AAAA,cAMnC,eAAA,WACX,OAAA,EAAS,eAAA,CAAgB,MAAA,MACxB,eAAA,CAAgB,oBAAA,CAAqB,MAAA"}
@@ -1,82 +1,73 @@
1
- import { commitResourceFiber, createResourceFiber, renderResourceFiber, unmountResourceFiber, } from "./core/ResourceFiber.js";
2
- import { UpdateScheduler } from "./core/scheduler.js";
1
+ import { isDevelopment } from "./core/helpers/env.js";
2
+ import { commitRoot, createResourceFiberRoot, setRootVersion } from "./core/helpers/root.js";
3
+ import { tapEffect } from "./hooks/tap-effect.js";
4
+ import { tapRef } from "./hooks/tap-ref.js";
3
5
  import { tapConst } from "./hooks/tap-const.js";
4
6
  import { tapMemo } from "./hooks/tap-memo.js";
5
- import { tapEffect } from "./hooks/tap-effect.js";
6
7
  import { tapEffectEvent } from "./hooks/tap-effect-event.js";
7
- import { tapRef } from "./hooks/tap-ref.js";
8
- import { isDevelopment } from "./core/helpers/env.js";
9
- import { commitRoot, createResourceFiberRoot, setRootVersion, } from "./core/helpers/root.js";
10
- // currently we never reset the root, because rollbakcs are not supported in tapResourceRoot
11
- export const tapResourceRoot = (element) => {
12
- const scheduler = tapConst(() => new UpdateScheduler(() => handleUpdate(null)), []);
13
- const queue = tapConst(() => [], []);
14
- const fiber = tapMemo(() => {
15
- void element.key;
16
- return createResourceFiber(element.type, createResourceFiberRoot((callback) => {
17
- if (!scheduler.isDirty && !callback())
18
- return;
19
- queue.push(callback);
20
- scheduler.markDirty();
21
- }));
22
- }, [element.type, element.key]);
23
- setRootVersion(fiber.root, fiber.root.committedVersion);
24
- const render = renderResourceFiber(fiber, element.props);
25
- const isMountedRef = tapRef(false);
26
- const committedPropsRef = tapRef(element.props);
27
- const valueRef = tapRef(render.output);
28
- const subscribers = tapConst(() => new Set(), []);
29
- const handleUpdate = tapEffectEvent((render) => {
30
- if (render === null) {
31
- setRootVersion(fiber.root, 2);
32
- setRootVersion(fiber.root, 1);
33
- queue.forEach((callback) => {
34
- if (isDevelopment && fiber.devStrictMode) {
35
- callback();
36
- }
37
- callback();
38
- });
39
- if (isDevelopment && fiber.devStrictMode) {
40
- void renderResourceFiber(fiber, committedPropsRef.current);
41
- }
42
- render = renderResourceFiber(fiber, committedPropsRef.current);
43
- }
44
- if (scheduler.isDirty)
45
- throw new Error("Scheduler is dirty, this should never happen");
46
- commitRoot(fiber.root);
47
- queue.length = 0;
48
- if (isMountedRef.current) {
49
- commitResourceFiber(fiber, render);
50
- }
51
- if (scheduler.isDirty || valueRef.current === render.output)
52
- return;
53
- valueRef.current = render.output;
54
- // biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
55
- subscribers.forEach((callback) => callback());
56
- });
57
- tapEffect(() => {
58
- isMountedRef.current = true;
59
- return () => {
60
- isMountedRef.current = false;
61
- unmountResourceFiber(fiber);
62
- };
63
- }, [fiber]);
64
- tapEffect(() => {
65
- committedPropsRef.current = render.props;
66
- commitRoot(fiber.root);
67
- commitResourceFiber(fiber, render);
68
- if (scheduler.isDirty || valueRef.current === render.output)
69
- return;
70
- valueRef.current = render.output;
71
- // biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
72
- subscribers.forEach((callback) => callback());
73
- });
74
- return tapMemo(() => ({
75
- getValue: () => valueRef.current,
76
- subscribe: (listener) => {
77
- subscribers.add(listener);
78
- return () => subscribers.delete(listener);
79
- },
80
- }), []);
8
+ import { commitResourceFiber, createResourceFiber, renderResourceFiber, unmountResourceFiber } from "./core/ResourceFiber.js";
9
+ import { UpdateScheduler } from "./core/scheduler.js";
10
+ //#region src/tapResourceRoot.ts
11
+ const tapResourceRoot = (element) => {
12
+ const scheduler = tapConst(() => new UpdateScheduler(() => handleUpdate(null)), []);
13
+ const queue = tapConst(() => [], []);
14
+ const fiber = tapMemo(() => {
15
+ element.key;
16
+ return createResourceFiber(element.type, createResourceFiberRoot((callback) => {
17
+ if (!scheduler.isDirty && !callback()) return;
18
+ queue.push(callback);
19
+ scheduler.markDirty();
20
+ }));
21
+ }, [element.type, element.key]);
22
+ setRootVersion(fiber.root, fiber.root.committedVersion);
23
+ const render = renderResourceFiber(fiber, element.props);
24
+ const isMountedRef = tapRef(false);
25
+ const committedPropsRef = tapRef(element.props);
26
+ const valueRef = tapRef(render.output);
27
+ const subscribers = tapConst(() => /* @__PURE__ */ new Set(), []);
28
+ const handleUpdate = tapEffectEvent((render) => {
29
+ if (render === null) {
30
+ setRootVersion(fiber.root, 2);
31
+ setRootVersion(fiber.root, 1);
32
+ queue.forEach((callback) => {
33
+ if (isDevelopment && fiber.devStrictMode) callback();
34
+ callback();
35
+ });
36
+ if (isDevelopment && fiber.devStrictMode) renderResourceFiber(fiber, committedPropsRef.current);
37
+ render = renderResourceFiber(fiber, committedPropsRef.current);
38
+ }
39
+ if (scheduler.isDirty) throw new Error("Scheduler is dirty, this should never happen");
40
+ commitRoot(fiber.root);
41
+ queue.length = 0;
42
+ if (isMountedRef.current) commitResourceFiber(fiber, render);
43
+ if (scheduler.isDirty || valueRef.current === render.output) return;
44
+ valueRef.current = render.output;
45
+ subscribers.forEach((callback) => callback());
46
+ });
47
+ tapEffect(() => {
48
+ isMountedRef.current = true;
49
+ return () => {
50
+ isMountedRef.current = false;
51
+ unmountResourceFiber(fiber);
52
+ };
53
+ }, [fiber]);
54
+ tapEffect(() => {
55
+ committedPropsRef.current = render.props;
56
+ commitRoot(fiber.root);
57
+ commitResourceFiber(fiber, render);
58
+ if (scheduler.isDirty || valueRef.current === render.output) return;
59
+ valueRef.current = render.output;
60
+ subscribers.forEach((callback) => callback());
61
+ });
62
+ return tapMemo(() => ({
63
+ getValue: () => valueRef.current,
64
+ subscribe: (listener) => {
65
+ subscribers.add(listener);
66
+ return () => subscribers.delete(listener);
67
+ }
68
+ }), []);
81
69
  };
70
+ //#endregion
71
+ export { tapResourceRoot };
72
+
82
73
  //# sourceMappingURL=tapResourceRoot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tapResourceRoot.js","sourceRoot":"","sources":["../src/tapResourceRoot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tapResourceRoot.js","names":[],"sources":["../src/tapResourceRoot.ts"],"sourcesContent":["import {\n commitResourceFiber,\n createResourceFiber,\n renderResourceFiber,\n unmountResourceFiber,\n} from \"./core/ResourceFiber\";\nimport { UpdateScheduler } from \"./core/scheduler\";\nimport { tapConst } from \"./hooks/tap-const\";\nimport { tapMemo } from \"./hooks/tap-memo\";\nimport { tapEffect } from \"./hooks/tap-effect\";\nimport { tapEffectEvent } from \"./hooks/tap-effect-event\";\nimport { tapRef } from \"./hooks/tap-ref\";\nimport type { RenderResult, ResourceElement } from \"./core/types\";\nimport { isDevelopment } from \"./core/helpers/env\";\nimport {\n commitRoot,\n createResourceFiberRoot,\n setRootVersion,\n} from \"./core/helpers/root\";\n\nexport namespace tapResourceRoot {\n export type Unsubscribe = () => void;\n\n export interface SubscribableResource<TState> {\n /**\n * Get the current state of the store.\n */\n getValue(): TState;\n\n /**\n * Subscribe to the store.\n */\n subscribe(listener: () => void): Unsubscribe;\n }\n}\n\n// currently we never reset the root, because rollbakcs are not supported in tapResourceRoot\n\nexport const tapResourceRoot = <TState>(\n element: ResourceElement<TState>,\n): tapResourceRoot.SubscribableResource<TState> => {\n const scheduler = tapConst(\n () => new UpdateScheduler(() => handleUpdate(null)),\n [],\n );\n const queue = tapConst(() => [] as (() => void)[], []);\n\n const fiber = tapMemo(() => {\n void element.key;\n\n return createResourceFiber(\n element.type,\n createResourceFiberRoot((callback) => {\n if (!scheduler.isDirty && !callback()) return;\n queue.push(callback);\n scheduler.markDirty();\n }),\n );\n }, [element.type, element.key]);\n\n setRootVersion(fiber.root, fiber.root.committedVersion);\n const render = renderResourceFiber(fiber, element.props);\n\n const isMountedRef = tapRef(false);\n const committedPropsRef = tapRef(element.props);\n const valueRef = tapRef<TState>(render.output);\n const subscribers = tapConst(() => new Set<() => void>(), []);\n const handleUpdate = tapEffectEvent((render: RenderResult | null) => {\n if (render === null) {\n setRootVersion(fiber.root, 2);\n setRootVersion(fiber.root, 1);\n\n queue.forEach((callback) => {\n if (isDevelopment && fiber.devStrictMode) {\n callback();\n }\n\n callback();\n });\n\n if (isDevelopment && fiber.devStrictMode) {\n void renderResourceFiber(fiber, committedPropsRef.current);\n }\n\n render = renderResourceFiber(fiber, committedPropsRef.current);\n }\n\n if (scheduler.isDirty)\n throw new Error(\"Scheduler is dirty, this should never happen\");\n\n commitRoot(fiber.root);\n queue.length = 0;\n\n if (isMountedRef.current) {\n commitResourceFiber(fiber, render);\n }\n\n if (scheduler.isDirty || valueRef.current === render.output) return;\n valueRef.current = render.output;\n // biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return\n subscribers.forEach((callback) => callback());\n });\n\n tapEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n unmountResourceFiber(fiber);\n };\n }, [fiber]);\n\n tapEffect(() => {\n committedPropsRef.current = render.props;\n commitRoot(fiber.root);\n commitResourceFiber(fiber, render);\n\n if (scheduler.isDirty || valueRef.current === render.output) return;\n valueRef.current = render.output;\n // biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return\n subscribers.forEach((callback) => callback());\n });\n\n return tapMemo(\n () => ({\n getValue: () => valueRef.current,\n subscribe: (listener: () => void) => {\n subscribers.add(listener);\n return () => subscribers.delete(listener);\n },\n }),\n [],\n );\n};\n"],"mappings":";;;;;;;;;;AAsCA,MAAa,mBACX,YACiD;CACjD,MAAM,YAAY,eACV,IAAI,sBAAsB,aAAa,IAAI,CAAC,GAClD,CAAC,CACH;CACA,MAAM,QAAQ,eAAe,CAAC,GAAqB,CAAC,CAAC;CAErD,MAAM,QAAQ,cAAc;EAC1B,QAAa;EAEb,OAAO,oBACL,QAAQ,MACR,yBAAyB,aAAa;GACpC,IAAI,CAAC,UAAU,WAAW,CAAC,SAAS,GAAG;GACvC,MAAM,KAAK,QAAQ;GACnB,UAAU,UAAU;EACtB,CAAC,CACH;CACF,GAAG,CAAC,QAAQ,MAAM,QAAQ,GAAG,CAAC;CAE9B,eAAe,MAAM,MAAM,MAAM,KAAK,gBAAgB;CACtD,MAAM,SAAS,oBAAoB,OAAO,QAAQ,KAAK;CAEvD,MAAM,eAAe,OAAO,KAAK;CACjC,MAAM,oBAAoB,OAAO,QAAQ,KAAK;CAC9C,MAAM,WAAW,OAAe,OAAO,MAAM;CAC7C,MAAM,cAAc,+BAAe,IAAI,IAAgB,GAAG,CAAC,CAAC;CAC5D,MAAM,eAAe,gBAAgB,WAAgC;EACnE,IAAI,WAAW,MAAM;GACnB,eAAe,MAAM,MAAM,CAAC;GAC5B,eAAe,MAAM,MAAM,CAAC;GAE5B,MAAM,SAAS,aAAa;IAC1B,IAAI,iBAAiB,MAAM,eACzB,SAAS;IAGX,SAAS;GACX,CAAC;GAED,IAAI,iBAAiB,MAAM,eACzB,oBAAyB,OAAO,kBAAkB,OAAO;GAG3D,SAAS,oBAAoB,OAAO,kBAAkB,OAAO;EAC/D;EAEA,IAAI,UAAU,SACZ,MAAM,IAAI,MAAM,8CAA8C;EAEhE,WAAW,MAAM,IAAI;EACrB,MAAM,SAAS;EAEf,IAAI,aAAa,SACf,oBAAoB,OAAO,MAAM;EAGnC,IAAI,UAAU,WAAW,SAAS,YAAY,OAAO,QAAQ;EAC7D,SAAS,UAAU,OAAO;EAE1B,YAAY,SAAS,aAAa,SAAS,CAAC;CAC9C,CAAC;CAED,gBAAgB;EACd,aAAa,UAAU;EACvB,aAAa;GACX,aAAa,UAAU;GACvB,qBAAqB,KAAK;EAC5B;CACF,GAAG,CAAC,KAAK,CAAC;CAEV,gBAAgB;EACd,kBAAkB,UAAU,OAAO;EACnC,WAAW,MAAM,IAAI;EACrB,oBAAoB,OAAO,MAAM;EAEjC,IAAI,UAAU,WAAW,SAAS,YAAY,OAAO,QAAQ;EAC7D,SAAS,UAAU,OAAO;EAE1B,YAAY,SAAS,aAAa,SAAS,CAAC;CAC9C,CAAC;CAED,OAAO,eACE;EACL,gBAAgB,SAAS;EACzB,YAAY,aAAyB;GACnC,YAAY,IAAI,QAAQ;GACxB,aAAa,YAAY,OAAO,QAAQ;EAC1C;CACF,IACA,CAAC,CACH;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/tap",
3
- "version": "0.5.11",
3
+ "version": "0.5.13",
4
4
  "description": "Zero-dependency reactive state management inspired by React hooks",
5
5
  "keywords": [
6
6
  "state-management",
@@ -46,15 +46,15 @@
46
46
  "devDependencies": {
47
47
  "@testing-library/dom": "^10.4.1",
48
48
  "@testing-library/react": "^16.3.2",
49
- "@types/node": "^25.6.0",
50
- "@types/react": "^19.2.14",
49
+ "@types/node": "^25.9.1",
50
+ "@types/react": "^19.2.15",
51
51
  "@types/react-dom": "^19.2.3",
52
- "@vitest/ui": "^4.1.5",
52
+ "@vitest/ui": "^4.1.7",
53
53
  "jsdom": "^29.1.1",
54
- "react": "^19.2.5",
55
- "react-dom": "^19.2.5",
56
- "vitest": "^4.1.5",
57
- "@assistant-ui/x-buildutils": "0.0.7"
54
+ "react": "^19.2.6",
55
+ "react-dom": "^19.2.6",
56
+ "vitest": "^4.1.7",
57
+ "@assistant-ui/x-buildutils": "0.0.9"
58
58
  },
59
59
  "publishConfig": {
60
60
  "access": "public",
@@ -91,15 +91,18 @@ describe("tapEffect - Basic Functionality", () => {
91
91
  describe("Multiple Effects", () => {
92
92
  it("should execute multiple effects in registration order", () => {
93
93
  const executionOrder: string[] = [];
94
- const effects = [
94
+ const effects: tapEffect.EffectCallback[] = [
95
95
  () => {
96
96
  executionOrder.push("effect1");
97
+ return undefined;
97
98
  },
98
99
  () => {
99
100
  executionOrder.push("effect2");
101
+ return undefined;
100
102
  },
101
103
  () => {
102
104
  executionOrder.push("effect3");
105
+ return undefined;
103
106
  },
104
107
  ];
105
108
 
@@ -1,4 +1,4 @@
1
- /** biome-ignore-all lint/correctness/useExhaustiveDependencies: tests */
1
+ /* oxlint-disable tap-hooks/exhaustive-deps -- tests deliberately exercise invalid dep arrays */
2
2
  import { describe, it, expect, vi } from "vitest";
3
3
  import { tapEffect } from "../../hooks/tap-effect";
4
4
  import { tapState } from "../../hooks/tap-state";
@@ -83,9 +83,8 @@ describe("Errors - Effect Errors", () => {
83
83
  });
84
84
 
85
85
  // Should throw aggregate error
86
- expect(() =>
87
- renderTest(resource, undefined),
88
- ).toThrowErrorMatchingInlineSnapshot(`
86
+ expect(() => renderTest(resource, undefined))
87
+ .toThrowErrorMatchingInlineSnapshot(`
89
88
  [AggregateError: Errors during commit]
90
89
  `);
91
90
  expect(goodEffect).toHaveBeenCalledTimes(1);
@@ -1,4 +1,4 @@
1
- /** biome-ignore-all lint/correctness/useExhaustiveDependencies: tests */
1
+ /* oxlint-disable tap-hooks/exhaustive-deps -- tests deliberately exercise invalid dep arrays */
2
2
  import { describe, it, expect, vi } from "vitest";
3
3
  import { tapEffect } from "../../hooks/tap-effect";
4
4
  import { tapState } from "../../hooks/tap-state";
@@ -2,6 +2,7 @@
2
2
  * Tests to verify React's strict mode behavior
3
3
  * These tests verify React's own behavior, not tap's implementation
4
4
  */
5
+ /* oxlint-disable tap-hooks/exhaustive-deps -- intentional missing-dep patterns for strict-mode tests */
5
6
 
6
7
  import { describe, it, expect } from "vitest";
7
8
  import { render } from "@testing-library/react";
@@ -3,9 +3,9 @@
3
3
  * for different sources of setState calls
4
4
  */
5
5
 
6
- import { describe, it, expect } from "vitest";
6
+ import { describe, it, expect, vi, afterEach } from "vitest";
7
7
  import { render, fireEvent, waitFor } from "@testing-library/react";
8
- import { StrictMode, useState, useEffect } from "react";
8
+ import { StrictMode, useState, useEffect, useLayoutEffect } from "react";
9
9
 
10
10
  describe("React Strict Mode - Rerender Sources", () => {
11
11
  describe("Source 1: Initial render", () => {
@@ -190,7 +190,17 @@ describe("React Strict Mode - Rerender Sources", () => {
190
190
  });
191
191
 
192
192
  describe("Source 5: setState in setTimeout", () => {
193
+ afterEach(() => {
194
+ vi.useRealTimers();
195
+ });
196
+
193
197
  it("should double-render AND double-call setTimeout callback (React 19)", async () => {
198
+ // Use fake timers so both strict-mode setTimeout callbacks fire
199
+ // synchronously before React gets a chance to flush a re-render
200
+ // between them. Without this, slow CI can process the first
201
+ // setTimeout, run its renders, and only then fire the second.
202
+ vi.useFakeTimers();
203
+
194
204
  const events: string[] = [];
195
205
 
196
206
  function TestComponent() {
@@ -215,9 +225,12 @@ describe("React Strict Mode - Rerender Sources", () => {
215
225
  </StrictMode>,
216
226
  );
217
227
 
218
- // Wait for setTimeout
228
+ // Fire both setTimeout callbacks synchronously via fake timers
229
+ vi.advanceTimersByTime(10);
230
+ // Restore real timers and wait for React's scheduler (MessageChannel) to flush
231
+ vi.useRealTimers();
219
232
  await waitFor(() => {
220
- expect(events).toContain("setTimeout");
233
+ expect(events).toHaveLength(6);
221
234
  });
222
235
 
223
236
  // ACTUAL: setTimeout callback runs TWICE and renders are DOUBLED
@@ -353,7 +366,6 @@ describe("React Strict Mode - Rerender Sources", () => {
353
366
  describe("Source 8: setState in useLayoutEffect", () => {
354
367
  it("should double-render after setState in useLayoutEffect", () => {
355
368
  const events: string[] = [];
356
- const { useLayoutEffect } = require("react");
357
369
 
358
370
  function TestComponent() {
359
371
  const [count, setCount] = useState(0);
@@ -2,6 +2,7 @@
2
2
  * Tests to verify when tap strict mode causes double-rendering
3
3
  * These tests should mirror the React strict mode behavior
4
4
  */
5
+ /* oxlint-disable tap-hooks/exhaustive-deps -- empty dep arrays are part of the test scenarios */
5
6
 
6
7
  import { afterEach, describe, it, expect, vi } from "vitest";
7
8
  import { resource } from "../../core/resource";
package/src/core/types.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { tapEffect } from "../hooks/tap-effect";
2
- import type { fnSymbol } from "./helpers/callResourceFn";
3
2
 
4
3
  export type ResourceElement<R, P = any> = {
5
- readonly type: Resource<R, P> & { [fnSymbol]: (props: P) => R };
4
+ readonly type: Resource<R, P>;
6
5
  readonly props: P;
7
6
  readonly key?: string | number;
8
7
  };
@@ -4,6 +4,6 @@ export const tapCallback = <T extends (...args: any[]) => any>(
4
4
  fn: T,
5
5
  deps: readonly unknown[],
6
6
  ): T => {
7
- // biome-ignore lint/correctness/useExhaustiveDependencies: user provided deps instead of callback identity
7
+ // oxlint-disable-next-line tap-hooks/exhaustive-deps -- user-provided dep array forwarded verbatim
8
8
  return tapMemo(() => fn, deps);
9
9
  };
@@ -32,9 +32,9 @@ export function tapResource<E extends ResourceElement<any, any>>(
32
32
  }, [element.type, element.key, parentFiber]);
33
33
 
34
34
  const result = propsDeps
35
- ? // biome-ignore lint/correctness/useExhaustiveDependencies: user provided deps instead of prop identity
36
- tapMemo(
35
+ ? tapMemo(
37
36
  () => renderResourceFiber(fiber, element.props),
37
+ // oxlint-disable-next-line tap-hooks/exhaustive-deps -- props identity replaced by user-provided deps
38
38
  [fiber, ...propsDeps, versionRef.current],
39
39
  )
40
40
  : renderResourceFiber(fiber, element.props);
@@ -43,7 +43,7 @@ export function tapResources<E extends ResourceElement<any, any>>(
43
43
  const fibers = tapConst(() => new Map<string | number, FiberState>(), []);
44
44
 
45
45
  const getElementsMemo = getElementsDeps
46
- ? // biome-ignore lint/correctness/useExhaustiveDependencies: library code
46
+ ? // oxlint-disable-next-line tap-hooks/exhaustive-deps -- deps forwarded by caller
47
47
  tapCallback(getElements, getElementsDeps)
48
48
  : getElements;
49
49
 
@@ -20,10 +20,10 @@ import {
20
20
  const useDevStrictMode = () => {
21
21
  if (!isDevelopment) return null;
22
22
 
23
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
23
+ // oxlint-disable-next-line react-hooks/rules-of-hooks -- isDevelopment is a build-time constant, so this branch is stable per build
24
24
  const count = useRef(0);
25
25
  const isFirstRender = count.current === 0;
26
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
26
+ // oxlint-disable-next-line react-hooks/rules-of-hooks -- isDevelopment is a build-time constant, so this branch is stable per build
27
27
  useState(() => count.current++);
28
28
  if (count.current !== 2) return null;
29
29
  return isFirstRender ? ("child" as const) : ("root" as const);
@@ -1 +0,0 @@
1
- {"version":3,"file":"callResourceFn.d.ts","sourceRoot":"","sources":["../../../src/core/helpers/callResourceFn.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,2BAAwB;AAC3C,OAAO,EAAE,OAAO,EAAE,0BAAuB;AAGzC,OAAO,EAAE,QAAQ,EAAE,6BAA0B;AAC7C,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,+BAA4B;AAC7E,OAAO,EAAE,SAAS,EAAE,8BAA2B;AAG/C,OAAO,EAAE,MAAM,EAAE,2BAAwB;AACzC,OAAO,EAAE,QAAQ,EAAE,6BAA0B;AAC7C,OAAO,EAAE,OAAO,EAAE,4BAAyB;AAC3C,OAAO,EAAE,WAAW,EAAE,gCAA6B;AACnD,OAAO,EAAE,cAAc,EAAE,oCAAiC;AAG1D,OAAO,EAAE,WAAW,EAAE,gCAA6B;AACnD,OAAO,EAAE,YAAY,EAAE,iCAA8B;AAGrD,OAAO,EAAE,eAAe,EAAE,6BAA0B;AAGpD,OAAO,EAAE,kBAAkB,EAAE,qCAAkC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,4BAAyB;AAGtD,OAAO,EACL,qBAAqB,EACrB,GAAG,EACH,mBAAmB,GACpB,0BAAuB;AAGxB,YAAY,EACV,QAAQ,EACR,qBAAqB,EACrB,eAAe,GAChB,wBAAqB"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,2BAAwB;AAC3C,OAAO,EAAE,OAAO,EAAE,0BAAuB;AAEzC,kBAAkB;AAClB,OAAO,EAAE,QAAQ,EAAE,6BAA0B;AAC7C,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,+BAA4B;AAC7E,OAAO,EAAE,SAAS,EAAE,8BAA2B;AAE/C,gBAAgB;AAChB,OAAO,EAAE,MAAM,EAAE,2BAAwB;AACzC,OAAO,EAAE,QAAQ,EAAE,6BAA0B;AAC7C,OAAO,EAAE,OAAO,EAAE,4BAAyB;AAC3C,OAAO,EAAE,WAAW,EAAE,gCAA6B;AACnD,OAAO,EAAE,cAAc,EAAE,oCAAiC;AAE1D,YAAY;AACZ,OAAO,EAAE,WAAW,EAAE,gCAA6B;AACnD,OAAO,EAAE,YAAY,EAAE,iCAA8B;AAErD,eAAe;AACf,OAAO,EAAE,eAAe,EAAE,6BAA0B;AAEpD,aAAa;AACb,OAAO,EAAE,kBAAkB,EAAE,qCAAkC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,4BAAyB;AAEtD,UAAU;AACV,OAAO,EACL,qBAAqB,EACrB,GAAG,EACH,mBAAmB,GACpB,0BAAuB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,0BAAuB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,0BAAuB"}