@roit/roit-data-firestore 1.2.39 → 1.2.42

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.
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.BigQueryFirestoreReadAuditProvider = void 0;
13
4
  const PlatformTools_1 = require("../../platform/PlatformTools");
@@ -35,67 +26,63 @@ class BigQueryFirestoreReadAuditProvider {
35
26
  }
36
27
  }
37
28
  }
38
- createFirestoreAuditDatasetAndTableIfNecessary() {
39
- return __awaiter(this, void 0, void 0, function* () {
40
- const TWO_DAYS_IN_MS = '172800000';
41
- const schema = [
42
- { name: 'collection', type: 'STRING' },
43
- { name: 'service', type: 'STRING' },
44
- { name: 'projectId', type: 'STRING' },
45
- { name: 'env', type: 'STRING' },
46
- { name: 'repositoryClassName', type: 'STRING' },
47
- { name: 'functionSignature', type: 'STRING' },
48
- { name: 'params', type: 'STRING' },
49
- { name: 'queryResult', type: 'STRING' },
50
- { name: 'queryResultLength', type: 'INTEGER' },
51
- { name: 'insertAt', type: 'DATETIME' },
52
- ];
53
- const options = {
54
- schema,
55
- location: 'US',
56
- timePartitioning: {
57
- type: 'DAY',
58
- expirationMS: TWO_DAYS_IN_MS,
59
- field: 'insertAt'
60
- }
61
- };
62
- const handleBigQueryError = (e) => {
63
- if (!JSON.stringify(e.errors).includes('Exists')) {
64
- console.log('Error [createFirestoreAuditDatasetAndTableIfNecessary]', e);
65
- }
66
- };
67
- try {
68
- yield this.bigQuery.createDataset(dataset);
69
- }
70
- catch (e) {
71
- handleBigQueryError(e);
29
+ async createFirestoreAuditDatasetAndTableIfNecessary() {
30
+ const TWO_DAYS_IN_MS = '172800000';
31
+ const schema = [
32
+ { name: 'collection', type: 'STRING' },
33
+ { name: 'service', type: 'STRING' },
34
+ { name: 'projectId', type: 'STRING' },
35
+ { name: 'env', type: 'STRING' },
36
+ { name: 'repositoryClassName', type: 'STRING' },
37
+ { name: 'functionSignature', type: 'STRING' },
38
+ { name: 'params', type: 'STRING' },
39
+ { name: 'queryResult', type: 'STRING' },
40
+ { name: 'queryResultLength', type: 'INTEGER' },
41
+ { name: 'insertAt', type: 'DATETIME' },
42
+ ];
43
+ const options = {
44
+ schema,
45
+ location: 'US',
46
+ timePartitioning: {
47
+ type: 'DAY',
48
+ expirationMS: TWO_DAYS_IN_MS,
49
+ field: 'insertAt'
72
50
  }
73
- try {
74
- yield this.bigQuery
75
- .dataset(dataset)
76
- .createTable(table, options);
51
+ };
52
+ const handleBigQueryError = (e) => {
53
+ if (!JSON.stringify(e.errors).includes('Exists')) {
54
+ console.log('Error [createFirestoreAuditDatasetAndTableIfNecessary]', e);
77
55
  }
78
- catch (e) {
79
- handleBigQueryError(e);
80
- }
81
- });
56
+ };
57
+ try {
58
+ await this.bigQuery.createDataset(dataset);
59
+ }
60
+ catch (e) {
61
+ handleBigQueryError(e);
62
+ }
63
+ try {
64
+ await this.bigQuery
65
+ .dataset(dataset)
66
+ .createTable(table, options);
67
+ }
68
+ catch (e) {
69
+ handleBigQueryError(e);
70
+ }
82
71
  }
83
- persistFirestoreRead(props) {
84
- return __awaiter(this, void 0, void 0, function* () {
85
- try {
86
- if (!this.isTableCreated) {
87
- yield this.createFirestoreAuditDatasetAndTableIfNecessary();
88
- }
89
- this.isTableCreated = true;
90
- yield this.bigQuery
91
- .dataset(dataset)
92
- .table(table)
93
- .insert([props]);
94
- }
95
- catch (error) {
96
- console.log(JSON.stringify(error.errors));
72
+ async persistFirestoreRead(props) {
73
+ try {
74
+ if (!this.isTableCreated) {
75
+ await this.createFirestoreAuditDatasetAndTableIfNecessary();
97
76
  }
98
- });
77
+ this.isTableCreated = true;
78
+ await this.bigQuery
79
+ .dataset(dataset)
80
+ .table(table)
81
+ .insert([props]);
82
+ }
83
+ catch (error) {
84
+ console.log(JSON.stringify(error.errors));
85
+ }
99
86
  }
100
87
  loadBigQuery() {
101
88
  try {
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.PubSubFirestoreReadAuditProvider = void 0;
13
4
  const PlatformTools_1 = require("../../platform/PlatformTools");
@@ -27,23 +18,21 @@ class PubSubFirestoreReadAuditProvider {
27
18
  }
28
19
  this.topic = this.pubsub.topic(envTopic, { batching: { maxMessages: 1 } });
29
20
  }
30
- persistFirestoreRead(params) {
31
- return __awaiter(this, void 0, void 0, function* () {
32
- const limitBytes = 9900000;
33
- let buffer = Buffer.from(JSON.stringify(params));
34
- if (buffer.length > limitBytes) {
35
- params.queryResult = 'too-big-to-save';
36
- buffer = Buffer.from(JSON.stringify(params));
37
- }
38
- try {
39
- yield this.topic.publishMessage({
40
- data: buffer
41
- });
42
- }
43
- catch (error) {
44
- console.log(JSON.stringify(error));
45
- }
46
- });
21
+ async persistFirestoreRead(params) {
22
+ const limitBytes = 9900000;
23
+ let buffer = Buffer.from(JSON.stringify(params));
24
+ if (buffer.length > limitBytes) {
25
+ params.queryResult = 'too-big-to-save';
26
+ buffer = Buffer.from(JSON.stringify(params));
27
+ }
28
+ try {
29
+ await this.topic.publishMessage({
30
+ data: buffer
31
+ });
32
+ }
33
+ catch (error) {
34
+ console.log(JSON.stringify(error));
35
+ }
47
36
  }
48
37
  loadPubSub() {
49
38
  try {
@@ -6,4 +6,4 @@ var CacheProviders;
6
6
  CacheProviders["LOCAL"] = "LOCAL";
7
7
  CacheProviders["REDIS"] = "REDIS";
8
8
  CacheProviders["REDIS_ARCHIVE"] = "REDIS_ARCHIVE";
9
- })(CacheProviders = exports.CacheProviders || (exports.CacheProviders = {}));
9
+ })(CacheProviders || (exports.CacheProviders = CacheProviders = {}));
@@ -5,12 +5,15 @@ export declare class MQuery {
5
5
  operator: FirebaseFirestore.WhereFilterOp;
6
6
  value: any;
7
7
  }
8
+ export declare class MQueryOr {
9
+ or: Array<MQuery | MQuerySimple>;
10
+ }
8
11
  export declare class MQuerySimple {
9
12
  [key: string]: string | number;
10
13
  }
11
14
  export declare class Config {
12
15
  orderBy?: OrderBy;
13
- query?: Array<MQuery | MQuerySimple>;
16
+ query?: Array<MQuery | MQuerySimple | MQueryOr>;
14
17
  select?: Array<string>;
15
18
  paging?: Paging;
16
19
  }
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Options = exports.OrderBy = exports.Config = exports.MQuerySimple = exports.MQuery = void 0;
3
+ exports.Options = exports.OrderBy = exports.Config = exports.MQuerySimple = exports.MQueryOr = exports.MQuery = void 0;
4
4
  class MQuery {
5
5
  constructor() {
6
6
  this.operator = '==';
7
7
  }
8
8
  }
9
9
  exports.MQuery = MQuery;
10
+ class MQueryOr {
11
+ }
12
+ exports.MQueryOr = MQueryOr;
10
13
  class MQuerySimple {
11
14
  }
12
15
  exports.MQuerySimple = MQuerySimple;
@@ -1,15 +1,7 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.ManualQueryHelper = void 0;
4
+ const firestore_1 = require("@google-cloud/firestore");
13
5
  const FirestoreInstance_1 = require("../config/FirestoreInstance");
14
6
  const MQuery_1 = require("../model/MQuery");
15
7
  const QueryCreatorConfig_1 = require("./QueryCreatorConfig");
@@ -17,162 +9,175 @@ const QueryPredicateFunctionTransform_1 = require("./QueryPredicateFunctionTrans
17
9
  const ArchiveService_1 = require("../archive/ArchiveService");
18
10
  // import { ArchiveConfig } from "../config/ArchiveConfig";
19
11
  class ManualQueryHelper {
20
- static executeQueryManual(className, config, queryRef = false) {
21
- return __awaiter(this, void 0, void 0, function* () {
22
- const result = yield this.handleExecuteQueryManual(className, config, { showCount: false }, queryRef);
23
- if (queryRef) {
24
- return result;
25
- }
26
- const { data } = result;
27
- return data;
28
- });
12
+ static async executeQueryManual(className, config, queryRef = false) {
13
+ const result = await this.handleExecuteQueryManual(className, config, { showCount: false }, queryRef);
14
+ if (queryRef) {
15
+ return result;
16
+ }
17
+ const { data } = result;
18
+ return data;
29
19
  }
30
- static executeQueryManualPaginated(className, config) {
31
- return __awaiter(this, void 0, void 0, function* () {
32
- return this.handleExecuteQueryManual(className, config, { showCount: true });
33
- });
20
+ static async executeQueryManualPaginated(className, config) {
21
+ return this.handleExecuteQueryManual(className, config, { showCount: true });
34
22
  }
35
23
  /**
36
24
  * Processa documentos arquivados, recuperando seus dados completos do Cloud Storage
37
25
  */
38
- static processArchivedDocuments(docs, collectionName) {
39
- return __awaiter(this, void 0, void 0, function* () {
40
- const archiveService = yield ArchiveService_1.ArchiveService.getInstance();
41
- // Verifica se o arquivamento está habilitado
42
- if (!archiveService.isEnabled()) {
43
- return docs;
26
+ static async processArchivedDocuments(docs, collectionName) {
27
+ const archiveService = await ArchiveService_1.ArchiveService.getInstance();
28
+ // Verifica se o arquivamento está habilitado
29
+ if (!archiveService.isEnabled()) {
30
+ return docs;
31
+ }
32
+ const docsMap = new Map();
33
+ const recoveryPromises = docs.map(async (doc) => {
34
+ docsMap.set(doc.id, doc);
35
+ if (!archiveService.isDocumentArchived(doc)) {
36
+ return null;
44
37
  }
45
- const docsMap = new Map();
46
- const recoveryPromises = docs.map((doc) => __awaiter(this, void 0, void 0, function* () {
47
- docsMap.set(doc.id, doc);
48
- if (!archiveService.isDocumentArchived(doc)) {
49
- return null;
50
- }
51
- try {
52
- // O ArchiveService agora gerencia o cache internamente baseado na configuração
53
- const archivedData = yield archiveService.getArchivedDocument(collectionName, doc);
54
- if (archivedData) {
55
- // Remove o flag de arquivamento e mescla os dados
56
- // const { fbArchivedAt, ...archivedDataWithoutFlag } = archivedData;
57
- return Object.assign(Object.assign({}, doc), archivedData);
58
- }
59
- return doc;
60
- }
61
- catch (error) {
62
- console.warn(`Erro ao recuperar documento arquivado ${doc.id}:`, error);
63
- return doc;
64
- }
65
- }));
66
- const recoveredDocs = yield Promise.all(recoveryPromises);
67
- recoveredDocs.forEach(doc => {
68
- if (doc) {
69
- docsMap.set(doc.id, doc);
38
+ try {
39
+ // O ArchiveService agora gerencia o cache internamente baseado na configuração
40
+ const archivedData = await archiveService.getArchivedDocument(collectionName, doc);
41
+ if (archivedData) {
42
+ // Remove o flag de arquivamento e mescla os dados
43
+ // const { fbArchivedAt, ...archivedDataWithoutFlag } = archivedData;
44
+ return { ...doc, ...archivedData };
70
45
  }
71
- });
72
- return Array.from(docsMap.values());
46
+ return doc;
47
+ }
48
+ catch (error) {
49
+ console.warn(`Erro ao recuperar documento arquivado ${doc.id}:`, error);
50
+ return doc;
51
+ }
73
52
  });
53
+ const recoveredDocs = await Promise.all(recoveryPromises);
54
+ recoveredDocs.forEach(doc => {
55
+ if (doc) {
56
+ docsMap.set(doc.id, doc);
57
+ }
58
+ });
59
+ return Array.from(docsMap.values());
74
60
  }
75
- static handleExecuteQueryManual(className, config, options, queryRef = false) {
76
- return __awaiter(this, void 0, void 0, function* () {
77
- return yield global.instances.startTracer('firestore.query', (span) => __awaiter(this, void 0, void 0, function* () {
78
- var _a;
79
- try {
80
- const cacheResolver = global.instances.cacheResolver;
81
- const result = yield cacheResolver.getCacheResult(className, 'any', JSON.stringify(Object.assign(Object.assign({}, config), { options })));
82
- if (result) {
83
- return result;
84
- }
85
- const repositoryOptions = QueryPredicateFunctionTransform_1.QueryPredicateFunctionTransform.classConfig.get(className);
86
- const traceQuery = [];
87
- const pushTraceQuery = (query) => {
88
- traceQuery.push({ field: query.field, operator: query.operator, value: '?' });
89
- };
90
- if (repositoryOptions) {
91
- const firestoreInstance = FirestoreInstance_1.FirestoreInstance.getInstance();
92
- const collection = firestoreInstance.collection(repositoryOptions.collection);
93
- let queryList;
94
- let queryExecute = collection;
95
- if ((config === null || config === void 0 ? void 0 : config.query) && ((_a = config === null || config === void 0 ? void 0 : config.query) === null || _a === void 0 ? void 0 : _a.length) > 0) {
96
- queryList = config.query.map(query => {
97
- if (Object.keys(query).length === 1) {
98
- return this.convertToMQuery(query);
61
+ static async handleExecuteQueryManual(className, config, options, queryRef = false) {
62
+ return await global.instances.startTracer('firestore.query', async (span) => {
63
+ try {
64
+ const cacheResolver = global.instances.cacheResolver;
65
+ const result = await cacheResolver.getCacheResult(className, 'any', JSON.stringify({ ...config, options }));
66
+ if (result) {
67
+ return result;
68
+ }
69
+ const repositoryOptions = QueryPredicateFunctionTransform_1.QueryPredicateFunctionTransform.classConfig.get(className);
70
+ const traceQuery = [];
71
+ const pushTraceQuery = (query) => {
72
+ traceQuery.push({ field: query.field, operator: query.operator, value: '?' });
73
+ };
74
+ if (repositoryOptions) {
75
+ const firestoreInstance = FirestoreInstance_1.FirestoreInstance.getInstance();
76
+ const collection = firestoreInstance.collection(repositoryOptions.collection);
77
+ let queryList;
78
+ let queryExecute = collection;
79
+ if (config?.query && config?.query?.length > 0) {
80
+ queryList = config.query.map(query => {
81
+ return query;
82
+ });
83
+ for (const queryItem of queryList) {
84
+ if (queryItem && typeof queryItem === 'object' && 'or' in queryItem && Array.isArray(queryItem.or)) {
85
+ const orFilters = queryItem.or.map((orCondition) => {
86
+ const mQuery = Object.keys(orCondition).length === 1
87
+ ? this.convertToMQuery(orCondition)
88
+ : orCondition;
89
+ pushTraceQuery(mQuery);
90
+ return firestore_1.Filter.where(mQuery.field, mQuery.operator, mQuery.value);
91
+ });
92
+ if (orFilters.length > 0) {
93
+ const orFilter = firestore_1.Filter.or(...orFilters);
94
+ if (queryExecute === collection) {
95
+ queryExecute = collection.where(orFilter);
96
+ }
97
+ else {
98
+ queryExecute = queryExecute.where(orFilter);
99
+ }
99
100
  }
100
- return query;
101
- });
102
- const queryInit = queryList[0];
103
- queryExecute = collection.where(queryInit.field, queryInit.operator, queryInit.value);
104
- pushTraceQuery(queryInit);
105
- queryList.shift();
106
- queryList.forEach(que => {
107
- queryExecute = queryExecute.where(que.field, que.operator, que.value);
108
- pushTraceQuery(que);
109
- });
110
- }
111
- if (config && (config === null || config === void 0 ? void 0 : config.select)) {
112
- if (queryExecute) {
113
- queryExecute = queryExecute.select(...config.select);
114
101
  }
115
102
  else {
116
- queryExecute = collection.select(...config.select);
103
+ const mQuery = Object.keys(queryItem).length === 1
104
+ ? this.convertToMQuery(queryItem)
105
+ : queryItem;
106
+ if (queryExecute === collection) {
107
+ queryExecute = collection.where(mQuery.field, mQuery.operator, mQuery.value);
108
+ }
109
+ else {
110
+ queryExecute = queryExecute.where(mQuery.field, mQuery.operator, mQuery.value);
111
+ }
112
+ pushTraceQuery(mQuery);
117
113
  }
118
114
  }
119
- if (config && (config === null || config === void 0 ? void 0 : config.orderBy)) {
120
- if (queryExecute) {
121
- queryExecute = queryExecute.orderBy(config.orderBy.field, config.orderBy.direction);
122
- }
123
- else {
124
- queryExecute = collection.orderBy(config.orderBy.field, config.orderBy.direction);
125
- }
115
+ }
116
+ if (config && config?.select) {
117
+ if (queryExecute) {
118
+ queryExecute = queryExecute.select(...config.select);
126
119
  }
120
+ else {
121
+ queryExecute = collection.select(...config.select);
122
+ }
123
+ }
124
+ if (config && config?.orderBy) {
127
125
  if (queryExecute) {
128
- let count = null;
129
- if (config === null || config === void 0 ? void 0 : config.paging) {
130
- const { documentRef, totalItens } = yield new QueryCreatorConfig_1.QueryCreatorConfig().buildPaging(queryExecute, config.paging, options);
131
- queryExecute = documentRef;
132
- count = totalItens;
133
- }
134
- if (queryRef) {
135
- return queryExecute;
136
- }
137
- const snapshot = yield queryExecute.get();
138
- const data = this.getData(snapshot);
139
- // PROCESSAR DOCUMENTOS ARQUIVADOS
140
- const processedData = yield this.processArchivedDocuments(data, repositoryOptions.collection);
141
- yield cacheResolver.cacheResult(className, 'any', { data: processedData, count }, JSON.stringify(Object.assign(Object.assign({}, config), { options })));
142
- const firestoreReadAuditResolver = global.instances.firestoreReadAuditResolver;
143
- yield firestoreReadAuditResolver.persistFirestoreRead({
144
- collection: repositoryOptions.collection,
145
- repositoryClassName: className,
146
- functionSignature: 'manual-query',
147
- params: JSON.stringify(config),
148
- queryResult: data
149
- });
150
- span.setAttributes({
151
- 'firestore.operation.name': 'query',
152
- 'firestore.operation.query': JSON.stringify(traceQuery),
153
- 'firestore.collection.name': repositoryOptions.collection,
154
- 'firestore.operation.size': data.length,
155
- });
156
- return {
157
- data: processedData,
158
- totalItens: count
159
- };
126
+ queryExecute = queryExecute.orderBy(config.orderBy.field, config.orderBy.direction);
127
+ }
128
+ else {
129
+ queryExecute = collection.orderBy(config.orderBy.field, config.orderBy.direction);
160
130
  }
161
131
  }
162
- return {
163
- data: [],
164
- totalItens: 0
165
- };
166
- }
167
- catch (error) {
168
- span.setStatus({
169
- code: 2,
170
- message: error.message
171
- });
172
- span.recordException(error);
173
- throw error;
132
+ if (queryExecute) {
133
+ let count = null;
134
+ if (config?.paging) {
135
+ const { documentRef, totalItens } = await new QueryCreatorConfig_1.QueryCreatorConfig().buildPaging(queryExecute, config.paging, options);
136
+ queryExecute = documentRef;
137
+ count = totalItens;
138
+ }
139
+ if (queryRef) {
140
+ return queryExecute;
141
+ }
142
+ const snapshot = await queryExecute.get();
143
+ const data = this.getData(snapshot);
144
+ // PROCESSAR DOCUMENTOS ARQUIVADOS
145
+ const processedData = await this.processArchivedDocuments(data, repositoryOptions.collection);
146
+ await cacheResolver.cacheResult(className, 'any', { data: processedData, count }, JSON.stringify({ ...config, options }));
147
+ const firestoreReadAuditResolver = global.instances.firestoreReadAuditResolver;
148
+ await firestoreReadAuditResolver.persistFirestoreRead({
149
+ collection: repositoryOptions.collection,
150
+ repositoryClassName: className,
151
+ functionSignature: 'manual-query',
152
+ params: JSON.stringify(config),
153
+ queryResult: data
154
+ });
155
+ span.setAttributes({
156
+ 'firestore.operation.name': 'query',
157
+ 'firestore.operation.query': JSON.stringify(traceQuery),
158
+ 'firestore.collection.name': repositoryOptions.collection,
159
+ 'firestore.operation.size': data.length,
160
+ });
161
+ return {
162
+ data: processedData,
163
+ totalItens: count
164
+ };
165
+ }
174
166
  }
175
- }));
167
+ return {
168
+ data: [],
169
+ totalItens: 0
170
+ };
171
+ }
172
+ catch (error) {
173
+ console.error(error);
174
+ span.setStatus({
175
+ code: 2,
176
+ message: error.message
177
+ });
178
+ span.recordException(error);
179
+ throw error;
180
+ }
176
181
  });
177
182
  }
178
183
  static convertToMQuery(query) {
@@ -188,7 +193,7 @@ class ManualQueryHelper {
188
193
  let items = [];
189
194
  try {
190
195
  snapshot.forEach((doc) => {
191
- let element = Object.assign({}, doc.data());
196
+ let element = { ...doc.data() };
192
197
  element.id = doc.id;
193
198
  items.push(element);
194
199
  });