@spacelr/sdk 0.9.1 → 0.10.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/dist/index.d.mts +65 -20
- package/dist/index.d.ts +65 -20
- package/dist/index.js +94 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -385,7 +385,7 @@ var BrowserTokenStorage = class {
|
|
|
385
385
|
|
|
386
386
|
// libs/sdk/src/core/token-manager.ts
|
|
387
387
|
var TokenManager = class {
|
|
388
|
-
constructor(storage, refreshBufferSeconds = 60) {
|
|
388
|
+
constructor(storage, refreshBufferSeconds = 60, refreshLockName = "spacelr_token_refresh") {
|
|
389
389
|
this.refreshCallback = null;
|
|
390
390
|
this.refreshPromise = null;
|
|
391
391
|
this.tokenRefreshedListeners = /* @__PURE__ */ new Set();
|
|
@@ -396,6 +396,7 @@ var TokenManager = class {
|
|
|
396
396
|
this.authLostEmitted = false;
|
|
397
397
|
this.storage = storage ?? new MemoryTokenStorage();
|
|
398
398
|
this.refreshBufferSeconds = refreshBufferSeconds;
|
|
399
|
+
this.refreshLockName = refreshLockName;
|
|
399
400
|
}
|
|
400
401
|
setRefreshCallback(callback) {
|
|
401
402
|
this.refreshCallback = callback;
|
|
@@ -425,7 +426,7 @@ var TokenManager = class {
|
|
|
425
426
|
this.authLostEmitted = false;
|
|
426
427
|
}
|
|
427
428
|
async getStoredTokens() {
|
|
428
|
-
return this.
|
|
429
|
+
return this.safeGetTokens();
|
|
429
430
|
}
|
|
430
431
|
/**
|
|
431
432
|
* Force a refresh using the current stored refresh token.
|
|
@@ -492,9 +493,18 @@ var TokenManager = class {
|
|
|
492
493
|
}
|
|
493
494
|
}
|
|
494
495
|
async executeRefresh(refreshToken) {
|
|
496
|
+
return this.withCrossTabLock(() => this.doRefresh(refreshToken));
|
|
497
|
+
}
|
|
498
|
+
async doRefresh(refreshToken) {
|
|
495
499
|
const callback = this.refreshCallback;
|
|
500
|
+
const current = await this.safeGetTokens();
|
|
501
|
+
if (current && current.refreshToken && current.refreshToken !== refreshToken && !this.isTokenExpired(current)) {
|
|
502
|
+
this.emitTokenRefreshed(current);
|
|
503
|
+
return current;
|
|
504
|
+
}
|
|
505
|
+
const activeRefreshToken = current?.refreshToken || refreshToken;
|
|
496
506
|
try {
|
|
497
|
-
const newTokens = await callback(
|
|
507
|
+
const newTokens = await callback(activeRefreshToken);
|
|
498
508
|
await this.storage.setTokens(newTokens);
|
|
499
509
|
this.emitTokenRefreshed(newTokens);
|
|
500
510
|
return newTokens;
|
|
@@ -503,6 +513,22 @@ var TokenManager = class {
|
|
|
503
513
|
throw error;
|
|
504
514
|
}
|
|
505
515
|
}
|
|
516
|
+
async safeGetTokens() {
|
|
517
|
+
try {
|
|
518
|
+
return await this.storage.getTokens();
|
|
519
|
+
} catch {
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
withCrossTabLock(fn) {
|
|
524
|
+
const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
|
|
525
|
+
if (!locks) return fn();
|
|
526
|
+
try {
|
|
527
|
+
return locks.request(this.refreshLockName, fn);
|
|
528
|
+
} catch {
|
|
529
|
+
return fn();
|
|
530
|
+
}
|
|
531
|
+
}
|
|
506
532
|
emitTokenRefreshed(tokens) {
|
|
507
533
|
for (const listener of this.tokenRefreshedListeners) {
|
|
508
534
|
try {
|
|
@@ -588,6 +614,57 @@ var PERMANENT_STREAM_ACK_ERRORS = /* @__PURE__ */ new Set([
|
|
|
588
614
|
"Not a member of this project",
|
|
589
615
|
"Subscribe denied"
|
|
590
616
|
]);
|
|
617
|
+
function isWhereScalar(value) {
|
|
618
|
+
const t = typeof value;
|
|
619
|
+
return t === "string" || t === "number" || t === "boolean";
|
|
620
|
+
}
|
|
621
|
+
function isWhereOperator(value) {
|
|
622
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
const keys = Object.keys(value);
|
|
626
|
+
if (keys.length !== 1) return false;
|
|
627
|
+
const op = keys[0];
|
|
628
|
+
if (op !== "in" && op !== "array-contains-any") return false;
|
|
629
|
+
const candidates = value[op];
|
|
630
|
+
return Array.isArray(candidates) && candidates.every(isWhereScalar);
|
|
631
|
+
}
|
|
632
|
+
var WHERE_PATH_ABSENT = /* @__PURE__ */ Symbol("where-path-absent");
|
|
633
|
+
var WHERE_PATH_UNRESOLVABLE = /* @__PURE__ */ Symbol("where-path-unresolvable");
|
|
634
|
+
var RESERVED_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
635
|
+
var MAX_WHERE_PATH_SEGMENTS = 16;
|
|
636
|
+
function resolveWherePath(document2, key) {
|
|
637
|
+
const segments = key.split(".");
|
|
638
|
+
if (segments.length > MAX_WHERE_PATH_SEGMENTS) return WHERE_PATH_ABSENT;
|
|
639
|
+
let cur = document2;
|
|
640
|
+
for (const segment of segments) {
|
|
641
|
+
if (RESERVED_PATH_SEGMENTS.has(segment)) return WHERE_PATH_ABSENT;
|
|
642
|
+
if (cur === null || cur === void 0) return WHERE_PATH_ABSENT;
|
|
643
|
+
if (Array.isArray(cur)) return WHERE_PATH_UNRESOLVABLE;
|
|
644
|
+
if (typeof cur !== "object") return WHERE_PATH_ABSENT;
|
|
645
|
+
if (!Object.prototype.hasOwnProperty.call(cur, segment)) {
|
|
646
|
+
return WHERE_PATH_ABSENT;
|
|
647
|
+
}
|
|
648
|
+
cur = cur[segment];
|
|
649
|
+
}
|
|
650
|
+
return cur === void 0 ? WHERE_PATH_ABSENT : cur;
|
|
651
|
+
}
|
|
652
|
+
function whereValueMatches(actual, expected) {
|
|
653
|
+
if (actual === WHERE_PATH_ABSENT || actual === WHERE_PATH_UNRESOLVABLE) {
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
if (isWhereOperator(expected)) {
|
|
657
|
+
if ("in" in expected) {
|
|
658
|
+
if (Array.isArray(actual)) return false;
|
|
659
|
+
return expected.in.includes(actual);
|
|
660
|
+
}
|
|
661
|
+
if (!Array.isArray(actual)) return false;
|
|
662
|
+
const candidates = expected["array-contains-any"];
|
|
663
|
+
return actual.some((el) => candidates.includes(el));
|
|
664
|
+
}
|
|
665
|
+
if (Array.isArray(actual)) return actual.includes(expected);
|
|
666
|
+
return actual === expected;
|
|
667
|
+
}
|
|
591
668
|
var RealtimeClient = class {
|
|
592
669
|
constructor(config) {
|
|
593
670
|
this.socket = null;
|
|
@@ -976,12 +1053,7 @@ var RealtimeClient = class {
|
|
|
976
1053
|
if (!where) return false;
|
|
977
1054
|
if (!event.document) return false;
|
|
978
1055
|
for (const [key, value] of Object.entries(where)) {
|
|
979
|
-
|
|
980
|
-
if (Array.isArray(docValue)) {
|
|
981
|
-
if (!docValue.includes(value)) {
|
|
982
|
-
return false;
|
|
983
|
-
}
|
|
984
|
-
} else if (docValue !== value) {
|
|
1056
|
+
if (!whereValueMatches(resolveWherePath(event.document, key), value)) {
|
|
985
1057
|
return false;
|
|
986
1058
|
}
|
|
987
1059
|
}
|
|
@@ -2942,10 +3014,14 @@ var FunctionsModule = class {
|
|
|
2942
3014
|
* `config.apiUrl` (which already carries the `/api/v1` prefix).
|
|
2943
3015
|
*
|
|
2944
3016
|
* Auth defaults, based on `invokeMode` semantics:
|
|
2945
|
-
* - webhook:
|
|
2946
|
-
* - authenticated:
|
|
2947
|
-
*
|
|
2948
|
-
* -
|
|
3017
|
+
* - webhook: pass `secret` → Authorization is NOT attached
|
|
3018
|
+
* - authenticated: pass nothing → Authorization IS attached (from token manager);
|
|
3019
|
+
* caller must be a member of the target project
|
|
3020
|
+
* - public: pass nothing → Authorization is attached if logged in, else omitted
|
|
3021
|
+
* - hybrid: pass both `secret` and `authenticated: true`; JWT path
|
|
3022
|
+
* requires project membership like `authenticated`
|
|
3023
|
+
* - platform-authenticated: pass nothing → Authorization IS attached (from token manager);
|
|
3024
|
+
* any signed-in user is accepted, still just attaches the bearer token
|
|
2949
3025
|
*
|
|
2950
3026
|
* To force a specific behaviour, set `authenticated` explicitly — it wins
|
|
2951
3027
|
* over the `secret`-based default.
|
|
@@ -3031,7 +3107,8 @@ function createClient(config) {
|
|
|
3031
3107
|
const tokenStorage = config.tokenStorage ?? (typeof window !== "undefined" && typeof window.localStorage !== "undefined" ? new BrowserTokenStorage() : new MemoryTokenStorage());
|
|
3032
3108
|
const tokenManager = new TokenManager(
|
|
3033
3109
|
tokenStorage,
|
|
3034
|
-
config.refreshBufferSeconds ?? 60
|
|
3110
|
+
config.refreshBufferSeconds ?? 60,
|
|
3111
|
+
`spacelr_token_refresh:${config.projectId}`
|
|
3035
3112
|
);
|
|
3036
3113
|
const httpClient = new HttpClient(config, tokenManager);
|
|
3037
3114
|
const realtime = new RealtimeClient({
|
|
@@ -3058,6 +3135,9 @@ function createClient(config) {
|
|
|
3058
3135
|
clearTokens() {
|
|
3059
3136
|
return tokenManager.clearTokens();
|
|
3060
3137
|
},
|
|
3138
|
+
hasStoredSession() {
|
|
3139
|
+
return tokenManager.getStoredTokens().then((tokens) => !!tokens);
|
|
3140
|
+
},
|
|
3061
3141
|
disconnect() {
|
|
3062
3142
|
realtime.disconnect();
|
|
3063
3143
|
},
|