fict 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-5AWNWCDT.js → chunk-T5BGEI6S.js} +15 -2
- package/dist/chunk-T5BGEI6S.js.map +1 -0
- package/dist/index.cjs +13 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plus.cjs +60 -12
- package/dist/plus.cjs.map +1 -1
- package/dist/plus.d.cts +213 -13
- package/dist/plus.d.ts +213 -13
- package/dist/plus.js +48 -13
- package/dist/plus.js.map +1 -1
- package/dist/slim.cjs +14 -0
- package/dist/slim.cjs.map +1 -0
- package/dist/slim.d.cts +21 -0
- package/dist/slim.d.ts +21 -0
- package/dist/slim.js +11 -0
- package/dist/slim.js.map +1 -0
- package/dist/store-UpSYx4_N.d.cts +44 -0
- package/dist/store-UpSYx4_N.d.ts +44 -0
- package/package.json +8 -3
- package/src/lazy.ts +140 -15
- package/src/resource.ts +180 -8
- package/src/slim.ts +25 -0
- package/src/store.ts +79 -4
- package/dist/chunk-5AWNWCDT.js.map +0 -1
- package/dist/store-BmwKJIj6.d.cts +0 -3
- package/dist/store-BmwKJIj6.d.ts +0 -3
package/dist/plus.d.cts
CHANGED
|
@@ -1,48 +1,248 @@
|
|
|
1
|
-
export { $ as $store } from './store-
|
|
1
|
+
export { $ as $store } from './store-UpSYx4_N.cjs';
|
|
2
2
|
import { Component } from '@fictjs/runtime';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview Async data fetching with caching and Suspense support.
|
|
6
|
+
*
|
|
7
|
+
* The `resource` function creates a reactive data fetcher that:
|
|
8
|
+
* - Automatically cancels in-flight requests when args change
|
|
9
|
+
* - Supports Suspense for loading states
|
|
10
|
+
* - Provides caching with TTL and stale-while-revalidate
|
|
11
|
+
* - Handles errors gracefully
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The result of reading a resource.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam T - The type of data returned by the fetcher
|
|
17
|
+
*/
|
|
4
18
|
interface ResourceResult<T> {
|
|
5
|
-
data
|
|
6
|
-
|
|
7
|
-
|
|
19
|
+
/** The fetched data, or undefined if not yet loaded or on error */
|
|
20
|
+
readonly data: T | undefined;
|
|
21
|
+
/** Whether the resource is currently loading (initial fetch or refetch) */
|
|
22
|
+
readonly loading: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Any error that occurred during fetching.
|
|
25
|
+
* Type is unknown since errors can be any value in JavaScript.
|
|
26
|
+
*/
|
|
27
|
+
readonly error: unknown;
|
|
28
|
+
/** Manually trigger a refetch of the resource */
|
|
8
29
|
refresh: () => void;
|
|
9
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Cache configuration options for a resource.
|
|
33
|
+
*/
|
|
10
34
|
interface ResourceCacheOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Caching mode:
|
|
37
|
+
* - `'memory'`: Cache responses in memory (default)
|
|
38
|
+
* - `'none'`: No caching, always refetch
|
|
39
|
+
* @default 'memory'
|
|
40
|
+
*/
|
|
11
41
|
mode?: 'memory' | 'none';
|
|
42
|
+
/**
|
|
43
|
+
* Time-to-live in milliseconds before cached data is considered stale.
|
|
44
|
+
* @default Infinity
|
|
45
|
+
*/
|
|
12
46
|
ttlMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* If true, return stale cached data immediately while refetching in background.
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
13
51
|
staleWhileRevalidate?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* If true, cache error responses as well.
|
|
54
|
+
* @default false
|
|
55
|
+
*/
|
|
14
56
|
cacheErrors?: boolean;
|
|
15
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Configuration options for creating a resource.
|
|
60
|
+
*
|
|
61
|
+
* @typeParam T - The type of data returned by the fetcher
|
|
62
|
+
* @typeParam Args - The type of arguments passed to the fetcher
|
|
63
|
+
*/
|
|
16
64
|
interface ResourceOptions<T, Args> {
|
|
17
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Custom cache key. Can be a static value or a function that computes
|
|
67
|
+
* the key from the args. If not provided, args are used as the key.
|
|
68
|
+
*/
|
|
69
|
+
key?: unknown | ((args: Args) => unknown);
|
|
70
|
+
/**
|
|
71
|
+
* The fetcher function that performs the async data retrieval.
|
|
72
|
+
* Receives an AbortController signal for cancellation support.
|
|
73
|
+
*/
|
|
18
74
|
fetch: (ctx: {
|
|
19
75
|
signal: AbortSignal;
|
|
20
76
|
}, args: Args) => Promise<T>;
|
|
77
|
+
/**
|
|
78
|
+
* If true, the resource will throw a Suspense token while loading,
|
|
79
|
+
* enabling React-like Suspense boundaries.
|
|
80
|
+
* @default false
|
|
81
|
+
*/
|
|
21
82
|
suspense?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Cache configuration options.
|
|
85
|
+
*/
|
|
22
86
|
cache?: ResourceCacheOptions;
|
|
87
|
+
/**
|
|
88
|
+
* A value or reactive getter that, when changed, resets the resource.
|
|
89
|
+
* Useful for clearing cache when certain conditions change.
|
|
90
|
+
*/
|
|
23
91
|
reset?: unknown | (() => unknown);
|
|
24
92
|
}
|
|
25
93
|
/**
|
|
26
|
-
*
|
|
94
|
+
* Return type of the resource factory.
|
|
27
95
|
*
|
|
28
|
-
* @
|
|
96
|
+
* @typeParam T - The type of data returned by the fetcher
|
|
97
|
+
* @typeParam Args - The type of arguments passed to the fetcher
|
|
98
|
+
*/
|
|
99
|
+
interface Resource<T, Args> {
|
|
100
|
+
/**
|
|
101
|
+
* Read the resource data, triggering a fetch if needed.
|
|
102
|
+
* Can accept static args or a reactive getter.
|
|
103
|
+
*
|
|
104
|
+
* @param argsAccessor - Arguments or a getter returning arguments
|
|
105
|
+
*/
|
|
106
|
+
read(argsAccessor: (() => Args) | Args): ResourceResult<T>;
|
|
107
|
+
/**
|
|
108
|
+
* Invalidate cached data, causing the next read to refetch.
|
|
109
|
+
*
|
|
110
|
+
* @param key - Optional specific key to invalidate. If omitted, invalidates all.
|
|
111
|
+
*/
|
|
112
|
+
invalidate(key?: unknown): void;
|
|
113
|
+
/**
|
|
114
|
+
* Prefetch data without reading it. Useful for eager loading.
|
|
115
|
+
*
|
|
116
|
+
* @param args - Arguments to pass to the fetcher
|
|
117
|
+
* @param keyOverride - Optional cache key override
|
|
118
|
+
*/
|
|
119
|
+
prefetch(args: Args, keyOverride?: unknown): void;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create a reactive async data resource.
|
|
123
|
+
*
|
|
124
|
+
* Resources handle async data fetching with automatic caching, cancellation,
|
|
125
|
+
* and optional Suspense integration.
|
|
126
|
+
*
|
|
127
|
+
* @param optionsOrFetcher - A fetcher function or full configuration object
|
|
128
|
+
* @returns A resource factory with read, invalidate, and prefetch methods
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```tsx
|
|
132
|
+
* import { resource } from 'fict'
|
|
133
|
+
*
|
|
134
|
+
* // Simple fetcher
|
|
135
|
+
* const userResource = resource(
|
|
136
|
+
* ({ signal }, userId: string) =>
|
|
137
|
+
* fetch(`/api/users/${userId}`, { signal }).then(r => r.json())
|
|
138
|
+
* )
|
|
139
|
+
*
|
|
140
|
+
* // With full options
|
|
141
|
+
* const postsResource = resource({
|
|
142
|
+
* fetch: ({ signal }, userId: string) =>
|
|
143
|
+
* fetch(`/api/users/${userId}/posts`, { signal }).then(r => r.json()),
|
|
144
|
+
* suspense: true,
|
|
145
|
+
* cache: {
|
|
146
|
+
* ttlMs: 60_000,
|
|
147
|
+
* staleWhileRevalidate: true,
|
|
148
|
+
* },
|
|
149
|
+
* })
|
|
150
|
+
*
|
|
151
|
+
* // Usage in component
|
|
152
|
+
* function UserProfile({ userId }: { userId: string }) {
|
|
153
|
+
* const { data, loading, error, refresh } = userResource.read(() => userId)
|
|
154
|
+
*
|
|
155
|
+
* if (loading) return <Spinner />
|
|
156
|
+
* if (error) return <ErrorMessage error={error} />
|
|
157
|
+
* return <div>{data.name}</div>
|
|
158
|
+
* }
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @public
|
|
29
162
|
*/
|
|
30
163
|
declare function resource<T, Args = void>(optionsOrFetcher: ((ctx: {
|
|
31
164
|
signal: AbortSignal;
|
|
32
|
-
}, args: Args) => Promise<T>) | ResourceOptions<T, Args>):
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
165
|
+
}, args: Args) => Promise<T>) | ResourceOptions<T, Args>): Resource<T, Args>;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @fileoverview Lazy component loading with Suspense support.
|
|
169
|
+
*
|
|
170
|
+
* Creates a component that loads its implementation asynchronously,
|
|
171
|
+
* suspending rendering until the module is loaded.
|
|
172
|
+
*/
|
|
37
173
|
|
|
174
|
+
/** Module shape expected from dynamic imports */
|
|
38
175
|
interface LazyModule<TProps extends Record<string, unknown>> {
|
|
39
176
|
default: Component<TProps>;
|
|
40
177
|
}
|
|
178
|
+
/** Options for lazy loading behavior */
|
|
179
|
+
interface LazyOptions {
|
|
180
|
+
/**
|
|
181
|
+
* Maximum number of retry attempts on load failure.
|
|
182
|
+
* Set to 0 to disable retries (default behavior).
|
|
183
|
+
* @default 0
|
|
184
|
+
*/
|
|
185
|
+
maxRetries?: number;
|
|
186
|
+
/**
|
|
187
|
+
* Delay in milliseconds between retry attempts.
|
|
188
|
+
* Uses exponential backoff: delay * 2^(attempt - 1)
|
|
189
|
+
* @default 1000
|
|
190
|
+
*/
|
|
191
|
+
retryDelay?: number;
|
|
192
|
+
}
|
|
193
|
+
/** Extended component with retry capability */
|
|
194
|
+
interface LazyComponent<TProps extends Record<string, unknown>> extends Component<TProps> {
|
|
195
|
+
/**
|
|
196
|
+
* Reset the lazy component state, allowing it to retry loading.
|
|
197
|
+
* Useful when used with ErrorBoundary reset functionality.
|
|
198
|
+
*/
|
|
199
|
+
reset: () => void;
|
|
200
|
+
/**
|
|
201
|
+
* Preload the component without rendering it.
|
|
202
|
+
* Returns a promise that resolves when the component is loaded.
|
|
203
|
+
*/
|
|
204
|
+
preload: () => Promise<void>;
|
|
205
|
+
}
|
|
41
206
|
/**
|
|
42
207
|
* Create a lazy component that suspends while loading.
|
|
208
|
+
*
|
|
209
|
+
* @param loader - Function that returns a promise resolving to the component module
|
|
210
|
+
* @param options - Optional configuration for retry behavior
|
|
211
|
+
* @returns A component that suspends during loading and supports retry on failure
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```tsx
|
|
215
|
+
* import { lazy, Suspense } from 'fict'
|
|
216
|
+
*
|
|
217
|
+
* // Basic usage
|
|
218
|
+
* const LazyChart = lazy(() => import('./Chart'))
|
|
219
|
+
*
|
|
220
|
+
* // With retry options
|
|
221
|
+
* const LazyDashboard = lazy(() => import('./Dashboard'), {
|
|
222
|
+
* maxRetries: 3,
|
|
223
|
+
* retryDelay: 1000
|
|
224
|
+
* })
|
|
225
|
+
*
|
|
226
|
+
* function App() {
|
|
227
|
+
* return (
|
|
228
|
+
* <Suspense fallback={<Loading />}>
|
|
229
|
+
* <LazyChart />
|
|
230
|
+
* </Suspense>
|
|
231
|
+
* )
|
|
232
|
+
* }
|
|
233
|
+
*
|
|
234
|
+
* // Reset on error (with ErrorBoundary)
|
|
235
|
+
* <ErrorBoundary fallback={(err, reset) => (
|
|
236
|
+
* <button onClick={() => { LazyChart.reset(); reset(); }}>Retry</button>
|
|
237
|
+
* )}>
|
|
238
|
+
* <LazyChart />
|
|
239
|
+
* </ErrorBoundary>
|
|
240
|
+
* ```
|
|
241
|
+
*
|
|
242
|
+
* @public
|
|
43
243
|
*/
|
|
44
244
|
declare function lazy<TProps extends Record<string, unknown> = Record<string, unknown>>(loader: () => Promise<LazyModule<TProps> | {
|
|
45
245
|
default: Component<TProps>;
|
|
46
|
-
}
|
|
246
|
+
}>, options?: LazyOptions): LazyComponent<TProps>;
|
|
47
247
|
|
|
48
248
|
export { type LazyModule, type ResourceCacheOptions, type ResourceOptions, type ResourceResult, lazy, resource };
|
package/dist/plus.d.ts
CHANGED
|
@@ -1,48 +1,248 @@
|
|
|
1
|
-
export { $ as $store } from './store-
|
|
1
|
+
export { $ as $store } from './store-UpSYx4_N.js';
|
|
2
2
|
import { Component } from '@fictjs/runtime';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview Async data fetching with caching and Suspense support.
|
|
6
|
+
*
|
|
7
|
+
* The `resource` function creates a reactive data fetcher that:
|
|
8
|
+
* - Automatically cancels in-flight requests when args change
|
|
9
|
+
* - Supports Suspense for loading states
|
|
10
|
+
* - Provides caching with TTL and stale-while-revalidate
|
|
11
|
+
* - Handles errors gracefully
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The result of reading a resource.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam T - The type of data returned by the fetcher
|
|
17
|
+
*/
|
|
4
18
|
interface ResourceResult<T> {
|
|
5
|
-
data
|
|
6
|
-
|
|
7
|
-
|
|
19
|
+
/** The fetched data, or undefined if not yet loaded or on error */
|
|
20
|
+
readonly data: T | undefined;
|
|
21
|
+
/** Whether the resource is currently loading (initial fetch or refetch) */
|
|
22
|
+
readonly loading: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Any error that occurred during fetching.
|
|
25
|
+
* Type is unknown since errors can be any value in JavaScript.
|
|
26
|
+
*/
|
|
27
|
+
readonly error: unknown;
|
|
28
|
+
/** Manually trigger a refetch of the resource */
|
|
8
29
|
refresh: () => void;
|
|
9
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Cache configuration options for a resource.
|
|
33
|
+
*/
|
|
10
34
|
interface ResourceCacheOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Caching mode:
|
|
37
|
+
* - `'memory'`: Cache responses in memory (default)
|
|
38
|
+
* - `'none'`: No caching, always refetch
|
|
39
|
+
* @default 'memory'
|
|
40
|
+
*/
|
|
11
41
|
mode?: 'memory' | 'none';
|
|
42
|
+
/**
|
|
43
|
+
* Time-to-live in milliseconds before cached data is considered stale.
|
|
44
|
+
* @default Infinity
|
|
45
|
+
*/
|
|
12
46
|
ttlMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* If true, return stale cached data immediately while refetching in background.
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
13
51
|
staleWhileRevalidate?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* If true, cache error responses as well.
|
|
54
|
+
* @default false
|
|
55
|
+
*/
|
|
14
56
|
cacheErrors?: boolean;
|
|
15
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Configuration options for creating a resource.
|
|
60
|
+
*
|
|
61
|
+
* @typeParam T - The type of data returned by the fetcher
|
|
62
|
+
* @typeParam Args - The type of arguments passed to the fetcher
|
|
63
|
+
*/
|
|
16
64
|
interface ResourceOptions<T, Args> {
|
|
17
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Custom cache key. Can be a static value or a function that computes
|
|
67
|
+
* the key from the args. If not provided, args are used as the key.
|
|
68
|
+
*/
|
|
69
|
+
key?: unknown | ((args: Args) => unknown);
|
|
70
|
+
/**
|
|
71
|
+
* The fetcher function that performs the async data retrieval.
|
|
72
|
+
* Receives an AbortController signal for cancellation support.
|
|
73
|
+
*/
|
|
18
74
|
fetch: (ctx: {
|
|
19
75
|
signal: AbortSignal;
|
|
20
76
|
}, args: Args) => Promise<T>;
|
|
77
|
+
/**
|
|
78
|
+
* If true, the resource will throw a Suspense token while loading,
|
|
79
|
+
* enabling React-like Suspense boundaries.
|
|
80
|
+
* @default false
|
|
81
|
+
*/
|
|
21
82
|
suspense?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Cache configuration options.
|
|
85
|
+
*/
|
|
22
86
|
cache?: ResourceCacheOptions;
|
|
87
|
+
/**
|
|
88
|
+
* A value or reactive getter that, when changed, resets the resource.
|
|
89
|
+
* Useful for clearing cache when certain conditions change.
|
|
90
|
+
*/
|
|
23
91
|
reset?: unknown | (() => unknown);
|
|
24
92
|
}
|
|
25
93
|
/**
|
|
26
|
-
*
|
|
94
|
+
* Return type of the resource factory.
|
|
27
95
|
*
|
|
28
|
-
* @
|
|
96
|
+
* @typeParam T - The type of data returned by the fetcher
|
|
97
|
+
* @typeParam Args - The type of arguments passed to the fetcher
|
|
98
|
+
*/
|
|
99
|
+
interface Resource<T, Args> {
|
|
100
|
+
/**
|
|
101
|
+
* Read the resource data, triggering a fetch if needed.
|
|
102
|
+
* Can accept static args or a reactive getter.
|
|
103
|
+
*
|
|
104
|
+
* @param argsAccessor - Arguments or a getter returning arguments
|
|
105
|
+
*/
|
|
106
|
+
read(argsAccessor: (() => Args) | Args): ResourceResult<T>;
|
|
107
|
+
/**
|
|
108
|
+
* Invalidate cached data, causing the next read to refetch.
|
|
109
|
+
*
|
|
110
|
+
* @param key - Optional specific key to invalidate. If omitted, invalidates all.
|
|
111
|
+
*/
|
|
112
|
+
invalidate(key?: unknown): void;
|
|
113
|
+
/**
|
|
114
|
+
* Prefetch data without reading it. Useful for eager loading.
|
|
115
|
+
*
|
|
116
|
+
* @param args - Arguments to pass to the fetcher
|
|
117
|
+
* @param keyOverride - Optional cache key override
|
|
118
|
+
*/
|
|
119
|
+
prefetch(args: Args, keyOverride?: unknown): void;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create a reactive async data resource.
|
|
123
|
+
*
|
|
124
|
+
* Resources handle async data fetching with automatic caching, cancellation,
|
|
125
|
+
* and optional Suspense integration.
|
|
126
|
+
*
|
|
127
|
+
* @param optionsOrFetcher - A fetcher function or full configuration object
|
|
128
|
+
* @returns A resource factory with read, invalidate, and prefetch methods
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```tsx
|
|
132
|
+
* import { resource } from 'fict'
|
|
133
|
+
*
|
|
134
|
+
* // Simple fetcher
|
|
135
|
+
* const userResource = resource(
|
|
136
|
+
* ({ signal }, userId: string) =>
|
|
137
|
+
* fetch(`/api/users/${userId}`, { signal }).then(r => r.json())
|
|
138
|
+
* )
|
|
139
|
+
*
|
|
140
|
+
* // With full options
|
|
141
|
+
* const postsResource = resource({
|
|
142
|
+
* fetch: ({ signal }, userId: string) =>
|
|
143
|
+
* fetch(`/api/users/${userId}/posts`, { signal }).then(r => r.json()),
|
|
144
|
+
* suspense: true,
|
|
145
|
+
* cache: {
|
|
146
|
+
* ttlMs: 60_000,
|
|
147
|
+
* staleWhileRevalidate: true,
|
|
148
|
+
* },
|
|
149
|
+
* })
|
|
150
|
+
*
|
|
151
|
+
* // Usage in component
|
|
152
|
+
* function UserProfile({ userId }: { userId: string }) {
|
|
153
|
+
* const { data, loading, error, refresh } = userResource.read(() => userId)
|
|
154
|
+
*
|
|
155
|
+
* if (loading) return <Spinner />
|
|
156
|
+
* if (error) return <ErrorMessage error={error} />
|
|
157
|
+
* return <div>{data.name}</div>
|
|
158
|
+
* }
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @public
|
|
29
162
|
*/
|
|
30
163
|
declare function resource<T, Args = void>(optionsOrFetcher: ((ctx: {
|
|
31
164
|
signal: AbortSignal;
|
|
32
|
-
}, args: Args) => Promise<T>) | ResourceOptions<T, Args>):
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
165
|
+
}, args: Args) => Promise<T>) | ResourceOptions<T, Args>): Resource<T, Args>;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @fileoverview Lazy component loading with Suspense support.
|
|
169
|
+
*
|
|
170
|
+
* Creates a component that loads its implementation asynchronously,
|
|
171
|
+
* suspending rendering until the module is loaded.
|
|
172
|
+
*/
|
|
37
173
|
|
|
174
|
+
/** Module shape expected from dynamic imports */
|
|
38
175
|
interface LazyModule<TProps extends Record<string, unknown>> {
|
|
39
176
|
default: Component<TProps>;
|
|
40
177
|
}
|
|
178
|
+
/** Options for lazy loading behavior */
|
|
179
|
+
interface LazyOptions {
|
|
180
|
+
/**
|
|
181
|
+
* Maximum number of retry attempts on load failure.
|
|
182
|
+
* Set to 0 to disable retries (default behavior).
|
|
183
|
+
* @default 0
|
|
184
|
+
*/
|
|
185
|
+
maxRetries?: number;
|
|
186
|
+
/**
|
|
187
|
+
* Delay in milliseconds between retry attempts.
|
|
188
|
+
* Uses exponential backoff: delay * 2^(attempt - 1)
|
|
189
|
+
* @default 1000
|
|
190
|
+
*/
|
|
191
|
+
retryDelay?: number;
|
|
192
|
+
}
|
|
193
|
+
/** Extended component with retry capability */
|
|
194
|
+
interface LazyComponent<TProps extends Record<string, unknown>> extends Component<TProps> {
|
|
195
|
+
/**
|
|
196
|
+
* Reset the lazy component state, allowing it to retry loading.
|
|
197
|
+
* Useful when used with ErrorBoundary reset functionality.
|
|
198
|
+
*/
|
|
199
|
+
reset: () => void;
|
|
200
|
+
/**
|
|
201
|
+
* Preload the component without rendering it.
|
|
202
|
+
* Returns a promise that resolves when the component is loaded.
|
|
203
|
+
*/
|
|
204
|
+
preload: () => Promise<void>;
|
|
205
|
+
}
|
|
41
206
|
/**
|
|
42
207
|
* Create a lazy component that suspends while loading.
|
|
208
|
+
*
|
|
209
|
+
* @param loader - Function that returns a promise resolving to the component module
|
|
210
|
+
* @param options - Optional configuration for retry behavior
|
|
211
|
+
* @returns A component that suspends during loading and supports retry on failure
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```tsx
|
|
215
|
+
* import { lazy, Suspense } from 'fict'
|
|
216
|
+
*
|
|
217
|
+
* // Basic usage
|
|
218
|
+
* const LazyChart = lazy(() => import('./Chart'))
|
|
219
|
+
*
|
|
220
|
+
* // With retry options
|
|
221
|
+
* const LazyDashboard = lazy(() => import('./Dashboard'), {
|
|
222
|
+
* maxRetries: 3,
|
|
223
|
+
* retryDelay: 1000
|
|
224
|
+
* })
|
|
225
|
+
*
|
|
226
|
+
* function App() {
|
|
227
|
+
* return (
|
|
228
|
+
* <Suspense fallback={<Loading />}>
|
|
229
|
+
* <LazyChart />
|
|
230
|
+
* </Suspense>
|
|
231
|
+
* )
|
|
232
|
+
* }
|
|
233
|
+
*
|
|
234
|
+
* // Reset on error (with ErrorBoundary)
|
|
235
|
+
* <ErrorBoundary fallback={(err, reset) => (
|
|
236
|
+
* <button onClick={() => { LazyChart.reset(); reset(); }}>Retry</button>
|
|
237
|
+
* )}>
|
|
238
|
+
* <LazyChart />
|
|
239
|
+
* </ErrorBoundary>
|
|
240
|
+
* ```
|
|
241
|
+
*
|
|
242
|
+
* @public
|
|
43
243
|
*/
|
|
44
244
|
declare function lazy<TProps extends Record<string, unknown> = Record<string, unknown>>(loader: () => Promise<LazyModule<TProps> | {
|
|
45
245
|
default: Component<TProps>;
|
|
46
|
-
}
|
|
246
|
+
}>, options?: LazyOptions): LazyComponent<TProps>;
|
|
47
247
|
|
|
48
248
|
export { type LazyModule, type ResourceCacheOptions, type ResourceOptions, type ResourceResult, lazy, resource };
|
package/dist/plus.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { $store } from './chunk-
|
|
1
|
+
export { $store } from './chunk-T5BGEI6S.js';
|
|
2
2
|
import { createEffect, createSuspenseToken, onCleanup } from '@fictjs/runtime';
|
|
3
3
|
import { createSignal } from '@fictjs/runtime/advanced';
|
|
4
4
|
|
|
@@ -207,12 +207,38 @@ function resource(optionsOrFetcher) {
|
|
|
207
207
|
prefetch
|
|
208
208
|
};
|
|
209
209
|
}
|
|
210
|
-
function lazy(loader) {
|
|
210
|
+
function lazy(loader, options = {}) {
|
|
211
|
+
const { maxRetries = 0, retryDelay = 1e3 } = options;
|
|
211
212
|
let loaded = null;
|
|
212
213
|
let loadError = null;
|
|
213
214
|
let loadingPromise = null;
|
|
214
215
|
let pendingToken = null;
|
|
215
|
-
|
|
216
|
+
let retryCount = 0;
|
|
217
|
+
const attemptLoad = () => {
|
|
218
|
+
return loader().then((mod) => {
|
|
219
|
+
loaded = mod.default;
|
|
220
|
+
loadError = null;
|
|
221
|
+
retryCount = 0;
|
|
222
|
+
pendingToken?.resolve();
|
|
223
|
+
}).catch((err) => {
|
|
224
|
+
if (retryCount < maxRetries) {
|
|
225
|
+
retryCount++;
|
|
226
|
+
const delay = retryDelay * Math.pow(2, retryCount - 1);
|
|
227
|
+
return new Promise((resolve) => {
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
resolve(attemptLoad());
|
|
230
|
+
}, delay);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
loadError = err;
|
|
234
|
+
pendingToken?.reject(err);
|
|
235
|
+
return void 0;
|
|
236
|
+
}).finally(() => {
|
|
237
|
+
loadingPromise = null;
|
|
238
|
+
pendingToken = null;
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
const component = ((props) => {
|
|
216
242
|
if (loaded) {
|
|
217
243
|
return loaded(props);
|
|
218
244
|
}
|
|
@@ -221,22 +247,31 @@ function lazy(loader) {
|
|
|
221
247
|
}
|
|
222
248
|
if (!loadingPromise) {
|
|
223
249
|
pendingToken = createSuspenseToken();
|
|
224
|
-
loadingPromise =
|
|
225
|
-
loaded = mod.default;
|
|
226
|
-
pendingToken?.resolve();
|
|
227
|
-
}).catch((err) => {
|
|
228
|
-
loadError = err;
|
|
229
|
-
pendingToken?.reject(err);
|
|
230
|
-
}).finally(() => {
|
|
231
|
-
loadingPromise = null;
|
|
232
|
-
pendingToken = null;
|
|
233
|
-
});
|
|
250
|
+
loadingPromise = attemptLoad();
|
|
234
251
|
}
|
|
235
252
|
if (pendingToken) {
|
|
236
253
|
throw pendingToken.token;
|
|
237
254
|
}
|
|
238
255
|
throw new Error("Lazy component failed to start loading");
|
|
256
|
+
});
|
|
257
|
+
component.reset = () => {
|
|
258
|
+
loadError = null;
|
|
259
|
+
loadingPromise = null;
|
|
260
|
+
pendingToken = null;
|
|
261
|
+
retryCount = 0;
|
|
262
|
+
};
|
|
263
|
+
component.preload = () => {
|
|
264
|
+
if (loaded) {
|
|
265
|
+
return Promise.resolve();
|
|
266
|
+
}
|
|
267
|
+
if (loadingPromise) {
|
|
268
|
+
return loadingPromise;
|
|
269
|
+
}
|
|
270
|
+
pendingToken = createSuspenseToken();
|
|
271
|
+
loadingPromise = attemptLoad();
|
|
272
|
+
return loadingPromise;
|
|
239
273
|
};
|
|
274
|
+
return component;
|
|
240
275
|
}
|
|
241
276
|
|
|
242
277
|
export { lazy, resource };
|