@hf-chimera/store
Version:
Cross-end reactivity API
1,476 lines (1,463 loc) • 57.7 kB
JavaScript
import { D as ChimeraInternalError, E as ChimeraError, S as ChimeraQueryUnsuccessfulDeletionError, _ as ChimeraQueryNotReadyError, a as chimeraDefaultFilterConfig, b as ChimeraQueryTrustFetchedCollectionError, d as ChimeraQueryDeletedItemError, f as ChimeraQueryDeletingError, g as ChimeraQueryNotCreatedError, h as ChimeraQueryIdMismatchError, i as chimeraDefaultOrderConfig, l as chimeraDefaultDebugConfig, m as ChimeraQueryFetchingError, t as chimeraDefaultQueryConfig, u as ChimeraQueryAlreadyRunningError, x as ChimeraQueryTrustIdMismatchError } from "./defaults-CLUQg2zK.js";
//#region src/filter/errors.ts
var ChimeraFilterError = class extends ChimeraError {};
var ChimeraFilterOperatorError = class extends ChimeraFilterError {
constructor(operator, message) {
super(`Operator "${operator}" ${message}`);
}
};
var ChimeraFilterOperatorNotFoundError = class extends ChimeraFilterOperatorError {
constructor(operator) {
super(operator, "not found");
}
};
//#endregion
//#region src/shared/shared.ts
const deepObjectAssign = (dst, srcObj, visited = /* @__PURE__ */ new WeakSet()) => {
for (const { 0: key, 1: srcVal } of Object.entries(srcObj)) {
if (srcVal === null || typeof srcVal !== "object" || Array.isArray(srcVal)) {
dst[key] = srcVal;
continue;
}
if (visited.has(srcVal)) {
dst[key] = srcVal;
continue;
}
visited.add(srcVal);
const destVal = dst[key];
dst[key] = destVal === null || typeof destVal !== "object" || Array.isArray(destVal) ? {} : destVal;
deepObjectAssign(dst[key], srcVal, visited);
visited.delete(srcVal);
}
return dst;
};
const deepObjectFreeze = (obj, frozenObjects = /* @__PURE__ */ new WeakSet()) => {
if (obj === null || typeof obj !== "object" || Object.isFrozen(obj) || frozenObjects.has(obj)) return obj;
frozenObjects.add(obj);
for (const value of Object.values(obj)) if (value && typeof value === "object") deepObjectFreeze(value, frozenObjects);
return Object.freeze(obj);
};
const TypedArray = Object.getPrototypeOf(Int8Array);
const deepObjectClone = (value, refs) => {
if (value === null) return null;
if (value === void 0) return void 0;
if (typeof value !== "object") return value;
if (refs) {
const ref = refs.get(value);
if (ref !== void 0) return ref;
}
if (value.constructor === Object) {
const keys$1 = Object.keys(value).concat(Object.getOwnPropertySymbols(value));
const length$1 = keys$1.length;
const clone$1 = {};
refs ??= /* @__PURE__ */ new Map();
refs.set(value, clone$1);
for (let i = 0; i < length$1; i++) clone$1[keys$1[i]] = deepObjectClone(value[keys$1[i]], refs);
return clone$1;
}
if (Array.isArray(value)) {
const length$1 = value.length;
const clone$1 = new Array(length$1);
refs ??= /* @__PURE__ */ new Map();
refs.set(value, clone$1);
for (let i = 0; i < length$1; i++) clone$1[i] = deepObjectClone(value[i], refs);
return clone$1;
}
if (value instanceof Date) return new value.constructor(value.valueOf());
if (value instanceof RegExp) return value.constructor;
if (value instanceof Map) {
const clone$1 = new value.constructor();
refs ??= /* @__PURE__ */ new Map();
refs.set(value, clone$1);
for (const entry of value.entries()) clone$1.set(entry[0], deepObjectClone(entry[1], refs));
return clone$1;
}
if (value instanceof Set) {
const clone$1 = new value.constructor();
refs ??= /* @__PURE__ */ new Map();
refs.set(value, clone$1);
for (const entry of value.values()) clone$1.add(deepObjectClone(entry, refs));
return clone$1;
}
if (value instanceof Error) {
const clone$1 = new value.constructor(value.message);
const keys$1 = Object.keys(value).concat(Object.getOwnPropertySymbols(value));
const length$1 = keys$1.length;
refs ??= /* @__PURE__ */ new Map();
refs.set(value, clone$1);
for (let i = 0; i < length$1; i++) clone$1[keys$1[i]] = deepObjectClone(value[keys$1[i]], refs);
return clone$1;
}
if (value instanceof ArrayBuffer) return value.slice();
if (value instanceof TypedArray) return value.slice();
if (value instanceof DataView) return new DataView(value.buffer.slice());
if (value instanceof WeakMap) return value;
if (value instanceof WeakSet) return value;
const clone = Object.create(value.constructor.prototype);
const keys = Object.keys(value).concat(Object.getOwnPropertySymbols(value));
const length = keys.length;
refs ??= /* @__PURE__ */ new Map();
refs.set(value, clone);
for (let i = 0; i < length; i++) clone[keys[i]] = deepObjectClone(value[keys[i]], refs);
return clone;
};
const compilePropertyGetter = ({ get }) => typeof get === "function" ? get : (e) => e[get];
const simplifyPropertyGetter = ({ key }) => key;
const makeCancellablePromise = (promise, controller = new AbortController()) => {
const signal = controller.signal;
const newPromise = promise.then((v) => signal.aborted ? new Promise(() => null) : v, (err) => {
return signal.aborted ? new Promise(() => null) : Promise.reject(err);
});
newPromise.cancel = () => controller.abort();
newPromise.cancelled = (cb) => signal.aborted ? queueMicrotask(cb) : signal.addEventListener("abort", cb);
if ("cancelled" in promise) {
promise.cancelled(() => newPromise.cancel());
controller.signal.addEventListener("abort", () => promise.cancel());
}
return newPromise;
};
//#endregion
//#region src/filter/constants.ts
const ChimeraOperatorSymbol = Symbol("ChimeraOperatorSymbol");
const ChimeraConjunctionSymbol = Symbol("ChimeraConjunctionSymbol");
//#endregion
//#region src/filter/filter.ts
const filterConjunctions = {
and: (operations) => operations.every((op) => op()),
not: (operations) => !operations.every((op) => op()),
or: (operations) => operations.some((op) => op())
};
const compileOperator = (config, { op, value, test }) => {
const operatorFunc = config.operators[op];
if (!operatorFunc) throw new ChimeraFilterOperatorNotFoundError(op);
const getter = compilePropertyGetter(value);
return (entity) => operatorFunc(getter(entity), test);
};
const compileConjunction = (config, { kind, operations }) => {
const conjunction = filterConjunctions[kind];
const compiledOperations = operations.map((operation) => {
switch (operation.type) {
case ChimeraOperatorSymbol: return compileOperator(config, operation);
case ChimeraConjunctionSymbol: return compileConjunction(config, operation);
default: throw new ChimeraInternalError(`Invalid filter operation ${operation.type}`);
}
}).filter(Boolean);
return (entity) => conjunction(compiledOperations.map((op) => () => op(entity)));
};
const simplifyOperator = ({ op, value, test }) => ({
key: simplifyPropertyGetter(value),
op,
test,
type: ChimeraOperatorSymbol
});
const compareSimplifiedOperator = (a, b) => a.key.localeCompare(b.key) || a.op.localeCompare(b.op) || JSON.stringify(a.test).localeCompare(JSON.stringify(b.test));
const compareSimplifiedOperation = (a, b) => {
if (a.type !== b.type) return a.type === ChimeraOperatorSymbol ? -1 : 1;
if (a.type === ChimeraOperatorSymbol && b.type === ChimeraOperatorSymbol) return compareSimplifiedOperator(a, b);
if (a.type === ChimeraConjunctionSymbol && b.type === ChimeraConjunctionSymbol) return compareSimplifiedConjunction(a, b);
return 0;
};
const compareSimplifiedConjunction = (a, b) => {
const kindCompare = a.kind.localeCompare(b.kind);
if (kindCompare !== 0) return kindCompare;
const aOps = a.operations;
const bOps = b.operations;
const minLength = Math.min(aOps.length, bOps.length);
for (let i = 0; i < minLength; i++) {
const aOp = aOps[i];
const bOp = bOps[i];
if (aOp && bOp) {
const compare = compareSimplifiedOperation(aOp, bOp);
if (compare !== 0) return compare;
}
}
return aOps.length - bOps.length;
};
const simplifyConjunction = ({ kind, operations }) => {
return {
kind,
operations: operations.map((op) => {
switch (op.type) {
case ChimeraOperatorSymbol: return simplifyOperator(op);
case ChimeraConjunctionSymbol: return simplifyConjunction(op);
default: throw new ChimeraInternalError(`Invalid filter operation ${op.type}`);
}
}).filter(Boolean).sort((a, b) => compareSimplifiedOperation(a, b)),
type: ChimeraConjunctionSymbol
};
};
const chimeraCreateOperator = (op, value, test) => ({
op,
test,
type: ChimeraOperatorSymbol,
value: typeof value === "string" ? {
get: value,
key: value
} : value
});
const chimeraCreateConjunction = (kind, operations) => ({
kind,
operations,
type: ChimeraConjunctionSymbol
});
const chimeraCreateNot = (operation) => ({
kind: "not",
operations: [operation],
type: ChimeraConjunctionSymbol
});
function chimeraIsConjunction(item) {
return item.type === ChimeraConjunctionSymbol;
}
function chimeraIsOperator(item) {
return item.type === ChimeraOperatorSymbol;
}
const compileFilter = (config, descriptor) => descriptor ? compileConjunction(config, descriptor) : () => true;
const simplifyFilter = (descriptor) => descriptor ? simplifyConjunction(descriptor) : null;
const isOperationSubset = (candidateOp, targetOp, getOperatorKey) => {
if (candidateOp.type !== targetOp.type) return false;
if (candidateOp.type === ChimeraOperatorSymbol && targetOp.type === ChimeraOperatorSymbol) return candidateOp.key === targetOp.key && candidateOp.op === targetOp.op && getOperatorKey(candidateOp) === getOperatorKey(targetOp);
if (candidateOp.type === ChimeraConjunctionSymbol && targetOp.type === ChimeraConjunctionSymbol) return isConjunctionSubset(candidateOp, targetOp, getOperatorKey);
return false;
};
const isConjunctionSubset = (candidate, target, getOperatorKey) => {
if (candidate.kind !== target.kind) return false;
switch (candidate.kind) {
case "and":
case "not": return candidate.operations.every((candidateOp) => target.operations.some((targetOp) => isOperationSubset(candidateOp, targetOp, getOperatorKey)));
case "or": return target.operations.every((targetOp) => candidate.operations.some((candidateOp) => isOperationSubset(candidateOp, targetOp, getOperatorKey)));
}
};
const isFilterSubset = (candidate, target, getOperatorKey) => {
if (candidate === null) return true;
if (target === null) return false;
return isConjunctionSubset(candidate, target, getOperatorKey);
};
//#endregion
//#region src/order/types.ts
let ChimeraOrderNulls = /* @__PURE__ */ function(ChimeraOrderNulls$1) {
ChimeraOrderNulls$1["First"] = "first";
ChimeraOrderNulls$1["Last"] = "last";
return ChimeraOrderNulls$1;
}({});
//#endregion
//#region src/order/order.ts
const compileOrderDescriptor = ({ key, desc, nulls }) => ({
desc,
get: compilePropertyGetter(key),
nulls
});
const chimeraCreateOrderBy = (key, desc = false, nulls = ChimeraOrderNulls.Last) => ({
desc,
key: typeof key === "string" ? {
get: key,
key
} : key,
nulls
});
const nullsComparator = (a, b, nulls) => {
return a == b ? 0 : (a == null ? -1 : 1) * (nulls === ChimeraOrderNulls.First ? 1 : -1);
};
const buildComparator = (comparator, orderBy) => {
if (!orderBy) return () => 0;
const compiledPriority = orderBy.map((ob) => compileOrderDescriptor(ob));
return (a, b) => {
let result = 0;
for (const descriptor of compiledPriority) {
const vA = descriptor.get(a);
const vB = descriptor.get(b);
if (vA == null || vB == null) {
result = nullsComparator(vA, vB, descriptor.nulls);
if (result) break;
continue;
}
result = comparator(descriptor.get(a), descriptor.get(b));
descriptor.desc && (result *= -1);
if (result) break;
}
return result;
};
};
const simplifyOrderBy = (orderBy) => orderBy ? orderBy.map((ob) => ({
desc: ob.desc,
field: ob.key.key,
nulls: ob.nulls
})) : null;
//#endregion
//#region src/shared/ChimeraEventEmitter/ChimeraEventEmitter.ts
var Events = function Events$1() {};
Events.prototype = Object.create(null);
var ChimeraEventEmitter = class {
_events;
_eventsCount;
constructor() {
this._events = new Events();
this._eventsCount = 0;
}
#addListener(event, fn, once) {
var listener = {
fn,
once
};
if (!this._events[event]) {
this._events[event] = listener;
this._eventsCount++;
} else if (!this._events[event].fn) this._events[event].push(listener);
else this._events[event] = [this._events[event], listener];
return this;
}
#clearEvent(event) {
if (--this._eventsCount === 0) this._events = new Events();
else delete this._events[event];
}
eventNames() {
return Object.keys(this._events);
}
listeners(event) {
var handlers = this._events[event];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) ee[i] = handlers[i].fn;
return ee;
}
listenerCount(event) {
var listeners = this._events[event];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
}
removeListener(event, fn, once) {
if (!this._events[event]) return this;
if (!fn) {
this.#clearEvent(event);
return this;
}
var listeners = this._events[event];
if (listeners.fn) {
if (listeners.fn === fn && (!once || listeners.once)) this.#clearEvent(event);
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) if (listeners[i].fn !== fn || once && !listeners[i].once) events.push(listeners[i]);
if (events.length) this._events[event] = events.length === 1 ? events[0] : events;
else this.#clearEvent(event);
}
return this;
}
emit(event, arg) {
if (!this._events[event]) return false;
var listeners = this._events[event];
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, true);
listeners.fn.call(this, arg);
} else for (var i = 0, length = listeners.length; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, true);
listeners[i].fn.call(this, arg);
}
return true;
}
on(event, fn) {
return this.#addListener(event, fn, false);
}
once(event, fn) {
return this.#addListener(event, fn, true);
}
removeAllListeners(event) {
if (event) {
if (this._events[event]) this.#clearEvent(event);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
}
off = this.removeListener;
addListener = this.on;
};
//#endregion
//#region src/query/types.ts
let ChimeraQueryFetchingState = /* @__PURE__ */ function(ChimeraQueryFetchingState$1) {
/** Query just initialized. */
ChimeraQueryFetchingState$1["Initialized"] = "initialized";
/** Not used yet. */
ChimeraQueryFetchingState$1["Scheduled"] = "scheduled";
/** Fetching in progress. */
ChimeraQueryFetchingState$1["Fetching"] = "fetching";
/** Creating in progress */
ChimeraQueryFetchingState$1["Creating"] = "creating";
/** Updating in progress. */
ChimeraQueryFetchingState$1["Updating"] = "updating";
/** Deleting in progress. */
ChimeraQueryFetchingState$1["Deleting"] = "deleting";
/** Fetch requested after reaching the Fetched, Errored, or Prefetched states. */
ChimeraQueryFetchingState$1["Refetching"] = "refetching";
/** Data retrieved from existing queries without initiating a fetch. */
ChimeraQueryFetchingState$1["Prefetched"] = "prefetched";
/** Fetch ended successfully; data is ready for use. */
ChimeraQueryFetchingState$1["Fetched"] = "fetched";
/** Fetch ended with an error; no data is available. */
ChimeraQueryFetchingState$1["Errored"] = "errored";
/** Refetch ended with an error, but old data is still available. */
ChimeraQueryFetchingState$1["ReErrored"] = "reErrored";
/**
* Only for the item query, data is deleted, but the local value is still present,
* no longer allows updates, but `refetch` still works (in case of strange errors, allows recovering state)
*/
ChimeraQueryFetchingState$1["Deleted"] = "deleted";
/** Only for the item query, data was actualized from an external event */
ChimeraQueryFetchingState$1["Actualized"] = "actualized";
return ChimeraQueryFetchingState$1;
}({});
//#endregion
//#region src/query/constants.ts
const ChimeraGetParamsSym = Symbol("ChimeraGetParamsSym");
const ChimeraSetOneSym = Symbol("ChimeraSetOneSym");
const ChimeraSetManySym = Symbol("ChimeraSetManySym");
const ChimeraDeleteOneSym = Symbol("ChimeraDeleteOneSym");
const ChimeraDeleteManySym = Symbol("ChimeraDeleteManySym");
const ChimeraUpdateMixedSym = Symbol("ChimeraUpdateMixedSym");
const IN_PROGRESS_STATES = [
ChimeraQueryFetchingState.Scheduled,
ChimeraQueryFetchingState.Creating,
ChimeraQueryFetchingState.Fetching,
ChimeraQueryFetchingState.Refetching,
ChimeraQueryFetchingState.Updating,
ChimeraQueryFetchingState.Deleting
];
//#endregion
//#region src/query/ChimeraCollectionQuery.ts
var ChimeraCollectionQuery = class extends ChimeraEventEmitter {
#state;
#promise;
#lastError;
#items;
#config;
#idGetter;
#params;
#order;
#filter;
#emit(event, arg) {
queueMicrotask(() => super.emit(event, arg));
}
emit() {
throw new ChimeraInternalError("External events dispatching is not supported.");
}
#prepareRequestParams() {
return { controller: new AbortController() };
}
#readyItems(internalMessage) {
if (this.#items) return this.#items;
throw internalMessage ? new ChimeraInternalError(internalMessage) : new ChimeraQueryNotReadyError(this.#config.name);
}
#addItem(item) {
const items = this.#readyItems("Trying to update not ready collection");
const foundIndex = items.findIndex((el) => this.#order(el, item) > 0);
items.splice(foundIndex !== -1 ? foundIndex : items.length, 0, item);
this.#emit("itemAdded", {
instance: this,
item
});
}
#setItems(items) {
!this.#items && this.#emit("ready", { instance: this });
const oldItems = this.#items;
this.#items = items;
this.#emit("updated", {
instance: this,
items,
oldItems
});
}
#setNewItems(items) {
items.forEach((i) => void deepObjectFreeze(i));
this.#emit("selfUpdated", {
instance: this,
items,
oldItems: this.#items
});
this.#setItems(items);
}
#setPromise(promise) {
this.#promise?.cancel();
this.#promise = promise;
return promise;
}
#deleteAtIndex(index) {
if (index === -1) return;
const { 0: old } = this.#readyItems("Trying to update not ready collection").splice(index, 1);
this.#emit("itemDeleted", {
instance: this,
item: old
});
}
#deleteItem(item) {
this.#deleteAtIndex(this.#readyItems("Trying to update not ready collection").indexOf(item));
}
#deleteById(id) {
this.#deleteAtIndex(this.#readyItems("Trying to update not ready collection").findIndex((item) => this.#idGetter(item) === id));
}
#replaceItem(oldItem, newItem) {
const items = this.#readyItems("Trying to update not ready collection");
const index = items.indexOf(oldItem);
const old = items[index];
items[index] = newItem;
this.#emit("itemUpdated", {
instance: this,
newItem,
oldItem: old
});
}
#getById(id) {
return this.#readyItems("Trying to update not ready collection").find((item) => this.#idGetter(item) === id);
}
#setOne(item) {
const existingItem = this.#getById(this.#idGetter(item));
const nowMatches = this.#filter(item);
if (!(nowMatches || existingItem)) return;
if (existingItem) {
if (this.#order(existingItem, item) === 0) {
this.#replaceItem(existingItem, item);
return;
}
this.#deleteItem(existingItem);
}
nowMatches && this.#addItem(item);
}
#setNewOne(item) {
deepObjectFreeze(item);
this.#setOne(item);
}
#apply(input) {
return input.filter((item) => this.#filter(item)).sort((a, b) => this.#order(a, b));
}
#validate(input) {
if (this.#config.trustQuery && !this.#config.devMode) return input;
const prepared = this.#apply(input);
if (!this.#config.trustQuery) return prepared;
if (this.#config.devMode) {
for (let i = 0; i < input.length; i++) if (input[i] !== prepared[i]) {
console.warn(new ChimeraQueryTrustFetchedCollectionError(this.#config.name, input, prepared));
break;
}
}
return input;
}
#setError(error, source) {
this.#state = this.#items ? ChimeraQueryFetchingState.ReErrored : ChimeraQueryFetchingState.Errored;
this.#lastError = error;
this.#emit("error", {
error,
instance: this
});
throw source;
}
#watchPromise(promise, controller) {
return makeCancellablePromise(promise.then((response) => {
this.#setNewItems(this.#validate(response.data));
this.#state = ChimeraQueryFetchingState.Fetched;
return response;
}).catch((error) => this.#setError(error, new ChimeraQueryFetchingError(this.#config.name, error))), controller);
}
constructor(config, params, existingItems, order, filter, alreadyValid) {
super();
this.#config = config;
this.#params = params;
this.#promise = null;
this.#items = null;
this.#state = ChimeraQueryFetchingState.Initialized;
this.#idGetter = config.idGetter;
this.#filter = filter;
this.#order = order;
if (existingItems) {
const input = Array.from(existingItems);
this.#setItems(alreadyValid ? this.#validate(input) : this.#apply(input));
this.#state = ChimeraQueryFetchingState.Prefetched;
} else {
this.#state = ChimeraQueryFetchingState.Fetching;
const { controller } = this.#prepareRequestParams();
this.#setPromise(this.#watchPromise(makeCancellablePromise(config.collectionFetcher(params, { signal: controller.signal }), controller), controller));
}
this.#emit("initialized", { instance: this });
}
get [ChimeraGetParamsSym]() {
return this.#params;
}
[ChimeraSetOneSym](item) {
this.#items && this.#setOne(item);
}
[ChimeraDeleteOneSym](id) {
this.#items && this.#deleteById(id);
}
[ChimeraSetManySym](items) {
if (this.#items) for (const item of items) this.#setOne(item);
}
[ChimeraDeleteManySym](ids) {
if (this.#items) for (const id of ids) this.#deleteById(id);
}
[ChimeraUpdateMixedSym](toAdd, toDelete) {
if (this.#items) {
for (const id of toDelete) this.#deleteById(id);
for (const item of toAdd) this.#setOne(item);
}
}
get state() {
return this.#state;
}
get inProgress() {
return IN_PROGRESS_STATES.includes(this.#state);
}
get ready() {
return this.#items !== null;
}
get lastError() {
return this.#lastError;
}
/**
* Wait for the current progress process to complete (both success or error)
*/
get progress() {
return new Promise((res) => {
const resolve = () => queueMicrotask(() => res());
if (this.#promise) {
this.#promise.then(resolve, resolve);
this.#promise.cancelled(() => this.progress.then(resolve, resolve));
} else resolve();
});
}
/**
* Wait for the current progress process to complete, throw an error if it fails
*/
get result() {
return new Promise((res, rej) => {
const resolve = () => queueMicrotask(() => res());
if (this.#promise) {
this.#promise.then(resolve, rej);
this.#promise.cancelled(() => this.#promise ? this.result.then(res, rej) : rej("cancelled"));
} else resolve();
});
}
/** Return an item if it is ready, throw error otherwise */
getById(id) {
return this.#readyItems().find((item) => this.#idGetter(item) === id);
}
/** Return mutable ref to item by idx if it is ready, throw error otherwise */
mutableAt(idx) {
return deepObjectClone(this.#readyItems().at(idx));
}
/** Return mutable ref to item by [id] if it is ready, throw error otherwise */
mutableGetById(id) {
return deepObjectClone(this.#readyItems().find((item) => this.#idGetter(item) === id));
}
/**
* Trigger refetch, return existing refetch promise if already running
* @param force If true cancels any running process and starts a new one
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
*/
refetch(force = false) {
if (!force && this.#promise && [ChimeraQueryFetchingState.Fetching, ChimeraQueryFetchingState.Refetching].includes(this.#state)) return this.#promise;
if (!force && [ChimeraQueryFetchingState.Updating, ChimeraQueryFetchingState.Deleting].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#config.name, this.#state);
this.#state = ChimeraQueryFetchingState.Refetching;
const { controller } = this.#prepareRequestParams();
return this.#setPromise(this.#watchPromise(makeCancellablePromise(this.#config.collectionFetcher(this.#params, { signal: controller.signal }), controller), controller));
}
/**
* Update item using updated copy
* @param newItem new item to update
*/
update(newItem) {
const { controller } = this.#prepareRequestParams();
return this.#config.itemUpdater(newItem, { signal: controller.signal }).then((response) => {
const { data } = response;
this.#items && this.#setNewOne(data);
this.#emit("selfItemUpdated", {
instance: this,
item: data
});
return response;
});
}
/**
* Update item using updated copy
* @param newItems array of items to update
*/
batchedUpdate(newItems) {
const { controller } = this.#prepareRequestParams();
return this.#config.batchedUpdater(Array.from(newItems), { signal: controller.signal }).then((response) => {
const ready = this.ready;
response.data.forEach((item) => {
ready && this.#setNewOne(item);
this.#emit("selfItemUpdated", {
instance: this,
item
});
});
return response;
});
}
/**
* Delete item using its [id]
* @param id id of item to delete
*/
delete(id) {
const { controller } = this.#prepareRequestParams();
return this.#config.itemDeleter(id, { signal: controller.signal }).then((response) => {
const { result: { id: newId, success } } = response;
if (!this.#items) {
success && this.#emit("selfItemDeleted", {
id: newId,
instance: this
});
return response;
}
if (this.#config.trustQuery && !this.#config.devMode && success) {
this.#deleteById(newId);
this.#emit("selfItemDeleted", {
id: newId,
instance: this
});
return response;
}
if (id !== newId) {
this.#config.devMode && this.#config.trustQuery && console.warn(new ChimeraQueryTrustIdMismatchError(this.#config.name, id, newId));
if (!this.#config.trustQuery) {
success && this.#emit("selfItemDeleted", {
id: newId,
instance: this
});
throw new ChimeraQueryTrustIdMismatchError(this.#config.name, id, newId);
}
}
if (success) {
this.#deleteById(newId);
this.#emit("selfItemDeleted", {
id: newId,
instance: this
});
return response;
}
const error = new ChimeraQueryUnsuccessfulDeletionError(this.#config.name, id);
this.#state = ChimeraQueryFetchingState.ReErrored;
this.#lastError = error;
throw error;
}, (error) => this.#setError(error, new ChimeraQueryDeletingError(this.#config.name, error)));
}
/**
* Delete a list of items by their [id]s
* @param ids array of items to delete
*/
batchedDelete(ids) {
const { controller } = this.#prepareRequestParams();
return this.#config.batchedDeleter(Array.from(ids), { signal: controller.signal }).then((response) => {
this.#items && response.result.forEach(({ id: newId, success }) => {
if (success) {
this.#deleteById(newId);
this.#emit("selfItemDeleted", {
id: newId,
instance: this
});
} else {
const error = new ChimeraQueryUnsuccessfulDeletionError(this.#config.name, newId);
this.#state = ChimeraQueryFetchingState.ReErrored;
this.#lastError = error;
throw error;
}
});
return response;
}, (error) => this.#setError(error, new ChimeraQueryDeletingError(this.#config.name, error)));
}
/**
* Create new item using partial data
* @param item partial item data to create new item
*/
create(item) {
const { controller } = this.#prepareRequestParams();
return this.#config.itemCreator(item, { signal: controller.signal }).then((response) => {
const { data } = response;
this.#items && this.#setNewOne(data);
this.#emit("selfItemCreated", {
instance: this,
item: data
});
return response;
}, (error) => this.#setError(error, new ChimeraQueryFetchingError(this.#config.name, error)));
}
/**
* Create multiple items using partial data
* @param items array of partial item data to create new items
*/
batchedCreate(items) {
const { controller } = this.#prepareRequestParams();
return this.#config.batchedCreator(Array.from(items), { signal: controller.signal }).then((response) => {
this.#items && response.data.forEach((item) => {
this.#setNewOne(item);
this.#emit("selfItemCreated", {
instance: this,
item
});
});
return response;
}, (error) => this.#setError(error, new ChimeraQueryFetchingError(this.#config.name, error)));
}
/**
* Standard Array API without mutations
*/
get length() {
return this.#readyItems().length;
}
[Symbol.iterator]() {
return this.#readyItems()[Symbol.iterator]();
}
at(idx) {
return this.#readyItems().at(idx);
}
entries() {
return this.#readyItems().entries();
}
values() {
return this.#readyItems().values();
}
keys() {
return this.#readyItems().keys();
}
every(predicate) {
return this.#readyItems().every((item, idx) => predicate(item, idx, this));
}
some(predicate) {
return this.#readyItems().some((item, idx) => predicate(item, idx, this));
}
filter(predicate) {
return this.#readyItems().filter((item, idx) => predicate(item, idx, this));
}
find(predicate) {
return this.#readyItems().find((item, idx) => predicate(item, idx, this));
}
findIndex(predicate) {
return this.#readyItems().findIndex((item, idx) => predicate(item, idx, this));
}
findLast(predicate) {
return this.#readyItems().findLast((item, idx) => predicate(item, idx, this));
}
findLastIndex(predicate) {
return this.#readyItems().findLastIndex((item, idx) => predicate(item, idx, this));
}
forEach(cb) {
this.#readyItems().forEach((item, idx) => void cb(item, idx, this));
}
includes(item) {
return this.#readyItems().includes(item);
}
indexOf(item) {
return this.#readyItems().indexOf(item);
}
map(cb) {
return this.#readyItems().map((item, idx) => cb(item, idx, this));
}
reduce(cb, initialValue) {
return this.#readyItems().reduce((prev, cur, idx) => cb(prev, cur, idx, this), initialValue);
}
reduceRight(cb, initialValue) {
return this.#readyItems().reduceRight((prev, cur, idx) => cb(prev, cur, idx, this), initialValue);
}
slice(start, end) {
return this.#readyItems().slice(start, end);
}
toSorted(compareFn) {
return this.#readyItems().toSorted(compareFn);
}
toSpliced(start, deleteCount, ...items) {
return this.#readyItems().toSpliced(start, deleteCount, ...items);
}
toJSON() {
return Array.from(this.#readyItems());
}
toString() {
return this.#readyItems().toString();
}
};
//#endregion
//#region src/query/ChimeraItemQuery.ts
var ChimeraItemQuery = class extends ChimeraEventEmitter {
#item;
#mutable;
#state;
#promise;
#lastError;
#params;
#config;
#idGetter;
#emit(event, arg) {
queueMicrotask(() => super.emit(event, arg));
}
emit() {
throw new ChimeraInternalError("External events dispatching is not supported.");
}
#prepareRequestParams() {
return { controller: new AbortController() };
}
#setPromise(promise) {
this.#promise?.cancel();
this.#promise = promise;
return promise;
}
#readyItem(internalMessage) {
if (this.#item) return this.#item;
throw internalMessage ? new ChimeraInternalError(internalMessage) : new ChimeraQueryNotReadyError(this.#config.name);
}
#mutableItem(internalMessage) {
if (this.#state === ChimeraQueryFetchingState.Deleted) throw internalMessage ? new ChimeraInternalError(internalMessage) : new ChimeraQueryDeletedItemError(this.#config.name, this.#params.id);
return this.#readyItem(internalMessage);
}
#setMutable(item) {
if (item != null) if (this.#mutable) deepObjectAssign(this.#mutable, item);
else this.#mutable = deepObjectClone(item);
else this.#mutable = item;
}
#resetMutable() {
this.#setMutable(this.#readyItem(`Trying to reset mutable ref for empty item (${this.#config.name}[${this.#params.id}])`));
}
#setItem(item) {
!this.#item && this.#emit("ready", { instance: this });
const oldItem = this.#item;
this.#item = item;
this.#resetMutable();
this.#emit("updated", {
instance: this,
item,
oldItem
});
}
#setNewItem(item) {
deepObjectFreeze(item);
const oldItem = this.#item;
this.#setItem(item);
this.#emit("selfUpdated", {
instance: this,
item,
oldItem
});
}
#deleteItem() {
this.#state = ChimeraQueryFetchingState.Deleted;
this.#emit("deleted", {
id: this.#params.id,
instance: this
});
}
#setError(error, source) {
this.#state = this.#item ? ChimeraQueryFetchingState.ReErrored : ChimeraQueryFetchingState.Errored;
this.#lastError = error;
this.#emit("error", {
error,
instance: this
});
throw source;
}
#watchPromise(promise, controller) {
return makeCancellablePromise(promise.then(({ data }) => {
if (this.#config.trustQuery && !this.#config.devMode) {
this.#setNewItem(data);
this.#state = ChimeraQueryFetchingState.Fetched;
return { data };
}
const localId = this.#params.id;
const newId = this.#idGetter(data);
if (localId === newId || this.#state === ChimeraQueryFetchingState.Creating) {
this.#setNewItem(data);
if (this.#state === ChimeraQueryFetchingState.Creating) this.#emit("selfCreated", {
instance: this,
item: data
});
this.#state = ChimeraQueryFetchingState.Fetched;
} else {
this.#config.devMode && this.#config.trustQuery && console.warn(new ChimeraQueryTrustIdMismatchError(this.#config.name, localId, newId));
if (!this.#config.trustQuery) throw new ChimeraQueryTrustIdMismatchError(this.#config.name, localId, newId);
this.#setNewItem(data);
this.#params.id = newId;
this.#state = ChimeraQueryFetchingState.Fetched;
}
return { data };
}).catch((error) => this.#setError(error, new ChimeraQueryFetchingError(this.#config.name, error))), controller);
}
#updateItem(newItem) {
const newId = this.#idGetter(newItem);
const oldId = this.#idGetter(this.#readyItem(`Trying to update not ready item (${this.#config.name}[${this.#params.id}])`));
if (newId !== oldId && !this.#config.trustQuery) {
this.#resetMutable();
throw new ChimeraQueryIdMismatchError(this.#config.name, oldId, newId);
}
this.#state = ChimeraQueryFetchingState.Updating;
const { controller } = this.#prepareRequestParams();
return this.#setPromise(this.#watchPromise(makeCancellablePromise(this.#config.itemUpdater(newItem, { signal: controller.signal }), controller), controller));
}
#requestDelete() {
this.#state = ChimeraQueryFetchingState.Deleting;
const { controller } = this.#prepareRequestParams();
return this.#setPromise(makeCancellablePromise(makeCancellablePromise(this.#config.itemDeleter(this.#params.id, { signal: controller.signal }), controller).then(({ result }) => {
const { id, success } = result;
if (this.#config.trustQuery && !this.#config.devMode && success) {
this.#deleteItem();
return { result };
}
const localId = this.#params.id;
if (localId !== id) {
this.#config.devMode && this.#config.trustQuery && console.warn(new ChimeraQueryTrustIdMismatchError(this.#config.name, localId, id));
if (!this.#config.trustQuery) throw new ChimeraQueryTrustIdMismatchError(this.#config.name, localId, id);
}
if (success) {
this.#deleteItem();
this.#emit("selfDeleted", {
id,
instance: this
});
} else {
const error = new ChimeraQueryUnsuccessfulDeletionError(this.#config.name, this.#params.id);
this.#state = ChimeraQueryFetchingState.ReErrored;
this.#lastError = error;
throw error;
}
return { result };
}, (error) => this.#setError(error, new ChimeraQueryDeletingError(this.#config.name, error)))));
}
constructor(config, params, existingItem, toCreateItem) {
super();
this.#config = config;
this.#idGetter = config.idGetter;
this.#params = params;
this.#promise = null;
this.#item = null;
this.#mutable = null;
this.#state = ChimeraQueryFetchingState.Initialized;
if (existingItem) {
const item = existingItem;
this.#setItem(item);
if (config.devMode && this.#idGetter(item) !== params.id) {
this.#state = ChimeraQueryFetchingState.Errored;
throw new ChimeraInternalError(`Invalid item query [id] (changed from "${params.id}" to "${this.#idGetter(item)}")`);
}
this.#state = ChimeraQueryFetchingState.Prefetched;
} else if (toCreateItem) {
this.#state = ChimeraQueryFetchingState.Creating;
const { controller } = this.#prepareRequestParams();
this.#setPromise(this.#watchPromise(makeCancellablePromise(config.itemCreator(toCreateItem, { signal: controller.signal }), controller).then(({ data }) => {
this.#params.id = this.#idGetter(data);
return { data };
}), controller));
} else {
this.#state = ChimeraQueryFetchingState.Fetching;
const { controller } = this.#prepareRequestParams();
this.#setPromise(this.#watchPromise(makeCancellablePromise(config.itemFetcher(params, { signal: controller.signal }), controller), controller));
}
this.#emit("initialized", { instance: this });
}
get [ChimeraGetParamsSym]() {
return this.#params;
}
[ChimeraSetOneSym](item) {
this.#setItem(item);
!this.inProgress && (this.#state = ChimeraQueryFetchingState.Actualized);
}
[ChimeraDeleteOneSym](id) {
if (id === this.#params.id) {
this.#promise?.cancel();
this.#promise = null;
this.#deleteItem();
}
}
get state() {
return this.#state;
}
get inProgress() {
return IN_PROGRESS_STATES.includes(this.#state);
}
get ready() {
return this.#item !== null;
}
get lastError() {
return this.#lastError;
}
get id() {
return this.#params.id;
}
/** Return an item if it is ready, throw error otherwise */
get data() {
return this.#readyItem();
}
/** Get ref for an item that can be changed as a regular object. To send changes to updater, use <commit> method */
get mutable() {
this.#readyItem();
return this.#mutable;
}
get promise() {
return this.#promise;
}
/**
* Wait for the current progress process to complete (both success or error)
*/
get progress() {
return new Promise((res) => {
const resolve = () => queueMicrotask(() => res());
if (this.#promise) {
this.#promise.then(resolve, resolve);
this.#promise.cancelled(() => this.progress.then(resolve, resolve));
} else resolve();
});
}
/**
* Wait for the current progress process to complete, throw an error if it fails
*/
get result() {
return new Promise((res, rej) => {
const resolve = () => queueMicrotask(() => res());
if (this.#promise) {
this.#promise.then(resolve, rej);
this.#promise.cancelled(() => this.#promise ? this.result.then(res, rej) : rej("cancelled"));
} else resolve();
});
}
/**
* Trigger refetch, return existing refetch promise if already running
* @param force If true cancels any running process and starts a new one
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
*/
refetch(force = false) {
if (!force && this.#promise && [ChimeraQueryFetchingState.Fetching, ChimeraQueryFetchingState.Refetching].includes(this.#state)) return this.#promise;
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#config.name);
if (!force && [ChimeraQueryFetchingState.Updating, ChimeraQueryFetchingState.Deleting].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#config.name, this.#state);
this.#state = ChimeraQueryFetchingState.Refetching;
const { controller } = this.#prepareRequestParams();
return this.#setPromise(this.#watchPromise(makeCancellablePromise(this.#config.itemFetcher(this.#params, { signal: controller.signal }), controller), controller));
}
/**
* Update item using updated copy, a running update process will be cancelled
* @param newItem new item to replace existing
* @param force if true cancels any running process including fetch and delete
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
*/
update(newItem, force = false) {
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#config.name);
if (!force && [
ChimeraQueryFetchingState.Fetching,
ChimeraQueryFetchingState.Refetching,
ChimeraQueryFetchingState.Deleting
].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#config.name, this.#state);
this.#mutableItem();
return this.#updateItem(newItem);
}
/**
* Update item using function with draft item as argument
* that can be used to patch item in place or return a patched value,
* a running update process will be cancelled
* @param mutator mutator function
* @param force if true cancels any running process including fetch and delete
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
*/
mutate(mutator, force = false) {
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#config.name);
if (!force && [
ChimeraQueryFetchingState.Fetching,
ChimeraQueryFetchingState.Refetching,
ChimeraQueryFetchingState.Deleting
].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#config.name, this.#state);
const item = deepObjectClone(this.#mutableItem());
return this.#updateItem(mutator(item) ?? item);
}
/**
* Commit updated value from mutable ref, a running update process will be canceled
* @param force if true cancels any running process including fetch and delete
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
*/
commit(force = false) {
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#config.name);
if (!force && [
ChimeraQueryFetchingState.Fetching,
ChimeraQueryFetchingState.Refetching,
ChimeraQueryFetchingState.Deleting
].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#config.name, this.#state);
this.#mutableItem();
return this.#updateItem(this.#mutable);
}
/**
* Request to delete the value.
* Local copy will still be available if it was present.
* A running delete process will be canceled
* @param force if true cancels any running process including fetch and update
* @throws {ChimeraQueryAlreadyRunningError} If deleting or updating already in progress
*/
delete(force = false) {
if (this.#state === ChimeraQueryFetchingState.Creating) throw new ChimeraQueryNotCreatedError(this.#config.name);
if (!force && [
ChimeraQueryFetchingState.Fetching,
ChimeraQueryFetchingState.Refetching,
ChimeraQueryFetchingState.Updating
].includes(this.#state)) throw new ChimeraQueryAlreadyRunningError(this.#config.name, this.#state);
return this.#requestDelete();
}
toJSON() {
return this.#readyItem();
}
toString() {
return `${this.#readyItem()}`;
}
};
//#endregion
//#region src/shared/ChimeraWeakValueMap/ChimeraWeakValueMap.ts
var ChimeraWeakValueMap = class extends ChimeraEventEmitter {
#map;
#registry;
#cleanupScheduled = false;
#emit(event, arg) {
queueMicrotask(() => super.emit(event, arg));
}
emit() {
throw new ChimeraInternalError("External events dispatching is not supported.");
}
#scheduleCleanup() {
if (this.#cleanupScheduled) return;
this.#cleanupScheduled = true;
(typeof requestIdleCallback !== "undefined" ? requestIdleCallback : (cb) => setTimeout(cb, 0))(() => {
this.#cleanup();
this.#cleanupScheduled = false;
});
}
#cleanup() {
for (const [key, weakRef] of this.#map.entries()) if (weakRef.deref() === void 0) {
this.#map.delete(key);
this.#emit("finalize", {
instance: this,
key
});
}
}
constructor(values) {
super();
this.#registry = new FinalizationRegistry((key) => {
const weakRef = this.#map.get(key);
if (weakRef && weakRef.deref() === void 0) {
this.#map.delete(key);
this.#emit("finalize", {
instance: this,
key
});
}
});
this.#map = new Map(values ? values.map(([k, v]) => {
this.#registry.register(v, k, v);
return [k, new WeakRef(v)];
}) : null);
}
set(key, value) {
const existingRef = this.#map.get(key);
if (existingRef) {
const existingValue = existingRef.deref();
if (existingValue) this.#registry.unregister(existingValue);
}
this.#registry.register(value, key, value);
this.#map.set(key, new WeakRef(value));
this.#emit("set", {
instance: this,
key,
value
});
return this;
}
delete(key) {
if (!this.#map.has(key)) return false;
const value = this.#map.get(key)?.deref();
if (value === void 0) {
this.#map.delete(key);
this.#emit("finalize", {
instance: this,
key
});
return true;
}
this.#map.delete(key);
this.#registry.unregister(value);
this.#emit("delete", {
instance: this,
key,
value
});
return true;
}
has(key) {
const weakRef = this.#map.get(key);
const value = weakRef?.deref();
if (value === void 0 && weakRef) {
this.#map.delete(key);
this.#emit("finalize", {
instance: this,
key
});
this.#scheduleCleanup();
}
return value !== void 0;
}
forEach(callbackFn, thisArg) {
this.#map.forEach((weakRef, k) => {
const value = weakRef.deref();
if (value !== void 0) callbackFn.call(thisArg, value, k, this);
else {
this.#map.delete(k);
this.#emit("finalize", {
instance: this,
key: k
});
}
});
if (this.#map.size > 0) this.#scheduleCleanup();
}
get(key) {
const weakRef = this.#map.get(key);
const value = weakRef?.deref();
if (value === void 0 && weakRef) {
this.#map.delete(key);
this.#emit("finalize", {
instance: this,
key
});
this.#scheduleCleanup();
}
return value;
}
get size() {
this.#cleanup();
return this.#map.size;
}
*entries() {
for (const [k, weakRef] of this.#map.entries()) {
const value = weakRef.deref();
if (value !== void 0) yield [k, value];
else {
this.#map.delete(k);
this.#emit("finalize", {
instance: this,
key: k
});
}
}
if (this.#map.size > 0) this.#scheduleCleanup();
}
*keys() {
for (const [k, weakRef] of this.#map.entries()) if (weakRef.deref() !== void 0) yield k;
else {
this.#map.delete(k);
this.#emit("finalize", {
instance: this,
key: k
});
}
if (this.#map.size > 0) this.#scheduleCleanup();
}
*values() {
for (const weakRef of this.#map.values()) {
const value = weakRef.deref();
if (value !== void 0) yield value;
}
this.#cleanup();
}
*[Symbol.iterator]() {
yield* this.entries();
}
clear() {
for (const weakRef of this.#map.values()) {
const value = weakRef.deref();
if (value !== void 0) this.#registry.unregister(value);
}
this.#map.clear();
this.#emit("clear", { instance: this });
}
cleanup() {
this.#cleanup();
}
get rawSize() {
return this.#map.size;
}
};
//#endregion
//#region src/store/ChimeraEntityRepository.ts
var ChimeraEntityRepository = class extends ChimeraEventEmitter {
#entityConfig;
#filterConfig;
#orderConfig;
#idGetter;
#itemsMap;
#collectionQueryMap;
#itemQueryMap;
#emit(event, arg) {
queueMicrotask(() => super.emit(event, arg));
}
emit() {
throw new ChimeraInternalError("External events dispatching is not supported.");
}
#registerUpdate(item, skipItem) {
const id = this.#idGetter(item);
const oldItem = this.#itemsMap.get(id);
this.#itemsMap.set(id, item);
const itemQuery = this.#itemQueryMap.get(id);
itemQuery && skipItem !== itemQuery && itemQuery[ChimeraSetOneSym](item);
!oldItem && this.#emit("itemAdded", {
instance: this,
item
});
this.#emit("itemUpdated", {
instance: this,
item,
oldItem: oldItem ?? null
});
}
#registerDelete(id, skipItem) {
const oldItem = this.#itemsMap.get(id);
if (!oldItem) return;
this.#itemsMap.delete(id);
const itemQuery = this.#itemQueryMap.get(id);
itemQuery && skipItem !== itemQuery && itemQuery[ChimeraDeleteOneSym](id);
this.#emit("itemDeleted", {
instance: this,
oldItem: oldItem ?? null
});
}
#propagateUpdateOne(item, { item: skipItem, collection: skipCollection } = {}) {
this.#registerUpdate(item, skipItem);
for (const c of this.#collectionQueryMap.values()) c !== skipCollection && c[ChimeraSetOneSym](item);
}
#propagateDeleteOne(id, { item: skipItem, collection: skipCollection } = {}) {
this.#registerDelete(id, skipItem);
for (const c of this.#collectionQueryMap.values()) c !== skipCollection && c[ChimeraDeleteOneSym](id);
}
#propagateUpdateMany(items, { item: skipItem, collection: skipCollection } = {}) {
for (const item of items) this.#registerUpdate(item, skipItem);
this.#emit("updated", {
instance: this,
items
});
for (const c of this.#collectionQueryMap.values()) c !== skipCollection && c[ChimeraSetManySym](items);
}
#propagateDeleteMany(ids, { item: skipItem, collection: skipCollection } = {}) {
for (const id of ids) this.#registerDelete(id, skipItem);
this.#emit("deleted", {
ids,
instance: this
});
for (const c of this.#collectionQueryMap.values()) c !== skipCollection && c[ChimeraDeleteManySym](ids);
}
#itemUpdateHandler(query, item) {
this.#propagateUpdateOne(item, { item: query });
}
#itemDeleteHandler(query, id) {
this.#itemQueryMap.delete(id);
this.#propagateDeleteOne(id, { item: query });
}
#prepareItemQuery(query) {
if (query.id !== "") this.#itemQueryMap.set(query.id, query);
query.on("selfCreated", ({ instance }) => this.#itemQueryMap.set(instance.id, instance));
query.on("selfUpdated", ({ instance, item }) => this.#itemUpdateHandler(instance, item));
query.on("selfDeleted", ({ instance, id }) => this.#itemDeleteHandler(instance, id));
return query;
}
#simplifyCollectionParams(params) {
return {
filter: simplifyFilter(params.filter),
meta: params.meta,
order: simplifyOrderBy(params.order)
};
}
#getCollectionKey({ order, filter }) {
return `ORDER<${order ? this.#orderConfig.getKey(order) : ""}>\nFILTER