rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
283 lines (272 loc) • 10.6 kB
JavaScript
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
;