UNPKG

@firebase/firestore

Version:

The Cloud Firestore component of the Firebase JS SDK.

1,298 lines (1,294 loc) • 1.12 MB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var tslib = require('tslib'); var app = require('@firebase/app'); var component = require('@firebase/component'); var logger = require('@firebase/logger'); var util$1 = require('util'); var util = require('@firebase/util'); var crypto = require('crypto'); var grpcJs = require('@grpc/grpc-js'); var package_json = require('@grpc/grpc-js/package.json'); var path = require('path'); var protoLoader = require('@grpc/proto-loader'); var name = "@firebase/firestore"; var version = "2.3.6"; var version$1 = "8.6.7"; /** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var SDK_VERSION = version$1; function setSDKVersion(version) { SDK_VERSION = version; } /** * @license * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * `ListenSequence` is a monotonic sequence. It is initialized with a minimum value to * exceed. All subsequent calls to next will return increasing values. If provided with a * `SequenceNumberSyncer`, it will additionally bump its next value when told of a new value, as * well as write out sequence numbers that it produces via `next()`. */ var ListenSequence = /** @class */ (function () { function ListenSequence(previousValue, sequenceNumberSyncer) { var _this = this; this.previousValue = previousValue; if (sequenceNumberSyncer) { sequenceNumberSyncer.sequenceNumberHandler = function (sequenceNumber) { return _this.setPreviousValue(sequenceNumber); }; this.writeNewSequenceNumber = function (sequenceNumber) { return sequenceNumberSyncer.writeSequenceNumber(sequenceNumber); }; } } ListenSequence.prototype.setPreviousValue = function (externalPreviousValue) { this.previousValue = Math.max(externalPreviousValue, this.previousValue); return this.previousValue; }; ListenSequence.prototype.next = function () { var nextValue = ++this.previousValue; if (this.writeNewSequenceNumber) { this.writeNewSequenceNumber(nextValue); } return nextValue; }; return ListenSequence; }()); ListenSequence.INVALID = -1; /** * @license * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** Formats an object as a JSON string, suitable for logging. */ function formatJSON(value) { // util.inspect() results in much more readable output than JSON.stringify() return util$1.inspect(value, { depth: 100 }); } /** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var logClient = new logger.Logger('@firebase/firestore'); // Helper methods are needed because variables can't be exported as read/write function getLogLevel() { return logClient.logLevel; } /** * Sets the verbosity of Cloud Firestore logs (debug, error, or silent). * * @param logLevel - The verbosity you set for activity and error logging. Can * be any of the following values: * * <ul> * <li>`debug` for the most verbose logging level, primarily for * debugging.</li> * <li>`error` to log errors only.</li> * <li><code>`silent` to turn off logging.</li> * </ul> */ function setLogLevel(logLevel) { logClient.setLogLevel(logLevel); } function logDebug(msg) { var obj = []; for (var _i = 1; _i < arguments.length; _i++) { obj[_i - 1] = arguments[_i]; } if (logClient.logLevel <= logger.LogLevel.DEBUG) { var args = obj.map(argToString); logClient.debug.apply(logClient, tslib.__spreadArray(["Firestore (" + SDK_VERSION + "): " + msg], args)); } } function logError(msg) { var obj = []; for (var _i = 1; _i < arguments.length; _i++) { obj[_i - 1] = arguments[_i]; } if (logClient.logLevel <= logger.LogLevel.ERROR) { var args = obj.map(argToString); logClient.error.apply(logClient, tslib.__spreadArray(["Firestore (" + SDK_VERSION + "): " + msg], args)); } } function logWarn(msg) { var obj = []; for (var _i = 1; _i < arguments.length; _i++) { obj[_i - 1] = arguments[_i]; } if (logClient.logLevel <= logger.LogLevel.WARN) { var args = obj.map(argToString); logClient.warn.apply(logClient, tslib.__spreadArray(["Firestore (" + SDK_VERSION + "): " + msg], args)); } } /** * Converts an additional log parameter to a string representation. */ function argToString(obj) { if (typeof obj === 'string') { return obj; } else { try { return formatJSON(obj); } catch (e) { // Converting to JSON failed, just log the object directly return obj; } } } /** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Unconditionally fails, throwing an Error with the given message. * Messages are stripped in production builds. * * Returns `never` and can be used in expressions: * @example * let futureVar = fail('not implemented yet'); */ function fail(failure) { if (failure === void 0) { failure = 'Unexpected state'; } // Log the failure in addition to throw an exception, just in case the // exception is swallowed. var message = "FIRESTORE (" + SDK_VERSION + ") INTERNAL ASSERTION FAILED: " + failure; logError(message); // NOTE: We don't use FirestoreError here because these are internal failures // that cannot be handled by the user. (Also it would create a circular // dependency between the error and assert modules which doesn't work.) throw new Error(message); } /** * Fails if the given assertion condition is false, throwing an Error with the * given message if it did. * * Messages are stripped in production builds. */ function hardAssert(assertion, message) { if (!assertion) { fail(); } } /** * Casts `obj` to `T`. In non-production builds, verifies that `obj` is an * instance of `T` before casting. */ function debugCast(obj, // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor) { return obj; } /** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var Code = { // Causes are copied from: // https://github.com/grpc/grpc/blob/bceec94ea4fc5f0085d81235d8e1c06798dc341a/include/grpc%2B%2B/impl/codegen/status_code_enum.h /** Not an error; returned on success. */ OK: 'ok', /** The operation was cancelled (typically by the caller). */ CANCELLED: 'cancelled', /** Unknown error or an error from a different error domain. */ UNKNOWN: 'unknown', /** * Client specified an invalid argument. Note that this differs from * FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are * problematic regardless of the state of the system (e.g., a malformed file * name). */ INVALID_ARGUMENT: 'invalid-argument', /** * Deadline expired before operation could complete. For operations that * change the state of the system, this error may be returned even if the * operation has completed successfully. For example, a successful response * from a server could have been delayed long enough for the deadline to * expire. */ DEADLINE_EXCEEDED: 'deadline-exceeded', /** Some requested entity (e.g., file or directory) was not found. */ NOT_FOUND: 'not-found', /** * Some entity that we attempted to create (e.g., file or directory) already * exists. */ ALREADY_EXISTS: 'already-exists', /** * The caller does not have permission to execute the specified operation. * PERMISSION_DENIED must not be used for rejections caused by exhausting * some resource (use RESOURCE_EXHAUSTED instead for those errors). * PERMISSION_DENIED must not be used if the caller can not be identified * (use UNAUTHENTICATED instead for those errors). */ PERMISSION_DENIED: 'permission-denied', /** * The request does not have valid authentication credentials for the * operation. */ UNAUTHENTICATED: 'unauthenticated', /** * Some resource has been exhausted, perhaps a per-user quota, or perhaps the * entire file system is out of space. */ RESOURCE_EXHAUSTED: 'resource-exhausted', /** * Operation was rejected because the system is not in a state required for * the operation's execution. For example, directory to be deleted may be * non-empty, an rmdir operation is applied to a non-directory, etc. * * A litmus test that may help a service implementor in deciding * between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: * (a) Use UNAVAILABLE if the client can retry just the failing call. * (b) Use ABORTED if the client should retry at a higher-level * (e.g., restarting a read-modify-write sequence). * (c) Use FAILED_PRECONDITION if the client should not retry until * the system state has been explicitly fixed. E.g., if an "rmdir" * fails because the directory is non-empty, FAILED_PRECONDITION * should be returned since the client should not retry unless * they have first fixed up the directory by deleting files from it. * (d) Use FAILED_PRECONDITION if the client performs conditional * REST Get/Update/Delete on a resource and the resource on the * server does not match the condition. E.g., conflicting * read-modify-write on the same resource. */ FAILED_PRECONDITION: 'failed-precondition', /** * The operation was aborted, typically due to a concurrency issue like * sequencer check failures, transaction aborts, etc. * * See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, * and UNAVAILABLE. */ ABORTED: 'aborted', /** * Operation was attempted past the valid range. E.g., seeking or reading * past end of file. * * Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed * if the system state changes. For example, a 32-bit file system will * generate INVALID_ARGUMENT if asked to read at an offset that is not in the * range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from * an offset past the current file size. * * There is a fair bit of overlap between FAILED_PRECONDITION and * OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error) * when it applies so that callers who are iterating through a space can * easily look for an OUT_OF_RANGE error to detect when they are done. */ OUT_OF_RANGE: 'out-of-range', /** Operation is not implemented or not supported/enabled in this service. */ UNIMPLEMENTED: 'unimplemented', /** * Internal errors. Means some invariants expected by underlying System has * been broken. If you see one of these errors, Something is very broken. */ INTERNAL: 'internal', /** * The service is currently unavailable. This is a most likely a transient * condition and may be corrected by retrying with a backoff. * * See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, * and UNAVAILABLE. */ UNAVAILABLE: 'unavailable', /** Unrecoverable data loss or corruption. */ DATA_LOSS: 'data-loss' }; /** An error returned by a Firestore operation. */ var FirestoreError = /** @class */ (function (_super) { tslib.__extends(FirestoreError, _super); /** @hideconstructor */ function FirestoreError( /** * The backend error code associated with this error. */ code, /** * A custom error description. */ message) { var _this = _super.call(this, message) || this; _this.code = code; _this.message = message; /** The custom name for all FirestoreErrors. */ _this.name = 'FirebaseError'; // HACK: We write a toString property directly because Error is not a real // class and so inheritance does not work correctly. We could alternatively // do the same "back-door inheritance" trick that FirebaseError does. _this.toString = function () { return _this.name + ": [code=" + _this.code + "]: " + _this.message; }; return _this; } return FirestoreError; }(Error)); /** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var DOCUMENT_KEY_NAME = '__name__'; /** * Path represents an ordered sequence of string segments. */ var BasePath = /** @class */ (function () { function BasePath(segments, offset, length) { if (offset === undefined) { offset = 0; } else if (offset > segments.length) { fail(); } if (length === undefined) { length = segments.length - offset; } else if (length > segments.length - offset) { fail(); } this.segments = segments; this.offset = offset; this.len = length; } Object.defineProperty(BasePath.prototype, "length", { get: function () { return this.len; }, enumerable: false, configurable: true }); BasePath.prototype.isEqual = function (other) { return BasePath.comparator(this, other) === 0; }; BasePath.prototype.child = function (nameOrPath) { var segments = this.segments.slice(this.offset, this.limit()); if (nameOrPath instanceof BasePath) { nameOrPath.forEach(function (segment) { segments.push(segment); }); } else { segments.push(nameOrPath); } return this.construct(segments); }; /** The index of one past the last segment of the path. */ BasePath.prototype.limit = function () { return this.offset + this.length; }; BasePath.prototype.popFirst = function (size) { size = size === undefined ? 1 : size; return this.construct(this.segments, this.offset + size, this.length - size); }; BasePath.prototype.popLast = function () { return this.construct(this.segments, this.offset, this.length - 1); }; BasePath.prototype.firstSegment = function () { return this.segments[this.offset]; }; BasePath.prototype.lastSegment = function () { return this.get(this.length - 1); }; BasePath.prototype.get = function (index) { return this.segments[this.offset + index]; }; BasePath.prototype.isEmpty = function () { return this.length === 0; }; BasePath.prototype.isPrefixOf = function (other) { if (other.length < this.length) { return false; } for (var i = 0; i < this.length; i++) { if (this.get(i) !== other.get(i)) { return false; } } return true; }; BasePath.prototype.isImmediateParentOf = function (potentialChild) { if (this.length + 1 !== potentialChild.length) { return false; } for (var i = 0; i < this.length; i++) { if (this.get(i) !== potentialChild.get(i)) { return false; } } return true; }; BasePath.prototype.forEach = function (fn) { for (var i = this.offset, end = this.limit(); i < end; i++) { fn(this.segments[i]); } }; BasePath.prototype.toArray = function () { return this.segments.slice(this.offset, this.limit()); }; BasePath.comparator = function (p1, p2) { var len = Math.min(p1.length, p2.length); for (var i = 0; i < len; i++) { var left = p1.get(i); var right = p2.get(i); if (left < right) { return -1; } if (left > right) { return 1; } } if (p1.length < p2.length) { return -1; } if (p1.length > p2.length) { return 1; } return 0; }; return BasePath; }()); /** * A slash-separated path for navigating resources (documents and collections) * within Firestore. */ var ResourcePath = /** @class */ (function (_super) { tslib.__extends(ResourcePath, _super); function ResourcePath() { return _super !== null && _super.apply(this, arguments) || this; } ResourcePath.prototype.construct = function (segments, offset, length) { return new ResourcePath(segments, offset, length); }; ResourcePath.prototype.canonicalString = function () { // NOTE: The client is ignorant of any path segments containing escape // sequences (e.g. __id123__) and just passes them through raw (they exist // for legacy reasons and should not be used frequently). return this.toArray().join('/'); }; ResourcePath.prototype.toString = function () { return this.canonicalString(); }; /** * Creates a resource path from the given slash-delimited string. If multiple * arguments are provided, all components are combined. Leading and trailing * slashes from all components are ignored. */ ResourcePath.fromString = function () { var pathComponents = []; for (var _i = 0; _i < arguments.length; _i++) { pathComponents[_i] = arguments[_i]; } // NOTE: The client is ignorant of any path segments containing escape // sequences (e.g. __id123__) and just passes them through raw (they exist // for legacy reasons and should not be used frequently). var segments = []; for (var _d = 0, pathComponents_1 = pathComponents; _d < pathComponents_1.length; _d++) { var path = pathComponents_1[_d]; if (path.indexOf('//') >= 0) { throw new FirestoreError(Code.INVALID_ARGUMENT, "Invalid segment (" + path + "). Paths must not contain // in them."); } // Strip leading and traling slashed. segments.push.apply(segments, path.split('/').filter(function (segment) { return segment.length > 0; })); } return new ResourcePath(segments); }; ResourcePath.emptyPath = function () { return new ResourcePath([]); }; return ResourcePath; }(BasePath)); var identifierRegExp = /^[_a-zA-Z][_a-zA-Z0-9]*$/; /** A dot-separated path for navigating sub-objects within a document. */ var FieldPath = /** @class */ (function (_super) { tslib.__extends(FieldPath, _super); function FieldPath() { return _super !== null && _super.apply(this, arguments) || this; } FieldPath.prototype.construct = function (segments, offset, length) { return new FieldPath(segments, offset, length); }; /** * Returns true if the string could be used as a segment in a field path * without escaping. */ FieldPath.isValidIdentifier = function (segment) { return identifierRegExp.test(segment); }; FieldPath.prototype.canonicalString = function () { return this.toArray() .map(function (str) { str = str.replace(/\\/g, '\\\\').replace(/`/g, '\\`'); if (!FieldPath.isValidIdentifier(str)) { str = '`' + str + '`'; } return str; }) .join('.'); }; FieldPath.prototype.toString = function () { return this.canonicalString(); }; /** * Returns true if this field references the key of a document. */ FieldPath.prototype.isKeyField = function () { return this.length === 1 && this.get(0) === DOCUMENT_KEY_NAME; }; /** * The field designating the key of a document. */ FieldPath.keyField = function () { return new FieldPath([DOCUMENT_KEY_NAME]); }; /** * Parses a field string from the given server-formatted string. * * - Splitting the empty string is not allowed (for now at least). * - Empty segments within the string (e.g. if there are two consecutive * separators) are not allowed. * * TODO(b/37244157): we should make this more strict. Right now, it allows * non-identifier path components, even if they aren't escaped. */ FieldPath.fromServerFormat = function (path) { var segments = []; var current = ''; var i = 0; var addCurrentSegment = function () { if (current.length === 0) { throw new FirestoreError(Code.INVALID_ARGUMENT, "Invalid field path (" + path + "). Paths must not be empty, begin " + "with '.', end with '.', or contain '..'"); } segments.push(current); current = ''; }; var inBackticks = false; while (i < path.length) { var c = path[i]; if (c === '\\') { if (i + 1 === path.length) { throw new FirestoreError(Code.INVALID_ARGUMENT, 'Path has trailing escape character: ' + path); } var next = path[i + 1]; if (!(next === '\\' || next === '.' || next === '`')) { throw new FirestoreError(Code.INVALID_ARGUMENT, 'Path has invalid escape sequence: ' + path); } current += next; i += 2; } else if (c === '`') { inBackticks = !inBackticks; i++; } else if (c === '.' && !inBackticks) { addCurrentSegment(); i++; } else { current += c; i++; } } addCurrentSegment(); if (inBackticks) { throw new FirestoreError(Code.INVALID_ARGUMENT, 'Unterminated ` in path: ' + path); } return new FieldPath(segments); }; FieldPath.emptyPath = function () { return new FieldPath([]); }; return FieldPath; }(BasePath)); /** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var escapeChar = '\u0001'; var encodedSeparatorChar = '\u0001'; var encodedNul = '\u0010'; var encodedEscape = '\u0011'; /** * Encodes a resource path into a IndexedDb-compatible string form. */ function encodeResourcePath(path) { var result = ''; for (var i = 0; i < path.length; i++) { if (result.length > 0) { result = encodeSeparator(result); } result = encodeSegment(path.get(i), result); } return encodeSeparator(result); } /** Encodes a single segment of a resource path into the given result */ function encodeSegment(segment, resultBuf) { var result = resultBuf; var length = segment.length; for (var i = 0; i < length; i++) { var c = segment.charAt(i); switch (c) { case '\0': result += escapeChar + encodedNul; break; case escapeChar: result += escapeChar + encodedEscape; break; default: result += c; } } return result; } /** Encodes a path separator into the given result */ function encodeSeparator(result) { return result + escapeChar + encodedSeparatorChar; } /** * Decodes the given IndexedDb-compatible string form of a resource path into * a ResourcePath instance. Note that this method is not suitable for use with * decoding resource names from the server; those are One Platform format * strings. */ function decodeResourcePath(path) { // Event the empty path must encode as a path of at least length 2. A path // with exactly 2 must be the empty path. var length = path.length; hardAssert(length >= 2); if (length === 2) { hardAssert(path.charAt(0) === escapeChar && path.charAt(1) === encodedSeparatorChar); return ResourcePath.emptyPath(); } // Escape characters cannot exist past the second-to-last position in the // source value. var lastReasonableEscapeIndex = length - 2; var segments = []; var segmentBuilder = ''; for (var start = 0; start < length;) { // The last two characters of a valid encoded path must be a separator, so // there must be an end to this segment. var end = path.indexOf(escapeChar, start); if (end < 0 || end > lastReasonableEscapeIndex) { fail(); } var next = path.charAt(end + 1); switch (next) { case encodedSeparatorChar: var currentPiece = path.substring(start, end); var segment = void 0; if (segmentBuilder.length === 0) { // Avoid copying for the common case of a segment that excludes \0 // and \001 segment = currentPiece; } else { segmentBuilder += currentPiece; segment = segmentBuilder; segmentBuilder = ''; } segments.push(segment); break; case encodedNul: segmentBuilder += path.substring(start, end); segmentBuilder += '\0'; break; case encodedEscape: // The escape character can be used in the output to encode itself. segmentBuilder += path.substring(start, end + 1); break; default: fail(); } start = end + 2; } return new ResourcePath(segments); } /** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Schema Version for the Web client: * 1. Initial version including Mutation Queue, Query Cache, and Remote * Document Cache * 2. Used to ensure a targetGlobal object exists and add targetCount to it. No * longer required because migration 3 unconditionally clears it. * 3. Dropped and re-created Query Cache to deal with cache corruption related * to limbo resolution. Addresses * https://github.com/firebase/firebase-ios-sdk/issues/1548 * 4. Multi-Tab Support. * 5. Removal of held write acks. * 6. Create document global for tracking document cache size. * 7. Ensure every cached document has a sentinel row with a sequence number. * 8. Add collection-parent index for Collection Group queries. * 9. Change RemoteDocumentChanges store to be keyed by readTime rather than * an auto-incrementing ID. This is required for Index-Free queries. * 10. Rewrite the canonical IDs to the explicit Protobuf-based format. * 11. Add bundles and named_queries for bundle support. */ var SCHEMA_VERSION = 11; /** * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects. */ var DbTimestamp = /** @class */ (function () { function DbTimestamp(seconds, nanoseconds) { this.seconds = seconds; this.nanoseconds = nanoseconds; } return DbTimestamp; }()); /** * A singleton object to be stored in the 'owner' store in IndexedDb. * * A given database can have a single primary tab assigned at a given time. That * tab must validate that it is still holding the primary lease before every * operation that requires locked access. The primary tab should regularly * write an updated timestamp to this lease to prevent other tabs from * "stealing" the primary lease */ var DbPrimaryClient = /** @class */ (function () { function DbPrimaryClient(ownerId, /** Whether to allow shared access from multiple tabs. */ allowTabSynchronization, leaseTimestampMs) { this.ownerId = ownerId; this.allowTabSynchronization = allowTabSynchronization; this.leaseTimestampMs = leaseTimestampMs; } return DbPrimaryClient; }()); /** * Name of the IndexedDb object store. * * Note that the name 'owner' is chosen to ensure backwards compatibility with * older clients that only supported single locked access to the persistence * layer. */ DbPrimaryClient.store = 'owner'; /** * The key string used for the single object that exists in the * DbPrimaryClient store. */ DbPrimaryClient.key = 'owner'; /** * An object to be stored in the 'mutationQueues' store in IndexedDb. * * Each user gets a single queue of MutationBatches to apply to the server. * DbMutationQueue tracks the metadata about the queue. */ var DbMutationQueue = /** @class */ (function () { function DbMutationQueue( /** * The normalized user ID to which this queue belongs. */ userId, /** * An identifier for the highest numbered batch that has been acknowledged * by the server. All MutationBatches in this queue with batchIds less * than or equal to this value are considered to have been acknowledged by * the server. * * NOTE: this is deprecated and no longer used by the code. */ lastAcknowledgedBatchId, /** * A stream token that was previously sent by the server. * * See StreamingWriteRequest in datastore.proto for more details about * usage. * * After sending this token, earlier tokens may not be used anymore so * only a single stream token is retained. * * NOTE: this is deprecated and no longer used by the code. */ lastStreamToken) { this.userId = userId; this.lastAcknowledgedBatchId = lastAcknowledgedBatchId; this.lastStreamToken = lastStreamToken; } return DbMutationQueue; }()); /** Name of the IndexedDb object store. */ DbMutationQueue.store = 'mutationQueues'; /** Keys are automatically assigned via the userId property. */ DbMutationQueue.keyPath = 'userId'; /** * An object to be stored in the 'mutations' store in IndexedDb. * * Represents a batch of user-level mutations intended to be sent to the server * in a single write. Each user-level batch gets a separate DbMutationBatch * with a new batchId. */ var DbMutationBatch = /** @class */ (function () { function DbMutationBatch( /** * The normalized user ID to which this batch belongs. */ userId, /** * An identifier for this batch, allocated using an auto-generated key. */ batchId, /** * The local write time of the batch, stored as milliseconds since the * epoch. */ localWriteTimeMs, /** * A list of "mutations" that represent a partial base state from when this * write batch was initially created. During local application of the write * batch, these baseMutations are applied prior to the real writes in order * to override certain document fields from the remote document cache. This * is necessary in the case of non-idempotent writes (e.g. `increment()` * transforms) to make sure that the local view of the modified documents * doesn't flicker if the remote document cache receives the result of the * non-idempotent write before the write is removed from the queue. * * These mutations are never sent to the backend. */ baseMutations, /** * A list of mutations to apply. All mutations will be applied atomically. * * Mutations are serialized via toMutation(). */ mutations) { this.userId = userId; this.batchId = batchId; this.localWriteTimeMs = localWriteTimeMs; this.baseMutations = baseMutations; this.mutations = mutations; } return DbMutationBatch; }()); /** Name of the IndexedDb object store. */ DbMutationBatch.store = 'mutations'; /** Keys are automatically assigned via the userId, batchId properties. */ DbMutationBatch.keyPath = 'batchId'; /** The index name for lookup of mutations by user. */ DbMutationBatch.userMutationsIndex = 'userMutationsIndex'; /** The user mutations index is keyed by [userId, batchId] pairs. */ DbMutationBatch.userMutationsKeyPath = ['userId', 'batchId']; /** * An object to be stored in the 'documentMutations' store in IndexedDb. * * A manually maintained index of all the mutation batches that affect a given * document key. The rows in this table are references based on the contents of * DbMutationBatch.mutations. */ var DbDocumentMutation = /** @class */ (function () { function DbDocumentMutation() { } /** * Creates a [userId] key for use in the DbDocumentMutations index to iterate * over all of a user's document mutations. */ DbDocumentMutation.prefixForUser = function (userId) { return [userId]; }; /** * Creates a [userId, encodedPath] key for use in the DbDocumentMutations * index to iterate over all at document mutations for a given path or lower. */ DbDocumentMutation.prefixForPath = function (userId, path) { return [userId, encodeResourcePath(path)]; }; /** * Creates a full index key of [userId, encodedPath, batchId] for inserting * and deleting into the DbDocumentMutations index. */ DbDocumentMutation.key = function (userId, path, batchId) { return [userId, encodeResourcePath(path), batchId]; }; return DbDocumentMutation; }()); DbDocumentMutation.store = 'documentMutations'; /** * Because we store all the useful information for this store in the key, * there is no useful information to store as the value. The raw (unencoded) * path cannot be stored because IndexedDb doesn't store prototype * information. */ DbDocumentMutation.PLACEHOLDER = new DbDocumentMutation(); /** * Represents the known absence of a document at a particular version. * Stored in IndexedDb as part of a DbRemoteDocument object. */ var DbNoDocument = /** @class */ (function () { function DbNoDocument(path, readTime) { this.path = path; this.readTime = readTime; } return DbNoDocument; }()); /** * Represents a document that is known to exist but whose data is unknown. * Stored in IndexedDb as part of a DbRemoteDocument object. */ var DbUnknownDocument = /** @class */ (function () { function DbUnknownDocument(path, version) { this.path = path; this.version = version; } return DbUnknownDocument; }()); /** * An object to be stored in the 'remoteDocuments' store in IndexedDb. * It represents either: * * - A complete document. * - A "no document" representing a document that is known not to exist (at * some version). * - An "unknown document" representing a document that is known to exist (at * some version) but whose contents are unknown. * * Note: This is the persisted equivalent of a MaybeDocument and could perhaps * be made more general if necessary. */ var DbRemoteDocument = /** @class */ (function () { // TODO: We are currently storing full document keys almost three times // (once as part of the primary key, once - partly - as `parentPath` and once // inside the encoded documents). During our next migration, we should // rewrite the primary key as parentPath + document ID which would allow us // to drop one value. function DbRemoteDocument( /** * Set to an instance of DbUnknownDocument if the data for a document is * not known, but it is known that a document exists at the specified * version (e.g. it had a successful update applied to it) */ unknownDocument, /** * Set to an instance of a DbNoDocument if it is known that no document * exists. */ noDocument, /** * Set to an instance of a Document if there's a cached version of the * document. */ document, /** * Documents that were written to the remote document store based on * a write acknowledgment are marked with `hasCommittedMutations`. These * documents are potentially inconsistent with the backend's copy and use * the write's commit version as their document version. */ hasCommittedMutations, /** * When the document was read from the backend. Undefined for data written * prior to schema version 9. */ readTime, /** * The path of the collection this document is part of. Undefined for data * written prior to schema version 9. */ parentPath) { this.unknownDocument = unknownDocument; this.noDocument = noDocument; this.document = document; this.hasCommittedMutations = hasCommittedMutations; this.readTime = readTime; this.parentPath = parentPath; } return DbRemoteDocument; }()); DbRemoteDocument.store = 'remoteDocuments'; /** * An index that provides access to all entries sorted by read time (which * corresponds to the last modification time of each row). * * This index is used to provide a changelog for Multi-Tab. */ DbRemoteDocument.readTimeIndex = 'readTimeIndex'; DbRemoteDocument.readTimeIndexPath = 'readTime'; /** * An index that provides access to documents in a collection sorted by read * time. * * This index is used to allow the RemoteDocumentCache to fetch newly changed * documents in a collection. */ DbRemoteDocument.collectionReadTimeIndex = 'collectionReadTimeIndex'; DbRemoteDocument.collectionReadTimeIndexPath = ['parentPath', 'readTime']; /** * Contains a single entry that has metadata about the remote document cache. */ var DbRemoteDocumentGlobal = /** @class */ (function () { /** * @param byteSize - Approximately the total size in bytes of all the * documents in the document cache. */ function DbRemoteDocumentGlobal(byteSize) { this.byteSize = byteSize; } return DbRemoteDocumentGlobal; }()); DbRemoteDocumentGlobal.store = 'remoteDocumentGlobal'; DbRemoteDocumentGlobal.key = 'remoteDocumentGlobalKey'; /** * An object to be stored in the 'targets' store in IndexedDb. * * This is based on and should be kept in sync with the proto used in the iOS * client. * * Each query the client listens to against the server is tracked on disk so * that the query can be efficiently resumed on restart. */ var DbTarget = /** @class */ (function () { function DbTarget( /** * An auto-generated sequential numeric identifier for the query. * * Queries are stored using their canonicalId as the key, but these * canonicalIds can be quite long so we additionally assign a unique * queryId which can be used by referenced data structures (e.g. * indexes) to minimize the on-disk cost. */ targetId, /** * The canonical string representing this query. This is not unique. */ canonicalId, /** * The last readTime received from the Watch Service for this query. * * This is the same value as TargetChange.read_time in the protos. */ readTime, /** * An opaque, server-assigned token that allows watching a query to be * resumed after disconnecting without retransmitting all the data * that matches the query. The resume token essentially identifies a * point in time from which the server should resume sending results. * * This is related to the snapshotVersion in that the resumeToken * effectively also encodes that value, but the resumeToken is opaque * and sometimes encodes additional information. * * A consequence of this is that the resumeToken should be used when * asking the server to reason about where this client is in the watch * stream, but the client should use the snapshotVersion for its own * purposes. * * This is the same value as TargetChange.resume_token in the protos. */ resumeToken, /** * A sequence number representing the last time this query was * listened to, used for garbage collection purposes. * * Conventionally this would be a timestamp value, but device-local * clocks are unreliable and they must be able to create new listens * even while disconnected. Instead this should be a monotonically * increasing number that's incremented on each listen call. * * This is different from the queryId since the queryId is an * immutable identifier assigned to the Query on first use while * lastListenSequenceNumber is updated every time the query is * listened to. */ lastListenSequenceNumber, /** * Denotes the maximum snapshot version at which the associated query view * contained no limbo documents. Undefined for data written prior to * schema version 9. */ lastLimboFreeSnapshotVersion, /** * The query for this target. * * Because canonical ids are not unique we must store the actual query. We * use the proto to have an object we can persist without having to * duplicate translation logic to and from a `Query` object. */ query) { this.targetId = targetId; this.canonicalId = canonicalId; this.readTime = readTime; this.resumeToken = resumeToken; this.lastListenSequenceNumber = lastListenSequenceNumber; this.lastLimboFreeSnapshotVersion = lastLimboFreeSnapshotVersion; this.query = query; } return DbTarget; }()); DbTarget.store = 'targets'; /** Keys are automatically assigned via the targetId property. */ DbTarget.keyPath = 'targetId'; /** The name of the queryTargets index. */ DbTarget.queryTargetsIndexName = 'queryTargetsIndex'; /** * The index of all canonicalIds to the targets that they match. This is not * a unique mapping because canonicalId does not promise a unique name for all * possible queries, so we append the targetId to make the mapping unique. */ DbTarget.queryTargetsKeyPath = ['canonicalId', 'targetId']; /** * An object representing an association between a target and a document, or a * sentinel row marking the last sequence number at which a document was used. * Each document cached must have a corresponding sentinel row before lru * garbage collection is enabled. * * The target associations and sentinel rows are co-located so that orphaned * documents and their sequence numbers can be identified efficiently via a scan * of this store. */ var DbTargetDocument = /** @class */ (function () { function DbTargetDocument( /** * The targetId identifying a target or 0 for a sentinel row. */ targetId, /** * The path to the document, as encoded in the key. */ path, /** * If this is a sentinel row, this should be the sequence number of the last * time the document specified by `path` was used. Otherwise, it should be * `undefined`. */ sequenceNumber) { this.targetId = targetId; this.path = path; this.sequenceNumber = sequenceNumber; } return DbTargetDocument; }()); /** Name of the IndexedDb object store. */ DbTargetDocument.store = 'targetDocuments'; /** Keys are automatically assigned via the targetId, path properties. */ DbTargetDocument.keyPath = ['targetId', 'path']; /** The index name for the reverse index. */ DbTargetDocument.documentTargetsIndex = 'documentTargetsIndex'; /** We also need to create the reverse index for these properties. */ DbTargetDocument.documentTargetsKeyPath = ['path', 'targetId']; /** * A record of global state tracked across all Targets, tracked separately * to avoid the need for extra indexes. * * This should be kept in-sync with the proto used in the iOS client. */ var DbTargetGlobal = /** @class */ (function () { function DbTargetGlobal( /** * The highest numbered target id across all targets. * * See DbTarget.targetId. */ highestTargetId, /** * The highest numbered lastListenSequenceNumber across all targets. * * See DbTarget.lastListenSequenceNumber. */ highestListenSequenceNumber, /** * A global snapshot version representing the last consistent snapshot we