zenit-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,279 @@
1
+ # zenit-sdk
2
+
3
+ ## Introducción
4
+ `zenit-sdk` es una librería en TypeScript para consumir el backend de Zenit de forma sencilla. El core es agnóstico al framework y se enfoca en ofrecer clientes HTTP y helpers de autenticación. Además, incluye un componente React (`ZenitMap`) para integraciones de UI basado en Leaflet.
5
+
6
+ ## Instalación
7
+ ```bash
8
+ npm install zenit-sdk
9
+ # o con yarn
10
+ yarn add zenit-sdk
11
+ ```
12
+
13
+ Para usar el componente React instala también sus peer dependencies en tu proyecto:
14
+ ```bash
15
+ npm install react react-dom leaflet react-leaflet
16
+ # o con yarn
17
+ yarn add react react-dom leaflet react-leaflet
18
+ ```
19
+
20
+ ## Entradas del SDK
21
+ - **Core (Node/HTTP)**: `import { ZenitClient } from 'zenit-sdk';`
22
+ - **React (UI)**: `import { ZenitMap } from 'zenit-sdk/react';`
23
+
24
+ ### Uso básico – Cliente de usuario (core)
25
+ ```ts
26
+ import { ZenitClient } from 'zenit-sdk';
27
+
28
+ const client = new ZenitClient({
29
+ baseUrl: 'https://mi-zenit.com/api/v1'
30
+ });
31
+
32
+ async function demo() {
33
+ const login = await client.auth.login({
34
+ email: '<EMAIL>',
35
+ password: '<PASSWORD>'
36
+ });
37
+
38
+ console.log('User:', login.user);
39
+
40
+ const me = await client.auth.me();
41
+ console.log('Me:', me);
42
+
43
+ const valid = await client.auth.validate();
44
+ console.log('Validate:', valid);
45
+
46
+ const refreshed = await client.auth.refresh(login.refreshToken);
47
+ console.log('Refresh:', refreshed);
48
+ }
49
+ ```
50
+
51
+ ### Uso básico – SDK Token (core)
52
+ ```ts
53
+ import { ZenitClient } from 'zenit-sdk';
54
+
55
+ const client = new ZenitClient({
56
+ baseUrl: 'https://mi-zenit.com/api/v1',
57
+ sdkToken: '<SDK_TOKEN>'
58
+ });
59
+
60
+ async function demoSdk() {
61
+ const validation = await client.sdkAuth.validateSdkToken();
62
+ console.log('SDK token validation:', validation);
63
+
64
+ const exchange = await client.sdkAuth.exchangeSdkToken();
65
+ console.log('SDK exchange:', exchange);
66
+
67
+ const me = await client.auth.me();
68
+ console.log('Me using SDK access token:', me);
69
+ }
70
+ ```
71
+
72
+ ### Uso básico – Componente React `ZenitMap`
73
+ ```tsx
74
+ import React from 'react';
75
+ import { ZenitClient } from 'zenit-sdk';
76
+ import { ZenitMap } from 'zenit-sdk/react';
77
+ import 'leaflet/dist/leaflet.css';
78
+
79
+ const client = new ZenitClient({
80
+ baseUrl: 'https://mi-zenit.com/api/v1',
81
+ sdkToken: '<SDK_TOKEN>'
82
+ });
83
+
84
+ export function App() {
85
+ return (
86
+ <div>
87
+ <h1>Demo ZenitMap</h1>
88
+ <ZenitMap client={client} mapId={123} />
89
+ </div>
90
+ );
91
+ }
92
+ ```
93
+ Si las capas del mapa incluyen `layer.label`, ZenitMap mostrará marcadores de etiqueta usando esa propiedad de las
94
+ features (respeta visibilidad y opacidad de la capa).
95
+
96
+ ### Chat flotante `FloatingChatBox` (Zenit AI)
97
+ El SDK incluye un chat flotante para conversar con Zenit AI usando los endpoints de mapas. Solo necesitas entregar
98
+ `baseUrl` y `mapId` (el token es opcional, pero se puede inyectar con `accessToken` o `getAccessToken`).
99
+
100
+ ```tsx
101
+ import React from 'react';
102
+ import { FloatingChatBox } from 'zenit-sdk/react';
103
+
104
+ export function App() {
105
+ return (
106
+ <>
107
+ <FloatingChatBox
108
+ baseUrl="https://mi-zenit.com/api/v1"
109
+ mapId={123}
110
+ filteredLayerIds={[45, 98]}
111
+ filters={{ CODREGION: 10 }}
112
+ getAccessToken={() => localStorage.getItem('access_token') ?? ''}
113
+ />
114
+ </>
115
+ );
116
+ }
117
+ ```
118
+
119
+ El chat funciona también en modo guest (`userId` nulo/omitido). Si no hay `mapId`, el componente muestra un estado
120
+ deshabilitado con el mensaje “Selecciona un mapa para usar el asistente”.
121
+
122
+ ### Panel reutilizable de capas y filtros `ZenitLayerManager`
123
+ El SDK incluye un panel listo para usar que administra visibilidad, opacidad y filtros por propiedades usando los endpoints `getLayerFeaturesCatalog` y `filter-multiple`. Puedes combinarlo con `ZenitMap` para mostrar el GeoJSON filtrado como overlay:
124
+
125
+ ```tsx
126
+ import React, { useState } from 'react';
127
+ import { ZenitClient, type FilterMultipleMetadata, type GeoJsonFeatureCollection } from 'zenit-sdk';
128
+ import { ZenitLayerManager, ZenitMap } from 'zenit-sdk/react';
129
+ import 'leaflet/dist/leaflet.css';
130
+
131
+ const client = new ZenitClient({ baseUrl: 'https://mi-zenit.com/api/v1', sdkToken: '<SDK_TOKEN>' });
132
+
133
+ export function App() {
134
+ const [overlay, setOverlay] = useState<GeoJsonFeatureCollection | null>(null);
135
+ const [metadata, setMetadata] = useState<FilterMultipleMetadata | undefined>();
136
+ const [layerControls, setLayerControls] = useState<
137
+ Array<{ layerId: number | string; visible: boolean; opacity: number }>
138
+ >([]);
139
+
140
+ return (
141
+ <div style={{ display: 'flex', height: 600 }}>
142
+ <ZenitLayerManager
143
+ client={client}
144
+ mapId={11}
145
+ onFilteredGeojson={(geojson, meta) => {
146
+ setOverlay(geojson);
147
+ setMetadata(meta);
148
+ }}
149
+ onLayerStatesChange={setLayerControls}
150
+ />
151
+ <ZenitMap
152
+ client={client}
153
+ mapId={11}
154
+ showLayerPanel={false}
155
+ overlayGeojson={overlay}
156
+ layerControls={layerControls}
157
+ />
158
+ <pre>{JSON.stringify(metadata, null, 2)}</pre>
159
+ </div>
160
+ );
161
+ }
162
+ ```
163
+
164
+ ### API de mapas y capas (core)
165
+ ```ts
166
+ import { ZenitClient, getCatalogSupport, ZenitCatalogNotSupportedError } from 'zenit-sdk';
167
+
168
+ const client = new ZenitClient({ baseUrl: 'https://mi-zenit.com/api/v1', accessToken: '<JWT>' });
169
+
170
+ // Metadatos de mapa (incluye capas visibles si includeLayers=true)
171
+ const map = await client.maps.getMap(11, true);
172
+
173
+ // Metadatos de capa (las respuestas usan ApiResponse con `data` y metadata opcional)
174
+ const layer = await client.layers.getLayer(123);
175
+ console.log('Layer:', layer.data);
176
+
177
+ // GeoJSON completo
178
+ const geojson = await client.layers.getLayerGeoJson(123);
179
+ console.log('GeoJSON features:', geojson.data.features?.length ?? 0);
180
+
181
+ // GeoJSON limitado por bounding box
182
+ const geojsonBBox = await client.layers.getLayerGeoJsonBBox({
183
+ id: 123,
184
+ bbox: {
185
+ minLon: -58.6,
186
+ minLat: -34.7,
187
+ maxLon: -58.3,
188
+ maxLat: -34.4,
189
+ },
190
+ });
191
+ console.log('GeoJSON bbox features:', geojsonBBox.data.features?.length ?? 0);
192
+
193
+ // Intersección con una geometría
194
+ const intersected = await client.layers.getLayerGeoJsonIntersect({ id: 123, geometry, maxFeatures: 5000 });
195
+ console.log('GeoJSON intersected features:', intersected.data.features?.length ?? 0);
196
+
197
+ // Catálogo de propiedades de features de una capa
198
+ const catalog = await client.layers.getLayerFeaturesCatalog(17);
199
+ console.log('Catalogo de capa', catalog.data);
200
+
201
+ // Catálogo con validación previa
202
+ const support = getCatalogSupport({ layerType: 'polygon' });
203
+ if (support.supported) {
204
+ const safeCatalog = await client.layers.getLayerFeaturesCatalog(17, {
205
+ layerType: 'polygon',
206
+ strict: true, // fail-fast sin request si la geometría no está soportada
207
+ });
208
+ console.log('Catálogo soportado', safeCatalog.data);
209
+ }
210
+
211
+ // Filtrado simultáneo en múltiples capas multipolygon
212
+ const filtered = await client.layers.filterMultipleLayersFeatures({
213
+ layerIds: [32, 17],
214
+ filters: { CODREGION: 10 },
215
+ });
216
+ console.log('filter-multiple data', filtered.data);
217
+
218
+ // Filtrado resiliente con capas mixtas (multipolygon + otras)
219
+ const mixedFiltered = await client.layers.filterMultipleWithFallback({
220
+ layerIds: [32, 17, 99],
221
+ filters: { CODREGION: 10 },
222
+ layerMetas: [
223
+ { layerId: 32, layerType: 'multipolygon' },
224
+ { layerId: 17, layerType: 'polygon' },
225
+ { layerId: 99, layerType: 'mixed' },
226
+ ],
227
+ });
228
+ console.log('filter-multiple fallback perLayer', mixedFiltered.perLayer);
229
+
230
+ // Helper de alto nivel
231
+ const loaded = await client.layers.loadFilteredFeatures({
232
+ layerIds: [32, 17],
233
+ filters: { CODREGION: 10 },
234
+ });
235
+ console.log('GeoJSON filtrado', loaded.geojson.features.length, 'elementos');
236
+ ```
237
+
238
+ El endpoint de catálogo solo admite capas `polygon`/`multipolygon`. Usa `getCatalogSupport` para decidir si corresponde
239
+ llamarlo, o pasa `{ strict: true }` a `getLayerFeaturesCatalog` para que el SDK arroje un `ZenitCatalogNotSupportedError`
240
+ antes de hacer la request.
241
+
242
+ Los métodos expuestos en `client.layers` permiten construir el panel de filtros sin dependencias externas:
243
+ - `getLayerFeaturesCatalog(layerId)` carga el catálogo de propiedades para cada capa.
244
+ - `filterMultipleLayersFeatures({ layerIds, filters })` ejecuta `filter-multiple` y retorna GeoJSON + metadata.
245
+ - `loadFilteredFeatures` es un helper tipado que entrega `geojson`, `metadata` y `totalFeatures` listos para usar.
246
+
247
+ Notas de filtrado:
248
+
249
+ - `layerIds` se envía como CSV (`[32,17]` -> `layerIds=32,17`).
250
+ - Los filtros son pares dinámicos `key/value`. Valores en arreglos se serializan como `a,b,c`.
251
+ - Valores `null`/`undefined` o cadenas vacías no se envían en la query.
252
+
253
+ ## Ejecutar ejemplos con `.env` opcional
254
+ Los ejemplos usan `ts-node` y leen variables de entorno. Puedes definirlas en tu shell o en un archivo `.env` (no se publica):
255
+
256
+ ```env
257
+ ZENIT_BASE_URL=http://localhost:3200/api/v1
258
+ ZENIT_EMAIL=<EMAIL>
259
+ ZENIT_PASSWORD=<PASSWORD>
260
+ ZENIT_SDK_TOKEN=<SDK_TOKEN>
261
+ ZENIT_ACCESS_TOKEN=<ACCESS_TOKEN>
262
+ ZENIT_MAP_ID=11
263
+ ```
264
+
265
+ En PowerShell:
266
+ ```powershell
267
+ $env:ZENIT_BASE_URL="http://localhost:3200/api/v1"
268
+ $env:ZENIT_EMAIL="user@example.com"
269
+ $env:ZENIT_PASSWORD="secret"
270
+ ```
271
+
272
+ Comandos disponibles:
273
+ ```bash
274
+ npm run example:auth
275
+ npm run example:sdk
276
+ npm run example:map
277
+ ```
278
+
279
+ El `baseUrl` por defecto de los ejemplos es `http://localhost:3200/api/v1` si no se especifica.