live-cache 0.2.1 → 0.2.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 +122 -6
- package/dist/core/Controller.d.ts +15 -11
- package/dist/core/ObjectStore.d.ts +1 -1
- package/dist/index.cjs +65 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +65 -41
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +65 -41
- package/dist/index.umd.js.map +1 -1
- package/dist/react/useController.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,12 @@ A lightweight, type-safe client-side database library for JavaScript written in
|
|
|
14
14
|
- ♻️ Pluggable invalidation strategies (timeouts, focus, websockets)
|
|
15
15
|
- 🎨 Beautiful examples included
|
|
16
16
|
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
See the `examples/` folder for ready-to-run demos:
|
|
20
|
+
- `examples/react`: PokéAPI explorer built with controllers + `useController`
|
|
21
|
+
- `examples/vanilla-js`: Simple browser demo using the UMD build
|
|
22
|
+
|
|
17
23
|
## Installation
|
|
18
24
|
|
|
19
25
|
```bash
|
|
@@ -132,17 +138,29 @@ Use `Controller<T, Name>` for **server-backed** resources: it wraps a `Collectio
|
|
|
132
138
|
|
|
133
139
|
`commit()` is the important part: it **publishes** the latest snapshot to subscribers and **persists** the snapshot using the configured `StorageManager`.
|
|
134
140
|
|
|
141
|
+
The `fetch(where?)` method can fetch all data or query-specific data based on the `where` parameter:
|
|
142
|
+
|
|
135
143
|
```ts
|
|
136
144
|
import { Controller } from "live-cache";
|
|
137
145
|
|
|
138
146
|
type User = { id: number; name: string };
|
|
139
147
|
|
|
140
148
|
class UsersController extends Controller<User, "users"> {
|
|
141
|
-
async
|
|
142
|
-
|
|
143
|
-
if (!
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
async fetch(where?: string | Partial<User>): Promise<[User[], number]> {
|
|
150
|
+
// Fetch all users if no where clause
|
|
151
|
+
if (!where) {
|
|
152
|
+
const res = await fetch("/api/users");
|
|
153
|
+
if (!res.ok) throw new Error("Failed to fetch users");
|
|
154
|
+
const data = (await res.json()) as User[];
|
|
155
|
+
return [data, data.length];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Fetch specific user by id or name
|
|
159
|
+
const id = typeof where === "string" ? where : where.id;
|
|
160
|
+
const res = await fetch(`/api/users/${id}`);
|
|
161
|
+
if (!res.ok) throw new Error("Failed to fetch user");
|
|
162
|
+
const data = (await res.json()) as User;
|
|
163
|
+
return [[data], 1];
|
|
146
164
|
}
|
|
147
165
|
|
|
148
166
|
/**
|
|
@@ -151,7 +169,7 @@ class UsersController extends Controller<User, "users"> {
|
|
|
151
169
|
*/
|
|
152
170
|
invalidate() {
|
|
153
171
|
this.abort();
|
|
154
|
-
void this.
|
|
172
|
+
void this.update();
|
|
155
173
|
}
|
|
156
174
|
|
|
157
175
|
async renameUser(id: number, name: string) {
|
|
@@ -163,6 +181,68 @@ class UsersController extends Controller<User, "users"> {
|
|
|
163
181
|
}
|
|
164
182
|
```
|
|
165
183
|
|
|
184
|
+
### Real-world example: PokéAPI integration
|
|
185
|
+
|
|
186
|
+
Here's a complete example from the `examples/react` demo showing how to build controllers for a public API:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { Controller } from "live-cache";
|
|
190
|
+
|
|
191
|
+
const API_BASE = "https://pokeapi.co/api/v2";
|
|
192
|
+
|
|
193
|
+
// Controller for fetching the list of Pokémon
|
|
194
|
+
class PokemonListController extends Controller<{ name: string; url: string }, "pokemonList"> {
|
|
195
|
+
constructor(name, options) {
|
|
196
|
+
super(name, options);
|
|
197
|
+
this.limit = 24;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async fetch() {
|
|
201
|
+
this.abort();
|
|
202
|
+
const response = await fetch(`${API_BASE}/pokemon?limit=${this.limit}`, {
|
|
203
|
+
signal: this.abortController?.signal,
|
|
204
|
+
});
|
|
205
|
+
if (!response.ok) throw new Error(`GET /pokemon failed (${response.status})`);
|
|
206
|
+
const data = await response.json();
|
|
207
|
+
return [data.results ?? [], data.count ?? 0];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
invalidate() {
|
|
211
|
+
this.abort();
|
|
212
|
+
void this.update();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Controller for fetching individual Pokémon details
|
|
217
|
+
class PokemonDetailsController extends Controller<any, "pokemonDetails"> {
|
|
218
|
+
resolveQuery(where) {
|
|
219
|
+
if (!where) return null;
|
|
220
|
+
if (typeof where === "string") return where;
|
|
221
|
+
if (where.name) return String(where.name);
|
|
222
|
+
if (where.id !== undefined) return String(where.id);
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async fetch(where) {
|
|
227
|
+
const query = this.resolveQuery(where);
|
|
228
|
+
if (!query) return [[], 0];
|
|
229
|
+
|
|
230
|
+
this.abort();
|
|
231
|
+
const response = await fetch(`${API_BASE}/pokemon/${query}`, {
|
|
232
|
+
signal: this.abortController?.signal,
|
|
233
|
+
});
|
|
234
|
+
if (!response.ok) throw new Error(`GET /pokemon/${query} failed (${response.status})`);
|
|
235
|
+
const data = await response.json();
|
|
236
|
+
return [[data], 1];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
invalidate() {
|
|
240
|
+
this.abort();
|
|
241
|
+
void this.update(this.lastQuery);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
166
246
|
### Persistence (`StorageManager`)
|
|
167
247
|
|
|
168
248
|
Controllers persist snapshots through a `StorageManager` (array-of-models, not a JSON string).
|
|
@@ -213,6 +293,8 @@ Use `ContextProvider` to provide an `ObjectStore`, `useRegister()` to register c
|
|
|
213
293
|
`controller.invalidator.registerInvalidation()` on mount and
|
|
214
294
|
`controller.invalidator.unregisterInvalidation()` on unmount.
|
|
215
295
|
|
|
296
|
+
### Basic example
|
|
297
|
+
|
|
216
298
|
```tsx
|
|
217
299
|
import React from "react";
|
|
218
300
|
import { ContextProvider, useRegister, useController } from "live-cache";
|
|
@@ -251,6 +333,40 @@ export default function Root() {
|
|
|
251
333
|
}
|
|
252
334
|
```
|
|
253
335
|
|
|
336
|
+
### Query-based fetching example
|
|
337
|
+
|
|
338
|
+
You can pass a `where` clause to `useController()` to fetch specific data:
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
import { useController } from "live-cache";
|
|
342
|
+
import { useMemo } from "react";
|
|
343
|
+
|
|
344
|
+
function PokemonDetails({ query }) {
|
|
345
|
+
// Convert query string to where clause
|
|
346
|
+
const where = useMemo(() => ({ name: query }), [query]);
|
|
347
|
+
|
|
348
|
+
const { data, loading, error } = useController(
|
|
349
|
+
"pokemonDetails",
|
|
350
|
+
where,
|
|
351
|
+
{ initialise: !!where }
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
const pokemon = data[0];
|
|
355
|
+
if (loading) return <div>Loading Pokémon…</div>;
|
|
356
|
+
if (error) return <div>Error: {String(error)}</div>;
|
|
357
|
+
if (!pokemon) return null;
|
|
358
|
+
|
|
359
|
+
return (
|
|
360
|
+
<div>
|
|
361
|
+
<h2>{pokemon.name}</h2>
|
|
362
|
+
<img src={pokemon.sprites.front_default} alt={pokemon.name} />
|
|
363
|
+
</div>
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
See `examples/react` for a complete PokéAPI explorer implementation with multiple components using controllers.
|
|
369
|
+
|
|
254
370
|
## Cache invalidation recipes
|
|
255
371
|
|
|
256
372
|
These show **framework-agnostic** controller patterns and a **React** wiring example for each.
|
|
@@ -46,19 +46,20 @@ export interface ControllerOptions<TVariable, TName extends string> {
|
|
|
46
46
|
storageManager?: StorageManager<TVariable[]>;
|
|
47
47
|
pageSize?: number;
|
|
48
48
|
invalidator?: Invalidator<TVariable>;
|
|
49
|
-
initialiseOnMount?: boolean;
|
|
50
49
|
}
|
|
51
50
|
export default class Controller<TVariable, TName extends string> {
|
|
52
51
|
name: TName;
|
|
53
52
|
collection: Collection<TVariable, TName>;
|
|
54
53
|
protected subscribers: Set<(model: ModelType<TVariable>[]) => void>;
|
|
55
|
-
|
|
54
|
+
storageManager: StorageManager<TVariable[]>;
|
|
56
55
|
loading: boolean;
|
|
57
56
|
error: unknown;
|
|
58
57
|
total: number;
|
|
59
|
-
|
|
58
|
+
page: number;
|
|
59
|
+
limit: number;
|
|
60
60
|
abortController: AbortController | null;
|
|
61
61
|
invalidator: Invalidator<TVariable>;
|
|
62
|
+
initialised: boolean;
|
|
62
63
|
/**
|
|
63
64
|
* Abort any in-flight work owned by this controller (typically network fetches).
|
|
64
65
|
*
|
|
@@ -66,15 +67,18 @@ export default class Controller<TVariable, TName extends string> {
|
|
|
66
67
|
* pass `this.abortController.signal` to the next request.
|
|
67
68
|
*/
|
|
68
69
|
abort(): void;
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
updateTotal(total: number): void;
|
|
71
|
+
updatePage(page: number): void;
|
|
72
|
+
updateLimit(limit: number): void;
|
|
71
73
|
/**
|
|
72
74
|
* Fetch the complete dataset for this controller.
|
|
73
75
|
*
|
|
74
76
|
* Subclasses must implement this. Return `[rows, total]` where `total` is the
|
|
75
77
|
* total number of rows available on the backend (useful for pagination).
|
|
76
78
|
*/
|
|
77
|
-
|
|
79
|
+
fetch(where?: string | Partial<TVariable>): Promise<[TVariable[], number]>;
|
|
80
|
+
nextPage(where?: string | Partial<TVariable>): Promise<void>;
|
|
81
|
+
previousPage(where?: string | Partial<TVariable>): Promise<void>;
|
|
78
82
|
/**
|
|
79
83
|
* Initialise (hydrate) the controller's collection.
|
|
80
84
|
*
|
|
@@ -85,7 +89,7 @@ export default class Controller<TVariable, TName extends string> {
|
|
|
85
89
|
*
|
|
86
90
|
* A successful initialise ends with `commit()` so subscribers receive the latest snapshot.
|
|
87
91
|
*/
|
|
88
|
-
initialise(): Promise<void>;
|
|
92
|
+
initialise(where?: string | Partial<TVariable>): Promise<void>;
|
|
89
93
|
/**
|
|
90
94
|
* Subscribe to controller updates.
|
|
91
95
|
*
|
|
@@ -99,13 +103,13 @@ export default class Controller<TVariable, TName extends string> {
|
|
|
99
103
|
* unsubscribe();
|
|
100
104
|
* ```
|
|
101
105
|
*/
|
|
102
|
-
|
|
106
|
+
subscribe(onChange: (models: ModelType<TVariable>[]) => void): () => boolean;
|
|
103
107
|
/**
|
|
104
108
|
* Persist the latest snapshot and notify all subscribers.
|
|
105
109
|
*
|
|
106
110
|
* This is intentionally private: consumers should use `commit()` which computes the snapshot.
|
|
107
111
|
*/
|
|
108
|
-
private
|
|
112
|
+
private publish;
|
|
109
113
|
/**
|
|
110
114
|
* Publish + persist the current snapshot.
|
|
111
115
|
*
|
|
@@ -119,7 +123,7 @@ export default class Controller<TVariable, TName extends string> {
|
|
|
119
123
|
*
|
|
120
124
|
* Subclasses typically use this inside `invalidate()`.
|
|
121
125
|
*/
|
|
122
|
-
|
|
126
|
+
update(where?: string | Partial<TVariable>): Promise<void>;
|
|
123
127
|
/**
|
|
124
128
|
* Invalidate the cache for this controller.
|
|
125
129
|
*
|
|
@@ -144,5 +148,5 @@ export default class Controller<TVariable, TName extends string> {
|
|
|
144
148
|
* @param storageManager - where snapshots are persisted (defaults to no-op)
|
|
145
149
|
* @param pageSize - optional pagination hint (userland)
|
|
146
150
|
*/
|
|
147
|
-
constructor(name: TName, { storageManager, pageSize, invalidator,
|
|
151
|
+
constructor(name: TName, { storageManager, pageSize, invalidator, }: ControllerOptions<TVariable, TName>);
|
|
148
152
|
}
|
|
@@ -38,7 +38,7 @@ export default class ObjectStore {
|
|
|
38
38
|
/**
|
|
39
39
|
* Initialise a controller once per store, even if multiple callers request it.
|
|
40
40
|
*/
|
|
41
|
-
initialiseOnce<TVariable, TName extends string>(name: TName): Promise<void>;
|
|
41
|
+
initialiseOnce<TVariable, TName extends string>(name: TName, where?: string | Partial<TVariable>): Promise<void>;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
44
|
* Returns a singleton store instance.
|
package/dist/index.cjs
CHANGED
|
@@ -529,14 +529,17 @@ class Controller {
|
|
|
529
529
|
abort() {
|
|
530
530
|
if (this.abortController) {
|
|
531
531
|
this.abortController.abort();
|
|
532
|
+
this.abortController = null;
|
|
532
533
|
}
|
|
533
|
-
this.abortController = new AbortController();
|
|
534
534
|
}
|
|
535
535
|
updateTotal(total) {
|
|
536
536
|
this.total = total;
|
|
537
537
|
}
|
|
538
|
-
|
|
539
|
-
this.
|
|
538
|
+
updatePage(page) {
|
|
539
|
+
this.page = page;
|
|
540
|
+
}
|
|
541
|
+
updateLimit(limit) {
|
|
542
|
+
this.limit = limit;
|
|
540
543
|
}
|
|
541
544
|
/**
|
|
542
545
|
* Fetch the complete dataset for this controller.
|
|
@@ -544,11 +547,23 @@ class Controller {
|
|
|
544
547
|
* Subclasses must implement this. Return `[rows, total]` where `total` is the
|
|
545
548
|
* total number of rows available on the backend (useful for pagination).
|
|
546
549
|
*/
|
|
547
|
-
|
|
550
|
+
fetch(where) {
|
|
548
551
|
return __awaiter(this, void 0, void 0, function* () {
|
|
549
552
|
throw Error("Not Implemented");
|
|
550
553
|
});
|
|
551
554
|
}
|
|
555
|
+
nextPage(where) {
|
|
556
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
557
|
+
this.updatePage(this.page + 1);
|
|
558
|
+
yield this.update(where);
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
previousPage(where) {
|
|
562
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
563
|
+
this.updatePage(this.page - 1);
|
|
564
|
+
yield this.update(where);
|
|
565
|
+
});
|
|
566
|
+
}
|
|
552
567
|
/**
|
|
553
568
|
* Initialise (hydrate) the controller's collection.
|
|
554
569
|
*
|
|
@@ -559,37 +574,41 @@ class Controller {
|
|
|
559
574
|
*
|
|
560
575
|
* A successful initialise ends with `commit()` so subscribers receive the latest snapshot.
|
|
561
576
|
*/
|
|
562
|
-
initialise() {
|
|
577
|
+
initialise(where) {
|
|
563
578
|
return __awaiter(this, void 0, void 0, function* () {
|
|
564
|
-
|
|
565
|
-
if (this.loading)
|
|
566
|
-
return;
|
|
579
|
+
this.abortController = new AbortController();
|
|
567
580
|
// If the collection is not empty, return.
|
|
568
|
-
let data = this.collection.find().map((doc) => doc.toData());
|
|
581
|
+
let data = this.collection.find(where).map((doc) => doc.toData());
|
|
569
582
|
if (data.length !== 0) {
|
|
570
|
-
|
|
571
|
-
}
|
|
572
|
-
// If the collection is empty, check the storage manager.
|
|
573
|
-
data = (_a = (yield this.storageManager.get(this.name))) !== null && _a !== void 0 ? _a : [];
|
|
574
|
-
if (data.length !== 0) {
|
|
575
|
-
this.updateTotal(this.collection.find().length);
|
|
576
|
-
this.collection.insertMany(data);
|
|
583
|
+
this.updateTotal(data.length);
|
|
577
584
|
yield this.commit();
|
|
578
585
|
return;
|
|
579
586
|
}
|
|
587
|
+
const fromStorage = yield this.storageManager.get(this.name);
|
|
588
|
+
if (fromStorage && fromStorage.length !== 0) {
|
|
589
|
+
const __collection = new Collection(this.name);
|
|
590
|
+
__collection.insertMany(fromStorage);
|
|
591
|
+
const __data = __collection.find(where).map(x => x.toData());
|
|
592
|
+
if (__data.length !== 0) {
|
|
593
|
+
this.collection.insertMany(__data);
|
|
594
|
+
this.updateTotal(__data.length);
|
|
595
|
+
yield this.commit();
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
580
599
|
// If the storage manager is empty, fetch the data from the server.
|
|
581
600
|
try {
|
|
582
601
|
this.loading = true;
|
|
583
|
-
const [_data, total] = yield this.
|
|
602
|
+
const [_data, total] = yield this.fetch(where);
|
|
584
603
|
this.collection.insertMany(_data);
|
|
585
604
|
this.updateTotal(total);
|
|
586
|
-
yield this.commit();
|
|
587
605
|
}
|
|
588
606
|
catch (error) {
|
|
589
607
|
this.error = error;
|
|
590
608
|
}
|
|
591
609
|
finally {
|
|
592
610
|
this.loading = false;
|
|
611
|
+
yield this.commit();
|
|
593
612
|
}
|
|
594
613
|
});
|
|
595
614
|
}
|
|
@@ -606,7 +625,7 @@ class Controller {
|
|
|
606
625
|
* unsubscribe();
|
|
607
626
|
* ```
|
|
608
627
|
*/
|
|
609
|
-
|
|
628
|
+
subscribe(onChange) {
|
|
610
629
|
this.subscribers.add(onChange);
|
|
611
630
|
return () => this.subscribers.delete(onChange);
|
|
612
631
|
}
|
|
@@ -615,12 +634,12 @@ class Controller {
|
|
|
615
634
|
*
|
|
616
635
|
* This is intentionally private: consumers should use `commit()` which computes the snapshot.
|
|
617
636
|
*/
|
|
618
|
-
|
|
637
|
+
publish(models) {
|
|
619
638
|
return __awaiter(this, void 0, void 0, function* () {
|
|
620
639
|
// Persist the full cache snapshot for hydration.
|
|
621
640
|
yield this.storageManager.set(this.name, this.collection.find().map((doc) => doc.toModel()));
|
|
622
641
|
this.subscribers.forEach((sub) => {
|
|
623
|
-
sub(
|
|
642
|
+
sub(models);
|
|
624
643
|
});
|
|
625
644
|
});
|
|
626
645
|
}
|
|
@@ -634,7 +653,7 @@ class Controller {
|
|
|
634
653
|
commit() {
|
|
635
654
|
return __awaiter(this, void 0, void 0, function* () {
|
|
636
655
|
const models = this.collection.find().map((doc) => doc.toModel());
|
|
637
|
-
yield this.
|
|
656
|
+
yield this.publish(models);
|
|
638
657
|
});
|
|
639
658
|
}
|
|
640
659
|
/**
|
|
@@ -642,8 +661,13 @@ class Controller {
|
|
|
642
661
|
*
|
|
643
662
|
* Subclasses typically use this inside `invalidate()`.
|
|
644
663
|
*/
|
|
645
|
-
|
|
646
|
-
return this
|
|
664
|
+
update(where) {
|
|
665
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
666
|
+
const [response, total] = yield this.fetch(where);
|
|
667
|
+
this.collection.insertMany(response);
|
|
668
|
+
this.updateTotal(total);
|
|
669
|
+
yield this.commit();
|
|
670
|
+
});
|
|
647
671
|
}
|
|
648
672
|
/**
|
|
649
673
|
* Invalidate the cache for this controller.
|
|
@@ -667,10 +691,11 @@ class Controller {
|
|
|
667
691
|
void this.storageManager.delete(this.name);
|
|
668
692
|
this.collection.clear();
|
|
669
693
|
this.updateTotal(0);
|
|
670
|
-
this.
|
|
694
|
+
this.updatePage(0);
|
|
695
|
+
this.updateLimit(10);
|
|
671
696
|
this.error = null;
|
|
672
697
|
this.loading = false;
|
|
673
|
-
void this.
|
|
698
|
+
void this.publish([]);
|
|
674
699
|
}
|
|
675
700
|
/**
|
|
676
701
|
* Create a controller.
|
|
@@ -679,22 +704,22 @@ class Controller {
|
|
|
679
704
|
* @param storageManager - where snapshots are persisted (defaults to no-op)
|
|
680
705
|
* @param pageSize - optional pagination hint (userland)
|
|
681
706
|
*/
|
|
682
|
-
constructor(name, { storageManager = new DefaultStorageManager("live-cache:"), pageSize =
|
|
707
|
+
constructor(name, { storageManager = new DefaultStorageManager("live-cache:"), pageSize = 10, invalidator = new DefaultInvalidator(), }) {
|
|
683
708
|
this.subscribers = new Set();
|
|
684
709
|
this.loading = false;
|
|
685
710
|
this.error = null;
|
|
686
711
|
this.total = -1;
|
|
687
|
-
this.
|
|
712
|
+
this.page = 0;
|
|
713
|
+
this.limit = 10;
|
|
688
714
|
this.abortController = null;
|
|
715
|
+
this.initialised = false;
|
|
689
716
|
this.name = name;
|
|
690
717
|
this.collection = new Collection(name);
|
|
691
718
|
this.storageManager = storageManager;
|
|
692
|
-
this.
|
|
719
|
+
this.page = 0;
|
|
720
|
+
this.limit = pageSize;
|
|
693
721
|
this.invalidator = invalidator;
|
|
694
722
|
this.invalidator.bind(this.invalidate.bind(this));
|
|
695
|
-
if (initialiseOnMount) {
|
|
696
|
-
this.initialise();
|
|
697
|
-
}
|
|
698
723
|
}
|
|
699
724
|
}
|
|
700
725
|
|
|
@@ -947,12 +972,12 @@ class ObjectStore {
|
|
|
947
972
|
/**
|
|
948
973
|
* Initialise a controller once per store, even if multiple callers request it.
|
|
949
974
|
*/
|
|
950
|
-
initialiseOnce(name) {
|
|
975
|
+
initialiseOnce(name, where) {
|
|
951
976
|
const controller = this.get(name);
|
|
952
977
|
const existing = this.initialisePromises.get(controller);
|
|
953
978
|
if (existing)
|
|
954
979
|
return existing;
|
|
955
|
-
const promise = controller.initialise().finally(() => {
|
|
980
|
+
const promise = controller.initialise(where).finally(() => {
|
|
956
981
|
if (this.initialisePromises.get(controller) === promise) {
|
|
957
982
|
this.initialisePromises.delete(controller);
|
|
958
983
|
}
|
|
@@ -1302,7 +1327,7 @@ function useRegister(controller, store = getDefaultObjectStore()) {
|
|
|
1302
1327
|
*/
|
|
1303
1328
|
function useController(name, where, options) {
|
|
1304
1329
|
var _a, _b, _c, _d;
|
|
1305
|
-
|
|
1330
|
+
(_a = options === null || options === void 0 ? void 0 : options.initialise) !== null && _a !== void 0 ? _a : true;
|
|
1306
1331
|
const optionalStore = options === null || options === void 0 ? void 0 : options.store;
|
|
1307
1332
|
const abortOnUnmount = (_b = options === null || options === void 0 ? void 0 : options.abortOnUnmount) !== null && _b !== void 0 ? _b : true;
|
|
1308
1333
|
const withInvalidation = (_c = options === null || options === void 0 ? void 0 : options.withInvalidation) !== null && _c !== void 0 ? _c : true;
|
|
@@ -1324,13 +1349,12 @@ function useController(name, where, options) {
|
|
|
1324
1349
|
};
|
|
1325
1350
|
// Prime state immediately.
|
|
1326
1351
|
callback();
|
|
1327
|
-
const cleanup = controller.
|
|
1352
|
+
const cleanup = controller.subscribe(callback);
|
|
1328
1353
|
if (withInvalidation) {
|
|
1329
1354
|
controller.invalidator.registerInvalidation();
|
|
1330
1355
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
}
|
|
1356
|
+
void store.initialiseOnce(name, where);
|
|
1357
|
+
// controller.initialise(where);
|
|
1334
1358
|
return () => {
|
|
1335
1359
|
if (abortOnUnmount) {
|
|
1336
1360
|
controller.abort();
|
|
@@ -1338,7 +1362,7 @@ function useController(name, where, options) {
|
|
|
1338
1362
|
cleanup();
|
|
1339
1363
|
controller.invalidator.unregisterInvalidation();
|
|
1340
1364
|
};
|
|
1341
|
-
}, [controller, where,
|
|
1365
|
+
}, [controller, where, abortOnUnmount, withInvalidation]);
|
|
1342
1366
|
return { controller, data, loading, error };
|
|
1343
1367
|
}
|
|
1344
1368
|
|
|
@@ -1361,7 +1385,7 @@ function useJoinController({ from, where, select }) {
|
|
|
1361
1385
|
setData(join(from, where, select));
|
|
1362
1386
|
};
|
|
1363
1387
|
callback();
|
|
1364
|
-
const cleanup = from.map((c) => c.
|
|
1388
|
+
const cleanup = from.map((c) => c.subscribe(callback));
|
|
1365
1389
|
return () => {
|
|
1366
1390
|
cleanup.forEach((c) => c());
|
|
1367
1391
|
};
|