@assistant-ui/tap 0.3.2 → 0.3.3

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 (47) hide show
  1. package/README.md +30 -8
  2. package/dist/__tests__/test-utils.js +7 -7
  3. package/dist/__tests__/test-utils.js.map +1 -1
  4. package/dist/core/ResourceFiber.d.ts +3 -3
  5. package/dist/core/ResourceFiber.d.ts.map +1 -1
  6. package/dist/core/ResourceFiber.js +6 -6
  7. package/dist/core/ResourceFiber.js.map +1 -1
  8. package/dist/core/{ResourceHandle.d.ts → createResource.d.ts} +6 -5
  9. package/dist/core/createResource.d.ts.map +1 -0
  10. package/dist/core/{ResourceHandle.js → createResource.js} +35 -33
  11. package/dist/core/createResource.js.map +1 -0
  12. package/dist/core/resource.d.ts +2 -2
  13. package/dist/core/resource.d.ts.map +1 -1
  14. package/dist/core/resource.js +2 -3
  15. package/dist/core/resource.js.map +1 -1
  16. package/dist/core/scheduler.d.ts +2 -4
  17. package/dist/core/scheduler.d.ts.map +1 -1
  18. package/dist/core/scheduler.js +60 -47
  19. package/dist/core/scheduler.js.map +1 -1
  20. package/dist/core/types.d.ts +9 -10
  21. package/dist/core/types.d.ts.map +1 -1
  22. package/dist/hooks/tap-inline-resource.d.ts +2 -2
  23. package/dist/hooks/tap-inline-resource.d.ts.map +1 -1
  24. package/dist/hooks/tap-inline-resource.js.map +1 -1
  25. package/dist/hooks/tap-resource.d.ts +3 -3
  26. package/dist/hooks/tap-resource.d.ts.map +1 -1
  27. package/dist/hooks/tap-resource.js +6 -6
  28. package/dist/hooks/tap-resource.js.map +1 -1
  29. package/dist/hooks/tap-resources.d.ts +9 -3
  30. package/dist/hooks/tap-resources.d.ts.map +1 -1
  31. package/dist/hooks/tap-resources.js +51 -60
  32. package/dist/hooks/tap-resources.js.map +1 -1
  33. package/dist/hooks/tap-state.d.ts +0 -2
  34. package/dist/hooks/tap-state.d.ts.map +1 -1
  35. package/dist/hooks/tap-state.js +2 -4
  36. package/dist/hooks/tap-state.js.map +1 -1
  37. package/dist/index.d.ts +3 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +3 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/react/use-resource.d.ts +2 -2
  42. package/dist/react/use-resource.d.ts.map +1 -1
  43. package/dist/react/use-resource.js +8 -8
  44. package/dist/react/use-resource.js.map +1 -1
  45. package/package.json +2 -2
  46. package/dist/core/ResourceHandle.d.ts.map +0 -1
  47. package/dist/core/ResourceHandle.js.map +0 -1
package/README.md CHANGED
@@ -176,7 +176,22 @@ const Timer = resource(() => {
176
176
 
177
177
  ### `tapResources`
178
178
 
179
- Renders multiple resources with keys, similar to React's list rendering. All resources must have a unique `key` property.
179
+ Renders multiple resources from a record/map, similar to React's list rendering. Returns a record with the same keys mapping to each resource's result.
180
+
181
+ ```typescript
182
+ tapResources<T, R, K extends string | number>(
183
+ map: Record<K, T>,
184
+ getElement: (value: T, key: K) => ResourceElement<R>,
185
+ getElementDeps?: any[]
186
+ ): Record<K, R>
187
+ ```
188
+
189
+ **Parameters:**
190
+ - `map`: A record/object where keys identify each resource instance
191
+ - `getElement`: A function that receives each value and key, returning a ResourceElement
192
+ - `getElementDeps`: Optional dependency array for memoizing the getElement function
193
+
194
+ **Example:**
180
195
 
181
196
  ```typescript
182
197
  const TodoItem = resource((props: { text: string }) => {
@@ -185,19 +200,26 @@ const TodoItem = resource((props: { text: string }) => {
185
200
  });
186
201
 
187
202
  const TodoList = resource(() => {
188
- const todos = [
189
- { id: "1", text: "Learn tap" },
190
- { id: "2", text: "Build something awesome" },
191
- ];
192
-
193
- const todoItems = tapResources(
194
- todos.map((todo) => new TodoItem({ text: todo.text }, { key: todo.id })),
203
+ const todos = tapMemo(
204
+ () => ({
205
+ "1": { text: "Learn tap" },
206
+ "2": { text: "Build something awesome" },
207
+ }),
208
+ [],
195
209
  );
196
210
 
211
+ // Returns Record<string, { text, completed, setCompleted }>
212
+ const todoItems = tapResources(todos, (todo) => TodoItem({ text: todo.text }));
213
+
197
214
  return todoItems;
198
215
  });
199
216
  ```
200
217
 
218
+ **Key features:**
219
+ - Resource instances are preserved when keys remain the same
220
+ - Automatically cleans up resources when keys are removed
221
+ - Handles resource type changes (recreates fiber if type changes)
222
+
201
223
  ### `tapContext` and Context Support
202
224
 
203
225
  Create and use context to pass values through resource boundaries without prop drilling.
@@ -2,9 +2,9 @@
2
2
  import { resource } from "../core/resource.js";
3
3
  import {
4
4
  createResourceFiber,
5
- unmountResource as unmountResourceFiber,
6
- renderResource as renderResourceFiber,
7
- commitResource
5
+ unmountResourceFiber,
6
+ renderResourceFiber,
7
+ commitResourceFiber
8
8
  } from "../core/ResourceFiber.js";
9
9
  import { tapState } from "../hooks/tap-state.js";
10
10
  function createTestResource(fn) {
@@ -12,7 +12,7 @@ function createTestResource(fn) {
12
12
  if (activeResources.has(fiber)) {
13
13
  const lastProps = propsMap.get(fiber);
14
14
  const result = renderResourceFiber(fiber, lastProps);
15
- commitResource(fiber, result);
15
+ commitResourceFiber(fiber, result);
16
16
  lastRenderResultMap.set(fiber, result);
17
17
  }
18
18
  };
@@ -26,7 +26,7 @@ function renderTest(fiber, props) {
26
26
  propsMap.set(fiber, props);
27
27
  activeResources.add(fiber);
28
28
  const result = renderResourceFiber(fiber, props);
29
- commitResource(fiber, result);
29
+ commitResourceFiber(fiber, result);
30
30
  lastRenderResultMap.set(fiber, result);
31
31
  return result.state;
32
32
  }
@@ -57,7 +57,7 @@ var TestSubscriber = class {
57
57
  this.fiber = fiber;
58
58
  const lastProps = propsMap.get(fiber) ?? void 0;
59
59
  const initialResult = renderResourceFiber(fiber, lastProps);
60
- commitResource(fiber, initialResult);
60
+ commitResourceFiber(fiber, initialResult);
61
61
  this.lastState = initialResult.state;
62
62
  lastRenderResultMap.set(fiber, initialResult);
63
63
  activeResources.add(fiber);
@@ -82,7 +82,7 @@ var TestResourceManager = class {
82
82
  activeResources.add(this.fiber);
83
83
  propsMap.set(this.fiber, props);
84
84
  const result = renderResourceFiber(this.fiber, props);
85
- commitResource(this.fiber, result);
85
+ commitResourceFiber(this.fiber, result);
86
86
  lastRenderResultMap.set(this.fiber, result);
87
87
  return result.state;
88
88
  }
@@ -1 +1 @@
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":";AAAA,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB;AAAA,OACK;AAEP,SAAS,gBAAgB;AAWlB,SAAS,mBAAyB,IAAqB;AAC5D,QAAM,mBAAmB,MAAM;AAE7B,QAAI,gBAAgB,IAAI,KAAK,GAAG;AAC9B,YAAM,YAAY,SAAS,IAAI,KAAK;AACpC,YAAM,SAAS,oBAAoB,OAAO,SAAS;AACnD,qBAAe,OAAO,MAAM;AAC5B,0BAAoB,IAAI,OAAO,MAAM;AAAA,IACvC;AAAA,EACF;AAEA,QAAM,QAAQ,oBAAoB,SAAS,EAAE,GAAG,gBAAgB;AAChE,SAAO;AACT;AAOA,IAAM,kBAAkB,oBAAI,IAA6B;AACzD,IAAM,WAAW,oBAAI,QAAsC;AAC3D,IAAM,sBAAsB,oBAAI,QAAsC;AAO/D,SAAS,WAAiB,OAA4B,OAAa;AACxE,WAAS,IAAI,OAAO,KAAK;AAGzB,kBAAgB,IAAI,KAAK;AAGzB,QAAM,SAAS,oBAAoB,OAAO,KAAK;AAC/C,iBAAe,OAAO,MAAM;AAC5B,sBAAoB,IAAI,OAAO,MAAM;AAIrC,SAAO,OAAO;AAChB;AAKO,SAAS,gBAAsB,OAA4B;AAChE,MAAI,gBAAgB,IAAI,KAAK,GAAG;AAC9B,yBAAqB,KAAK;AAC1B,oBAAgB,OAAO,KAAK;AAAA,EAC9B;AACF;AAKO,SAAS,sBAAsB;AACpC,kBAAgB,QAAQ,CAAC,UAAU,qBAAqB,KAAK,CAAC;AAC9D,kBAAgB,MAAM;AACxB;AAMO,SAAS,kBAAwB,OAA+B;AACrE,QAAM,aAAa,oBAAoB,IAAI,KAAK;AAChD,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,WAAW;AACpB;AAUO,IAAM,iBAAN,MAAwB;AAAA,EACtB,YAAY;AAAA,EACZ;AAAA,EACC;AAAA,EAER,YAAY,OAAgC;AAC1C,SAAK,QAAQ;AAEb,UAAM,YAAY,SAAS,IAAI,KAAK,KAAK;AACzC,UAAM,gBAAgB,oBAAoB,OAAO,SAAgB;AACjE,mBAAe,OAAO,aAAa;AACnC,SAAK,YAAY,cAAc;AAC/B,wBAAoB,IAAI,OAAO,aAAa;AAC5C,oBAAgB,IAAI,KAAK;AAAA,EAC3B;AAAA,EAEA,UAAU;AACR,QAAI,gBAAgB,IAAI,KAAK,KAAK,GAAG;AACnC,2BAAqB,KAAK,KAAK;AAC/B,sBAAgB,OAAO,KAAK,KAAK;AAAA,IACnC;AAAA,EACF;AACF;AAMO,IAAM,sBAAN,MAAgC;AAAA,EAGrC,YAAmB,OAA4B;AAA5B;AAAA,EAA6B;AAAA,EAFxC,WAAW;AAAA,EAInB,eAAe,OAAa;AAC1B,QAAI,KAAK,UAAU;AACjB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,SAAK,WAAW;AAChB,oBAAgB,IAAI,KAAK,KAAK;AAC9B,aAAS,IAAI,KAAK,OAAO,KAAK;AAC9B,UAAM,SAAS,oBAAoB,KAAK,OAAO,KAAK;AACpD,mBAAe,KAAK,OAAO,MAAM;AACjC,wBAAoB,IAAI,KAAK,OAAO,MAAM;AAC1C,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,UAAU;AACR,QAAI,KAAK,YAAY,gBAAgB,IAAI,KAAK,KAAK,GAAG;AACpD,2BAAqB,KAAK,KAAK;AAC/B,sBAAgB,OAAO,KAAK,KAAK;AACjC,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AACF;AAUO,SAAS,kBAAiC;AAC/C,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,CAAC,CAAC;AACxD;AAMA,eAAsB,QACpB,WACA,UAAU,KACV,WAAW,IACI;AACf,QAAM,QAAQ,KAAK,IAAI;AACvB,SAAO,CAAC,UAAU,GAAG;AACnB,QAAI,KAAK,IAAI,IAAI,QAAQ,SAAS;AAChC,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AACA,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,QAAQ,CAAC;AAAA,EAC9D;AACF;AAUO,SAAS,sBAAsB,eAAe,GAAG;AACtD,SAAO,CAAC,UAA8B;AACpC,UAAM,QAAQ,MAAM,SAAS;AAC7B,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB;AACF;AAMO,SAAS,gCAAgC;AAC9C,SAAO,CAAC,UAA+B;AACrC,UAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,MAAM,OAAO;AAChD,WAAO;AAAA,MACL;AAAA,MACA,WAAW,MAAM,SAAS,CAAC,MAAc,IAAI,CAAC;AAAA,MAC9C,WAAW,MAAM,SAAS,CAAC,MAAc,IAAI,CAAC;AAAA,IAChD;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/__tests__/test-utils.ts"],"sourcesContent":["import { resource } from \"../core/resource\";\nimport {\n createResourceFiber,\n unmountResourceFiber,\n renderResourceFiber,\n commitResourceFiber,\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 commitResourceFiber(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 commitResourceFiber(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 commitResourceFiber(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 commitResourceFiber(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,6 +1,6 @@
1
1
  import { ResourceFiber, RenderResult, Resource } from "./types";
2
2
  export declare function createResourceFiber<R, P>(resource: Resource<R, P>, scheduleRerender: () => void): ResourceFiber<R, P>;
3
- export declare function unmountResource<R, P>(fiber: ResourceFiber<R, P>): void;
4
- export declare function renderResource<R, P>(fiber: ResourceFiber<R, P>, props: P): RenderResult;
5
- export declare function commitResource<R, P>(fiber: ResourceFiber<R, P>, result: RenderResult): void;
3
+ export declare function unmountResourceFiber<R, P>(fiber: ResourceFiber<R, P>): void;
4
+ export declare function renderResourceFiber<R, P>(fiber: ResourceFiber<R, P>, props: P): RenderResult;
5
+ export declare function commitResourceFiber<R, P>(fiber: ResourceFiber<R, P>, result: RenderResult): void;
6
6
  //# sourceMappingURL=ResourceFiber.d.ts.map
@@ -1 +1 @@
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
+ {"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,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAI3E;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EACtC,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,KAAK,EAAE,CAAC,GACP,YAAY,CAiBd;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EACtC,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,MAAM,EAAE,YAAY,GACnB,IAAI,CAKN"}
@@ -14,11 +14,11 @@ function createResourceFiber(resource, scheduleRerender) {
14
14
  isNeverMounted: true
15
15
  };
16
16
  }
17
- function unmountResource(fiber) {
17
+ function unmountResourceFiber(fiber) {
18
18
  fiber.isMounted = false;
19
19
  cleanupAllEffects(fiber);
20
20
  }
21
- function renderResource(fiber, props) {
21
+ function renderResourceFiber(fiber, props) {
22
22
  const result = {
23
23
  commitTasks: [],
24
24
  props,
@@ -34,15 +34,15 @@ function renderResource(fiber, props) {
34
34
  });
35
35
  return result;
36
36
  }
37
- function commitResource(fiber, result) {
37
+ function commitResourceFiber(fiber, result) {
38
38
  fiber.isMounted = true;
39
39
  fiber.isNeverMounted = false;
40
40
  commitRender(result, fiber);
41
41
  }
42
42
  export {
43
- commitResource,
43
+ commitResourceFiber,
44
44
  createResourceFiber,
45
- renderResource,
46
- unmountResource
45
+ renderResourceFiber,
46
+ unmountResourceFiber
47
47
  };
48
48
  //# sourceMappingURL=ResourceFiber.js.map
@@ -1 +1 @@
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
+ {"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 unmountResourceFiber<R, P>(fiber: ResourceFiber<R, P>): void {\n // Clean up all effects\n fiber.isMounted = false;\n cleanupAllEffects(fiber);\n}\n\nexport function renderResourceFiber<R, P>(\n fiber: ResourceFiber<R, P>,\n props: P,\n): RenderResult {\n const result: 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 commitResourceFiber<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,qBAA2B,OAAkC;AAE3E,QAAM,YAAY;AAClB,oBAAkB,KAAK;AACzB;AAEO,SAAS,oBACd,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,oBACd,OACA,QACM;AACN,QAAM,YAAY;AAClB,QAAM,iBAAiB;AAEvB,eAAa,QAAQ,KAAK;AAC5B;","names":[]}
@@ -4,10 +4,11 @@ export declare namespace createResource {
4
4
  interface Handle<R, P> {
5
5
  getState(): R;
6
6
  subscribe(callback: () => void): Unsubscribe;
7
- updateInput(props: P): void;
8
- flushSync(): void;
9
- dispose(): void;
7
+ render(element: ResourceElement<R, P>): void;
8
+ unmount(): void;
10
9
  }
11
10
  }
12
- export declare const createResource: <R, P>(element: ResourceElement<R, P>, delayMount?: boolean) => createResource.Handle<R, P>;
13
- //# sourceMappingURL=ResourceHandle.d.ts.map
11
+ export declare const createResource: <R, P>(element: ResourceElement<R, P>, { mount }?: {
12
+ mount?: boolean;
13
+ }) => createResource.Handle<R, P>;
14
+ //# sourceMappingURL=createResource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createResource.d.ts","sourceRoot":"","sources":["../../src/core/createResource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,eAAe,EAAE,MAAM,SAAS,CAAC;AAexD,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,MAAM,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;QAC7C,OAAO,IAAI,IAAI,CAAC;KACjB;CACF;AA0CD,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EACjC,SAAS,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,YAAkB;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,KACzC,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAsC5B,CAAC"}
@@ -1,26 +1,21 @@
1
- // src/core/ResourceHandle.ts
1
+ // src/core/createResource.ts
2
2
  import {
3
3
  createResourceFiber,
4
- unmountResource,
5
- renderResource,
6
- commitResource
4
+ unmountResourceFiber,
5
+ renderResourceFiber,
6
+ commitResourceFiber
7
7
  } from "./ResourceFiber.js";
8
- import { UpdateScheduler } from "./scheduler.js";
8
+ import { flushSync, UpdateScheduler } from "./scheduler.js";
9
9
  import { tapRef } from "../hooks/tap-ref.js";
10
10
  import { tapState } from "../hooks/tap-state.js";
11
11
  import { tapMemo } from "../hooks/tap-memo.js";
12
- import { tapInlineResource } from "../hooks/tap-inline-resource.js";
13
12
  import { tapEffect } from "../hooks/tap-effect.js";
14
13
  import { resource } from "./resource.js";
14
+ import { tapResource } from "../hooks/tap-resource.js";
15
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 });
16
+ (state) => {
17
+ const [, setElement] = tapState(state.element);
18
+ const value = tapResource(state.element);
24
19
  const subscribers = tapRef(/* @__PURE__ */ new Set()).current;
25
20
  const valueRef = tapRef(value);
26
21
  tapEffect(() => {
@@ -36,45 +31,52 @@ var HandleWrapperResource = resource(
36
31
  subscribers.add(callback);
37
32
  return () => subscribers.delete(callback);
38
33
  },
39
- updateInput: (props2) => {
40
- onUpdateInput();
41
- setProps(() => props2);
34
+ render: (element) => {
35
+ state.element = element;
36
+ setElement(element);
37
+ state.onRender();
42
38
  },
43
- flushSync: onFlushSync,
44
- dispose: onDispose
39
+ unmount: state.onUnmount
45
40
  }),
46
41
  []
47
42
  );
48
43
  return handle;
49
44
  }
50
45
  );
51
- var createResource = (element, delayMount = false) => {
52
- let isMounted = !delayMount;
46
+ var createResource = (element, { mount = true } = {}) => {
47
+ let isMounted = mount;
48
+ let render;
53
49
  const props = {
54
50
  element,
55
- onUpdateInput: () => {
51
+ onRender: () => {
56
52
  if (isMounted) return;
57
53
  isMounted = true;
58
- commitResource(fiber, lastRender);
59
- },
60
- onFlushSync: () => {
61
- scheduler.flushSync();
54
+ if (scheduler.isDirty) return;
55
+ flushSync(() => {
56
+ commitResourceFiber(fiber, render);
57
+ });
62
58
  },
63
- onDispose: () => unmountResource(fiber)
59
+ onUnmount: () => {
60
+ if (!isMounted) return;
61
+ isMounted = false;
62
+ unmountResourceFiber(fiber);
63
+ }
64
64
  };
65
65
  const scheduler = new UpdateScheduler(() => {
66
- lastRender = renderResource(fiber, props);
67
- if (isMounted) commitResource(fiber, lastRender);
66
+ render = renderResourceFiber(fiber, props);
67
+ if (scheduler.isDirty || !isMounted) return;
68
+ commitResourceFiber(fiber, render);
68
69
  });
69
70
  const fiber = createResourceFiber(
70
71
  HandleWrapperResource,
71
72
  () => scheduler.markDirty()
72
73
  );
73
- let lastRender = renderResource(fiber, props);
74
- if (isMounted) commitResource(fiber, lastRender);
75
- return lastRender.state;
74
+ flushSync(() => {
75
+ scheduler.markDirty();
76
+ });
77
+ return render.state;
76
78
  };
77
79
  export {
78
80
  createResource
79
81
  };
80
- //# sourceMappingURL=ResourceHandle.js.map
82
+ //# sourceMappingURL=createResource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/createResource.ts"],"sourcesContent":["import { RenderResult, ResourceElement } from \"./types\";\nimport {\n createResourceFiber,\n unmountResourceFiber,\n renderResourceFiber,\n commitResourceFiber,\n} from \"./ResourceFiber\";\nimport { flushSync, UpdateScheduler } from \"./scheduler\";\nimport { tapRef } from \"../hooks/tap-ref\";\nimport { tapState } from \"../hooks/tap-state\";\nimport { tapMemo } from \"../hooks/tap-memo\";\nimport { tapEffect } from \"../hooks/tap-effect\";\nimport { resource } from \"./resource\";\nimport { tapResource } from \"../hooks/tap-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 render(element: ResourceElement<R, P>): void;\n unmount(): void;\n }\n}\n\nconst HandleWrapperResource = resource(\n <R, P>(state: {\n element: ResourceElement<R, P>;\n onRender: () => void;\n onUnmount: () => void;\n }): createResource.Handle<R, P> => {\n const [, setElement] = tapState(state.element);\n const value = tapResource(state.element);\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 render: (element: ResourceElement<R, P>) => {\n state.element = element;\n setElement(element);\n\n state.onRender();\n },\n unmount: state.onUnmount,\n }),\n [],\n );\n\n return handle;\n },\n);\n\nexport const createResource = <R, P>(\n element: ResourceElement<R, P>,\n { mount = true }: { mount?: boolean } = {},\n): createResource.Handle<R, P> => {\n let isMounted = mount;\n let render: RenderResult;\n const props = {\n element,\n onRender: () => {\n if (isMounted) return;\n isMounted = true;\n\n if (scheduler.isDirty) return;\n flushSync(() => {\n commitResourceFiber(fiber, render!);\n });\n },\n onUnmount: () => {\n if (!isMounted) return;\n isMounted = false;\n\n unmountResourceFiber(fiber);\n },\n };\n\n const scheduler = new UpdateScheduler(() => {\n render = renderResourceFiber(fiber, props);\n\n if (scheduler.isDirty || !isMounted) return;\n commitResourceFiber(fiber, render);\n });\n\n const fiber = createResourceFiber(HandleWrapperResource<R, P>, () =>\n scheduler.markDirty(),\n );\n\n flushSync(() => {\n scheduler.markDirty();\n });\n\n return render!.state;\n};\n"],"mappings":";AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,WAAW,uBAAuB;AAC3C,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAa5B,IAAM,wBAAwB;AAAA,EAC5B,CAAO,UAI4B;AACjC,UAAM,CAAC,EAAE,UAAU,IAAI,SAAS,MAAM,OAAO;AAC7C,UAAM,QAAQ,YAAY,MAAM,OAAO;AACvC,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,QAAQ,CAAC,YAAmC;AAC1C,gBAAM,UAAU;AAChB,qBAAW,OAAO;AAElB,gBAAM,SAAS;AAAA,QACjB;AAAA,QACA,SAAS,MAAM;AAAA,MACjB;AAAA,MACA,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,iBAAiB,CAC5B,SACA,EAAE,QAAQ,KAAK,IAAyB,CAAC,MACT;AAChC,MAAI,YAAY;AAChB,MAAI;AACJ,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,UAAU,MAAM;AACd,UAAI,UAAW;AACf,kBAAY;AAEZ,UAAI,UAAU,QAAS;AACvB,gBAAU,MAAM;AACd,4BAAoB,OAAO,MAAO;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,IACA,WAAW,MAAM;AACf,UAAI,CAAC,UAAW;AAChB,kBAAY;AAEZ,2BAAqB,KAAK;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,YAAY,IAAI,gBAAgB,MAAM;AAC1C,aAAS,oBAAoB,OAAO,KAAK;AAEzC,QAAI,UAAU,WAAW,CAAC,UAAW;AACrC,wBAAoB,OAAO,MAAM;AAAA,EACnC,CAAC;AAED,QAAM,QAAQ;AAAA,IAAoB;AAAA,IAA6B,MAC7D,UAAU,UAAU;AAAA,EACtB;AAEA,YAAU,MAAM;AACd,cAAU,UAAU;AAAA,EACtB,CAAC;AAED,SAAO,OAAQ;AACjB;","names":[]}
@@ -1,4 +1,4 @@
1
- import { ResourceElement, Resource } from "./types";
2
- export declare function resource<R>(fn: () => R): () => ResourceElement<R, undefined>;
1
+ import { Resource } from "./types";
2
+ export declare function resource<R>(fn: () => R): Resource<R, undefined>;
3
3
  export declare function resource<R, P>(fn: (props: P) => R): Resource<R, P>;
4
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,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
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/core/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAmB,MAAM,SAAS,CAAC;AAEpD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACjE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC"}
@@ -1,11 +1,10 @@
1
1
  // src/core/resource.ts
2
2
  import { fnSymbol } from "./callResourceFn.js";
3
3
  function resource(fn) {
4
- const type = (props, options) => {
4
+ const type = (props) => {
5
5
  return {
6
6
  type,
7
- props,
8
- ...options?.key !== void 0 && { key: options.key }
7
+ props
9
8
  };
10
9
  };
11
10
  type[fnSymbol] = fn;
@@ -1 +1 @@
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
+ {"version":3,"sources":["../../src/core/resource.ts"],"sourcesContent":["import { Resource, ResourceElement } from \"./types\";\nimport { fnSymbol } from \"./callResourceFn\";\nexport function resource<R>(fn: () => R): Resource<R, undefined>;\nexport function resource<R, P>(fn: (props: P) => R): Resource<R, P>;\nexport function resource<R, P = undefined>(fn: (props: P) => R) {\n const type = (props?: P) => {\n return {\n type,\n props: props!,\n } satisfies ResourceElement<R, P>;\n };\n\n type[fnSymbol] = fn;\n\n return type;\n}\n"],"mappings":";AACA,SAAS,gBAAgB;AAGlB,SAAS,SAA2B,IAAqB;AAC9D,QAAM,OAAO,CAAC,UAAc;AAC1B,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,OAAK,QAAQ,IAAI;AAEjB,SAAO;AACT;","names":[]}
@@ -2,13 +2,11 @@ type Task = () => void;
2
2
  export declare class UpdateScheduler {
3
3
  private readonly _task;
4
4
  private _isDirty;
5
- private _hasScheduledTask;
6
- private _isFlushing;
7
- private static readonly MAX_FLUSH_DEPTH;
8
5
  constructor(_task: Task);
9
6
  get isDirty(): boolean;
10
7
  markDirty(): void;
11
- flushSync(): void;
8
+ runTask(): void;
12
9
  }
10
+ export declare const flushSync: <T>(callback: () => T) => T;
13
11
  export {};
14
12
  //# sourceMappingURL=scheduler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AAAA,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;AA6BvB,qBAAa,eAAe;IAMd,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAM;gBAEhB,KAAK,EAAE,IAAI;IAExC,IAAI,OAAO,YAEV;IAED,SAAS;IAaT,SAAS;CAwBV"}
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AAAA,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;AAavB,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,OAAO,CAAC,QAAQ,CAAS;gBAEI,KAAK,EAAE,IAAI;IAExC,IAAI,OAAO,YAEV;IAED,SAAS;IAOT,OAAO;CAIR;AA8CD,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,UAAU,MAAM,CAAC,KAAG,CAehD,CAAC"}
@@ -1,66 +1,79 @@
1
1
  // src/core/scheduler.ts
2
- var queue = [];
3
- var isFlushPending = false;
4
- function flushQueue() {
5
- isFlushPending = false;
6
- const tasksToRun = queue;
7
- queue = [];
8
- for (const task of tasksToRun) {
9
- try {
10
- task();
11
- } catch (error) {
12
- console.error("Error in scheduled task:", error);
13
- }
14
- }
15
- }
16
- function scheduleUpdate(task) {
17
- queue.push(task);
18
- if (!isFlushPending) {
19
- isFlushPending = true;
20
- queueMicrotask(flushQueue);
21
- }
22
- }
23
- var UpdateScheduler = class _UpdateScheduler {
2
+ var MAX_FLUSH_LIMIT = 50;
3
+ var flushState = {
4
+ schedulers: /* @__PURE__ */ new Set([]),
5
+ isScheduled: false
6
+ };
7
+ var UpdateScheduler = class {
24
8
  constructor(_task) {
25
9
  this._task = _task;
26
10
  }
27
11
  _isDirty = false;
28
- _hasScheduledTask = false;
29
- _isFlushing = false;
30
- static MAX_FLUSH_DEPTH = 50;
31
12
  get isDirty() {
32
13
  return this._isDirty;
33
14
  }
34
15
  markDirty() {
35
16
  this._isDirty = true;
36
- if (this._hasScheduledTask || this._isFlushing) return;
37
- this._hasScheduledTask = true;
38
- scheduleUpdate(() => {
39
- this._hasScheduledTask = false;
40
- this.flushSync();
41
- });
17
+ flushState.schedulers.add(this);
18
+ scheduleFlush();
19
+ }
20
+ runTask() {
21
+ this._isDirty = false;
22
+ this._task();
42
23
  }
43
- flushSync() {
44
- if (this._isFlushing) return;
45
- this._isFlushing = true;
24
+ };
25
+ var scheduleFlush = () => {
26
+ if (flushState.isScheduled) return;
27
+ flushState.isScheduled = true;
28
+ queueMicrotask(flushScheduled);
29
+ };
30
+ var flushScheduled = () => {
31
+ try {
32
+ const errors = [];
46
33
  let flushDepth = 0;
47
- try {
48
- while (this._isDirty) {
49
- flushDepth++;
50
- if (flushDepth > _UpdateScheduler.MAX_FLUSH_DEPTH) {
51
- throw new Error(
52
- `Maximum update depth exceeded. This can happen when a resource repeatedly calls setState inside tapEffect.`
53
- );
54
- }
55
- this._isDirty = false;
56
- this._task();
34
+ for (const scheduler of flushState.schedulers) {
35
+ flushState.schedulers.delete(scheduler);
36
+ if (!scheduler.isDirty) continue;
37
+ flushDepth++;
38
+ if (flushDepth > MAX_FLUSH_LIMIT) {
39
+ throw new Error(
40
+ `Maximum update depth exceeded. This can happen when a resource repeatedly calls setState inside tapEffect.`
41
+ );
42
+ }
43
+ try {
44
+ scheduler.runTask();
45
+ } catch (error) {
46
+ errors.push(error);
47
+ }
48
+ }
49
+ if (errors.length > 0) {
50
+ if (errors.length === 1) {
51
+ throw errors[0];
52
+ } else {
53
+ throw new AggregateError(errors, "Errors occurred during flushSync");
57
54
  }
58
- } finally {
59
- this._isFlushing = false;
60
55
  }
56
+ } finally {
57
+ flushState.schedulers.clear();
58
+ flushState.isScheduled = false;
59
+ }
60
+ };
61
+ var flushSync = (callback) => {
62
+ const prev = flushState;
63
+ flushState = {
64
+ schedulers: /* @__PURE__ */ new Set([]),
65
+ isScheduled: true
66
+ };
67
+ try {
68
+ const result = callback();
69
+ flushScheduled();
70
+ return result;
71
+ } finally {
72
+ flushState = prev;
61
73
  }
62
74
  };
63
75
  export {
64
- UpdateScheduler
76
+ UpdateScheduler,
77
+ flushSync
65
78
  };
66
79
  //# sourceMappingURL=scheduler.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/scheduler.ts"],"sourcesContent":["type Task = () => void;\n\nlet queue: Task[] = [];\nlet isFlushPending = false;\n\nfunction flushQueue() {\n isFlushPending = false;\n\n const tasksToRun = queue;\n queue = [];\n\n for (const task of tasksToRun) {\n try {\n task();\n } catch (error) {\n console.error(\"Error in scheduled task:\", error);\n }\n }\n}\n\nfunction scheduleUpdate(task: Task) {\n queue.push(task);\n\n if (!isFlushPending) {\n isFlushPending = true;\n queueMicrotask(flushQueue);\n }\n}\n\nexport class UpdateScheduler {\n private _isDirty = false;\n private _hasScheduledTask = false;\n private _isFlushing = false;\n private static readonly MAX_FLUSH_DEPTH = 50;\n\n constructor(private readonly _task: Task) {}\n\n get isDirty() {\n return this._isDirty;\n }\n\n markDirty() {\n this._isDirty = true;\n\n if (this._hasScheduledTask || this._isFlushing) return;\n this._hasScheduledTask = true;\n\n scheduleUpdate(() => {\n this._hasScheduledTask = false;\n\n this.flushSync();\n });\n }\n\n flushSync() {\n if (this._isFlushing) return;\n\n this._isFlushing = true;\n let flushDepth = 0;\n\n try {\n while (this._isDirty) {\n flushDepth++;\n\n if (flushDepth > UpdateScheduler.MAX_FLUSH_DEPTH) {\n throw new Error(\n `Maximum update depth exceeded. This can happen when a resource ` +\n `repeatedly calls setState inside tapEffect.`,\n );\n }\n\n this._isDirty = false;\n this._task();\n }\n } finally {\n this._isFlushing = false;\n }\n }\n}\n"],"mappings":";AAEA,IAAI,QAAgB,CAAC;AACrB,IAAI,iBAAiB;AAErB,SAAS,aAAa;AACpB,mBAAiB;AAEjB,QAAM,aAAa;AACnB,UAAQ,CAAC;AAET,aAAW,QAAQ,YAAY;AAC7B,QAAI;AACF,WAAK;AAAA,IACP,SAAS,OAAO;AACd,cAAQ,MAAM,4BAA4B,KAAK;AAAA,IACjD;AAAA,EACF;AACF;AAEA,SAAS,eAAe,MAAY;AAClC,QAAM,KAAK,IAAI;AAEf,MAAI,CAAC,gBAAgB;AACnB,qBAAiB;AACjB,mBAAe,UAAU;AAAA,EAC3B;AACF;AAEO,IAAM,kBAAN,MAAM,iBAAgB;AAAA,EAM3B,YAA6B,OAAa;AAAb;AAAA,EAAc;AAAA,EALnC,WAAW;AAAA,EACX,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACtB,OAAwB,kBAAkB;AAAA,EAI1C,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAY;AACV,SAAK,WAAW;AAEhB,QAAI,KAAK,qBAAqB,KAAK,YAAa;AAChD,SAAK,oBAAoB;AAEzB,mBAAe,MAAM;AACnB,WAAK,oBAAoB;AAEzB,WAAK,UAAU;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEA,YAAY;AACV,QAAI,KAAK,YAAa;AAEtB,SAAK,cAAc;AACnB,QAAI,aAAa;AAEjB,QAAI;AACF,aAAO,KAAK,UAAU;AACpB;AAEA,YAAI,aAAa,iBAAgB,iBAAiB;AAChD,gBAAM,IAAI;AAAA,YACR;AAAA,UAEF;AAAA,QACF;AAEA,aAAK,WAAW;AAChB,aAAK,MAAM;AAAA,MACb;AAAA,IACF,UAAE;AACA,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/core/scheduler.ts"],"sourcesContent":["type Task = () => void;\n\ntype GlobalFlushState = {\n schedulers: Set<UpdateScheduler>;\n isScheduled: boolean;\n};\n\nconst MAX_FLUSH_LIMIT = 50;\nlet flushState: GlobalFlushState = {\n schedulers: new Set([]),\n isScheduled: false,\n};\n\nexport class UpdateScheduler {\n private _isDirty = false;\n\n constructor(private readonly _task: Task) {}\n\n get isDirty() {\n return this._isDirty;\n }\n\n markDirty() {\n this._isDirty = true;\n\n flushState.schedulers.add(this);\n scheduleFlush();\n }\n\n runTask() {\n this._isDirty = false;\n this._task();\n }\n}\n\nconst scheduleFlush = () => {\n if (flushState.isScheduled) return;\n flushState.isScheduled = true;\n queueMicrotask(flushScheduled);\n};\n\nconst flushScheduled = () => {\n try {\n const errors = [];\n let flushDepth = 0;\n\n for (const scheduler of flushState.schedulers) {\n flushState.schedulers.delete(scheduler);\n if (!scheduler.isDirty) continue;\n\n flushDepth++;\n\n if (flushDepth > MAX_FLUSH_LIMIT) {\n throw new Error(\n `Maximum update depth exceeded. This can happen when a resource ` +\n `repeatedly calls setState inside tapEffect.`,\n );\n }\n\n try {\n scheduler.runTask();\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length > 0) {\n if (errors.length === 1) {\n throw errors[0];\n } else {\n throw new AggregateError(errors, \"Errors occurred during flushSync\");\n }\n }\n } finally {\n flushState.schedulers.clear();\n flushState.isScheduled = false;\n }\n};\n\nexport const flushSync = <T>(callback: () => T): T => {\n const prev = flushState;\n flushState = {\n schedulers: new Set([]),\n isScheduled: true,\n };\n\n try {\n const result = callback();\n flushScheduled();\n\n return result;\n } finally {\n flushState = prev;\n }\n};\n"],"mappings":";AAOA,IAAM,kBAAkB;AACxB,IAAI,aAA+B;AAAA,EACjC,YAAY,oBAAI,IAAI,CAAC,CAAC;AAAA,EACtB,aAAa;AACf;AAEO,IAAM,kBAAN,MAAsB;AAAA,EAG3B,YAA6B,OAAa;AAAb;AAAA,EAAc;AAAA,EAFnC,WAAW;AAAA,EAInB,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAY;AACV,SAAK,WAAW;AAEhB,eAAW,WAAW,IAAI,IAAI;AAC9B,kBAAc;AAAA,EAChB;AAAA,EAEA,UAAU;AACR,SAAK,WAAW;AAChB,SAAK,MAAM;AAAA,EACb;AACF;AAEA,IAAM,gBAAgB,MAAM;AAC1B,MAAI,WAAW,YAAa;AAC5B,aAAW,cAAc;AACzB,iBAAe,cAAc;AAC/B;AAEA,IAAM,iBAAiB,MAAM;AAC3B,MAAI;AACF,UAAM,SAAS,CAAC;AAChB,QAAI,aAAa;AAEjB,eAAW,aAAa,WAAW,YAAY;AAC7C,iBAAW,WAAW,OAAO,SAAS;AACtC,UAAI,CAAC,UAAU,QAAS;AAExB;AAEA,UAAI,aAAa,iBAAiB;AAChC,cAAM,IAAI;AAAA,UACR;AAAA,QAEF;AAAA,MACF;AAEA,UAAI;AACF,kBAAU,QAAQ;AAAA,MACpB,SAAS,OAAO;AACd,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,UAAI,OAAO,WAAW,GAAG;AACvB,cAAM,OAAO,CAAC;AAAA,MAChB,OAAO;AACL,cAAM,IAAI,eAAe,QAAQ,kCAAkC;AAAA,MACrE;AAAA,IACF;AAAA,EACF,UAAE;AACA,eAAW,WAAW,MAAM;AAC5B,eAAW,cAAc;AAAA,EAC3B;AACF;AAEO,IAAM,YAAY,CAAI,aAAyB;AACpD,QAAM,OAAO;AACb,eAAa;AAAA,IACX,YAAY,oBAAI,IAAI,CAAC,CAAC;AAAA,IACtB,aAAa;AAAA,EACf;AAEA,MAAI;AACF,UAAM,SAAS,SAAS;AACxB,mBAAe;AAEf,WAAO;AAAA,EACT,UAAE;AACA,iBAAa;AAAA,EACf;AACF;","names":[]}
@@ -1,18 +1,16 @@
1
1
  import type { tapEffect } from "../hooks/tap-effect";
2
2
  import type { tapState } from "../hooks/tap-state";
3
+ import { fnSymbol } from "./callResourceFn";
3
4
  export type ResourceElement<R, P = any> = {
4
- type: Resource<R, P>;
5
+ type: Resource<R, P> & {
6
+ [fnSymbol]: (props: P) => R;
7
+ };
5
8
  props: P;
6
- key?: string | number;
7
9
  };
8
- export type Resource<R, P> = (...args: P extends undefined ? [props?: undefined, options?: {
9
- key?: string | number;
10
- }] : [props: P, options?: {
11
- key?: string | number;
12
- }]) => ResourceElement<R, P>;
13
- export type ContravariantResource<R, P> = (props: P, options?: {
14
- key?: string | number;
15
- }) => ResourceElement<R, any>;
10
+ type ResourceArgs<P> = undefined extends P ? [props?: P] : [props: P];
11
+ export type Resource<R, P> = (...args: ResourceArgs<P>) => ResourceElement<R, P>;
12
+ export type ContravariantResource<R, P> = (...args: ResourceArgs<P>) => ResourceElement<R>;
13
+ export type ExtractResourceOutput<T> = T extends ResourceElement<infer R, any> ? R : never;
16
14
  export type Cell = {
17
15
  type: "state";
18
16
  value: any;
@@ -43,4 +41,5 @@ export interface ResourceFiber<R, P> {
43
41
  isFirstRender: boolean;
44
42
  isNeverMounted: boolean;
45
43
  }
44
+ export {};
46
45
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
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"}
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;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,IAAI;IACxC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAA;KAAE,CAAC;IACvD,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF,KAAK,YAAY,CAAC,CAAC,IAAI,SAAS,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACtE,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAC3B,GAAG,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,KACrB,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE3B,MAAM,MAAM,qBAAqB,CAAC,CAAC,EAAE,CAAC,IAAI,CACxC,GAAG,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,KACrB,eAAe,CAAC,CAAC,CAAC,CAAC;AAExB,MAAM,MAAM,qBAAqB,CAAC,CAAC,IACjC,CAAC,SAAS,eAAe,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEtD,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"}
@@ -1,3 +1,3 @@
1
- import { ResourceElement } from "../core/types";
2
- export declare function tapInlineResource<R, P>(element: ResourceElement<R, P>): R;
1
+ import { ExtractResourceOutput, ResourceElement } from "../core/types";
2
+ export declare function tapInlineResource<E extends ResourceElement<any, any>>(element: E): ExtractResourceOutput<E>;
3
3
  //# sourceMappingURL=tap-inline-resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tap-inline-resource.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-inline-resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGhD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAEzE"}
1
+ {"version":3,"file":"tap-inline-resource.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-inline-resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGvE,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EACnE,OAAO,EAAE,CAAC,GACT,qBAAqB,CAAC,CAAC,CAAC,CAE1B"}
@@ -1 +1 @@
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
+ {"version":3,"sources":["../../src/hooks/tap-inline-resource.ts"],"sourcesContent":["import { ExtractResourceOutput, ResourceElement } from \"../core/types\";\nimport { callResourceFn } from \"../core/callResourceFn\";\n\nexport function tapInlineResource<E extends ResourceElement<any, any>>(\n element: E,\n): ExtractResourceOutput<E> {\n return callResourceFn(element.type, element.props);\n}\n"],"mappings":";AACA,SAAS,sBAAsB;AAExB,SAAS,kBACd,SAC0B;AAC1B,SAAO,eAAe,QAAQ,MAAM,QAAQ,KAAK;AACnD;","names":[]}
@@ -1,4 +1,4 @@
1
- import { ResourceElement } from "../core/types";
2
- export declare function tapResource<R, P>(element: ResourceElement<R, P>): R;
3
- export declare function tapResource<R, P>(element: ResourceElement<R, P>, deps: readonly unknown[]): R;
1
+ import { ExtractResourceOutput, ResourceElement } from "../core/types";
2
+ export declare function tapResource<E extends ResourceElement<any, any>>(element: E): ExtractResourceOutput<E>;
3
+ export declare function tapResource<E extends ResourceElement<any, any>>(element: E, deps: readonly unknown[]): ExtractResourceOutput<E>;
4
4
  //# sourceMappingURL=tap-resource.d.ts.map
@@ -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;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
+ {"version":3,"file":"tap-resource.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAWvE,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EAC7D,OAAO,EAAE,CAAC,GACT,qBAAqB,CAAC,CAAC,CAAC,CAAC;AAC5B,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EAC7D,OAAO,EAAE,CAAC,EACV,IAAI,EAAE,SAAS,OAAO,EAAE,GACvB,qBAAqB,CAAC,CAAC,CAAC,CAAC"}
@@ -2,9 +2,9 @@
2
2
  import { tapEffect } from "./tap-effect.js";
3
3
  import {
4
4
  createResourceFiber,
5
- unmountResource,
6
- renderResource,
7
- commitResource
5
+ unmountResourceFiber,
6
+ renderResourceFiber,
7
+ commitResourceFiber
8
8
  } from "../core/ResourceFiber.js";
9
9
  import { tapMemo } from "./tap-memo.js";
10
10
  import { tapState } from "./tap-state.js";
@@ -16,14 +16,14 @@ function tapResource(element, deps) {
16
16
  );
17
17
  const props = deps ? tapMemo(() => element.props, deps) : element.props;
18
18
  const result = tapMemo(
19
- () => renderResource(fiber, props),
19
+ () => renderResourceFiber(fiber, props),
20
20
  [fiber, props, stateVersion]
21
21
  );
22
22
  tapEffect(() => {
23
- return () => unmountResource(fiber);
23
+ return () => unmountResourceFiber(fiber);
24
24
  }, [fiber]);
25
25
  tapEffect(() => {
26
- commitResource(fiber, result);
26
+ commitResourceFiber(fiber, result);
27
27
  }, [fiber, result]);
28
28
  return result.state;
29
29
  }
@@ -1 +1 @@
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
+ {"version":3,"sources":["../../src/hooks/tap-resource.ts"],"sourcesContent":["import { ExtractResourceOutput, ResourceElement } from \"../core/types\";\nimport { tapEffect } from \"./tap-effect\";\nimport {\n createResourceFiber,\n unmountResourceFiber,\n renderResourceFiber,\n commitResourceFiber,\n} from \"../core/ResourceFiber\";\nimport { tapMemo } from \"./tap-memo\";\nimport { tapState } from \"./tap-state\";\n\nexport function tapResource<E extends ResourceElement<any, any>>(\n element: E,\n): ExtractResourceOutput<E>;\nexport function tapResource<E extends ResourceElement<any, any>>(\n element: E,\n deps: readonly unknown[],\n): ExtractResourceOutput<E>;\nexport function tapResource<E extends ResourceElement<any, any>>(\n element: E,\n deps?: readonly unknown[],\n): ExtractResourceOutput<E> {\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 () => renderResourceFiber(fiber, props),\n [fiber, props, stateVersion],\n );\n\n tapEffect(() => {\n return () => unmountResourceFiber(fiber);\n }, [fiber]);\n\n tapEffect(() => {\n commitResourceFiber(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;AASlB,SAAS,YACd,SACA,MAC0B;AAC1B,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,oBAAoB,OAAO,KAAK;AAAA,IACtC,CAAC,OAAO,OAAO,YAAY;AAAA,EAC7B;AAEA,YAAU,MAAM;AACd,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACzC,GAAG,CAAC,KAAK,CAAC;AAEV,YAAU,MAAM;AACd,wBAAoB,OAAO,MAAM;AAAA,EACnC,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,SAAO,OAAO;AAChB;","names":[]}
@@ -1,5 +1,11 @@
1
- import { ResourceElement } from "../core/types";
2
- export declare function tapResources<T extends ReadonlyArray<ResourceElement<any, any>>>(elements: T): {
3
- [K in keyof T]: T[K] extends ResourceElement<infer R, any> ? R : never;
1
+ import { ExtractResourceOutput, RenderResult, ResourceElement, ResourceFiber } from "../core/types";
2
+ export type TapResourcesRenderResult<R, K extends string | number | symbol> = {
3
+ add: [K, ResourceFiber<R, any>][];
4
+ remove: K[];
5
+ commit: [K, RenderResult][];
6
+ return: Record<K, R>;
7
+ };
8
+ export declare function tapResources<M extends Record<string | number | symbol, any>, E extends ResourceElement<any, any>>(map: M, getElement: (t: M[keyof M], key: keyof M) => E, getElementDeps: any[]): {
9
+ [K in keyof M]: ExtractResourceOutput<E>;
4
10
  };
5
11
  //# sourceMappingURL=tap-resources.d.ts.map
@@ -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;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
+ {"version":3,"file":"tap-resources.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-resources.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,eAAe,EACf,aAAa,EACd,MAAM,eAAe,CAAC;AAYvB,MAAM,MAAM,wBAAwB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,MAAM,IAAI;IAC5E,GAAG,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IAClC,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,MAAM,EAAE,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CACtB,CAAC;AAEF,wBAAgB,YAAY,CAC1B,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAC/C,CAAC,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EAEnC,GAAG,EAAE,CAAC,EACN,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,EAC9C,cAAc,EAAE,GAAG,EAAE,GACpB;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC;CAAE,CAgF9C"}
@@ -2,77 +2,68 @@
2
2
  import { tapEffect } from "./tap-effect.js";
3
3
  import { tapMemo } from "./tap-memo.js";
4
4
  import { tapState } from "./tap-state.js";
5
+ import { tapCallback } from "./tap-callback.js";
5
6
  import {
6
7
  createResourceFiber,
7
- unmountResource,
8
- renderResource,
9
- commitResource
8
+ unmountResourceFiber,
9
+ renderResourceFiber,
10
+ commitResourceFiber
10
11
  } from "../core/ResourceFiber.js";
11
- function tapResources(elements) {
12
- const seenKeys = /* @__PURE__ */ new Set();
13
- elements.forEach((element, index) => {
14
- if (element.key === void 0) {
15
- throw new Error(
16
- `tapResources: All resource elements must have a key. Element at index ${index} is missing a key.`
17
- );
18
- }
19
- if (seenKeys.has(element.key)) {
20
- throw new Error(
21
- `tapResources: Duplicate key "${element.key}" found. All keys must be unique.`
22
- );
23
- }
24
- seenKeys.add(element.key);
25
- });
26
- const [stateVersion, rerender] = tapState({});
27
- const elementsByKey = tapMemo(
28
- () => new Map(elements.map((element) => [element.key, element])),
29
- [elements]
30
- );
31
- const [fibers] = tapState(
32
- () => /* @__PURE__ */ new Map()
33
- );
12
+ function tapResources(map, getElement, getElementDeps) {
13
+ const [version, setVersion] = tapState(0);
14
+ const rerender = tapCallback(() => setVersion((v) => v + 1), []);
15
+ const [fibers] = tapState(() => /* @__PURE__ */ new Map());
16
+ const getElementMemo = tapMemo(() => getElement, getElementDeps);
34
17
  const results = tapMemo(() => {
35
- const resultMap = /* @__PURE__ */ new Map();
36
- const currentKeys = /* @__PURE__ */ new Set();
37
- elementsByKey.forEach((element, key) => {
38
- currentKeys.add(key);
18
+ const results2 = {
19
+ remove: [],
20
+ add: [],
21
+ commit: [],
22
+ return: {}
23
+ };
24
+ for (const key in map) {
25
+ const value = map[key];
26
+ const element = getElementMemo(value, key);
39
27
  let fiber = fibers.get(key);
40
28
  if (!fiber || fiber.resource !== element.type) {
41
- if (fiber) unmountResource(fiber);
42
- fiber = createResourceFiber(element.type, () => rerender({}));
43
- fibers.set(key, fiber);
29
+ if (fiber) results2.remove.push(key);
30
+ fiber = createResourceFiber(element.type, rerender);
31
+ results2.add.push([key, fiber]);
44
32
  }
45
- const result = renderResource(fiber, element.props);
46
- resultMap.set(key, result);
47
- });
48
- fibers.forEach((fiber, key) => {
49
- if (!currentKeys.has(key)) {
50
- unmountResource(fiber);
51
- fibers.delete(key);
52
- }
53
- });
54
- return resultMap;
55
- }, [elementsByKey, stateVersion]);
56
- tapEffect(() => {
57
- results.forEach((result, key) => {
58
- const fiber = fibers.get(key);
59
- if (fiber) {
60
- commitResource(fiber, result);
33
+ const renderResult = renderResourceFiber(fiber, element.props);
34
+ results2.commit.push([key, renderResult]);
35
+ results2.return[key] = renderResult.state;
36
+ }
37
+ if (fibers.size > results2.commit.length - results2.add.length + results2.remove.length) {
38
+ for (const key of fibers.keys()) {
39
+ if (!(key in map)) {
40
+ results2.remove.push(key);
41
+ }
61
42
  }
62
- });
63
- }, [results, fibers]);
43
+ }
44
+ return results2;
45
+ }, [map, getElementMemo, version]);
64
46
  tapEffect(() => {
65
47
  return () => {
66
- fibers.forEach((fiber) => {
67
- unmountResource(fiber);
68
- });
69
- fibers.clear();
48
+ for (const key of fibers.keys()) {
49
+ unmountResourceFiber(fibers.get(key));
50
+ fibers.delete(key);
51
+ }
70
52
  };
71
- }, [fibers]);
72
- return tapMemo(
73
- () => elements.map((element) => results.get(element.key)?.state),
74
- [elements, results]
75
- );
53
+ }, []);
54
+ tapEffect(() => {
55
+ for (const key of results.remove) {
56
+ unmountResourceFiber(fibers.get(key));
57
+ fibers.delete(key);
58
+ }
59
+ for (const [key, fiber] of results.add) {
60
+ fibers.set(key, fiber);
61
+ }
62
+ for (const [key, result] of results.commit) {
63
+ commitResourceFiber(fibers.get(key), result);
64
+ }
65
+ }, [results]);
66
+ return results.return;
76
67
  }
77
68
  export {
78
69
  tapResources
@@ -1 +1 @@
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
+ {"version":3,"sources":["../../src/hooks/tap-resources.ts"],"sourcesContent":["import {\n ExtractResourceOutput,\n RenderResult,\n ResourceElement,\n ResourceFiber,\n} from \"../core/types\";\nimport { tapEffect } from \"./tap-effect\";\nimport { tapMemo } from \"./tap-memo\";\nimport { tapState } from \"./tap-state\";\nimport { tapCallback } from \"./tap-callback\";\nimport {\n createResourceFiber,\n unmountResourceFiber,\n renderResourceFiber,\n commitResourceFiber,\n} from \"../core/ResourceFiber\";\n\nexport type TapResourcesRenderResult<R, K extends string | number | symbol> = {\n add: [K, ResourceFiber<R, any>][];\n remove: K[];\n commit: [K, RenderResult][];\n return: Record<K, R>;\n};\n\nexport function tapResources<\n M extends Record<string | number | symbol, any>,\n E extends ResourceElement<any, any>,\n>(\n map: M,\n getElement: (t: M[keyof M], key: keyof M) => E,\n getElementDeps: any[],\n): { [K in keyof M]: ExtractResourceOutput<E> } {\n type R = ExtractResourceOutput<E>;\n const [version, setVersion] = tapState(0);\n const rerender = tapCallback(() => setVersion((v) => v + 1), []);\n\n type K = keyof M;\n const [fibers] = tapState(() => new Map<K, ResourceFiber<R, any>>());\n\n const getElementMemo = tapMemo(() => getElement, getElementDeps);\n\n // Process each element\n\n const results = tapMemo(() => {\n const results: TapResourcesRenderResult<R, K> = {\n remove: [],\n add: [],\n commit: [],\n return: {} as Record<K, R>,\n };\n\n // Create/update fibers and render\n for (const key in map) {\n const value = map[key as K];\n const element = getElementMemo(value, 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) results.remove.push(key);\n fiber = createResourceFiber(element.type, rerender);\n results.add.push([key, fiber]);\n }\n\n // Render with current props\n const renderResult = renderResourceFiber(fiber, element.props);\n results.commit.push([key, renderResult]);\n\n results.return[key] = renderResult.state;\n }\n\n // Clean up removed fibers (only if there might be stale ones)\n if (\n fibers.size >\n results.commit.length - results.add.length + results.remove.length\n ) {\n for (const key of fibers.keys()) {\n if (!(key in map)) {\n results.remove.push(key);\n }\n }\n }\n\n return results;\n }, [map, getElementMemo, version]);\n\n // Cleanup on unmount\n tapEffect(() => {\n return () => {\n for (const key of fibers.keys()) {\n unmountResourceFiber(fibers.get(key)!);\n fibers.delete(key);\n }\n };\n }, []);\n\n tapEffect(() => {\n for (const key of results.remove) {\n unmountResourceFiber(fibers.get(key)!);\n fibers.delete(key);\n }\n for (const [key, fiber] of results.add) {\n fibers.set(key, fiber);\n }\n for (const [key, result] of results.commit) {\n commitResourceFiber(fibers.get(key)!, result);\n }\n }, [results]);\n\n return results.return;\n}\n"],"mappings":";AAMA,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASA,SAAS,aAId,KACA,YACA,gBAC8C;AAE9C,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,CAAC;AACxC,QAAM,WAAW,YAAY,MAAM,WAAW,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;AAG/D,QAAM,CAAC,MAAM,IAAI,SAAS,MAAM,oBAAI,IAA8B,CAAC;AAEnE,QAAM,iBAAiB,QAAQ,MAAM,YAAY,cAAc;AAI/D,QAAM,UAAU,QAAQ,MAAM;AAC5B,UAAMA,WAA0C;AAAA,MAC9C,QAAQ,CAAC;AAAA,MACT,KAAK,CAAC;AAAA,MACN,QAAQ,CAAC;AAAA,MACT,QAAQ,CAAC;AAAA,IACX;AAGA,eAAW,OAAO,KAAK;AACrB,YAAM,QAAQ,IAAI,GAAQ;AAC1B,YAAM,UAAU,eAAe,OAAO,GAAG;AAEzC,UAAI,QAAQ,OAAO,IAAI,GAAG;AAG1B,UAAI,CAAC,SAAS,MAAM,aAAa,QAAQ,MAAM;AAC7C,YAAI,MAAO,CAAAA,SAAQ,OAAO,KAAK,GAAG;AAClC,gBAAQ,oBAAoB,QAAQ,MAAM,QAAQ;AAClD,QAAAA,SAAQ,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC;AAAA,MAC/B;AAGA,YAAM,eAAe,oBAAoB,OAAO,QAAQ,KAAK;AAC7D,MAAAA,SAAQ,OAAO,KAAK,CAAC,KAAK,YAAY,CAAC;AAEvC,MAAAA,SAAQ,OAAO,GAAG,IAAI,aAAa;AAAA,IACrC;AAGA,QACE,OAAO,OACPA,SAAQ,OAAO,SAASA,SAAQ,IAAI,SAASA,SAAQ,OAAO,QAC5D;AACA,iBAAW,OAAO,OAAO,KAAK,GAAG;AAC/B,YAAI,EAAE,OAAO,MAAM;AACjB,UAAAA,SAAQ,OAAO,KAAK,GAAG;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAEA,WAAOA;AAAA,EACT,GAAG,CAAC,KAAK,gBAAgB,OAAO,CAAC;AAGjC,YAAU,MAAM;AACd,WAAO,MAAM;AACX,iBAAW,OAAO,OAAO,KAAK,GAAG;AAC/B,6BAAqB,OAAO,IAAI,GAAG,CAAE;AACrC,eAAO,OAAO,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,eAAW,OAAO,QAAQ,QAAQ;AAChC,2BAAqB,OAAO,IAAI,GAAG,CAAE;AACrC,aAAO,OAAO,GAAG;AAAA,IACnB;AACA,eAAW,CAAC,KAAK,KAAK,KAAK,QAAQ,KAAK;AACtC,aAAO,IAAI,KAAK,KAAK;AAAA,IACvB;AACA,eAAW,CAAC,KAAK,MAAM,KAAK,QAAQ,QAAQ;AAC1C,0BAAoB,OAAO,IAAI,GAAG,GAAI,MAAM;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,QAAQ;AACjB;","names":["results"]}
@@ -1,8 +1,6 @@
1
- import { ResourceFiber } from "../core/types";
2
1
  export declare namespace tapState {
3
2
  type StateUpdater<S> = S | ((prev: S) => S);
4
3
  }
5
- export declare const rerender: (fiber: ResourceFiber<any, any>) => void;
6
4
  export declare function tapState<S = undefined>(): [
7
5
  S | undefined,
8
6
  (updater: tapState.StateUpdater<S>) => void
@@ -1 +1 @@
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
+ {"version":3,"file":"tap-state.d.ts","sourceRoot":"","sources":["../../src/hooks/tap-state.ts"],"names":[],"mappings":"AAGA,yBAAiB,QAAQ,CAAC;IACxB,KAAY,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;CACpD;AAgED,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"}
@@ -4,11 +4,10 @@ var rerender = (fiber) => {
4
4
  if (fiber.renderContext) {
5
5
  throw new Error("Resource updated during render");
6
6
  }
7
- if (fiber.isNeverMounted) {
8
- throw new Error("Resource updated before mount");
9
- }
10
7
  if (fiber.isMounted) {
11
8
  fiber.scheduleRerender();
9
+ } else if (fiber.isNeverMounted) {
10
+ throw new Error("Resource updated before mount");
12
11
  }
13
12
  };
14
13
  function getStateCell(initialValue) {
@@ -46,7 +45,6 @@ function tapState(initial) {
46
45
  return [cell.value, cell.set];
47
46
  }
48
47
  export {
49
- rerender,
50
48
  tapState
51
49
  };
52
50
  //# sourceMappingURL=tap-state.js.map
@@ -1 +1 @@
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"]}
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\nconst rerender = (fiber: ResourceFiber<any, any>) => {\n if (fiber.renderContext) {\n throw new Error(\"Resource updated during render\");\n }\n\n if (fiber.isMounted) {\n // Only schedule rerender if currently mounted\n fiber.scheduleRerender();\n } else if (fiber.isNeverMounted) {\n throw new Error(\"Resource updated before mount\");\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;AAOxC,IAAM,WAAW,CAAC,UAAmC;AACnD,MAAI,MAAM,eAAe;AACvB,UAAM,IAAI,MAAM,gCAAgC;AAAA,EAClD;AAEA,MAAI,MAAM,WAAW;AAEnB,UAAM,iBAAiB;AAAA,EACzB,WAAW,MAAM,gBAAgB;AAC/B,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;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
@@ -8,7 +8,8 @@ export { tapEffectEvent } from "./hooks/tap-effect-event";
8
8
  export { tapResource } from "./hooks/tap-resource";
9
9
  export { tapInlineResource } from "./hooks/tap-inline-resource";
10
10
  export { tapResources } from "./hooks/tap-resources";
11
- export { createResource } from "./core/ResourceHandle";
11
+ export { createResource } from "./core/createResource";
12
+ export { flushSync } from "./core/scheduler";
12
13
  export { createContext, tapContext, withContextProvider } from "./core/context";
13
- export type { Resource, ContravariantResource, ResourceElement, } from "./core/types";
14
+ export type { Resource, ContravariantResource, ResourceElement, ExtractResourceOutput, } from "./core/types";
14
15
  //# 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,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"}
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;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGhF,YAAY,EACV,QAAQ,EACR,qBAAqB,EACrB,eAAe,EACf,qBAAqB,GACtB,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -9,11 +9,13 @@ import { tapEffectEvent } from "./hooks/tap-effect-event.js";
9
9
  import { tapResource } from "./hooks/tap-resource.js";
10
10
  import { tapInlineResource } from "./hooks/tap-inline-resource.js";
11
11
  import { tapResources } from "./hooks/tap-resources.js";
12
- import { createResource } from "./core/ResourceHandle.js";
12
+ import { createResource } from "./core/createResource.js";
13
+ import { flushSync } from "./core/scheduler.js";
13
14
  import { createContext, tapContext, withContextProvider } from "./core/context.js";
14
15
  export {
15
16
  createContext,
16
17
  createResource,
18
+ flushSync,
17
19
  resource,
18
20
  tapCallback,
19
21
  tapContext,
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 } 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
+ {"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/createResource\";\nexport { flushSync } from \"./core/scheduler\";\n\n// context\nexport { createContext, tapContext, withContextProvider } from \"./core/context\";\n\n// types\nexport type {\n Resource,\n ContravariantResource,\n ResourceElement,\n ExtractResourceOutput,\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;AAC/B,SAAS,iBAAiB;AAG1B,SAAS,eAAe,YAAY,2BAA2B;","names":[]}
@@ -1,3 +1,3 @@
1
- import { ResourceElement } from "../core/types";
2
- export declare function useResource<R, P>(element: ResourceElement<R, P>): R;
1
+ import { ExtractResourceOutput, ResourceElement } from "../core/types";
2
+ export declare function useResource<E extends ResourceElement<any, any>>(element: E): ExtractResourceOutput<E>;
3
3
  //# sourceMappingURL=use-resource.d.ts.map
@@ -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;AAehD,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,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAevE,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EAC7D,OAAO,EAAE,CAAC,GACT,qBAAqB,CAAC,CAAC,CAAC,CAgB1B"}
@@ -2,9 +2,9 @@
2
2
  import { useEffect, useLayoutEffect, useMemo, useState } from "react";
3
3
  import {
4
4
  createResourceFiber,
5
- unmountResource,
6
- renderResource,
7
- commitResource
5
+ unmountResourceFiber,
6
+ renderResourceFiber,
7
+ commitResourceFiber
8
8
  } from "../core/ResourceFiber.js";
9
9
  var shouldAvoidLayoutEffect = globalThis.__ASSISTANT_UI_DISABLE_LAYOUT_EFFECT__ === true;
10
10
  var useIsomorphicLayoutEffect = shouldAvoidLayoutEffect ? useEffect : useLayoutEffect;
@@ -12,14 +12,14 @@ function useResource(element) {
12
12
  const [, rerender] = useState({});
13
13
  const fiber = useMemo(
14
14
  () => createResourceFiber(element.type, () => rerender({})),
15
- [element.type, rerender]
15
+ [element.type]
16
16
  );
17
- const result = renderResource(fiber, element.props);
17
+ const result = renderResourceFiber(fiber, element.props);
18
18
  useIsomorphicLayoutEffect(() => {
19
- return () => unmountResource(fiber);
20
- }, []);
19
+ return () => unmountResourceFiber(fiber);
20
+ }, [fiber]);
21
21
  useIsomorphicLayoutEffect(() => {
22
- commitResource(fiber, result);
22
+ commitResourceFiber(fiber, result);
23
23
  });
24
24
  return result.state;
25
25
  }
@@ -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 {\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":[]}
1
+ {"version":3,"sources":["../../src/react/use-resource.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useMemo, useState } from \"react\";\nimport { ExtractResourceOutput, ResourceElement } from \"../core/types\";\nimport {\n createResourceFiber,\n unmountResourceFiber,\n renderResourceFiber,\n commitResourceFiber,\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<E extends ResourceElement<any, any>>(\n element: E,\n): ExtractResourceOutput<E> {\n const [, rerender] = useState({});\n const fiber = useMemo(\n () => createResourceFiber(element.type, () => rerender({})),\n [element.type],\n );\n\n const result = renderResourceFiber(fiber, element.props);\n useIsomorphicLayoutEffect(() => {\n return () => unmountResourceFiber(fiber);\n }, [fiber]);\n useIsomorphicLayoutEffect(() => {\n commitResourceFiber(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,YACd,SAC0B;AAC1B,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,IAAI;AAAA,EACf;AAEA,QAAM,SAAS,oBAAoB,OAAO,QAAQ,KAAK;AACvD,4BAA0B,MAAM;AAC9B,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACzC,GAAG,CAAC,KAAK,CAAC;AACV,4BAA0B,MAAM;AAC9B,wBAAoB,OAAO,MAAM;AAAA,EACnC,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.3.2",
3
+ "version": "0.3.3",
4
4
  "type": "module",
5
5
  "description": "Zero-dependency reactive state management inspired by React hooks",
6
6
  "main": "dist/index.js",
@@ -18,7 +18,7 @@
18
18
  }
19
19
  },
20
20
  "devDependencies": {
21
- "@types/node": "^24.10.1",
21
+ "@types/node": "^25.0.0",
22
22
  "@types/react": "*",
23
23
  "@vitest/ui": "^4.0.15",
24
24
  "tsx": "^4.21.0",
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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"]}