@volynets/reflex 0.1.0 → 0.1.2
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 +25 -25
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/unstable/index.cjs +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/unstable/index.js +1 -1
- package/dist/globals.d.ts +333 -32
- package/dist/unstable/index.d.ts +195 -0
- package/package.json +10 -1
- package/src/api/derived.ts +90 -0
- package/src/api/effect.ts +120 -0
- package/src/api/event.ts +257 -0
- package/src/api/index.ts +4 -0
- package/src/api/signal.ts +68 -0
- package/src/globals.d.ts +169 -0
- package/src/index.ts +6 -0
- package/src/infra/event.ts +182 -0
- package/src/infra/factory.ts +46 -0
- package/src/infra/index.ts +2 -0
- package/src/infra/runtime.ts +189 -0
- package/src/policy/SCHEDULER_SEMANTICS.md +389 -0
- package/src/policy/event_dispatcher.ts +39 -0
- package/src/policy/index.ts +1 -0
- package/src/policy/scheduler/index.ts +6 -0
- package/src/policy/scheduler/scheduler.constants.ts +17 -0
- package/src/policy/scheduler/scheduler.core.ts +165 -0
- package/src/policy/scheduler/scheduler.infra.ts +37 -0
- package/src/policy/scheduler/scheduler.queue.ts +74 -0
- package/src/policy/scheduler/scheduler.types.ts +54 -0
- package/src/policy/scheduler/variants/index.ts +3 -0
- package/src/policy/scheduler/variants/scheduler.eager.ts +46 -0
- package/src/policy/scheduler/variants/scheduler.flush.ts +35 -0
- package/src/policy/scheduler/variants/scheduler.sab.ts +37 -0
- package/src/unstable/index.ts +4 -0
- package/src/unstable/resource.ts +505 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import type { ReactiveNode } from "@reflex/runtime";
|
|
2
|
+
import {
|
|
3
|
+
disposeNode,
|
|
4
|
+
disposeWatcher,
|
|
5
|
+
getDefaultContext,
|
|
6
|
+
readProducer,
|
|
7
|
+
runWatcher,
|
|
8
|
+
writeProducer,
|
|
9
|
+
} from "@reflex/runtime";
|
|
10
|
+
import { createResourceStateNode, createWatcherNode } from "../infra";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Current lifecycle state of a resource request.
|
|
14
|
+
*
|
|
15
|
+
* - `"idle"` - no active request is in flight.
|
|
16
|
+
* - `"pending"` - the current request is still running.
|
|
17
|
+
* - `"resolved"` - the latest current request completed successfully.
|
|
18
|
+
* - `"rejected"` - the latest current request failed.
|
|
19
|
+
*/
|
|
20
|
+
export type ResourceStatus = "idle" | "pending" | "resolved" | "rejected";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Guard object bound to a single resource request token.
|
|
24
|
+
*
|
|
25
|
+
* Loaders receive a guard so they can tell whether their work is still current
|
|
26
|
+
* before committing side effects or returning follow-up work.
|
|
27
|
+
*/
|
|
28
|
+
export interface ResourceGuard {
|
|
29
|
+
/**
|
|
30
|
+
* Monotonic token identifying the request that produced this guard.
|
|
31
|
+
*/
|
|
32
|
+
readonly token: number;
|
|
33
|
+
/**
|
|
34
|
+
* Returns `true` while this request is still current and the resource has not
|
|
35
|
+
* been disposed.
|
|
36
|
+
*/
|
|
37
|
+
alive(): boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Mutable handle for manually settling a resource request.
|
|
42
|
+
*
|
|
43
|
+
* Manual resources expose this handle from `start()`. It extends
|
|
44
|
+
* `ResourceGuard` with methods that attempt to resolve or reject the current
|
|
45
|
+
* request.
|
|
46
|
+
*
|
|
47
|
+
* @typeParam T - Resolved value type.
|
|
48
|
+
* @typeParam E - Rejection type.
|
|
49
|
+
*/
|
|
50
|
+
export interface ResourceHandle<T, E = unknown> extends ResourceGuard {
|
|
51
|
+
/**
|
|
52
|
+
* Resolves the request with `value`.
|
|
53
|
+
*
|
|
54
|
+
* @param value - Value to commit as the latest successful result.
|
|
55
|
+
*
|
|
56
|
+
* @returns `true` if the value was accepted, or `false` if this handle is
|
|
57
|
+
* stale or the resource has already been disposed.
|
|
58
|
+
*/
|
|
59
|
+
resolve(value: T): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Rejects the request with `error`.
|
|
62
|
+
*
|
|
63
|
+
* @param error - Error to commit as the latest failure.
|
|
64
|
+
*
|
|
65
|
+
* @returns `true` if the error was accepted, or `false` if this handle is
|
|
66
|
+
* stale or the resource has already been disposed.
|
|
67
|
+
*/
|
|
68
|
+
reject(error: E): boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Reactive view of a resource request lifecycle.
|
|
73
|
+
*
|
|
74
|
+
* A resource exposes tracked accessors for its status, latest value, latest
|
|
75
|
+
* error, and current request token, along with imperative controls to reset or
|
|
76
|
+
* dispose the resource.
|
|
77
|
+
*
|
|
78
|
+
* @typeParam T - Resolved value type.
|
|
79
|
+
* @typeParam E - Rejection type.
|
|
80
|
+
*/
|
|
81
|
+
export interface Resource<T, E = unknown> {
|
|
82
|
+
/**
|
|
83
|
+
* Tracked accessor that returns the current request lifecycle state.
|
|
84
|
+
*/
|
|
85
|
+
readonly status: Accessor<ResourceStatus>;
|
|
86
|
+
/**
|
|
87
|
+
* Tracked accessor that returns the latest successfully resolved value, if
|
|
88
|
+
* one exists.
|
|
89
|
+
*
|
|
90
|
+
* The last resolved value is retained while a newer request is pending or
|
|
91
|
+
* rejected.
|
|
92
|
+
*/
|
|
93
|
+
readonly value: Accessor<T | undefined>;
|
|
94
|
+
/**
|
|
95
|
+
* Tracked accessor that returns the latest rejected error, if one exists.
|
|
96
|
+
*/
|
|
97
|
+
readonly error: Accessor<E | undefined>;
|
|
98
|
+
/**
|
|
99
|
+
* Tracked accessor that returns the current monotonic request token.
|
|
100
|
+
*
|
|
101
|
+
* The token increments whenever a new request starts, the resource is
|
|
102
|
+
* cleared, or the resource is disposed.
|
|
103
|
+
*/
|
|
104
|
+
readonly token: Accessor<number>;
|
|
105
|
+
/**
|
|
106
|
+
* Resets the resource to the `"idle"` state and invalidates any in-flight
|
|
107
|
+
* request.
|
|
108
|
+
*/
|
|
109
|
+
clear(): void;
|
|
110
|
+
/**
|
|
111
|
+
* Disposes the resource permanently and invalidates any in-flight request.
|
|
112
|
+
*
|
|
113
|
+
* After disposal, the resource stops reacting to future source changes or
|
|
114
|
+
* refetch requests.
|
|
115
|
+
*/
|
|
116
|
+
dispose(): void;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Imperative resource whose requests are started and settled manually.
|
|
121
|
+
*
|
|
122
|
+
* @typeParam T - Resolved value type.
|
|
123
|
+
* @typeParam E - Rejection type.
|
|
124
|
+
*/
|
|
125
|
+
export interface ManualResource<T, E = unknown> extends Resource<T, E> {
|
|
126
|
+
/**
|
|
127
|
+
* Starts a new request and returns a handle that can resolve or reject it.
|
|
128
|
+
*
|
|
129
|
+
* Starting a new request invalidates all older handles.
|
|
130
|
+
*/
|
|
131
|
+
start(): ResourceHandle<T, E>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Auto-loading resource backed by a loader function.
|
|
136
|
+
*
|
|
137
|
+
* @typeParam T - Resolved value type.
|
|
138
|
+
* @typeParam E - Rejection type.
|
|
139
|
+
*/
|
|
140
|
+
export interface AsyncResource<T, E = unknown> extends Resource<T, E> {
|
|
141
|
+
/**
|
|
142
|
+
* Requests another load.
|
|
143
|
+
*
|
|
144
|
+
* In the default runtime strategy, the reload starts when the surrounding
|
|
145
|
+
* runtime flushes scheduled effects.
|
|
146
|
+
*/
|
|
147
|
+
refetch(): void;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Loader used by `resource(load)`.
|
|
152
|
+
*
|
|
153
|
+
* It receives a guard for the current request and may return either a value or
|
|
154
|
+
* a promise-like value.
|
|
155
|
+
*
|
|
156
|
+
* @typeParam T - Resolved value type.
|
|
157
|
+
*/
|
|
158
|
+
export type ResourceJob<T> = (guard: ResourceGuard) => T | PromiseLike<T>;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Loader used by `resource(source, load)`.
|
|
162
|
+
*
|
|
163
|
+
* It receives the latest source value plus a guard for the current request, and
|
|
164
|
+
* may return either a value or a promise-like value.
|
|
165
|
+
*
|
|
166
|
+
* @typeParam S - Source value type.
|
|
167
|
+
* @typeParam T - Resolved value type.
|
|
168
|
+
*/
|
|
169
|
+
export type ResourceLoader<S, T> = (
|
|
170
|
+
source: S,
|
|
171
|
+
guard: ResourceGuard,
|
|
172
|
+
) => T | PromiseLike<T>;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Returns `true` when the resource's current request is pending.
|
|
176
|
+
*
|
|
177
|
+
* @param resource - Resource to inspect.
|
|
178
|
+
*
|
|
179
|
+
* @returns Whether `resource.status()` currently equals `"pending"`.
|
|
180
|
+
*/
|
|
181
|
+
export function isPending(resource: Resource<unknown, unknown>): boolean {
|
|
182
|
+
return "pending" === resource.status();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function isPromiseLike<T>(value: T | PromiseLike<T>): value is PromiseLike<T> {
|
|
186
|
+
return (
|
|
187
|
+
typeof value === "object" &&
|
|
188
|
+
value !== null &&
|
|
189
|
+
"then" in value &&
|
|
190
|
+
typeof value.then === "function"
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
class ResourceRequest<T, E = unknown> implements ResourceHandle<T, E> {
|
|
195
|
+
constructor(
|
|
196
|
+
private readonly owner: ResourceCore<T, E>,
|
|
197
|
+
readonly token: number,
|
|
198
|
+
) {}
|
|
199
|
+
|
|
200
|
+
alive(): boolean {
|
|
201
|
+
return this.owner.isAlive(this.token);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
resolve(value: T): boolean {
|
|
205
|
+
return this.owner.resolve(this.token, value);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
reject(error: E): boolean {
|
|
209
|
+
return this.owner.reject(this.token, error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
class ResourceCore<T, E = unknown> {
|
|
214
|
+
readonly context = getDefaultContext();
|
|
215
|
+
readonly stateNode = createResourceStateNode();
|
|
216
|
+
status: ResourceStatus = "idle";
|
|
217
|
+
value: T | undefined = undefined;
|
|
218
|
+
error: E | undefined = undefined;
|
|
219
|
+
token = 0;
|
|
220
|
+
disposed = false;
|
|
221
|
+
watcher: ReactiveNode | null = null;
|
|
222
|
+
refetchNode: ReactiveNode<number> | null = null;
|
|
223
|
+
|
|
224
|
+
track(): void {
|
|
225
|
+
readProducer(this.stateNode);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
bump(): void {
|
|
229
|
+
writeProducer(this.stateNode, this.stateNode.payload + 1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
isAlive(token: number): boolean {
|
|
233
|
+
return !this.disposed && this.token === token;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
start(): ResourceRequest<T, E> {
|
|
237
|
+
const nextToken = this.disposed ? this.token : this.token + 1;
|
|
238
|
+
|
|
239
|
+
if (!this.disposed) {
|
|
240
|
+
this.token = nextToken;
|
|
241
|
+
this.status = "pending";
|
|
242
|
+
this.error = undefined;
|
|
243
|
+
this.bump();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return new ResourceRequest(this, nextToken);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
clear(): void {
|
|
250
|
+
if (this.disposed) return;
|
|
251
|
+
|
|
252
|
+
this.token += 1;
|
|
253
|
+
this.status = "idle";
|
|
254
|
+
this.value = undefined;
|
|
255
|
+
this.error = undefined;
|
|
256
|
+
this.bump();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
dispose(): void {
|
|
260
|
+
if (this.disposed) return;
|
|
261
|
+
|
|
262
|
+
this.disposed = true;
|
|
263
|
+
this.token += 1;
|
|
264
|
+
this.status = "idle";
|
|
265
|
+
this.value = undefined;
|
|
266
|
+
this.error = undefined;
|
|
267
|
+
this.bump();
|
|
268
|
+
|
|
269
|
+
if (this.watcher !== null) {
|
|
270
|
+
disposeWatcher(this.watcher);
|
|
271
|
+
this.watcher = null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (this.refetchNode !== null) {
|
|
275
|
+
disposeNode(this.refetchNode);
|
|
276
|
+
this.refetchNode = null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
resolve(token: number, value: T): boolean {
|
|
281
|
+
if (!this.isAlive(token)) return false;
|
|
282
|
+
|
|
283
|
+
this.status = "resolved";
|
|
284
|
+
this.value = value;
|
|
285
|
+
this.error = undefined;
|
|
286
|
+
this.bump();
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
reject(token: number, error: E): boolean {
|
|
291
|
+
if (!this.isAlive(token)) return false;
|
|
292
|
+
|
|
293
|
+
this.status = "rejected";
|
|
294
|
+
this.error = error;
|
|
295
|
+
this.bump();
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
settle(result: T | PromiseLike<T>, request: ResourceRequest<T, E>): void {
|
|
300
|
+
if (!isPromiseLike(result)) {
|
|
301
|
+
request.resolve(result);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
void result.then(
|
|
306
|
+
(value) => {
|
|
307
|
+
request.resolve(value);
|
|
308
|
+
},
|
|
309
|
+
(error) => {
|
|
310
|
+
request.reject(error as E);
|
|
311
|
+
},
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
runLoad(load: ResourceJob<T>): void {
|
|
316
|
+
const request = this.start();
|
|
317
|
+
let result: T | PromiseLike<T>;
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
result = load(request);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
request.reject(error as E);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.settle(result, request);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
runSourceLoad<S>(sourceValue: S, load: ResourceLoader<S, T>): void {
|
|
330
|
+
const request = this.start();
|
|
331
|
+
let result: T | PromiseLike<T>;
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
result = load(sourceValue, request);
|
|
335
|
+
} catch (error) {
|
|
336
|
+
request.reject(error as E);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
this.settle(result, request);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
refetch(): void {
|
|
344
|
+
if (this.disposed || this.refetchNode === null) return;
|
|
345
|
+
|
|
346
|
+
writeProducer(this.refetchNode, this.refetchNode.payload + 1);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Creates an unstable resource for manual or loader-driven async state.
|
|
352
|
+
*
|
|
353
|
+
* `resource` models request lifecycles with tracked accessors for `status`,
|
|
354
|
+
* `value`, `error`, and `token`. It can be used in three modes:
|
|
355
|
+
*
|
|
356
|
+
* - `resource<T, E>()` creates a manual resource. Call `start()` to begin a
|
|
357
|
+
* request, then settle that request through the returned handle.
|
|
358
|
+
* - `resource(load)` creates an auto-loading resource that starts immediately
|
|
359
|
+
* and can be reloaded with `refetch()`.
|
|
360
|
+
* - `resource(source, load)` creates a source-driven resource that reloads
|
|
361
|
+
* whenever `source()` changes.
|
|
362
|
+
*
|
|
363
|
+
* @typeParam S - Source value type for the source-driven overload.
|
|
364
|
+
* @typeParam T - Resolved value type.
|
|
365
|
+
* @typeParam E - Rejection type tracked by `error()`.
|
|
366
|
+
*
|
|
367
|
+
* @param sourceOrLoad - Either the reactive source accessor to watch or the
|
|
368
|
+
* no-source loader function, depending on the overload.
|
|
369
|
+
* @param maybeLoad - Loader used with the source-driven overload.
|
|
370
|
+
*
|
|
371
|
+
* @returns Either a `ManualResource` or an `AsyncResource`, depending on the
|
|
372
|
+
* selected overload.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```ts
|
|
376
|
+
* import { createRuntime, signal } from "@volynets/reflex";
|
|
377
|
+
* import { resource } from "@volynets/reflex/unstable";
|
|
378
|
+
*
|
|
379
|
+
* const rt = createRuntime();
|
|
380
|
+
* const [userId, setUserId] = signal(1);
|
|
381
|
+
*
|
|
382
|
+
* const user = resource(() => userId(), async (id) => {
|
|
383
|
+
* await Promise.resolve();
|
|
384
|
+
* return { id, name: `user-${id}` };
|
|
385
|
+
* });
|
|
386
|
+
*
|
|
387
|
+
* console.log(user.status()); // "pending"
|
|
388
|
+
*
|
|
389
|
+
* setUserId(2);
|
|
390
|
+
* rt.flush();
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* @remarks
|
|
394
|
+
* - This API is exported from `@volynets/reflex/unstable` and may change
|
|
395
|
+
* between releases.
|
|
396
|
+
* - Each new request increments `token()`. Older handles and stale async
|
|
397
|
+
* resolutions are ignored automatically.
|
|
398
|
+
* - `value()` retains the last resolved value while a newer request is pending
|
|
399
|
+
* or rejected.
|
|
400
|
+
* - `clear()` resets the resource to `"idle"` and invalidates the current
|
|
401
|
+
* request.
|
|
402
|
+
* - `dispose()` invalidates the current request and permanently stops future
|
|
403
|
+
* updates.
|
|
404
|
+
* - `refetch()` and source changes schedule a new load through the runtime.
|
|
405
|
+
* With the default effect strategy, call `rt.flush()` to start it.
|
|
406
|
+
* - If a source accessor or loader throws synchronously, the current request
|
|
407
|
+
* is rejected with that error.
|
|
408
|
+
*
|
|
409
|
+
* @see isPending
|
|
410
|
+
* @see createRuntime
|
|
411
|
+
*/
|
|
412
|
+
export function resource<T, E = unknown>(): ManualResource<T, E>;
|
|
413
|
+
export function resource<T, E = unknown>(
|
|
414
|
+
load: ResourceJob<T>,
|
|
415
|
+
): AsyncResource<T, E>;
|
|
416
|
+
export function resource<S, T, E = unknown>(
|
|
417
|
+
source: Accessor<S>,
|
|
418
|
+
load: ResourceLoader<S, T>,
|
|
419
|
+
): AsyncResource<T, E>;
|
|
420
|
+
export function resource<S, T, E = unknown>(
|
|
421
|
+
sourceOrLoad?: Accessor<S> | ResourceJob<T>,
|
|
422
|
+
maybeLoad?: ResourceLoader<S, T>,
|
|
423
|
+
): ManualResource<T, E> | AsyncResource<T, E> {
|
|
424
|
+
const core = new ResourceCore<T, E>();
|
|
425
|
+
|
|
426
|
+
const baseResource: Resource<T, E> = {
|
|
427
|
+
status: () => {
|
|
428
|
+
core.track();
|
|
429
|
+
return core.status;
|
|
430
|
+
},
|
|
431
|
+
value: () => {
|
|
432
|
+
core.track();
|
|
433
|
+
return core.value;
|
|
434
|
+
},
|
|
435
|
+
error: () => {
|
|
436
|
+
core.track();
|
|
437
|
+
return core.error;
|
|
438
|
+
},
|
|
439
|
+
token: () => {
|
|
440
|
+
core.track();
|
|
441
|
+
return core.token;
|
|
442
|
+
},
|
|
443
|
+
clear() {
|
|
444
|
+
core.clear();
|
|
445
|
+
},
|
|
446
|
+
dispose() {
|
|
447
|
+
core.dispose();
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
if (typeof sourceOrLoad === "function") {
|
|
452
|
+
core.refetchNode = createResourceStateNode();
|
|
453
|
+
|
|
454
|
+
if (typeof maybeLoad === "function") {
|
|
455
|
+
const source = sourceOrLoad as Accessor<S>;
|
|
456
|
+
const load = maybeLoad;
|
|
457
|
+
|
|
458
|
+
core.watcher = createWatcherNode(() => {
|
|
459
|
+
readProducer(core.refetchNode!);
|
|
460
|
+
|
|
461
|
+
let sourceValue: S;
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
sourceValue = source();
|
|
465
|
+
} catch (error) {
|
|
466
|
+
const request = core.start();
|
|
467
|
+
request.reject(error as E);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
core.runSourceLoad(sourceValue, load);
|
|
472
|
+
});
|
|
473
|
+
} else {
|
|
474
|
+
const load = sourceOrLoad as ResourceJob<T>;
|
|
475
|
+
|
|
476
|
+
core.watcher = createWatcherNode(() => {
|
|
477
|
+
readProducer(core.refetchNode!);
|
|
478
|
+
core.runLoad(load);
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
runWatcher(core.watcher);
|
|
483
|
+
core.context.registerWatcherCleanup(() => {
|
|
484
|
+
core.dispose();
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
...baseResource,
|
|
489
|
+
refetch() {
|
|
490
|
+
core.refetch();
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
core.context.registerWatcherCleanup(() => {
|
|
496
|
+
core.dispose();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
...baseResource,
|
|
501
|
+
start() {
|
|
502
|
+
return core.start();
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
}
|