@arcote.tech/arc-adapter-db-postgres 0.8.3 → 0.8.5

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.
Files changed (2) hide show
  1. package/dist/index.js +104 -48
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1350,9 +1350,74 @@ function apply(state, patches, applyOptions) {
1350
1350
  var constructorString = Object.prototype.constructor.toString();
1351
1351
 
1352
1352
  // ../../../core/dist/index.js
1353
- var TOKEN_PREFIX = "arc:token:";
1354
- function hasLocalStorage() {
1355
- return typeof localStorage !== "undefined";
1353
+ var COOKIE_PREFIX = "arc_token";
1354
+ var TOKEN_BUMP_KEY = "arc:token-bump";
1355
+ function hasDocument() {
1356
+ return typeof document !== "undefined";
1357
+ }
1358
+ function signalCrossTabChange() {
1359
+ if (typeof localStorage === "undefined")
1360
+ return;
1361
+ try {
1362
+ localStorage.setItem(TOKEN_BUMP_KEY, String(Date.now()));
1363
+ } catch {}
1364
+ }
1365
+ function isSecureContext() {
1366
+ return typeof location !== "undefined" && location.protocol === "https:";
1367
+ }
1368
+ function tokenCookieName(scopeKey) {
1369
+ return scopeKey === "default" ? COOKIE_PREFIX : `${COOKIE_PREFIX}_${scopeKey.replace(/:/g, ".")}`;
1370
+ }
1371
+ function scopeKeyFromCookieName(name) {
1372
+ if (name === COOKIE_PREFIX)
1373
+ return "default";
1374
+ if (!name.startsWith(`${COOKIE_PREFIX}_`))
1375
+ return null;
1376
+ return name.slice(COOKIE_PREFIX.length + 1).replace(/\./g, ":");
1377
+ }
1378
+ function maxAgeFromExp(exp) {
1379
+ const THIRTY_DAYS = 30 * 24 * 60 * 60;
1380
+ if (typeof exp !== "number" || !Number.isFinite(exp))
1381
+ return THIRTY_DAYS;
1382
+ const expMs = exp > 1000000000000 ? exp : exp * 1000;
1383
+ const secs = Math.floor((expMs - Date.now()) / 1000);
1384
+ return secs > 0 ? secs : THIRTY_DAYS;
1385
+ }
1386
+ function writeTokenCookie(scopeKey, jwt, exp) {
1387
+ if (!hasDocument())
1388
+ return;
1389
+ const attrs = [
1390
+ "Path=/",
1391
+ "SameSite=Lax",
1392
+ `Max-Age=${maxAgeFromExp(exp)}`
1393
+ ];
1394
+ if (isSecureContext())
1395
+ attrs.push("Secure");
1396
+ document.cookie = `${tokenCookieName(scopeKey)}=${encodeURIComponent(jwt)}; ${attrs.join("; ")}`;
1397
+ signalCrossTabChange();
1398
+ }
1399
+ function removeTokenCookie(scopeKey) {
1400
+ if (!hasDocument())
1401
+ return;
1402
+ document.cookie = `${tokenCookieName(scopeKey)}=; Path=/; SameSite=Lax; Max-Age=0`;
1403
+ signalCrossTabChange();
1404
+ }
1405
+ function readAllTokenCookies() {
1406
+ if (!hasDocument() || !document.cookie)
1407
+ return [];
1408
+ const out = [];
1409
+ for (const part of document.cookie.split(";")) {
1410
+ const eq = part.indexOf("=");
1411
+ if (eq < 0)
1412
+ continue;
1413
+ const scopeKey = scopeKeyFromCookieName(part.slice(0, eq).trim());
1414
+ if (scopeKey === null)
1415
+ continue;
1416
+ const jwt = decodeURIComponent(part.slice(eq + 1).trim());
1417
+ if (jwt)
1418
+ out.push({ scopeKey, jwt });
1419
+ }
1420
+ return out;
1356
1421
  }
1357
1422
  function notifyTokenChange(scope) {
1358
1423
  if (typeof window !== "undefined") {
@@ -1368,24 +1433,21 @@ class AuthAdapter {
1368
1433
  constructor(options) {
1369
1434
  this.namespace = options?.storageNamespace || undefined;
1370
1435
  }
1371
- storageKey(scope) {
1372
- return this.namespace ? `${TOKEN_PREFIX}${this.namespace}:${scope}` : TOKEN_PREFIX + scope;
1436
+ scopeKey(scope) {
1437
+ return this.namespace ? `${this.namespace}:${scope}` : scope;
1373
1438
  }
1374
1439
  setToken(token, scope = "default") {
1375
1440
  if (!token) {
1376
1441
  this.scopes.delete(scope);
1377
- if (hasLocalStorage()) {
1378
- localStorage.removeItem(this.storageKey(scope));
1379
- notifyTokenChange(scope);
1380
- }
1442
+ removeTokenCookie(this.scopeKey(scope));
1443
+ notifyTokenChange(scope);
1381
1444
  return;
1382
1445
  }
1383
1446
  try {
1384
1447
  const parts = token.split(".");
1385
1448
  if (parts.length !== 3) {
1386
1449
  this.scopes.delete(scope);
1387
- if (hasLocalStorage())
1388
- localStorage.removeItem(this.storageKey(scope));
1450
+ removeTokenCookie(this.scopeKey(scope));
1389
1451
  return;
1390
1452
  }
1391
1453
  const payload = JSON.parse(atob(parts[1]));
@@ -1398,49 +1460,45 @@ class AuthAdapter {
1398
1460
  exp: payload.exp
1399
1461
  }
1400
1462
  });
1401
- if (hasLocalStorage()) {
1402
- localStorage.setItem(this.storageKey(scope), token);
1403
- notifyTokenChange(scope);
1404
- }
1463
+ writeTokenCookie(this.scopeKey(scope), token, payload.exp);
1464
+ notifyTokenChange(scope);
1405
1465
  } catch {
1406
1466
  this.scopes.delete(scope);
1407
- if (hasLocalStorage())
1408
- localStorage.removeItem(this.storageKey(scope));
1467
+ removeTokenCookie(this.scopeKey(scope));
1409
1468
  }
1410
1469
  }
1411
1470
  setDecoded(decoded, scope = "default") {
1412
1471
  this.scopes.set(scope, { raw: "", decoded });
1413
1472
  }
1414
1473
  loadPersisted() {
1415
- if (!hasLocalStorage())
1416
- return;
1417
- const prefix = this.namespace ? `${TOKEN_PREFIX}${this.namespace}:` : TOKEN_PREFIX;
1418
- for (let i = 0;i < localStorage.length; i++) {
1419
- const key = localStorage.key(i);
1420
- if (key?.startsWith(prefix)) {
1421
- const scope = key.slice(prefix.length);
1422
- if (!this.namespace && scope.includes(":"))
1474
+ const nsPrefix = this.namespace ? `${this.namespace}:` : "";
1475
+ for (const { scopeKey, jwt } of readAllTokenCookies()) {
1476
+ let scope;
1477
+ if (this.namespace) {
1478
+ if (!scopeKey.startsWith(nsPrefix))
1423
1479
  continue;
1424
- const raw = localStorage.getItem(key);
1425
- if (raw) {
1426
- try {
1427
- const parts = raw.split(".");
1428
- if (parts.length !== 3)
1429
- continue;
1430
- const payload = JSON.parse(atob(parts[1]));
1431
- this.scopes.set(scope, {
1432
- raw,
1433
- decoded: {
1434
- tokenName: payload.tokenName,
1435
- params: payload.params || {},
1436
- iat: payload.iat,
1437
- exp: payload.exp
1438
- }
1439
- });
1440
- } catch {
1441
- localStorage.removeItem(key);
1480
+ scope = scopeKey.slice(nsPrefix.length);
1481
+ } else {
1482
+ if (scopeKey.includes(":"))
1483
+ continue;
1484
+ scope = scopeKey;
1485
+ }
1486
+ try {
1487
+ const parts = jwt.split(".");
1488
+ if (parts.length !== 3)
1489
+ continue;
1490
+ const payload = JSON.parse(atob(parts[1]));
1491
+ this.scopes.set(scope, {
1492
+ raw: jwt,
1493
+ decoded: {
1494
+ tokenName: payload.tokenName,
1495
+ params: payload.params || {},
1496
+ iat: payload.iat,
1497
+ exp: payload.exp
1442
1498
  }
1443
- }
1499
+ });
1500
+ } catch {
1501
+ removeTokenCookie(scopeKey);
1444
1502
  }
1445
1503
  }
1446
1504
  }
@@ -1472,10 +1530,8 @@ class AuthAdapter {
1472
1530
  return Array.from(this.scopes.keys());
1473
1531
  }
1474
1532
  clear() {
1475
- if (hasLocalStorage()) {
1476
- for (const scope of this.scopes.keys()) {
1477
- localStorage.removeItem(this.storageKey(scope));
1478
- }
1533
+ for (const scope of this.scopes.keys()) {
1534
+ removeTokenCookie(this.scopeKey(scope));
1479
1535
  }
1480
1536
  this.scopes.clear();
1481
1537
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-adapter-db-postgres",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -23,7 +23,7 @@
23
23
  "postgres": "^3.4.4"
24
24
  },
25
25
  "peerDependencies": {
26
- "@arcote.tech/arc": "^0.8.3"
26
+ "@arcote.tech/arc": "^0.8.5"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/pg": "^8.11.0",