zenit-sdk 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs DELETED
@@ -1,1218 +0,0 @@
1
- import {
2
- ChevronLeft,
3
- ChevronRight,
4
- Eye,
5
- EyeOff,
6
- FloatingChatBox,
7
- Layers,
8
- Upload,
9
- X,
10
- ZenitFeatureFilterPanel,
11
- ZenitLayerManager,
12
- ZenitMap,
13
- ZoomIn,
14
- applyFilteredGeoJSONStrategy,
15
- applyLayerOverrides,
16
- centroidFromBBox,
17
- clampNumber,
18
- clampOpacity,
19
- computeBBoxFromFeature,
20
- createChatService,
21
- extractMapDto,
22
- getAccentByLayerId,
23
- getEffectiveLayerOpacity,
24
- getLayerColor,
25
- getLayerZoomOpacityFactor,
26
- getStyleByLayerId,
27
- getZoomOpacityFactor,
28
- initLayerStates,
29
- isPolygonLayer,
30
- normalizeMapCenter,
31
- normalizeMapLayers,
32
- resetOverrides,
33
- resolveLayerAccent,
34
- sendMessage,
35
- sendMessageStream,
36
- useSendMessage,
37
- useSendMessageStream
38
- } from "./chunk-URDEEWUZ.mjs";
39
-
40
- // src/http/HttpClient.ts
41
- var HttpClient = class {
42
- constructor(options) {
43
- this.baseUrl = options.baseUrl.replace(/\/$/, "");
44
- this.resolveTokens = options.resolveTokens;
45
- this.resolveAuthorizationHeader = options.resolveAuthorizationHeader ?? (() => ({}));
46
- }
47
- /**
48
- * Update base URL at runtime (e.g. when a host injects config).
49
- */
50
- setBaseUrl(baseUrl) {
51
- this.baseUrl = baseUrl.replace(/\/$/, "");
52
- }
53
- async get(path, options = {}) {
54
- const headers = {
55
- ...options.headers,
56
- ...this.resolveAuthorizationHeader()
57
- };
58
- return this.request(path, { ...options, method: "GET", headers });
59
- }
60
- async post(path, body, options = {}) {
61
- const headers = {
62
- "Content-Type": "application/json",
63
- ...options.headers,
64
- ...this.resolveAuthorizationHeader()
65
- };
66
- return this.request(path, {
67
- ...options,
68
- method: "POST",
69
- headers,
70
- body: body !== void 0 ? JSON.stringify(body) : void 0
71
- });
72
- }
73
- async put(path, body, options = {}) {
74
- const headers = {
75
- "Content-Type": "application/json",
76
- ...options.headers,
77
- ...this.resolveAuthorizationHeader()
78
- };
79
- return this.request(path, {
80
- ...options,
81
- method: "PUT",
82
- headers,
83
- body: body !== void 0 ? JSON.stringify(body) : void 0
84
- });
85
- }
86
- async patch(path, body, options = {}) {
87
- const headers = {
88
- "Content-Type": "application/json",
89
- ...options.headers,
90
- ...this.resolveAuthorizationHeader()
91
- };
92
- return this.request(path, {
93
- ...options,
94
- method: "PATCH",
95
- headers,
96
- body: body !== void 0 ? JSON.stringify(body) : void 0
97
- });
98
- }
99
- async delete(path, options = {}) {
100
- const headers = {
101
- ...options.headers,
102
- ...this.resolveAuthorizationHeader()
103
- };
104
- return this.request(path, { ...options, method: "DELETE", headers });
105
- }
106
- async request(path, options) {
107
- const url = `${this.baseUrl}${path}`;
108
- const tokenState = this.resolveTokens?.();
109
- const headers = {
110
- ...options.headers
111
- };
112
- if (!headers["Authorization"] && tokenState?.accessToken) {
113
- headers["Authorization"] = `Bearer ${tokenState.accessToken}`;
114
- }
115
- if (tokenState?.sdkToken) {
116
- headers["X-SDK-Token"] = tokenState.sdkToken;
117
- }
118
- const response = await fetch(url, {
119
- ...options,
120
- headers
121
- });
122
- const contentType = response.headers.get("content-type");
123
- const isJson = contentType?.includes("application/json");
124
- const payload = isJson ? await response.json() : await response.text();
125
- if (!response.ok) {
126
- const normalizedError = {
127
- success: false,
128
- statusCode: response.status,
129
- message: typeof payload === "string" ? payload : payload?.message || "Unknown error",
130
- timestamp: typeof payload === "object" ? payload.timestamp : void 0,
131
- path: typeof payload === "object" ? payload.path : void 0,
132
- method: typeof payload === "object" ? payload.method : void 0,
133
- error: typeof payload === "object" ? payload.error : void 0
134
- };
135
- throw normalizedError;
136
- }
137
- return payload;
138
- }
139
- };
140
-
141
- // src/auth/ZenitAuthClient.ts
142
- var ZenitAuthClient = class {
143
- constructor(http, updateTokens, config) {
144
- this.http = http;
145
- this.updateTokens = updateTokens;
146
- this.config = config;
147
- }
148
- async login(credentials) {
149
- try {
150
- const response = await this.http.post("/auth/login", credentials);
151
- const accessToken = response?.accessToken ?? response?.data?.accessToken;
152
- const refreshToken = response?.refreshToken ?? response?.data?.refreshToken;
153
- this.updateTokens({ accessToken, refreshToken });
154
- return response;
155
- } catch (error) {
156
- this.config.onAuthError?.(error);
157
- throw error;
158
- }
159
- }
160
- async refresh(refreshToken) {
161
- const tokenToUse = refreshToken || this.config.refreshToken;
162
- const headers = tokenToUse ? { Authorization: `Bearer ${tokenToUse}` } : void 0;
163
- const response = await this.http.post("/auth/refresh", void 0, { headers });
164
- const accessToken = response?.accessToken ?? response?.data?.accessToken;
165
- const nextRefreshToken = response?.refreshToken ?? response?.data?.refreshToken;
166
- this.updateTokens({ accessToken, refreshToken: nextRefreshToken });
167
- return response;
168
- }
169
- async me() {
170
- return this.http.get("/auth/me");
171
- }
172
- async validate() {
173
- return this.http.get("/auth/validate");
174
- }
175
- };
176
-
177
- // src/sdkAuth/ZenitSdkAuthClient.ts
178
- var ZenitSdkAuthClient = class {
179
- constructor(http, updateAccessToken, config) {
180
- this.http = http;
181
- this.updateAccessToken = updateAccessToken;
182
- this.config = config;
183
- }
184
- /**
185
- * Validate an SDK token. If no token argument is provided, it falls back to config.sdkToken.
186
- */
187
- async validateSdkToken(token) {
188
- const sdkToken = token || this.config.sdkToken;
189
- return this.http.post("/sdk-auth/validate", { token: sdkToken });
190
- }
191
- /**
192
- * Exchange an SDK token for an access token. Token can come from method args or config.sdkToken.
193
- */
194
- async exchangeSdkToken(token) {
195
- const sdkToken = token || this.config.sdkToken;
196
- const response = await this.http.post("/sdk-auth/exchange", { token: sdkToken });
197
- const accessToken = response?.accessToken ?? response?.data?.accessToken;
198
- this.updateAccessToken(accessToken);
199
- return response;
200
- }
201
- };
202
-
203
- // src/maps/ZenitMapsClient.ts
204
- var ZenitMapsClient = class {
205
- constructor(httpClient) {
206
- this.httpClient = httpClient;
207
- }
208
- async getMap(mapId, includeLayers = true) {
209
- const include = includeLayers ? "?includeLayers=true" : "";
210
- return this.httpClient.get(`/maps/${mapId}${include}`);
211
- }
212
- };
213
-
214
- // src/layers/catalogSupport.ts
215
- function isPolygonLike(value) {
216
- if (!value) {
217
- return false;
218
- }
219
- const normalized = value.toLowerCase();
220
- return normalized === "polygon" || normalized === "multipolygon";
221
- }
222
- function getCatalogSupport(args) {
223
- const { layerType, geometryType } = args;
224
- if (layerType !== void 0) {
225
- const supported = isPolygonLike(layerType);
226
- return {
227
- supported,
228
- reason: supported ? "supported" : "unsupported-geometry",
229
- layerType,
230
- geometryType
231
- };
232
- }
233
- if (geometryType !== void 0) {
234
- const supported = isPolygonLike(geometryType);
235
- return {
236
- supported,
237
- reason: supported ? "supported" : "unsupported-geometry",
238
- geometryType
239
- };
240
- }
241
- return {
242
- supported: false,
243
- reason: "unknown-geometry"
244
- };
245
- }
246
- var ZenitCatalogNotSupportedError = class extends Error {
247
- constructor(support, message) {
248
- super(message ?? "Catalog is not supported for this layer geometry");
249
- this.name = "ZenitCatalogNotSupportedError";
250
- this.support = support;
251
- Object.setPrototypeOf(this, new.target.prototype);
252
- }
253
- };
254
-
255
- // src/engine/PrefilterEngine.ts
256
- var normalizeString = (value) => value.trim().toLowerCase();
257
- var isPrefilterValue = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
258
- var matchesPrefilterValue = (candidate, expected) => {
259
- if (typeof expected === "string") {
260
- if (candidate === null || candidate === void 0) return false;
261
- return normalizeString(String(candidate)) === normalizeString(expected);
262
- }
263
- return candidate === expected;
264
- };
265
- var shouldApplyPrefilters = (prefilters) => !!prefilters && Object.keys(prefilters).length > 0;
266
- var featureMatchesPrefilters = (feature, prefilters) => {
267
- const properties = feature.properties ?? {};
268
- return Object.entries(prefilters).every(([key, expected]) => {
269
- if (!isPrefilterValue(expected)) return true;
270
- return matchesPrefilterValue(properties[key], expected);
271
- });
272
- };
273
- var applyPrefiltersToFeatureCollection = (featureCollection, prefilters) => {
274
- if (!shouldApplyPrefilters(prefilters)) return featureCollection;
275
- const nextFeatures = featureCollection.features.filter(
276
- (feature) => featureMatchesPrefilters(feature, prefilters)
277
- );
278
- return {
279
- ...featureCollection,
280
- features: nextFeatures
281
- };
282
- };
283
- var decorateFeaturesWithLayerId = (featureCollection, layerId) => {
284
- let shouldClone = false;
285
- featureCollection.features.forEach((feature) => {
286
- const existing = feature.properties?.__zenit_layerId;
287
- if (existing !== layerId) {
288
- shouldClone = true;
289
- }
290
- });
291
- if (!shouldClone) {
292
- return featureCollection;
293
- }
294
- return {
295
- ...featureCollection,
296
- features: featureCollection.features.map((feature) => ({
297
- ...feature,
298
- properties: {
299
- ...feature.properties ?? {},
300
- __zenit_layerId: layerId
301
- }
302
- }))
303
- };
304
- };
305
-
306
- // src/layers/ZenitLayersClient.ts
307
- function parseQueryParams(options = {}) {
308
- const params = new URLSearchParams();
309
- if (options.maxFeatures !== void 0) {
310
- params.set("maxFeatures", String(options.maxFeatures));
311
- }
312
- if (options.simplify !== void 0) {
313
- params.set("simplify", String(options.simplify));
314
- }
315
- return params;
316
- }
317
- function serializeFilterValue(value) {
318
- if (value === void 0 || value === null) {
319
- return void 0;
320
- }
321
- const serialized = String(value);
322
- return serialized.trim().length > 0 ? serialized : void 0;
323
- }
324
- function buildLayerFilters(filters) {
325
- const query = new URLSearchParams();
326
- if (!filters) {
327
- return query;
328
- }
329
- Object.entries(filters).forEach(([key, value]) => {
330
- if (Array.isArray(value)) {
331
- value.forEach((item) => {
332
- const serialized2 = serializeFilterValue(item);
333
- if (serialized2 !== void 0) {
334
- query.append(key, serialized2);
335
- }
336
- });
337
- return;
338
- }
339
- const serialized = serializeFilterValue(value);
340
- if (serialized !== void 0) {
341
- query.set(key, serialized);
342
- }
343
- });
344
- return query;
345
- }
346
- function buildFilterMultipleParams(params) {
347
- const query = buildLayerFilters(params.filters);
348
- if (!Array.isArray(params.layerIds) || params.layerIds.length === 0) {
349
- throw new Error("layerIds is required and cannot be empty");
350
- }
351
- query.set("layerIds", params.layerIds.map((id) => String(id)).join(","));
352
- return query;
353
- }
354
- function asOptionalNumber(value) {
355
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
356
- }
357
- function createEmptyFeatureCollection() {
358
- return { type: "FeatureCollection", features: [] };
359
- }
360
- function hasFilters(filters) {
361
- return buildLayerFilters(filters).size > 0;
362
- }
363
- function mergeLayerFilters(base, extra) {
364
- if (!base && !extra) return void 0;
365
- const merged = { ...base ?? {}, ...extra ?? {} };
366
- return hasFilters(merged) ? merged : void 0;
367
- }
368
- var DEFAULT_MAX_FEATURES_FULL_GEOJSON = 5e4;
369
- function normalizeLayerType(layerType) {
370
- return typeof layerType === "string" ? layerType.toLowerCase() : void 0;
371
- }
372
- function shouldUseFilterMultiple(layerType) {
373
- return normalizeLayerType(layerType) === "multipolygon";
374
- }
375
- function shouldSkipGeojsonDownload(layerType, totalFeatures, thresholds = {
376
- maxFeatures: DEFAULT_MAX_FEATURES_FULL_GEOJSON
377
- }) {
378
- if (thresholds.allowLargeGeojson) {
379
- return false;
380
- }
381
- const normalizedType = normalizeLayerType(layerType);
382
- if (normalizedType === "mixed") {
383
- return true;
384
- }
385
- if (typeof totalFeatures === "number" && totalFeatures > thresholds.maxFeatures) {
386
- return true;
387
- }
388
- return false;
389
- }
390
- function parseBbox(value) {
391
- if (!value || typeof value !== "object") {
392
- return void 0;
393
- }
394
- const candidate = value;
395
- if (typeof candidate.minLon === "number" && typeof candidate.minLat === "number" && typeof candidate.maxLon === "number" && typeof candidate.maxLat === "number") {
396
- return {
397
- minLon: candidate.minLon,
398
- minLat: candidate.minLat,
399
- maxLon: candidate.maxLon,
400
- maxLat: candidate.maxLat
401
- };
402
- }
403
- if (Array.isArray(value) && value.length === 4) {
404
- const [minLon, minLat, maxLon, maxLat] = value;
405
- if (typeof minLon === "number" && typeof minLat === "number" && typeof maxLon === "number" && typeof maxLat === "number") {
406
- return { minLon, minLat, maxLon, maxLat };
407
- }
408
- }
409
- return void 0;
410
- }
411
- function buildBboxFromFeatureCollection(featureCollection) {
412
- const coords = [];
413
- const collect = (candidate) => {
414
- if (!Array.isArray(candidate)) return;
415
- if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number" && Number.isFinite(candidate[0]) && Number.isFinite(candidate[1])) {
416
- coords.push([candidate[0], candidate[1]]);
417
- return;
418
- }
419
- candidate.forEach((item) => collect(item));
420
- };
421
- featureCollection.features.forEach((feature) => {
422
- collect(feature.geometry?.coordinates);
423
- });
424
- if (coords.length === 0) return void 0;
425
- const [firstLon, firstLat] = coords[0];
426
- const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
427
- coords.forEach(([lon, lat]) => {
428
- bbox.minLon = Math.min(bbox.minLon, lon);
429
- bbox.minLat = Math.min(bbox.minLat, lat);
430
- bbox.maxLon = Math.max(bbox.maxLon, lon);
431
- bbox.maxLat = Math.max(bbox.maxLat, lat);
432
- });
433
- return bbox;
434
- }
435
- function buildGeometryCollectionFromFeatureCollection(featureCollection) {
436
- return {
437
- type: "GeometryCollection",
438
- geometries: featureCollection.features.map((feature) => feature.geometry).filter((geometry) => geometry !== void 0 && geometry !== null)
439
- };
440
- }
441
- function buildAoiFromFeatureCollection(featureCollection, metadata) {
442
- const metadataBbox = parseBbox(metadata?.bbox);
443
- const derivedBbox = metadataBbox ?? buildBboxFromFeatureCollection(featureCollection);
444
- const hasFeatures = featureCollection.features.length > 0;
445
- const geometry = hasFeatures ? buildGeometryCollectionFromFeatureCollection(featureCollection) : void 0;
446
- if (!derivedBbox && !geometry) {
447
- return null;
448
- }
449
- return { bbox: derivedBbox, geometry };
450
- }
451
- function splitFeatureCollectionByLayerId(featureCollection, layerIds) {
452
- const grouped = {};
453
- layerIds.forEach((layerId) => {
454
- grouped[String(layerId)] = createEmptyFeatureCollection();
455
- });
456
- featureCollection.features.forEach((feature) => {
457
- const properties = feature.properties;
458
- const layerId = properties?.layerId ?? properties?.layer_id ?? properties?.__zenit_layerId;
459
- if (layerId === void 0 || layerId === null) {
460
- return;
461
- }
462
- const key = String(layerId);
463
- if (!grouped[key]) {
464
- grouped[key] = createEmptyFeatureCollection();
465
- }
466
- grouped[key] = {
467
- ...grouped[key],
468
- features: [...grouped[key].features, feature]
469
- };
470
- });
471
- return grouped;
472
- }
473
- function resolveLayerStrategy(params) {
474
- const {
475
- layerDetail,
476
- bbox,
477
- filterBBox,
478
- filteredGeoJSON,
479
- isVisible = true,
480
- maxFeaturesFullGeojson,
481
- allowLargeGeojson
482
- } = params;
483
- if (!isVisible) {
484
- return { strategy: "geojson", effectiveBBox: bbox };
485
- }
486
- if (filteredGeoJSON && layerDetail.layerType !== "multipolygon") {
487
- return {
488
- strategy: "intersect",
489
- intersectionGeometry: buildGeometryCollectionFromFeatureCollection(filteredGeoJSON)
490
- };
491
- }
492
- const shouldSkipGeojson = shouldSkipGeojsonDownload(layerDetail.layerType, layerDetail.totalFeatures, {
493
- maxFeatures: maxFeaturesFullGeojson ?? DEFAULT_MAX_FEATURES_FULL_GEOJSON,
494
- allowLargeGeojson
495
- });
496
- if (shouldSkipGeojson && (filterBBox ?? bbox)) {
497
- return { strategy: "bbox", effectiveBBox: filterBBox ?? bbox };
498
- }
499
- if ((layerDetail.totalFeatures ?? 0) > 5e3 && (filterBBox ?? bbox)) {
500
- return { strategy: "bbox", effectiveBBox: filterBBox ?? bbox };
501
- }
502
- return { strategy: "geojson", effectiveBBox: bbox };
503
- }
504
- function buildLimitedMessage(metadata) {
505
- if (!metadata) {
506
- return void 0;
507
- }
508
- const explicit = metadata.limitedMessage;
509
- if (typeof explicit === "string" && explicit.trim().length > 0) {
510
- return explicit;
511
- }
512
- const limit = typeof metadata.limit === "number" ? metadata.limit : void 0;
513
- const total = typeof metadata.totalFeatures === "number" ? metadata.totalFeatures : void 0;
514
- if (limit !== void 0 && total !== void 0 && total > limit) {
515
- return `Mostrando ${limit.toLocaleString()} de ${total.toLocaleString()} elementos por l\xEDmite de capa.`;
516
- }
517
- if (metadata.limited && limit !== void 0) {
518
- return `Mostrando un m\xE1ximo de ${limit.toLocaleString()} elementos por l\xEDmite de capa.`;
519
- }
520
- return void 0;
521
- }
522
- var ZenitLayersClient = class {
523
- constructor(httpClient) {
524
- this.httpClient = httpClient;
525
- }
526
- async getLayer(layerId) {
527
- const response = await this.httpClient.get(
528
- `/layers/${layerId}`
529
- );
530
- return this.unwrapResponse(response);
531
- }
532
- async getLayerGeoJson(layerId, options = {}) {
533
- const params = parseQueryParams(options);
534
- const suffix = params.size > 0 ? `?${params.toString()}` : "";
535
- const response = await this.httpClient.get(
536
- `/layers/${layerId}/geojson${suffix}`
537
- );
538
- return this.unwrapResponse(response);
539
- }
540
- async getLayerFeaturesCatalog(layerId, options) {
541
- if (options?.strict) {
542
- const support = getCatalogSupport({
543
- layerType: options.layerType,
544
- geometryType: options.geometryType
545
- });
546
- if (!support.supported) {
547
- throw new ZenitCatalogNotSupportedError(
548
- support,
549
- support.reason === "unknown-geometry" ? "Catalog support cannot be determined without a geometry type" : "Catalog is only supported for polygon or multipolygon geometries"
550
- );
551
- }
552
- }
553
- const response = await this.httpClient.get(
554
- `/layers/${layerId}/features/catalog`
555
- );
556
- return this.unwrapResponse(response);
557
- }
558
- async getLayerFeatures(layerId, featureIds) {
559
- if (!Array.isArray(featureIds) || featureIds.length === 0) {
560
- throw new Error("featureIds is required and cannot be empty");
561
- }
562
- const query = featureIds.map(String).join(",");
563
- const response = await this.httpClient.get(
564
- `/layers/${layerId}/features?featureIds=${encodeURIComponent(query)}`
565
- );
566
- return this.unwrapResponse(response);
567
- }
568
- async filterLayerFeatures(layerId, filters) {
569
- const query = buildLayerFilters(filters);
570
- const suffix = query.toString();
571
- const response = await this.httpClient.get(
572
- `/layers/${layerId}/features/filter${suffix ? `?${suffix}` : ""}`
573
- );
574
- return this.unwrapResponse(response);
575
- }
576
- async filterMultipleLayersFeatures(params) {
577
- const query = buildFilterMultipleParams(params);
578
- const suffix = query.toString();
579
- const response = await this.httpClient.get(`/layers/features/filter-multiple${suffix ? `?${suffix}` : ""}`);
580
- return this.unwrapResponse(response);
581
- }
582
- async filterMultiple(params) {
583
- return this.filterMultipleLayersFeatures(params);
584
- }
585
- /**
586
- * Resilient filter-multiple helper:
587
- * - Usa filter-multiple SOLO en capas multipolygon.
588
- * - Para otras capas aplica filterLayerFeatures o getLayerGeoJson según haya filtros.
589
- * - Nunca falla toda la operación por una capa inválida.
590
- *
591
- * Para mejor detección de layerType, pasa layerMetas con el layerType de cada capa.
592
- */
593
- async filterMultipleWithFallback(params) {
594
- const layerIds = params.layerIds ?? [];
595
- const perLayer = {};
596
- const warnings = [];
597
- const hasLayerMetas = (params.layerMetas ?? []).length > 0;
598
- const maxFeaturesFullGeojson = params.maxFeaturesFullGeojson ?? DEFAULT_MAX_FEATURES_FULL_GEOJSON;
599
- const allowLargeGeojson = params.allowLargeGeojson ?? false;
600
- layerIds.forEach((layerId) => {
601
- perLayer[String(layerId)] = createEmptyFeatureCollection();
602
- });
603
- if (layerIds.length === 0) {
604
- return { perLayer };
605
- }
606
- const globalFilters = hasFilters(params.filters) ? params.filters : void 0;
607
- const metaIndex = /* @__PURE__ */ new Map();
608
- (params.layerMetas ?? []).forEach((meta) => {
609
- metaIndex.set(String(meta.layerId), meta);
610
- });
611
- let multipolygonIds = [];
612
- let otherIds = [];
613
- const layerIdsWithData = /* @__PURE__ */ new Set();
614
- let aoi = null;
615
- const pushWarning = (message) => {
616
- warnings.push(message);
617
- console.warn(message);
618
- };
619
- const buildSkipWarning = (layerId, layerType, totalFeatures, reason) => {
620
- const typeLabel = layerType ?? "unknown";
621
- const totalLabel = typeof totalFeatures === "number" ? ` totalFeatures=${totalFeatures}` : "";
622
- const reasonLabel = reason ? ` ${reason}` : "";
623
- return `Layer ${layerId} skipped: layerType=${typeLabel}${totalLabel}${reasonLabel}`;
624
- };
625
- if (hasLayerMetas) {
626
- layerIds.forEach((layerId) => {
627
- const meta = metaIndex.get(String(layerId));
628
- if (shouldUseFilterMultiple(meta?.layerType)) {
629
- multipolygonIds.push(layerId);
630
- } else {
631
- otherIds.push(layerId);
632
- }
633
- });
634
- } else {
635
- multipolygonIds = [...layerIds];
636
- }
637
- let metadata;
638
- if (multipolygonIds.length > 0) {
639
- try {
640
- const response = await this.filterMultipleLayersFeatures({
641
- layerIds: multipolygonIds,
642
- filters: globalFilters
643
- });
644
- metadata = response.metadata;
645
- const geojson = response.data ?? createEmptyFeatureCollection();
646
- if (globalFilters) {
647
- aoi = buildAoiFromFeatureCollection(geojson, metadata);
648
- }
649
- const perLayerFromMultiple = splitFeatureCollectionByLayerId(geojson, layerIds);
650
- Object.entries(perLayerFromMultiple).forEach(([layerKey, collection]) => {
651
- if (collection.features.length === 0) {
652
- return;
653
- }
654
- perLayer[layerKey] = decorateFeaturesWithLayerId(collection, layerKey);
655
- layerIdsWithData.add(layerKey);
656
- });
657
- } catch {
658
- if (hasLayerMetas) {
659
- otherIds = [.../* @__PURE__ */ new Set([...otherIds, ...multipolygonIds])];
660
- } else {
661
- otherIds = [...layerIds];
662
- }
663
- multipolygonIds = [];
664
- }
665
- }
666
- if (otherIds.length > 0) {
667
- const pendingIds = otherIds.filter(
668
- (layerId) => !layerIdsWithData.has(String(layerId))
669
- );
670
- const resolved = await Promise.all(
671
- pendingIds.map(async (layerId) => {
672
- const meta = metaIndex.get(String(layerId));
673
- const layerType = meta?.layerType;
674
- const totalFeatures = meta?.totalFeatures;
675
- const layerFilters = mergeLayerFilters(globalFilters, meta?.prefilters);
676
- const canUseIntersect = !!globalFilters && !shouldUseFilterMultiple(layerType) && !!aoi?.geometry;
677
- if (canUseIntersect) {
678
- const intersectionGeometry = aoi?.geometry;
679
- if (!intersectionGeometry) {
680
- pushWarning(
681
- buildSkipWarning(
682
- layerId,
683
- layerType,
684
- totalFeatures,
685
- "no AOI geometry available"
686
- )
687
- );
688
- return { layerId, geojson: createEmptyFeatureCollection() };
689
- }
690
- try {
691
- const response = await this.getLayerGeoJsonIntersect({
692
- id: layerId,
693
- geometry: intersectionGeometry
694
- });
695
- if (aoi?.bbox) {
696
- console.debug(
697
- `Layer ${layerId} fetched by intersect using AOI bbox=${JSON.stringify(
698
- aoi.bbox
699
- )}`
700
- );
701
- } else {
702
- console.debug(`Layer ${layerId} fetched by intersect using AOI geometry`);
703
- }
704
- const geojson = response.data ?? createEmptyFeatureCollection();
705
- return {
706
- layerId,
707
- geojson: decorateFeaturesWithLayerId(geojson, layerId)
708
- };
709
- } catch (error) {
710
- const message = error instanceof Error ? error.message : "Unknown error";
711
- pushWarning(
712
- buildSkipWarning(
713
- layerId,
714
- layerType,
715
- totalFeatures,
716
- `intersect failed: ${message}`
717
- )
718
- );
719
- return { layerId, geojson: createEmptyFeatureCollection() };
720
- }
721
- }
722
- if (layerFilters && (hasLayerMetas ? shouldUseFilterMultiple(layerType) : true)) {
723
- try {
724
- const response = await this.filterLayerFeatures(layerId, layerFilters);
725
- const geojson = response.data ?? createEmptyFeatureCollection();
726
- return {
727
- layerId,
728
- geojson: decorateFeaturesWithLayerId(geojson, layerId)
729
- };
730
- } catch (error) {
731
- const message = error instanceof Error ? error.message : "Unknown error";
732
- pushWarning(
733
- `Layer ${layerId} filter failed: ${message}`
734
- );
735
- return { layerId, geojson: createEmptyFeatureCollection() };
736
- }
737
- }
738
- if (shouldSkipGeojsonDownload(layerType, totalFeatures, {
739
- maxFeatures: maxFeaturesFullGeojson,
740
- allowLargeGeojson
741
- })) {
742
- pushWarning(
743
- buildSkipWarning(
744
- layerId,
745
- layerType,
746
- totalFeatures,
747
- "not supported by filter endpoint and too large for /geojson; use intersect with AOI"
748
- )
749
- );
750
- return { layerId, geojson: createEmptyFeatureCollection() };
751
- }
752
- try {
753
- const response = await this.getLayerGeoJson(layerId);
754
- const geojson = response.data ?? createEmptyFeatureCollection();
755
- return {
756
- layerId,
757
- geojson: decorateFeaturesWithLayerId(geojson, layerId)
758
- };
759
- } catch (error) {
760
- const message = error instanceof Error ? error.message : "Unknown error";
761
- pushWarning(
762
- buildSkipWarning(layerId, layerType, totalFeatures, `error=${message}`)
763
- );
764
- return { layerId, geojson: createEmptyFeatureCollection() };
765
- }
766
- })
767
- );
768
- resolved.forEach(({ layerId, geojson }) => {
769
- perLayer[String(layerId)] = geojson;
770
- });
771
- }
772
- const mergedMetadata = warnings.length > 0 ? {
773
- ...metadata ?? {},
774
- warnings: [...metadata?.warnings ?? [], ...warnings]
775
- } : metadata;
776
- return { perLayer, metadata: mergedMetadata, aoi };
777
- }
778
- async getLayerGeoJsonBBox(request) {
779
- const params = new URLSearchParams({
780
- minLon: String(request.bbox.minLon),
781
- minLat: String(request.bbox.minLat),
782
- maxLon: String(request.bbox.maxLon),
783
- maxLat: String(request.bbox.maxLat)
784
- });
785
- parseQueryParams(request).forEach((value, key) => params.set(key, value));
786
- const response = await this.httpClient.get(
787
- `/layers/${request.id}/geojson/bbox?${params.toString()}`
788
- );
789
- return this.unwrapResponse(response);
790
- }
791
- async getLayerGeoJsonIntersect(request) {
792
- const params = parseQueryParams(request);
793
- const suffix = params.size > 0 ? `?${params.toString()}` : "";
794
- const response = await this.httpClient.post(
795
- `/layers/${request.id}/geojson/intersects${suffix}`,
796
- { geometry: request.geometry }
797
- );
798
- return this.unwrapResponse(response);
799
- }
800
- async getLayerTileTemplate(layerId) {
801
- const response = await this.httpClient.get(
802
- `/layers/${layerId}/tiles/template`
803
- );
804
- const unwrapped = this.unwrapResponse(response);
805
- if (typeof unwrapped.data === "string") {
806
- return unwrapped.data;
807
- }
808
- if (unwrapped.data && typeof unwrapped.data.template === "string") {
809
- return unwrapped.data.template;
810
- }
811
- throw new Error("Invalid tile template response");
812
- }
813
- async loadLayerData(params) {
814
- const { id, layerDetail, bbox, filterBBox, filteredGeoJSON, isVisible = true, ...options } = params;
815
- const resolution = resolveLayerStrategy({
816
- layerDetail,
817
- bbox,
818
- filterBBox,
819
- filteredGeoJSON,
820
- isVisible,
821
- maxFeaturesFullGeojson: params.maxFeaturesFullGeojson,
822
- allowLargeGeojson: params.allowLargeGeojson
823
- });
824
- const response = await this.fetchByStrategy(id, resolution, options);
825
- const totalFeatures = asOptionalNumber(response.totalFeatures) ?? asOptionalNumber(response.metadata?.totalFeatures) ?? asOptionalNumber(layerDetail.totalFeatures);
826
- const limitedMessage = response.limitedMessage ?? buildLimitedMessage(response.metadata);
827
- return {
828
- geojson: response.data,
829
- strategy: resolution.strategy,
830
- limitedMessage,
831
- totalFeatures,
832
- metadata: response.metadata
833
- };
834
- }
835
- async loadFilteredFeatures(params) {
836
- const response = await this.filterMultipleLayersFeatures(params);
837
- return {
838
- geojson: response.data,
839
- metadata: response.metadata,
840
- totalFeatures: response.metadata?.totalFeatures ?? response.totalFeatures
841
- };
842
- }
843
- async fetchByStrategy(layerId, resolution, options) {
844
- switch (resolution.strategy) {
845
- case "bbox": {
846
- if (!resolution.effectiveBBox) {
847
- throw new Error("Bounding box is required for bbox strategy");
848
- }
849
- return this.getLayerGeoJsonBBox({ id: layerId, bbox: resolution.effectiveBBox, ...options });
850
- }
851
- case "intersect": {
852
- if (!resolution.intersectionGeometry) {
853
- throw new Error("Geometry is required for intersect strategy");
854
- }
855
- return this.getLayerGeoJsonIntersect({
856
- id: layerId,
857
- geometry: resolution.intersectionGeometry,
858
- ...options
859
- });
860
- }
861
- case "geojson":
862
- default:
863
- return this.getLayerGeoJson(layerId, options);
864
- }
865
- }
866
- unwrapResponse(response) {
867
- if (!response || typeof response !== "object" || !("data" in response)) {
868
- throw new Error("Invalid API response: missing data field");
869
- }
870
- if ("success" in response && response.success === false) {
871
- throw new Error(response.message ?? "Request was not successful");
872
- }
873
- return response;
874
- }
875
- };
876
-
877
- // src/config/ZenitSdkConfig.ts
878
- var ZenitClient = class {
879
- constructor(config) {
880
- this.config = config;
881
- this.accessToken = config.accessToken;
882
- this.refreshToken = config.refreshToken;
883
- this.sdkToken = config.sdkToken;
884
- this.defaultFilters = config.defaultFilters;
885
- this.httpClient = new HttpClient({
886
- baseUrl: config.baseUrl,
887
- resolveTokens: () => ({ accessToken: this.accessToken, sdkToken: this.sdkToken }),
888
- resolveAuthorizationHeader: this.getAuthorizationHeader.bind(this)
889
- });
890
- this.auth = new ZenitAuthClient(this.httpClient, this.updateTokens.bind(this), config);
891
- this.sdkAuth = new ZenitSdkAuthClient(
892
- this.httpClient,
893
- this.updateAccessTokenFromSdkExchange.bind(this),
894
- config
895
- );
896
- this.maps = new ZenitMapsClient(this.httpClient);
897
- this.layers = new ZenitLayersClient(this.httpClient);
898
- }
899
- /**
900
- * Update tokens in memory and propagate to config callbacks.
901
- */
902
- updateTokens(tokens) {
903
- if (tokens.accessToken) {
904
- this.setAccessToken(tokens.accessToken);
905
- }
906
- if (tokens.refreshToken) {
907
- this.setRefreshToken(tokens.refreshToken);
908
- }
909
- this.config.onTokenRefreshed?.({
910
- accessToken: this.accessToken || "",
911
- refreshToken: this.refreshToken
912
- });
913
- }
914
- // Se usa cuando /sdk-auth/exchange devuelve un accessToken; pasa a ser el accessToken principal del SDK.
915
- updateAccessTokenFromSdkExchange(token) {
916
- if (token) {
917
- this.setAccessToken(token);
918
- }
919
- }
920
- /**
921
- * Update the access token at runtime.
922
- */
923
- setAccessToken(token) {
924
- this.accessToken = token;
925
- this.config.accessToken = token;
926
- }
927
- /**
928
- * Update the refresh token at runtime.
929
- */
930
- setRefreshToken(token) {
931
- this.refreshToken = token;
932
- this.config.refreshToken = token;
933
- }
934
- /**
935
- * Update the SDK token at runtime.
936
- */
937
- setSdkToken(token) {
938
- this.sdkToken = token;
939
- this.config.sdkToken = token;
940
- }
941
- /**
942
- * Update both access token and SDK token at once.
943
- */
944
- setAuth(params) {
945
- if ("accessToken" in params) {
946
- this.setAccessToken(params.accessToken);
947
- }
948
- if ("sdkToken" in params) {
949
- this.setSdkToken(params.sdkToken);
950
- }
951
- }
952
- /**
953
- * Update base URL at runtime (e.g. injected config from a host app).
954
- */
955
- setBaseUrl(baseUrl) {
956
- this.config.baseUrl = baseUrl;
957
- this.httpClient.setBaseUrl(baseUrl);
958
- }
959
- /**
960
- * Store default filters for UI integrations.
961
- */
962
- setDefaultFilters(filters) {
963
- this.defaultFilters = filters;
964
- this.config.defaultFilters = filters;
965
- }
966
- /**
967
- * Return default filters for UI integrations.
968
- */
969
- getDefaultFilters() {
970
- return this.defaultFilters ? { ...this.defaultFilters } : void 0;
971
- }
972
- /**
973
- * Get a readonly snapshot of the current SDK config.
974
- */
975
- getConfig() {
976
- return { ...this.config };
977
- }
978
- /**
979
- * Get a readonly snapshot of runtime config for debugging or host integrations.
980
- */
981
- getRuntimeConfig() {
982
- return {
983
- baseUrl: this.config.baseUrl,
984
- accessToken: this.accessToken,
985
- sdkToken: this.sdkToken,
986
- defaultFilters: this.defaultFilters ? { ...this.defaultFilters } : void 0
987
- };
988
- }
989
- /**
990
- * Build an authorization header on demand using the current token values.
991
- */
992
- getAuthorizationHeader() {
993
- const header = {};
994
- if (this.accessToken) {
995
- header.Authorization = `Bearer ${this.accessToken}`;
996
- }
997
- return header;
998
- }
999
- getHttpClient() {
1000
- return this.httpClient;
1001
- }
1002
- };
1003
-
1004
- // src/config/ZenitRuntimeConfig.ts
1005
- var isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1006
- var normalizeFilters = (filters) => {
1007
- if (!isPlainRecord(filters)) {
1008
- return {};
1009
- }
1010
- return { ...filters };
1011
- };
1012
- var mergeFilters = (base = {}, next = {}) => ({
1013
- ...base,
1014
- ...next
1015
- });
1016
- var resolveRuntimeConfig = (input) => {
1017
- if (!isPlainRecord(input)) {
1018
- return null;
1019
- }
1020
- const baseUrl = typeof input.baseUrl === "string" ? input.baseUrl : null;
1021
- if (!baseUrl) {
1022
- return null;
1023
- }
1024
- const accessToken = typeof input.accessToken === "string" ? input.accessToken : void 0;
1025
- const sdkToken = typeof input.sdkToken === "string" ? input.sdkToken : void 0;
1026
- let mapId;
1027
- if (typeof input.mapId === "number" && !Number.isNaN(input.mapId)) {
1028
- mapId = input.mapId;
1029
- } else if (typeof input.mapId === "string" && input.mapId.trim() !== "") {
1030
- const parsed = Number(input.mapId);
1031
- if (!Number.isNaN(parsed)) {
1032
- mapId = parsed;
1033
- }
1034
- }
1035
- let defaultFilters;
1036
- if (isPlainRecord(input.defaultFilters)) {
1037
- defaultFilters = normalizeFilters(input.defaultFilters);
1038
- }
1039
- return {
1040
- baseUrl,
1041
- accessToken,
1042
- sdkToken,
1043
- mapId,
1044
- defaultFilters
1045
- };
1046
- };
1047
-
1048
- // src/engine/FilterEngine.ts
1049
- var FilterEngine = class {
1050
- decide(params) {
1051
- if (params.request) {
1052
- return { nextRequest: params.request };
1053
- }
1054
- if (params.filters && params.layerIds && params.layerIds.length > 1) {
1055
- return {
1056
- nextRequest: {
1057
- type: "filter-multiple",
1058
- layerIds: params.layerIds,
1059
- filters: params.filters
1060
- },
1061
- reason: "filters+layerIds"
1062
- };
1063
- }
1064
- if (params.filters && params.layerId !== void 0) {
1065
- return {
1066
- nextRequest: {
1067
- type: "filter-layer",
1068
- layerId: params.layerId,
1069
- filters: params.filters
1070
- },
1071
- reason: "filters+layerId"
1072
- };
1073
- }
1074
- if (params.bbox && params.layerId !== void 0) {
1075
- return {
1076
- nextRequest: {
1077
- type: "bbox",
1078
- layerId: params.layerId,
1079
- bbox: params.bbox,
1080
- options: params.options
1081
- },
1082
- reason: "bbox+layerId"
1083
- };
1084
- }
1085
- if (params.geometry && params.layerId !== void 0) {
1086
- return {
1087
- nextRequest: {
1088
- type: "intersect",
1089
- layerId: params.layerId,
1090
- geometry: params.geometry,
1091
- options: params.options
1092
- },
1093
- reason: "geometry+layerId"
1094
- };
1095
- }
1096
- if (params.layerId !== void 0) {
1097
- return {
1098
- nextRequest: {
1099
- type: "geojson",
1100
- layerId: params.layerId,
1101
- options: params.options
1102
- },
1103
- reason: "layerId"
1104
- };
1105
- }
1106
- throw new Error("FilterEngine could not resolve a next request");
1107
- }
1108
- };
1109
- var filterEngine = new FilterEngine();
1110
-
1111
- // src/ai/normalize.ts
1112
- function normalizeBbox(input) {
1113
- if (!input) return null;
1114
- if (Array.isArray(input)) {
1115
- if (input.length !== 4) {
1116
- console.warn("[normalizeBbox] Array must have exactly 4 elements, got:", input.length);
1117
- return null;
1118
- }
1119
- const [minLon, minLat, maxLon, maxLat] = input;
1120
- if (typeof minLon !== "number" || typeof minLat !== "number" || typeof maxLon !== "number" || typeof maxLat !== "number" || !Number.isFinite(minLon) || !Number.isFinite(minLat) || !Number.isFinite(maxLon) || !Number.isFinite(maxLat)) {
1121
- console.warn("[normalizeBbox] Array contains non-finite numbers:", input);
1122
- return null;
1123
- }
1124
- if (minLon >= maxLon || minLat >= maxLat) {
1125
- console.warn("[normalizeBbox] Invalid bbox: min must be < max", {
1126
- minLon,
1127
- maxLon,
1128
- minLat,
1129
- maxLat
1130
- });
1131
- return null;
1132
- }
1133
- return { minLon, minLat, maxLon, maxLat };
1134
- }
1135
- if (typeof input === "object") {
1136
- const candidate = input;
1137
- const { minLon, minLat, maxLon, maxLat } = candidate;
1138
- if (typeof minLon !== "number" || typeof minLat !== "number" || typeof maxLon !== "number" || typeof maxLat !== "number" || !Number.isFinite(minLon) || !Number.isFinite(minLat) || !Number.isFinite(maxLon) || !Number.isFinite(maxLat)) {
1139
- console.warn("[normalizeBbox] Object missing valid bbox keys or contains non-finite numbers:", input);
1140
- return null;
1141
- }
1142
- if (minLon >= maxLon || minLat >= maxLat) {
1143
- console.warn("[normalizeBbox] Invalid bbox: min must be < max", {
1144
- minLon,
1145
- maxLon,
1146
- minLat,
1147
- maxLat
1148
- });
1149
- return null;
1150
- }
1151
- return { minLon, minLat, maxLon, maxLat };
1152
- }
1153
- console.warn("[normalizeBbox] Unsupported bbox format:", typeof input);
1154
- return null;
1155
- }
1156
- export {
1157
- ChevronLeft,
1158
- ChevronRight,
1159
- DEFAULT_MAX_FEATURES_FULL_GEOJSON,
1160
- Eye,
1161
- EyeOff,
1162
- FilterEngine,
1163
- FloatingChatBox,
1164
- HttpClient,
1165
- Layers,
1166
- Upload,
1167
- X,
1168
- ZenitAuthClient,
1169
- ZenitCatalogNotSupportedError,
1170
- ZenitClient,
1171
- ZenitFeatureFilterPanel,
1172
- ZenitLayerManager,
1173
- ZenitLayersClient,
1174
- ZenitMap,
1175
- ZenitMapsClient,
1176
- ZenitSdkAuthClient,
1177
- ZoomIn,
1178
- applyFilteredGeoJSONStrategy,
1179
- applyLayerOverrides,
1180
- applyPrefiltersToFeatureCollection,
1181
- buildAoiFromFeatureCollection,
1182
- buildFilterMultipleParams,
1183
- buildLayerFilters,
1184
- buildLimitedMessage,
1185
- centroidFromBBox,
1186
- clampNumber,
1187
- clampOpacity,
1188
- computeBBoxFromFeature,
1189
- createChatService,
1190
- decorateFeaturesWithLayerId,
1191
- extractMapDto,
1192
- filterEngine,
1193
- getAccentByLayerId,
1194
- getCatalogSupport,
1195
- getEffectiveLayerOpacity,
1196
- getLayerColor,
1197
- getLayerZoomOpacityFactor,
1198
- getStyleByLayerId,
1199
- getZoomOpacityFactor,
1200
- initLayerStates,
1201
- isPolygonLayer,
1202
- mergeFilters,
1203
- normalizeBbox,
1204
- normalizeFilters,
1205
- normalizeMapCenter,
1206
- normalizeMapLayers,
1207
- resetOverrides,
1208
- resolveLayerAccent,
1209
- resolveLayerStrategy,
1210
- resolveRuntimeConfig,
1211
- sendMessage,
1212
- sendMessageStream,
1213
- shouldSkipGeojsonDownload,
1214
- shouldUseFilterMultiple,
1215
- useSendMessage,
1216
- useSendMessageStream
1217
- };
1218
- //# sourceMappingURL=index.mjs.map