geo-engine-node 1.0.0 → 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.
Files changed (3) hide show
  1. package/Readme.md +135 -0
  2. package/index.js +88 -62
  3. package/package.json +24 -8
package/Readme.md ADDED
@@ -0,0 +1,135 @@
1
+
2
+ ---
3
+
4
+ # 📦 Geo-Engine Node.js SDK
5
+
6
+ Cliente oficial para interactuar con la plataforma **Geo-Engine**.
7
+ Permite enviar ubicaciones en tiempo real y gestionar **geocercas** desde tus aplicaciones Node.js.
8
+
9
+ ---
10
+
11
+ ## 🚀 Instalación
12
+
13
+ ```bash
14
+ npm install geo-engine-node
15
+ ```
16
+
17
+ ---
18
+
19
+ ## ⚡ Inicio Rápido
20
+
21
+ Envía la ubicación de un dispositivo en **3 líneas de código**:
22
+
23
+ ```js
24
+ const GeoEngine = require('geo-engine-node');
25
+
26
+ // Inicializa con tu API Key (consíguela en tu Dashboard)
27
+ const geo = new GeoEngine('sk_test_12345abcdef');
28
+
29
+ // Envía una coordenada (ID, Latitud, Longitud)
30
+ geo.sendLocation('camion-01', 19.4326, -99.1332)
31
+ .then(res => console.log('✅ Ubicación enviada:', res))
32
+ .catch(err => console.error('❌ Error:', err.message));
33
+ ```
34
+
35
+ ---
36
+
37
+ ## 📚 Referencia de la API
38
+
39
+ ### `new GeoEngine(apiKey, [options])`
40
+
41
+ Crea una nueva instancia del cliente.
42
+
43
+ **Parámetros:**
44
+
45
+ * `apiKey` (`string`): Tu clave secreta de API.
46
+ * `options` (`object`, opcional):
47
+
48
+ * `timeout`: Tiempo de espera en ms *(default: 10000)*.
49
+ * `ingestUrl`: URL personalizada para ingestión (útil para pruebas locales).
50
+ * `managementUrl`: URL personalizada para gestión.
51
+
52
+ ---
53
+
54
+ ### `geo.sendLocation(deviceId, lat, lng)`
55
+
56
+ Envía un punto de rastreo al motor de ingestión.
57
+ El sistema procesará automáticamente si este punto **entra o sale** de alguna geocerca activa.
58
+
59
+ **Parámetros:**
60
+
61
+ * `deviceId` (`string`): Identificador único del activo (ej: placa del vehículo, UUID).
62
+ * `lat` (`number`): Latitud decimal.
63
+ * `lng` (`number`): Longitud decimal.
64
+
65
+ **Retorna:**
66
+ `Promise<Object>` con el estado de la cola (`queued`).
67
+
68
+ ---
69
+
70
+ ### `geo.createGeofence(name, coordinates, webhookUrl)`
71
+
72
+ Crea programáticamente una nueva zona de interés (geocerca).
73
+
74
+ **Parámetros:**
75
+
76
+ * `name` (`string`): Nombre descriptivo (ej: `"Almacén Central"`).
77
+ * `coordinates` (`Array<Array<number>>`):
78
+ Lista de puntos que forman el polígono
79
+ `[[lat, lng], [lat, lng], ...]`
80
+ *(El SDK se encarga de cerrar el polígono si es necesario).*
81
+ * `webhookUrl` (`string`):
82
+ URL donde Geo-Engine enviará una petición **POST** cuando un dispositivo entre en esta zona.
83
+
84
+ **Ejemplo:**
85
+
86
+ ```js
87
+ const zona = [
88
+ [19.42, -99.16],
89
+ [19.41, -99.16],
90
+ [19.41, -99.15],
91
+ [19.42, -99.15]
92
+ ];
93
+
94
+ await geo.createGeofence(
95
+ 'Zona Prohibida 1',
96
+ zona,
97
+ 'https://mi-api.com/alertas'
98
+ );
99
+ ```
100
+
101
+ ---
102
+
103
+ ## 🛡️ Manejo de Errores
104
+
105
+ El SDK lanza errores descriptivos que puedes capturar fácilmente:
106
+
107
+ ```js
108
+ try {
109
+ await geo.sendLocation('x', 0, 0);
110
+ } catch (error) {
111
+ console.error(error.message);
112
+ // Ej: "GeoEngine API Error (401): Invalid API Key"
113
+ }
114
+ ```
115
+
116
+ ---
117
+
118
+ ## 🧪 Desarrollo Local
119
+
120
+ Si estás ejecutando **Geo-Engine** en tu máquina local, puedes configurar el cliente para que apunte a `localhost`:
121
+
122
+ ```js
123
+ const geo = new GeoEngine('mi-clave-local', {
124
+ ingestUrl: 'http://localhost:8080',
125
+ managementUrl: 'http://localhost:8081'
126
+ });
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 📄 Licencia
132
+
133
+ MIT © Geo-Engine Team
134
+
135
+ ---
package/index.js CHANGED
@@ -1,92 +1,118 @@
1
- import { create } from 'axios';
2
-
3
1
  const DEFAULTS = {
4
2
  managementUrl: 'https://api.geoengine.dev',
5
- ingestUrl: 'http://ingest.geoengine.dev',
3
+ ingestUrl: 'https://ingest.geoengine.dev',
6
4
  timeout: 10000
7
5
  };
8
6
 
9
7
  class GeoEngine {
10
8
  /**
11
- * Inicializa el cliente de Geo-Engine
12
- * @param {string} apiKey - Tu API Key (sk_...)
13
- * @param {Object} options - Configuración opcional { managementUrl, ingestUrl }
9
+ * Inicializa el cliente de Geo-Engine.
10
+ * @param {string} apiKey - Tu API Key.
11
+ * @param {Object} [options] - Configuración opcional.
14
12
  */
15
13
  constructor(apiKey, options = {}) {
16
- if (!apiKey) throw new Error('GeoEngine: API Key es requerida');
17
-
14
+ if (!apiKey) {
15
+ throw new Error('GeoEngine: API Key es requerida.');
16
+ }
17
+
18
18
  this.apiKey = apiKey;
19
19
  this.config = { ...DEFAULTS, ...options };
20
-
21
- this.client = create({
22
- timeout: this.config.timeout,
23
- headers: {
24
- 'Content-Type': 'application/json',
25
- 'X-API-Key': this.apiKey,
26
- 'User-Agent': 'GeoEngineNode/1.0.0'
27
- }
28
- });
20
+ this.userAgent = 'GeoEngineNode/1.0.0';
29
21
  }
30
22
 
31
23
  /**
32
- * Envía una ubicación al motor de ingestión
33
- * @param {string} deviceId - ID del dispositivo (ej: 'camion-01')
34
- * @param {number} lat - Latitud
35
- * @param {number} lng - Longitud
24
+ * Helper privado para hacer peticiones con timeout
36
25
  */
37
- async sendLocation(deviceId, lat, lng) {
26
+ async _request(url, method, body) {
27
+ const controller = new AbortController();
28
+ const id = setTimeout(() => controller.abort(), this.config.timeout);
29
+
38
30
  try {
39
- const payload = {
40
- device_id: deviceId,
41
- latitude: lat,
42
- longitude: lng,
43
- timestamp: Math.floor(Date.now() / 1000)
44
- };
45
-
46
- const res = await this.client.post(`${this.config.ingestUrl}/ingest`, payload);
47
- return res.data;
31
+ const response = await fetch(url, {
32
+ method: method,
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ 'X-API-Key': this.apiKey,
36
+ 'User-Agent': this.userAgent
37
+ },
38
+ body: JSON.stringify(body),
39
+ signal: controller.signal
40
+ });
41
+
42
+ clearTimeout(id);
43
+
44
+ if (!response.ok) {
45
+ // Intentar leer el error en JSON, si falla, leer texto
46
+ let errorData;
47
+ try {
48
+ errorData = await response.json();
49
+ } catch (e) {
50
+ errorData = { error: await response.text() };
51
+ }
52
+
53
+ throw new Error(`GeoEngine API Error (${response.status}): ${errorData.error || JSON.stringify(errorData)}`);
54
+ }
55
+
56
+ // Si la respuesta es 204 No Content o vacía, devolver null
57
+ if (response.status === 204) return null;
58
+
59
+ return await response.json();
60
+
48
61
  } catch (error) {
49
- this._handleError(error);
62
+ clearTimeout(id);
63
+ if (error.name === 'AbortError') {
64
+ throw new Error(`GeoEngine: La petición excedió el tiempo límite de ${this.config.timeout}ms`);
65
+ }
66
+ throw error;
50
67
  }
51
68
  }
52
69
 
53
70
  /**
54
- * Crea una nueva geocerca
55
- * @param {string} name - Nombre de la zona
56
- * @param {Array<Array<number>>} coordinates - Array de puntos [[lat, lng], ...]
57
- * @param {string} webhookUrl - URL para recibir alertas
71
+ * Envía una ubicación al motor de ingestión.
58
72
  */
59
- async createGeofence(name, coordinates, webhookUrl) {
60
- try {
61
- // Convertir formato simple [[lat,lng]] a GeoJSON
62
- // GeoJSON usa [Lng, Lat], invertimos el orden si viene como [Lat, Lng]
63
- const polygon = coordinates.map(p => [p[1], p[0]]);
64
- // Cerrar polígono si es necesario
65
- if (polygon[0][0] !== polygon[polygon.length-1][0]) {
66
- polygon.push(polygon[0]);
67
- }
73
+ async sendLocation(deviceId, lat, lng) {
74
+ if (!deviceId || lat === undefined || lng === undefined) {
75
+ throw new Error("GeoEngine: deviceId, lat y lng son obligatorios.");
76
+ }
68
77
 
69
- const payload = {
70
- name,
71
- webhook_url: webhookUrl,
72
- geojson: {
73
- type: 'Polygon',
74
- coordinates: [polygon]
75
- }
76
- };
78
+ const payload = {
79
+ device_id: deviceId,
80
+ latitude: parseFloat(lat),
81
+ longitude: parseFloat(lng),
82
+ timestamp: Math.floor(Date.now() / 1000)
83
+ };
77
84
 
78
- const res = await this.client.post(`${this.config.managementUrl}/geofences`, payload);
79
- return res.data;
80
- } catch (error) {
81
- this._handleError(error);
82
- }
85
+ return this._request(`${this.config.ingestUrl}/ingest`, 'POST', payload);
83
86
  }
84
87
 
85
- _handleError(error) {
86
- if (error.response) {
87
- throw new Error(`GeoEngine API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
88
+ /**
89
+ * Crea una nueva geocerca.
90
+ */
91
+ async createGeofence(name, coordinates, webhookUrl) {
92
+ if (!name || !coordinates || coordinates.length < 3) {
93
+ throw new Error("GeoEngine: Se requiere un nombre y al menos 3 coordenadas.");
94
+ }
95
+
96
+ // Convertir formato simple [[lat,lng]] a GeoJSON [Lng, Lat]
97
+ const polygon = coordinates.map(p => [p[1], p[0]]);
98
+
99
+ // Cerrar polígono automáticamente
100
+ const first = polygon[0];
101
+ const last = polygon[polygon.length - 1];
102
+ if (first[0] !== last[0] || first[1] !== last[1]) {
103
+ polygon.push(first);
88
104
  }
89
- throw error;
105
+
106
+ const payload = {
107
+ name,
108
+ webhook_url: webhookUrl,
109
+ geojson: {
110
+ type: 'Polygon',
111
+ coordinates: [polygon]
112
+ }
113
+ };
114
+
115
+ return this._request(`${this.config.managementUrl}/geofences`, 'POST', payload);
90
116
  }
91
117
  }
92
118
 
package/package.json CHANGED
@@ -1,9 +1,25 @@
1
1
  {
2
- "name": "geo-engine-node",
3
- "version": "1.0.0",
4
- "description": "SDK Oficial para Geo-Engine",
5
- "main": "index.js",
6
- "dependencies": {
7
- "axios": "^1.6.0"
8
- }
9
- }
2
+ "name": "geo-engine-node",
3
+ "version": "1.1.0",
4
+ "type": "module",
5
+ "description": "SDK oficial para Geo-Engine. Rastreo de activos y gestión de geocercas.",
6
+ "main": "index.js",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1",
9
+ "example": "node examples/simulation.js",
10
+ "release:patch": "npm version patch && npm publish --access public",
11
+ "release:minor": "npm version minor && npm publish --access public"
12
+ },
13
+ "keywords": [
14
+ "geofencing",
15
+ "location",
16
+ "tracking",
17
+ "sdk"
18
+ ],
19
+ "author": "Geo-Engine Team",
20
+ "license": "MIT",
21
+ "dependencies": {},
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ }
25
+ }