@roit/roit-data-firestore 1.2.42 → 1.2.44

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.
Files changed (37) hide show
  1. package/README.md +107 -0
  2. package/dist/archive/ArchivePluginRegistry.d.ts +78 -0
  3. package/dist/archive/ArchivePluginRegistry.js +135 -0
  4. package/dist/archive/ArchiveService.d.ts +61 -11
  5. package/dist/archive/ArchiveService.js +138 -120
  6. package/dist/archive/IArchivePlugin.d.ts +102 -0
  7. package/dist/archive/IArchivePlugin.js +12 -0
  8. package/dist/archive/index.d.ts +2 -0
  9. package/dist/archive/index.js +11 -0
  10. package/dist/cache/CacheResolver.js +6 -5
  11. package/dist/config/ArchiveConfig.d.ts +17 -12
  12. package/dist/config/ArchiveConfig.js +25 -41
  13. package/dist/config/BaseRepository.js +1 -1
  14. package/dist/config/ReadonlyRepository.js +1 -1
  15. package/dist/exception/RepositoryException.js +1 -1
  16. package/dist/index.d.ts +4 -0
  17. package/dist/index.js +10 -1
  18. package/dist/model/CacheProviders.d.ts +1 -2
  19. package/dist/model/CacheProviders.js +1 -2
  20. package/dist/query/ManualQueryHelper.js +0 -1
  21. package/dist/query/QueryPredicateFunctionTransform.js +4 -1
  22. package/dist/template/FunctionAggregationTemplate.txt +1 -1
  23. package/dist/template/FunctionAverageTemplate.txt +1 -1
  24. package/dist/template/FunctionCountTemplate.txt +20 -25
  25. package/dist/template/FunctionCreateOrUpdateTemplate.txt +69 -20
  26. package/dist/template/FunctionCreateTemplate.txt +15 -13
  27. package/dist/template/FunctionDeleteTemplate.txt +45 -13
  28. package/dist/template/FunctionFindAllTemplate.txt +39 -23
  29. package/dist/template/FunctionFindByIdTemplate.txt +24 -14
  30. package/dist/template/FunctionQueryTemplate.txt +67 -41
  31. package/dist/template/FunctionSumTemplate.txt +48 -32
  32. package/dist/template/FunctionUpdatePartialTemplate.txt +76 -21
  33. package/dist/template/FunctionUpdateTemplate.txt +64 -17
  34. package/dist/tsconfig.build.tsbuildinfo +1 -1
  35. package/package.json +1 -1
  36. package/dist/cache/providers/RedisCacheArchiveProvider.d.ts +0 -19
  37. package/dist/cache/providers/RedisCacheArchiveProvider.js +0 -115
@@ -1,5 +1,4 @@
1
1
  export declare enum CacheProviders {
2
2
  LOCAL = "LOCAL",
3
- REDIS = "REDIS",
4
- REDIS_ARCHIVE = "REDIS_ARCHIVE"
3
+ REDIS = "REDIS"
5
4
  }
@@ -5,5 +5,4 @@ var CacheProviders;
5
5
  (function (CacheProviders) {
6
6
  CacheProviders["LOCAL"] = "LOCAL";
7
7
  CacheProviders["REDIS"] = "REDIS";
8
- CacheProviders["REDIS_ARCHIVE"] = "REDIS_ARCHIVE";
9
- })(CacheProviders || (exports.CacheProviders = CacheProviders = {}));
8
+ })(CacheProviders = exports.CacheProviders || (exports.CacheProviders = {}));
@@ -170,7 +170,6 @@ class ManualQueryHelper {
170
170
  };
171
171
  }
172
172
  catch (error) {
173
- console.error(error);
174
173
  span.setStatus({
175
174
  code: 2,
176
175
  message: error.message
@@ -44,6 +44,7 @@ const fs_1 = __importDefault(require("fs"));
44
44
  const path_1 = __importDefault(require("path"));
45
45
  const Tracer_1 = require("../tracer/Tracer");
46
46
  const ArchiveService_1 = require("../archive/ArchiveService");
47
+ const archive_1 = require("../archive");
47
48
  const functionQueryTemplate = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../template/FunctionQueryTemplate.txt'), 'utf8');
48
49
  const methodList = {
49
50
  'aggregation': fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../template/FunctionAggregationTemplate.txt'), 'utf8'),
@@ -73,13 +74,15 @@ class QueryPredicateFunctionTransform {
73
74
  environmentUtil: new EnvironmentUtil_1.EnvironmentUtil,
74
75
  firestoreReadAuditResolver: FirestoreReadAuditResolver_1.FirestoreReadAuditResolver.getInstance(),
75
76
  fieldValueIncrement: firestore_1.FieldValue.increment,
77
+ FieldValue: firestore_1.FieldValue,
76
78
  getTtlTimestamp: TtlBuilderUtil_1.TtlBuilderUtil.getTtlTimestamp,
77
79
  convertToMQuery: ManualQueryHelper_1.ManualQueryHelper.convertToMQuery,
78
80
  aggregateAverage: firestore_1.AggregateField.average,
79
81
  aggregateSum: firestore_1.AggregateField.sum,
80
82
  aggregateCount: firestore_1.AggregateField.count,
81
83
  startTracer: Tracer_1.startTracer,
82
- archiveService: ArchiveService_1.ArchiveService.getInstance()
84
+ archiveService: ArchiveService_1.ArchiveService.getInstance(),
85
+ ARCHIVE_FIELDS: archive_1.ARCHIVE_METADATA_FIELDS
83
86
  };
84
87
  if (!options?.collection) {
85
88
  throw new Error(`Collection is required`);
@@ -65,4 +65,4 @@ aggregation(config) {
65
65
  }
66
66
  })
67
67
  });
68
- }
68
+ }
@@ -49,4 +49,4 @@ average(config) {
49
49
  }
50
50
  })
51
51
  });
52
- }
52
+ }
@@ -5,53 +5,48 @@ count(config) {
5
5
  const db = global.instances.globalDbFile.FirestoreInstance.getInstance();
6
6
  const environmentUtil = global.instances.environmentUtil;
7
7
  const convertToMQuery = global.instances.convertToMQuery;
8
+
8
9
  if (environmentUtil.areWeTesting()) {
9
10
  console.log('It was decreed that it is being executed try, no operation or effective transaction will be performed');
10
11
  return 0;
11
12
  }
13
+
12
14
  const collection = db.collection('<COLLECTION_REPLACE>');
13
- let queryList;
14
- let queryExecute;
15
- const traceQuery = []
16
- const pushTraceQuery = (query) => {
17
- traceQuery.push({ field: query.field, operator: query.operator, value: '?' })
18
- }
19
- if ((config === null || config === void 0 ? void 0 : config.query) && config.query.length > 0) {
20
- queryList = config.query.map(query => {
15
+ let queryExecute = collection;
16
+ const traceQuery = [];
17
+
18
+ if (config?.query && config.query.length > 0) {
19
+ const queryList = config.query.map(query => {
21
20
  if (Object.keys(query).length === 1) {
22
21
  return convertToMQuery(query);
23
22
  }
24
23
  return query;
25
24
  });
26
- const queryInit = queryList[0];
27
- queryExecute = collection.where(queryInit.field, queryInit.operator, queryInit.value);
28
- pushTraceQuery(queryInit)
29
- queryList.shift();
30
- queryList.forEach(que => {
31
- queryExecute = queryExecute.where(que.field, que.operator, que.value);
32
- pushTraceQuery(que)
33
- });
34
- }
35
- else {
36
- queryExecute = collection;
25
+
26
+ for (const query of queryList) {
27
+ queryExecute = queryExecute.where(query.field, query.operator, query.value);
28
+ traceQuery.push({ field: query.field, operator: query.operator, value: '?' });
29
+ }
37
30
  }
31
+
38
32
  const snapshot = yield queryExecute.count().get();
39
33
  const count = snapshot.data().count;
34
+
40
35
  span.setAttributes({
41
36
  'firestore.operation.name': 'count',
42
37
  'firestore.operation.query': JSON.stringify(traceQuery),
43
38
  'firestore.collection.name': '<COLLECTION_REPLACE>',
44
39
  'firestore.operation.result.size': count,
45
- })
40
+ });
46
41
  return count;
47
42
  } catch (error) {
48
43
  span.setStatus({
49
44
  code: 2,
50
45
  message: error.message
51
- })
52
- span.recordException(error)
53
- throw error
46
+ });
47
+ span.recordException(error);
48
+ throw error;
54
49
  }
55
- })
50
+ });
56
51
  });
57
- }
52
+ }
@@ -19,12 +19,55 @@ createOrUpdate(items) {
19
19
  const getTtlTimestamp = global.instances.getTtlTimestamp;
20
20
  const collection = db.collection('<COLLECTION_REPLACE>');
21
21
  const validatorDataHandle = global.instances.validatorDataHandle;
22
+ const archiveService = yield global.instances.archiveService;
22
23
  const batch = db.batch();
23
- for (const item of items) {
24
+
25
+ // Preparar IDs e buscar documentos existentes em lote (otimização)
26
+ const itemsWithIds = items.map(item => {
27
+ if (!item.id) item.id = uuid();
28
+ return item;
29
+ });
30
+
31
+ const docRefs = itemsWithIds.map(item => collection.doc(item.id));
32
+ const existingDocs = (archiveService.isEnabled() && docRefs.length > 0)
33
+ ? yield db.getAll(...docRefs)
34
+ : [];
35
+
36
+ for (let i = 0; i < itemsWithIds.length; i++) {
37
+ const item = itemsWithIds[i];
24
38
  yield validatorDataHandle.validateModel(modelName, item, validatorOptions);
25
- if (!item.id) {
26
- item.id = uuid();
39
+
40
+ const docRef = docRefs[i];
41
+ let shouldUnarchive = false;
42
+
43
+ // VERIFICAÇÃO DE DOCUMENTO ARQUIVADO
44
+ if (archiveService.isEnabled() && existingDocs[i]?.exists) {
45
+ const currentData = existingDocs[i].data();
46
+
47
+ if (currentData && archiveService.isDocumentArchived(currentData)) {
48
+ const archivePath = currentData.fbArchivePath;
49
+ const updateResult = yield archiveService.updateArchivedDocument(
50
+ '<COLLECTION_REPLACE>',
51
+ item.id,
52
+ item,
53
+ archivePath,
54
+ { unarchive: true }
55
+ );
56
+
57
+ if (updateResult.result.success && updateResult.mergedData) {
58
+ if (updateResult.mergedData.createAt) {
59
+ item.createAt = updateResult.mergedData.createAt;
60
+ item.createTimestampAt = updateResult.mergedData.createTimestampAt;
61
+ }
62
+ const originalItem = Object.assign({}, item);
63
+ Object.assign(item, updateResult.mergedData, originalItem);
64
+ shouldUnarchive = true;
65
+ } else {
66
+ throw new Error('Failed to update archived document; aborting createOrUpdate to prevent inconsistent state and stale archive overwriting user changes.');
67
+ }
68
+ }
27
69
  }
70
+
28
71
  if (!item.createAt) {
29
72
  item.createAt = newDate();
30
73
  item.createTimestampAt = new Date(item.createAt).getTime();
@@ -32,40 +75,46 @@ createOrUpdate(items) {
32
75
  item.updateAt = newDate();
33
76
  item.updateTimestampAt = new Date(item.updateAt).getTime();
34
77
  item.lastServiceModify = process.env.SERVICE || 'PROJECT_UNDEFINED';
35
- const docRef = collection.doc(item.id);
36
- if (ttlExpirationIn && item?.ttlExpirationAt) {
37
- delete item.ttlExpirationAt;
38
- }
39
- batch.set(docRef, JSON.parse(JSON.stringify(item)), { merge: true });
78
+
79
+ // Preparar dados para persistir (consolidado)
80
+ const itemData = JSON.parse(JSON.stringify(item));
40
81
  if (ttlExpirationIn && ttlUnit) {
41
- const ttl = getTtlTimestamp(ttlExpirationIn, ttlUnit);
42
- batch.set(docRef, {
43
- ttlExpirationAt: ttl,
44
- }, { merge: true });
82
+ itemData.ttlExpirationAt = getTtlTimestamp(ttlExpirationIn, ttlUnit);
83
+ }
84
+
85
+ if (shouldUnarchive) {
86
+ const FieldValue = global.instances.FieldValue;
87
+ const ARCHIVE_FIELDS = global.instances.ARCHIVE_FIELDS;
88
+ itemData[ARCHIVE_FIELDS.ARCHIVED_AT] = FieldValue.delete();
89
+ itemData[ARCHIVE_FIELDS.ARCHIVE_PATH] = FieldValue.delete();
90
+ itemData[ARCHIVE_FIELDS.ARCHIVE_HASH] = FieldValue.delete();
45
91
  }
92
+
93
+ batch.set(docRef, itemData, { merge: true });
46
94
  }
95
+
47
96
  if (!environmentUtil.areWeTesting()) {
48
97
  yield batch.commit();
49
98
  yield this.revokeCache();
50
- }
51
- else {
99
+ } else {
52
100
  console.log('It was decreed that it is being executed try, no operation or effective transaction will be performed');
53
101
  }
102
+
54
103
  span.setAttributes({
55
104
  'firestore.operation.name': 'createOrUpdate',
56
105
  'firestore.operation.size': items.length,
57
106
  'firestore.collection.name': '<COLLECTION_REPLACE>',
58
- })
107
+ });
59
108
  return items;
60
109
 
61
110
  } catch (error) {
62
111
  span.setStatus({
63
112
  code: 2,
64
113
  message: error.message
65
- })
66
- span.recordException(error)
67
- throw error
114
+ });
115
+ span.recordException(error);
116
+ throw error;
68
117
  }
69
- })
118
+ });
70
119
  });
71
- }
120
+ }
@@ -20,6 +20,7 @@ create(items) {
20
20
  const collection = db.collection('<COLLECTION_REPLACE>');
21
21
  const validatorDataHandle = global.instances.validatorDataHandle;
22
22
  const batch = db.batch();
23
+
23
24
  for (const item of items) {
24
25
  yield validatorDataHandle.validateModel(modelName, item, validatorOptions);
25
26
  if (!item.id) {
@@ -32,35 +33,36 @@ create(items) {
32
33
  item.updateAt = newDate();
33
34
  item.updateTimestampAt = new Date(item.updateAt).getTime();
34
35
  item.lastServiceModify = process.env.SERVICE || 'PROJECT_UNDEFINED';
35
- const docRef = collection.doc(item.id);
36
- batch.set(docRef, JSON.parse(JSON.stringify(item)));
36
+
37
+ // Preparar dados para persistir (consolidado)
38
+ const itemData = JSON.parse(JSON.stringify(item));
37
39
  if (ttlExpirationIn && ttlUnit) {
38
- const ttl = getTtlTimestamp(ttlExpirationIn, ttlUnit);
39
- batch.set(docRef, {
40
- ttlExpirationAt: ttl,
41
- }, { merge: true });
40
+ itemData.ttlExpirationAt = getTtlTimestamp(ttlExpirationIn, ttlUnit);
42
41
  }
42
+ batch.set(collection.doc(item.id), itemData);
43
43
  }
44
+
44
45
  if (!environmentUtil.areWeTesting()) {
45
46
  yield batch.commit();
46
47
  yield this.revokeCache();
47
- }
48
- else {
48
+ } else {
49
49
  console.log('It was decreed that it is being executed try, no operation or effective transaction will be performed');
50
50
  }
51
+
51
52
  span.setAttributes({
52
53
  'firestore.operation.name': 'create',
53
54
  'firestore.operation.size': items.length,
54
55
  'firestore.collection.name': '<COLLECTION_REPLACE>',
55
- })
56
+ });
56
57
  return items;
57
-
58
58
  } catch (error) {
59
59
  span.setStatus({
60
60
  code: 2,
61
61
  message: error.message
62
- })
62
+ });
63
+ span.recordException(error);
64
+ throw error;
63
65
  }
64
- })
66
+ });
65
67
  });
66
- }
68
+ }
@@ -13,33 +13,65 @@ delete(ids) {
13
13
  }
14
14
  const db = global.instances.globalDbFile.FirestoreInstance.getInstance();
15
15
  const environmentUtil = global.instances.environmentUtil;
16
+ const archiveService = yield global.instances.archiveService;
16
17
  const batch = db.batch();
17
18
  const collection = db.collection('<COLLECTION_REPLACE>');
18
- ids.forEach(id => {
19
- const docRef = collection.doc(id);
20
- batch.delete(docRef);
21
- });
19
+
20
+ // Verificar quais documentos estão arquivados antes de deletar
21
+ const archivedDocs = [];
22
+ if (archiveService.isEnabled()) {
23
+ const docRefs = ids.map(id => collection.doc(id));
24
+ const docs = docRefs.length > 0 ? yield db.getAll(...docRefs) : [];
25
+ for (let i = 0; i < docs.length; i++) {
26
+ const doc = docs[i];
27
+ if (doc && doc.exists) {
28
+ const data = doc.data();
29
+ if (data && archiveService.isDocumentArchived(data) && data.fbArchivePath) {
30
+ archivedDocs.push({ id: ids[i], archivePath: data.fbArchivePath });
31
+ }
32
+ }
33
+ }
34
+ }
35
+
36
+ for (const id of ids) {
37
+ batch.delete(collection.doc(id));
38
+ }
39
+
22
40
  if (!environmentUtil.areWeTesting()) {
23
41
  yield batch.commit();
42
+
43
+ // Deletar arquivos do Storage para documentos arquivados
44
+ if (archiveService.isEnabled() && archivedDocs.length > 0) {
45
+ const deletePromises = archivedDocs.map(({ id: docId, archivePath }) =>
46
+ archiveService.deleteArchivedDocument('<COLLECTION_REPLACE>', docId, archivePath)
47
+ .catch(err => {
48
+ // Log erro mas não falha a operação principal
49
+ console.warn(`Failed to delete archived file for ${docId}:`, err);
50
+ })
51
+ );
52
+ yield Promise.all(deletePromises);
53
+ }
54
+
24
55
  yield this.revokeCache();
25
- }
26
- else {
56
+ } else {
27
57
  console.log('It was decreed that it is being executed try, no operation or effective transaction will be performed');
28
58
  }
59
+
29
60
  span.setAttributes({
30
61
  'firestore.operation.name': 'delete',
31
62
  'firestore.collection.name': '<COLLECTION_REPLACE>',
32
- 'firestore.collection.id': JSON.stringify(ids)
33
- })
63
+ 'firestore.collection.id': JSON.stringify(ids),
64
+ 'firestore.archive.deleted': archivedDocs.length
65
+ });
34
66
  return ids;
35
67
  } catch (error) {
36
68
  span.setStatus({
37
69
  code: 2,
38
70
  message: error.message
39
- })
40
- span.recordException(error)
41
- throw error
71
+ });
72
+ span.recordException(error);
73
+ throw error;
42
74
  }
43
- })
75
+ });
44
76
  });
45
- }
77
+ }
@@ -18,31 +18,47 @@ findAll(paging) {
18
18
  console.log('It was decreed that it is being executed try, no operation or effective transaction will be performed');
19
19
  return [];
20
20
  }
21
- let { documentRef } = yield queryCreatorConfig.buildPaging(collection, paging, { showCount: false });
21
+ const { documentRef } = yield queryCreatorConfig.buildPaging(collection, paging, { showCount: false });
22
22
  const snapshot = yield documentRef.get();
23
- let items = new Array;
23
+ let items = [];
24
24
  snapshot.forEach(doc => {
25
- const data = doc.data();
26
- items.push(Object.assign({}, data));
25
+ items.push({ ...doc.data(), id: doc.id });
27
26
  });
27
+
28
28
  // VERIFICAÇÃO DE ARQUIVAMENTO PARA MÚLTIPLOS DOCUMENTOS
29
- const archivedItems = items.filter(item => archiveService.isDocumentArchived(item));
30
- if (archivedItems.length > 0) {
31
- const recoveryPromises = archivedItems.map((item) => {
32
- return archiveService.getArchivedDocument('<COLLECTION_REPLACE>', item)
33
- .then(archivedData => archivedData ? { ...item, ...archivedData } : item);
34
- });
35
-
36
- const recoveredItems = yield Promise.all(recoveryPromises);
37
-
38
- // Substituir itens arquivados pelos recuperados
39
- items = items.map(item => {
29
+ if (archiveService.isEnabled()) {
30
+ const archivedIndexes = [];
31
+ items.forEach((item, index) => {
40
32
  if (archiveService.isDocumentArchived(item)) {
41
- return recoveredItems.find(recovered => recovered.id === item.id) || item;
33
+ archivedIndexes.push(index);
42
34
  }
43
- return item;
44
35
  });
36
+
37
+ if (archivedIndexes.length > 0) {
38
+ const recoveryPromises = archivedIndexes.map(index =>
39
+ archiveService.getArchivedDocument('<COLLECTION_REPLACE>', items[index])
40
+ .then(archivedData => ({ index, data: archivedData }))
41
+ .catch(error => {
42
+ console.warn('Failed to recover archived document data', {
43
+ collection: '<COLLECTION_REPLACE>',
44
+ id: items[index] && items[index].id,
45
+ error: error && error.message ? error.message : String(error)
46
+ });
47
+ return { index, data: null };
48
+ })
49
+ );
50
+
51
+ const recoveredResults = yield Promise.all(recoveryPromises);
52
+
53
+ // Substituir itens arquivados pelos recuperados (O(n) em vez de O(n²))
54
+ for (const { index, data } of recoveredResults) {
55
+ if (data) {
56
+ items[index] = { ...items[index], ...data };
57
+ }
58
+ }
59
+ }
45
60
  }
61
+
46
62
  yield cacheResolver.cacheResult(repositoryClassName, methodSignature, items, 'Any');
47
63
  const firestoreReadAuditResolver = global.instances.firestoreReadAuditResolver;
48
64
  yield firestoreReadAuditResolver.persistFirestoreRead({
@@ -55,16 +71,16 @@ findAll(paging) {
55
71
  'firestore.operation.name': 'findAll',
56
72
  'firestore.collection.name': '<COLLECTION_REPLACE>',
57
73
  'firestore.operation.size': items.length
58
- })
74
+ });
59
75
  return items;
60
76
  } catch (error) {
61
77
  span.setStatus({
62
78
  code: 2,
63
79
  message: error.message
64
- })
65
- span.recordException(error)
66
- throw error
80
+ });
81
+ span.recordException(error);
82
+ throw error;
67
83
  }
68
- })
84
+ });
69
85
  });
70
- }
86
+ }
@@ -18,17 +18,27 @@ findById(id) {
18
18
  return undefined;
19
19
  }
20
20
  const response = yield collection.doc(id).get();
21
- const item = response.data();
21
+
22
+ const data = response.data();
23
+ let item = data ? { ...data, id: response.id } : undefined;
24
+
22
25
  // VERIFICAÇÃO DE ARQUIVAMENTO
23
- if (item && archiveService.isDocumentArchived(item)) {
24
- const archivedData = yield archiveService.getArchivedDocument('<COLLECTION_REPLACE>', item);
25
- if (archivedData) {
26
- const completeData = { ...item, ...archivedData };
27
- yield cacheResolver.cacheResult(repositoryClassName, methodSignature, completeData, id);
26
+ if (archiveService.isEnabled() && item && archiveService.isDocumentArchived(item)) {
27
+ try {
28
+ const archivedData = yield archiveService.getArchivedDocument('<COLLECTION_REPLACE>', item);
29
+ if (archivedData) {
30
+ item = { ...item, ...archivedData };
31
+ }
32
+ } catch (error) {
33
+ console.warn('Failed to recover archived document data', {
34
+ collection: '<COLLECTION_REPLACE>',
35
+ id,
36
+ error: error && error.message ? error.message : String(error)
37
+ });
28
38
  }
29
- } else {
30
- yield cacheResolver.cacheResult(repositoryClassName, methodSignature, item, id);
31
39
  }
40
+
41
+ yield cacheResolver.cacheResult(repositoryClassName, methodSignature, item, id);
32
42
  const firestoreReadAuditResolver = global.instances.firestoreReadAuditResolver;
33
43
  yield firestoreReadAuditResolver.persistFirestoreRead({
34
44
  collection: '<COLLECTION_REPLACE>',
@@ -41,16 +51,16 @@ findById(id) {
41
51
  'firestore.operation.name': 'findById',
42
52
  'firestore.collection.name': '<COLLECTION_REPLACE>',
43
53
  'firestore.collection.id': id
44
- })
54
+ });
45
55
  return item;
46
56
  } catch (error) {
47
57
  span.setStatus({
48
58
  code: 2,
49
59
  message: error.message
50
- })
51
- span.recordException(error)
52
- throw error
60
+ });
61
+ span.recordException(error);
62
+ throw error;
53
63
  }
54
- })
64
+ });
55
65
  });
56
- }
66
+ }