@geolonia/geonicdb-sdk 0.17.0 → 0.18.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/README.md +84 -4
- package/geonicdb.cjs +44 -17
- package/geonicdb.cjs.map +2 -2
- package/geonicdb.d.ts +31 -4
- package/geonicdb.iife.js +44 -17
- package/geonicdb.iife.js.map +2 -2
- package/geonicdb.mjs +44 -17
- package/geonicdb.mjs.map +2 -2
- package/package.json +1 -1
- package/react/index.d.ts +2 -2
package/geonicdb.d.ts
CHANGED
|
@@ -204,8 +204,8 @@ export interface EntityEvent {
|
|
|
204
204
|
servicePath: string;
|
|
205
205
|
/** Entity ID */
|
|
206
206
|
entityId: string;
|
|
207
|
-
/** Entity type, compacted with the subscription `@context` (#2026) */
|
|
208
|
-
entityType: string;
|
|
207
|
+
/** Entity type, compacted with the subscription `@context` (#2026). Multi-type is `string[]` (#2477 / Table 5.2.4). */
|
|
208
|
+
entityType: string | string[];
|
|
209
209
|
/**
|
|
210
210
|
* Entity attributes in NGSI-LD normalized representation (clause 4.5.2, #2026 / #2044).
|
|
211
211
|
* Attribute names are compacted with the subscription `@context`; sub-attributes are inline;
|
|
@@ -410,6 +410,26 @@ declare class EventEmitter {
|
|
|
410
410
|
* Copyright (c) 2026 Geolonia Inc.
|
|
411
411
|
* Released under the MIT License. See src/sdk/LICENSE for details.
|
|
412
412
|
*/
|
|
413
|
+
/**
|
|
414
|
+
* Which request produced an error.
|
|
415
|
+
*
|
|
416
|
+
* `login()` performs two requests — `POST /auth/login` and then
|
|
417
|
+
* `POST /auth/dpop-bind` — and both surface the server's `detail` /
|
|
418
|
+
* `description` verbatim as an `AuthenticationError`. Without this, a rejected
|
|
419
|
+
* credential and a rejected DPoP bind are indistinguishable from the error
|
|
420
|
+
* alone, so callers (and anyone reading a bug report) cannot tell which step
|
|
421
|
+
* actually failed.
|
|
422
|
+
*/
|
|
423
|
+
export interface RequestContext {
|
|
424
|
+
method: string;
|
|
425
|
+
/**
|
|
426
|
+
* The endpoint path, without the base URL — e.g. `/auth/dpop-bind`. When the
|
|
427
|
+
* base URL itself carries a path prefix (`https://host/broker`), the path on
|
|
428
|
+
* the wire includes that prefix; this field names the endpoint, not the full
|
|
429
|
+
* request path.
|
|
430
|
+
*/
|
|
431
|
+
path: string;
|
|
432
|
+
}
|
|
413
433
|
/**
|
|
414
434
|
* Base error class for GeonicDB SDK.
|
|
415
435
|
* All SDK errors extend this class, allowing `instanceof GeonicDBError` checks.
|
|
@@ -417,11 +437,18 @@ declare class EventEmitter {
|
|
|
417
437
|
export declare class GeonicDBError extends Error {
|
|
418
438
|
/** HTTP status code (if applicable) */
|
|
419
439
|
readonly statusCode: number;
|
|
440
|
+
/** The request that failed, when the SDK knows it. */
|
|
441
|
+
readonly request?: RequestContext;
|
|
420
442
|
constructor(message: string, statusCode?: number);
|
|
421
443
|
}
|
|
422
|
-
/**
|
|
444
|
+
/**
|
|
445
|
+
* Authentication failed (invalid credentials, expired token, or an HTTP
|
|
446
|
+
* failure on an auth endpoint). Defaults to 401; when thrown from an HTTP
|
|
447
|
+
* response, pass the real status so callers see what the server returned
|
|
448
|
+
* (#2649). Subclasses override with 409 / 403 for control-flow errors.
|
|
449
|
+
*/
|
|
423
450
|
export declare class AuthenticationError extends GeonicDBError {
|
|
424
|
-
constructor(message?: string);
|
|
451
|
+
constructor(message?: string, statusCode?: number);
|
|
425
452
|
}
|
|
426
453
|
/** 403 — Authorization denied (insufficient permissions). */
|
|
427
454
|
export declare class AuthorizationError extends GeonicDBError {
|
package/geonicdb.iife.js
CHANGED
|
@@ -296,13 +296,20 @@ var GeonicDBModule = (() => {
|
|
|
296
296
|
super(message);
|
|
297
297
|
/** HTTP status code (if applicable) */
|
|
298
298
|
__publicField(this, "statusCode");
|
|
299
|
+
/** The request that failed, when the SDK knows it. */
|
|
300
|
+
__publicField(this, "request");
|
|
299
301
|
this.name = "GeonicDBError";
|
|
300
302
|
this.statusCode = statusCode;
|
|
301
303
|
}
|
|
302
304
|
};
|
|
305
|
+
function withRequest(error, request) {
|
|
306
|
+
error.request = request;
|
|
307
|
+
error.message = `[${request.method} ${request.path}] ${error.message}`;
|
|
308
|
+
return error;
|
|
309
|
+
}
|
|
303
310
|
var AuthenticationError = class extends GeonicDBError {
|
|
304
|
-
constructor(message = "Authentication failed") {
|
|
305
|
-
super(message,
|
|
311
|
+
constructor(message = "Authentication failed", statusCode = 401) {
|
|
312
|
+
super(message, statusCode);
|
|
306
313
|
this.name = "AuthenticationError";
|
|
307
314
|
}
|
|
308
315
|
};
|
|
@@ -326,16 +333,14 @@ var GeonicDBModule = (() => {
|
|
|
326
333
|
};
|
|
327
334
|
var PasswordResetRequiredError = class extends AuthenticationError {
|
|
328
335
|
constructor(message = "Password reset required: set a new password to complete login") {
|
|
329
|
-
super(message);
|
|
336
|
+
super(message, 409);
|
|
330
337
|
this.name = "PasswordResetRequiredError";
|
|
331
|
-
this.statusCode = 409;
|
|
332
338
|
}
|
|
333
339
|
};
|
|
334
340
|
var TemporaryPasswordExpiredError = class extends AuthenticationError {
|
|
335
341
|
constructor(message = "Temporary password expired: ask an administrator to re-issue it") {
|
|
336
|
-
super(message);
|
|
342
|
+
super(message, 403);
|
|
337
343
|
this.name = "TemporaryPasswordExpiredError";
|
|
338
|
-
this.statusCode = 403;
|
|
339
344
|
}
|
|
340
345
|
};
|
|
341
346
|
var ValidationError = class extends GeonicDBError {
|
|
@@ -740,12 +745,21 @@ var GeonicDBModule = (() => {
|
|
|
740
745
|
const message = e.detail || e.description || "Login failed: " + res.status;
|
|
741
746
|
const code = e.error || e.type || e.title || "";
|
|
742
747
|
if (res.status === 409 && code.includes("PasswordResetRequired")) {
|
|
743
|
-
throw new PasswordResetRequiredError(message)
|
|
748
|
+
throw withRequest(new PasswordResetRequiredError(message), {
|
|
749
|
+
method: "POST",
|
|
750
|
+
path: "/auth/login"
|
|
751
|
+
});
|
|
744
752
|
}
|
|
745
753
|
if (res.status === 403 && code.includes("TemporaryPasswordExpired")) {
|
|
746
|
-
throw new TemporaryPasswordExpiredError(message)
|
|
754
|
+
throw withRequest(new TemporaryPasswordExpiredError(message), {
|
|
755
|
+
method: "POST",
|
|
756
|
+
path: "/auth/login"
|
|
757
|
+
});
|
|
747
758
|
}
|
|
748
|
-
throw new AuthenticationError(message)
|
|
759
|
+
throw withRequest(new AuthenticationError(message, res.status), {
|
|
760
|
+
method: "POST",
|
|
761
|
+
path: "/auth/login"
|
|
762
|
+
});
|
|
749
763
|
}
|
|
750
764
|
const data = await res.json();
|
|
751
765
|
this._anonymous = false;
|
|
@@ -848,8 +862,12 @@ var GeonicDBModule = (() => {
|
|
|
848
862
|
}
|
|
849
863
|
if (!res.ok) {
|
|
850
864
|
const e = await res.json().catch(() => ({}));
|
|
851
|
-
throw
|
|
852
|
-
|
|
865
|
+
throw withRequest(
|
|
866
|
+
new AuthenticationError(
|
|
867
|
+
e.detail || e.description || "DPoP bind failed: " + res.status,
|
|
868
|
+
res.status
|
|
869
|
+
),
|
|
870
|
+
{ method: "POST", path: "/auth/dpop-bind" }
|
|
853
871
|
);
|
|
854
872
|
}
|
|
855
873
|
return await res.json();
|
|
@@ -1021,7 +1039,7 @@ var GeonicDBModule = (() => {
|
|
|
1021
1039
|
this._refreshToken = null;
|
|
1022
1040
|
this._token = null;
|
|
1023
1041
|
this._tokenPromise = null;
|
|
1024
|
-
throw new AuthenticationError("Token refresh failed: " + res.status);
|
|
1042
|
+
throw new AuthenticationError("Token refresh failed: " + res.status, res.status);
|
|
1025
1043
|
}
|
|
1026
1044
|
const data = await res.json();
|
|
1027
1045
|
this._token = data.accessToken;
|
|
@@ -1089,7 +1107,7 @@ var GeonicDBModule = (() => {
|
|
|
1089
1107
|
body: JSON.stringify({ api_key: this._apiKey })
|
|
1090
1108
|
});
|
|
1091
1109
|
if (!nonceRes.ok)
|
|
1092
|
-
throw new AuthenticationError("Nonce request failed: " + nonceRes.status);
|
|
1110
|
+
throw new AuthenticationError("Nonce request failed: " + nonceRes.status, nonceRes.status);
|
|
1093
1111
|
const nonceData = await nonceRes.json();
|
|
1094
1112
|
if (nonceData.dpop_nonce) this._dpopNonce = nonceData.dpop_nonce;
|
|
1095
1113
|
const proof = await solvePoW(nonceData.challenge, nonceData.difficulty);
|
|
@@ -1102,7 +1120,7 @@ var GeonicDBModule = (() => {
|
|
|
1102
1120
|
});
|
|
1103
1121
|
const res = await this._doTokenExchange(tokenUrl, tokenBody, this._dpopNonce);
|
|
1104
1122
|
if (!res.ok)
|
|
1105
|
-
throw new AuthenticationError("Token request failed: " + res.status);
|
|
1123
|
+
throw new AuthenticationError("Token request failed: " + res.status, res.status);
|
|
1106
1124
|
const newNonce = res.headers.get("DPoP-Nonce");
|
|
1107
1125
|
if (newNonce) this._dpopNonce = newNonce;
|
|
1108
1126
|
const data = await res.json();
|
|
@@ -1144,8 +1162,8 @@ var GeonicDBModule = (() => {
|
|
|
1144
1162
|
return this._doTokenExchange(tokenUrl, tokenBody, serverNonce, retryCount + 1);
|
|
1145
1163
|
}
|
|
1146
1164
|
}
|
|
1147
|
-
throw new
|
|
1148
|
-
"Token request failed: " + (errBody.error_description || errBody.error),
|
|
1165
|
+
throw new AuthenticationError(
|
|
1166
|
+
"Token request failed: " + (errBody.error_description || errBody.error || "400"),
|
|
1149
1167
|
400
|
|
1150
1168
|
);
|
|
1151
1169
|
}
|
|
@@ -1325,6 +1343,7 @@ var GeonicDBModule = (() => {
|
|
|
1325
1343
|
__publicField(this, "_emit");
|
|
1326
1344
|
__publicField(this, "_ws", null);
|
|
1327
1345
|
__publicField(this, "_wsEndpoint", null);
|
|
1346
|
+
__publicField(this, "_deploymentHostname", null);
|
|
1328
1347
|
__publicField(this, "_wsIntentionalClose", false);
|
|
1329
1348
|
__publicField(this, "_reconnectAttempts", 0);
|
|
1330
1349
|
__publicField(this, "_tokenRefreshTimer", null);
|
|
@@ -1414,6 +1433,9 @@ var GeonicDBModule = (() => {
|
|
|
1414
1433
|
const info = await res.json();
|
|
1415
1434
|
if (info.enabled && info.endpoint) {
|
|
1416
1435
|
this._wsEndpoint = info.endpoint;
|
|
1436
|
+
if (info.deployment) {
|
|
1437
|
+
this._deploymentHostname = info.deployment;
|
|
1438
|
+
}
|
|
1417
1439
|
return this._wsEndpoint;
|
|
1418
1440
|
}
|
|
1419
1441
|
}
|
|
@@ -1425,7 +1447,11 @@ var GeonicDBModule = (() => {
|
|
|
1425
1447
|
async _openWebSocket(token) {
|
|
1426
1448
|
const endpoint = await this._discoverWsEndpoint();
|
|
1427
1449
|
return new Promise((resolve, reject) => {
|
|
1428
|
-
const
|
|
1450
|
+
const params = new URLSearchParams({ tenant: this._tenant });
|
|
1451
|
+
if (this._deploymentHostname) {
|
|
1452
|
+
params.set("deployment", this._deploymentHostname);
|
|
1453
|
+
}
|
|
1454
|
+
const wsUrl = endpoint + (endpoint.indexOf("?") === -1 ? "?" : "&") + params.toString();
|
|
1429
1455
|
this._log("connecting", wsUrl);
|
|
1430
1456
|
const ws = new WebSocket(wsUrl, [SUB_PROTOCOL, token]);
|
|
1431
1457
|
this._ws = ws;
|
|
@@ -1724,6 +1750,7 @@ var GeonicDBModule = (() => {
|
|
|
1724
1750
|
if (!tenant) tenant = script?.getAttribute?.("data-tenant") || "";
|
|
1725
1751
|
if (!baseUrl) baseUrl = script?.getAttribute?.("data-base-url") || "";
|
|
1726
1752
|
}
|
|
1753
|
+
baseUrl = baseUrl.replace(/\/+$/, "");
|
|
1727
1754
|
this._auth = new AuthManager(baseUrl, apiKey, tenant, opts.debug, opts.anonymous);
|
|
1728
1755
|
this._auth.onTokenRefresh = (creds) => {
|
|
1729
1756
|
this.onTokenRefresh?.(creds);
|