UNPKG

rxdb

Version:

A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/

283 lines (272 loc) 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.STORAGE_TOKEN_DOCUMENT_KEY = exports.STORAGE_TOKEN_DOCUMENT_ID = exports.INTERNAL_STORE_SCHEMA_TITLE = exports.INTERNAL_STORE_SCHEMA = exports.INTERNAL_CONTEXT_STORAGE_TOKEN = exports.INTERNAL_CONTEXT_PIPELINE_CHECKPOINT = exports.INTERNAL_CONTEXT_MIGRATION_STATUS = exports.INTERNAL_CONTEXT_COLLECTION = void 0; exports._collectionNamePrimary = _collectionNamePrimary; exports.addConnectedStorageToCollection = addConnectedStorageToCollection; exports.ensureStorageTokenDocumentExists = ensureStorageTokenDocumentExists; exports.getAllCollectionDocuments = getAllCollectionDocuments; exports.getPrimaryKeyOfInternalDocument = getPrimaryKeyOfInternalDocument; exports.isDatabaseStateVersionCompatibleWithDatabaseCode = isDatabaseStateVersionCompatibleWithDatabaseCode; exports.removeConnectedStorageFromCollection = removeConnectedStorageFromCollection; var _rxError = require("./rx-error.js"); var _rxSchemaHelper = require("./rx-schema-helper.js"); var _rxStorageHelper = require("./rx-storage-helper.js"); var _index = require("./plugins/utils/index.js"); var _rxQueryHelper = require("./rx-query-helper.js"); var INTERNAL_CONTEXT_COLLECTION = exports.INTERNAL_CONTEXT_COLLECTION = 'collection'; var INTERNAL_CONTEXT_STORAGE_TOKEN = exports.INTERNAL_CONTEXT_STORAGE_TOKEN = 'storage-token'; var INTERNAL_CONTEXT_MIGRATION_STATUS = exports.INTERNAL_CONTEXT_MIGRATION_STATUS = 'rx-migration-status'; var INTERNAL_CONTEXT_PIPELINE_CHECKPOINT = exports.INTERNAL_CONTEXT_PIPELINE_CHECKPOINT = 'rx-pipeline-checkpoint'; /** * Do not change the title, * we have to flag the internal schema so that * some RxStorage implementations are able * to detect if the created RxStorageInstance * is from the internals or not, * to do some optimizations in some cases. */ var INTERNAL_STORE_SCHEMA_TITLE = exports.INTERNAL_STORE_SCHEMA_TITLE = 'RxInternalDocument'; var INTERNAL_STORE_SCHEMA = exports.INTERNAL_STORE_SCHEMA = (0, _rxSchemaHelper.fillWithDefaultSettings)({ version: 0, title: INTERNAL_STORE_SCHEMA_TITLE, primaryKey: { key: 'id', fields: ['context', 'key'], separator: '|' }, type: 'object', properties: { id: { type: 'string', maxLength: 200 }, key: { type: 'string' }, context: { type: 'string', enum: [INTERNAL_CONTEXT_COLLECTION, INTERNAL_CONTEXT_STORAGE_TOKEN, INTERNAL_CONTEXT_MIGRATION_STATUS, INTERNAL_CONTEXT_PIPELINE_CHECKPOINT, 'OTHER'] }, data: { type: 'object', additionalProperties: true } }, indexes: [], required: ['key', 'context', 'data'], additionalProperties: false, /** * If the sharding plugin is used, * it must not shard on the internal RxStorageInstance * because that one anyway has only a small amount of documents * and also its creation is in the hot path of the initial page load, * so we should spend less time creating multiple RxStorageInstances. */ sharding: { shards: 1, mode: 'collection' } }); function getPrimaryKeyOfInternalDocument(key, context) { return (0, _rxSchemaHelper.getComposedPrimaryKeyOfDocumentData)(INTERNAL_STORE_SCHEMA, { key, context }); } /** * Returns all internal documents * with context 'collection' */ async function getAllCollectionDocuments(storageInstance) { var getAllQueryPrepared = (0, _rxQueryHelper.prepareQuery)(storageInstance.schema, { selector: { context: INTERNAL_CONTEXT_COLLECTION, _deleted: { $eq: false } }, sort: [{ id: 'asc' }], skip: 0 }); var queryResult = await storageInstance.query(getAllQueryPrepared); var allDocs = queryResult.documents; return allDocs; } /** * to not confuse multiInstance-messages with other databases that have the same * name and adapter, but do not share state with this one (for example in-memory-instances), * we set a storage-token and use it in the broadcast-channel */ var STORAGE_TOKEN_DOCUMENT_KEY = exports.STORAGE_TOKEN_DOCUMENT_KEY = 'storageToken'; var STORAGE_TOKEN_DOCUMENT_ID = exports.STORAGE_TOKEN_DOCUMENT_ID = getPrimaryKeyOfInternalDocument(STORAGE_TOKEN_DOCUMENT_KEY, INTERNAL_CONTEXT_STORAGE_TOKEN); async function ensureStorageTokenDocumentExists(rxDatabase) { /** * To have less read-write cycles, * we just try to insert a new document * and only fetch the existing one if a conflict happened. */ var storageToken = (0, _index.randomToken)(10); var passwordHash = rxDatabase.password ? await rxDatabase.hashFunction(JSON.stringify(rxDatabase.password)) : undefined; var docData = { id: STORAGE_TOKEN_DOCUMENT_ID, context: INTERNAL_CONTEXT_STORAGE_TOKEN, key: STORAGE_TOKEN_DOCUMENT_KEY, data: { rxdbVersion: rxDatabase.rxdbVersion, token: storageToken, /** * We add the instance token here * to be able to detect if a given RxDatabase instance * is the first instance that was ever created * or if databases have existed earlier on that storage * with the same database name. */ instanceToken: rxDatabase.token, passwordHash }, _deleted: false, _meta: (0, _index.getDefaultRxDocumentMeta)(), _rev: (0, _index.getDefaultRevision)(), _attachments: {} }; var writeRows = [{ document: docData }]; var writeResult = await rxDatabase.internalStore.bulkWrite(writeRows, 'internal-add-storage-token'); if (!writeResult.error[0]) { return (0, _rxStorageHelper.getWrittenDocumentsFromBulkWriteResponse)('id', writeRows, writeResult)[0]; } /** * If we get a 409 error, * it means another instance already inserted the storage token. * So we get that token from the database and return that one. */ var error = (0, _index.ensureNotFalsy)(writeResult.error[0]); if (error.isError && (0, _rxError.isBulkWriteConflictError)(error)) { var conflictError = error; if (!isDatabaseStateVersionCompatibleWithDatabaseCode(conflictError.documentInDb.data.rxdbVersion, rxDatabase.rxdbVersion)) { throw (0, _rxError.newRxError)('DM5', { args: { database: rxDatabase.name, databaseStateVersion: conflictError.documentInDb.data.rxdbVersion, codeVersion: rxDatabase.rxdbVersion } }); } if (passwordHash && passwordHash !== conflictError.documentInDb.data.passwordHash) { throw (0, _rxError.newRxError)('DB1', { passwordHash, existingPasswordHash: conflictError.documentInDb.data.passwordHash }); } var storageTokenDocInDb = conflictError.documentInDb; return (0, _index.ensureNotFalsy)(storageTokenDocInDb); } throw error; } function isDatabaseStateVersionCompatibleWithDatabaseCode(databaseStateVersion, codeVersion) { if (!databaseStateVersion) { return false; } var stateMajor = databaseStateVersion.split('.')[0]; var codeMajor = codeVersion.split('.')[0]; /** * Version v15 data must be upwards compatible to v16 */ if (stateMajor === '15' && codeMajor === '16') { return true; } if (stateMajor !== codeMajor) { return false; } return true; } async function addConnectedStorageToCollection(collection, storageCollectionName, schema) { if (collection.schema.version !== schema.version) { throw (0, _rxError.newRxError)('SNH', { schema, version: collection.schema.version, name: collection.name, collection, args: { storageCollectionName } }); } var collectionNameWithVersion = _collectionNamePrimary(collection.name, collection.schema.jsonSchema); var collectionDocId = getPrimaryKeyOfInternalDocument(collectionNameWithVersion, INTERNAL_CONTEXT_COLLECTION); while (true) { var collectionDoc = await (0, _rxStorageHelper.getSingleDocument)(collection.database.internalStore, collectionDocId); var saveData = (0, _index.clone)((0, _index.ensureNotFalsy)(collectionDoc)); // do nothing if already in array var alreadyThere = saveData.data.connectedStorages.find(row => row.collectionName === storageCollectionName && row.schema.version === schema.version); if (alreadyThere) { return; } // otherwise add to array and save saveData.data.connectedStorages.push({ collectionName: storageCollectionName, schema }); try { await (0, _rxStorageHelper.writeSingle)(collection.database.internalStore, { previous: (0, _index.ensureNotFalsy)(collectionDoc), document: saveData }, 'add-connected-storage-to-collection'); } catch (err) { if (!(0, _rxError.isBulkWriteConflictError)(err)) { throw err; } // retry on conflict } } } async function removeConnectedStorageFromCollection(collection, storageCollectionName, schema) { if (collection.schema.version !== schema.version) { throw (0, _rxError.newRxError)('SNH', { schema, version: collection.schema.version, name: collection.name, collection, args: { storageCollectionName } }); } var collectionNameWithVersion = _collectionNamePrimary(collection.name, collection.schema.jsonSchema); var collectionDocId = getPrimaryKeyOfInternalDocument(collectionNameWithVersion, INTERNAL_CONTEXT_COLLECTION); while (true) { var collectionDoc = await (0, _rxStorageHelper.getSingleDocument)(collection.database.internalStore, collectionDocId); var saveData = (0, _index.clone)((0, _index.ensureNotFalsy)(collectionDoc)); // do nothing if not there var isThere = saveData.data.connectedStorages.find(row => row.collectionName === storageCollectionName && row.schema.version === schema.version); if (!isThere) { return; } // otherwise remove from array and save saveData.data.connectedStorages = saveData.data.connectedStorages.filter(item => item.collectionName !== storageCollectionName); try { await (0, _rxStorageHelper.writeSingle)(collection.database.internalStore, { previous: (0, _index.ensureNotFalsy)(collectionDoc), document: saveData }, 'remove-connected-storage-from-collection'); } catch (err) { if (!(0, _rxError.isBulkWriteConflictError)(err)) { throw err; } // retry on conflict } } } /** * returns the primary for a given collection-data * used in the internal store of a RxDatabase */ function _collectionNamePrimary(name, schema) { return name + '-' + schema.version; } //# sourceMappingURL=rx-database-internal-store.js.map