@assistant-ui/tap 0.0.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 (82) hide show
  1. package/README.md +252 -0
  2. package/dist/__tests__/test-utils.d.ts +79 -0
  3. package/dist/__tests__/test-utils.d.ts.map +1 -0
  4. package/dist/__tests__/test-utils.js +216 -0
  5. package/dist/__tests__/test-utils.js.map +1 -0
  6. package/dist/core/ResourceFiber.d.ts +6 -0
  7. package/dist/core/ResourceFiber.d.ts.map +1 -0
  8. package/dist/core/ResourceFiber.js +41 -0
  9. package/dist/core/ResourceFiber.js.map +1 -0
  10. package/dist/core/ResourceHandle.d.ts +8 -0
  11. package/dist/core/ResourceHandle.d.ts.map +1 -0
  12. package/dist/core/ResourceHandle.js +64 -0
  13. package/dist/core/ResourceHandle.js.map +1 -0
  14. package/dist/core/commit.d.ts +4 -0
  15. package/dist/core/commit.d.ts.map +1 -0
  16. package/dist/core/commit.js +60 -0
  17. package/dist/core/commit.js.map +1 -0
  18. package/dist/core/execution-context.d.ts +4 -0
  19. package/dist/core/execution-context.d.ts.map +1 -0
  20. package/dist/core/execution-context.js +33 -0
  21. package/dist/core/execution-context.js.map +1 -0
  22. package/dist/core/resource.d.ts +3 -0
  23. package/dist/core/resource.d.ts.map +1 -0
  24. package/dist/core/resource.js +14 -0
  25. package/dist/core/resource.js.map +1 -0
  26. package/dist/core/scheduler.d.ts +14 -0
  27. package/dist/core/scheduler.d.ts.map +1 -0
  28. package/dist/core/scheduler.js +66 -0
  29. package/dist/core/scheduler.js.map +1 -0
  30. package/dist/core/types.d.ts +44 -0
  31. package/dist/core/types.d.ts.map +1 -0
  32. package/dist/core/types.js +1 -0
  33. package/dist/core/types.js.map +1 -0
  34. package/dist/hooks/depsShallowEqual.d.ts +2 -0
  35. package/dist/hooks/depsShallowEqual.d.ts.map +1 -0
  36. package/dist/hooks/depsShallowEqual.js +12 -0
  37. package/dist/hooks/depsShallowEqual.js.map +1 -0
  38. package/dist/hooks/tap-callback.d.ts +2 -0
  39. package/dist/hooks/tap-callback.d.ts.map +1 -0
  40. package/dist/hooks/tap-callback.js +9 -0
  41. package/dist/hooks/tap-callback.js.map +1 -0
  42. package/dist/hooks/tap-effect.d.ts +4 -0
  43. package/dist/hooks/tap-effect.d.ts.map +1 -0
  44. package/dist/hooks/tap-effect.js +32 -0
  45. package/dist/hooks/tap-effect.js.map +1 -0
  46. package/dist/hooks/tap-memo.d.ts +2 -0
  47. package/dist/hooks/tap-memo.d.ts.map +1 -0
  48. package/dist/hooks/tap-memo.js +18 -0
  49. package/dist/hooks/tap-memo.js.map +1 -0
  50. package/dist/hooks/tap-ref.d.ts +7 -0
  51. package/dist/hooks/tap-ref.d.ts.map +1 -0
  52. package/dist/hooks/tap-ref.js +12 -0
  53. package/dist/hooks/tap-ref.js.map +1 -0
  54. package/dist/hooks/tap-rerender.d.ts +2 -0
  55. package/dist/hooks/tap-rerender.d.ts.map +1 -0
  56. package/dist/hooks/tap-rerender.js +10 -0
  57. package/dist/hooks/tap-rerender.js.map +1 -0
  58. package/dist/hooks/tap-resource.d.ts +4 -0
  59. package/dist/hooks/tap-resource.d.ts.map +1 -0
  60. package/dist/hooks/tap-resource.js +33 -0
  61. package/dist/hooks/tap-resource.js.map +1 -0
  62. package/dist/hooks/tap-resources.d.ts +5 -0
  63. package/dist/hooks/tap-resources.d.ts.map +1 -0
  64. package/dist/hooks/tap-resources.js +84 -0
  65. package/dist/hooks/tap-resources.js.map +1 -0
  66. package/dist/hooks/tap-state.d.ts +7 -0
  67. package/dist/hooks/tap-state.d.ts.map +1 -0
  68. package/dist/hooks/tap-state.js +43 -0
  69. package/dist/hooks/tap-state.js.map +1 -0
  70. package/dist/index.d.ts +10 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +20 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/react/index.d.ts +2 -0
  75. package/dist/react/index.d.ts.map +1 -0
  76. package/dist/react/index.js +6 -0
  77. package/dist/react/index.js.map +1 -0
  78. package/dist/react/use-resource.d.ts +3 -0
  79. package/dist/react/use-resource.d.ts.map +1 -0
  80. package/dist/react/use-resource.js +30 -0
  81. package/dist/react/use-resource.js.map +1 -0
  82. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # @assistant-ui/tap
2
+
3
+ Zero-dependency reactive state management inspired by React hooks.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @assistant-ui/tap
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ This library brings React's hooks mental model to state management outside of React components. It provides familiar APIs like `tapState` and `tapEffect` that work exactly like their React counterparts, but can be used anywhere.
14
+
15
+ ## Core Concepts
16
+
17
+ ### Resources
18
+
19
+ Resources are self-contained units of reactive state and logic. They follow the same rules as React hooks:
20
+
21
+ - Hooks must be called in the same order every render
22
+ - Hooks cannot be called conditionally
23
+ - Resources automatically handle cleanup and lifecycle
24
+
25
+ ### Creating Resources
26
+
27
+ ```typescript
28
+ import { createResource, tapState, tapEffect } from "@assistant-ui/tap";
29
+
30
+ // Define a resource using familiar hook patterns
31
+ const Counter = resource(({ incrementBy = 1 }: { incrementBy?: number }) => {
32
+ const [count, setCount] = tapState(0);
33
+
34
+ tapEffect(() => {
35
+ console.log(`Count is now: ${count}`);
36
+ }, [count]);
37
+
38
+ return {
39
+ count,
40
+ increment: () => setCount((c) => c + incrementBy),
41
+ decrement: () => setCount((c) => c - incrementBy),
42
+ };
43
+ });
44
+
45
+ // Create an instance
46
+ const counter = createResource(new Counter({ incrementBy: 2 }));
47
+
48
+ // Subscribe to changes
49
+ const unsubscribe = counter.subscribe(() => {
50
+ console.log("Counter value:", counter.getState().count);
51
+ });
52
+
53
+ // Use the resource
54
+ counter.getState().increment();
55
+ ```
56
+
57
+ ## `resource`
58
+
59
+ Creates a resource element factory. Resource elements are plain objects of the type `{ type: ResourceFn<R, P>, props: P, key?: string | number }`.
60
+
61
+ ```typescript
62
+ const Counter = resource(({ incrementBy = 1 }: { incrementBy?: number }) => {
63
+ const [count, setCount] = tapState(0);
64
+ });
65
+
66
+ // create a Counter element
67
+ const counterEl = new Counter({ incrementBy: 2 });
68
+
69
+ // create a Counter instance
70
+ const counter = createResource(counterEl);
71
+ counter.dispose();
72
+ ```
73
+
74
+ ## Hook APIs
75
+
76
+ ### `tapState`
77
+
78
+ Manages local state within a resource, exactly like React's `useState`.
79
+
80
+ ```typescript
81
+ const [value, setValue] = tapState(initialValue);
82
+ const [value, setValue] = tapState(() => computeInitialValue());
83
+ ```
84
+
85
+ ### `tapEffect`
86
+
87
+ Runs side effects with automatic cleanup, exactly like React's `useEffect`.
88
+
89
+ ```typescript
90
+ tapEffect(() => {
91
+ // Effect logic
92
+ return () => {
93
+ // Cleanup logic
94
+ };
95
+ }, [dependencies]);
96
+ ```
97
+
98
+ ### `tapMemo`
99
+
100
+ Memoizes expensive computations, exactly like React's `useMemo`.
101
+
102
+ ```typescript
103
+ const expensiveValue = tapMemo(() => {
104
+ return computeExpensiveValue(dep1, dep2);
105
+ }, [dep1, dep2]);
106
+ ```
107
+
108
+ ### `tapCallback`
109
+
110
+ Memoizes callbacks to prevent unnecessary re-renders, exactly like React's `useCallback`.
111
+
112
+ ```typescript
113
+ const stableCallback = tapCallback(() => {
114
+ doSomething(value);
115
+ }, [value]);
116
+ ```
117
+
118
+ ### `tapRef`
119
+
120
+ Creates a mutable reference that persists across renders, exactly like React's `useRef`.
121
+
122
+ ```typescript
123
+ // With initial value
124
+ const ref = tapRef(initialValue);
125
+ ref.current = newValue;
126
+
127
+ // Without initial value
128
+ const ref = tapRef<string>(); // ref.current is undefined
129
+ ref.current = "hello";
130
+ ```
131
+
132
+ ### `tapResource`
133
+
134
+ Composes resources together - resources can render other resources.
135
+
136
+ ```typescript
137
+ const Timer = resource(() => {
138
+ const counter = tapResource({ type: Counter, props: { incrementBy: 1 } });
139
+
140
+ tapEffect(() => {
141
+ const interval = setInterval(() => {
142
+ counter.increment();
143
+ }, 1000);
144
+ return () => clearInterval(interval);
145
+ }, []);
146
+
147
+ return counter.count;
148
+ });
149
+ ```
150
+
151
+ ### `tapResources`
152
+
153
+ Renders multiple resources with keys, similar to React's list rendering. All resources must have a unique `key` property.
154
+
155
+ ```typescript
156
+ // Using with createResourceNodeConstructor
157
+ const TodoItem = resource((props: { text: string }) => {
158
+ const [completed, setCompleted] = tapState(false);
159
+ return { text: props.text, completed, setCompleted };
160
+ });
161
+
162
+ const MyTodos = resource(() => {
163
+ const todos = [
164
+ { id: "1", text: "Learn reactive-resources" },
165
+ { id: "2", text: "Build something awesome" },
166
+ ];
167
+
168
+ const todoResources = tapResources(
169
+ todos.map((todo) => new TodoItem({ text: todo.text }, { key: todo.id }))
170
+ );
171
+
172
+ return todoResources;
173
+ });
174
+ ```
175
+
176
+
177
+ ## Resource Management
178
+
179
+ ### `createResource`
180
+
181
+ Create an instance of a resource. This renders the resource and mounts the tapEffect hooks.
182
+
183
+ ```typescript
184
+ import { createResource } from "@assistant-ui/tap";
185
+
186
+ const handle = createResource(new Counter({ incrementBy: 1 }));
187
+
188
+ // Access current value
189
+ console.log(handle.getState().count);
190
+
191
+ // Subscribe to changes
192
+ const unsubscribe = handle.subscribe(() => {
193
+ console.log("Counter updated:", handle.getState());
194
+ });
195
+
196
+ // Update props to the resource
197
+ handle.updateInput({ incrementBy: 2 });
198
+
199
+ // Cleanup
200
+ unsubscribe();
201
+ ```
202
+
203
+ ## Why Reactive Resources?
204
+
205
+ ### Unified Mental Model
206
+
207
+ Use the same hooks pattern everywhere - no need to learn different state management concepts for component state vs application state.
208
+
209
+ ### Lifecycle Management
210
+
211
+ Unlike traditional state management libraries, resources handle their own lifecycle:
212
+
213
+ ```typescript
214
+ const WebSocketResource = () => {
215
+ const [messages, setMessages] = tapState<string[]>([]);
216
+
217
+ tapEffect(() => {
218
+ const ws = new WebSocket("ws://localhost:8080");
219
+
220
+ ws.onmessage = (event) => {
221
+ setMessages((prev) => [...prev, event.data]);
222
+ };
223
+
224
+ // Cleanup happens automatically when resource unmounts
225
+ return () => ws.close();
226
+ }, []);
227
+
228
+ return messages;
229
+ };
230
+ ```
231
+
232
+ ### Framework Agnostic
233
+
234
+ Works with React or vanilla JavaScript. The core library has zero dependencies and doesn't require any specific framework.
235
+
236
+ ### Type Safety
237
+
238
+ Full TypeScript support with proper type inference throughout.
239
+
240
+ ## Comparison with React Hooks
241
+
242
+ | React Hook | Reactive Resource | Behavior |
243
+ | ------------- | ----------------- | --------- |
244
+ | `useState` | `tapState` | Identical |
245
+ | `useEffect` | `tapEffect` | Identical |
246
+ | `useMemo` | `tapMemo` | Identical |
247
+ | `useCallback` | `tapCallback` | Identical |
248
+ | `useRef` | `tapRef` | Identical |
249
+
250
+ ## License
251
+
252
+ MIT
@@ -0,0 +1,79 @@
1
+ import { ResourceFn, ResourceFiber } from "../core/types";
2
+ /**
3
+ * Creates a test resource fiber for unit testing.
4
+ * This is a low-level utility that creates a ResourceFiber directly.
5
+ * Sets up a rerender callback that automatically re-renders when state changes.
6
+ */
7
+ export declare function createTestResource<R, P>(type: ResourceFn<R, P>): ResourceFiber;
8
+ /**
9
+ * Renders a test resource fiber with the given props and manages its lifecycle.
10
+ * - Tracks resources for cleanup
11
+ * - Returns the current state after render
12
+ */
13
+ export declare function renderTest<R, P>(fiber: ResourceFiber, props: P): R;
14
+ /**
15
+ * Unmounts a specific resource fiber and removes it from tracking.
16
+ */
17
+ export declare function unmountResource(fiber: ResourceFiber): void;
18
+ /**
19
+ * Cleans up all resources. Should be called after each test.
20
+ */
21
+ export declare function cleanupAllResources(): void;
22
+ /**
23
+ * Gets the current committed state of a resource fiber.
24
+ * Returns the state from the last render/commit cycle.
25
+ */
26
+ export declare function getCommittedState<R>(fiber: ResourceFiber): R;
27
+ /**
28
+ * Helper to subscribe to resource state changes for testing.
29
+ * Tracks call count and latest state value.
30
+ */
31
+ export declare class TestSubscriber<T> {
32
+ callCount: number;
33
+ lastState: T;
34
+ private fiber;
35
+ constructor(fiber: ResourceFiber);
36
+ cleanup(): void;
37
+ }
38
+ /**
39
+ * Helper class to manage resource lifecycle in tests with explicit control.
40
+ * Useful when you need fine-grained control over mount/unmount timing.
41
+ */
42
+ export declare class TestResourceManager<R, P> {
43
+ fiber: ResourceFiber;
44
+ private isActive;
45
+ constructor(fiber: ResourceFiber);
46
+ renderAndMount(props: P): R;
47
+ cleanup(): void;
48
+ }
49
+ /**
50
+ * Waits for the next tick of the event loop.
51
+ * Useful for testing async state updates.
52
+ */
53
+ export declare function waitForNextTick(): Promise<void>;
54
+ /**
55
+ * Waits for a condition to be true with timeout.
56
+ * Useful for testing eventual consistency.
57
+ */
58
+ export declare function waitFor(condition: () => boolean, timeout?: number, interval?: number): Promise<void>;
59
+ /**
60
+ * Creates a simple counter resource for testing.
61
+ * Commonly used across multiple test files.
62
+ */
63
+ export declare function createCounterResource(initialValue?: number): (props: {
64
+ value?: number;
65
+ }) => {
66
+ count: number;
67
+ };
68
+ /**
69
+ * Creates a stateful counter resource for testing.
70
+ * Includes increment/decrement functions.
71
+ */
72
+ export declare function createStatefulCounterResource(): (props: {
73
+ initial: number;
74
+ }) => {
75
+ count: any;
76
+ increment: () => any;
77
+ decrement: () => any;
78
+ };
79
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../src/__tests__/test-utils.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAM1D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,iBAe9D;AAWD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAclE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,QAKnD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,SAGlC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,GAAG,CAAC,CAQ5D;AAMD;;;GAGG;AACH,qBAAa,cAAc,CAAC,CAAC;IACpB,SAAS,SAAK;IACd,SAAS,EAAE,CAAC,CAAC;IACpB,OAAO,CAAC,KAAK,CAAgB;gBAEjB,KAAK,EAAE,aAAa;IAsBhC,OAAO;CAMR;AAED;;;GAGG;AACH,qBAAa,mBAAmB,CAAC,CAAC,EAAE,CAAC;IAGhB,KAAK,EAAE,aAAa;IAFvC,OAAO,CAAC,QAAQ,CAAS;gBAEN,KAAK,EAAE,aAAa;IAEvC,cAAc,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC;IAc3B,OAAO;CAOR;AAMD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,OAAO,EACxB,OAAO,SAAO,EACd,QAAQ,SAAK,GACZ,OAAO,CAAC,IAAI,CAAC,CAQf;AAMD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,SAAI,IAC5C,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;EAIlC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,KAInC,OAAO;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE;;;;EAQnC"}
@@ -0,0 +1,216 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/hooks/tap-state.ts
23
+ var tap_state_exports = {};
24
+ __export(tap_state_exports, {
25
+ tapState: () => tapState
26
+ });
27
+ import { getCurrentResourceFiber } from "../core/execution-context.js";
28
+ function getStateCell(initialValue) {
29
+ const executionContext = getCurrentResourceFiber();
30
+ const index = executionContext.currentIndex++;
31
+ if (!executionContext.isFirstRender && index >= executionContext.cells.length) {
32
+ throw new Error(
33
+ "Rendered more hooks than during the previous render. Hooks must be called in the exact same order in every render."
34
+ );
35
+ }
36
+ if (!executionContext.cells[index]) {
37
+ const value = typeof initialValue === "function" ? initialValue() : initialValue;
38
+ const cell2 = {
39
+ type: "state",
40
+ value,
41
+ set: (updater) => {
42
+ const currentValue = cell2.value;
43
+ const nextValue = typeof updater === "function" ? updater(currentValue) : updater;
44
+ if (!Object.is(currentValue, nextValue)) {
45
+ cell2.value = nextValue;
46
+ if (executionContext.isRendering) {
47
+ throw new Error("Resource updated during render");
48
+ }
49
+ executionContext.scheduleRerender();
50
+ }
51
+ }
52
+ };
53
+ executionContext.cells[index] = cell2;
54
+ }
55
+ const cell = executionContext.cells[index];
56
+ if (cell.type !== "state") {
57
+ throw new Error("Hook order changed between renders");
58
+ }
59
+ return cell;
60
+ }
61
+ function tapState(initial) {
62
+ const cell = getStateCell(initial);
63
+ return [cell.value, cell.set];
64
+ }
65
+ var init_tap_state = __esm({
66
+ "src/hooks/tap-state.ts"() {
67
+ "use strict";
68
+ }
69
+ });
70
+
71
+ // src/__tests__/test-utils.ts
72
+ import {
73
+ createResourceFiber,
74
+ unmountResource as unmountResourceFiber,
75
+ renderResource as renderResourceFiber,
76
+ commitResource
77
+ } from "../core/ResourceFiber.js";
78
+ function createTestResource(type) {
79
+ let fiber;
80
+ const rerenderCallback = () => {
81
+ if (activeResources.has(fiber)) {
82
+ const lastProps = propsMap.get(fiber);
83
+ const result = renderResourceFiber(fiber, lastProps);
84
+ commitResource(fiber, result);
85
+ lastRenderResultMap.set(fiber, result);
86
+ }
87
+ };
88
+ fiber = createResourceFiber(type, rerenderCallback);
89
+ return fiber;
90
+ }
91
+ var activeResources = /* @__PURE__ */ new Set();
92
+ var propsMap = /* @__PURE__ */ new WeakMap();
93
+ var lastRenderResultMap = /* @__PURE__ */ new WeakMap();
94
+ function renderTest(fiber, props) {
95
+ propsMap.set(fiber, props);
96
+ activeResources.add(fiber);
97
+ const result = renderResourceFiber(fiber, props);
98
+ commitResource(fiber, result);
99
+ lastRenderResultMap.set(fiber, result);
100
+ return result.state;
101
+ }
102
+ function unmountResource(fiber) {
103
+ if (activeResources.has(fiber)) {
104
+ unmountResourceFiber(fiber);
105
+ activeResources.delete(fiber);
106
+ }
107
+ }
108
+ function cleanupAllResources() {
109
+ activeResources.forEach((fiber) => unmountResourceFiber(fiber));
110
+ activeResources.clear();
111
+ }
112
+ function getCommittedState(fiber) {
113
+ const lastResult = lastRenderResultMap.get(fiber);
114
+ if (!lastResult) {
115
+ throw new Error(
116
+ "No render result found for fiber. Make sure to call renderResource first."
117
+ );
118
+ }
119
+ return lastResult.state;
120
+ }
121
+ var TestSubscriber = class {
122
+ callCount = 0;
123
+ lastState;
124
+ fiber;
125
+ constructor(fiber) {
126
+ this.fiber = fiber;
127
+ const lastProps = propsMap.get(fiber) ?? void 0;
128
+ const initialResult = renderResourceFiber(fiber, lastProps);
129
+ commitResource(fiber, initialResult);
130
+ this.lastState = initialResult.state;
131
+ lastRenderResultMap.set(fiber, initialResult);
132
+ activeResources.add(fiber);
133
+ fiber._rerenderCallback = () => {
134
+ this.callCount++;
135
+ const lastProps2 = propsMap.get(fiber) ?? void 0;
136
+ const result = renderResourceFiber(fiber, lastProps2);
137
+ commitResource(fiber, result);
138
+ this.lastState = result.state;
139
+ lastRenderResultMap.set(fiber, result);
140
+ };
141
+ }
142
+ cleanup() {
143
+ if (activeResources.has(this.fiber)) {
144
+ unmountResourceFiber(this.fiber);
145
+ activeResources.delete(this.fiber);
146
+ }
147
+ }
148
+ };
149
+ var TestResourceManager = class {
150
+ constructor(fiber) {
151
+ this.fiber = fiber;
152
+ }
153
+ isActive = false;
154
+ renderAndMount(props) {
155
+ if (this.isActive) {
156
+ throw new Error("Resource already active");
157
+ }
158
+ this.isActive = true;
159
+ activeResources.add(this.fiber);
160
+ propsMap.set(this.fiber, props);
161
+ const result = renderResourceFiber(this.fiber, props);
162
+ commitResource(this.fiber, result);
163
+ lastRenderResultMap.set(this.fiber, result);
164
+ return result.state;
165
+ }
166
+ cleanup() {
167
+ if (this.isActive && activeResources.has(this.fiber)) {
168
+ unmountResourceFiber(this.fiber);
169
+ activeResources.delete(this.fiber);
170
+ this.isActive = false;
171
+ }
172
+ }
173
+ };
174
+ function waitForNextTick() {
175
+ return new Promise((resolve) => setTimeout(resolve, 0));
176
+ }
177
+ async function waitFor(condition, timeout = 1e3, interval = 10) {
178
+ const start = Date.now();
179
+ while (!condition()) {
180
+ if (Date.now() - start > timeout) {
181
+ throw new Error("Timeout waiting for condition");
182
+ }
183
+ await new Promise((resolve) => setTimeout(resolve, interval));
184
+ }
185
+ }
186
+ function createCounterResource(initialValue = 0) {
187
+ return (props) => {
188
+ const value = props.value ?? initialValue;
189
+ return { count: value };
190
+ };
191
+ }
192
+ function createStatefulCounterResource() {
193
+ const { tapState: tapState2 } = (init_tap_state(), __toCommonJS(tap_state_exports));
194
+ return (props) => {
195
+ const [count, setCount] = tapState2(props.initial);
196
+ return {
197
+ count,
198
+ increment: () => setCount((c) => c + 1),
199
+ decrement: () => setCount((c) => c - 1)
200
+ };
201
+ };
202
+ }
203
+ export {
204
+ TestResourceManager,
205
+ TestSubscriber,
206
+ cleanupAllResources,
207
+ createCounterResource,
208
+ createStatefulCounterResource,
209
+ createTestResource,
210
+ getCommittedState,
211
+ renderTest,
212
+ unmountResource,
213
+ waitFor,
214
+ waitForNextTick
215
+ };
216
+ //# sourceMappingURL=test-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/tap-state.ts","../../src/__tests__/test-utils.ts"],"sourcesContent":["import { getCurrentResourceFiber } from \"../core/execution-context\";\nimport { StateUpdater, Cell } from \"../core/types\";\n\nfunction getStateCell<T>(\n initialValue: T | (() => T)\n): Cell & { type: \"state\" } {\n const executionContext = getCurrentResourceFiber();\n const index = executionContext.currentIndex++;\n\n // Check if we're trying to use more hooks than in previous renders\n if (\n !executionContext.isFirstRender &&\n index >= executionContext.cells.length\n ) {\n throw new Error(\n \"Rendered more hooks than during the previous render. \" +\n \"Hooks must be called in the exact same order in every render.\"\n );\n }\n\n if (!executionContext.cells[index]) {\n // Initialize the value immediately\n const value =\n typeof initialValue === \"function\"\n ? (initialValue as () => T)()\n : initialValue;\n\n const cell: Cell & { type: \"state\" } = {\n type: \"state\",\n value,\n set: (updater: StateUpdater<T>) => {\n const currentValue = cell.value;\n const nextValue =\n typeof updater === \"function\"\n ? (updater as (prev: T) => T)(currentValue)\n : updater;\n\n if (!Object.is(currentValue, nextValue)) {\n cell.value = nextValue;\n\n // Check if called during render (not allowed)\n if (executionContext.isRendering) {\n throw new Error(\"Resource updated during render\");\n }\n executionContext.scheduleRerender();\n }\n },\n };\n\n executionContext.cells[index] = cell;\n }\n\n const cell = executionContext.cells[index];\n if (cell.type !== \"state\") {\n throw new Error(\"Hook order changed between renders\");\n }\n\n return cell as Cell & { type: \"state\" };\n}\n\nexport function tapState<S = undefined>(): [\n S | undefined,\n (updater: StateUpdater<S>) => void\n];\nexport function tapState<S>(\n initial: S | (() => S)\n): [S, (updater: StateUpdater<S>) => void];\nexport function tapState<S>(\n initial?: S | (() => S)\n): [S | undefined, (updater: StateUpdater<S>) => void] {\n const cell = getStateCell(initial as S | (() => S));\n\n return [cell.value, cell.set];\n}\n","import {\n createResourceFiber,\n unmountResource as unmountResourceFiber,\n renderResource as renderResourceFiber,\n commitResource,\n} from \"../core/ResourceFiber\";\nimport { ResourceFn, ResourceFiber } from \"../core/types\";\n\n// ============================================================================\n// Resource Creation\n// ============================================================================\n\n/**\n * Creates a test resource fiber for unit testing.\n * This is a low-level utility that creates a ResourceFiber directly.\n * Sets up a rerender callback that automatically re-renders when state changes.\n */\nexport function createTestResource<R, P>(type: ResourceFn<R, P>) {\n let fiber: ResourceFiber;\n\n const rerenderCallback = () => {\n // Re-render when state changes\n if (activeResources.has(fiber)) {\n const lastProps = propsMap.get(fiber);\n const result = renderResourceFiber(fiber, lastProps);\n commitResource(fiber, result);\n lastRenderResultMap.set(fiber, result);\n }\n };\n\n fiber = createResourceFiber(type, rerenderCallback);\n return fiber;\n}\n\n// ============================================================================\n// Resource Lifecycle Management\n// ============================================================================\n\n// Track resources for cleanup\nconst activeResources = new Set<ResourceFiber>();\nconst propsMap = new WeakMap<ResourceFiber, any>();\nconst lastRenderResultMap = new WeakMap<ResourceFiber, any>();\n\n/**\n * Renders a test resource fiber with the given props and manages its lifecycle.\n * - Tracks resources for cleanup\n * - Returns the current state after render\n */\nexport function renderTest<R, P>(fiber: ResourceFiber, props: P): R {\n propsMap.set(fiber, props);\n\n // Track resource for cleanup\n activeResources.add(fiber);\n\n // Render with new props\n const result = renderResourceFiber(fiber, props);\n commitResource(fiber, result);\n lastRenderResultMap.set(fiber, result);\n\n // Return the committed state from the result\n // This accounts for any re-renders that happened during commit\n return result.state;\n}\n\n/**\n * Unmounts a specific resource fiber and removes it from tracking.\n */\nexport function unmountResource(fiber: ResourceFiber) {\n if (activeResources.has(fiber)) {\n unmountResourceFiber(fiber);\n activeResources.delete(fiber);\n }\n}\n\n/**\n * Cleans up all resources. Should be called after each test.\n */\nexport function cleanupAllResources() {\n activeResources.forEach((fiber) => unmountResourceFiber(fiber));\n activeResources.clear();\n}\n\n/**\n * Gets the current committed state of a resource fiber.\n * Returns the state from the last render/commit cycle.\n */\nexport function getCommittedState<R>(fiber: ResourceFiber): R {\n const lastResult = lastRenderResultMap.get(fiber);\n if (!lastResult) {\n throw new Error(\n \"No render result found for fiber. Make sure to call renderResource first.\"\n );\n }\n return lastResult.state;\n}\n\n// ============================================================================\n// Test Helpers\n// ============================================================================\n\n/**\n * Helper to subscribe to resource state changes for testing.\n * Tracks call count and latest state value.\n */\nexport class TestSubscriber<T> {\n public callCount = 0;\n public lastState: T;\n private fiber: ResourceFiber;\n\n constructor(fiber: ResourceFiber) {\n this.fiber = fiber;\n // Need to render once to get initial state\n const lastProps = propsMap.get(fiber) ?? undefined;\n const initialResult = renderResourceFiber(fiber, lastProps as any);\n commitResource(fiber, initialResult);\n this.lastState = initialResult.state;\n lastRenderResultMap.set(fiber, initialResult);\n activeResources.add(fiber);\n\n // Update the fiber's rerender callback\n (fiber as any)._rerenderCallback = () => {\n this.callCount++;\n // Re-render to get latest state\n const lastProps = propsMap.get(fiber) ?? undefined;\n const result = renderResourceFiber(fiber, lastProps as any);\n commitResource(fiber, result);\n this.lastState = result.state;\n lastRenderResultMap.set(fiber, result);\n };\n }\n\n cleanup() {\n if (activeResources.has(this.fiber)) {\n unmountResourceFiber(this.fiber);\n activeResources.delete(this.fiber);\n }\n }\n}\n\n/**\n * Helper class to manage resource lifecycle in tests with explicit control.\n * Useful when you need fine-grained control over mount/unmount timing.\n */\nexport class TestResourceManager<R, P> {\n private isActive = false;\n\n constructor(public fiber: ResourceFiber) {}\n\n renderAndMount(props: P): R {\n if (this.isActive) {\n throw new Error(\"Resource already active\");\n }\n\n this.isActive = true;\n activeResources.add(this.fiber);\n propsMap.set(this.fiber, props);\n const result = renderResourceFiber(this.fiber, props);\n commitResource(this.fiber, result);\n lastRenderResultMap.set(this.fiber, result);\n return result.state;\n }\n\n cleanup() {\n if (this.isActive && activeResources.has(this.fiber)) {\n unmountResourceFiber(this.fiber);\n activeResources.delete(this.fiber);\n this.isActive = false;\n }\n }\n}\n\n// ============================================================================\n// Async Utilities\n// ============================================================================\n\n/**\n * Waits for the next tick of the event loop.\n * Useful for testing async state updates.\n */\nexport function waitForNextTick(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, 0));\n}\n\n/**\n * Waits for a condition to be true with timeout.\n * Useful for testing eventual consistency.\n */\nexport async function waitFor(\n condition: () => boolean,\n timeout = 1000,\n interval = 10\n): Promise<void> {\n const start = Date.now();\n while (!condition()) {\n if (Date.now() - start > timeout) {\n throw new Error(\"Timeout waiting for condition\");\n }\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n}\n\n// ============================================================================\n// Test Data Factories\n// ============================================================================\n\n/**\n * Creates a simple counter resource for testing.\n * Commonly used across multiple test files.\n */\nexport function createCounterResource(initialValue = 0) {\n return (props: { value?: number }) => {\n const value = props.value ?? initialValue;\n return { count: value };\n };\n}\n\n/**\n * Creates a stateful counter resource for testing.\n * Includes increment/decrement functions.\n */\nexport function createStatefulCounterResource() {\n // Import at top of file to avoid issues\n const { tapState } = require(\"../hooks/tap-state\");\n\n return (props: { initial: number }) => {\n const [count, setCount] = tapState(props.initial);\n return {\n count,\n increment: () => setCount((c: number) => c + 1),\n decrement: () => setCount((c: number) => c - 1),\n };\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,+BAA+B;AAGxC,SAAS,aACP,cAC0B;AAC1B,QAAM,mBAAmB,wBAAwB;AACjD,QAAM,QAAQ,iBAAiB;AAG/B,MACE,CAAC,iBAAiB,iBAClB,SAAS,iBAAiB,MAAM,QAChC;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,CAAC,iBAAiB,MAAM,KAAK,GAAG;AAElC,UAAM,QACJ,OAAO,iBAAiB,aACnB,aAAyB,IAC1B;AAEN,UAAMA,QAAiC;AAAA,MACrC,MAAM;AAAA,MACN;AAAA,MACA,KAAK,CAAC,YAA6B;AACjC,cAAM,eAAeA,MAAK;AAC1B,cAAM,YACJ,OAAO,YAAY,aACd,QAA2B,YAAY,IACxC;AAEN,YAAI,CAAC,OAAO,GAAG,cAAc,SAAS,GAAG;AACvC,UAAAA,MAAK,QAAQ;AAGb,cAAI,iBAAiB,aAAa;AAChC,kBAAM,IAAI,MAAM,gCAAgC;AAAA,UAClD;AACA,2BAAiB,iBAAiB;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AAEA,qBAAiB,MAAM,KAAK,IAAIA;AAAA,EAClC;AAEA,QAAM,OAAO,iBAAiB,MAAM,KAAK;AACzC,MAAI,KAAK,SAAS,SAAS;AACzB,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAEA,SAAO;AACT;AASO,SAAS,SACd,SACqD;AACrD,QAAM,OAAO,aAAa,OAAwB;AAElD,SAAO,CAAC,KAAK,OAAO,KAAK,GAAG;AAC9B;AAzEA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA,EACE;AAAA,EACA,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB;AAAA,OACK;AAYA,SAAS,mBAAyB,MAAwB;AAC/D,MAAI;AAEJ,QAAM,mBAAmB,MAAM;AAE7B,QAAI,gBAAgB,IAAI,KAAK,GAAG;AAC9B,YAAM,YAAY,SAAS,IAAI,KAAK;AACpC,YAAM,SAAS,oBAAoB,OAAO,SAAS;AACnD,qBAAe,OAAO,MAAM;AAC5B,0BAAoB,IAAI,OAAO,MAAM;AAAA,IACvC;AAAA,EACF;AAEA,UAAQ,oBAAoB,MAAM,gBAAgB;AAClD,SAAO;AACT;AAOA,IAAM,kBAAkB,oBAAI,IAAmB;AAC/C,IAAM,WAAW,oBAAI,QAA4B;AACjD,IAAM,sBAAsB,oBAAI,QAA4B;AAOrD,SAAS,WAAiB,OAAsB,OAAa;AAClE,WAAS,IAAI,OAAO,KAAK;AAGzB,kBAAgB,IAAI,KAAK;AAGzB,QAAM,SAAS,oBAAoB,OAAO,KAAK;AAC/C,iBAAe,OAAO,MAAM;AAC5B,sBAAoB,IAAI,OAAO,MAAM;AAIrC,SAAO,OAAO;AAChB;AAKO,SAAS,gBAAgB,OAAsB;AACpD,MAAI,gBAAgB,IAAI,KAAK,GAAG;AAC9B,yBAAqB,KAAK;AAC1B,oBAAgB,OAAO,KAAK;AAAA,EAC9B;AACF;AAKO,SAAS,sBAAsB;AACpC,kBAAgB,QAAQ,CAAC,UAAU,qBAAqB,KAAK,CAAC;AAC9D,kBAAgB,MAAM;AACxB;AAMO,SAAS,kBAAqB,OAAyB;AAC5D,QAAM,aAAa,oBAAoB,IAAI,KAAK;AAChD,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,WAAW;AACpB;AAUO,IAAM,iBAAN,MAAwB;AAAA,EACtB,YAAY;AAAA,EACZ;AAAA,EACC;AAAA,EAER,YAAY,OAAsB;AAChC,SAAK,QAAQ;AAEb,UAAM,YAAY,SAAS,IAAI,KAAK,KAAK;AACzC,UAAM,gBAAgB,oBAAoB,OAAO,SAAgB;AACjE,mBAAe,OAAO,aAAa;AACnC,SAAK,YAAY,cAAc;AAC/B,wBAAoB,IAAI,OAAO,aAAa;AAC5C,oBAAgB,IAAI,KAAK;AAGzB,IAAC,MAAc,oBAAoB,MAAM;AACvC,WAAK;AAEL,YAAMC,aAAY,SAAS,IAAI,KAAK,KAAK;AACzC,YAAM,SAAS,oBAAoB,OAAOA,UAAgB;AAC1D,qBAAe,OAAO,MAAM;AAC5B,WAAK,YAAY,OAAO;AACxB,0BAAoB,IAAI,OAAO,MAAM;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,UAAU;AACR,QAAI,gBAAgB,IAAI,KAAK,KAAK,GAAG;AACnC,2BAAqB,KAAK,KAAK;AAC/B,sBAAgB,OAAO,KAAK,KAAK;AAAA,IACnC;AAAA,EACF;AACF;AAMO,IAAM,sBAAN,MAAgC;AAAA,EAGrC,YAAmB,OAAsB;AAAtB;AAAA,EAAuB;AAAA,EAFlC,WAAW;AAAA,EAInB,eAAe,OAAa;AAC1B,QAAI,KAAK,UAAU;AACjB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,SAAK,WAAW;AAChB,oBAAgB,IAAI,KAAK,KAAK;AAC9B,aAAS,IAAI,KAAK,OAAO,KAAK;AAC9B,UAAM,SAAS,oBAAoB,KAAK,OAAO,KAAK;AACpD,mBAAe,KAAK,OAAO,MAAM;AACjC,wBAAoB,IAAI,KAAK,OAAO,MAAM;AAC1C,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,UAAU;AACR,QAAI,KAAK,YAAY,gBAAgB,IAAI,KAAK,KAAK,GAAG;AACpD,2BAAqB,KAAK,KAAK;AAC/B,sBAAgB,OAAO,KAAK,KAAK;AACjC,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AACF;AAUO,SAAS,kBAAiC;AAC/C,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,CAAC,CAAC;AACxD;AAMA,eAAsB,QACpB,WACA,UAAU,KACV,WAAW,IACI;AACf,QAAM,QAAQ,KAAK,IAAI;AACvB,SAAO,CAAC,UAAU,GAAG;AACnB,QAAI,KAAK,IAAI,IAAI,QAAQ,SAAS;AAChC,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AACA,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,QAAQ,CAAC;AAAA,EAC9D;AACF;AAUO,SAAS,sBAAsB,eAAe,GAAG;AACtD,SAAO,CAAC,UAA8B;AACpC,UAAM,QAAQ,MAAM,SAAS;AAC7B,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB;AACF;AAMO,SAAS,gCAAgC;AAE9C,QAAM,EAAE,UAAAC,UAAS,IAAI;AAErB,SAAO,CAAC,UAA+B;AACrC,UAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,MAAM,OAAO;AAChD,WAAO;AAAA,MACL;AAAA,MACA,WAAW,MAAM,SAAS,CAAC,MAAc,IAAI,CAAC;AAAA,MAC9C,WAAW,MAAM,SAAS,CAAC,MAAc,IAAI,CAAC;AAAA,IAChD;AAAA,EACF;AACF;","names":["cell","lastProps","tapState"]}
@@ -0,0 +1,6 @@
1
+ import { ResourceFn, ResourceFiber, RenderResult } from "./types";
2
+ export declare function createResourceFiber<R, P>(resourceFn: ResourceFn<R, P>, scheduleRerender: () => void): ResourceFiber;
3
+ export declare function unmountResource(fiber: ResourceFiber): void;
4
+ export declare function renderResource<R, P>(fiber: ResourceFiber, props: P): RenderResult;
5
+ export declare function commitResource(fiber: ResourceFiber, result: RenderResult): void;
6
+ //# sourceMappingURL=ResourceFiber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResourceFiber.d.ts","sourceRoot":"","sources":["../../src/core/ResourceFiber.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAIlE,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EACtC,UAAU,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAC5B,gBAAgB,EAAE,MAAM,IAAI,GAC3B,aAAa,CAWf;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAG1D;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EACjC,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,CAAC,GACP,YAAY,CAed;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,YAAY,GACnB,IAAI,CAGN"}
@@ -0,0 +1,41 @@
1
+ // src/core/ResourceFiber.ts
2
+ import { commitRender, cleanupAllEffects } from "./commit.js";
3
+ import { withResourceFiber } from "./execution-context.js";
4
+ function createResourceFiber(resourceFn, scheduleRerender) {
5
+ return {
6
+ resourceFn,
7
+ scheduleRerender,
8
+ cells: [],
9
+ commitTasks: [],
10
+ currentIndex: 0,
11
+ committedProps: void 0,
12
+ isRendering: false,
13
+ isFirstRender: true
14
+ };
15
+ }
16
+ function unmountResource(fiber) {
17
+ cleanupAllEffects(fiber);
18
+ }
19
+ function renderResource(fiber, props) {
20
+ let state;
21
+ withResourceFiber(fiber, () => {
22
+ state = fiber.resourceFn(props);
23
+ });
24
+ fiber.committedProps ??= props;
25
+ return {
26
+ commitTasks: fiber.commitTasks,
27
+ props,
28
+ state
29
+ };
30
+ }
31
+ function commitResource(fiber, result) {
32
+ commitRender(result, fiber);
33
+ fiber.committedProps = result.props;
34
+ }
35
+ export {
36
+ commitResource,
37
+ createResourceFiber,
38
+ renderResource,
39
+ unmountResource
40
+ };
41
+ //# sourceMappingURL=ResourceFiber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/ResourceFiber.ts"],"sourcesContent":["import { ResourceFn, ResourceFiber, RenderResult } from \"./types\";\nimport { commitRender, cleanupAllEffects } from \"./commit\";\nimport { withResourceFiber } from \"./execution-context\";\n\nexport function createResourceFiber<R, P>(\n resourceFn: ResourceFn<R, P>,\n scheduleRerender: () => void\n): ResourceFiber {\n return {\n resourceFn,\n scheduleRerender,\n cells: [],\n commitTasks: [],\n currentIndex: 0,\n committedProps: undefined,\n isRendering: false,\n isFirstRender: true,\n };\n}\n\nexport function unmountResource(fiber: ResourceFiber): void {\n // Clean up all effects\n cleanupAllEffects(fiber);\n}\n\nexport function renderResource<R, P>(\n fiber: ResourceFiber,\n props: P\n): RenderResult {\n let state: R | undefined;\n\n withResourceFiber(fiber, () => {\n state = fiber.resourceFn(props);\n });\n\n // on first render, we save the props for setState calls post render before commit\n fiber.committedProps ??= props;\n\n return {\n commitTasks: fiber.commitTasks,\n props,\n state,\n };\n}\n\nexport function commitResource(\n fiber: ResourceFiber,\n result: RenderResult\n): void {\n commitRender(result, fiber);\n fiber.committedProps = result.props;\n}\n"],"mappings":";AACA,SAAS,cAAc,yBAAyB;AAChD,SAAS,yBAAyB;AAE3B,SAAS,oBACd,YACA,kBACe;AACf,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO,CAAC;AAAA,IACR,aAAa,CAAC;AAAA,IACd,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AACF;AAEO,SAAS,gBAAgB,OAA4B;AAE1D,oBAAkB,KAAK;AACzB;AAEO,SAAS,eACd,OACA,OACc;AACd,MAAI;AAEJ,oBAAkB,OAAO,MAAM;AAC7B,YAAQ,MAAM,WAAW,KAAK;AAAA,EAChC,CAAC;AAGD,QAAM,mBAAmB;AAEzB,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,eACd,OACA,QACM;AACN,eAAa,QAAQ,KAAK;AAC1B,QAAM,iBAAiB,OAAO;AAChC;","names":[]}
@@ -0,0 +1,8 @@
1
+ import { ResourceElement, Unsubscribe } from "./types";
2
+ export interface ResourceHandle<R, P> {
3
+ getState(): R;
4
+ subscribe(callback: () => void): Unsubscribe;
5
+ updateInput(props: P): void;
6
+ }
7
+ export declare const createResource: <R, P>(element: ResourceElement<R, P>) => ResourceHandle<R, P>;
8
+ //# sourceMappingURL=ResourceHandle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResourceHandle.d.ts","sourceRoot":"","sources":["../../src/core/ResourceHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAcvD,MAAM,WAAW,cAAc,CAAC,CAAC,EAAE,CAAC;IAClC,QAAQ,IAAI,CAAC,CAAC;IACd,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;IAC7C,WAAW,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CAC7B;AAuCD,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EACjC,SAAS,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,KAC7B,cAAc,CAAC,CAAC,EAAE,CAAC,CAmBrB,CAAC"}