@ionite/redis-core 0.0.23-beta.20260803.1 → 0.0.23-beta.20260809.2

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
@@ -25,6 +25,13 @@ interface RedisAdapter {
25
25
  srem(key: string, ...members: string[]): Promise<void>;
26
26
  smembers(key: string): Promise<string[]>;
27
27
  mget(keys: string[]): Promise<(string | null)[]>;
28
+ /** Optional Redis Pub/Sub publisher used by distributed LFV callback transports. */
29
+ publish?(channel: string, message: string): Promise<void>;
30
+ /**
31
+ * Optional Redis Pub/Sub subscription. Implementations MUST use a dedicated subscriber connection
32
+ * so normal store commands remain usable while subscribed.
33
+ */
34
+ subscribeChannel?(channel: string, listener: (message: string) => void): Promise<() => void | Promise<void>>;
28
35
  /**
29
36
  * Optional: atomically get a key's value and delete it (Redis `GETDEL`, 6.2+). When implemented,
30
37
  * the magic-link store uses it for a truly atomic single-use consume; otherwise it falls back to
@@ -224,6 +231,7 @@ declare class RedisAccessIndex implements AccessIndex {
224
231
  * available; otherwise a sequential fallback applying the same diff (documented: not isolated).
225
232
  */
226
233
  materialize(rootId: string, edges: EffectiveEdge[]): Promise<void>;
234
+ replaceSubject(subject: string, subjectType: SubjectType, edges: EffectiveEdge[]): Promise<void>;
227
235
  removeByRoot(rootId: string): Promise<void>;
228
236
  listSubjects(): Promise<SubjectRef[]>;
229
237
  setSubjectDisabled(subject: string, subjectType: SubjectType, disabled: boolean): Promise<void>;
@@ -373,6 +381,16 @@ declare function splitIonRedisSecret(secret: {
373
381
  redisUrl?: string;
374
382
  clientOptions: Record<string, unknown>;
375
383
  };
384
+ import { LfvCallbackTransport, Logger as Logger3 } from "@ionite/core";
385
+ type RedisLfvTransportOptions = {
386
+ channelPrefix?: string;
387
+ logger?: Logger3;
388
+ };
389
+ /**
390
+ * Distributes original signed LFV callback envelopes through Redis Pub/Sub. Redis is a transport,
391
+ * never a trust anchor: every receiving LFV source verifies the envelope against its own JWKS.
392
+ */
393
+ declare function createRedisLfvTransport(adapter: RedisAdapter, options?: RedisLfvTransportOptions): LfvCallbackTransport;
376
394
  /** Default page size applied to a paginated `list()` when the caller omits `limit` (§7.3). */
377
395
  declare const DEFAULT_LIST_LIMIT = 100;
378
396
  /**
@@ -392,7 +410,7 @@ declare class RedisMagicLinkStore<TExtended = object> implements MagicLinkStore<
392
410
  getAndDelete(token: string): Promise<MagicLink<TExtended> | null>;
393
411
  delete(token: string): Promise<void>;
394
412
  }
395
- import { Logger as Logger3, Secret, SecretsOperationOptions as SecretsOperationOptions2, SecretsSource, SecretsSubscriptionOptions as SecretsSubscriptionOptions2 } from "@ionite/core";
413
+ import { Logger as Logger4, Secret, SecretsOperationOptions as SecretsOperationOptions2, SecretsSource, SecretsSubscriptionOptions as SecretsSubscriptionOptions2 } from "@ionite/core";
396
414
  type MaybePromise<T> = Promise<T> | T;
397
415
  type ReactiveRedisClient = {
398
416
  adapter: RedisAdapter;
@@ -402,7 +420,7 @@ type ReactiveRedisOptions<TSecret> = {
402
420
  path: string;
403
421
  sourceName?: string;
404
422
  source: SecretsSource;
405
- logger?: Logger3;
423
+ logger?: Logger4;
406
424
  secretOptions?: SecretsOperationOptions2;
407
425
  subscription?: SecretsSubscriptionOptions2;
408
426
  createClient: (secret: Secret<TSecret>) => MaybePromise<ReactiveRedisClient>;
@@ -504,4 +522,4 @@ declare class RedisWorkloadTokenStore<TExtended = object> implements WorkloadTok
504
522
  isValid(workload_id: string): Promise<boolean>;
505
523
  cleanup(): Promise<void>;
506
524
  }
507
- export { supportsRunScript, splitIonRedisSecret, scriptSha1, prefixedKey, getLogger, getKeyPrefix, encodeKeyId, createReactiveRedis, createEvalshaRunner, chunkedMget, atomicCreate, atomicCompareAndSet, RedisWorkloadTokenStore, RedisWorkloadIdentityStoreOptions, RedisWorkloadIdentityStore, RedisUserStoreOptions, RedisUserStore, RedisTxn, RedisTenantStoreOptions, RedisTenantStore, RedisStoreOptions, RedisSessionStore, RedisResourceStore, RedisMagicLinkStore, RedisIamDeltaIdMapStoreOptions, RedisIamDeltaIdMapStore, RedisCustomerStoreOptions, RedisCustomerStore, RedisAdapter, RedisAccessIndex, RedisAccessCatalogStore, RedisAccessAssignmentStore, ReactiveRedisOptions, ReactiveRedisClient, ReactiveRedisAdapter, MAX_LIST_LIMIT, IonRedisOptions, DEFAULT_MGET_CHUNK_SIZE, DEFAULT_LIST_LIMIT, AtomicCreateResult, AtomicCompareAndSetResult };
525
+ export { supportsRunScript, splitIonRedisSecret, scriptSha1, prefixedKey, getLogger, getKeyPrefix, encodeKeyId, createRedisLfvTransport, createReactiveRedis, createEvalshaRunner, chunkedMget, atomicCreate, atomicCompareAndSet, RedisWorkloadTokenStore, RedisWorkloadIdentityStoreOptions, RedisWorkloadIdentityStore, RedisUserStoreOptions, RedisUserStore, RedisTxn, RedisTenantStoreOptions, RedisTenantStore, RedisStoreOptions, RedisSessionStore, RedisResourceStore, RedisMagicLinkStore, RedisLfvTransportOptions, RedisIamDeltaIdMapStoreOptions, RedisIamDeltaIdMapStore, RedisCustomerStoreOptions, RedisCustomerStore, RedisAdapter, RedisAccessIndex, RedisAccessCatalogStore, RedisAccessAssignmentStore, ReactiveRedisOptions, ReactiveRedisClient, ReactiveRedisAdapter, MAX_LIST_LIMIT, IonRedisOptions, DEFAULT_MGET_CHUNK_SIZE, DEFAULT_LIST_LIMIT, AtomicCreateResult, AtomicCompareAndSetResult };
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import{accessRefId as u,accessRootId as kZ,list as $Z}from"@ionite/core";function A(Z){return typeof Z.runScript==="function"}async function T(Z,$,Q,W){let Y=W?.ttlSeconds;if(A(Z)){let q=`local cur=redis.call('GET',KEYS[1])
1
+ import{accessRefId as c,accessRootId as bZ,list as QZ}from"@ionite/core";function S(Z){return typeof Z.runScript==="function"}async function v(Z,$,Q,Y){let W=Y?.ttlSeconds;if(S(Z)){let q=`local cur=redis.call('GET',KEYS[1])
2
2
  if cur then if cur==ARGV[1] then return 'exists_identical' else return 'conflict' end end
3
- ${Y!=null?"redis.call('SET',KEYS[1],ARGV[1],'EX',ARGV[2])":"redis.call('SET',KEYS[1],ARGV[1])"}
4
- return 'created'`,B=Y!=null?[Q,String(Math.max(1,Math.floor(Y)))]:[Q],J=String(await Z.runScript(q,[$],B));return J==="exists_identical"||J==="conflict"?J:"created"}let V=await Z.get($);if(V!=null)return V===Q?"exists_identical":"conflict";if(Y!=null)await Z.setex($,Math.max(1,Math.floor(Y)),Q);else await Z.set($,Q);return"created"}async function g(Z,$,Q,W,Y){if(!A(Z))return"unsupported";let V=Y!=null&&Y>0?Math.max(1,Math.floor(Y)):void 0,q=`local cur=redis.call('GET',KEYS[1])
3
+ ${W!=null?"redis.call('SET',KEYS[1],ARGV[1],'EX',ARGV[2])":"redis.call('SET',KEYS[1],ARGV[1])"}
4
+ return 'created'`,B=W!=null?[Q,String(Math.max(1,Math.floor(W)))]:[Q],U=String(await Z.runScript(q,[$],B));return U==="exists_identical"||U==="conflict"?U:"created"}let V=await Z.get($);if(V!=null)return V===Q?"exists_identical":"conflict";if(W!=null)await Z.setex($,Math.max(1,Math.floor(W)),Q);else await Z.set($,Q);return"created"}async function u(Z,$,Q,Y,W){if(!S(Z))return"unsupported";let V=W!=null&&W>0?Math.max(1,Math.floor(W)):void 0,q=`local cur=redis.call('GET',KEYS[1])
5
5
  if not cur then return 'missing' end
6
6
  if cur~=ARGV[1] then return 'retry' end
7
7
  ${V!=null?"redis.call('SET',KEYS[1],ARGV[2],'EX',ARGV[3])":"redis.call('SET',KEYS[1],ARGV[2],'KEEPTTL')"}
8
- return 'ok'`,B=V!=null?[Q,W,String(V)]:[Q,W],J=String(await Z.runScript(q,[$],B));if(J==="missing"||J==="retry")return J;return"ok"}var y=500,hZ=`local ki=1
8
+ return 'ok'`,B=V!=null?[Q,Y,String(V)]:[Q,Y],U=String(await Z.runScript(q,[$],B));if(U==="missing"||U==="retry")return U;return"ok"}var l=500,TZ=`local ki=1
9
9
  local ai=1
10
10
  local C=tonumber(ARGV[ai]);ai=ai+1
11
11
  for i=1,C do
@@ -43,7 +43,7 @@ for i=1,W do
43
43
  redis.call('SREM',k,unpack(members))
44
44
  end
45
45
  end
46
- return 'ok'`;class L{claims=[];writes=[];claimUnique(Z,$){return this.claims.push({key:Z,owner:$}),this}set(Z,$){return this.writes.push({op:"set",key:Z,value:$}),this}setex(Z,$,Q){return this.writes.push({op:"setex",key:Z,ttl:Math.max(1,Math.floor($)),value:Q}),this}del(Z){return this.writes.push({op:"del",key:Z}),this}sadd(Z,...$){if($.length>0)this.writes.push({op:"sadd",key:Z,members:$});return this}srem(Z,...$){if($.length>0)this.writes.push({op:"srem",key:Z,members:$});return this}async exec(Z){if(A(Z))return this.execLua(Z);return this.execSequential(Z)}async execLua(Z){if(this.claims.length>0||this.writes.length<=y)return o(Z,this.claims,this.writes);for(let $=0;$<this.writes.length;$+=y)await o(Z,[],this.writes.slice($,$+y));return"ok"}async execSequential(Z){for(let $ of this.claims){let Q=await Z.get($.key);if(Q!=null&&Q!==$.owner)return"conflict"}for(let $ of this.writes)switch($.op){case"set":await Z.set($.key,$.value);break;case"setex":await Z.setex($.key,$.ttl,$.value);break;case"del":await Z.del($.key);break;case"sadd":await Z.sadd($.key,...$.members);break;case"srem":await Z.srem($.key,...$.members);break}return"ok"}}async function o(Z,$,Q){let W=[],Y=[String($.length)];for(let G of $)W.push(G.key),Y.push(G.owner);Y.push(String(Q.length));for(let G of Q)switch(G.op){case"set":Y.push("set"),W.push(G.key),Y.push(G.value);break;case"setex":Y.push("setex"),W.push(G.key),Y.push(String(G.ttl),G.value);break;case"del":Y.push("del"),W.push(G.key);break;case"sadd":Y.push("sadd"),W.push(G.key),Y.push(String(G.members.length),...G.members);break;case"srem":Y.push("srem"),W.push(G.key),Y.push(String(G.members.length),...G.members);break}return String(await Z.runScript(hZ,W,Y))==="conflict"?"conflict":"ok"}import{list as jZ}from"@ionite/core";var r=100,s=1000;function R(Z){let $=Z?.limit,Q=$==null||!Number.isFinite($)||$<=0?r:Math.min(Math.floor($),s);return{...Z,limit:Q}}function _(Z,$,Q=(W,Y)=>W[Y]){let W=Math.max(0,$?.start??0),Y=$?.limit,V=$?.filter?.requestable,G=Z;if(V!==void 0)G=Z.filter((O)=>Boolean(Q(O,"requestable"))===V);let q=G.length,B=Y!=null?W+Y:void 0,J=G.slice(W,B);return jZ(q,J,W,Y)}import{defaultLogger as TZ}from"@ionite/core";function C(Z,$){let Q=$.log??TZ,W={"ion.operation":"redis.revive","ion.outcome":"corrupt",store:$.store,...$.key?{key:$.key}:{}},Y;try{Y=JSON.parse(Z)}catch{Q.warn("Redis store: discarded corrupt JSON payload (failed to parse)",W);return}if(Y===null||typeof Y!=="object"||Array.isArray(Y)){Q.warn("Redis store: discarded JSON payload that is not a record object",W);return}return Y}import{createHash as vZ}from"node:crypto";import{defaultLogger as wZ}from"@ionite/core";var IZ=4096,t=new Set,e=new Set;function ZZ(Z,$){Z.delete($),Z.add($);while(Z.size>IZ){let Q=Z.values().next().value;if(Q===void 0)break;Z.delete(Q)}}function F(Z,$){let Q=Z.keyPrefix;if(typeof Q!=="string"||Q.length===0)throw Error('Redis store keyPrefix is required and must be a non-empty string. Pass an explicit per-tenant prefix (e.g. "ion:t:<tenantId>:") so tenant keyspaces never collide.');if($){let W=`${$}\x00${Q}`;if(t.has(W)&&!e.has(W))D(Z).warn(`Redis ${$} constructed twice with the same keyPrefix "${Q}". Each tenant MUST use a distinct keyPrefix (e.g. "ion:t:<tenantId>:") so tenant keyspaces never collide; sharing a prefix across tenants lets them read/write each other's data.`),ZZ(e,W);ZZ(t,W)}return Q}function D(Z){return Z.logger??wZ}function X(Z,...$){return Z+$.join(":")}var mZ=/^[A-Za-z0-9_.@|-]{1,64}$/;function H(Z){if(mZ.test(Z))return`=${Z}`;return`#${vZ("sha256").update(Z,"utf8").digest("base64url").slice(0,22)}`}var bZ="access:ids",yZ="access:subject:",gZ="access:target:";function lZ(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString(),updatedAt:Z.updatedAt.toISOString()})}function l(Z,$){let Q=C(Z,{store:"access",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),updatedAt:new Date(Q.updatedAt)}}function QZ(Z){return{id:Z.id,subject:Z.subject,subjectType:Z.subjectType,targetType:Z.targetType,target:Z.target,resources:Z.resources,applyToApplication:Z.applyToApplication,provenance:Z.provenance,status:Z.status,metadata:Z.metadata}}function WZ(Z){let $=u(Z.subject),Q=u(Z.target);if(!$)throw Error("Access change subject must resolve to a non-empty id.");if(!Z.targetType)throw Error("Access change targetType is required.");if(!Q)throw Error("Access change target must resolve to a non-empty id.");return{id:Z.id??kZ(Z),subjectId:$,targetId:Q}}class YZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"access-assignment-store"),this.log=D(Z)}key(...Z){return X(this.prefix,...Z)}idsSetKey(){return this.key(bZ)}accessStorageKey(Z){return this.key("access",H(Z))}subjectIndexKey(Z,$){return this.key(`${yZ}${$??"user"}:${H(Z)}`)}targetIndexKey(Z,$){return this.key(gZ+Z,H($))}async grant(Z){let $=WZ(Z),Q=this.accessStorageKey($.id),W=await this.redis.get(Q),Y=W?l(W,this.log):void 0,V=new Date,G={...Z,id:$.id,status:Z.status??"active",subjectId:$.subjectId,targetId:$.targetId,createdAt:Y?.createdAt??V,updatedAt:V},q=new L;q.set(Q,lZ(G)),q.sadd(this.idsSetKey(),$.id),q.sadd(this.subjectIndexKey($.subjectId,Z.subjectType),$.id),q.sadd(this.targetIndexKey(Z.targetType,$.targetId),$.id),await q.exec(this.redis)}async revoke(Z){let $=WZ(Z);if(await this.redis.get(this.accessStorageKey($.id))==null)return;let W=new L;W.del(this.accessStorageKey($.id)),W.srem(this.idsSetKey(),$.id),W.srem(this.subjectIndexKey($.subjectId,Z.subjectType),$.id),W.srem(this.targetIndexKey(Z.targetType,$.targetId),$.id),await W.exec(this.redis)}async get(Z){let $=await this.redis.get(this.accessStorageKey(Z));if($==null)return;let Q=l($,this.log);return Q?QZ(Q):void 0}async list(Z){return this.listInMemory(R(Z))}async listByTarget(Z,$,Q){let W=await this.redis.smembers(this.targetIndexKey(Z,$.trim()));return this.listFromIds(W,Q)}async listBySubject(Z,$,Q){let W=u(Z);if(!W)return $Z(0,[],Q?.start??0,Q?.limit);let Y=await this.redis.smembers(this.subjectIndexKey(W,$));return this.listFromIds(Y,Q)}async listInMemory(Z){let $=await this.redis.smembers(this.idsSetKey());return this.listFromIds($,Z)}async listFromIds(Z,$){if(Z.length===0)return $Z(0,[],$?.start??0,$?.limit);let W=[...Z].sort().map((q)=>this.accessStorageKey(q)),V=(await this.redis.mget(W)).filter((q)=>q!=null).map((q)=>l(q,this.log)).filter((q)=>q!=null).filter((q)=>(q.status??"active")==="active"),G=_(V,$);return{...G,items:G.items.map(QZ)}}}import{readyCatalogEntry as uZ}from"@ionite/core";class GZ{groups;roles;permissions;listeners=new Set;constructor(Z){let $=Z.redis,Q=F(Z,"access-catalog-store"),W=D(Z),Y=(V)=>{for(let G of this.listeners)G(V)};this.groups=new I($,Q,"group",Y,W),this.roles=new I($,Q,"role",Y,W),this.permissions=new I($,Q,"permission",Y,W)}afterChange(Z){return this.listeners.add(Z),()=>{this.listeners.delete(Z)}}}class VZ{redis;prefix;log;listeners=new Set;entitlements;entitlement;constructor(Z){if(this.redis=Z.redis,this.prefix=F(Z,"resource-store"),this.log=D(Z),Z.deriveEntitlements){let{deriveEntitlements:$,resolveEntitlement:Q}=Z;if(!Q)throw Error("RedisResourceStore: resolveEntitlement must be provided with deriveEntitlements.");this.entitlements=(W)=>$(W),this.entitlement=(W)=>Q(W,(Y)=>this.get(Y))}}key(...Z){return X(this.prefix,"authz","resource",...Z)}entityKey(Z){return this.key(H(Z))}idsSetKey(){return this.key("ids")}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;let Q=C($,{store:"authz:resource",key:Z,log:this.log});return Q===void 0?void 0:Q}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return _([],Z);let W=(await this.redis.mget($.map((Y)=>this.entityKey(Y)))).filter((Y)=>Y!=null).map((Y)=>C(Y,{store:"authz:resource",log:this.log})).filter((Y)=>Y!==void 0).map((Y)=>Y);return _(W,Z)}async children(Z){return(await this.list()).items.filter((Q)=>{return(typeof Q.parent==="string"?Q.parent:Q.parent?.id)===Z})}changed(Z){return this.listeners.add(Z),()=>{this.listeners.delete(Z)}}async set(Z){await new L().set(this.entityKey(Z.id),JSON.stringify(Z)).sadd(this.idsSetKey(),Z.id).exec(this.redis),this.emitChange({id:Z.id})}async delete(Z){await new L().del(this.entityKey(Z)).srem(this.idsSetKey(),Z).exec(this.redis),this.emitChange({id:Z,deleted:!0})}emitChange(Z){for(let $ of this.listeners)$(Z)}}class I{redis;prefix;kind;emit;log;constructor(Z,$,Q,W,Y){this.redis=Z;this.prefix=$;this.kind=Q;this.emit=W;this.log=Y}key(...Z){return X(this.prefix,"authz",this.kind,...Z)}idsSetKey(){return this.key("ids")}entityKey(Z){return this.key(H(Z))}externalIdKey(Z){return this.key("byExternalId",H(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;let Q=C($,{store:`authz:${this.kind}`,key:Z,log:this.log});return Q===void 0?void 0:Q}async getByExternalId(Z){let $=await this.redis.get(this.externalIdKey(Z));if($==null)return;return this.get($)}set(Z){let $=(async()=>{let Q=await this.get(Z.id),W=new L;if(W.set(this.entityKey(Z.id),JSON.stringify(Z)),W.sadd(this.idsSetKey(),Z.id),Q?.externalId&&Q.externalId!==Z.externalId)W.del(this.externalIdKey(Q.externalId));if(Z.externalId)W.set(this.externalIdKey(Z.externalId),Z.id);await W.exec(this.redis)})();return $.then(()=>this.emit({op:"set",kind:this.kind,entity:Z}),(Q)=>{this.log?.warn("authz catalog store: background persist failed",{"ion.operation":`authz.catalog.${this.kind}.set`,"ion.outcome":"error",id:Z.id,error:Q instanceof Error?Q.message:String(Q)})}),uZ(Z,$)}async delete(Z){let $=await this.get(Z),Q=new L;if(Q.del(this.entityKey(Z)),Q.srem(this.idsSetKey(),Z),$?.externalId)Q.del(this.externalIdKey($.externalId));await Q.exec(this.redis),this.emit({op:"delete",kind:this.kind,id:Z})}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return _([],Z);let W=(await this.redis.mget($.map((Y)=>this.entityKey(Y)))).filter((Y)=>Y!=null).map((Y)=>C(Y,{store:`authz:${this.kind}`,log:this.log})).filter((Y)=>Y!==void 0).map((Y)=>Y);return _(W,Z)}}import{accessRefId as P}from"@ionite/core";var m="__app__",qZ=100;function pZ(Z){if(!Array.isArray(Z))return[];return Z.map(($)=>$==null?"":String($)).filter(($)=>$.length>0)}var cZ=`local rootId=ARGV[1]
46
+ return 'ok'`;class C{claims=[];writes=[];claimUnique(Z,$){return this.claims.push({key:Z,owner:$}),this}set(Z,$){return this.writes.push({op:"set",key:Z,value:$}),this}setex(Z,$,Q){return this.writes.push({op:"setex",key:Z,ttl:Math.max(1,Math.floor($)),value:Q}),this}del(Z){return this.writes.push({op:"del",key:Z}),this}sadd(Z,...$){if($.length>0)this.writes.push({op:"sadd",key:Z,members:$});return this}srem(Z,...$){if($.length>0)this.writes.push({op:"srem",key:Z,members:$});return this}async exec(Z){if(S(Z))return this.execLua(Z);return this.execSequential(Z)}async execLua(Z){if(this.claims.length>0||this.writes.length<=l)return s(Z,this.claims,this.writes);for(let $=0;$<this.writes.length;$+=l)await s(Z,[],this.writes.slice($,$+l));return"ok"}async execSequential(Z){for(let $ of this.claims){let Q=await Z.get($.key);if(Q!=null&&Q!==$.owner)return"conflict"}for(let $ of this.writes)switch($.op){case"set":await Z.set($.key,$.value);break;case"setex":await Z.setex($.key,$.ttl,$.value);break;case"del":await Z.del($.key);break;case"sadd":await Z.sadd($.key,...$.members);break;case"srem":await Z.srem($.key,...$.members);break}return"ok"}}async function s(Z,$,Q){let Y=[],W=[String($.length)];for(let G of $)Y.push(G.key),W.push(G.owner);W.push(String(Q.length));for(let G of Q)switch(G.op){case"set":W.push("set"),Y.push(G.key),W.push(G.value);break;case"setex":W.push("setex"),Y.push(G.key),W.push(String(G.ttl),G.value);break;case"del":W.push("del"),Y.push(G.key);break;case"sadd":W.push("sadd"),Y.push(G.key),W.push(String(G.members.length),...G.members);break;case"srem":W.push("srem"),Y.push(G.key),W.push(String(G.members.length),...G.members);break}return String(await Z.runScript(TZ,Y,W))==="conflict"?"conflict":"ok"}import{list as jZ}from"@ionite/core";var r=100,t=1000;function h(Z){let $=Z?.limit,Q=$==null||!Number.isFinite($)||$<=0?r:Math.min(Math.floor($),t);return{...Z,limit:Q}}function P(Z,$,Q=(Y,W)=>Y[W]){let Y=Math.max(0,$?.start??0),W=$?.limit,V=$?.filter?.requestable,G=Z;if(V!==void 0)G=Z.filter((X)=>Boolean(Q(X,"requestable"))===V);let q=G.length,B=W!=null?Y+W:void 0,U=G.slice(Y,B);return jZ(q,U,Y,W)}import{defaultLogger as wZ}from"@ionite/core";function N(Z,$){let Q=$.log??wZ,Y={"ion.operation":"redis.revive","ion.outcome":"corrupt",store:$.store,...$.key?{key:$.key}:{}},W;try{W=JSON.parse(Z)}catch{Q.warn("Redis store: discarded corrupt JSON payload (failed to parse)",Y);return}if(W===null||typeof W!=="object"||Array.isArray(W)){Q.warn("Redis store: discarded JSON payload that is not a record object",Y);return}return W}import{createHash as vZ}from"node:crypto";import{defaultLogger as IZ}from"@ionite/core";var mZ=4096,e=new Set,ZZ=new Set;function $Z(Z,$){Z.delete($),Z.add($);while(Z.size>mZ){let Q=Z.values().next().value;if(Q===void 0)break;Z.delete(Q)}}function F(Z,$){let Q=Z.keyPrefix;if(typeof Q!=="string"||Q.length===0)throw Error('Redis store keyPrefix is required and must be a non-empty string. Pass an explicit per-tenant prefix (e.g. "ion:t:<tenantId>:") so tenant keyspaces never collide.');if($){let Y=`${$}\x00${Q}`;if(e.has(Y)&&!ZZ.has(Y))_(Z).warn(`Redis ${$} constructed twice with the same keyPrefix "${Q}". Each tenant MUST use a distinct keyPrefix (e.g. "ion:t:<tenantId>:") so tenant keyspaces never collide; sharing a prefix across tenants lets them read/write each other's data.`),$Z(ZZ,Y);$Z(e,Y)}return Q}function _(Z){return Z.logger??IZ}function z(Z,...$){return Z+$.join(":")}var kZ=/^[A-Za-z0-9_.@|-]{1,64}$/;function J(Z){if(kZ.test(Z))return`=${Z}`;return`#${vZ("sha256").update(Z,"utf8").digest("base64url").slice(0,22)}`}var yZ="access:ids",gZ="access:subject:",lZ="access:target:";function uZ(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString(),updatedAt:Z.updatedAt.toISOString()})}function p(Z,$){let Q=N(Z,{store:"access",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),updatedAt:new Date(Q.updatedAt)}}function YZ(Z){return{id:Z.id,subject:Z.subject,subjectType:Z.subjectType,targetType:Z.targetType,target:Z.target,resources:Z.resources,applyToApplication:Z.applyToApplication,provenance:Z.provenance,status:Z.status,metadata:Z.metadata}}function WZ(Z){let $=c(Z.subject),Q=c(Z.target);if(!$)throw Error("Access change subject must resolve to a non-empty id.");if(!Z.targetType)throw Error("Access change targetType is required.");if(!Q)throw Error("Access change target must resolve to a non-empty id.");return{id:Z.id??bZ(Z),subjectId:$,targetId:Q}}class GZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"access-assignment-store"),this.log=_(Z)}key(...Z){return z(this.prefix,...Z)}idsSetKey(){return this.key(yZ)}accessStorageKey(Z){return this.key("access",J(Z))}subjectIndexKey(Z,$){return this.key(`${gZ}${$??"user"}:${J(Z)}`)}targetIndexKey(Z,$){return this.key(lZ+Z,J($))}async grant(Z){let $=WZ(Z),Q=this.accessStorageKey($.id),Y=await this.redis.get(Q),W=Y?p(Y,this.log):void 0,V=new Date,G={...Z,id:$.id,status:Z.status??"active",subjectId:$.subjectId,targetId:$.targetId,createdAt:W?.createdAt??V,updatedAt:V},q=new C;q.set(Q,uZ(G)),q.sadd(this.idsSetKey(),$.id),q.sadd(this.subjectIndexKey($.subjectId,Z.subjectType),$.id),q.sadd(this.targetIndexKey(Z.targetType,$.targetId),$.id),await q.exec(this.redis)}async revoke(Z){let $=WZ(Z);if(await this.redis.get(this.accessStorageKey($.id))==null)return;let Y=new C;Y.del(this.accessStorageKey($.id)),Y.srem(this.idsSetKey(),$.id),Y.srem(this.subjectIndexKey($.subjectId,Z.subjectType),$.id),Y.srem(this.targetIndexKey(Z.targetType,$.targetId),$.id),await Y.exec(this.redis)}async get(Z){let $=await this.redis.get(this.accessStorageKey(Z));if($==null)return;let Q=p($,this.log);return Q?YZ(Q):void 0}async list(Z){return this.listInMemory(h(Z))}async listByTarget(Z,$,Q){let Y=await this.redis.smembers(this.targetIndexKey(Z,$.trim()));return this.listFromIds(Y,Q)}async listBySubject(Z,$,Q){let Y=c(Z);if(!Y)return QZ(0,[],Q?.start??0,Q?.limit);let W=await this.redis.smembers(this.subjectIndexKey(Y,$));return this.listFromIds(W,Q)}async listInMemory(Z){let $=await this.redis.smembers(this.idsSetKey());return this.listFromIds($,Z)}async listFromIds(Z,$){if(Z.length===0)return QZ(0,[],$?.start??0,$?.limit);let Y=[...Z].sort().map((q)=>this.accessStorageKey(q)),V=(await this.redis.mget(Y)).filter((q)=>q!=null).map((q)=>p(q,this.log)).filter((q)=>q!=null).filter((q)=>(q.status??"active")==="active"),G=P(V,$);return{...G,items:G.items.map(YZ)}}}import{readyCatalogEntry as pZ}from"@ionite/core";class VZ{groups;roles;permissions;listeners=new Set;constructor(Z){let $=Z.redis,Q=F(Z,"access-catalog-store"),Y=_(Z),W=(V)=>{for(let G of this.listeners)G(V)};this.groups=new m($,Q,"group",W,Y),this.roles=new m($,Q,"role",W,Y),this.permissions=new m($,Q,"permission",W,Y)}afterChange(Z){return this.listeners.add(Z),()=>{this.listeners.delete(Z)}}}class qZ{redis;prefix;log;listeners=new Set;entitlements;entitlement;constructor(Z){if(this.redis=Z.redis,this.prefix=F(Z,"resource-store"),this.log=_(Z),Z.deriveEntitlements){let{deriveEntitlements:$,resolveEntitlement:Q}=Z;if(!Q)throw Error("RedisResourceStore: resolveEntitlement must be provided with deriveEntitlements.");this.entitlements=(Y)=>$(Y),this.entitlement=(Y)=>Q(Y,(W)=>this.get(W))}}key(...Z){return z(this.prefix,"authz","resource",...Z)}entityKey(Z){return this.key(J(Z))}idsSetKey(){return this.key("ids")}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;let Q=N($,{store:"authz:resource",key:Z,log:this.log});return Q===void 0?void 0:Q}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return P([],Z);let Y=(await this.redis.mget($.map((W)=>this.entityKey(W)))).filter((W)=>W!=null).map((W)=>N(W,{store:"authz:resource",log:this.log})).filter((W)=>W!==void 0).map((W)=>W);return P(Y,Z)}async children(Z){return(await this.list()).items.filter((Q)=>{return(typeof Q.parent==="string"?Q.parent:Q.parent?.id)===Z})}changed(Z){return this.listeners.add(Z),()=>{this.listeners.delete(Z)}}async set(Z){await new C().set(this.entityKey(Z.id),JSON.stringify(Z)).sadd(this.idsSetKey(),Z.id).exec(this.redis),this.emitChange({id:Z.id})}async delete(Z){await new C().del(this.entityKey(Z)).srem(this.idsSetKey(),Z).exec(this.redis),this.emitChange({id:Z,deleted:!0})}emitChange(Z){for(let $ of this.listeners)$(Z)}}class m{redis;prefix;kind;emit;log;constructor(Z,$,Q,Y,W){this.redis=Z;this.prefix=$;this.kind=Q;this.emit=Y;this.log=W}key(...Z){return z(this.prefix,"authz",this.kind,...Z)}idsSetKey(){return this.key("ids")}entityKey(Z){return this.key(J(Z))}externalIdKey(Z){return this.key("byExternalId",J(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;let Q=N($,{store:`authz:${this.kind}`,key:Z,log:this.log});return Q===void 0?void 0:Q}async getByExternalId(Z){let $=await this.redis.get(this.externalIdKey(Z));if($==null)return;return this.get($)}set(Z){let $=(async()=>{let Q=await this.get(Z.id),Y=new C;if(Y.set(this.entityKey(Z.id),JSON.stringify(Z)),Y.sadd(this.idsSetKey(),Z.id),Q?.externalId&&Q.externalId!==Z.externalId)Y.del(this.externalIdKey(Q.externalId));if(Z.externalId)Y.set(this.externalIdKey(Z.externalId),Z.id);await Y.exec(this.redis)})();return $.then(()=>this.emit({op:"set",kind:this.kind,entity:Z}),(Q)=>{this.log?.warn("authz catalog store: background persist failed",{"ion.operation":`authz.catalog.${this.kind}.set`,"ion.outcome":"error",id:Z.id,error:Q instanceof Error?Q.message:String(Q)})}),pZ(Z,$)}async delete(Z){let $=await this.get(Z),Q=new C;if(Q.del(this.entityKey(Z)),Q.srem(this.idsSetKey(),Z),$?.externalId)Q.del(this.externalIdKey($.externalId));await Q.exec(this.redis),this.emit({op:"delete",kind:this.kind,id:Z})}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return P([],Z);let Y=(await this.redis.mget($.map((W)=>this.entityKey(W)))).filter((W)=>W!=null).map((W)=>N(W,{store:`authz:${this.kind}`,log:this.log})).filter((W)=>W!==void 0).map((W)=>W);return P(Y,Z)}}import{accessRefId as A}from"@ionite/core";var b="__app__",BZ=100;function cZ(Z){if(!Array.isArray(Z))return[];return Z.map(($)=>$==null?"":String($)).filter(($)=>$.length>0)}var dZ=`local rootId=ARGV[1]
47
47
  local A=tonumber(ARGV[2])
48
48
  local R=tonumber(ARGV[3])
49
49
  local rootKey=KEYS[1]
@@ -92,7 +92,7 @@ for i=0,R-1 do
92
92
  end
93
93
  redis.call('SREM',rootKey,descriptor)
94
94
  end
95
- return 'ok'`,dZ=`local rootId=ARGV[1]
95
+ return 'ok'`,nZ=`local rootId=ARGV[1]
96
96
  local n=tonumber(ARGV[2])
97
97
  for i=0,n-1 do
98
98
  local kb=1+i*5
@@ -116,7 +116,7 @@ for i=0,n-1 do
116
116
  end
117
117
  end
118
118
  redis.call('DEL',KEYS[1])
119
- return 'ok'`,nZ=`local n=tonumber(ARGV[1])
119
+ return 'ok'`,iZ=`local n=tonumber(ARGV[1])
120
120
  local roots={}
121
121
  local seen={}
122
122
  for i=0,n-1 do
@@ -135,5 +135,38 @@ for i=0,n-1 do
135
135
  redis.call('DEL',ak)
136
136
  end
137
137
  redis.call('DEL',KEYS[1])
138
- return roots`,iZ=`for i=1,#KEYS do redis.call('DEL',KEYS[i]) end
139
- return 'ok'`;function S(Z,$){return`${$}:${Z}`}function p(Z){return Z&&Z.length>0?Z:m}function BZ(Z){return JSON.stringify([Z.subject,Z.targetType,Z.target,Z.rToken])}function v(Z){let $;try{$=JSON.parse(Z)}catch{$=[]}let[Q,W,Y,V]=Array.isArray($)?$:[];return{subject:Q??"",targetType:W??"permission",target:Y??"",rToken:V??m}}class UZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"access-index"),this.log=D(Z)}key(...Z){return X(this.prefix,...Z)}subjectKeyPart(Z){let $=Z.indexOf(":");if($<=0)return H(Z);return`${Z.slice(0,$)}:${H(Z.slice($+1))}`}hasKey(Z,$,Q,W){return this.key("idx:has",this.subjectKeyPart(Z),$,H(Q),H(W))}anyKey(Z,$,Q){return this.key("idx:any",this.subjectKeyPart(Z),$,H(Q))}edgeRootsKey(Z,$,Q,W){return this.key("idx:er",this.subjectKeyPart(Z),$,H(Q),H(W))}tripleKey(Z,$,Q){return this.key("idx:tr",this.subjectKeyPart(Z),$,H(Q))}rootKey(Z){return this.key("idx:root",H(Z))}subjectKey(Z){return this.key("idx:subj",this.subjectKeyPart(Z))}subjectsKey(){return this.key("idx:subjects")}disabledKey(Z){return this.key("idx:disabled",this.subjectKeyPart(Z))}async has(Z,$,Q,W,Y="user"){let V=P(Z),G=P(Q);if(!V||!G)return{allow:!1};let q=S(V,Y);if(W===void 0){let[O,N]=await this.redis.mget([this.disabledKey(q),this.anyKey(q,$,G)]);if(O!=null)return{allow:!1,metadata:{reason:"subject_disabled"}};return N!=null?this.allow(V,$,G,void 0):{allow:!1}}let B=P(W),J=await this.redis.mget([this.disabledKey(q),this.hasKey(q,$,G,p(B)),this.hasKey(q,$,G,m)]);if(J[0]!=null)return{allow:!1,metadata:{reason:"subject_disabled"}};if(J[1]!=null)return this.allow(V,$,G,B);if(J[2]!=null)return this.allow(V,$,G,void 0);return{allow:!1}}async hasGroup(Z,$,Q,W="user"){return this.has(Z,"group",$,Q,W)}async hasRole(Z,$,Q,W="user"){return this.has(Z,"role",$,Q,W)}async hasPermission(Z,$,Q,W="user"){return this.has(Z,"permission",$,Q,W)}async hasAny(Z,$,Q="user"){if($.length===0)return{allow:!1};this.warnLargeCheck("redis.access_index.has_any",$.length);let W=await this.evaluateTargets(Z,$,Q);for(let Y of W)if(Y.allow)return Y;return{allow:!1}}async hasAll(Z,$,Q="user"){if($.length===0){let V=P(Z);if(V){if(await this.redis.get(this.disabledKey(S(V,Q)))!=null)return{allow:!1,metadata:{reason:"subject_disabled"}}}return{allow:!0}}this.warnLargeCheck("redis.access_index.has_all",$.length);let W=await this.evaluateTargets(Z,$,Q),Y=[];for(let V of W){if(!V.allow)return{allow:!1,matched:Y};if(V.matched)Y.push(...V.matched)}return{allow:!0,matched:Y}}async evaluateTargets(Z,$,Q){let W=P(Z);if(!W)return $.map(()=>({allow:!1}));let Y=S(W,Q),V=[this.disabledKey(Y)],G=$.map((B)=>{let J=P(B.target);if(!J)return{kind:"invalid"};if(B.resource===void 0){let f=V.push(this.anyKey(Y,B.targetType,J))-1;return{kind:"any",targetType:B.targetType,targetId:J,idx:f}}let O=P(B.resource),N=V.push(this.hasKey(Y,B.targetType,J,p(O)))-1,E=V.push(this.hasKey(Y,B.targetType,J,m))-1;return{kind:"resource",targetType:B.targetType,targetId:J,resourceId:O,idxResource:N,idxApp:E}}),q=await this.redis.mget(V);if(q[0]!=null)return $.map(()=>({allow:!1,metadata:{reason:"subject_disabled"}}));return G.map((B)=>{if(B.kind==="invalid")return{allow:!1};if(B.kind==="any")return q[B.idx]!=null?this.allow(W,B.targetType,B.targetId,void 0):{allow:!1};if(q[B.idxResource]!=null)return this.allow(W,B.targetType,B.targetId,B.resourceId);if(q[B.idxApp]!=null)return this.allow(W,B.targetType,B.targetId,void 0);return{allow:!1}})}edgeKeys(Z){return[this.edgeRootsKey(Z.subject,Z.targetType,Z.target,Z.rToken),this.hasKey(Z.subject,Z.targetType,Z.target,Z.rToken),this.tripleKey(Z.subject,Z.targetType,Z.target),this.anyKey(Z.subject,Z.targetType,Z.target),this.subjectKey(Z.subject)]}async materialize(Z,$){let Q=new Map;for(let G of $){let q=P(G.subject),B=P(G.target);if(!q||!B)continue;let J=S(q,G.subjectType??"user"),O=p(G.resource),N={subject:J,targetType:G.targetType,target:B,rToken:O};Q.set(BZ(N),N)}let W=new Set(await this.redis.smembers(this.rootKey(Z))),Y=[];for(let[G,q]of Q)if(!W.has(G))Y.push({encoded:G,d:q});let V=[];for(let G of W)if(!Q.has(G))V.push({encoded:G,d:v(G)});if(Y.length===0&&V.length===0)return;if(A(this.redis)){let G=[this.rootKey(Z),this.subjectsKey()],q=[Z,String(Y.length),String(V.length)];for(let{encoded:B,d:J}of Y)G.push(...this.edgeKeys(J)),q.push(B,J.rToken,J.subject);for(let{encoded:B,d:J}of V)G.push(...this.edgeKeys(J)),q.push(B,J.rToken);await this.redis.runScript(cZ,G,q);return}for(let{encoded:G,d:q}of Y)await this.redis.sadd(this.edgeRootsKey(q.subject,q.targetType,q.target,q.rToken),Z),await this.redis.set(this.hasKey(q.subject,q.targetType,q.target,q.rToken),"1"),await this.redis.sadd(this.tripleKey(q.subject,q.targetType,q.target),q.rToken),await this.redis.set(this.anyKey(q.subject,q.targetType,q.target),"1"),await this.redis.sadd(this.rootKey(Z),G),await this.redis.sadd(this.subjectKey(q.subject),G),await this.redis.sadd(this.subjectsKey(),q.subject);for(let{encoded:G,d:q}of V)await this.dropRootFromEdge(Z,q.subject,q.targetType,q.target,q.rToken),await this.redis.srem(this.rootKey(Z),G)}async removeByRoot(Z){let $=this.rootKey(Z),Q=await this.redis.smembers($);if(Q.length===0){await this.redis.del($);return}if(A(this.redis)){let W=[$],Y=[Z,String(Q.length)];for(let V of Q){let G=v(V);W.push(...this.edgeKeys(G)),Y.push(V,G.rToken)}await this.redis.runScript(dZ,W,Y);return}for(let W of Q){let{subject:Y,targetType:V,target:G,rToken:q}=v(W);await this.dropRootFromEdge(Z,Y,V,G,q)}await this.redis.del($)}async listSubjects(){let Z=await this.redis.smembers(this.subjectsKey()),$=[];for(let Q of Z){let W=Q.indexOf(":");if(W<=0){$.push({subject:Q});continue}$.push({subjectType:Q.slice(0,W),subject:Q.slice(W+1)})}return $}async setSubjectDisabled(Z,$,Q){let W=P(Z);if(!W)return;let Y=this.disabledKey(S(W,$));if(Q)await this.redis.set(Y,"1");else await this.redis.del(Y)}async isSubjectDisabled(Z,$="user"){let Q=P(Z);if(!Q)return!1;return await this.redis.get(this.disabledKey(S(Q,$)))!=null}async clear(Z,$="user"){if(Z===void 0){let Y=await this.redis.smembers(this.subjectsKey());for(let V of Y)await this.clearSubject(V);await this.redis.del(this.subjectsKey());return}let Q=P(Z);if(!Q)return;let W=S(Q,$);await this.clearSubject(W),await this.redis.srem(this.subjectsKey(),W)}async clearSubject(Z){let $=this.subjectKey(Z),Q=await this.redis.smembers($);if(Q.length===0){await this.redis.del($);return}if(A(this.redis)){let W=[$];for(let V of Q){let{targetType:G,target:q,rToken:B}=v(V);W.push(this.edgeRootsKey(Z,G,q,B),this.hasKey(Z,G,q,B),this.tripleKey(Z,G,q),this.anyKey(Z,G,q))}let Y=pZ(await this.redis.runScript(nZ,W,[String(Q.length)]));if(Y.length>0)await this.redis.runScript(iZ,Y.map((V)=>this.rootKey(V)),[]);return}for(let W of Q){let{targetType:Y,target:V,rToken:G}=v(W),q=this.edgeRootsKey(Z,Y,V,G),B=await this.redis.smembers(q);for(let O of B)await this.redis.srem(this.rootKey(O),W);if(await this.redis.del(q),await this.redis.del(this.hasKey(Z,Y,V,G)),await this.redis.srem(this.tripleKey(Z,Y,V),G),(await this.redis.smembers(this.tripleKey(Z,Y,V))).length===0)await this.redis.del(this.tripleKey(Z,Y,V)),await this.redis.del(this.anyKey(Z,Y,V))}await this.redis.del($)}async dropRootFromEdge(Z,$,Q,W,Y){let V=this.edgeRootsKey($,Q,W,Y);if(await this.redis.srem(V,Z),(await this.redis.smembers(V)).length>0)return;await this.redis.del(V),await this.redis.del(this.hasKey($,Q,W,Y));let q=BZ({subject:$,targetType:Q,target:W,rToken:Y});if(await this.redis.srem(this.subjectKey($),q),await this.redis.srem(this.tripleKey($,Q,W),Y),(await this.redis.smembers(this.tripleKey($,Q,W))).length===0)await this.redis.del(this.tripleKey($,Q,W)),await this.redis.del(this.anyKey($,Q,W))}allow(Z,$,Q,W){return{allow:!0,matched:[{subject:Z,targetType:$,target:Q,resources:W?[W]:void 0}]}}warnLargeCheck(Z,$){if($<=qZ)return;this.log.warn("Redis access index check received a large bounded target set; issuing per-target reads.",{"ion.operation":Z,"ion.outcome":"degraded",count:$,threshold:qZ})}}var aZ=512;async function oZ(Z,$,Q=512){if(Z.length===0)return[];let W=Q>0?Q:512,Y=[];for(let V=0;V<Z.length;V+=W){let G=await $(Z.slice(V,V+W));for(let q of G)Y.push(q??null)}return Y}import{list as rZ,StoreConflictError as sZ}from"@ionite/core";var tZ="customer:ids",eZ="customer:name:";function Z$(Z){let $=Z.createdAt??new Date,Q=Z.updatedAt??$,W={...Z,createdAt:$.toISOString(),updatedAt:Q.toISOString()};if(W.ciam&&typeof W.ciam==="object"&&W.ciam!==null){let Y=W.ciam;if(Y.expires instanceof Date)Y.expires=Y.expires.toISOString()}return JSON.stringify(W)}function c(Z,$){let Q=C(Z,{store:"customer",log:$});if(Q===void 0)return;if(Q.ciam&&typeof Q.ciam==="object"&&Q.ciam!==null){let W=Q.ciam;if(typeof W.expires==="string")W.expires=new Date(W.expires)}return{...Q,createdAt:typeof Q.createdAt==="string"?new Date(Q.createdAt):void 0,updatedAt:typeof Q.updatedAt==="string"?new Date(Q.updatedAt):void 0}}class HZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"customer-store"),this.log=D(Z)}key(...Z){return X(this.prefix,...Z)}entityKey(Z){return this.key("customer",H(Z))}idsSetKey(){return this.key(tZ)}nameIndexKey(Z){return this.key(eZ+H(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return c($,this.log)}async lookup(Z){if(Z.id){let $=await this.get(Z.id);if($)return $}if(Z.userName){let $=await this.redis.get(this.nameIndexKey(Z.userName.toLowerCase()));if($!=null)return this.get($)}return}async upsert(Z){let $=Z.id?.trim()||crypto.randomUUID(),Q={...Z,id:$},W=this.entityKey($),Y=await this.redis.get(W),V=Y!=null?c(Y,this.log)??null:null,G=Q.userName?.toLowerCase(),q=V?.userName?.toLowerCase(),B=new L;if(G)B.claimUnique(this.nameIndexKey(G),$);if(q&&q!==G)B.del(this.nameIndexKey(q));if(B.set(W,Z$(Q)),G)B.set(this.nameIndexKey(G),$);if(B.sadd(this.idsSetKey(),$),await B.exec(this.redis)==="conflict")throw new sZ(`userName "${Q.userName}" is already in use`,"unique_violation","userName");return Q}async delete(Z){let $=await this.get(Z);if($==null)return 0;let Q=new L;if(Q.del(this.entityKey(Z)),$.userName)Q.del(this.nameIndexKey($.userName.toLowerCase()));return Q.srem(this.idsSetKey(),Z),await Q.exec(this.redis),1}async list(Z){let $=R(Z),Q=(await this.redis.smembers(this.idsSetKey())).sort();if(Q.length===0)return rZ(0,[],$.start??0,$.limit);let W=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(W)).filter((G)=>G!=null).map((G)=>c(G,this.log)).filter((G)=>G!=null);return _(V,$)}}import{createHash as $$}from"node:crypto";function JZ(Z){return $$("sha1").update(Z,"utf8").digest("hex")}function Q$(Z){return(Z instanceof Error?Z.message:String(Z)).includes("NOSCRIPT")}function W$(Z){let $=new Map;return async(Q,W,Y)=>{let V=$.get(Q);if(!V)V=JZ(Q),$.set(Q,V);try{return await Z.evalsha(V,W,Y)}catch(G){if(Q$(G))return Z.eval(Q,W,Y);throw G}}}var Y$=7776000;class MZ{redis;prefix;ttlSeconds;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"iam-delta-id-map-store");let $=Z.idMapTtlSeconds;this.ttlSeconds=$==null?Y$:Math.max(0,Math.floor($))}key(Z){return X(this.prefix,"iam","delta","idmap",H(Z.tenantId??"_"),H(Z.resourceType),H(Z.destination),H(Z.externalId))}async get(Z){return await this.redis.get(this.key(Z))??void 0}async set(Z,$){if(this.ttlSeconds>0)await this.redis.setex(this.key(Z),this.ttlSeconds,$);else await this.redis.set(this.key(Z),$)}async delete(Z){await this.redis.del(this.key(Z))}}function FZ(Z){return typeof Z==="object"&&Z!==null&&!Array.isArray(Z)}function G$(Z){let $=FZ(Z.clientOptions)?{...Z.clientOptions}:FZ(Z)?{...Z}:{};return delete $.redisUrl,delete $.clientOptions,{redisUrl:(typeof Z.redisUrl==="string"?Z.redisUrl.trim():"")||void 0,clientOptions:$}}import{StoreConflictError as V$}from"@ionite/core";function q$(Z){return JSON.stringify({token:Z.token,customer:Z.customer,createdAt:Z.createdAt.toISOString(),expiresAt:Z.expiresAt.toISOString()})}function XZ(Z,$){let Q=C(Z,{store:"magic-link",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),expiresAt:new Date(Q.expiresAt)}}class OZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"magic-link-store"),this.log=D(Z)}key(Z){return X(this.prefix,"magic",H(Z))}async create(Z,$,Q){let W=this.key(Z),Y={token:Z,customer:$,createdAt:new Date,expiresAt:Q},V=Math.max(1,Math.floor((Q.getTime()-Date.now())/1000));if(await T(this.redis,W,q$(Y),{ttlSeconds:V})==="conflict")throw new V$(`Magic link with token ${Z} already exists`,"already_exists","token")}async get(Z){let $=await this.redis.get(this.key(Z));if($==null)return null;return XZ($,this.log)??null}async getAndDelete(Z){let $=this.key(Z),Q;if(typeof this.redis.getdel==="function")Q=await this.redis.getdel($);else if(Q=await this.redis.get($),Q!=null)await this.redis.del($);if(Q==null)return null;let W=XZ(Q,this.log);if(W==null)return null;if(Date.now()>W.expiresAt.getTime())return null;return W}async delete(Z){await this.redis.del(this.key(Z))}}import{defaultLogger as B$}from"@ionite/core";function U$(Z,$){if(typeof $!=="number"||$<=0)return Z;return new Promise((Q,W)=>{let Y=setTimeout(()=>{W(Error(`Reactive Redis adapter did not become ready within ${$}ms`))},$);Z.then(()=>{clearTimeout(Y),Q()},(V)=>{clearTimeout(Y),W(V)})})}function H$(){let Z,$;return{promise:new Promise((W,Y)=>{Z=W,$=Y}),resolve:Z,reject:$}}function zZ(Z){return typeof Z==="function"}function J$(Z){let $=Z.logger??B$,Q=Z.sourceName?.trim()||"redis",W=Z.path.trim(),Y,V,G=!1,q,B,J,O=!1,N=!1,E=!1,f=Promise.resolve(),x=H$();x.promise.catch(()=>{});let j={async get(U){return K((M)=>M.get(U))},async set(U,M){await K((z)=>z.set(U,M))},async setex(U,M,z){await K((h)=>h.setex(U,M,z))},async del(U){await K((M)=>M.del(U))},async sadd(U,...M){await K((z)=>z.sadd(U,...M))},async srem(U,...M){await K((z)=>z.srem(U,...M))},async smembers(U){return K((M)=>M.smembers(U))},async mget(U){return K((M)=>M.mget(U))},ready(U){return U$(x.promise,U)},isReady(){return Y!==void 0},async close(){if(O=!0,a(),!G)G=!0,x.reject(Error(`Reactive Redis adapter for source "${Q}" at path "${W}" was closed before becoming ready.`));await f.catch(()=>{}),await k(Y),Y=void 0,b()}};function i(U){f=f.then(U).catch((M)=>{if(!G)G=!0,x.reject(M);$.warn("Reactive Redis adapter update failed",{"ion.operation":"redis.adapter.update","ion.outcome":"degraded",sourceName:Q,path:W,error:M instanceof Error?M.message:String(M)})})}async function SZ(){if(O)throw Error(`Reactive Redis adapter for source "${Q}" at path "${W}" is closed.`);let U=Y?.adapter;if(U)return U;if(await f.catch(()=>{}),O)throw Error(`Reactive Redis adapter for source "${Q}" at path "${W}" is closed.`);let M=Y?.adapter;if(M)return M;if(await x.promise,O)throw Error(`Reactive Redis adapter for source "${Q}" at path "${W}" is closed.`);let z=Y?.adapter;if(!z)throw Error(`Reactive Redis adapter for source "${Q}" at path "${W}" did not become ready.`);return z}async function K(U){let M=await SZ();return U(M)}function a(){V?.(),V=void 0}async function fZ(U){a(),V=await U.subscribe(W,(M)=>{i(async()=>{if(O)return;await xZ(M)})},Z.subscription??Z.secretOptions)}async function xZ(U){if(O)return;let M=`${Q}:${W}:${U.metadata.version}`;if(Y!==void 0&&B===M&&q===U.metadata.version)return;let z=Z.endpointOf?.(U);if(Y!==void 0&&z!=null&&z!==""&&J!=null&&J!==""&&z!==J)throw Error(`Reactive Redis adapter for source "${Q}" at path "${W}" rejected a rotation that changed the endpoint (${J} -> ${z}); only credentials may rotate under an active adapter.`);let h=await Z.createClient(U);if(O){await k(h);return}let w=Y;if(Y=h,B=M,q=U.metadata.version,z!=null&&z!=="")J=z;if(b(),!G)G=!0,x.resolve();await k(w)}async function k(U){if(!U?.dispose)return;await U.dispose()}function b(){let U=Y?.adapter;if(U?.runScript&&zZ(U.runScript))j.runScript=async(M,z,h)=>{return K(async(w)=>{if(!w.runScript)throw Error("Active Redis adapter does not support runScript");return w.runScript(M,z,h)})};else if(delete j.runScript,U!==void 0&&!N)N=!0,$.warn("Active Redis adapter does not support runScript (EVAL); stores fall back to non-isolated writes.",{"ion.operation":"redis.adapter.capabilities","ion.outcome":"degraded",sourceName:Q,path:W,capability:"runScript"});if(U?.getdel&&zZ(U.getdel))j.getdel=async(M)=>{return K(async(z)=>{if(!z.getdel)return null;return z.getdel(M)})};else if(delete j.getdel,U!==void 0&&!E)E=!0,$.warn("Active Redis adapter does not support getdel (GETDEL); single-use consume falls back to get+del.",{"ion.operation":"redis.adapter.capabilities","ion.outcome":"degraded",sourceName:Q,path:W,capability:"getdel"})}return b(),i(async()=>{if(O)return;await fZ(Z.source)}),j}import{StoreConflictError as M$}from"@ionite/core";var F$=5;function X$(Z,$){let Q;try{Q=JSON.parse(Z)}catch{return}if(Q==null||typeof Q!=="object"||Array.isArray(Q))return;return JSON.stringify({...Q,...$})}function DZ(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString()})}function LZ(Z,$){let Q=C(Z,{store:"session",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt)}}function CZ(Z){if(!Z)return;let $=Z instanceof Date?Z.getTime():new Date(Z).getTime();if(Number.isNaN($))return;let Q=Math.ceil(($-Date.now())/1000);return Q>0?Q:1}class _Z{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"session-store"),this.log=D(Z)}key(Z){return X(this.prefix,"session",H(Z))}async create(Z){let $=this.key(Z.sid),Q=CZ(Z.expiresAt);if(await T(this.redis,$,DZ(Z),{ttlSeconds:Q})==="conflict")throw new M$(`Session with sid ${Z.sid} already exists`,"already_exists","sid")}async get(Z){let $=await this.redis.get(this.key(Z));if($==null)return null;return LZ($,this.log)??null}async update(Z,$){let Q=this.key(Z),W=JSON.parse(JSON.stringify($)),Y="expiresAt"in W?CZ(W.expiresAt):void 0;if(A(this.redis))for(let J=0;J<F$;J++){let O=await this.redis.get(Q);if(O==null)throw Error(`Session with sid ${Z} not found`);let N=X$(O,W);if(N===void 0)throw Error(`Session with sid ${Z} not found`);let E=await g(this.redis,Q,O,N,Y);if(E==="ok")return;if(E==="missing")throw Error(`Session with sid ${Z} not found`)}let V=await this.redis.get(Q);if(V==null)throw Error(`Session with sid ${Z} not found`);let G=LZ(V,this.log);if(G==null)throw Error(`Session with sid ${Z} not found`);let q={...G,...$},B=DZ(q);if(Y!=null&&Y>0)await this.redis.setex(Q,Math.max(1,Math.floor(Y)),B);else await this.redis.set(Q,B)}async delete(Z){await this.redis.del(this.key(Z))}}var O$="tenant:ids";function z$(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString(),updatedAt:Z.updatedAt.toISOString()})}function NZ(Z,$){let Q=C(Z,{store:"tenant",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),updatedAt:new Date(Q.updatedAt)}}class PZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"tenant-store"),this.log=D(Z)}key(...Z){return X(this.prefix,...Z)}entityKey(Z){return this.key("tenant",H(Z))}idsSetKey(){return this.key(O$)}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return NZ($,this.log)}async upsert(Z){return await new L().set(this.entityKey(Z.id),z$(Z)).sadd(this.idsSetKey(),Z.id).exec(this.redis),Z}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return _([],Z);let Q=$.map((V)=>this.entityKey(V)),Y=(await this.redis.mget(Q)).map((V)=>V!=null?NZ(V,this.log):void 0).filter((V)=>V!=null);return _(Y,Z)}async delete(Z){let $=await this.get(Z);return await new L().srem(this.idsSetKey(),Z).del(this.entityKey(Z)).exec(this.redis),$?1:0}}class AZ{store;ionCache;constructor(Z){this.ionCache=Z.ionCache,this.store=new PZ(Z)}async get(Z){return this.store.get(Z)}async upsert(Z){let $=await this.store.upsert(Z);return this.ionCache?.delete(Z.id),$}async list(Z){return this.store.list(Z)}async delete(Z){let $=await this.store.delete(Z);return this.ionCache?.delete(Z),$}async getIon(Z){if(!this.ionCache)throw Error(`${this.constructor.name}.getIon() requires an app-owned ionCache. Pass { ionCache } (an lru-cache instance whose fetchMethod builds the tenant Ionite and whose dispose `+"closes it) — the SDK does not cache tenant runtimes. See the multi-tenant docs.");return this.ionCache.fetch(Z)}}import{list as D$,StoreConflictError as L$}from"@ionite/core";var C$="user:ids",_$="user:name:";function N$(Z){let $=Z.createdAt??new Date,Q=Z.updatedAt??$,W={...Z,createdAt:$.toISOString(),updatedAt:Q.toISOString()};if(W.sso&&typeof W.sso==="object"&&W.sso!==null){let Y=W.sso;if(Y.expires instanceof Date)Y.expires=Y.expires.toISOString()}return JSON.stringify(W)}function d(Z,$){let Q=C(Z,{store:"user",log:$});if(Q===void 0)return;if(Q.sso&&typeof Q.sso==="object"&&Q.sso!==null){let W=Q.sso;if(typeof W.expires==="string")W.expires=new Date(W.expires)}return{...Q,createdAt:typeof Q.createdAt==="string"?new Date(Q.createdAt):void 0,updatedAt:typeof Q.updatedAt==="string"?new Date(Q.updatedAt):void 0}}class KZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"user-store"),this.log=D(Z)}key(...Z){return X(this.prefix,...Z)}entityKey(Z){return this.key("user",H(Z))}idsSetKey(){return this.key(C$)}nameIndexKey(Z){return this.key(_$+H(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return d($,this.log)}async lookup(Z){return this.get(Z.id??"")}async upsert(Z){let $=Z.id?.trim()||crypto.randomUUID(),Q={...Z,id:$},W=this.entityKey($),Y=await this.redis.get(W),V=Y!=null?d(Y,this.log)??null:null,G=Q.userName?.toLowerCase(),q=V?.userName?.toLowerCase(),B=new L;if(G)B.claimUnique(this.nameIndexKey(G),$);if(q&&q!==G)B.del(this.nameIndexKey(q));if(B.set(W,N$(Q)),G)B.set(this.nameIndexKey(G),$);if(B.sadd(this.idsSetKey(),$),await B.exec(this.redis)==="conflict")throw new L$(`userName "${Q.userName}" is already in use`,"unique_violation","userName");return Q}async delete(Z){let $=await this.get(Z);if($==null)return 0;let Q=new L;if(Q.del(this.entityKey(Z)),$.userName)Q.del(this.nameIndexKey($.userName.toLowerCase()));return Q.srem(this.idsSetKey(),Z),await Q.exec(this.redis),1}async list(Z){let $=R(Z),Q=(await this.redis.smembers(this.idsSetKey())).sort();if(Q.length===0)return D$(0,[],$.start??0,$.limit);let W=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(W)).filter((G)=>G!=null).map((G)=>d(G,this.log)).filter((G)=>G!=null);return _(V,$)}}import{list as P$,StoreConflictError as A$}from"@ionite/core";var K$="workload-identity:ids",R$="workload-identity:client:";function E$(Z){let $=Z.createdAt??new Date,Q=Z.updatedAt??$;return JSON.stringify({...Z,createdAt:$.toISOString(),updatedAt:Q.toISOString()})}function n(Z,$){let Q=C(Z,{store:"workload-identity",log:$});if(Q===void 0)return;return{...Q,createdAt:typeof Q.createdAt==="string"?new Date(Q.createdAt):void 0,updatedAt:typeof Q.updatedAt==="string"?new Date(Q.updatedAt):void 0}}class RZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"workload-identity-store"),this.log=D(Z)}key(...Z){return X(this.prefix,...Z)}entityKey(Z){return this.key("workload-identity",H(Z))}idsSetKey(){return this.key(K$)}clientIndexKey(Z){return this.key(R$+H(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return n($,this.log)}async lookup(Z){if(Z.id){let $=await this.get(Z.id);if($)return $}if(Z.clientId){let $=await this.redis.get(this.clientIndexKey(Z.clientId));if($!=null)return this.get($)}return}async upsert(Z){let $=Z.id?.trim()||crypto.randomUUID(),Q=this.entityKey($),W=await this.redis.get(Q),Y=null;if(W!=null)Y=n(W,this.log)??null;let V=new Date,G={...Z,id:$,createdAt:Y?.createdAt??Z.createdAt??V,updatedAt:V},q=new L;if(G.clientId)q.claimUnique(this.clientIndexKey(G.clientId),$);if(Y?.clientId&&Y.clientId!==G.clientId)q.del(this.clientIndexKey(Y.clientId));if(q.set(Q,E$(G)),G.clientId)q.set(this.clientIndexKey(G.clientId),$);if(q.sadd(this.idsSetKey(),$),await q.exec(this.redis)==="conflict")throw new A$(`clientId "${G.clientId}" is already in use`,"unique_violation","clientId");return G}async delete(Z){let $=await this.get(Z);if($==null)return 0;let Q=new L;if(Q.del(this.entityKey(Z)),$.clientId)Q.del(this.clientIndexKey($.clientId));return Q.srem(this.idsSetKey(),Z),await Q.exec(this.redis),1}async list(Z){let $=R(Z),Q=(await this.redis.smembers(this.idsSetKey())).sort();if(Q.length===0)return P$(0,[],$.start??0,$.limit);let W=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(W)).filter((G)=>G!=null).map((G)=>n(G,this.log)).filter((G)=>G!=null);return _(V,$)}}function S$(Z){return JSON.stringify({...Z,expires_at:Z.expires_at.toISOString(),created_at:Z.created_at.toISOString()})}function f$(Z,$){let Q=C(Z,{store:"workload-token",log:$});if(Q===void 0)return;return{...Q,expires_at:new Date(Q.expires_at),created_at:new Date(Q.created_at)}}class EZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"workload-token-store"),this.log=D(Z)}key(Z){return X(this.prefix,"workload","token",H(Z))}async set(Z){let $=this.key(Z.workload_id),Q=Math.max(1,Math.floor((Z.expires_at.getTime()-Date.now())/1000));await this.redis.setex($,Q,S$(Z))}async get(Z){let $=await this.redis.get(this.key(Z));if($==null)return null;let Q=f$($,this.log);if(Q==null)return null;if(Date.now()>Q.expires_at.getTime())return await this.redis.del(this.key(Z)),null;return Q}async delete(Z){await this.redis.del(this.key(Z))}async isValid(Z){return await this.get(Z)!==null}async cleanup(){}}export{A as supportsRunScript,G$ as splitIonRedisSecret,JZ as scriptSha1,X as prefixedKey,D as getLogger,F as getKeyPrefix,H as encodeKeyId,J$ as createReactiveRedis,W$ as createEvalshaRunner,oZ as chunkedMget,T as atomicCreate,g as atomicCompareAndSet,EZ as RedisWorkloadTokenStore,RZ as RedisWorkloadIdentityStore,KZ as RedisUserStore,L as RedisTxn,AZ as RedisTenantStore,_Z as RedisSessionStore,VZ as RedisResourceStore,OZ as RedisMagicLinkStore,MZ as RedisIamDeltaIdMapStore,HZ as RedisCustomerStore,UZ as RedisAccessIndex,GZ as RedisAccessCatalogStore,YZ as RedisAccessAssignmentStore,s as MAX_LIST_LIMIT,aZ as DEFAULT_MGET_CHUNK_SIZE,r as DEFAULT_LIST_LIMIT};
138
+ return roots`,aZ=`for i=1,#KEYS do redis.call('DEL',KEYS[i]) end
139
+ return 'ok'`,oZ=`local subjectMember=ARGV[1]
140
+ local C=tonumber(ARGV[2])
141
+ local R=tonumber(ARGV[3])
142
+ local D=tonumber(ARGV[4])
143
+ for i=0,C-1 do
144
+ local kb=2+i*4
145
+ redis.call('DEL',KEYS[kb+1])
146
+ redis.call('DEL',KEYS[kb+2])
147
+ redis.call('DEL',KEYS[kb+3])
148
+ redis.call('DEL',KEYS[kb+4])
149
+ end
150
+ local rootsStart=2+C*4
151
+ for i=1,R do redis.call('DEL',KEYS[rootsStart+i]) end
152
+ redis.call('DEL',KEYS[1])
153
+ local desiredStart=rootsStart+R
154
+ for i=0,D-1 do
155
+ local kb=desiredStart+i*5
156
+ local ab=4+i*3
157
+ local rootId=ARGV[ab+1]
158
+ local descriptor=ARGV[ab+2]
159
+ local rToken=ARGV[ab+3]
160
+ redis.call('SADD',KEYS[kb+2],rootId)
161
+ redis.call('SET',KEYS[kb+3],'1')
162
+ redis.call('SADD',KEYS[kb+4],rToken)
163
+ redis.call('SET',KEYS[kb+5],'1')
164
+ redis.call('SADD',KEYS[kb+1],descriptor)
165
+ redis.call('SADD',KEYS[1],descriptor)
166
+ end
167
+ if D==0 then
168
+ redis.call('SREM',KEYS[2],subjectMember)
169
+ else
170
+ redis.call('SADD',KEYS[2],subjectMember)
171
+ end
172
+ return 'ok'`;function T(Z,$){return`${$}:${Z}`}function k(Z){return Z&&Z.length>0?Z:b}function d(Z){return JSON.stringify([Z.subject,Z.targetType,Z.target,Z.rToken])}function w(Z){let $;try{$=JSON.parse(Z)}catch{$=[]}let[Q,Y,W,V]=Array.isArray($)?$:[];return{subject:Q??"",targetType:Y??"permission",target:W??"",rToken:V??b}}class UZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"access-index"),this.log=_(Z)}key(...Z){return z(this.prefix,...Z)}subjectKeyPart(Z){let $=Z.indexOf(":");if($<=0)return J(Z);return`${Z.slice(0,$)}:${J(Z.slice($+1))}`}hasKey(Z,$,Q,Y){return this.key("idx:has",this.subjectKeyPart(Z),$,J(Q),J(Y))}anyKey(Z,$,Q){return this.key("idx:any",this.subjectKeyPart(Z),$,J(Q))}edgeRootsKey(Z,$,Q,Y){return this.key("idx:er",this.subjectKeyPart(Z),$,J(Q),J(Y))}tripleKey(Z,$,Q){return this.key("idx:tr",this.subjectKeyPart(Z),$,J(Q))}rootKey(Z){return this.key("idx:root",J(Z))}subjectKey(Z){return this.key("idx:subj",this.subjectKeyPart(Z))}subjectsKey(){return this.key("idx:subjects")}disabledKey(Z){return this.key("idx:disabled",this.subjectKeyPart(Z))}async has(Z,$,Q,Y,W="user"){let V=A(Z),G=A(Q);if(!V||!G)return{allow:!1};let q=T(V,W);if(Y===void 0){let[X,D]=await this.redis.mget([this.disabledKey(q),this.anyKey(q,$,G)]);if(X!=null)return{allow:!1,metadata:{reason:"subject_disabled"}};return D!=null?this.allow(V,$,G,void 0):{allow:!1}}let B=A(Y),U=await this.redis.mget([this.disabledKey(q),this.hasKey(q,$,G,k(B)),this.hasKey(q,$,G,b)]);if(U[0]!=null)return{allow:!1,metadata:{reason:"subject_disabled"}};if(U[1]!=null)return this.allow(V,$,G,B);if(U[2]!=null)return this.allow(V,$,G,void 0);return{allow:!1}}async hasGroup(Z,$,Q,Y="user"){return this.has(Z,"group",$,Q,Y)}async hasRole(Z,$,Q,Y="user"){return this.has(Z,"role",$,Q,Y)}async hasPermission(Z,$,Q,Y="user"){return this.has(Z,"permission",$,Q,Y)}async hasAny(Z,$,Q="user"){if($.length===0)return{allow:!1};this.warnLargeCheck("redis.access_index.has_any",$.length);let Y=await this.evaluateTargets(Z,$,Q);for(let W of Y)if(W.allow)return W;return{allow:!1}}async hasAll(Z,$,Q="user"){if($.length===0){let V=A(Z);if(V){if(await this.redis.get(this.disabledKey(T(V,Q)))!=null)return{allow:!1,metadata:{reason:"subject_disabled"}}}return{allow:!0}}this.warnLargeCheck("redis.access_index.has_all",$.length);let Y=await this.evaluateTargets(Z,$,Q),W=[];for(let V of Y){if(!V.allow)return{allow:!1,matched:W};if(V.matched)W.push(...V.matched)}return{allow:!0,matched:W}}async evaluateTargets(Z,$,Q){let Y=A(Z);if(!Y)return $.map(()=>({allow:!1}));let W=T(Y,Q),V=[this.disabledKey(W)],G=$.map((B)=>{let U=A(B.target);if(!U)return{kind:"invalid"};if(B.resource===void 0){let K=V.push(this.anyKey(W,B.targetType,U))-1;return{kind:"any",targetType:B.targetType,targetId:U,idx:K}}let X=A(B.resource),D=V.push(this.hasKey(W,B.targetType,U,k(X)))-1,O=V.push(this.hasKey(W,B.targetType,U,b))-1;return{kind:"resource",targetType:B.targetType,targetId:U,resourceId:X,idxResource:D,idxApp:O}}),q=await this.redis.mget(V);if(q[0]!=null)return $.map(()=>({allow:!1,metadata:{reason:"subject_disabled"}}));return G.map((B)=>{if(B.kind==="invalid")return{allow:!1};if(B.kind==="any")return q[B.idx]!=null?this.allow(Y,B.targetType,B.targetId,void 0):{allow:!1};if(q[B.idxResource]!=null)return this.allow(Y,B.targetType,B.targetId,B.resourceId);if(q[B.idxApp]!=null)return this.allow(Y,B.targetType,B.targetId,void 0);return{allow:!1}})}edgeKeys(Z){return[this.edgeRootsKey(Z.subject,Z.targetType,Z.target,Z.rToken),this.hasKey(Z.subject,Z.targetType,Z.target,Z.rToken),this.tripleKey(Z.subject,Z.targetType,Z.target),this.anyKey(Z.subject,Z.targetType,Z.target),this.subjectKey(Z.subject)]}async materialize(Z,$){let Q=new Map;for(let G of $){let q=A(G.subject),B=A(G.target);if(!q||!B)continue;let U=T(q,G.subjectType??"user"),X=k(G.resource),D={subject:U,targetType:G.targetType,target:B,rToken:X};Q.set(d(D),D)}let Y=new Set(await this.redis.smembers(this.rootKey(Z))),W=[];for(let[G,q]of Q)if(!Y.has(G))W.push({encoded:G,d:q});let V=[];for(let G of Y)if(!Q.has(G))V.push({encoded:G,d:w(G)});if(W.length===0&&V.length===0)return;if(S(this.redis)){let G=[this.rootKey(Z),this.subjectsKey()],q=[Z,String(W.length),String(V.length)];for(let{encoded:B,d:U}of W)G.push(...this.edgeKeys(U)),q.push(B,U.rToken,U.subject);for(let{encoded:B,d:U}of V)G.push(...this.edgeKeys(U)),q.push(B,U.rToken);await this.redis.runScript(dZ,G,q);return}for(let{encoded:G,d:q}of W)await this.redis.sadd(this.edgeRootsKey(q.subject,q.targetType,q.target,q.rToken),Z),await this.redis.set(this.hasKey(q.subject,q.targetType,q.target,q.rToken),"1"),await this.redis.sadd(this.tripleKey(q.subject,q.targetType,q.target),q.rToken),await this.redis.set(this.anyKey(q.subject,q.targetType,q.target),"1"),await this.redis.sadd(this.rootKey(Z),G),await this.redis.sadd(this.subjectKey(q.subject),G),await this.redis.sadd(this.subjectsKey(),q.subject);for(let{encoded:G,d:q}of V)await this.dropRootFromEdge(Z,q.subject,q.targetType,q.target,q.rToken),await this.redis.srem(this.rootKey(Z),G)}async replaceSubject(Z,$,Q){if(!S(this.redis))throw Error("RedisAccessIndex.replaceSubject requires an adapter with atomic runScript support.");let Y=A(Z);if(!Y)return;let W=T(Y,$),V=[];for(let O of Q){let K=A(O.subject),E=O.subjectType??"user";if(K!==Y||E!==$)throw Error(`AccessIndex.replaceSubject received an edge for ${E}:${K}; expected ${W}.`);let R=A(O.target);if(!R)continue;let x={subject:W,targetType:O.targetType,target:R,rToken:k(O.resource)};V.push({rootId:O.rootId,encoded:d(x),d:x})}let q=(await this.redis.smembers(this.subjectKey(W))).map(w),B=await Promise.all(q.map((O)=>this.redis.smembers(this.edgeRootsKey(O.subject,O.targetType,O.target,O.rToken)))),U=[...new Set(B.flat())],X=[this.subjectKey(W),this.subjectsKey()];for(let O of q){let[K,E,R,x]=this.edgeKeys(O);X.push(K,E,R,x)}for(let O of U)X.push(this.rootKey(O));for(let O of V){let[K,E,R,x]=this.edgeKeys(O.d);X.push(this.rootKey(O.rootId),K,E,R,x)}let D=[W,String(q.length),String(U.length),String(V.length)];for(let O of V)D.push(O.rootId,O.encoded,O.d.rToken);await this.redis.runScript(oZ,X,D)}async removeByRoot(Z){let $=this.rootKey(Z),Q=await this.redis.smembers($);if(Q.length===0){await this.redis.del($);return}if(S(this.redis)){let Y=[$],W=[Z,String(Q.length)];for(let V of Q){let G=w(V);Y.push(...this.edgeKeys(G)),W.push(V,G.rToken)}await this.redis.runScript(nZ,Y,W);return}for(let Y of Q){let{subject:W,targetType:V,target:G,rToken:q}=w(Y);await this.dropRootFromEdge(Z,W,V,G,q)}await this.redis.del($)}async listSubjects(){let Z=await this.redis.smembers(this.subjectsKey()),$=[];for(let Q of Z){let Y=Q.indexOf(":");if(Y<=0){$.push({subject:Q});continue}$.push({subjectType:Q.slice(0,Y),subject:Q.slice(Y+1)})}return $}async setSubjectDisabled(Z,$,Q){let Y=A(Z);if(!Y)return;let W=this.disabledKey(T(Y,$));if(Q)await this.redis.set(W,"1");else await this.redis.del(W)}async isSubjectDisabled(Z,$="user"){let Q=A(Z);if(!Q)return!1;return await this.redis.get(this.disabledKey(T(Q,$)))!=null}async clear(Z,$="user"){if(Z===void 0){let W=await this.redis.smembers(this.subjectsKey());for(let V of W)await this.clearSubject(V);await this.redis.del(this.subjectsKey());return}let Q=A(Z);if(!Q)return;let Y=T(Q,$);await this.clearSubject(Y),await this.redis.srem(this.subjectsKey(),Y)}async clearSubject(Z){let $=this.subjectKey(Z),Q=await this.redis.smembers($);if(Q.length===0){await this.redis.del($);return}if(S(this.redis)){let Y=[$];for(let V of Q){let{targetType:G,target:q,rToken:B}=w(V);Y.push(this.edgeRootsKey(Z,G,q,B),this.hasKey(Z,G,q,B),this.tripleKey(Z,G,q),this.anyKey(Z,G,q))}let W=cZ(await this.redis.runScript(iZ,Y,[String(Q.length)]));if(W.length>0)await this.redis.runScript(aZ,W.map((V)=>this.rootKey(V)),[]);return}for(let Y of Q){let{targetType:W,target:V,rToken:G}=w(Y),q=this.edgeRootsKey(Z,W,V,G),B=await this.redis.smembers(q);for(let X of B)await this.redis.srem(this.rootKey(X),Y);if(await this.redis.del(q),await this.redis.del(this.hasKey(Z,W,V,G)),await this.redis.srem(this.tripleKey(Z,W,V),G),(await this.redis.smembers(this.tripleKey(Z,W,V))).length===0)await this.redis.del(this.tripleKey(Z,W,V)),await this.redis.del(this.anyKey(Z,W,V))}await this.redis.del($)}async dropRootFromEdge(Z,$,Q,Y,W){let V=this.edgeRootsKey($,Q,Y,W);if(await this.redis.srem(V,Z),(await this.redis.smembers(V)).length>0)return;await this.redis.del(V),await this.redis.del(this.hasKey($,Q,Y,W));let q=d({subject:$,targetType:Q,target:Y,rToken:W});if(await this.redis.srem(this.subjectKey($),q),await this.redis.srem(this.tripleKey($,Q,Y),W),(await this.redis.smembers(this.tripleKey($,Q,Y))).length===0)await this.redis.del(this.tripleKey($,Q,Y)),await this.redis.del(this.anyKey($,Q,Y))}allow(Z,$,Q,Y){return{allow:!0,matched:[{subject:Z,targetType:$,target:Q,resources:Y?[Y]:void 0}]}}warnLargeCheck(Z,$){if($<=BZ)return;this.log.warn("Redis access index check received a large bounded target set; issuing per-target reads.",{"ion.operation":Z,"ion.outcome":"degraded",count:$,threshold:BZ})}}var sZ=512;async function rZ(Z,$,Q=512){if(Z.length===0)return[];let Y=Q>0?Q:512,W=[];for(let V=0;V<Z.length;V+=Y){let G=await $(Z.slice(V,V+Y));for(let q of G)W.push(q??null)}return W}import{list as tZ,StoreConflictError as eZ}from"@ionite/core";var Z$="customer:ids",$$="customer:name:";function Q$(Z){let $=Z.createdAt??new Date,Q=Z.updatedAt??$,Y={...Z,createdAt:$.toISOString(),updatedAt:Q.toISOString()};if(Y.ciam&&typeof Y.ciam==="object"&&Y.ciam!==null){let W=Y.ciam;if(W.expires instanceof Date)W.expires=W.expires.toISOString()}return JSON.stringify(Y)}function n(Z,$){let Q=N(Z,{store:"customer",log:$});if(Q===void 0)return;if(Q.ciam&&typeof Q.ciam==="object"&&Q.ciam!==null){let Y=Q.ciam;if(typeof Y.expires==="string")Y.expires=new Date(Y.expires)}return{...Q,createdAt:typeof Q.createdAt==="string"?new Date(Q.createdAt):void 0,updatedAt:typeof Q.updatedAt==="string"?new Date(Q.updatedAt):void 0}}class HZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"customer-store"),this.log=_(Z)}key(...Z){return z(this.prefix,...Z)}entityKey(Z){return this.key("customer",J(Z))}idsSetKey(){return this.key(Z$)}nameIndexKey(Z){return this.key($$+J(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return n($,this.log)}async lookup(Z){if(Z.id){let $=await this.get(Z.id);if($)return $}if(Z.userName){let $=await this.redis.get(this.nameIndexKey(Z.userName.toLowerCase()));if($!=null)return this.get($)}return}async upsert(Z){let $=Z.id?.trim()||crypto.randomUUID(),Q={...Z,id:$},Y=this.entityKey($),W=await this.redis.get(Y),V=W!=null?n(W,this.log)??null:null,G=Q.userName?.toLowerCase(),q=V?.userName?.toLowerCase(),B=new C;if(G)B.claimUnique(this.nameIndexKey(G),$);if(q&&q!==G)B.del(this.nameIndexKey(q));if(B.set(Y,Q$(Q)),G)B.set(this.nameIndexKey(G),$);if(B.sadd(this.idsSetKey(),$),await B.exec(this.redis)==="conflict")throw new eZ(`userName "${Q.userName}" is already in use`,"unique_violation","userName");return Q}async delete(Z){let $=await this.get(Z);if($==null)return 0;let Q=new C;if(Q.del(this.entityKey(Z)),$.userName)Q.del(this.nameIndexKey($.userName.toLowerCase()));return Q.srem(this.idsSetKey(),Z),await Q.exec(this.redis),1}async list(Z){let $=h(Z),Q=(await this.redis.smembers(this.idsSetKey())).sort();if(Q.length===0)return tZ(0,[],$.start??0,$.limit);let Y=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(Y)).filter((G)=>G!=null).map((G)=>n(G,this.log)).filter((G)=>G!=null);return P(V,$)}}import{createHash as Y$}from"node:crypto";function JZ(Z){return Y$("sha1").update(Z,"utf8").digest("hex")}function W$(Z){return(Z instanceof Error?Z.message:String(Z)).includes("NOSCRIPT")}function G$(Z){let $=new Map;return async(Q,Y,W)=>{let V=$.get(Q);if(!V)V=JZ(Q),$.set(Q,V);try{return await Z.evalsha(V,Y,W)}catch(G){if(W$(G))return Z.eval(Q,Y,W);throw G}}}var V$=7776000;class MZ{redis;prefix;ttlSeconds;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"iam-delta-id-map-store");let $=Z.idMapTtlSeconds;this.ttlSeconds=$==null?V$:Math.max(0,Math.floor($))}key(Z){return z(this.prefix,"iam","delta","idmap",J(Z.tenantId??"_"),J(Z.resourceType),J(Z.destination),J(Z.externalId))}async get(Z){return await this.redis.get(this.key(Z))??void 0}async set(Z,$){if(this.ttlSeconds>0)await this.redis.setex(this.key(Z),this.ttlSeconds,$);else await this.redis.set(this.key(Z),$)}async delete(Z){await this.redis.del(this.key(Z))}}function XZ(Z){return typeof Z==="object"&&Z!==null&&!Array.isArray(Z)}function q$(Z){let $=XZ(Z.clientOptions)?{...Z.clientOptions}:XZ(Z)?{...Z}:{};return delete $.redisUrl,delete $.clientOptions,{redisUrl:(typeof Z.redisUrl==="string"?Z.redisUrl.trim():"")||void 0,clientOptions:$}}import{defaultLogger as B$}from"@ionite/core";function OZ(Z,$){return`${Z}:${encodeURIComponent($)}`}function U$(Z,$){if(typeof Z!=="object"||Z===null||Array.isArray(Z))return!1;let Q=Z,Y=Q.headers;return(Q.kind==="delivery"||Q.kind==="events")&&Q.key===$&&typeof Q.originId==="string"&&Q.originId.length>0&&typeof Q.rawBody==="string"&&typeof Y==="object"&&Y!==null&&typeof Y.requestId==="string"&&typeof Y.keyId==="string"&&typeof Y.signature==="string"}function H$(Z,$={}){if(!Z.publish||!Z.subscribeChannel)throw Error("Redis adapter must implement publish() and subscribeChannel() for LFV callback coordination");let Q=Z.publish.bind(Z),Y=Z.subscribeChannel.bind(Z),W=$.channelPrefix?.trim()||"ionite:lfv:callbacks",V=$.logger??B$;return{async publish(G){await Q(OZ(W,G.key),JSON.stringify(G))},async subscribe(G,q){let B=await Y(OZ(W,G),(U)=>{let X;try{X=JSON.parse(U)}catch(D){V.warn("Invalid JSON received on LFV Redis callback channel",{"ion.operation":"lfv.callback.redis.receive","ion.outcome":"rejected",key:G,error:D instanceof Error?D.message:String(D)});return}if(!U$(X,G)){V.warn("Invalid envelope received on LFV Redis callback channel",{"ion.operation":"lfv.callback.redis.receive","ion.outcome":"rejected",key:G});return}Promise.resolve(q(X)).catch((D)=>{V.warn("LFV Redis callback listener failed",{"ion.operation":"lfv.callback.redis.apply","ion.outcome":"degraded",key:G,error:D instanceof Error?D.message:String(D)})})});return()=>{Promise.resolve(B()).catch((U)=>{V.warn("LFV Redis callback unsubscribe failed",{"ion.operation":"lfv.callback.redis.unsubscribe","ion.outcome":"degraded",key:G,error:U instanceof Error?U.message:String(U)})})}}}}import{StoreConflictError as J$}from"@ionite/core";function M$(Z){return JSON.stringify({token:Z.token,customer:Z.customer,createdAt:Z.createdAt.toISOString(),expiresAt:Z.expiresAt.toISOString()})}function FZ(Z,$){let Q=N(Z,{store:"magic-link",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),expiresAt:new Date(Q.expiresAt)}}class zZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"magic-link-store"),this.log=_(Z)}key(Z){return z(this.prefix,"magic",J(Z))}async create(Z,$,Q){let Y=this.key(Z),W={token:Z,customer:$,createdAt:new Date,expiresAt:Q},V=Math.max(1,Math.floor((Q.getTime()-Date.now())/1000));if(await v(this.redis,Y,M$(W),{ttlSeconds:V})==="conflict")throw new J$(`Magic link with token ${Z} already exists`,"already_exists","token")}async get(Z){let $=await this.redis.get(this.key(Z));if($==null)return null;return FZ($,this.log)??null}async getAndDelete(Z){let $=this.key(Z),Q;if(typeof this.redis.getdel==="function")Q=await this.redis.getdel($);else if(Q=await this.redis.get($),Q!=null)await this.redis.del($);if(Q==null)return null;let Y=FZ(Q,this.log);if(Y==null)return null;if(Date.now()>Y.expiresAt.getTime())return null;return Y}async delete(Z){await this.redis.del(this.key(Z))}}import{defaultLogger as X$}from"@ionite/core";function O$(Z,$){if(typeof $!=="number"||$<=0)return Z;return new Promise((Q,Y)=>{let W=setTimeout(()=>{Y(Error(`Reactive Redis adapter did not become ready within ${$}ms`))},$);Z.then(()=>{clearTimeout(W),Q()},(V)=>{clearTimeout(W),Y(V)})})}function F$(){let Z,$;return{promise:new Promise((Y,W)=>{Z=Y,$=W}),resolve:Z,reject:$}}function DZ(Z){return typeof Z==="function"}function z$(Z){let $=Z.logger??X$,Q=Z.sourceName?.trim()||"redis",Y=Z.path.trim(),W,V,G=!1,q,B,U,X=!1,D=!1,O=!1,K=Promise.resolve(),E=F$();E.promise.catch(()=>{});let R={async get(H){return f((M)=>M.get(H))},async set(H,M){await f((L)=>L.set(H,M))},async setex(H,M,L){await f((j)=>j.setex(H,M,L))},async del(H){await f((M)=>M.del(H))},async sadd(H,...M){await f((L)=>L.sadd(H,...M))},async srem(H,...M){await f((L)=>L.srem(H,...M))},async smembers(H){return f((M)=>M.smembers(H))},async mget(H){return f((M)=>M.mget(H))},ready(H){return O$(E.promise,H)},isReady(){return W!==void 0},async close(){if(X=!0,o(),!G)G=!0,E.reject(Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" was closed before becoming ready.`));await K.catch(()=>{}),await y(W),W=void 0,g()}};function x(H){K=K.then(H).catch((M)=>{if(!G)G=!0,E.reject(M);$.warn("Reactive Redis adapter update failed",{"ion.operation":"redis.adapter.update","ion.outcome":"degraded",sourceName:Q,path:Y,error:M instanceof Error?M.message:String(M)})})}async function fZ(){if(X)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" is closed.`);let H=W?.adapter;if(H)return H;if(await K.catch(()=>{}),X)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" is closed.`);let M=W?.adapter;if(M)return M;if(await E.promise,X)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" is closed.`);let L=W?.adapter;if(!L)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" did not become ready.`);return L}async function f(H){let M=await fZ();return H(M)}function o(){V?.(),V=void 0}async function xZ(H){o(),V=await H.subscribe(Y,(M)=>{x(async()=>{if(X)return;await hZ(M)})},Z.subscription??Z.secretOptions)}async function hZ(H){if(X)return;let M=`${Q}:${Y}:${H.metadata.version}`;if(W!==void 0&&B===M&&q===H.metadata.version)return;let L=Z.endpointOf?.(H);if(W!==void 0&&L!=null&&L!==""&&U!=null&&U!==""&&L!==U)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" rejected a rotation that changed the endpoint (${U} -> ${L}); only credentials may rotate under an active adapter.`);let j=await Z.createClient(H);if(X){await y(j);return}let I=W;if(W=j,B=M,q=H.metadata.version,L!=null&&L!=="")U=L;if(g(),!G)G=!0,E.resolve();await y(I)}async function y(H){if(!H?.dispose)return;await H.dispose()}function g(){let H=W?.adapter;if(H?.runScript&&DZ(H.runScript))R.runScript=async(M,L,j)=>{return f(async(I)=>{if(!I.runScript)throw Error("Active Redis adapter does not support runScript");return I.runScript(M,L,j)})};else if(delete R.runScript,H!==void 0&&!D)D=!0,$.warn("Active Redis adapter does not support runScript (EVAL); stores fall back to non-isolated writes.",{"ion.operation":"redis.adapter.capabilities","ion.outcome":"degraded",sourceName:Q,path:Y,capability:"runScript"});if(H?.getdel&&DZ(H.getdel))R.getdel=async(M)=>{return f(async(L)=>{if(!L.getdel)return null;return L.getdel(M)})};else if(delete R.getdel,H!==void 0&&!O)O=!0,$.warn("Active Redis adapter does not support getdel (GETDEL); single-use consume falls back to get+del.",{"ion.operation":"redis.adapter.capabilities","ion.outcome":"degraded",sourceName:Q,path:Y,capability:"getdel"})}return g(),x(async()=>{if(X)return;await xZ(Z.source)}),R}import{StoreConflictError as D$}from"@ionite/core";var L$=5;function _$(Z,$){let Q;try{Q=JSON.parse(Z)}catch{return}if(Q==null||typeof Q!=="object"||Array.isArray(Q))return;return JSON.stringify({...Q,...$})}function LZ(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString()})}function _Z(Z,$){let Q=N(Z,{store:"session",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt)}}function CZ(Z){if(!Z)return;let $=Z instanceof Date?Z.getTime():new Date(Z).getTime();if(Number.isNaN($))return;let Q=Math.ceil(($-Date.now())/1000);return Q>0?Q:1}class NZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"session-store"),this.log=_(Z)}key(Z){return z(this.prefix,"session",J(Z))}async create(Z){let $=this.key(Z.sid),Q=CZ(Z.expiresAt);if(await v(this.redis,$,LZ(Z),{ttlSeconds:Q})==="conflict")throw new D$(`Session with sid ${Z.sid} already exists`,"already_exists","sid")}async get(Z){let $=await this.redis.get(this.key(Z));if($==null)return null;return _Z($,this.log)??null}async update(Z,$){let Q=this.key(Z),Y=JSON.parse(JSON.stringify($)),W="expiresAt"in Y?CZ(Y.expiresAt):void 0;if(S(this.redis))for(let U=0;U<L$;U++){let X=await this.redis.get(Q);if(X==null)throw Error(`Session with sid ${Z} not found`);let D=_$(X,Y);if(D===void 0)throw Error(`Session with sid ${Z} not found`);let O=await u(this.redis,Q,X,D,W);if(O==="ok")return;if(O==="missing")throw Error(`Session with sid ${Z} not found`)}let V=await this.redis.get(Q);if(V==null)throw Error(`Session with sid ${Z} not found`);let G=_Z(V,this.log);if(G==null)throw Error(`Session with sid ${Z} not found`);let q={...G,...$},B=LZ(q);if(W!=null&&W>0)await this.redis.setex(Q,Math.max(1,Math.floor(W)),B);else await this.redis.set(Q,B)}async delete(Z){await this.redis.del(this.key(Z))}}var C$="tenant:ids";function N$(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString(),updatedAt:Z.updatedAt.toISOString()})}function PZ(Z,$){let Q=N(Z,{store:"tenant",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),updatedAt:new Date(Q.updatedAt)}}class AZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"tenant-store"),this.log=_(Z)}key(...Z){return z(this.prefix,...Z)}entityKey(Z){return this.key("tenant",J(Z))}idsSetKey(){return this.key(C$)}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return PZ($,this.log)}async upsert(Z){return await new C().set(this.entityKey(Z.id),N$(Z)).sadd(this.idsSetKey(),Z.id).exec(this.redis),Z}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return P([],Z);let Q=$.map((V)=>this.entityKey(V)),W=(await this.redis.mget(Q)).map((V)=>V!=null?PZ(V,this.log):void 0).filter((V)=>V!=null);return P(W,Z)}async delete(Z){let $=await this.get(Z);return await new C().srem(this.idsSetKey(),Z).del(this.entityKey(Z)).exec(this.redis),$?1:0}}class KZ{store;ionCache;constructor(Z){this.ionCache=Z.ionCache,this.store=new AZ(Z)}async get(Z){return this.store.get(Z)}async upsert(Z){let $=await this.store.upsert(Z);return this.ionCache?.delete(Z.id),$}async list(Z){return this.store.list(Z)}async delete(Z){let $=await this.store.delete(Z);return this.ionCache?.delete(Z),$}async getIon(Z){if(!this.ionCache)throw Error(`${this.constructor.name}.getIon() requires an app-owned ionCache. Pass { ionCache } (an lru-cache instance whose fetchMethod builds the tenant Ionite and whose dispose `+"closes it) — the SDK does not cache tenant runtimes. See the multi-tenant docs.");return this.ionCache.fetch(Z)}}import{list as P$,StoreConflictError as A$}from"@ionite/core";var K$="user:ids",E$="user:name:";function R$(Z){let $=Z.createdAt??new Date,Q=Z.updatedAt??$,Y={...Z,createdAt:$.toISOString(),updatedAt:Q.toISOString()};if(Y.sso&&typeof Y.sso==="object"&&Y.sso!==null){let W=Y.sso;if(W.expires instanceof Date)W.expires=W.expires.toISOString()}return JSON.stringify(Y)}function i(Z,$){let Q=N(Z,{store:"user",log:$});if(Q===void 0)return;if(Q.sso&&typeof Q.sso==="object"&&Q.sso!==null){let Y=Q.sso;if(typeof Y.expires==="string")Y.expires=new Date(Y.expires)}return{...Q,createdAt:typeof Q.createdAt==="string"?new Date(Q.createdAt):void 0,updatedAt:typeof Q.updatedAt==="string"?new Date(Q.updatedAt):void 0}}class EZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"user-store"),this.log=_(Z)}key(...Z){return z(this.prefix,...Z)}entityKey(Z){return this.key("user",J(Z))}idsSetKey(){return this.key(K$)}nameIndexKey(Z){return this.key(E$+J(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return i($,this.log)}async lookup(Z){return this.get(Z.id??"")}async upsert(Z){let $=Z.id?.trim()||crypto.randomUUID(),Q={...Z,id:$},Y=this.entityKey($),W=await this.redis.get(Y),V=W!=null?i(W,this.log)??null:null,G=Q.userName?.toLowerCase(),q=V?.userName?.toLowerCase(),B=new C;if(G)B.claimUnique(this.nameIndexKey(G),$);if(q&&q!==G)B.del(this.nameIndexKey(q));if(B.set(Y,R$(Q)),G)B.set(this.nameIndexKey(G),$);if(B.sadd(this.idsSetKey(),$),await B.exec(this.redis)==="conflict")throw new A$(`userName "${Q.userName}" is already in use`,"unique_violation","userName");return Q}async delete(Z){let $=await this.get(Z);if($==null)return 0;let Q=new C;if(Q.del(this.entityKey(Z)),$.userName)Q.del(this.nameIndexKey($.userName.toLowerCase()));return Q.srem(this.idsSetKey(),Z),await Q.exec(this.redis),1}async list(Z){let $=h(Z),Q=(await this.redis.smembers(this.idsSetKey())).sort();if(Q.length===0)return P$(0,[],$.start??0,$.limit);let Y=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(Y)).filter((G)=>G!=null).map((G)=>i(G,this.log)).filter((G)=>G!=null);return P(V,$)}}import{list as S$,StoreConflictError as f$}from"@ionite/core";var x$="workload-identity:ids",h$="workload-identity:client:";function T$(Z){let $=Z.createdAt??new Date,Q=Z.updatedAt??$;return JSON.stringify({...Z,createdAt:$.toISOString(),updatedAt:Q.toISOString()})}function a(Z,$){let Q=N(Z,{store:"workload-identity",log:$});if(Q===void 0)return;return{...Q,createdAt:typeof Q.createdAt==="string"?new Date(Q.createdAt):void 0,updatedAt:typeof Q.updatedAt==="string"?new Date(Q.updatedAt):void 0}}class RZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"workload-identity-store"),this.log=_(Z)}key(...Z){return z(this.prefix,...Z)}entityKey(Z){return this.key("workload-identity",J(Z))}idsSetKey(){return this.key(x$)}clientIndexKey(Z){return this.key(h$+J(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return a($,this.log)}async lookup(Z){if(Z.id){let $=await this.get(Z.id);if($)return $}if(Z.clientId){let $=await this.redis.get(this.clientIndexKey(Z.clientId));if($!=null)return this.get($)}return}async upsert(Z){let $=Z.id?.trim()||crypto.randomUUID(),Q=this.entityKey($),Y=await this.redis.get(Q),W=null;if(Y!=null)W=a(Y,this.log)??null;let V=new Date,G={...Z,id:$,createdAt:W?.createdAt??Z.createdAt??V,updatedAt:V},q=new C;if(G.clientId)q.claimUnique(this.clientIndexKey(G.clientId),$);if(W?.clientId&&W.clientId!==G.clientId)q.del(this.clientIndexKey(W.clientId));if(q.set(Q,T$(G)),G.clientId)q.set(this.clientIndexKey(G.clientId),$);if(q.sadd(this.idsSetKey(),$),await q.exec(this.redis)==="conflict")throw new f$(`clientId "${G.clientId}" is already in use`,"unique_violation","clientId");return G}async delete(Z){let $=await this.get(Z);if($==null)return 0;let Q=new C;if(Q.del(this.entityKey(Z)),$.clientId)Q.del(this.clientIndexKey($.clientId));return Q.srem(this.idsSetKey(),Z),await Q.exec(this.redis),1}async list(Z){let $=h(Z),Q=(await this.redis.smembers(this.idsSetKey())).sort();if(Q.length===0)return S$(0,[],$.start??0,$.limit);let Y=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(Y)).filter((G)=>G!=null).map((G)=>a(G,this.log)).filter((G)=>G!=null);return P(V,$)}}function j$(Z){return JSON.stringify({...Z,expires_at:Z.expires_at.toISOString(),created_at:Z.created_at.toISOString()})}function w$(Z,$){let Q=N(Z,{store:"workload-token",log:$});if(Q===void 0)return;return{...Q,expires_at:new Date(Q.expires_at),created_at:new Date(Q.created_at)}}class SZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=F(Z,"workload-token-store"),this.log=_(Z)}key(Z){return z(this.prefix,"workload","token",J(Z))}async set(Z){let $=this.key(Z.workload_id),Q=Math.max(1,Math.floor((Z.expires_at.getTime()-Date.now())/1000));await this.redis.setex($,Q,j$(Z))}async get(Z){let $=await this.redis.get(this.key(Z));if($==null)return null;let Q=w$($,this.log);if(Q==null)return null;if(Date.now()>Q.expires_at.getTime())return await this.redis.del(this.key(Z)),null;return Q}async delete(Z){await this.redis.del(this.key(Z))}async isValid(Z){return await this.get(Z)!==null}async cleanup(){}}export{S as supportsRunScript,q$ as splitIonRedisSecret,JZ as scriptSha1,z as prefixedKey,_ as getLogger,F as getKeyPrefix,J as encodeKeyId,H$ as createRedisLfvTransport,z$ as createReactiveRedis,G$ as createEvalshaRunner,rZ as chunkedMget,v as atomicCreate,u as atomicCompareAndSet,SZ as RedisWorkloadTokenStore,RZ as RedisWorkloadIdentityStore,EZ as RedisUserStore,C as RedisTxn,KZ as RedisTenantStore,NZ as RedisSessionStore,qZ as RedisResourceStore,zZ as RedisMagicLinkStore,MZ as RedisIamDeltaIdMapStore,HZ as RedisCustomerStore,UZ as RedisAccessIndex,VZ as RedisAccessCatalogStore,GZ as RedisAccessAssignmentStore,t as MAX_LIST_LIMIT,sZ as DEFAULT_MGET_CHUNK_SIZE,r as DEFAULT_LIST_LIMIT};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ionite/redis-core",
3
- "version": "0.0.23-beta.20260803.1",
3
+ "version": "0.0.23-beta.20260809.2",
4
4
  "description": "Redis adapter interface and store implementations for ionite. Use with @ionite/redis (node-redis), @ionite/ioredis, or a custom RedisAdapter.",
5
5
  "private": false,
6
6
  "author": "ionite",
@@ -24,7 +24,7 @@
24
24
  "./package.json": "./package.json"
25
25
  },
26
26
  "dependencies": {
27
- "@ionite/core": "0.0.23-beta.20260803.1"
27
+ "@ionite/core": "0.0.23-beta.20260809.2"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^26.1.0"