@geolonia/geonicdb-sdk 0.2.0 → 0.3.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.
package/geonicdb.iife.js CHANGED
@@ -23,7 +23,15 @@ var GeonicDBModule = (() => {
23
23
  // src/sdk/index.ts
24
24
  var index_exports = {};
25
25
  __export(index_exports, {
26
+ AuthenticationError: () => AuthenticationError,
27
+ AuthorizationError: () => AuthorizationError,
28
+ ConflictError: () => ConflictError,
26
29
  GeonicDB: () => GeonicDB,
30
+ GeonicDBError: () => GeonicDBError,
31
+ NetworkError: () => NetworkError,
32
+ NotFoundError: () => NotFoundError,
33
+ RateLimitError: () => RateLimitError,
34
+ ValidationError: () => ValidationError,
27
35
  default: () => index_default
28
36
  });
29
37
 
@@ -173,12 +181,88 @@ var GeonicDBModule = (() => {
173
181
  var RECONNECT_MAX_DELAY_MS = 3e4;
174
182
  var SUB_PROTOCOL = "access_token";
175
183
 
184
+ // src/sdk/errors.ts
185
+ var GeonicDBError = class extends Error {
186
+ constructor(message, statusCode = 0) {
187
+ super(message);
188
+ /** HTTP status code (if applicable) */
189
+ __publicField(this, "statusCode");
190
+ this.name = "GeonicDBError";
191
+ this.statusCode = statusCode;
192
+ }
193
+ };
194
+ var AuthenticationError = class extends GeonicDBError {
195
+ constructor(message = "Authentication failed") {
196
+ super(message, 401);
197
+ this.name = "AuthenticationError";
198
+ }
199
+ };
200
+ var AuthorizationError = class extends GeonicDBError {
201
+ constructor(message = "Access denied") {
202
+ super(message, 403);
203
+ this.name = "AuthorizationError";
204
+ }
205
+ };
206
+ var NotFoundError = class extends GeonicDBError {
207
+ constructor(message = "Not found") {
208
+ super(message, 404);
209
+ this.name = "NotFoundError";
210
+ }
211
+ };
212
+ var ConflictError = class extends GeonicDBError {
213
+ constructor(message = "Conflict") {
214
+ super(message, 409);
215
+ this.name = "ConflictError";
216
+ }
217
+ };
218
+ var ValidationError = class extends GeonicDBError {
219
+ constructor(message = "Validation failed") {
220
+ super(message, 422);
221
+ this.name = "ValidationError";
222
+ }
223
+ };
224
+ var RateLimitError = class extends GeonicDBError {
225
+ constructor(message = "Rate limit exceeded", retryAfter = 1) {
226
+ super(message, 429);
227
+ /** Seconds to wait before retrying (from Retry-After header) */
228
+ __publicField(this, "retryAfter");
229
+ this.name = "RateLimitError";
230
+ this.retryAfter = retryAfter;
231
+ }
232
+ };
233
+ var NetworkError = class extends GeonicDBError {
234
+ constructor(message = "Network error") {
235
+ super(message, 0);
236
+ this.name = "NetworkError";
237
+ }
238
+ };
239
+ function createErrorFromResponse(status, body, fallbackMessage) {
240
+ const message = body.detail || body.description || fallbackMessage;
241
+ switch (status) {
242
+ case 401:
243
+ return new AuthenticationError(message);
244
+ case 403:
245
+ return new AuthorizationError(message);
246
+ case 404:
247
+ return new NotFoundError(message);
248
+ case 409:
249
+ return new ConflictError(message);
250
+ case 422:
251
+ return new ValidationError(message);
252
+ case 429:
253
+ return new RateLimitError(message);
254
+ default:
255
+ return new GeonicDBError(message, status);
256
+ }
257
+ }
258
+
176
259
  // src/sdk/auth.ts
177
260
  var _AuthManager = class _AuthManager {
178
- constructor(baseUrl, apiKey, tenant) {
261
+ constructor(baseUrl, apiKey, tenant, debug = false) {
179
262
  __publicField(this, "_baseUrl");
180
263
  __publicField(this, "_apiKey");
181
264
  __publicField(this, "_tenant");
265
+ __publicField(this, "_debug");
182
266
  __publicField(this, "_token", null);
183
267
  __publicField(this, "_tokenExpiry", 0);
184
268
  __publicField(this, "_tokenType", "Bearer");
@@ -192,6 +276,7 @@ var GeonicDBModule = (() => {
192
276
  this._baseUrl = baseUrl;
193
277
  this._apiKey = apiKey;
194
278
  this._tenant = tenant;
279
+ this._debug = debug;
195
280
  if (dpopSupported) {
196
281
  this._dpopReady = generateDPoPKeyPair().then((kp) => {
197
282
  this._dpopKeyPair = kp;
@@ -200,8 +285,12 @@ var GeonicDBModule = (() => {
200
285
  });
201
286
  }
202
287
  }
288
+ _log(...args) {
289
+ if (this._debug) console.log("[GeonicDB]", ...args);
290
+ }
203
291
  /** Login with email and password (Bearer JWT). */
204
292
  async login(email, password) {
293
+ this._log("login", email);
205
294
  const headers = {
206
295
  "Content-Type": "application/json"
207
296
  };
@@ -213,7 +302,7 @@ var GeonicDBModule = (() => {
213
302
  });
214
303
  if (!res.ok) {
215
304
  const e = await res.json().catch(() => ({}));
216
- throw new Error(
305
+ throw new AuthenticationError(
217
306
  e.detail || e.description || "Login failed: " + res.status
218
307
  );
219
308
  }
@@ -260,6 +349,7 @@ var GeonicDBModule = (() => {
260
349
  return this._tokenPromise;
261
350
  }
262
351
  async _refreshBearerToken() {
352
+ this._log("refreshing Bearer token");
263
353
  try {
264
354
  const res = await fetch(this._baseUrl + "/auth/refresh", {
265
355
  method: "POST",
@@ -270,7 +360,7 @@ var GeonicDBModule = (() => {
270
360
  this._refreshToken = null;
271
361
  this._token = null;
272
362
  this._tokenPromise = null;
273
- throw new Error("Token refresh failed: " + res.status);
363
+ throw new AuthenticationError("Token refresh failed: " + res.status);
274
364
  }
275
365
  const data = await res.json();
276
366
  this._token = data.accessToken;
@@ -299,7 +389,7 @@ var GeonicDBModule = (() => {
299
389
  body: JSON.stringify({ api_key: this._apiKey })
300
390
  });
301
391
  if (!nonceRes.ok)
302
- throw new Error("Nonce request failed: " + nonceRes.status);
392
+ throw new AuthenticationError("Nonce request failed: " + nonceRes.status);
303
393
  const nonceData = await nonceRes.json();
304
394
  if (nonceData.dpop_nonce) this._dpopNonce = nonceData.dpop_nonce;
305
395
  const proof = await solvePoW(nonceData.challenge, nonceData.difficulty);
@@ -312,7 +402,7 @@ var GeonicDBModule = (() => {
312
402
  });
313
403
  const res = await this._doTokenExchange(tokenUrl, tokenBody, this._dpopNonce);
314
404
  if (!res.ok)
315
- throw new Error("Token request failed: " + res.status);
405
+ throw new AuthenticationError("Token request failed: " + res.status);
316
406
  const newNonce = res.headers.get("DPoP-Nonce");
317
407
  if (newNonce) this._dpopNonce = newNonce;
318
408
  const data = await res.json();
@@ -352,8 +442,9 @@ var GeonicDBModule = (() => {
352
442
  return this._doTokenExchange(tokenUrl, tokenBody, serverNonce, retryCount + 1);
353
443
  }
354
444
  }
355
- throw new Error(
356
- "Token request failed: " + (errBody.error_description || errBody.error)
445
+ throw new GeonicDBError(
446
+ "Token request failed: " + (errBody.error_description || errBody.error),
447
+ 400
357
448
  );
358
449
  }
359
450
  return res;
@@ -362,8 +453,11 @@ var GeonicDBModule = (() => {
362
453
  * Make an authenticated HTTP request with automatic token refresh and DPoP.
363
454
  */
364
455
  async request(method, path, body) {
456
+ this._log(method, path);
365
457
  const token = await this.ensureToken();
366
- return this._doAuthenticatedRequest(method, path, body, token);
458
+ const res = await this._doAuthenticatedRequest(method, path, body, token);
459
+ this._log(method, path, "\u2192", res.status);
460
+ return res;
367
461
  }
368
462
  async _doAuthenticatedRequest(method, path, body, token, retryCount = 0) {
369
463
  const url = this._baseUrl + path;
@@ -448,6 +542,9 @@ var GeonicDBModule = (() => {
448
542
  this._emit = emit;
449
543
  this._wsEndpointOverride = wsEndpointOverride || null;
450
544
  }
545
+ _log(...args) {
546
+ if (this._auth._debug) console.log("[GeonicDB:WS]", ...args);
547
+ }
451
548
  /** Establish WebSocket connection (authentication is automatic). */
452
549
  async connect() {
453
550
  if (this._reconnectTimer) {
@@ -525,10 +622,12 @@ var GeonicDBModule = (() => {
525
622
  const endpoint = await this._discoverWsEndpoint();
526
623
  return new Promise((resolve, reject) => {
527
624
  const wsUrl = endpoint + (endpoint.indexOf("?") === -1 ? "?" : "&") + "tenant=" + encodeURIComponent(this._tenant);
625
+ this._log("connecting", wsUrl);
528
626
  const ws = new WebSocket(wsUrl, [SUB_PROTOCOL, token]);
529
627
  this._ws = ws;
530
628
  ws.onopen = () => {
531
629
  if (this._ws !== ws) return;
630
+ this._log("connected");
532
631
  this._reconnectAttempts = 0;
533
632
  const isDPoP = this._auth._tokenType === "DPoP" && !!this._auth._dpopKeyPair;
534
633
  const bindPromise = isDPoP ? createDPoPProof(this._auth._dpopKeyPair, "GET", wsUrl, null).then(
@@ -573,6 +672,14 @@ var GeonicDBModule = (() => {
573
672
  this._emit("error", new Error(msg.message));
574
673
  return;
575
674
  }
675
+ this._log("event", msg.type, msg.entityId || "");
676
+ if (msg.entityId && msg.data && typeof msg.data === "object") {
677
+ msg.entity = {
678
+ id: msg.entityId,
679
+ type: msg.entityType,
680
+ ...msg.data
681
+ };
682
+ }
576
683
  this._emit(msg.type, msg);
577
684
  this._emit("message", msg);
578
685
  };
@@ -675,7 +782,7 @@ var GeonicDBModule = (() => {
675
782
  if (!tenant) tenant = script?.getAttribute?.("data-tenant") || "";
676
783
  if (!baseUrl) baseUrl = script?.getAttribute?.("data-base-url") || "";
677
784
  }
678
- this._auth = new AuthManager(baseUrl, apiKey, tenant);
785
+ this._auth = new AuthManager(baseUrl, apiKey, tenant, opts.debug);
679
786
  this._auth.onTokenRefresh = (creds) => {
680
787
  this.onTokenRefresh?.(creds);
681
788
  this.emit("tokenRefresh", creds);
@@ -712,9 +819,7 @@ var GeonicDBModule = (() => {
712
819
  const res = await this._auth.request("POST", "/ngsi-ld/v1/entities", entity);
713
820
  if (!res.ok) {
714
821
  const e = await res.json().catch(() => ({}));
715
- throw new Error(
716
- e.detail || e.description || "Create failed"
717
- );
822
+ throw createErrorFromResponse(res.status, e, "Create failed");
718
823
  }
719
824
  return { created: true };
720
825
  }
@@ -726,9 +831,7 @@ var GeonicDBModule = (() => {
726
831
  );
727
832
  if (!res.ok) {
728
833
  const e = await res.json().catch(() => ({}));
729
- throw new Error(
730
- e.detail || e.description || "Not found"
731
- );
834
+ throw createErrorFromResponse(res.status, e, "Not found");
732
835
  }
733
836
  return await res.json();
734
837
  }
@@ -749,9 +852,7 @@ var GeonicDBModule = (() => {
749
852
  );
750
853
  if (!res.ok) {
751
854
  const e = await res.json().catch(() => ({}));
752
- throw new Error(
753
- e.detail || e.description || "Query failed"
754
- );
855
+ throw createErrorFromResponse(res.status, e, "Query failed");
755
856
  }
756
857
  return await res.json();
757
858
  }
@@ -767,9 +868,7 @@ var GeonicDBModule = (() => {
767
868
  );
768
869
  if (!res.ok) {
769
870
  const e = await res.json().catch(() => ({}));
770
- throw new Error(
771
- e.detail || e.description || "Count failed"
772
- );
871
+ throw createErrorFromResponse(res.status, e, "Count failed");
773
872
  }
774
873
  const countHeader = res.headers.get("NGSILD-Results-Count");
775
874
  return countHeader ? parseInt(countHeader, 10) : 0;
@@ -783,9 +882,7 @@ var GeonicDBModule = (() => {
783
882
  );
784
883
  if (!res.ok) {
785
884
  const e = await res.json().catch(() => ({}));
786
- throw new Error(
787
- e.detail || e.description || "Update failed"
788
- );
885
+ throw createErrorFromResponse(res.status, e, "Update failed");
789
886
  }
790
887
  return { updated: true };
791
888
  }
@@ -797,9 +894,7 @@ var GeonicDBModule = (() => {
797
894
  );
798
895
  if (!res.ok) {
799
896
  const e = await res.json().catch(() => ({}));
800
- throw new Error(
801
- e.detail || e.description || "Delete failed"
802
- );
897
+ throw createErrorFromResponse(res.status, e, "Delete failed");
803
898
  }
804
899
  return { deleted: true };
805
900
  }
@@ -896,9 +991,7 @@ var GeonicDBModule = (() => {
896
991
  const res = await this._auth.request("GET", path);
897
992
  if (!res.ok) {
898
993
  const e = await res.json().catch(() => ({}));
899
- throw new Error(
900
- e.detail || e.description || fallbackError
901
- );
994
+ throw createErrorFromResponse(res.status, e, fallbackError);
902
995
  }
903
996
  return await res.json();
904
997
  }
@@ -907,9 +1000,7 @@ var GeonicDBModule = (() => {
907
1000
  const res = await this._auth.request("POST", path, body);
908
1001
  if (!res.ok) {
909
1002
  const e = await res.json().catch(() => ({}));
910
- throw new Error(
911
- e.detail || e.description || fallbackError
912
- );
1003
+ throw createErrorFromResponse(res.status, e, fallbackError);
913
1004
  }
914
1005
  if (res.status === 204) return {};
915
1006
  return await res.json();
@@ -923,9 +1014,7 @@ var GeonicDBModule = (() => {
923
1014
  const res = await this._auth.request(method, path, body);
924
1015
  if (!res.ok) {
925
1016
  const e = await res.json().catch(() => ({}));
926
- throw new Error(
927
- e.detail || e.description || "Request failed: " + res.status
928
- );
1017
+ throw createErrorFromResponse(res.status, e, "Request failed: " + res.status);
929
1018
  }
930
1019
  const ct = res.headers.get("Content-Type") || "";
931
1020
  if (res.status === 204 || !ct) return null;