@fhiron/sdk 0.1.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.
- package/README.md +128 -0
- package/package.json +57 -0
- package/src/_linter/catalog.js +638 -0
- package/src/_linter/examples.js +270 -0
- package/src/_linter/i18n.js +403 -0
- package/src/_linter/resources.js +173 -0
- package/src/_linter/rules.d.ts +105 -0
- package/src/_linter/rules.js +1196 -0
- package/src/_linter/terminology.js +265 -0
- package/src/errors.js +73 -0
- package/src/http.js +127 -0
- package/src/index.d.ts +125 -0
- package/src/index.js +194 -0
- package/src/index.mjs +16 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
// Fhiron linter — catálogo pedagógico de códigos.
|
|
2
|
+
//
|
|
3
|
+
// Para cada código (cl-* y hapi-*) responde las cuatro preguntas del feedback:
|
|
4
|
+
//
|
|
5
|
+
// • what → resumen corto (en español; el linter ya lo emite como `message`)
|
|
6
|
+
// • why → POR QUÉ está mal — frase pedagógica con contexto CL Core / FHIR
|
|
7
|
+
// • profileUrl → DÓNDE SE INDICA — URL canónica del StructureDefinition / CodeSystem
|
|
8
|
+
// • docsUrl → link humano a la doc explicativa (fhiron.cl/errores/{code})
|
|
9
|
+
// • suggestion → SOLUCIÓN explicada cuando no hay quickFix mecánico
|
|
10
|
+
// • example → recorte JSON correcto cercano al error (5–10 líneas)
|
|
11
|
+
//
|
|
12
|
+
// Diseño:
|
|
13
|
+
// - Tabla pura (objeto) — sin lógica. La función enrichIssue() vive en rules.js.
|
|
14
|
+
// - Si un código no está en el catálogo, enrichIssue lo deja intacto (compat).
|
|
15
|
+
// - Para issues hapi-error / hapi-warning de `route.ts`, el código viene
|
|
16
|
+
// enriquecido con un sub-tipo categorizado por la traducción (ej.
|
|
17
|
+
// hapi-error.cardinality, hapi-error.terminology). Si no, fallback genérico.
|
|
18
|
+
|
|
19
|
+
"use strict";
|
|
20
|
+
|
|
21
|
+
// ── URLs canónicas CL Core v1.9.4 ───────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
const IG_BASE = "https://hl7chile.cl/fhir/ig/clcore";
|
|
24
|
+
const SD = (id) => `${IG_BASE}/StructureDefinition/${id}`;
|
|
25
|
+
const CS = (id) => `${IG_BASE}/CodeSystem/${id}`;
|
|
26
|
+
const VS = (id) => `${IG_BASE}/ValueSet/${id}`;
|
|
27
|
+
const DOCS = (code) => `https://docs.fhiron.cl/errores/${code}`;
|
|
28
|
+
|
|
29
|
+
// ── URLs canónicas MINSAL EIS v0.1.0 ────────────────────────────────────────
|
|
30
|
+
// IG paralelo a CL Core publicado por Unidad de Interoperabilidad del MINSAL.
|
|
31
|
+
// Sólo terminología (49 CodeSystems + 49 ValueSets), 0 perfiles.
|
|
32
|
+
const EIS_BASE = "https://interoperabilidad.minsal.cl/fhir/ig/eis";
|
|
33
|
+
const EIS_CS = (id) => `${EIS_BASE}/CodeSystem/${id}`;
|
|
34
|
+
const EIS_VS = (id) => `${EIS_BASE}/ValueSet/${id}`;
|
|
35
|
+
|
|
36
|
+
// ── Ejemplos válidos breves (para campo `example` por código) ────────────────
|
|
37
|
+
|
|
38
|
+
const EX_PATIENT_RUN = {
|
|
39
|
+
resourceType: "Patient",
|
|
40
|
+
meta: { profile: [SD("CorePacienteCl")] },
|
|
41
|
+
identifier: [{
|
|
42
|
+
use: "official",
|
|
43
|
+
type: { coding: [{ system: CS("CSTipoIdentificador"), code: "01" }] },
|
|
44
|
+
system: "http://www.registrocivil.cl/run",
|
|
45
|
+
value: "12345678-5",
|
|
46
|
+
}],
|
|
47
|
+
name: [{ family: "Pérez", given: ["María"] }],
|
|
48
|
+
gender: "female",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const EX_PATIENT_NAME = {
|
|
52
|
+
resourceType: "Patient",
|
|
53
|
+
name: [{ family: "Pérez", given: ["María"] }],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const EX_OBSERVATION = {
|
|
57
|
+
resourceType: "Observation",
|
|
58
|
+
status: "final",
|
|
59
|
+
code: { coding: [{ system: "http://loinc.org", code: "29463-7", display: "Peso corporal" }] },
|
|
60
|
+
subject: { reference: "Patient/123" },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const EX_MEDICATION = {
|
|
64
|
+
resourceType: "Medication",
|
|
65
|
+
code: {
|
|
66
|
+
coding: [{
|
|
67
|
+
system: "http://www.whocc.no/atc",
|
|
68
|
+
code: "C09AA02",
|
|
69
|
+
display: "Enalapril 10 mg",
|
|
70
|
+
}],
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const EX_MEDREQ = {
|
|
75
|
+
resourceType: "MedicationRequest",
|
|
76
|
+
status: "active",
|
|
77
|
+
intent: "order",
|
|
78
|
+
subject: { reference: "Patient/123" },
|
|
79
|
+
medicationCodeableConcept: { coding: [{ system: "http://www.whocc.no/atc", code: "C09AA02" }] },
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const EX_ENCOUNTER = {
|
|
83
|
+
resourceType: "Encounter",
|
|
84
|
+
status: "finished",
|
|
85
|
+
class: { system: "http://terminology.hl7.org/CodeSystem/v3-ActCode", code: "AMB", display: "ambulatorio" },
|
|
86
|
+
subject: { reference: "Patient/123" },
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const EX_CONDITION = {
|
|
90
|
+
resourceType: "Condition",
|
|
91
|
+
subject: { reference: "Patient/123" },
|
|
92
|
+
code: { coding: [{ system: "http://hl7.org/fhir/sid/icd-10", code: "I10", display: "Hipertensión esencial" }] },
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const EX_ALLERGY = {
|
|
96
|
+
resourceType: "AllergyIntolerance",
|
|
97
|
+
patient: { reference: "Patient/123" },
|
|
98
|
+
code: { coding: [{ system: "http://snomed.info/sct", code: "256349002", display: "Maní" }] },
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const EX_PROCEDURE = {
|
|
102
|
+
resourceType: "Procedure",
|
|
103
|
+
status: "completed",
|
|
104
|
+
subject: { reference: "Patient/123" },
|
|
105
|
+
code: { coding: [{ system: "http://snomed.info/sct", code: "80146002", display: "Apendicectomía" }] },
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const EX_COVERAGE = {
|
|
109
|
+
resourceType: "Coverage",
|
|
110
|
+
status: "active",
|
|
111
|
+
beneficiary: { reference: "Patient/123" },
|
|
112
|
+
payor: [{ reference: "Organization/fonasa" }],
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const EX_ORG = {
|
|
116
|
+
resourceType: "Organization",
|
|
117
|
+
name: "Hospital Clínico Universidad de Chile",
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const EX_IMM = {
|
|
121
|
+
resourceType: "Immunization",
|
|
122
|
+
status: "completed",
|
|
123
|
+
vaccineCode: { coding: [{ system: "http://hl7.org/fhir/sid/cvx", code: "208", display: "COVID-19 mRNA" }] },
|
|
124
|
+
patient: { reference: "Patient/123" },
|
|
125
|
+
occurrenceDateTime: "2026-04-29",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const EX_DR = {
|
|
129
|
+
resourceType: "DiagnosticReport",
|
|
130
|
+
status: "final",
|
|
131
|
+
code: { coding: [{ system: "http://loinc.org", code: "11502-2", display: "Laboratory report" }] },
|
|
132
|
+
subject: { reference: "Patient/123" },
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const EX_BUNDLE = {
|
|
136
|
+
resourceType: "Bundle",
|
|
137
|
+
type: "collection",
|
|
138
|
+
entry: [{ resource: EX_PATIENT_RUN }],
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// ── Catálogo principal ──────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
const CATALOG = {
|
|
144
|
+
// ── Formato y estructura ──────────────────────────────────────────────────
|
|
145
|
+
"cl-json-01": {
|
|
146
|
+
why: "El cuerpo de la petición no es JSON válido — un coma faltante, una comilla suelta o una llave sin cerrar bastan para que el parser falle. FHIR exige JSON estricto (no JSON5).",
|
|
147
|
+
suggestion: "Pega el recurso en jsonlint.com o en un IDE que resalte errores de JSON. Revisa el carácter en la posición indicada por el mensaje.",
|
|
148
|
+
docsUrl: DOCS("cl-json-01"),
|
|
149
|
+
},
|
|
150
|
+
"cl-json-02": {
|
|
151
|
+
why: "FHIR R4 modela cada recurso como un objeto JSON con la propiedad `resourceType`. Arrays, números o strings sueltos no son recursos válidos.",
|
|
152
|
+
suggestion: "Envuelve el contenido en `{ \"resourceType\": \"<Tipo>\", ... }`.",
|
|
153
|
+
docsUrl: DOCS("cl-json-02"),
|
|
154
|
+
},
|
|
155
|
+
"cl-json-03": {
|
|
156
|
+
why: "`resourceType` es el discriminador que le dice al servidor qué perfil aplicar. Sin él, HAPI no puede invocar la validación correcta.",
|
|
157
|
+
suggestion: "Agrega `\"resourceType\": \"Patient\"` (o el tipo que corresponda) como primera propiedad.",
|
|
158
|
+
example: { resourceType: "Patient", identifier: [], name: [{ family: "..." }] },
|
|
159
|
+
docsUrl: DOCS("cl-json-03"),
|
|
160
|
+
},
|
|
161
|
+
"cl-json-04": {
|
|
162
|
+
why: "FHIR R4 define ~145 resourceTypes oficiales. Si usas un nombre que no existe, HAPI rechazará el recurso con HTTP 404.",
|
|
163
|
+
suggestion: "Revisa la lista oficial en https://hl7.org/fhir/R4/resourcelist.html. Los recursos más usados en CL Core son Patient, Practitioner, Encounter, Condition, MedicationRequest.",
|
|
164
|
+
docsUrl: DOCS("cl-json-04"),
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
// ── RUN chileno (perfil CorePacienteCl + CSTipoIdentificador) ────────────
|
|
168
|
+
"cl-run-01": {
|
|
169
|
+
why: "CL Core v1.9.4 normaliza el RUN al cuerpo numérico sin puntos: `12345678-5`. Los puntos son una convención visual chilena, pero no se almacenan en sistemas FHIR. CL Core indica el tipo de identificador con `code: \"01\"` del CodeSystem CSTipoIdentificador.",
|
|
170
|
+
profileUrl: SD("CorePacienteCl"),
|
|
171
|
+
suggestion: "Aplica el quickFix incluido o quita los puntos manualmente. El dígito verificador conserva su posición tras el guión.",
|
|
172
|
+
example: { identifier: [{ system: CS("CSTipoIdentificador"), value: "12345678-5" }] },
|
|
173
|
+
docsUrl: DOCS("cl-run-01"),
|
|
174
|
+
},
|
|
175
|
+
"cl-run-02": {
|
|
176
|
+
why: "Un identifier con `system` que apunta al RUN sin `value` es un campo a medio llenar — FHIR rechaza identifiers parciales porque comprometen la integridad referencial.",
|
|
177
|
+
profileUrl: SD("CorePacienteCl"),
|
|
178
|
+
suggestion: "Agrega el RUN como string en `identifier[i].value` con formato `12345678-5`.",
|
|
179
|
+
example: { identifier: [{ system: CS("CSTipoIdentificador"), value: "12345678-5" }] },
|
|
180
|
+
docsUrl: DOCS("cl-run-02"),
|
|
181
|
+
},
|
|
182
|
+
"cl-run-03": {
|
|
183
|
+
why: "El dígito verificador se calcula con módulo 11 sobre el cuerpo del RUN (Decreto Ley 1.094 / Servicio de Registro Civil). Si no coincide, el RUN no existe o fue tipeado mal — CL Core lo rechaza para evitar pacientes fantasma.",
|
|
184
|
+
profileUrl: SD("CorePacienteCl"),
|
|
185
|
+
suggestion: "Verifica el RUN contra el registro civil o usa una calculadora de DV. El mensaje indica qué DV se esperaba.",
|
|
186
|
+
example: { identifier: [{ system: CS("CSTipoIdentificador"), value: "12345678-5" }] },
|
|
187
|
+
docsUrl: DOCS("cl-run-03"),
|
|
188
|
+
},
|
|
189
|
+
"cl-run-04": {
|
|
190
|
+
why: "El system `https://hl7chile.cl/identificador/run` quedó obsoleto en CL Core v1.7+. La nueva versión unifica todos los identificadores chilenos bajo CSTipoIdentificador con `code: \"01\"` (RUN).",
|
|
191
|
+
profileUrl: CS("CSTipoIdentificador"),
|
|
192
|
+
suggestion: "Reemplaza el system por `https://hl7chile.cl/fhir/ig/clcore/CodeSystem/CSTipoIdentificador` y agrega `type.coding[0].code = \"01\"`.",
|
|
193
|
+
docsUrl: DOCS("cl-run-04"),
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// ── Patient (CorePacienteCl) ──────────────────────────────────────────────
|
|
197
|
+
"cl-patient-01": {
|
|
198
|
+
why: "Un Patient sin identifier es un paciente anónimo — incumple la traza de auditoría exigida por la Ley 21.719 y el perfil CorePacienteCl. Sin identifier, no puedes vincular Encounters, Observations ni Coverage.",
|
|
199
|
+
profileUrl: SD("CorePacienteCl"),
|
|
200
|
+
suggestion: "Incluye al menos un identifier — usualmente el RUN con system CSTipoIdentificador. Para extranjeros usa pasaporte (code 02) o DNI (code 03).",
|
|
201
|
+
example: EX_PATIENT_RUN,
|
|
202
|
+
docsUrl: DOCS("cl-patient-01"),
|
|
203
|
+
},
|
|
204
|
+
"cl-patient-02": {
|
|
205
|
+
why: "CorePacienteCl marca `name` como [1..*] obligatorio porque sin nombre el recurso no es legible para clínicos ni cumple el deber de identificar al titular del dato (principio de calidad de la Ley 21.719).",
|
|
206
|
+
profileUrl: SD("CorePacienteCl"),
|
|
207
|
+
suggestion: "Agrega `name: [{ family: \"<Apellido Paterno>\", given: [\"<Nombres>\"] }]`. CL Core acepta múltiples apellidos como entradas separadas en `family` o como extensión `SegundoApellido`.",
|
|
208
|
+
example: EX_PATIENT_NAME,
|
|
209
|
+
docsUrl: DOCS("cl-patient-02"),
|
|
210
|
+
},
|
|
211
|
+
"cl-patient-03": {
|
|
212
|
+
why: "Cada elemento de `name` debe tener al menos `family` o `given` — un objeto vacío es ruido que rompe el binding del HumanName.",
|
|
213
|
+
profileUrl: SD("CorePacienteCl"),
|
|
214
|
+
suggestion: "Llena al menos uno de los dos campos por cada nombre del paciente.",
|
|
215
|
+
docsUrl: DOCS("cl-patient-03"),
|
|
216
|
+
},
|
|
217
|
+
"cl-patient-04": {
|
|
218
|
+
why: "`gender` administrativo (no biológico) es recomendado para vincular trayectorias clínicas y reportar al MINSAL. Su ausencia no rompe la validación pero reduce calidad de los reportes.",
|
|
219
|
+
profileUrl: SD("CorePacienteCl"),
|
|
220
|
+
suggestion: "Agrega `gender` con uno de: `male`, `female`, `other`, `unknown`. Para sexo biológico distinto del administrativo, usa la extensión `SexoBiologico` de CL Core.",
|
|
221
|
+
docsUrl: DOCS("cl-patient-04"),
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
// ── Practitioner (CorePrestadorCl) ────────────────────────────────────────
|
|
225
|
+
"cl-practitioner-01": {
|
|
226
|
+
why: "CorePrestadorCl exige un identificador único — usualmente el RUN del prestador o su código DEIS. Sin él no puedes vincular el profesional a Encounters ni acreditar la atención ante FONASA/Isapre.",
|
|
227
|
+
profileUrl: SD("CorePrestadorCl"),
|
|
228
|
+
suggestion: "Agrega `identifier` con system CSTipoIdentificador (RUN) o el system del registro de prestadores SIS.",
|
|
229
|
+
docsUrl: DOCS("cl-practitioner-01"),
|
|
230
|
+
},
|
|
231
|
+
"cl-practitioner-02": {
|
|
232
|
+
why: "Sin nombre el prestador no es identificable en la historia clínica ni en facturación.",
|
|
233
|
+
profileUrl: SD("CorePrestadorCl"),
|
|
234
|
+
suggestion: "Agrega `name: [{ family, given }]`.",
|
|
235
|
+
docsUrl: DOCS("cl-practitioner-02"),
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
// ── Observation ───────────────────────────────────────────────────────────
|
|
239
|
+
"cl-obs-01": {
|
|
240
|
+
why: "`status` indica si la observación es preliminar, final o corregida — sin ese contexto un valor clínico no se puede interpretar legalmente.",
|
|
241
|
+
suggestion: "Usa `final` para resultados confirmados, `preliminary` para borradores, `amended` para correcciones.",
|
|
242
|
+
example: EX_OBSERVATION,
|
|
243
|
+
docsUrl: DOCS("cl-obs-01"),
|
|
244
|
+
},
|
|
245
|
+
"cl-obs-02": {
|
|
246
|
+
why: "`code` define qué se midió — sin él, un valor numérico no significa nada. CL Core recomienda LOINC para signos vitales y laboratorio.",
|
|
247
|
+
suggestion: "Usa `code: { coding: [{ system: \"http://loinc.org\", code: \"<LOINC>\" }] }`.",
|
|
248
|
+
example: EX_OBSERVATION,
|
|
249
|
+
docsUrl: DOCS("cl-obs-02"),
|
|
250
|
+
},
|
|
251
|
+
"cl-obs-03": {
|
|
252
|
+
why: "Toda observación se hace sobre alguien — sin `subject` el valor flota desconectado del paciente y rompe la cadena clínica.",
|
|
253
|
+
suggestion: "Agrega `subject: { reference: \"Patient/<id>\" }`.",
|
|
254
|
+
docsUrl: DOCS("cl-obs-03"),
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// ── Medication (CoreMedicamentoCl) ────────────────────────────────────────
|
|
258
|
+
"cl-med-01": {
|
|
259
|
+
why: "Sin código no se puede dispensar ni facturar. CL Core CoreMedicamentoCl recomienda ATC (http://www.whocc.no/atc), SNOMED CT o RxNorm como systems canónicos.",
|
|
260
|
+
profileUrl: SD("CoreMedicamentoCl"),
|
|
261
|
+
suggestion: "Usa el código ATC del medicamento. Para fármacos sin ATC asignado, usa SNOMED CT o RxNorm.",
|
|
262
|
+
example: EX_MEDICATION,
|
|
263
|
+
docsUrl: DOCS("cl-med-01"),
|
|
264
|
+
},
|
|
265
|
+
"cl-med-02": {
|
|
266
|
+
why: "Los códigos propietarios (custom.example.com) no se pueden mapear a otros sistemas — CL Core privilegia ATC, SNOMED CT y RxNorm para garantizar interoperabilidad.",
|
|
267
|
+
profileUrl: SD("CoreMedicamentoCl"),
|
|
268
|
+
suggestion: "Migra al sistema ATC (http://www.whocc.no/atc). Para fármacos sin ATC, usa SNOMED CT o RxNorm.",
|
|
269
|
+
docsUrl: DOCS("cl-med-02"),
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
// ── MedicationRequest ─────────────────────────────────────────────────────
|
|
273
|
+
"cl-medreq-01": {
|
|
274
|
+
why: "El status (`active`, `completed`, `cancelled`...) es lo que un sistema de despacho consulta para decidir si entregar el medicamento.",
|
|
275
|
+
suggestion: "`active` para órdenes vigentes, `completed` cuando se despachó, `cancelled` si fue revocada.",
|
|
276
|
+
example: EX_MEDREQ,
|
|
277
|
+
docsUrl: DOCS("cl-medreq-01"),
|
|
278
|
+
},
|
|
279
|
+
"cl-medreq-02": {
|
|
280
|
+
why: "`intent` distingue si es propuesta (`proposal`), plan (`plan`) o orden ejecutable (`order`). Sin él, la cascada de autorizaciones no funciona.",
|
|
281
|
+
suggestion: "`order` para una receta médica formal, `plan` para una indicación pendiente de aprobación.",
|
|
282
|
+
example: EX_MEDREQ,
|
|
283
|
+
docsUrl: DOCS("cl-medreq-02"),
|
|
284
|
+
},
|
|
285
|
+
"cl-medreq-03": {
|
|
286
|
+
why: "Toda receta es para alguien — el `subject` enlaza al Patient destinatario.",
|
|
287
|
+
suggestion: "`subject: { reference: \"Patient/<id>\" }`.",
|
|
288
|
+
docsUrl: DOCS("cl-medreq-03"),
|
|
289
|
+
},
|
|
290
|
+
"cl-medreq-04": {
|
|
291
|
+
why: "El medicamento puede ir como CodeableConcept (in-line) o Reference a un Medication separado. Sin uno de los dos, la receta no tiene contenido.",
|
|
292
|
+
suggestion: "Para casos simples usa `medicationCodeableConcept`; para reutilizar un Medication catalogado usa `medicationReference`.",
|
|
293
|
+
example: EX_MEDREQ,
|
|
294
|
+
docsUrl: DOCS("cl-medreq-04"),
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
// ── Encounter (EncounterCL) ───────────────────────────────────────────────
|
|
298
|
+
"cl-enc-01": {
|
|
299
|
+
why: "El status indica el estado del encuentro. Sin él no puedes facturar ni cerrar la atención.",
|
|
300
|
+
profileUrl: SD("EncounterCL"),
|
|
301
|
+
suggestion: "`finished` para encuentros cerrados, `in-progress` para activos, `arrived` para esperando.",
|
|
302
|
+
example: EX_ENCOUNTER,
|
|
303
|
+
docsUrl: DOCS("cl-enc-01"),
|
|
304
|
+
},
|
|
305
|
+
"cl-enc-02": {
|
|
306
|
+
why: "`class` clasifica el tipo de atención (ambulatoria, hospitalizada, urgencia). Es input crítico para reportería FONASA y GES.",
|
|
307
|
+
profileUrl: SD("EncounterCL"),
|
|
308
|
+
suggestion: "Usa el ValueSet v3-ActCode: `AMB` ambulatorio, `IMP` hospitalizado, `EMER` urgencia.",
|
|
309
|
+
example: EX_ENCOUNTER,
|
|
310
|
+
docsUrl: DOCS("cl-enc-02"),
|
|
311
|
+
},
|
|
312
|
+
"cl-enc-03": {
|
|
313
|
+
why: "Un encuentro sin paciente no es atendible — el subject ancla todo el resto del registro clínico.",
|
|
314
|
+
profileUrl: SD("EncounterCL"),
|
|
315
|
+
suggestion: "`subject: { reference: \"Patient/<id>\" }`.",
|
|
316
|
+
docsUrl: DOCS("cl-enc-03"),
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
// ── Condition (CoreDiagnosticoCl) ───────────────────────────────────────────
|
|
320
|
+
"cl-cond-01": {
|
|
321
|
+
why: "Un diagnóstico siempre pertenece a alguien.",
|
|
322
|
+
profileUrl: SD("CoreDiagnosticoCl"),
|
|
323
|
+
suggestion: "`subject: { reference: \"Patient/<id>\" }`.",
|
|
324
|
+
docsUrl: DOCS("cl-cond-01"),
|
|
325
|
+
},
|
|
326
|
+
"cl-cond-02": {
|
|
327
|
+
why: "Sin código, el diagnóstico es solo texto y no se puede agregar a estadísticas DEIS ni cubrir bajo GES.",
|
|
328
|
+
profileUrl: SD("CoreDiagnosticoCl"),
|
|
329
|
+
suggestion: "Usa CIE-10 (`http://hl7.org/fhir/sid/icd-10`) o SNOMED CT (`http://snomed.info/sct`).",
|
|
330
|
+
example: EX_CONDITION,
|
|
331
|
+
docsUrl: DOCS("cl-cond-02"),
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
// ── AllergyIntolerance (CoreAlergiaIntCl) ───────────────────────────────────
|
|
335
|
+
"cl-allergy-01": {
|
|
336
|
+
why: "La alergia siempre es de un paciente — sin el campo `patient` el aviso no llega a HCE.",
|
|
337
|
+
profileUrl: SD("CoreAlergiaIntCl"),
|
|
338
|
+
suggestion: "`patient: { reference: \"Patient/<id>\" }`.",
|
|
339
|
+
docsUrl: DOCS("cl-allergy-01"),
|
|
340
|
+
},
|
|
341
|
+
"cl-allergy-02": {
|
|
342
|
+
why: "Sin código no se puede generar alerta automática en farmacia ni cruzar con interacciones.",
|
|
343
|
+
profileUrl: SD("CoreAlergiaIntCl"),
|
|
344
|
+
suggestion: "Usa SNOMED CT (sustancia) o el catálogo de alergias DEIS.",
|
|
345
|
+
example: EX_ALLERGY,
|
|
346
|
+
docsUrl: DOCS("cl-allergy-02"),
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
// ── Procedure ─────────────────────────────────────────────────────────────
|
|
350
|
+
"cl-proc-01": {
|
|
351
|
+
why: "Status indica si el procedimiento se completó, se interrumpió o quedó preparado.",
|
|
352
|
+
suggestion: "`completed` para terminados, `in-progress` durante ejecución.",
|
|
353
|
+
example: EX_PROCEDURE,
|
|
354
|
+
docsUrl: DOCS("cl-proc-01"),
|
|
355
|
+
},
|
|
356
|
+
"cl-proc-02": {
|
|
357
|
+
why: "El procedimiento se realiza sobre un paciente.",
|
|
358
|
+
suggestion: "`subject: { reference: \"Patient/<id>\" }`.",
|
|
359
|
+
docsUrl: DOCS("cl-proc-02"),
|
|
360
|
+
},
|
|
361
|
+
"cl-proc-03": {
|
|
362
|
+
why: "Sin código no se puede facturar a FONASA ni cruzar con la canasta GES.",
|
|
363
|
+
suggestion: "Usa SNOMED CT o la codificación de prestaciones FONASA/MAI.",
|
|
364
|
+
example: EX_PROCEDURE,
|
|
365
|
+
docsUrl: DOCS("cl-proc-03"),
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
// ── Coverage ──────────────────────────────────────────────────────────────
|
|
369
|
+
// CL Core 1.9.4 no incluye un perfil dedicado para Coverage. Se valida
|
|
370
|
+
// contra la base R4 de FHIR; las recomendaciones siguen aplicando para
|
|
371
|
+
// que el flujo FONASA / Isapre quede bien tipado.
|
|
372
|
+
"cl-cov-01": {
|
|
373
|
+
why: "Status define si la cobertura está vigente — input crítico para autorizaciones.",
|
|
374
|
+
suggestion: "`active` para vigente, `cancelled` para terminada.",
|
|
375
|
+
example: EX_COVERAGE,
|
|
376
|
+
docsUrl: DOCS("cl-cov-01"),
|
|
377
|
+
},
|
|
378
|
+
"cl-cov-02": {
|
|
379
|
+
why: "El asegurado es el paciente cubierto por el plan.",
|
|
380
|
+
suggestion: "`beneficiary: { reference: \"Patient/<id>\" }`.",
|
|
381
|
+
docsUrl: DOCS("cl-cov-02"),
|
|
382
|
+
},
|
|
383
|
+
"cl-cov-03": {
|
|
384
|
+
why: "El payor es FONASA, una Isapre, una mutual o seguro privado.",
|
|
385
|
+
suggestion: "`payor: [{ reference: \"Organization/<id>\" }]`.",
|
|
386
|
+
example: EX_COVERAGE,
|
|
387
|
+
docsUrl: DOCS("cl-cov-03"),
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
// ── Organization (CoreOrganizacionCl) ─────────────────────────────────────
|
|
391
|
+
"cl-org-01": {
|
|
392
|
+
why: "Una organización debe ser identificable — por código DEIS (establecimientos) o al menos un nombre legible.",
|
|
393
|
+
profileUrl: SD("CoreOrganizacionCl"),
|
|
394
|
+
suggestion: "Mejor: agrega `identifier` con el código DEIS del establecimiento (catálogo MINSAL). Mínimo: agrega `name`. CL Core 1.9.4 no incluye un CodeSystem dedicado al catálogo DEIS — usa `system` propio del establecimiento.",
|
|
395
|
+
example: EX_ORG,
|
|
396
|
+
docsUrl: DOCS("cl-org-01"),
|
|
397
|
+
},
|
|
398
|
+
"cl-org-02": {
|
|
399
|
+
why: "Si declaras un system de identificación de establecimiento pero no el código, el identifier no apunta a nada.",
|
|
400
|
+
suggestion: "Busca el código DEIS en el SIS-MINSAL. Si no lo conoces, llama `fhiron_search_terminology` con `system: \"deis\"`.",
|
|
401
|
+
docsUrl: DOCS("cl-org-02"),
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
// ── Immunization (ImmunizationCL) ─────────────────────────────────────
|
|
405
|
+
"cl-imm-01": {
|
|
406
|
+
why: "Status define si la dosis se administró, no se aplicó o quedó registrada con error.",
|
|
407
|
+
profileUrl: SD("ImmunizationCL"),
|
|
408
|
+
suggestion: "`completed` para vacunas administradas, `not-done` cuando se omitió.",
|
|
409
|
+
example: EX_IMM,
|
|
410
|
+
docsUrl: DOCS("cl-imm-01"),
|
|
411
|
+
},
|
|
412
|
+
"cl-imm-02": {
|
|
413
|
+
why: "vaccineCode identifica la vacuna — clave para reportería al RNI (Registro Nacional de Inmunizaciones).",
|
|
414
|
+
profileUrl: SD("ImmunizationCL"),
|
|
415
|
+
suggestion: "Usa el catálogo CVX (`http://hl7.org/fhir/sid/cvx`). El catálogo PNI chileno (`CSNombreCampana`) está pendiente de publicación oficial por MINSAL.",
|
|
416
|
+
example: EX_IMM,
|
|
417
|
+
docsUrl: DOCS("cl-imm-02"),
|
|
418
|
+
},
|
|
419
|
+
"cl-imm-03": {
|
|
420
|
+
why: "La vacuna se aplica a un paciente concreto.",
|
|
421
|
+
profileUrl: SD("ImmunizationCL"),
|
|
422
|
+
suggestion: "`patient: { reference: \"Patient/<id>\" }`.",
|
|
423
|
+
docsUrl: DOCS("cl-imm-03"),
|
|
424
|
+
},
|
|
425
|
+
"cl-imm-04": {
|
|
426
|
+
why: "Sin fecha no se puede calcular cobertura ni intervalo entre dosis.",
|
|
427
|
+
profileUrl: SD("ImmunizationCL"),
|
|
428
|
+
suggestion: "`occurrenceDateTime: \"YYYY-MM-DD\"` (o ISO 8601 con hora).",
|
|
429
|
+
docsUrl: DOCS("cl-imm-04"),
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
// ── DiagnosticReport ──────────────────────────────────────────────────────
|
|
433
|
+
"cl-dr-01": {
|
|
434
|
+
why: "Status diferencia un informe preliminar de uno final firmado por el patólogo/radiólogo.",
|
|
435
|
+
suggestion: "`final` para informes firmados, `preliminary` para borradores.",
|
|
436
|
+
example: EX_DR,
|
|
437
|
+
docsUrl: DOCS("cl-dr-01"),
|
|
438
|
+
},
|
|
439
|
+
"cl-dr-02": {
|
|
440
|
+
why: "El code define el tipo de informe (laboratorio, imagenología, anatomía patológica). LOINC es el estándar.",
|
|
441
|
+
suggestion: "Usa LOINC: `11502-2` informe de laboratorio, `18748-4` informe de imagenología.",
|
|
442
|
+
example: EX_DR,
|
|
443
|
+
docsUrl: DOCS("cl-dr-02"),
|
|
444
|
+
},
|
|
445
|
+
"cl-dr-03": {
|
|
446
|
+
why: "Todo informe es sobre un paciente.",
|
|
447
|
+
suggestion: "`subject: { reference: \"Patient/<id>\" }`.",
|
|
448
|
+
docsUrl: DOCS("cl-dr-03"),
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
// ── Bundle ────────────────────────────────────────────────────────────────
|
|
452
|
+
"cl-bundle-01": {
|
|
453
|
+
why: "El `type` define cómo HAPI procesa el Bundle: `transaction` ejecuta atómico, `document` valida como documento clínico, `collection` solo agrupa.",
|
|
454
|
+
suggestion: "`transaction` para enviar varios recursos atómicamente, `collection` para agrupar sin transaccionalidad, `document` para historia clínica electrónica.",
|
|
455
|
+
example: EX_BUNDLE,
|
|
456
|
+
docsUrl: DOCS("cl-bundle-01"),
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
// ── MINSAL EIS — terminología paralela a CL Core ──────────────────────────
|
|
460
|
+
"cl-eis-comuna-01": {
|
|
461
|
+
why: "Estás usando el CodeSystem oficial MINSAL de comunas. Es la lista de 347 comunas autorizada por DEIS — equivalente a la lista CL Core pero bajo el namespace minsal.cl.",
|
|
462
|
+
suggestion: "Tanto el system EIS (`https://interoperabilidad.minsal.cl/fhir/ig/eis/CodeSystem/CSComunas`) como el CL Core (`https://hl7chile.cl/fhir/ig/clcore/CodeSystem/CSCodComunasCL`) son válidos. Elige según con qué sistema te integras.",
|
|
463
|
+
docsUrl: DOCS("cl-eis-comuna-01"),
|
|
464
|
+
},
|
|
465
|
+
"cl-eis-prevision-01": {
|
|
466
|
+
why: "Estás usando el CodeSystem oficial MINSAL de previsión (Fonasa A/B/C/D, ISAPRE, PRAIS, Ley 21.418). No existe equivalente en CL Core — EIS es la única fuente oficial.",
|
|
467
|
+
suggestion: "Acepta el código y valida que `code` esté en la lista oficial (ej. `1` Fonasa A, `2` Fonasa B, `3` Fonasa C, `4` Fonasa D, `5` ISAPRE).",
|
|
468
|
+
docsUrl: DOCS("cl-eis-prevision-01"),
|
|
469
|
+
},
|
|
470
|
+
"cl-eis-system-01": {
|
|
471
|
+
why: "El system declarado parece de MINSAL EIS pero no aparece en el catálogo de los 49 CodeSystems oficiales del paquete `hl7.fhir.cl.minsal.eis@0.1.0`. Puede ser un typo o un namespace inventado.",
|
|
472
|
+
suggestion: "Verifica el system exacto. Los CodeSystems EIS publicados oficialmente listados en `/conceptos/cl-core#minsal-eis-terminología-paralela`.",
|
|
473
|
+
docsUrl: DOCS("cl-eis-system-01"),
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
// ── Reglas adicionales de calidad — recursos previamente parciales ───────
|
|
477
|
+
"cl-proc-04": {
|
|
478
|
+
why: "Procedure debe declarar cuándo ocurrió el procedimiento (`performedDateTime` o `performedPeriod`) para que sea analizable epidemiológicamente y trazable bajo Ley 21.719.",
|
|
479
|
+
suggestion: "`performedDateTime: \"2026-05-12T14:30:00-04:00\"` para procedimientos puntuales, `performedPeriod` para intervenciones extendidas.",
|
|
480
|
+
docsUrl: DOCS("cl-proc-04"),
|
|
481
|
+
},
|
|
482
|
+
"cl-allergy-04": {
|
|
483
|
+
why: "AllergyIntolerance sin `clinicalStatus` deja ambiguo si la alergia está activa, resuelta o inactiva. CL Core exige el binding required al ValueSet `allergyintolerance-clinical`.",
|
|
484
|
+
suggestion: "`clinicalStatus: { coding: [{ system: \"http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical\", code: \"active\" }] }`.",
|
|
485
|
+
docsUrl: DOCS("cl-allergy-04"),
|
|
486
|
+
},
|
|
487
|
+
"cl-cov-04": {
|
|
488
|
+
why: "Coverage en Chile debe identificar al titular de la cobertura. Para Fonasa es el RUN del cotizante, para ISAPRE el número de contrato. Sin `subscriberId` el dispensador no puede liquidar.",
|
|
489
|
+
suggestion: "`subscriberId: \"15420097-3\"` (RUN del cotizante en Fonasa) o número de contrato ISAPRE.",
|
|
490
|
+
docsUrl: DOCS("cl-cov-04"),
|
|
491
|
+
},
|
|
492
|
+
"cl-imm-05": {
|
|
493
|
+
why: "Immunization en el PNI chileno requiere trazabilidad del lote de la vacuna para investigación epidemiológica ante eventos adversos. Sin `lotNumber` no es auditable.",
|
|
494
|
+
suggestion: "`lotNumber: \"AB1234\"` — el número impreso en el frasco/cápsula de la dosis aplicada.",
|
|
495
|
+
docsUrl: DOCS("cl-imm-05"),
|
|
496
|
+
},
|
|
497
|
+
"cl-dr-04": {
|
|
498
|
+
why: "DiagnosticReport sin fecha del informe deja ambiguo cuándo se generó. Para trazabilidad clínica conviene declarar `effectiveDateTime`.",
|
|
499
|
+
suggestion: "`effectiveDateTime: \"2026-05-12T09:00:00-04:00\"` — fecha/hora de toma de muestra o realización del estudio.",
|
|
500
|
+
docsUrl: DOCS("cl-dr-04"),
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
// ── Narrativa text.div ────────────────────────────────────────────────────
|
|
504
|
+
"cl-narrative-01": {
|
|
505
|
+
why: "FHIR R4 define solo cuatro valores válidos para `text.status`: `generated` (la narrativa se generó automáticamente del JSON), `extensions` (incluye texto de extensiones), `additional` (texto adicional aportado por el humano) y `empty` (no hay narrativa). Cualquier otro string rompe el binding.",
|
|
506
|
+
suggestion: "Usa `generated` por defecto cuando el div es derivado del recurso.",
|
|
507
|
+
docsUrl: DOCS("cl-narrative-01"),
|
|
508
|
+
},
|
|
509
|
+
"cl-narrative-02": {
|
|
510
|
+
why: "FHIR R4 dom-6 recomienda que cada DomainResource incluya `text.div` con narrativa HTML legible. No es obligatorio, pero ayuda a sistemas que no parsean el JSON estructurado y mejora la auditoría humana del recurso.",
|
|
511
|
+
suggestion: "Agrega `text: { status: 'generated', div: '<div xmlns=\"http://www.w3.org/1999/xhtml\">...</div>' }`. El quickFix inserta una narrativa mínima — reemplázala por una descripción humana real antes de producción.",
|
|
512
|
+
docsUrl: DOCS("cl-narrative-02"),
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
// ── HAPI fallback (categorías genéricas) ──────────────────────────────────
|
|
516
|
+
"hapi-error": {
|
|
517
|
+
why: "El validador HAPI detectó una violación al StructureDefinition del perfil — slicing, cardinalidad, binding terminológico o invariante FHIRPath. El detalle exacto está en `message`.",
|
|
518
|
+
suggestion: "Lee el `message` (en español): te indica el campo y la causa. Si menciona un código, búscalo en el ValueSet referido. Si menciona un perfil, revisa su definición canónica.",
|
|
519
|
+
docsUrl: DOCS("hapi-error"),
|
|
520
|
+
},
|
|
521
|
+
"hapi-warning": {
|
|
522
|
+
why: "HAPI emitió una advertencia — el recurso es válido pero hay una recomendación incumplida (best practice, narrative ausente, código deprecado).",
|
|
523
|
+
suggestion: "Las warnings no rompen el flujo pero degradan la calidad del dato. Atiéndelas antes de un release.",
|
|
524
|
+
docsUrl: DOCS("hapi-warning"),
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
// ── HAPI por categoría (route.ts categoriza el mensaje y emite estos códigos) ─
|
|
528
|
+
"hapi-error-cardinality": {
|
|
529
|
+
why: "Cada perfil CL Core declara un rango [min..max] obligatorio para cada campo. Un campo con cardinalidad [1..1] que falta o un [0..1] que aparece dos veces detiene la validación.",
|
|
530
|
+
suggestion: "Revisa el StructureDefinition del perfil para el campo afectado y ajusta la cantidad de ocurrencias enviadas.",
|
|
531
|
+
docsUrl: DOCS("hapi-error-cardinality"),
|
|
532
|
+
},
|
|
533
|
+
"hapi-error-terminology": {
|
|
534
|
+
why: "El binding del perfil exige que el campo use un código de un ValueSet específico (LOINC, SNOMED CT, TFC, CIE-10, CodeSystem chileno). El código enviado no pertenece al CodeSystem referenciado o el `system` está mal escrito.",
|
|
535
|
+
suggestion: "Verifica que `coding[].system` apunte al CodeSystem correcto y que `coding[].code` exista en él. Llama `fhiron_search_terminology` para buscar el código que necesitas.",
|
|
536
|
+
docsUrl: DOCS("hapi-error-terminology"),
|
|
537
|
+
},
|
|
538
|
+
"hapi-error-slicing": {
|
|
539
|
+
why: "El campo tiene múltiples variantes (slices) declaradas por el perfil. La instancia enviada no coincide con ninguna porque le falta una extensión, un `system` discriminador, o una combinación de campos esperada.",
|
|
540
|
+
suggestion: "Revisa el slicing del StructureDefinition. Cada slice define un discriminador (usualmente `system` + `code` o una extensión) que debe coincidir exactamente.",
|
|
541
|
+
docsUrl: DOCS("hapi-error-slicing"),
|
|
542
|
+
},
|
|
543
|
+
"hapi-error-run": {
|
|
544
|
+
why: "El RUN chileno debe seguir el formato 12345678-5: cuerpo numérico hasta 8 dígitos, guión, y dígito verificador (0-9 o K) calculado por módulo 11. CL Core lo valida en el slot identifier del perfil CorePacienteCl, con `type.coding[0].code = \"01\"` (CSTipoIdentificador).",
|
|
545
|
+
profileUrl: SD("CorePacienteCl"),
|
|
546
|
+
suggestion: "Si tienes puntos, usa el quickFix de `cl-run-01` para quitarlos. Verifica el DV con `fhiron_fhir_lint_run` antes de enviar al servidor.",
|
|
547
|
+
example: { identifier: [{ system: CS("CSTipoIdentificador"), value: "12345678-5" }] },
|
|
548
|
+
docsUrl: DOCS("hapi-error-run"),
|
|
549
|
+
},
|
|
550
|
+
"hapi-error-profile": {
|
|
551
|
+
why: "El recurso no satisface el StructureDefinition completo del perfil declarado en `meta.profile` o detectado automáticamente por HAPI. Pueden ser múltiples violaciones acumuladas (cardinalidad + terminología + slicing).",
|
|
552
|
+
suggestion: "Revisa los issues anteriores en este mismo OperationOutcome — usualmente el error de perfil es la suma de los issues más específicos.",
|
|
553
|
+
docsUrl: DOCS("hapi-error-profile"),
|
|
554
|
+
},
|
|
555
|
+
"hapi-error-reference": {
|
|
556
|
+
why: "Una referencia (`Patient/123`, `Practitioner/X`, etc.) no se pudo resolver — el recurso target no existe en el Bundle, no se proporcionó como contained, ni la URL absoluta es accesible.",
|
|
557
|
+
suggestion: "Para Bundles transactionales, asegúrate de que el recurso referenciado esté en otra entry del mismo Bundle. Para servidores con recursos pre-cargados, verifica que la referencia apunte al ID correcto.",
|
|
558
|
+
docsUrl: DOCS("hapi-error-reference"),
|
|
559
|
+
},
|
|
560
|
+
"hapi-error-format": {
|
|
561
|
+
why: "El validador detectó un valor que no calza con el tipo de dato esperado por el perfil — un string donde se esperaba un número, una fecha mal formada, un MIME type incorrecto, o un valor que no cumple el regex declarado.",
|
|
562
|
+
suggestion: "Revisa el tipo declarado en el StructureDefinition para el campo y ajusta el formato del valor enviado.",
|
|
563
|
+
docsUrl: DOCS("hapi-error-format"),
|
|
564
|
+
},
|
|
565
|
+
"hapi-warning-best-practice": {
|
|
566
|
+
why: "Recomendación opcional del IG: el recurso es técnicamente válido pero le falta algo que mejora la interoperabilidad (narrativa text.div, performer en Observation, identifier business, etc.).",
|
|
567
|
+
suggestion: "No bloquea el envío. Atiende estas warnings antes de producción para mejorar la calidad del dato compartido.",
|
|
568
|
+
docsUrl: DOCS("hapi-warning-best-practice"),
|
|
569
|
+
},
|
|
570
|
+
|
|
571
|
+
// ── MCP infraestructura ───────────────────────────────────────────────────
|
|
572
|
+
"mcp-auth-01": {
|
|
573
|
+
why: "El conector necesita una API key para llamar al validador HAPI remoto. Sin ella solo puede correr reglas locales.",
|
|
574
|
+
suggestion: "Pasa `--key TU_API_KEY` al invocar el MCP, o define la variable de entorno FHIRON_API_KEY. Obtén una gratis en https://fhiron.cl/registro.",
|
|
575
|
+
docsUrl: DOCS("mcp-auth-01"),
|
|
576
|
+
},
|
|
577
|
+
"mcp-auth-02": {
|
|
578
|
+
why: "La API key existe pero el servidor la rechazó (401/403). Pudo haber sido revocada, rotada o nunca existió.",
|
|
579
|
+
suggestion: "Verifica el valor en https://fhiron.cl/dashboard/connect-ai. Si la rotaste, actualiza tu config MCP.",
|
|
580
|
+
docsUrl: DOCS("mcp-auth-02"),
|
|
581
|
+
},
|
|
582
|
+
"mcp-quota-01": {
|
|
583
|
+
why: "Se agotó la cuota mensual de validaciones del plan actual. Las reglas locales (cl-*) siguen funcionando offline.",
|
|
584
|
+
suggestion: "Mejora el plan en https://fhiron.cl/precios o espera al primer día del próximo mes. Las tools `fhiron_fhir_lint_local` y `fhiron_fhir_lint_run` son gratis.",
|
|
585
|
+
docsUrl: DOCS("mcp-quota-01"),
|
|
586
|
+
},
|
|
587
|
+
"mcp-net-01": {
|
|
588
|
+
why: "No se pudo establecer conexión con Fhiron Bridge — red caída, firewall, DNS o el servicio está reiniciando.",
|
|
589
|
+
suggestion: "Revisa tu conexión, el firewall corporativo y el estado del servicio en https://status.fhiron.cl.",
|
|
590
|
+
docsUrl: DOCS("mcp-net-01"),
|
|
591
|
+
},
|
|
592
|
+
"mcp-server-01": {
|
|
593
|
+
why: "El servidor respondió 500 — bug en Fhiron Bridge, no en tu recurso.",
|
|
594
|
+
suggestion: "Reintenta en unos segundos. Si persiste, reporta el caso en https://github.com/Fhironstack/skills/issues incluyendo el recurso (sin datos sensibles).",
|
|
595
|
+
docsUrl: DOCS("mcp-server-01"),
|
|
596
|
+
},
|
|
597
|
+
"mcp-server-02": {
|
|
598
|
+
why: "El servidor respondió 502/503/504 — gateway o servicio temporalmente no disponible (deploy, restart, rolling update).",
|
|
599
|
+
suggestion: "Reintenta en 1–2 minutos. Si persiste, revisa https://status.fhiron.cl.",
|
|
600
|
+
docsUrl: DOCS("mcp-server-02"),
|
|
601
|
+
},
|
|
602
|
+
"mcp-server-03": {
|
|
603
|
+
why: "Respuesta HTTP inesperada del servidor (no 200, 401, 403, 422, 429, 500, 502-504). Probablemente un error de proxy intermedio.",
|
|
604
|
+
suggestion: "Reintenta. Si persiste, captura el `message` y reporta el caso.",
|
|
605
|
+
docsUrl: DOCS("mcp-server-03"),
|
|
606
|
+
},
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Devuelve la entrada del catálogo para un código, o null si no existe.
|
|
613
|
+
* @param {string} code
|
|
614
|
+
* @returns {{ why?:string, profileUrl?:string, docsUrl?:string, suggestion?:string, example?:any }|null}
|
|
615
|
+
*/
|
|
616
|
+
function lookupCode(code) {
|
|
617
|
+
if (!code) return null;
|
|
618
|
+
return CATALOG[code] || null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Lista todos los códigos del catálogo (útil para fhiron_explain_code y tests).
|
|
623
|
+
* @returns {string[]}
|
|
624
|
+
*/
|
|
625
|
+
function listCodes() {
|
|
626
|
+
return Object.keys(CATALOG);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
module.exports = {
|
|
630
|
+
CATALOG,
|
|
631
|
+
lookupCode,
|
|
632
|
+
listCodes,
|
|
633
|
+
IG_BASE,
|
|
634
|
+
SD,
|
|
635
|
+
CS,
|
|
636
|
+
VS,
|
|
637
|
+
DOCS,
|
|
638
|
+
};
|