feat: complete Ancestor quote bot with production-ready Docker support

This commit is contained in:
unfunny
2026-09-13 14:14:38 -04:00
parent f2016da05a
commit fe7351a7dc
1145 changed files with 30890 additions and 97684 deletions

View File

@@ -1,4 +1,15 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.LocalIndexedDBStoreBackend = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _syncAccumulator = require("../sync-accumulator");
var _utils = require("../utils");
var _indexeddbHelpers = require("../indexeddb-helpers");
var _logger = require("../logger");
/*
Copyright 2017 - 2021 The Matrix.org Foundation C.I.C.
@@ -15,10 +26,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { SyncAccumulator } from "../sync-accumulator.js";
import { deepCopy, promiseTry } from "../utils.js";
import { exists as idbExists } from "../indexeddb-helpers.js";
import { logger } from "../logger.js";
const DB_MIGRATIONS = [db => {
// Make user store, clobber based on user ID. (userId property of User objects)
db.createObjectStore("users", {
@@ -48,13 +55,10 @@ const DB_MIGRATIONS = [db => {
db.createObjectStore("to_device_queue", {
autoIncrement: true
});
}, db => {
db.createObjectStore("user_profile", {
keyPath: ["userId"]
});
}
// Expand as needed.
];
const VERSION = DB_MIGRATIONS.length;
/**
@@ -66,16 +70,13 @@ const VERSION = DB_MIGRATIONS.length;
* Return the data you want to keep.
* @returns Promise which resolves to an array of whatever you returned from
* resultMapper.
* @throws If there was an error completing the query.
*/
function selectQuery(store, keyRange, resultMapper) {
const query = store.openCursor(keyRange);
return new Promise((resolve, reject) => {
const results = [];
query.onerror = () => {
reject(new Error(`selectQuery failed for ${store.name}`, {
cause: query.error
}));
reject(new Error("Query failed: " + query.error));
};
// collect results
query.onsuccess = () => {
@@ -84,6 +85,7 @@ function selectQuery(store, keyRange, resultMapper) {
resolve(results);
return; // end of results
}
results.push(resultMapper(cursor));
cursor.continue();
};
@@ -118,10 +120,10 @@ function reqAsPromise(req) {
function reqAsCursorPromise(req) {
return reqAsEventPromise(req).then(event => req.result);
}
export class LocalIndexedDBStoreBackend {
class LocalIndexedDBStoreBackend {
static exists(indexedDB, dbName) {
dbName = "matrix-js-sdk:" + (dbName || "default");
return idbExists(indexedDB, dbName);
return (0, _indexeddbHelpers.exists)(indexedDB, dbName);
}
/**
* Does the actual reading from and writing to the indexeddb
@@ -134,16 +136,16 @@ export class LocalIndexedDBStoreBackend {
* to open the same database.
*/
constructor(indexedDB, dbName = "default") {
_defineProperty(this, "dbName", void 0);
_defineProperty(this, "syncAccumulator", void 0);
_defineProperty(this, "db", void 0);
_defineProperty(this, "disconnected", true);
_defineProperty(this, "_isNewlyCreated", false);
_defineProperty(this, "syncToDatabasePromise", void 0);
_defineProperty(this, "pendingUserPresenceData", []);
this.indexedDB = indexedDB;
(0, _defineProperty2.default)(this, "dbName", void 0);
(0, _defineProperty2.default)(this, "syncAccumulator", void 0);
(0, _defineProperty2.default)(this, "db", void 0);
(0, _defineProperty2.default)(this, "disconnected", true);
(0, _defineProperty2.default)(this, "_isNewlyCreated", false);
(0, _defineProperty2.default)(this, "syncToDatabasePromise", void 0);
(0, _defineProperty2.default)(this, "pendingUserPresenceData", []);
this.dbName = "matrix-js-sdk:" + dbName;
this.syncAccumulator = new SyncAccumulator();
this.syncAccumulator = new _syncAccumulator.SyncAccumulator();
}
/**
@@ -153,16 +155,16 @@ export class LocalIndexedDBStoreBackend {
*/
connect(onClose) {
if (!this.disconnected) {
logger.log(`LocalIndexedDBStoreBackend.connect: already connected or connecting`);
_logger.logger.log(`LocalIndexedDBStoreBackend.connect: already connected or connecting`);
return Promise.resolve();
}
this.disconnected = false;
logger.log(`LocalIndexedDBStoreBackend.connect: connecting...`);
_logger.logger.log(`LocalIndexedDBStoreBackend.connect: connecting...`);
const req = this.indexedDB.open(this.dbName, VERSION);
req.onupgradeneeded = ev => {
const db = req.result;
const oldVersion = ev.oldVersion;
logger.log(`LocalIndexedDBStoreBackend.connect: upgrading from ${oldVersion}`);
_logger.logger.log(`LocalIndexedDBStoreBackend.connect: upgrading from ${oldVersion}`);
if (oldVersion < 1) {
// The database did not previously exist
this._isNewlyCreated = true;
@@ -172,24 +174,26 @@ export class LocalIndexedDBStoreBackend {
});
};
req.onblocked = () => {
logger.log(`can't yet open LocalIndexedDBStoreBackend because it is open elsewhere`);
_logger.logger.log(`can't yet open LocalIndexedDBStoreBackend because it is open elsewhere`);
};
logger.log(`LocalIndexedDBStoreBackend.connect: awaiting connection...`);
_logger.logger.log(`LocalIndexedDBStoreBackend.connect: awaiting connection...`);
return reqAsEventPromise(req).then(async () => {
logger.log(`LocalIndexedDBStoreBackend.connect: connected`);
_logger.logger.log(`LocalIndexedDBStoreBackend.connect: connected`);
this.db = req.result;
// add a poorly-named listener for when deleteDatabase is called
// so we can close our db connections.
this.db.onversionchange = () => {
this.db?.close(); // this does not call onclose
var _this$db;
(_this$db = this.db) === null || _this$db === void 0 ? void 0 : _this$db.close(); // this does not call onclose
this.disconnected = true;
this.db = undefined;
onClose === null || onClose === void 0 ? void 0 : onClose();
};
this.db.onclose = () => {
this.disconnected = true;
this.db = undefined;
onClose?.();
onClose === null || onClose === void 0 ? void 0 : onClose();
};
await this.init();
});
@@ -206,7 +210,7 @@ export class LocalIndexedDBStoreBackend {
*/
init() {
return Promise.all([this.loadAccountData(), this.loadSyncData()]).then(([accountData, syncData]) => {
logger.log(`LocalIndexedDBStoreBackend: loaded initial data`);
_logger.logger.log(`LocalIndexedDBStoreBackend: loaded initial data`);
this.syncAccumulator.accumulate({
next_batch: syncData.nextBatch,
rooms: syncData.roomsData,
@@ -258,7 +262,7 @@ export class LocalIndexedDBStoreBackend {
reject(err);
};
}).then(events => {
logger.log(`LL: got ${events?.length} membershipEvents from storage for room ${roomId} ...`);
_logger.logger.log(`LL: got ${events === null || events === void 0 ? void 0 : events.length} membershipEvents from storage for room ${roomId} ...`);
return events;
});
}
@@ -270,7 +274,7 @@ export class LocalIndexedDBStoreBackend {
* @param membershipEvents - the membership events to store
*/
async setOutOfBandMembers(roomId, membershipEvents) {
logger.log(`LL: backend about to store ${membershipEvents.length} members for ${roomId}`);
_logger.logger.log(`LL: backend about to store ${membershipEvents.length}` + ` members for ${roomId}`);
const tx = this.db.transaction(["oob_membership_events"], "readwrite");
const store = tx.objectStore("oob_membership_events");
membershipEvents.forEach(e => {
@@ -288,7 +292,7 @@ export class LocalIndexedDBStoreBackend {
};
store.put(markerObject);
await txnAsPromise(tx);
logger.log(`LL: backend done storing for ${roomId}!`);
_logger.logger.log(`LL: backend done storing for ${roomId}!`);
}
async clearOutOfBandMembers(roomId) {
// the approach to delete all members for a room
@@ -301,13 +305,13 @@ export class LocalIndexedDBStoreBackend {
const store = readTx.objectStore("oob_membership_events");
const roomIndex = store.index("room");
const roomRange = IDBKeyRange.only(roomId);
const minStateKeyProm = reqAsCursorPromise(roomIndex.openKeyCursor(roomRange, "next")).then(cursor => cursor?.primaryKey?.[1]);
const maxStateKeyProm = reqAsCursorPromise(roomIndex.openKeyCursor(roomRange, "prev")).then(cursor => cursor?.primaryKey?.[1]);
const minStateKeyProm = reqAsCursorPromise(roomIndex.openKeyCursor(roomRange, "next")).then(cursor => (cursor === null || cursor === void 0 ? void 0 : cursor.primaryKey)[1]);
const maxStateKeyProm = reqAsCursorPromise(roomIndex.openKeyCursor(roomRange, "prev")).then(cursor => (cursor === null || cursor === void 0 ? void 0 : cursor.primaryKey)[1]);
const [minStateKey, maxStateKey] = await Promise.all([minStateKeyProm, maxStateKeyProm]);
const writeTx = this.db.transaction(["oob_membership_events"], "readwrite");
const writeStore = writeTx.objectStore("oob_membership_events");
const membersKeyRange = IDBKeyRange.bound([roomId, minStateKey], [roomId, maxStateKey]);
logger.log(`LL: Deleting all users + marker in storage for room ${roomId}, with key range:`, [roomId, minStateKey], [roomId, maxStateKey]);
_logger.logger.log(`LL: Deleting all users + marker in storage for room ${roomId}, with key range:`, [roomId, minStateKey], [roomId, maxStateKey]);
await reqAsPromise(writeStore.delete(membersKeyRange));
}
@@ -318,23 +322,24 @@ export class LocalIndexedDBStoreBackend {
*/
clearDatabase() {
return new Promise(resolve => {
logger.log(`Removing indexeddb instance: ${this.dbName}`);
var _this$db2;
_logger.logger.log(`Removing indexeddb instance: ${this.dbName}`);
// Close the database first to avoid firing unexpected close events
this.db?.close();
(_this$db2 = this.db) === null || _this$db2 === void 0 ? void 0 : _this$db2.close();
const req = this.indexedDB.deleteDatabase(this.dbName);
req.onblocked = () => {
logger.log(`can't yet delete indexeddb ${this.dbName} because it is open elsewhere`);
_logger.logger.log(`can't yet delete indexeddb ${this.dbName} because it is open elsewhere`);
};
req.onerror = () => {
// in firefox, with indexedDB disabled, this fails with a
// DOMError. We treat this as non-fatal, so that we can still
// use the app.
logger.warn(`unable to delete js-sdk store indexeddb: ${req.error?.name}`);
_logger.logger.warn(`unable to delete js-sdk store indexeddb: ${req.error}`);
resolve();
};
req.onsuccess = () => {
logger.log(`Removed indexeddb instance: ${this.dbName}`);
_logger.logger.log(`Removed indexeddb instance: ${this.dbName}`);
resolve();
};
});
@@ -355,7 +360,7 @@ export class LocalIndexedDBStoreBackend {
if (copy) {
// We must deep copy the stored data so that the /sync processing code doesn't
// corrupt the internal state of the sync accumulator (it adds non-clonable keys)
return Promise.resolve(deepCopy(data));
return Promise.resolve((0, _utils.deepCopy)(data));
} else {
return Promise.resolve(data);
}
@@ -378,7 +383,7 @@ export class LocalIndexedDBStoreBackend {
*/
async syncToDatabase(userTuples) {
if (this.syncToDatabasePromise) {
logger.warn("Skipping syncToDatabase() as persist already in flight");
_logger.logger.warn("Skipping syncToDatabase() as persist already in flight");
this.pendingUserPresenceData.push(...userTuples);
return this.syncToDatabasePromise;
}
@@ -402,8 +407,8 @@ export class LocalIndexedDBStoreBackend {
* @returns Promise which resolves if the data was persisted.
*/
persistSyncData(nextBatch, roomsData) {
logger.log("Persisting sync data up to", nextBatch);
return promiseTry(() => {
_logger.logger.log("Persisting sync data up to", nextBatch);
return (0, _utils.promiseTry)(() => {
const txn = this.db.transaction(["sync"], "readwrite");
const store = txn.objectStore("sync");
store.put({
@@ -413,7 +418,7 @@ export class LocalIndexedDBStoreBackend {
roomsData
}); // put == UPSERT
return txnAsPromise(txn).then(() => {
logger.log("Persisted sync data up to", nextBatch);
_logger.logger.log("Persisted sync data up to", nextBatch);
});
});
}
@@ -425,12 +430,13 @@ export class LocalIndexedDBStoreBackend {
* @returns Promise which resolves if the events were persisted.
*/
persistAccountData(accountData) {
return promiseTry(() => {
return (0, _utils.promiseTry)(() => {
const txn = this.db.transaction(["accountData"], "readwrite");
const store = txn.objectStore("accountData");
for (const event of accountData) {
store.put(event); // put == UPSERT
}
return txnAsPromise(txn).then();
});
}
@@ -444,7 +450,7 @@ export class LocalIndexedDBStoreBackend {
* @returns Promise which resolves if the users were persisted.
*/
persistUserPresenceEvents(tuples) {
return promiseTry(() => {
return (0, _utils.promiseTry)(() => {
const txn = this.db.transaction(["users"], "readwrite");
const store = txn.objectStore("users");
for (const tuple of tuples) {
@@ -453,6 +459,7 @@ export class LocalIndexedDBStoreBackend {
event: tuple[1]
}); // put == UPSERT
}
return txnAsPromise(txn).then();
});
}
@@ -464,7 +471,7 @@ export class LocalIndexedDBStoreBackend {
* @returns A list of presence events in their raw form.
*/
getUserPresenceEvents() {
return promiseTry(() => {
return (0, _utils.promiseTry)(() => {
const txn = this.db.transaction(["users"], "readonly");
const store = txn.objectStore("users");
return selectQuery(store, undefined, cursor => {
@@ -478,14 +485,14 @@ export class LocalIndexedDBStoreBackend {
* @returns A list of raw global account events.
*/
loadAccountData() {
logger.log(`LocalIndexedDBStoreBackend: loading account data...`);
return promiseTry(() => {
_logger.logger.log(`LocalIndexedDBStoreBackend: loading account data...`);
return (0, _utils.promiseTry)(() => {
const txn = this.db.transaction(["accountData"], "readonly");
const store = txn.objectStore("accountData");
return selectQuery(store, undefined, cursor => {
return cursor.value;
}).then(result => {
logger.log(`LocalIndexedDBStoreBackend: loaded account data`);
_logger.logger.log(`LocalIndexedDBStoreBackend: loaded account data`);
return result;
});
});
@@ -496,16 +503,16 @@ export class LocalIndexedDBStoreBackend {
* @returns An object with "roomsData" and "nextBatch" keys.
*/
loadSyncData() {
logger.log(`LocalIndexedDBStoreBackend: loading sync data...`);
return promiseTry(() => {
_logger.logger.log(`LocalIndexedDBStoreBackend: loading sync data...`);
return (0, _utils.promiseTry)(() => {
const txn = this.db.transaction(["sync"], "readonly");
const store = txn.objectStore("sync");
return selectQuery(store, undefined, cursor => {
return cursor.value;
}).then(results => {
logger.log(`LocalIndexedDBStoreBackend: loaded sync data`);
_logger.logger.log(`LocalIndexedDBStoreBackend: loaded sync data`);
if (results.length > 1) {
logger.warn("loadSyncData: More than 1 sync row found.");
_logger.logger.warn("loadSyncData: More than 1 sync row found.");
}
return results.length > 0 ? results[0] : {};
});
@@ -516,7 +523,8 @@ export class LocalIndexedDBStoreBackend {
const txn = this.db.transaction(["client_options"], "readonly");
const store = txn.objectStore("client_options");
return selectQuery(store, undefined, cursor => {
return cursor.value?.options;
var _cursor$value;
return (_cursor$value = cursor.value) === null || _cursor$value === void 0 ? void 0 : _cursor$value.options;
}).then(results => results[0]);
});
}
@@ -557,49 +565,14 @@ export class LocalIndexedDBStoreBackend {
store.delete(id);
await txnAsPromise(txn);
}
async getUserProfile(userId) {
return Promise.resolve().then(() => {
const txn = this.db.transaction(["user_profile"], "readonly");
const store = txn.objectStore("user_profile");
return selectQuery(store, [userId], cursor => {
return cursor.value?.profile;
}).then(results => results[0]);
});
}
async storeUserProfiles(userProfiles) {
const txn = this.db.transaction(["user_profile"], "readwrite");
const store = txn.objectStore("user_profile");
for (const [userId, profile] of userProfiles.entries()) {
store.put({
profile,
userId
});
}
await txnAsPromise(txn);
}
async removeUserProfiles(userIds) {
const txn = this.db.transaction(["user_profile"], "readwrite");
const store = txn.objectStore("user_profile");
for (const userId of userIds) {
store.delete([userId]);
}
await txnAsPromise(txn);
}
async removeEventsFromRoom(roomId, eventIds) {
try {
this.syncAccumulator.removeEventsFromRoom(roomId, eventIds);
const syncData = this.syncAccumulator.getJSON(true);
await this.persistSyncData(syncData.nextBatch, syncData.roomsData);
} finally {
this.syncToDatabasePromise = undefined;
}
}
/*
* Close the database
*/
async destroy() {
this.db?.close();
var _this$db3;
(_this$db3 = this.db) === null || _this$db3 === void 0 ? void 0 : _this$db3.close();
}
}
exports.LocalIndexedDBStoreBackend = LocalIndexedDBStoreBackend;
//# sourceMappingURL=indexeddb-local-backend.js.map