@assistant-ui/tap 0.2.2 → 0.3.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 (55) hide show
  1. package/README.md +151 -20
  2. package/dist/__tests__/test-utils.d.ts +2 -2
  3. package/dist/__tests__/test-utils.d.ts.map +1 -1
  4. package/dist/__tests__/test-utils.js +3 -2
  5. package/dist/__tests__/test-utils.js.map +1 -1
  6. package/dist/core/ResourceFiber.d.ts +2 -2
  7. package/dist/core/ResourceFiber.d.ts.map +1 -1
  8. package/dist/core/ResourceFiber.js +4 -3
  9. package/dist/core/ResourceFiber.js.map +1 -1
  10. package/dist/core/ResourceHandle.d.ts +11 -8
  11. package/dist/core/ResourceHandle.d.ts.map +1 -1
  12. package/dist/core/ResourceHandle.js +37 -34
  13. package/dist/core/ResourceHandle.js.map +1 -1
  14. package/dist/core/callResourceFn.d.ts +2 -0
  15. package/dist/core/callResourceFn.d.ts.map +1 -0
  16. package/dist/core/{getResourceFn.js → callResourceFn.js} +6 -6
  17. package/dist/core/callResourceFn.js.map +1 -0
  18. package/dist/core/resource.d.ts +3 -2
  19. package/dist/core/resource.d.ts.map +1 -1
  20. package/dist/core/resource.js +1 -1
  21. package/dist/core/resource.js.map +1 -1
  22. package/dist/core/types.d.ts +10 -10
  23. package/dist/core/types.d.ts.map +1 -1
  24. package/dist/hooks/tap-effect-event.d.ts +17 -0
  25. package/dist/hooks/tap-effect-event.d.ts.map +1 -0
  26. package/dist/hooks/tap-effect-event.js +14 -0
  27. package/dist/hooks/tap-effect-event.js.map +1 -0
  28. package/dist/hooks/tap-effect.d.ts +6 -3
  29. package/dist/hooks/tap-effect.d.ts.map +1 -1
  30. package/dist/hooks/tap-effect.js.map +1 -1
  31. package/dist/hooks/tap-inline-resource.js +2 -2
  32. package/dist/hooks/tap-inline-resource.js.map +1 -1
  33. package/dist/hooks/tap-ref.d.ts +6 -4
  34. package/dist/hooks/tap-ref.d.ts.map +1 -1
  35. package/dist/hooks/tap-ref.js.map +1 -1
  36. package/dist/hooks/tap-resource.d.ts.map +1 -1
  37. package/dist/hooks/tap-resource.js +1 -2
  38. package/dist/hooks/tap-resource.js.map +1 -1
  39. package/dist/hooks/tap-resources.d.ts.map +1 -1
  40. package/dist/hooks/tap-resources.js +2 -6
  41. package/dist/hooks/tap-resources.js.map +1 -1
  42. package/dist/hooks/tap-state.d.ts +6 -3
  43. package/dist/hooks/tap-state.d.ts.map +1 -1
  44. package/dist/hooks/tap-state.js.map +1 -1
  45. package/dist/index.d.ts +4 -3
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/react/use-resource.d.ts.map +1 -1
  50. package/dist/react/use-resource.js +1 -2
  51. package/dist/react/use-resource.js.map +1 -1
  52. package/package.json +14 -3
  53. package/dist/core/getResourceFn.d.ts +0 -2
  54. package/dist/core/getResourceFn.d.ts.map +0 -1
  55. package/dist/core/getResourceFn.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @assistant-ui/tap
2
2
 
3
- Zero-dependency reactive state management inspired by React hooks.
3
+ **tap** (Reactive Resources) is a zero-dependency reactive state management library that brings **React's hooks mental model to state management outside of React components**.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,9 +8,34 @@ Zero-dependency reactive state management inspired by React hooks.
8
8
  npm install @assistant-ui/tap
9
9
  ```
10
10
 
11
- ## Overview
11
+ ## What is tap?
12
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.
13
+ Instead of limiting hooks to React components, tap lets you use the same familiar hooks pattern (`useState`, `useEffect`, `useMemo`, etc.) to create self-contained, reusable units of reactive state and logic called **Resources** that can be used anywhere - in vanilla JavaScript, servers, or outside of React.
14
+
15
+ ## Philosophy
16
+
17
+ - **Unified mental model**: Use the same hooks pattern everywhere
18
+ - **Framework agnostic**: Zero dependencies, works with or without React
19
+ - **Lifecycle management**: Resources handle their own cleanup automatically
20
+ - **Type-safe**: Full TypeScript support with proper type inference
21
+
22
+ ## How It Works
23
+
24
+ tap implements a **render-commit pattern** similar to React:
25
+
26
+ ### Render Phase
27
+
28
+ 1. Each resource instance has a "fiber" that tracks state and effects
29
+ 2. When a resource function runs, hooks record their data in the fiber
30
+ 3. The library maintains an execution context to track which fiber's hooks are being called
31
+ 4. Each hook stores its data in cells indexed by call order (enforcing React's rules)
32
+
33
+ ### Commit Phase
34
+
35
+ 1. After render, collected effect tasks are processed
36
+ 2. Effects check if dependencies changed using shallow equality
37
+ 3. Old effects are cleaned up before new ones run
38
+ 4. Updates are batched using microtasks to prevent excessive re-renders
14
39
 
15
40
  ## Core Concepts
16
41
 
@@ -18,8 +43,9 @@ This library brings React's hooks mental model to state management outside of Re
18
43
 
19
44
  Resources are self-contained units of reactive state and logic. They follow the same rules as React hooks:
20
45
 
21
- - Hooks must be called in the same order every render
22
- - Hooks cannot be called conditionally
46
+ - **Hook Order**: Hooks must be called in the same order in every render
47
+ - **No Conditional Hooks**: Can't call hooks inside conditionals or loops
48
+ - **No Async Hooks**: Hooks must be called synchronously during render
23
49
  - Resources automatically handle cleanup and lifecycle
24
50
 
25
51
  ### Creating Resources
@@ -153,26 +179,47 @@ const Timer = resource(() => {
153
179
  Renders multiple resources with keys, similar to React's list rendering. All resources must have a unique `key` property.
154
180
 
155
181
  ```typescript
156
- // Using with createResourceNodeConstructor
157
182
  const TodoItem = resource((props: { text: string }) => {
158
183
  const [completed, setCompleted] = tapState(false);
159
184
  return { text: props.text, completed, setCompleted };
160
185
  });
161
186
 
162
- const MyTodos = resource(() => {
187
+ const TodoList = resource(() => {
163
188
  const todos = [
164
- { id: "1", text: "Learn reactive-resources" },
189
+ { id: "1", text: "Learn tap" },
165
190
  { id: "2", text: "Build something awesome" },
166
191
  ];
167
192
 
168
- const todoResources = tapResources(
193
+ const todoItems = tapResources(
169
194
  todos.map((todo) => new TodoItem({ text: todo.text }, { key: todo.id })),
170
195
  );
171
196
 
172
- return todoResources;
197
+ return todoItems;
173
198
  });
174
199
  ```
175
200
 
201
+ ### `tapContext` and Context Support
202
+
203
+ Create and use context to pass values through resource boundaries without prop drilling.
204
+
205
+ ```typescript
206
+ import {
207
+ createContext,
208
+ tapContext,
209
+ withContextProvider,
210
+ } from "@assistant-ui/tap";
211
+
212
+ const MyContext = createContext(defaultValue);
213
+
214
+ // Provide context
215
+ withContextProvider(MyContext, value, () => {
216
+ // Inside this function, tapContext can access the value
217
+ });
218
+
219
+ // Access context in a resource
220
+ const value = tapContext(MyContext);
221
+ ```
222
+
176
223
  ## Resource Management
177
224
 
178
225
  ### `createResource`
@@ -196,21 +243,36 @@ const unsubscribe = handle.subscribe(() => {
196
243
  handle.updateInput({ incrementBy: 2 });
197
244
 
198
245
  // Cleanup
246
+ handle.dispose();
199
247
  unsubscribe();
200
248
  ```
201
249
 
202
- ## Why Reactive Resources?
250
+ ## React Integration
203
251
 
204
- ### Unified Mental Model
252
+ Use resources directly in React components with the `useResource` hook:
205
253
 
206
- Use the same hooks pattern everywhere - no need to learn different state management concepts for component state vs application state.
254
+ ```typescript
255
+ import { useResource } from "@assistant-ui/tap/react";
256
+
257
+ function MyComponent() {
258
+ const state = useResource(new Counter({ incrementBy: 1 }));
259
+ return (
260
+ <div>
261
+ <p>Count: {state.count}</p>
262
+ <button onClick={state.increment}>Increment</button>
263
+ </div>
264
+ );
265
+ }
266
+ ```
267
+
268
+ ## Design Patterns
207
269
 
208
- ### Lifecycle Management
270
+ ### Automatic Cleanup
209
271
 
210
- Unlike traditional state management libraries, resources handle their own lifecycle:
272
+ Resources automatically clean up after themselves when unmounted:
211
273
 
212
274
  ```typescript
213
- const WebSocketResource = () => {
275
+ const WebSocketResource = resource(() => {
214
276
  const [messages, setMessages] = tapState<string[]>([]);
215
277
 
216
278
  tapEffect(() => {
@@ -225,16 +287,85 @@ const WebSocketResource = () => {
225
287
  }, []);
226
288
 
227
289
  return messages;
290
+ });
291
+ ```
292
+
293
+ ### API Wrapper Pattern
294
+
295
+ A common pattern in assistant-ui is to wrap resource state in a stable API object:
296
+
297
+ ```typescript
298
+ export const tapApi = <TApi extends ApiObject & { getState: () => any }>(
299
+ api: TApi,
300
+ ) => {
301
+ const ref = tapRef(api);
302
+
303
+ tapEffect(() => {
304
+ ref.current = api;
305
+ });
306
+
307
+ const apiProxy = tapMemo(
308
+ () =>
309
+ new Proxy<TApi>({} as TApi, new ReadonlyApiHandler(() => ref.current)),
310
+ [],
311
+ );
312
+
313
+ return tapMemo(
314
+ () => ({
315
+ state: api.getState(),
316
+ api: apiProxy,
317
+ }),
318
+ [api.getState()],
319
+ );
228
320
  };
229
321
  ```
230
322
 
231
- ### Framework Agnostic
323
+ ## Use Cases
324
+
325
+ tap is used throughout assistant-ui for:
232
326
 
233
- Works with React or vanilla JavaScript. The core library has zero dependencies and doesn't require any specific framework.
327
+ 1. **State Management**: Application-wide state without Redux/Zustand
328
+ 2. **Event Handling**: Managing event subscriptions and cleanup
329
+ 3. **Resource Lifecycle**: Auto-cleanup of WebSockets, timers, subscriptions
330
+ 4. **Composition**: Nested resource management (threads, messages, tools)
331
+ 5. **Context Injection**: Passing values through resource boundaries without prop drilling
332
+ 6. **API Wrapping**: Creating reactive API objects with `getState()` and `subscribe()`
333
+
334
+ ### Example: Tools Management
335
+
336
+ ```typescript
337
+ export const Tools = resource(({ toolkit }: { toolkit?: Toolkit }) => {
338
+ const [state, setState] = tapState<ToolsState>(() => ({
339
+ tools: {},
340
+ }));
341
+
342
+ const modelContext = tapModelContext();
343
+
344
+ tapEffect(() => {
345
+ if (!toolkit) return;
346
+
347
+ // Register tools and setup subscriptions
348
+ const unsubscribes: (() => void)[] = [];
349
+ // ... registration logic
350
+
351
+ return () => unsubscribes.forEach((fn) => fn());
352
+ }, [toolkit, modelContext]);
353
+
354
+ return tapApi<ToolsApi>({
355
+ getState: () => state,
356
+ setToolUI,
357
+ });
358
+ });
359
+ ```
234
360
 
235
- ### Type Safety
361
+ ## Why tap?
236
362
 
237
- Full TypeScript support with proper type inference throughout.
363
+ - **Reuse React knowledge**: Developers already familiar with hooks can immediately work with tap
364
+ - **Framework flexibility**: Core logic can work outside React components
365
+ - **Automatic cleanup**: No memory leaks from forgotten unsubscribes
366
+ - **Composability**: Resources can nest and combine naturally
367
+ - **Type safety**: Full TypeScript inference for state and APIs
368
+ - **Zero dependencies**: Lightweight and portable
238
369
 
239
370
  ## Comparison with React Hooks
240
371
 
@@ -1,10 +1,10 @@
1
- import { ResourceFn, ResourceFiber } from "../core/types";
1
+ import { ResourceFiber } from "../core/types";
2
2
  /**
3
3
  * Creates a test resource fiber for unit testing.
4
4
  * This is a low-level utility that creates a ResourceFiber directly.
5
5
  * Sets up a rerender callback that automatically re-renders when state changes.
6
6
  */
7
- export declare function createTestResource<R, P>(type: ResourceFn<R, P>): ResourceFiber<R, P>;
7
+ export declare function createTestResource<R, P>(fn: (props: P) => R): ResourceFiber<R, P>;
8
8
  /**
9
9
  * Renders a test resource fiber with the given props and manages its lifecycle.
10
10
  * - Tracks resources for cleanup
@@ -1 +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;AAO1D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,uBAa9D;AAWD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAcxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,QAK/D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,SAGlC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAQrE;AAMD;;;GAGG;AACH,qBAAa,cAAc,CAAC,CAAC;IACpB,SAAS,SAAK;IACd,SAAS,EAAE,CAAC,CAAC;IACpB,OAAO,CAAC,KAAK,CAA0B;gBAE3B,KAAK,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC;IAW1C,OAAO;CAMR;AAED;;;GAGG;AACH,qBAAa,mBAAmB,CAAC,CAAC,EAAE,CAAC;IAGhB,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC;IAF7C,OAAO,CAAC,QAAQ,CAAS;gBAEN,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC;IAE7C,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,KACnC,OAAO;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE;;;;EAQnC"}
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../src/__tests__/test-utils.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAO9C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,uBAa3D;AAWD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAcxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,QAK/D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,SAGlC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAQrE;AAMD;;;GAGG;AACH,qBAAa,cAAc,CAAC,CAAC;IACpB,SAAS,SAAK;IACd,SAAS,EAAE,CAAC,CAAC;IACpB,OAAO,CAAC,KAAK,CAA0B;gBAE3B,KAAK,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC;IAW1C,OAAO;CAMR;AAED;;;GAGG;AACH,qBAAa,mBAAmB,CAAC,CAAC,EAAE,CAAC;IAGhB,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC;IAF7C,OAAO,CAAC,QAAQ,CAAS;gBAEN,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC;IAE7C,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,KACnC,OAAO;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE;;;;EAQnC"}
@@ -1,4 +1,5 @@
1
1
  // src/__tests__/test-utils.ts
2
+ import { resource } from "../core/resource.js";
2
3
  import {
3
4
  createResourceFiber,
4
5
  unmountResource as unmountResourceFiber,
@@ -6,7 +7,7 @@ import {
6
7
  commitResource
7
8
  } from "../core/ResourceFiber.js";
8
9
  import { tapState } from "../hooks/tap-state.js";
9
- function createTestResource(type) {
10
+ function createTestResource(fn) {
10
11
  const rerenderCallback = () => {
11
12
  if (activeResources.has(fiber)) {
12
13
  const lastProps = propsMap.get(fiber);
@@ -15,7 +16,7 @@ function createTestResource(type) {
15
16
  lastRenderResultMap.set(fiber, result);
16
17
  }
17
18
  };
18
- const fiber = createResourceFiber(type, rerenderCallback);
19
+ const fiber = createResourceFiber(resource(fn), rerenderCallback);
19
20
  return fiber;
20
21
  }
21
22
  var activeResources = /* @__PURE__ */ new Set();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/__tests__/test-utils.ts"],"sourcesContent":["import {\n createResourceFiber,\n unmountResource as unmountResourceFiber,\n renderResource as renderResourceFiber,\n commitResource,\n} from \"../core/ResourceFiber\";\nimport { ResourceFn, ResourceFiber } from \"../core/types\";\nimport { tapState } from \"../hooks/tap-state\";\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 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 const 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<any, any>>();\nconst propsMap = new WeakMap<ResourceFiber<any, any>, any>();\nconst lastRenderResultMap = new WeakMap<ResourceFiber<any, any>, 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<R, P>, 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<R, P>(fiber: ResourceFiber<R, P>) {\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, P>(fiber: ResourceFiber<R, P>): 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<any, any>;\n\n constructor(fiber: ResourceFiber<any, any>) {\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\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<R, P>) {}\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 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":"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","names":[]}
1
+ {"version":3,"sources":["../../src/__tests__/test-utils.ts"],"sourcesContent":["import { resource } from \"../core/resource\";\nimport {\n createResourceFiber,\n unmountResource as unmountResourceFiber,\n renderResource as renderResourceFiber,\n commitResource,\n} from \"../core/ResourceFiber\";\nimport { ResourceFiber } from \"../core/types\";\nimport { tapState } from \"../hooks/tap-state\";\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>(fn: (props: P) => R) {\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 const fiber = createResourceFiber(resource(fn), rerenderCallback);\n return fiber;\n}\n\n// ============================================================================\n// Resource Lifecycle Management\n// ============================================================================\n\n// Track resources for cleanup\nconst activeResources = new Set<ResourceFiber<any, any>>();\nconst propsMap = new WeakMap<ResourceFiber<any, any>, any>();\nconst lastRenderResultMap = new WeakMap<ResourceFiber<any, any>, 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<R, P>, 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<R, P>(fiber: ResourceFiber<R, P>) {\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, P>(fiber: ResourceFiber<R, P>): 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<any, any>;\n\n constructor(fiber: ResourceFiber<any, any>) {\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\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<R, P>) {}\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 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":"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","names":[]}
@@ -1,5 +1,5 @@
1
- import { ResourceFn, ResourceFiber, RenderResult } from "./types";
2
- export declare function createResourceFiber<R, P>(resourceFn: ResourceFn<R, P>, scheduleRerender: () => void): ResourceFiber<R, P>;
1
+ import { ResourceFiber, RenderResult, Resource } from "./types";
2
+ export declare function createResourceFiber<R, P>(resource: Resource<R, P>, scheduleRerender: () => void): ResourceFiber<R, P>;
3
3
  export declare function unmountResource<R, P>(fiber: ResourceFiber<R, P>): void;
4
4
  export declare function renderResource<R, P>(fiber: ResourceFiber<R, P>, props: P): RenderResult;
5
5
  export declare function commitResource<R, P>(fiber: ResourceFiber<R, P>, result: RenderResult): void;
@@ -1 +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,CAAC,CAAC,EAAE,CAAC,CAAC,CAWrB;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAItE;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EACjC,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,KAAK,EAAE,CAAC,GACP,YAAY,CAiBd;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EACjC,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,MAAM,EAAE,YAAY,GACnB,IAAI,CAKN"}
1
+ {"version":3,"file":"ResourceFiber.d.ts","sourceRoot":"","sources":["../../src/core/ResourceFiber.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAKhE,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EACtC,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EACxB,gBAAgB,EAAE,MAAM,IAAI,GAC3B,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAWrB;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAItE;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EACjC,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,KAAK,EAAE,CAAC,GACP,YAAY,CAiBd;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EACjC,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,MAAM,EAAE,YAAY,GACnB,IAAI,CAKN"}
@@ -1,9 +1,10 @@
1
1
  // src/core/ResourceFiber.ts
2
2
  import { commitRender, cleanupAllEffects } from "./commit.js";
3
3
  import { withResourceFiber } from "./execution-context.js";
4
- function createResourceFiber(resourceFn, scheduleRerender) {
4
+ import { callResourceFn } from "./callResourceFn.js";
5
+ function createResourceFiber(resource, scheduleRerender) {
5
6
  return {
6
- resourceFn,
7
+ resource,
7
8
  scheduleRerender,
8
9
  cells: [],
9
10
  currentIndex: 0,
@@ -26,7 +27,7 @@ function renderResource(fiber, props) {
26
27
  withResourceFiber(fiber, () => {
27
28
  fiber.renderContext = result;
28
29
  try {
29
- result.state = fiber.resourceFn(props);
30
+ result.state = callResourceFn(fiber.resource, props);
30
31
  } finally {
31
32
  fiber.renderContext = void 0;
32
33
  }
@@ -1 +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<R, P> {\n return {\n resourceFn,\n scheduleRerender,\n cells: [],\n currentIndex: 0,\n renderContext: undefined,\n isFirstRender: true,\n isMounted: false,\n isNeverMounted: true,\n };\n}\n\nexport function unmountResource<R, P>(fiber: ResourceFiber<R, P>): void {\n // Clean up all effects\n fiber.isMounted = false;\n cleanupAllEffects(fiber);\n}\n\nexport function renderResource<R, P>(\n fiber: ResourceFiber<R, P>,\n props: P,\n): RenderResult {\n const result: RenderResult = {\n commitTasks: [],\n props,\n state: undefined,\n };\n\n withResourceFiber(fiber, () => {\n fiber.renderContext = result;\n try {\n result.state = fiber.resourceFn(props);\n } finally {\n fiber.renderContext = undefined;\n }\n });\n\n return result;\n}\n\nexport function commitResource<R, P>(\n fiber: ResourceFiber<R, P>,\n result: RenderResult,\n): void {\n fiber.isMounted = true;\n fiber.isNeverMounted = false;\n\n commitRender(result, fiber);\n}\n"],"mappings":";AACA,SAAS,cAAc,yBAAyB;AAChD,SAAS,yBAAyB;AAE3B,SAAS,oBACd,YACA,kBACqB;AACrB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO,CAAC;AAAA,IACR,cAAc;AAAA,IACd,eAAe;AAAA,IACf,eAAe;AAAA,IACf,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AACF;AAEO,SAAS,gBAAsB,OAAkC;AAEtE,QAAM,YAAY;AAClB,oBAAkB,KAAK;AACzB;AAEO,SAAS,eACd,OACA,OACc;AACd,QAAM,SAAuB;AAAA,IAC3B,aAAa,CAAC;AAAA,IACd;AAAA,IACA,OAAO;AAAA,EACT;AAEA,oBAAkB,OAAO,MAAM;AAC7B,UAAM,gBAAgB;AACtB,QAAI;AACF,aAAO,QAAQ,MAAM,WAAW,KAAK;AAAA,IACvC,UAAE;AACA,YAAM,gBAAgB;AAAA,IACxB;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAEO,SAAS,eACd,OACA,QACM;AACN,QAAM,YAAY;AAClB,QAAM,iBAAiB;AAEvB,eAAa,QAAQ,KAAK;AAC5B;","names":[]}
1
+ {"version":3,"sources":["../../src/core/ResourceFiber.ts"],"sourcesContent":["import { ResourceFiber, RenderResult, Resource } from \"./types\";\nimport { commitRender, cleanupAllEffects } from \"./commit\";\nimport { withResourceFiber } from \"./execution-context\";\nimport { callResourceFn } from \"./callResourceFn\";\n\nexport function createResourceFiber<R, P>(\n resource: Resource<R, P>,\n scheduleRerender: () => void,\n): ResourceFiber<R, P> {\n return {\n resource,\n scheduleRerender,\n cells: [],\n currentIndex: 0,\n renderContext: undefined,\n isFirstRender: true,\n isMounted: false,\n isNeverMounted: true,\n };\n}\n\nexport function unmountResource<R, P>(fiber: ResourceFiber<R, P>): void {\n // Clean up all effects\n fiber.isMounted = false;\n cleanupAllEffects(fiber);\n}\n\nexport function renderResource<R, P>(\n fiber: ResourceFiber<R, P>,\n props: P,\n): RenderResult {\n const result: RenderResult = {\n commitTasks: [],\n props,\n state: undefined,\n };\n\n withResourceFiber(fiber, () => {\n fiber.renderContext = result;\n try {\n result.state = callResourceFn(fiber.resource, props);\n } finally {\n fiber.renderContext = undefined;\n }\n });\n\n return result;\n}\n\nexport function commitResource<R, P>(\n fiber: ResourceFiber<R, P>,\n result: RenderResult,\n): void {\n fiber.isMounted = true;\n fiber.isNeverMounted = false;\n\n commitRender(result, fiber);\n}\n"],"mappings":";AACA,SAAS,cAAc,yBAAyB;AAChD,SAAS,yBAAyB;AAClC,SAAS,sBAAsB;AAExB,SAAS,oBACd,UACA,kBACqB;AACrB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO,CAAC;AAAA,IACR,cAAc;AAAA,IACd,eAAe;AAAA,IACf,eAAe;AAAA,IACf,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AACF;AAEO,SAAS,gBAAsB,OAAkC;AAEtE,QAAM,YAAY;AAClB,oBAAkB,KAAK;AACzB;AAEO,SAAS,eACd,OACA,OACc;AACd,QAAM,SAAuB;AAAA,IAC3B,aAAa,CAAC;AAAA,IACd;AAAA,IACA,OAAO;AAAA,EACT;AAEA,oBAAkB,OAAO,MAAM;AAC7B,UAAM,gBAAgB;AACtB,QAAI;AACF,aAAO,QAAQ,eAAe,MAAM,UAAU,KAAK;AAAA,IACrD,UAAE;AACA,YAAM,gBAAgB;AAAA,IACxB;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAEO,SAAS,eACd,OACA,QACM;AACN,QAAM,YAAY;AAClB,QAAM,iBAAiB;AAEvB,eAAa,QAAQ,KAAK;AAC5B;","names":[]}
@@ -1,10 +1,13 @@
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
- flushSync(): void;
7
- dispose(): void;
1
+ import { ResourceElement } from "./types";
2
+ export declare namespace createResource {
3
+ type Unsubscribe = () => void;
4
+ interface Handle<R, P> {
5
+ getState(): R;
6
+ subscribe(callback: () => void): Unsubscribe;
7
+ updateInput(props: P): void;
8
+ flushSync(): void;
9
+ dispose(): void;
10
+ }
8
11
  }
9
- export declare const createResource: <R, P>(element: ResourceElement<R, P>, delayMount?: boolean) => ResourceHandle<R, P>;
12
+ export declare const createResource: <R, P>(element: ResourceElement<R, P>, delayMount?: boolean) => createResource.Handle<R, P>;
10
13
  //# sourceMappingURL=ResourceHandle.d.ts.map
@@ -1 +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;IAC5B,SAAS,IAAI,IAAI,CAAC;IAClB,OAAO,IAAI,IAAI,CAAC;CACjB;AA6CD,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EACjC,SAAS,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,oBAAkB,KACjB,cAAc,CAAC,CAAC,EAAE,CAAC,CA2BrB,CAAC"}
1
+ {"version":3,"file":"ResourceHandle.d.ts","sourceRoot":"","sources":["../../src/core/ResourceHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAe1C,yBAAiB,cAAc,CAAC;IAC9B,KAAY,WAAW,GAAG,MAAM,IAAI,CAAC;IAErC,UAAiB,MAAM,CAAC,CAAC,EAAE,CAAC;QAC1B,QAAQ,IAAI,CAAC,CAAC;QACd,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;QAC7C,WAAW,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QAC5B,SAAS,IAAI,IAAI,CAAC;QAClB,OAAO,IAAI,IAAI,CAAC;KACjB;CACF;AA+CD,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EACjC,SAAS,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,oBAAkB,KACjB,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CA2B5B,CAAC"}
@@ -11,40 +11,43 @@ import { tapState } from "../hooks/tap-state.js";
11
11
  import { tapMemo } from "../hooks/tap-memo.js";
12
12
  import { tapInlineResource } from "../hooks/tap-inline-resource.js";
13
13
  import { tapEffect } from "../hooks/tap-effect.js";
14
- var HandleWrapperResource = ({
15
- element,
16
- onUpdateInput,
17
- onFlushSync,
18
- onDispose
19
- }) => {
20
- const [props, setProps] = tapState(element.props);
21
- const value = tapInlineResource({ type: element.type, props });
22
- const subscribers = tapRef(/* @__PURE__ */ new Set()).current;
23
- const valueRef = tapRef(value);
24
- tapEffect(() => {
25
- if (value !== valueRef.current) {
26
- valueRef.current = value;
27
- subscribers.forEach((callback) => callback());
28
- }
29
- });
30
- const handle = tapMemo(
31
- () => ({
32
- getState: () => valueRef.current,
33
- subscribe: (callback) => {
34
- subscribers.add(callback);
35
- return () => subscribers.delete(callback);
36
- },
37
- updateInput: (props2) => {
38
- onUpdateInput();
39
- setProps(() => props2);
40
- },
41
- flushSync: onFlushSync,
42
- dispose: onDispose
43
- }),
44
- []
45
- );
46
- return handle;
47
- };
14
+ import { resource } from "./resource.js";
15
+ var HandleWrapperResource = resource(
16
+ ({
17
+ element,
18
+ onUpdateInput,
19
+ onFlushSync,
20
+ onDispose
21
+ }) => {
22
+ const [props, setProps] = tapState(element.props);
23
+ const value = tapInlineResource({ type: element.type, props });
24
+ const subscribers = tapRef(/* @__PURE__ */ new Set()).current;
25
+ const valueRef = tapRef(value);
26
+ tapEffect(() => {
27
+ if (value !== valueRef.current) {
28
+ valueRef.current = value;
29
+ subscribers.forEach((callback) => callback());
30
+ }
31
+ });
32
+ const handle = tapMemo(
33
+ () => ({
34
+ getState: () => valueRef.current,
35
+ subscribe: (callback) => {
36
+ subscribers.add(callback);
37
+ return () => subscribers.delete(callback);
38
+ },
39
+ updateInput: (props2) => {
40
+ onUpdateInput();
41
+ setProps(() => props2);
42
+ },
43
+ flushSync: onFlushSync,
44
+ dispose: onDispose
45
+ }),
46
+ []
47
+ );
48
+ return handle;
49
+ }
50
+ );
48
51
  var createResource = (element, delayMount = false) => {
49
52
  let isMounted = !delayMount;
50
53
  const props = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/ResourceHandle.ts"],"sourcesContent":["import { ResourceElement, Unsubscribe } from \"./types\";\nimport {\n createResourceFiber,\n unmountResource,\n renderResource,\n commitResource,\n} from \"./ResourceFiber\";\nimport { UpdateScheduler } from \"./scheduler\";\nimport { tapRef } from \"../hooks/tap-ref\";\nimport { tapState } from \"../hooks/tap-state\";\nimport { tapMemo } from \"../hooks/tap-memo\";\nimport { tapInlineResource } from \"../hooks/tap-inline-resource\";\nimport { tapEffect } from \"../hooks/tap-effect\";\n\nexport interface ResourceHandle<R, P> {\n getState(): R;\n subscribe(callback: () => void): Unsubscribe;\n updateInput(props: P): void;\n flushSync(): void;\n dispose(): void;\n}\n\nconst HandleWrapperResource = <R, P>({\n element,\n onUpdateInput,\n onFlushSync,\n onDispose,\n}: {\n element: ResourceElement<R, P>;\n onUpdateInput: () => void;\n onFlushSync: () => void;\n onDispose: () => void;\n}): ResourceHandle<R, P> => {\n const [props, setProps] = tapState(element.props);\n const value = tapInlineResource({ type: element.type, props });\n const subscribers = tapRef(new Set<() => void>()).current;\n const valueRef = tapRef(value);\n\n tapEffect(() => {\n if (value !== valueRef.current) {\n valueRef.current = value;\n subscribers.forEach((callback) => callback());\n }\n });\n\n const handle = tapMemo(\n () => ({\n getState: () => valueRef.current,\n subscribe: (callback: () => void) => {\n subscribers.add(callback);\n return () => subscribers.delete(callback);\n },\n updateInput: (props: P) => {\n onUpdateInput();\n setProps(() => props);\n },\n flushSync: onFlushSync,\n dispose: onDispose,\n }),\n [],\n );\n\n return handle;\n};\n\nexport const createResource = <R, P>(\n element: ResourceElement<R, P>,\n delayMount = false,\n): ResourceHandle<R, P> => {\n let isMounted = !delayMount;\n const props = {\n element,\n onUpdateInput: () => {\n if (isMounted) return;\n isMounted = true;\n commitResource(fiber, lastRender);\n },\n onFlushSync: () => {\n scheduler.flushSync();\n },\n onDispose: () => unmountResource(fiber),\n };\n\n const scheduler = new UpdateScheduler(() => {\n lastRender = renderResource(fiber, props);\n if (isMounted) commitResource(fiber, lastRender);\n });\n\n const fiber = createResourceFiber(HandleWrapperResource<R, P>, () =>\n scheduler.markDirty(),\n );\n\n let lastRender = renderResource(fiber, props);\n if (isMounted) commitResource(fiber, lastRender);\n return lastRender.state;\n};\n"],"mappings":";AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;AAU1B,IAAM,wBAAwB,CAAO;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAK4B;AAC1B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,QAAQ,KAAK;AAChD,QAAM,QAAQ,kBAAkB,EAAE,MAAM,QAAQ,MAAM,MAAM,CAAC;AAC7D,QAAM,cAAc,OAAO,oBAAI,IAAgB,CAAC,EAAE;AAClD,QAAM,WAAW,OAAO,KAAK;AAE7B,YAAU,MAAM;AACd,QAAI,UAAU,SAAS,SAAS;AAC9B,eAAS,UAAU;AACnB,kBAAY,QAAQ,CAAC,aAAa,SAAS,CAAC;AAAA,IAC9C;AAAA,EACF,CAAC;AAED,QAAM,SAAS;AAAA,IACb,OAAO;AAAA,MACL,UAAU,MAAM,SAAS;AAAA,MACzB,WAAW,CAAC,aAAyB;AACnC,oBAAY,IAAI,QAAQ;AACxB,eAAO,MAAM,YAAY,OAAO,QAAQ;AAAA,MAC1C;AAAA,MACA,aAAa,CAACA,WAAa;AACzB,sBAAc;AACd,iBAAS,MAAMA,MAAK;AAAA,MACtB;AAAA,MACA,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEO,IAAM,iBAAiB,CAC5B,SACA,aAAa,UACY;AACzB,MAAI,YAAY,CAAC;AACjB,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,eAAe,MAAM;AACnB,UAAI,UAAW;AACf,kBAAY;AACZ,qBAAe,OAAO,UAAU;AAAA,IAClC;AAAA,IACA,aAAa,MAAM;AACjB,gBAAU,UAAU;AAAA,IACtB;AAAA,IACA,WAAW,MAAM,gBAAgB,KAAK;AAAA,EACxC;AAEA,QAAM,YAAY,IAAI,gBAAgB,MAAM;AAC1C,iBAAa,eAAe,OAAO,KAAK;AACxC,QAAI,UAAW,gBAAe,OAAO,UAAU;AAAA,EACjD,CAAC;AAED,QAAM,QAAQ;AAAA,IAAoB;AAAA,IAA6B,MAC7D,UAAU,UAAU;AAAA,EACtB;AAEA,MAAI,aAAa,eAAe,OAAO,KAAK;AAC5C,MAAI,UAAW,gBAAe,OAAO,UAAU;AAC/C,SAAO,WAAW;AACpB;","names":["props"]}
1
+ {"version":3,"sources":["../../src/core/ResourceHandle.ts"],"sourcesContent":["import { ResourceElement } from \"./types\";\nimport {\n createResourceFiber,\n unmountResource,\n renderResource,\n commitResource,\n} from \"./ResourceFiber\";\nimport { UpdateScheduler } from \"./scheduler\";\nimport { tapRef } from \"../hooks/tap-ref\";\nimport { tapState } from \"../hooks/tap-state\";\nimport { tapMemo } from \"../hooks/tap-memo\";\nimport { tapInlineResource } from \"../hooks/tap-inline-resource\";\nimport { tapEffect } from \"../hooks/tap-effect\";\nimport { resource } from \"./resource\";\n\nexport namespace createResource {\n export type Unsubscribe = () => void;\n\n export interface Handle<R, P> {\n getState(): R;\n subscribe(callback: () => void): Unsubscribe;\n updateInput(props: P): void;\n flushSync(): void;\n dispose(): void;\n }\n}\n\nconst HandleWrapperResource = resource(\n <R, P>({\n element,\n onUpdateInput,\n onFlushSync,\n onDispose,\n }: {\n element: ResourceElement<R, P>;\n onUpdateInput: () => void;\n onFlushSync: () => void;\n onDispose: () => void;\n }): createResource.Handle<R, P> => {\n const [props, setProps] = tapState(element.props);\n const value = tapInlineResource({ type: element.type, props });\n const subscribers = tapRef(new Set<() => void>()).current;\n const valueRef = tapRef(value);\n\n tapEffect(() => {\n if (value !== valueRef.current) {\n valueRef.current = value;\n subscribers.forEach((callback) => callback());\n }\n });\n\n const handle = tapMemo(\n () => ({\n getState: () => valueRef.current,\n subscribe: (callback: () => void) => {\n subscribers.add(callback);\n return () => subscribers.delete(callback);\n },\n updateInput: (props: P) => {\n onUpdateInput();\n setProps(() => props);\n },\n flushSync: onFlushSync,\n dispose: onDispose,\n }),\n [],\n );\n\n return handle;\n },\n);\n\nexport const createResource = <R, P>(\n element: ResourceElement<R, P>,\n delayMount = false,\n): createResource.Handle<R, P> => {\n let isMounted = !delayMount;\n const props = {\n element,\n onUpdateInput: () => {\n if (isMounted) return;\n isMounted = true;\n commitResource(fiber, lastRender);\n },\n onFlushSync: () => {\n scheduler.flushSync();\n },\n onDispose: () => unmountResource(fiber),\n };\n\n const scheduler = new UpdateScheduler(() => {\n lastRender = renderResource(fiber, props);\n if (isMounted) commitResource(fiber, lastRender);\n });\n\n const fiber = createResourceFiber(HandleWrapperResource<R, P>, () =>\n scheduler.markDirty(),\n );\n\n let lastRender = renderResource(fiber, props);\n if (isMounted) commitResource(fiber, lastRender);\n return lastRender.state;\n};\n"],"mappings":";AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;AAC1B,SAAS,gBAAgB;AAczB,IAAM,wBAAwB;AAAA,EAC5B,CAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAKmC;AACjC,UAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,QAAQ,KAAK;AAChD,UAAM,QAAQ,kBAAkB,EAAE,MAAM,QAAQ,MAAM,MAAM,CAAC;AAC7D,UAAM,cAAc,OAAO,oBAAI,IAAgB,CAAC,EAAE;AAClD,UAAM,WAAW,OAAO,KAAK;AAE7B,cAAU,MAAM;AACd,UAAI,UAAU,SAAS,SAAS;AAC9B,iBAAS,UAAU;AACnB,oBAAY,QAAQ,CAAC,aAAa,SAAS,CAAC;AAAA,MAC9C;AAAA,IACF,CAAC;AAED,UAAM,SAAS;AAAA,MACb,OAAO;AAAA,QACL,UAAU,MAAM,SAAS;AAAA,QACzB,WAAW,CAAC,aAAyB;AACnC,sBAAY,IAAI,QAAQ;AACxB,iBAAO,MAAM,YAAY,OAAO,QAAQ;AAAA,QAC1C;AAAA,QACA,aAAa,CAACA,WAAa;AACzB,wBAAc;AACd,mBAAS,MAAMA,MAAK;AAAA,QACtB;AAAA,QACA,WAAW;AAAA,QACX,SAAS;AAAA,MACX;AAAA,MACA,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,iBAAiB,CAC5B,SACA,aAAa,UACmB;AAChC,MAAI,YAAY,CAAC;AACjB,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,eAAe,MAAM;AACnB,UAAI,UAAW;AACf,kBAAY;AACZ,qBAAe,OAAO,UAAU;AAAA,IAClC;AAAA,IACA,aAAa,MAAM;AACjB,gBAAU,UAAU;AAAA,IACtB;AAAA,IACA,WAAW,MAAM,gBAAgB,KAAK;AAAA,EACxC;AAEA,QAAM,YAAY,IAAI,gBAAgB,MAAM;AAC1C,iBAAa,eAAe,OAAO,KAAK;AACxC,QAAI,UAAW,gBAAe,OAAO,UAAU;AAAA,EACjD,CAAC;AAED,QAAM,QAAQ;AAAA,IAAoB;AAAA,IAA6B,MAC7D,UAAU,UAAU;AAAA,EACtB;AAEA,MAAI,aAAa,eAAe,OAAO,KAAK;AAC5C,MAAI,UAAW,gBAAe,OAAO,UAAU;AAC/C,SAAO,WAAW;AACpB;","names":["props"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=callResourceFn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callResourceFn.d.ts","sourceRoot":"","sources":["../../src/core/callResourceFn.ts"],"names":[],"mappings":""}
@@ -1,14 +1,14 @@
1
- // src/core/getResourceFn.ts
2
- function getResourceFn(resource) {
1
+ // src/core/callResourceFn.ts
2
+ function callResourceFn(resource, props) {
3
3
  const fn = resource[fnSymbol];
4
4
  if (!fn) {
5
5
  throw new Error("ResourceElement.type is not a valid Resource");
6
6
  }
7
- return fn;
7
+ return fn(props);
8
8
  }
9
9
  var fnSymbol = Symbol("fnSymbol");
10
10
  export {
11
- fnSymbol,
12
- getResourceFn
11
+ callResourceFn,
12
+ fnSymbol
13
13
  };
14
- //# sourceMappingURL=getResourceFn.js.map
14
+ //# sourceMappingURL=callResourceFn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/callResourceFn.ts"],"sourcesContent":["import { Resource } from \"./types\";\n\n/**\n * Renders a resource with the given props.\n * @internal This is for internal use only.\n */\nexport function callResourceFn<R, P>(resource: Resource<R, P>, props: P): R {\n const fn = (resource as unknown as { [fnSymbol]?: (props: P) => R })[\n fnSymbol\n ];\n if (!fn) {\n throw new Error(\"ResourceElement.type is not a valid Resource\");\n }\n return fn(props);\n}\n\n/**\n * Symbol used to store the ResourceFn in the Resource constructor.\n * @internal This is for internal use only.\n */\nexport const fnSymbol = Symbol(\"fnSymbol\");\n"],"mappings":";AAMO,SAAS,eAAqB,UAA0B,OAAa;AAC1E,QAAM,KAAM,SACV,QACF;AACA,MAAI,CAAC,IAAI;AACP,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,SAAO,GAAG,KAAK;AACjB;AAMO,IAAM,WAAW,OAAO,UAAU;","names":[]}
@@ -1,3 +1,4 @@
1
- import { ResourceFn, Resource } from "./types";
2
- export declare function resource<R, P = undefined>(fn: ResourceFn<R, P>): Resource<R, P>;
1
+ import { ResourceElement, Resource } from "./types";
2
+ export declare function resource<R>(fn: () => R): () => ResourceElement<R, undefined>;
3
+ export declare function resource<R, P>(fn: (props: P) => R): Resource<R, P>;
3
4
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/core/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGhE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,EACvC,EAAE,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GACnB,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAYhB"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/core/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEpD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,MAAM,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAC9E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  // src/core/resource.ts
2
- import { fnSymbol } from "./getResourceFn.js";
2
+ import { fnSymbol } from "./callResourceFn.js";
3
3
  function resource(fn) {
4
4
  const type = (props, options) => {
5
5
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/resource.ts"],"sourcesContent":["import { ResourceFn, ResourceElement, Resource } from \"./types\";\nimport { fnSymbol } from \"./getResourceFn\";\n\nexport function resource<R, P = undefined>(\n fn: ResourceFn<R, P>,\n): Resource<R, P> {\n const type = (props?: P, options?: { key?: string | number }) => {\n return {\n type,\n props,\n ...(options?.key !== undefined && { key: options.key }),\n } as ResourceElement<R, P>;\n };\n\n type[fnSymbol] = fn;\n\n return type;\n}\n"],"mappings":";AACA,SAAS,gBAAgB;AAElB,SAAS,SACd,IACgB;AAChB,QAAM,OAAO,CAAC,OAAW,YAAwC;AAC/D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,GAAI,SAAS,QAAQ,UAAa,EAAE,KAAK,QAAQ,IAAI;AAAA,IACvD;AAAA,EACF;AAEA,OAAK,QAAQ,IAAI;AAEjB,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/core/resource.ts"],"sourcesContent":["import { ResourceElement, Resource } from \"./types\";\nimport { fnSymbol } from \"./callResourceFn\";\nexport function resource<R>(fn: () => R): () => ResourceElement<R, undefined>;\nexport function resource<R, P>(fn: (props: P) => R): Resource<R, P>;\nexport function resource<R, P = undefined>(\n fn: (props: P) => R,\n): Resource<R, P> {\n const type = (props?: P, options?: { key?: string | number }) => {\n return {\n type,\n props: props!,\n ...(options?.key !== undefined && { key: options.key }),\n } satisfies ResourceElement<R, P>;\n };\n\n type[fnSymbol] = fn;\n\n return type;\n}\n"],"mappings":";AACA,SAAS,gBAAgB;AAGlB,SAAS,SACd,IACgB;AAChB,QAAM,OAAO,CAAC,OAAW,YAAwC;AAC/D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,GAAI,SAAS,QAAQ,UAAa,EAAE,KAAK,QAAQ,IAAI;AAAA,IACvD;AAAA,EACF;AAEA,OAAK,QAAQ,IAAI;AAEjB,SAAO;AACT;","names":[]}
@@ -1,30 +1,30 @@
1
- export type ResourceFn<R, P> = (props: P) => R;
2
- export type Unsubscribe = () => void;
1
+ import type { tapEffect } from "../hooks/tap-effect";
2
+ import type { tapState } from "../hooks/tap-state";
3
3
  export type ResourceElement<R, P = any> = {
4
4
  type: Resource<R, P>;
5
5
  props: P;
6
6
  key?: string | number;
7
7
  };
8
- export type Resource<R, P> = (...args: undefined extends P ? [props?: P, options?: {
8
+ export type Resource<R, P> = (...args: P extends undefined ? [props?: undefined, options?: {
9
9
  key?: string | number;
10
10
  }] : [props: P, options?: {
11
11
  key?: string | number;
12
12
  }]) => ResourceElement<R, P>;
13
- export type StateUpdater<S> = S | ((prev: S) => S);
14
- export type Destructor = () => void;
15
- export type EffectCallback = () => void | Destructor;
13
+ export type ContravariantResource<R, P> = (props: P, options?: {
14
+ key?: string | number;
15
+ }) => ResourceElement<R, any>;
16
16
  export type Cell = {
17
17
  type: "state";
18
18
  value: any;
19
- set: (updater: StateUpdater<any>) => void;
19
+ set: (updater: tapState.StateUpdater<any>) => void;
20
20
  } | {
21
21
  type: "effect";
22
22
  mounted: boolean;
23
- cleanup?: Destructor | undefined;
23
+ cleanup?: tapEffect.Destructor | undefined;
24
24
  deps?: readonly unknown[] | undefined;
25
25
  };
26
26
  export interface EffectTask {
27
- effect: EffectCallback;
27
+ effect: tapEffect.EffectCallback;
28
28
  deps?: readonly unknown[] | undefined;
29
29
  cellIndex: number;
30
30
  }
@@ -35,7 +35,7 @@ export interface RenderResult {
35
35
  }
36
36
  export interface ResourceFiber<R, P> {
37
37
  readonly scheduleRerender: () => void;
38
- readonly resourceFn: ResourceFn<R, P>;
38
+ readonly resource: Resource<R, P>;
39
39
  cells: Cell[];
40
40
  currentIndex: number;
41
41
  renderContext: RenderResult | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,IAAI;IACxC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrB,KAAK,EAAE,CAAC,CAAC;IACT,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAC3B,GAAG,IAAI,EAAE,SAAS,SAAS,CAAC,GACxB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC,GAChD,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC,KAChD,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE3B,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;AAEnD,MAAM,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC;AACpC,MAAM,MAAM,cAAc,GAAG,MAAM,IAAI,GAAG,UAAU,CAAC;AAErD,MAAM,MAAM,IAAI,GACZ;IACE,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,GAAG,CAAC;IACX,GAAG,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;CAC3C,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;CACvC,CAAC;AAEN,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,EAAE,GAAG,CAAC;IACX,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEtC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IAErB,aAAa,EAAE,YAAY,GAAG,SAAS,CAAC;IAExC,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;CACzB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,IAAI;IACxC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrB,KAAK,EAAE,CAAC,CAAC;IACT,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAC3B,GAAG,IAAI,EAAE,CAAC,SAAS,SAAS,GACxB,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC,GACxD,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC,KAChD,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE3B,MAAM,MAAM,qBAAqB,CAAC,CAAC,EAAE,CAAC,IAAI,CACxC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,KAChC,eAAe,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAE7B,MAAM,MAAM,IAAI,GACZ;IACE,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,GAAG,CAAC;IACX,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;CACpD,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,CAAC,UAAU,GAAG,SAAS,CAAC;IAC3C,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;CACvC,CAAC;AAEN,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,CAAC,cAAc,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,EAAE,GAAG,CAAC;IACX,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAElC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IAErB,aAAa,EAAE,YAAY,GAAG,SAAS,CAAC;IAExC,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;CACzB"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Creates a stable function reference that always calls the most recent version of the callback.
3
+ * Similar to React's useEffectEvent hook.
4
+ *
5
+ * @param callback - The callback function to wrap
6
+ * @returns A stable function reference that always calls the latest callback
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const handleClick = tapEffectEvent((value: string) => {
11
+ * console.log(value);
12
+ * });
13
+ * // handleClick reference is stable, but always calls the latest version
14
+ * ```
15
+ */
16
+ export declare function tapEffectEvent<T extends (...args: any[]) => any>(callback: T): T;
17
+ //# sourceMappingURL=tap-effect-event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tap-effect-event.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-effect-event.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAC9D,QAAQ,EAAE,CAAC,GACV,CAAC,CAQH"}
@@ -0,0 +1,14 @@
1
+ // src/hooks/tap-effect-event.ts
2
+ import { tapRef } from "./tap-ref.js";
3
+ import { tapEffect } from "./tap-effect.js";
4
+ function tapEffectEvent(callback) {
5
+ const callbackRef = tapRef(callback);
6
+ tapEffect(() => {
7
+ callbackRef.current = callback;
8
+ });
9
+ return callbackRef.current;
10
+ }
11
+ export {
12
+ tapEffectEvent
13
+ };
14
+ //# sourceMappingURL=tap-effect-event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/tap-effect-event.ts"],"sourcesContent":["import { tapRef } from \"./tap-ref\";\nimport { tapEffect } from \"./tap-effect\";\n\n/**\n * Creates a stable function reference that always calls the most recent version of the callback.\n * Similar to React's useEffectEvent hook.\n *\n * @param callback - The callback function to wrap\n * @returns A stable function reference that always calls the latest callback\n *\n * @example\n * ```typescript\n * const handleClick = tapEffectEvent((value: string) => {\n * console.log(value);\n * });\n * // handleClick reference is stable, but always calls the latest version\n * ```\n */\nexport function tapEffectEvent<T extends (...args: any[]) => any>(\n callback: T,\n): T {\n const callbackRef = tapRef(callback);\n\n tapEffect(() => {\n callbackRef.current = callback;\n });\n\n return callbackRef.current;\n}\n"],"mappings":";AAAA,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAiBnB,SAAS,eACd,UACG;AACH,QAAM,cAAc,OAAO,QAAQ;AAEnC,YAAU,MAAM;AACd,gBAAY,UAAU;AAAA,EACxB,CAAC;AAED,SAAO,YAAY;AACrB;","names":[]}
@@ -1,4 +1,7 @@
1
- import { EffectCallback } from "../core/types";
2
- export declare function tapEffect(effect: EffectCallback): void;
3
- export declare function tapEffect(effect: EffectCallback, deps: readonly unknown[]): void;
1
+ export declare namespace tapEffect {
2
+ type Destructor = () => void;
3
+ type EffectCallback = () => void | Destructor;
4
+ }
5
+ export declare function tapEffect(effect: tapEffect.EffectCallback): void;
6
+ export declare function tapEffect(effect: tapEffect.EffectCallback, deps: readonly unknown[]): void;
4
7
  //# sourceMappingURL=tap-effect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tap-effect.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-effect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAQ,MAAM,eAAe,CAAC;AAgCrD,wBAAgB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;AACxD,wBAAgB,SAAS,CACvB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,SAAS,OAAO,EAAE,GACvB,IAAI,CAAC"}
1
+ {"version":3,"file":"tap-effect.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-effect.ts"],"names":[],"mappings":"AAiCA,yBAAiB,SAAS,CAAC;IACzB,KAAY,UAAU,GAAG,MAAM,IAAI,CAAC;IACpC,KAAY,cAAc,GAAG,MAAM,IAAI,GAAG,UAAU,CAAC;CACtD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC;AAClE,wBAAgB,SAAS,CACvB,MAAM,EAAE,SAAS,CAAC,cAAc,EAChC,IAAI,EAAE,SAAS,OAAO,EAAE,GACvB,IAAI,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/tap-effect.ts"],"sourcesContent":["import { getCurrentResourceFiber } from \"../core/execution-context\";\nimport { EffectCallback, Cell } from \"../core/types\";\n\nfunction getEffectCell(): number {\n const fiber = getCurrentResourceFiber();\n const index = fiber.currentIndex++;\n\n // Check if we're trying to use more hooks than in previous renders\n if (!fiber.isFirstRender && index >= fiber.cells.length) {\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 (!fiber.cells[index]) {\n // Create the effect cell\n const cell: Cell & { type: \"effect\" } = {\n type: \"effect\",\n mounted: false,\n };\n\n fiber.cells[index] = cell;\n }\n\n const cell = fiber.cells[index];\n if (cell.type !== \"effect\") {\n throw new Error(\"Hook order changed between renders\");\n }\n\n return index;\n}\n\nexport function tapEffect(effect: EffectCallback): void;\nexport function tapEffect(\n effect: EffectCallback,\n deps: readonly unknown[],\n): void;\nexport function tapEffect(\n effect: EffectCallback,\n deps?: readonly unknown[],\n): void {\n const fiber = getCurrentResourceFiber();\n\n // Reserve a spot for the effect cell and get its index\n const cellIndex = getEffectCell();\n\n // Add task to render context for execution in commit phase\n fiber.renderContext!.commitTasks.push({\n effect,\n deps,\n cellIndex,\n });\n}\n"],"mappings":";AAAA,SAAS,+BAA+B;AAGxC,SAAS,gBAAwB;AAC/B,QAAM,QAAQ,wBAAwB;AACtC,QAAM,QAAQ,MAAM;AAGpB,MAAI,CAAC,MAAM,iBAAiB,SAAS,MAAM,MAAM,QAAQ;AACvD,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,CAAC,MAAM,MAAM,KAAK,GAAG;AAEvB,UAAMA,QAAkC;AAAA,MACtC,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAEA,UAAM,MAAM,KAAK,IAAIA;AAAA,EACvB;AAEA,QAAM,OAAO,MAAM,MAAM,KAAK;AAC9B,MAAI,KAAK,SAAS,UAAU;AAC1B,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAEA,SAAO;AACT;AAOO,SAAS,UACd,QACA,MACM;AACN,QAAM,QAAQ,wBAAwB;AAGtC,QAAM,YAAY,cAAc;AAGhC,QAAM,cAAe,YAAY,KAAK;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;","names":["cell"]}
1
+ {"version":3,"sources":["../../src/hooks/tap-effect.ts"],"sourcesContent":["import { getCurrentResourceFiber } from \"../core/execution-context\";\nimport { Cell } from \"../core/types\";\n\nfunction getEffectCell(): number {\n const fiber = getCurrentResourceFiber();\n const index = fiber.currentIndex++;\n\n // Check if we're trying to use more hooks than in previous renders\n if (!fiber.isFirstRender && index >= fiber.cells.length) {\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 (!fiber.cells[index]) {\n // Create the effect cell\n const cell: Cell & { type: \"effect\" } = {\n type: \"effect\",\n mounted: false,\n };\n\n fiber.cells[index] = cell;\n }\n\n const cell = fiber.cells[index];\n if (cell.type !== \"effect\") {\n throw new Error(\"Hook order changed between renders\");\n }\n\n return index;\n}\n\nexport namespace tapEffect {\n export type Destructor = () => void;\n export type EffectCallback = () => void | Destructor;\n}\n\nexport function tapEffect(effect: tapEffect.EffectCallback): void;\nexport function tapEffect(\n effect: tapEffect.EffectCallback,\n deps: readonly unknown[],\n): void;\nexport function tapEffect(\n effect: tapEffect.EffectCallback,\n deps?: readonly unknown[],\n): void {\n const fiber = getCurrentResourceFiber();\n\n // Reserve a spot for the effect cell and get its index\n const cellIndex = getEffectCell();\n\n // Add task to render context for execution in commit phase\n fiber.renderContext!.commitTasks.push({\n effect,\n deps,\n cellIndex,\n });\n}\n"],"mappings":";AAAA,SAAS,+BAA+B;AAGxC,SAAS,gBAAwB;AAC/B,QAAM,QAAQ,wBAAwB;AACtC,QAAM,QAAQ,MAAM;AAGpB,MAAI,CAAC,MAAM,iBAAiB,SAAS,MAAM,MAAM,QAAQ;AACvD,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,CAAC,MAAM,MAAM,KAAK,GAAG;AAEvB,UAAMA,QAAkC;AAAA,MACtC,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAEA,UAAM,MAAM,KAAK,IAAIA;AAAA,EACvB;AAEA,QAAM,OAAO,MAAM,MAAM,KAAK;AAC9B,MAAI,KAAK,SAAS,UAAU;AAC1B,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAEA,SAAO;AACT;AAYO,SAAS,UACd,QACA,MACM;AACN,QAAM,QAAQ,wBAAwB;AAGtC,QAAM,YAAY,cAAc;AAGhC,QAAM,cAAe,YAAY,KAAK;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;","names":["cell"]}
@@ -1,7 +1,7 @@
1
1
  // src/hooks/tap-inline-resource.ts
2
- import { getResourceFn } from "../core/getResourceFn.js";
2
+ import { callResourceFn } from "../core/callResourceFn.js";
3
3
  function tapInlineResource(element) {
4
- return getResourceFn(element.type)(element.props);
4
+ return callResourceFn(element.type, element.props);
5
5
  }
6
6
  export {
7
7
  tapInlineResource
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/tap-inline-resource.ts"],"sourcesContent":["import { ResourceElement } from \"../core/types\";\nimport { getResourceFn } from \"../core/getResourceFn\";\n\nexport function tapInlineResource<R, P>(element: ResourceElement<R, P>): R {\n return getResourceFn(element.type)(element.props);\n}\n"],"mappings":";AACA,SAAS,qBAAqB;AAEvB,SAAS,kBAAwB,SAAmC;AACzE,SAAO,cAAc,QAAQ,IAAI,EAAE,QAAQ,KAAK;AAClD;","names":[]}
1
+ {"version":3,"sources":["../../src/hooks/tap-inline-resource.ts"],"sourcesContent":["import { ResourceElement } from \"../core/types\";\nimport { callResourceFn } from \"../core/callResourceFn\";\n\nexport function tapInlineResource<R, P>(element: ResourceElement<R, P>): R {\n return callResourceFn(element.type, element.props);\n}\n"],"mappings":";AACA,SAAS,sBAAsB;AAExB,SAAS,kBAAwB,SAAmC;AACzE,SAAO,eAAe,QAAQ,MAAM,QAAQ,KAAK;AACnD;","names":[]}
@@ -1,6 +1,8 @@
1
- export interface RefObject<T> {
2
- current: T;
1
+ export declare namespace tapRef {
2
+ interface RefObject<T> {
3
+ current: T;
4
+ }
3
5
  }
4
- export declare function tapRef<T>(initialValue: T): RefObject<T>;
5
- export declare function tapRef<T = undefined>(): RefObject<T | undefined>;
6
+ export declare function tapRef<T>(initialValue: T): tapRef.RefObject<T>;
7
+ export declare function tapRef<T = undefined>(): tapRef.RefObject<T | undefined>;
6
8
  //# sourceMappingURL=tap-ref.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tap-ref.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-ref.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,OAAO,EAAE,CAAC,CAAC;CACZ;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AACzD,wBAAgB,MAAM,CAAC,CAAC,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"tap-ref.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-ref.ts"],"names":[],"mappings":"AAEA,yBAAiB,MAAM,CAAC;IACtB,UAAiB,SAAS,CAAC,CAAC;QAC1B,OAAO,EAAE,CAAC,CAAC;KACZ;CACF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AAChE,wBAAgB,MAAM,CAAC,CAAC,GAAG,SAAS,KAAK,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/tap-ref.ts"],"sourcesContent":["import { tapState } from \"./tap-state\";\n\nexport interface RefObject<T> {\n current: T;\n}\n\nexport function tapRef<T>(initialValue: T): RefObject<T>;\nexport function tapRef<T = undefined>(): RefObject<T | undefined>;\nexport function tapRef<T>(initialValue?: T): RefObject<T | undefined> {\n const [state] = tapState(() => ({\n current: initialValue,\n }));\n return state;\n}\n"],"mappings":";AAAA,SAAS,gBAAgB;AAQlB,SAAS,OAAU,cAA4C;AACpE,QAAM,CAAC,KAAK,IAAI,SAAS,OAAO;AAAA,IAC9B,SAAS;AAAA,EACX,EAAE;AACF,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/hooks/tap-ref.ts"],"sourcesContent":["import { tapState } from \"./tap-state\";\n\nexport namespace tapRef {\n export interface RefObject<T> {\n current: T;\n }\n}\n\nexport function tapRef<T>(initialValue: T): tapRef.RefObject<T>;\nexport function tapRef<T = undefined>(): tapRef.RefObject<T | undefined>;\nexport function tapRef<T>(initialValue?: T): tapRef.RefObject<T | undefined> {\n const [state] = tapState(() => ({\n current: initialValue,\n }));\n return state;\n}\n"],"mappings":";AAAA,SAAS,gBAAgB;AAUlB,SAAS,OAAU,cAAmD;AAC3E,QAAM,CAAC,KAAK,IAAI,SAAS,OAAO;AAAA,IAC9B,SAAS;AAAA,EACX,EAAE;AACF,SAAO;AACT;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"tap-resource.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAYhD,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;AACrE,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAC9B,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,IAAI,EAAE,SAAS,OAAO,EAAE,GACvB,CAAC,CAAC"}
1
+ {"version":3,"file":"tap-resource.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAWhD,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;AACrE,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAC9B,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,IAAI,EAAE,SAAS,OAAO,EAAE,GACvB,CAAC,CAAC"}
@@ -1,5 +1,4 @@
1
1
  // src/hooks/tap-resource.ts
2
- import { getResourceFn } from "../core/getResourceFn.js";
3
2
  import { tapEffect } from "./tap-effect.js";
4
3
  import {
5
4
  createResourceFiber,
@@ -12,7 +11,7 @@ import { tapState } from "./tap-state.js";
12
11
  function tapResource(element, deps) {
13
12
  const [stateVersion, rerender] = tapState({});
14
13
  const fiber = tapMemo(
15
- () => createResourceFiber(getResourceFn(element.type), () => rerender({})),
14
+ () => createResourceFiber(element.type, () => rerender({})),
16
15
  [element.type]
17
16
  );
18
17
  const props = deps ? tapMemo(() => element.props, deps) : element.props;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/tap-resource.ts"],"sourcesContent":["import { ResourceElement } from \"../core/types\";\nimport { getResourceFn } from \"../core/getResourceFn\";\nimport { tapEffect } from \"./tap-effect\";\nimport {\n createResourceFiber,\n unmountResource,\n renderResource,\n commitResource,\n} from \"../core/ResourceFiber\";\nimport { tapMemo } from \"./tap-memo\";\nimport { tapState } from \"./tap-state\";\n\nexport function tapResource<R, P>(element: ResourceElement<R, P>): R;\nexport function tapResource<R, P>(\n element: ResourceElement<R, P>,\n deps: readonly unknown[],\n): R;\nexport function tapResource<R, P>(\n element: ResourceElement<R, P>,\n deps?: readonly unknown[],\n): R {\n const [stateVersion, rerender] = tapState({});\n const fiber = tapMemo(\n () => createResourceFiber(getResourceFn(element.type), () => rerender({})),\n [element.type],\n );\n\n const props = deps ? tapMemo(() => element.props, deps) : element.props;\n const result = tapMemo(\n () => renderResource(fiber, props),\n [fiber, props, stateVersion],\n );\n\n tapEffect(() => {\n return () => unmountResource(fiber);\n }, [fiber]);\n\n tapEffect(() => {\n commitResource(fiber, result);\n }, [fiber, result]);\n\n return result.state;\n}\n"],"mappings":";AACA,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,eAAe;AACxB,SAAS,gBAAgB;AAOlB,SAAS,YACd,SACA,MACG;AACH,QAAM,CAAC,cAAc,QAAQ,IAAI,SAAS,CAAC,CAAC;AAC5C,QAAM,QAAQ;AAAA,IACZ,MAAM,oBAAoB,cAAc,QAAQ,IAAI,GAAG,MAAM,SAAS,CAAC,CAAC,CAAC;AAAA,IACzE,CAAC,QAAQ,IAAI;AAAA,EACf;AAEA,QAAM,QAAQ,OAAO,QAAQ,MAAM,QAAQ,OAAO,IAAI,IAAI,QAAQ;AAClE,QAAM,SAAS;AAAA,IACb,MAAM,eAAe,OAAO,KAAK;AAAA,IACjC,CAAC,OAAO,OAAO,YAAY;AAAA,EAC7B;AAEA,YAAU,MAAM;AACd,WAAO,MAAM,gBAAgB,KAAK;AAAA,EACpC,GAAG,CAAC,KAAK,CAAC;AAEV,YAAU,MAAM;AACd,mBAAe,OAAO,MAAM;AAAA,EAC9B,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,SAAO,OAAO;AAChB;","names":[]}
1
+ {"version":3,"sources":["../../src/hooks/tap-resource.ts"],"sourcesContent":["import { ResourceElement } from \"../core/types\";\nimport { tapEffect } from \"./tap-effect\";\nimport {\n createResourceFiber,\n unmountResource,\n renderResource,\n commitResource,\n} from \"../core/ResourceFiber\";\nimport { tapMemo } from \"./tap-memo\";\nimport { tapState } from \"./tap-state\";\n\nexport function tapResource<R, P>(element: ResourceElement<R, P>): R;\nexport function tapResource<R, P>(\n element: ResourceElement<R, P>,\n deps: readonly unknown[],\n): R;\nexport function tapResource<R, P>(\n element: ResourceElement<R, P>,\n deps?: readonly unknown[],\n): R {\n const [stateVersion, rerender] = tapState({});\n const fiber = tapMemo(\n () => createResourceFiber(element.type, () => rerender({})),\n [element.type],\n );\n\n const props = deps ? tapMemo(() => element.props, deps) : element.props;\n const result = tapMemo(\n () => renderResource(fiber, props),\n [fiber, props, stateVersion],\n );\n\n tapEffect(() => {\n return () => unmountResource(fiber);\n }, [fiber]);\n\n tapEffect(() => {\n commitResource(fiber, result);\n }, [fiber, result]);\n\n return result.state;\n}\n"],"mappings":";AACA,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,eAAe;AACxB,SAAS,gBAAgB;AAOlB,SAAS,YACd,SACA,MACG;AACH,QAAM,CAAC,cAAc,QAAQ,IAAI,SAAS,CAAC,CAAC;AAC5C,QAAM,QAAQ;AAAA,IACZ,MAAM,oBAAoB,QAAQ,MAAM,MAAM,SAAS,CAAC,CAAC,CAAC;AAAA,IAC1D,CAAC,QAAQ,IAAI;AAAA,EACf;AAEA,QAAM,QAAQ,OAAO,QAAQ,MAAM,QAAQ,OAAO,IAAI,IAAI,QAAQ;AAClE,QAAM,SAAS;AAAA,IACb,MAAM,eAAe,OAAO,KAAK;AAAA,IACjC,CAAC,OAAO,OAAO,YAAY;AAAA,EAC7B;AAEA,YAAU,MAAM;AACd,WAAO,MAAM,gBAAgB,KAAK;AAAA,EACpC,GAAG,CAAC,KAAK,CAAC;AAEV,YAAU,MAAM;AACd,mBAAe,OAAO,MAAM;AAAA,EAC9B,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,SAAO,OAAO;AAChB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"tap-resources.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAY/D,wBAAgB,YAAY,CAC1B,CAAC,SAAS,aAAa,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAElD,QAAQ,EAAE,CAAC,GACV;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,eAAe,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK;CAAE,CA2F5E"}
1
+ {"version":3,"file":"tap-resources.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAW/D,wBAAgB,YAAY,CAC1B,CAAC,SAAS,aAAa,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAElD,QAAQ,EAAE,CAAC,GACV;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,eAAe,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK;CAAE,CAyF5E"}
@@ -1,5 +1,4 @@
1
1
  // src/hooks/tap-resources.ts
2
- import { getResourceFn } from "../core/getResourceFn.js";
3
2
  import { tapEffect } from "./tap-effect.js";
4
3
  import { tapMemo } from "./tap-memo.js";
5
4
  import { tapState } from "./tap-state.js";
@@ -38,12 +37,9 @@ function tapResources(elements) {
38
37
  elementsByKey.forEach((element, key) => {
39
38
  currentKeys.add(key);
40
39
  let fiber = fibers.get(key);
41
- if (!fiber || fiber.resourceFn !== getResourceFn(element.type)) {
40
+ if (!fiber || fiber.resource !== element.type) {
42
41
  if (fiber) unmountResource(fiber);
43
- fiber = createResourceFiber(
44
- getResourceFn(element.type),
45
- () => rerender({})
46
- );
42
+ fiber = createResourceFiber(element.type, () => rerender({}));
47
43
  fibers.set(key, fiber);
48
44
  }
49
45
  const result = renderResource(fiber, element.props);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/tap-resources.ts"],"sourcesContent":["import { ResourceElement, ResourceFiber } from \"../core/types\";\nimport { getResourceFn } from \"../core/getResourceFn\";\nimport { tapEffect } from \"./tap-effect\";\nimport { tapMemo } from \"./tap-memo\";\nimport { tapState } from \"./tap-state\";\nimport {\n createResourceFiber,\n unmountResource,\n renderResource,\n commitResource,\n} from \"../core/ResourceFiber\";\n\nexport function tapResources<\n T extends ReadonlyArray<ResourceElement<any, any>>,\n>(\n elements: T,\n): { [K in keyof T]: T[K] extends ResourceElement<infer R, any> ? R : never } {\n // Validate keys\n const seenKeys = new Set<string | number>();\n elements.forEach((element, index) => {\n if (element.key === undefined) {\n throw new Error(\n `tapResources: All resource elements must have a key. Element at index ${index} is missing a key.`,\n );\n }\n if (seenKeys.has(element.key)) {\n throw new Error(\n `tapResources: Duplicate key \"${element.key}\" found. All keys must be unique.`,\n );\n }\n seenKeys.add(element.key);\n });\n\n const [stateVersion, rerender] = tapState({});\n\n // Create a map of current elements by key for efficient lookup\n const elementsByKey = tapMemo(\n () => new Map(elements.map((element) => [element.key!, element])),\n [elements],\n );\n\n // Track fibers persistently across renders\n const [fibers] = tapState(\n () => new Map<string | number, ResourceFiber<any, any>>(),\n );\n\n // Process each element\n const results = tapMemo(() => {\n const resultMap = new Map<string | number, any>();\n const currentKeys = new Set<string | number>();\n\n // Create/update fibers and render\n elementsByKey.forEach((element, key) => {\n currentKeys.add(key);\n\n let fiber = fibers.get(key);\n\n // Create new fiber if needed or type changed\n if (!fiber || fiber.resourceFn !== getResourceFn(element.type)) {\n if (fiber) unmountResource(fiber);\n fiber = createResourceFiber(getResourceFn(element.type), () =>\n rerender({}),\n );\n fibers.set(key, fiber);\n }\n\n // Render with current props\n const result = renderResource(fiber, element.props);\n resultMap.set(key, result);\n });\n\n // Clean up removed fibers\n fibers.forEach((fiber, key) => {\n if (!currentKeys.has(key)) {\n unmountResource(fiber);\n fibers.delete(key);\n }\n });\n\n return resultMap;\n }, [elementsByKey, stateVersion]);\n\n // Commit all renders\n tapEffect(() => {\n results.forEach((result, key) => {\n const fiber = fibers.get(key);\n if (fiber) {\n commitResource(fiber, result);\n }\n });\n }, [results, fibers]);\n\n // Cleanup on unmount\n tapEffect(() => {\n return () => {\n fibers.forEach((fiber) => {\n unmountResource(fiber);\n });\n fibers.clear();\n };\n }, [fibers]);\n\n // Return results in the same order as input elements\n return tapMemo(\n () => elements.map((element) => results.get(element.key!)?.state),\n [elements, results],\n ) as any;\n}\n"],"mappings":";AACA,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,SAAS,aAGd,UAC4E;AAE5E,QAAM,WAAW,oBAAI,IAAqB;AAC1C,WAAS,QAAQ,CAAC,SAAS,UAAU;AACnC,QAAI,QAAQ,QAAQ,QAAW;AAC7B,YAAM,IAAI;AAAA,QACR,yEAAyE,KAAK;AAAA,MAChF;AAAA,IACF;AACA,QAAI,SAAS,IAAI,QAAQ,GAAG,GAAG;AAC7B,YAAM,IAAI;AAAA,QACR,gCAAgC,QAAQ,GAAG;AAAA,MAC7C;AAAA,IACF;AACA,aAAS,IAAI,QAAQ,GAAG;AAAA,EAC1B,CAAC;AAED,QAAM,CAAC,cAAc,QAAQ,IAAI,SAAS,CAAC,CAAC;AAG5C,QAAM,gBAAgB;AAAA,IACpB,MAAM,IAAI,IAAI,SAAS,IAAI,CAAC,YAAY,CAAC,QAAQ,KAAM,OAAO,CAAC,CAAC;AAAA,IAChE,CAAC,QAAQ;AAAA,EACX;AAGA,QAAM,CAAC,MAAM,IAAI;AAAA,IACf,MAAM,oBAAI,IAA8C;AAAA,EAC1D;AAGA,QAAM,UAAU,QAAQ,MAAM;AAC5B,UAAM,YAAY,oBAAI,IAA0B;AAChD,UAAM,cAAc,oBAAI,IAAqB;AAG7C,kBAAc,QAAQ,CAAC,SAAS,QAAQ;AACtC,kBAAY,IAAI,GAAG;AAEnB,UAAI,QAAQ,OAAO,IAAI,GAAG;AAG1B,UAAI,CAAC,SAAS,MAAM,eAAe,cAAc,QAAQ,IAAI,GAAG;AAC9D,YAAI,MAAO,iBAAgB,KAAK;AAChC,gBAAQ;AAAA,UAAoB,cAAc,QAAQ,IAAI;AAAA,UAAG,MACvD,SAAS,CAAC,CAAC;AAAA,QACb;AACA,eAAO,IAAI,KAAK,KAAK;AAAA,MACvB;AAGA,YAAM,SAAS,eAAe,OAAO,QAAQ,KAAK;AAClD,gBAAU,IAAI,KAAK,MAAM;AAAA,IAC3B,CAAC;AAGD,WAAO,QAAQ,CAAC,OAAO,QAAQ;AAC7B,UAAI,CAAC,YAAY,IAAI,GAAG,GAAG;AACzB,wBAAgB,KAAK;AACrB,eAAO,OAAO,GAAG;AAAA,MACnB;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,YAAY,CAAC;AAGhC,YAAU,MAAM;AACd,YAAQ,QAAQ,CAAC,QAAQ,QAAQ;AAC/B,YAAM,QAAQ,OAAO,IAAI,GAAG;AAC5B,UAAI,OAAO;AACT,uBAAe,OAAO,MAAM;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,MAAM,CAAC;AAGpB,YAAU,MAAM;AACd,WAAO,MAAM;AACX,aAAO,QAAQ,CAAC,UAAU;AACxB,wBAAgB,KAAK;AAAA,MACvB,CAAC;AACD,aAAO,MAAM;AAAA,IACf;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,SAAO;AAAA,IACL,MAAM,SAAS,IAAI,CAAC,YAAY,QAAQ,IAAI,QAAQ,GAAI,GAAG,KAAK;AAAA,IAChE,CAAC,UAAU,OAAO;AAAA,EACpB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/hooks/tap-resources.ts"],"sourcesContent":["import { ResourceElement, ResourceFiber } from \"../core/types\";\nimport { tapEffect } from \"./tap-effect\";\nimport { tapMemo } from \"./tap-memo\";\nimport { tapState } from \"./tap-state\";\nimport {\n createResourceFiber,\n unmountResource,\n renderResource,\n commitResource,\n} from \"../core/ResourceFiber\";\n\nexport function tapResources<\n T extends ReadonlyArray<ResourceElement<any, any>>,\n>(\n elements: T,\n): { [K in keyof T]: T[K] extends ResourceElement<infer R, any> ? R : never } {\n // Validate keys\n const seenKeys = new Set<string | number>();\n elements.forEach((element, index) => {\n if (element.key === undefined) {\n throw new Error(\n `tapResources: All resource elements must have a key. Element at index ${index} is missing a key.`,\n );\n }\n if (seenKeys.has(element.key)) {\n throw new Error(\n `tapResources: Duplicate key \"${element.key}\" found. All keys must be unique.`,\n );\n }\n seenKeys.add(element.key);\n });\n\n const [stateVersion, rerender] = tapState({});\n\n // Create a map of current elements by key for efficient lookup\n const elementsByKey = tapMemo(\n () => new Map(elements.map((element) => [element.key!, element])),\n [elements],\n );\n\n // Track fibers persistently across renders\n const [fibers] = tapState(\n () => new Map<string | number, ResourceFiber<any, any>>(),\n );\n\n // Process each element\n const results = tapMemo(() => {\n const resultMap = new Map<string | number, any>();\n const currentKeys = new Set<string | number>();\n\n // Create/update fibers and render\n elementsByKey.forEach((element, key) => {\n currentKeys.add(key);\n\n let fiber = fibers.get(key);\n\n // Create new fiber if needed or type changed\n if (!fiber || fiber.resource !== element.type) {\n if (fiber) unmountResource(fiber);\n fiber = createResourceFiber(element.type, () => rerender({}));\n fibers.set(key, fiber);\n }\n\n // Render with current props\n const result = renderResource(fiber, element.props);\n resultMap.set(key, result);\n });\n\n // Clean up removed fibers\n fibers.forEach((fiber, key) => {\n if (!currentKeys.has(key)) {\n unmountResource(fiber);\n fibers.delete(key);\n }\n });\n\n return resultMap;\n }, [elementsByKey, stateVersion]);\n\n // Commit all renders\n tapEffect(() => {\n results.forEach((result, key) => {\n const fiber = fibers.get(key);\n if (fiber) {\n commitResource(fiber, result);\n }\n });\n }, [results, fibers]);\n\n // Cleanup on unmount\n tapEffect(() => {\n return () => {\n fibers.forEach((fiber) => {\n unmountResource(fiber);\n });\n fibers.clear();\n };\n }, [fibers]);\n\n // Return results in the same order as input elements\n return tapMemo(\n () => elements.map((element) => results.get(element.key!)?.state),\n [elements, results],\n ) as any;\n}\n"],"mappings":";AACA,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,SAAS,aAGd,UAC4E;AAE5E,QAAM,WAAW,oBAAI,IAAqB;AAC1C,WAAS,QAAQ,CAAC,SAAS,UAAU;AACnC,QAAI,QAAQ,QAAQ,QAAW;AAC7B,YAAM,IAAI;AAAA,QACR,yEAAyE,KAAK;AAAA,MAChF;AAAA,IACF;AACA,QAAI,SAAS,IAAI,QAAQ,GAAG,GAAG;AAC7B,YAAM,IAAI;AAAA,QACR,gCAAgC,QAAQ,GAAG;AAAA,MAC7C;AAAA,IACF;AACA,aAAS,IAAI,QAAQ,GAAG;AAAA,EAC1B,CAAC;AAED,QAAM,CAAC,cAAc,QAAQ,IAAI,SAAS,CAAC,CAAC;AAG5C,QAAM,gBAAgB;AAAA,IACpB,MAAM,IAAI,IAAI,SAAS,IAAI,CAAC,YAAY,CAAC,QAAQ,KAAM,OAAO,CAAC,CAAC;AAAA,IAChE,CAAC,QAAQ;AAAA,EACX;AAGA,QAAM,CAAC,MAAM,IAAI;AAAA,IACf,MAAM,oBAAI,IAA8C;AAAA,EAC1D;AAGA,QAAM,UAAU,QAAQ,MAAM;AAC5B,UAAM,YAAY,oBAAI,IAA0B;AAChD,UAAM,cAAc,oBAAI,IAAqB;AAG7C,kBAAc,QAAQ,CAAC,SAAS,QAAQ;AACtC,kBAAY,IAAI,GAAG;AAEnB,UAAI,QAAQ,OAAO,IAAI,GAAG;AAG1B,UAAI,CAAC,SAAS,MAAM,aAAa,QAAQ,MAAM;AAC7C,YAAI,MAAO,iBAAgB,KAAK;AAChC,gBAAQ,oBAAoB,QAAQ,MAAM,MAAM,SAAS,CAAC,CAAC,CAAC;AAC5D,eAAO,IAAI,KAAK,KAAK;AAAA,MACvB;AAGA,YAAM,SAAS,eAAe,OAAO,QAAQ,KAAK;AAClD,gBAAU,IAAI,KAAK,MAAM;AAAA,IAC3B,CAAC;AAGD,WAAO,QAAQ,CAAC,OAAO,QAAQ;AAC7B,UAAI,CAAC,YAAY,IAAI,GAAG,GAAG;AACzB,wBAAgB,KAAK;AACrB,eAAO,OAAO,GAAG;AAAA,MACnB;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,YAAY,CAAC;AAGhC,YAAU,MAAM;AACd,YAAQ,QAAQ,CAAC,QAAQ,QAAQ;AAC/B,YAAM,QAAQ,OAAO,IAAI,GAAG;AAC5B,UAAI,OAAO;AACT,uBAAe,OAAO,MAAM;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,MAAM,CAAC;AAGpB,YAAU,MAAM;AACd,WAAO,MAAM;AACX,aAAO,QAAQ,CAAC,UAAU;AACxB,wBAAgB,KAAK;AAAA,MACvB,CAAC;AACD,aAAO,MAAM;AAAA,IACf;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,SAAO;AAAA,IACL,MAAM,SAAS,IAAI,CAAC,YAAY,QAAQ,IAAI,QAAQ,GAAI,GAAG,KAAK;AAAA,IAChE,CAAC,UAAU,OAAO;AAAA,EACpB;AACF;","names":[]}
@@ -1,8 +1,11 @@
1
- import { StateUpdater, ResourceFiber } from "../core/types";
1
+ import { ResourceFiber } from "../core/types";
2
+ export declare namespace tapState {
3
+ type StateUpdater<S> = S | ((prev: S) => S);
4
+ }
2
5
  export declare const rerender: (fiber: ResourceFiber<any, any>) => void;
3
6
  export declare function tapState<S = undefined>(): [
4
7
  S | undefined,
5
- (updater: StateUpdater<S>) => void
8
+ (updater: tapState.StateUpdater<S>) => void
6
9
  ];
7
- export declare function tapState<S>(initial: S | (() => S)): [S, (updater: StateUpdater<S>) => void];
10
+ export declare function tapState<S>(initial: S | (() => S)): [S, (updater: tapState.StateUpdater<S>) => void];
8
11
  //# sourceMappingURL=tap-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tap-state.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAQ,aAAa,EAAE,MAAM,eAAe,CAAC;AAElE,eAAO,MAAM,QAAQ,GAAI,OAAO,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,SAWtD,CAAC;AAmDF,wBAAgB,QAAQ,CAAC,CAAC,GAAG,SAAS,KAAK;IACzC,CAAC,GAAG,SAAS;IACb,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI;CACnC,CAAC;AACF,wBAAgB,QAAQ,CAAC,CAAC,EACxB,OAAO,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GACrB,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"tap-state.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,aAAa,EAAE,MAAM,eAAe,CAAC;AAEpD,yBAAiB,QAAQ,CAAC;IACxB,KAAY,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;CACpD;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,SAWtD,CAAC;AAmDF,wBAAgB,QAAQ,CAAC,CAAC,GAAG,SAAS,KAAK;IACzC,CAAC,GAAG,SAAS;IACb,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI;CAC5C,CAAC;AACF,wBAAgB,QAAQ,CAAC,CAAC,EACxB,OAAO,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GACrB,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/tap-state.ts"],"sourcesContent":["import { getCurrentResourceFiber } from \"../core/execution-context\";\nimport { StateUpdater, Cell, ResourceFiber } from \"../core/types\";\n\nexport const rerender = (fiber: ResourceFiber<any, any>) => {\n if (fiber.renderContext) {\n throw new Error(\"Resource updated during render\");\n }\n if (fiber.isNeverMounted) {\n throw new Error(\"Resource updated before mount\");\n }\n // Only schedule rerender if currently mounted\n if (fiber.isMounted) {\n fiber.scheduleRerender();\n }\n};\n\nfunction getStateCell<T>(\n initialValue: T | (() => T),\n): Cell & { type: \"state\" } {\n const fiber = getCurrentResourceFiber();\n const index = fiber.currentIndex++;\n\n // Check if we're trying to use more hooks than in previous renders\n if (!fiber.isFirstRender && index >= fiber.cells.length) {\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 (!fiber.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 rerender(fiber);\n }\n },\n };\n\n fiber.cells[index] = cell;\n }\n\n const cell = fiber.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"],"mappings":";AAAA,SAAS,+BAA+B;AAGjC,IAAM,WAAW,CAAC,UAAmC;AAC1D,MAAI,MAAM,eAAe;AACvB,UAAM,IAAI,MAAM,gCAAgC;AAAA,EAClD;AACA,MAAI,MAAM,gBAAgB;AACxB,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,MAAI,MAAM,WAAW;AACnB,UAAM,iBAAiB;AAAA,EACzB;AACF;AAEA,SAAS,aACP,cAC0B;AAC1B,QAAM,QAAQ,wBAAwB;AACtC,QAAM,QAAQ,MAAM;AAGpB,MAAI,CAAC,MAAM,iBAAiB,SAAS,MAAM,MAAM,QAAQ;AACvD,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,CAAC,MAAM,MAAM,KAAK,GAAG;AAEvB,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;AACb,mBAAS,KAAK;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,KAAK,IAAIA;AAAA,EACvB;AAEA,QAAM,OAAO,MAAM,MAAM,KAAK;AAC9B,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;","names":["cell"]}
1
+ {"version":3,"sources":["../../src/hooks/tap-state.ts"],"sourcesContent":["import { getCurrentResourceFiber } from \"../core/execution-context\";\nimport { Cell, ResourceFiber } from \"../core/types\";\n\nexport namespace tapState {\n export type StateUpdater<S> = S | ((prev: S) => S);\n}\n\nexport const rerender = (fiber: ResourceFiber<any, any>) => {\n if (fiber.renderContext) {\n throw new Error(\"Resource updated during render\");\n }\n if (fiber.isNeverMounted) {\n throw new Error(\"Resource updated before mount\");\n }\n // Only schedule rerender if currently mounted\n if (fiber.isMounted) {\n fiber.scheduleRerender();\n }\n};\n\nfunction getStateCell<T>(\n initialValue: T | (() => T),\n): Cell & { type: \"state\" } {\n const fiber = getCurrentResourceFiber();\n const index = fiber.currentIndex++;\n\n // Check if we're trying to use more hooks than in previous renders\n if (!fiber.isFirstRender && index >= fiber.cells.length) {\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 (!fiber.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: tapState.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 rerender(fiber);\n }\n },\n };\n\n fiber.cells[index] = cell;\n }\n\n const cell = fiber.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: tapState.StateUpdater<S>) => void,\n];\nexport function tapState<S>(\n initial: S | (() => S),\n): [S, (updater: tapState.StateUpdater<S>) => void];\nexport function tapState<S>(\n initial?: S | (() => S),\n): [S | undefined, (updater: tapState.StateUpdater<S>) => void] {\n const cell = getStateCell(initial as S | (() => S));\n\n return [cell.value, cell.set];\n}\n"],"mappings":";AAAA,SAAS,+BAA+B;AAOjC,IAAM,WAAW,CAAC,UAAmC;AAC1D,MAAI,MAAM,eAAe;AACvB,UAAM,IAAI,MAAM,gCAAgC;AAAA,EAClD;AACA,MAAI,MAAM,gBAAgB;AACxB,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,MAAI,MAAM,WAAW;AACnB,UAAM,iBAAiB;AAAA,EACzB;AACF;AAEA,SAAS,aACP,cAC0B;AAC1B,QAAM,QAAQ,wBAAwB;AACtC,QAAM,QAAQ,MAAM;AAGpB,MAAI,CAAC,MAAM,iBAAiB,SAAS,MAAM,MAAM,QAAQ;AACvD,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,CAAC,MAAM,MAAM,KAAK,GAAG;AAEvB,UAAM,QACJ,OAAO,iBAAiB,aACnB,aAAyB,IAC1B;AAEN,UAAMA,QAAiC;AAAA,MACrC,MAAM;AAAA,MACN;AAAA,MACA,KAAK,CAAC,YAAsC;AAC1C,cAAM,eAAeA,MAAK;AAC1B,cAAM,YACJ,OAAO,YAAY,aACd,QAA2B,YAAY,IACxC;AAEN,YAAI,CAAC,OAAO,GAAG,cAAc,SAAS,GAAG;AACvC,UAAAA,MAAK,QAAQ;AACb,mBAAS,KAAK;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,KAAK,IAAIA;AAAA,EACvB;AAEA,QAAM,OAAO,MAAM,MAAM,KAAK;AAC9B,MAAI,KAAK,SAAS,SAAS;AACzB,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAEA,SAAO;AACT;AASO,SAAS,SACd,SAC8D;AAC9D,QAAM,OAAO,aAAa,OAAwB;AAElD,SAAO,CAAC,KAAK,OAAO,KAAK,GAAG;AAC9B;","names":["cell"]}
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  export { resource } from "./core/resource";
2
2
  export { tapState } from "./hooks/tap-state";
3
3
  export { tapEffect } from "./hooks/tap-effect";
4
- export { tapRef, type RefObject } from "./hooks/tap-ref";
4
+ export { tapRef } from "./hooks/tap-ref";
5
5
  export { tapMemo } from "./hooks/tap-memo";
6
6
  export { tapCallback } from "./hooks/tap-callback";
7
+ export { tapEffectEvent } from "./hooks/tap-effect-event";
7
8
  export { tapResource } from "./hooks/tap-resource";
8
9
  export { tapInlineResource } from "./hooks/tap-inline-resource";
9
10
  export { tapResources } from "./hooks/tap-resources";
10
- export { createResource, type ResourceHandle } from "./core/ResourceHandle";
11
+ export { createResource } from "./core/ResourceHandle";
11
12
  export { createContext, tapContext, withContextProvider } from "./core/context";
12
- export type { ResourceFn, ResourceElement, Resource, Unsubscribe, StateUpdater, EffectCallback, Destructor, } from "./core/types";
13
+ export type { Resource, ContravariantResource, ResourceElement, } from "./core/types";
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG/C,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,YAAY,EACV,UAAU,EACV,eAAe,EACf,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,cAAc,EACd,UAAU,GACX,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG/C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGhF,YAAY,EACV,QAAQ,EACR,qBAAqB,EACrB,eAAe,GAChB,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { tapEffect } from "./hooks/tap-effect.js";
5
5
  import { tapRef } from "./hooks/tap-ref.js";
6
6
  import { tapMemo } from "./hooks/tap-memo.js";
7
7
  import { tapCallback } from "./hooks/tap-callback.js";
8
+ import { tapEffectEvent } from "./hooks/tap-effect-event.js";
8
9
  import { tapResource } from "./hooks/tap-resource.js";
9
10
  import { tapInlineResource } from "./hooks/tap-inline-resource.js";
10
11
  import { tapResources } from "./hooks/tap-resources.js";
@@ -17,6 +18,7 @@ export {
17
18
  tapCallback,
18
19
  tapContext,
19
20
  tapEffect,
21
+ tapEffectEvent,
20
22
  tapInlineResource,
21
23
  tapMemo,
22
24
  tapRef,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { resource } from \"./core/resource\";\n\n// primitive hooks\nexport { tapState } from \"./hooks/tap-state\";\nexport { tapEffect } from \"./hooks/tap-effect\";\n\n// utility hooks\nexport { tapRef, type RefObject } from \"./hooks/tap-ref\";\nexport { tapMemo } from \"./hooks/tap-memo\";\nexport { tapCallback } from \"./hooks/tap-callback\";\n\n// resources\nexport { tapResource } from \"./hooks/tap-resource\";\nexport { tapInlineResource } from \"./hooks/tap-inline-resource\";\nexport { tapResources } from \"./hooks/tap-resources\";\n\n// imperative\nexport { createResource, type ResourceHandle } from \"./core/ResourceHandle\";\n\n// context\nexport { createContext, tapContext, withContextProvider } from \"./core/context\";\n\nexport type {\n ResourceFn,\n ResourceElement,\n Resource,\n Unsubscribe,\n StateUpdater,\n EffectCallback,\n Destructor,\n} from \"./core/types\";\n"],"mappings":";AAAA,SAAS,gBAAgB;AAGzB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAG1B,SAAS,cAA8B;AACvC,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAG5B,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAG7B,SAAS,sBAA2C;AAGpD,SAAS,eAAe,YAAY,2BAA2B;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { resource } from \"./core/resource\";\n\n// primitive hooks\nexport { tapState } from \"./hooks/tap-state\";\nexport { tapEffect } from \"./hooks/tap-effect\";\n\n// utility hooks\nexport { tapRef } from \"./hooks/tap-ref\";\nexport { tapMemo } from \"./hooks/tap-memo\";\nexport { tapCallback } from \"./hooks/tap-callback\";\nexport { tapEffectEvent } from \"./hooks/tap-effect-event\";\n\n// resources\nexport { tapResource } from \"./hooks/tap-resource\";\nexport { tapInlineResource } from \"./hooks/tap-inline-resource\";\nexport { tapResources } from \"./hooks/tap-resources\";\n\n// imperative\nexport { createResource } from \"./core/ResourceHandle\";\n\n// context\nexport { createContext, tapContext, withContextProvider } from \"./core/context\";\n\n// types\nexport type {\n Resource,\n ContravariantResource,\n ResourceElement,\n} from \"./core/types\";\n"],"mappings":";AAAA,SAAS,gBAAgB;AAGzB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAG1B,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAG/B,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAG7B,SAAS,sBAAsB;AAG/B,SAAS,eAAe,YAAY,2BAA2B;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"use-resource.d.ts","sourceRoot":"","sources":["../../src/react/use-resource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAgBhD,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAgBnE"}
1
+ {"version":3,"file":"use-resource.d.ts","sourceRoot":"","sources":["../../src/react/use-resource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAehD,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAgBnE"}
@@ -1,6 +1,5 @@
1
1
  // src/react/use-resource.ts
2
2
  import { useEffect, useLayoutEffect, useMemo, useState } from "react";
3
- import { getResourceFn } from "../core/getResourceFn.js";
4
3
  import {
5
4
  createResourceFiber,
6
5
  unmountResource,
@@ -12,7 +11,7 @@ var useIsomorphicLayoutEffect = shouldAvoidLayoutEffect ? useEffect : useLayoutE
12
11
  function useResource(element) {
13
12
  const [, rerender] = useState({});
14
13
  const fiber = useMemo(
15
- () => createResourceFiber(getResourceFn(element.type), () => rerender({})),
14
+ () => createResourceFiber(element.type, () => rerender({})),
16
15
  [element.type, rerender]
17
16
  );
18
17
  const result = renderResource(fiber, element.props);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/use-resource.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useMemo, useState } from \"react\";\nimport { ResourceElement } from \"../core/types\";\nimport { getResourceFn } from \"../core/getResourceFn\";\nimport {\n createResourceFiber,\n unmountResource,\n renderResource,\n commitResource,\n} from \"../core/ResourceFiber\";\n\nconst shouldAvoidLayoutEffect =\n (globalThis as any).__ASSISTANT_UI_DISABLE_LAYOUT_EFFECT__ === true;\n\nconst useIsomorphicLayoutEffect = shouldAvoidLayoutEffect\n ? useEffect\n : useLayoutEffect;\n\nexport function useResource<R, P>(element: ResourceElement<R, P>): R {\n const [, rerender] = useState({});\n const fiber = useMemo(\n () => createResourceFiber(getResourceFn(element.type), () => rerender({})),\n [element.type, rerender],\n );\n\n const result = renderResource(fiber, element.props);\n useIsomorphicLayoutEffect(() => {\n return () => unmountResource(fiber);\n }, []);\n useIsomorphicLayoutEffect(() => {\n commitResource(fiber, result);\n });\n\n return result.state;\n}\n"],"mappings":";AAAA,SAAS,WAAW,iBAAiB,SAAS,gBAAgB;AAE9D,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,IAAM,0BACH,WAAmB,2CAA2C;AAEjE,IAAM,4BAA4B,0BAC9B,YACA;AAEG,SAAS,YAAkB,SAAmC;AACnE,QAAM,CAAC,EAAE,QAAQ,IAAI,SAAS,CAAC,CAAC;AAChC,QAAM,QAAQ;AAAA,IACZ,MAAM,oBAAoB,cAAc,QAAQ,IAAI,GAAG,MAAM,SAAS,CAAC,CAAC,CAAC;AAAA,IACzE,CAAC,QAAQ,MAAM,QAAQ;AAAA,EACzB;AAEA,QAAM,SAAS,eAAe,OAAO,QAAQ,KAAK;AAClD,4BAA0B,MAAM;AAC9B,WAAO,MAAM,gBAAgB,KAAK;AAAA,EACpC,GAAG,CAAC,CAAC;AACL,4BAA0B,MAAM;AAC9B,mBAAe,OAAO,MAAM;AAAA,EAC9B,CAAC;AAED,SAAO,OAAO;AAChB;","names":[]}
1
+ {"version":3,"sources":["../../src/react/use-resource.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useMemo, useState } from \"react\";\nimport { ResourceElement } from \"../core/types\";\nimport {\n createResourceFiber,\n unmountResource,\n renderResource,\n commitResource,\n} from \"../core/ResourceFiber\";\n\nconst shouldAvoidLayoutEffect =\n (globalThis as any).__ASSISTANT_UI_DISABLE_LAYOUT_EFFECT__ === true;\n\nconst useIsomorphicLayoutEffect = shouldAvoidLayoutEffect\n ? useEffect\n : useLayoutEffect;\n\nexport function useResource<R, P>(element: ResourceElement<R, P>): R {\n const [, rerender] = useState({});\n const fiber = useMemo(\n () => createResourceFiber(element.type, () => rerender({})),\n [element.type, rerender],\n );\n\n const result = renderResource(fiber, element.props);\n useIsomorphicLayoutEffect(() => {\n return () => unmountResource(fiber);\n }, []);\n useIsomorphicLayoutEffect(() => {\n commitResource(fiber, result);\n });\n\n return result.state;\n}\n"],"mappings":";AAAA,SAAS,WAAW,iBAAiB,SAAS,gBAAgB;AAE9D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,IAAM,0BACH,WAAmB,2CAA2C;AAEjE,IAAM,4BAA4B,0BAC9B,YACA;AAEG,SAAS,YAAkB,SAAmC;AACnE,QAAM,CAAC,EAAE,QAAQ,IAAI,SAAS,CAAC,CAAC;AAChC,QAAM,QAAQ;AAAA,IACZ,MAAM,oBAAoB,QAAQ,MAAM,MAAM,SAAS,CAAC,CAAC,CAAC;AAAA,IAC1D,CAAC,QAAQ,MAAM,QAAQ;AAAA,EACzB;AAEA,QAAM,SAAS,eAAe,OAAO,QAAQ,KAAK;AAClD,4BAA0B,MAAM;AAC9B,WAAO,MAAM,gBAAgB,KAAK;AAAA,EACpC,GAAG,CAAC,CAAC;AACL,4BAA0B,MAAM;AAC9B,mBAAe,OAAO,MAAM;AAAA,EAC9B,CAAC;AAED,SAAO,OAAO;AAChB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/tap",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "description": "Zero-dependency reactive state management inspired by React hooks",
6
6
  "main": "dist/index.js",
@@ -20,10 +20,10 @@
20
20
  "devDependencies": {
21
21
  "@types/node": "^24.10.1",
22
22
  "@types/react": "*",
23
- "@vitest/ui": "^4.0.10",
23
+ "@vitest/ui": "^4.0.14",
24
24
  "tsx": "^4.20.6",
25
25
  "typescript": "^5.9.3",
26
- "vitest": "^4.0.10",
26
+ "vitest": "^4.0.14",
27
27
  "@assistant-ui/x-buildutils": "0.0.1"
28
28
  },
29
29
  "exports": {
@@ -39,6 +39,17 @@
39
39
  "files": [
40
40
  "dist"
41
41
  ],
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "provenance": true
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/assistant-ui/assistant-ui/tree/main/packages/tap"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/assistant-ui/assistant-ui/issues"
52
+ },
42
53
  "scripts": {
43
54
  "build": "tsx build.mts",
44
55
  "lint": "eslint .",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=getResourceFn.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getResourceFn.d.ts","sourceRoot":"","sources":["../../src/core/getResourceFn.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/core/getResourceFn.ts"],"sourcesContent":["import { ResourceFn, Resource } from \"./types\";\n\n/**\n * Get the ResourceFn from a Resource constructor.\n * @internal This is for internal use only.\n */\nexport function getResourceFn<R, P>(\n resource: Resource<R, P>,\n): ResourceFn<R, P> {\n const fn = (resource as unknown as { [fnSymbol]?: ResourceFn<R, P> })[\n fnSymbol\n ];\n if (!fn) {\n throw new Error(\"ResourceElement.type is not a valid Resource\");\n }\n return fn;\n}\n\n/**\n * Symbol used to store the ResourceFn in the Resource constructor.\n * @internal This is for internal use only.\n */\nexport const fnSymbol = Symbol(\"fnSymbol\");\n"],"mappings":";AAMO,SAAS,cACd,UACkB;AAClB,QAAM,KAAM,SACV,QACF;AACA,MAAI,CAAC,IAAI;AACP,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,SAAO;AACT;AAMO,IAAM,WAAW,OAAO,UAAU;","names":[]}