@runnerpro/backend 1.33.1 → 1.34.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.
@@ -0,0 +1,153 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.urlPublica = exports.Storage = exports.Contenedor = exports.Archivo = exports.TIPO_POR_DEFECTO = void 0;
13
+ /**
14
+ * Almacenamiento de archivos en Azure Blob Storage.
15
+ *
16
+ * Expone la MISMA forma que el cliente de `@google-cloud/storage` que había antes
17
+ * (`storage.bucket(nombre).file(ruta).save()/download()/exists()/delete()`), para que los
18
+ * puntos de llamada no cambien más allá del import. Se migró el 19-ago-2026: los servicios
19
+ * ya corrían en Azure pero seguían escribiendo en los cubos de Google, así que cada archivo
20
+ * nuevo nacía en la nube que estábamos apagando.
21
+ *
22
+ * El "nombre de cubo" es ahora `cuenta/contenedor` (p.ej. `rppublicprod/public`), que es lo
23
+ * que llevan las variables CLOUD_STORAGE_BUCKET y CLOUD_STORAGE_BUCKET_PUBLIC. La clave de
24
+ * cada cuenta se lee de `AZURE_STORAGE_KEY_<CUENTA EN MAYÚSCULAS>`.
25
+ *
26
+ * Gemelo de `Cliente-backend/src/utils/almacenamiento.js`: si cambias uno, cambia el otro.
27
+ */
28
+ const storage_blob_1 = require("@azure/storage-blob");
29
+ /**
30
+ * Tipo por defecto cuando quien sube no dice cuál es. Es exactamente lo que ponía Google en
31
+ * los archivos sin extensión (los mapas de entreno no la tienen), así que respetarlo evita
32
+ * que un archivo servido cambie de tipo al migrar.
33
+ */
34
+ exports.TIPO_POR_DEFECTO = 'application/octet-stream';
35
+ /** Reintentos por defecto del SDK de Azure si nadie pide otra cosa. Son los suyos. */
36
+ const REINTENTOS_POR_DEFECTO = 4;
37
+ /** Clientes ya construidos, por cuenta+presupuesto. Uno por petición sería tirar conexiones. */
38
+ const clientesPorCuenta = new Map();
39
+ /**
40
+ * Nombres de los cubos viejos de Google y dónde viven ahora en Azure.
41
+ *
42
+ * Está aquí para que el despliegue no tenga que ser coordinado: los consumidores piden el
43
+ * paquete con `^1.31`, así que cualquier servicio que se redespliegue por un motivo ajeno se
44
+ * trae esta versión. Si además hubiera que haber cambiado sus variables a mano y en el mismo
45
+ * momento, ese servicio subiría a ninguna parte hasta que alguien se diera cuenta. Con la
46
+ * traducción aquí, la configuración vieja sigue funcionando y cambiar las variables pasa a ser
47
+ * limpieza, no un requisito.
48
+ */
49
+ const CUBOS_ANTIGUOS = {
50
+ web_runnerpro: 'rpwebprod/web',
51
+ runnerpro_public_prod: 'rppublicprod/public',
52
+ assets_runnerpro: 'rppublicprod/assets',
53
+ };
54
+ const partirNombre = (nombre) => {
55
+ if (!nombre) {
56
+ throw new Error('almacenamiento: falta el nombre del contenedor (cuenta/contenedor)');
57
+ }
58
+ const [cuenta, contenedor] = (CUBOS_ANTIGUOS[nombre] || nombre).split('/');
59
+ if (!cuenta || !contenedor) {
60
+ throw new Error(`almacenamiento: "${nombre}" no tiene la forma cuenta/contenedor (p.ej. rppublicprod/public)`);
61
+ }
62
+ return { cuenta, contenedor };
63
+ };
64
+ const clienteDeCuenta = (cuenta, presupuestoS) => {
65
+ const llave = `${cuenta}|${presupuestoS || 0}`;
66
+ const yaEsta = clientesPorCuenta.get(llave);
67
+ if (yaEsta)
68
+ return yaEsta;
69
+ const clave = process.env[`AZURE_STORAGE_KEY_${cuenta.toUpperCase()}`];
70
+ if (!clave) {
71
+ throw new Error(`almacenamiento: falta la variable AZURE_STORAGE_KEY_${cuenta.toUpperCase()}`);
72
+ }
73
+ const opciones = presupuestoS
74
+ ? {
75
+ retryOptions: {
76
+ maxTries: REINTENTOS_POR_DEFECTO,
77
+ tryTimeoutInMs: Math.floor((presupuestoS * 1000) / REINTENTOS_POR_DEFECTO),
78
+ },
79
+ }
80
+ : undefined;
81
+ const cliente = new storage_blob_1.BlobServiceClient(`https://${cuenta}.blob.core.windows.net`, new storage_blob_1.StorageSharedKeyCredential(cuenta, clave), opciones);
82
+ clientesPorCuenta.set(llave, cliente);
83
+ return cliente;
84
+ };
85
+ /**
86
+ * Un archivo dentro de un contenedor. `download()` y `exists()` devuelven ARRAY porque así
87
+ * lo desestructura el código que ya existe (`const [data] = await ....download()`).
88
+ */
89
+ class Archivo {
90
+ constructor(contenedor, ruta) {
91
+ this.ruta = ruta;
92
+ this.blob = contenedor.getBlockBlobClient(ruta);
93
+ }
94
+ save(datos, opciones = {}) {
95
+ return __awaiter(this, void 0, void 0, function* () {
96
+ const buffer = Buffer.isBuffer(datos) ? datos : Buffer.from(datos);
97
+ yield this.blob.uploadData(buffer, {
98
+ blobHTTPHeaders: { blobContentType: opciones.contentType || exports.TIPO_POR_DEFECTO },
99
+ });
100
+ });
101
+ }
102
+ download() {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ return [yield this.blob.downloadToBuffer()];
105
+ });
106
+ }
107
+ exists() {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ return [yield this.blob.exists()];
110
+ });
111
+ }
112
+ delete() {
113
+ return __awaiter(this, void 0, void 0, function* () {
114
+ yield this.blob.deleteIfExists();
115
+ });
116
+ }
117
+ }
118
+ exports.Archivo = Archivo;
119
+ /** Un contenedor. Equivale al `Bucket` de Google. */
120
+ class Contenedor {
121
+ constructor(nombre, presupuestoS) {
122
+ const { cuenta, contenedor } = partirNombre(nombre);
123
+ this.contenedor = clienteDeCuenta(cuenta, presupuestoS).getContainerClient(contenedor);
124
+ }
125
+ file(ruta) {
126
+ return new Archivo(this.contenedor, ruta);
127
+ }
128
+ }
129
+ exports.Contenedor = Contenedor;
130
+ /** Equivale al `Storage` de Google: `new Storage().bucket(nombre)`. */
131
+ class Storage {
132
+ /**
133
+ * Acepta el mismo objeto de opciones que el cliente de Google. `keyFilename` se ignora (la
134
+ * credencial de Azure va por variable de entorno); `retryOptions.totalTimeout` sí se respeta.
135
+ */
136
+ constructor(opciones = {}) {
137
+ var _a;
138
+ this.presupuestoS = (_a = opciones === null || opciones === void 0 ? void 0 : opciones.retryOptions) === null || _a === void 0 ? void 0 : _a.totalTimeout;
139
+ }
140
+ bucket(nombre) {
141
+ return new Contenedor(nombre, this.presupuestoS);
142
+ }
143
+ }
144
+ exports.Storage = Storage;
145
+ /**
146
+ * Dirección pública de un archivo. Sustituye a los
147
+ * `https://storage.googleapis.com/${bucket}/${ruta}` que se construían a mano.
148
+ */
149
+ const urlPublica = (nombre, ruta) => {
150
+ const { cuenta, contenedor } = partirNombre(nombre);
151
+ return `https://${cuenta}.blob.core.windows.net/${contenedor}/${ruta}`;
152
+ };
153
+ exports.urlPublica = urlPublica;
package/lib/cjs/index.js CHANGED
@@ -23,8 +23,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.describeImage = exports.transcribeAudio = exports.updateSenderView = exports.saveResponseTime = exports.getZone = exports.saveDoneStructuraWorkout = exports.computeStreakStats = exports.getUnlockedAchievements = exports.evaluateAchievementsBounded = exports.evaluateAchievements = exports.saveWorkoutLaps = exports.saveWorkoutAplication = exports.getPlanificacionPrueba7dias = exports.sendWorkoutToWatch = exports.getDefaultWorkoutImage = exports.generateShareMap = exports.reduceSizeImage = exports.getLetter = exports.getNumberByLetter = exports.appendSheet = exports.writeSheet = exports.findCellByValue = exports.readSheet = exports.NOTION_DATABASES_ID = exports.notionEditPage = exports.notionAddPage = exports.notionGetDatabase = exports.notionGetUsers = exports.getCountNotificaciones = exports.chatExposed = exports.chatApi = exports.chat = exports.getExerciseTranslatedDescription = exports.useTranslation = exports.LANGUAGES = exports.translate = exports.CHANNEL_SLACK = exports.notifySlack = exports.fetchIA = exports.err = exports.sendMail = exports.pool = exports.toPgArray = exports.batchQuery = exports.longRunningQuery = exports.queryWithClient = exports.query = exports.sleep = exports.sendNotification = exports.achievementsCatalog = void 0;
27
- exports.calculateCost = exports.MODEL_PRICING = exports.AISTUDIO_PREFIX = exports.BEDROCK_CLAUDE_HAIKU = exports.BEDROCK_CLAUDE_SONNET_4 = exports.BEDROCK_CLAUDE_SONNET_4_5 = exports.BEDROCK_CLAUDE_OPUS_4_1 = exports.BEDROCK_CLAUDE_OPUS_4_5 = exports.BEDROCK_CLAUDE_SONNET = exports.BEDROCK_CLAUDE_OPUS = exports.FALLBACK_MODELS = exports.PRODUCTION_MODELS = exports.AZURE_FALLBACK_MODELS = exports.AZURE_PRIMARY_MODELS = exports.AZURE_DEEPSEEK_V4_FLASH = exports.AZURE_DEEPSEEK_V4_PRO = exports.AZURE_GPT_5_4_NANO = exports.AZURE_GPT_5_4_MINI = exports.AZURE_GPT_5_6_LUNA = exports.AZURE_GPT_5_6_TERRA = exports.AZURE_GPT_5_6_SOL = exports.AZURE_GPT_5_5 = exports.AZURE_DEEPSEEK_V4_FLASH_DEPLOYMENT = exports.AZURE_DEEPSEEK_V4_PRO_DEPLOYMENT = exports.AZURE_GPT_5_4_NANO_DEPLOYMENT = exports.AZURE_GPT_5_4_MINI_DEPLOYMENT = exports.AZURE_GPT_5_6_LUNA_DEPLOYMENT = exports.AZURE_GPT_5_6_TERRA_DEPLOYMENT = exports.AZURE_GPT_5_6_SOL_DEPLOYMENT = exports.AZURE_GPT_5_5_DEPLOYMENT = exports.AZURE_PREFIX = exports.GOOGLE_MODELS = exports.AI_MODELS = exports.MODEL_TIER = exports.runWithModels = exports.runWithCostTracking = exports.createModelFromString = exports.createAzureModelFromString = exports.createBedrockModelFromString = exports.createGoogleModelFromString = exports.createGoogleModel = exports.generateText = exports.generateObject = exports.reprocessRecentMedia = exports.processMediaFile = exports.analyzeFile = exports.describeTextFile = exports.describePdf = exports.describeVideo = void 0;
26
+ exports.saveDoneStructuraWorkout = exports.computeStreakStats = exports.getUnlockedAchievements = exports.evaluateAchievementsBounded = exports.evaluateAchievements = exports.saveWorkoutLaps = exports.saveWorkoutAplication = exports.getPlanificacionPrueba7dias = exports.sendWorkoutToWatch = exports.getDefaultWorkoutImage = exports.generateShareMap = exports.reduceSizeImage = exports.getLetter = exports.getNumberByLetter = exports.appendSheet = exports.writeSheet = exports.findCellByValue = exports.readSheet = exports.NOTION_DATABASES_ID = exports.notionEditPage = exports.notionAddPage = exports.notionGetDatabase = exports.notionGetUsers = exports.getCountNotificaciones = exports.chatExposed = exports.chatApi = exports.chat = exports.getExerciseTranslatedDescription = exports.useTranslation = exports.LANGUAGES = exports.translate = exports.CHANNEL_SLACK = exports.notifySlack = exports.fetchIA = exports.err = exports.sendMail = exports.pool = exports.toPgArray = exports.batchQuery = exports.longRunningQuery = exports.queryWithClient = exports.query = exports.sleep = exports.sendNotification = exports.TIPO_POR_DEFECTO = exports.urlPublica = exports.Archivo = exports.Contenedor = exports.StorageAzure = exports.achievementsCatalog = void 0;
27
+ exports.BEDROCK_CLAUDE_SONNET_4 = exports.BEDROCK_CLAUDE_SONNET_4_5 = exports.BEDROCK_CLAUDE_OPUS_4_1 = exports.BEDROCK_CLAUDE_OPUS_4_5 = exports.BEDROCK_CLAUDE_SONNET = exports.BEDROCK_CLAUDE_OPUS = exports.FALLBACK_MODELS = exports.PRODUCTION_MODELS = exports.AZURE_FALLBACK_MODELS = exports.AZURE_PRIMARY_MODELS = exports.AZURE_DEEPSEEK_V4_FLASH = exports.AZURE_DEEPSEEK_V4_PRO = exports.AZURE_GPT_5_4_NANO = exports.AZURE_GPT_5_4_MINI = exports.AZURE_GPT_5_6_LUNA = exports.AZURE_GPT_5_6_TERRA = exports.AZURE_GPT_5_6_SOL = exports.AZURE_GPT_5_5 = exports.AZURE_DEEPSEEK_V4_FLASH_DEPLOYMENT = exports.AZURE_DEEPSEEK_V4_PRO_DEPLOYMENT = exports.AZURE_GPT_5_4_NANO_DEPLOYMENT = exports.AZURE_GPT_5_4_MINI_DEPLOYMENT = exports.AZURE_GPT_5_6_LUNA_DEPLOYMENT = exports.AZURE_GPT_5_6_TERRA_DEPLOYMENT = exports.AZURE_GPT_5_6_SOL_DEPLOYMENT = exports.AZURE_GPT_5_5_DEPLOYMENT = exports.AZURE_PREFIX = exports.GOOGLE_MODELS = exports.AI_MODELS = exports.MODEL_TIER = exports.runWithModels = exports.runWithCostTracking = exports.createModelFromString = exports.createAzureModelFromString = exports.createBedrockModelFromString = exports.createGoogleModelFromString = exports.createGoogleModel = exports.generateText = exports.generateObject = exports.reprocessRecentMedia = exports.processMediaFile = exports.analyzeFile = exports.describeTextFile = exports.describePdf = exports.describeVideo = exports.describeImage = exports.transcribeAudio = exports.updateSenderView = exports.saveResponseTime = exports.getZone = void 0;
28
+ exports.calculateCost = exports.MODEL_PRICING = exports.AISTUDIO_PREFIX = exports.BEDROCK_CLAUDE_HAIKU = void 0;
28
29
  const sendNotification_1 = require("./sendNotification");
29
30
  Object.defineProperty(exports, "sendNotification", { enumerable: true, get: function () { return sendNotification_1.sendNotification; } });
30
31
  const sleep_1 = require("./sleep");
@@ -152,3 +153,9 @@ Object.defineProperty(exports, "BEDROCK_CLAUDE_HAIKU", { enumerable: true, get:
152
153
  Object.defineProperty(exports, "AISTUDIO_PREFIX", { enumerable: true, get: function () { return prompt_1.AISTUDIO_PREFIX; } });
153
154
  Object.defineProperty(exports, "MODEL_PRICING", { enumerable: true, get: function () { return prompt_1.MODEL_PRICING; } });
154
155
  Object.defineProperty(exports, "calculateCost", { enumerable: true, get: function () { return prompt_1.calculateCost; } });
156
+ var almacenamiento_1 = require("./almacenamiento");
157
+ Object.defineProperty(exports, "StorageAzure", { enumerable: true, get: function () { return almacenamiento_1.Storage; } });
158
+ Object.defineProperty(exports, "Contenedor", { enumerable: true, get: function () { return almacenamiento_1.Contenedor; } });
159
+ Object.defineProperty(exports, "Archivo", { enumerable: true, get: function () { return almacenamiento_1.Archivo; } });
160
+ Object.defineProperty(exports, "urlPublica", { enumerable: true, get: function () { return almacenamiento_1.urlPublica; } });
161
+ Object.defineProperty(exports, "TIPO_POR_DEFECTO", { enumerable: true, get: function () { return almacenamiento_1.TIPO_POR_DEFECTO; } });
@@ -0,0 +1,65 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Almacenamiento de archivos en Azure Blob Storage.
4
+ *
5
+ * Expone la MISMA forma que el cliente de `@google-cloud/storage` que había antes
6
+ * (`storage.bucket(nombre).file(ruta).save()/download()/exists()/delete()`), para que los
7
+ * puntos de llamada no cambien más allá del import. Se migró el 19-ago-2026: los servicios
8
+ * ya corrían en Azure pero seguían escribiendo en los cubos de Google, así que cada archivo
9
+ * nuevo nacía en la nube que estábamos apagando.
10
+ *
11
+ * El "nombre de cubo" es ahora `cuenta/contenedor` (p.ej. `rppublicprod/public`), que es lo
12
+ * que llevan las variables CLOUD_STORAGE_BUCKET y CLOUD_STORAGE_BUCKET_PUBLIC. La clave de
13
+ * cada cuenta se lee de `AZURE_STORAGE_KEY_<CUENTA EN MAYÚSCULAS>`.
14
+ *
15
+ * Gemelo de `Cliente-backend/src/utils/almacenamiento.js`: si cambias uno, cambia el otro.
16
+ */
17
+ import { ContainerClient } from '@azure/storage-blob';
18
+ /**
19
+ * Tipo por defecto cuando quien sube no dice cuál es. Es exactamente lo que ponía Google en
20
+ * los archivos sin extensión (los mapas de entreno no la tienen), así que respetarlo evita
21
+ * que un archivo servido cambie de tipo al migrar.
22
+ */
23
+ export declare const TIPO_POR_DEFECTO = "application/octet-stream";
24
+ /**
25
+ * Un archivo dentro de un contenedor. `download()` y `exists()` devuelven ARRAY porque así
26
+ * lo desestructura el código que ya existe (`const [data] = await ....download()`).
27
+ */
28
+ export declare class Archivo {
29
+ ruta: string;
30
+ private blob;
31
+ constructor(contenedor: ContainerClient, ruta: string);
32
+ save(datos: Buffer | string, opciones?: {
33
+ contentType?: string;
34
+ }): Promise<void>;
35
+ download(): Promise<[Buffer]>;
36
+ exists(): Promise<[boolean]>;
37
+ delete(): Promise<void>;
38
+ }
39
+ /** Un contenedor. Equivale al `Bucket` de Google. */
40
+ export declare class Contenedor {
41
+ private contenedor;
42
+ constructor(nombre?: string, presupuestoS?: number);
43
+ file(ruta: string): Archivo;
44
+ }
45
+ /** Equivale al `Storage` de Google: `new Storage().bucket(nombre)`. */
46
+ export declare class Storage {
47
+ private presupuestoS?;
48
+ /**
49
+ * Acepta el mismo objeto de opciones que el cliente de Google. `keyFilename` se ignora (la
50
+ * credencial de Azure va por variable de entorno); `retryOptions.totalTimeout` sí se respeta.
51
+ */
52
+ constructor(opciones?: {
53
+ keyFilename?: string;
54
+ retryOptions?: {
55
+ totalTimeout?: number;
56
+ };
57
+ });
58
+ bucket(nombre?: string): Contenedor;
59
+ }
60
+ /**
61
+ * Dirección pública de un archivo. Sustituye a los
62
+ * `https://storage.googleapis.com/${bucket}/${ruta}` que se construían a mano.
63
+ */
64
+ export declare const urlPublica: (nombre: string | undefined, ruta: string) => string;
65
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/almacenamiento/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAiD,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAErG;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAiE3D;;;GAGG;AACH,qBAAa,OAAO;IAG8B,IAAI,EAAE,MAAM;IAF5D,OAAO,CAAC,IAAI,CAAC;gBAED,UAAU,EAAE,eAAe,EAAS,IAAI,EAAE,MAAM;IAItD,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpF,QAAQ,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;IAI7B,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC;IAI5B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAG9B;AAED,qDAAqD;AACrD,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAkB;gBAExB,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM;IAKlD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAG5B;AAED,uEAAuE;AACvE,qBAAa,OAAO;IAClB,OAAO,CAAC,YAAY,CAAC,CAAS;IAE9B;;;OAGG;gBACS,QAAQ,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE;YAAE,YAAY,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAO;IAI7F,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU;CAGpC;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,WAAY,MAAM,GAAG,SAAS,QAAQ,MAAM,KAAG,MAGrE,CAAC"}
@@ -24,5 +24,6 @@ import { saveResponseTime } from './chat/saveResponseTime';
24
24
  import { updateSenderView } from './chat/api/conversation';
25
25
  import { transcribeAudio, describeImage, describeVideo, describePdf, describeTextFile, analyzeFile, processMediaFile, reprocessRecentMedia } from './mediaProcessing';
26
26
  import { generateObject, generateText, createGoogleModel, createGoogleModelFromString, createBedrockModelFromString, createAzureModelFromString, createModelFromString, runWithCostTracking, runWithModels, MODEL_TIER, AI_MODELS, GOOGLE_MODELS, AZURE_PREFIX, AZURE_GPT_5_5_DEPLOYMENT, AZURE_GPT_5_6_SOL_DEPLOYMENT, AZURE_GPT_5_6_TERRA_DEPLOYMENT, AZURE_GPT_5_6_LUNA_DEPLOYMENT, AZURE_GPT_5_4_MINI_DEPLOYMENT, AZURE_GPT_5_4_NANO_DEPLOYMENT, AZURE_DEEPSEEK_V4_PRO_DEPLOYMENT, AZURE_DEEPSEEK_V4_FLASH_DEPLOYMENT, AZURE_GPT_5_5, AZURE_GPT_5_6_SOL, AZURE_GPT_5_6_TERRA, AZURE_GPT_5_6_LUNA, AZURE_GPT_5_4_MINI, AZURE_GPT_5_4_NANO, AZURE_DEEPSEEK_V4_PRO, AZURE_DEEPSEEK_V4_FLASH, AZURE_PRIMARY_MODELS, AZURE_FALLBACK_MODELS, PRODUCTION_MODELS, FALLBACK_MODELS, BEDROCK_CLAUDE_OPUS, BEDROCK_CLAUDE_SONNET, BEDROCK_CLAUDE_OPUS_4_5, BEDROCK_CLAUDE_OPUS_4_1, BEDROCK_CLAUDE_SONNET_4_5, BEDROCK_CLAUDE_SONNET_4, BEDROCK_CLAUDE_HAIKU, AISTUDIO_PREFIX, MODEL_PRICING, calculateCost } from './prompt';
27
+ export { Storage as StorageAzure, Contenedor, Archivo, urlPublica, TIPO_POR_DEFECTO, } from './almacenamiento';
27
28
  export { sendNotification, sleep, query, queryWithClient, longRunningQuery, batchQuery, toPgArray, pool, sendMail, err, fetchIA, notifySlack, CHANNEL_SLACK, translate, LANGUAGES, useTranslation, getExerciseTranslatedDescription, chat, chatApi, chatExposed, getCountNotificaciones, notionGetUsers, notionGetDatabase, notionAddPage, notionEditPage, NOTION_DATABASES_ID, readSheet, findCellByValue, writeSheet, appendSheet, getNumberByLetter, getLetter, reduceSizeImage, generateShareMap, getDefaultWorkoutImage, sendWorkoutToWatch, getPlanificacionPrueba7dias, saveWorkoutAplication, saveWorkoutLaps, evaluateAchievements, evaluateAchievementsBounded, getUnlockedAchievements, computeStreakStats, saveDoneStructuraWorkout, getZone, saveResponseTime, updateSenderView, transcribeAudio, describeImage, describeVideo, describePdf, describeTextFile, analyzeFile, processMediaFile, reprocessRecentMedia, generateObject, generateText, createGoogleModel, createGoogleModelFromString, createBedrockModelFromString, createAzureModelFromString, createModelFromString, runWithCostTracking, runWithModels, MODEL_TIER, AI_MODELS, GOOGLE_MODELS, AZURE_PREFIX, AZURE_GPT_5_5_DEPLOYMENT, AZURE_GPT_5_6_SOL_DEPLOYMENT, AZURE_GPT_5_6_TERRA_DEPLOYMENT, AZURE_GPT_5_6_LUNA_DEPLOYMENT, AZURE_GPT_5_4_MINI_DEPLOYMENT, AZURE_GPT_5_4_NANO_DEPLOYMENT, AZURE_DEEPSEEK_V4_PRO_DEPLOYMENT, AZURE_DEEPSEEK_V4_FLASH_DEPLOYMENT, AZURE_GPT_5_5, AZURE_GPT_5_6_SOL, AZURE_GPT_5_6_TERRA, AZURE_GPT_5_6_LUNA, AZURE_GPT_5_4_MINI, AZURE_GPT_5_4_NANO, AZURE_DEEPSEEK_V4_PRO, AZURE_DEEPSEEK_V4_FLASH, AZURE_PRIMARY_MODELS, AZURE_FALLBACK_MODELS, PRODUCTION_MODELS, FALLBACK_MODELS, BEDROCK_CLAUDE_OPUS, BEDROCK_CLAUDE_SONNET, BEDROCK_CLAUDE_OPUS_4_5, BEDROCK_CLAUDE_OPUS_4_1, BEDROCK_CLAUDE_SONNET_4_5, BEDROCK_CLAUDE_SONNET_4, BEDROCK_CLAUDE_HAIKU, AISTUDIO_PREFIX, MODEL_PRICING, calculateCost, };
28
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC7F,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,gCAAgC,EAAE,MAAM,eAAe,CAAC;AACvG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACjH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAClH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAIhI,OAAO,KAAK,mBAAmB,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,2BAA2B,EAC3B,4BAA4B,EAC5B,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,YAAY,EACZ,wBAAwB,EACxB,4BAA4B,EAC5B,8BAA8B,EAC9B,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,EAC7B,gCAAgC,EAChC,kCAAkC,EAClC,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,aAAa,EACd,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,OAAO,IAAI,YAAY,EACvB,UAAU,EACV,OAAO,EACP,UAAU,EACV,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,gBAAgB,EAChB,KAAK,EACL,KAAK,EACL,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,OAAO,EACP,WAAW,EACX,aAAa,EACb,SAAS,EACT,SAAS,EACT,cAAc,EACd,gCAAgC,EAChC,IAAI,EACJ,OAAO,EACP,WAAW,EACX,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,2BAA2B,EAC3B,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,EACvB,kBAAkB,EAClB,wBAAwB,EACxB,OAAO,EACP,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,2BAA2B,EAC3B,4BAA4B,EAC5B,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,YAAY,EACZ,wBAAwB,EACxB,4BAA4B,EAC5B,8BAA8B,EAC9B,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,EAC7B,gCAAgC,EAChC,kCAAkC,EAClC,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,aAAa,GACd,CAAC"}
@@ -1,3 +1,24 @@
1
+ /**
2
+ * Algunas ingestas entregan la distancia del RESUMEN en millas pero rotulada en metros
3
+ * (9.051 m reales -> se guarda 5.600, que son 5,6 millas x 1000). El gráfico y los
4
+ * parciales salen bien porque se calculan del stream, pero la distancia total y el ritmo
5
+ * medio quedan ~1,61x peores, y `getZone` clasifica mal porque sale de duración/distancia.
6
+ *
7
+ * El stream SÍ viene en metros, así que hace de árbitro: se corrige sólo si reconstruir
8
+ * "millas x 1000" desde el stream reproduce la distancia candidata con menos de un 1% de
9
+ * desvío.
10
+ *
11
+ * OJO con leer el filtro de ratio de más abajo como un segundo criterio independiente:
12
+ * NO lo es, es la misma cuenta despejada. Sirve para descartar barato el 99,99% de las
13
+ * ingestas sin dividir. El rango que decide de verdad es ratio ∈ [1,5932 – 1,6254].
14
+ *
15
+ * Ese pasillo es estrecho a propósito, y por eso la corrección deja traza: en producción
16
+ * hay entrenos SANOS pegados a los dos bordes —w699672 a ratio 1,5784 y w694890 a 1,6301,
17
+ * con el stream más largo que el resumen por pausas o deriva de GPS—, y entre 1,40 y 1,80
18
+ * hay una nube continua de entrenos legítimos. Si alguno acaba cayendo dentro, el rastro
19
+ * es lo único que permitirá distinguirlo de un fallo de la fuente.
20
+ */
21
+ declare const corregirDistanciaEnMillas: (distance: any, streamFinal: number | null) => any;
1
22
  /**
2
23
  * @param opts {{
3
24
  * date, type, title, description, distance, duration, power, desnivel, polyline,
@@ -10,5 +31,5 @@
10
31
  * @returns {Promise<number|null>} idWorkout (o null si no se pudo crear) — firma intacta
11
32
  */
12
33
  declare const saveWorkoutAplication: (opts: any, _data?: any) => Promise<number | null>;
13
- export { saveWorkoutAplication };
34
+ export { saveWorkoutAplication, corregirDistanciaEnMillas };
14
35
  //# sourceMappingURL=saveWorkoutAplication.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"saveWorkoutAplication.d.ts","sourceRoot":"","sources":["../../../../src/workout/saveWorkoutAplication.ts"],"names":[],"mappings":"AAsTA;;;;;;;;;;GAUG;AACH,QAAA,MAAM,qBAAqB,SACnB,GAAG,UAED,GAAG,KACV,QAAQ,MAAM,GAAG,IAAI,CAqFvB,CAAC;AA0GF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"saveWorkoutAplication.d.ts","sourceRoot":"","sources":["../../../../src/workout/saveWorkoutAplication.ts"],"names":[],"mappings":"AA2RA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,QAAA,MAAM,yBAAyB,aAAc,GAAG,eAAe,MAAM,GAAG,IAAI,KAAG,GAe9E,CAAC;AA8FF;;;;;;;;;;GAUG;AACH,QAAA,MAAM,qBAAqB,SACnB,GAAG,UAED,GAAG,KACV,QAAQ,MAAM,GAAG,IAAI,CAqFvB,CAAC;AAuGF,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,CAAC"}
@@ -12,10 +12,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.saveWorkoutAplication = void 0;
15
+ exports.corregirDistanciaEnMillas = exports.saveWorkoutAplication = void 0;
16
16
  const db_1 = require("../db");
17
- const storage_1 = require("@google-cloud/storage");
18
- const path_1 = __importDefault(require("path"));
17
+ const almacenamiento_1 = require("../almacenamiento");
19
18
  const axios_1 = __importDefault(require("axios"));
20
19
  const generateShareMap_1 = require("../image/generateShareMap");
21
20
  const getDefaultWorkoutImage_1 = require("../image/getDefaultWorkoutImage");
@@ -244,6 +243,95 @@ const registrarFuente = (q, idWorkout, tipo, idApp) => __awaiter(void 0, void 0,
244
243
  ON CONFLICT ("ID WORKOUT", "TIPO APLICATION")
245
244
  DO UPDATE SET "ID APLICATION" = EXCLUDED."ID APLICATION", "INGESTED AT" = NOW()`, [idWorkout, String(tipo), idApp !== null && idApp !== undefined ? String(idApp) : null]);
246
245
  });
246
+ // =============================================================================
247
+ // Guardarraíl de unidad: distancia en MILLAS rotulada como metros
248
+ // =============================================================================
249
+ const METROS_POR_MILLA = 1609.344;
250
+ // Desvío máximo admitido al reconstruir la candidata desde el stream. NO se puede
251
+ // apretar: Strava redondea la distancia del resumen a una décima de milla, y en un
252
+ // entreno corto ese redondeo ya vale casi un 1% (0,05 mi sobre 3,9 mi = 1,3%).
253
+ const TOLERANCIA_RECONSTRUCCION = 0.01;
254
+ /**
255
+ * Último valor útil del stream de distancia, en metros. Mira primero el stream que
256
+ * entra con esta ingesta y, si no viene, el que ya esté guardado para el workout
257
+ * (p.ej. lo dejó otra fuente). Devuelve null si no hay ninguno: sin árbitro no se
258
+ * toca la distancia.
259
+ *
260
+ * La consulta pide SÓLO el último punto (`-> -1`) y no el array entero: un stream de
261
+ * distancia son miles de puntos en un campo TOAST, y esto corre dentro de la tx que
262
+ * sostiene el advisory lock de la ingesta. Traerlo completo para leer un número sería
263
+ * alargar el lock que ya es el punto donde más se cae el pool.
264
+ */
265
+ const distanciaFinalDelStream = (q, idWorkout, streams) => __awaiter(void 0, void 0, void 0, function* () {
266
+ const valido = (v) => {
267
+ const n = Number(v);
268
+ return Number.isFinite(n) && n > 0 ? n : null;
269
+ };
270
+ const entrantes = streams === null || streams === void 0 ? void 0 : streams.distance;
271
+ if (Array.isArray(entrantes) && entrantes.length > 0) {
272
+ const ultimo = valido(entrantes[entrantes.length - 1]);
273
+ if (ultimo !== null)
274
+ return ultimo;
275
+ }
276
+ const [row] = yield q(`SELECT CASE WHEN jsonb_typeof("DATA"::jsonb) = 'array' AND jsonb_array_length("DATA"::jsonb) > 0
277
+ THEN "DATA"::jsonb -> -1 ELSE NULL END AS ultimo
278
+ FROM "WORKOUT STREAM" WHERE "ID WORKOUT" = ? AND "METRIC TYPE" = ?`, [idWorkout, 'distance']);
279
+ return row ? valido(row.ultimo) : null;
280
+ });
281
+ /**
282
+ * Algunas ingestas entregan la distancia del RESUMEN en millas pero rotulada en metros
283
+ * (9.051 m reales -> se guarda 5.600, que son 5,6 millas x 1000). El gráfico y los
284
+ * parciales salen bien porque se calculan del stream, pero la distancia total y el ritmo
285
+ * medio quedan ~1,61x peores, y `getZone` clasifica mal porque sale de duración/distancia.
286
+ *
287
+ * El stream SÍ viene en metros, así que hace de árbitro: se corrige sólo si reconstruir
288
+ * "millas x 1000" desde el stream reproduce la distancia candidata con menos de un 1% de
289
+ * desvío.
290
+ *
291
+ * OJO con leer el filtro de ratio de más abajo como un segundo criterio independiente:
292
+ * NO lo es, es la misma cuenta despejada. Sirve para descartar barato el 99,99% de las
293
+ * ingestas sin dividir. El rango que decide de verdad es ratio ∈ [1,5932 – 1,6254].
294
+ *
295
+ * Ese pasillo es estrecho a propósito, y por eso la corrección deja traza: en producción
296
+ * hay entrenos SANOS pegados a los dos bordes —w699672 a ratio 1,5784 y w694890 a 1,6301,
297
+ * con el stream más largo que el resumen por pausas o deriva de GPS—, y entre 1,40 y 1,80
298
+ * hay una nube continua de entrenos legítimos. Si alguno acaba cayendo dentro, el rastro
299
+ * es lo único que permitirá distinguirlo de un fallo de la fuente.
300
+ */
301
+ const corregirDistanciaEnMillas = (distance, streamFinal) => {
302
+ const d = Number(distance);
303
+ if (!Number.isFinite(d) || d <= 0 || streamFinal === null || !(streamFinal > 0))
304
+ return distance;
305
+ // Filtro barato: fuera de la horquilla de la milla no hay nada que mirar.
306
+ const ratio = streamFinal / d;
307
+ if (ratio < 1.58 || ratio > 1.64)
308
+ return distance;
309
+ // La fuente trató las MILLAS como si fueran kilómetros: el valor que nos llega es
310
+ // "millas x 1000" (5,6 mi -> 5600). Reconstruimos ese número desde el stream y exigimos
311
+ // que reproduzca la candidata; si no, el desajuste tiene otra causa y no se toca.
312
+ const millasEscaladas = (streamFinal / METROS_POR_MILLA) * 1000;
313
+ if (Math.abs(millasEscaladas - d) / d > TOLERANCIA_RECONSTRUCCION)
314
+ return distance;
315
+ return Math.round(streamFinal / 10) * 10;
316
+ };
317
+ exports.corregirDistanciaEnMillas = corregirDistanciaEnMillas;
318
+ const resolverDistanciaConStream = (q, idWorkout, distance, streams) => __awaiter(void 0, void 0, void 0, function* () {
319
+ const d = Number(distance);
320
+ if (!Number.isFinite(d) || d <= 0)
321
+ return distance;
322
+ const streamFinal = yield distanciaFinalDelStream(q, idWorkout, streams);
323
+ const corregida = corregirDistanciaEnMillas(distance, streamFinal);
324
+ // Traza obligatoria: esto pisa un dato del cliente por heurística y en un margen fino.
325
+ // Sin rastro, un "mi entreno marca de más" no se podría distinguir de un fallo de la
326
+ // fuente, ni se sabría cuántas veces ha actuado el guardarraíl.
327
+ if (Number(corregida) !== d) {
328
+ // eslint-disable-next-line no-console
329
+ console.warn(`corregirDistanciaEnMillas: idWorkout=${idWorkout} DISTANCE ${d} -> ${corregida} ` +
330
+ `(stream=${streamFinal}, ratio=${(Number(streamFinal) / d).toFixed(4)}). ` +
331
+ 'La fuente entregó la distancia del resumen en millas.');
332
+ }
333
+ return corregida;
334
+ });
247
335
  const aplicarMerge = (q, idWorkout, opts, esFilaNueva) => __awaiter(void 0, void 0, void 0, function* () {
248
336
  var _a, _b, _c, _d;
249
337
  const [row] = yield q(`SELECT "DISTANCE", "DURATION", "POTENCIA", "DESNIVEL", "CALORIES", "FC MEDIA",
@@ -251,7 +339,10 @@ const aplicarMerge = (q, idWorkout, opts, esFilaNueva) => __awaiter(void 0, void
251
339
  FROM "WORKOUT" WHERE "ID" = ? FOR UPDATE`, [idWorkout]);
252
340
  const prioNueva = prioridadFuente(opts.aplicationType);
253
341
  const prioActual = esFilaNueva ? -1 : prioridadFuente(row === null || row === void 0 ? void 0 : row.tipoAplication);
254
- const distance = resolverEscalar(row === null || row === void 0 ? void 0 : row.distance, opts.distance, prioActual, prioNueva);
342
+ const distanceMerge = resolverEscalar(row === null || row === void 0 ? void 0 : row.distance, opts.distance, prioActual, prioNueva);
343
+ // El stream (metros) arbitra sobre la distancia del resumen: corrige el caso en que la
344
+ // fuente la entrega en millas. Va ANTES de getZone, que se calcula con esta distancia.
345
+ const distance = yield resolverDistanciaConStream(q, idWorkout, distanceMerge, opts.streams);
255
346
  const duration = resolverEscalar(row === null || row === void 0 ? void 0 : row.duration, opts.duration, prioActual, prioNueva);
256
347
  const potencia = resolverEscalar(row === null || row === void 0 ? void 0 : row.potencia, opts.power, prioActual, prioNueva);
257
348
  const desnivel = resolverEscalar(row === null || row === void 0 ? void 0 : row.desnivel, opts.desnivel, prioActual, prioNueva);
@@ -440,8 +531,7 @@ const saveMap = (idWorkout, polyline) => __awaiter(void 0, void 0, void 0, funct
440
531
  if (!polyline)
441
532
  return;
442
533
  // @ts-ignore
443
- const serviceKey = path_1.default.join(process.cwd(), process.env.FIREBASE_STORAGE_CREDENTIALS);
444
- const storage = new storage_1.Storage({ keyFilename: serviceKey });
534
+ const storage = new almacenamiento_1.Storage();
445
535
  const encodedPolyline = encodeURIComponent(typeof polyline === 'string' ? polyline : getPolyline(polyline) || '');
446
536
  if (!encodedPolyline)
447
537
  return;
@@ -449,7 +539,7 @@ const saveMap = (idWorkout, polyline) => __awaiter(void 0, void 0, void 0, funct
449
539
  const imageBuffer = Buffer.from(result.data, 'binary');
450
540
  // @ts-ignore
451
541
  yield storage.bucket(process.env.CLOUD_STORAGE_BUCKET_PUBLIC).file(`Workout/${idWorkout}`).save(imageBuffer);
452
- const urlMap = `https://storage.googleapis.com/${process.env.CLOUD_STORAGE_BUCKET_PUBLIC}/Workout/${idWorkout}`;
542
+ const urlMap = (0, almacenamiento_1.urlPublica)(process.env.CLOUD_STORAGE_BUCKET_PUBLIC, `Workout/${idWorkout}`);
453
543
  try {
454
544
  yield (0, db_1.query)('UPDATE "WORKOUT" SET "HAVE MAP IMAGE" = TRUE, "PHOTO URL" = ? WHERE "ID" = ?', [urlMap, idWorkout]);
455
545
  }
@@ -461,9 +551,7 @@ const saveMap = (idWorkout, polyline) => __awaiter(void 0, void 0, void 0, funct
461
551
  }
462
552
  });
463
553
  const saveShareWorkoutImage = (id, type) => __awaiter(void 0, void 0, void 0, function* () {
464
- // @ts-ignore
465
- const serviceKey = path_1.default.join(process.cwd(), process.env.FIREBASE_STORAGE_CREDENTIALS);
466
- const storage = new storage_1.Storage({ keyFilename: serviceKey });
554
+ const storage = new almacenamiento_1.Storage();
467
555
  // @ts-ignore
468
556
  const [exists] = yield storage.bucket(process.env.CLOUD_STORAGE_BUCKET_PUBLIC).file(`Workout/${id}`).exists();
469
557
  let image;
@@ -479,7 +567,7 @@ const saveShareWorkoutImage = (id, type) => __awaiter(void 0, void 0, void 0, fu
479
567
  return;
480
568
  // @ts-ignore
481
569
  yield storage.bucket(process.env.CLOUD_STORAGE_BUCKET_PUBLIC).file(`Workout/${id}-share`).save(shareMap);
482
- const urlShare = `https://storage.googleapis.com/${process.env.CLOUD_STORAGE_BUCKET_PUBLIC}/Workout/${id}-share`;
570
+ const urlShare = (0, almacenamiento_1.urlPublica)(process.env.CLOUD_STORAGE_BUCKET_PUBLIC, `Workout/${id}-share`);
483
571
  try {
484
572
  yield (0, db_1.query)('UPDATE "WORKOUT" SET "PHOTO URL SHARE" = ? WHERE "ID" = ?', [urlShare, id]);
485
573
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runnerpro/backend",
3
- "version": "1.33.1",
3
+ "version": "1.34.1",
4
4
  "description": "A collection of common backend functions",
5
5
  "exports": {
6
6
  ".": "./lib/cjs/index.js"
@@ -20,7 +20,8 @@
20
20
  "test:generate-share-map-guard": "ts-node --transpile-only -P configs/tsconfig.cjs.json scripts/testGenerateShareMapGuard.ts",
21
21
  "test:pg-query-conexion": "ts-node --transpile-only -P configs/tsconfig.cjs.json scripts/testPgQueryConexion.ts",
22
22
  "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
23
- "prepare": "husky"
23
+ "prepare": "husky",
24
+ "test:distancia-millas": "ts-node --transpile-only -P configs/tsconfig.cjs.json scripts/testDistanciaMillas.ts"
24
25
  },
25
26
  "release": {
26
27
  "branches": [
@@ -68,6 +69,7 @@
68
69
  "@ai-sdk/azure": "^3.0.74",
69
70
  "@ai-sdk/google": "^3.0.43",
70
71
  "@ai-sdk/google-vertex": "^4.0.102",
72
+ "@azure/storage-blob": "^12.32.0",
71
73
  "@google-cloud/speech": "^7.2.1",
72
74
  "@google-cloud/storage": "^7.11.3",
73
75
  "@google-cloud/translate": "^8.3.0",