@rixl/sdk 0.21.0 → 0.21.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/dist/index.d.ts CHANGED
@@ -14839,6 +14839,11 @@ declare const hasPermission: (required: string) => boolean;
14839
14839
  * Called on sign-out so no grant outlives the session, and before an
14840
14840
  * organization switch so the previous organization's grants cannot be read as
14841
14841
  * the new one's while the fresh answer is in flight.
14842
+ *
14843
+ * Emptying the set is not enough on its own: a resolution issued before the
14844
+ * clear can land after it and repopulate the store with the retired scope's
14845
+ * grants. Retiring the epoch here makes any such answer be discarded, so
14846
+ * sign-out cannot be undone by a request that was already in the air.
14842
14847
  */
14843
14848
  declare const clearPermissions: () => void;
14844
14849
  //#endregion
@@ -14848,7 +14853,8 @@ declare const clearPermissions: () => void;
14848
14853
  *
14849
14854
  * Call before switching the active organization: without it, a request issued
14850
14855
  * for the previous organization can land afterwards and repopulate the store
14851
- * with permissions the user no longer holds.
14856
+ * with permissions the user no longer holds. Sign-out needs no such call —
14857
+ * `clearPermissions` retires the scope itself.
14852
14858
  */
14853
14859
  declare const invalidatePermissions: () => void;
14854
14860
  /**
package/dist/index.js CHANGED
@@ -3370,6 +3370,25 @@ const matches = (held, required) => {
3370
3370
  return (SUPERSETS[verb] ?? []).some((superset) => held.has(resource + superset));
3371
3371
  };
3372
3372
 
3373
+ //#endregion
3374
+ //#region src/auth/permissions/epoch.ts
3375
+ /**
3376
+ * Counts the identity scopes that have come and gone — an organization switch,
3377
+ * a sign-out. A response that started under an earlier scope answers a question
3378
+ * nobody is asking any more and must not be written into the store.
3379
+ *
3380
+ * It lives here rather than in `./resolve` so `permissionStore` can retire a
3381
+ * scope from `clearPermissions` without importing the resolver, whose module
3382
+ * graph reaches back into `authStore`.
3383
+ */
3384
+ const state$1 = shared("permissionEpoch", () => ({ current: 0 }));
3385
+ /** The scope in effect right now. Compare against a value captured earlier. */
3386
+ const permissionEpoch = () => state$1.current;
3387
+ /** Retires the current scope, so answers already in flight for it are dropped. */
3388
+ const retirePermissionEpoch = () => {
3389
+ state$1.current += 1;
3390
+ };
3391
+
3373
3392
  //#endregion
3374
3393
  //#region src/auth/permissionStore.ts
3375
3394
  /**
@@ -3401,8 +3420,14 @@ const hasPermission = (required) => matches(permissions.get(), required);
3401
3420
  * Called on sign-out so no grant outlives the session, and before an
3402
3421
  * organization switch so the previous organization's grants cannot be read as
3403
3422
  * the new one's while the fresh answer is in flight.
3423
+ *
3424
+ * Emptying the set is not enough on its own: a resolution issued before the
3425
+ * clear can land after it and repopulate the store with the retired scope's
3426
+ * grants. Retiring the epoch here makes any such answer be discarded, so
3427
+ * sign-out cannot be undone by a request that was already in the air.
3404
3428
  */
3405
3429
  const clearPermissions = () => {
3430
+ retirePermissionEpoch();
3406
3431
  permissions.set(/* @__PURE__ */ new Set());
3407
3432
  permissionsResolved.set(false);
3408
3433
  };
@@ -4962,23 +4987,20 @@ const verifyPasskeyForLogin = async (session_id, credential) => {
4962
4987
 
4963
4988
  //#endregion
4964
4989
  //#region src/auth/permissions/resolve.ts
4965
- /**
4966
- * Incremented whenever the identity's scope changes — an organization switch,
4967
- * a sign-out. A response that started before the change belongs to the previous
4968
- * scope and must not be written into the store.
4969
- */
4970
- let epoch = 0;
4971
4990
  /** An `ensurePermissions` call already in flight, so concurrent guards share one request. */
4972
4991
  let pending = null;
4992
+ /** The scope `pending` was issued under; once retired, its answer is worthless. */
4993
+ let pendingEpoch = 0;
4973
4994
  /**
4974
4995
  * Invalidates any resolution already in flight.
4975
4996
  *
4976
4997
  * Call before switching the active organization: without it, a request issued
4977
4998
  * for the previous organization can land afterwards and repopulate the store
4978
- * with permissions the user no longer holds.
4999
+ * with permissions the user no longer holds. Sign-out needs no such call —
5000
+ * `clearPermissions` retires the scope itself.
4979
5001
  */
4980
5002
  const invalidatePermissions = () => {
4981
- epoch += 1;
5003
+ retirePermissionEpoch();
4982
5004
  pending = null;
4983
5005
  };
4984
5006
  /**
@@ -4996,9 +5018,9 @@ const invalidatePermissions = () => {
4996
5018
  * than keeps stale grants.
4997
5019
  */
4998
5020
  const resolvePermissions = async () => {
4999
- const started = epoch;
5021
+ const started = permissionEpoch();
5000
5022
  const info = await getUserInfo();
5001
- if (started !== epoch) return;
5023
+ if (started !== permissionEpoch()) return;
5002
5024
  permissions.set(new Set(info.permissions));
5003
5025
  permissionsResolved.set(true);
5004
5026
  };
@@ -5013,12 +5035,14 @@ const resolvePermissions = async () => {
5013
5035
  */
5014
5036
  const ensurePermissions = async () => {
5015
5037
  if (permissionsResolved.get()) return;
5016
- if (pending) return pending;
5017
- const started = epoch;
5018
- pending = resolvePermissions().finally(() => {
5019
- if (started === epoch) pending = null;
5038
+ const started = permissionEpoch();
5039
+ if (pending && pendingEpoch === started) return pending;
5040
+ pendingEpoch = started;
5041
+ const request = resolvePermissions().finally(() => {
5042
+ if (pending === request) pending = null;
5020
5043
  });
5021
- return pending;
5044
+ pending = request;
5045
+ return request;
5022
5046
  };
5023
5047
 
5024
5048
  //#endregion