@squidcloud/client 1.0.195 → 1.0.196
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +799 -759
- package/dist/internal-common/src/public-types/ai-chatbot.public-types.d.ts +1 -1
- package/dist/internal-common/src/public-types/document.public-types.d.ts +2 -0
- package/dist/internal-common/src/public-types/integration.public-types.d.ts +1 -0
- package/dist/internal-common/src/public-utils/id-utils.d.ts +5 -2
- package/dist/internal-common/src/utils/object.d.ts +1 -1
- package/dist/typescript-client/src/ai-chatbot-client.d.ts +2 -2
- package/dist/typescript-client/src/collection-reference.d.ts +22 -2
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -28258,7 +28258,10 @@ function map(project, thisArg) {
|
|
|
28258
28258
|
});
|
|
28259
28259
|
}
|
|
28260
28260
|
//# sourceMappingURL=map.js.map
|
|
28261
|
+
// EXTERNAL MODULE: ../node_modules/assertic/dist/index.js
|
|
28262
|
+
var dist = __webpack_require__(8975);
|
|
28261
28263
|
;// CONCATENATED MODULE: ../internal-common/src/public-utils/id-utils.ts
|
|
28264
|
+
|
|
28262
28265
|
/** Generates a UUID-style ID. */
|
|
28263
28266
|
function generateId() {
|
|
28264
28267
|
let dt = new Date().getTime();
|
|
@@ -28270,12 +28273,19 @@ function generateId() {
|
|
|
28270
28273
|
}
|
|
28271
28274
|
/** Default length if ID produced by `generateShortId`. */
|
|
28272
28275
|
const DEFAULT_SHORT_ID_LENGTH = 18;
|
|
28273
|
-
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
+
const SHORT_ID_CHARACTERS = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
28277
|
+
/**
|
|
28278
|
+
* Generates an ID of the given `length` using lowercase latin letters and digits.
|
|
28279
|
+
* If the 'prefix' provided replaces the first generated characters with the prefix.
|
|
28280
|
+
*/
|
|
28281
|
+
function generateShortId(length = DEFAULT_SHORT_ID_LENGTH, prefix = '') {
|
|
28282
|
+
(0,dist.assertTruthy)(prefix.length < length, 'ID prefix is too long');
|
|
28276
28283
|
let id = '';
|
|
28277
28284
|
for (let i = 0; i < length; i++) {
|
|
28278
|
-
id +=
|
|
28285
|
+
id += SHORT_ID_CHARACTERS.charAt(Math.floor(Math.random() * SHORT_ID_CHARACTERS.length));
|
|
28286
|
+
}
|
|
28287
|
+
if (prefix.length > 0) {
|
|
28288
|
+
id = prefix + id.substring(prefix.length);
|
|
28279
28289
|
}
|
|
28280
28290
|
return id;
|
|
28281
28291
|
}
|
|
@@ -28433,7 +28443,7 @@ class AiChatbotProfileReference {
|
|
|
28433
28443
|
* Adds a new profile to the chatbot. This will result in an error if a profile already exists with the same id.
|
|
28434
28444
|
*
|
|
28435
28445
|
* @param data An object containing options for creating the profile.
|
|
28436
|
-
* @param data.modelName - The name of the OpenAI model (`gpt-3.5, `gpt-4` or `claude-
|
|
28446
|
+
* @param data.modelName - The name of the OpenAI model (`gpt-3.5, `gpt-4`, `claude-3-opus-20240229`, `claude-3-sonnet-20240229` or `claude-3-haiku-20240307`).
|
|
28437
28447
|
* @param data.isPublic - Whether the chat functionality of the profile can be accessed without security rules.
|
|
28438
28448
|
* @returns A promise that resolves when the profile is successfully created.
|
|
28439
28449
|
*/
|
|
@@ -28458,7 +28468,7 @@ class AiChatbotProfileReference {
|
|
|
28458
28468
|
* current profile id.
|
|
28459
28469
|
*
|
|
28460
28470
|
* @param data An object containing options for updating the profile.
|
|
28461
|
-
* @param data.modelName - The name of the OpenAI model (`gpt-3.5, `gpt-4` or `claude-
|
|
28471
|
+
* @param data.modelName - The name of the OpenAI model (`gpt-3.5, `gpt-4`, `claude-3-opus-20240229`, `claude-3-sonnet-20240229` or `claude-3-haiku-20240307`).
|
|
28462
28472
|
* @param data.isPublic - Whether the chat functionality of the profile can be accessed without security rules.
|
|
28463
28473
|
* @returns A promise that resolves when the profile is successfully updated.
|
|
28464
28474
|
*/
|
|
@@ -28777,8 +28787,6 @@ class ApiClient {
|
|
|
28777
28787
|
|
|
28778
28788
|
// EXTERNAL MODULE: ../node_modules/lodash/lodash.js
|
|
28779
28789
|
var lodash = __webpack_require__(8784);
|
|
28780
|
-
// EXTERNAL MODULE: ../node_modules/assertic/dist/index.js
|
|
28781
|
-
var dist = __webpack_require__(8975);
|
|
28782
28790
|
// EXTERNAL MODULE: ../node_modules/rfdc/index.js
|
|
28783
28791
|
var rfdc = __webpack_require__(7795);
|
|
28784
28792
|
var rfdc_default = /*#__PURE__*/__webpack_require__.n(rfdc);
|
|
@@ -29355,7 +29363,9 @@ const AI_MODEL_NAMES = [
|
|
|
29355
29363
|
'gpt-3.5-turbo',
|
|
29356
29364
|
'gpt-3.5-turbo-1106',
|
|
29357
29365
|
'gpt-4',
|
|
29358
|
-
'claude-
|
|
29366
|
+
'claude-3-opus-20240229',
|
|
29367
|
+
'claude-3-sonnet-20240229',
|
|
29368
|
+
'claude-3-haiku-20240307',
|
|
29359
29369
|
'gpt-4-turbo-preview',
|
|
29360
29370
|
];
|
|
29361
29371
|
|
|
@@ -29612,6 +29622,7 @@ var IntegrationType;
|
|
|
29612
29622
|
IntegrationType["cognito"] = "cognito";
|
|
29613
29623
|
IntegrationType["okta"] = "okta";
|
|
29614
29624
|
IntegrationType["descope"] = "descope";
|
|
29625
|
+
IntegrationType["firebase_auth"] = "firebase_auth";
|
|
29615
29626
|
IntegrationType["kafka"] = "kafka";
|
|
29616
29627
|
IntegrationType["confluent"] = "confluent";
|
|
29617
29628
|
IntegrationType["built_in_queue"] = "built_in_queue";
|
|
@@ -30174,235 +30185,6 @@ var ClientConnectionState;
|
|
|
30174
30185
|
|
|
30175
30186
|
|
|
30176
30187
|
|
|
30177
|
-
|
|
30178
|
-
;// CONCATENATED MODULE: ../internal-common/src/utils/assert.ts
|
|
30179
|
-
|
|
30180
|
-
|
|
30181
|
-
|
|
30182
|
-
/** @internal */
|
|
30183
|
-
function assertValidateTruthy(value, message, statusCode = HttpStatus.BAD_REQUEST, details) {
|
|
30184
|
-
assertTruthy(value, () => new ValidationError(message, statusCode, details));
|
|
30185
|
-
}
|
|
30186
|
-
/** @internal */
|
|
30187
|
-
function assert_validateTruthy(value, message, statusCode = HttpStatus.BAD_REQUEST, details) {
|
|
30188
|
-
return truthy(value, () => new ValidationError(message, statusCode, details));
|
|
30189
|
-
}
|
|
30190
|
-
|
|
30191
|
-
;// CONCATENATED MODULE: ../internal-common/src/utils/validation.ts
|
|
30192
|
-
/**
|
|
30193
|
-
* This file contains general validators for the different objects being received from the client. The parameters are
|
|
30194
|
-
* usually of type 'any' to make sure there are no assumptions that the object has the correct type.
|
|
30195
|
-
* Also, this file should avoid importing from other files that are not for validation to avoid circular deps.
|
|
30196
|
-
*/
|
|
30197
|
-
|
|
30198
|
-
|
|
30199
|
-
|
|
30200
|
-
class validation_ValidationError extends Error {
|
|
30201
|
-
constructor(error, statusCode, details) {
|
|
30202
|
-
super(error);
|
|
30203
|
-
this.statusCode = statusCode;
|
|
30204
|
-
this.details = details;
|
|
30205
|
-
}
|
|
30206
|
-
}
|
|
30207
|
-
function validatePathPart(part) {
|
|
30208
|
-
if (!part || !part.match(/^[a-zA-Z][a-zA-Z0-9!@#$%^&*~_]{0,49}$/)) {
|
|
30209
|
-
throw new Error('A document id and a collection id can contain only a-z, A-Z, 0-9,!@#$%^&*~_, starting' +
|
|
30210
|
-
'with a letter, at least one character, and up to 50.');
|
|
30211
|
-
}
|
|
30212
|
-
}
|
|
30213
|
-
function validateCollectionName(collectionName) {
|
|
30214
|
-
if (typeof collectionName !== 'string' || !collectionName) {
|
|
30215
|
-
throw new Error('Collection path has to be a non empty string');
|
|
30216
|
-
}
|
|
30217
|
-
validatePathPart(collectionName);
|
|
30218
|
-
}
|
|
30219
|
-
function validateFieldName(fieldName) {
|
|
30220
|
-
if (!fieldName || typeof fieldName !== 'string') {
|
|
30221
|
-
throw new Error('Field name has to be a non-empty string');
|
|
30222
|
-
}
|
|
30223
|
-
if (fieldName === '__docId__') {
|
|
30224
|
-
// __docId__ is the only valid fieldName that can start with '_'
|
|
30225
|
-
return;
|
|
30226
|
-
}
|
|
30227
|
-
if (!fieldName.match(/^[a-zA-Z_$][a-zA-Z0-9!@#$%^&*~_ ]{0,49}$/)) {
|
|
30228
|
-
throw new Error('A Field name can contain only a-z, A-Z, 0-9,!@#$%^&*~_, starting with a letter, at least one character, and up to 50. Field name: ' +
|
|
30229
|
-
fieldName);
|
|
30230
|
-
}
|
|
30231
|
-
}
|
|
30232
|
-
function isValidId(id) {
|
|
30233
|
-
return typeof id === 'string' && !!id && id.length <= 50 && /^[a-zA-Z0-9_-]+$/.test(id);
|
|
30234
|
-
}
|
|
30235
|
-
function validateId(id, message) {
|
|
30236
|
-
validateTruthy(isValidId(id), message, HttpStatus.BAD_REQUEST);
|
|
30237
|
-
}
|
|
30238
|
-
function validateFieldSort(fieldSort) {
|
|
30239
|
-
if (!(fieldSort instanceof Object)) {
|
|
30240
|
-
throw new Error('Field sort has to be an object');
|
|
30241
|
-
}
|
|
30242
|
-
const safeFieldSort = fieldSort;
|
|
30243
|
-
(0,dist.assertTruthy)(hasOnlyKeys(safeFieldSort, ['fieldName', 'asc']), 'Field sort should only contain a fieldName and asc');
|
|
30244
|
-
(0,dist.assertTruthy)(isRightType(safeFieldSort.asc, 'boolean'), 'Asc needs to be boolean');
|
|
30245
|
-
validateFieldName(safeFieldSort.fieldName);
|
|
30246
|
-
}
|
|
30247
|
-
/** @internal */
|
|
30248
|
-
function validateOpenIdProvider(openIdProvider) {
|
|
30249
|
-
assertTruthy(openIdProvider, 'INVALID_PROVIDER');
|
|
30250
|
-
validateOpenIdProviderType(openIdProvider.providerType);
|
|
30251
|
-
assertTruthy(openIdProvider.providerType, 'INVALID_CLIENT_ID');
|
|
30252
|
-
assertTruthy(openIdProvider.clientId, 'INVALID_CLIENT_ID');
|
|
30253
|
-
assertTruthy(openIdProvider.domain, 'INVALID_DOMAIN');
|
|
30254
|
-
return openIdProvider;
|
|
30255
|
-
}
|
|
30256
|
-
/** @internal */
|
|
30257
|
-
function validateOpenIdProviderType(providerType) {
|
|
30258
|
-
const providerArray = ['auth0'];
|
|
30259
|
-
assertTruthy(providerArray.includes(providerType), 'INVALID_OPEN_ID_PROVIDER_TYPE');
|
|
30260
|
-
}
|
|
30261
|
-
function validateDeleteMutation(mutation) {
|
|
30262
|
-
if (!mutation || mutation.type !== 'delete') {
|
|
30263
|
-
throw new Error('Mutation has to be non empty with type delete.');
|
|
30264
|
-
}
|
|
30265
|
-
// Not much to validate for delete.
|
|
30266
|
-
}
|
|
30267
|
-
function validateInsertMutation(mutation) {
|
|
30268
|
-
if (!mutation || mutation.type !== 'insert') {
|
|
30269
|
-
throw new Error('Mutation has to be non empty with type insert.');
|
|
30270
|
-
}
|
|
30271
|
-
if (!mutation.properties || typeof mutation.properties !== 'object') {
|
|
30272
|
-
throw new Error('The properties in insert mutation need to be a JSON object.');
|
|
30273
|
-
}
|
|
30274
|
-
for (const [fieldName] of Object.entries(mutation.properties)) {
|
|
30275
|
-
validateFieldName(fieldName);
|
|
30276
|
-
// TODO - figure out how to validate the value
|
|
30277
|
-
}
|
|
30278
|
-
}
|
|
30279
|
-
function validateUpdatePropertyMutation(propertyMutation) {
|
|
30280
|
-
if (!propertyMutation || propertyMutation.type !== 'update') {
|
|
30281
|
-
throw new Error('Update value property mutation has to be of type update');
|
|
30282
|
-
}
|
|
30283
|
-
if (propertyMutation.value === undefined) {
|
|
30284
|
-
throw new Error('Value has to exist in an update value property mutation..');
|
|
30285
|
-
}
|
|
30286
|
-
}
|
|
30287
|
-
function validateApplyNumericFnPropertyMutation(propertyMutation) {
|
|
30288
|
-
if (!propertyMutation || propertyMutation.type !== 'applyNumericFn') {
|
|
30289
|
-
throw new Error('Apply numeric fn mutation has to be of type applyNumericFn');
|
|
30290
|
-
}
|
|
30291
|
-
if (!['increment'].includes(propertyMutation.fn)) {
|
|
30292
|
-
throw new Error('Invalid fn for apply numeric fn.');
|
|
30293
|
-
}
|
|
30294
|
-
if (typeof propertyMutation.value !== 'number') {
|
|
30295
|
-
throw new Error('The value in an apply numeric fn function has to be numeric.');
|
|
30296
|
-
}
|
|
30297
|
-
}
|
|
30298
|
-
function validateApplyStringFnPropertyMutation(propertyMutation) {
|
|
30299
|
-
if (!propertyMutation || propertyMutation.type !== 'applyStringFn') {
|
|
30300
|
-
throw new Error('Apply string fn mutation has to be of type applyStringFn');
|
|
30301
|
-
}
|
|
30302
|
-
if (!['trim', 'extendString'].includes(propertyMutation.fn)) {
|
|
30303
|
-
throw new Error('Invalid fn for apply string fn.');
|
|
30304
|
-
}
|
|
30305
|
-
if (typeof propertyMutation.value !== 'string') {
|
|
30306
|
-
throw new Error('The value in an apply string fn function has to be a string.');
|
|
30307
|
-
}
|
|
30308
|
-
}
|
|
30309
|
-
function validatePropertyMutation(propertyMutation) {
|
|
30310
|
-
if (!propertyMutation || typeof propertyMutation !== 'object') {
|
|
30311
|
-
throw new Error('Property mutation need to be a JSON object.');
|
|
30312
|
-
}
|
|
30313
|
-
if (!['update', 'applyNumericFn', 'applyStringFn'].includes(propertyMutation.type)) {
|
|
30314
|
-
throw new Error(`Property mutation can be of type 'update', 'applyNumericFn', 'applyStringFn'`);
|
|
30315
|
-
}
|
|
30316
|
-
switch (propertyMutation.type) {
|
|
30317
|
-
case 'update':
|
|
30318
|
-
validateUpdatePropertyMutation(propertyMutation);
|
|
30319
|
-
break;
|
|
30320
|
-
case 'applyNumericFn':
|
|
30321
|
-
validateApplyNumericFnPropertyMutation(propertyMutation);
|
|
30322
|
-
break;
|
|
30323
|
-
case 'applyStringFn':
|
|
30324
|
-
validateApplyStringFnPropertyMutation(propertyMutation);
|
|
30325
|
-
break;
|
|
30326
|
-
}
|
|
30327
|
-
}
|
|
30328
|
-
function validateUpdateMutation(mutation) {
|
|
30329
|
-
if (!mutation || mutation.type !== 'update') {
|
|
30330
|
-
throw new Error('Mutation has to be non empty with type update.');
|
|
30331
|
-
}
|
|
30332
|
-
if (!mutation.properties || typeof mutation.properties !== 'object') {
|
|
30333
|
-
throw new Error('The properties in update mutation need to be a JSON object.');
|
|
30334
|
-
}
|
|
30335
|
-
const entries = Object.entries(mutation.properties);
|
|
30336
|
-
for (const [fieldName, propertyMutations] of entries) {
|
|
30337
|
-
validateFieldName(fieldName);
|
|
30338
|
-
for (const propertyMutation of propertyMutations) {
|
|
30339
|
-
validatePropertyMutation(propertyMutation);
|
|
30340
|
-
}
|
|
30341
|
-
}
|
|
30342
|
-
}
|
|
30343
|
-
function validateMutation(mutation) {
|
|
30344
|
-
if (!mutation) {
|
|
30345
|
-
throw new Error('Mutation cannot be empty');
|
|
30346
|
-
}
|
|
30347
|
-
if (!['insert', 'delete', 'update'].includes(mutation.type)) {
|
|
30348
|
-
throw new Error(`Mutation type has to be one of 'insert', 'delete', or 'update'`);
|
|
30349
|
-
}
|
|
30350
|
-
validateCollectionName(mutation.squidDocIdObj.collectionName);
|
|
30351
|
-
validatePathPart(mutation.squidDocIdObj.docId);
|
|
30352
|
-
switch (mutation.type) {
|
|
30353
|
-
case 'delete':
|
|
30354
|
-
validateDeleteMutation(mutation);
|
|
30355
|
-
break;
|
|
30356
|
-
case 'insert':
|
|
30357
|
-
validateInsertMutation(mutation);
|
|
30358
|
-
break;
|
|
30359
|
-
case 'update':
|
|
30360
|
-
validateUpdateMutation(mutation);
|
|
30361
|
-
break;
|
|
30362
|
-
}
|
|
30363
|
-
}
|
|
30364
|
-
function validateMutations(mutations) {
|
|
30365
|
-
if (!mutations || !(mutations instanceof Array) || !mutations.length) {
|
|
30366
|
-
throw new Error('The list of mutations has to be a non-empty array.');
|
|
30367
|
-
}
|
|
30368
|
-
for (const mutation of mutations) {
|
|
30369
|
-
validateMutation(mutation);
|
|
30370
|
-
}
|
|
30371
|
-
}
|
|
30372
|
-
function validateQueryLimit(limit) {
|
|
30373
|
-
(0,dist.assertNumber)(limit, 'Limit needs to be a number');
|
|
30374
|
-
if (limit === -1)
|
|
30375
|
-
return;
|
|
30376
|
-
(0,dist.assertTruthy)(limit > 0, 'query limit has to be greater than 0');
|
|
30377
|
-
(0,dist.assertTruthy)(Math.floor(limit) === limit, 'query limit has to be an integer');
|
|
30378
|
-
(0,dist.assertTruthy)(limit <= 20000, 'Limit can be maximum 20000');
|
|
30379
|
-
}
|
|
30380
|
-
/** Returns true if the value is not an empty string (undefined/null are considered empty). */
|
|
30381
|
-
function isNotEmpty(value) {
|
|
30382
|
-
validateCorrectStringType(value);
|
|
30383
|
-
return typeof value === 'string' && !!value;
|
|
30384
|
-
}
|
|
30385
|
-
/**
|
|
30386
|
-
* TODO: deprecated: this method is used in both validate...() and is...() methods.
|
|
30387
|
-
* The validate...() must throw an error, while is...() must not.
|
|
30388
|
-
*/
|
|
30389
|
-
function validateCorrectStringType(value) {
|
|
30390
|
-
if (value !== null && value !== undefined && typeof value !== 'string') {
|
|
30391
|
-
throw new Error(`Unexpected input type ${typeof value}`);
|
|
30392
|
-
}
|
|
30393
|
-
}
|
|
30394
|
-
/** Returns true if 'typeof' of the 'value' is 'type' or 'type[]'. */
|
|
30395
|
-
function isRightType(value, type) {
|
|
30396
|
-
// TODO: the method is ambiguous when the value is an empty array and will return 'true' for any type.
|
|
30397
|
-
if (Array.isArray(value)) {
|
|
30398
|
-
return value.every(element => typeof element === type);
|
|
30399
|
-
}
|
|
30400
|
-
return typeof value === type;
|
|
30401
|
-
}
|
|
30402
|
-
/** Returns true if 'obj' has only keys listed in the 'keys'. Object can't be an array. */
|
|
30403
|
-
function hasOnlyKeys(obj, keys) {
|
|
30404
|
-
return !Array.isArray(obj) && [...Object.keys(obj)].every(key => keys.includes(key));
|
|
30405
|
-
}
|
|
30406
30188
|
|
|
30407
30189
|
;// CONCATENATED MODULE: ../internal-common/src/types/document.types.ts
|
|
30408
30190
|
|
|
@@ -30428,27 +30210,545 @@ function getSquidDocId(...args) {
|
|
|
30428
30210
|
return normalizeJsonAsString(squidDocIdObjObj);
|
|
30429
30211
|
}
|
|
30430
30212
|
|
|
30431
|
-
;// CONCATENATED MODULE: ./src/
|
|
30213
|
+
;// CONCATENATED MODULE: ./src/document-reference.ts
|
|
30432
30214
|
|
|
30433
30215
|
|
|
30434
30216
|
|
|
30435
|
-
|
|
30436
|
-
|
|
30437
|
-
|
|
30438
|
-
|
|
30439
|
-
|
|
30440
|
-
|
|
30441
|
-
|
|
30442
|
-
|
|
30443
|
-
|
|
30444
|
-
|
|
30445
|
-
|
|
30446
|
-
|
|
30447
|
-
|
|
30448
|
-
|
|
30449
|
-
|
|
30450
|
-
|
|
30451
|
-
|
|
30217
|
+
|
|
30218
|
+
|
|
30219
|
+
|
|
30220
|
+
|
|
30221
|
+
/**
|
|
30222
|
+
* Holds a reference to a document. A document reference is a reference to a specific record in a collection. You can
|
|
30223
|
+
* use it to read or write data to the document. A document can refer to a row in a table in a relational database or a
|
|
30224
|
+
* document in a NoSQL database. Additionally, a document reference can refer to a non-existent document, which you can
|
|
30225
|
+
* use to create a new document.
|
|
30226
|
+
*
|
|
30227
|
+
* Read more about document references in the
|
|
30228
|
+
* {@link https://docs.squid.cloud/docs/development-tools/client-sdk/document-reference documentation}.
|
|
30229
|
+
* @typeParam T The type of the document data.
|
|
30230
|
+
*/
|
|
30231
|
+
class DocumentReference {
|
|
30232
|
+
/**
|
|
30233
|
+
* @internal
|
|
30234
|
+
*/
|
|
30235
|
+
constructor(_squidDocId, dataManager, queryBuilderFactory) {
|
|
30236
|
+
this._squidDocId = _squidDocId;
|
|
30237
|
+
this.dataManager = dataManager;
|
|
30238
|
+
this.queryBuilderFactory = queryBuilderFactory;
|
|
30239
|
+
this.refId = generateId();
|
|
30240
|
+
}
|
|
30241
|
+
/**
|
|
30242
|
+
* @internal
|
|
30243
|
+
*/
|
|
30244
|
+
get squidDocId() {
|
|
30245
|
+
return this._squidDocId;
|
|
30246
|
+
}
|
|
30247
|
+
/**
|
|
30248
|
+
* Returns the document data. Throws an error if the document does not exist.
|
|
30249
|
+
*
|
|
30250
|
+
* @returns The document data.
|
|
30251
|
+
* @throws Error if the document does not exist.
|
|
30252
|
+
*/
|
|
30253
|
+
get data() {
|
|
30254
|
+
return cloneDeep(this.dataRef);
|
|
30255
|
+
}
|
|
30256
|
+
/**
|
|
30257
|
+
* Returns a read-only internal copy of the document data. This works similar to `this.data`, except it does not
|
|
30258
|
+
* perform a defensive copy. The caller may not modify this object, on penalty of unexpected behavior.
|
|
30259
|
+
*
|
|
30260
|
+
* @returns The document data.
|
|
30261
|
+
* @throws Error if the document does not exist.
|
|
30262
|
+
*/
|
|
30263
|
+
get dataRef() {
|
|
30264
|
+
const getError = () => {
|
|
30265
|
+
const { collectionName, integrationId, docId } = parseSquidDocId(this.squidDocId);
|
|
30266
|
+
return `No data found for document reference: ${JSON.stringify({
|
|
30267
|
+
docId,
|
|
30268
|
+
collectionName,
|
|
30269
|
+
integrationId,
|
|
30270
|
+
}, null, 2)}`;
|
|
30271
|
+
};
|
|
30272
|
+
return (0,dist.truthy)(this.dataManager.getProperties(this.squidDocId), getError);
|
|
30273
|
+
}
|
|
30274
|
+
/**
|
|
30275
|
+
* Returns whether data has been populated for this document reference. Data
|
|
30276
|
+
* will not present if a document has not been queried, does not exist, or
|
|
30277
|
+
* has been deleted.
|
|
30278
|
+
*
|
|
30279
|
+
* @returns Whether the document has data.
|
|
30280
|
+
*/
|
|
30281
|
+
get hasData() {
|
|
30282
|
+
return !!this.dataManager.getProperties(this.squidDocId);
|
|
30283
|
+
}
|
|
30284
|
+
/**
|
|
30285
|
+
* A promise that resolves with the latest data from the server or undefined if the document does not exist on the
|
|
30286
|
+
* server.
|
|
30287
|
+
*
|
|
30288
|
+
* @returns A promise that resolves with latest data from the server or undefined if the document does not exist on
|
|
30289
|
+
* the server.
|
|
30290
|
+
*/
|
|
30291
|
+
async snapshot() {
|
|
30292
|
+
if (this.hasSquidPlaceholderId()) {
|
|
30293
|
+
throw new Error('Cannot invoke snapshot of a document that was created locally without storing it on the server.');
|
|
30294
|
+
}
|
|
30295
|
+
if (this.isTracked() && this.hasData)
|
|
30296
|
+
return this.data;
|
|
30297
|
+
const results = await this.queryBuilderFactory.getForDocument(this.squidDocId).dereference().snapshot();
|
|
30298
|
+
(0,dist.truthy)(results.length <= 1, 'Got more than one doc for the same id:' + this.squidDocId);
|
|
30299
|
+
return results.length ? results[0] : undefined;
|
|
30300
|
+
}
|
|
30301
|
+
/**
|
|
30302
|
+
* Returns an observable that emits the latest data from the server or undefined if the document is deleted or does
|
|
30303
|
+
* not exist on the server.
|
|
30304
|
+
*
|
|
30305
|
+
* @returns An observable that emits the latest data from the server or undefined if the document is deleted or does
|
|
30306
|
+
* not exist on the server.
|
|
30307
|
+
*/
|
|
30308
|
+
snapshots() {
|
|
30309
|
+
return this.queryBuilderFactory
|
|
30310
|
+
.getForDocument(this.squidDocId)
|
|
30311
|
+
.dereference()
|
|
30312
|
+
.snapshots()
|
|
30313
|
+
.pipe((0,external_rxjs_.map)(results => {
|
|
30314
|
+
(0,dist.truthy)(results.length <= 1, 'Got more than one doc for the same id:' + this.squidDocId);
|
|
30315
|
+
return results.length ? results[0] : undefined;
|
|
30316
|
+
}));
|
|
30317
|
+
}
|
|
30318
|
+
/**
|
|
30319
|
+
* Returns the data that is currently available on the client or undefined if data has not yet been populated.
|
|
30320
|
+
*
|
|
30321
|
+
* @returns The data that is currently available on the client or undefined if data has not yet been populated.
|
|
30322
|
+
*/
|
|
30323
|
+
peek() {
|
|
30324
|
+
return this.isTracked() && this.hasData ? this.data : undefined;
|
|
30325
|
+
}
|
|
30326
|
+
/**
|
|
30327
|
+
* Returns whether the locally available version of the document may not be the latest version on the server.
|
|
30328
|
+
*
|
|
30329
|
+
* @returns Whether the locally available version of the document may not be the latest version on the server.
|
|
30330
|
+
*/
|
|
30331
|
+
isDirty() {
|
|
30332
|
+
return this.dataManager.isDirty(this.squidDocId);
|
|
30333
|
+
}
|
|
30334
|
+
isTracked() {
|
|
30335
|
+
return this.dataManager.isTracked(this.squidDocId);
|
|
30336
|
+
}
|
|
30337
|
+
/**
|
|
30338
|
+
* Updates the document with the given data.
|
|
30339
|
+
* The `update` will be reflected optimistically locally and will be applied to the server later.
|
|
30340
|
+
* If a transactionId is provided, the `update` will be applied to the server as an atomic operation together with
|
|
30341
|
+
* the rest of the operations in the transaction and the `update` will not reflect locally until the transaction is
|
|
30342
|
+
* completed locally.
|
|
30343
|
+
*
|
|
30344
|
+
* The returned promise will resolve once the `update` has been applied to the server or immediately if the `update`
|
|
30345
|
+
* is part of a transaction.
|
|
30346
|
+
*
|
|
30347
|
+
* @param data The data to update - can be partial.
|
|
30348
|
+
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
30349
|
+
* immediately.
|
|
30350
|
+
*/
|
|
30351
|
+
async update(data, transactionId) {
|
|
30352
|
+
const properties = {};
|
|
30353
|
+
Object.entries(data).forEach(([fieldName, value]) => {
|
|
30354
|
+
const propertyMutation = value !== undefined
|
|
30355
|
+
? {
|
|
30356
|
+
type: 'update',
|
|
30357
|
+
value: value,
|
|
30358
|
+
}
|
|
30359
|
+
: {
|
|
30360
|
+
type: 'removeProperty',
|
|
30361
|
+
};
|
|
30362
|
+
properties[fieldName] = [propertyMutation];
|
|
30363
|
+
});
|
|
30364
|
+
const mutation = {
|
|
30365
|
+
type: 'update',
|
|
30366
|
+
squidDocIdObj: parseSquidDocId(this.squidDocId),
|
|
30367
|
+
properties,
|
|
30368
|
+
};
|
|
30369
|
+
return this.dataManager.applyOutgoingMutation(mutation, transactionId);
|
|
30370
|
+
}
|
|
30371
|
+
/**
|
|
30372
|
+
* Similar to {@link update}, but only updates the given path.
|
|
30373
|
+
* @param path The path to update.
|
|
30374
|
+
* @param value The value to set at the given path.
|
|
30375
|
+
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
30376
|
+
* immediately.
|
|
30377
|
+
*/
|
|
30378
|
+
async setInPath(path, value, transactionId) {
|
|
30379
|
+
// Not sure why TypeScript doesn't understand that `path` below is restricted, but we need the explicit type
|
|
30380
|
+
// assertion to make it compile.
|
|
30381
|
+
return this.update({ [path]: cloneDeep(value) }, transactionId);
|
|
30382
|
+
}
|
|
30383
|
+
/**
|
|
30384
|
+
* Similar to `update`, but only deletes the given path.
|
|
30385
|
+
* @param path The path to delete.
|
|
30386
|
+
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
30387
|
+
* immediately.
|
|
30388
|
+
*/
|
|
30389
|
+
async deleteInPath(path, transactionId) {
|
|
30390
|
+
return this.update({ [path]: undefined }, transactionId);
|
|
30391
|
+
}
|
|
30392
|
+
/**
|
|
30393
|
+
* Increments the value at the given path by the given value. The value may be both positive and negative.
|
|
30394
|
+
* @param path The path to the value to increment.
|
|
30395
|
+
* @param value The value to increment by.
|
|
30396
|
+
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
30397
|
+
* immediately.
|
|
30398
|
+
*/
|
|
30399
|
+
incrementInPath(path, value, transactionId) {
|
|
30400
|
+
const propertyMutation = {
|
|
30401
|
+
type: 'applyNumericFn',
|
|
30402
|
+
fn: 'increment',
|
|
30403
|
+
value,
|
|
30404
|
+
};
|
|
30405
|
+
const mutation = {
|
|
30406
|
+
type: 'update',
|
|
30407
|
+
squidDocIdObj: parseSquidDocId(this.squidDocId),
|
|
30408
|
+
properties: {
|
|
30409
|
+
[path]: [propertyMutation],
|
|
30410
|
+
},
|
|
30411
|
+
};
|
|
30412
|
+
return this.dataManager.applyOutgoingMutation(mutation, transactionId);
|
|
30413
|
+
}
|
|
30414
|
+
/**
|
|
30415
|
+
* Decrements the value at the given path by the given value. The value may be both positive and negative.
|
|
30416
|
+
* @param path The path to the value to decrement.
|
|
30417
|
+
* @param value The value to decrement by.
|
|
30418
|
+
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
30419
|
+
* immediately.
|
|
30420
|
+
*/
|
|
30421
|
+
decrementInPath(path, value, transactionId) {
|
|
30422
|
+
return this.incrementInPath(path, -value, transactionId);
|
|
30423
|
+
}
|
|
30424
|
+
/**
|
|
30425
|
+
* Inserts the document with the given data. If the document already exists, the operation will be treated as
|
|
30426
|
+
* `upsert`. The `insert` will be reflected optimistically locally and will be applied to the server later. If a
|
|
30427
|
+
* transactionId is provided, the `insert` will be applied to the server as an atomic operation together with the
|
|
30428
|
+
* rest
|
|
30429
|
+
* of the operations in the transaction and the `insert` will not reflect locally until the transaction is completed
|
|
30430
|
+
* locally.
|
|
30431
|
+
*
|
|
30432
|
+
* The returned promise will resolve once the `insert` has been applied to the server or immediately if the `insert`
|
|
30433
|
+
* is part of a transaction.
|
|
30434
|
+
*
|
|
30435
|
+
* @param data The data to insert.
|
|
30436
|
+
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
30437
|
+
* immediately.
|
|
30438
|
+
*/
|
|
30439
|
+
async insert(data, transactionId) {
|
|
30440
|
+
const squidDocIdObj = parseSquidDocId(this.squidDocId);
|
|
30441
|
+
const integrationId = squidDocIdObj.integrationId;
|
|
30442
|
+
// Exclude the generated client id from the mutation properties.
|
|
30443
|
+
let docIdProps = deserializeObj(squidDocIdObj.docId);
|
|
30444
|
+
if (docIdProps[SquidPlaceholderId])
|
|
30445
|
+
docIdProps = {};
|
|
30446
|
+
// Destructure composite __ids for the built_in_db.
|
|
30447
|
+
if (integrationId === IntegrationType.built_in_db && docIdProps.__id) {
|
|
30448
|
+
try {
|
|
30449
|
+
const idProps = deserializeObj(docIdProps.__id);
|
|
30450
|
+
docIdProps = Object.assign(Object.assign({}, docIdProps), idProps);
|
|
30451
|
+
}
|
|
30452
|
+
catch (_a) { }
|
|
30453
|
+
}
|
|
30454
|
+
const mutation = {
|
|
30455
|
+
type: 'insert',
|
|
30456
|
+
squidDocIdObj,
|
|
30457
|
+
properties: Object.assign(Object.assign(Object.assign({}, data), { __docId__: squidDocIdObj.docId }), docIdProps),
|
|
30458
|
+
};
|
|
30459
|
+
return this.dataManager.applyOutgoingMutation(mutation, transactionId);
|
|
30460
|
+
}
|
|
30461
|
+
/**
|
|
30462
|
+
* Deletes the document.
|
|
30463
|
+
* The `delete` will be reflected optimistically locally and will be applied to the server later.
|
|
30464
|
+
* If a transactionId is provided, the `delete` will be applied to the server as an atomic operation together with
|
|
30465
|
+
* the rest of the operations in the transaction and the `delete` will not reflect locally until the transaction is
|
|
30466
|
+
* completed locally.
|
|
30467
|
+
*
|
|
30468
|
+
* The returned promise will resolve once the `delete` has been applied to the server or immediately if the `delete`
|
|
30469
|
+
* is part of a transaction.
|
|
30470
|
+
*
|
|
30471
|
+
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
30472
|
+
* immediately.
|
|
30473
|
+
*/
|
|
30474
|
+
async delete(transactionId) {
|
|
30475
|
+
const mutation = {
|
|
30476
|
+
type: 'delete',
|
|
30477
|
+
squidDocIdObj: parseSquidDocId(this.squidDocId),
|
|
30478
|
+
};
|
|
30479
|
+
return this.dataManager.applyOutgoingMutation(mutation, transactionId);
|
|
30480
|
+
}
|
|
30481
|
+
/**
|
|
30482
|
+
* @internal
|
|
30483
|
+
*/
|
|
30484
|
+
migrateDocIds(idResolutionMap) {
|
|
30485
|
+
const newSquidDocId = idResolutionMap[this.squidDocId];
|
|
30486
|
+
if (newSquidDocId) {
|
|
30487
|
+
this._squidDocId = newSquidDocId;
|
|
30488
|
+
}
|
|
30489
|
+
}
|
|
30490
|
+
hasSquidPlaceholderId() {
|
|
30491
|
+
const obj = deserializeObj(this._squidDocId);
|
|
30492
|
+
if (typeof obj === 'object' && !!obj.docId) {
|
|
30493
|
+
const docIdObj = deserializeObj(obj.docId);
|
|
30494
|
+
if (typeof docIdObj === 'object' && Object.keys(docIdObj).includes(SquidPlaceholderId)) {
|
|
30495
|
+
return true;
|
|
30496
|
+
}
|
|
30497
|
+
}
|
|
30498
|
+
return false;
|
|
30499
|
+
}
|
|
30500
|
+
}
|
|
30501
|
+
|
|
30502
|
+
;// CONCATENATED MODULE: ../internal-common/src/utils/assert.ts
|
|
30503
|
+
|
|
30504
|
+
|
|
30505
|
+
|
|
30506
|
+
/** @internal */
|
|
30507
|
+
function assertValidateTruthy(value, message, statusCode = HttpStatus.BAD_REQUEST, details) {
|
|
30508
|
+
assertTruthy(value, () => new ValidationError(message, statusCode, details));
|
|
30509
|
+
}
|
|
30510
|
+
/** @internal */
|
|
30511
|
+
function assert_validateTruthy(value, message, statusCode = HttpStatus.BAD_REQUEST, details) {
|
|
30512
|
+
return truthy(value, () => new ValidationError(message, statusCode, details));
|
|
30513
|
+
}
|
|
30514
|
+
|
|
30515
|
+
;// CONCATENATED MODULE: ../internal-common/src/utils/validation.ts
|
|
30516
|
+
/**
|
|
30517
|
+
* This file contains general validators for the different objects being received from the client. The parameters are
|
|
30518
|
+
* usually of type 'any' to make sure there are no assumptions that the object has the correct type.
|
|
30519
|
+
* Also, this file should avoid importing from other files that are not for validation to avoid circular deps.
|
|
30520
|
+
*/
|
|
30521
|
+
|
|
30522
|
+
|
|
30523
|
+
|
|
30524
|
+
class validation_ValidationError extends Error {
|
|
30525
|
+
constructor(error, statusCode, details) {
|
|
30526
|
+
super(error);
|
|
30527
|
+
this.statusCode = statusCode;
|
|
30528
|
+
this.details = details;
|
|
30529
|
+
}
|
|
30530
|
+
}
|
|
30531
|
+
function validatePathPart(part) {
|
|
30532
|
+
if (!part || !part.match(/^[a-zA-Z][a-zA-Z0-9!@#$%^&*~_]{0,49}$/)) {
|
|
30533
|
+
throw new Error('A document id and a collection id can contain only a-z, A-Z, 0-9,!@#$%^&*~_, starting' +
|
|
30534
|
+
'with a letter, at least one character, and up to 50.');
|
|
30535
|
+
}
|
|
30536
|
+
}
|
|
30537
|
+
function validateCollectionName(collectionName) {
|
|
30538
|
+
if (typeof collectionName !== 'string' || !collectionName) {
|
|
30539
|
+
throw new Error('Collection path has to be a non empty string');
|
|
30540
|
+
}
|
|
30541
|
+
validatePathPart(collectionName);
|
|
30542
|
+
}
|
|
30543
|
+
function validateFieldName(fieldName) {
|
|
30544
|
+
if (!fieldName || typeof fieldName !== 'string') {
|
|
30545
|
+
throw new Error('Field name has to be a non-empty string');
|
|
30546
|
+
}
|
|
30547
|
+
if (fieldName === '__docId__') {
|
|
30548
|
+
// __docId__ is the only valid fieldName that can start with '_'
|
|
30549
|
+
return;
|
|
30550
|
+
}
|
|
30551
|
+
if (!fieldName.match(/^[a-zA-Z_$][a-zA-Z0-9!@#$%^&*~_ ]{0,49}$/)) {
|
|
30552
|
+
throw new Error('A Field name can contain only a-z, A-Z, 0-9,!@#$%^&*~_, starting with a letter, at least one character, and up to 50. Field name: ' +
|
|
30553
|
+
fieldName);
|
|
30554
|
+
}
|
|
30555
|
+
}
|
|
30556
|
+
function isValidId(id) {
|
|
30557
|
+
return typeof id === 'string' && !!id && id.length <= 50 && /^[a-zA-Z0-9_-]+$/.test(id);
|
|
30558
|
+
}
|
|
30559
|
+
function validateId(id, message) {
|
|
30560
|
+
validateTruthy(isValidId(id), message, HttpStatus.BAD_REQUEST);
|
|
30561
|
+
}
|
|
30562
|
+
function validateFieldSort(fieldSort) {
|
|
30563
|
+
if (!(fieldSort instanceof Object)) {
|
|
30564
|
+
throw new Error('Field sort has to be an object');
|
|
30565
|
+
}
|
|
30566
|
+
const safeFieldSort = fieldSort;
|
|
30567
|
+
(0,dist.assertTruthy)(hasOnlyKeys(safeFieldSort, ['fieldName', 'asc']), 'Field sort should only contain a fieldName and asc');
|
|
30568
|
+
(0,dist.assertTruthy)(isRightType(safeFieldSort.asc, 'boolean'), 'Asc needs to be boolean');
|
|
30569
|
+
validateFieldName(safeFieldSort.fieldName);
|
|
30570
|
+
}
|
|
30571
|
+
/** @internal */
|
|
30572
|
+
function validateOpenIdProvider(openIdProvider) {
|
|
30573
|
+
assertTruthy(openIdProvider, 'INVALID_PROVIDER');
|
|
30574
|
+
validateOpenIdProviderType(openIdProvider.providerType);
|
|
30575
|
+
assertTruthy(openIdProvider.providerType, 'INVALID_CLIENT_ID');
|
|
30576
|
+
assertTruthy(openIdProvider.clientId, 'INVALID_CLIENT_ID');
|
|
30577
|
+
assertTruthy(openIdProvider.domain, 'INVALID_DOMAIN');
|
|
30578
|
+
return openIdProvider;
|
|
30579
|
+
}
|
|
30580
|
+
/** @internal */
|
|
30581
|
+
function validateOpenIdProviderType(providerType) {
|
|
30582
|
+
const providerArray = ['auth0'];
|
|
30583
|
+
assertTruthy(providerArray.includes(providerType), 'INVALID_OPEN_ID_PROVIDER_TYPE');
|
|
30584
|
+
}
|
|
30585
|
+
function validateDeleteMutation(mutation) {
|
|
30586
|
+
if (!mutation || mutation.type !== 'delete') {
|
|
30587
|
+
throw new Error('Mutation has to be non empty with type delete.');
|
|
30588
|
+
}
|
|
30589
|
+
// Not much to validate for delete.
|
|
30590
|
+
}
|
|
30591
|
+
function validateInsertMutation(mutation) {
|
|
30592
|
+
if (!mutation || mutation.type !== 'insert') {
|
|
30593
|
+
throw new Error('Mutation has to be non empty with type insert.');
|
|
30594
|
+
}
|
|
30595
|
+
if (!mutation.properties || typeof mutation.properties !== 'object') {
|
|
30596
|
+
throw new Error('The properties in insert mutation need to be a JSON object.');
|
|
30597
|
+
}
|
|
30598
|
+
for (const [fieldName] of Object.entries(mutation.properties)) {
|
|
30599
|
+
validateFieldName(fieldName);
|
|
30600
|
+
// TODO - figure out how to validate the value
|
|
30601
|
+
}
|
|
30602
|
+
}
|
|
30603
|
+
function validateUpdatePropertyMutation(propertyMutation) {
|
|
30604
|
+
if (!propertyMutation || propertyMutation.type !== 'update') {
|
|
30605
|
+
throw new Error('Update value property mutation has to be of type update');
|
|
30606
|
+
}
|
|
30607
|
+
if (propertyMutation.value === undefined) {
|
|
30608
|
+
throw new Error('Value has to exist in an update value property mutation..');
|
|
30609
|
+
}
|
|
30610
|
+
}
|
|
30611
|
+
function validateApplyNumericFnPropertyMutation(propertyMutation) {
|
|
30612
|
+
if (!propertyMutation || propertyMutation.type !== 'applyNumericFn') {
|
|
30613
|
+
throw new Error('Apply numeric fn mutation has to be of type applyNumericFn');
|
|
30614
|
+
}
|
|
30615
|
+
if (!['increment'].includes(propertyMutation.fn)) {
|
|
30616
|
+
throw new Error('Invalid fn for apply numeric fn.');
|
|
30617
|
+
}
|
|
30618
|
+
if (typeof propertyMutation.value !== 'number') {
|
|
30619
|
+
throw new Error('The value in an apply numeric fn function has to be numeric.');
|
|
30620
|
+
}
|
|
30621
|
+
}
|
|
30622
|
+
function validateApplyStringFnPropertyMutation(propertyMutation) {
|
|
30623
|
+
if (!propertyMutation || propertyMutation.type !== 'applyStringFn') {
|
|
30624
|
+
throw new Error('Apply string fn mutation has to be of type applyStringFn');
|
|
30625
|
+
}
|
|
30626
|
+
if (!['trim', 'extendString'].includes(propertyMutation.fn)) {
|
|
30627
|
+
throw new Error('Invalid fn for apply string fn.');
|
|
30628
|
+
}
|
|
30629
|
+
if (typeof propertyMutation.value !== 'string') {
|
|
30630
|
+
throw new Error('The value in an apply string fn function has to be a string.');
|
|
30631
|
+
}
|
|
30632
|
+
}
|
|
30633
|
+
function validatePropertyMutation(propertyMutation) {
|
|
30634
|
+
if (!propertyMutation || typeof propertyMutation !== 'object') {
|
|
30635
|
+
throw new Error('Property mutation need to be a JSON object.');
|
|
30636
|
+
}
|
|
30637
|
+
if (!['update', 'applyNumericFn', 'applyStringFn'].includes(propertyMutation.type)) {
|
|
30638
|
+
throw new Error(`Property mutation can be of type 'update', 'applyNumericFn', 'applyStringFn'`);
|
|
30639
|
+
}
|
|
30640
|
+
switch (propertyMutation.type) {
|
|
30641
|
+
case 'update':
|
|
30642
|
+
validateUpdatePropertyMutation(propertyMutation);
|
|
30643
|
+
break;
|
|
30644
|
+
case 'applyNumericFn':
|
|
30645
|
+
validateApplyNumericFnPropertyMutation(propertyMutation);
|
|
30646
|
+
break;
|
|
30647
|
+
case 'applyStringFn':
|
|
30648
|
+
validateApplyStringFnPropertyMutation(propertyMutation);
|
|
30649
|
+
break;
|
|
30650
|
+
}
|
|
30651
|
+
}
|
|
30652
|
+
function validateUpdateMutation(mutation) {
|
|
30653
|
+
if (!mutation || mutation.type !== 'update') {
|
|
30654
|
+
throw new Error('Mutation has to be non empty with type update.');
|
|
30655
|
+
}
|
|
30656
|
+
if (!mutation.properties || typeof mutation.properties !== 'object') {
|
|
30657
|
+
throw new Error('The properties in update mutation need to be a JSON object.');
|
|
30658
|
+
}
|
|
30659
|
+
const entries = Object.entries(mutation.properties);
|
|
30660
|
+
for (const [fieldName, propertyMutations] of entries) {
|
|
30661
|
+
validateFieldName(fieldName);
|
|
30662
|
+
for (const propertyMutation of propertyMutations) {
|
|
30663
|
+
validatePropertyMutation(propertyMutation);
|
|
30664
|
+
}
|
|
30665
|
+
}
|
|
30666
|
+
}
|
|
30667
|
+
function validateMutation(mutation) {
|
|
30668
|
+
if (!mutation) {
|
|
30669
|
+
throw new Error('Mutation cannot be empty');
|
|
30670
|
+
}
|
|
30671
|
+
if (!['insert', 'delete', 'update'].includes(mutation.type)) {
|
|
30672
|
+
throw new Error(`Mutation type has to be one of 'insert', 'delete', or 'update'`);
|
|
30673
|
+
}
|
|
30674
|
+
validateCollectionName(mutation.squidDocIdObj.collectionName);
|
|
30675
|
+
validatePathPart(mutation.squidDocIdObj.docId);
|
|
30676
|
+
switch (mutation.type) {
|
|
30677
|
+
case 'delete':
|
|
30678
|
+
validateDeleteMutation(mutation);
|
|
30679
|
+
break;
|
|
30680
|
+
case 'insert':
|
|
30681
|
+
validateInsertMutation(mutation);
|
|
30682
|
+
break;
|
|
30683
|
+
case 'update':
|
|
30684
|
+
validateUpdateMutation(mutation);
|
|
30685
|
+
break;
|
|
30686
|
+
}
|
|
30687
|
+
}
|
|
30688
|
+
function validateMutations(mutations) {
|
|
30689
|
+
if (!mutations || !(mutations instanceof Array) || !mutations.length) {
|
|
30690
|
+
throw new Error('The list of mutations has to be a non-empty array.');
|
|
30691
|
+
}
|
|
30692
|
+
for (const mutation of mutations) {
|
|
30693
|
+
validateMutation(mutation);
|
|
30694
|
+
}
|
|
30695
|
+
}
|
|
30696
|
+
function validateQueryLimit(limit) {
|
|
30697
|
+
(0,dist.assertNumber)(limit, 'Limit needs to be a number');
|
|
30698
|
+
if (limit === -1)
|
|
30699
|
+
return;
|
|
30700
|
+
(0,dist.assertTruthy)(limit > 0, 'query limit has to be greater than 0');
|
|
30701
|
+
(0,dist.assertTruthy)(Math.floor(limit) === limit, 'query limit has to be an integer');
|
|
30702
|
+
(0,dist.assertTruthy)(limit <= 20000, 'Limit can be maximum 20000');
|
|
30703
|
+
}
|
|
30704
|
+
/** Returns true if the value is not an empty string (undefined/null are considered empty). */
|
|
30705
|
+
function isNotEmpty(value) {
|
|
30706
|
+
validateCorrectStringType(value);
|
|
30707
|
+
return typeof value === 'string' && !!value;
|
|
30708
|
+
}
|
|
30709
|
+
/**
|
|
30710
|
+
* TODO: deprecated: this method is used in both validate...() and is...() methods.
|
|
30711
|
+
* The validate...() must throw an error, while is...() must not.
|
|
30712
|
+
*/
|
|
30713
|
+
function validateCorrectStringType(value) {
|
|
30714
|
+
if (value !== null && value !== undefined && typeof value !== 'string') {
|
|
30715
|
+
throw new Error(`Unexpected input type ${typeof value}`);
|
|
30716
|
+
}
|
|
30717
|
+
}
|
|
30718
|
+
/** Returns true if 'typeof' of the 'value' is 'type' or 'type[]'. */
|
|
30719
|
+
function isRightType(value, type) {
|
|
30720
|
+
// TODO: the method is ambiguous when the value is an empty array and will return 'true' for any type.
|
|
30721
|
+
if (Array.isArray(value)) {
|
|
30722
|
+
return value.every(element => typeof element === type);
|
|
30723
|
+
}
|
|
30724
|
+
return typeof value === type;
|
|
30725
|
+
}
|
|
30726
|
+
/** Returns true if 'obj' has only keys listed in the 'keys'. Object can't be an array. */
|
|
30727
|
+
function hasOnlyKeys(obj, keys) {
|
|
30728
|
+
return !Array.isArray(obj) && [...Object.keys(obj)].every(key => keys.includes(key));
|
|
30729
|
+
}
|
|
30730
|
+
|
|
30731
|
+
;// CONCATENATED MODULE: ./src/query/pagination.ts
|
|
30732
|
+
|
|
30733
|
+
|
|
30734
|
+
|
|
30735
|
+
class Pagination {
|
|
30736
|
+
/** @internal */
|
|
30737
|
+
constructor(snapshotEmitter, options = {}) {
|
|
30738
|
+
/* Invariants:
|
|
30739
|
+
internalStateObserver.value.data
|
|
30740
|
+
- always contains the pageSize elements that are on the page (fewer if we're on the last page)
|
|
30741
|
+
- always contains some elements before the first one on the page and some elements after
|
|
30742
|
+
the last one on the page, unless at the beginning/end.
|
|
30743
|
+
Note: it is possible that all the elements before/after the page get deleted. Our current strategy is to grab
|
|
30744
|
+
a full page of them and hope they don't all get deleted. An alternative strategy would be to check in dataReceived
|
|
30745
|
+
if numBefore and numAfter are getting low, and rerun the query in that case. However, in order to do that we would need to be
|
|
30746
|
+
able to check if we are at the next-to-last page, because numAfter can be as low as 1 in that case, and similarly for numBefore.
|
|
30747
|
+
|
|
30748
|
+
firstElement
|
|
30749
|
+
- is an extracted document that may or may not be part of the data
|
|
30750
|
+
- the page consists of the first pageSize elements of data that are >= firstElement
|
|
30751
|
+
- firstElement === null refers to a hypothetical document that is ordered before all other documents.
|
|
30452
30752
|
*/
|
|
30453
30753
|
this.internalStateObserver = new external_rxjs_.BehaviorSubject(null);
|
|
30454
30754
|
this.firstElement = null;
|
|
@@ -31526,6 +31826,7 @@ class GroupedJoin {
|
|
|
31526
31826
|
|
|
31527
31827
|
|
|
31528
31828
|
|
|
31829
|
+
|
|
31529
31830
|
/**
|
|
31530
31831
|
* Holds a reference to a data collection. A collection reference is a reference to a collection in a database. You
|
|
31531
31832
|
* can use it to read or write data to the collection. A collection can refer to a table in a relational database or a
|
|
@@ -31539,12 +31840,13 @@ class CollectionReference {
|
|
|
31539
31840
|
/**
|
|
31540
31841
|
* @internal
|
|
31541
31842
|
*/
|
|
31542
|
-
constructor(collectionName, integrationId, documentReferenceFactory, queryBuilderFactory, querySubscriptionManager) {
|
|
31843
|
+
constructor(collectionName, integrationId, documentReferenceFactory, queryBuilderFactory, querySubscriptionManager, dataManager) {
|
|
31543
31844
|
this.collectionName = collectionName;
|
|
31544
31845
|
this.integrationId = integrationId;
|
|
31545
31846
|
this.documentReferenceFactory = documentReferenceFactory;
|
|
31546
31847
|
this.queryBuilderFactory = queryBuilderFactory;
|
|
31547
31848
|
this.querySubscriptionManager = querySubscriptionManager;
|
|
31849
|
+
this.dataManager = dataManager;
|
|
31548
31850
|
this.refId = generateId();
|
|
31549
31851
|
}
|
|
31550
31852
|
/**
|
|
@@ -31602,6 +31904,31 @@ class CollectionReference {
|
|
|
31602
31904
|
const squidDocId = getSquidDocId(docIdAsJsonString, this.collectionName, this.integrationId);
|
|
31603
31905
|
return this.documentReferenceFactory.create(squidDocId, this.queryBuilderFactory);
|
|
31604
31906
|
}
|
|
31907
|
+
/**
|
|
31908
|
+
* Inserts multiple documents into the collection.
|
|
31909
|
+
*/
|
|
31910
|
+
async insertMany(docs, txId) {
|
|
31911
|
+
await this.dataManager.runInTransaction(async (innerTxId) => {
|
|
31912
|
+
for (const doc of docs) {
|
|
31913
|
+
await this.doc(doc.id).insert(doc.data, innerTxId);
|
|
31914
|
+
}
|
|
31915
|
+
}, txId);
|
|
31916
|
+
}
|
|
31917
|
+
/**
|
|
31918
|
+
* Deletes multiple documents from the collection.
|
|
31919
|
+
*/
|
|
31920
|
+
async deleteMany(docIdsOrReferences, txId) {
|
|
31921
|
+
await this.dataManager.runInTransaction(async (innerTxId) => {
|
|
31922
|
+
for (const idOrRef of docIdsOrReferences) {
|
|
31923
|
+
if (idOrRef instanceof DocumentReference) {
|
|
31924
|
+
await idOrRef.delete(innerTxId);
|
|
31925
|
+
}
|
|
31926
|
+
else {
|
|
31927
|
+
await this.doc(idOrRef).delete(innerTxId);
|
|
31928
|
+
}
|
|
31929
|
+
}
|
|
31930
|
+
}, txId);
|
|
31931
|
+
}
|
|
31605
31932
|
/**
|
|
31606
31933
|
* Creates a `QueryBuilder` that can be used to query the collection.
|
|
31607
31934
|
*
|
|
@@ -31748,10 +32075,11 @@ class MergedQueryBuilder {
|
|
|
31748
32075
|
|
|
31749
32076
|
/** @internal */
|
|
31750
32077
|
class CollectionReferenceFactory {
|
|
31751
|
-
constructor(documentReferenceFactory, queryBuilderFactory, querySubscriptionManager) {
|
|
32078
|
+
constructor(documentReferenceFactory, queryBuilderFactory, querySubscriptionManager, dataManager) {
|
|
31752
32079
|
this.documentReferenceFactory = documentReferenceFactory;
|
|
31753
32080
|
this.queryBuilderFactory = queryBuilderFactory;
|
|
31754
32081
|
this.querySubscriptionManager = querySubscriptionManager;
|
|
32082
|
+
this.dataManager = dataManager;
|
|
31755
32083
|
this.collections = new Map();
|
|
31756
32084
|
}
|
|
31757
32085
|
get(collectionName, integrationId) {
|
|
@@ -31762,7 +32090,7 @@ class CollectionReferenceFactory {
|
|
|
31762
32090
|
}
|
|
31763
32091
|
let collectionRef = integrationCollectionMap.get(collectionName);
|
|
31764
32092
|
if (!collectionRef) {
|
|
31765
|
-
collectionRef = new CollectionReference(collectionName, integrationId, this.documentReferenceFactory, this.queryBuilderFactory, this.querySubscriptionManager);
|
|
32093
|
+
collectionRef = new CollectionReference(collectionName, integrationId, this.documentReferenceFactory, this.queryBuilderFactory, this.querySubscriptionManager, this.dataManager);
|
|
31766
32094
|
integrationCollectionMap.set(collectionName, collectionRef);
|
|
31767
32095
|
}
|
|
31768
32096
|
return collectionRef;
|
|
@@ -32565,537 +32893,248 @@ class DataManager {
|
|
|
32565
32893
|
}
|
|
32566
32894
|
return Object.entries(outgoingMutationsByIntegrationId);
|
|
32567
32895
|
}
|
|
32568
|
-
/**
|
|
32569
|
-
* Handles the case that due to some change (an incoming or outgoing change to a document), a document becomes orphan.
|
|
32570
|
-
* That is, there are no ongoing queries that will keep it up-to-date.
|
|
32571
|
-
* An orphan document should not stay locally since it may be stale after some time.
|
|
32572
|
-
*/
|
|
32573
|
-
handleOrphanDocs() {
|
|
32574
|
-
this.querySubscriptionManager.onOrphanDocuments.subscribe(orphanDocs => {
|
|
32575
|
-
for (const squidDocId of orphanDocs) {
|
|
32576
|
-
if (!this.isTracked(squidDocId)) {
|
|
32577
|
-
this.forgetDocument(squidDocId);
|
|
32578
|
-
}
|
|
32579
|
-
}
|
|
32580
|
-
});
|
|
32581
|
-
}
|
|
32582
|
-
acknowledgeDocument(squidDocId, timestamp, expires = false) {
|
|
32583
|
-
this.docIdToServerTimestamp.set(squidDocId, { timestamp });
|
|
32584
|
-
this.setExpiration(squidDocId, expires);
|
|
32585
|
-
}
|
|
32586
|
-
setExpiration(squidDocId, expires) {
|
|
32587
|
-
const docTimestamp = this.docIdToServerTimestamp.get(squidDocId);
|
|
32588
|
-
if (docTimestamp) {
|
|
32589
|
-
docTimestamp.expireTimestamp = expires ? Date.now() + 20000 : undefined;
|
|
32590
|
-
}
|
|
32591
|
-
}
|
|
32592
|
-
forgetDocument(squidDocId) {
|
|
32593
|
-
this.docIdToLocalTimestamp.delete(squidDocId);
|
|
32594
|
-
/**
|
|
32595
|
-
* Keep the document timestamp for a bit more just to make sure there is nothing in-flight that will be accepted
|
|
32596
|
-
* with the wrong timestamp.
|
|
32597
|
-
*/
|
|
32598
|
-
this.setExpiration(squidDocId, true);
|
|
32599
|
-
}
|
|
32600
|
-
migrateDocIds(idResolutionMap) {
|
|
32601
|
-
this.pendingOutgoingMutations.forEach(outgoingMutations => {
|
|
32602
|
-
outgoingMutations.forEach(outgoingMutation => {
|
|
32603
|
-
const squidDocId = getSquidDocId(outgoingMutation.mutation.squidDocIdObj);
|
|
32604
|
-
const resolvedId = idResolutionMap[squidDocId];
|
|
32605
|
-
if (resolvedId) {
|
|
32606
|
-
outgoingMutation.mutation.squidDocIdObj = parseSquidDocId(resolvedId);
|
|
32607
|
-
}
|
|
32608
|
-
});
|
|
32609
|
-
});
|
|
32610
|
-
Object.entries(idResolutionMap).forEach(([squidDocId, newSquidDocId]) => {
|
|
32611
|
-
replaceKeyInMap(this.pendingOutgoingMutations, squidDocId, newSquidDocId);
|
|
32612
|
-
replaceKeyInMap(this.docIdToLocalTimestamp, squidDocId, newSquidDocId);
|
|
32613
|
-
replaceKeyInMap(this.docIdToServerTimestamp, squidDocId, newSquidDocId);
|
|
32614
|
-
});
|
|
32615
|
-
}
|
|
32616
|
-
hasPendingSentMutations() {
|
|
32617
|
-
for (const outgoingMutationsForDoc of this.pendingOutgoingMutations.values()) {
|
|
32618
|
-
for (const outgoingMutation of outgoingMutationsForDoc) {
|
|
32619
|
-
if (outgoingMutation.sentToServer) {
|
|
32620
|
-
return true;
|
|
32621
|
-
}
|
|
32622
|
-
}
|
|
32623
|
-
}
|
|
32624
|
-
return false;
|
|
32625
|
-
}
|
|
32626
|
-
}
|
|
32627
|
-
/** A constant used to differentiate undefined values provided by user from the real undefined variable state. */
|
|
32628
|
-
const UNSET_VALUE = Symbol('undefined');
|
|
32629
|
-
|
|
32630
|
-
;// CONCATENATED MODULE: ./src/destruct.manager.ts
|
|
32631
|
-
|
|
32632
|
-
|
|
32633
|
-
class DestructManager {
|
|
32634
|
-
constructor() {
|
|
32635
|
-
this.preDestructors = [];
|
|
32636
|
-
this.destructors = [];
|
|
32637
|
-
this.isDestructedSubject = new external_rxjs_.BehaviorSubject(false);
|
|
32638
|
-
}
|
|
32639
|
-
get isDestructing() {
|
|
32640
|
-
return this.isDestructedSubject.value;
|
|
32641
|
-
}
|
|
32642
|
-
observeIsDestructing() {
|
|
32643
|
-
return this.isDestructedSubject.asObservable().pipe((0,external_rxjs_.filter)(Boolean), map(() => undefined));
|
|
32644
|
-
}
|
|
32645
|
-
onPreDestruct(fn) {
|
|
32646
|
-
this.preDestructors.push(fn);
|
|
32647
|
-
}
|
|
32648
|
-
onDestruct(fn) {
|
|
32649
|
-
this.destructors.push(fn);
|
|
32650
|
-
}
|
|
32651
|
-
async destruct() {
|
|
32652
|
-
this.reportDestructed();
|
|
32653
|
-
const fns = this.preDestructors.concat(this.destructors);
|
|
32654
|
-
let fn = fns.shift();
|
|
32655
|
-
while (fn) {
|
|
32656
|
-
try {
|
|
32657
|
-
await fn();
|
|
32658
|
-
}
|
|
32659
|
-
catch (e) {
|
|
32660
|
-
console.error('Error while destructing Squid', e);
|
|
32661
|
-
}
|
|
32662
|
-
fn = fns.shift();
|
|
32663
|
-
}
|
|
32664
|
-
}
|
|
32665
|
-
reportDestructed() {
|
|
32666
|
-
if (this.isDestructing) {
|
|
32667
|
-
return;
|
|
32668
|
-
}
|
|
32669
|
-
this.isDestructedSubject.next(true);
|
|
32670
|
-
}
|
|
32671
|
-
}
|
|
32672
|
-
|
|
32673
|
-
;// CONCATENATED MODULE: ./src/distributed-lock.manager.ts
|
|
32674
|
-
|
|
32675
|
-
|
|
32676
|
-
|
|
32677
|
-
/**
|
|
32678
|
-
* @internal
|
|
32679
|
-
*/
|
|
32680
|
-
class DistributedLockManager {
|
|
32681
|
-
constructor(socketManager, destructManager) {
|
|
32682
|
-
this.socketManager = socketManager;
|
|
32683
|
-
this.destructManager = destructManager;
|
|
32684
|
-
this.ongoingLocks = {};
|
|
32685
|
-
this.acquireLockMessagesFromServer = this.socketManager
|
|
32686
|
-
.observeNotifications()
|
|
32687
|
-
.pipe((0,external_rxjs_.filter)(message => message.type === 'lockAcquired'));
|
|
32688
|
-
this.releaseLockMessagesFromServer = this.socketManager
|
|
32689
|
-
.observeNotifications()
|
|
32690
|
-
.pipe((0,external_rxjs_.filter)(message => message.type === 'lockReleased'));
|
|
32691
|
-
// we may override this value in tests
|
|
32692
|
-
this.lockWaitForConnectionThreshold = 2000;
|
|
32693
|
-
destructManager.onPreDestruct(() => {
|
|
32694
|
-
this.releaseAllLocks();
|
|
32695
|
-
});
|
|
32696
|
-
this.socketManager.observeConnectionReady().subscribe(ready => {
|
|
32697
|
-
if (ready)
|
|
32698
|
-
return;
|
|
32699
|
-
this.releaseAllLocks();
|
|
32700
|
-
});
|
|
32701
|
-
this.releaseLockMessagesFromServer.subscribe(message => {
|
|
32702
|
-
const lock = this.ongoingLocks[message.payload.clientRequestId];
|
|
32703
|
-
if (lock === undefined)
|
|
32704
|
-
return;
|
|
32705
|
-
lock.release();
|
|
32706
|
-
});
|
|
32707
|
-
}
|
|
32708
|
-
async lock(mutex) {
|
|
32709
|
-
/**
|
|
32710
|
-
* Wait up to 2 seconds for the connection to be ready and if it is not, consider the client to be not connected.
|
|
32711
|
-
* This is useful because right after the Squid client is created, the socket is still not connected but the user
|
|
32712
|
-
* may want to acquire a lock; it is a valid situation. Also, there may be short disconnect and in this case we
|
|
32713
|
-
* still want to wait for the connection to be ready.
|
|
32714
|
-
*/
|
|
32715
|
-
const isConnected = await (0,external_rxjs_.firstValueFrom)((0,external_rxjs_.race)((0,external_rxjs_.timer)(this.lockWaitForConnectionThreshold).pipe(map(() => false)), this.socketManager.observeConnectionReady().pipe((0,external_rxjs_.filter)(Boolean)), this.destructManager.observeIsDestructing()));
|
|
32716
|
-
if (!isConnected) {
|
|
32717
|
-
return Promise.reject('CLIENT_NOT_CONNECTED');
|
|
32718
|
-
}
|
|
32719
|
-
const clientRequestId = generateId();
|
|
32720
|
-
const acquireLockMessage = {
|
|
32721
|
-
type: 'acquireLock',
|
|
32722
|
-
payload: {
|
|
32723
|
-
mutex,
|
|
32724
|
-
clientRequestId,
|
|
32725
|
-
},
|
|
32726
|
-
};
|
|
32727
|
-
this.socketManager.sendMessage(acquireLockMessage);
|
|
32728
|
-
const result = await (0,external_rxjs_.firstValueFrom)((0,external_rxjs_.race)((0,external_rxjs_.timer)(5000).pipe((0,external_rxjs_.take)(1), map(() => {
|
|
32729
|
-
return {
|
|
32730
|
-
payload: {
|
|
32731
|
-
error: 'TIMEOUT_GETTING_LOCK',
|
|
32732
|
-
lockId: undefined,
|
|
32733
|
-
},
|
|
32734
|
-
};
|
|
32735
|
-
})), this.acquireLockMessagesFromServer.pipe((0,external_rxjs_.filter)(message => message.payload.clientRequestId === clientRequestId))));
|
|
32736
|
-
if (this.destructManager.isDestructing) {
|
|
32737
|
-
throw new Error('Destructing');
|
|
32738
|
-
}
|
|
32739
|
-
if (!result.payload.lockId) {
|
|
32740
|
-
throw new Error(`Failed to acquire lock: ${result.payload.error}`);
|
|
32741
|
-
}
|
|
32742
|
-
const lockId = result.payload.lockId;
|
|
32743
|
-
const lock = new DistributedLockImpl(lockId, clientRequestId, this.ongoingLocks, this.socketManager);
|
|
32744
|
-
this.ongoingLocks[lockId] = lock;
|
|
32745
|
-
return lock;
|
|
32746
|
-
}
|
|
32747
|
-
releaseAllLocks() {
|
|
32748
|
-
for (const [lockId, lock] of Object.entries(this.ongoingLocks)) {
|
|
32749
|
-
lock.release();
|
|
32750
|
-
delete this.ongoingLocks[lockId];
|
|
32751
|
-
}
|
|
32752
|
-
}
|
|
32753
|
-
}
|
|
32754
|
-
/**
|
|
32755
|
-
* @internal
|
|
32756
|
-
*/
|
|
32757
|
-
class DistributedLockImpl {
|
|
32758
|
-
constructor(lockId, clientRequestId, ongoingLocks, socketManager) {
|
|
32759
|
-
this.lockId = lockId;
|
|
32760
|
-
this.clientRequestId = clientRequestId;
|
|
32761
|
-
this.ongoingLocks = ongoingLocks;
|
|
32762
|
-
this.socketManager = socketManager;
|
|
32763
|
-
this.released = false;
|
|
32764
|
-
this.onReleaseSubject = new external_rxjs_.Subject();
|
|
32896
|
+
/**
|
|
32897
|
+
* Handles the case that due to some change (an incoming or outgoing change to a document), a document becomes orphan.
|
|
32898
|
+
* That is, there are no ongoing queries that will keep it up-to-date.
|
|
32899
|
+
* An orphan document should not stay locally since it may be stale after some time.
|
|
32900
|
+
*/
|
|
32901
|
+
handleOrphanDocs() {
|
|
32902
|
+
this.querySubscriptionManager.onOrphanDocuments.subscribe(orphanDocs => {
|
|
32903
|
+
for (const squidDocId of orphanDocs) {
|
|
32904
|
+
if (!this.isTracked(squidDocId)) {
|
|
32905
|
+
this.forgetDocument(squidDocId);
|
|
32906
|
+
}
|
|
32907
|
+
}
|
|
32908
|
+
});
|
|
32765
32909
|
}
|
|
32766
|
-
|
|
32767
|
-
|
|
32768
|
-
|
|
32769
|
-
this.released = true;
|
|
32770
|
-
delete this.ongoingLocks[this.lockId];
|
|
32771
|
-
const releaseLockMessage = {
|
|
32772
|
-
type: 'releaseLock',
|
|
32773
|
-
payload: {
|
|
32774
|
-
lockId: this.lockId,
|
|
32775
|
-
clientRequestId: this.clientRequestId,
|
|
32776
|
-
},
|
|
32777
|
-
};
|
|
32778
|
-
this.socketManager.sendMessage(releaseLockMessage);
|
|
32779
|
-
this.onReleaseSubject.next();
|
|
32910
|
+
acknowledgeDocument(squidDocId, timestamp, expires = false) {
|
|
32911
|
+
this.docIdToServerTimestamp.set(squidDocId, { timestamp });
|
|
32912
|
+
this.setExpiration(squidDocId, expires);
|
|
32780
32913
|
}
|
|
32781
|
-
|
|
32782
|
-
|
|
32914
|
+
setExpiration(squidDocId, expires) {
|
|
32915
|
+
const docTimestamp = this.docIdToServerTimestamp.get(squidDocId);
|
|
32916
|
+
if (docTimestamp) {
|
|
32917
|
+
docTimestamp.expireTimestamp = expires ? Date.now() + 20000 : undefined;
|
|
32918
|
+
}
|
|
32783
32919
|
}
|
|
32784
|
-
|
|
32785
|
-
|
|
32920
|
+
forgetDocument(squidDocId) {
|
|
32921
|
+
this.docIdToLocalTimestamp.delete(squidDocId);
|
|
32922
|
+
/**
|
|
32923
|
+
* Keep the document timestamp for a bit more just to make sure there is nothing in-flight that will be accepted
|
|
32924
|
+
* with the wrong timestamp.
|
|
32925
|
+
*/
|
|
32926
|
+
this.setExpiration(squidDocId, true);
|
|
32786
32927
|
}
|
|
32787
|
-
|
|
32788
|
-
|
|
32789
|
-
|
|
32790
|
-
|
|
32791
|
-
|
|
32792
|
-
|
|
32793
|
-
|
|
32794
|
-
|
|
32795
|
-
|
|
32796
|
-
this.changeNotifier = new external_rxjs_.BehaviorSubject({});
|
|
32797
|
-
this.destructManager.onDestruct(() => {
|
|
32798
|
-
this.changeNotifier.complete();
|
|
32928
|
+
migrateDocIds(idResolutionMap) {
|
|
32929
|
+
this.pendingOutgoingMutations.forEach(outgoingMutations => {
|
|
32930
|
+
outgoingMutations.forEach(outgoingMutation => {
|
|
32931
|
+
const squidDocId = getSquidDocId(outgoingMutation.mutation.squidDocIdObj);
|
|
32932
|
+
const resolvedId = idResolutionMap[squidDocId];
|
|
32933
|
+
if (resolvedId) {
|
|
32934
|
+
outgoingMutation.mutation.squidDocIdObj = parseSquidDocId(resolvedId);
|
|
32935
|
+
}
|
|
32936
|
+
});
|
|
32799
32937
|
});
|
|
32800
|
-
}
|
|
32801
|
-
migrate(idResolutionMap) {
|
|
32802
|
-
// Update the document in the database before notifying any other subscribers.
|
|
32803
32938
|
Object.entries(idResolutionMap).forEach(([squidDocId, newSquidDocId]) => {
|
|
32804
|
-
this.
|
|
32939
|
+
replaceKeyInMap(this.pendingOutgoingMutations, squidDocId, newSquidDocId);
|
|
32940
|
+
replaceKeyInMap(this.docIdToLocalTimestamp, squidDocId, newSquidDocId);
|
|
32941
|
+
replaceKeyInMap(this.docIdToServerTimestamp, squidDocId, newSquidDocId);
|
|
32805
32942
|
});
|
|
32806
|
-
this.changeNotifier.next(idResolutionMap);
|
|
32807
32943
|
}
|
|
32808
|
-
|
|
32809
|
-
|
|
32944
|
+
hasPendingSentMutations() {
|
|
32945
|
+
for (const outgoingMutationsForDoc of this.pendingOutgoingMutations.values()) {
|
|
32946
|
+
for (const outgoingMutation of outgoingMutationsForDoc) {
|
|
32947
|
+
if (outgoingMutation.sentToServer) {
|
|
32948
|
+
return true;
|
|
32949
|
+
}
|
|
32950
|
+
}
|
|
32951
|
+
}
|
|
32952
|
+
return false;
|
|
32810
32953
|
}
|
|
32811
32954
|
}
|
|
32955
|
+
/** A constant used to differentiate undefined values provided by user from the real undefined variable state. */
|
|
32956
|
+
const UNSET_VALUE = Symbol('undefined');
|
|
32812
32957
|
|
|
32813
|
-
;// CONCATENATED MODULE: ./src/
|
|
32814
|
-
|
|
32815
|
-
|
|
32816
|
-
|
|
32817
|
-
|
|
32818
|
-
|
|
32958
|
+
;// CONCATENATED MODULE: ./src/destruct.manager.ts
|
|
32819
32959
|
|
|
32820
32960
|
|
|
32821
|
-
|
|
32822
|
-
|
|
32823
|
-
|
|
32824
|
-
|
|
32825
|
-
|
|
32826
|
-
*
|
|
32827
|
-
* Read more about document references in the
|
|
32828
|
-
* {@link https://docs.squid.cloud/docs/development-tools/client-sdk/document-reference documentation}.
|
|
32829
|
-
* @typeParam T The type of the document data.
|
|
32830
|
-
*/
|
|
32831
|
-
class DocumentReference {
|
|
32832
|
-
/**
|
|
32833
|
-
* @internal
|
|
32834
|
-
*/
|
|
32835
|
-
constructor(_squidDocId, dataManager, queryBuilderFactory) {
|
|
32836
|
-
this._squidDocId = _squidDocId;
|
|
32837
|
-
this.dataManager = dataManager;
|
|
32838
|
-
this.queryBuilderFactory = queryBuilderFactory;
|
|
32839
|
-
this.refId = generateId();
|
|
32961
|
+
class DestructManager {
|
|
32962
|
+
constructor() {
|
|
32963
|
+
this.preDestructors = [];
|
|
32964
|
+
this.destructors = [];
|
|
32965
|
+
this.isDestructedSubject = new external_rxjs_.BehaviorSubject(false);
|
|
32840
32966
|
}
|
|
32841
|
-
|
|
32842
|
-
|
|
32843
|
-
*/
|
|
32844
|
-
get squidDocId() {
|
|
32845
|
-
return this._squidDocId;
|
|
32967
|
+
get isDestructing() {
|
|
32968
|
+
return this.isDestructedSubject.value;
|
|
32846
32969
|
}
|
|
32847
|
-
|
|
32848
|
-
|
|
32849
|
-
*
|
|
32850
|
-
* @returns The document data.
|
|
32851
|
-
* @throws Error if the document does not exist.
|
|
32852
|
-
*/
|
|
32853
|
-
get data() {
|
|
32854
|
-
return cloneDeep(this.dataRef);
|
|
32970
|
+
observeIsDestructing() {
|
|
32971
|
+
return this.isDestructedSubject.asObservable().pipe((0,external_rxjs_.filter)(Boolean), map(() => undefined));
|
|
32855
32972
|
}
|
|
32856
|
-
|
|
32857
|
-
|
|
32858
|
-
* perform a defensive copy. The caller may not modify this object, on penalty of unexpected behavior.
|
|
32859
|
-
*
|
|
32860
|
-
* @returns The document data.
|
|
32861
|
-
* @throws Error if the document does not exist.
|
|
32862
|
-
*/
|
|
32863
|
-
get dataRef() {
|
|
32864
|
-
const getError = () => {
|
|
32865
|
-
const { collectionName, integrationId, docId } = parseSquidDocId(this.squidDocId);
|
|
32866
|
-
return `No data found for document reference: ${JSON.stringify({
|
|
32867
|
-
docId,
|
|
32868
|
-
collectionName,
|
|
32869
|
-
integrationId,
|
|
32870
|
-
}, null, 2)}`;
|
|
32871
|
-
};
|
|
32872
|
-
return (0,dist.truthy)(this.dataManager.getProperties(this.squidDocId), getError);
|
|
32973
|
+
onPreDestruct(fn) {
|
|
32974
|
+
this.preDestructors.push(fn);
|
|
32873
32975
|
}
|
|
32874
|
-
|
|
32875
|
-
|
|
32876
|
-
* will not present if a document has not been queried, does not exist, or
|
|
32877
|
-
* has been deleted.
|
|
32878
|
-
*
|
|
32879
|
-
* @returns Whether the document has data.
|
|
32880
|
-
*/
|
|
32881
|
-
get hasData() {
|
|
32882
|
-
return !!this.dataManager.getProperties(this.squidDocId);
|
|
32976
|
+
onDestruct(fn) {
|
|
32977
|
+
this.destructors.push(fn);
|
|
32883
32978
|
}
|
|
32884
|
-
|
|
32885
|
-
|
|
32886
|
-
|
|
32887
|
-
|
|
32888
|
-
|
|
32889
|
-
|
|
32890
|
-
|
|
32891
|
-
|
|
32892
|
-
|
|
32893
|
-
|
|
32979
|
+
async destruct() {
|
|
32980
|
+
this.reportDestructed();
|
|
32981
|
+
const fns = this.preDestructors.concat(this.destructors);
|
|
32982
|
+
let fn = fns.shift();
|
|
32983
|
+
while (fn) {
|
|
32984
|
+
try {
|
|
32985
|
+
await fn();
|
|
32986
|
+
}
|
|
32987
|
+
catch (e) {
|
|
32988
|
+
console.error('Error while destructing Squid', e);
|
|
32989
|
+
}
|
|
32990
|
+
fn = fns.shift();
|
|
32894
32991
|
}
|
|
32895
|
-
if (this.isTracked() && this.hasData)
|
|
32896
|
-
return this.data;
|
|
32897
|
-
const results = await this.queryBuilderFactory.getForDocument(this.squidDocId).dereference().snapshot();
|
|
32898
|
-
(0,dist.truthy)(results.length <= 1, 'Got more than one doc for the same id:' + this.squidDocId);
|
|
32899
|
-
return results.length ? results[0] : undefined;
|
|
32900
|
-
}
|
|
32901
|
-
/**
|
|
32902
|
-
* Returns an observable that emits the latest data from the server or undefined if the document is deleted or does
|
|
32903
|
-
* not exist on the server.
|
|
32904
|
-
*
|
|
32905
|
-
* @returns An observable that emits the latest data from the server or undefined if the document is deleted or does
|
|
32906
|
-
* not exist on the server.
|
|
32907
|
-
*/
|
|
32908
|
-
snapshots() {
|
|
32909
|
-
return this.queryBuilderFactory
|
|
32910
|
-
.getForDocument(this.squidDocId)
|
|
32911
|
-
.dereference()
|
|
32912
|
-
.snapshots()
|
|
32913
|
-
.pipe((0,external_rxjs_.map)(results => {
|
|
32914
|
-
(0,dist.truthy)(results.length <= 1, 'Got more than one doc for the same id:' + this.squidDocId);
|
|
32915
|
-
return results.length ? results[0] : undefined;
|
|
32916
|
-
}));
|
|
32917
|
-
}
|
|
32918
|
-
/**
|
|
32919
|
-
* Returns the data that is currently available on the client or undefined if data has not yet been populated.
|
|
32920
|
-
*
|
|
32921
|
-
* @returns The data that is currently available on the client or undefined if data has not yet been populated.
|
|
32922
|
-
*/
|
|
32923
|
-
peek() {
|
|
32924
|
-
return this.isTracked() && this.hasData ? this.data : undefined;
|
|
32925
|
-
}
|
|
32926
|
-
/**
|
|
32927
|
-
* Returns whether the locally available version of the document may not be the latest version on the server.
|
|
32928
|
-
*
|
|
32929
|
-
* @returns Whether the locally available version of the document may not be the latest version on the server.
|
|
32930
|
-
*/
|
|
32931
|
-
isDirty() {
|
|
32932
|
-
return this.dataManager.isDirty(this.squidDocId);
|
|
32933
|
-
}
|
|
32934
|
-
isTracked() {
|
|
32935
|
-
return this.dataManager.isTracked(this.squidDocId);
|
|
32936
32992
|
}
|
|
32937
|
-
|
|
32938
|
-
|
|
32939
|
-
|
|
32940
|
-
|
|
32941
|
-
|
|
32942
|
-
|
|
32943
|
-
|
|
32944
|
-
|
|
32945
|
-
|
|
32946
|
-
|
|
32947
|
-
|
|
32948
|
-
|
|
32949
|
-
|
|
32950
|
-
|
|
32951
|
-
|
|
32952
|
-
|
|
32953
|
-
|
|
32954
|
-
|
|
32955
|
-
|
|
32956
|
-
|
|
32957
|
-
|
|
32958
|
-
|
|
32959
|
-
|
|
32960
|
-
|
|
32961
|
-
|
|
32962
|
-
|
|
32993
|
+
reportDestructed() {
|
|
32994
|
+
if (this.isDestructing) {
|
|
32995
|
+
return;
|
|
32996
|
+
}
|
|
32997
|
+
this.isDestructedSubject.next(true);
|
|
32998
|
+
}
|
|
32999
|
+
}
|
|
33000
|
+
|
|
33001
|
+
;// CONCATENATED MODULE: ./src/distributed-lock.manager.ts
|
|
33002
|
+
|
|
33003
|
+
|
|
33004
|
+
|
|
33005
|
+
/**
|
|
33006
|
+
* @internal
|
|
33007
|
+
*/
|
|
33008
|
+
class DistributedLockManager {
|
|
33009
|
+
constructor(socketManager, destructManager) {
|
|
33010
|
+
this.socketManager = socketManager;
|
|
33011
|
+
this.destructManager = destructManager;
|
|
33012
|
+
this.ongoingLocks = {};
|
|
33013
|
+
this.acquireLockMessagesFromServer = this.socketManager
|
|
33014
|
+
.observeNotifications()
|
|
33015
|
+
.pipe((0,external_rxjs_.filter)(message => message.type === 'lockAcquired'));
|
|
33016
|
+
this.releaseLockMessagesFromServer = this.socketManager
|
|
33017
|
+
.observeNotifications()
|
|
33018
|
+
.pipe((0,external_rxjs_.filter)(message => message.type === 'lockReleased'));
|
|
33019
|
+
// we may override this value in tests
|
|
33020
|
+
this.lockWaitForConnectionThreshold = 2000;
|
|
33021
|
+
destructManager.onPreDestruct(() => {
|
|
33022
|
+
this.releaseAllLocks();
|
|
32963
33023
|
});
|
|
32964
|
-
|
|
32965
|
-
|
|
32966
|
-
|
|
32967
|
-
|
|
33024
|
+
this.socketManager.observeConnectionReady().subscribe(ready => {
|
|
33025
|
+
if (ready)
|
|
33026
|
+
return;
|
|
33027
|
+
this.releaseAllLocks();
|
|
33028
|
+
});
|
|
33029
|
+
this.releaseLockMessagesFromServer.subscribe(message => {
|
|
33030
|
+
const lock = this.ongoingLocks[message.payload.clientRequestId];
|
|
33031
|
+
if (lock === undefined)
|
|
33032
|
+
return;
|
|
33033
|
+
lock.release();
|
|
33034
|
+
});
|
|
33035
|
+
}
|
|
33036
|
+
async lock(mutex) {
|
|
33037
|
+
/**
|
|
33038
|
+
* Wait up to 2 seconds for the connection to be ready and if it is not, consider the client to be not connected.
|
|
33039
|
+
* This is useful because right after the Squid client is created, the socket is still not connected but the user
|
|
33040
|
+
* may want to acquire a lock; it is a valid situation. Also, there may be short disconnect and in this case we
|
|
33041
|
+
* still want to wait for the connection to be ready.
|
|
33042
|
+
*/
|
|
33043
|
+
const isConnected = await (0,external_rxjs_.firstValueFrom)((0,external_rxjs_.race)((0,external_rxjs_.timer)(this.lockWaitForConnectionThreshold).pipe(map(() => false)), this.socketManager.observeConnectionReady().pipe((0,external_rxjs_.filter)(Boolean)), this.destructManager.observeIsDestructing()));
|
|
33044
|
+
if (!isConnected) {
|
|
33045
|
+
return Promise.reject('CLIENT_NOT_CONNECTED');
|
|
33046
|
+
}
|
|
33047
|
+
const clientRequestId = generateId();
|
|
33048
|
+
const acquireLockMessage = {
|
|
33049
|
+
type: 'acquireLock',
|
|
33050
|
+
payload: {
|
|
33051
|
+
mutex,
|
|
33052
|
+
clientRequestId,
|
|
33053
|
+
},
|
|
32968
33054
|
};
|
|
32969
|
-
|
|
33055
|
+
this.socketManager.sendMessage(acquireLockMessage);
|
|
33056
|
+
const result = await (0,external_rxjs_.firstValueFrom)((0,external_rxjs_.race)((0,external_rxjs_.timer)(5000).pipe((0,external_rxjs_.take)(1), map(() => {
|
|
33057
|
+
return {
|
|
33058
|
+
payload: {
|
|
33059
|
+
error: 'TIMEOUT_GETTING_LOCK',
|
|
33060
|
+
lockId: undefined,
|
|
33061
|
+
},
|
|
33062
|
+
};
|
|
33063
|
+
})), this.acquireLockMessagesFromServer.pipe((0,external_rxjs_.filter)(message => message.payload.clientRequestId === clientRequestId))));
|
|
33064
|
+
if (this.destructManager.isDestructing) {
|
|
33065
|
+
throw new Error('Destructing');
|
|
33066
|
+
}
|
|
33067
|
+
if (!result.payload.lockId) {
|
|
33068
|
+
throw new Error(`Failed to acquire lock: ${result.payload.error}`);
|
|
33069
|
+
}
|
|
33070
|
+
const lockId = result.payload.lockId;
|
|
33071
|
+
const lock = new DistributedLockImpl(lockId, clientRequestId, this.ongoingLocks, this.socketManager);
|
|
33072
|
+
this.ongoingLocks[lockId] = lock;
|
|
33073
|
+
return lock;
|
|
32970
33074
|
}
|
|
32971
|
-
|
|
32972
|
-
|
|
32973
|
-
|
|
32974
|
-
|
|
32975
|
-
|
|
32976
|
-
* immediately.
|
|
32977
|
-
*/
|
|
32978
|
-
async setInPath(path, value, transactionId) {
|
|
32979
|
-
// Not sure why TypeScript doesn't understand that `path` below is restricted, but we need the explicit type
|
|
32980
|
-
// assertion to make it compile.
|
|
32981
|
-
return this.update({ [path]: cloneDeep(value) }, transactionId);
|
|
33075
|
+
releaseAllLocks() {
|
|
33076
|
+
for (const [lockId, lock] of Object.entries(this.ongoingLocks)) {
|
|
33077
|
+
lock.release();
|
|
33078
|
+
delete this.ongoingLocks[lockId];
|
|
33079
|
+
}
|
|
32982
33080
|
}
|
|
32983
|
-
|
|
32984
|
-
|
|
32985
|
-
|
|
32986
|
-
|
|
32987
|
-
|
|
32988
|
-
|
|
32989
|
-
|
|
32990
|
-
|
|
33081
|
+
}
|
|
33082
|
+
/**
|
|
33083
|
+
* @internal
|
|
33084
|
+
*/
|
|
33085
|
+
class DistributedLockImpl {
|
|
33086
|
+
constructor(lockId, clientRequestId, ongoingLocks, socketManager) {
|
|
33087
|
+
this.lockId = lockId;
|
|
33088
|
+
this.clientRequestId = clientRequestId;
|
|
33089
|
+
this.ongoingLocks = ongoingLocks;
|
|
33090
|
+
this.socketManager = socketManager;
|
|
33091
|
+
this.released = false;
|
|
33092
|
+
this.onReleaseSubject = new external_rxjs_.Subject();
|
|
32991
33093
|
}
|
|
32992
|
-
|
|
32993
|
-
|
|
32994
|
-
|
|
32995
|
-
|
|
32996
|
-
|
|
32997
|
-
|
|
32998
|
-
|
|
32999
|
-
|
|
33000
|
-
|
|
33001
|
-
|
|
33002
|
-
fn: 'increment',
|
|
33003
|
-
value,
|
|
33004
|
-
};
|
|
33005
|
-
const mutation = {
|
|
33006
|
-
type: 'update',
|
|
33007
|
-
squidDocIdObj: parseSquidDocId(this.squidDocId),
|
|
33008
|
-
properties: {
|
|
33009
|
-
[path]: [propertyMutation],
|
|
33094
|
+
release() {
|
|
33095
|
+
if (this.released)
|
|
33096
|
+
return;
|
|
33097
|
+
this.released = true;
|
|
33098
|
+
delete this.ongoingLocks[this.lockId];
|
|
33099
|
+
const releaseLockMessage = {
|
|
33100
|
+
type: 'releaseLock',
|
|
33101
|
+
payload: {
|
|
33102
|
+
lockId: this.lockId,
|
|
33103
|
+
clientRequestId: this.clientRequestId,
|
|
33010
33104
|
},
|
|
33011
33105
|
};
|
|
33012
|
-
|
|
33106
|
+
this.socketManager.sendMessage(releaseLockMessage);
|
|
33107
|
+
this.onReleaseSubject.next();
|
|
33013
33108
|
}
|
|
33014
|
-
|
|
33015
|
-
|
|
33016
|
-
* @param path The path to the value to decrement.
|
|
33017
|
-
* @param value The value to decrement by.
|
|
33018
|
-
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
33019
|
-
* immediately.
|
|
33020
|
-
*/
|
|
33021
|
-
decrementInPath(path, value, transactionId) {
|
|
33022
|
-
return this.incrementInPath(path, -value, transactionId);
|
|
33109
|
+
observeRelease() {
|
|
33110
|
+
return this.onReleaseSubject.asObservable();
|
|
33023
33111
|
}
|
|
33024
|
-
|
|
33025
|
-
|
|
33026
|
-
* `upsert`. The `insert` will be reflected optimistically locally and will be applied to the server later. If a
|
|
33027
|
-
* transactionId is provided, the `insert` will be applied to the server as an atomic operation together with the
|
|
33028
|
-
* rest
|
|
33029
|
-
* of the operations in the transaction and the `insert` will not reflect locally until the transaction is completed
|
|
33030
|
-
* locally.
|
|
33031
|
-
*
|
|
33032
|
-
* The returned promise will resolve once the `insert` has been applied to the server or immediately if the `insert`
|
|
33033
|
-
* is part of a transaction.
|
|
33034
|
-
*
|
|
33035
|
-
* @param data The data to insert.
|
|
33036
|
-
* @param transactionId The transaction to use for this operation. If not provided, the operation will be applied
|
|
33037
|
-
* immediately.
|
|
33038
|
-
*/
|
|
33039
|
-
async insert(data, transactionId) {
|
|
33040
|
-
const squidDocIdObj = parseSquidDocId(this.squidDocId);
|
|
33041
|
-
const integrationId = squidDocIdObj.integrationId;
|
|
33042
|
-
// Exclude the generated client id from the mutation properties.
|
|
33043
|
-
let docIdProps = deserializeObj(squidDocIdObj.docId);
|
|
33044
|
-
if (docIdProps[SquidPlaceholderId])
|
|
33045
|
-
docIdProps = {};
|
|
33046
|
-
// Destructure composite __ids for the built_in_db.
|
|
33047
|
-
if (integrationId === IntegrationType.built_in_db && docIdProps.__id) {
|
|
33048
|
-
try {
|
|
33049
|
-
const idProps = deserializeObj(docIdProps.__id);
|
|
33050
|
-
docIdProps = Object.assign(Object.assign({}, docIdProps), idProps);
|
|
33051
|
-
}
|
|
33052
|
-
catch (_a) { }
|
|
33053
|
-
}
|
|
33054
|
-
const mutation = {
|
|
33055
|
-
type: 'insert',
|
|
33056
|
-
squidDocIdObj,
|
|
33057
|
-
properties: Object.assign(Object.assign(Object.assign({}, data), { __docId__: squidDocIdObj.docId }), docIdProps),
|
|
33058
|
-
};
|
|
33059
|
-
return this.dataManager.applyOutgoingMutation(mutation, transactionId);
|
|
33112
|
+
isReleased() {
|
|
33113
|
+
return this.released;
|
|
33060
33114
|
}
|
|
33061
|
-
|
|
33062
|
-
|
|
33063
|
-
|
|
33064
|
-
|
|
33065
|
-
|
|
33066
|
-
|
|
33067
|
-
|
|
33068
|
-
|
|
33069
|
-
|
|
33070
|
-
|
|
33071
|
-
|
|
33072
|
-
|
|
33073
|
-
|
|
33074
|
-
async delete(transactionId) {
|
|
33075
|
-
const mutation = {
|
|
33076
|
-
type: 'delete',
|
|
33077
|
-
squidDocIdObj: parseSquidDocId(this.squidDocId),
|
|
33078
|
-
};
|
|
33079
|
-
return this.dataManager.applyOutgoingMutation(mutation, transactionId);
|
|
33115
|
+
}
|
|
33116
|
+
|
|
33117
|
+
;// CONCATENATED MODULE: ./src/document-identity.service.ts
|
|
33118
|
+
|
|
33119
|
+
/** @internal */
|
|
33120
|
+
class DocumentIdentityService {
|
|
33121
|
+
constructor(documentStore, destructManager) {
|
|
33122
|
+
this.documentStore = documentStore;
|
|
33123
|
+
this.destructManager = destructManager;
|
|
33124
|
+
this.changeNotifier = new external_rxjs_.BehaviorSubject({});
|
|
33125
|
+
this.destructManager.onDestruct(() => {
|
|
33126
|
+
this.changeNotifier.complete();
|
|
33127
|
+
});
|
|
33080
33128
|
}
|
|
33081
|
-
|
|
33082
|
-
|
|
33083
|
-
|
|
33084
|
-
|
|
33085
|
-
|
|
33086
|
-
|
|
33087
|
-
this._squidDocId = newSquidDocId;
|
|
33088
|
-
}
|
|
33129
|
+
migrate(idResolutionMap) {
|
|
33130
|
+
// Update the document in the database before notifying any other subscribers.
|
|
33131
|
+
Object.entries(idResolutionMap).forEach(([squidDocId, newSquidDocId]) => {
|
|
33132
|
+
this.documentStore.migrateDocId(squidDocId, newSquidDocId);
|
|
33133
|
+
});
|
|
33134
|
+
this.changeNotifier.next(idResolutionMap);
|
|
33089
33135
|
}
|
|
33090
|
-
|
|
33091
|
-
|
|
33092
|
-
if (typeof obj === 'object' && !!obj.docId) {
|
|
33093
|
-
const docIdObj = deserializeObj(obj.docId);
|
|
33094
|
-
if (typeof docIdObj === 'object' && Object.keys(docIdObj).includes(SquidPlaceholderId)) {
|
|
33095
|
-
return true;
|
|
33096
|
-
}
|
|
33097
|
-
}
|
|
33098
|
-
return false;
|
|
33136
|
+
observeChanges() {
|
|
33137
|
+
return this.changeNotifier.asObservable();
|
|
33099
33138
|
}
|
|
33100
33139
|
}
|
|
33101
33140
|
|
|
@@ -49132,6 +49171,7 @@ const kotlinControllers = [
|
|
|
49132
49171
|
'mutation',
|
|
49133
49172
|
'application',
|
|
49134
49173
|
'storage',
|
|
49174
|
+
'internalCodeExecutor',
|
|
49135
49175
|
];
|
|
49136
49176
|
function getApplicationUrl(regionPrefix, appId, path) {
|
|
49137
49177
|
const baseUrl = 'https://squid.cloud';
|
|
@@ -51410,8 +51450,8 @@ class Squid {
|
|
|
51410
51450
|
this.localQueryManager = new LocalQueryManager(this.documentStore, this.documentReferenceFactory, this.querySubscriptionManager);
|
|
51411
51451
|
const mutationSender = new MutationSender(this.rpcManager, this.lockManager, this.querySender);
|
|
51412
51452
|
this.queryBuilderFactory = new QueryBuilderFactory(this.querySubscriptionManager, this.localQueryManager, this.documentReferenceFactory, this.documentIdentityService);
|
|
51413
|
-
this.collectionReferenceFactory = new CollectionReferenceFactory(this.documentReferenceFactory, this.queryBuilderFactory, this.querySubscriptionManager);
|
|
51414
51453
|
this.dataManager = new DataManager(this.documentStore, mutationSender, this.socketManager, this.querySubscriptionManager, this.queryBuilderFactory, this.lockManager, this.destructManager, this.documentIdentityService, this.querySender);
|
|
51454
|
+
this.collectionReferenceFactory = new CollectionReferenceFactory(this.documentReferenceFactory, this.queryBuilderFactory, this.querySubscriptionManager, this.dataManager);
|
|
51415
51455
|
this.documentReferenceFactory.setDataManager(this.dataManager);
|
|
51416
51456
|
this.backendFunctionManager = new BackendFunctionManager(this.clientIdService, this.rpcManager);
|
|
51417
51457
|
this.nativeQueryManager = new NativeQueryManager(this.rpcManager);
|