seyfert
Version:
The most advanced framework for discord bots
356 lines (355 loc) • 12.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LimitedMemoryAdapter = void 0;
const __1 = require("../..");
const common_1 = require("../../common");
class LimitedMemoryAdapter {
isAsync = false;
storage = new Map();
relationships = new Map();
keyToStorage = new Map();
options;
constructor(options) {
this.options = (0, common_1.MergeOptions)({
default: {
expire: undefined,
limit: Number.POSITIVE_INFINITY,
},
encode(data) {
return data;
},
decode(data) {
return data;
},
}, options);
}
start() {
//
}
scan(query, keys = false) {
const sq = query.split('.');
const values = [];
for (const storageEntry of this.storage.values()) {
for (const [key, entry] of storageEntry.entries()) {
const keySplit = key.split('.');
if (keySplit.length === sq.length &&
keySplit.every((segment, i) => (sq[i] === '*' ? !!segment : sq[i] === segment))) {
values.push(keys ? key : this.options.decode(entry.value));
}
}
}
return values;
}
bulkGet(keys) {
const result = [];
for (const key of keys) {
const data = this.get(key);
if (data !== undefined && data !== null)
result.push(data);
}
return result;
}
_getKeyResource(key) {
const separatorIndex = key.indexOf('.');
return separatorIndex === -1 ? key : key.slice(0, separatorIndex);
}
_getKeyScope(key) {
const separatorIndex = key.indexOf('.');
if (separatorIndex === -1) {
return '';
}
const scopeStart = separatorIndex + 1;
const scopeEnd = key.indexOf('.', scopeStart);
return scopeEnd === -1 ? key.slice(scopeStart) : key.slice(scopeStart, scopeEnd);
}
_supportsNamespaceIndex(resource) {
switch (resource) {
case 'channel':
case 'emoji':
case 'presence':
case 'role':
case 'stage_instance':
case 'sticker':
case 'overwrite':
case 'message':
return true;
default:
return false;
}
}
_setIndexedStorage(resource, key, storageEntry) {
if (!this._supportsNamespaceIndex(resource)) {
return;
}
this.keyToStorage.set(key, storageEntry);
}
_deleteIndexedStorage(resource, key) {
if (!this._supportsNamespaceIndex(resource)) {
return;
}
this.keyToStorage.delete(key);
}
_getIndexedStorage(resource, key) {
if (!this._supportsNamespaceIndex(resource)) {
return;
}
return this.keyToStorage.get(key);
}
_getDerivedNamespace(resource, scope) {
switch (resource) {
case 'ban':
case 'member':
case 'voice_state':
return scope ? `${resource}.${scope}` : resource;
default:
return resource;
}
}
_isResourceNamespace(resource, namespace) {
return namespace === resource || namespace.startsWith(`${resource}.`);
}
_findNamespaceByStorage(resource, storageEntry) {
for (const [namespace, candidate] of this.storage.entries()) {
if (candidate === storageEntry && this._isResourceNamespace(resource, namespace)) {
return namespace;
}
}
return undefined;
}
_getStorageNamespace(resource, data) {
const isArray = Array.isArray(data);
if (isArray && data.length === 0) {
return;
}
const scope = isArray ? data[0].guild_id : data.guild_id;
return `${resource}${scope ? `.${scope}` : ''}`;
}
_getStorageEntry(key) {
const resource = this._getKeyResource(key);
const supportsNamespaceIndex = this._supportsNamespaceIndex(resource);
const indexedStorage = this._getIndexedStorage(resource, key);
if (indexedStorage?.has(key)) {
return {
resource,
storageEntry: indexedStorage,
};
}
if (indexedStorage) {
this._deleteIndexedStorage(resource, key);
}
if (!supportsNamespaceIndex) {
const namespace = this._getDerivedNamespace(resource, this._getKeyScope(key));
const storageEntry = this.storage.get(namespace);
if (storageEntry?.has(key)) {
return {
resource,
namespace,
storageEntry,
};
}
}
for (const [namespace, storageEntry] of this.storage.entries()) {
if (this._isResourceNamespace(resource, namespace) && storageEntry.has(key)) {
this._setIndexedStorage(resource, key, storageEntry);
return {
resource,
namespace,
storageEntry,
};
}
}
return;
}
get(key) {
const entry = this._getStorageEntry(key);
if (!entry) {
return null;
}
return this.options.decode(entry.storageEntry.get(key));
}
__set(key, data) {
const resource = this._getKeyResource(key);
const namespace = this._getStorageNamespace(resource, data);
if (!namespace) {
return;
}
let storageEntry = this.storage.get(namespace);
if (!storageEntry) {
const self = this;
storageEntry = new __1.LimitedCollection({
expire: this.options[resource]?.expire ??
this.options.default.expire,
limit: this.options[resource]?.limit ??
this.options.default.limit,
resetOnDemand: true,
onDelete(k, value) {
self._deleteIndexedStorage(resource, k);
const relationshipNamespace = resource;
const existsRelation = self.relationships.has(relationshipNamespace);
if (existsRelation) {
switch (relationshipNamespace) {
case 'message':
{
const decodedValue = self.options.decode(value);
if (decodedValue.channel_id)
self.removeToRelationship(`${relationshipNamespace}.${decodedValue.channel_id}`, k.split('.')[1]);
}
break;
case 'ban':
case 'member':
case 'voice_state':
{
const split = k.split('.');
self.removeToRelationship(`${namespace}.${split[1]}`, split[2]);
}
break;
case 'channel':
case 'emoji':
case 'presence':
case 'role':
case 'stage_instance':
case 'sticker':
case 'overwrite':
self.removeToRelationship(namespace, k.split('.')[1]);
break;
// case 'guild':
// case 'user':
default:
self.removeToRelationship(namespace, k.split('.')[1]);
break;
}
}
},
});
this.storage.set(namespace, storageEntry);
}
const previousBucket = this._getIndexedStorage(resource, key);
if (previousBucket && previousBucket !== storageEntry) {
previousBucket?.delete(key);
if (previousBucket?.size === 0) {
const previousNamespace = this._findNamespaceByStorage(resource, previousBucket);
if (previousNamespace) {
this.storage.delete(previousNamespace);
}
}
}
this._setIndexedStorage(resource, key, storageEntry);
storageEntry.set(key, this.options.encode(data));
}
bulkSet(keys) {
for (const [key, value] of keys) {
this.__set(key, value);
}
}
set(keys, data) {
this.__set(keys, data);
}
bulkPatch(keys) {
for (const [key, value] of keys) {
const oldData = this.get(key);
this.__set(key, Array.isArray(value) ? value : { ...(oldData ?? {}), ...value });
}
}
patch(keys, data) {
const oldData = this.get(keys);
this.__set(keys, Array.isArray(data) ? data : { ...(oldData ?? {}), ...data });
}
values(to) {
const array = [];
const data = this.keys(to);
for (const key of data) {
const content = this.get(key);
if (content !== undefined && content !== null) {
array.push(content);
}
}
return array;
}
keys(to) {
const result = [];
for (const id of this._getRelationshipSet(to)) {
result.push(`${to}.${id}`);
}
return result;
}
count(to) {
return this._getRelationshipSet(to).size;
}
bulkRemove(keys) {
for (const i of keys) {
this.remove(i);
}
}
remove(key) {
const entry = this._getStorageEntry(key);
if (!entry) {
return;
}
entry.storageEntry.delete(key);
this._deleteIndexedStorage(entry.resource, key);
if (entry.storageEntry.size === 0) {
const namespace = entry.namespace ?? this._findNamespaceByStorage(entry.resource, entry.storageEntry);
if (namespace) {
this.storage.delete(namespace);
}
}
}
flush() {
this.storage.clear();
this.relationships.clear();
this.keyToStorage.clear();
}
contains(to, keys) {
return this._getRelationshipSet(to).has(keys);
}
_getRelationshipData(to) {
const [key, subrelationKey = '*'] = to.split('.');
return { key, subrelationKey };
}
_getRelationshipSet(to) {
const { key, subrelationKey } = this._getRelationshipData(to);
if (!this.relationships.has(key))
this.relationships.set(key, new Map());
const relation = this.relationships.get(key);
if (!relation.has(subrelationKey)) {
relation.set(subrelationKey, new Set());
}
return relation.get(subrelationKey);
}
getToRelationship(to) {
return [...this._getRelationshipSet(to)];
}
bulkAddToRelationShip(data) {
for (const i in data) {
this.addToRelationship(i, data[i]);
}
}
addToRelationship(to, keys) {
const data = this._getRelationshipSet(to);
for (const key of Array.isArray(keys) ? keys : [keys]) {
data.add(key);
}
}
removeToRelationship(to, keys) {
const data = this._getRelationshipSet(to);
for (const key of Array.isArray(keys) ? keys : [keys]) {
data.delete(key);
}
}
removeRelationship(to) {
for (const i of Array.isArray(to) ? to : [to]) {
const { key, subrelationKey } = this._getRelationshipData(i);
if (subrelationKey === '*') {
this.relationships.delete(key);
continue;
}
const relation = this.relationships.get(key);
if (!relation)
continue;
relation.delete(subrelationKey);
if (!relation.size)
this.relationships.delete(key);
}
}
}
exports.LimitedMemoryAdapter = LimitedMemoryAdapter;