@phenyxhealth/sdk 1.0.15 → 1.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.
@@ -0,0 +1,481 @@
1
+ # PhenyxHealth SDK - Asistente de desarrollo
2
+
3
+ Estoy trabajando en un proyecto JavaScript/TypeScript que usa `@phenyxhealth/sdk`, el SDK oficial de PhenyxHealth para gestionar sistemas de radioterapia.
4
+
5
+ Usa la siguiente referencia completa de la API para ayudarme a escribir codigo correcto, eficiente y siguiendo los patrones del SDK.
6
+
7
+ ---
8
+
9
+ ## Instalacion
10
+
11
+ ```bash
12
+ npm install @phenyxhealth/sdk
13
+ ```
14
+
15
+ ## Imports disponibles
16
+
17
+ ```javascript
18
+ import {
19
+ PhenyxSDK, // Clase principal
20
+ checkGlobals, // Validar configuracion global
21
+ beSureGlobalExists, // Asegurar elementos globales
22
+ beSureDoctorExists, // Validar/crear doctores
23
+ beSureLinacsExists, // Validar/crear LINACs
24
+ today, // Fecha actual YYYY-MM-DD
25
+ formatDate, // Date -> YYYY-MM-DD
26
+ formatDateString, // DD/MM/YYYY -> YYYY-MM-DD
27
+ } from '@phenyxhealth/sdk';
28
+ ```
29
+
30
+ ---
31
+
32
+ ## PhenyxSDK - Clase principal
33
+
34
+ ### Propiedades de instancia
35
+
36
+ | Propiedad | Tipo | Descripcion |
37
+ |-----------|------|-------------|
38
+ | `apiToken` | `string \| null` | Token JWT de autenticacion |
39
+ | `apiHost` | `string \| null` | URL base de la API |
40
+ | `user` | `UserInfo \| null` | Info del usuario autenticado |
41
+ | `config` | `Record<string, any>` | Configuracion del cliente |
42
+ | `globalInfo` | `GlobalInfo[] \| null` | Info global del sistema |
43
+ | `activeTables` | `any[] \| null` | Tablas activas |
44
+ | `doctors` | `HumanResourceData[]` | Lista de doctores/fisicos medicos |
45
+ | `defaultDuration` | `Record<string, string>` | Duraciones por defecto de sesiones |
46
+ | `defaultCompatibleLinacs` | `Array<{id, name}>` | LINACs compatibles por defecto |
47
+ | `oars` | `any[]` | Lista de Organs At Risk |
48
+
49
+ ### Metodos estaticos
50
+
51
+ ```typescript
52
+ // Generar UUID v4
53
+ PhenyxSDK.newId(): string
54
+
55
+ // Generar color RGB contrastante aleatorio ("R, G, B")
56
+ PhenyxSDK.getContrastingColor(): string
57
+ ```
58
+
59
+ ### Autenticacion
60
+
61
+ ```typescript
62
+ // Login - inicializa la instancia con token, globalInfo, doctors, etc.
63
+ await api.login({ apiHost: string, user: string, password: string }): Promise<void>
64
+
65
+ // Obtener token JWT actual
66
+ api.getToken(): string | null
67
+
68
+ // Habilitar/deshabilitar logs HTTP
69
+ api.showLogs(showLogs: boolean): void
70
+ ```
71
+
72
+ ### Informacion global
73
+
74
+ ```typescript
75
+ // Recuperar info global del servidor
76
+ await api.retrieveGlobalInfo(): Promise<GlobalInfo[]>
77
+
78
+ // Recuperar tablas activas
79
+ await api.retrieveActiveTables(): Promise<any[]>
80
+
81
+ // Recuperar configuracion por defecto
82
+ await api.retrieveDefaults(): Promise<Object>
83
+
84
+ // Recuperar lista de OARs
85
+ await api.retrieveOars(): Promise<any[]>
86
+
87
+ // Obtener info global por tipo
88
+ api.getGlobalInfo(globalType: GlobalType): GlobalInfo | undefined
89
+
90
+ // Obtener elemento especifico de info global
91
+ api.getGlobalInfoElement(globalType: GlobalType, elementName: string): GlobalInfoElement | undefined
92
+
93
+ // Obtener ID de elemento global por nombre
94
+ api.getGlobalInfoElementId(globalType: GlobalType, elementName: string): string | null
95
+
96
+ // Actualizar info global
97
+ // IMPORTANTE: El servidor reasigna los IDs de los elementos nuevos al guardar.
98
+ // Despues de llamar a updateGlobalInfo, debes volver a llamar a getGlobalInfo()
99
+ // para obtener los IDs reales asignados por el servidor.
100
+ await api.updateGlobalInfo(globalTypeId: string, data: any): Promise<void>
101
+ ```
102
+
103
+ ### Pacientes
104
+
105
+ ```typescript
106
+ // Obtener todos los pacientes
107
+ await api.getPatients(): Promise<PatientData[]>
108
+
109
+ // Obtener datos clinicos de un paciente
110
+ await api.getPatient(id: string): Promise<PatientData>
111
+
112
+ // Crear paciente - retorna el ID del paciente creado
113
+ await api.createPatient(data: CreatePatientData): Promise<string>
114
+
115
+ // Eliminar paciente
116
+ await api.deletePatient(id: string): Promise<void>
117
+
118
+ // Crear cambio de estado
119
+ await api.createPatientStateChanges({
120
+ formDataId: string, // ID del caso
121
+ date?: string, // Fecha YYYY-MM-DD
122
+ humanResourceId?: string, // ID del doctor responsable
123
+ nextStateCode: string // Codigo del siguiente estado
124
+ }): Promise<void>
125
+ ```
126
+
127
+ ### Recursos humanos
128
+
129
+ ```typescript
130
+ // Obtener todos los recursos humanos
131
+ await api.getHumanResources(): Promise<HumanResourceData[]>
132
+
133
+ // Crear recurso humano
134
+ await api.createHumanResource({ name, typeId, agenda }): Promise<void>
135
+
136
+ // Buscar doctor por nombre
137
+ api.getDoctorIdByName(name: string): string | null
138
+ api.getDoctorTypeIdByName(name: string): string | null
139
+ api.getDoctorTypeNameByName(name: string): string | null
140
+ ```
141
+
142
+ ### Equipos/Recursos
143
+
144
+ ```typescript
145
+ // Obtener tipos de recursos
146
+ await api.getResourcesTypes(): Promise<any[]>
147
+
148
+ // Obtener todos los recursos (LINACs, etc)
149
+ await api.getResources(): Promise<ResourceData[]>
150
+
151
+ // Crear recurso
152
+ await api.createResource({ name, author?, typeId, agenda }): Promise<void>
153
+ ```
154
+
155
+ ### Lookups de elementos
156
+
157
+ ```typescript
158
+ api.getSendingDepartmentIdByName(name: string): string // throws si no existe
159
+ api.getCei9IdByName(name: string): string // throws si no existe
160
+ api.getCie10IdByName(name: string): string // throws si no existe
161
+ api.getCie10CodeExists(name: string): boolean
162
+ api.getElementIdByName(type: string, name: string): string | null
163
+ ```
164
+
165
+ ### QA y estados
166
+
167
+ ```typescript
168
+ api.getQaStatus(section: any): string | null
169
+ api.getRewiewedStatus(status: string): any[]
170
+ api.getSortedQa(qa: any): any[]
171
+ await api.updateSectionQaStatus(qa: any, formDataId: string): Promise<void>
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Constantes del SDK
177
+
178
+ ### `api.globalTypes` - 20 tipos de configuracion global
179
+
180
+ ```javascript
181
+ api.globalTypes.humanResources
182
+ api.globalTypes.sendingDepartment
183
+ api.globalTypes.cei9 // Codigos CEI-9
184
+ api.globalTypes.cie10 // Codigos CIE-10
185
+ api.globalTypes.treatmentClass
186
+ api.globalTypes.treatmentSubClass
187
+ api.globalTypes.treatmentTechnique
188
+ api.globalTypes.treatmentType
189
+ api.globalTypes.treatmentGoal
190
+ api.globalTypes.treatmentParticles
191
+ api.globalTypes.noTreatmentReasons
192
+ api.globalTypes.ctSimProtocols
193
+ api.globalTypes.linacsAllocationCriteria
194
+ api.globalTypes.sessionDurationCriteria
195
+ api.globalTypes.contouringTools
196
+ api.globalTypes.fractionationTypes
197
+ api.globalTypes.dosimetryCalculationMethods
198
+ api.globalTypes.dosimetrySecondaryCalculationMethods
199
+ api.globalTypes.tps
200
+ api.globalTypes.treatmentVerificationProtocols
201
+ ```
202
+
203
+ ### `api.allStatus` - 16 estados de workflow
204
+
205
+ ```javascript
206
+ api.allStatus.NOTSTARTED
207
+ api.allStatus.COMPLETED
208
+ api.allStatus.INPROGRESS
209
+ api.allStatus.ONREQUEST
210
+ api.allStatus.READYTOSTART
211
+ api.allStatus.DONE
212
+ api.allStatus.REVIEWED
213
+ api.allStatus.VALIDATED
214
+ api.allStatus.PEERREVIEWING
215
+ api.allStatus.PEERREVIEWED
216
+ api.allStatus.DONEMAIN
217
+ api.allStatus.DONESECONDCALCULATION
218
+ api.allStatus.STARTPROPOSED
219
+ api.allStatus.STARTCONFIRMED
220
+ api.allStatus.CANCELED
221
+ api.allStatus.CANCELLED
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Funciones de validacion (standalone)
227
+
228
+ ### `beSureDoctorExists(api, doctors)`
229
+ Valida que los doctores existan en el sistema. Si no existen, los crea automaticamente.
230
+
231
+ ```javascript
232
+ const doctors = await beSureDoctorExists(api, [
233
+ { name: 'Dr. Garcia', hrGroup: 'RadOnc' }, // Radiation Oncologist
234
+ { name: 'Dr. Lopez', hrGroup: 'MedPhys' }, // Medical Physicist
235
+ ]);
236
+ // Retorna: HumanResourceData[]
237
+ ```
238
+
239
+ ### `beSureLinacsExists(api, linacs)`
240
+ Valida que los LINACs existan. Si no, los crea.
241
+
242
+ ```javascript
243
+ const linacs = await beSureLinacsExists(api, ['LINAC-01', 'LINAC-02']);
244
+ // Retorna: ResourceData[]
245
+ ```
246
+
247
+ ### `checkGlobals(api, globalType, extraValues?)`
248
+ Valida que los valores globales existan. Si no, los crea.
249
+
250
+ ```javascript
251
+ await checkGlobals(api, api.globalTypes.treatmentType, ['IMRT', 'VMAT', 'SRS']);
252
+ ```
253
+
254
+ ### `beSureGlobalExists(api, currentList, toBeList)`
255
+ Nivel mas bajo: asegura que elementos especificos existan en una lista global.
256
+
257
+ ```javascript
258
+ const treatmentTypes = api.getGlobalInfo(api.globalTypes.treatmentType);
259
+ await beSureGlobalExists(api, treatmentTypes, ['IMRT', 'VMAT']);
260
+ ```
261
+
262
+ ---
263
+
264
+ ## Utilidades de fecha
265
+
266
+ ```javascript
267
+ today() // "2026-02-16" (fecha actual)
268
+ formatDate(new Date()) // "2026-02-16" (Date -> string)
269
+ formatDateString('16/02/2026') // "2026-02-16" (DD/MM/YYYY -> YYYY-MM-DD)
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Interfaces TypeScript principales
275
+
276
+ ```typescript
277
+ interface LoginCredentials {
278
+ apiHost: string;
279
+ user: string;
280
+ password: string;
281
+ }
282
+
283
+ interface PatientData {
284
+ id: string;
285
+ name: string;
286
+ birthDate: string;
287
+ sendingDepartmentId?: string;
288
+ cei9Id?: string;
289
+ cie10Id?: string;
290
+ [key: string]: any;
291
+ }
292
+
293
+ interface CreatePatientData {
294
+ name: string;
295
+ birthDate: string;
296
+ sendingDepartmentId?: string;
297
+ cei9Id?: string;
298
+ cie10Id?: string;
299
+ [key: string]: any;
300
+ }
301
+
302
+ interface HumanResourceData {
303
+ id: string;
304
+ name: string;
305
+ typeId: string;
306
+ typeName: string;
307
+ agenda: AgendaItem[];
308
+ }
309
+
310
+ interface DoctorValidation {
311
+ name: string;
312
+ hrGroup: 'RadOnc' | 'MedPhys';
313
+ }
314
+
315
+ interface ResourceData {
316
+ id: string;
317
+ name: string;
318
+ typeId: string;
319
+ author: string;
320
+ agenda: AgendaItem[];
321
+ }
322
+
323
+ interface GlobalInfo {
324
+ id: string;
325
+ name: string;
326
+ typeName: string;
327
+ elements: GlobalInfoElement[];
328
+ }
329
+
330
+ interface GlobalInfoElement {
331
+ id: string;
332
+ name: string;
333
+ color: string;
334
+ createdAt: string;
335
+ updatedAt: string;
336
+ isDeleted: boolean;
337
+ modality: string;
338
+ }
339
+
340
+ interface GlobalType {
341
+ type: string;
342
+ name: string;
343
+ values?: Record<string, string>;
344
+ }
345
+
346
+ interface StateChangeParams {
347
+ formDataId: string;
348
+ date?: string;
349
+ humanResourceId?: string | null;
350
+ nextStateCode: string;
351
+ }
352
+
353
+ interface AgendaItem {
354
+ id: string;
355
+ date: string;
356
+ startTime: string;
357
+ endTime: string;
358
+ description?: string;
359
+ }
360
+ ```
361
+
362
+ ---
363
+
364
+ ## Patrones comunes
365
+
366
+ ### Setup basico
367
+
368
+ ```javascript
369
+ const api = new PhenyxSDK();
370
+ await api.login({ apiHost: HOST, user: USER, password: PASS });
371
+ ```
372
+
373
+ ### Crear paciente completo
374
+
375
+ ```javascript
376
+ const patientId = await api.createPatient({
377
+ name: 'Juan Perez',
378
+ birthDate: '1975-03-15',
379
+ sendingDepartmentId: api.getSendingDepartmentIdByName('Oncologia'),
380
+ cei9Id: api.getCei9IdByName('C78.9'),
381
+ cie10Id: api.getCie10IdByName('C78.9'),
382
+ });
383
+
384
+ await api.createPatientStateChanges({
385
+ formDataId: patientId,
386
+ date: today(),
387
+ humanResourceId: api.getDoctorIdByName('Dr. Garcia'),
388
+ nextStateCode: api.allStatus.INPROGRESS,
389
+ });
390
+ ```
391
+
392
+ ### Configurar clinica
393
+
394
+ ```javascript
395
+ await beSureDoctorExists(api, [
396
+ { name: 'Dr. Garcia', hrGroup: 'RadOnc' },
397
+ { name: 'Dr. Lopez', hrGroup: 'MedPhys' },
398
+ ]);
399
+
400
+ await beSureLinacsExists(api, ['LINAC-01', 'LINAC-02']);
401
+
402
+ await checkGlobals(api, api.globalTypes.treatmentType, ['IMRT', 'VMAT', 'SRS']);
403
+ await checkGlobals(api, api.globalTypes.treatmentGoal, ['Curative', 'Palliative']);
404
+ ```
405
+
406
+ ### Crear elementos globales con dependencias entre si
407
+
408
+ Cuando creas elementos en un globalInfo, debes enviar un `id` (con `PhenyxSDK.newId()`), pero el servidor **ignora ese id y asigna uno nuevo**. Por lo tanto, despues de `updateGlobalInfo()` debes volver a obtener los datos con `getGlobalInfo()` para tener los IDs reales. Esto es critico cuando necesitas referenciar los elementos recien creados (ej: crear subclases que apuntan a clases).
409
+
410
+ ```javascript
411
+ // 1) Obtener el global y añadir elementos
412
+ const treatmentClass = api.getGlobalInfo(api.globalTypes.treatmentClass);
413
+
414
+ treatmentClass.elements.push({
415
+ id: PhenyxSDK.newId(), // obligatorio enviarlo, pero el servidor lo va a reemplazar
416
+ name: 'Radioterapia Externa',
417
+ color: PhenyxSDK.getContrastingColor(),
418
+ createdAt: new Date().toISOString(),
419
+ updatedAt: new Date().toISOString(),
420
+ isDeleted: false,
421
+ modality: '',
422
+ });
423
+
424
+ // 2) Guardar
425
+ await api.updateGlobalInfo(treatmentClass.id, treatmentClass);
426
+
427
+ // 3) OBLIGATORIO: Recargar para obtener los IDs reales del servidor
428
+ const createdClasses = api.getGlobalInfo(api.globalTypes.treatmentClass);
429
+
430
+ // 4) Ahora si puedes usar los IDs reales para crear elementos que los referencian
431
+ const parentClass = createdClasses.elements.find(e => e.name === 'Radioterapia Externa');
432
+ const treatmentSubClass = api.getGlobalInfo(api.globalTypes.treatmentSubClass);
433
+
434
+ treatmentSubClass.elements.push({
435
+ id: PhenyxSDK.newId(),
436
+ name: 'IMRT',
437
+ treatmentClass: parentClass.id, // ID real del servidor
438
+ color: PhenyxSDK.getContrastingColor(),
439
+ createdAt: new Date().toISOString(),
440
+ updatedAt: new Date().toISOString(),
441
+ isDeleted: false,
442
+ modality: '',
443
+ });
444
+
445
+ await api.updateGlobalInfo(treatmentSubClass.id, treatmentSubClass);
446
+ ```
447
+
448
+ ### Flujo QA
449
+
450
+ ```javascript
451
+ const doctorId = api.getDoctorIdByName('Dr. Lopez');
452
+
453
+ // Listo para QA -> Peer reviewing -> Peer reviewed -> Validado
454
+ for (const status of [
455
+ api.allStatus.READYTOSTART,
456
+ api.allStatus.PEERREVIEWING,
457
+ api.allStatus.PEERREVIEWED,
458
+ api.allStatus.VALIDATED,
459
+ ]) {
460
+ await api.createPatientStateChanges({
461
+ formDataId: patientId,
462
+ date: today(),
463
+ humanResourceId: doctorId,
464
+ nextStateCode: status,
465
+ });
466
+ }
467
+ ```
468
+
469
+ ---
470
+
471
+ ## Notas importantes
472
+
473
+ - `PhenyxSDK` es un alias de `PhenyxApi` (se exporta como default y como named export `PhenyxSDK`)
474
+ - `login()` inicializa automaticamente: `globalInfo`, `activeTables`, `doctors`, `defaultDuration`, `defaultCompatibleLinacs`, y `oars`
475
+ - Los metodos `get*ByName` buscan en los datos ya cargados en memoria (no hacen llamadas HTTP)
476
+ - Los metodos `getSendingDepartmentIdByName`, `getCei9IdByName`, `getCie10IdByName` lanzan Error si no encuentran el elemento
477
+ - `getContrastingColor()` y `newId()` son metodos estaticos - se usan sin instancia: `PhenyxSDK.newId()`
478
+ - **IDs en GlobalInfo**: Al hacer `push` de un elemento nuevo a `globalInfo.elements`, debes enviar un `id` (el servidor da error si no lo envias), pero el servidor lo ignora y asigna su propio ID. Siempre haz `getGlobalInfo()` despues de `updateGlobalInfo()` si necesitas referenciar los elementos creados
479
+ - Las funciones de validacion (`beSureDoctorExists`, `beSureLinacsExists`, `checkGlobals`) crean automaticamente los recursos que no existan
480
+ - El SDK usa ESM (`"type": "module"` en package.json)
481
+ - Compatible con Node.js >= 16
package/README.md CHANGED
@@ -408,6 +408,18 @@ const patient: PatientData = await api.getPatient("id");
408
408
  const doctors: HumanResourceData[] = await api.getHumanResources();
409
409
  ```
410
410
 
411
+ ## Uso con Claude Code
412
+
413
+ Si usas [Claude Code](https://claude.com/claude-code) para desarrollar, este SDK incluye un archivo de referencia completa de la API que puedes cargar automaticamente en tu proyecto.
414
+
415
+ Agrega la siguiente linea en el `CLAUDE.md` de tu proyecto:
416
+
417
+ ```
418
+ @node_modules/@phenyxhealth/sdk/.claude/phenyx-sdk.md
419
+ ```
420
+
421
+ Esto le dara a Claude Code el contexto completo de la API del SDK: todos los metodos, interfaces TypeScript, constantes, patrones de uso y notas importantes para que te asista correctamente al escribir codigo con `@phenyxhealth/sdk`.
422
+
411
423
  ## Contribución
412
424
 
413
425
  Las contribuciones son bienvenidas. Please:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phenyxhealth/sdk",
3
- "version": "1.0.15",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "SDK oficial para PhenyxHealth - Sistema integral de gestión de radioterapia. Incluye gestión de pacientes, recursos humanos, equipos médicos y configuración global.",
6
6
  "main": "./src/index.js",
@@ -11,6 +11,7 @@
11
11
  "files": [
12
12
  "src/",
13
13
  "types/",
14
+ ".claude/",
14
15
  "README.md"
15
16
  ],
16
17
  "scripts": {