@hf-chimera/store 0.1.0 → 0.2.0
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 +171 -360
- package/dist/defaults-CDnbUToo.cjs +175 -0
- package/dist/defaults-CDnbUToo.cjs.map +1 -0
- package/dist/defaults-DkrKTPXY.mjs +91 -0
- package/dist/defaults-DkrKTPXY.mjs.map +1 -0
- package/dist/defaults.cjs +3 -14
- package/dist/defaults.d.cts +16 -42
- package/dist/defaults.d.cts.map +1 -1
- package/dist/defaults.d.mts +33 -0
- package/dist/defaults.d.mts.map +1 -0
- package/dist/defaults.mjs +3 -0
- package/dist/index.cjs +1709 -23
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +634 -2
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +634 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/{src-C74sq0jQ.js → index.mjs} +252 -246
- package/dist/index.mjs.map +1 -0
- package/dist/types-CNGIuRUw.d.mts +117 -0
- package/dist/types-CNGIuRUw.d.mts.map +1 -0
- package/dist/types-CuI5yXiY.d.cts +117 -0
- package/dist/types-CuI5yXiY.d.cts.map +1 -0
- package/package.json +10 -51
- package/.changeset/README.md +0 -8
- package/CHANGELOG.md +0 -98
- package/dist/adapters/react.cjs +0 -70
- package/dist/adapters/react.cjs.map +0 -1
- package/dist/adapters/react.d.cts +0 -15
- package/dist/adapters/react.d.cts.map +0 -1
- package/dist/adapters/react.d.ts +0 -15
- package/dist/adapters/react.d.ts.map +0 -1
- package/dist/adapters/react.js +0 -69
- package/dist/adapters/react.js.map +0 -1
- package/dist/adapters/vue.cjs +0 -92
- package/dist/adapters/vue.cjs.map +0 -1
- package/dist/adapters/vue.d.cts +0 -19
- package/dist/adapters/vue.d.cts.map +0 -1
- package/dist/adapters/vue.d.ts +0 -19
- package/dist/adapters/vue.d.ts.map +0 -1
- package/dist/adapters/vue.js +0 -91
- package/dist/adapters/vue.js.map +0 -1
- package/dist/defaults-C48gY1ow.cjs +0 -372
- package/dist/defaults-C48gY1ow.cjs.map +0 -1
- package/dist/defaults-CLUQg2zK.js +0 -210
- package/dist/defaults-CLUQg2zK.js.map +0 -1
- package/dist/defaults.cjs.map +0 -1
- package/dist/defaults.d.ts +0 -59
- package/dist/defaults.d.ts.map +0 -1
- package/dist/defaults.js +0 -13
- package/dist/defaults.js.map +0 -1
- package/dist/index-BuYMaiND.d.ts +0 -22
- package/dist/index-BuYMaiND.d.ts.map +0 -1
- package/dist/index-C45y61aH.d.ts +0 -821
- package/dist/index-C45y61aH.d.ts.map +0 -1
- package/dist/index-DP6-nR2O.d.cts +0 -821
- package/dist/index-DP6-nR2O.d.cts.map +0 -1
- package/dist/index-FQNcJwA7.d.cts +0 -22
- package/dist/index-FQNcJwA7.d.cts.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -4
- package/dist/params-B-wurzdZ.d.ts +0 -8
- package/dist/params-B-wurzdZ.d.ts.map +0 -1
- package/dist/params-C5dnCvJr.cjs +0 -47
- package/dist/params-C5dnCvJr.cjs.map +0 -1
- package/dist/params-DmOyCS2B.js +0 -13
- package/dist/params-DmOyCS2B.js.map +0 -1
- package/dist/params-uxNE-e4a.d.cts +0 -8
- package/dist/params-uxNE-e4a.d.cts.map +0 -1
- package/dist/qb-D6vPK6P0.cjs +0 -50
- package/dist/qb-D6vPK6P0.cjs.map +0 -1
- package/dist/qb-pchs-vdM.js +0 -45
- package/dist/qb-pchs-vdM.js.map +0 -1
- package/dist/qb.cjs +0 -5
- package/dist/qb.d.cts +0 -3
- package/dist/qb.d.ts +0 -3
- package/dist/qb.js +0 -5
- package/dist/src-C74sq0jQ.js.map +0 -1
- package/dist/src-YxpDmKvq.cjs +0 -1771
- package/dist/src-YxpDmKvq.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,29 +1,1715 @@
|
|
|
1
|
-
const require_defaults = require('./defaults-
|
|
2
|
-
const require_src = require('./src-YxpDmKvq.cjs');
|
|
1
|
+
const require_defaults = require('./defaults-CDnbUToo.cjs');
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
//#region ../../src/shared/shared.ts
|
|
4
|
+
const deepObjectAssign = (dst, srcObj, visited = /* @__PURE__ */ new WeakSet()) => {
|
|
5
|
+
for (const { 0: key, 1: srcVal } of Object.entries(srcObj)) {
|
|
6
|
+
if (srcVal === null || typeof srcVal !== "object" || Array.isArray(srcVal)) {
|
|
7
|
+
dst[key] = srcVal;
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (visited.has(srcVal)) {
|
|
11
|
+
dst[key] = srcVal;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
visited.add(srcVal);
|
|
15
|
+
const destVal = dst[key];
|
|
16
|
+
dst[key] = destVal === null || typeof destVal !== "object" || Array.isArray(destVal) ? {} : destVal;
|
|
17
|
+
deepObjectAssign(dst[key], srcVal, visited);
|
|
18
|
+
visited.delete(srcVal);
|
|
19
|
+
}
|
|
20
|
+
return dst;
|
|
21
|
+
};
|
|
22
|
+
const deepObjectFreeze = (obj, frozenObjects = /* @__PURE__ */ new WeakSet()) => {
|
|
23
|
+
if (obj === null || typeof obj !== "object" || Object.isFrozen(obj) || frozenObjects.has(obj)) return obj;
|
|
24
|
+
frozenObjects.add(obj);
|
|
25
|
+
for (const value of Object.values(obj)) if (value && typeof value === "object") deepObjectFreeze(value, frozenObjects);
|
|
26
|
+
return Object.freeze(obj);
|
|
27
|
+
};
|
|
28
|
+
const TypedArray = Object.getPrototypeOf(Int8Array);
|
|
29
|
+
const deepObjectClone = (value, refs) => {
|
|
30
|
+
if (value === null) return null;
|
|
31
|
+
if (value === void 0) return void 0;
|
|
32
|
+
if (typeof value !== "object") return value;
|
|
33
|
+
if (refs) {
|
|
34
|
+
const ref = refs.get(value);
|
|
35
|
+
if (ref !== void 0) return ref;
|
|
36
|
+
}
|
|
37
|
+
if (value.constructor === Object) {
|
|
38
|
+
const keys$1 = Object.keys(value).concat(Object.getOwnPropertySymbols(value));
|
|
39
|
+
const length$1 = keys$1.length;
|
|
40
|
+
const clone$1 = {};
|
|
41
|
+
refs ??= /* @__PURE__ */ new Map();
|
|
42
|
+
refs.set(value, clone$1);
|
|
43
|
+
for (let i = 0; i < length$1; i++) clone$1[keys$1[i]] = deepObjectClone(value[keys$1[i]], refs);
|
|
44
|
+
return clone$1;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
const length$1 = value.length;
|
|
48
|
+
const clone$1 = new Array(length$1);
|
|
49
|
+
refs ??= /* @__PURE__ */ new Map();
|
|
50
|
+
refs.set(value, clone$1);
|
|
51
|
+
for (let i = 0; i < length$1; i++) clone$1[i] = deepObjectClone(value[i], refs);
|
|
52
|
+
return clone$1;
|
|
53
|
+
}
|
|
54
|
+
if (value instanceof Date) return new value.constructor(value.valueOf());
|
|
55
|
+
if (value instanceof RegExp) return value.constructor;
|
|
56
|
+
if (value instanceof Map) {
|
|
57
|
+
const clone$1 = new value.constructor();
|
|
58
|
+
refs ??= /* @__PURE__ */ new Map();
|
|
59
|
+
refs.set(value, clone$1);
|
|
60
|
+
for (const entry of value.entries()) clone$1.set(entry[0], deepObjectClone(entry[1], refs));
|
|
61
|
+
return clone$1;
|
|
62
|
+
}
|
|
63
|
+
if (value instanceof Set) {
|
|
64
|
+
const clone$1 = new value.constructor();
|
|
65
|
+
refs ??= /* @__PURE__ */ new Map();
|
|
66
|
+
refs.set(value, clone$1);
|
|
67
|
+
for (const entry of value.values()) clone$1.add(deepObjectClone(entry, refs));
|
|
68
|
+
return clone$1;
|
|
69
|
+
}
|
|
70
|
+
if (value instanceof Error) {
|
|
71
|
+
const clone$1 = new value.constructor(value.message);
|
|
72
|
+
const keys$1 = Object.keys(value).concat(Object.getOwnPropertySymbols(value));
|
|
73
|
+
const length$1 = keys$1.length;
|
|
74
|
+
refs ??= /* @__PURE__ */ new Map();
|
|
75
|
+
refs.set(value, clone$1);
|
|
76
|
+
for (let i = 0; i < length$1; i++) clone$1[keys$1[i]] = deepObjectClone(value[keys$1[i]], refs);
|
|
77
|
+
return clone$1;
|
|
78
|
+
}
|
|
79
|
+
if (value instanceof ArrayBuffer) return value.slice();
|
|
80
|
+
if (value instanceof TypedArray) return value.slice();
|
|
81
|
+
if (value instanceof DataView) return new DataView(value.buffer.slice());
|
|
82
|
+
if (value instanceof WeakMap) return value;
|
|
83
|
+
if (value instanceof WeakSet) return value;
|
|
84
|
+
const clone = Object.create(value.constructor.prototype);
|
|
85
|
+
const keys = Object.keys(value).concat(Object.getOwnPropertySymbols(value));
|
|
86
|
+
const length = keys.length;
|
|
87
|
+
refs ??= /* @__PURE__ */ new Map();
|
|
88
|
+
refs.set(value, clone);
|
|
89
|
+
for (let i = 0; i < length; i++) clone[keys[i]] = deepObjectClone(value[keys[i]], refs);
|
|
90
|
+
return clone;
|
|
91
|
+
};
|
|
92
|
+
const compilePropertyGetter = ({ get }) => typeof get === "function" ? get : (e) => e[get];
|
|
93
|
+
const simplifyPropertyGetter = ({ key }) => key;
|
|
94
|
+
const makeCancellablePromise = (promise, controller = new AbortController()) => {
|
|
95
|
+
const signal = controller.signal;
|
|
96
|
+
const newPromise = promise.then((v) => signal.aborted ? new Promise(() => null) : v, (err) => {
|
|
97
|
+
return signal.aborted ? new Promise(() => null) : Promise.reject(err);
|
|
98
|
+
});
|
|
99
|
+
newPromise.cancel = () => controller.abort();
|
|
100
|
+
newPromise.cancelled = (cb) => signal.aborted ? queueMicrotask(cb) : signal.addEventListener("abort", cb);
|
|
101
|
+
if ("cancelled" in promise) {
|
|
102
|
+
promise.cancelled(() => newPromise.cancel());
|
|
103
|
+
controller.signal.addEventListener("abort", () => promise.cancel());
|
|
104
|
+
}
|
|
105
|
+
return newPromise;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region ../../src/filter/constants.ts
|
|
110
|
+
const ChimeraOperatorSymbol = Symbol("ChimeraOperatorSymbol");
|
|
111
|
+
const ChimeraConjunctionSymbol = Symbol("ChimeraConjunctionSymbol");
|
|
112
|
+
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region ../../src/filter/errors.ts
|
|
115
|
+
var ChimeraFilterError = class extends require_defaults.ChimeraError {};
|
|
116
|
+
var ChimeraFilterOperatorError = class extends ChimeraFilterError {
|
|
117
|
+
constructor(operator, message) {
|
|
118
|
+
super(`Operator "${operator}" ${message}`);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var ChimeraFilterOperatorNotFoundError = class extends ChimeraFilterOperatorError {
|
|
122
|
+
constructor(operator) {
|
|
123
|
+
super(operator, "not found");
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region ../../src/filter/filter.ts
|
|
129
|
+
const filterConjunctions = {
|
|
130
|
+
and: (operations) => operations.every((op) => op()),
|
|
131
|
+
not: (operations) => !operations.every((op) => op()),
|
|
132
|
+
or: (operations) => operations.some((op) => op())
|
|
133
|
+
};
|
|
134
|
+
const compileOperator = (config, { op, value, test }) => {
|
|
135
|
+
const operatorFunc = config.operators[op];
|
|
136
|
+
if (!operatorFunc) throw new ChimeraFilterOperatorNotFoundError(op);
|
|
137
|
+
const getter = compilePropertyGetter(value);
|
|
138
|
+
return (entity) => operatorFunc(getter(entity), test);
|
|
139
|
+
};
|
|
140
|
+
const compileConjunction = (config, { kind, operations }) => {
|
|
141
|
+
const conjunction = filterConjunctions[kind];
|
|
142
|
+
const compiledOperations = operations.map((operation) => {
|
|
143
|
+
switch (operation.type) {
|
|
144
|
+
case ChimeraOperatorSymbol: return compileOperator(config, operation);
|
|
145
|
+
case ChimeraConjunctionSymbol: return compileConjunction(config, operation);
|
|
146
|
+
default: throw new require_defaults.ChimeraInternalError(`Invalid filter operation ${operation.type}`);
|
|
147
|
+
}
|
|
148
|
+
}).filter(Boolean);
|
|
149
|
+
return (entity) => conjunction(compiledOperations.map((op) => () => op(entity)));
|
|
150
|
+
};
|
|
151
|
+
const simplifyOperator = ({ op, value, test }) => ({
|
|
152
|
+
key: simplifyPropertyGetter(value),
|
|
153
|
+
op,
|
|
154
|
+
test,
|
|
155
|
+
type: ChimeraOperatorSymbol
|
|
156
|
+
});
|
|
157
|
+
const compareSimplifiedOperator = (a, b) => a.key.localeCompare(b.key) || a.op.localeCompare(b.op) || JSON.stringify(a.test).localeCompare(JSON.stringify(b.test));
|
|
158
|
+
const compareSimplifiedOperation = (a, b) => {
|
|
159
|
+
if (a.type !== b.type) return a.type === ChimeraOperatorSymbol ? -1 : 1;
|
|
160
|
+
if (a.type === ChimeraOperatorSymbol && b.type === ChimeraOperatorSymbol) return compareSimplifiedOperator(a, b);
|
|
161
|
+
if (a.type === ChimeraConjunctionSymbol && b.type === ChimeraConjunctionSymbol) return compareSimplifiedConjunction(a, b);
|
|
162
|
+
return 0;
|
|
163
|
+
};
|
|
164
|
+
const compareSimplifiedConjunction = (a, b) => {
|
|
165
|
+
const kindCompare = a.kind.localeCompare(b.kind);
|
|
166
|
+
if (kindCompare !== 0) return kindCompare;
|
|
167
|
+
const aOps = a.operations;
|
|
168
|
+
const bOps = b.operations;
|
|
169
|
+
const minLength = Math.min(aOps.length, bOps.length);
|
|
170
|
+
for (let i = 0; i < minLength; i++) {
|
|
171
|
+
const aOp = aOps[i];
|
|
172
|
+
const bOp = bOps[i];
|
|
173
|
+
if (aOp && bOp) {
|
|
174
|
+
const compare = compareSimplifiedOperation(aOp, bOp);
|
|
175
|
+
if (compare !== 0) return compare;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return aOps.length - bOps.length;
|
|
179
|
+
};
|
|
180
|
+
const simplifyConjunction = ({ kind, operations }) => {
|
|
181
|
+
return {
|
|
182
|
+
kind,
|
|
183
|
+
operations: operations.map((op) => {
|
|
184
|
+
switch (op.type) {
|
|
185
|
+
case ChimeraOperatorSymbol: return simplifyOperator(op);
|
|
186
|
+
case ChimeraConjunctionSymbol: return simplifyConjunction(op);
|
|
187
|
+
default: throw new require_defaults.ChimeraInternalError(`Invalid filter operation ${op.type}`);
|
|
188
|
+
}
|
|
189
|
+
}).filter(Boolean).sort((a, b) => compareSimplifiedOperation(a, b)),
|
|
190
|
+
type: ChimeraConjunctionSymbol
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
const chimeraCreateOperator = (op, value, test) => ({
|
|
194
|
+
op,
|
|
195
|
+
test,
|
|
196
|
+
type: ChimeraOperatorSymbol,
|
|
197
|
+
value: typeof value === "string" ? {
|
|
198
|
+
get: value,
|
|
199
|
+
key: value
|
|
200
|
+
} : value
|
|
201
|
+
});
|
|
202
|
+
const chimeraCreateConjunction = (kind, operations) => ({
|
|
203
|
+
kind,
|
|
204
|
+
operations,
|
|
205
|
+
type: ChimeraConjunctionSymbol
|
|
206
|
+
});
|
|
207
|
+
const chimeraCreateNot = (operation) => ({
|
|
208
|
+
kind: "not",
|
|
209
|
+
operations: [operation],
|
|
210
|
+
type: ChimeraConjunctionSymbol
|
|
211
|
+
});
|
|
212
|
+
function chimeraIsConjunction(item) {
|
|
213
|
+
return item.type === ChimeraConjunctionSymbol;
|
|
214
|
+
}
|
|
215
|
+
function chimeraIsOperator(item) {
|
|
216
|
+
return item.type === ChimeraOperatorSymbol;
|
|
217
|
+
}
|
|
218
|
+
const compileFilter = (config, descriptor) => descriptor ? compileConjunction(config, descriptor) : () => true;
|
|
219
|
+
const simplifyFilter = (descriptor) => descriptor ? simplifyConjunction(descriptor) : null;
|
|
220
|
+
const isOperationSubset = (candidateOp, targetOp, getOperatorKey) => {
|
|
221
|
+
if (candidateOp.type !== targetOp.type) return false;
|
|
222
|
+
if (candidateOp.type === ChimeraOperatorSymbol && targetOp.type === ChimeraOperatorSymbol) return candidateOp.key === targetOp.key && candidateOp.op === targetOp.op && getOperatorKey(candidateOp) === getOperatorKey(targetOp);
|
|
223
|
+
if (candidateOp.type === ChimeraConjunctionSymbol && targetOp.type === ChimeraConjunctionSymbol) return isConjunctionSubset(candidateOp, targetOp, getOperatorKey);
|
|
224
|
+
return false;
|
|
225
|
+
};
|
|
226
|
+
const isConjunctionSubset = (candidate, target, getOperatorKey) => {
|
|
227
|
+
if (candidate.kind !== target.kind) return false;
|
|
228
|
+
switch (candidate.kind) {
|
|
229
|
+
case "and":
|
|
230
|
+
case "not": return candidate.operations.every((candidateOp) => target.operations.some((targetOp) => isOperationSubset(candidateOp, targetOp, getOperatorKey)));
|
|
231
|
+
case "or": return target.operations.every((targetOp) => candidate.operations.some((candidateOp) => isOperationSubset(candidateOp, targetOp, getOperatorKey)));
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const isFilterSubset = (candidate, target, getOperatorKey) => {
|
|
235
|
+
if (candidate === null) return true;
|
|
236
|
+
if (target === null) return false;
|
|
237
|
+
return isConjunctionSubset(candidate, target, getOperatorKey);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region ../../src/order/types.ts
|
|
242
|
+
let ChimeraOrderNulls = /* @__PURE__ */ function(ChimeraOrderNulls$1) {
|
|
243
|
+
ChimeraOrderNulls$1["First"] = "first";
|
|
244
|
+
ChimeraOrderNulls$1["Last"] = "last";
|
|
245
|
+
return ChimeraOrderNulls$1;
|
|
246
|
+
}({});
|
|
247
|
+
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region ../../src/order/order.ts
|
|
250
|
+
const compileOrderDescriptor = ({ key, desc, nulls }) => ({
|
|
251
|
+
desc,
|
|
252
|
+
get: compilePropertyGetter(key),
|
|
253
|
+
nulls
|
|
254
|
+
});
|
|
255
|
+
const chimeraCreateOrderBy = (key, desc = false, nulls = ChimeraOrderNulls.Last) => ({
|
|
256
|
+
desc,
|
|
257
|
+
key: typeof key === "string" ? {
|
|
258
|
+
get: key,
|
|
259
|
+
key
|
|
260
|
+
} : key,
|
|
261
|
+
nulls
|
|
262
|
+
});
|
|
263
|
+
const nullsComparator = (a, b, nulls) => {
|
|
264
|
+
return a == b ? 0 : (a == null ? -1 : 1) * (nulls === ChimeraOrderNulls.First ? 1 : -1);
|
|
265
|
+
};
|
|
266
|
+
const buildComparator = (comparator, orderBy) => {
|
|
267
|
+
if (!orderBy) return () => 0;
|
|
268
|
+
const compiledPriority = orderBy.map((ob) => compileOrderDescriptor(ob));
|
|
269
|
+
return (a, b) => {
|
|
270
|
+
let result = 0;
|
|
271
|
+
for (const descriptor of compiledPriority) {
|
|
272
|
+
const vA = descriptor.get(a);
|
|
273
|
+
const vB = descriptor.get(b);
|
|
274
|
+
if (vA == null || vB == null) {
|
|
275
|
+
result = nullsComparator(vA, vB, descriptor.nulls);
|
|
276
|
+
if (result) break;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
result = comparator(descriptor.get(a), descriptor.get(b));
|
|
280
|
+
descriptor.desc && (result *= -1);
|
|
281
|
+
if (result) break;
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
const simplifyOrderBy = (orderBy) => orderBy ? orderBy.map((ob) => ({
|
|
287
|
+
desc: ob.desc,
|
|
288
|
+
field: ob.key.key,
|
|
289
|
+
nulls: ob.nulls
|
|
290
|
+
})) : null;
|
|
291
|
+
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region ../../src/shared/ChimeraEventEmitter/ChimeraEventEmitter.ts
|
|
294
|
+
var Events = function Events$1() {};
|
|
295
|
+
Events.prototype = Object.create(null);
|
|
296
|
+
var ChimeraEventEmitter = class {
|
|
297
|
+
_events;
|
|
298
|
+
_eventsCount;
|
|
299
|
+
constructor() {
|
|
300
|
+
this._events = new Events();
|
|
301
|
+
this._eventsCount = 0;
|
|
302
|
+
}
|
|
303
|
+
#addListener(event, fn, once) {
|
|
304
|
+
var listener = {
|
|
305
|
+
fn,
|
|
306
|
+
once
|
|
307
|
+
};
|
|
308
|
+
if (!this._events[event]) {
|
|
309
|
+
this._events[event] = listener;
|
|
310
|
+
this._eventsCount++;
|
|
311
|
+
} else if (!this._events[event].fn) this._events[event].push(listener);
|
|
312
|
+
else this._events[event] = [this._events[event], listener];
|
|
313
|
+
return this;
|
|
314
|
+
}
|
|
315
|
+
#clearEvent(event) {
|
|
316
|
+
if (--this._eventsCount === 0) this._events = new Events();
|
|
317
|
+
else delete this._events[event];
|
|
318
|
+
}
|
|
319
|
+
eventNames() {
|
|
320
|
+
return Object.keys(this._events);
|
|
321
|
+
}
|
|
322
|
+
listeners(event) {
|
|
323
|
+
var handlers = this._events[event];
|
|
324
|
+
if (!handlers) return [];
|
|
325
|
+
if (handlers.fn) return [handlers.fn];
|
|
326
|
+
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) ee[i] = handlers[i].fn;
|
|
327
|
+
return ee;
|
|
328
|
+
}
|
|
329
|
+
listenerCount(event) {
|
|
330
|
+
var listeners = this._events[event];
|
|
331
|
+
if (!listeners) return 0;
|
|
332
|
+
if (listeners.fn) return 1;
|
|
333
|
+
return listeners.length;
|
|
334
|
+
}
|
|
335
|
+
removeListener(event, fn, once) {
|
|
336
|
+
if (!this._events[event]) return this;
|
|
337
|
+
if (!fn) {
|
|
338
|
+
this.#clearEvent(event);
|
|
339
|
+
return this;
|
|
340
|
+
}
|
|
341
|
+
var listeners = this._events[event];
|
|
342
|
+
if (listeners.fn) {
|
|
343
|
+
if (listeners.fn === fn && (!once || listeners.once)) this.#clearEvent(event);
|
|
344
|
+
} else {
|
|
345
|
+
for (var i = 0, events = [], length = listeners.length; i < length; i++) if (listeners[i].fn !== fn || once && !listeners[i].once) events.push(listeners[i]);
|
|
346
|
+
if (events.length) this._events[event] = events.length === 1 ? events[0] : events;
|
|
347
|
+
else this.#clearEvent(event);
|
|
348
|
+
}
|
|
349
|
+
return this;
|
|
350
|
+
}
|
|
351
|
+
emit(event, arg) {
|
|
352
|
+
if (!this._events[event]) return false;
|
|
353
|
+
var listeners = this._events[event];
|
|
354
|
+
if (listeners.fn) {
|
|
355
|
+
if (listeners.once) this.removeListener(event, listeners.fn, true);
|
|
356
|
+
listeners.fn.call(this, arg);
|
|
357
|
+
} else for (var i = 0, length = listeners.length; i < length; i++) {
|
|
358
|
+
if (listeners[i].once) this.removeListener(event, listeners[i].fn, true);
|
|
359
|
+
listeners[i].fn.call(this, arg);
|
|
360
|
+
}
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
on(event, fn) {
|
|
364
|
+
return this.#addListener(event, fn, false);
|
|
365
|
+
}
|
|
366
|
+
once(event, fn) {
|
|
367
|
+
return this.#addListener(event, fn, true);
|
|
368
|
+
}
|
|
369
|
+
removeAllListeners(event) {
|
|
370
|
+
if (event) {
|
|
371
|
+
if (this._events[event]) this.#clearEvent(event);
|
|
372
|
+
} else {
|
|
373
|
+
this._events = new Events();
|
|
374
|
+
this._eventsCount = 0;
|
|
375
|
+
}
|
|
376
|
+
return this;
|
|
377
|
+
}
|
|
378
|
+
off = this.removeListener;
|
|
379
|
+
addListener = this.on;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
//#endregion
|
|
383
|
+
//#region ../../src/query/types.ts
|
|
384
|
+
let ChimeraQueryFetchingState = /* @__PURE__ */ function(ChimeraQueryFetchingState$1) {
|
|
385
|
+
/** Query just initialized. */
|
|
386
|
+
ChimeraQueryFetchingState$1["Initialized"] = "initialized";
|
|
387
|
+
/** Not used yet. */
|
|
388
|
+
ChimeraQueryFetchingState$1["Scheduled"] = "scheduled";
|
|
389
|
+
/** Fetching in progress. */
|
|
390
|
+
ChimeraQueryFetchingState$1["Fetching"] = "fetching";
|
|
391
|
+
/** Creating in progress */
|
|
392
|
+
ChimeraQueryFetchingState$1["Creating"] = "creating";
|
|
393
|
+
/** Updating in progress. */
|
|
394
|
+
ChimeraQueryFetchingState$1["Updating"] = "updating";
|
|
395
|
+
/** Deleting in progress. */
|
|
396
|
+
ChimeraQueryFetchingState$1["Deleting"] = "deleting";
|
|
397
|
+
/** Fetch requested after reaching the Fetched, Errored, or Prefetched states. */
|
|
398
|
+
ChimeraQueryFetchingState$1["Refetching"] = "refetching";
|
|
399
|
+
/** Data retrieved from existing queries without initiating a fetch. */
|
|
400
|
+
ChimeraQueryFetchingState$1["Prefetched"] = "prefetched";
|
|
401
|
+
/** Fetch ended successfully; data is ready for use. */
|
|
402
|
+
ChimeraQueryFetchingState$1["Fetched"] = "fetched";
|
|
403
|
+
/** Fetch ended with an error; no data is available. */
|
|
404
|
+
ChimeraQueryFetchingState$1["Errored"] = "errored";
|
|
405
|
+
/** Refetch ended with an error, but old data is still available. */
|
|
406
|
+
ChimeraQueryFetchingState$1["ReErrored"] = "reErrored";
|
|
407
|
+
/**
|
|
408
|
+
* Only for the item query, data is deleted, but the local value is still present,
|
|
409
|
+
* no longer allows updates, but `refetch` still works (in case of strange errors, allows recovering state)
|
|
410
|
+
*/
|
|
411
|
+
ChimeraQueryFetchingState$1["Deleted"] = "deleted";
|
|
412
|
+
/** Only for the item query, data was actualized from an external event */
|
|
413
|
+
ChimeraQueryFetchingState$1["Actualized"] = "actualized";
|
|
414
|
+
return ChimeraQueryFetchingState$1;
|
|
415
|
+
}({});
|
|
416
|
+
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region ../../src/query/constants.ts
|
|
419
|
+
const ChimeraGetParamsSym = Symbol("ChimeraGetParamsSym");
|
|
420
|
+
const ChimeraSetOneSym = Symbol("ChimeraSetOneSym");
|
|
421
|
+
const ChimeraSetManySym = Symbol("ChimeraSetManySym");
|
|
422
|
+
const ChimeraDeleteOneSym = Symbol("ChimeraDeleteOneSym");
|
|
423
|
+
const ChimeraDeleteManySym = Symbol("ChimeraDeleteManySym");
|
|
424
|
+
const ChimeraUpdateMixedSym = Symbol("ChimeraUpdateMixedSym");
|
|
425
|
+
const IN_PROGRESS_STATES = [
|
|
426
|
+
ChimeraQueryFetchingState.Scheduled,
|
|
427
|
+
ChimeraQueryFetchingState.Creating,
|
|
428
|
+
ChimeraQueryFetchingState.Fetching,
|
|
429
|
+
ChimeraQueryFetchingState.Refetching,
|
|
430
|
+
ChimeraQueryFetchingState.Updating,
|
|
431
|
+
ChimeraQueryFetchingState.Deleting
|
|
432
|
+
];
|
|
433
|
+
|
|
434
|
+
//#endregion
|
|
435
|
+
//#region ../../src/query/errors.ts
|
|
436
|
+
const formatDeepErrorMessage = (message, cause) => `${message}: ${cause instanceof Error ? `\n ${cause.stack}` : cause}`;
|
|
437
|
+
var ChimeraQueryError = class extends require_defaults.ChimeraError {
|
|
438
|
+
entityName;
|
|
439
|
+
constructor(entityName, message, options) {
|
|
440
|
+
super(message, options);
|
|
441
|
+
this.entityName = entityName;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
var ChimeraQueryIdMismatchError = class extends ChimeraQueryError {
|
|
445
|
+
old;
|
|
446
|
+
new;
|
|
447
|
+
constructor(entityName, oldId, newId) {
|
|
448
|
+
super(entityName, `
|
|
449
|
+
Can't update "${entityName}" item if the change updates it's [id] (changed from "${oldId}" to "${newId}").
|
|
450
|
+
If such an update should not be an error, update <idGetter> field in "${entityName}" entity config to make Chimera get the [id] value properly.
|
|
451
|
+
`.trim());
|
|
452
|
+
this.old = oldId;
|
|
453
|
+
this.new = newId;
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
var ChimeraQueryNotSpecifiedError = class extends ChimeraQueryError {
|
|
457
|
+
methodName;
|
|
458
|
+
constructor(entityName, methodName) {
|
|
459
|
+
super(entityName, `<${methodName}> for entity "${entityName}" was not specified`);
|
|
460
|
+
this.methodName = methodName;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
var ChimeraQueryTrustError = class extends ChimeraQueryError {
|
|
464
|
+
constructor(entityName, description) {
|
|
465
|
+
super(entityName, `
|
|
466
|
+
DO NOT IGNORE THIS ERROR OR YOUR PROD MAY BREAK!
|
|
467
|
+
|
|
468
|
+
Looks like your "${entityName}" query provider ${description}
|
|
469
|
+
|
|
470
|
+
By default Chimera tend to trust external query provider to avoid extra data processing.
|
|
471
|
+
If it is not your case, set field "trustQuery" to "false" in config defaults or for specific entity.
|
|
472
|
+
This error visible only if "devMode" is "true".
|
|
473
|
+
If you'll ignore it, your production may fail, because Chimera won't check the data correctness.
|
|
474
|
+
`.trim());
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
var ChimeraQueryTrustIdMismatchError = class extends ChimeraQueryTrustError {
|
|
478
|
+
old;
|
|
479
|
+
new;
|
|
480
|
+
constructor(entityName, oldId, newId) {
|
|
481
|
+
super(entityName, `
|
|
482
|
+
returned an item with [id] that not matches with the [id] of item that was updated (changed from "${oldId}" to "${newId}").
|
|
483
|
+
If it is not an error, update <idGetter> field in "${entityName}" entity config to make Chimera get the [id] value properly.
|
|
484
|
+
`.trim());
|
|
485
|
+
this.old = oldId;
|
|
486
|
+
this.new = newId;
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
var ChimeraQueryTrustFetchedCollectionError = class extends ChimeraQueryTrustError {
|
|
490
|
+
old;
|
|
491
|
+
new;
|
|
492
|
+
constructor(entityName, input, output) {
|
|
493
|
+
super(entityName, "returned not properly sorted or ordered collection.");
|
|
494
|
+
this.old = input;
|
|
495
|
+
this.new = output;
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
var ChimeraQueryFetchingError = class extends ChimeraQueryError {
|
|
499
|
+
constructor(entityName, cause) {
|
|
500
|
+
super(entityName, formatDeepErrorMessage("Something went wrong", cause), { cause });
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
var ChimeraQueryDeletingError = class extends ChimeraQueryError {
|
|
504
|
+
constructor(entityName, cause) {
|
|
505
|
+
super(entityName, formatDeepErrorMessage("Something went wrong", cause), { cause });
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
var ChimeraQueryNotReadyError = class extends ChimeraQueryError {
|
|
509
|
+
constructor(entityName) {
|
|
510
|
+
super(entityName, "Unable to get unready value.");
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var ChimeraQueryDeletedItemError = class extends ChimeraQueryError {
|
|
514
|
+
constructor(entityName, id) {
|
|
515
|
+
super(entityName, `Unable to updated deleted item with [id] "${id}."`);
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
var ChimeraQueryUnsuccessfulDeletionError = class extends ChimeraQueryError {
|
|
519
|
+
constructor(entityName, id) {
|
|
520
|
+
super(entityName, `Item with [id] "${id}" was not deleted.`);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
var ChimeraQueryAlreadyRunningError = class extends ChimeraQueryError {
|
|
524
|
+
constructor(entityName, status) {
|
|
525
|
+
super(entityName, `Unable to operate query. Other process already running ${status}.`);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var ChimeraQueryNotCreatedError = class extends ChimeraQueryError {
|
|
529
|
+
constructor(entityName) {
|
|
530
|
+
super(entityName, "Unable to operate not created item.");
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
//#endregion
|
|
535
|
+
//#region ../../src/query/ChimeraCollectionQuery.ts
|
|
536
|
+
var ChimeraCollectionQuery = class extends ChimeraEventEmitter {
|
|
537
|
+
#state;
|
|
538
|
+
#promise;
|
|
539
|
+
#lastError;
|
|
540
|
+
#items;
|
|
541
|
+
#entityConfig;
|
|
542
|
+
#debugConfig;
|
|
543
|
+
#idGetter;
|
|
544
|
+
#params;
|
|
545
|
+
#order;
|
|
546
|
+
#filter;
|
|
547
|
+
#emit(event, arg) {
|
|
548
|
+
queueMicrotask(() => super.emit(event, arg));
|
|
549
|
+
}
|
|
550
|
+
emit() {
|
|
551
|
+
throw new require_defaults.ChimeraInternalError("External events dispatching is not supported.");
|
|
552
|
+
}
|
|
553
|
+
#prepareRequestParams() {
|
|
554
|
+
return { controller: new AbortController() };
|
|
555
|
+
}
|
|
556
|
+
#readyItems(internalMessage) {
|
|
557
|
+
if (this.#items) return this.#items;
|
|
558
|
+
throw internalMessage ? new require_defaults.ChimeraInternalError(internalMessage) : new ChimeraQueryNotReadyError(this.#entityConfig.name);
|
|
559
|
+
}
|
|
560
|
+
#addItem(item) {
|
|
561
|
+
const items = this.#readyItems("Trying to update not ready collection");
|
|
562
|
+
const foundIndex = items.findIndex((el) => this.#order(el, item) > 0);
|
|
563
|
+
items.splice(foundIndex !== -1 ? foundIndex : items.length, 0, item);
|
|
564
|
+
this.#emit("itemAdded", {
|
|
565
|
+
instance: this,
|
|
566
|
+
item
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
#setItems(items) {
|
|
570
|
+
!this.#items && this.#emit("ready", { instance: this });
|
|
571
|
+
const oldItems = this.#items;
|
|
572
|
+
this.#items = items;
|
|
573
|
+
this.#emit("updated", {
|
|
574
|
+
instance: this,
|
|
575
|
+
items,
|
|
576
|
+
oldItems
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
#setNewItems(items) {
|
|
580
|
+
items.forEach((i) => void deepObjectFreeze(i));
|
|
581
|
+
this.#emit("selfUpdated", {
|
|
582
|
+
instance: this,
|
|
583
|
+
items,
|
|
584
|
+
oldItems: this.#items
|
|
585
|
+
});
|
|
586
|
+
this.#setItems(items);
|
|
587
|
+
}
|
|
588
|
+
#setPromise(promise) {
|
|
589
|
+
this.#promise?.cancel();
|
|
590
|
+
this.#promise = promise;
|
|
591
|
+
return promise;
|
|
592
|
+
}
|
|
593
|
+
#deleteAtIndex(index) {
|
|
594
|
+
if (index === -1) return;
|
|
595
|
+
const { 0: old } = this.#readyItems("Trying to update not ready collection").splice(index, 1);
|
|
596
|
+
this.#emit("itemDeleted", {
|
|
597
|
+
instance: this,
|
|
598
|
+
item: old
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
#deleteItem(item) {
|
|
602
|
+
this.#deleteAtIndex(this.#readyItems("Trying to update not ready collection").indexOf(item));
|
|
603
|
+
}
|
|
604
|
+
#deleteById(id) {
|
|
605
|
+
const name = this.#entityConfig.name;
|
|
606
|
+
this.#deleteAtIndex(this.#readyItems("Trying to update not ready collection").findIndex((item) => this.#idGetter(item, name) === id));
|
|
607
|
+
}
|
|
608
|
+
#replaceItem(oldItem, newItem) {
|
|
609
|
+
const items = this.#readyItems("Trying to update not ready collection");
|
|
610
|
+
const index = items.indexOf(oldItem);
|
|
611
|
+
const old = items[index];
|
|
612
|
+
items[index] = newItem;
|
|
613
|
+
this.#emit("itemUpdated", {
|
|
614
|
+
instance: this,
|
|
615
|
+
newItem,
|
|
616
|
+
oldItem: old
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
#getById(id) {
|
|
620
|
+
const name = this.#entityConfig.name;
|
|
621
|
+
return this.#readyItems("Trying to update not ready collection").find((item) => this.#idGetter(item, name) === id);
|
|
622
|
+
}
|
|
623
|
+
#setOne(item) {
|
|
624
|
+
const existingItem = this.#getById(this.#idGetter(item, this.#entityConfig.name));
|
|
625
|
+
const nowMatches = this.#filter(item);
|
|
626
|
+
if (!(nowMatches || existingItem)) return;
|
|
627
|
+
if (existingItem) {
|
|
628
|
+
if (this.#order(existingItem, item) === 0) {
|
|
629
|
+
this.#replaceItem(existingItem, item);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
this.#deleteItem(existingItem);
|
|
633
|
+
}
|
|
634
|
+
nowMatches && this.#addItem(item);
|
|
635
|
+
}
|
|
636
|
+
#setNewOne(item) {
|
|
637
|
+
deepObjectFreeze(item);
|
|
638
|
+
this.#setOne(item);
|
|
639
|
+
}
|
|
640
|
+
#apply(input) {
|
|
641
|
+
return input.filter((item) => this.#filter(item)).sort((a, b) => this.#order(a, b));
|
|
642
|
+
}
|
|
643
|
+
#validate(input) {
|
|
644
|
+
if (this.#entityConfig.trustQuery && !this.#debugConfig.devMode) return input;
|
|
645
|
+
const prepared = this.#apply(input);
|
|
646
|
+
if (!this.#entityConfig.trustQuery) return prepared;
|
|
647
|
+
if (this.#debugConfig.devMode) {
|
|
648
|
+
for (let i = 0; i < input.length; i++) if (input[i] !== prepared[i]) {
|
|
649
|
+
console.warn(new ChimeraQueryTrustFetchedCollectionError(this.#entityConfig.name, input, prepared));
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return input;
|
|
654
|
+
}
|
|
655
|
+
#setError(error, source) {
|
|
656
|
+
this.#state = this.#items ? ChimeraQueryFetchingState.ReErrored : ChimeraQueryFetchingState.Errored;
|
|
657
|
+
this.#lastError = error;
|
|
658
|
+
this.#emit("error", {
|
|
659
|
+
error,
|
|
660
|
+
instance: this
|
|
661
|
+
});
|
|
662
|
+
throw source;
|
|
663
|
+
}
|
|
664
|
+
#watchPromise(promise, controller) {
|
|
665
|
+
return makeCancellablePromise(promise.then((response) => {
|
|
666
|
+
this.#setNewItems(this.#validate(response.data));
|
|
667
|
+
this.#state = ChimeraQueryFetchingState.Fetched;
|
|
668
|
+
return response;
|
|
669
|
+
}).catch((error) => this.#setError(error, new ChimeraQueryFetchingError(this.#entityConfig.name, error))), controller);
|
|
670
|
+
}
|
|
671
|
+
constructor(config, debugConfig, params, existingItems, order, filter, alreadyValid) {
|
|
672
|
+
super();
|
|
673
|
+
this.#entityConfig = config;
|
|
674
|
+
this.#debugConfig = debugConfig;
|
|
675
|
+
this.#params = params;
|
|
676
|
+
this.#promise = null;
|
|
677
|
+
this.#items = null;
|
|
678
|
+
this.#state = ChimeraQueryFetchingState.Initialized;
|
|
679
|
+
this.#idGetter = config.idGetter;
|
|
680
|
+
this.#filter = filter;
|
|
681
|
+
this.#order = order;
|
|
682
|
+
if (existingItems) {
|
|
683
|
+
const input = Array.from(existingItems);
|
|
684
|
+
this.#setItems(alreadyValid ? this.#validate(input) : this.#apply(input));
|
|
685
|
+
this.#state = ChimeraQueryFetchingState.Prefetched;
|
|
686
|
+
} else {
|
|
687
|
+
this.#state = ChimeraQueryFetchingState.Fetching;
|
|
688
|
+
const { controller } = this.#prepareRequestParams();
|
|
689
|
+
this.#setPromise(this.#watchPromise(makeCancellablePromise(config.collectionFetcher(params, { signal: controller.signal }, this.#entityConfig.name), controller), controller));
|
|
690
|
+
}
|
|
691
|
+
this.#emit("initialized", { instance: this });
|
|
692
|
+
}
|
|
693
|
+
get name() {
|
|
694
|
+
return this.#entityConfig.name;
|
|
695
|
+
}
|
|
696
|
+
get [ChimeraGetParamsSym]() {
|
|
697
|
+
return this.#params;
|
|
698
|
+
}
|
|
699
|
+
[ChimeraSetOneSym](item) {
|
|
700
|
+
this.#items && this.#setOne(item);
|
|
701
|
+
}
|
|
702
|
+
[ChimeraDeleteOneSym](id) {
|
|
703
|
+
this.#items && this.#deleteById(id);
|
|
704
|
+
}
|
|
705
|
+
[ChimeraSetManySym](items) {
|
|
706
|
+
if (this.#items) for (const item of items) this.#setOne(item);
|
|
707
|
+
}
|
|
708
|
+
[ChimeraDeleteManySym](ids) {
|
|
709
|
+
if (this.#items) for (const id of ids) this.#deleteById(id);
|
|
710
|
+
}
|
|
711
|
+
[ChimeraUpdateMixedSym](toAdd, toDelete) {
|
|
712
|
+
if (this.#items) {
|
|
713
|
+
for (const id of toDelete) this.#deleteById(id);
|
|
714
|
+
for (const item of toAdd) this.#setOne(item);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
get state() {
|
|
718
|
+
return this.#state;
|
|
719
|
+
}
|
|
720
|
+
get inProgress() {
|
|
721
|
+
return IN_PROGRESS_STATES.includes(this.#state);
|
|
722
|
+
}
|
|
723
|
+
get ready() {
|
|
724
|
+
return this.#items !== null;
|
|
725
|
+
}
|
|
726
|
+
get lastError() {
|
|
727
|
+
return this.#lastError;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Wait for the current progress process to complete (both success or error)
|
|
731
|
+
*/
|
|
732
|
+
get progress() {
|
|
733
|
+
return new Promise((res) => {
|
|
734
|
+
const resolve = () => queueMicrotask(() => res());
|
|
735
|
+
if (this.#promise) {
|
|
736
|
+
this.#promise.then(resolve, resolve);
|
|
737
|
+
this.#promise.cancelled(() => this.progress.then(resolve, resolve));
|
|
738
|
+
} else resolve();
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Wait for the current progress process to complete, throw an error if it fails
|
|
743
|
+
*/
|
|
744
|
+
get result() {
|
|
745
|
+
return new Promise((res, rej) => {
|
|
746
|
+
const resolve = () => queueMicrotask(() => res());
|
|
747
|
+
if (this.#promise) {
|
|
748
|
+
this.#promise.then(resolve, rej);
|
|
749
|
+
this.#promise.cancelled(() => this.#promise ? this.result.then(res, rej) : rej("cancelled"));
|
|
750
|
+
} else resolve();
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
/** Return an item if it is ready, throw error otherwise */
|
|
754
|
+
getById(id) {
|
|
755
|
+
const name = this.#entityConfig.name;
|
|
756
|
+
return this.#readyItems().find((item) => this.#idGetter(item, name) === id);
|
|
757
|
+
}
|
|
758
|
+
/** Return mutable ref to item by idx if it is ready, throw error otherwise */
|
|
759
|
+
mutableAt(idx) {
|
|
760
|
+
return deepObjectClone(this.#readyItems().at(idx));
|
|
761
|
+
}
|
|
762
|
+
/** Return mutable ref to item by [id] if it is ready, throw error otherwise */
|
|
763
|
+
mutableGetById(id) {
|
|
764
|
+
const name = this.#entityConfig.name;
|
|
765
|
+
return deepObjectClone(this.#readyItems().find((item) => this.#idGetter(item, name) === id));
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Trigger refetch, return existing refetch promise if already running
|
|
769
|
+
* @param force If true cancels any running process and starts a new one
|
|
770
|
+
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
|
|
771
|
+
*/
|
|
772
|
+
refetch(force = false) {
|
|
773
|
+
if (!force && this.#promise && [ChimeraQueryFetchingState.Fetching, ChimeraQueryFetchingState.Refetching].includes(this.#state)) return this.#promise;
|
|
774
|
+
if (!force && [ChimeraQueryFetchingState.Updating, ChimeraQueryFetchingState.Deleting].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#entityConfig.name, this.#state);
|
|
775
|
+
this.#state = ChimeraQueryFetchingState.Refetching;
|
|
776
|
+
const { controller } = this.#prepareRequestParams();
|
|
777
|
+
return this.#setPromise(this.#watchPromise(makeCancellablePromise(this.#entityConfig.collectionFetcher(this.#params, { signal: controller.signal }, this.#entityConfig.name), controller), controller));
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Update item using updated copy
|
|
781
|
+
* @param newItem new item to update
|
|
782
|
+
*/
|
|
783
|
+
update(newItem) {
|
|
784
|
+
const { controller } = this.#prepareRequestParams();
|
|
785
|
+
return this.#entityConfig.itemUpdater(newItem, { signal: controller.signal }, this.#entityConfig.name).then((response) => {
|
|
786
|
+
const { data } = response;
|
|
787
|
+
this.#items && this.#setNewOne(data);
|
|
788
|
+
this.#emit("selfItemUpdated", {
|
|
789
|
+
instance: this,
|
|
790
|
+
item: data
|
|
791
|
+
});
|
|
792
|
+
return response;
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Update item using updated copy
|
|
797
|
+
* @param newItems array of items to update
|
|
798
|
+
*/
|
|
799
|
+
batchedUpdate(newItems) {
|
|
800
|
+
const { controller } = this.#prepareRequestParams();
|
|
801
|
+
return this.#entityConfig.batchedUpdater(Array.from(newItems), { signal: controller.signal }, this.#entityConfig.name).then((response) => {
|
|
802
|
+
const ready = this.ready;
|
|
803
|
+
response.data.forEach((item) => {
|
|
804
|
+
ready && this.#setNewOne(item);
|
|
805
|
+
this.#emit("selfItemUpdated", {
|
|
806
|
+
instance: this,
|
|
807
|
+
item
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
return response;
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Delete item using its [id]
|
|
815
|
+
* @param id id of item to delete
|
|
816
|
+
*/
|
|
817
|
+
delete(id) {
|
|
818
|
+
const name = this.#entityConfig.name;
|
|
819
|
+
const { controller } = this.#prepareRequestParams();
|
|
820
|
+
return this.#entityConfig.itemDeleter(id, { signal: controller.signal }, name).then((response) => {
|
|
821
|
+
const { result: { id: newId, success } } = response;
|
|
822
|
+
if (!this.#items) {
|
|
823
|
+
success && this.#emit("selfItemDeleted", {
|
|
824
|
+
id: newId,
|
|
825
|
+
instance: this
|
|
826
|
+
});
|
|
827
|
+
return response;
|
|
828
|
+
}
|
|
829
|
+
if (this.#entityConfig.trustQuery && !this.#debugConfig.devMode && success) {
|
|
830
|
+
this.#deleteById(newId);
|
|
831
|
+
this.#emit("selfItemDeleted", {
|
|
832
|
+
id: newId,
|
|
833
|
+
instance: this
|
|
834
|
+
});
|
|
835
|
+
return response;
|
|
836
|
+
}
|
|
837
|
+
if (id !== newId) {
|
|
838
|
+
this.#debugConfig.devMode && this.#entityConfig.trustQuery && console.warn(new ChimeraQueryTrustIdMismatchError(this.#entityConfig.name, id, newId));
|
|
839
|
+
if (!this.#entityConfig.trustQuery) {
|
|
840
|
+
success && this.#emit("selfItemDeleted", {
|
|
841
|
+
id: newId,
|
|
842
|
+
instance: this
|
|
843
|
+
});
|
|
844
|
+
throw new ChimeraQueryTrustIdMismatchError(this.#entityConfig.name, id, newId);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (success) {
|
|
848
|
+
this.#deleteById(newId);
|
|
849
|
+
this.#emit("selfItemDeleted", {
|
|
850
|
+
id: newId,
|
|
851
|
+
instance: this
|
|
852
|
+
});
|
|
853
|
+
return response;
|
|
854
|
+
}
|
|
855
|
+
const error = new ChimeraQueryUnsuccessfulDeletionError(this.#entityConfig.name, id);
|
|
856
|
+
this.#state = ChimeraQueryFetchingState.ReErrored;
|
|
857
|
+
this.#lastError = error;
|
|
858
|
+
throw error;
|
|
859
|
+
}, (error) => this.#setError(error, new ChimeraQueryDeletingError(name, error)));
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Delete a list of items by their [id]s
|
|
863
|
+
* @param ids array of items to delete
|
|
864
|
+
*/
|
|
865
|
+
batchedDelete(ids) {
|
|
866
|
+
const name = this.#entityConfig.name;
|
|
867
|
+
const { controller } = this.#prepareRequestParams();
|
|
868
|
+
return this.#entityConfig.batchedDeleter(Array.from(ids), { signal: controller.signal }, name).then((response) => {
|
|
869
|
+
this.#items && response.result.forEach(({ id: newId, success }) => {
|
|
870
|
+
if (success) {
|
|
871
|
+
this.#deleteById(newId);
|
|
872
|
+
this.#emit("selfItemDeleted", {
|
|
873
|
+
id: newId,
|
|
874
|
+
instance: this
|
|
875
|
+
});
|
|
876
|
+
} else {
|
|
877
|
+
const error = new ChimeraQueryUnsuccessfulDeletionError(this.#entityConfig.name, newId);
|
|
878
|
+
this.#state = ChimeraQueryFetchingState.ReErrored;
|
|
879
|
+
this.#lastError = error;
|
|
880
|
+
throw error;
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
return response;
|
|
884
|
+
}, (error) => this.#setError(error, new ChimeraQueryDeletingError(name, error)));
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Create new item using partial data
|
|
888
|
+
* @param item partial item data to create new item
|
|
889
|
+
*/
|
|
890
|
+
create(item) {
|
|
891
|
+
const name = this.#entityConfig.name;
|
|
892
|
+
const { controller } = this.#prepareRequestParams();
|
|
893
|
+
return this.#entityConfig.itemCreator(item, { signal: controller.signal }, name).then((response) => {
|
|
894
|
+
const { data } = response;
|
|
895
|
+
this.#items && this.#setNewOne(data);
|
|
896
|
+
this.#emit("selfItemCreated", {
|
|
897
|
+
instance: this,
|
|
898
|
+
item: data
|
|
899
|
+
});
|
|
900
|
+
return response;
|
|
901
|
+
}, (error) => this.#setError(error, new ChimeraQueryFetchingError(name, error)));
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Create multiple items using partial data
|
|
905
|
+
* @param items array of partial item data to create new items
|
|
906
|
+
*/
|
|
907
|
+
batchedCreate(items) {
|
|
908
|
+
const name = this.#entityConfig.name;
|
|
909
|
+
const { controller } = this.#prepareRequestParams();
|
|
910
|
+
return this.#entityConfig.batchedCreator(Array.from(items), { signal: controller.signal }, name).then((response) => {
|
|
911
|
+
this.#items && response.data.forEach((item) => {
|
|
912
|
+
this.#setNewOne(item);
|
|
913
|
+
this.#emit("selfItemCreated", {
|
|
914
|
+
instance: this,
|
|
915
|
+
item
|
|
916
|
+
});
|
|
917
|
+
});
|
|
918
|
+
return response;
|
|
919
|
+
}, (error) => this.#setError(error, new ChimeraQueryFetchingError(name, error)));
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Standard Array API without mutations
|
|
923
|
+
*/
|
|
924
|
+
get length() {
|
|
925
|
+
return this.#readyItems().length;
|
|
926
|
+
}
|
|
927
|
+
[Symbol.iterator]() {
|
|
928
|
+
return this.#readyItems()[Symbol.iterator]();
|
|
929
|
+
}
|
|
930
|
+
at(idx) {
|
|
931
|
+
return this.#readyItems().at(idx);
|
|
932
|
+
}
|
|
933
|
+
entries() {
|
|
934
|
+
return this.#readyItems().entries();
|
|
935
|
+
}
|
|
936
|
+
values() {
|
|
937
|
+
return this.#readyItems().values();
|
|
938
|
+
}
|
|
939
|
+
keys() {
|
|
940
|
+
return this.#readyItems().keys();
|
|
941
|
+
}
|
|
942
|
+
every(predicate) {
|
|
943
|
+
return this.#readyItems().every((item, idx) => predicate(item, idx, this));
|
|
944
|
+
}
|
|
945
|
+
some(predicate) {
|
|
946
|
+
return this.#readyItems().some((item, idx) => predicate(item, idx, this));
|
|
947
|
+
}
|
|
948
|
+
filter(predicate) {
|
|
949
|
+
return this.#readyItems().filter((item, idx) => predicate(item, idx, this));
|
|
950
|
+
}
|
|
951
|
+
find(predicate) {
|
|
952
|
+
return this.#readyItems().find((item, idx) => predicate(item, idx, this));
|
|
953
|
+
}
|
|
954
|
+
findIndex(predicate) {
|
|
955
|
+
return this.#readyItems().findIndex((item, idx) => predicate(item, idx, this));
|
|
956
|
+
}
|
|
957
|
+
findLast(predicate) {
|
|
958
|
+
return this.#readyItems().findLast((item, idx) => predicate(item, idx, this));
|
|
959
|
+
}
|
|
960
|
+
findLastIndex(predicate) {
|
|
961
|
+
return this.#readyItems().findLastIndex((item, idx) => predicate(item, idx, this));
|
|
962
|
+
}
|
|
963
|
+
forEach(cb) {
|
|
964
|
+
this.#readyItems().forEach((item, idx) => void cb(item, idx, this));
|
|
965
|
+
}
|
|
966
|
+
includes(item) {
|
|
967
|
+
return this.#readyItems().includes(item);
|
|
968
|
+
}
|
|
969
|
+
indexOf(item) {
|
|
970
|
+
return this.#readyItems().indexOf(item);
|
|
971
|
+
}
|
|
972
|
+
map(cb) {
|
|
973
|
+
return this.#readyItems().map((item, idx) => cb(item, idx, this));
|
|
974
|
+
}
|
|
975
|
+
reduce(cb, initialValue) {
|
|
976
|
+
return this.#readyItems().reduce((prev, cur, idx) => cb(prev, cur, idx, this), initialValue);
|
|
977
|
+
}
|
|
978
|
+
reduceRight(cb, initialValue) {
|
|
979
|
+
return this.#readyItems().reduceRight((prev, cur, idx) => cb(prev, cur, idx, this), initialValue);
|
|
980
|
+
}
|
|
981
|
+
slice(start, end) {
|
|
982
|
+
return this.#readyItems().slice(start, end);
|
|
983
|
+
}
|
|
984
|
+
toSorted(compareFn) {
|
|
985
|
+
return this.#readyItems().toSorted(compareFn);
|
|
986
|
+
}
|
|
987
|
+
toSpliced(start, deleteCount, ...items) {
|
|
988
|
+
return this.#readyItems().toSpliced(start, deleteCount, ...items);
|
|
989
|
+
}
|
|
990
|
+
toJSON() {
|
|
991
|
+
return Array.from(this.#readyItems());
|
|
992
|
+
}
|
|
993
|
+
toString() {
|
|
994
|
+
return this.#readyItems().toString();
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
//#endregion
|
|
999
|
+
//#region ../../src/query/ChimeraItemQuery.ts
|
|
1000
|
+
var ChimeraItemQuery = class extends ChimeraEventEmitter {
|
|
1001
|
+
#item;
|
|
1002
|
+
#mutable;
|
|
1003
|
+
#state;
|
|
1004
|
+
#promise;
|
|
1005
|
+
#lastError;
|
|
1006
|
+
#params;
|
|
1007
|
+
#entityConfig;
|
|
1008
|
+
#debugConfig;
|
|
1009
|
+
#idGetter;
|
|
1010
|
+
#emit(event, arg) {
|
|
1011
|
+
queueMicrotask(() => super.emit(event, arg));
|
|
1012
|
+
}
|
|
1013
|
+
emit() {
|
|
1014
|
+
throw new require_defaults.ChimeraInternalError("External events dispatching is not supported.");
|
|
1015
|
+
}
|
|
1016
|
+
#prepareRequestParams() {
|
|
1017
|
+
return { controller: new AbortController() };
|
|
1018
|
+
}
|
|
1019
|
+
#setPromise(promise) {
|
|
1020
|
+
this.#promise?.cancel();
|
|
1021
|
+
this.#promise = promise;
|
|
1022
|
+
return promise;
|
|
1023
|
+
}
|
|
1024
|
+
#readyItem(internalMessage) {
|
|
1025
|
+
if (this.#item) return this.#item;
|
|
1026
|
+
throw internalMessage ? new require_defaults.ChimeraInternalError(internalMessage) : new ChimeraQueryNotReadyError(this.#entityConfig.name);
|
|
1027
|
+
}
|
|
1028
|
+
#mutableItem(internalMessage) {
|
|
1029
|
+
if (this.#state === ChimeraQueryFetchingState.Deleted) throw internalMessage ? new require_defaults.ChimeraInternalError(internalMessage) : new ChimeraQueryDeletedItemError(this.#entityConfig.name, this.#params.id);
|
|
1030
|
+
return this.#readyItem(internalMessage);
|
|
1031
|
+
}
|
|
1032
|
+
#setMutable(item) {
|
|
1033
|
+
if (item != null) if (this.#mutable) deepObjectAssign(this.#mutable, item);
|
|
1034
|
+
else this.#mutable = deepObjectClone(item);
|
|
1035
|
+
else this.#mutable = item;
|
|
1036
|
+
}
|
|
1037
|
+
#resetMutable() {
|
|
1038
|
+
this.#setMutable(this.#readyItem(`Trying to reset mutable ref for empty item (${this.#entityConfig.name}[${this.#params.id}])`));
|
|
1039
|
+
}
|
|
1040
|
+
#setItem(item) {
|
|
1041
|
+
!this.#item && this.#emit("ready", { instance: this });
|
|
1042
|
+
const oldItem = this.#item;
|
|
1043
|
+
this.#item = item;
|
|
1044
|
+
this.#resetMutable();
|
|
1045
|
+
this.#emit("updated", {
|
|
1046
|
+
instance: this,
|
|
1047
|
+
item,
|
|
1048
|
+
oldItem
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
#setNewItem(item) {
|
|
1052
|
+
deepObjectFreeze(item);
|
|
1053
|
+
const oldItem = this.#item;
|
|
1054
|
+
this.#setItem(item);
|
|
1055
|
+
this.#emit("selfUpdated", {
|
|
1056
|
+
instance: this,
|
|
1057
|
+
item,
|
|
1058
|
+
oldItem
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
#deleteItem() {
|
|
1062
|
+
this.#state = ChimeraQueryFetchingState.Deleted;
|
|
1063
|
+
this.#emit("deleted", {
|
|
1064
|
+
id: this.#params.id,
|
|
1065
|
+
instance: this
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
#setError(error, source) {
|
|
1069
|
+
this.#state = this.#item ? ChimeraQueryFetchingState.ReErrored : ChimeraQueryFetchingState.Errored;
|
|
1070
|
+
this.#lastError = error;
|
|
1071
|
+
this.#emit("error", {
|
|
1072
|
+
error,
|
|
1073
|
+
instance: this
|
|
1074
|
+
});
|
|
1075
|
+
throw source;
|
|
1076
|
+
}
|
|
1077
|
+
#watchPromise(promise, controller) {
|
|
1078
|
+
const name = this.#entityConfig.name;
|
|
1079
|
+
return makeCancellablePromise(promise.then(({ data }) => {
|
|
1080
|
+
if (this.#entityConfig.trustQuery && !this.#debugConfig.devMode) {
|
|
1081
|
+
this.#setNewItem(data);
|
|
1082
|
+
this.#state = ChimeraQueryFetchingState.Fetched;
|
|
1083
|
+
return { data };
|
|
1084
|
+
}
|
|
1085
|
+
const localId = this.#params.id;
|
|
1086
|
+
const newId = this.#idGetter(data, name);
|
|
1087
|
+
if (localId === newId || this.#state === ChimeraQueryFetchingState.Creating) {
|
|
1088
|
+
this.#setNewItem(data);
|
|
1089
|
+
if (this.#state === ChimeraQueryFetchingState.Creating) this.#emit("selfCreated", {
|
|
1090
|
+
instance: this,
|
|
1091
|
+
item: data
|
|
1092
|
+
});
|
|
1093
|
+
this.#state = ChimeraQueryFetchingState.Fetched;
|
|
1094
|
+
} else {
|
|
1095
|
+
this.#debugConfig.devMode && this.#entityConfig.trustQuery && console.warn(new ChimeraQueryTrustIdMismatchError(this.#entityConfig.name, localId, newId));
|
|
1096
|
+
if (!this.#entityConfig.trustQuery) throw new ChimeraQueryTrustIdMismatchError(this.#entityConfig.name, localId, newId);
|
|
1097
|
+
this.#setNewItem(data);
|
|
1098
|
+
this.#params.id = newId;
|
|
1099
|
+
this.#state = ChimeraQueryFetchingState.Fetched;
|
|
1100
|
+
}
|
|
1101
|
+
return { data };
|
|
1102
|
+
}).catch((error) => this.#setError(error, new ChimeraQueryFetchingError(this.#entityConfig.name, error))), controller);
|
|
1103
|
+
}
|
|
1104
|
+
#updateItem(newItem) {
|
|
1105
|
+
const name = this.#entityConfig.name;
|
|
1106
|
+
const newId = this.#idGetter(newItem, name);
|
|
1107
|
+
const oldId = this.#idGetter(this.#readyItem(`Trying to update not ready item (${this.#entityConfig.name}[${this.#params.id}])`), name);
|
|
1108
|
+
if (newId !== oldId && !this.#entityConfig.trustQuery) {
|
|
1109
|
+
this.#resetMutable();
|
|
1110
|
+
throw new ChimeraQueryIdMismatchError(this.#entityConfig.name, oldId, newId);
|
|
1111
|
+
}
|
|
1112
|
+
this.#state = ChimeraQueryFetchingState.Updating;
|
|
1113
|
+
const { controller } = this.#prepareRequestParams();
|
|
1114
|
+
return this.#setPromise(this.#watchPromise(makeCancellablePromise(this.#entityConfig.itemUpdater(newItem, { signal: controller.signal }, name), controller), controller));
|
|
1115
|
+
}
|
|
1116
|
+
#requestDelete() {
|
|
1117
|
+
this.#state = ChimeraQueryFetchingState.Deleting;
|
|
1118
|
+
const { controller } = this.#prepareRequestParams();
|
|
1119
|
+
const name = this.#entityConfig.name;
|
|
1120
|
+
return this.#setPromise(makeCancellablePromise(makeCancellablePromise(this.#entityConfig.itemDeleter(this.#params.id, { signal: controller.signal }, name), controller).then(({ result }) => {
|
|
1121
|
+
const { id, success } = result;
|
|
1122
|
+
if (this.#entityConfig.trustQuery && !this.#debugConfig.devMode && success) {
|
|
1123
|
+
this.#deleteItem();
|
|
1124
|
+
return { result };
|
|
1125
|
+
}
|
|
1126
|
+
const localId = this.#params.id;
|
|
1127
|
+
if (localId !== id) {
|
|
1128
|
+
this.#debugConfig.devMode && this.#entityConfig.trustQuery && console.warn(new ChimeraQueryTrustIdMismatchError(this.#entityConfig.name, localId, id));
|
|
1129
|
+
if (!this.#entityConfig.trustQuery) throw new ChimeraQueryTrustIdMismatchError(this.#entityConfig.name, localId, id);
|
|
1130
|
+
}
|
|
1131
|
+
if (success) {
|
|
1132
|
+
this.#deleteItem();
|
|
1133
|
+
this.#emit("selfDeleted", {
|
|
1134
|
+
id,
|
|
1135
|
+
instance: this
|
|
1136
|
+
});
|
|
1137
|
+
} else {
|
|
1138
|
+
const error = new ChimeraQueryUnsuccessfulDeletionError(this.#entityConfig.name, this.#params.id);
|
|
1139
|
+
this.#state = ChimeraQueryFetchingState.ReErrored;
|
|
1140
|
+
this.#lastError = error;
|
|
1141
|
+
throw error;
|
|
1142
|
+
}
|
|
1143
|
+
return { result };
|
|
1144
|
+
}, (error) => this.#setError(error, new ChimeraQueryDeletingError(this.#entityConfig.name, error)))));
|
|
1145
|
+
}
|
|
1146
|
+
constructor(config, debugConfig, params, existingItem, toCreateItem) {
|
|
1147
|
+
super();
|
|
1148
|
+
this.#entityConfig = config;
|
|
1149
|
+
this.#debugConfig = debugConfig;
|
|
1150
|
+
this.#idGetter = config.idGetter;
|
|
1151
|
+
this.#params = params;
|
|
1152
|
+
this.#promise = null;
|
|
1153
|
+
this.#item = null;
|
|
1154
|
+
this.#mutable = null;
|
|
1155
|
+
this.#state = ChimeraQueryFetchingState.Initialized;
|
|
1156
|
+
const name = config.name;
|
|
1157
|
+
if (existingItem) {
|
|
1158
|
+
const item = existingItem;
|
|
1159
|
+
this.#setItem(item);
|
|
1160
|
+
if (debugConfig.devMode && this.#idGetter(item, name) !== params.id) {
|
|
1161
|
+
this.#state = ChimeraQueryFetchingState.Errored;
|
|
1162
|
+
throw new require_defaults.ChimeraInternalError(`Invalid item query [id] (changed from "${params.id}" to "${this.#idGetter(item, name)}")`);
|
|
1163
|
+
}
|
|
1164
|
+
this.#state = ChimeraQueryFetchingState.Prefetched;
|
|
1165
|
+
} else if (toCreateItem) {
|
|
1166
|
+
this.#state = ChimeraQueryFetchingState.Creating;
|
|
1167
|
+
const { controller } = this.#prepareRequestParams();
|
|
1168
|
+
this.#setPromise(this.#watchPromise(makeCancellablePromise(config.itemCreator(toCreateItem, { signal: controller.signal }, name), controller).then(({ data }) => {
|
|
1169
|
+
this.#params.id = this.#idGetter(data, name);
|
|
1170
|
+
return { data };
|
|
1171
|
+
}), controller));
|
|
1172
|
+
} else {
|
|
1173
|
+
this.#state = ChimeraQueryFetchingState.Fetching;
|
|
1174
|
+
const { controller } = this.#prepareRequestParams();
|
|
1175
|
+
this.#setPromise(this.#watchPromise(makeCancellablePromise(config.itemFetcher(params, { signal: controller.signal }, name), controller), controller));
|
|
1176
|
+
}
|
|
1177
|
+
this.#emit("initialized", { instance: this });
|
|
1178
|
+
}
|
|
1179
|
+
get name() {
|
|
1180
|
+
return this.#entityConfig.name;
|
|
1181
|
+
}
|
|
1182
|
+
get [ChimeraGetParamsSym]() {
|
|
1183
|
+
return this.#params;
|
|
1184
|
+
}
|
|
1185
|
+
[ChimeraSetOneSym](item) {
|
|
1186
|
+
this.#setItem(item);
|
|
1187
|
+
!this.inProgress && (this.#state = ChimeraQueryFetchingState.Actualized);
|
|
1188
|
+
}
|
|
1189
|
+
[ChimeraDeleteOneSym](id) {
|
|
1190
|
+
if (id === this.#params.id) {
|
|
1191
|
+
this.#promise?.cancel();
|
|
1192
|
+
this.#promise = null;
|
|
1193
|
+
this.#deleteItem();
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
get state() {
|
|
1197
|
+
return this.#state;
|
|
1198
|
+
}
|
|
1199
|
+
get inProgress() {
|
|
1200
|
+
return IN_PROGRESS_STATES.includes(this.#state);
|
|
1201
|
+
}
|
|
1202
|
+
get ready() {
|
|
1203
|
+
return this.#item !== null;
|
|
1204
|
+
}
|
|
1205
|
+
get lastError() {
|
|
1206
|
+
return this.#lastError;
|
|
1207
|
+
}
|
|
1208
|
+
get id() {
|
|
1209
|
+
return this.#params.id;
|
|
1210
|
+
}
|
|
1211
|
+
/** Return an item if it is ready, throw error otherwise */
|
|
1212
|
+
get data() {
|
|
1213
|
+
return this.#readyItem();
|
|
1214
|
+
}
|
|
1215
|
+
/** Get ref for an item that can be changed as a regular object. To send changes to updater, use <commit> method */
|
|
1216
|
+
get mutable() {
|
|
1217
|
+
this.#readyItem();
|
|
1218
|
+
return this.#mutable;
|
|
1219
|
+
}
|
|
1220
|
+
get promise() {
|
|
1221
|
+
return this.#promise;
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Wait for the current progress process to complete (both success or error)
|
|
1225
|
+
*/
|
|
1226
|
+
get progress() {
|
|
1227
|
+
return new Promise((res) => {
|
|
1228
|
+
const resolve = () => queueMicrotask(() => res());
|
|
1229
|
+
if (this.#promise) {
|
|
1230
|
+
this.#promise.then(resolve, resolve);
|
|
1231
|
+
this.#promise.cancelled(() => this.progress.then(resolve, resolve));
|
|
1232
|
+
} else resolve();
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Wait for the current progress process to complete, throw an error if it fails
|
|
1237
|
+
*/
|
|
1238
|
+
get result() {
|
|
1239
|
+
return new Promise((res, rej) => {
|
|
1240
|
+
const resolve = () => queueMicrotask(() => res());
|
|
1241
|
+
if (this.#promise) {
|
|
1242
|
+
this.#promise.then(resolve, rej);
|
|
1243
|
+
this.#promise.cancelled(() => this.#promise ? this.result.then(res, rej) : rej("cancelled"));
|
|
1244
|
+
} else resolve();
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Trigger refetch, return existing refetch promise if already running
|
|
1249
|
+
* @param force If true cancels any running process and starts a new one
|
|
1250
|
+
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
|
|
1251
|
+
*/
|
|
1252
|
+
refetch(force = false) {
|
|
1253
|
+
if (!force && this.#promise && [ChimeraQueryFetchingState.Fetching, ChimeraQueryFetchingState.Refetching].includes(this.#state)) return this.#promise;
|
|
1254
|
+
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#entityConfig.name);
|
|
1255
|
+
if (!force && [ChimeraQueryFetchingState.Updating, ChimeraQueryFetchingState.Deleting].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#entityConfig.name, this.#state);
|
|
1256
|
+
this.#state = ChimeraQueryFetchingState.Refetching;
|
|
1257
|
+
const { controller } = this.#prepareRequestParams();
|
|
1258
|
+
return this.#setPromise(this.#watchPromise(makeCancellablePromise(this.#entityConfig.itemFetcher(this.#params, { signal: controller.signal }, this.#entityConfig.name), controller), controller));
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Update item using updated copy, a running update process will be cancelled
|
|
1262
|
+
* @param newItem new item to replace existing
|
|
1263
|
+
* @param force if true cancels any running process including fetch and delete
|
|
1264
|
+
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
|
|
1265
|
+
*/
|
|
1266
|
+
update(newItem, force = false) {
|
|
1267
|
+
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#entityConfig.name);
|
|
1268
|
+
if (!force && [
|
|
1269
|
+
ChimeraQueryFetchingState.Fetching,
|
|
1270
|
+
ChimeraQueryFetchingState.Refetching,
|
|
1271
|
+
ChimeraQueryFetchingState.Deleting
|
|
1272
|
+
].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#entityConfig.name, this.#state);
|
|
1273
|
+
this.#mutableItem();
|
|
1274
|
+
return this.#updateItem(newItem);
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Update item using function with draft item as argument
|
|
1278
|
+
* that can be used to patch item in place or return a patched value,
|
|
1279
|
+
* a running update process will be cancelled
|
|
1280
|
+
* @param mutator mutator function
|
|
1281
|
+
* @param force if true cancels any running process including fetch and delete
|
|
1282
|
+
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
|
|
1283
|
+
*/
|
|
1284
|
+
mutate(mutator, force = false) {
|
|
1285
|
+
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#entityConfig.name);
|
|
1286
|
+
if (!force && [
|
|
1287
|
+
ChimeraQueryFetchingState.Fetching,
|
|
1288
|
+
ChimeraQueryFetchingState.Refetching,
|
|
1289
|
+
ChimeraQueryFetchingState.Deleting
|
|
1290
|
+
].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#entityConfig.name, this.#state);
|
|
1291
|
+
const item = deepObjectClone(this.#mutableItem());
|
|
1292
|
+
return this.#updateItem(mutator(item) ?? item);
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Commit updated value from mutable ref, a running update process will be canceled
|
|
1296
|
+
* @param force if true cancels any running process including fetch and delete
|
|
1297
|
+
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
|
|
1298
|
+
*/
|
|
1299
|
+
commit(force = false) {
|
|
1300
|
+
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#entityConfig.name);
|
|
1301
|
+
if (!force && [
|
|
1302
|
+
ChimeraQueryFetchingState.Fetching,
|
|
1303
|
+
ChimeraQueryFetchingState.Refetching,
|
|
1304
|
+
ChimeraQueryFetchingState.Deleting
|
|
1305
|
+
].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#entityConfig.name, this.#state);
|
|
1306
|
+
this.#mutableItem();
|
|
1307
|
+
return this.#updateItem(this.#mutable);
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Request to delete the value.
|
|
1311
|
+
* Local copy will still be available if it was present.
|
|
1312
|
+
* A running delete process will be canceled
|
|
1313
|
+
* @param force if true cancels any running process including fetch and update
|
|
1314
|
+
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
|
|
1315
|
+
*/
|
|
1316
|
+
delete(force = false) {
|
|
1317
|
+
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#entityConfig.name);
|
|
1318
|
+
if (!force && [
|
|
1319
|
+
ChimeraQueryFetchingState.Fetching,
|
|
1320
|
+
ChimeraQueryFetchingState.Refetching,
|
|
1321
|
+
ChimeraQueryFetchingState.Updating
|
|
1322
|
+
].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#entityConfig.name, this.#state);
|
|
1323
|
+
return this.#requestDelete();
|
|
1324
|
+
}
|
|
1325
|
+
toJSON() {
|
|
1326
|
+
return this.#readyItem();
|
|
1327
|
+
}
|
|
1328
|
+
toString() {
|
|
1329
|
+
return `${this.#readyItem()}`;
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
//#endregion
|
|
1334
|
+
//#region ../../src/shared/ChimeraWeakValueMap/ChimeraWeakValueMap.ts
|
|
1335
|
+
var ChimeraWeakValueMap = class extends ChimeraEventEmitter {
|
|
1336
|
+
#map;
|
|
1337
|
+
#registry;
|
|
1338
|
+
#cleanupScheduled = false;
|
|
1339
|
+
#emit(event, arg) {
|
|
1340
|
+
queueMicrotask(() => super.emit(event, arg));
|
|
1341
|
+
}
|
|
1342
|
+
emit() {
|
|
1343
|
+
throw new require_defaults.ChimeraInternalError("External events dispatching is not supported.");
|
|
1344
|
+
}
|
|
1345
|
+
#scheduleCleanup() {
|
|
1346
|
+
if (this.#cleanupScheduled) return;
|
|
1347
|
+
this.#cleanupScheduled = true;
|
|
1348
|
+
(typeof requestIdleCallback !== "undefined" ? requestIdleCallback : (cb) => setTimeout(cb, 0))(() => {
|
|
1349
|
+
this.#cleanup();
|
|
1350
|
+
this.#cleanupScheduled = false;
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
#cleanup() {
|
|
1354
|
+
for (const [key, weakRef] of this.#map.entries()) if (weakRef.deref() === void 0) {
|
|
1355
|
+
this.#map.delete(key);
|
|
1356
|
+
this.#emit("finalize", {
|
|
1357
|
+
instance: this,
|
|
1358
|
+
key
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
constructor(values) {
|
|
1363
|
+
super();
|
|
1364
|
+
this.#registry = new FinalizationRegistry((key) => {
|
|
1365
|
+
const weakRef = this.#map.get(key);
|
|
1366
|
+
if (weakRef && weakRef.deref() === void 0) {
|
|
1367
|
+
this.#map.delete(key);
|
|
1368
|
+
this.#emit("finalize", {
|
|
1369
|
+
instance: this,
|
|
1370
|
+
key
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
this.#map = new Map(values ? values.map(([k, v]) => {
|
|
1375
|
+
this.#registry.register(v, k, v);
|
|
1376
|
+
return [k, new WeakRef(v)];
|
|
1377
|
+
}) : null);
|
|
1378
|
+
}
|
|
1379
|
+
set(key, value) {
|
|
1380
|
+
const existingRef = this.#map.get(key);
|
|
1381
|
+
if (existingRef) {
|
|
1382
|
+
const existingValue = existingRef.deref();
|
|
1383
|
+
if (existingValue) this.#registry.unregister(existingValue);
|
|
1384
|
+
}
|
|
1385
|
+
this.#registry.register(value, key, value);
|
|
1386
|
+
this.#map.set(key, new WeakRef(value));
|
|
1387
|
+
this.#emit("set", {
|
|
1388
|
+
instance: this,
|
|
1389
|
+
key,
|
|
1390
|
+
value
|
|
1391
|
+
});
|
|
1392
|
+
return this;
|
|
1393
|
+
}
|
|
1394
|
+
delete(key) {
|
|
1395
|
+
if (!this.#map.has(key)) return false;
|
|
1396
|
+
const value = this.#map.get(key)?.deref();
|
|
1397
|
+
if (value === void 0) {
|
|
1398
|
+
this.#map.delete(key);
|
|
1399
|
+
this.#emit("finalize", {
|
|
1400
|
+
instance: this,
|
|
1401
|
+
key
|
|
1402
|
+
});
|
|
1403
|
+
return true;
|
|
1404
|
+
}
|
|
1405
|
+
this.#map.delete(key);
|
|
1406
|
+
this.#registry.unregister(value);
|
|
1407
|
+
this.#emit("delete", {
|
|
1408
|
+
instance: this,
|
|
1409
|
+
key,
|
|
1410
|
+
value
|
|
1411
|
+
});
|
|
1412
|
+
return true;
|
|
1413
|
+
}
|
|
1414
|
+
has(key) {
|
|
1415
|
+
const weakRef = this.#map.get(key);
|
|
1416
|
+
const value = weakRef?.deref();
|
|
1417
|
+
if (value === void 0 && weakRef) {
|
|
1418
|
+
this.#map.delete(key);
|
|
1419
|
+
this.#emit("finalize", {
|
|
1420
|
+
instance: this,
|
|
1421
|
+
key
|
|
1422
|
+
});
|
|
1423
|
+
this.#scheduleCleanup();
|
|
1424
|
+
}
|
|
1425
|
+
return value !== void 0;
|
|
1426
|
+
}
|
|
1427
|
+
forEach(callbackFn, thisArg) {
|
|
1428
|
+
this.#map.forEach((weakRef, k) => {
|
|
1429
|
+
const value = weakRef.deref();
|
|
1430
|
+
if (value !== void 0) callbackFn.call(thisArg, value, k, this);
|
|
1431
|
+
else {
|
|
1432
|
+
this.#map.delete(k);
|
|
1433
|
+
this.#emit("finalize", {
|
|
1434
|
+
instance: this,
|
|
1435
|
+
key: k
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
if (this.#map.size > 0) this.#scheduleCleanup();
|
|
1440
|
+
}
|
|
1441
|
+
get(key) {
|
|
1442
|
+
const weakRef = this.#map.get(key);
|
|
1443
|
+
const value = weakRef?.deref();
|
|
1444
|
+
if (value === void 0 && weakRef) {
|
|
1445
|
+
this.#map.delete(key);
|
|
1446
|
+
this.#emit("finalize", {
|
|
1447
|
+
instance: this,
|
|
1448
|
+
key
|
|
1449
|
+
});
|
|
1450
|
+
this.#scheduleCleanup();
|
|
1451
|
+
}
|
|
1452
|
+
return value;
|
|
1453
|
+
}
|
|
1454
|
+
get size() {
|
|
1455
|
+
this.#cleanup();
|
|
1456
|
+
return this.#map.size;
|
|
1457
|
+
}
|
|
1458
|
+
*entries() {
|
|
1459
|
+
for (const [k, weakRef] of this.#map.entries()) {
|
|
1460
|
+
const value = weakRef.deref();
|
|
1461
|
+
if (value !== void 0) yield [k, value];
|
|
1462
|
+
else {
|
|
1463
|
+
this.#map.delete(k);
|
|
1464
|
+
this.#emit("finalize", {
|
|
1465
|
+
instance: this,
|
|
1466
|
+
key: k
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (this.#map.size > 0) this.#scheduleCleanup();
|
|
1471
|
+
}
|
|
1472
|
+
*keys() {
|
|
1473
|
+
for (const [k, weakRef] of this.#map.entries()) if (weakRef.deref() !== void 0) yield k;
|
|
1474
|
+
else {
|
|
1475
|
+
this.#map.delete(k);
|
|
1476
|
+
this.#emit("finalize", {
|
|
1477
|
+
instance: this,
|
|
1478
|
+
key: k
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
if (this.#map.size > 0) this.#scheduleCleanup();
|
|
1482
|
+
}
|
|
1483
|
+
*values() {
|
|
1484
|
+
for (const weakRef of this.#map.values()) {
|
|
1485
|
+
const value = weakRef.deref();
|
|
1486
|
+
if (value !== void 0) yield value;
|
|
1487
|
+
}
|
|
1488
|
+
this.#cleanup();
|
|
1489
|
+
}
|
|
1490
|
+
*[Symbol.iterator]() {
|
|
1491
|
+
yield* this.entries();
|
|
1492
|
+
}
|
|
1493
|
+
clear() {
|
|
1494
|
+
for (const weakRef of this.#map.values()) {
|
|
1495
|
+
const value = weakRef.deref();
|
|
1496
|
+
if (value !== void 0) this.#registry.unregister(value);
|
|
1497
|
+
}
|
|
1498
|
+
this.#map.clear();
|
|
1499
|
+
this.#emit("clear", { instance: this });
|
|
1500
|
+
}
|
|
1501
|
+
cleanup() {
|
|
1502
|
+
this.#cleanup();
|
|
1503
|
+
}
|
|
1504
|
+
get rawSize() {
|
|
1505
|
+
return this.#map.size;
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
|
|
1509
|
+
//#endregion
|
|
1510
|
+
//#region ../../src/entity-store/ChimeraEntityStore.ts
|
|
1511
|
+
var ChimeraEntityStore = class extends ChimeraEventEmitter {
|
|
1512
|
+
#entityConfig;
|
|
1513
|
+
#filterConfig;
|
|
1514
|
+
#orderConfig;
|
|
1515
|
+
#debugConfig;
|
|
1516
|
+
#idGetter;
|
|
1517
|
+
#itemsMap;
|
|
1518
|
+
#collectionQueryMap;
|
|
1519
|
+
#itemQueryMap;
|
|
1520
|
+
#emit(event, arg) {
|
|
1521
|
+
queueMicrotask(() => super.emit(event, arg));
|
|
1522
|
+
}
|
|
1523
|
+
emit() {
|
|
1524
|
+
throw new require_defaults.ChimeraInternalError("External events dispatching is not supported.");
|
|
1525
|
+
}
|
|
1526
|
+
#registerUpdate(item, skipItem) {
|
|
1527
|
+
const id = this.#idGetter(item, this.#entityConfig.name);
|
|
1528
|
+
const oldItem = this.#itemsMap.get(id);
|
|
1529
|
+
this.#itemsMap.set(id, item);
|
|
1530
|
+
const itemQuery = this.#itemQueryMap.get(id);
|
|
1531
|
+
itemQuery && skipItem !== itemQuery && itemQuery[ChimeraSetOneSym](item);
|
|
1532
|
+
!oldItem && this.#emit("itemAdded", {
|
|
1533
|
+
instance: this,
|
|
1534
|
+
item
|
|
1535
|
+
});
|
|
1536
|
+
this.#emit("itemUpdated", {
|
|
1537
|
+
instance: this,
|
|
1538
|
+
item,
|
|
1539
|
+
oldItem: oldItem ?? null
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
#registerDelete(id, skipItem) {
|
|
1543
|
+
const oldItem = this.#itemsMap.get(id);
|
|
1544
|
+
if (!oldItem) return;
|
|
1545
|
+
this.#itemsMap.delete(id);
|
|
1546
|
+
const itemQuery = this.#itemQueryMap.get(id);
|
|
1547
|
+
itemQuery && skipItem !== itemQuery && itemQuery[ChimeraDeleteOneSym](id);
|
|
1548
|
+
this.#emit("itemDeleted", {
|
|
1549
|
+
instance: this,
|
|
1550
|
+
oldItem: oldItem ?? null
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
#propagateUpdateOne(item, { item: skipItem, collection: skipCollection } = {}) {
|
|
1554
|
+
this.#registerUpdate(item, skipItem);
|
|
1555
|
+
for (const c of this.#collectionQueryMap.values()) c !== skipCollection && c[ChimeraSetOneSym](item);
|
|
1556
|
+
}
|
|
1557
|
+
#propagateDeleteOne(id, { item: skipItem, collection: skipCollection } = {}) {
|
|
1558
|
+
this.#registerDelete(id, skipItem);
|
|
1559
|
+
for (const c of this.#collectionQueryMap.values()) c !== skipCollection && c[ChimeraDeleteOneSym](id);
|
|
1560
|
+
}
|
|
1561
|
+
#propagateUpdateMany(items, { item: skipItem, collection: skipCollection } = {}) {
|
|
1562
|
+
for (const item of items) this.#registerUpdate(item, skipItem);
|
|
1563
|
+
this.#emit("updated", {
|
|
1564
|
+
instance: this,
|
|
1565
|
+
items
|
|
1566
|
+
});
|
|
1567
|
+
for (const c of this.#collectionQueryMap.values()) c !== skipCollection && c[ChimeraSetManySym](items);
|
|
1568
|
+
}
|
|
1569
|
+
#propagateDeleteMany(ids, { item: skipItem, collection: skipCollection } = {}) {
|
|
1570
|
+
for (const id of ids) this.#registerDelete(id, skipItem);
|
|
1571
|
+
this.#emit("deleted", {
|
|
1572
|
+
ids,
|
|
1573
|
+
instance: this
|
|
1574
|
+
});
|
|
1575
|
+
for (const c of this.#collectionQueryMap.values()) c !== skipCollection && c[ChimeraDeleteManySym](ids);
|
|
1576
|
+
}
|
|
1577
|
+
#itemUpdateHandler(query, item) {
|
|
1578
|
+
this.#propagateUpdateOne(item, { item: query });
|
|
1579
|
+
}
|
|
1580
|
+
#itemDeleteHandler(query, id) {
|
|
1581
|
+
this.#itemQueryMap.delete(id);
|
|
1582
|
+
this.#propagateDeleteOne(id, { item: query });
|
|
1583
|
+
}
|
|
1584
|
+
#prepareItemQuery(query) {
|
|
1585
|
+
if (query.id !== "") this.#itemQueryMap.set(query.id, query);
|
|
1586
|
+
query.on("selfCreated", ({ instance }) => this.#itemQueryMap.set(instance.id, instance));
|
|
1587
|
+
query.on("selfUpdated", ({ instance, item }) => this.#itemUpdateHandler(instance, item));
|
|
1588
|
+
query.on("selfDeleted", ({ instance, id }) => this.#itemDeleteHandler(instance, id));
|
|
1589
|
+
return query;
|
|
1590
|
+
}
|
|
1591
|
+
#simplifyCollectionParams(params) {
|
|
1592
|
+
return {
|
|
1593
|
+
filter: simplifyFilter(params.filter),
|
|
1594
|
+
order: simplifyOrderBy(params.order),
|
|
1595
|
+
meta: params.meta
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
#getCollectionKey({ order, filter }) {
|
|
1599
|
+
return `${this.#entityConfig.name}:ORDER<${order ? this.#orderConfig.getKey(order) : ""}>:FILTER<${filter ? this.#filterConfig.getFilterKey(filter) : ""}>`;
|
|
1600
|
+
}
|
|
1601
|
+
#collectionUpdateHandler(query, items) {
|
|
1602
|
+
this.#propagateUpdateMany(items, { collection: query });
|
|
1603
|
+
}
|
|
1604
|
+
#collectionCreateHandler(query, item) {
|
|
1605
|
+
this.#propagateUpdateOne(item, { collection: query });
|
|
1606
|
+
}
|
|
1607
|
+
#collectionItemUpdated(query, item) {
|
|
1608
|
+
this.#propagateUpdateOne(item, { collection: query });
|
|
1609
|
+
}
|
|
1610
|
+
#collectionItemDeleted(query, id) {
|
|
1611
|
+
this.#propagateDeleteOne(id, { collection: query });
|
|
1612
|
+
}
|
|
1613
|
+
#prepareCollectionQuery(query) {
|
|
1614
|
+
this.#collectionQueryMap.set(this.#getCollectionKey(query[ChimeraGetParamsSym]), query);
|
|
1615
|
+
query.on("selfUpdated", ({ instance, items }) => this.#collectionUpdateHandler(instance, items));
|
|
1616
|
+
query.on("selfItemCreated", ({ instance, item }) => this.#collectionCreateHandler(instance, item));
|
|
1617
|
+
query.on("selfItemUpdated", ({ instance, item }) => this.#collectionItemUpdated(instance, item));
|
|
1618
|
+
query.on("selfItemDeleted", ({ instance, id }) => this.#collectionItemDeleted(instance, id));
|
|
1619
|
+
return query;
|
|
1620
|
+
}
|
|
1621
|
+
#getParentQuery(filter) {
|
|
1622
|
+
for (const q of this.#collectionQueryMap.values()) if (q.ready && isFilterSubset(q[ChimeraGetParamsSym].filter, filter, this.#filterConfig.getOperatorKey)) return q;
|
|
1623
|
+
return null;
|
|
1624
|
+
}
|
|
1625
|
+
constructor(config, filterConfig, orderConfig, debugConfig) {
|
|
1626
|
+
super();
|
|
1627
|
+
this.#entityConfig = config;
|
|
1628
|
+
this.#filterConfig = filterConfig;
|
|
1629
|
+
this.#orderConfig = orderConfig;
|
|
1630
|
+
this.#debugConfig = debugConfig;
|
|
1631
|
+
this.#idGetter = typeof this.#entityConfig.idGetter === "function" ? this.#entityConfig.idGetter : (entity) => entity[this.#entityConfig.idGetter];
|
|
1632
|
+
this.#itemsMap = new ChimeraWeakValueMap();
|
|
1633
|
+
this.#collectionQueryMap = new ChimeraWeakValueMap();
|
|
1634
|
+
this.#itemQueryMap = new ChimeraWeakValueMap();
|
|
1635
|
+
this.#emit("initialized", { instance: this });
|
|
1636
|
+
}
|
|
1637
|
+
get name() {
|
|
1638
|
+
return this.#entityConfig.name;
|
|
1639
|
+
}
|
|
1640
|
+
updateOne(TItem) {
|
|
1641
|
+
this.#propagateUpdateOne(TItem);
|
|
1642
|
+
}
|
|
1643
|
+
deleteOne(id) {
|
|
1644
|
+
this.#propagateDeleteOne(id);
|
|
1645
|
+
}
|
|
1646
|
+
updateMany(items) {
|
|
1647
|
+
this.#propagateUpdateMany(items);
|
|
1648
|
+
}
|
|
1649
|
+
deleteMany(ids) {
|
|
1650
|
+
this.#propagateDeleteMany(ids);
|
|
1651
|
+
}
|
|
1652
|
+
updateMixed(toAdd, toDelete) {
|
|
1653
|
+
this.#propagateUpdateMany(toAdd);
|
|
1654
|
+
this.#propagateDeleteMany(toDelete);
|
|
1655
|
+
}
|
|
1656
|
+
createItem(TItem, meta) {
|
|
1657
|
+
return this.#prepareItemQuery(new ChimeraItemQuery(this.#entityConfig, this.#debugConfig, {
|
|
1658
|
+
id: "",
|
|
1659
|
+
meta
|
|
1660
|
+
}, null, TItem));
|
|
1661
|
+
}
|
|
1662
|
+
getItem(id, meta) {
|
|
1663
|
+
const query = this.#itemQueryMap.get(id);
|
|
1664
|
+
if (query) return query;
|
|
1665
|
+
return this.#prepareItemQuery(new ChimeraItemQuery(this.#entityConfig, this.#debugConfig, {
|
|
1666
|
+
id,
|
|
1667
|
+
meta
|
|
1668
|
+
}, this.#itemsMap.get(id) ?? null, null));
|
|
1669
|
+
}
|
|
1670
|
+
getCollection(params) {
|
|
1671
|
+
const simplifiedParams = this.#simplifyCollectionParams(params);
|
|
1672
|
+
const key = this.#getCollectionKey(simplifiedParams);
|
|
1673
|
+
const query = this.#collectionQueryMap.get(key);
|
|
1674
|
+
if (query) return query;
|
|
1675
|
+
return this.#prepareCollectionQuery(new ChimeraCollectionQuery(this.#entityConfig, this.#debugConfig, simplifiedParams, this.#getParentQuery(simplifiedParams.filter), buildComparator(this.#orderConfig.primitiveComparator, params.order), compileFilter(this.#filterConfig, params.filter), false));
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
function createChimeraEntityStore(entityConfig, filterConfig, orderConfig, debugConfig) {
|
|
1679
|
+
return new ChimeraEntityStore({
|
|
1680
|
+
...require_defaults.chimeraDefaultQueryEntityConfig,
|
|
1681
|
+
...entityConfig,
|
|
1682
|
+
idGetter: typeof entityConfig.idGetter === "function" ? entityConfig.idGetter : (entity) => entity[entityConfig.idGetter]
|
|
1683
|
+
}, filterConfig ?? require_defaults.chimeraDefaultFilterConfig, orderConfig ?? require_defaults.chimeraDefaultOrderConfig, debugConfig ?? require_defaults.chimeraDefaultDebugConfig);
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
//#endregion
|
|
1687
|
+
exports.ChimeraCollectionQuery = ChimeraCollectionQuery;
|
|
1688
|
+
exports.ChimeraEntityStore = ChimeraEntityStore;
|
|
5
1689
|
exports.ChimeraError = require_defaults.ChimeraError;
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
1690
|
+
exports.ChimeraEventEmitter = ChimeraEventEmitter;
|
|
1691
|
+
exports.ChimeraFilterError = ChimeraFilterError;
|
|
1692
|
+
exports.ChimeraFilterOperatorError = ChimeraFilterOperatorError;
|
|
1693
|
+
exports.ChimeraFilterOperatorNotFoundError = ChimeraFilterOperatorNotFoundError;
|
|
9
1694
|
exports.ChimeraInternalError = require_defaults.ChimeraInternalError;
|
|
10
|
-
exports.ChimeraItemQuery =
|
|
1695
|
+
exports.ChimeraItemQuery = ChimeraItemQuery;
|
|
11
1696
|
exports.ChimeraOrderError = require_defaults.ChimeraOrderError;
|
|
12
|
-
exports.ChimeraOrderNulls =
|
|
1697
|
+
exports.ChimeraOrderNulls = ChimeraOrderNulls;
|
|
13
1698
|
exports.ChimeraOrderTypeComparisonError = require_defaults.ChimeraOrderTypeComparisonError;
|
|
14
1699
|
exports.ChimeraOrderTypeError = require_defaults.ChimeraOrderTypeError;
|
|
15
|
-
exports.ChimeraQueryError =
|
|
16
|
-
exports.ChimeraQueryFetchingState =
|
|
17
|
-
exports.ChimeraQueryIdMismatchError =
|
|
18
|
-
exports.ChimeraQueryNotSpecifiedError =
|
|
19
|
-
exports.ChimeraQueryTrustError =
|
|
20
|
-
exports.ChimeraQueryTrustFetchedCollectionError =
|
|
21
|
-
exports.ChimeraQueryTrustIdMismatchError =
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.
|
|
25
|
-
exports.
|
|
26
|
-
exports.
|
|
27
|
-
exports.
|
|
28
|
-
exports.
|
|
29
|
-
exports.
|
|
1700
|
+
exports.ChimeraQueryError = ChimeraQueryError;
|
|
1701
|
+
exports.ChimeraQueryFetchingState = ChimeraQueryFetchingState;
|
|
1702
|
+
exports.ChimeraQueryIdMismatchError = ChimeraQueryIdMismatchError;
|
|
1703
|
+
exports.ChimeraQueryNotSpecifiedError = ChimeraQueryNotSpecifiedError;
|
|
1704
|
+
exports.ChimeraQueryTrustError = ChimeraQueryTrustError;
|
|
1705
|
+
exports.ChimeraQueryTrustFetchedCollectionError = ChimeraQueryTrustFetchedCollectionError;
|
|
1706
|
+
exports.ChimeraQueryTrustIdMismatchError = ChimeraQueryTrustIdMismatchError;
|
|
1707
|
+
exports.ChimeraWeakValueMap = ChimeraWeakValueMap;
|
|
1708
|
+
exports.chimeraCreateConjunction = chimeraCreateConjunction;
|
|
1709
|
+
exports.chimeraCreateNot = chimeraCreateNot;
|
|
1710
|
+
exports.chimeraCreateOperator = chimeraCreateOperator;
|
|
1711
|
+
exports.chimeraCreateOrderBy = chimeraCreateOrderBy;
|
|
1712
|
+
exports.chimeraIsConjunction = chimeraIsConjunction;
|
|
1713
|
+
exports.chimeraIsOperator = chimeraIsOperator;
|
|
1714
|
+
exports.createChimeraEntityStore = createChimeraEntityStore;
|
|
1715
|
+
//# sourceMappingURL=index.cjs.map
|