hvp-shared 13.2.0 → 13.4.0

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,161 @@
1
+ /**
2
+ * Google Calendar Integration — Constants
3
+ *
4
+ * Implements the appointment standard documented in
5
+ * `resources/notes/google-calendar/standard-v1.md`.
6
+ *
7
+ * Consumed by:
8
+ * - Backend diagnostic engine (parser + scorer)
9
+ * - Frontend dashboard (dimension labels, weights)
10
+ *
11
+ * Changes to scoring rules require a new version (STANDARD_V2) so previous
12
+ * runs remain reproducible.
13
+ */
14
+ import type { AppointmentBranch, AppointmentStandard } from "../contracts/google-calendar/responses";
15
+ /**
16
+ * Multi-factor appointment standard v2.
17
+ *
18
+ * Ten dimensions covering everything a recepción needs to capture to make an
19
+ * appointment actionable: branch, title format, pet identity (name/age/breed),
20
+ * tutor identity (name/phone), motive, and scheduling provenance (who and when).
21
+ *
22
+ * Weights sum to 100. An event is "compliant" when its score >= 80.
23
+ */
24
+ export declare const STANDARD_V2: AppointmentStandard;
25
+ /**
26
+ * Backward-compatible alias for existing code/tests that imported `STANDARD_V1`.
27
+ * Always points to the current standard.
28
+ *
29
+ * @deprecated Use `STANDARD_V2` (or whatever the current version is) explicitly.
30
+ */
31
+ export declare const STANDARD_V1: AppointmentStandard;
32
+ /**
33
+ * Google Calendar colorId values mapped to HVP branches.
34
+ *
35
+ * Confirmed via Phase 0 discovery (2026-05-18):
36
+ * - 11 (Tomato) → Urban
37
+ * - 9 (Blueberry) → Harbor
38
+ * - 3 (Grape) → Montejo (citas) + recordatorios operacionales
39
+ */
40
+ export declare const BRANCH_COLOR_IDS: Readonly<Record<AppointmentBranch, string>>;
41
+ /**
42
+ * Reverse lookup: colorId → branch.
43
+ */
44
+ export declare const COLOR_ID_TO_BRANCH: Readonly<Record<string, AppointmentBranch>>;
45
+ /** colorId 8 (Graphite) = cancelled. Tracked as a separate metric. */
46
+ export declare const CANCELLED_COLOR_ID = "8";
47
+ /**
48
+ * colorIds reserved for special events (NOT patient appointments).
49
+ *
50
+ * - 2 (Sage) — aniversario HVP, "no agendar nada", recordatorios admin
51
+ * - 4 (Flamingo) — cumpleaños colaboradores
52
+ * - 5 (Banana) — bloqueos (vacaciones, cursos, NO AGENDAR)
53
+ * - 10 (Basil) — reuniones, eventos importantes
54
+ */
55
+ export declare const EXCLUDED_COLOR_IDS: ReadonlySet<string>;
56
+ /**
57
+ * Canonical service codes — the diagnostic engine's source of truth.
58
+ *
59
+ * The parser maps free-form prefixes (via SERVICE_CODE_SYNONYMS) to one of these.
60
+ */
61
+ export declare const CANONICAL_SERVICE_CODES: readonly ["CONSULTA", "SIN_CITA", "CONSULTA_SEGUIMIENTO", "VACUNA", "CIRUGIA", "CERTIFICADO", "ULTRASONIDO", "RADIOGRAFIA", "ECOCARDIO", "OVH", "PROFILAXIS_DENTAL", "ANESTESIA", "EUTANASIA", "RETIRO_PUNTOS", "REVISION", "DESPARASITACION", "APLICACION", "TOMA_MUESTRAS", "TERAPIA_MEDICA", "INYECCION"];
62
+ export type CanonicalServiceCode = (typeof CANONICAL_SERVICE_CODES)[number];
63
+ /**
64
+ * Maps free-form prefixes (uppercase, trimmed, period-stripped) to canonical codes.
65
+ *
66
+ * Example: title "C. OFT LUKE RGL*" → first token "C" → CONSULTA.
67
+ */
68
+ export declare const SERVICE_CODE_SYNONYMS: Readonly<Record<string, CanonicalServiceCode>>;
69
+ /**
70
+ * Title prefixes (uppercase, first token) that mark NON-clinical events.
71
+ *
72
+ * Diagnostic engine excludes these — they are not patient appointments.
73
+ */
74
+ export declare const EXCLUDED_TITLE_PREFIXES: ReadonlySet<string>;
75
+ /**
76
+ * Detects an asterisk at the end of the title (after optional whitespace).
77
+ * `*` at the end = "médico preferido" = the vet earns a commission.
78
+ *
79
+ * Examples that match:
80
+ * "C. ARCHIE AAT*"
81
+ * "C. ARCHIE AAT *"
82
+ * "AM CAMILA APL*"
83
+ */
84
+ export declare const PREFERRED_VET_REGEX: RegExp;
85
+ /**
86
+ * Detects "ENVIAR RECORDATORIOS *" titles (Montejo recordatorios). Even though
87
+ * they live in colorId 3 (Montejo), they are NOT patient appointments.
88
+ */
89
+ export declare const REMINDER_TITLE_REGEX: RegExp;
90
+ /**
91
+ * Detects cancelled events by title content. Used in addition to colorId 8.
92
+ */
93
+ export declare const CANCELLED_TITLE_REGEX: RegExp;
94
+ /**
95
+ * Matches a QVET client id ("Q123456").
96
+ * Range chosen wide enough to cover historical and future ids.
97
+ */
98
+ export declare const QVET_ID_REGEX: RegExp;
99
+ /**
100
+ * Captures Mexican phone numbers in any of the common written formats.
101
+ * Returns the full match; caller normalizes to last 10 digits.
102
+ *
103
+ * Tolerates:
104
+ * +52 1 999 442 9488
105
+ * +52 999 442 9488
106
+ * 999 442 9488
107
+ * 999-442-9488
108
+ * 9994429488
109
+ */
110
+ export declare const PHONE_REGEX: RegExp;
111
+ /**
112
+ * Captures the "agendado por" col_code from a description.
113
+ *
114
+ * Variants observed:
115
+ * "AGENDO XZA 16 MAYO"
116
+ * "AG YMP 14.05.26"
117
+ * "AGENDADA POR SLR 02/12/25"
118
+ * "AGENDADO POR SLR 02/12/25"
119
+ */
120
+ export declare const SCHEDULER_REGEX: RegExp;
121
+ /**
122
+ * Captures the "agendado por" col_code AND the date annotation that often follows it.
123
+ *
124
+ * "AGENDO XZA 16 MAYO" → groups: XZA, "16 MAYO"
125
+ * "AG YMP 14.05.26" → groups: YMP, "14.05.26"
126
+ * "AGENDADA POR SLR 02/12/25" → groups: SLR, "02/12/25"
127
+ * "AGENDADO POR SLR" → groups: SLR, undefined
128
+ */
129
+ export declare const SCHEDULER_WITH_DATE_REGEX: RegExp;
130
+ /**
131
+ * Captures pet age expressions in Spanish from descriptions.
132
+ *
133
+ * "YORKIE 4 MESES" → "4 MESES"
134
+ * "11 MESES, COCKER SPANIEL" → "11 MESES"
135
+ * "1 año y 4 meses" → "1 año y 4 meses"
136
+ * "8 semanas" → "8 semanas"
137
+ * "2 AÑOS" → "2 AÑOS"
138
+ */
139
+ export declare const PET_AGE_REGEX: RegExp;
140
+ /**
141
+ * Common dog/cat breeds observed in HVP descriptions. Used by the parser to
142
+ * detect a breed mention. Match is case-insensitive whole-word.
143
+ *
144
+ * NOT exhaustive — extend as new breeds appear in real data.
145
+ */
146
+ export declare const KNOWN_BREEDS: readonly string[];
147
+ /**
148
+ * Keys used in Google Calendar `extendedProperties.private` for events created
149
+ * from HVP. Phase 2 writes these; Phase 1 reads them for full attribution.
150
+ */
151
+ export declare const HVP_EVENT_PROP_KEYS: {
152
+ readonly qvetClientId: "qvetClientId";
153
+ readonly qvetPetId: "qvetPetId";
154
+ readonly serviceCode: "serviceCode";
155
+ readonly branchId: "branchId";
156
+ readonly vetColCode: "vetColCode";
157
+ readonly scheduledByColCode: "scheduledByColCode";
158
+ readonly isPreferredVet: "isPreferredVet";
159
+ readonly source: "source";
160
+ readonly standardVersion: "standardVersion";
161
+ };
@@ -0,0 +1,428 @@
1
+ "use strict";
2
+ /**
3
+ * Google Calendar Integration — Constants
4
+ *
5
+ * Implements the appointment standard documented in
6
+ * `resources/notes/google-calendar/standard-v1.md`.
7
+ *
8
+ * Consumed by:
9
+ * - Backend diagnostic engine (parser + scorer)
10
+ * - Frontend dashboard (dimension labels, weights)
11
+ *
12
+ * Changes to scoring rules require a new version (STANDARD_V2) so previous
13
+ * runs remain reproducible.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.HVP_EVENT_PROP_KEYS = exports.KNOWN_BREEDS = exports.PET_AGE_REGEX = exports.SCHEDULER_WITH_DATE_REGEX = exports.SCHEDULER_REGEX = exports.PHONE_REGEX = exports.QVET_ID_REGEX = exports.CANCELLED_TITLE_REGEX = exports.REMINDER_TITLE_REGEX = exports.PREFERRED_VET_REGEX = exports.EXCLUDED_TITLE_PREFIXES = exports.SERVICE_CODE_SYNONYMS = exports.CANONICAL_SERVICE_CODES = exports.EXCLUDED_COLOR_IDS = exports.CANCELLED_COLOR_ID = exports.COLOR_ID_TO_BRANCH = exports.BRANCH_COLOR_IDS = exports.STANDARD_V1 = exports.STANDARD_V2 = void 0;
17
+ // ─── Standard v1 ─────────────────────────────────────────────────────────────
18
+ /**
19
+ * Multi-factor appointment standard v2.
20
+ *
21
+ * Ten dimensions covering everything a recepción needs to capture to make an
22
+ * appointment actionable: branch, title format, pet identity (name/age/breed),
23
+ * tutor identity (name/phone), motive, and scheduling provenance (who and when).
24
+ *
25
+ * Weights sum to 100. An event is "compliant" when its score >= 80.
26
+ */
27
+ exports.STANDARD_V2 = {
28
+ version: "2.0",
29
+ minimumCompliantScore: 80,
30
+ dimensions: [
31
+ {
32
+ id: "branch",
33
+ weight: 10,
34
+ label: "Sucursal",
35
+ description: "El evento tiene un color asignado (Urban=rojo, Harbor=azul, Montejo=morado). Sin color, no se puede asignar carga ni recursos por sucursal.",
36
+ },
37
+ {
38
+ id: "title",
39
+ weight: 10,
40
+ label: "Título bien formado",
41
+ description: "El título empieza con un código de servicio del diccionario (C, V, SC, CS, CX, USG, CERT, etc.).",
42
+ },
43
+ {
44
+ id: "pet_name",
45
+ weight: 10,
46
+ label: "Nombre de la mascota",
47
+ description: "Nombre de la mascota detectable en el título.",
48
+ },
49
+ {
50
+ id: "pet_age",
51
+ weight: 8,
52
+ label: "Edad de la mascota",
53
+ description: 'Edad anotada en la descripción (ej. "4 meses", "1 año", "8 semanas"). Importante para vacunación, dosis y diagnóstico.',
54
+ },
55
+ {
56
+ id: "pet_breed",
57
+ weight: 8,
58
+ label: "Raza de la mascota",
59
+ description: 'Raza detectada en la descripción (ej. "Yorkie", "Shih Tzu", "Cocker Spaniel"). Útil para protocolos clínicos.',
60
+ },
61
+ {
62
+ id: "owner_name",
63
+ weight: 12,
64
+ label: "Nombre del tutor",
65
+ description: "Nombre del cliente/tutor detectable en la descripción, o linkeable a QVET vía Q{id} (auto-pasa).",
66
+ },
67
+ {
68
+ id: "owner_phone",
69
+ weight: 12,
70
+ label: "Teléfono del tutor",
71
+ description: "Teléfono del tutor en la descripción (cualquier formato común). Necesario para confirmar y avisar de cambios.",
72
+ },
73
+ {
74
+ id: "context",
75
+ weight: 10,
76
+ label: "Motivo / contexto",
77
+ description: 'Descripción tiene contexto clínico relevante (motivo, síntomas, antecedentes). >20 caracteres tras descontar nombre/teléfono/Q{id}.',
78
+ },
79
+ {
80
+ id: "scheduler",
81
+ weight: 10,
82
+ label: "Iniciales de quien agendó",
83
+ description: 'Iniciales del colaborador que agendó la cita ("AGENDO XZA", "AG YMP"). Permite reporte de productividad por persona.',
84
+ },
85
+ {
86
+ id: "scheduling_date",
87
+ weight: 10,
88
+ label: "Fecha en que se agendó",
89
+ description: 'Fecha de la anotación de quién agendó (ej. "AGENDO XZA 16 MAYO"). Permite medir lead time entre agendado y atención.',
90
+ },
91
+ ],
92
+ };
93
+ /**
94
+ * Backward-compatible alias for existing code/tests that imported `STANDARD_V1`.
95
+ * Always points to the current standard.
96
+ *
97
+ * @deprecated Use `STANDARD_V2` (or whatever the current version is) explicitly.
98
+ */
99
+ exports.STANDARD_V1 = exports.STANDARD_V2;
100
+ // ─── ColorId → Branch mapping ────────────────────────────────────────────────
101
+ /**
102
+ * Google Calendar colorId values mapped to HVP branches.
103
+ *
104
+ * Confirmed via Phase 0 discovery (2026-05-18):
105
+ * - 11 (Tomato) → Urban
106
+ * - 9 (Blueberry) → Harbor
107
+ * - 3 (Grape) → Montejo (citas) + recordatorios operacionales
108
+ */
109
+ exports.BRANCH_COLOR_IDS = {
110
+ urban: "11",
111
+ harbor: "9",
112
+ montejo: "3",
113
+ };
114
+ /**
115
+ * Reverse lookup: colorId → branch.
116
+ */
117
+ exports.COLOR_ID_TO_BRANCH = {
118
+ "11": "urban",
119
+ "9": "harbor",
120
+ "3": "montejo",
121
+ };
122
+ // ─── Excluded colorIds ───────────────────────────────────────────────────────
123
+ /** colorId 8 (Graphite) = cancelled. Tracked as a separate metric. */
124
+ exports.CANCELLED_COLOR_ID = "8";
125
+ /**
126
+ * colorIds reserved for special events (NOT patient appointments).
127
+ *
128
+ * - 2 (Sage) — aniversario HVP, "no agendar nada", recordatorios admin
129
+ * - 4 (Flamingo) — cumpleaños colaboradores
130
+ * - 5 (Banana) — bloqueos (vacaciones, cursos, NO AGENDAR)
131
+ * - 10 (Basil) — reuniones, eventos importantes
132
+ */
133
+ exports.EXCLUDED_COLOR_IDS = new Set([
134
+ "2",
135
+ "4",
136
+ "5",
137
+ "10",
138
+ ]);
139
+ // ─── Service codes ───────────────────────────────────────────────────────────
140
+ /**
141
+ * Canonical service codes — the diagnostic engine's source of truth.
142
+ *
143
+ * The parser maps free-form prefixes (via SERVICE_CODE_SYNONYMS) to one of these.
144
+ */
145
+ exports.CANONICAL_SERVICE_CODES = [
146
+ "CONSULTA",
147
+ "SIN_CITA",
148
+ "CONSULTA_SEGUIMIENTO",
149
+ "VACUNA",
150
+ "CIRUGIA",
151
+ "CERTIFICADO",
152
+ "ULTRASONIDO",
153
+ "RADIOGRAFIA",
154
+ "ECOCARDIO",
155
+ "OVH",
156
+ "PROFILAXIS_DENTAL",
157
+ "ANESTESIA",
158
+ "EUTANASIA",
159
+ "RETIRO_PUNTOS",
160
+ "REVISION",
161
+ "DESPARASITACION",
162
+ "APLICACION",
163
+ "TOMA_MUESTRAS",
164
+ "TERAPIA_MEDICA",
165
+ "INYECCION",
166
+ ];
167
+ /**
168
+ * Maps free-form prefixes (uppercase, trimmed, period-stripped) to canonical codes.
169
+ *
170
+ * Example: title "C. OFT LUKE RGL*" → first token "C" → CONSULTA.
171
+ */
172
+ exports.SERVICE_CODE_SYNONYMS = {
173
+ // Consulta
174
+ C: "CONSULTA",
175
+ CONSULTA: "CONSULTA",
176
+ // Sin Cita (walk-in)
177
+ SC: "SIN_CITA",
178
+ // Consulta Seguimiento
179
+ CS: "CONSULTA_SEGUIMIENTO",
180
+ // Vacuna
181
+ V: "VACUNA",
182
+ VAC: "VACUNA",
183
+ VACUNA: "VACUNA",
184
+ // Cirugía
185
+ CX: "CIRUGIA",
186
+ CIRUGIA: "CIRUGIA",
187
+ CIRUGÍA: "CIRUGIA",
188
+ // Certificado
189
+ CERT: "CERTIFICADO",
190
+ CERTIF: "CERTIFICADO",
191
+ CERTIFICADO: "CERTIFICADO",
192
+ // Ultrasonido
193
+ USG: "ULTRASONIDO",
194
+ ULTRASONIDO: "ULTRASONIDO",
195
+ // Radiografía
196
+ RX: "RADIOGRAFIA",
197
+ RADIOGRAFIA: "RADIOGRAFIA",
198
+ RADIOGRAFÍA: "RADIOGRAFIA",
199
+ // Ecocardio
200
+ ECO: "ECOCARDIO",
201
+ ECOCARDIOGRAMA: "ECOCARDIO",
202
+ // OVH (ovariohisterectomía)
203
+ OVH: "OVH",
204
+ // Profilaxis dental
205
+ PROFILAXIS: "PROFILAXIS_DENTAL",
206
+ LIMPIEZA: "PROFILAXIS_DENTAL",
207
+ // Anestesia
208
+ ANESTESIA: "ANESTESIA",
209
+ // Eutanasia
210
+ EUTANASIA: "EUTANASIA",
211
+ // Retiro de puntos / vendaje
212
+ RETIRO: "RETIRO_PUNTOS",
213
+ RET: "RETIRO_PUNTOS",
214
+ // Revisión
215
+ REV: "REVISION",
216
+ REVISION: "REVISION",
217
+ REVISIÓN: "REVISION",
218
+ // Desparasitación
219
+ D: "DESPARASITACION",
220
+ DES: "DESPARASITACION",
221
+ DESPARASITACION: "DESPARASITACION",
222
+ DESPARASITACIÓN: "DESPARASITACION",
223
+ // Aplicación de medicamento / microchip / etc.
224
+ AP: "APLICACION",
225
+ APL: "APLICACION",
226
+ AM: "APLICACION",
227
+ APLI: "APLICACION",
228
+ APLICACION: "APLICACION",
229
+ APLICACIÓN: "APLICACION",
230
+ // Toma de muestras
231
+ TM: "TOMA_MUESTRAS",
232
+ // Terapia médica (visto como "T.M." → TM)
233
+ T: "TERAPIA_MEDICA",
234
+ // Inyección
235
+ INYECCION: "INYECCION",
236
+ INYECCIÓN: "INYECCION",
237
+ };
238
+ /**
239
+ * Title prefixes (uppercase, first token) that mark NON-clinical events.
240
+ *
241
+ * Diagnostic engine excludes these — they are not patient appointments.
242
+ */
243
+ exports.EXCLUDED_TITLE_PREFIXES = new Set([
244
+ "RECIBIR",
245
+ "ENTREGAR",
246
+ "DESPACHAR",
247
+ "DESP",
248
+ "ENVIAR",
249
+ "RECEPCION",
250
+ "RECEPCIÓN",
251
+ "RECORDATORIO",
252
+ "RECORDATORIOS",
253
+ "ENTREVISTA",
254
+ "REUNION",
255
+ "REUNIÓN",
256
+ "CUMPLE",
257
+ "CUMPLEAÑOS",
258
+ "ANIVERSARIO",
259
+ "VACACIONES",
260
+ "NO",
261
+ "IMPORTANTE",
262
+ ]);
263
+ // ─── Title patterns ──────────────────────────────────────────────────────────
264
+ /**
265
+ * Detects an asterisk at the end of the title (after optional whitespace).
266
+ * `*` at the end = "médico preferido" = the vet earns a commission.
267
+ *
268
+ * Examples that match:
269
+ * "C. ARCHIE AAT*"
270
+ * "C. ARCHIE AAT *"
271
+ * "AM CAMILA APL*"
272
+ */
273
+ exports.PREFERRED_VET_REGEX = /\*\s*$/;
274
+ /**
275
+ * Detects "ENVIAR RECORDATORIOS *" titles (Montejo recordatorios). Even though
276
+ * they live in colorId 3 (Montejo), they are NOT patient appointments.
277
+ */
278
+ exports.REMINDER_TITLE_REGEX = /^ENVIAR\s+RECORDATORIOS\b/i;
279
+ /**
280
+ * Detects cancelled events by title content. Used in addition to colorId 8.
281
+ */
282
+ exports.CANCELLED_TITLE_REGEX = /\b(?:CANCEL(?:ADA|ADO))\b|\bse\s+cancela\b/i;
283
+ // ─── Description patterns ────────────────────────────────────────────────────
284
+ /**
285
+ * Matches a QVET client id ("Q123456").
286
+ * Range chosen wide enough to cover historical and future ids.
287
+ */
288
+ exports.QVET_ID_REGEX = /Q\d{4,7}/;
289
+ /**
290
+ * Captures Mexican phone numbers in any of the common written formats.
291
+ * Returns the full match; caller normalizes to last 10 digits.
292
+ *
293
+ * Tolerates:
294
+ * +52 1 999 442 9488
295
+ * +52 999 442 9488
296
+ * 999 442 9488
297
+ * 999-442-9488
298
+ * 9994429488
299
+ */
300
+ exports.PHONE_REGEX = /(?:\+?52\s?1?\s?)?(?:\d{3}[\s-]?\d{3}[\s-]?\d{4}|\d{10})/;
301
+ /**
302
+ * Captures the "agendado por" col_code from a description.
303
+ *
304
+ * Variants observed:
305
+ * "AGENDO XZA 16 MAYO"
306
+ * "AG YMP 14.05.26"
307
+ * "AGENDADA POR SLR 02/12/25"
308
+ * "AGENDADO POR SLR 02/12/25"
309
+ */
310
+ exports.SCHEDULER_REGEX = /\b(?:AG|AGENDO|AGENDAD[OA]\s+POR)\s+([A-Z]{2,4})\b/i;
311
+ /**
312
+ * Captures the "agendado por" col_code AND the date annotation that often follows it.
313
+ *
314
+ * "AGENDO XZA 16 MAYO" → groups: XZA, "16 MAYO"
315
+ * "AG YMP 14.05.26" → groups: YMP, "14.05.26"
316
+ * "AGENDADA POR SLR 02/12/25" → groups: SLR, "02/12/25"
317
+ * "AGENDADO POR SLR" → groups: SLR, undefined
318
+ */
319
+ exports.SCHEDULER_WITH_DATE_REGEX = /\b(?:AG|AGENDO|AGENDAD[OA]\s+POR)\s+([A-Z]{2,4})(?:\s+(\d{1,2}[./\-]\d{1,2}(?:[./\-]\d{2,4})?|\d{1,2}\s+(?:ENE|FEB|MAR|ABR|MAY|JUN|JUL|AGO|SEP|OCT|NOV|DIC|ENERO|FEBRERO|MARZO|ABRIL|MAYO|JUNIO|JULIO|AGOSTO|SEPTIEMBRE|OCTUBRE|NOVIEMBRE|DICIEMBRE)))?\b/i;
320
+ // ─── Pet age detection ───────────────────────────────────────────────────────
321
+ /**
322
+ * Captures pet age expressions in Spanish from descriptions.
323
+ *
324
+ * "YORKIE 4 MESES" → "4 MESES"
325
+ * "11 MESES, COCKER SPANIEL" → "11 MESES"
326
+ * "1 año y 4 meses" → "1 año y 4 meses"
327
+ * "8 semanas" → "8 semanas"
328
+ * "2 AÑOS" → "2 AÑOS"
329
+ */
330
+ exports.PET_AGE_REGEX = /\b\d+\s*(?:años?|añoss?|meses?|semanas?|días?|años?\s+y\s+\d+\s+meses?)\b/i;
331
+ // ─── Pet breed detection ────────────────────────────────────────────────────
332
+ /**
333
+ * Common dog/cat breeds observed in HVP descriptions. Used by the parser to
334
+ * detect a breed mention. Match is case-insensitive whole-word.
335
+ *
336
+ * NOT exhaustive — extend as new breeds appear in real data.
337
+ */
338
+ exports.KNOWN_BREEDS = [
339
+ // Perros
340
+ "yorkie",
341
+ "yorkshire",
342
+ "shih tzu",
343
+ "shihtzu",
344
+ "schnauzer",
345
+ "schnauzer mini",
346
+ "cocker spaniel",
347
+ "cocker",
348
+ "golden retriever",
349
+ "golden",
350
+ "labrador",
351
+ "labrador retriever",
352
+ "chihuahua",
353
+ "bulldog",
354
+ "bulldog frances",
355
+ "bulldog francés",
356
+ "bulldog ingles",
357
+ "bulldog inglés",
358
+ "french bulldog",
359
+ "poodle",
360
+ "french poodle",
361
+ "caniche",
362
+ "bichon",
363
+ "bichón",
364
+ "bichon maltes",
365
+ "maltes",
366
+ "maltés",
367
+ "border collie",
368
+ "border",
369
+ "pug",
370
+ "carlino",
371
+ "pastor aleman",
372
+ "pastor alemán",
373
+ "pastor",
374
+ "husky",
375
+ "husky siberiano",
376
+ "boxer",
377
+ "boxer aleman",
378
+ "pitbull",
379
+ "pit bull",
380
+ "american bully",
381
+ "doberman",
382
+ "rottweiler",
383
+ "dachshund",
384
+ "salchicha",
385
+ "beagle",
386
+ "dalmata",
387
+ "dalmata",
388
+ "san bernardo",
389
+ "akita",
390
+ "shar pei",
391
+ "sharpei",
392
+ "mestizo",
393
+ "criollo",
394
+ "xolo",
395
+ "xoloitzcuintle",
396
+ "chow chow",
397
+ // Gatos
398
+ "persa",
399
+ "siames",
400
+ "siamés",
401
+ "british shorthair",
402
+ "maine coon",
403
+ "ragdoll",
404
+ "bengala",
405
+ "esfinge",
406
+ "sphynx",
407
+ "mestizo",
408
+ "domestic shorthair",
409
+ "domestic short hair",
410
+ "domestico",
411
+ "doméstico",
412
+ ];
413
+ // ─── HVP-created extendedProperties keys ─────────────────────────────────────
414
+ /**
415
+ * Keys used in Google Calendar `extendedProperties.private` for events created
416
+ * from HVP. Phase 2 writes these; Phase 1 reads them for full attribution.
417
+ */
418
+ exports.HVP_EVENT_PROP_KEYS = {
419
+ qvetClientId: "qvetClientId",
420
+ qvetPetId: "qvetPetId",
421
+ serviceCode: "serviceCode",
422
+ branchId: "branchId",
423
+ vetColCode: "vetColCode",
424
+ scheduledByColCode: "scheduledByColCode",
425
+ isPreferredVet: "isPreferredVet",
426
+ source: "source", // "hvp"
427
+ standardVersion: "standardVersion",
428
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const google_calendar_constants_1 = require("./google-calendar.constants");
4
+ describe("STANDARD_V2", () => {
5
+ it("has weights that sum to 100", () => {
6
+ const total = google_calendar_constants_1.STANDARD_V2.dimensions.reduce((sum, d) => sum + d.weight, 0);
7
+ expect(total).toBe(100);
8
+ });
9
+ it("declares the ten expected dimensions", () => {
10
+ const ids = google_calendar_constants_1.STANDARD_V2.dimensions.map((d) => d.id).sort();
11
+ expect(ids).toEqual([
12
+ "branch",
13
+ "context",
14
+ "owner_name",
15
+ "owner_phone",
16
+ "pet_age",
17
+ "pet_breed",
18
+ "pet_name",
19
+ "scheduler",
20
+ "scheduling_date",
21
+ "title",
22
+ ]);
23
+ });
24
+ it("every dimension has a label and a description (used by the dashboard tooltip)", () => {
25
+ for (const dim of google_calendar_constants_1.STANDARD_V2.dimensions) {
26
+ expect(dim.label.length).toBeGreaterThan(0);
27
+ expect(dim.description.length).toBeGreaterThan(0);
28
+ }
29
+ });
30
+ it("has a sensible compliance threshold", () => {
31
+ expect(google_calendar_constants_1.STANDARD_V2.minimumCompliantScore).toBeGreaterThanOrEqual(50);
32
+ expect(google_calendar_constants_1.STANDARD_V2.minimumCompliantScore).toBeLessThanOrEqual(100);
33
+ });
34
+ it("uses semver-like version 2.x", () => {
35
+ expect(google_calendar_constants_1.STANDARD_V2.version).toMatch(/^2\.\d+$/);
36
+ });
37
+ });
38
+ describe("Branch ↔ colorId mapping", () => {
39
+ it("is a round-trip", () => {
40
+ for (const [branch, colorId] of Object.entries(google_calendar_constants_1.BRANCH_COLOR_IDS)) {
41
+ expect(google_calendar_constants_1.COLOR_ID_TO_BRANCH[colorId]).toBe(branch);
42
+ }
43
+ });
44
+ it("uses the discovery-confirmed colorIds", () => {
45
+ expect(google_calendar_constants_1.BRANCH_COLOR_IDS.urban).toBe("11");
46
+ expect(google_calendar_constants_1.BRANCH_COLOR_IDS.harbor).toBe("9");
47
+ expect(google_calendar_constants_1.BRANCH_COLOR_IDS.montejo).toBe("3");
48
+ });
49
+ it("does not collide with excluded or cancelled colorIds", () => {
50
+ for (const colorId of Object.values(google_calendar_constants_1.BRANCH_COLOR_IDS)) {
51
+ expect(google_calendar_constants_1.EXCLUDED_COLOR_IDS.has(colorId)).toBe(false);
52
+ expect(colorId).not.toBe(google_calendar_constants_1.CANCELLED_COLOR_ID);
53
+ }
54
+ });
55
+ });
56
+ describe("Service code synonyms", () => {
57
+ it("only maps to canonical codes", () => {
58
+ const canonicalSet = new Set(google_calendar_constants_1.CANONICAL_SERVICE_CODES);
59
+ for (const [synonym, canonical] of Object.entries(google_calendar_constants_1.SERVICE_CODE_SYNONYMS)) {
60
+ expect(canonicalSet.has(canonical)).toBe(true);
61
+ // synonyms should be uppercase (we normalize before lookup)
62
+ expect(synonym).toBe(synonym.toUpperCase());
63
+ }
64
+ });
65
+ it("covers every canonical code with at least one synonym", () => {
66
+ const reverseIndex = new Map();
67
+ for (const [syn, canonical] of Object.entries(google_calendar_constants_1.SERVICE_CODE_SYNONYMS)) {
68
+ const arr = reverseIndex.get(canonical) ?? [];
69
+ arr.push(syn);
70
+ reverseIndex.set(canonical, arr);
71
+ }
72
+ for (const canonical of google_calendar_constants_1.CANONICAL_SERVICE_CODES) {
73
+ expect(reverseIndex.get(canonical)?.length ?? 0).toBeGreaterThan(0);
74
+ }
75
+ });
76
+ it("collapses VACUNA variants", () => {
77
+ expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.V).toBe("VACUNA");
78
+ expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.VAC).toBe("VACUNA");
79
+ expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.VACUNA).toBe("VACUNA");
80
+ });
81
+ it("collapses APLICACION variants (AP/APL/AM)", () => {
82
+ expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.AP).toBe("APLICACION");
83
+ expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.APL).toBe("APLICACION");
84
+ expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.AM).toBe("APLICACION");
85
+ });
86
+ });
87
+ describe("Title regexes", () => {
88
+ it("detects the preferred-vet asterisk at the end of titles", () => {
89
+ expect(google_calendar_constants_1.PREFERRED_VET_REGEX.test("C. ARCHIE AAT*")).toBe(true);
90
+ expect(google_calendar_constants_1.PREFERRED_VET_REGEX.test("AM CAMILA APL* ")).toBe(true);
91
+ expect(google_calendar_constants_1.PREFERRED_VET_REGEX.test("CX. DANNA MAT*")).toBe(true);
92
+ });
93
+ it("does not flag asterisk in the middle of a title", () => {
94
+ expect(google_calendar_constants_1.PREFERRED_VET_REGEX.test("C. MILO *fpo IS A BUG")).toBe(false);
95
+ });
96
+ it("identifies Montejo reminder events", () => {
97
+ expect(google_calendar_constants_1.REMINDER_TITLE_REGEX.test("ENVIAR RECORDATORIOS MONT")).toBe(true);
98
+ expect(google_calendar_constants_1.REMINDER_TITLE_REGEX.test("enviar recordatorios mont")).toBe(true);
99
+ expect(google_calendar_constants_1.REMINDER_TITLE_REGEX.test("D. MILO")).toBe(false);
100
+ });
101
+ it("flags operational prefixes as excluded", () => {
102
+ expect(google_calendar_constants_1.EXCLUDED_TITLE_PREFIXES.has("RECIBIR")).toBe(true);
103
+ expect(google_calendar_constants_1.EXCLUDED_TITLE_PREFIXES.has("ENTREGAR")).toBe(true);
104
+ expect(google_calendar_constants_1.EXCLUDED_TITLE_PREFIXES.has("ENTREVISTA")).toBe(true);
105
+ expect(google_calendar_constants_1.EXCLUDED_TITLE_PREFIXES.has("C")).toBe(false);
106
+ });
107
+ });
108
+ describe("Description regexes", () => {
109
+ it("captures Q{id} client ids", () => {
110
+ expect("DIANA LAURA | KIRI | Q913562".match(google_calendar_constants_1.QVET_ID_REGEX)?.[0]).toBe("Q913562");
111
+ expect("Q966648".match(google_calendar_constants_1.QVET_ID_REGEX)?.[0]).toBe("Q966648");
112
+ expect("no qvet here".match(google_calendar_constants_1.QVET_ID_REGEX)).toBeNull();
113
+ });
114
+ it("captures phones in common Mexican formats", () => {
115
+ expect("+52 1 999 442 9488".match(google_calendar_constants_1.PHONE_REGEX)?.[0]).toBe("+52 1 999 442 9488");
116
+ expect("999 442 9488".match(google_calendar_constants_1.PHONE_REGEX)?.[0]).toBe("999 442 9488");
117
+ expect("9994429488".match(google_calendar_constants_1.PHONE_REGEX)?.[0]).toBe("9994429488");
118
+ expect("999-442-9488".match(google_calendar_constants_1.PHONE_REGEX)?.[0]).toBe("999-442-9488");
119
+ });
120
+ it("captures scheduler col_code from descriptions", () => {
121
+ const cases = [
122
+ ["AGENDO XZA 16 MAYO", "XZA"],
123
+ ["AG YMP 14.05.26", "YMP"],
124
+ ["AGENDADA POR SLR 02/12/25", "SLR"],
125
+ ["AGENDADO POR SCP", "SCP"],
126
+ ];
127
+ for (const [text, expected] of cases) {
128
+ const match = text.match(google_calendar_constants_1.SCHEDULER_REGEX);
129
+ expect(match?.[1]?.toUpperCase()).toBe(expected);
130
+ }
131
+ });
132
+ it("SCHEDULER_WITH_DATE_REGEX captures both col_code and date annotation", () => {
133
+ const cases = [
134
+ ["AGENDO XZA 16 MAYO", "XZA", "16 MAYO"],
135
+ ["AG YMP 14.05.26", "YMP", "14.05.26"],
136
+ ["AGENDADA POR SLR 02/12/25", "SLR", "02/12/25"],
137
+ ["AGENDADO POR SCP", "SCP", undefined],
138
+ ];
139
+ for (const [text, codeExp, dateExp] of cases) {
140
+ const match = text.match(google_calendar_constants_1.SCHEDULER_WITH_DATE_REGEX);
141
+ expect(match?.[1]?.toUpperCase()).toBe(codeExp);
142
+ if (dateExp) {
143
+ expect(match?.[2]?.toUpperCase()).toBe(dateExp.toUpperCase());
144
+ }
145
+ else {
146
+ expect(match?.[2]).toBeUndefined();
147
+ }
148
+ }
149
+ });
150
+ });
151
+ describe("PET_AGE_REGEX", () => {
152
+ it("detects age in months / years / weeks", () => {
153
+ expect("YORKIE 4 MESES".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]?.toUpperCase()).toBe("4 MESES");
154
+ expect("11 MESES, COCKER SPANIEL".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]?.toUpperCase()).toBe("11 MESES");
155
+ expect("Shih tzu 1 año y 4 meses".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]?.toLowerCase()).toContain("1 año");
156
+ expect("8 semanas".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]).toBe("8 semanas");
157
+ expect("2 AÑOS".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]).toBe("2 AÑOS");
158
+ });
159
+ it("does not match arbitrary numbers", () => {
160
+ expect("Peso: 8.7 Kg".match(google_calendar_constants_1.PET_AGE_REGEX)).toBeNull();
161
+ expect("Cytopoint 30MG".match(google_calendar_constants_1.PET_AGE_REGEX)).toBeNull();
162
+ });
163
+ });
164
+ describe("KNOWN_BREEDS", () => {
165
+ it("includes common breeds observed in the discovery data", () => {
166
+ const lower = google_calendar_constants_1.KNOWN_BREEDS.map((b) => b.toLowerCase());
167
+ expect(lower).toContain("yorkie");
168
+ expect(lower).toContain("shih tzu");
169
+ expect(lower).toContain("cocker spaniel");
170
+ expect(lower).toContain("french bulldog");
171
+ expect(lower).toContain("chihuahua");
172
+ });
173
+ it("includes both dog and cat breeds", () => {
174
+ const lower = google_calendar_constants_1.KNOWN_BREEDS.map((b) => b.toLowerCase());
175
+ expect(lower.some((b) => b.includes("persa"))).toBe(true);
176
+ expect(lower.some((b) => b.includes("maine"))).toBe(true);
177
+ });
178
+ });
@@ -28,3 +28,4 @@ export * from './settlement.enums';
28
28
  export * from './client-billing.enums';
29
29
  export * from './sat-income-invoice';
30
30
  export * from './global-invoice.enums';
31
+ export * from './google-calendar.constants';
@@ -44,3 +44,4 @@ __exportStar(require("./settlement.enums"), exports);
44
44
  __exportStar(require("./client-billing.enums"), exports);
45
45
  __exportStar(require("./sat-income-invoice"), exports);
46
46
  __exportStar(require("./global-invoice.enums"), exports);
47
+ __exportStar(require("./google-calendar.constants"), exports);
@@ -0,0 +1,2 @@
1
+ export * from './requests';
2
+ export * from './responses';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./requests"), exports);
18
+ __exportStar(require("./responses"), exports);
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Google Calendar Integration - Request Types
3
+ *
4
+ * Request contracts for the Google Calendar diagnostic + appointment management feature.
5
+ * See: resources/notes/google-calendar/standard-v1.md
6
+ */
7
+ /**
8
+ * Diagnose request — score appointments against the standard for a date window.
9
+ *
10
+ * @example GET /api/google-calendar/diagnose?from=2026-04-01&to=2026-05-01
11
+ */
12
+ export interface DiagnoseAppointmentsRequest {
13
+ /** ISO date (inclusive). Start of the window in Mexico_City timezone. */
14
+ from: string;
15
+ /** ISO date (inclusive). End of the window in Mexico_City timezone. */
16
+ to: string;
17
+ /** Filter results to a single branch. Optional. */
18
+ branchId?: 'urban' | 'harbor' | 'montejo';
19
+ /** Filter to events where this col_code is detected as the attending vet. Optional. */
20
+ vetColCode?: string;
21
+ }
22
+ /**
23
+ * Single-event diagnose request.
24
+ *
25
+ * @example GET /api/google-calendar/diagnose/event/:eventId
26
+ */
27
+ export interface DiagnoseEventRequest {
28
+ eventId: string;
29
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Google Calendar Integration - Request Types
4
+ *
5
+ * Request contracts for the Google Calendar diagnostic + appointment management feature.
6
+ * See: resources/notes/google-calendar/standard-v1.md
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Google Calendar Integration - Response Types
3
+ *
4
+ * Response contracts for the Google Calendar diagnostic feature.
5
+ * See: resources/notes/google-calendar/standard-v1.md
6
+ */
7
+ /**
8
+ * One of the six dimensions of the appointment standard.
9
+ */
10
+ export type AppointmentStandardDimensionId = 'branch' | 'title' | 'pet_name' | 'pet_age' | 'pet_breed' | 'owner_name' | 'owner_phone' | 'context' | 'scheduler' | 'scheduling_date';
11
+ /**
12
+ * One of the three branches.
13
+ */
14
+ export type AppointmentBranch = 'urban' | 'harbor' | 'montejo';
15
+ /**
16
+ * Definition of the multi-factor scoring standard used by the diagnostic engine.
17
+ *
18
+ * Defined in code as a constant (see constants/google-calendar.constants.ts).
19
+ * Sent to the frontend so the dashboard can render dimension names/weights without
20
+ * hardcoding.
21
+ */
22
+ export interface AppointmentStandard {
23
+ /** Semver-like version identifier (e.g. "1.0"). */
24
+ version: string;
25
+ /** Dimensions and their weights (must sum to 100). */
26
+ dimensions: ReadonlyArray<{
27
+ id: AppointmentStandardDimensionId;
28
+ weight: number;
29
+ /** Display label in Spanish. */
30
+ label: string;
31
+ /** One-sentence explanation of what the dimension evaluates (shown as a tooltip in the frontend). */
32
+ description: string;
33
+ }>;
34
+ /** Threshold above which an event is considered "compliant". */
35
+ minimumCompliantScore: number;
36
+ }
37
+ /**
38
+ * A single dimension result for one event.
39
+ */
40
+ export interface DiagnosticDimensionResult {
41
+ id: AppointmentStandardDimensionId;
42
+ passed: boolean;
43
+ weight: number;
44
+ /** Human-readable evidence ("Q966648 in description", "phone 999... matched", etc.) or null when missing. */
45
+ evidence: string | null;
46
+ }
47
+ /**
48
+ * Reason an event was excluded from scoring.
49
+ */
50
+ export type DiagnosticExclusionReason = 'cancelled' | 'blocker' | 'special' | 'operational' | 'interview' | 'reminder';
51
+ /**
52
+ * Full diagnostic result for one event.
53
+ */
54
+ export interface DiagnosticResult {
55
+ eventId: string;
56
+ /** ISO datetime in Mexico_City. */
57
+ eventStart: string;
58
+ /** Duration in minutes. */
59
+ durationMinutes: number;
60
+ /** Title as it appears in GCal. */
61
+ title: string;
62
+ /** htmlLink to the event in Google Calendar. */
63
+ htmlLink: string | null;
64
+ /** Score 0-100. */
65
+ score: number;
66
+ /** Score >= minimumCompliantScore. */
67
+ isCompliant: boolean;
68
+ /** Per-dimension results. */
69
+ dimensions: DiagnosticDimensionResult[];
70
+ /** Inferred branch from colorId or extendedProperties. Null if undetected. */
71
+ inferredBranch: AppointmentBranch | null;
72
+ /** Inferred canonical service code (e.g. "CONSULTA"). Null if undetected. */
73
+ inferredService: string | null;
74
+ /** Inferred pet name from the title. */
75
+ inferredPetName: string | null;
76
+ /** Pet age detected from description (e.g. "4 meses", "1 año"). Null if not found. */
77
+ inferredPetAge: string | null;
78
+ /** Pet breed detected from description (e.g. "YORKIE", "SHIH TZU"). Null if not found. */
79
+ inferredPetBreed: string | null;
80
+ /** Tutor (client) full name as detected from description. */
81
+ inferredOwnerName: string | null;
82
+ /** col_code of the attending vet, when detectable. */
83
+ inferredVetColCode: string | null;
84
+ /** col_code of the person who scheduled, when detectable in description. */
85
+ inferredSchedulerColCode: string | null;
86
+ /** Date string near the scheduler annotation (e.g. "16 MAYO", "02/12/25"). Null if not found. */
87
+ inferredSchedulingDate: string | null;
88
+ /** Q{id} or null. */
89
+ inferredQvetClientId: string | null;
90
+ /** Last 10 digits of phone, normalized. Null if not found. */
91
+ inferredPhone: string | null;
92
+ /** Asterisk at end of title => preferred vet => generates commission. */
93
+ isPreferredVet: boolean;
94
+ /** True iff event has extendedProperties.private (HVP-created). */
95
+ isHvpCreated: boolean;
96
+ }
97
+ /**
98
+ * Excluded event — does not have a score.
99
+ */
100
+ export interface DiagnosticExcludedEvent {
101
+ eventId: string;
102
+ eventStart: string;
103
+ title: string;
104
+ reason: DiagnosticExclusionReason;
105
+ }
106
+ /**
107
+ * Aggregated summary of diagnostic results for a window.
108
+ *
109
+ * @example GET /api/google-calendar/diagnose
110
+ */
111
+ export interface DiagnoseAppointmentsResponse {
112
+ /** Window inputs echoed back. */
113
+ window: {
114
+ from: string;
115
+ to: string;
116
+ };
117
+ /** Standard used for scoring (so frontend can render labels/weights). */
118
+ standard: AppointmentStandard;
119
+ /** Counts. */
120
+ totalFetched: number;
121
+ totalEvaluated: number;
122
+ totalExcluded: number;
123
+ totalCancelled: number;
124
+ /** totalEvaluated where score >= minimumCompliantScore. */
125
+ compliantCount: number;
126
+ /** Average score across evaluated events (0-100). */
127
+ averageScore: number;
128
+ /** Per-dimension breakdown: how many evaluated events passed each dimension. */
129
+ byDimension: Record<AppointmentStandardDimensionId, {
130
+ passed: number;
131
+ total: number;
132
+ }>;
133
+ /** Per-branch breakdown. */
134
+ byBranch: Record<AppointmentBranch | 'unknown', {
135
+ count: number;
136
+ avgScore: number;
137
+ }>;
138
+ /** Per-vet breakdown (top 20 by count). col_code => { totalCount, preferredCount }. */
139
+ byVet: Record<string, {
140
+ count: number;
141
+ preferredCount: number;
142
+ avgScore: number;
143
+ }>;
144
+ /** Detailed results per evaluated event (paginated by caller — capped at 500 in response for now). */
145
+ results: DiagnosticResult[];
146
+ /** Detailed excluded events (capped at 100). */
147
+ excluded: DiagnosticExcludedEvent[];
148
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Google Calendar Integration - Response Types
4
+ *
5
+ * Response contracts for the Google Calendar diagnostic feature.
6
+ * See: resources/notes/google-calendar/standard-v1.md
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -16,6 +16,7 @@ export * from './client-billing';
16
16
  export * from './global-invoice';
17
17
  export * from './inventory-report';
18
18
  export * from './google-contacts';
19
+ export * from './google-calendar';
19
20
  export * from './study-type-catalog';
20
21
  export * from './supplier-overlay';
21
22
  export * from './external-study';
@@ -32,6 +32,7 @@ __exportStar(require("./client-billing"), exports);
32
32
  __exportStar(require("./global-invoice"), exports);
33
33
  __exportStar(require("./inventory-report"), exports);
34
34
  __exportStar(require("./google-contacts"), exports);
35
+ __exportStar(require("./google-calendar"), exports);
35
36
  __exportStar(require("./study-type-catalog"), exports);
36
37
  __exportStar(require("./supplier-overlay"), exports);
37
38
  __exportStar(require("./external-study"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hvp-shared",
3
- "version": "13.2.0",
3
+ "version": "13.4.0",
4
4
  "description": "Shared types and utilities for HVP backend and frontend",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",