npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zenit-sdk

v0.1.6

Published

TypeScript SDK for interacting with the Zenit backend APIs.

Readme

zenit-sdk

Introducción

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.

Instalación

npm install zenit-sdk
# o con yarn
yarn add zenit-sdk

Para usar el componente React instala también sus peer dependencies en tu proyecto:

npm install react react-dom leaflet react-leaflet
# o con yarn
yarn add react react-dom leaflet react-leaflet

Entradas del SDK

  • Core (Node/HTTP): import { ZenitClient } from 'zenit-sdk';
  • React (UI): import { ZenitMap } from 'zenit-sdk/react';

Uso básico – Cliente de usuario (core)

import { ZenitClient } from 'zenit-sdk';

const client = new ZenitClient({
  baseUrl: 'https://mi-zenit.com/api/v1'
});

async function demo() {
  const login = await client.auth.login({
    email: '<EMAIL>',
    password: '<PASSWORD>'
  });

  console.log('User:', login.user);

  const me = await client.auth.me();
  console.log('Me:', me);

  const valid = await client.auth.validate();
  console.log('Validate:', valid);

  const refreshed = await client.auth.refresh(login.refreshToken);
  console.log('Refresh:', refreshed);
}

Uso básico – SDK Token (core)

import { ZenitClient } from 'zenit-sdk';

const client = new ZenitClient({
  baseUrl: 'https://mi-zenit.com/api/v1',
  sdkToken: '<SDK_TOKEN>'
});

async function demoSdk() {
  const validation = await client.sdkAuth.validateSdkToken();
  console.log('SDK token validation:', validation);

  const exchange = await client.sdkAuth.exchangeSdkToken();
  console.log('SDK exchange:', exchange);

  const me = await client.auth.me();
  console.log('Me using SDK access token:', me);
}

Runtime config (WebView / host) + filtros

El cliente ahora permite actualizar configuración y tokens en runtime sin recrear la app:

import {
  ZenitClient,
  mergeFilters,
  normalizeFilters,
  resolveRuntimeConfig,
} from 'zenit-sdk';

const runtime = resolveRuntimeConfig(window.__ZENIT_RUNTIME_CONFIG__);

const client = new ZenitClient({
  baseUrl: runtime?.baseUrl ?? 'https://mi-zenit.com/api/v1',
  accessToken: runtime?.accessToken,
  sdkToken: runtime?.sdkToken,
});

client.setBaseUrl('https://mi-zenit.com/api/v2');
client.setAccessToken('token-2');
client.setSdkToken('sdk-2');
client.setAuth({ accessToken: 'token-3', sdkToken: 'sdk-3' });

client.setDefaultFilters(normalizeFilters({ CODREGION: 10 }));
const filters = mergeFilters(client.getDefaultFilters(), { CODDEPTO: 7 });
console.log('filters', filters);

Todos los requests construyen Authorization dinámicamente, así que un setAccessToken() afecta a la siguiente llamada sin reiniciar el cliente.

Uso básico – Componente React ZenitMap

import React from 'react';
import { ZenitClient } from 'zenit-sdk';
import { ZenitMap } from 'zenit-sdk/react';
import 'leaflet/dist/leaflet.css';

const client = new ZenitClient({
  baseUrl: 'https://mi-zenit.com/api/v1',
  sdkToken: '<SDK_TOKEN>'
});

export function App() {
  return (
    <div>
      <h1>Demo ZenitMap</h1>
      <ZenitMap client={client} mapId={123} />
    </div>
  );
}

Si las capas del mapa incluyen layer.label, ZenitMap mostrará marcadores de etiqueta usando esa propiedad de las features (respeta visibilidad y opacidad de la capa).

Chat flotante FloatingChatBox (Zenit AI)

El SDK incluye un chat flotante para conversar con Zenit AI usando los endpoints de mapas. Solo necesitas entregar baseUrl y mapId (el token es opcional, pero se puede inyectar con accessToken o getAccessToken).

import React from 'react';
import { FloatingChatBox } from 'zenit-sdk/react';

export function App() {
  return (
    <>
      <FloatingChatBox
        baseUrl="https://mi-zenit.com/api/v1"
        mapId={123}
        filteredLayerIds={[45, 98]}
        filters={{ CODREGION: 10 }}
        getAccessToken={() => localStorage.getItem('access_token') ?? ''}
      />
    </>
  );
}

El chat funciona también en modo guest (userId nulo/omitido). Si no hay mapId, el componente muestra un estado deshabilitado con el mensaje “Selecciona un mapa para usar el asistente”.

Panel reutilizable de capas y filtros ZenitLayerManager

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:

import React, { useState } from 'react';
import { ZenitClient, type FilterMultipleMetadata, type GeoJsonFeatureCollection } from 'zenit-sdk';
import { ZenitLayerManager, ZenitMap } from 'zenit-sdk/react';
import 'leaflet/dist/leaflet.css';

const client = new ZenitClient({ baseUrl: 'https://mi-zenit.com/api/v1', sdkToken: '<SDK_TOKEN>' });

export function App() {
  const [overlay, setOverlay] = useState<GeoJsonFeatureCollection | null>(null);
  const [metadata, setMetadata] = useState<FilterMultipleMetadata | undefined>();
  const [layerControls, setLayerControls] = useState<
    Array<{ layerId: number | string; visible: boolean; opacity: number }>
  >([]);

  return (
    <div style={{ display: 'flex', height: 600 }}>
      <ZenitLayerManager
        client={client}
        mapId={11}
        onFilteredGeojson={(geojson, meta) => {
          setOverlay(geojson);
          setMetadata(meta);
        }}
        onLayerStatesChange={setLayerControls}
      />
      <ZenitMap
        client={client}
        mapId={11}
        showLayerPanel={false}
        overlayGeojson={overlay}
        layerControls={layerControls}
      />
      <pre>{JSON.stringify(metadata, null, 2)}</pre>
    </div>
  );
}

API de mapas y capas (core)

import { ZenitClient, getCatalogSupport, ZenitCatalogNotSupportedError } from 'zenit-sdk';

const client = new ZenitClient({ baseUrl: 'https://mi-zenit.com/api/v1', accessToken: '<JWT>' });

// Metadatos de mapa (incluye capas visibles si includeLayers=true)
const map = await client.maps.getMap(11, true);

// Metadatos de capa (las respuestas usan ApiResponse con `data` y metadata opcional)
const layer = await client.layers.getLayer(123);
console.log('Layer:', layer.data);

// GeoJSON completo
const geojson = await client.layers.getLayerGeoJson(123);
console.log('GeoJSON features:', geojson.data.features?.length ?? 0);

// GeoJSON limitado por bounding box
const geojsonBBox = await client.layers.getLayerGeoJsonBBox({
  id: 123,
  bbox: {
    minLon: -58.6,
    minLat: -34.7,
    maxLon: -58.3,
    maxLat: -34.4,
  },
});
console.log('GeoJSON bbox features:', geojsonBBox.data.features?.length ?? 0);

// Intersección con una geometría
const intersected = await client.layers.getLayerGeoJsonIntersect({ id: 123, geometry, maxFeatures: 5000 });
console.log('GeoJSON intersected features:', intersected.data.features?.length ?? 0);

// Catálogo de propiedades de features de una capa
const catalog = await client.layers.getLayerFeaturesCatalog(17);
console.log('Catalogo de capa', catalog.data);

// Catálogo con validación previa
const support = getCatalogSupport({ layerType: 'polygon' });
if (support.supported) {
  const safeCatalog = await client.layers.getLayerFeaturesCatalog(17, {
    layerType: 'polygon',
    strict: true, // fail-fast sin request si la geometría no está soportada
  });
  console.log('Catálogo soportado', safeCatalog.data);
}

// Filtrado simultáneo en múltiples capas multipolygon
const filtered = await client.layers.filterMultipleLayersFeatures({
  layerIds: [32, 17],
  filters: { CODREGION: 10 },
});
console.log('filter-multiple data', filtered.data);

// Filtrado resiliente con capas mixtas (multipolygon + otras)
const mixedFiltered = await client.layers.filterMultipleWithFallback({
  layerIds: [32, 17, 99],
  filters: { CODREGION: 10 },
  layerMetas: [
    { layerId: 32, layerType: 'multipolygon' },
    { layerId: 17, layerType: 'polygon' },
    { layerId: 99, layerType: 'mixed' },
  ],
});
console.log('filter-multiple fallback perLayer', mixedFiltered.perLayer);

// Helper de alto nivel
const loaded = await client.layers.loadFilteredFeatures({
  layerIds: [32, 17],
  filters: { CODREGION: 10 },
});
console.log('GeoJSON filtrado', loaded.geojson.features.length, 'elementos');

El endpoint de catálogo solo admite capas polygon/multipolygon. Usa getCatalogSupport para decidir si corresponde llamarlo, o pasa { strict: true } a getLayerFeaturesCatalog para que el SDK arroje un ZenitCatalogNotSupportedError antes de hacer la request.

Los métodos expuestos en client.layers permiten construir el panel de filtros sin dependencias externas:

  • getLayerFeaturesCatalog(layerId) carga el catálogo de propiedades para cada capa.
  • filterMultipleLayersFeatures({ layerIds, filters }) ejecuta filter-multiple y retorna GeoJSON + metadata.
  • loadFilteredFeatures es un helper tipado que entrega geojson, metadata y totalFeatures listos para usar.

Notas de filtrado:

  • layerIds se envía como CSV ([32,17] -> layerIds=32,17).
  • Los filtros son pares dinámicos key/value. Valores en arreglos se serializan como a,b,c.
  • Valores null/undefined o cadenas vacías no se envían en la query.

Ejecutar ejemplos con .env opcional

Los ejemplos usan ts-node y leen variables de entorno. Puedes definirlas en tu shell o en un archivo .env (no se publica):

ZENIT_BASE_URL=http://localhost:3200/api/v1
ZENIT_EMAIL=<EMAIL>
ZENIT_PASSWORD=<PASSWORD>
ZENIT_SDK_TOKEN=<SDK_TOKEN>
ZENIT_ACCESS_TOKEN=<ACCESS_TOKEN>
ZENIT_MAP_ID=11

En PowerShell:

$env:ZENIT_BASE_URL="http://localhost:3200/api/v1"
$env:ZENIT_EMAIL="[email protected]"
$env:ZENIT_PASSWORD="secret"

Comandos disponibles:

npm run example:auth
npm run example:sdk
npm run example:map

El baseUrl por defecto de los ejemplos es http://localhost:3200/api/v1 si no se especifica.