@runnerpro/backend 1.22.3 → 1.22.5

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.
@@ -260,6 +260,64 @@ function generateObject(options) {
260
260
  });
261
261
  }
262
262
  exports.generateObject = generateObject;
263
+ // Tope por cadena al derivar la firma de unos `messages`. Existe por las llamadas multimodales
264
+ // (analizar-foto manda una imagen en base64 de hasta 10 MB): sin él, cada foto metería megas en la
265
+ // clave y en la columna INPUT PROMPT. Dos payloads enormes que compartan los primeros 4000
266
+ // caracteres caerían en la misma fila, pero el ON CONFLICT acumula, así que el coste total sigue
267
+ // siendo correcto: solo se pierde granularidad.
268
+ const TEXT_SIG_MAX_CHARS = 4000;
269
+ /**
270
+ * Deriva el texto que identifica una llamada de `generateText`.
271
+ *
272
+ * El SDK admite `prompt` (string) o `messages` (array). Sin cubrir el segundo caso, todas las
273
+ * llamadas con `messages` compartirían system+prompt indefinidos y colapsarían en UNA sola fila.
274
+ */
275
+ function textPromptSignature(options) {
276
+ if (typeof (options === null || options === void 0 ? void 0 : options.prompt) === 'string')
277
+ return options.prompt;
278
+ if (!Array.isArray(options === null || options === void 0 ? void 0 : options.messages))
279
+ return undefined;
280
+ try {
281
+ return JSON.stringify(options.messages, (_k, v) => typeof v === 'string' && v.length > TEXT_SIG_MAX_CHARS ? `${v.slice(0, TEXT_SIG_MAX_CHARS)}…(${v.length})` : v);
282
+ }
283
+ catch (_a) {
284
+ return undefined;
285
+ }
286
+ }
287
+ /**
288
+ * Registra en "LLM CACHE" el coste de una llamada de `generateText`.
289
+ *
290
+ * REGISTRO, NO CACHÉ: no hay `lookup` previo y nunca se reutiliza el texto guardado. Cachear
291
+ * `generateText` daría a dos clientes con el mismo contexto la misma respuesta literal, que en el
292
+ * chat del entrenador es inaceptable. La fila existe solo para saber qué modelo respondió y cuánto
293
+ * costó; usa TEXT_CALL_SIG para no compartir espacio de claves con `generateObject`.
294
+ *
295
+ * Se anota el coste YA saneado, que incluye el retry anti-CoT y el sanitizer LITE si llegaron a
296
+ * correr. Ojo con la atribución: el gasto es el total real de la llamada lógica, pero la columna
297
+ * MODEL guarda el modelo principal, así que si intervino el sanitizer parte de ese coste es de LITE.
298
+ *
299
+ * Opt-out con `options.log === false`. Fire-and-forget: nunca bloquea ni rompe la llamada.
300
+ */
301
+ function logTextCost(options, modelName, result) {
302
+ if ((options === null || options === void 0 ? void 0 : options.log) === false)
303
+ return;
304
+ try {
305
+ const prompt = textPromptSignature(options);
306
+ const key = (0, llmCacheStore_1.llmCacheKey)({ system: options === null || options === void 0 ? void 0 : options.system, prompt, schemaSig: llmCacheStore_1.TEXT_CALL_SIG, temperature: options === null || options === void 0 ? void 0 : options.temperature });
307
+ (0, llmCacheStore_1.store)(key, {
308
+ modelName,
309
+ system: options === null || options === void 0 ? void 0 : options.system,
310
+ prompt,
311
+ schemaSig: llmCacheStore_1.TEXT_CALL_SIG,
312
+ temperature: options === null || options === void 0 ? void 0 : options.temperature,
313
+ output: result.text,
314
+ cost: result.cost,
315
+ });
316
+ }
317
+ catch (_a) {
318
+ // best-effort: registrar el coste jamás debe tumbar una generación válida
319
+ }
320
+ }
263
321
  /**
264
322
  * Wrapper de generateText con retry automático, cadena de fallbacks y saneamiento
265
323
  * de chain-of-thought.
@@ -304,6 +362,7 @@ function generateText(options) {
304
362
  const tracker = costTrackingStorage.getStore();
305
363
  if (tracker)
306
364
  tracker.push(cleanResult.cost);
365
+ logTextCost(currentOptions, modelName, cleanResult);
307
366
  return cleanResult;
308
367
  }
309
368
  catch (error) {
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.store = exports.lookup = exports.llmCacheKey = exports.schemaSignature = void 0;
12
+ exports.TEXT_CALL_SIG = exports.store = exports.lookup = exports.llmCacheKey = exports.schemaSignature = void 0;
13
13
  /**
14
14
  * Caché LLM (cache-aside) para `generateObject`.
15
15
  *
@@ -39,6 +39,13 @@ function ensureReady() {
39
39
  return _ready;
40
40
  });
41
41
  }
42
+ /**
43
+ * Firma reservada para las llamadas de `generateText`, que se registran por su coste pero NUNCA
44
+ * se cachean. Al ocupar su propio espacio de claves, una fila de texto no puede ser devuelta por
45
+ * el `lookup` de `generateObject` aunque coincidan system, prompt y temperatura.
46
+ */
47
+ const TEXT_CALL_SIG = '__text__';
48
+ exports.TEXT_CALL_SIG = TEXT_CALL_SIG;
42
49
  /** Firma ligera del schema (zod) para distinguir llamadas con el mismo prompt pero distinta forma. */
43
50
  function schemaSignature(schema) {
44
51
  var _a, _b;
@@ -1 +1 @@
1
- {"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../../../src/prompt/ai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,IAAI,sBAAsB,EAAE,YAAY,IAAI,oBAAoB,EAAE,MAAM,IAAI,CAAC;AAEpG,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAwKhE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,iBAAe,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAoD/H;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,iBAAe,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CA0C5H;AA+ED;;;;;;;;;;;;;;;;;GAiBG;AACH,iBAAe,mBAAmB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,SAAS,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB5H;AAED,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../../../src/prompt/ai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,IAAI,sBAAsB,EAAE,YAAY,IAAI,oBAAoB,EAAE,MAAM,IAAI,CAAC;AAEpG,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAwKhE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,iBAAe,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAoD/H;AA4DD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,iBAAe,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CA4C5H;AA+ED;;;;;;;;;;;;;;;;;GAiBG;AACH,iBAAe,mBAAmB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,SAAS,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB5H;AAED,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
@@ -1,4 +1,10 @@
1
1
  import type { CostResult } from './modelPricing';
2
+ /**
3
+ * Firma reservada para las llamadas de `generateText`, que se registran por su coste pero NUNCA
4
+ * se cachean. Al ocupar su propio espacio de claves, una fila de texto no puede ser devuelta por
5
+ * el `lookup` de `generateObject` aunque coincidan system, prompt y temperatura.
6
+ */
7
+ declare const TEXT_CALL_SIG = "__text__";
2
8
  /** Firma ligera del schema (zod) para distinguir llamadas con el mismo prompt pero distinta forma. */
3
9
  declare function schemaSignature(schema: any): string;
4
10
  /**
@@ -38,5 +44,5 @@ declare function store(key: string, data: {
38
44
  output: any;
39
45
  cost?: CostResult;
40
46
  }): void;
41
- export { schemaSignature, llmCacheKey, lookup, store };
47
+ export { schemaSignature, llmCacheKey, lookup, store, TEXT_CALL_SIG };
42
48
  //# sourceMappingURL=llmCacheStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"llmCacheStore.d.ts","sourceRoot":"","sources":["../../../../src/prompt/llmCacheStore.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAejD,sGAAsG;AACtG,iBAAS,eAAe,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAW5C;AAED;;;;GAIG;AACH,iBAAS,WAAW,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,MAAM,CAQ/H;AAED;;;;;;GAMG;AACH,iBAAe,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAWtD;AAED;;;;;;;;GAQG;AACH,iBAAS,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAA;CAAE,GAAG,IAAI,CAkCxL;AAED,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"llmCacheStore.d.ts","sourceRoot":"","sources":["../../../../src/prompt/llmCacheStore.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAejD;;;;GAIG;AACH,QAAA,MAAM,aAAa,aAAa,CAAC;AAEjC,sGAAsG;AACtG,iBAAS,eAAe,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAW5C;AAED;;;;GAIG;AACH,iBAAS,WAAW,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,MAAM,CAQ/H;AAED;;;;;;GAMG;AACH,iBAAe,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAWtD;AAED;;;;;;;;GAQG;AACH,iBAAS,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAA;CAAE,GAAG,IAAI,CAkCxL;AAED,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"getPlanificacionPrueba7dias10k.d.ts","sourceRoot":"","sources":["../../../../../../src/workout/planificacionPrueba7dias/10k/getPlanificacionPrueba7dias10k.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKnC,CAAC;AAEF,QAAA,MAAM,kCAAkC,kBAMvC,CAAC;AAEF,OAAO,EAAE,8BAA8B,EAAE,kCAAkC,EAAE,CAAC"}
1
+ {"version":3,"file":"getPlanificacionPrueba7dias10k.d.ts","sourceRoot":"","sources":["../../../../../../src/workout/planificacionPrueba7dias/10k/getPlanificacionPrueba7dias10k.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKnC,CAAC;AAEF,QAAA,MAAM,kCAAkC,kBAQvC,CAAC;AAEF,OAAO,EAAE,8BAA8B,EAAE,kCAAkC,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"getPlanificacionPrueba7dias21k.d.ts","sourceRoot":"","sources":["../../../../../../src/workout/planificacionPrueba7dias/21k/getPlanificacionPrueba7dias21k.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKnC,CAAC;AAEF,QAAA,MAAM,kCAAkC,kBAQvC,CAAC;AAEF,OAAO,EAAE,8BAA8B,EAAE,kCAAkC,EAAE,CAAC"}
1
+ {"version":3,"file":"getPlanificacionPrueba7dias21k.d.ts","sourceRoot":"","sources":["../../../../../../src/workout/planificacionPrueba7dias/21k/getPlanificacionPrueba7dias21k.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKnC,CAAC;AAEF,QAAA,MAAM,kCAAkC,kBAUvC,CAAC;AAEF,OAAO,EAAE,8BAA8B,EAAE,kCAAkC,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"getPlanificacionPrueba7dias42k.d.ts","sourceRoot":"","sources":["../../../../../../src/workout/planificacionPrueba7dias/42k/getPlanificacionPrueba7dias42k.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKnC,CAAC;AAEF,QAAA,MAAM,kCAAkC,kBAQvC,CAAC;AAEF,OAAO,EAAE,8BAA8B,EAAE,kCAAkC,EAAE,CAAC"}
1
+ {"version":3,"file":"getPlanificacionPrueba7dias42k.d.ts","sourceRoot":"","sources":["../../../../../../src/workout/planificacionPrueba7dias/42k/getPlanificacionPrueba7dias42k.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKnC,CAAC;AAEF,QAAA,MAAM,kCAAkC,kBAUvC,CAAC;AAEF,OAAO,EAAE,8BAA8B,EAAE,kCAAkC,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"getPlanificacionPrueba7dias5k.d.ts","sourceRoot":"","sources":["../../../../../../src/workout/planificacionPrueba7dias/5k/getPlanificacionPrueba7dias5k.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;IAIlC,CAAC;AAEF,QAAA,MAAM,iCAAiC,kBAItC,CAAC;AAEF,OAAO,EAAE,6BAA6B,EAAE,iCAAiC,EAAE,CAAC"}
1
+ {"version":3,"file":"getPlanificacionPrueba7dias5k.d.ts","sourceRoot":"","sources":["../../../../../../src/workout/planificacionPrueba7dias/5k/getPlanificacionPrueba7dias5k.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;IAIlC,CAAC;AAEF,QAAA,MAAM,iCAAiC,kBAMtC,CAAC;AAEF,OAAO,EAAE,6BAA6B,EAAE,iCAAiC,EAAE,CAAC"}
@@ -2,5 +2,36 @@ declare const paceByZone: (c: any, zone: any) => any;
2
2
  declare const calculateDistance: (c: any, duration: any, zone: any) => number;
3
3
  declare const calculateDuration: (c: any, distance: any, zone: any) => number;
4
4
  declare const prettyPace: (stringPace: any) => string;
5
- export { paceByZone, calculateDistance, calculateDuration, prettyPace };
5
+ /**
6
+ * Duración de carrera estimada (segundos) a partir del rodaje suave declarado.
7
+ * Misma relación que el resto del código: el "100%" es el 70% del rodaje
8
+ * (calcularZonasDesdePaceSlow, calculateInitialPaces) y de ahí sale el ritmo de
9
+ * carrera según la distancia (espejo de calculateTimeFromPace del onboarding).
10
+ *
11
+ * @param {number|string} paceSlowSeg - Rodaje suave en seg/km.
12
+ * @param {number} distanciaMetros - Distancia objetivo en metros.
13
+ * @returns {number|null} Segundos estimados, o null si no hay ritmo utilizable.
14
+ */
15
+ declare const estimarDuracionDesdePaceSlow: (paceSlowSeg: any, distanciaMetros: any) => number;
16
+ /**
17
+ * Duración de referencia (segundos) para ELEGIR la plantilla de la semana. Orden:
18
+ * 1) marca real declarada (vamDuration > 0)
19
+ * 2) estimación desde el rodaje suave declarado
20
+ * 3) null → el selector cae en la plantilla más conservadora (PRIMER)
21
+ *
22
+ * Motivo: `vamDuration` llega como STRING desde Postgres, así que un "0" es
23
+ * truthy y `!c.vamDuration` NO lo detecta. Sin esta normalización, una marca
24
+ * inválida caía en la plantilla MÁS DURA de cada distancia (SUB30/SUB50/
25
+ * SUB105/SUB210) — justo la peor dirección de fallo.
26
+ *
27
+ * OJO: no escribir el resultado en c.vamDuration. `tieneMarcaVerificada`
28
+ * distingue marca REAL de estimación para decidir si la semana puede arrancar
29
+ * con una sesión de calidad; una estimación no debe contar como marca.
30
+ *
31
+ * @param {Object} c - Cliente (vamDuration, paceSlow).
32
+ * @param {number} distanciaMetros - Distancia objetivo en metros.
33
+ * @returns {number|null} Segundos de referencia, o null si no hay nada fiable.
34
+ */
35
+ declare const getDuracionReferenciaPlantilla: (c: any, distanciaMetros: any) => number;
36
+ export { paceByZone, calculateDistance, calculateDuration, prettyPace, estimarDuracionDesdePaceSlow, getDuracionReferenciaPlantilla, };
6
37
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../../src/workout/planificacionPrueba7dias/common.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,UAAU,4BAQf,CAAC;AAEF,QAAA,MAAM,iBAAiB,8CAEtB,CAAC;AAEF,QAAA,MAAM,iBAAiB,8CAEtB,CAAC;AAEF,QAAA,MAAM,UAAU,6BAKf,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../../src/workout/planificacionPrueba7dias/common.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,UAAU,4BAQf,CAAC;AAEF,QAAA,MAAM,iBAAiB,8CAEtB,CAAC;AAEF,QAAA,MAAM,iBAAiB,8CAEtB,CAAC;AAEF,QAAA,MAAM,UAAU,6BAKf,CAAC;AAEF;;;;;;;;;GASG;AACH,QAAA,MAAM,4BAA4B,oDAQjC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,8BAA8B,0CAInC,CAAC;AAEF,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,4BAA4B,EAC5B,8BAA8B,GAC/B,CAAC"}
@@ -4,6 +4,7 @@ exports.changeGoalDistanceByNivelActual10k = exports.getPlanificacionPrueba7dias
4
4
  const getPlanificacionPrueba7dias10kPrimer_1 = require("./getPlanificacionPrueba7dias10kPrimer");
5
5
  const getPlanificacionPrueba7dias10kMas50_1 = require("./getPlanificacionPrueba7dias10kMas50");
6
6
  const getPlanificacionPrueba7dias10kSub50_1 = require("./getPlanificacionPrueba7dias10kSub50");
7
+ const common_1 = require("../common");
7
8
  const getPlanificacionPrueba7dias10k = (c) => {
8
9
  if (c.type === 'PRIMER')
9
10
  return (0, getPlanificacionPrueba7dias10kPrimer_1.getPlanificacionPrueba7dias10kPrimer)(c);
@@ -16,11 +17,13 @@ const getPlanificacionPrueba7dias10k = (c) => {
16
17
  exports.getPlanificacionPrueba7dias10k = getPlanificacionPrueba7dias10k;
17
18
  const changeGoalDistanceByNivelActual10k = (c) => {
18
19
  c.goalDistance = 10000;
19
- if (!c.vamDuration)
20
+ // Marca real si la hay; si no, estimación desde el rodaje declarado; si no, PRIMER.
21
+ const ref = (0, common_1.getDuracionReferenciaPlantilla)(c, 10000);
22
+ if (ref === null)
20
23
  c.type = 'PRIMER';
21
- else if (Number(c.vamDuration) < 52 * 60)
24
+ else if (ref < 52 * 60)
22
25
  c.type = 'SUB50';
23
- else if (Number(c.vamDuration) >= 52 * 60 && Number(c.vamDuration) < 62 * 60)
26
+ else if (ref < 62 * 60)
24
27
  c.type = 'MAS50';
25
28
  else
26
29
  c.type = 'PRIMER';
@@ -4,6 +4,7 @@ exports.changeGoalDistanceByNivelActual21k = exports.getPlanificacionPrueba7dias
4
4
  const getPlanificacionPrueba7dias21kPrimer_1 = require("./getPlanificacionPrueba7dias21kPrimer");
5
5
  const getPlanificacionPrueba7dias21kMas105_1 = require("./getPlanificacionPrueba7dias21kMas105");
6
6
  const getPlanificacionPrueba7dias21kSub105_1 = require("./getPlanificacionPrueba7dias21kSub105");
7
+ const common_1 = require("../common");
7
8
  const getPlanificacionPrueba7dias21k = (c) => {
8
9
  if (c.type === 'PRIMER')
9
10
  return (0, getPlanificacionPrueba7dias21kPrimer_1.getPlanificacionPrueba7dias21kPrimer)(c);
@@ -16,13 +17,15 @@ const getPlanificacionPrueba7dias21k = (c) => {
16
17
  exports.getPlanificacionPrueba7dias21k = getPlanificacionPrueba7dias21k;
17
18
  const changeGoalDistanceByNivelActual21k = (c) => {
18
19
  c.goalDistance = 21000;
19
- // Si es su primer maratón o el anterior lo hizo en más de 2h10min
20
- if (!c.vamDuration || Number(c.vamDuration) > 130 * 60)
20
+ // Marca real si la hay; si no, estimación desde el rodaje declarado; si no, PRIMER.
21
+ const ref = (0, common_1.getDuracionReferenciaPlantilla)(c, 21000);
22
+ // Sin referencia, o más de 2h10min
23
+ if (ref === null || ref > 130 * 60)
21
24
  c.type = 'PRIMER';
22
- // Si el anterior lo hizo en menos de 2h10min y más de 1h50min
23
- else if (Number(c.vamDuration) > 110 * 60 && Number(c.vamDuration) <= 130 * 60)
25
+ // Entre 1h50min y 2h10min
26
+ else if (ref > 110 * 60)
24
27
  c.type = 'MAS105';
25
- // Si el anterior lo hizo en menos de 1h50min
28
+ // Menos de 1h50min
26
29
  else
27
30
  c.type = 'SUB105';
28
31
  };
@@ -4,6 +4,7 @@ exports.changeGoalDistanceByNivelActual42k = exports.getPlanificacionPrueba7dias
4
4
  const getPlanificacionPrueba7dias42kPrimer_1 = require("./getPlanificacionPrueba7dias42kPrimer");
5
5
  const getPlanificacionPrueba7dias42kMas210_1 = require("./getPlanificacionPrueba7dias42kMas210");
6
6
  const getPlanificacionPrueba7dias42kSub210_1 = require("./getPlanificacionPrueba7dias42kSub210");
7
+ const common_1 = require("../common");
7
8
  const getPlanificacionPrueba7dias42k = (c) => {
8
9
  if (c.type === 'PRIMER')
9
10
  return (0, getPlanificacionPrueba7dias42kPrimer_1.getPlanificacionPrueba7dias42kPrimer)(c);
@@ -16,13 +17,15 @@ const getPlanificacionPrueba7dias42k = (c) => {
16
17
  exports.getPlanificacionPrueba7dias42k = getPlanificacionPrueba7dias42k;
17
18
  const changeGoalDistanceByNivelActual42k = (c) => {
18
19
  c.goalDistance = 42000;
19
- // Si es su primer maratón o el anterior lo hizo en más de 4h30min
20
- if (!c.vamDuration || Number(c.vamDuration) > 270 * 60)
20
+ // Marca real si la hay; si no, estimación desde el rodaje declarado; si no, PRIMER.
21
+ const ref = (0, common_1.getDuracionReferenciaPlantilla)(c, 42000);
22
+ // Sin referencia, o más de 4h30min
23
+ if (ref === null || ref > 270 * 60)
21
24
  c.type = 'PRIMER';
22
- // Si el anterior lo hizo en menos de 4h30min y más de 3h45min
23
- else if (Number(c.vamDuration) > 225 * 60 && Number(c.vamDuration) <= 270 * 60)
25
+ // Entre 3h45min y 4h30min
26
+ else if (ref > 225 * 60)
24
27
  c.type = 'MAS210';
25
- // Si el anterior lo hizo en menos de 3h45min
28
+ // Menos de 3h45min
26
29
  else
27
30
  c.type = 'SUB210';
28
31
  };
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.changeGoalDistanceByNivelActual5k = exports.getPlanificacionPrueba7dias5k = void 0;
4
4
  const getPlanificacionPrueba7dias5kPrimer_1 = require("./getPlanificacionPrueba7dias5kPrimer");
5
5
  const getPlanificacionPrueba7dias5kSub30_1 = require("./getPlanificacionPrueba7dias5kSub30");
6
+ const common_1 = require("../common");
6
7
  const getPlanificacionPrueba7dias5k = (c) => {
7
8
  if (c.type === 'PRIMER')
8
9
  return (0, getPlanificacionPrueba7dias5kPrimer_1.getPlanificacionPrueba7dias5kPrimer)(c);
@@ -13,7 +14,9 @@ const getPlanificacionPrueba7dias5k = (c) => {
13
14
  exports.getPlanificacionPrueba7dias5k = getPlanificacionPrueba7dias5k;
14
15
  const changeGoalDistanceByNivelActual5k = (c) => {
15
16
  c.goalDistance = 5000;
16
- if (!c.vamDuration || Number(c.vamDuration) > 32 * 60)
17
+ // Marca real si la hay; si no, estimación desde el rodaje declarado; si no, PRIMER.
18
+ const ref = (0, common_1.getDuracionReferenciaPlantilla)(c, 5000);
19
+ if (ref === null || ref > 32 * 60)
17
20
  c.type = 'PRIMER';
18
21
  else
19
22
  c.type = 'SUB30';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.prettyPace = exports.calculateDuration = exports.calculateDistance = exports.paceByZone = void 0;
3
+ exports.getDuracionReferenciaPlantilla = exports.estimarDuracionDesdePaceSlow = exports.prettyPace = exports.calculateDuration = exports.calculateDistance = exports.paceByZone = void 0;
4
4
  const paceByZone = (c, zone) => {
5
5
  if (zone === 1)
6
6
  return c.paceZone1;
@@ -32,3 +32,51 @@ const prettyPace = (stringPace) => {
32
32
  return `${Number(m)}'${s}"/km`;
33
33
  };
34
34
  exports.prettyPace = prettyPace;
35
+ /**
36
+ * Duración de carrera estimada (segundos) a partir del rodaje suave declarado.
37
+ * Misma relación que el resto del código: el "100%" es el 70% del rodaje
38
+ * (calcularZonasDesdePaceSlow, calculateInitialPaces) y de ahí sale el ritmo de
39
+ * carrera según la distancia (espejo de calculateTimeFromPace del onboarding).
40
+ *
41
+ * @param {number|string} paceSlowSeg - Rodaje suave en seg/km.
42
+ * @param {number} distanciaMetros - Distancia objetivo en metros.
43
+ * @returns {number|null} Segundos estimados, o null si no hay ritmo utilizable.
44
+ */
45
+ const estimarDuracionDesdePaceSlow = (paceSlowSeg, distanciaMetros) => {
46
+ const paceRef = Number(paceSlowSeg);
47
+ const dist = Number(distanciaMetros);
48
+ if (!Number.isFinite(paceRef) || paceRef <= 0)
49
+ return null;
50
+ if (!Number.isFinite(dist) || dist <= 0)
51
+ return null;
52
+ const pace100 = paceRef * 0.7;
53
+ const paceCarrera = dist < 6000 ? pace100 / 0.8 : pace100 / 0.7;
54
+ return (dist * paceCarrera) / 1000;
55
+ };
56
+ exports.estimarDuracionDesdePaceSlow = estimarDuracionDesdePaceSlow;
57
+ /**
58
+ * Duración de referencia (segundos) para ELEGIR la plantilla de la semana. Orden:
59
+ * 1) marca real declarada (vamDuration > 0)
60
+ * 2) estimación desde el rodaje suave declarado
61
+ * 3) null → el selector cae en la plantilla más conservadora (PRIMER)
62
+ *
63
+ * Motivo: `vamDuration` llega como STRING desde Postgres, así que un "0" es
64
+ * truthy y `!c.vamDuration` NO lo detecta. Sin esta normalización, una marca
65
+ * inválida caía en la plantilla MÁS DURA de cada distancia (SUB30/SUB50/
66
+ * SUB105/SUB210) — justo la peor dirección de fallo.
67
+ *
68
+ * OJO: no escribir el resultado en c.vamDuration. `tieneMarcaVerificada`
69
+ * distingue marca REAL de estimación para decidir si la semana puede arrancar
70
+ * con una sesión de calidad; una estimación no debe contar como marca.
71
+ *
72
+ * @param {Object} c - Cliente (vamDuration, paceSlow).
73
+ * @param {number} distanciaMetros - Distancia objetivo en metros.
74
+ * @returns {number|null} Segundos de referencia, o null si no hay nada fiable.
75
+ */
76
+ const getDuracionReferenciaPlantilla = (c, distanciaMetros) => {
77
+ const declarada = Number(c === null || c === void 0 ? void 0 : c.vamDuration);
78
+ if (Number.isFinite(declarada) && declarada > 0)
79
+ return declarada;
80
+ return estimarDuracionDesdePaceSlow(c === null || c === void 0 ? void 0 : c.paceSlow, distanciaMetros);
81
+ };
82
+ exports.getDuracionReferenciaPlantilla = getDuracionReferenciaPlantilla;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runnerpro/backend",
3
- "version": "1.22.3",
3
+ "version": "1.22.5",
4
4
  "description": "A collection of common backend functions",
5
5
  "exports": {
6
6
  ".": "./lib/cjs/index.js"