@assistant-ui/tap 0.3.1 → 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.
- package/README.md +30 -8
- package/dist/__tests__/test-utils.js +7 -7
- package/dist/__tests__/test-utils.js.map +1 -1
- package/dist/core/ResourceFiber.d.ts +3 -3
- package/dist/core/ResourceFiber.d.ts.map +1 -1
- package/dist/core/ResourceFiber.js +6 -6
- package/dist/core/ResourceFiber.js.map +1 -1
- package/dist/core/callResourceFn.js +1 -1
- package/dist/core/callResourceFn.js.map +1 -1
- package/dist/core/commit.d.ts.map +1 -1
- package/dist/core/commit.js +8 -12
- package/dist/core/commit.js.map +1 -1
- package/dist/core/context.js +1 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/{ResourceHandle.d.ts → createResource.d.ts} +6 -5
- package/dist/core/createResource.d.ts.map +1 -0
- package/dist/core/{ResourceHandle.js → createResource.js} +35 -33
- package/dist/core/createResource.js.map +1 -0
- package/dist/core/resource.d.ts +2 -2
- package/dist/core/resource.d.ts.map +1 -1
- package/dist/core/resource.js +2 -3
- package/dist/core/resource.js.map +1 -1
- package/dist/core/scheduler.d.ts +2 -4
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +60 -47
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/types.d.ts +9 -10
- package/dist/core/types.d.ts.map +1 -1
- package/dist/hooks/tap-inline-resource.d.ts +2 -2
- package/dist/hooks/tap-inline-resource.d.ts.map +1 -1
- package/dist/hooks/tap-inline-resource.js.map +1 -1
- package/dist/hooks/tap-resource.d.ts +3 -3
- package/dist/hooks/tap-resource.d.ts.map +1 -1
- package/dist/hooks/tap-resource.js +6 -6
- package/dist/hooks/tap-resource.js.map +1 -1
- package/dist/hooks/tap-resources.d.ts +9 -3
- package/dist/hooks/tap-resources.d.ts.map +1 -1
- package/dist/hooks/tap-resources.js +51 -60
- package/dist/hooks/tap-resources.js.map +1 -1
- package/dist/hooks/tap-state.d.ts +0 -2
- package/dist/hooks/tap-state.d.ts.map +1 -1
- package/dist/hooks/tap-state.js +2 -4
- package/dist/hooks/tap-state.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/react/use-resource.d.ts +2 -2
- package/dist/react/use-resource.d.ts.map +1 -1
- package/dist/react/use-resource.js +8 -8
- package/dist/react/use-resource.js.map +1 -1
- package/package.json +5 -6
- package/dist/core/ResourceHandle.d.ts.map +0 -1
- 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
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
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,
|
|
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
|
|
17
|
+
function unmountResourceFiber(fiber) {
|
|
18
18
|
fiber.isMounted = false;
|
|
19
19
|
cleanupAllEffects(fiber);
|
|
20
20
|
}
|
|
21
|
-
function
|
|
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
|
|
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
|
-
|
|
43
|
+
commitResourceFiber,
|
|
44
44
|
createResourceFiber,
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
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":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/callResourceFn.ts"],"sourcesContent":["import { Resource } from \"./types\";\n\n/**\n * Renders a resource with the given props.\n * @internal This is for internal use only.\n */\nexport function callResourceFn<R, P>(resource: Resource<R, P>, props: P): R {\n const fn = (resource as unknown as { [fnSymbol]?: (props: P) => R })[\n fnSymbol\n ];\n if (!fn) {\n throw new Error(\"ResourceElement.type is not a valid Resource\");\n }\n return fn(props);\n}\n\n/**\n * Symbol used to store the ResourceFn in the Resource constructor.\n * @internal This is for internal use only.\n */\nexport const fnSymbol = Symbol(\"fnSymbol\");\n"],"mappings":";AAMO,SAAS,eAAqB,UAA0B,OAAa;AAC1E,QAAM,KAAM,SACV,QACF;AACA,MAAI,CAAC,IAAI;AACP,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,SAAO,GAAG,KAAK;AACjB;AAMO,IAAM,WAAW,
|
|
1
|
+
{"version":3,"sources":["../../src/core/callResourceFn.ts"],"sourcesContent":["import { Resource } from \"./types\";\n\n/**\n * Renders a resource with the given props.\n * @internal This is for internal use only.\n */\nexport function callResourceFn<R, P>(resource: Resource<R, P>, props: P): R {\n const fn = (resource as unknown as { [fnSymbol]?: (props: P) => R })[\n fnSymbol\n ];\n if (!fn) {\n throw new Error(\"ResourceElement.type is not a valid Resource\");\n }\n return fn(props);\n}\n\n/**\n * Symbol used to store the ResourceFn in the Resource constructor.\n * @internal This is for internal use only.\n */\nexport const fnSymbol = Symbol(\"fnSymbol\");\n"],"mappings":";AAMO,SAAS,eAAqB,UAA0B,OAAa;AAC1E,QAAM,KAAM,SACV,QACF;AACA,MAAI,CAAC,IAAI;AACP,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,SAAO,GAAG,KAAK;AACjB;AAMO,IAAM,WAAW,uBAAO,UAAU;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commit.d.ts","sourceRoot":"","sources":["../../src/core/commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEtD,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAC/B,YAAY,EAAE,YAAY,EAC1B,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GACzB,IAAI,
|
|
1
|
+
{"version":3,"file":"commit.d.ts","sourceRoot":"","sources":["../../src/core/commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEtD,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAC/B,YAAY,EAAE,YAAY,EAC1B,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GACzB,IAAI,CAiDN;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,QAgB5E"}
|
package/dist/core/commit.js
CHANGED
|
@@ -25,19 +25,15 @@ function commitRender(renderResult, fiber) {
|
|
|
25
25
|
effectCell.mounted = false;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
effectCell.mounted = true;
|
|
36
|
-
effectCell.cleanup = typeof cleanup === "function" ? cleanup : void 0;
|
|
37
|
-
effectCell.deps = task.deps;
|
|
38
|
-
} catch (error) {
|
|
39
|
-
throw error;
|
|
28
|
+
const cleanup = task.effect();
|
|
29
|
+
if (cleanup !== void 0 && typeof cleanup !== "function") {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`An effect function must either return a cleanup function or nothing. Received: ${typeof cleanup}`
|
|
32
|
+
);
|
|
40
33
|
}
|
|
34
|
+
effectCell.mounted = true;
|
|
35
|
+
effectCell.cleanup = typeof cleanup === "function" ? cleanup : void 0;
|
|
36
|
+
effectCell.deps = task.deps;
|
|
41
37
|
}
|
|
42
38
|
});
|
|
43
39
|
}
|
package/dist/core/commit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/commit.ts"],"sourcesContent":["import { ResourceFiber, RenderResult } from \"./types\";\n\nexport function commitRender<R, P>(\n renderResult: RenderResult,\n fiber: ResourceFiber<R, P>,\n): void {\n // Process all tasks collected during render\n renderResult.commitTasks.forEach((task) => {\n const cellIndex = task.cellIndex;\n const effectCell = fiber.cells[cellIndex]!;\n if (effectCell.type !== \"effect\") {\n throw new Error(\"Cannot find effect cell\");\n }\n\n // Check if deps changed\n let shouldRunEffect = true;\n\n if (effectCell.deps !== undefined && task.deps !== undefined) {\n shouldRunEffect =\n effectCell.deps.length !== task.deps.length ||\n effectCell.deps.some((dep, j) => !Object.is(dep, task.deps![j]));\n }\n\n // Run cleanup if effect will re-run\n if (shouldRunEffect) {\n if (effectCell.mounted) {\n if (typeof effectCell.deps !== typeof task.deps) {\n throw new Error(\n \"tapEffect called with and without dependencies across re-renders\",\n );\n }\n\n try {\n if (effectCell.mounted && effectCell.cleanup) {\n effectCell.cleanup();\n }\n } finally {\n effectCell.mounted = false;\n }\n }\n
|
|
1
|
+
{"version":3,"sources":["../../src/core/commit.ts"],"sourcesContent":["import { ResourceFiber, RenderResult } from \"./types\";\n\nexport function commitRender<R, P>(\n renderResult: RenderResult,\n fiber: ResourceFiber<R, P>,\n): void {\n // Process all tasks collected during render\n renderResult.commitTasks.forEach((task) => {\n const cellIndex = task.cellIndex;\n const effectCell = fiber.cells[cellIndex]!;\n if (effectCell.type !== \"effect\") {\n throw new Error(\"Cannot find effect cell\");\n }\n\n // Check if deps changed\n let shouldRunEffect = true;\n\n if (effectCell.deps !== undefined && task.deps !== undefined) {\n shouldRunEffect =\n effectCell.deps.length !== task.deps.length ||\n effectCell.deps.some((dep, j) => !Object.is(dep, task.deps![j]));\n }\n\n // Run cleanup if effect will re-run\n if (shouldRunEffect) {\n if (effectCell.mounted) {\n if (typeof effectCell.deps !== typeof task.deps) {\n throw new Error(\n \"tapEffect called with and without dependencies across re-renders\",\n );\n }\n\n try {\n if (effectCell.mounted && effectCell.cleanup) {\n effectCell.cleanup();\n }\n } finally {\n effectCell.mounted = false;\n }\n }\n const cleanup = task.effect();\n\n if (cleanup !== undefined && typeof cleanup !== \"function\") {\n throw new Error(\n \"An effect function must either return a cleanup function or nothing. \" +\n `Received: ${typeof cleanup}`,\n );\n }\n\n effectCell.mounted = true;\n effectCell.cleanup = typeof cleanup === \"function\" ? cleanup : undefined;\n effectCell.deps = task.deps;\n }\n });\n}\n\nexport function cleanupAllEffects<R, P>(executionContext: ResourceFiber<R, P>) {\n let firstError: unknown | null = null;\n // Run cleanups in reverse order\n for (let i = executionContext.cells.length - 1; i >= 0; i--) {\n const cell = executionContext.cells[i];\n if (cell?.type === \"effect\" && cell.mounted && cell.cleanup) {\n try {\n cell.cleanup();\n } catch (e) {\n if (firstError == null) firstError = e;\n } finally {\n cell.mounted = false;\n }\n }\n }\n if (firstError != null) throw firstError;\n}\n"],"mappings":";AAEO,SAAS,aACd,cACA,OACM;AAEN,eAAa,YAAY,QAAQ,CAAC,SAAS;AACzC,UAAM,YAAY,KAAK;AACvB,UAAM,aAAa,MAAM,MAAM,SAAS;AACxC,QAAI,WAAW,SAAS,UAAU;AAChC,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAGA,QAAI,kBAAkB;AAEtB,QAAI,WAAW,SAAS,UAAa,KAAK,SAAS,QAAW;AAC5D,wBACE,WAAW,KAAK,WAAW,KAAK,KAAK,UACrC,WAAW,KAAK,KAAK,CAAC,KAAK,MAAM,CAAC,OAAO,GAAG,KAAK,KAAK,KAAM,CAAC,CAAC,CAAC;AAAA,IACnE;AAGA,QAAI,iBAAiB;AACnB,UAAI,WAAW,SAAS;AACtB,YAAI,OAAO,WAAW,SAAS,OAAO,KAAK,MAAM;AAC/C,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,YAAI;AACF,cAAI,WAAW,WAAW,WAAW,SAAS;AAC5C,uBAAW,QAAQ;AAAA,UACrB;AAAA,QACF,UAAE;AACA,qBAAW,UAAU;AAAA,QACvB;AAAA,MACF;AACA,YAAM,UAAU,KAAK,OAAO;AAE5B,UAAI,YAAY,UAAa,OAAO,YAAY,YAAY;AAC1D,cAAM,IAAI;AAAA,UACR,kFACe,OAAO,OAAO;AAAA,QAC/B;AAAA,MACF;AAEA,iBAAW,UAAU;AACrB,iBAAW,UAAU,OAAO,YAAY,aAAa,UAAU;AAC/D,iBAAW,OAAO,KAAK;AAAA,IACzB;AAAA,EACF,CAAC;AACH;AAEO,SAAS,kBAAwB,kBAAuC;AAC7E,MAAI,aAA6B;AAEjC,WAAS,IAAI,iBAAiB,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3D,UAAM,OAAO,iBAAiB,MAAM,CAAC;AACrC,QAAI,MAAM,SAAS,YAAY,KAAK,WAAW,KAAK,SAAS;AAC3D,UAAI;AACF,aAAK,QAAQ;AAAA,MACf,SAAS,GAAG;AACV,YAAI,cAAc,KAAM,cAAa;AAAA,MACvC,UAAE;AACA,aAAK,UAAU;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACA,MAAI,cAAc,KAAM,OAAM;AAChC;","names":[]}
|
package/dist/core/context.js
CHANGED
package/dist/core/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/context.ts"],"sourcesContent":["const contextValue: unique symbol = Symbol(\"tap.Context\");\ntype Context<T> = {\n [contextValue]: T;\n};\n\nexport const createContext = <T>(defaultValue: T): Context<T> => {\n return {\n [contextValue]: defaultValue,\n };\n};\n\nexport const withContextProvider = <T, TResult>(\n context: Context<T>,\n value: T,\n fn: () => TResult,\n) => {\n const previousValue = context[contextValue];\n context[contextValue] = value;\n try {\n return fn();\n } finally {\n context[contextValue] = previousValue;\n }\n};\n\nexport const tapContext = <T>(context: Context<T>) => {\n return context[contextValue];\n};\n"],"mappings":";AAAA,IAAM,eAA8B,
|
|
1
|
+
{"version":3,"sources":["../../src/core/context.ts"],"sourcesContent":["const contextValue: unique symbol = Symbol(\"tap.Context\");\ntype Context<T> = {\n [contextValue]: T;\n};\n\nexport const createContext = <T>(defaultValue: T): Context<T> => {\n return {\n [contextValue]: defaultValue,\n };\n};\n\nexport const withContextProvider = <T, TResult>(\n context: Context<T>,\n value: T,\n fn: () => TResult,\n) => {\n const previousValue = context[contextValue];\n context[contextValue] = value;\n try {\n return fn();\n } finally {\n context[contextValue] = previousValue;\n }\n};\n\nexport const tapContext = <T>(context: Context<T>) => {\n return context[contextValue];\n};\n"],"mappings":";AAAA,IAAM,eAA8B,uBAAO,aAAa;AAKjD,IAAM,gBAAgB,CAAI,iBAAgC;AAC/D,SAAO;AAAA,IACL,CAAC,YAAY,GAAG;AAAA,EAClB;AACF;AAEO,IAAM,sBAAsB,CACjC,SACA,OACA,OACG;AACH,QAAM,gBAAgB,QAAQ,YAAY;AAC1C,UAAQ,YAAY,IAAI;AACxB,MAAI;AACF,WAAO,GAAG;AAAA,EACZ,UAAE;AACA,YAAQ,YAAY,IAAI;AAAA,EAC1B;AACF;AAEO,IAAM,aAAa,CAAI,YAAwB;AACpD,SAAO,QAAQ,YAAY;AAC7B;","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
|
-
|
|
8
|
-
|
|
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>,
|
|
13
|
-
|
|
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/
|
|
1
|
+
// src/core/createResource.ts
|
|
2
2
|
import {
|
|
3
3
|
createResourceFiber,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
render: (element) => {
|
|
35
|
+
state.element = element;
|
|
36
|
+
setElement(element);
|
|
37
|
+
state.onRender();
|
|
42
38
|
},
|
|
43
|
-
|
|
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,
|
|
52
|
-
let isMounted =
|
|
46
|
+
var createResource = (element, { mount = true } = {}) => {
|
|
47
|
+
let isMounted = mount;
|
|
48
|
+
let render;
|
|
53
49
|
const props = {
|
|
54
50
|
element,
|
|
55
|
-
|
|
51
|
+
onRender: () => {
|
|
56
52
|
if (isMounted) return;
|
|
57
53
|
isMounted = true;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
if (scheduler.isDirty) return;
|
|
55
|
+
flushSync(() => {
|
|
56
|
+
commitResourceFiber(fiber, render);
|
|
57
|
+
});
|
|
62
58
|
},
|
|
63
|
-
|
|
59
|
+
onUnmount: () => {
|
|
60
|
+
if (!isMounted) return;
|
|
61
|
+
isMounted = false;
|
|
62
|
+
unmountResourceFiber(fiber);
|
|
63
|
+
}
|
|
64
64
|
};
|
|
65
65
|
const scheduler = new UpdateScheduler(() => {
|
|
66
|
-
|
|
67
|
-
if (isMounted)
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
flushSync(() => {
|
|
75
|
+
scheduler.markDirty();
|
|
76
|
+
});
|
|
77
|
+
return render.state;
|
|
76
78
|
};
|
|
77
79
|
export {
|
|
78
80
|
createResource
|
|
79
81
|
};
|
|
80
|
-
//# sourceMappingURL=
|
|
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":[]}
|
package/dist/core/resource.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function resource<R>(fn: () => R):
|
|
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,
|
|
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"}
|
package/dist/core/resource.js
CHANGED
|
@@ -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
|
|
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 {
|
|
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":[]}
|
package/dist/core/scheduler.d.ts
CHANGED
|
@@ -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
|
-
|
|
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;
|
|
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"}
|