diff --git a/src/EventEmitter.js b/src/EventEmitter.js index 27a4e17d..fd155a7e 100644 --- a/src/EventEmitter.js +++ b/src/EventEmitter.js @@ -1,36 +1,36 @@ export default class EventEmitter { - constructor() { - this._handlersByName = {}; - } + constructor() { + this._handlersByName = {}; + } - emit(name, ...values) { - const handlers = this._handlersByName[name]; - if (handlers) { - for(const h of handlers) { - h(...values); - } - } - } + emit(name, ...values) { + const handlers = this._handlersByName[name]; + if (handlers) { + for(const h of handlers) { + h(...values); + } + } + } - on(name, callback) { - let handlers = this._handlersByName[name]; - if (!handlers) { + on(name, callback) { + let handlers = this._handlersByName[name]; + if (!handlers) { this.onFirstSubscriptionAdded(name); - this._handlersByName[name] = handlers = new Set(); - } - handlers.add(callback); - } + this._handlersByName[name] = handlers = new Set(); + } + handlers.add(callback); + } - off(name, callback) { - const handlers = this._handlersByName[name]; - if (handlers) { - handlers.delete(callback); - if (handlers.length === 0) { - delete this._handlersByName[name]; + off(name, callback) { + const handlers = this._handlersByName[name]; + if (handlers) { + handlers.delete(callback); + if (handlers.length === 0) { + delete this._handlersByName[name]; this.onLastSubscriptionRemoved(name); - } - } - } + } + } + } onFirstSubscriptionAdded(name) {} @@ -38,38 +38,38 @@ export default class EventEmitter { } //#ifdef TESTS export function tests() { - return { - test_on_off(assert) { - let counter = 0; - const e = new EventEmitter(); - const callback = () => counter += 1; - e.on("change", callback); - e.emit("change"); - e.off("change", callback); - e.emit("change"); - assert.equal(counter, 1); - }, + return { + test_on_off(assert) { + let counter = 0; + const e = new EventEmitter(); + const callback = () => counter += 1; + e.on("change", callback); + e.emit("change"); + e.off("change", callback); + e.emit("change"); + assert.equal(counter, 1); + }, - test_emit_value(assert) { - let value = 0; - const e = new EventEmitter(); - const callback = (v) => value = v; - e.on("change", callback); - e.emit("change", 5); - e.off("change", callback); - assert.equal(value, 5); - }, + test_emit_value(assert) { + let value = 0; + const e = new EventEmitter(); + const callback = (v) => value = v; + e.on("change", callback); + e.emit("change", 5); + e.off("change", callback); + assert.equal(value, 5); + }, - test_double_on(assert) { - let counter = 0; - const e = new EventEmitter(); - const callback = () => counter += 1; - e.on("change", callback); - e.on("change", callback); - e.emit("change"); - e.off("change", callback); - assert.equal(counter, 1); - } - }; + test_double_on(assert) { + let counter = 0; + const e = new EventEmitter(); + const callback = () => counter += 1; + e.on("change", callback); + e.on("change", callback); + e.emit("change"); + e.off("change", callback); + assert.equal(counter, 1); + } + }; } //#endif diff --git a/src/main.js b/src/main.js index 02289a9a..82a67efc 100644 --- a/src/main.js +++ b/src/main.js @@ -12,14 +12,14 @@ const USER_ID = `@${USERNAME}:localhost`; const PASSWORD = "testtest"; function getSessionInfo(userId) { - const sessionsJson = localStorage.getItem("brawl_sessions_v1"); - if (sessionsJson) { - const sessions = JSON.parse(sessionsJson); - const session = sessions.find(session => session.userId === userId); - if (session) { - return session; - } - } + const sessionsJson = localStorage.getItem("brawl_sessions_v1"); + if (sessionsJson) { + const sessions = JSON.parse(sessionsJson); + const session = sessions.find(session => session.userId === userId); + if (session) { + return session; + } + } } function storeSessionInfo(loginData) { @@ -39,9 +39,9 @@ function storeSessionInfo(loginData) { } async function login(username, password, homeserver) { - const hsApi = new HomeServerApi(homeserver); - const loginData = await hsApi.passwordLogin(username, password).response(); - return storeSessionInfo(loginData); + const hsApi = new HomeServerApi(homeserver); + const loginData = await hsApi.passwordLogin(username, password).response(); + return storeSessionInfo(loginData); } function showSession(container, session, sync) { @@ -51,11 +51,11 @@ function showSession(container, session, sync) { } export default async function main(container) { - try { - let sessionInfo = getSessionInfo(USER_ID); - if (!sessionInfo) { - sessionInfo = await login(USERNAME, PASSWORD, HOMESERVER); - } + try { + let sessionInfo = getSessionInfo(USER_ID); + if (!sessionInfo) { + sessionInfo = await login(USERNAME, PASSWORD, HOMESERVER); + } const storage = await createIdbStorage(`brawl_session_${sessionInfo.id}`); const hsApi = new HomeServerApi(HOMESERVER, sessionInfo.accessToken); const session = new Session({storage, hsApi, sessionInfo: { @@ -65,18 +65,18 @@ export default async function main(container) { }}); await session.load(); console.log("session loaded"); - const sync = new Sync(hsApi, session, storage); + const sync = new Sync(hsApi, session, storage); const needsInitialSync = !session.syncToken; if (needsInitialSync) { console.log("session needs initial sync"); } else { showSession(container, session, sync); } - await sync.start(); + await sync.start(); if (needsInitialSync) { showSession(container, session, sync); } - } catch(err) { + } catch(err) { console.error(`${err.message}:\n${err.stack}`); - } + } } diff --git a/src/matrix/error.js b/src/matrix/error.js index 847d50b2..a70ec3fa 100644 --- a/src/matrix/error.js +++ b/src/matrix/error.js @@ -1,8 +1,8 @@ export class HomeServerError extends Error { - constructor(method, url, body) { - super(`${body.error} on ${method} ${url}`); - this.errcode = body.errcode; - } + constructor(method, url, body) { + super(`${body.error} on ${method} ${url}`); + this.errcode = body.errcode; + } } export class RequestAbortError extends Error { diff --git a/src/matrix/hs-api.js b/src/matrix/hs-api.js index cc2e60f4..2bc1ff72 100644 --- a/src/matrix/hs-api.js +++ b/src/matrix/hs-api.js @@ -1,11 +1,11 @@ import { - HomeServerError, - RequestAbortError, + HomeServerError, + RequestAbortError, NetworkError } from "./error.js"; class RequestWrapper { - constructor(promise, controller) { + constructor(promise, controller) { if (!controller) { const abortPromise = new Promise((_, reject) => { this._controller = { @@ -21,63 +21,63 @@ class RequestWrapper { this._promise = promise; this._controller = controller; } - } + } - abort() { - this._controller.abort(); - } + abort() { + this._controller.abort(); + } - response() { - return this._promise; - } + response() { + return this._promise; + } } export default class HomeServerApi { - constructor(homeserver, accessToken) { + constructor(homeserver, accessToken) { // store these both in a closure somehow so it's harder to get at in case of XSS? // one could change the homeserver as well so the token gets sent there, so both must be protected from read/write this._homeserver = homeserver; - this._accessToken = accessToken; - } + this._accessToken = accessToken; + } - _url(csPath) { - return `${this._homeserver}/_matrix/client/r0${csPath}`; - } + _url(csPath) { + return `${this._homeserver}/_matrix/client/r0${csPath}`; + } - _request(method, csPath, queryParams = {}, body) { - const queryString = Object.entries(queryParams) - .filter(([, value]) => value !== undefined) - .map(([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`) - .join("&"); - const url = this._url(`${csPath}?${queryString}`); - let bodyString; - const headers = new Headers(); - if (this._accessToken) { - headers.append("Authorization", `Bearer ${this._accessToken}`); - } - headers.append("Accept", "application/json"); - if (body) { - headers.append("Content-Type", "application/json"); - bodyString = JSON.stringify(body); - } - const controller = typeof AbortController === "function" ? new AbortController() : null; - // TODO: set authenticated headers with second arguments, cache them - let promise = fetch(url, { - method, - headers, - body: bodyString, - signal: controller && controller.signal - }); - promise = promise.then(async (response) => { - if (response.ok) { - return await response.json(); - } else { - switch (response.status) { - default: - throw new HomeServerError(method, url, await response.json()) - } - } - }, err => { + _request(method, csPath, queryParams = {}, body) { + const queryString = Object.entries(queryParams) + .filter(([, value]) => value !== undefined) + .map(([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`) + .join("&"); + const url = this._url(`${csPath}?${queryString}`); + let bodyString; + const headers = new Headers(); + if (this._accessToken) { + headers.append("Authorization", `Bearer ${this._accessToken}`); + } + headers.append("Accept", "application/json"); + if (body) { + headers.append("Content-Type", "application/json"); + bodyString = JSON.stringify(body); + } + const controller = typeof AbortController === "function" ? new AbortController() : null; + // TODO: set authenticated headers with second arguments, cache them + let promise = fetch(url, { + method, + headers, + body: bodyString, + signal: controller && controller.signal + }); + promise = promise.then(async (response) => { + if (response.ok) { + return await response.json(); + } else { + switch (response.status) { + default: + throw new HomeServerError(method, url, await response.json()) + } + } + }, err => { if (err.name === "AbortError") { throw new RequestAbortError(); } else if (err instanceof TypeError) { @@ -89,29 +89,29 @@ export default class HomeServerApi { // but the 2 later ones are indistinguishable from javascript. throw new NetworkError(err.message); } - throw err; - }); - return new RequestWrapper(promise, controller); - } + throw err; + }); + return new RequestWrapper(promise, controller); + } - _post(csPath, queryParams, body) { - return this._request("POST", csPath, queryParams, body); - } + _post(csPath, queryParams, body) { + return this._request("POST", csPath, queryParams, body); + } - _get(csPath, queryParams, body) { - return this._request("GET", csPath, queryParams, body); - } + _get(csPath, queryParams, body) { + return this._request("GET", csPath, queryParams, body); + } - sync(since, filter, timeout) { - return this._get("/sync", {since, timeout, filter}); - } + sync(since, filter, timeout) { + return this._get("/sync", {since, timeout, filter}); + } // params is from, dir and optionally to, limit, filter. messages(roomId, params) { return this._get(`/rooms/${roomId}/messages`, params); } - passwordLogin(username, password) { + passwordLogin(username, password) { return this._post("/login", undefined, { "type": "m.login.password", "identifier": { @@ -120,5 +120,5 @@ export default class HomeServerApi { }, "password": password }); - } + } } diff --git a/src/matrix/storage/idb/query-target.js b/src/matrix/storage/idb/query-target.js index a5c38b15..e8eb36fb 100644 --- a/src/matrix/storage/idb/query-target.js +++ b/src/matrix/storage/idb/query-target.js @@ -1,9 +1,9 @@ import {iterateCursor, reqAsPromise} from "./utils.js"; export default class QueryTarget { - constructor(target) { - this._target = target; - } + constructor(target) { + this._target = target; + } _openCursor(range, direction) { if (range && direction) { @@ -21,55 +21,55 @@ export default class QueryTarget { return reqAsPromise(this._target.get(key)); } - reduce(range, reducer, initialValue) { - return this._reduce(range, reducer, initialValue, "next"); - } + reduce(range, reducer, initialValue) { + return this._reduce(range, reducer, initialValue, "next"); + } - reduceReverse(range, reducer, initialValue) { - return this._reduce(range, reducer, initialValue, "prev"); - } - - selectLimit(range, amount) { - return this._selectLimit(range, amount, "next"); - } + reduceReverse(range, reducer, initialValue) { + return this._reduce(range, reducer, initialValue, "prev"); + } + + selectLimit(range, amount) { + return this._selectLimit(range, amount, "next"); + } - selectLimitReverse(range, amount) { - return this._selectLimit(range, amount, "prev"); - } + selectLimitReverse(range, amount) { + return this._selectLimit(range, amount, "prev"); + } - selectWhile(range, predicate) { - return this._selectWhile(range, predicate, "next"); - } + selectWhile(range, predicate) { + return this._selectWhile(range, predicate, "next"); + } - selectWhileReverse(range, predicate) { - return this._selectWhile(range, predicate, "prev"); - } + selectWhileReverse(range, predicate) { + return this._selectWhile(range, predicate, "prev"); + } - async selectAll(range, direction) { - const cursor = this._openCursor(range, direction); - const results = []; - await iterateCursor(cursor, (value) => { - results.push(value); - return {done: false}; - }); - return results; - } + async selectAll(range, direction) { + const cursor = this._openCursor(range, direction); + const results = []; + await iterateCursor(cursor, (value) => { + results.push(value); + return {done: false}; + }); + return results; + } - selectFirst(range) { - return this._find(range, () => true, "next"); - } + selectFirst(range) { + return this._find(range, () => true, "next"); + } - selectLast(range) { - return this._find(range, () => true, "prev"); - } + selectLast(range) { + return this._find(range, () => true, "prev"); + } - find(range, predicate) { - return this._find(range, predicate, "next"); - } + find(range, predicate) { + return this._find(range, predicate, "next"); + } - findReverse(range, predicate) { - return this._find(range, predicate, "prev"); - } + findReverse(range, predicate) { + return this._find(range, predicate, "prev"); + } /** * Checks if a given set of keys exist. @@ -107,43 +107,43 @@ export default class QueryTarget { } } - _reduce(range, reducer, initialValue, direction) { - let reducedValue = initialValue; - const cursor = this._openCursor(range, direction); - return iterateCursor(cursor, (value) => { - reducedValue = reducer(reducedValue, value); - return {done: false}; - }); - } + _reduce(range, reducer, initialValue, direction) { + let reducedValue = initialValue; + const cursor = this._openCursor(range, direction); + return iterateCursor(cursor, (value) => { + reducedValue = reducer(reducedValue, value); + return {done: false}; + }); + } - _selectLimit(range, amount, direction) { - return this._selectWhile(range, (results) => { - return results.length === amount; - }, direction); - } + _selectLimit(range, amount, direction) { + return this._selectWhile(range, (results) => { + return results.length === amount; + }, direction); + } - async _selectWhile(range, predicate, direction) { - const cursor = this._openCursor(range, direction); - const results = []; - await iterateCursor(cursor, (value) => { - results.push(value); - return {done: predicate(results)}; - }); - return results; - } + async _selectWhile(range, predicate, direction) { + const cursor = this._openCursor(range, direction); + const results = []; + await iterateCursor(cursor, (value) => { + results.push(value); + return {done: predicate(results)}; + }); + return results; + } - async _find(range, predicate, direction) { - const cursor = this._openCursor(range, direction); - let result; - const found = await iterateCursor(cursor, (value) => { - const found = predicate(value); - if (found) { - result = value; - } - return {done: found}; - }); - if (found) { - return result; - } - } + async _find(range, predicate, direction) { + const cursor = this._openCursor(range, direction); + let result; + const found = await iterateCursor(cursor, (value) => { + const found = predicate(value); + if (found) { + result = value; + } + return {done: found}; + }); + if (found) { + return result; + } + } } diff --git a/src/matrix/storage/idb/storage.js b/src/matrix/storage/idb/storage.js index 25bb0d39..5466ab76 100644 --- a/src/matrix/storage/idb/storage.js +++ b/src/matrix/storage/idb/storage.js @@ -2,39 +2,39 @@ import Transaction from "./transaction.js"; import { STORE_NAMES, StorageError } from "../common.js"; export default class Storage { - constructor(idbDatabase) { - this._db = idbDatabase; - const nameMap = STORE_NAMES.reduce((nameMap, name) => { - nameMap[name] = name; - return nameMap; - }, {}); - this.storeNames = Object.freeze(nameMap); - } + constructor(idbDatabase) { + this._db = idbDatabase; + const nameMap = STORE_NAMES.reduce((nameMap, name) => { + nameMap[name] = name; + return nameMap; + }, {}); + this.storeNames = Object.freeze(nameMap); + } - _validateStoreNames(storeNames) { - const idx = storeNames.findIndex(name => !STORE_NAMES.includes(name)); - if (idx !== -1) { - throw new StorageError(`Tried top, a transaction unknown store ${storeNames[idx]}`); - } - } + _validateStoreNames(storeNames) { + const idx = storeNames.findIndex(name => !STORE_NAMES.includes(name)); + if (idx !== -1) { + throw new StorageError(`Tried top, a transaction unknown store ${storeNames[idx]}`); + } + } - async readTxn(storeNames) { - this._validateStoreNames(storeNames); - try { - const txn = this._db.transaction(storeNames, "readonly"); - return new Transaction(txn, storeNames); - } catch(err) { - throw new StorageError("readTxn failed", err); - } - } + async readTxn(storeNames) { + this._validateStoreNames(storeNames); + try { + const txn = this._db.transaction(storeNames, "readonly"); + return new Transaction(txn, storeNames); + } catch(err) { + throw new StorageError("readTxn failed", err); + } + } - async readWriteTxn(storeNames) { - this._validateStoreNames(storeNames); - try { - const txn = this._db.transaction(storeNames, "readwrite"); - return new Transaction(txn, storeNames); - } catch(err) { - throw new StorageError("readWriteTxn failed", err); - } - } + async readWriteTxn(storeNames) { + this._validateStoreNames(storeNames); + try { + const txn = this._db.transaction(storeNames, "readwrite"); + return new Transaction(txn, storeNames); + } catch(err) { + throw new StorageError("readWriteTxn failed", err); + } + } } diff --git a/src/matrix/storage/idb/store.js b/src/matrix/storage/idb/store.js index fd2cc84d..eea33cc1 100644 --- a/src/matrix/storage/idb/store.js +++ b/src/matrix/storage/idb/store.js @@ -57,23 +57,23 @@ class QueryTargetWrapper { } export default class Store extends QueryTarget { - constructor(idbStore) { - super(new QueryTargetWrapper(idbStore)); - } + constructor(idbStore) { + super(new QueryTargetWrapper(idbStore)); + } - get _idbStore() { - return this._target; - } + get _idbStore() { + return this._target; + } - index(indexName) { - return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName))); - } + index(indexName) { + return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName))); + } - put(value) { - return reqAsPromise(this._idbStore.put(value)); - } + put(value) { + return reqAsPromise(this._idbStore.put(value)); + } - add(value) { - return reqAsPromise(this._idbStore.add(value)); - } + add(value) { + return reqAsPromise(this._idbStore.add(value)); + } } diff --git a/src/matrix/storage/idb/stores/TimelineEventStore.js b/src/matrix/storage/idb/stores/TimelineEventStore.js index d88884bf..5285cd6b 100644 --- a/src/matrix/storage/idb/stores/TimelineEventStore.js +++ b/src/matrix/storage/idb/stores/TimelineEventStore.js @@ -87,9 +87,9 @@ class Range { * @property {?Gap} gap if a gap entry, the gap */ export default class TimelineEventStore { - constructor(timelineStore) { - this._timelineStore = timelineStore; - } + constructor(timelineStore) { + this._timelineStore = timelineStore; + } /** Creates a range that only includes the given key * @param {EventKey} eventKey the key @@ -134,11 +134,11 @@ export default class TimelineEventStore { * @param {number} amount * @return {Promise} a promise resolving to an array with 0 or more entries, in ascending order. */ - async lastEvents(roomId, fragmentId, amount) { + async lastEvents(roomId, fragmentId, amount) { const eventKey = EventKey.maxKey; eventKey.fragmentId = fragmentId; - return this.eventsBefore(roomId, eventKey, amount); - } + return this.eventsBefore(roomId, eventKey, amount); + } /** Looks up the first `amount` entries in the timeline for `roomId`. * @param {string} roomId @@ -146,11 +146,11 @@ export default class TimelineEventStore { * @param {number} amount * @return {Promise} a promise resolving to an array with 0 or more entries, in ascending order. */ - async firstEvents(roomId, fragmentId, amount) { + async firstEvents(roomId, fragmentId, amount) { const eventKey = EventKey.minKey; eventKey.fragmentId = fragmentId; - return this.eventsAfter(roomId, eventKey, amount); - } + return this.eventsAfter(roomId, eventKey, amount); + } /** Looks up `amount` entries after `eventKey` in the timeline for `roomId` within the same fragment. * The entry for `eventKey` is not included. @@ -159,10 +159,10 @@ export default class TimelineEventStore { * @param {number} amount * @return {Promise} a promise resolving to an array with 0 or more entries, in ascending order. */ - eventsAfter(roomId, eventKey, amount) { + eventsAfter(roomId, eventKey, amount) { const idbRange = this.lowerBoundRange(eventKey, true).asIDBKeyRange(roomId); - return this._timelineStore.selectLimit(idbRange, amount); - } + return this._timelineStore.selectLimit(idbRange, amount); + } /** Looks up `amount` entries before `eventKey` in the timeline for `roomId` within the same fragment. * The entry for `eventKey` is not included. diff --git a/src/matrix/storage/idb/transaction.js b/src/matrix/storage/idb/transaction.js index a005706d..e66b6d5f 100644 --- a/src/matrix/storage/idb/transaction.js +++ b/src/matrix/storage/idb/transaction.js @@ -8,58 +8,58 @@ import RoomStateStore from "./stores/RoomStateStore.js"; import TimelineFragmentStore from "./stores/TimelineFragmentStore.js"; export default class Transaction { - constructor(txn, allowedStoreNames) { - this._txn = txn; - this._allowedStoreNames = allowedStoreNames; - this._stores = { - session: null, - roomSummary: null, - roomTimeline: null, - roomState: null, - }; - } + constructor(txn, allowedStoreNames) { + this._txn = txn; + this._allowedStoreNames = allowedStoreNames; + this._stores = { + session: null, + roomSummary: null, + roomTimeline: null, + roomState: null, + }; + } - _idbStore(name) { - if (!this._allowedStoreNames.includes(name)) { - // more specific error? this is a bug, so maybe not ... - throw new StorageError(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`); - } - return new Store(this._txn.objectStore(name)); - } + _idbStore(name) { + if (!this._allowedStoreNames.includes(name)) { + // more specific error? this is a bug, so maybe not ... + throw new StorageError(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`); + } + return new Store(this._txn.objectStore(name)); + } - _store(name, mapStore) { - if (!this._stores[name]) { - const idbStore = this._idbStore(name); - this._stores[name] = mapStore(idbStore); - } - return this._stores[name]; - } + _store(name, mapStore) { + if (!this._stores[name]) { + const idbStore = this._idbStore(name); + this._stores[name] = mapStore(idbStore); + } + return this._stores[name]; + } - get session() { - return this._store("session", idbStore => new SessionStore(idbStore)); - } + get session() { + return this._store("session", idbStore => new SessionStore(idbStore)); + } - get roomSummary() { - return this._store("roomSummary", idbStore => new RoomSummaryStore(idbStore)); - } + get roomSummary() { + return this._store("roomSummary", idbStore => new RoomSummaryStore(idbStore)); + } get timelineFragments() { return this._store("timelineFragments", idbStore => new TimelineFragmentStore(idbStore)); } - get timelineEvents() { - return this._store("timelineEvents", idbStore => new TimelineEventStore(idbStore)); - } + get timelineEvents() { + return this._store("timelineEvents", idbStore => new TimelineEventStore(idbStore)); + } - get roomState() { - return this._store("roomState", idbStore => new RoomStateStore(idbStore)); - } + get roomState() { + return this._store("roomState", idbStore => new RoomStateStore(idbStore)); + } - complete() { - return txnAsPromise(this._txn); - } + complete() { + return txnAsPromise(this._txn); + } - abort() { - this._txn.abort(); - } + abort() { + this._txn.abort(); + } } diff --git a/src/ui/web/WebPlatform.js b/src/ui/web/WebPlatform.js index cfc43fe4..1026d142 100644 --- a/src/ui/web/WebPlatform.js +++ b/src/ui/web/WebPlatform.js @@ -1,16 +1,16 @@ export default { - get minStorageKey() { - // for indexeddb, we use unsigned 32 bit integers as keys - return 0; - }, - - get middleStorageKey() { - // for indexeddb, we use unsigned 32 bit integers as keys - return 0x7FFFFFFF; - }, + get minStorageKey() { + // for indexeddb, we use unsigned 32 bit integers as keys + return 0; + }, + + get middleStorageKey() { + // for indexeddb, we use unsigned 32 bit integers as keys + return 0x7FFFFFFF; + }, - get maxStorageKey() { - // for indexeddb, we use unsigned 32 bit integers as keys - return 0xFFFFFFFF; - }, + get maxStorageKey() { + // for indexeddb, we use unsigned 32 bit integers as keys + return 0xFFFFFFFF; + }, }