@runnerpro/backend 1.10.17 → 1.11.1

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.
@@ -54,10 +54,10 @@ const conversationRoute = (_a) => {
54
54
  router.post('/conversation/read', (req, res, next) => readMessage(req, res, params).catch((error) => (0, index_1.err)(req, res, error, next)));
55
55
  };
56
56
  exports.conversationRoute = conversationRoute;
57
- const getConversation = (req, res, { query, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
57
+ const getConversation = (req, res, { isClient }) => __awaiter(void 0, void 0, void 0, function* () {
58
58
  const idCliente = isClient ? req.session.userid : req.query.id;
59
- const [header] = yield query('SELECT [NAME], [PREFERRED LANGUAGE] FROM [CLIENTE] WHERE [ID] = ?', [idCliente]);
60
- let messages = yield query(`SELECT [CHAT MESSAGE].*,
59
+ const [header] = yield (0, index_1.query)('SELECT [NAME], [PREFERRED LANGUAGE] FROM [CLIENTE] WHERE [ID] = ?', [idCliente]);
60
+ let messages = yield (0, index_1.query)(`SELECT [CHAT MESSAGE].*,
61
61
  [WORKOUT].[DATE] AS [WORKOUT DATE], [WORKOUT].[TYPE] AS [WORKOUT TYPE], [WORKOUT].[TITLE] AS [WORKOUT TITLE], [WORKOUT].[TITLE PREFERRED LANGUAGE] AS [WORKOUT TITLE],
62
62
  [WORKOUT].[DURATION] AS [WORKOUT DURATION], [WORKOUT].[DISTANCE] AS [WORKOUT DISTANCE],
63
63
  [WORKOUT].[PHOTO URL SHARE] AS [WORKOUT PHOTO URL SHARE], [WORKOUT].[PHOTO URL] AS [WORKOUT PHOTO URL], [FEELINGS], [FEELINGS DESCRIPTION]
@@ -89,61 +89,64 @@ const getConversation = (req, res, { query, isClient }) => __awaiter(void 0, voi
89
89
  res.send({ header, messages });
90
90
  // Solo se marca como leído si es el cliente | El entrenador tiene el botón o enviar mensaje para marcar como leído
91
91
  if (isClient)
92
- yield markReadMessage({ isClient, query, idCliente });
92
+ yield markReadMessage({ isClient, idCliente });
93
93
  });
94
- const deleteConversationMessage = (req, res, { query, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
94
+ const deleteConversationMessage = (req, res, { isClient }) => __awaiter(void 0, void 0, void 0, function* () {
95
95
  const { id } = req.params;
96
- const [message] = yield query('SELECT [ID] FROM [CHAT MESSAGE] WHERE [ID] = ? AND (? = FALSE OR [ID CLIENTE] = ?)', [
97
- id,
98
- isClient,
99
- req.session.userid,
100
- ]);
101
- if (!message)
102
- return res.status(404).send('Not found');
103
- if (!(yield canEditOrDeleteMessage({ idMessage: id, isClient })))
96
+ if (!(yield canEditOrDeleteMessage({ idMessage: id, isClient, userid: req.session.userid })))
104
97
  return res.send({ status: 'ok' });
105
98
  if (isClient)
106
- yield query('UPDATE [CHAT MESSAGE] SET [ELIMINADO] = TRUE WHERE [ID] = ?', [id]);
99
+ yield (0, index_1.query)('UPDATE [CHAT MESSAGE] SET [ELIMINADO] = TRUE WHERE [ID] = ?', [id]);
107
100
  else
108
- yield query('DELETE FROM [CHAT MESSAGE] WHERE [ID] = ?', [id]);
101
+ yield (0, index_1.query)('DELETE FROM [CHAT MESSAGE] WHERE [ID] = ?', [id]);
109
102
  res.send({ status: 'ok' });
110
103
  });
111
104
  // TODO: Si el cliente elimina o edita y ya se ha sugerido el mensaje
112
105
  // - Se elimina la sugerencia
113
106
  // - Se elimina el mensaje programado
114
107
  // TODO: Comprobar que el cliente/entrenador puede editar/eliminar el mensaje (no se haya contestado ya y que no haya pasado el tiempo de cortesía)
115
- const editConversationMessage = (req, res, { query, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
108
+ const editConversationMessage = (req, res, { isClient }) => __awaiter(void 0, void 0, void 0, function* () {
116
109
  const { id } = req.params;
117
110
  const { text } = req.body;
118
- const [message] = yield query('SELECT [ID], [ID CLIENTE] FROM [CHAT MESSAGE] WHERE [ID] = ? AND (? = FALSE OR [ID CLIENTE] = ?)', [
119
- id,
120
- isClient,
121
- req.session.userid,
122
- ]);
123
- if (!message)
124
- return res.status(404).send('Not found');
125
- if (!(yield canEditOrDeleteMessage({ idMessage: id, isClient })))
111
+ if (!(yield canEditOrDeleteMessage({ idMessage: id, isClient, userid: req.session.userid })))
126
112
  return res.send({ status: 'ok' });
113
+ const [message] = yield (0, index_1.query)('SELECT [ID CLIENTE] FROM [CHAT MESSAGE] WHERE [ID] = ?', [id]);
127
114
  // Devuelve el texto en el otro idioma si el cliente no habla español y el idioma del cliente
128
115
  const { textSpanish, textPreferredLanguage } = yield getPreferredLanguageForChat({
129
116
  text,
130
117
  idCliente: message.idCliente,
131
118
  isClient,
132
- query,
133
119
  });
134
- yield query('UPDATE [CHAT MESSAGE] SET [TEXT] = ?, [TEXT PREFERRED LANGUAGE] = ?, [EDITADO] = TRUE WHERE [ID] = ?', [
120
+ yield (0, index_1.query)('UPDATE [CHAT MESSAGE] SET [TEXT] = ?, [TEXT PREFERRED LANGUAGE] = ?, [EDITADO] = TRUE WHERE [ID] = ?', [
135
121
  textSpanish,
136
122
  textPreferredLanguage,
137
123
  id,
138
124
  ]);
139
125
  res.send({ status: 'ok' });
140
126
  });
141
- const canEditOrDeleteMessage = ({ idMessage, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
127
+ const canEditOrDeleteMessage = ({ idMessage, isClient, userid }) => __awaiter(void 0, void 0, void 0, function* () {
128
+ const [message] = yield (0, index_1.query)('SELECT [ID], "ID CLIENTE", "ID SENDER" FROM [CHAT MESSAGE] WHERE [ID] = ? AND (? = FALSE OR [ID CLIENTE] = ?)', [
129
+ idMessage,
130
+ isClient,
131
+ userid,
132
+ ]);
133
+ if (!message)
134
+ return false;
135
+ // Si es servidor del cliente, pero el mensaje no es del cliente || Si es el servidor del entrenador, pero el mensaje es del cliente
136
+ const messageOwnerCliente = message.idCliente === message.idSender;
137
+ if ((isClient && !messageOwnerCliente) || (!isClient && messageOwnerCliente))
138
+ return false;
139
+ // Si es servidor del cliente, pero el último mensaje es del entrenador || Si es el servidor del entrenador, pero el último mensaje es del cliente
140
+ const [lastMessage] = yield (0, index_1.query)('SELECT "ID CLIENTE", "ID SENDER" FROM [CHAT MESSAGE] WHERE [ID CLIENTE] = ? ORDER BY [ID] DESC LIMIT 1', [
141
+ message.idCliente,
142
+ ]);
143
+ const lastMessageOfClient = lastMessage.idCliente === lastMessage.idSender;
144
+ if ((isClient && !lastMessageOfClient) || (!isClient && lastMessageOfClient))
145
+ return false;
142
146
  return true;
143
147
  });
144
- const getConversationImage = (req, res, { query, bucket, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
148
+ const getConversationImage = (req, res, { bucket, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
145
149
  const result = yield getChatFile({
146
- query,
147
150
  bucket,
148
151
  id: req.params.id,
149
152
  isClient,
@@ -158,9 +161,8 @@ const getConversationImage = (req, res, { query, bucket, isClient }) => __awaite
158
161
  mimetype: result.message.mimetype,
159
162
  });
160
163
  });
161
- const getConversationFile = (req, res, { query, bucket, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
164
+ const getConversationFile = (req, res, { bucket, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
162
165
  const result = yield getChatFile({
163
- query,
164
166
  bucket,
165
167
  id: req.params.id,
166
168
  isClient,
@@ -175,9 +177,9 @@ const getConversationFile = (req, res, { query, bucket, isClient }) => __awaiter
175
177
  });
176
178
  res.end(result.file);
177
179
  });
178
- const getChatFile = ({ query, bucket, id, isClient, userid }) => __awaiter(void 0, void 0, void 0, function* () {
180
+ const getChatFile = ({ bucket, id, isClient, userid }) => __awaiter(void 0, void 0, void 0, function* () {
179
181
  const idBBDD = id.includes('-original') ? id.replace('-original', '') : id;
180
- const [message] = yield query('SELECT [MIMETYPE], [TEXT] FROM [CHAT MESSAGE] WHERE [ID] = ? AND (? = FALSE OR [ID CLIENTE] = ?)', [
182
+ const [message] = yield (0, index_1.query)('SELECT [MIMETYPE], [TEXT] FROM [CHAT MESSAGE] WHERE [ID] = ? AND (? = FALSE OR [ID CLIENTE] = ?)', [
181
183
  idBBDD,
182
184
  isClient,
183
185
  userid,
@@ -192,21 +194,20 @@ const getChatFile = ({ query, bucket, id, isClient, userid }) => __awaiter(void
192
194
  return null;
193
195
  }
194
196
  });
195
- const sendMessage = (req, res, { sendNotification, firebaseMessaging, query, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
197
+ const sendMessage = (req, res, { sendNotification, firebaseMessaging, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
196
198
  const { text, replyMessageId } = req.body;
197
199
  const { userid } = req.session;
198
200
  const idCliente = isClient ? req.session.userid : req.body.idCliente;
199
201
  // Si es entrenador, se marca leído cuando se envía un mensaje
200
202
  if (!isClient)
201
- yield markReadMessage({ isClient, query, idCliente });
203
+ yield markReadMessage({ isClient, idCliente });
202
204
  // Devuelve el texto en el otro idioma si el cliente no habla español y el idioma del cliente
203
205
  const { textSpanish, textPreferredLanguage, preferredLanguage } = yield getPreferredLanguageForChat({
204
206
  text,
205
207
  idCliente,
206
208
  isClient,
207
- query,
208
209
  });
209
- const [message] = yield query('INSERT INTO [CHAT MESSAGE] ([ID CLIENTE], [ID SENDER], [TEXT], [TEXT PREFERRED LANGUAGE], [PREFERRED LANGUAGE], [REPLY MESSAGE ID], [TYPE]) VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING [ID]', [isClient ? userid : idCliente, userid, textSpanish, textPreferredLanguage, preferredLanguage, replyMessageId, 1]);
210
+ const [message] = yield (0, index_1.query)('INSERT INTO [CHAT MESSAGE] ([ID CLIENTE], [ID SENDER], [TEXT], [TEXT PREFERRED LANGUAGE], [PREFERRED LANGUAGE], [REPLY MESSAGE ID], [TYPE]) VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING [ID]', [isClient ? userid : idCliente, userid, textSpanish, textPreferredLanguage, preferredLanguage, replyMessageId, 1]);
210
211
  res.send({ idMessage: message.id });
211
212
  if (!isClient) {
212
213
  sendNotification({
@@ -216,7 +217,7 @@ const sendMessage = (req, res, { sendNotification, firebaseMessaging, query, isC
216
217
  screen: common_1.NOTIFICATION_SCREEN_TYPES.CHAT,
217
218
  });
218
219
  // Enviar a N8N lo que ha escrito el entrenador
219
- const [lastSuggestionMsg] = yield query('SELECT [ID] FROM [CHAT MESSAGE] WHERE [ID CLIENTE] = ? AND [SUGGESTION TEXT] IS NOT NULL ORDER BY [ID] DESC LIMIT 1', [idCliente]);
220
+ const [lastSuggestionMsg] = yield (0, index_1.query)('SELECT [ID] FROM [CHAT MESSAGE] WHERE [ID CLIENTE] = ? AND [SUGGESTION TEXT] IS NOT NULL ORDER BY [ID] DESC LIMIT 1', [idCliente]);
220
221
  if (lastSuggestionMsg) {
221
222
  yield axios_1.default.put(`${process.env.N8N_URL}/edc2484f-7010-44c1-8c1d-82924496a2eb`, {
222
223
  id: lastSuggestionMsg.id,
@@ -225,8 +226,8 @@ const sendMessage = (req, res, { sendNotification, firebaseMessaging, query, isC
225
226
  }
226
227
  }
227
228
  });
228
- const getPreferredLanguageForChat = ({ text, idCliente, isClient, query }) => __awaiter(void 0, void 0, void 0, function* () {
229
- const [{ preferredLanguage }] = yield query('SELECT [PREFERRED LANGUAGE] FROM [CLIENTE] WHERE [ID] = ?', [idCliente]);
229
+ const getPreferredLanguageForChat = ({ text, idCliente, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
230
+ const [{ preferredLanguage }] = yield (0, index_1.query)('SELECT [PREFERRED LANGUAGE] FROM [CLIENTE] WHERE [ID] = ?', [idCliente]);
230
231
  if (preferredLanguage === common_1.LANGUAGES.ES)
231
232
  return {
232
233
  textSpanish: text,
@@ -243,10 +244,10 @@ const getPreferredLanguageForChat = ({ text, idCliente, isClient, query }) => __
243
244
  preferredLanguage: preferredLanguage,
244
245
  };
245
246
  });
246
- const sendFile = (req, res, { sendNotification, firebaseMessaging, query, isClient, bucket }) => __awaiter(void 0, void 0, void 0, function* () {
247
+ const sendFile = (req, res, { sendNotification, firebaseMessaging, isClient, bucket }) => __awaiter(void 0, void 0, void 0, function* () {
247
248
  const { idCliente, type, duration } = req.body;
248
249
  const { userid } = req.session;
249
- const [{ id: idFile }] = yield query('INSERT INTO [CHAT MESSAGE] ([ID CLIENTE], [ID SENDER], [TEXT], [MIMETYPE], [DURATION], [TYPE]) VALUES (?, ?, ?, ?, ?, ?) RETURNING [ID]', [isClient ? userid : idCliente, userid, req.file.originalname, req.file.mimetype, duration || null, type || 2]);
250
+ const [{ id: idFile }] = yield (0, index_1.query)('INSERT INTO [CHAT MESSAGE] ([ID CLIENTE], [ID SENDER], [TEXT], [MIMETYPE], [DURATION], [TYPE]) VALUES (?, ?, ?, ?, ?, ?) RETURNING [ID]', [isClient ? userid : idCliente, userid, req.file.originalname, req.file.mimetype, duration || null, type || 2]);
250
251
  const filePath = path_1.default.join('./uploads', req.file.filename);
251
252
  const fileData = fs_1.default.readFileSync(filePath);
252
253
  const files = [];
@@ -269,7 +270,7 @@ const sendFile = (req, res, { sendNotification, firebaseMessaging, query, isClie
269
270
  }
270
271
  fs_1.default.unlinkSync(filePath);
271
272
  if (!isClient) {
272
- const [cliente] = yield query('SELECT [PREFERRED LANGUAGE] FROM [CLIENTE] WHERE [ID] = ?', [idCliente]);
273
+ const [cliente] = yield (0, index_1.query)('SELECT [PREFERRED LANGUAGE] FROM [CLIENTE] WHERE [ID] = ?', [idCliente]);
273
274
  sendNotification({
274
275
  firebaseMessaging,
275
276
  idCliente,
@@ -279,14 +280,14 @@ const sendFile = (req, res, { sendNotification, firebaseMessaging, query, isClie
279
280
  }
280
281
  res.send({ idFile });
281
282
  });
282
- const readMessage = (req, res, { query, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
283
+ const readMessage = (req, res, { isClient }) => __awaiter(void 0, void 0, void 0, function* () {
283
284
  const { idCliente } = req.body;
284
- yield markReadMessage({ isClient, query, idCliente });
285
+ yield markReadMessage({ isClient, idCliente });
285
286
  res.send({ status: 'ok' });
286
287
  });
287
- const markReadMessage = ({ isClient, query, idCliente }) => __awaiter(void 0, void 0, void 0, function* () {
288
+ const markReadMessage = ({ isClient, idCliente }) => __awaiter(void 0, void 0, void 0, function* () {
288
289
  const conditionSender = isClient ? ' AND [ID SENDER] != ?' : ' AND ([ID SENDER] = ? OR [ID SENDER] IS NULL)';
289
- yield query('UPDATE [CHAT MESSAGE] SET [READ] = TRUE WHERE [ID CLIENTE] = ? AND [READ] = FALSE ' + conditionSender, [idCliente, idCliente]);
290
+ yield (0, index_1.query)('UPDATE [CHAT MESSAGE] SET [READ] = TRUE WHERE [ID CLIENTE] = ? AND [READ] = FALSE ' + conditionSender, [idCliente, idCliente]);
290
291
  if (!isClient)
291
292
  yield (0, saveResponseTime_1.saveResponseTime)(idCliente);
292
293
  });
@@ -140,18 +140,72 @@ const writeSheet = ({ sheetInstance, sheetName, sheetPage, cellValue, cellPositi
140
140
  }
141
141
  });
142
142
  exports.writeSheet = writeSheet;
143
+ // Función para detectar si un valor es una fecha en formato DD/MM/YYYY o YYYY/MM/DD
144
+ const isDateString = (value) => {
145
+ if (typeof value !== 'string')
146
+ return false;
147
+ // Patrones para DD/MM/YYYY y YYYY/MM/DD
148
+ const datePatterns = [
149
+ /^\d{1,2}\/\d{1,2}\/\d{4}$/, // DD/MM/YYYY o D/M/YYYY
150
+ /^\d{4}\/\d{1,2}\/\d{1,2}$/, // YYYY/MM/DD o YYYY/M/D
151
+ ];
152
+ return datePatterns.some((pattern) => pattern.test(value));
153
+ };
154
+ // Función para convertir fecha a formato que Google Sheets reconozca
155
+ const formatDateForGoogleSheets = (dateString) => {
156
+ if (!isDateString(dateString))
157
+ return dateString;
158
+ try {
159
+ let day, month, year;
160
+ // Detectar si es formato DD/MM/YYYY o YYYY/MM/DD
161
+ const parts = dateString.split('/');
162
+ if (parts[0].length === 4) {
163
+ // Formato YYYY/MM/DD
164
+ year = parseInt(parts[0]);
165
+ month = parseInt(parts[1]);
166
+ day = parseInt(parts[2]);
167
+ }
168
+ else {
169
+ // Formato DD/MM/YYYY
170
+ day = parseInt(parts[0]);
171
+ month = parseInt(parts[1]);
172
+ year = parseInt(parts[2]);
173
+ }
174
+ // Validar que sea una fecha válida
175
+ const date = new Date(year, month - 1, day);
176
+ if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
177
+ return dateString; // Si no es válida, devolver el valor original
178
+ }
179
+ // Formatear para Google Sheets (MM/DD/YYYY es el formato más compatible)
180
+ return `${month.toString().padStart(2, '0')}/${day.toString().padStart(2, '0')}/${year}`;
181
+ }
182
+ catch (error) {
183
+ return dateString; // Si hay error, devolver el valor original
184
+ }
185
+ };
186
+ // Función para procesar array de valores y formatear fechas
187
+ const processValuesForGoogleSheets = (values) => {
188
+ return values.map((value) => {
189
+ if (typeof value === 'string' && isDateString(value)) {
190
+ return formatDateForGoogleSheets(value);
191
+ }
192
+ return value;
193
+ });
194
+ };
143
195
  const appendSheet = ({ sheetInstance, sheetName, sheetPage, cellValues, cellPositionAppend }) => __awaiter(void 0, void 0, void 0, function* () {
144
196
  try {
145
197
  if (!sheetInstance)
146
198
  sheetInstance = yield getSheetInstance();
199
+ // Procesar los valores para formatear fechas automáticamente
200
+ const processedValues = processValuesForGoogleSheets(cellValues);
147
201
  yield sheetInstance.spreadsheets.values.append({
148
202
  auth: googleAuth,
149
203
  spreadsheetId: googleSheeIds[sheetName],
150
204
  range: `${sheetPage}!${cellPositionAppend}`,
151
- valueInputOption: 'RAW',
205
+ valueInputOption: 'USER_ENTERED', // Cambiado de 'RAW' a 'USER_ENTERED' para que Google Sheets interprete fechas
152
206
  insertDataOption: 'INSERT_ROWS',
153
207
  resource: {
154
- values: [cellValues],
208
+ values: [processedValues],
155
209
  },
156
210
  });
157
211
  }
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../../../../src/chat/api/conversation.ts"],"names":[],"mappings":"AAYA,QAAA,MAAM,iBAAiB,0BAA2B,GAAG,SAoBpD,CAAC;AAwSF,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../../../../src/chat/api/conversation.ts"],"names":[],"mappings":"AAYA,QAAA,MAAM,iBAAiB,0BAA2B,GAAG,SAoBpD,CAAC;AA2SF,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/googleSheet/index.ts"],"names":[],"mappings":"AA6BA,iBAAe,SAAS,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,eAAe,EAAE;;;;;;CAAA,gBAgBnG;AAED,QAAA,MAAM,eAAe;;;;;;;;;0BAiCpB,CAAC;AAGF,iBAAS,SAAS,CAAC,MAAM,KAAA,UAaxB;AAED,iBAAS,iBAAiB,CAAC,MAAM,KAAA,UAOhC;AAED,QAAA,MAAM,UAAU;;;;;;mBAkBf,CAAC;AAEF,QAAA,MAAM,WAAW;;;;;;mBAmBhB,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/googleSheet/index.ts"],"names":[],"mappings":"AA6BA,iBAAe,SAAS,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,eAAe,EAAE;;;;;;CAAA,gBAgBnG;AAED,QAAA,MAAM,eAAe;;;;;;;;;0BAiCpB,CAAC;AAGF,iBAAS,SAAS,CAAC,MAAM,KAAA,UAaxB;AAED,iBAAS,iBAAiB,CAAC,MAAM,KAAA,UAOhC;AAED,QAAA,MAAM,UAAU;;;;;;mBAkBf,CAAC;AA4DF,QAAA,MAAM,WAAW;;;;;;mBAsBhB,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runnerpro/backend",
3
- "version": "1.10.17",
3
+ "version": "1.11.1",
4
4
  "description": "A collection of common backend functions",
5
5
  "exports": {
6
6
  ".": "./lib/cjs/index.js"