@geolonia/geonicdb-sdk 0.10.1 → 0.11.1
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 +32 -44
- package/geonicdb.cjs +66 -54
- package/geonicdb.cjs.map +2 -2
- package/geonicdb.d.ts +41 -45
- package/geonicdb.iife.js +66 -54
- package/geonicdb.iife.js.map +2 -2
- package/geonicdb.mjs +66 -54
- package/geonicdb.mjs.map +2 -2
- package/package.json +1 -1
package/geonicdb.d.ts
CHANGED
|
@@ -121,6 +121,16 @@ export interface GetEntitiesParams {
|
|
|
121
121
|
count?: boolean;
|
|
122
122
|
/** GeoProperty key to use (default `location`) */
|
|
123
123
|
geoproperty?: string;
|
|
124
|
+
/**
|
|
125
|
+
* NGSI-LD representation options (`options` query param)。カンマ区切りで複数指定可。
|
|
126
|
+
* - `sysAttrs` — システム属性 (`createdAt` / `modifiedAt`) を含める
|
|
127
|
+
* - `keyValues` — 簡易表現 (属性を値だけに圧縮)
|
|
128
|
+
* - `concise` — concise 表現
|
|
129
|
+
* - `normalized` — 正規化表現 (デフォルト)
|
|
130
|
+
*
|
|
131
|
+
* 例: `'sysAttrs'`, `'keyValues,sysAttrs'`。
|
|
132
|
+
*/
|
|
133
|
+
options?: string;
|
|
124
134
|
}
|
|
125
135
|
/** Parameters for counting entities. */
|
|
126
136
|
export interface CountEntitiesParams {
|
|
@@ -171,37 +181,20 @@ export interface ReconnectingEvent {
|
|
|
171
181
|
/** Options for `db.login(...)`. */
|
|
172
182
|
export interface LoginOptions {
|
|
173
183
|
/**
|
|
174
|
-
* If true,
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* Recommended for browser apps that persist tokens in storage that may be
|
|
178
|
-
* exposed to XSS (localStorage, sessionStorage). DPoP-bound tokens are
|
|
179
|
-
* unusable without the client's non-extractable private key, so a stolen
|
|
180
|
-
* token cannot be replayed from another origin.
|
|
181
|
-
*
|
|
182
|
-
* Throws `AuthenticationError` if DPoP is not supported in the runtime
|
|
183
|
-
* (no Web Crypto subtle.generateKey).
|
|
184
|
-
*
|
|
185
|
-
* Default: false (Bearer remains).
|
|
186
|
-
*/
|
|
187
|
-
dpop?: boolean;
|
|
188
|
-
/**
|
|
189
|
-
* If true, persist the DPoP session (non-extractable private key + tokens)
|
|
190
|
-
* to IndexedDB so the session survives a page reload. Implies `dpop: true`
|
|
191
|
-
* — passing `dpopPersist: true` without `dpop: true` throws.
|
|
192
|
-
*
|
|
193
|
-
* The private key is stored as a `CryptoKey` via the structured clone
|
|
194
|
-
* algorithm; `extractable: false` is preserved, so the key cannot be read
|
|
195
|
-
* back as JWK / PEM even with full DOM access. A token leaked from
|
|
196
|
-
* localStorage remains unusable without this SDK origin's IndexedDB.
|
|
184
|
+
* If true, keep the user signed in across page reloads by persisting the
|
|
185
|
+
* session to IndexedDB. After a reload, call `db.restoreSession()` to
|
|
186
|
+
* rehydrate the session; `db.logout()` clears the persisted entry.
|
|
197
187
|
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
188
|
+
* The session is bound to a non-extractable key (DPoP, RFC 9449) stored via
|
|
189
|
+
* the structured clone algorithm — `extractable: false` is preserved, so the
|
|
190
|
+
* key cannot be read back as JWK / PEM even with full DOM access. A token
|
|
191
|
+
* leaked from localStorage stays unusable without this origin's IndexedDB.
|
|
200
192
|
*
|
|
201
193
|
* Default: false. Opt-out by design: shared-device flows should not enable
|
|
202
|
-
* this.
|
|
194
|
+
* this. Has no effect when the runtime lacks Web Crypto — the session cannot
|
|
195
|
+
* be sender-constrained, so it is not persisted.
|
|
203
196
|
*/
|
|
204
|
-
|
|
197
|
+
rememberSession?: boolean;
|
|
205
198
|
}
|
|
206
199
|
/** Response from the login endpoint. */
|
|
207
200
|
export interface LoginResponse {
|
|
@@ -271,6 +264,15 @@ export interface GetTemporalEntitiesParams {
|
|
|
271
264
|
limit?: number;
|
|
272
265
|
/** Pagination offset */
|
|
273
266
|
offset?: number;
|
|
267
|
+
/**
|
|
268
|
+
* NGSI-LD representation options (`options` query param)。カンマ区切りで複数指定可。
|
|
269
|
+
* - `sysAttrs` — システム属性 (`createdAt` / `modifiedAt`) を含める
|
|
270
|
+
* - `temporalValues` — 時系列を簡易配列表現で返す
|
|
271
|
+
* - `aggregatedValues` — 集約表現で返す
|
|
272
|
+
*
|
|
273
|
+
* 例: `'sysAttrs'`, `'temporalValues'`。
|
|
274
|
+
*/
|
|
275
|
+
options?: string;
|
|
274
276
|
}
|
|
275
277
|
export type Listener<T> = (data: T) => void;
|
|
276
278
|
declare class EventEmitter {
|
|
@@ -364,33 +366,27 @@ export declare class GeonicDB extends EventEmitter {
|
|
|
364
366
|
*/
|
|
365
367
|
clearCache(): void;
|
|
366
368
|
/**
|
|
367
|
-
* Login with email and password
|
|
369
|
+
* Login with email and password.
|
|
368
370
|
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
371
|
+
* The session is sender-constrained with DPoP (RFC 9449) automatically and
|
|
372
|
+
* transparently — there is no DPoP option to set. A token leaked from
|
|
373
|
+
* storage (e.g. localStorage XSS) is therefore unusable from another origin.
|
|
374
|
+
* On runtimes without Web Crypto, the SDK silently keeps a plain Bearer
|
|
375
|
+
* session and login still succeeds.
|
|
374
376
|
*
|
|
375
377
|
* @example
|
|
376
378
|
* ```ts
|
|
377
|
-
* //
|
|
379
|
+
* // Standard login — DPoP applied transparently when supported.
|
|
378
380
|
* await db.login('user@example.com', 'password');
|
|
379
381
|
*
|
|
380
|
-
* //
|
|
381
|
-
* await db.login('user@example.com', 'password', {
|
|
382
|
-
*
|
|
383
|
-
* // DPoP-bound + persisted across reloads (browser SPA UX)
|
|
384
|
-
* await db.login('user@example.com', 'password', {
|
|
385
|
-
* dpop: true,
|
|
386
|
-
* dpopPersist: true,
|
|
387
|
-
* });
|
|
382
|
+
* // Keep the user signed in across page reloads (browser SPA UX).
|
|
383
|
+
* await db.login('user@example.com', 'password', { rememberSession: true });
|
|
388
384
|
* ```
|
|
389
385
|
*/
|
|
390
386
|
login(email: string, password: string, options?: LoginOptions): Promise<LoginResponse>;
|
|
391
387
|
/**
|
|
392
|
-
* Rehydrate a
|
|
393
|
-
* `login(
|
|
388
|
+
* Rehydrate a session previously persisted via
|
|
389
|
+
* `login(email, password, { rememberSession: true })`.
|
|
394
390
|
*
|
|
395
391
|
* Returns true if a session for this tenant was found and loaded; false
|
|
396
392
|
* if there was no entry, IndexedDB is unavailable, or the persisted
|
|
@@ -401,7 +397,7 @@ export declare class GeonicDB extends EventEmitter {
|
|
|
401
397
|
* ```ts
|
|
402
398
|
* const db = new GeonicDB({ baseUrl, tenant });
|
|
403
399
|
* if (await db.restoreSession()) {
|
|
404
|
-
* //
|
|
400
|
+
* // Session (tokens + non-extractable key) recovered — boot the app.
|
|
405
401
|
* } else {
|
|
406
402
|
* // No session; show the login form.
|
|
407
403
|
* }
|
package/geonicdb.iife.js
CHANGED
|
@@ -477,11 +477,12 @@ var GeonicDBModule = (() => {
|
|
|
477
477
|
__publicField(this, "_dpopReady", null);
|
|
478
478
|
__publicField(this, "_dpopNonce", null);
|
|
479
479
|
/**
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
480
|
+
* When true, every DPoP token mutation (login bind, refresh) is mirrored to
|
|
481
|
+
* IndexedDB so the session survives a page reload (#1230). Toggled by
|
|
482
|
+
* `login(email, password, { rememberSession: true })` and
|
|
483
|
+
* `restoreSession()` (#1244).
|
|
483
484
|
*/
|
|
484
|
-
__publicField(this, "
|
|
485
|
+
__publicField(this, "_rememberSession", false);
|
|
485
486
|
/** Email captured at login, persisted for UI display. */
|
|
486
487
|
__publicField(this, "_email", null);
|
|
487
488
|
__publicField(this, "_tokenPromise", null);
|
|
@@ -546,22 +547,22 @@ var GeonicDBModule = (() => {
|
|
|
546
547
|
this._emitCacheEvent?.(name, payload);
|
|
547
548
|
}
|
|
548
549
|
/**
|
|
549
|
-
* Login with email and password
|
|
550
|
+
* Login with email and password.
|
|
550
551
|
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
552
|
+
* #1244: DPoP (RFC 9449) is transparent and always-on — there is no `dpop`
|
|
553
|
+
* option to set. After the Bearer login succeeds, the SDK automatically
|
|
554
|
+
* exchanges it for a DPoP sender-constrained session so a token leaked from
|
|
555
|
+
* storage (e.g. localStorage XSS) cannot be replayed from another origin.
|
|
556
|
+
*
|
|
557
|
+
* When the runtime lacks Web Crypto (`crypto.subtle.generateKey`, e.g. a
|
|
558
|
+
* non-HTTPS context), DPoP cannot be used and the SDK silently keeps the
|
|
559
|
+
* Bearer session — login still succeeds. Callers never deal with DPoP
|
|
560
|
+
* directly; pass `{ rememberSession: true }` only to keep the user signed in
|
|
561
|
+
* across reloads.
|
|
556
562
|
*/
|
|
557
563
|
async login(email, password, options) {
|
|
558
564
|
this._log("login", email);
|
|
559
|
-
const
|
|
560
|
-
if (wantsDpopPersist && !options?.dpop) {
|
|
561
|
-
throw new AuthenticationError(
|
|
562
|
-
"dpopPersist: true requires dpop: true. Persisting Bearer tokens is the caller's responsibility (localStorage / sessionStorage)."
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
+
const wantsRemember = options?.rememberSession === true;
|
|
565
566
|
const headers = {
|
|
566
567
|
"Content-Type": "application/json"
|
|
567
568
|
};
|
|
@@ -584,14 +585,30 @@ var GeonicDBModule = (() => {
|
|
|
584
585
|
this._tokenType = "Bearer";
|
|
585
586
|
this._refreshToken = data.refreshToken;
|
|
586
587
|
this._email = email;
|
|
587
|
-
this.
|
|
588
|
+
this._rememberSession = wantsRemember;
|
|
588
589
|
this._invalidateAuthScopedState();
|
|
589
|
-
if (
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
590
|
+
if (!wantsRemember && this._tenant) {
|
|
591
|
+
void clearDPoPSession(this._tenant);
|
|
592
|
+
}
|
|
593
|
+
if (this._dpopReady) await this._dpopReady;
|
|
594
|
+
if (this._dpopKeyPair) {
|
|
595
|
+
try {
|
|
596
|
+
const bound = await this._bindDpopSession();
|
|
597
|
+
if (this._rememberSession) {
|
|
598
|
+
await this._persistDpopSession();
|
|
599
|
+
}
|
|
600
|
+
return bound;
|
|
601
|
+
} catch (err) {
|
|
602
|
+
this._token = null;
|
|
603
|
+
this._tokenExpiry = 0;
|
|
604
|
+
this._tokenType = "Bearer";
|
|
605
|
+
this._refreshToken = null;
|
|
606
|
+
this._tokenPromise = null;
|
|
607
|
+
this._rememberSession = false;
|
|
608
|
+
this._email = null;
|
|
609
|
+
this._invalidateAuthScopedState();
|
|
610
|
+
throw err;
|
|
593
611
|
}
|
|
594
|
-
return bound;
|
|
595
612
|
}
|
|
596
613
|
return data;
|
|
597
614
|
}
|
|
@@ -685,18 +702,19 @@ var GeonicDBModule = (() => {
|
|
|
685
702
|
this._refreshToken = null;
|
|
686
703
|
this._tokenPromise = null;
|
|
687
704
|
this._email = null;
|
|
688
|
-
if (this.
|
|
705
|
+
if (this._tenant) {
|
|
689
706
|
void clearDPoPSession(this._tenant);
|
|
690
707
|
}
|
|
691
|
-
this.
|
|
708
|
+
this._rememberSession = false;
|
|
692
709
|
this._anonymous = this._initialAnonymous;
|
|
693
710
|
this._invalidateAuthScopedState();
|
|
694
711
|
}
|
|
695
712
|
/**
|
|
696
|
-
* Restore a
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
713
|
+
* Restore a session previously persisted via
|
|
714
|
+
* `login(email, password, { rememberSession: true })`. Returns true if a
|
|
715
|
+
* usable session was found and loaded into the
|
|
716
|
+
* SDK, false otherwise (no entry, IndexedDB unavailable, or the entry was
|
|
717
|
+
* for a different tenant).
|
|
700
718
|
*
|
|
701
719
|
* After a successful restore the SDK behaves as if `login()` had just
|
|
702
720
|
* succeeded: tokens are set, the DPoP key pair is rehydrated as a
|
|
@@ -724,19 +742,19 @@ var GeonicDBModule = (() => {
|
|
|
724
742
|
this._dpopKeyPair = persisted.keyPair;
|
|
725
743
|
this._dpopReady = Promise.resolve();
|
|
726
744
|
this._dpopNonce = null;
|
|
727
|
-
this.
|
|
745
|
+
this._rememberSession = true;
|
|
728
746
|
this._email = persisted.email ?? null;
|
|
729
747
|
this._tokenPromise = null;
|
|
730
748
|
this._invalidateAuthScopedState();
|
|
731
749
|
return true;
|
|
732
750
|
}
|
|
733
751
|
/**
|
|
734
|
-
* Whether this AuthManager currently
|
|
735
|
-
* Exposed for the `GeonicDB` wrapper
|
|
736
|
-
* consumers should read `
|
|
752
|
+
* Whether this AuthManager currently keeps the session persisted across
|
|
753
|
+
* reloads (`rememberSession`). Exposed for the `GeonicDB` wrapper; internal
|
|
754
|
+
* consumers should read `_rememberSession` directly.
|
|
737
755
|
*/
|
|
738
|
-
|
|
739
|
-
return this.
|
|
756
|
+
isSessionRemembered() {
|
|
757
|
+
return this._rememberSession;
|
|
740
758
|
}
|
|
741
759
|
/**
|
|
742
760
|
* Persist the current DPoP session to IndexedDB. Best-effort: a failed
|
|
@@ -746,7 +764,7 @@ var GeonicDBModule = (() => {
|
|
|
746
764
|
* @internal
|
|
747
765
|
*/
|
|
748
766
|
async _persistDpopSession() {
|
|
749
|
-
if (!this.
|
|
767
|
+
if (!this._rememberSession || !this._tenant || !this._dpopKeyPair || !this._token || !this._refreshToken) {
|
|
750
768
|
return;
|
|
751
769
|
}
|
|
752
770
|
const ok = await saveDPoPSession({
|
|
@@ -829,7 +847,7 @@ var GeonicDBModule = (() => {
|
|
|
829
847
|
expiresIn: data.expiresIn,
|
|
830
848
|
refreshToken: this._refreshToken
|
|
831
849
|
});
|
|
832
|
-
if (this.
|
|
850
|
+
if (this._rememberSession) void this._persistDpopSession();
|
|
833
851
|
return this._token;
|
|
834
852
|
} catch (err) {
|
|
835
853
|
this._tokenPromise = null;
|
|
@@ -1410,35 +1428,29 @@ var GeonicDBModule = (() => {
|
|
|
1410
1428
|
}
|
|
1411
1429
|
// --- Authentication ---
|
|
1412
1430
|
/**
|
|
1413
|
-
* Login with email and password
|
|
1431
|
+
* Login with email and password.
|
|
1414
1432
|
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1417
|
-
*
|
|
1418
|
-
*
|
|
1419
|
-
*
|
|
1433
|
+
* The session is sender-constrained with DPoP (RFC 9449) automatically and
|
|
1434
|
+
* transparently — there is no DPoP option to set. A token leaked from
|
|
1435
|
+
* storage (e.g. localStorage XSS) is therefore unusable from another origin.
|
|
1436
|
+
* On runtimes without Web Crypto, the SDK silently keeps a plain Bearer
|
|
1437
|
+
* session and login still succeeds.
|
|
1420
1438
|
*
|
|
1421
1439
|
* @example
|
|
1422
1440
|
* ```ts
|
|
1423
|
-
* //
|
|
1441
|
+
* // Standard login — DPoP applied transparently when supported.
|
|
1424
1442
|
* await db.login('user@example.com', 'password');
|
|
1425
1443
|
*
|
|
1426
|
-
* //
|
|
1427
|
-
* await db.login('user@example.com', 'password', {
|
|
1428
|
-
*
|
|
1429
|
-
* // DPoP-bound + persisted across reloads (browser SPA UX)
|
|
1430
|
-
* await db.login('user@example.com', 'password', {
|
|
1431
|
-
* dpop: true,
|
|
1432
|
-
* dpopPersist: true,
|
|
1433
|
-
* });
|
|
1444
|
+
* // Keep the user signed in across page reloads (browser SPA UX).
|
|
1445
|
+
* await db.login('user@example.com', 'password', { rememberSession: true });
|
|
1434
1446
|
* ```
|
|
1435
1447
|
*/
|
|
1436
1448
|
async login(email, password, options) {
|
|
1437
1449
|
return this._auth.login(email, password, options);
|
|
1438
1450
|
}
|
|
1439
1451
|
/**
|
|
1440
|
-
* Rehydrate a
|
|
1441
|
-
* `login(
|
|
1452
|
+
* Rehydrate a session previously persisted via
|
|
1453
|
+
* `login(email, password, { rememberSession: true })`.
|
|
1442
1454
|
*
|
|
1443
1455
|
* Returns true if a session for this tenant was found and loaded; false
|
|
1444
1456
|
* if there was no entry, IndexedDB is unavailable, or the persisted
|
|
@@ -1449,7 +1461,7 @@ var GeonicDBModule = (() => {
|
|
|
1449
1461
|
* ```ts
|
|
1450
1462
|
* const db = new GeonicDB({ baseUrl, tenant });
|
|
1451
1463
|
* if (await db.restoreSession()) {
|
|
1452
|
-
* //
|
|
1464
|
+
* // Session (tokens + non-extractable key) recovered — boot the app.
|
|
1453
1465
|
* } else {
|
|
1454
1466
|
* // No session; show the login form.
|
|
1455
1467
|
* }
|