@geolonia/geonicdb-sdk 0.16.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 +123 -12
- package/geonicdb.cjs +72 -25
- package/geonicdb.cjs.map +2 -2
- package/geonicdb.d.ts +145 -13
- package/geonicdb.iife.js +72 -25
- package/geonicdb.iife.js.map +2 -2
- package/geonicdb.mjs +72 -25
- package/geonicdb.mjs.map +2 -2
- package/package.json +1 -1
- package/react/index.d.ts +116 -11
package/geonicdb.d.ts
CHANGED
|
@@ -103,6 +103,12 @@ export interface GetEntitiesParams {
|
|
|
103
103
|
geometry?: string;
|
|
104
104
|
/** Coordinates string (semicolon-delimited pairs or flat comma-separated) */
|
|
105
105
|
coordinates?: string;
|
|
106
|
+
/**
|
|
107
|
+
* 多重属性のインスタンス絞り込み (`datasetId` クエリ、ETSI GS CIM 009 clause 4.5.5.1)。
|
|
108
|
+
* カンマ区切りで複数指定可。既定インスタンスは `@none`。一致が 1 件なら単一要素、
|
|
109
|
+
* 0 件ならその属性は応答から落ちる。
|
|
110
|
+
*/
|
|
111
|
+
datasetId?: string;
|
|
106
112
|
/** Comma-separated attribute names to retrieve */
|
|
107
113
|
attrs?: string;
|
|
108
114
|
/** Comma-separated attribute names to keep (alternative to attrs) */
|
|
@@ -124,13 +130,24 @@ export interface GetEntitiesParams {
|
|
|
124
130
|
/**
|
|
125
131
|
* NGSI-LD representation options (`options` query param)。カンマ区切りで複数指定可。
|
|
126
132
|
* - `sysAttrs` — システム属性 (`createdAt` / `modifiedAt`) を含める
|
|
127
|
-
* - `keyValues` — 簡易表現 (属性を値だけに圧縮)
|
|
133
|
+
* - `keyValues` — 簡易表現 (属性を値だけに圧縮)。多重属性 (`datasetId` 付きインスタンス) は
|
|
134
|
+
* 一致が複数のときだけ `dataset` マップになる (既定インスタンスのキーは `@none`)。
|
|
135
|
+
* 1 件なら素の値 (ETSI GS CIM 009 clause 4.5.4 / 4.5.5.1)
|
|
128
136
|
* - `concise` — concise 表現
|
|
129
137
|
* - `normalized` — 正規化表現 (デフォルト)
|
|
130
138
|
*
|
|
131
139
|
* 例: `'sysAttrs'`, `'keyValues,sysAttrs'`。
|
|
132
140
|
*/
|
|
133
141
|
options?: string;
|
|
142
|
+
/**
|
|
143
|
+
* ローカルスコープに限定する (`local` クエリパラメータ、ETSI GS CIM 009 clause 5.5.13 / Table 6.3.18-1)。
|
|
144
|
+
*
|
|
145
|
+
* **#2278**: `GET /ngsi-ld/v1/entities` は clause 5.7.2.4 により
|
|
146
|
+
* `type` / `attrs` (非システム属性を含む) / `q` (非システム属性を含む) / geoquery / local scope の
|
|
147
|
+
* いずれかを必須とする。**絞り込み無しで全件を取りたい場合は `local: true` を指定する**
|
|
148
|
+
* (指定しないと `400 BadRequestData` = too wide query)。
|
|
149
|
+
*/
|
|
150
|
+
local?: boolean;
|
|
134
151
|
}
|
|
135
152
|
/** Parameters for counting entities. */
|
|
136
153
|
export interface CountEntitiesParams {
|
|
@@ -140,15 +157,42 @@ export interface CountEntitiesParams {
|
|
|
140
157
|
q?: string;
|
|
141
158
|
/** NGSI-LD scope query expression (#1132) */
|
|
142
159
|
scopeQ?: string;
|
|
160
|
+
/**
|
|
161
|
+
* ローカルスコープに限定する (`local` クエリパラメータ、ETSI GS CIM 009 clause 5.5.13 / Table 6.3.18-1)。
|
|
162
|
+
*
|
|
163
|
+
* **#2278**: `GET /ngsi-ld/v1/entities` は clause 5.7.2.4 により
|
|
164
|
+
* `type` / `attrs` (非システム属性を含む) / `q` (非システム属性を含む) / geoquery / local scope の
|
|
165
|
+
* いずれかを必須とする。**絞り込み無しで全件を取りたい場合は `local: true` を指定する**
|
|
166
|
+
* (指定しないと `400 BadRequestData` = too wide query)。
|
|
167
|
+
*/
|
|
168
|
+
local?: boolean;
|
|
143
169
|
}
|
|
144
170
|
/** Options for subscribing to entity events via WebSocket. */
|
|
145
171
|
export interface SubscribeOptions {
|
|
146
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* Entity types to subscribe to.
|
|
174
|
+
*
|
|
175
|
+
* **#2055.** Matched against the canonical stored form: the supplied `@context` is
|
|
176
|
+
* resolved once at `subscribe` time and these names are expanded before matching,
|
|
177
|
+
* so a term defined by a custom `@context` changes which entities match.
|
|
178
|
+
*/
|
|
147
179
|
entityTypes?: string[];
|
|
148
|
-
/** Entity ID regex pattern */
|
|
180
|
+
/** Entity ID regex pattern. Matched against the stored form verbatim (no `@context` expansion). */
|
|
149
181
|
idPattern?: string;
|
|
150
182
|
/** NGSI-LD scope query expression (#1132). Server-side filter; only events whose entity scope matches are delivered. */
|
|
151
183
|
scopeQ?: string;
|
|
184
|
+
/**
|
|
185
|
+
* JSON-LD `@context` used to render delivered events (#2026).
|
|
186
|
+
*
|
|
187
|
+
* The entity type and attribute names are compacted against it (ETSI GS CIM 009 clause 5.5.7)
|
|
188
|
+
* and the value is echoed back in each event's `@context`. Omit for the core `@context`.
|
|
189
|
+
* URL string / array of URL strings only — inline context objects are rejected.
|
|
190
|
+
*
|
|
191
|
+
* **#2055: it also governs `entityTypes` matching** — the context is resolved once at
|
|
192
|
+
* `subscribe` time to expand `entityTypes` into the canonical stored form. `idPattern`
|
|
193
|
+
* alone is matched against the stored form verbatim.
|
|
194
|
+
*/
|
|
195
|
+
"@context"?: string | string[];
|
|
152
196
|
}
|
|
153
197
|
/** Entity event received via WebSocket. */
|
|
154
198
|
export interface EntityEvent {
|
|
@@ -160,14 +204,20 @@ export interface EntityEvent {
|
|
|
160
204
|
servicePath: string;
|
|
161
205
|
/** Entity ID */
|
|
162
206
|
entityId: string;
|
|
163
|
-
/** Entity type */
|
|
164
|
-
entityType: string;
|
|
165
|
-
/**
|
|
207
|
+
/** Entity type, compacted with the subscription `@context` (#2026). Multi-type is `string[]` (#2477 / Table 5.2.4). */
|
|
208
|
+
entityType: string | string[];
|
|
209
|
+
/**
|
|
210
|
+
* Entity attributes in NGSI-LD normalized representation (clause 4.5.2, #2026 / #2044).
|
|
211
|
+
* Attribute names are compacted with the subscription `@context`; sub-attributes are inline;
|
|
212
|
+
* multi-attributes appear as an instance array carrying `datasetId` (clause 4.5.5).
|
|
213
|
+
*/
|
|
166
214
|
data: Record<string, unknown>;
|
|
167
215
|
/** Complete NGSI-LD entity (id + type + attributes from data). Undefined when data is unavailable (e.g. some delete events). */
|
|
168
216
|
entity?: Record<string, unknown>;
|
|
169
217
|
/** Changed attribute names (update only) */
|
|
170
218
|
changedAttributes?: string[];
|
|
219
|
+
/** Vocabulary the names in this event were rendered with (#2026) */
|
|
220
|
+
"@context"?: string | string[];
|
|
171
221
|
/** ISO 8601 timestamp */
|
|
172
222
|
timestamp: string;
|
|
173
223
|
}
|
|
@@ -235,6 +285,14 @@ export interface GeonicDBEventMap {
|
|
|
235
285
|
};
|
|
236
286
|
disconnected: undefined;
|
|
237
287
|
reconnecting: ReconnectingEvent;
|
|
288
|
+
/**
|
|
289
|
+
* #2055: 購読フィルタがサーバ側で有効になった (`{"type":"subscribed"}` の受信)。
|
|
290
|
+
*
|
|
291
|
+
* `subscribe()` の適用は非同期 — サーバは渡された `@context` を解決して `entityTypes` を
|
|
292
|
+
* 照合用の canonical 形へ展開する (ETSI GS CIM 009 clause 5.5.7)。このイベントより前に
|
|
293
|
+
* 発生した更新は配信が保証されない。
|
|
294
|
+
*/
|
|
295
|
+
subscribed: undefined;
|
|
238
296
|
error: Error;
|
|
239
297
|
message: Record<string, unknown>;
|
|
240
298
|
tokenRefresh: RefreshedCredentials;
|
|
@@ -273,13 +331,46 @@ export interface GetTemporalEntitiesParams {
|
|
|
273
331
|
offset?: number;
|
|
274
332
|
/**
|
|
275
333
|
* NGSI-LD representation options (`options` query param)。カンマ区切りで複数指定可。
|
|
276
|
-
* - `sysAttrs` — システム属性 (`createdAt` / `modifiedAt`) を含める
|
|
277
334
|
* - `temporalValues` — 時系列を簡易配列表現で返す
|
|
278
|
-
* - `
|
|
335
|
+
* - `simplified` — `temporalValues` の同義語 (GeonicDB 拡張)
|
|
336
|
+
* - `aggregatedValues` — 集約表現で返す (ETSI clause 4.5.19 の「メソッド名をキー +
|
|
337
|
+
* [値, 開始, 終了] の 3 要素配列」形。#1815 で独自形から是正)
|
|
338
|
+
* - `sysAttrs` — システム時相属性 (`createdAt` / `modifiedAt`、TTL 設定時は `expiresAt`) を
|
|
339
|
+
* normalized 表現の各インスタンスへ含める (ETSI 6.3.11、#1817)。簡易表現・集約表現には
|
|
340
|
+
* 載せる場所が無いため受理して無視される (400 にはならない)
|
|
279
341
|
*
|
|
280
|
-
*
|
|
342
|
+
* 注:
|
|
343
|
+
* - この SDK では `aggregatedValues` は `options` で指定し、`aggrMethods` との併用が必須
|
|
344
|
+
* - サーバ側では `format=aggregatedValues` に対しても同じ必須条件が適用される
|
|
345
|
+
* - `options` は ETSI 6.3.12 で deprecated。`format` を併用した場合は **`format` が優先される**
|
|
346
|
+
*
|
|
347
|
+
* 例: `'temporalValues'`, `'aggregatedValues'`。
|
|
281
348
|
*/
|
|
282
349
|
options?: string;
|
|
350
|
+
/**
|
|
351
|
+
* #1814: 表現形式 (`format` query param、ETSI GS CIM 009 clause 6.3.12)。
|
|
352
|
+
* `'temporalValues'` / `'aggregatedValues'` のいずれか (`'simplified'` は `'temporalValues'` の
|
|
353
|
+
* 別名として受理される GeonicDB 拡張)。**未知値はサーバが 400 InvalidRequest で拒否する。**
|
|
354
|
+
* `options` と併用した場合は **`format` が優先される**。
|
|
355
|
+
*
|
|
356
|
+
* SDK は npm 向けに単独ビルドされ本体の `@config/defaults` を import しないため、
|
|
357
|
+
* 許可値はここでリテラル union として持つ。**本体の
|
|
358
|
+
* `NGSI_LD.TEMPORAL_REPRESENTATION_FORMATS` を変えたらこの union も同期すること**
|
|
359
|
+
* (SDK Consistency)。
|
|
360
|
+
*/
|
|
361
|
+
format?: "temporalValues" | "aggregatedValues" | "simplified";
|
|
362
|
+
/**
|
|
363
|
+
* #1815: 集約メソッド (カンマ区切り)。`totalCount` / `distinctCount` / `sum` / `avg` / `min` /
|
|
364
|
+
* `max` / `stddev` / `sumsq`。`aggregatedValues` を `format` か `options` で指定する場合は必須。
|
|
365
|
+
* 応答は clause 4.5.19 形 (メソッド名がキー、各期間は `[値, 開始, 終了]` の 3 要素配列)。
|
|
366
|
+
*/
|
|
367
|
+
aggrMethods?: string;
|
|
368
|
+
/**
|
|
369
|
+
* 集約期間 (ISO 8601 duration。例 `'PT1H'`)。
|
|
370
|
+
* ETSI GS CIM 009 Table 5.2.21-1 の cardinality は 0..1 で**任意**。省略時は `PT0S` として
|
|
371
|
+
* 扱われ、クエリの時間範囲全体が 1 期間として集約される (clause 4.5.19.1)。
|
|
372
|
+
*/
|
|
373
|
+
aggrPeriodDuration?: string;
|
|
283
374
|
}
|
|
284
375
|
/**
|
|
285
376
|
* `GET /ngsi-ld/v1/types`(`details` 未指定)のレスポンス。
|
|
@@ -319,6 +410,26 @@ declare class EventEmitter {
|
|
|
319
410
|
* Copyright (c) 2026 Geolonia Inc.
|
|
320
411
|
* Released under the MIT License. See src/sdk/LICENSE for details.
|
|
321
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
|
+
}
|
|
322
433
|
/**
|
|
323
434
|
* Base error class for GeonicDB SDK.
|
|
324
435
|
* All SDK errors extend this class, allowing `instanceof GeonicDBError` checks.
|
|
@@ -326,11 +437,18 @@ declare class EventEmitter {
|
|
|
326
437
|
export declare class GeonicDBError extends Error {
|
|
327
438
|
/** HTTP status code (if applicable) */
|
|
328
439
|
readonly statusCode: number;
|
|
440
|
+
/** The request that failed, when the SDK knows it. */
|
|
441
|
+
readonly request?: RequestContext;
|
|
329
442
|
constructor(message: string, statusCode?: number);
|
|
330
443
|
}
|
|
331
|
-
/**
|
|
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
|
+
*/
|
|
332
450
|
export declare class AuthenticationError extends GeonicDBError {
|
|
333
|
-
constructor(message?: string);
|
|
451
|
+
constructor(message?: string, statusCode?: number);
|
|
334
452
|
}
|
|
335
453
|
/** 403 — Authorization denied (insufficient permissions). */
|
|
336
454
|
export declare class AuthorizationError extends GeonicDBError {
|
|
@@ -528,8 +646,22 @@ export declare class GeonicDB extends EventEmitter {
|
|
|
528
646
|
getTemporalEntity(entityId: string): Promise<Record<string, unknown>>;
|
|
529
647
|
/** Create multiple entities in a single request. */
|
|
530
648
|
batchCreate(entities: Record<string, unknown>[]): Promise<Record<string, unknown>>;
|
|
531
|
-
/**
|
|
532
|
-
|
|
649
|
+
/**
|
|
650
|
+
* Upsert multiple entities in a single request.
|
|
651
|
+
*
|
|
652
|
+
* #1742: the server follows ETSI GS CIM 009 clause 5.6.8.5 / Table 6.15.3.1-1:
|
|
653
|
+
* - **201** with a `string[]` of the **created** entity IDs, when at least one entity
|
|
654
|
+
* was newly created and none failed (merely updated entities are not listed);
|
|
655
|
+
* - **204** with no body, when every entity already existed and was updated
|
|
656
|
+
* (`_jsonPost` maps this to `{}`);
|
|
657
|
+
* - **207** with a `BatchOperationResult` (`{ created, updated, errors }`), on partial
|
|
658
|
+
* or total failure.
|
|
659
|
+
*
|
|
660
|
+
* The return type reflects all three shapes. Narrow with `Array.isArray(result)` to
|
|
661
|
+
* detect the "something was created" case.
|
|
662
|
+
* https://cim.etsi.org/NGSI-LD/official/clause-6.html
|
|
663
|
+
*/
|
|
664
|
+
batchUpsert(entities: Record<string, unknown>[]): Promise<string[] | Record<string, unknown>>;
|
|
533
665
|
/** Update multiple entities in a single request. */
|
|
534
666
|
batchUpdate(entities: Record<string, unknown>[]): Promise<Record<string, unknown>>;
|
|
535
667
|
/** Delete multiple entities by ID. */
|
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
|
}
|
|
@@ -1246,13 +1264,12 @@ var GeonicDBModule = (() => {
|
|
|
1246
1264
|
}
|
|
1247
1265
|
const url = this._baseUrl + path;
|
|
1248
1266
|
const isDPoP = this._tokenType === "DPoP" && !!this._dpopKeyPair && token !== null;
|
|
1249
|
-
const
|
|
1267
|
+
const pathname = path.split("?")[0];
|
|
1268
|
+
const isNgsiLdWrite = pathname.startsWith("/ngsi-ld/v1/") && pathname !== "/ngsi-ld/v1/jsonldContexts";
|
|
1269
|
+
const withCoreContext = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && !("@context" in value) ? { ...value, "@context": NGSI_LD_CORE_CONTEXT } : value;
|
|
1250
1270
|
let effectiveBody = body;
|
|
1251
|
-
if (
|
|
1252
|
-
effectiveBody =
|
|
1253
|
-
...effectiveBody,
|
|
1254
|
-
"@context": NGSI_LD_CORE_CONTEXT
|
|
1255
|
-
};
|
|
1271
|
+
if (isNgsiLdWrite && effectiveBody !== void 0) {
|
|
1272
|
+
effectiveBody = Array.isArray(effectiveBody) ? effectiveBody.map(withCoreContext) : withCoreContext(effectiveBody);
|
|
1256
1273
|
}
|
|
1257
1274
|
const bodyStr = effectiveBody !== void 0 ? JSON.stringify(effectiveBody) : void 0;
|
|
1258
1275
|
const doRequest = async (reqToken, dpopNonce) => {
|
|
@@ -1326,6 +1343,7 @@ var GeonicDBModule = (() => {
|
|
|
1326
1343
|
__publicField(this, "_emit");
|
|
1327
1344
|
__publicField(this, "_ws", null);
|
|
1328
1345
|
__publicField(this, "_wsEndpoint", null);
|
|
1346
|
+
__publicField(this, "_deploymentHostname", null);
|
|
1329
1347
|
__publicField(this, "_wsIntentionalClose", false);
|
|
1330
1348
|
__publicField(this, "_reconnectAttempts", 0);
|
|
1331
1349
|
__publicField(this, "_tokenRefreshTimer", null);
|
|
@@ -1398,6 +1416,7 @@ var GeonicDBModule = (() => {
|
|
|
1398
1416
|
if (options.entityTypes) msg.entityTypes = options.entityTypes;
|
|
1399
1417
|
if (options.idPattern) msg.idPattern = options.idPattern;
|
|
1400
1418
|
if (options.scopeQ) msg.scopeQ = options.scopeQ;
|
|
1419
|
+
if (options["@context"]) msg["@context"] = options["@context"];
|
|
1401
1420
|
}
|
|
1402
1421
|
this._subscription = msg;
|
|
1403
1422
|
if (this._pendingSubscription) return;
|
|
@@ -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;
|
|
@@ -1465,6 +1491,10 @@ var GeonicDBModule = (() => {
|
|
|
1465
1491
|
return;
|
|
1466
1492
|
}
|
|
1467
1493
|
if (msg.type === "pong") return;
|
|
1494
|
+
if (msg.type === "subscribed") {
|
|
1495
|
+
this._emit("subscribed", void 0);
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1468
1498
|
if (msg.type === "dpop_verified") {
|
|
1469
1499
|
this._pendingSubscription = false;
|
|
1470
1500
|
if (this._subscription) {
|
|
@@ -1720,6 +1750,7 @@ var GeonicDBModule = (() => {
|
|
|
1720
1750
|
if (!tenant) tenant = script?.getAttribute?.("data-tenant") || "";
|
|
1721
1751
|
if (!baseUrl) baseUrl = script?.getAttribute?.("data-base-url") || "";
|
|
1722
1752
|
}
|
|
1753
|
+
baseUrl = baseUrl.replace(/\/+$/, "");
|
|
1723
1754
|
this._auth = new AuthManager(baseUrl, apiKey, tenant, opts.debug, opts.anonymous);
|
|
1724
1755
|
this._auth.onTokenRefresh = (creds) => {
|
|
1725
1756
|
this.onTokenRefresh?.(creds);
|
|
@@ -1931,7 +1962,9 @@ var GeonicDBModule = (() => {
|
|
|
1931
1962
|
limit: 0,
|
|
1932
1963
|
type: params?.type,
|
|
1933
1964
|
q: params?.q,
|
|
1934
|
-
scopeQ: params?.scopeQ
|
|
1965
|
+
scopeQ: params?.scopeQ,
|
|
1966
|
+
// #2278: clause 5.7.2.4 の "too wide query" 免除条件 (e) local scope。
|
|
1967
|
+
local: params?.local
|
|
1935
1968
|
});
|
|
1936
1969
|
const res = await this._auth.request("GET", path);
|
|
1937
1970
|
if (!res.ok) {
|
|
@@ -2028,7 +2061,21 @@ var GeonicDBModule = (() => {
|
|
|
2028
2061
|
"Batch create failed"
|
|
2029
2062
|
);
|
|
2030
2063
|
}
|
|
2031
|
-
/**
|
|
2064
|
+
/**
|
|
2065
|
+
* Upsert multiple entities in a single request.
|
|
2066
|
+
*
|
|
2067
|
+
* #1742: the server follows ETSI GS CIM 009 clause 5.6.8.5 / Table 6.15.3.1-1:
|
|
2068
|
+
* - **201** with a `string[]` of the **created** entity IDs, when at least one entity
|
|
2069
|
+
* was newly created and none failed (merely updated entities are not listed);
|
|
2070
|
+
* - **204** with no body, when every entity already existed and was updated
|
|
2071
|
+
* (`_jsonPost` maps this to `{}`);
|
|
2072
|
+
* - **207** with a `BatchOperationResult` (`{ created, updated, errors }`), on partial
|
|
2073
|
+
* or total failure.
|
|
2074
|
+
*
|
|
2075
|
+
* The return type reflects all three shapes. Narrow with `Array.isArray(result)` to
|
|
2076
|
+
* detect the "something was created" case.
|
|
2077
|
+
* https://cim.etsi.org/NGSI-LD/official/clause-6.html
|
|
2078
|
+
*/
|
|
2032
2079
|
async batchUpsert(entities) {
|
|
2033
2080
|
return this._jsonPost(
|
|
2034
2081
|
"/ngsi-ld/v1/entityOperations/upsert",
|