@flurryx/store 0.8.4 → 1.0.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/index.cjs +1233 -320
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +588 -18
- package/dist/index.d.ts +588 -18
- package/dist/index.js +1236 -334
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,32 +1,1080 @@
|
|
|
1
1
|
// src/base-store.ts
|
|
2
|
-
import { signal } from "@angular/core";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
import { signal as signal2 } from "@angular/core";
|
|
3
|
+
|
|
4
|
+
// src/store-clone.ts
|
|
5
|
+
function cloneValue(value) {
|
|
6
|
+
if (value !== null && typeof value === "object") {
|
|
7
|
+
const existingClone = cloneReference(value, /* @__PURE__ */ new WeakMap());
|
|
8
|
+
return existingClone;
|
|
9
|
+
}
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
function cloneReference(value, seen) {
|
|
13
|
+
const seenClone = seen.get(value);
|
|
14
|
+
if (seenClone) {
|
|
15
|
+
return seenClone;
|
|
16
|
+
}
|
|
17
|
+
if (value instanceof Date) {
|
|
18
|
+
return new Date(value.getTime());
|
|
19
|
+
}
|
|
20
|
+
if (value instanceof Map) {
|
|
21
|
+
const clonedMap = /* @__PURE__ */ new Map();
|
|
22
|
+
seen.set(value, clonedMap);
|
|
23
|
+
value.forEach((entryValue, key) => {
|
|
24
|
+
clonedMap.set(
|
|
25
|
+
cloneValueWithSeen(key, seen),
|
|
26
|
+
cloneValueWithSeen(entryValue, seen)
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
return clonedMap;
|
|
30
|
+
}
|
|
31
|
+
if (value instanceof Set) {
|
|
32
|
+
const clonedSet = /* @__PURE__ */ new Set();
|
|
33
|
+
seen.set(value, clonedSet);
|
|
34
|
+
value.forEach((entryValue) => {
|
|
35
|
+
clonedSet.add(cloneValueWithSeen(entryValue, seen));
|
|
36
|
+
});
|
|
37
|
+
return clonedSet;
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
const clonedArray = [];
|
|
41
|
+
seen.set(value, clonedArray);
|
|
42
|
+
value.forEach((item, index) => {
|
|
43
|
+
clonedArray[index] = cloneValueWithSeen(item, seen);
|
|
44
|
+
});
|
|
45
|
+
return clonedArray;
|
|
46
|
+
}
|
|
47
|
+
const clonedObject = Object.create(Object.getPrototypeOf(value));
|
|
48
|
+
seen.set(value, clonedObject);
|
|
49
|
+
Reflect.ownKeys(value).forEach((key) => {
|
|
50
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
51
|
+
if (!descriptor) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if ("value" in descriptor) {
|
|
55
|
+
descriptor.value = cloneValueWithSeen(descriptor.value, seen);
|
|
56
|
+
}
|
|
57
|
+
Object.defineProperty(clonedObject, key, descriptor);
|
|
58
|
+
});
|
|
59
|
+
return clonedObject;
|
|
60
|
+
}
|
|
61
|
+
function cloneValueWithSeen(value, seen) {
|
|
62
|
+
if (value !== null && typeof value === "object") {
|
|
63
|
+
return cloneReference(value, seen);
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
function createSnapshotRestorePatch(currentState, snapshotState) {
|
|
68
|
+
const patch = {};
|
|
69
|
+
const keys = /* @__PURE__ */ new Set([
|
|
70
|
+
...Reflect.ownKeys(currentState),
|
|
71
|
+
...Reflect.ownKeys(snapshotState)
|
|
72
|
+
]);
|
|
73
|
+
keys.forEach((key) => {
|
|
74
|
+
if (Object.prototype.hasOwnProperty.call(snapshotState, key)) {
|
|
75
|
+
patch[key] = cloneValue(
|
|
76
|
+
snapshotState[key]
|
|
77
|
+
);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
patch[key] = void 0;
|
|
81
|
+
});
|
|
82
|
+
return patch;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/store-replay.ts
|
|
86
|
+
import { signal, computed } from "@angular/core";
|
|
87
|
+
|
|
88
|
+
// src/store-messages.ts
|
|
89
|
+
var INVALID_HISTORY_INDEX_ERROR = "History index is out of range";
|
|
90
|
+
var INVALID_HISTORY_MESSAGE_ID_ERROR = "History message id is out of range";
|
|
91
|
+
var MESSAGE_NOT_ACKNOWLEDGED_ERROR = "Message was not acknowledged";
|
|
92
|
+
|
|
93
|
+
// src/store-channels.ts
|
|
94
|
+
function serializeStoreMessageChannelValue(value) {
|
|
95
|
+
if (value === void 0) {
|
|
96
|
+
return { __flurryxType: "undefined" };
|
|
97
|
+
}
|
|
98
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
if (value instanceof Date) {
|
|
102
|
+
return {
|
|
103
|
+
__flurryxType: "date",
|
|
104
|
+
value: value.toISOString()
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (value instanceof Map) {
|
|
108
|
+
return {
|
|
109
|
+
__flurryxType: "map",
|
|
110
|
+
entries: Array.from(value.entries(), ([key, entryValue]) => [
|
|
111
|
+
serializeStoreMessageChannelValue(key),
|
|
112
|
+
serializeStoreMessageChannelValue(entryValue)
|
|
113
|
+
])
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (value instanceof Set) {
|
|
117
|
+
return {
|
|
118
|
+
__flurryxType: "set",
|
|
119
|
+
values: Array.from(
|
|
120
|
+
value.values(),
|
|
121
|
+
(entryValue) => serializeStoreMessageChannelValue(entryValue)
|
|
122
|
+
)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(value)) {
|
|
126
|
+
return {
|
|
127
|
+
__flurryxType: "array",
|
|
128
|
+
values: value.map(
|
|
129
|
+
(entryValue) => serializeStoreMessageChannelValue(entryValue)
|
|
130
|
+
)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (typeof value === "object") {
|
|
134
|
+
return {
|
|
135
|
+
__flurryxType: "object",
|
|
136
|
+
entries: Object.entries(value).map(
|
|
137
|
+
([key, entryValue]) => [
|
|
138
|
+
key,
|
|
139
|
+
serializeStoreMessageChannelValue(entryValue)
|
|
140
|
+
]
|
|
141
|
+
)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
throw new Error("Store message channel cannot serialize this value");
|
|
145
|
+
}
|
|
146
|
+
function deserializeStoreMessageChannelValue(value) {
|
|
147
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
switch (value.__flurryxType) {
|
|
151
|
+
case "undefined":
|
|
152
|
+
return void 0;
|
|
153
|
+
case "date":
|
|
154
|
+
return new Date(value.value);
|
|
155
|
+
case "array":
|
|
156
|
+
return value.values.map(
|
|
157
|
+
(entryValue) => deserializeStoreMessageChannelValue(entryValue)
|
|
158
|
+
);
|
|
159
|
+
case "set":
|
|
160
|
+
return new Set(
|
|
161
|
+
value.values.map(
|
|
162
|
+
(entryValue) => deserializeStoreMessageChannelValue(entryValue)
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
case "map":
|
|
166
|
+
return new Map(
|
|
167
|
+
value.entries.map(([key, entryValue]) => [
|
|
168
|
+
deserializeStoreMessageChannelValue(key),
|
|
169
|
+
deserializeStoreMessageChannelValue(entryValue)
|
|
170
|
+
])
|
|
171
|
+
);
|
|
172
|
+
case "object": {
|
|
173
|
+
const result = {};
|
|
174
|
+
value.entries.forEach(([key, entryValue]) => {
|
|
175
|
+
result[key] = deserializeStoreMessageChannelValue(entryValue);
|
|
176
|
+
});
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function defaultSerializeStoreMessageChannelState(state) {
|
|
182
|
+
return JSON.stringify(serializeStoreMessageChannelValue(state));
|
|
183
|
+
}
|
|
184
|
+
function defaultDeserializeStoreMessageChannelState(value) {
|
|
185
|
+
return deserializeStoreMessageChannelValue(
|
|
186
|
+
JSON.parse(value)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
function normalizeStoreMessageChannelState(state) {
|
|
190
|
+
const maxId = state.messages.reduce(
|
|
191
|
+
(currentMax, entry) => Math.max(currentMax, entry.id),
|
|
192
|
+
0
|
|
193
|
+
);
|
|
194
|
+
return {
|
|
195
|
+
nextId: Math.max(state.nextId, maxId + 1),
|
|
196
|
+
messages: state.messages.map((entry) => cloneValue(entry))
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function createInitialStoreMessageRecord(id, message, clock = Date.now) {
|
|
200
|
+
return {
|
|
201
|
+
id,
|
|
202
|
+
message: cloneValue(message),
|
|
203
|
+
status: "pending",
|
|
204
|
+
attempts: 0,
|
|
205
|
+
createdAt: clock(),
|
|
206
|
+
lastAttemptedAt: null,
|
|
207
|
+
acknowledgedAt: null,
|
|
208
|
+
error: null
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function isQuotaExceededError(error) {
|
|
212
|
+
return error instanceof DOMException && (error.code === 22 || error.code === 1014 || error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED");
|
|
213
|
+
}
|
|
214
|
+
function resolveGlobalStorage(name) {
|
|
215
|
+
const storage = globalThis[name];
|
|
216
|
+
if (!storage) {
|
|
217
|
+
throw new Error(`${name} is not available in this environment`);
|
|
218
|
+
}
|
|
219
|
+
return storage;
|
|
220
|
+
}
|
|
221
|
+
function createInMemoryStoreMessageChannel() {
|
|
222
|
+
let messages = [];
|
|
223
|
+
let nextId = 1;
|
|
224
|
+
return {
|
|
225
|
+
publish(message) {
|
|
226
|
+
const record = createInitialStoreMessageRecord(nextId++, message);
|
|
227
|
+
messages = [...messages, record];
|
|
228
|
+
return cloneValue(record);
|
|
229
|
+
},
|
|
230
|
+
getMessage(id) {
|
|
231
|
+
const record = messages.find((entry) => entry.id === id);
|
|
232
|
+
return record ? cloneValue(record) : void 0;
|
|
233
|
+
},
|
|
234
|
+
getMessages() {
|
|
235
|
+
return messages.map((entry) => cloneValue(entry));
|
|
236
|
+
},
|
|
237
|
+
saveMessage(entry) {
|
|
238
|
+
const record = cloneValue(entry);
|
|
239
|
+
const existingIndex = messages.findIndex(
|
|
240
|
+
(candidate) => candidate.id === record.id
|
|
241
|
+
);
|
|
242
|
+
if (existingIndex === -1) {
|
|
243
|
+
messages = [...messages, record];
|
|
244
|
+
} else {
|
|
245
|
+
messages = messages.map((c, i) => i === existingIndex ? record : c);
|
|
246
|
+
}
|
|
247
|
+
nextId = Math.max(nextId, record.id + 1);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function createStorageStoreMessageChannel(options) {
|
|
252
|
+
const serialize = options.serialize ?? defaultSerializeStoreMessageChannelState;
|
|
253
|
+
const deserialize = options.deserialize ?? defaultDeserializeStoreMessageChannelState;
|
|
254
|
+
function readState() {
|
|
255
|
+
const rawState = options.storage.getItem(options.storageKey);
|
|
256
|
+
if (rawState === null) {
|
|
257
|
+
return { nextId: 1, messages: [] };
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
return normalizeStoreMessageChannelState(deserialize(rawState));
|
|
261
|
+
} catch {
|
|
262
|
+
return { nextId: 1, messages: [] };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function writeState(state) {
|
|
266
|
+
const normalized = normalizeStoreMessageChannelState(state);
|
|
267
|
+
try {
|
|
268
|
+
options.storage.setItem(options.storageKey, serialize(normalized));
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (!isQuotaExceededError(error) || normalized.messages.length === 0) {
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
let remaining = [...normalized.messages];
|
|
274
|
+
while (remaining.length > 0) {
|
|
275
|
+
remaining = remaining.slice(1);
|
|
276
|
+
try {
|
|
277
|
+
options.storage.setItem(
|
|
278
|
+
options.storageKey,
|
|
279
|
+
serialize({ nextId: normalized.nextId, messages: remaining })
|
|
280
|
+
);
|
|
281
|
+
return;
|
|
282
|
+
} catch (retryError) {
|
|
283
|
+
if (!isQuotaExceededError(retryError)) {
|
|
284
|
+
throw retryError;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
options.storage.setItem(
|
|
289
|
+
options.storageKey,
|
|
290
|
+
serialize({ nextId: normalized.nextId, messages: [] })
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
publish(message) {
|
|
296
|
+
const state = readState();
|
|
297
|
+
const record = createInitialStoreMessageRecord(state.nextId, message);
|
|
298
|
+
writeState({
|
|
299
|
+
nextId: state.nextId + 1,
|
|
300
|
+
messages: [...state.messages, record]
|
|
301
|
+
});
|
|
302
|
+
return cloneValue(record);
|
|
303
|
+
},
|
|
304
|
+
getMessage(id) {
|
|
305
|
+
const record = readState().messages.find((entry) => entry.id === id);
|
|
306
|
+
return record ? cloneValue(record) : void 0;
|
|
307
|
+
},
|
|
308
|
+
getMessages() {
|
|
309
|
+
return readState().messages.map((entry) => cloneValue(entry));
|
|
310
|
+
},
|
|
311
|
+
saveMessage(entry) {
|
|
312
|
+
const state = readState();
|
|
313
|
+
const record = cloneValue(entry);
|
|
314
|
+
const existingIndex = state.messages.findIndex(
|
|
315
|
+
(candidate) => candidate.id === record.id
|
|
316
|
+
);
|
|
317
|
+
if (existingIndex === -1) {
|
|
318
|
+
writeState({
|
|
319
|
+
nextId: Math.max(state.nextId, record.id + 1),
|
|
320
|
+
messages: [...state.messages, record]
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const nextMessages = state.messages.map(
|
|
325
|
+
(c, i) => i === existingIndex ? record : c
|
|
326
|
+
);
|
|
327
|
+
writeState({
|
|
328
|
+
nextId: Math.max(state.nextId, record.id + 1),
|
|
329
|
+
messages: nextMessages
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function createLocalStorageStoreMessageChannel(options) {
|
|
335
|
+
return createStorageStoreMessageChannel({
|
|
336
|
+
...options,
|
|
337
|
+
storage: options.storage ?? resolveGlobalStorage("localStorage")
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
function createSessionStorageStoreMessageChannel(options) {
|
|
341
|
+
return createStorageStoreMessageChannel({
|
|
342
|
+
...options,
|
|
343
|
+
storage: options.storage ?? resolveGlobalStorage("sessionStorage")
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
function createCompositeStoreMessageChannel(options) {
|
|
347
|
+
if (options.channels.length === 0) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
"createCompositeStoreMessageChannel: 'channels' option must contain at least one channel"
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
const primaryChannel = options.channels[0];
|
|
353
|
+
const replicaChannels = options.channels.slice(1);
|
|
354
|
+
return {
|
|
355
|
+
publish(message) {
|
|
356
|
+
const record = primaryChannel.publish(message);
|
|
357
|
+
replicaChannels.forEach((channel) => {
|
|
358
|
+
channel.saveMessage(record);
|
|
359
|
+
});
|
|
360
|
+
return cloneValue(record);
|
|
361
|
+
},
|
|
362
|
+
getMessage(id) {
|
|
363
|
+
const record = primaryChannel.getMessage(id);
|
|
364
|
+
return record ? cloneValue(record) : void 0;
|
|
365
|
+
},
|
|
366
|
+
getMessages() {
|
|
367
|
+
return primaryChannel.getMessages().map((record) => cloneValue(record));
|
|
368
|
+
},
|
|
369
|
+
saveMessage(entry) {
|
|
370
|
+
primaryChannel.saveMessage(entry);
|
|
371
|
+
replicaChannels.forEach((channel) => {
|
|
372
|
+
channel.saveMessage(entry);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/store-replay.ts
|
|
379
|
+
function messageAffectsKey(message, key) {
|
|
380
|
+
if (message.type === "clearAll") {
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
383
|
+
return "key" in message && message.key === key;
|
|
384
|
+
}
|
|
385
|
+
function toDeadLetterEntry(record) {
|
|
386
|
+
return {
|
|
387
|
+
id: record.id,
|
|
388
|
+
message: cloneValue(record.message),
|
|
389
|
+
attempts: record.attempts,
|
|
390
|
+
error: record.error ?? MESSAGE_NOT_ACKNOWLEDGED_ERROR,
|
|
391
|
+
failedAt: record.lastAttemptedAt ?? record.createdAt
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function areValuesEquivalent(left, right, seen = /* @__PURE__ */ new WeakMap()) {
|
|
395
|
+
if (Object.is(left, right)) {
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
if (typeof left !== typeof right || left === null || right === null) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
if (typeof left !== "object" || typeof right !== "object") {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
let seenRights = seen.get(left);
|
|
405
|
+
if (seenRights?.has(right)) {
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
if (!seenRights) {
|
|
409
|
+
seenRights = /* @__PURE__ */ new WeakSet();
|
|
410
|
+
seen.set(left, seenRights);
|
|
411
|
+
}
|
|
412
|
+
seenRights.add(right);
|
|
413
|
+
if (left instanceof Date || right instanceof Date) {
|
|
414
|
+
return left instanceof Date && right instanceof Date && left.getTime() === right.getTime();
|
|
415
|
+
}
|
|
416
|
+
if (left instanceof Map || right instanceof Map) {
|
|
417
|
+
if (!(left instanceof Map) || !(right instanceof Map)) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
const leftEntries = Array.from(left.entries());
|
|
421
|
+
const rightEntries = Array.from(right.entries());
|
|
422
|
+
if (leftEntries.length !== rightEntries.length) {
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
return leftEntries.every(([leftKey, leftValue], index) => {
|
|
426
|
+
const rightEntry = rightEntries[index];
|
|
427
|
+
if (!rightEntry) {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
return areValuesEquivalent(leftKey, rightEntry[0], seen) && areValuesEquivalent(leftValue, rightEntry[1], seen);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
if (left instanceof Set || right instanceof Set) {
|
|
434
|
+
if (!(left instanceof Set) || !(right instanceof Set)) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
const leftValues = Array.from(left.values());
|
|
438
|
+
const rightValues = Array.from(right.values());
|
|
439
|
+
if (leftValues.length !== rightValues.length) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
return leftValues.every(
|
|
443
|
+
(leftValue, index) => areValuesEquivalent(leftValue, rightValues[index], seen)
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
447
|
+
if (!Array.isArray(left) || !Array.isArray(right)) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
if (left.length !== right.length) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
return left.every(
|
|
454
|
+
(leftValue, index) => areValuesEquivalent(leftValue, right[index], seen)
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
if (Object.getPrototypeOf(left) !== Object.getPrototypeOf(right)) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
const leftRecord = left;
|
|
461
|
+
const rightRecord = right;
|
|
462
|
+
const leftKeys = Reflect.ownKeys(leftRecord);
|
|
463
|
+
const rightKeys = Reflect.ownKeys(rightRecord);
|
|
464
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
return leftKeys.every((key) => {
|
|
468
|
+
if (!Object.prototype.hasOwnProperty.call(rightRecord, key)) {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
return areValuesEquivalent(leftRecord[key], rightRecord[key], seen);
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
function areStoreMessageRecordsEquivalent(sourceRecord, cachedRecord) {
|
|
475
|
+
return sourceRecord.id === cachedRecord.id && sourceRecord.status === cachedRecord.status && sourceRecord.attempts === cachedRecord.attempts && sourceRecord.createdAt === cachedRecord.createdAt && sourceRecord.lastAttemptedAt === cachedRecord.lastAttemptedAt && sourceRecord.acknowledgedAt === cachedRecord.acknowledgedAt && sourceRecord.error === cachedRecord.error && areValuesEquivalent(sourceRecord.message, cachedRecord.message);
|
|
476
|
+
}
|
|
477
|
+
function createStableReadonlyCollection(items) {
|
|
478
|
+
return Object.freeze([...items]);
|
|
479
|
+
}
|
|
480
|
+
function appendStableReadonlyCollectionItem(input) {
|
|
481
|
+
return createStableReadonlyCollection([...input.items, input.item]);
|
|
482
|
+
}
|
|
483
|
+
function upsertStableReadonlyCollectionItem(input) {
|
|
484
|
+
const existingIndex = input.items.findIndex(
|
|
485
|
+
(candidate) => candidate.id === input.item.id
|
|
486
|
+
);
|
|
487
|
+
if (existingIndex === -1) {
|
|
488
|
+
return appendStableReadonlyCollectionItem(input);
|
|
489
|
+
}
|
|
490
|
+
if (Object.is(input.items[existingIndex], input.item)) {
|
|
491
|
+
return input.items;
|
|
492
|
+
}
|
|
493
|
+
const nextItems = [...input.items];
|
|
494
|
+
nextItems[existingIndex] = input.item;
|
|
495
|
+
return createStableReadonlyCollection(nextItems);
|
|
496
|
+
}
|
|
497
|
+
function syncStableReadonlyCollectionById(input) {
|
|
498
|
+
const cachedItemsById = /* @__PURE__ */ new Map();
|
|
499
|
+
input.items.forEach((item) => {
|
|
500
|
+
cachedItemsById.set(item.id, item);
|
|
501
|
+
});
|
|
502
|
+
let didChange = input.items.length !== input.sourceItems.length;
|
|
503
|
+
const nextItems = input.sourceItems.map((sourceItem, index) => {
|
|
504
|
+
const cachedItem = cachedItemsById.get(input.getSourceId(sourceItem));
|
|
505
|
+
const nextItem = cachedItem && input.areEquivalent(sourceItem, cachedItem) ? cachedItem : input.createItem(sourceItem);
|
|
506
|
+
if (!didChange && input.items[index] !== nextItem) {
|
|
507
|
+
didChange = true;
|
|
508
|
+
}
|
|
509
|
+
return nextItem;
|
|
510
|
+
});
|
|
511
|
+
return didChange ? createStableReadonlyCollection(nextItems) : input.items;
|
|
512
|
+
}
|
|
513
|
+
function createStoreHistory(config) {
|
|
514
|
+
const messageChannel = config.channel ?? createInMemoryStoreMessageChannel();
|
|
515
|
+
const clock = config.clock ?? Date.now;
|
|
516
|
+
let history = [
|
|
517
|
+
{
|
|
518
|
+
id: null,
|
|
519
|
+
index: 0,
|
|
520
|
+
message: null,
|
|
521
|
+
snapshot: config.captureSnapshot(),
|
|
522
|
+
acknowledgedAt: null
|
|
523
|
+
}
|
|
524
|
+
];
|
|
525
|
+
let currentIndex = 0;
|
|
526
|
+
let historyCollection = createStableReadonlyCollection(
|
|
527
|
+
history.map((entry) => cloneValue(entry))
|
|
528
|
+
);
|
|
529
|
+
let messageCollection = createStableReadonlyCollection(
|
|
530
|
+
messageChannel.getMessages().map((record) => cloneValue(record))
|
|
531
|
+
);
|
|
532
|
+
const version = signal(0);
|
|
533
|
+
function notifyVersion() {
|
|
534
|
+
version.update((v) => v + 1);
|
|
535
|
+
}
|
|
536
|
+
function recordSnapshot(record) {
|
|
537
|
+
const nextIndex = history.length;
|
|
538
|
+
const nextHistoryEntry = {
|
|
539
|
+
id: record.id,
|
|
540
|
+
index: nextIndex,
|
|
541
|
+
message: cloneValue(record.message),
|
|
542
|
+
snapshot: config.captureSnapshot(),
|
|
543
|
+
acknowledgedAt: record.acknowledgedAt
|
|
544
|
+
};
|
|
545
|
+
history = [...history, nextHistoryEntry];
|
|
546
|
+
historyCollection = appendStableReadonlyCollectionItem({
|
|
547
|
+
items: historyCollection,
|
|
548
|
+
item: cloneValue(nextHistoryEntry)
|
|
549
|
+
});
|
|
550
|
+
currentIndex = nextIndex;
|
|
551
|
+
}
|
|
552
|
+
function truncateFutureHistory() {
|
|
553
|
+
if (currentIndex === history.length - 1) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
history = history.slice(0, currentIndex + 1);
|
|
557
|
+
historyCollection = createStableReadonlyCollection(
|
|
558
|
+
historyCollection.slice(0, currentIndex + 1)
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
function ensureIndexInRange(index) {
|
|
562
|
+
if (!Number.isInteger(index) || index < 0 || index >= history.length) {
|
|
563
|
+
throw new Error(INVALID_HISTORY_INDEX_ERROR);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function travelTo(index) {
|
|
567
|
+
ensureIndexInRange(index);
|
|
568
|
+
config.applySnapshot(history[index].snapshot);
|
|
569
|
+
currentIndex = index;
|
|
570
|
+
notifyVersion();
|
|
571
|
+
}
|
|
572
|
+
function undo() {
|
|
573
|
+
if (currentIndex === 0) {
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
travelTo(currentIndex - 1);
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
function redo() {
|
|
580
|
+
if (currentIndex >= history.length - 1) {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
travelTo(currentIndex + 1);
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
function getErrorMessage(error) {
|
|
587
|
+
if (error instanceof Error && error.message) {
|
|
588
|
+
return error.message;
|
|
589
|
+
}
|
|
590
|
+
return MESSAGE_NOT_ACKNOWLEDGED_ERROR;
|
|
591
|
+
}
|
|
592
|
+
function persistMessageAttempt(record, status, error, attemptedAt) {
|
|
593
|
+
const nextRecord = {
|
|
594
|
+
...record,
|
|
595
|
+
message: cloneValue(record.message),
|
|
596
|
+
status,
|
|
597
|
+
attempts: record.attempts + 1,
|
|
598
|
+
lastAttemptedAt: attemptedAt,
|
|
599
|
+
acknowledgedAt: status === "acknowledged" ? attemptedAt : record.acknowledgedAt,
|
|
600
|
+
error
|
|
601
|
+
};
|
|
602
|
+
messageChannel.saveMessage(nextRecord);
|
|
603
|
+
messageCollection = upsertStableReadonlyCollectionItem({
|
|
604
|
+
items: messageCollection,
|
|
605
|
+
item: cloneValue(nextRecord)
|
|
606
|
+
});
|
|
607
|
+
return nextRecord;
|
|
608
|
+
}
|
|
609
|
+
function consumeRecord(record, options) {
|
|
610
|
+
const clonedMessage = cloneValue(record.message);
|
|
611
|
+
const attemptedAt = clock();
|
|
612
|
+
try {
|
|
613
|
+
const acknowledged = config.applyMessage(clonedMessage);
|
|
614
|
+
if (!acknowledged) {
|
|
615
|
+
throw new Error(MESSAGE_NOT_ACKNOWLEDGED_ERROR);
|
|
616
|
+
}
|
|
617
|
+
const acknowledgedRecord = persistMessageAttempt(
|
|
618
|
+
{
|
|
619
|
+
...record,
|
|
620
|
+
message: clonedMessage
|
|
621
|
+
},
|
|
622
|
+
"acknowledged",
|
|
623
|
+
null,
|
|
624
|
+
attemptedAt
|
|
625
|
+
);
|
|
626
|
+
if (options?.recordHistory !== false) {
|
|
627
|
+
truncateFutureHistory();
|
|
628
|
+
recordSnapshot(acknowledgedRecord);
|
|
629
|
+
}
|
|
630
|
+
notifyVersion();
|
|
631
|
+
return true;
|
|
632
|
+
} catch (error) {
|
|
633
|
+
persistMessageAttempt(
|
|
634
|
+
{
|
|
635
|
+
...record,
|
|
636
|
+
message: clonedMessage
|
|
637
|
+
},
|
|
638
|
+
"dead-letter",
|
|
639
|
+
getErrorMessage(error),
|
|
640
|
+
attemptedAt
|
|
641
|
+
);
|
|
642
|
+
notifyVersion();
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function resolveReplayRecords(ids) {
|
|
647
|
+
return ids.map((id) => {
|
|
648
|
+
if (!Number.isInteger(id) || id < 1) {
|
|
649
|
+
throw new Error(INVALID_HISTORY_MESSAGE_ID_ERROR);
|
|
650
|
+
}
|
|
651
|
+
const record = messageChannel.getMessage(id);
|
|
652
|
+
if (!record) {
|
|
653
|
+
throw new Error(INVALID_HISTORY_MESSAGE_ID_ERROR);
|
|
654
|
+
}
|
|
655
|
+
return cloneValue(record);
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
function replayByIds(input) {
|
|
659
|
+
const ids = Array.isArray(input) ? input : [input];
|
|
660
|
+
const records = resolveReplayRecords(ids);
|
|
661
|
+
let acknowledgedCount = 0;
|
|
662
|
+
records.forEach((record) => {
|
|
663
|
+
if (consumeRecord(record)) {
|
|
664
|
+
acknowledgedCount += 1;
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
return acknowledgedCount;
|
|
668
|
+
}
|
|
669
|
+
function replayDeadLetter(id) {
|
|
670
|
+
const record = messageChannel.getMessage(id);
|
|
671
|
+
if (!record || record.status !== "dead-letter") {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
return consumeRecord(record);
|
|
675
|
+
}
|
|
676
|
+
function replayDeadLetters() {
|
|
677
|
+
const ids = messageChannel.getMessages().filter((record) => record.status === "dead-letter").map((record) => record.id);
|
|
678
|
+
let acknowledgedCount = 0;
|
|
679
|
+
ids.forEach((id) => {
|
|
680
|
+
if (replayDeadLetter(id)) {
|
|
681
|
+
acknowledgedCount += 1;
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
return acknowledgedCount;
|
|
685
|
+
}
|
|
686
|
+
const historySignal = computed(() => {
|
|
687
|
+
version();
|
|
688
|
+
return historyCollection;
|
|
689
|
+
});
|
|
690
|
+
const messagesSignal = computed(() => {
|
|
691
|
+
version();
|
|
692
|
+
messageCollection = syncStableReadonlyCollectionById({
|
|
693
|
+
items: messageCollection,
|
|
694
|
+
sourceItems: messageChannel.getMessages(),
|
|
695
|
+
getSourceId: (record) => record.id,
|
|
696
|
+
createItem: (record) => cloneValue(record),
|
|
697
|
+
areEquivalent: areStoreMessageRecordsEquivalent
|
|
698
|
+
});
|
|
699
|
+
return messageCollection;
|
|
700
|
+
});
|
|
701
|
+
const currentIndexSignal = computed(() => {
|
|
702
|
+
version();
|
|
703
|
+
return currentIndex;
|
|
704
|
+
});
|
|
705
|
+
return {
|
|
706
|
+
historySignal,
|
|
707
|
+
messagesSignal,
|
|
708
|
+
currentIndexSignal,
|
|
709
|
+
publish(message) {
|
|
710
|
+
const record = messageChannel.publish(message);
|
|
711
|
+
messageCollection = appendStableReadonlyCollectionItem({
|
|
712
|
+
items: messageCollection,
|
|
713
|
+
item: cloneValue(record)
|
|
714
|
+
});
|
|
715
|
+
return consumeRecord(record);
|
|
716
|
+
},
|
|
717
|
+
replay(input) {
|
|
718
|
+
return replayByIds(input);
|
|
719
|
+
},
|
|
720
|
+
travelTo,
|
|
721
|
+
undo,
|
|
722
|
+
redo,
|
|
723
|
+
getHistory(key) {
|
|
724
|
+
if (key === void 0) {
|
|
725
|
+
return history.map((entry) => cloneValue(entry));
|
|
726
|
+
}
|
|
727
|
+
return history.filter((entry) => {
|
|
728
|
+
if (entry.message === null) {
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
return messageAffectsKey(entry.message, key);
|
|
732
|
+
}).map((entry) => cloneValue(entry));
|
|
733
|
+
},
|
|
734
|
+
getMessages(key) {
|
|
735
|
+
const records = messageChannel.getMessages();
|
|
736
|
+
if (key === void 0) {
|
|
737
|
+
return records.map((record) => cloneValue(record));
|
|
738
|
+
}
|
|
739
|
+
return records.filter((record) => messageAffectsKey(record.message, key)).map((record) => cloneValue(record));
|
|
740
|
+
},
|
|
741
|
+
getDeadLetters() {
|
|
742
|
+
return messageChannel.getMessages().filter((record) => record.status === "dead-letter").map((record) => toDeadLetterEntry(record));
|
|
743
|
+
},
|
|
744
|
+
replayDeadLetter,
|
|
745
|
+
replayDeadLetters,
|
|
746
|
+
getCurrentIndex() {
|
|
747
|
+
return currentIndex;
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
}
|
|
8
751
|
|
|
9
752
|
// src/store-registry.ts
|
|
10
753
|
var trackedStores = /* @__PURE__ */ new Set();
|
|
11
754
|
function trackStore(store) {
|
|
12
755
|
trackedStores.add(store);
|
|
13
756
|
}
|
|
14
|
-
function clearAllStores() {
|
|
15
|
-
for (const store of [...trackedStores]) {
|
|
16
|
-
store.clearAll();
|
|
17
|
-
}
|
|
757
|
+
function clearAllStores() {
|
|
758
|
+
for (const store of [...trackedStores]) {
|
|
759
|
+
store.clearAll();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/store-message-consumer.ts
|
|
764
|
+
import {
|
|
765
|
+
isKeyedResourceData,
|
|
766
|
+
createKeyedResourceData,
|
|
767
|
+
isAnyKeyLoading
|
|
768
|
+
} from "@flurryx/core";
|
|
769
|
+
function createDefaultState() {
|
|
770
|
+
return {
|
|
771
|
+
data: void 0,
|
|
772
|
+
isLoading: false,
|
|
773
|
+
status: void 0,
|
|
774
|
+
errors: void 0
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
function createStoreMessageConsumer(signals, notifier) {
|
|
778
|
+
function applyUpdate(key, newState, notify = true) {
|
|
779
|
+
const sig = signals.getOrCreate(key);
|
|
780
|
+
const previousState = sig();
|
|
781
|
+
sig.update((state) => ({ ...state, ...newState }));
|
|
782
|
+
if (notify) {
|
|
783
|
+
const updatedState = sig();
|
|
784
|
+
notifier.notify(key, updatedState, previousState);
|
|
785
|
+
}
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
function applyClear(key) {
|
|
789
|
+
const sig = signals.getOrCreate(key);
|
|
790
|
+
const previousState = sig();
|
|
791
|
+
sig.set(createDefaultState());
|
|
792
|
+
const nextState = sig();
|
|
793
|
+
notifier.notify(key, nextState, previousState);
|
|
794
|
+
return true;
|
|
795
|
+
}
|
|
796
|
+
function applyClearAll() {
|
|
797
|
+
const keys = Array.from(signals.getAllKeys());
|
|
798
|
+
if (keys.length === 0) {
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
keys.forEach((key) => {
|
|
802
|
+
applyClear(key);
|
|
803
|
+
});
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
function applyStartLoading(key) {
|
|
807
|
+
const sig = signals.getOrCreate(key);
|
|
808
|
+
sig.update(
|
|
809
|
+
(state) => ({
|
|
810
|
+
...state,
|
|
811
|
+
status: void 0,
|
|
812
|
+
isLoading: true,
|
|
813
|
+
errors: void 0
|
|
814
|
+
})
|
|
815
|
+
);
|
|
816
|
+
return true;
|
|
817
|
+
}
|
|
818
|
+
function applyStopLoading(key) {
|
|
819
|
+
const sig = signals.getOrCreate(key);
|
|
820
|
+
sig.update(
|
|
821
|
+
(state) => ({
|
|
822
|
+
...state,
|
|
823
|
+
isLoading: false
|
|
824
|
+
})
|
|
825
|
+
);
|
|
826
|
+
return true;
|
|
827
|
+
}
|
|
828
|
+
function applyUpdateKeyedOne(key, resourceKey, entity) {
|
|
829
|
+
const sig = signals.getOrCreate(key);
|
|
830
|
+
const state = sig();
|
|
831
|
+
const data = isKeyedResourceData(state.data) ? state.data : createKeyedResourceData();
|
|
832
|
+
const nextErrors = { ...data.errors };
|
|
833
|
+
delete nextErrors[resourceKey];
|
|
834
|
+
const nextData = {
|
|
835
|
+
...data,
|
|
836
|
+
entities: { ...data.entities, [resourceKey]: entity },
|
|
837
|
+
isLoading: { ...data.isLoading, [resourceKey]: false },
|
|
838
|
+
status: { ...data.status, [resourceKey]: "Success" },
|
|
839
|
+
errors: nextErrors
|
|
840
|
+
};
|
|
841
|
+
return applyUpdate(key, {
|
|
842
|
+
data: nextData,
|
|
843
|
+
isLoading: isAnyKeyLoading(nextData.isLoading),
|
|
844
|
+
status: void 0,
|
|
845
|
+
errors: void 0
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
function applyClearKeyedOne(key, resourceKey) {
|
|
849
|
+
const sig = signals.getOrCreate(key);
|
|
850
|
+
const previousState = sig();
|
|
851
|
+
const state = previousState;
|
|
852
|
+
if (!isKeyedResourceData(state.data)) {
|
|
853
|
+
return true;
|
|
854
|
+
}
|
|
855
|
+
const data = state.data;
|
|
856
|
+
const nextEntities = { ...data.entities };
|
|
857
|
+
delete nextEntities[resourceKey];
|
|
858
|
+
const nextIsLoading = { ...data.isLoading };
|
|
859
|
+
delete nextIsLoading[resourceKey];
|
|
860
|
+
const nextStatus = { ...data.status };
|
|
861
|
+
delete nextStatus[resourceKey];
|
|
862
|
+
const nextErrors = { ...data.errors };
|
|
863
|
+
delete nextErrors[resourceKey];
|
|
864
|
+
const nextData = {
|
|
865
|
+
...data,
|
|
866
|
+
entities: nextEntities,
|
|
867
|
+
isLoading: nextIsLoading,
|
|
868
|
+
status: nextStatus,
|
|
869
|
+
errors: nextErrors
|
|
870
|
+
};
|
|
871
|
+
sig.update(
|
|
872
|
+
(prev) => ({
|
|
873
|
+
...prev,
|
|
874
|
+
data: nextData,
|
|
875
|
+
status: void 0,
|
|
876
|
+
isLoading: isAnyKeyLoading(nextIsLoading),
|
|
877
|
+
errors: void 0
|
|
878
|
+
})
|
|
879
|
+
);
|
|
880
|
+
const updatedState = sig();
|
|
881
|
+
notifier.notify(key, updatedState, previousState);
|
|
882
|
+
return true;
|
|
883
|
+
}
|
|
884
|
+
function applyStartKeyedLoading(key, resourceKey) {
|
|
885
|
+
const sig = signals.getOrCreate(key);
|
|
886
|
+
const state = sig();
|
|
887
|
+
if (!isKeyedResourceData(state.data)) {
|
|
888
|
+
return applyStartLoading(key);
|
|
889
|
+
}
|
|
890
|
+
const previousState = state;
|
|
891
|
+
const data = state.data;
|
|
892
|
+
const nextIsLoading = {
|
|
893
|
+
...data.isLoading,
|
|
894
|
+
[resourceKey]: true
|
|
895
|
+
};
|
|
896
|
+
const nextStatus = { ...data.status };
|
|
897
|
+
delete nextStatus[resourceKey];
|
|
898
|
+
const nextErrors = { ...data.errors };
|
|
899
|
+
delete nextErrors[resourceKey];
|
|
900
|
+
const nextData = {
|
|
901
|
+
...data,
|
|
902
|
+
isLoading: nextIsLoading,
|
|
903
|
+
status: nextStatus,
|
|
904
|
+
errors: nextErrors
|
|
905
|
+
};
|
|
906
|
+
sig.update(
|
|
907
|
+
(previous) => ({
|
|
908
|
+
...previous,
|
|
909
|
+
data: nextData,
|
|
910
|
+
status: void 0,
|
|
911
|
+
isLoading: isAnyKeyLoading(nextIsLoading),
|
|
912
|
+
errors: void 0
|
|
913
|
+
})
|
|
914
|
+
);
|
|
915
|
+
const updatedState = sig();
|
|
916
|
+
notifier.notify(key, updatedState, previousState);
|
|
917
|
+
return true;
|
|
918
|
+
}
|
|
919
|
+
function applyMessage(message) {
|
|
920
|
+
switch (message.type) {
|
|
921
|
+
case "update":
|
|
922
|
+
return applyUpdate(message.key, cloneValue(message.state));
|
|
923
|
+
case "clear":
|
|
924
|
+
return applyClear(message.key);
|
|
925
|
+
case "clearAll":
|
|
926
|
+
return applyClearAll();
|
|
927
|
+
case "startLoading":
|
|
928
|
+
return applyStartLoading(message.key);
|
|
929
|
+
case "stopLoading":
|
|
930
|
+
return applyStopLoading(message.key);
|
|
931
|
+
case "updateKeyedOne":
|
|
932
|
+
return applyUpdateKeyedOne(
|
|
933
|
+
message.key,
|
|
934
|
+
message.resourceKey,
|
|
935
|
+
cloneValue(message.entity)
|
|
936
|
+
);
|
|
937
|
+
case "clearKeyedOne":
|
|
938
|
+
return applyClearKeyedOne(message.key, message.resourceKey);
|
|
939
|
+
case "startKeyedLoading":
|
|
940
|
+
return applyStartKeyedLoading(message.key, message.resourceKey);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
function applySnapshot(snapshot) {
|
|
944
|
+
const keys = /* @__PURE__ */ new Set([
|
|
945
|
+
...Array.from(signals.getAllKeys()),
|
|
946
|
+
...Object.keys(snapshot)
|
|
947
|
+
]);
|
|
948
|
+
keys.forEach((rawKey) => {
|
|
949
|
+
const key = rawKey;
|
|
950
|
+
const sig = signals.getOrCreate(key);
|
|
951
|
+
const snapshotState = snapshot[key] ?? createDefaultState();
|
|
952
|
+
applyUpdate(key, createSnapshotRestorePatch(sig(), snapshotState), true);
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
function captureSnapshot() {
|
|
956
|
+
const entries = Array.from(signals.getAllKeys()).map((key) => [
|
|
957
|
+
key,
|
|
958
|
+
cloneValue(signals.getOrCreate(key)())
|
|
959
|
+
]);
|
|
960
|
+
return Object.fromEntries(entries);
|
|
961
|
+
}
|
|
962
|
+
return {
|
|
963
|
+
applyMessage,
|
|
964
|
+
applySnapshot,
|
|
965
|
+
createSnapshot: captureSnapshot
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
function createUpdateMessage(key, state) {
|
|
969
|
+
return { type: "update", key, state };
|
|
970
|
+
}
|
|
971
|
+
function createClearMessage(key) {
|
|
972
|
+
return { type: "clear", key };
|
|
973
|
+
}
|
|
974
|
+
function createClearAllMessage() {
|
|
975
|
+
return { type: "clearAll" };
|
|
976
|
+
}
|
|
977
|
+
function createStartLoadingMessage(key) {
|
|
978
|
+
return { type: "startLoading", key };
|
|
979
|
+
}
|
|
980
|
+
function createStopLoadingMessage(key) {
|
|
981
|
+
return { type: "stopLoading", key };
|
|
982
|
+
}
|
|
983
|
+
function createUpdateKeyedOneMessage(key, resourceKey, entity) {
|
|
984
|
+
return {
|
|
985
|
+
type: "updateKeyedOne",
|
|
986
|
+
key,
|
|
987
|
+
resourceKey,
|
|
988
|
+
entity
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
function createClearKeyedOneMessage(key, resourceKey) {
|
|
992
|
+
return {
|
|
993
|
+
type: "clearKeyedOne",
|
|
994
|
+
key,
|
|
995
|
+
resourceKey
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
function createStartKeyedLoadingMessage(key, resourceKey) {
|
|
999
|
+
return {
|
|
1000
|
+
type: "startKeyedLoading",
|
|
1001
|
+
key,
|
|
1002
|
+
resourceKey
|
|
1003
|
+
};
|
|
18
1004
|
}
|
|
19
1005
|
|
|
20
1006
|
// src/base-store.ts
|
|
21
1007
|
var updateHooksMap = /* @__PURE__ */ new WeakMap();
|
|
22
1008
|
var BaseStore = class {
|
|
23
|
-
constructor(storeEnum) {
|
|
1009
|
+
constructor(storeEnum, options) {
|
|
24
1010
|
this.storeEnum = storeEnum;
|
|
1011
|
+
this.storeKeys = Object.keys(storeEnum);
|
|
25
1012
|
this.initializeState();
|
|
26
1013
|
updateHooksMap.set(this, /* @__PURE__ */ new Map());
|
|
1014
|
+
const consumer = createStoreMessageConsumer(
|
|
1015
|
+
{
|
|
1016
|
+
getOrCreate: (key) => this.signalsState.get(key),
|
|
1017
|
+
getAllKeys: () => this.storeKeys
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
notify: (key, next, prev) => this.notifyUpdateHooks(key, next, prev)
|
|
1021
|
+
}
|
|
1022
|
+
);
|
|
1023
|
+
this.historyDriver = createStoreHistory({
|
|
1024
|
+
captureSnapshot: () => consumer.createSnapshot(),
|
|
1025
|
+
applySnapshot: (snapshot) => consumer.applySnapshot(snapshot),
|
|
1026
|
+
applyMessage: (message) => consumer.applyMessage(message),
|
|
1027
|
+
channel: options?.channel
|
|
1028
|
+
});
|
|
1029
|
+
this.history = this.historyDriver.historySignal;
|
|
1030
|
+
this.messages = this.historyDriver.messagesSignal;
|
|
1031
|
+
this.currentIndex = this.historyDriver.currentIndexSignal;
|
|
1032
|
+
this.keys = signal2([...this.storeKeys]).asReadonly();
|
|
27
1033
|
trackStore(this);
|
|
28
1034
|
}
|
|
29
1035
|
signalsState = /* @__PURE__ */ new Map();
|
|
1036
|
+
storeKeys;
|
|
1037
|
+
historyDriver;
|
|
1038
|
+
/** @inheritDoc */
|
|
1039
|
+
travelTo = (index) => this.historyDriver.travelTo(index);
|
|
1040
|
+
/** @inheritDoc */
|
|
1041
|
+
undo = () => this.historyDriver.undo();
|
|
1042
|
+
/** @inheritDoc */
|
|
1043
|
+
redo = () => this.historyDriver.redo();
|
|
1044
|
+
/** @inheritDoc */
|
|
1045
|
+
getDeadLetters = () => this.historyDriver.getDeadLetters();
|
|
1046
|
+
/** @inheritDoc */
|
|
1047
|
+
replayDeadLetter = (id) => this.historyDriver.replayDeadLetter(id);
|
|
1048
|
+
/** @inheritDoc */
|
|
1049
|
+
replayDeadLetters = () => this.historyDriver.replayDeadLetters();
|
|
1050
|
+
/** @inheritDoc */
|
|
1051
|
+
getCurrentIndex = () => this.historyDriver.getCurrentIndex();
|
|
1052
|
+
/** @inheritDoc */
|
|
1053
|
+
history;
|
|
1054
|
+
/** @inheritDoc */
|
|
1055
|
+
messages;
|
|
1056
|
+
/** @inheritDoc */
|
|
1057
|
+
currentIndex;
|
|
1058
|
+
/** @inheritDoc */
|
|
1059
|
+
keys;
|
|
1060
|
+
replay(idOrIds) {
|
|
1061
|
+
if (Array.isArray(idOrIds)) {
|
|
1062
|
+
return this.historyDriver.replay(idOrIds);
|
|
1063
|
+
}
|
|
1064
|
+
return this.historyDriver.replay(idOrIds);
|
|
1065
|
+
}
|
|
1066
|
+
getHistory(key) {
|
|
1067
|
+
if (key === void 0) {
|
|
1068
|
+
return this.historyDriver.getHistory();
|
|
1069
|
+
}
|
|
1070
|
+
return this.historyDriver.getHistory(key);
|
|
1071
|
+
}
|
|
1072
|
+
getMessages(key) {
|
|
1073
|
+
if (key === void 0) {
|
|
1074
|
+
return this.historyDriver.getMessages();
|
|
1075
|
+
}
|
|
1076
|
+
return this.historyDriver.getMessages(key);
|
|
1077
|
+
}
|
|
30
1078
|
/**
|
|
31
1079
|
* Returns a **read-only** `Signal` for the given store slot.
|
|
32
1080
|
*
|
|
@@ -71,23 +1119,13 @@ var BaseStore = class {
|
|
|
71
1119
|
* @param newState - Partial state to merge (e.g. `{ data: newData, status: 'Success' }`).
|
|
72
1120
|
*/
|
|
73
1121
|
update(key, newState) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
const previousState = currentState();
|
|
79
|
-
currentState.update((state) => ({
|
|
80
|
-
...state,
|
|
81
|
-
...newState
|
|
82
|
-
}));
|
|
83
|
-
const updatedState = currentState();
|
|
84
|
-
this.notifyUpdateHooks(key, updatedState, previousState);
|
|
1122
|
+
this.historyDriver.publish(
|
|
1123
|
+
createUpdateMessage(key, cloneValue(newState))
|
|
1124
|
+
);
|
|
85
1125
|
}
|
|
86
1126
|
/** Resets every slot in this store to its initial idle state. */
|
|
87
1127
|
clearAll() {
|
|
88
|
-
|
|
89
|
-
this.clear(key);
|
|
90
|
-
});
|
|
1128
|
+
this.historyDriver.publish(createClearAllMessage());
|
|
91
1129
|
}
|
|
92
1130
|
/**
|
|
93
1131
|
* Resets a single slot to `{ data: undefined, isLoading: false, status: undefined, errors: undefined }`.
|
|
@@ -95,20 +1133,7 @@ var BaseStore = class {
|
|
|
95
1133
|
* @param key - The slot to clear.
|
|
96
1134
|
*/
|
|
97
1135
|
clear(key) {
|
|
98
|
-
|
|
99
|
-
if (!currentState) {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
const previousState = currentState();
|
|
103
|
-
const _typedKey = key;
|
|
104
|
-
currentState.set({
|
|
105
|
-
data: void 0,
|
|
106
|
-
isLoading: false,
|
|
107
|
-
status: void 0,
|
|
108
|
-
errors: void 0
|
|
109
|
-
});
|
|
110
|
-
const nextState = currentState();
|
|
111
|
-
this.notifyUpdateHooks(key, nextState, previousState);
|
|
1136
|
+
this.historyDriver.publish(createClearMessage(key));
|
|
112
1137
|
}
|
|
113
1138
|
/**
|
|
114
1139
|
* Marks a slot as loading: sets `isLoading: true` and clears `status` and `errors`.
|
|
@@ -116,19 +1141,7 @@ var BaseStore = class {
|
|
|
116
1141
|
* @param key - The slot to mark as loading.
|
|
117
1142
|
*/
|
|
118
1143
|
startLoading(key) {
|
|
119
|
-
|
|
120
|
-
if (!currentState) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const _typedKey = key;
|
|
124
|
-
currentState.update(
|
|
125
|
-
(state) => ({
|
|
126
|
-
...state,
|
|
127
|
-
status: void 0,
|
|
128
|
-
isLoading: true,
|
|
129
|
-
errors: void 0
|
|
130
|
-
})
|
|
131
|
-
);
|
|
1144
|
+
this.historyDriver.publish(createStartLoadingMessage(key));
|
|
132
1145
|
}
|
|
133
1146
|
/**
|
|
134
1147
|
* Marks a slot as no longer loading: sets `isLoading: false`.
|
|
@@ -137,17 +1150,7 @@ var BaseStore = class {
|
|
|
137
1150
|
* @param key - The slot to stop loading.
|
|
138
1151
|
*/
|
|
139
1152
|
stopLoading(key) {
|
|
140
|
-
|
|
141
|
-
if (!currentState) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const _typedKey = key;
|
|
145
|
-
currentState.update(
|
|
146
|
-
(state) => ({
|
|
147
|
-
...state,
|
|
148
|
-
isLoading: false
|
|
149
|
-
})
|
|
150
|
-
);
|
|
1153
|
+
this.historyDriver.publish(createStopLoadingMessage(key));
|
|
151
1154
|
}
|
|
152
1155
|
/**
|
|
153
1156
|
* Merges a single entity into a {@link KeyedResourceData} slot.
|
|
@@ -159,36 +1162,13 @@ var BaseStore = class {
|
|
|
159
1162
|
* @param entity - The entity value to store.
|
|
160
1163
|
*/
|
|
161
1164
|
updateKeyedOne(key, resourceKey, entity) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
delete nextErrors[resourceKey];
|
|
170
|
-
const nextData = {
|
|
171
|
-
...data,
|
|
172
|
-
entities: {
|
|
173
|
-
...data.entities,
|
|
174
|
-
[resourceKey]: entity
|
|
175
|
-
},
|
|
176
|
-
isLoading: {
|
|
177
|
-
...data.isLoading,
|
|
178
|
-
[resourceKey]: false
|
|
179
|
-
},
|
|
180
|
-
status: {
|
|
181
|
-
...data.status,
|
|
182
|
-
[resourceKey]: "Success"
|
|
183
|
-
},
|
|
184
|
-
errors: nextErrors
|
|
185
|
-
};
|
|
186
|
-
this.update(key, {
|
|
187
|
-
data: nextData,
|
|
188
|
-
isLoading: isAnyKeyLoading(nextData.isLoading),
|
|
189
|
-
status: void 0,
|
|
190
|
-
errors: void 0
|
|
191
|
-
});
|
|
1165
|
+
this.historyDriver.publish(
|
|
1166
|
+
createUpdateKeyedOneMessage(
|
|
1167
|
+
key,
|
|
1168
|
+
resourceKey,
|
|
1169
|
+
cloneValue(entity)
|
|
1170
|
+
)
|
|
1171
|
+
);
|
|
192
1172
|
}
|
|
193
1173
|
/**
|
|
194
1174
|
* Removes a single entity from a {@link KeyedResourceData} slot,
|
|
@@ -199,43 +1179,9 @@ var BaseStore = class {
|
|
|
199
1179
|
* @param resourceKey - The entity identifier to remove.
|
|
200
1180
|
*/
|
|
201
1181
|
clearKeyedOne(key, resourceKey) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
const previousState = currentState();
|
|
207
|
-
const state = previousState;
|
|
208
|
-
if (!isKeyedResourceData(state.data)) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
const data = state.data;
|
|
212
|
-
const nextEntities = { ...data.entities };
|
|
213
|
-
delete nextEntities[resourceKey];
|
|
214
|
-
const nextIsLoading = { ...data.isLoading };
|
|
215
|
-
delete nextIsLoading[resourceKey];
|
|
216
|
-
const nextStatus = { ...data.status };
|
|
217
|
-
delete nextStatus[resourceKey];
|
|
218
|
-
const nextErrors = { ...data.errors };
|
|
219
|
-
delete nextErrors[resourceKey];
|
|
220
|
-
const nextData = {
|
|
221
|
-
...data,
|
|
222
|
-
entities: nextEntities,
|
|
223
|
-
isLoading: nextIsLoading,
|
|
224
|
-
status: nextStatus,
|
|
225
|
-
errors: nextErrors
|
|
226
|
-
};
|
|
227
|
-
const _typedKey = key;
|
|
228
|
-
currentState.update(
|
|
229
|
-
(prev) => ({
|
|
230
|
-
...prev,
|
|
231
|
-
data: nextData,
|
|
232
|
-
status: void 0,
|
|
233
|
-
isLoading: isAnyKeyLoading(nextIsLoading),
|
|
234
|
-
errors: void 0
|
|
235
|
-
})
|
|
1182
|
+
this.historyDriver.publish(
|
|
1183
|
+
createClearKeyedOneMessage(key, resourceKey)
|
|
236
1184
|
);
|
|
237
|
-
const updatedState = currentState();
|
|
238
|
-
this.notifyUpdateHooks(key, updatedState, previousState);
|
|
239
1185
|
}
|
|
240
1186
|
/**
|
|
241
1187
|
* Marks a single entity within a keyed slot as loading.
|
|
@@ -246,47 +1192,9 @@ var BaseStore = class {
|
|
|
246
1192
|
* @param resourceKey - The entity identifier to mark as loading.
|
|
247
1193
|
*/
|
|
248
1194
|
startKeyedLoading(key, resourceKey) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
const _typedKey = key;
|
|
254
|
-
const state = currentState();
|
|
255
|
-
if (!isKeyedResourceData(state.data)) {
|
|
256
|
-
this.startLoading(key);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const previousState = state;
|
|
260
|
-
const data = state.data;
|
|
261
|
-
const nextIsLoading = {
|
|
262
|
-
...data.isLoading,
|
|
263
|
-
[resourceKey]: true
|
|
264
|
-
};
|
|
265
|
-
const nextStatus = {
|
|
266
|
-
...data.status
|
|
267
|
-
};
|
|
268
|
-
delete nextStatus[resourceKey];
|
|
269
|
-
const nextErrors = {
|
|
270
|
-
...data.errors
|
|
271
|
-
};
|
|
272
|
-
delete nextErrors[resourceKey];
|
|
273
|
-
const nextData = {
|
|
274
|
-
...data,
|
|
275
|
-
isLoading: nextIsLoading,
|
|
276
|
-
status: nextStatus,
|
|
277
|
-
errors: nextErrors
|
|
278
|
-
};
|
|
279
|
-
currentState.update(
|
|
280
|
-
(previous) => ({
|
|
281
|
-
...previous,
|
|
282
|
-
data: nextData,
|
|
283
|
-
status: void 0,
|
|
284
|
-
isLoading: isAnyKeyLoading(nextIsLoading),
|
|
285
|
-
errors: void 0
|
|
286
|
-
})
|
|
1195
|
+
this.historyDriver.publish(
|
|
1196
|
+
createStartKeyedLoadingMessage(key, resourceKey)
|
|
287
1197
|
);
|
|
288
|
-
const updatedState = currentState();
|
|
289
|
-
this.notifyUpdateHooks(key, updatedState, previousState);
|
|
290
1198
|
}
|
|
291
1199
|
notifyUpdateHooks(key, nextState, previousState) {
|
|
292
1200
|
const hooks = updateHooksMap.get(this);
|
|
@@ -294,191 +1202,165 @@ var BaseStore = class {
|
|
|
294
1202
|
if (!keyHooks) {
|
|
295
1203
|
return;
|
|
296
1204
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
1205
|
+
const errors = [];
|
|
1206
|
+
keyHooks.forEach((hook) => {
|
|
1207
|
+
try {
|
|
1208
|
+
hook(
|
|
1209
|
+
nextState,
|
|
1210
|
+
previousState
|
|
1211
|
+
);
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
errors.push(error);
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
if (errors.length > 0) {
|
|
1217
|
+
queueMicrotask(() => {
|
|
1218
|
+
if (errors.length === 1) {
|
|
1219
|
+
throw errors[0];
|
|
1220
|
+
}
|
|
1221
|
+
throw new AggregateError(
|
|
1222
|
+
errors,
|
|
1223
|
+
`${errors.length} onUpdate hooks threw for key "${String(key)}"`
|
|
1224
|
+
);
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
303
1227
|
}
|
|
304
1228
|
initializeState() {
|
|
305
|
-
|
|
306
|
-
const _typedKey = key;
|
|
307
|
-
const initialState = {
|
|
308
|
-
data: void 0,
|
|
309
|
-
isLoading: false,
|
|
310
|
-
status: void 0,
|
|
311
|
-
errors: void 0
|
|
312
|
-
};
|
|
1229
|
+
this.storeKeys.forEach((key) => {
|
|
313
1230
|
this.signalsState.set(
|
|
314
|
-
|
|
315
|
-
|
|
1231
|
+
key,
|
|
1232
|
+
signal2(createDefaultState())
|
|
316
1233
|
);
|
|
317
1234
|
});
|
|
318
1235
|
}
|
|
319
1236
|
};
|
|
320
1237
|
|
|
321
1238
|
// src/lazy-store.ts
|
|
322
|
-
import { signal as
|
|
323
|
-
import {
|
|
324
|
-
isAnyKeyLoading as isAnyKeyLoading2,
|
|
325
|
-
isKeyedResourceData as isKeyedResourceData2,
|
|
326
|
-
createKeyedResourceData as createKeyedResourceData2
|
|
327
|
-
} from "@flurryx/core";
|
|
328
|
-
function createDefaultState() {
|
|
329
|
-
return {
|
|
330
|
-
data: void 0,
|
|
331
|
-
isLoading: false,
|
|
332
|
-
status: void 0,
|
|
333
|
-
errors: void 0
|
|
334
|
-
};
|
|
335
|
-
}
|
|
1239
|
+
import { signal as signal3 } from "@angular/core";
|
|
336
1240
|
var LazyStore = class {
|
|
337
1241
|
signals = /* @__PURE__ */ new Map();
|
|
338
1242
|
hooks = /* @__PURE__ */ new Map();
|
|
339
|
-
|
|
1243
|
+
historyDriver;
|
|
1244
|
+
/** @inheritDoc */
|
|
1245
|
+
travelTo = (index) => this.historyDriver.travelTo(index);
|
|
1246
|
+
/** @inheritDoc */
|
|
1247
|
+
undo = () => this.historyDriver.undo();
|
|
1248
|
+
/** @inheritDoc */
|
|
1249
|
+
redo = () => this.historyDriver.redo();
|
|
1250
|
+
getMessages(key) {
|
|
1251
|
+
if (key === void 0) {
|
|
1252
|
+
return this.historyDriver.getMessages();
|
|
1253
|
+
}
|
|
1254
|
+
return this.historyDriver.getMessages(key);
|
|
1255
|
+
}
|
|
1256
|
+
getDeadLetters = () => this.historyDriver.getDeadLetters();
|
|
1257
|
+
/** @inheritDoc */
|
|
1258
|
+
replayDeadLetter = (id) => this.historyDriver.replayDeadLetter(id);
|
|
1259
|
+
/** @inheritDoc */
|
|
1260
|
+
replayDeadLetters = () => this.historyDriver.replayDeadLetters();
|
|
1261
|
+
/** @inheritDoc */
|
|
1262
|
+
getCurrentIndex = () => this.historyDriver.getCurrentIndex();
|
|
1263
|
+
/** @inheritDoc */
|
|
1264
|
+
history;
|
|
1265
|
+
/** @inheritDoc */
|
|
1266
|
+
messages;
|
|
1267
|
+
/** @inheritDoc */
|
|
1268
|
+
currentIndex;
|
|
1269
|
+
/** @inheritDoc */
|
|
1270
|
+
keys;
|
|
1271
|
+
keysSignal = signal3([]);
|
|
1272
|
+
replay(idOrIds) {
|
|
1273
|
+
if (Array.isArray(idOrIds)) {
|
|
1274
|
+
return this.historyDriver.replay(idOrIds);
|
|
1275
|
+
}
|
|
1276
|
+
return this.historyDriver.replay(idOrIds);
|
|
1277
|
+
}
|
|
1278
|
+
getHistory(key) {
|
|
1279
|
+
if (key === void 0) {
|
|
1280
|
+
return this.historyDriver.getHistory();
|
|
1281
|
+
}
|
|
1282
|
+
return this.historyDriver.getHistory(key);
|
|
1283
|
+
}
|
|
1284
|
+
constructor(options) {
|
|
1285
|
+
const consumer = createStoreMessageConsumer(
|
|
1286
|
+
{
|
|
1287
|
+
getOrCreate: (key) => this.getOrCreate(key),
|
|
1288
|
+
getAllKeys: () => this.signals.keys()
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
notify: (key, next, prev) => this.notifyHooks(key, next, prev)
|
|
1292
|
+
}
|
|
1293
|
+
);
|
|
1294
|
+
this.historyDriver = createStoreHistory({
|
|
1295
|
+
captureSnapshot: () => consumer.createSnapshot(),
|
|
1296
|
+
applySnapshot: (snapshot) => consumer.applySnapshot(snapshot),
|
|
1297
|
+
applyMessage: (message) => consumer.applyMessage(message),
|
|
1298
|
+
channel: options?.channel
|
|
1299
|
+
});
|
|
1300
|
+
this.history = this.historyDriver.historySignal;
|
|
1301
|
+
this.messages = this.historyDriver.messagesSignal;
|
|
1302
|
+
this.currentIndex = this.historyDriver.currentIndexSignal;
|
|
1303
|
+
this.keys = this.keysSignal.asReadonly();
|
|
340
1304
|
trackStore(this);
|
|
341
1305
|
}
|
|
342
1306
|
getOrCreate(key) {
|
|
343
1307
|
let sig = this.signals.get(key);
|
|
344
1308
|
if (!sig) {
|
|
345
|
-
sig =
|
|
1309
|
+
sig = signal3(createDefaultState());
|
|
346
1310
|
this.signals.set(key, sig);
|
|
1311
|
+
this.keysSignal.update((prev) => [...prev, key]);
|
|
347
1312
|
}
|
|
348
1313
|
return sig;
|
|
349
1314
|
}
|
|
1315
|
+
/** @inheritDoc */
|
|
350
1316
|
get(key) {
|
|
351
1317
|
return this.getOrCreate(key);
|
|
352
1318
|
}
|
|
1319
|
+
/** @inheritDoc */
|
|
353
1320
|
update(key, newState) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const nextState = sig();
|
|
358
|
-
this.notifyHooks(key, nextState, previousState);
|
|
1321
|
+
this.historyDriver.publish(
|
|
1322
|
+
createUpdateMessage(key, cloneValue(newState))
|
|
1323
|
+
);
|
|
359
1324
|
}
|
|
1325
|
+
/** @inheritDoc */
|
|
360
1326
|
clear(key) {
|
|
361
|
-
|
|
362
|
-
const previousState = sig();
|
|
363
|
-
sig.set(createDefaultState());
|
|
364
|
-
const nextState = sig();
|
|
365
|
-
this.notifyHooks(key, nextState, previousState);
|
|
1327
|
+
this.historyDriver.publish(createClearMessage(key));
|
|
366
1328
|
}
|
|
1329
|
+
/** @inheritDoc */
|
|
367
1330
|
clearAll() {
|
|
368
|
-
|
|
369
|
-
this.clear(key);
|
|
370
|
-
}
|
|
1331
|
+
this.historyDriver.publish(createClearAllMessage());
|
|
371
1332
|
}
|
|
1333
|
+
/** @inheritDoc */
|
|
372
1334
|
startLoading(key) {
|
|
373
|
-
|
|
374
|
-
sig.update(
|
|
375
|
-
(state) => ({
|
|
376
|
-
...state,
|
|
377
|
-
status: void 0,
|
|
378
|
-
isLoading: true,
|
|
379
|
-
errors: void 0
|
|
380
|
-
})
|
|
381
|
-
);
|
|
1335
|
+
this.historyDriver.publish(createStartLoadingMessage(key));
|
|
382
1336
|
}
|
|
1337
|
+
/** @inheritDoc */
|
|
383
1338
|
stopLoading(key) {
|
|
384
|
-
|
|
385
|
-
sig.update(
|
|
386
|
-
(state) => ({
|
|
387
|
-
...state,
|
|
388
|
-
isLoading: false
|
|
389
|
-
})
|
|
390
|
-
);
|
|
1339
|
+
this.historyDriver.publish(createStopLoadingMessage(key));
|
|
391
1340
|
}
|
|
1341
|
+
/** @inheritDoc */
|
|
392
1342
|
updateKeyedOne(key, resourceKey, entity) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
entities: { ...data.entities, [resourceKey]: entity },
|
|
401
|
-
isLoading: { ...data.isLoading, [resourceKey]: false },
|
|
402
|
-
status: { ...data.status, [resourceKey]: "Success" },
|
|
403
|
-
errors: nextErrors
|
|
404
|
-
};
|
|
405
|
-
this.update(key, {
|
|
406
|
-
data: nextData,
|
|
407
|
-
isLoading: isAnyKeyLoading2(nextData.isLoading),
|
|
408
|
-
status: void 0,
|
|
409
|
-
errors: void 0
|
|
410
|
-
});
|
|
1343
|
+
this.historyDriver.publish(
|
|
1344
|
+
createUpdateKeyedOneMessage(
|
|
1345
|
+
key,
|
|
1346
|
+
resourceKey,
|
|
1347
|
+
cloneValue(entity)
|
|
1348
|
+
)
|
|
1349
|
+
);
|
|
411
1350
|
}
|
|
1351
|
+
/** @inheritDoc */
|
|
412
1352
|
clearKeyedOne(key, resourceKey) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if (!isKeyedResourceData2(state.data)) {
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
const data = state.data;
|
|
419
|
-
const previousState = state;
|
|
420
|
-
const nextEntities = { ...data.entities };
|
|
421
|
-
delete nextEntities[resourceKey];
|
|
422
|
-
const nextIsLoading = { ...data.isLoading };
|
|
423
|
-
delete nextIsLoading[resourceKey];
|
|
424
|
-
const nextStatus = { ...data.status };
|
|
425
|
-
delete nextStatus[resourceKey];
|
|
426
|
-
const nextErrors = { ...data.errors };
|
|
427
|
-
delete nextErrors[resourceKey];
|
|
428
|
-
const nextData = {
|
|
429
|
-
...data,
|
|
430
|
-
entities: nextEntities,
|
|
431
|
-
isLoading: nextIsLoading,
|
|
432
|
-
status: nextStatus,
|
|
433
|
-
errors: nextErrors
|
|
434
|
-
};
|
|
435
|
-
sig.update(
|
|
436
|
-
(prev) => ({
|
|
437
|
-
...prev,
|
|
438
|
-
data: nextData,
|
|
439
|
-
status: void 0,
|
|
440
|
-
isLoading: isAnyKeyLoading2(nextIsLoading),
|
|
441
|
-
errors: void 0
|
|
442
|
-
})
|
|
1353
|
+
this.historyDriver.publish(
|
|
1354
|
+
createClearKeyedOneMessage(key, resourceKey)
|
|
443
1355
|
);
|
|
444
|
-
const updatedState = sig();
|
|
445
|
-
this.notifyHooks(key, updatedState, previousState);
|
|
446
1356
|
}
|
|
1357
|
+
/** @inheritDoc */
|
|
447
1358
|
startKeyedLoading(key, resourceKey) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
if (!isKeyedResourceData2(state.data)) {
|
|
451
|
-
this.startLoading(key);
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
const previousState = state;
|
|
455
|
-
const data = state.data;
|
|
456
|
-
const nextIsLoading = {
|
|
457
|
-
...data.isLoading,
|
|
458
|
-
[resourceKey]: true
|
|
459
|
-
};
|
|
460
|
-
const nextStatus = { ...data.status };
|
|
461
|
-
delete nextStatus[resourceKey];
|
|
462
|
-
const nextErrors = { ...data.errors };
|
|
463
|
-
delete nextErrors[resourceKey];
|
|
464
|
-
const nextData = {
|
|
465
|
-
...data,
|
|
466
|
-
isLoading: nextIsLoading,
|
|
467
|
-
status: nextStatus,
|
|
468
|
-
errors: nextErrors
|
|
469
|
-
};
|
|
470
|
-
sig.update(
|
|
471
|
-
(previous) => ({
|
|
472
|
-
...previous,
|
|
473
|
-
data: nextData,
|
|
474
|
-
status: void 0,
|
|
475
|
-
isLoading: isAnyKeyLoading2(nextIsLoading),
|
|
476
|
-
errors: void 0
|
|
477
|
-
})
|
|
1359
|
+
this.historyDriver.publish(
|
|
1360
|
+
createStartKeyedLoadingMessage(key, resourceKey)
|
|
478
1361
|
);
|
|
479
|
-
const updatedState = sig();
|
|
480
|
-
this.notifyHooks(key, updatedState, previousState);
|
|
481
1362
|
}
|
|
1363
|
+
/** @inheritDoc */
|
|
482
1364
|
onUpdate(key, callback) {
|
|
483
1365
|
if (!this.hooks.has(key)) {
|
|
484
1366
|
this.hooks.set(key, []);
|
|
@@ -501,12 +1383,28 @@ var LazyStore = class {
|
|
|
501
1383
|
if (!keyHooks) {
|
|
502
1384
|
return;
|
|
503
1385
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
1386
|
+
const errors = [];
|
|
1387
|
+
keyHooks.forEach((hook) => {
|
|
1388
|
+
try {
|
|
1389
|
+
hook(
|
|
1390
|
+
nextState,
|
|
1391
|
+
previousState
|
|
1392
|
+
);
|
|
1393
|
+
} catch (error) {
|
|
1394
|
+
errors.push(error);
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
if (errors.length > 0) {
|
|
1398
|
+
queueMicrotask(() => {
|
|
1399
|
+
if (errors.length === 1) {
|
|
1400
|
+
throw errors[0];
|
|
1401
|
+
}
|
|
1402
|
+
throw new AggregateError(
|
|
1403
|
+
errors,
|
|
1404
|
+
`${errors.length} onUpdate hooks threw for key "${String(key)}"`
|
|
1405
|
+
);
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
510
1408
|
}
|
|
511
1409
|
};
|
|
512
1410
|
|
|
@@ -515,12 +1413,12 @@ import { InjectionToken, inject } from "@angular/core";
|
|
|
515
1413
|
|
|
516
1414
|
// src/dynamic-store.ts
|
|
517
1415
|
var DynamicStore = class extends BaseStore {
|
|
518
|
-
constructor(config) {
|
|
1416
|
+
constructor(config, options) {
|
|
519
1417
|
const identityEnum = Object.keys(config).reduce(
|
|
520
1418
|
(acc, key) => ({ ...acc, [key]: key }),
|
|
521
1419
|
{}
|
|
522
1420
|
);
|
|
523
|
-
super(identityEnum);
|
|
1421
|
+
super(identityEnum, options);
|
|
524
1422
|
}
|
|
525
1423
|
};
|
|
526
1424
|
|
|
@@ -541,12 +1439,12 @@ function mirrorKey(source, sourceKey, target, targetKeyOrOptions, options) {
|
|
|
541
1439
|
}
|
|
542
1440
|
|
|
543
1441
|
// src/collect-keyed.ts
|
|
544
|
-
import { createKeyedResourceData as
|
|
1442
|
+
import { createKeyedResourceData as createKeyedResourceData2, isAnyKeyLoading as isAnyKeyLoading2 } from "@flurryx/core";
|
|
545
1443
|
function collectKeyed(source, sourceKey, target, targetKeyOrOptions, options) {
|
|
546
1444
|
const resolvedTargetKey = typeof targetKeyOrOptions === "string" ? targetKeyOrOptions : sourceKey;
|
|
547
1445
|
const resolvedOptions = typeof targetKeyOrOptions === "object" ? targetKeyOrOptions : options;
|
|
548
1446
|
target.update(resolvedTargetKey, {
|
|
549
|
-
data:
|
|
1447
|
+
data: createKeyedResourceData2()
|
|
550
1448
|
});
|
|
551
1449
|
let previousId;
|
|
552
1450
|
const cleanup = source.onUpdate(sourceKey, (state) => {
|
|
@@ -577,7 +1475,7 @@ function collectKeyed(source, sourceKey, target, targetKeyOrOptions, options) {
|
|
|
577
1475
|
};
|
|
578
1476
|
target.update(resolvedTargetKey, {
|
|
579
1477
|
data: updatedKeyed,
|
|
580
|
-
isLoading:
|
|
1478
|
+
isLoading: isAnyKeyLoading2(newIsLoading),
|
|
581
1479
|
status: "Success"
|
|
582
1480
|
});
|
|
583
1481
|
previousId = currentId;
|
|
@@ -599,7 +1497,7 @@ function collectKeyed(source, sourceKey, target, targetKeyOrOptions, options) {
|
|
|
599
1497
|
};
|
|
600
1498
|
target.update(resolvedTargetKey, {
|
|
601
1499
|
data: updatedKeyed,
|
|
602
|
-
isLoading:
|
|
1500
|
+
isLoading: isAnyKeyLoading2(newIsLoading)
|
|
603
1501
|
});
|
|
604
1502
|
previousId = currentId;
|
|
605
1503
|
} else if (resourceState.data === void 0 && previousId !== void 0) {
|
|
@@ -615,7 +1513,7 @@ function collectKeyed(source, sourceKey, target, targetKeyOrOptions, options) {
|
|
|
615
1513
|
};
|
|
616
1514
|
target.update(resolvedTargetKey, {
|
|
617
1515
|
data: updatedKeyed,
|
|
618
|
-
isLoading:
|
|
1516
|
+
isLoading: isAnyKeyLoading2(remainingLoading)
|
|
619
1517
|
});
|
|
620
1518
|
previousId = void 0;
|
|
621
1519
|
} else if (resourceState.isLoading && currentId !== void 0) {
|
|
@@ -655,7 +1553,6 @@ function wireMirrors(store, mirrors) {
|
|
|
655
1553
|
def.targetKey
|
|
656
1554
|
);
|
|
657
1555
|
}
|
|
658
|
-
return store;
|
|
659
1556
|
}
|
|
660
1557
|
function wireMirrorKeyed(store, defs) {
|
|
661
1558
|
for (const def of defs) {
|
|
@@ -670,7 +1567,6 @@ function wireMirrorKeyed(store, defs) {
|
|
|
670
1567
|
}
|
|
671
1568
|
);
|
|
672
1569
|
}
|
|
673
|
-
return store;
|
|
674
1570
|
}
|
|
675
1571
|
var MIRROR_SELF_SAME_KEY_ERROR = "mirrorSelf source and target keys must be different";
|
|
676
1572
|
function wireSelfMirrors(store, defs) {
|
|
@@ -685,7 +1581,6 @@ function wireSelfMirrors(store, defs) {
|
|
|
685
1581
|
def.targetKey
|
|
686
1582
|
);
|
|
687
1583
|
}
|
|
688
|
-
return store;
|
|
689
1584
|
}
|
|
690
1585
|
function createBuilder(accum, mirrors = [], mirrorKeyedDefs = [], selfMirrors = []) {
|
|
691
1586
|
return {
|
|
@@ -742,11 +1637,11 @@ function createBuilder(accum, mirrors = [], mirrorKeyedDefs = [], selfMirrors =
|
|
|
742
1637
|
selfMirrors
|
|
743
1638
|
);
|
|
744
1639
|
},
|
|
745
|
-
build() {
|
|
1640
|
+
build(options) {
|
|
746
1641
|
return new InjectionToken("FlurryxStore", {
|
|
747
1642
|
providedIn: "root",
|
|
748
1643
|
factory: () => {
|
|
749
|
-
const store = new DynamicStore(accum);
|
|
1644
|
+
const store = new DynamicStore(accum, options);
|
|
750
1645
|
wireMirrors(store, mirrors);
|
|
751
1646
|
wireMirrorKeyed(store, mirrorKeyedDefs);
|
|
752
1647
|
wireSelfMirrors(store, selfMirrors);
|
|
@@ -817,11 +1712,11 @@ function createConstrainedBuilder(_enumObj, accum, mirrors = [], mirrorKeyedDefs
|
|
|
817
1712
|
selfMirrors
|
|
818
1713
|
);
|
|
819
1714
|
},
|
|
820
|
-
build() {
|
|
1715
|
+
build(options) {
|
|
821
1716
|
return new InjectionToken("FlurryxStore", {
|
|
822
1717
|
providedIn: "root",
|
|
823
1718
|
factory: () => {
|
|
824
|
-
const store = new DynamicStore(accum);
|
|
1719
|
+
const store = new DynamicStore(accum, options);
|
|
825
1720
|
wireMirrors(store, mirrors);
|
|
826
1721
|
wireMirrorKeyed(store, mirrorKeyedDefs);
|
|
827
1722
|
wireSelfMirrors(store, selfMirrors);
|
|
@@ -868,11 +1763,11 @@ function createInterfaceBuilder(mirrors = [], mirrorKeyedDefs = [], selfMirrors
|
|
|
868
1763
|
selfMirrors
|
|
869
1764
|
);
|
|
870
1765
|
},
|
|
871
|
-
build() {
|
|
1766
|
+
build(options) {
|
|
872
1767
|
return new InjectionToken("FlurryxStore", {
|
|
873
1768
|
providedIn: "root",
|
|
874
1769
|
factory: () => {
|
|
875
|
-
const store = new LazyStore();
|
|
1770
|
+
const store = new LazyStore(options);
|
|
876
1771
|
wireMirrors(store, mirrors);
|
|
877
1772
|
wireMirrorKeyed(store, mirrorKeyedDefs);
|
|
878
1773
|
wireSelfMirrors(store, selfMirrors);
|
|
@@ -897,7 +1792,14 @@ export {
|
|
|
897
1792
|
LazyStore,
|
|
898
1793
|
Store,
|
|
899
1794
|
clearAllStores,
|
|
1795
|
+
cloneValue,
|
|
900
1796
|
collectKeyed,
|
|
1797
|
+
createCompositeStoreMessageChannel,
|
|
1798
|
+
createInMemoryStoreMessageChannel,
|
|
1799
|
+
createLocalStorageStoreMessageChannel,
|
|
1800
|
+
createSessionStorageStoreMessageChannel,
|
|
1801
|
+
createSnapshotRestorePatch,
|
|
1802
|
+
createStorageStoreMessageChannel,
|
|
901
1803
|
mirrorKey
|
|
902
1804
|
};
|
|
903
1805
|
//# sourceMappingURL=index.js.map
|