@ionite/redis-core 0.0.23 → 0.0.24-beta.20260907.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
@@ -300,8 +300,10 @@ declare class RedisCustomerStore<TExtended = Record<string, never>> implements C
300
300
  private entityKey;
301
301
  private idsSetKey;
302
302
  private nameIndexKey;
303
+ private externalIdIndexKey;
303
304
  get(sub: string): Promise<BaseCustomer<TExtended> | undefined>;
304
305
  lookup(customer: BaseCustomer<TExtended>): Promise<BaseCustomer<TExtended> | undefined>;
306
+ getByExternalId(externalId: string): Promise<BaseCustomer<TExtended> | undefined>;
305
307
  upsert(customer: BaseCustomer<TExtended>): Promise<BaseCustomer<TExtended>>;
306
308
  delete(sub: string): Promise<number>;
307
309
  list(options?: ListOptions3): Promise<ListResult3<BaseCustomer<TExtended>>>;
@@ -478,8 +480,10 @@ declare class RedisWorkloadIdentityStore<TExtended = Record<string, never>> impl
478
480
  private entityKey;
479
481
  private idsSetKey;
480
482
  private clientIndexKey;
483
+ private externalIdIndexKey;
481
484
  get(id: string): Promise<BaseWorkloadIdentity<TExtended> | undefined>;
482
485
  lookup(identity: BaseWorkloadIdentity<TExtended>): Promise<BaseWorkloadIdentity<TExtended> | undefined>;
486
+ getByExternalId(externalId: string): Promise<BaseWorkloadIdentity<TExtended> | undefined>;
483
487
  upsert(identity: BaseWorkloadIdentity<TExtended>): Promise<BaseWorkloadIdentity<TExtended>>;
484
488
  delete(id: string): Promise<number>;
485
489
  list(options?: ListOptions7): Promise<ListResult7<BaseWorkloadIdentity<TExtended>>>;
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import{accessRefId as i,accessRootId as nZ,assertRequiredSubjectType as VZ,list as BZ}from"@ionite/core";function R(Z){return typeof Z.runScript==="function"}async function m(Z,$,Q,Y){let W=Y?.ttlSeconds;if(R(Z)){let B=`local cur=redis.call('GET',KEYS[1])
1
+ import{accessRefId as i,accessRootId as aZ,assertRequiredSubjectType as VZ,list as BZ}from"@ionite/core";function E(Z){return typeof Z.runScript==="function"}async function m(Z,$,Q,Y){let W=Y?.ttlSeconds;if(E(Z)){let B=`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
3
  ${W!=null?"redis.call('SET',KEYS[1],ARGV[1],'EX',ARGV[2])":"redis.call('SET',KEYS[1],ARGV[1])"}
4
- return 'created'`,U=W!=null?[Q,String(Math.max(1,Math.floor(W)))]:[Q],M=String(await Z.runScript(B,[$],U));return M==="exists_identical"||M==="conflict"?M:"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 c(Z,$,Q,Y,W){if(!R(Z))return"unsupported";let V=W!=null&&W>0?Math.max(1,Math.floor(W)):void 0,B=`local cur=redis.call('GET',KEYS[1])
4
+ return 'created'`,U=W!=null?[Q,String(Math.max(1,Math.floor(W)))]:[Q],J=String(await Z.runScript(B,[$],U));return J==="exists_identical"||J==="conflict"?J:"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 c(Z,$,Q,Y,W){if(!E(Z))return"unsupported";let V=W!=null&&W>0?Math.max(1,Math.floor(W)):void 0,B=`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'`,U=V!=null?[Q,Y,String(V)]:[Q,Y],M=String(await Z.runScript(B,[$],U));if(M==="missing"||M==="retry")return M;return"ok"}var p=500,yZ=`local ki=1
8
+ return 'ok'`,U=V!=null?[Q,Y,String(V)]:[Q,Y],J=String(await Z.runScript(B,[$],U));if(J==="missing"||J==="retry")return J;return"ok"}var p=500,lZ=`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,11 +43,11 @@ for i=1,W do
43
43
  redis.call('SREM',k,unpack(members))
44
44
  end
45
45
  end
46
- return 'ok'`;class q{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(R(Z))return this.execLua(Z);return this.execSequential(Z)}async execLua(Z){if(this.claims.length>0||this.writes.length<=p)return ZZ(Z,this.claims,this.writes);for(let $=0;$<this.writes.length;$+=p)await ZZ(Z,[],this.writes.slice($,$+p));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 ZZ(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(yZ,Y,W))==="conflict"?"conflict":"ok"}import{list as lZ}from"@ionite/core";var $Z=100,QZ=1000;function h(Z){let $=Z?.limit,Q=$==null||!Number.isFinite($)||$<=0?$Z:Math.min(Math.floor($),QZ);return{...Z,limit:Q}}function K(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((F)=>Boolean(Q(F,"requestable"))===V);let B=G.length,U=W!=null?Y+W:void 0,M=G.slice(Y,U);return lZ(B,M,Y,W)}import{defaultLogger as uZ}from"@ionite/core";function L(Z,$){let Q=$.log??uZ,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 pZ}from"node:crypto";import{defaultLogger as cZ}from"@ionite/core";var dZ=4096,YZ=new Set,WZ=new Set;function GZ(Z,$){Z.delete($),Z.add($);while(Z.size>dZ){let Q=Z.values().next().value;if(Q===void 0)break;Z.delete(Q)}}function _(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(YZ.has(Y)&&!WZ.has(Y))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.`),GZ(WZ,Y);GZ(YZ,Y)}return Q}function D(Z){return Z.logger??cZ}function C(Z,...$){return Z+$.join(":")}var iZ=/^[A-Za-z0-9_.@|-]{1,64}$/;function O(Z){if(iZ.test(Z))return`=${Z}`;return`#${pZ("sha256").update(Z,"utf8").digest("base64url").slice(0,22)}`}var oZ="access:ids",aZ="access:subject:",sZ="access:target:";function rZ(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString(),updatedAt:Z.updatedAt.toISOString()})}function d(Z,$){let Q=L(Z,{store:"access",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),updatedAt:new Date(Q.updatedAt)}}function UZ(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 HZ(Z){let $=i(Z.subject),Q=i(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??nZ(Z),subjectId:$,targetId:Q}}class JZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"access-assignment-store"),this.log=D(Z)}key(...Z){return C(this.prefix,...Z)}idsSetKey(){return this.key(oZ)}accessStorageKey(Z){return this.key("access",O(Z))}subjectIndexKey(Z,$){return this.key(`${aZ}${$}:${O(Z)}`)}targetIndexKey(Z,$){return this.key(sZ+Z,O($))}async grant(Z){VZ(Z.subjectType,"Access change subjectType");let $=HZ(Z),Q=this.accessStorageKey($.id),Y=await this.redis.get(Q),W=Y?d(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},B=new q;B.set(Q,rZ(G)),B.sadd(this.idsSetKey(),$.id),B.sadd(this.subjectIndexKey($.subjectId,Z.subjectType),$.id),B.sadd(this.targetIndexKey(Z.targetType,$.targetId),$.id),await B.exec(this.redis)}async revoke(Z){let $=HZ(Z);if(await this.redis.get(this.accessStorageKey($.id))==null)return;let Y=new q;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=d($,this.log);return Q?UZ(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){VZ($);let Y=i(Z);if(!Y)return BZ(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 BZ(0,[],$?.start??0,$?.limit);let Y=[...Z].sort().map((B)=>this.accessStorageKey(B)),V=(await this.redis.mget(Y)).filter((B)=>B!=null).map((B)=>d(B,this.log)).filter((B)=>B!=null).filter((B)=>(B.status??"active")==="active"),G=K(V,$);return{...G,items:G.items.map(UZ)}}}import{assertValidSubjectTypeScoping as tZ,deepEqualPlain as eZ,readyCatalogEntry as MZ}from"@ionite/core";class XZ{groups;roles;permissions;listeners=new Set;constructor(Z){let $=Z.redis,Q=_(Z,"access-catalog-store"),Y=D(Z),W=(V)=>{for(let G of this.listeners)G(V)};this.groups=new I($,Q,"group",W,Y),this.roles=new I($,Q,"role",W,Y),this.permissions=new I($,Q,"permission",W,Y)}afterChange(Z){return this.listeners.add(Z),()=>{this.listeners.delete(Z)}}}class FZ{redis;prefix;log;listeners=new Set;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"resource-store"),this.log=D(Z)}key(...Z){return C(this.prefix,"authz","resource",...Z)}entityKey(Z){return this.key(O(Z))}idsSetKey(){return this.key("ids")}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;let Q=L($,{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 K([],Z);let Y=(await this.redis.mget($.map((W)=>this.entityKey(W)))).filter((W)=>W!=null).map((W)=>L(W,{store:"authz:resource",log:this.log})).filter((W)=>W!==void 0).map((W)=>W);return K(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 q().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 q().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,Y,W){this.redis=Z;this.prefix=$;this.kind=Q;this.emit=Y;this.log=W}key(...Z){return C(this.prefix,"authz",this.kind,...Z)}idsSetKey(){return this.key("ids")}entityKey(Z){return this.key(O(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;let Q=L($,{store:`authz:${this.kind}`,key:Z,log:this.log});return Q===void 0?void 0:Q}set(Z){let $,Q=!0,Y=(async()=>{$=await this.get(Z.id),Q=!$||!eZ($,Z);let W=new q;W.set(this.entityKey(Z.id),JSON.stringify(Z)),W.sadd(this.idsSetKey(),Z.id),await W.exec(this.redis)})();return Y.then(()=>{if(!Q)return;this.emit({op:"set",kind:this.kind,entity:Z,previous:$})},(W)=>{this.log?.warn("authz catalog store: background persist failed",{"ion.operation":`authz.catalog.${this.kind}.set`,"ion.outcome":"error",id:Z.id,error:W instanceof Error?W.message:String(W)})}),MZ(Z,Y)}insertIfAbsent(Z){tZ(Z);let $={...Z},Q=!1,Y=(async()=>{if(!R(this.redis))throw Error("RedisAccessCatalogStore.insertIfAbsent requires a Redis adapter with runScript support.");let W=JSON.stringify(Z),V=await this.redis.runScript(`local cur=redis.call('GET',KEYS[1])
46
+ return 'ok'`;class D{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(E(Z))return this.execLua(Z);return this.execSequential(Z)}async execLua(Z){if(this.claims.length>0||this.writes.length<=p)return ZZ(Z,this.claims,this.writes);for(let $=0;$<this.writes.length;$+=p)await ZZ(Z,[],this.writes.slice($,$+p));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 ZZ(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(lZ,Y,W))==="conflict"?"conflict":"ok"}import{list as uZ,matchesListFilter as pZ}from"@ionite/core";var $Z=100,QZ=1000;function h(Z){let $=Z?.limit,Q=$==null||!Number.isFinite($)||$<=0?$Z:Math.min(Math.floor($),QZ);return{...Z,limit:Q}}function K(Z,$,Q=(Y,W)=>Y[W]){let Y=Math.max(0,$?.start??0),W=$?.limit,V=$?.filter?Z.filter((J)=>pZ({requestable:Q(J,"requestable"),userName:Q(J,"userName"),clientId:Q(J,"clientId"),externalId:Q(J,"externalId"),updatedAt:Q(J,"updatedAt")},$.filter)):Z,G=V.length,B=W!=null?Y+W:void 0,U=V.slice(Y,B);return uZ(G,U,Y,W)}import{defaultLogger as cZ}from"@ionite/core";function L(Z,$){let Q=$.log??cZ,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 dZ}from"node:crypto";import{defaultLogger as iZ}from"@ionite/core";var oZ=4096,YZ=new Set,WZ=new Set;function GZ(Z,$){Z.delete($),Z.add($);while(Z.size>oZ){let Q=Z.values().next().value;if(Q===void 0)break;Z.delete(Q)}}function _(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(YZ.has(Y)&&!WZ.has(Y))F(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.`),GZ(WZ,Y);GZ(YZ,Y)}return Q}function F(Z){return Z.logger??iZ}function C(Z,...$){return Z+$.join(":")}var nZ=/^[A-Za-z0-9_.@|-]{1,64}$/;function X(Z){if(nZ.test(Z))return`=${Z}`;return`#${dZ("sha256").update(Z,"utf8").digest("base64url").slice(0,22)}`}var sZ="access:ids",rZ="access:subject:",tZ="access:target:";function eZ(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString(),updatedAt:Z.updatedAt.toISOString()})}function d(Z,$){let Q=L(Z,{store:"access",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),updatedAt:new Date(Q.updatedAt)}}function UZ(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 HZ(Z){let $=i(Z.subject),Q=i(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??aZ(Z),subjectId:$,targetId:Q}}class JZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"access-assignment-store"),this.log=F(Z)}key(...Z){return C(this.prefix,...Z)}idsSetKey(){return this.key(sZ)}accessStorageKey(Z){return this.key("access",X(Z))}subjectIndexKey(Z,$){return this.key(`${rZ}${$}:${X(Z)}`)}targetIndexKey(Z,$){return this.key(tZ+Z,X($))}async grant(Z){VZ(Z.subjectType,"Access change subjectType");let $=HZ(Z),Q=this.accessStorageKey($.id),Y=await this.redis.get(Q),W=Y?d(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},B=new D;B.set(Q,eZ(G)),B.sadd(this.idsSetKey(),$.id),B.sadd(this.subjectIndexKey($.subjectId,Z.subjectType),$.id),B.sadd(this.targetIndexKey(Z.targetType,$.targetId),$.id),await B.exec(this.redis)}async revoke(Z){let $=HZ(Z);if(await this.redis.get(this.accessStorageKey($.id))==null)return;let Y=new D;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=d($,this.log);return Q?UZ(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){VZ($);let Y=i(Z);if(!Y)return BZ(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 BZ(0,[],$?.start??0,$?.limit);let Y=[...Z].sort().map((B)=>this.accessStorageKey(B)),V=(await this.redis.mget(Y)).filter((B)=>B!=null).map((B)=>d(B,this.log)).filter((B)=>B!=null).filter((B)=>(B.status??"active")==="active"),G=K(V,$);return{...G,items:G.items.map(UZ)}}}import{assertValidSubjectTypeScoping as MZ,deepEqualPlain as Z$,readyCatalogEntry as OZ}from"@ionite/core";class zZ{groups;roles;permissions;listeners=new Set;constructor(Z){let $=Z.redis,Q=_(Z,"access-catalog-store"),Y=F(Z),W=(V)=>{for(let G of this.listeners)G(V)};this.groups=new I($,Q,"group",W,Y),this.roles=new I($,Q,"role",W,Y),this.permissions=new I($,Q,"permission",W,Y)}afterChange(Z){return this.listeners.add(Z),()=>{this.listeners.delete(Z)}}}class XZ{redis;prefix;log;listeners=new Set;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"resource-store"),this.log=F(Z)}key(...Z){return C(this.prefix,"authz","resource",...Z)}entityKey(Z){return this.key(X(Z))}idsSetKey(){return this.key("ids")}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;let Q=L($,{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 K([],Z);let Y=(await this.redis.mget($.map((W)=>this.entityKey(W)))).filter((W)=>W!=null).map((W)=>L(W,{store:"authz:resource",log:this.log})).filter((W)=>W!==void 0).map((W)=>W);return K(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 D().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 D().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,Y,W){this.redis=Z;this.prefix=$;this.kind=Q;this.emit=Y;this.log=W}key(...Z){return C(this.prefix,"authz",this.kind,...Z)}idsSetKey(){return this.key("ids")}entityKey(Z){return this.key(X(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;let Q=L($,{store:`authz:${this.kind}`,key:Z,log:this.log});return Q===void 0?void 0:Q}set(Z){MZ(Z);let $,Q=!0,Y=(async()=>{$=await this.get(Z.id),Q=!$||!Z$($,Z);let W=new D;W.set(this.entityKey(Z.id),JSON.stringify(Z)),W.sadd(this.idsSetKey(),Z.id),await W.exec(this.redis)})();return Y.then(()=>{if(!Q)return;this.emit({op:"set",kind:this.kind,entity:Z,previous:$})},(W)=>{this.log?.warn("authz catalog store: background persist failed",{"ion.operation":`authz.catalog.${this.kind}.set`,"ion.outcome":"error",id:Z.id,error:W instanceof Error?W.message:String(W)})}),OZ(Z,Y)}insertIfAbsent(Z){MZ(Z);let $={...Z},Q=!1,Y=(async()=>{if(!E(this.redis))throw Error("RedisAccessCatalogStore.insertIfAbsent requires a Redis adapter with runScript support.");let W=JSON.stringify(Z),V=await this.redis.runScript(`local cur=redis.call('GET',KEYS[1])
47
47
  if cur then return {0,cur} end
48
48
  redis.call('SET',KEYS[1],ARGV[1])
49
49
  redis.call('SADD',KEYS[2],ARGV[2])
50
- return {1,ARGV[1]}`,[this.entityKey(Z.id),this.idsSetKey()],[W,Z.id]);if(!Array.isArray(V)||V.length<2)throw Error("RedisAccessCatalogStore.insertIfAbsent received an invalid Redis response.");Q=String(V[0])==="1";let G=String(V[1]),B=L(G,{store:`authz:${this.kind}`,key:Z.id,log:this.log});if(!B)throw Error("RedisAccessCatalogStore.insertIfAbsent could not parse the winning row.");let U=$;for(let M of Object.keys(U))delete U[M];Object.assign(U,B)})();return Y.then(()=>{if(!Q)return;this.emit({op:"set",kind:this.kind,entity:$,previous:void 0})},(W)=>{this.log?.warn("authz catalog store: atomic insert-if-absent failed",{"ion.operation":`authz.catalog.${this.kind}.insert_if_absent`,"ion.outcome":"error",id:Z.id,error:W instanceof Error?W.message:String(W)})}),MZ($,Y)}async delete(Z){let $=await this.get(Z),Q=new q;if(Q.del(this.entityKey(Z)),Q.srem(this.idsSetKey(),Z),await Q.exec(this.redis),!$)return;this.emit({op:"delete",kind:this.kind,entity:$})}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return K([],Z);let Y=(await this.redis.mget($.map((W)=>this.entityKey(W)))).filter((W)=>W!=null).map((W)=>L(W,{store:`authz:${this.kind}`,log:this.log})).filter((W)=>W!==void 0).map((W)=>W);return K(Y,Z)}}import{accessRefId as E,assertRequiredSubjectType as T}from"@ionite/core";var g="__app__",OZ=100;function Z$(Z){if(!Array.isArray(Z))return[];return Z.map(($)=>$==null?"":String($)).filter(($)=>$.length>0)}var $$=`local rootId=ARGV[1]
50
+ return {1,ARGV[1]}`,[this.entityKey(Z.id),this.idsSetKey()],[W,Z.id]);if(!Array.isArray(V)||V.length<2)throw Error("RedisAccessCatalogStore.insertIfAbsent received an invalid Redis response.");Q=String(V[0])==="1";let G=String(V[1]),B=L(G,{store:`authz:${this.kind}`,key:Z.id,log:this.log});if(!B)throw Error("RedisAccessCatalogStore.insertIfAbsent could not parse the winning row.");let U=$;for(let J of Object.keys(U))delete U[J];Object.assign(U,B)})();return Y.then(()=>{if(!Q)return;this.emit({op:"set",kind:this.kind,entity:$,previous:void 0})},(W)=>{this.log?.warn("authz catalog store: atomic insert-if-absent failed",{"ion.operation":`authz.catalog.${this.kind}.insert_if_absent`,"ion.outcome":"error",id:Z.id,error:W instanceof Error?W.message:String(W)})}),OZ($,Y)}async delete(Z){let $=await this.get(Z),Q=new D;if(Q.del(this.entityKey(Z)),Q.srem(this.idsSetKey(),Z),await Q.exec(this.redis),!$)return;this.emit({op:"delete",kind:this.kind,entity:$})}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return K([],Z);let Y=(await this.redis.mget($.map((W)=>this.entityKey(W)))).filter((W)=>W!=null).map((W)=>L(W,{store:`authz:${this.kind}`,log:this.log})).filter((W)=>W!==void 0).map((W)=>W);return K(Y,Z)}}import{accessRefId as R,assertRequiredSubjectType as w}from"@ionite/core";var y="__app__",qZ=100;function $$(Z){if(!Array.isArray(Z))return[];return Z.map(($)=>$==null?"":String($)).filter(($)=>$.length>0)}var Q$=`local rootId=ARGV[1]
51
51
  local A=tonumber(ARGV[2])
52
52
  local R=tonumber(ARGV[3])
53
53
  local rootKey=KEYS[1]
@@ -96,7 +96,7 @@ for i=0,R-1 do
96
96
  end
97
97
  redis.call('SREM',rootKey,descriptor)
98
98
  end
99
- return 'ok'`,Q$=`local rootId=ARGV[1]
99
+ return 'ok'`,Y$=`local rootId=ARGV[1]
100
100
  local n=tonumber(ARGV[2])
101
101
  for i=0,n-1 do
102
102
  local kb=1+i*5
@@ -120,7 +120,7 @@ for i=0,n-1 do
120
120
  end
121
121
  end
122
122
  redis.call('DEL',KEYS[1])
123
- return 'ok'`,Y$=`local n=tonumber(ARGV[1])
123
+ return 'ok'`,W$=`local n=tonumber(ARGV[1])
124
124
  local roots={}
125
125
  local seen={}
126
126
  for i=0,n-1 do
@@ -139,8 +139,8 @@ for i=0,n-1 do
139
139
  redis.call('DEL',ak)
140
140
  end
141
141
  redis.call('DEL',KEYS[1])
142
- return roots`,W$=`for i=1,#KEYS do redis.call('DEL',KEYS[i]) end
143
- return 'ok'`,G$=`local subjectMember=ARGV[1]
142
+ return roots`,G$=`for i=1,#KEYS do redis.call('DEL',KEYS[i]) end
143
+ return 'ok'`,V$=`local subjectMember=ARGV[1]
144
144
  local C=tonumber(ARGV[2])
145
145
  local R=tonumber(ARGV[3])
146
146
  local D=tonumber(ARGV[4])
@@ -173,4 +173,4 @@ if D==0 then
173
173
  else
174
174
  redis.call('SADD',KEYS[2],subjectMember)
175
175
  end
176
- return 'ok'`;function w(Z,$){return`${$}:${Z}`}function b(Z){return Z&&Z.length>0?Z:g}function n(Z){return JSON.stringify([Z.subject,Z.targetType,Z.target,Z.rToken])}function j(Z){let $;try{$=JSON.parse(Z)}catch{$=[]}let[Q,Y,W,V]=Array.isArray($)?$:[];return{subject:Q??"",targetType:Y??"permission",target:W??"",rToken:V??g}}class zZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"access-index"),this.log=D(Z)}key(...Z){return C(this.prefix,...Z)}subjectKeyPart(Z){let $=Z.indexOf(":");if($<=0)return O(Z);return`${Z.slice(0,$)}:${O(Z.slice($+1))}`}hasKey(Z,$,Q,Y){return this.key("idx:has",this.subjectKeyPart(Z),$,O(Q),O(Y))}anyKey(Z,$,Q){return this.key("idx:any",this.subjectKeyPart(Z),$,O(Q))}edgeRootsKey(Z,$,Q,Y){return this.key("idx:er",this.subjectKeyPart(Z),$,O(Q),O(Y))}tripleKey(Z,$,Q){return this.key("idx:tr",this.subjectKeyPart(Z),$,O(Q))}rootKey(Z){return this.key("idx:root",O(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){T(W);let V=E(Z),G=E(Q);if(!V||!G)return{allow:!1};let B=w(V,W);if(Y===void 0){let[F,A]=await this.redis.mget([this.disabledKey(B),this.anyKey(B,$,G)]);if(F!=null)return{allow:!1,metadata:{reason:"subject_disabled"}};return A!=null?this.allow(V,W,$,G,void 0):{allow:!1}}let U=E(Y),M=await this.redis.mget([this.disabledKey(B),this.hasKey(B,$,G,b(U)),this.hasKey(B,$,G,g)]);if(M[0]!=null)return{allow:!1,metadata:{reason:"subject_disabled"}};if(M[1]!=null)return this.allow(V,W,$,G,U);if(M[2]!=null)return this.allow(V,W,$,G,void 0);return{allow:!1}}async hasGroup(Z,$,Q,Y){return this.has(Z,"group",$,Q,Y)}async hasRole(Z,$,Q,Y){return this.has(Z,"role",$,Q,Y)}async hasPermission(Z,$,Q,Y){return this.has(Z,"permission",$,Q,Y)}async hasAny(Z,$,Q){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){if($.length===0){let V=E(Z);if(V){if(await this.redis.get(this.disabledKey(w(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=E(Z);if(!Y)return $.map(()=>({allow:!1}));let W=w(Y,Q),V=[this.disabledKey(W)],G=$.map((U)=>{let M=E(U.target);if(!M)return{kind:"invalid"};if(U.resource===void 0){let P=V.push(this.anyKey(W,U.targetType,M))-1;return{kind:"any",targetType:U.targetType,targetId:M,idx:P}}let F=E(U.resource),A=V.push(this.hasKey(W,U.targetType,M,b(F)))-1,z=V.push(this.hasKey(W,U.targetType,M,g))-1;return{kind:"resource",targetType:U.targetType,targetId:M,resourceId:F,idxResource:A,idxApp:z}}),B=await this.redis.mget(V);if(B[0]!=null)return $.map(()=>({allow:!1,metadata:{reason:"subject_disabled"}}));return G.map((U)=>{if(U.kind==="invalid")return{allow:!1};if(U.kind==="any")return B[U.idx]!=null?this.allow(Y,Q,U.targetType,U.targetId,void 0):{allow:!1};if(B[U.idxResource]!=null)return this.allow(Y,Q,U.targetType,U.targetId,U.resourceId);if(B[U.idxApp]!=null)return this.allow(Y,Q,U.targetType,U.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 B=E(G.subject),U=E(G.target);if(!B||!U)continue;T(G.subjectType,"Effective edge subjectType");let M=w(B,G.subjectType),F=b(G.resource),A={subject:M,targetType:G.targetType,target:U,rToken:F};Q.set(n(A),A)}let Y=new Set(await this.redis.smembers(this.rootKey(Z))),W=[];for(let[G,B]of Q)if(!Y.has(G))W.push({encoded:G,d:B});let V=[];for(let G of Y)if(!Q.has(G))V.push({encoded:G,d:j(G)});if(W.length===0&&V.length===0)return;if(R(this.redis)){let G=[this.rootKey(Z),this.subjectsKey()],B=[Z,String(W.length),String(V.length)];for(let{encoded:U,d:M}of W)G.push(...this.edgeKeys(M)),B.push(U,M.rToken,M.subject);for(let{encoded:U,d:M}of V)G.push(...this.edgeKeys(M)),B.push(U,M.rToken);await this.redis.runScript($$,G,B);return}for(let{encoded:G,d:B}of W)await this.redis.sadd(this.edgeRootsKey(B.subject,B.targetType,B.target,B.rToken),Z),await this.redis.set(this.hasKey(B.subject,B.targetType,B.target,B.rToken),"1"),await this.redis.sadd(this.tripleKey(B.subject,B.targetType,B.target),B.rToken),await this.redis.set(this.anyKey(B.subject,B.targetType,B.target),"1"),await this.redis.sadd(this.rootKey(Z),G),await this.redis.sadd(this.subjectKey(B.subject),G),await this.redis.sadd(this.subjectsKey(),B.subject);for(let{encoded:G,d:B}of V)await this.dropRootFromEdge(Z,B.subject,B.targetType,B.target,B.rToken),await this.redis.srem(this.rootKey(Z),G)}async replaceSubject(Z,$,Q){if(!R(this.redis))throw Error("RedisAccessIndex.replaceSubject requires an adapter with atomic runScript support.");let Y=E(Z);if(!Y)return;let W=w(Y,$),V=[];for(let z of Q){let P=E(z.subject);T(z.subjectType,"Effective edge subjectType");let v=z.subjectType;if(P!==Y||v!==$)throw Error(`AccessIndex.replaceSubject received an edge for ${v}:${P}; expected ${W}.`);let f=E(z.target);if(!f)continue;let S={subject:W,targetType:z.targetType,target:f,rToken:b(z.resource)};V.push({rootId:z.rootId,encoded:n(S),d:S})}let B=(await this.redis.smembers(this.subjectKey(W))).map(j),U=await Promise.all(B.map((z)=>this.redis.smembers(this.edgeRootsKey(z.subject,z.targetType,z.target,z.rToken)))),M=[...new Set(U.flat())],F=[this.subjectKey(W),this.subjectsKey()];for(let z of B){let[P,v,f,S]=this.edgeKeys(z);F.push(P,v,f,S)}for(let z of M)F.push(this.rootKey(z));for(let z of V){let[P,v,f,S]=this.edgeKeys(z.d);F.push(this.rootKey(z.rootId),P,v,f,S)}let A=[W,String(B.length),String(M.length),String(V.length)];for(let z of V)A.push(z.rootId,z.encoded,z.d.rToken);await this.redis.runScript(G$,F,A)}async removeByRoot(Z){let $=this.rootKey(Z),Q=await this.redis.smembers($);if(Q.length===0){await this.redis.del($);return}if(R(this.redis)){let Y=[$],W=[Z,String(Q.length)];for(let V of Q){let G=j(V);Y.push(...this.edgeKeys(G)),W.push(V,G.rToken)}await this.redis.runScript(Q$,Y,W);return}for(let Y of Q){let{subject:W,targetType:V,target:G,rToken:B}=j(Y);await this.dropRootFromEdge(Z,W,V,G,B)}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)throw Error(`AccessIndex subject key "${Q}" is missing a subjectType prefix.`);let W=Q.slice(0,Y);T(W,"AccessIndex subject key subjectType"),$.push({subjectType:W,subject:Q.slice(Y+1)})}return $}async setSubjectDisabled(Z,$,Q){let Y=E(Z);if(!Y)return;let W=this.disabledKey(w(Y,$));if(Q)await this.redis.set(W,"1");else await this.redis.del(W)}async isSubjectDisabled(Z,$){T($);let Q=E(Z);if(!Q)return!1;return await this.redis.get(this.disabledKey(w(Q,$)))!=null}async clear(Z,$){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}T($);let Q=E(Z);if(!Q)return;let Y=w(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(R(this.redis)){let Y=[$];for(let V of Q){let{targetType:G,target:B,rToken:U}=j(V);Y.push(this.edgeRootsKey(Z,G,B,U),this.hasKey(Z,G,B,U),this.tripleKey(Z,G,B),this.anyKey(Z,G,B))}let W=Z$(await this.redis.runScript(Y$,Y,[String(Q.length)]));if(W.length>0)await this.redis.runScript(W$,W.map((V)=>this.rootKey(V)),[]);return}for(let Y of Q){let{targetType:W,target:V,rToken:G}=j(Y),B=this.edgeRootsKey(Z,W,V,G),U=await this.redis.smembers(B);for(let F of U)await this.redis.srem(this.rootKey(F),Y);if(await this.redis.del(B),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 B=n({subject:$,targetType:Q,target:Y,rToken:W});if(await this.redis.srem(this.subjectKey($),B),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,W){return{allow:!0,matched:[{subject:Z,subjectType:$,targetType:Q,target:Y,resources:W?[W]:void 0}]}}warnLargeCheck(Z,$){if($<=OZ)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:OZ})}}var V$=512;async function B$(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 B of G)W.push(B??null)}return W}import{list as U$,StoreConflictError as H$}from"@ionite/core";var J$="customer:ids",M$="customer:name:";function X$(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 o(Z,$){let Q=L(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 DZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"customer-store"),this.log=D(Z)}key(...Z){return C(this.prefix,...Z)}entityKey(Z){return this.key("customer",O(Z))}idsSetKey(){return this.key(J$)}nameIndexKey(Z){return this.key(M$+O(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return o($,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?o(W,this.log)??null:null,G=Q.userName?.toLowerCase(),B=V?.userName?.toLowerCase(),U=new q;if(G)U.claimUnique(this.nameIndexKey(G),$);if(B&&B!==G)U.del(this.nameIndexKey(B));if(U.set(Y,X$(Q)),G)U.set(this.nameIndexKey(G),$);if(U.sadd(this.idsSetKey(),$),await U.exec(this.redis)==="conflict")throw new H$(`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 q;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 U$(0,[],$.start??0,$.limit);let Y=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(Y)).filter((G)=>G!=null).map((G)=>o(G,this.log)).filter((G)=>G!=null);return K(V,$)}}import{createHash as F$}from"node:crypto";function qZ(Z){return F$("sha1").update(Z,"utf8").digest("hex")}function O$(Z){return(Z instanceof Error?Z.message:String(Z)).includes("NOSCRIPT")}function z$(Z){let $=new Map;return async(Q,Y,W)=>{let V=$.get(Q);if(!V)V=qZ(Q),$.set(Q,V);try{return await Z.evalsha(V,Y,W)}catch(G){if(O$(G))return Z.eval(Q,Y,W);throw G}}}function LZ(Z){return typeof Z==="object"&&Z!==null&&!Array.isArray(Z)}function D$(Z){let $=LZ(Z.clientOptions)?{...Z.clientOptions}:LZ(Z)?{...Z}:{};return delete $.redisUrl,delete $.clientOptions,{redisUrl:(typeof Z.redisUrl==="string"?Z.redisUrl.trim():"")||void 0,clientOptions:$}}import{defaultLogger as q$}from"@ionite/core";function _Z(Z,$){return`${Z}:${encodeURIComponent($)}`}function L$(Z,$){if(typeof Z!=="object"||Z===null||Array.isArray(Z))return!1;let Q=Z,Y=Q.headers;return Q.kind==="delivery"&&typeof Q.key==="string"&&typeof Q.rawBody==="string"&&typeof Y==="object"&&Y!==null&&Y.requestId===$&&typeof Y.keyId==="string"&&typeof Y.signature==="string"}function _$(Z,$={}){if(!Z.getdel)throw Error("Redis adapter must implement getdel() for LFV callback coordination");let Q=Z.getdel.bind(Z),Y=$.channelPrefix?.trim()||"ionite:lfv:otp",W=$.logger??q$;return{async put(V,G,B){if(G.kind!=="delivery"||G.headers.requestId!==V)throw Error("LFV callback mailbox only accepts matching delivery envelopes");let U=Math.max(1,Math.ceil(B/1000));await Z.setex(_Z(Y,V),U,JSON.stringify(G))},async take(V){let G=await Q(_Z(Y,V));if(G==null)return;let B;try{B=JSON.parse(G)}catch(U){W.warn("Invalid JSON received from LFV Redis callback mailbox",{"ion.operation":"lfv.callback.redis.take","ion.outcome":"rejected",requestId:V,error:U instanceof Error?U.message:String(U)});return}if(!L$(B,V)){W.warn("Invalid envelope received from LFV Redis callback mailbox",{"ion.operation":"lfv.callback.redis.take","ion.outcome":"rejected",requestId:V});return}return B}}}import{StoreConflictError as C$}from"@ionite/core";function N$(Z){return JSON.stringify({token:Z.token,customer:Z.customer,createdAt:Z.createdAt.toISOString(),expiresAt:Z.expiresAt.toISOString()})}function CZ(Z,$){let Q=L(Z,{store:"magic-link",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),expiresAt:new Date(Q.expiresAt)}}class NZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"magic-link-store"),this.log=D(Z)}key(Z){return C(this.prefix,"magic",O(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 m(this.redis,Y,N$(W),{ttlSeconds:V})==="conflict")throw new C$(`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 CZ($,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=CZ(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 A$}from"@ionite/core";function P$(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 K$(){let Z,$;return{promise:new Promise((Y,W)=>{Z=Y,$=W}),resolve:Z,reject:$}}function AZ(Z){return typeof Z==="function"}function E$(Z){let $=Z.logger??A$,Q=Z.sourceName?.trim()||"redis",Y=Z.path.trim(),W,V,G=!1,B,U,M,F=!1,A=!1,z=!1,P=new Map,v=Promise.resolve(),f=K$();f.promise.catch(()=>{});let S={async get(H){return k((J)=>J.get(H))},async set(H,J){await k((X)=>X.set(H,J))},async setex(H,J,X){await k((N)=>N.setex(H,J,X))},async del(H){await k((J)=>J.del(H))},async sadd(H,...J){await k((X)=>X.sadd(H,...J))},async srem(H,...J){await k((X)=>X.srem(H,...J))},async smembers(H){return k((J)=>J.smembers(H))},async mget(H){return k((J)=>J.mget(H))},async publish(H,J){await k(async(X)=>{if(!X.publish)throw Error("Active Redis adapter does not support publish (Pub/Sub)");await X.publish(H,J)})},async subscribeChannel(H,J){let X=P.get(H);if(!X){let x=new Set;X={listeners:x,dispatch:(bZ)=>{for(let gZ of Array.from(x))try{gZ(bZ)}catch(u){$.warn("Redis channel listener threw",{"ion.operation":"redis.adapter.subscribe","ion.outcome":"failure",sourceName:Q,path:Y,channel:H,error:u instanceof Error?u.message:String(u)})}}},P.set(H,X)}let N=X;N.listeners.add(J);try{await TZ(H,N)}catch(x){if(N.listeners.delete(J),N.listeners.size===0)P.delete(H);throw x}return async()=>{if(N.listeners.delete(J),N.listeners.size===0){P.delete(H);let x=N.unsubscribe;N.unsubscribe=void 0,await x?.()}}},ready(H){return P$(f.promise,H)},isReady(){return W!==void 0},async close(){if(F=!0,e(),!G)G=!0,f.reject(Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" was closed before becoming ready.`));await v.catch(()=>{}),await y(W),W=void 0;for(let H of P.values())H.unsubscribe=void 0;P.clear(),l()}};function r(H){v=v.then(H).catch((J)=>{if(!G)G=!0,f.reject(J);$.warn("Reactive Redis adapter update failed",{"ion.operation":"redis.adapter.update","ion.outcome":"degraded",sourceName:Q,path:Y,error:J instanceof Error?J.message:String(J)})})}async function t(){if(F)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" is closed.`);let H=W?.adapter;if(H)return H;if(await v.catch(()=>{}),F)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" is closed.`);let J=W?.adapter;if(J)return J;if(await f.promise,F)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" is closed.`);let X=W?.adapter;if(!X)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" did not become ready.`);return X}async function k(H){let J=await t();return H(J)}function e(){V?.(),V=void 0}async function TZ(H,J){if(J.unsubscribe)return;if(!J.attaching)J.attaching=(async()=>{let X=await t();if(!X.subscribeChannel)throw Error("Active Redis adapter does not support subscribeChannel (Pub/Sub)");if(J.listeners.size===0)return;J.unsubscribe=await X.subscribeChannel(H,J.dispatch)})().finally(()=>{J.attaching=void 0});await J.attaching}async function jZ(H){for(let[J,X]of P){if(X.attaching)await X.attaching.catch(()=>{});if(X.unsubscribe=void 0,!H.subscribeChannel){$.warn("Active Redis adapter does not support subscribeChannel; channel subscriptions dropped.",{"ion.operation":"redis.adapter.capabilities","ion.outcome":"degraded",sourceName:Q,path:Y,capability:"subscribeChannel"});return}try{X.unsubscribe=await H.subscribeChannel(J,X.dispatch)}catch(N){$.warn("Failed to re-subscribe Redis channel after client rotation",{"ion.operation":"redis.adapter.subscribe","ion.outcome":"failure",sourceName:Q,path:Y,channel:J,error:N instanceof Error?N.message:String(N)})}}}async function mZ(H){e(),V=await H.subscribe(Y,(J)=>{r(async()=>{if(F)return;await IZ(J)})},Z.subscription??Z.secretOptions)}async function IZ(H){if(F)return;let J=`${Q}:${Y}:${H.metadata.version}`;if(W!==void 0&&U===J&&B===H.metadata.version)return;let X=Z.endpointOf?.(H);if(W!==void 0&&X!=null&&X!==""&&M!=null&&M!==""&&X!==M)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" rejected a rotation that changed the endpoint (${M} -> ${X}); only credentials may rotate under an active adapter.`);let N=await Z.createClient(H);if(F){await y(N);return}let x=W;if(W=N,U=J,B=H.metadata.version,X!=null&&X!=="")M=X;if(l(),x!==void 0)await jZ(N.adapter);if(!G)G=!0,f.resolve();await y(x)}async function y(H){if(!H?.dispose)return;await H.dispose()}function l(){let H=W?.adapter;if(H?.runScript&&AZ(H.runScript))S.runScript=async(J,X,N)=>{return k(async(x)=>{if(!x.runScript)throw Error("Active Redis adapter does not support runScript");return x.runScript(J,X,N)})};else if(delete S.runScript,H!==void 0&&!A)A=!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&&AZ(H.getdel))S.getdel=async(J)=>{return k(async(X)=>{if(!X.getdel)return null;return X.getdel(J)})};else if(delete S.getdel,H!==void 0&&!z)z=!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 l(),r(async()=>{if(F)return;await mZ(Z.source)}),S}import{StoreConflictError as R$}from"@ionite/core";var f$=5;function S$(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 PZ(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString()})}function KZ(Z,$){let Q=L(Z,{store:"session",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt)}}function EZ(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 RZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"session-store"),this.log=D(Z)}key(Z){return C(this.prefix,"session",O(Z))}async create(Z){let $=this.key(Z.sid),Q=EZ(Z.expiresAt);if(await m(this.redis,$,PZ(Z),{ttlSeconds:Q})==="conflict")throw new R$(`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 KZ($,this.log)??null}async update(Z,$){let Q=this.key(Z),Y=JSON.parse(JSON.stringify($)),W="expiresAt"in Y?EZ(Y.expiresAt):void 0;if(R(this.redis))for(let M=0;M<f$;M++){let F=await this.redis.get(Q);if(F==null)throw Error(`Session with sid ${Z} not found`);let A=S$(F,Y);if(A===void 0)throw Error(`Session with sid ${Z} not found`);let z=await c(this.redis,Q,F,A,W);if(z==="ok")return;if(z==="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=KZ(V,this.log);if(G==null)throw Error(`Session with sid ${Z} not found`);let B={...G,...$},U=PZ(B);if(W!=null&&W>0)await this.redis.setex(Q,Math.max(1,Math.floor(W)),U);else await this.redis.set(Q,U)}async delete(Z){await this.redis.del(this.key(Z))}}var x$="tenant:ids";function v$(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString(),updatedAt:Z.updatedAt.toISOString()})}function fZ(Z,$){let Q=L(Z,{store:"tenant",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),updatedAt:new Date(Q.updatedAt)}}class SZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"tenant-store"),this.log=D(Z)}key(...Z){return C(this.prefix,...Z)}entityKey(Z){return this.key("tenant",O(Z))}idsSetKey(){return this.key(x$)}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return fZ($,this.log)}async upsert(Z){return await new q().set(this.entityKey(Z.id),v$(Z)).sadd(this.idsSetKey(),Z.id).exec(this.redis),Z}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return K([],Z);let Q=$.map((V)=>this.entityKey(V)),W=(await this.redis.mget(Q)).map((V)=>V!=null?fZ(V,this.log):void 0).filter((V)=>V!=null);return K(W,Z)}async delete(Z){let $=await this.get(Z);return await new q().srem(this.idsSetKey(),Z).del(this.entityKey(Z)).exec(this.redis),$?1:0}}class xZ{store;ionCache;constructor(Z){this.ionCache=Z.ionCache,this.store=new SZ(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 k$,StoreConflictError as vZ}from"@ionite/core";var h$="user:ids",w$="user:name:",T$="user:external-id:";function j$(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 a(Z,$){let Q=L(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 kZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"user-store"),this.log=D(Z)}key(...Z){return C(this.prefix,...Z)}entityKey(Z){return this.key("user",O(Z))}idsSetKey(){return this.key(h$)}nameIndexKey(Z){return this.key(w$+O(Z))}externalIdIndexKey(Z){return this.key(T$+O(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return a($,this.log)}async getByExternalId(Z){let $=await this.redis.get(this.externalIdIndexKey(Z));return $?this.get($):void 0}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?a(W,this.log)??null:null,G=Q.userName?.toLowerCase(),B=V?.userName?.toLowerCase(),U=Q.externalId,M=V?.externalId,F=new q;if(G)F.claimUnique(this.nameIndexKey(G),$);if(U)F.claimUnique(this.externalIdIndexKey(U),$);if(B&&B!==G)F.del(this.nameIndexKey(B));if(M&&M!==U)F.del(this.externalIdIndexKey(M));if(F.set(Y,j$(Q)),G)F.set(this.nameIndexKey(G),$);if(U)F.set(this.externalIdIndexKey(U),$);if(F.sadd(this.idsSetKey(),$),await F.exec(this.redis)==="conflict"){if(U){let A=await this.redis.get(this.externalIdIndexKey(U));if(A&&A!==$)throw new vZ(`externalId "${U}" is already in use`,"unique_violation","externalId")}throw new vZ(`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 q;if(Q.del(this.entityKey(Z)),$.userName)Q.del(this.nameIndexKey($.userName.toLowerCase()));if($.externalId)Q.del(this.externalIdIndexKey($.externalId));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 k$(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 K(V,$)}}import{list as m$,StoreConflictError as I$}from"@ionite/core";var b$="workload-identity:ids",g$="workload-identity:client:";function y$(Z){let $=Z.createdAt??new Date,Q=Z.updatedAt??$;return JSON.stringify({...Z,createdAt:$.toISOString(),updatedAt:Q.toISOString()})}function s(Z,$){let Q=L(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 hZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"workload-identity-store"),this.log=D(Z)}key(...Z){return C(this.prefix,...Z)}entityKey(Z){return this.key("workload-identity",O(Z))}idsSetKey(){return this.key(b$)}clientIndexKey(Z){return this.key(g$+O(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return s($,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=s(Y,this.log)??null;let V=new Date,G={...Z,id:$,createdAt:W?.createdAt??Z.createdAt??V,updatedAt:V},B=new q;if(G.clientId)B.claimUnique(this.clientIndexKey(G.clientId),$);if(W?.clientId&&W.clientId!==G.clientId)B.del(this.clientIndexKey(W.clientId));if(B.set(Q,y$(G)),G.clientId)B.set(this.clientIndexKey(G.clientId),$);if(B.sadd(this.idsSetKey(),$),await B.exec(this.redis)==="conflict")throw new I$(`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 q;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 m$(0,[],$.start??0,$.limit);let Y=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(Y)).filter((G)=>G!=null).map((G)=>s(G,this.log)).filter((G)=>G!=null);return K(V,$)}}function l$(Z){return JSON.stringify({...Z,expires_at:Z.expires_at.toISOString(),created_at:Z.created_at.toISOString()})}function u$(Z,$){let Q=L(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 wZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"workload-token-store"),this.log=D(Z)}key(Z){return C(this.prefix,"workload","token",O(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,l$(Z))}async get(Z){let $=await this.redis.get(this.key(Z));if($==null)return null;let Q=u$($,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{R as supportsRunScript,D$ as splitIonRedisSecret,qZ as scriptSha1,C as prefixedKey,D as getLogger,_ as getKeyPrefix,O as encodeKeyId,_$ as createRedisLfvTransport,E$ as createReactiveRedis,z$ as createEvalshaRunner,B$ as chunkedMget,m as atomicCreate,c as atomicCompareAndSet,wZ as RedisWorkloadTokenStore,hZ as RedisWorkloadIdentityStore,kZ as RedisUserStore,q as RedisTxn,xZ as RedisTenantStore,RZ as RedisSessionStore,FZ as RedisResourceStore,NZ as RedisMagicLinkStore,DZ as RedisCustomerStore,zZ as RedisAccessIndex,XZ as RedisAccessCatalogStore,JZ as RedisAccessAssignmentStore,QZ as MAX_LIST_LIMIT,V$ as DEFAULT_MGET_CHUNK_SIZE,$Z as DEFAULT_LIST_LIMIT};
176
+ return 'ok'`;function T(Z,$){return`${$}:${Z}`}function b(Z){return Z&&Z.length>0?Z:y}function o(Z){return JSON.stringify([Z.subject,Z.targetType,Z.target,Z.rToken])}function j(Z){let $;try{$=JSON.parse(Z)}catch{$=[]}let[Q,Y,W,V]=Array.isArray($)?$:[];return{subject:Q??"",targetType:Y??"permission",target:W??"",rToken:V??y}}class FZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"access-index"),this.log=F(Z)}key(...Z){return C(this.prefix,...Z)}subjectKeyPart(Z){let $=Z.indexOf(":");if($<=0)return X(Z);return`${Z.slice(0,$)}:${X(Z.slice($+1))}`}hasKey(Z,$,Q,Y){return this.key("idx:has",this.subjectKeyPart(Z),$,X(Q),X(Y))}anyKey(Z,$,Q){return this.key("idx:any",this.subjectKeyPart(Z),$,X(Q))}edgeRootsKey(Z,$,Q,Y){return this.key("idx:er",this.subjectKeyPart(Z),$,X(Q),X(Y))}tripleKey(Z,$,Q){return this.key("idx:tr",this.subjectKeyPart(Z),$,X(Q))}rootKey(Z){return this.key("idx:root",X(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){w(W);let V=R(Z),G=R(Q);if(!V||!G)return{allow:!1};let B=T(V,W);if(Y===void 0){let[O,A]=await this.redis.mget([this.disabledKey(B),this.anyKey(B,$,G)]);if(O!=null)return{allow:!1,metadata:{reason:"subject_disabled"}};return A!=null?this.allow(V,W,$,G,void 0):{allow:!1}}let U=R(Y),J=await this.redis.mget([this.disabledKey(B),this.hasKey(B,$,G,b(U)),this.hasKey(B,$,G,y)]);if(J[0]!=null)return{allow:!1,metadata:{reason:"subject_disabled"}};if(J[1]!=null)return this.allow(V,W,$,G,U);if(J[2]!=null)return this.allow(V,W,$,G,void 0);return{allow:!1}}async hasGroup(Z,$,Q,Y){return this.has(Z,"group",$,Q,Y)}async hasRole(Z,$,Q,Y){return this.has(Z,"role",$,Q,Y)}async hasPermission(Z,$,Q,Y){return this.has(Z,"permission",$,Q,Y)}async hasAny(Z,$,Q){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){if($.length===0){let V=R(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=R(Z);if(!Y)return $.map(()=>({allow:!1}));let W=T(Y,Q),V=[this.disabledKey(W)],G=$.map((U)=>{let J=R(U.target);if(!J)return{kind:"invalid"};if(U.resource===void 0){let P=V.push(this.anyKey(W,U.targetType,J))-1;return{kind:"any",targetType:U.targetType,targetId:J,idx:P}}let O=R(U.resource),A=V.push(this.hasKey(W,U.targetType,J,b(O)))-1,q=V.push(this.hasKey(W,U.targetType,J,y))-1;return{kind:"resource",targetType:U.targetType,targetId:J,resourceId:O,idxResource:A,idxApp:q}}),B=await this.redis.mget(V);if(B[0]!=null)return $.map(()=>({allow:!1,metadata:{reason:"subject_disabled"}}));return G.map((U)=>{if(U.kind==="invalid")return{allow:!1};if(U.kind==="any")return B[U.idx]!=null?this.allow(Y,Q,U.targetType,U.targetId,void 0):{allow:!1};if(B[U.idxResource]!=null)return this.allow(Y,Q,U.targetType,U.targetId,U.resourceId);if(B[U.idxApp]!=null)return this.allow(Y,Q,U.targetType,U.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 B=R(G.subject),U=R(G.target);if(!B||!U)continue;w(G.subjectType,"Effective edge subjectType");let J=T(B,G.subjectType),O=b(G.resource),A={subject:J,targetType:G.targetType,target:U,rToken:O};Q.set(o(A),A)}let Y=new Set(await this.redis.smembers(this.rootKey(Z))),W=[];for(let[G,B]of Q)if(!Y.has(G))W.push({encoded:G,d:B});let V=[];for(let G of Y)if(!Q.has(G))V.push({encoded:G,d:j(G)});if(W.length===0&&V.length===0)return;if(E(this.redis)){let G=[this.rootKey(Z),this.subjectsKey()],B=[Z,String(W.length),String(V.length)];for(let{encoded:U,d:J}of W)G.push(...this.edgeKeys(J)),B.push(U,J.rToken,J.subject);for(let{encoded:U,d:J}of V)G.push(...this.edgeKeys(J)),B.push(U,J.rToken);await this.redis.runScript(Q$,G,B);return}for(let{encoded:G,d:B}of W)await this.redis.sadd(this.edgeRootsKey(B.subject,B.targetType,B.target,B.rToken),Z),await this.redis.set(this.hasKey(B.subject,B.targetType,B.target,B.rToken),"1"),await this.redis.sadd(this.tripleKey(B.subject,B.targetType,B.target),B.rToken),await this.redis.set(this.anyKey(B.subject,B.targetType,B.target),"1"),await this.redis.sadd(this.rootKey(Z),G),await this.redis.sadd(this.subjectKey(B.subject),G),await this.redis.sadd(this.subjectsKey(),B.subject);for(let{encoded:G,d:B}of V)await this.dropRootFromEdge(Z,B.subject,B.targetType,B.target,B.rToken),await this.redis.srem(this.rootKey(Z),G)}async replaceSubject(Z,$,Q){if(!E(this.redis))throw Error("RedisAccessIndex.replaceSubject requires an adapter with atomic runScript support.");let Y=R(Z);if(!Y)return;let W=T(Y,$),V=[];for(let q of Q){let P=R(q.subject);w(q.subjectType,"Effective edge subjectType");let k=q.subjectType;if(P!==Y||k!==$)throw Error(`AccessIndex.replaceSubject received an edge for ${k}:${P}; expected ${W}.`);let f=R(q.target);if(!f)continue;let S={subject:W,targetType:q.targetType,target:f,rToken:b(q.resource)};V.push({rootId:q.rootId,encoded:o(S),d:S})}let B=(await this.redis.smembers(this.subjectKey(W))).map(j),U=await Promise.all(B.map((q)=>this.redis.smembers(this.edgeRootsKey(q.subject,q.targetType,q.target,q.rToken)))),J=[...new Set(U.flat())],O=[this.subjectKey(W),this.subjectsKey()];for(let q of B){let[P,k,f,S]=this.edgeKeys(q);O.push(P,k,f,S)}for(let q of J)O.push(this.rootKey(q));for(let q of V){let[P,k,f,S]=this.edgeKeys(q.d);O.push(this.rootKey(q.rootId),P,k,f,S)}let A=[W,String(B.length),String(J.length),String(V.length)];for(let q of V)A.push(q.rootId,q.encoded,q.d.rToken);await this.redis.runScript(V$,O,A)}async removeByRoot(Z){let $=this.rootKey(Z),Q=await this.redis.smembers($);if(Q.length===0){await this.redis.del($);return}if(E(this.redis)){let Y=[$],W=[Z,String(Q.length)];for(let V of Q){let G=j(V);Y.push(...this.edgeKeys(G)),W.push(V,G.rToken)}await this.redis.runScript(Y$,Y,W);return}for(let Y of Q){let{subject:W,targetType:V,target:G,rToken:B}=j(Y);await this.dropRootFromEdge(Z,W,V,G,B)}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)throw Error(`AccessIndex subject key "${Q}" is missing a subjectType prefix.`);let W=Q.slice(0,Y);w(W,"AccessIndex subject key subjectType"),$.push({subjectType:W,subject:Q.slice(Y+1)})}return $}async setSubjectDisabled(Z,$,Q){let Y=R(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,$){w($);let Q=R(Z);if(!Q)return!1;return await this.redis.get(this.disabledKey(T(Q,$)))!=null}async clear(Z,$){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}w($);let Q=R(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(E(this.redis)){let Y=[$];for(let V of Q){let{targetType:G,target:B,rToken:U}=j(V);Y.push(this.edgeRootsKey(Z,G,B,U),this.hasKey(Z,G,B,U),this.tripleKey(Z,G,B),this.anyKey(Z,G,B))}let W=$$(await this.redis.runScript(W$,Y,[String(Q.length)]));if(W.length>0)await this.redis.runScript(G$,W.map((V)=>this.rootKey(V)),[]);return}for(let Y of Q){let{targetType:W,target:V,rToken:G}=j(Y),B=this.edgeRootsKey(Z,W,V,G),U=await this.redis.smembers(B);for(let O of U)await this.redis.srem(this.rootKey(O),Y);if(await this.redis.del(B),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 B=o({subject:$,targetType:Q,target:Y,rToken:W});if(await this.redis.srem(this.subjectKey($),B),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,W){return{allow:!0,matched:[{subject:Z,subjectType:$,targetType:Q,target:Y,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 B$=512;async function U$(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 B of G)W.push(B??null)}return W}import{list as H$,StoreConflictError as J$}from"@ionite/core";var M$="customer:ids",O$="customer:name:",z$="customer:external:";function X$(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=L(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 DZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"customer-store"),this.log=F(Z)}key(...Z){return C(this.prefix,...Z)}entityKey(Z){return this.key("customer",X(Z))}idsSetKey(){return this.key(M$)}nameIndexKey(Z){return this.key(O$+X(Z))}externalIdIndexKey(Z){return this.key(z$+X(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($)}if(Z.externalId)return this.getByExternalId(Z.externalId);return}async getByExternalId(Z){let $=await this.redis.get(this.externalIdIndexKey(Z));if($==null)return;return this.get($)}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(),B=V?.userName?.toLowerCase(),U=Q.externalId,J=V?.externalId,O=new D;if(G)O.claimUnique(this.nameIndexKey(G),$);if(U)O.claimUnique(this.externalIdIndexKey(U),$);if(B&&B!==G)O.del(this.nameIndexKey(B));if(J&&J!==U)O.del(this.externalIdIndexKey(J));if(O.set(Y,X$(Q)),G)O.set(this.nameIndexKey(G),$);if(U)O.set(this.externalIdIndexKey(U),$);if(O.sadd(this.idsSetKey(),$),await O.exec(this.redis)==="conflict")throw new J$(`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 D;if(Q.del(this.entityKey(Z)),$.userName)Q.del(this.nameIndexKey($.userName.toLowerCase()));if($.externalId)Q.del(this.externalIdIndexKey($.externalId));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 H$(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 K(V,$)}}import{createHash as q$}from"node:crypto";function LZ(Z){return q$("sha1").update(Z,"utf8").digest("hex")}function F$(Z){return(Z instanceof Error?Z.message:String(Z)).includes("NOSCRIPT")}function D$(Z){let $=new Map;return async(Q,Y,W)=>{let V=$.get(Q);if(!V)V=LZ(Q),$.set(Q,V);try{return await Z.evalsha(V,Y,W)}catch(G){if(F$(G))return Z.eval(Q,Y,W);throw G}}}function _Z(Z){return typeof Z==="object"&&Z!==null&&!Array.isArray(Z)}function L$(Z){let $=_Z(Z.clientOptions)?{...Z.clientOptions}:_Z(Z)?{...Z}:{};return delete $.redisUrl,delete $.clientOptions,{redisUrl:(typeof Z.redisUrl==="string"?Z.redisUrl.trim():"")||void 0,clientOptions:$}}import{defaultLogger as _$}from"@ionite/core";function CZ(Z,$){return`${Z}:${encodeURIComponent($)}`}function C$(Z,$){if(typeof Z!=="object"||Z===null||Array.isArray(Z))return!1;let Q=Z,Y=Q.headers;return Q.kind==="delivery"&&typeof Q.key==="string"&&typeof Q.rawBody==="string"&&typeof Y==="object"&&Y!==null&&Y.requestId===$&&typeof Y.keyId==="string"&&typeof Y.signature==="string"}function N$(Z,$={}){if(!Z.getdel)throw Error("Redis adapter must implement getdel() for LFV callback coordination");let Q=Z.getdel.bind(Z),Y=$.channelPrefix?.trim()||"ionite:lfv:otp",W=$.logger??_$;return{async put(V,G,B){if(G.kind!=="delivery"||G.headers.requestId!==V)throw Error("LFV callback mailbox only accepts matching delivery envelopes");let U=Math.max(1,Math.ceil(B/1000));await Z.setex(CZ(Y,V),U,JSON.stringify(G))},async take(V){let G=await Q(CZ(Y,V));if(G==null)return;let B;try{B=JSON.parse(G)}catch(U){W.warn("Invalid JSON received from LFV Redis callback mailbox",{"ion.operation":"lfv.callback.redis.take","ion.outcome":"rejected",requestId:V,error:U instanceof Error?U.message:String(U)});return}if(!C$(B,V)){W.warn("Invalid envelope received from LFV Redis callback mailbox",{"ion.operation":"lfv.callback.redis.take","ion.outcome":"rejected",requestId:V});return}return B}}}import{StoreConflictError as A$}from"@ionite/core";function P$(Z){return JSON.stringify({token:Z.token,customer:Z.customer,createdAt:Z.createdAt.toISOString(),expiresAt:Z.expiresAt.toISOString()})}function NZ(Z,$){let Q=L(Z,{store:"magic-link",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),expiresAt:new Date(Q.expiresAt)}}class AZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"magic-link-store"),this.log=F(Z)}key(Z){return C(this.prefix,"magic",X(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 m(this.redis,Y,P$(W),{ttlSeconds:V})==="conflict")throw new A$(`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 NZ($,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=NZ(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 K$}from"@ionite/core";function R$(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 E$(){let Z,$;return{promise:new Promise((Y,W)=>{Z=Y,$=W}),resolve:Z,reject:$}}function PZ(Z){return typeof Z==="function"}function f$(Z){let $=Z.logger??K$,Q=Z.sourceName?.trim()||"redis",Y=Z.path.trim(),W,V,G=!1,B,U,J,O=!1,A=!1,q=!1,P=new Map,k=Promise.resolve(),f=E$();f.promise.catch(()=>{});let S={async get(H){return x((M)=>M.get(H))},async set(H,M){await x((z)=>z.set(H,M))},async setex(H,M,z){await x((N)=>N.setex(H,M,z))},async del(H){await x((M)=>M.del(H))},async sadd(H,...M){await x((z)=>z.sadd(H,...M))},async srem(H,...M){await x((z)=>z.srem(H,...M))},async smembers(H){return x((M)=>M.smembers(H))},async mget(H){return x((M)=>M.mget(H))},async publish(H,M){await x(async(z)=>{if(!z.publish)throw Error("Active Redis adapter does not support publish (Pub/Sub)");await z.publish(H,M)})},async subscribeChannel(H,M){let z=P.get(H);if(!z){let v=new Set;z={listeners:v,dispatch:(yZ)=>{for(let gZ of Array.from(v))try{gZ(yZ)}catch(u){$.warn("Redis channel listener threw",{"ion.operation":"redis.adapter.subscribe","ion.outcome":"failure",sourceName:Q,path:Y,channel:H,error:u instanceof Error?u.message:String(u)})}}},P.set(H,z)}let N=z;N.listeners.add(M);try{await jZ(H,N)}catch(v){if(N.listeners.delete(M),N.listeners.size===0)P.delete(H);throw v}return async()=>{if(N.listeners.delete(M),N.listeners.size===0){P.delete(H);let v=N.unsubscribe;N.unsubscribe=void 0,await v?.()}}},ready(H){return R$(f.promise,H)},isReady(){return W!==void 0},async close(){if(O=!0,e(),!G)G=!0,f.reject(Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" was closed before becoming ready.`));await k.catch(()=>{}),await g(W),W=void 0;for(let H of P.values())H.unsubscribe=void 0;P.clear(),l()}};function r(H){k=k.then(H).catch((M)=>{if(!G)G=!0,f.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 t(){if(O)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(()=>{}),O)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" is closed.`);let M=W?.adapter;if(M)return M;if(await f.promise,O)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" is closed.`);let z=W?.adapter;if(!z)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" did not become ready.`);return z}async function x(H){let M=await t();return H(M)}function e(){V?.(),V=void 0}async function jZ(H,M){if(M.unsubscribe)return;if(!M.attaching)M.attaching=(async()=>{let z=await t();if(!z.subscribeChannel)throw Error("Active Redis adapter does not support subscribeChannel (Pub/Sub)");if(M.listeners.size===0)return;M.unsubscribe=await z.subscribeChannel(H,M.dispatch)})().finally(()=>{M.attaching=void 0});await M.attaching}async function mZ(H){for(let[M,z]of P){if(z.attaching)await z.attaching.catch(()=>{});if(z.unsubscribe=void 0,!H.subscribeChannel){$.warn("Active Redis adapter does not support subscribeChannel; channel subscriptions dropped.",{"ion.operation":"redis.adapter.capabilities","ion.outcome":"degraded",sourceName:Q,path:Y,capability:"subscribeChannel"});return}try{z.unsubscribe=await H.subscribeChannel(M,z.dispatch)}catch(N){$.warn("Failed to re-subscribe Redis channel after client rotation",{"ion.operation":"redis.adapter.subscribe","ion.outcome":"failure",sourceName:Q,path:Y,channel:M,error:N instanceof Error?N.message:String(N)})}}}async function IZ(H){e(),V=await H.subscribe(Y,(M)=>{r(async()=>{if(O)return;await bZ(M)})},Z.subscription??Z.secretOptions)}async function bZ(H){if(O)return;let M=`${Q}:${Y}:${H.metadata.version}`;if(W!==void 0&&U===M&&B===H.metadata.version)return;let z=Z.endpointOf?.(H);if(W!==void 0&&z!=null&&z!==""&&J!=null&&J!==""&&z!==J)throw Error(`Reactive Redis adapter for source "${Q}" at path "${Y}" rejected a rotation that changed the endpoint (${J} -> ${z}); only credentials may rotate under an active adapter.`);let N=await Z.createClient(H);if(O){await g(N);return}let v=W;if(W=N,U=M,B=H.metadata.version,z!=null&&z!=="")J=z;if(l(),v!==void 0)await mZ(N.adapter);if(!G)G=!0,f.resolve();await g(v)}async function g(H){if(!H?.dispose)return;await H.dispose()}function l(){let H=W?.adapter;if(H?.runScript&&PZ(H.runScript))S.runScript=async(M,z,N)=>{return x(async(v)=>{if(!v.runScript)throw Error("Active Redis adapter does not support runScript");return v.runScript(M,z,N)})};else if(delete S.runScript,H!==void 0&&!A)A=!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&&PZ(H.getdel))S.getdel=async(M)=>{return x(async(z)=>{if(!z.getdel)return null;return z.getdel(M)})};else if(delete S.getdel,H!==void 0&&!q)q=!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 l(),r(async()=>{if(O)return;await IZ(Z.source)}),S}import{StoreConflictError as S$}from"@ionite/core";var v$=5;function k$(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 KZ(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString()})}function RZ(Z,$){let Q=L(Z,{store:"session",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt)}}function EZ(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 fZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"session-store"),this.log=F(Z)}key(Z){return C(this.prefix,"session",X(Z))}async create(Z){let $=this.key(Z.sid),Q=EZ(Z.expiresAt);if(await m(this.redis,$,KZ(Z),{ttlSeconds:Q})==="conflict")throw new S$(`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 RZ($,this.log)??null}async update(Z,$){let Q=this.key(Z),Y=JSON.parse(JSON.stringify($)),W="expiresAt"in Y?EZ(Y.expiresAt):void 0;if(E(this.redis))for(let J=0;J<v$;J++){let O=await this.redis.get(Q);if(O==null)throw Error(`Session with sid ${Z} not found`);let A=k$(O,Y);if(A===void 0)throw Error(`Session with sid ${Z} not found`);let q=await c(this.redis,Q,O,A,W);if(q==="ok")return;if(q==="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=RZ(V,this.log);if(G==null)throw Error(`Session with sid ${Z} not found`);let B={...G,...$},U=KZ(B);if(W!=null&&W>0)await this.redis.setex(Q,Math.max(1,Math.floor(W)),U);else await this.redis.set(Q,U)}async delete(Z){await this.redis.del(this.key(Z))}}var x$="tenant:ids";function h$(Z){return JSON.stringify({...Z,createdAt:Z.createdAt.toISOString(),updatedAt:Z.updatedAt.toISOString()})}function SZ(Z,$){let Q=L(Z,{store:"tenant",log:$});if(Q===void 0)return;return{...Q,createdAt:new Date(Q.createdAt),updatedAt:new Date(Q.updatedAt)}}class vZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"tenant-store"),this.log=F(Z)}key(...Z){return C(this.prefix,...Z)}entityKey(Z){return this.key("tenant",X(Z))}idsSetKey(){return this.key(x$)}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return SZ($,this.log)}async upsert(Z){return await new D().set(this.entityKey(Z.id),h$(Z)).sadd(this.idsSetKey(),Z.id).exec(this.redis),Z}async list(Z){let $=await this.redis.smembers(this.idsSetKey());if($.length===0)return K([],Z);let Q=$.map((V)=>this.entityKey(V)),W=(await this.redis.mget(Q)).map((V)=>V!=null?SZ(V,this.log):void 0).filter((V)=>V!=null);return K(W,Z)}async delete(Z){let $=await this.get(Z);return await new D().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 vZ(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 T$,StoreConflictError as xZ}from"@ionite/core";var w$="user:ids",j$="user:name:",m$="user:external-id:";function I$(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 a(Z,$){let Q=L(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 hZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"user-store"),this.log=F(Z)}key(...Z){return C(this.prefix,...Z)}entityKey(Z){return this.key("user",X(Z))}idsSetKey(){return this.key(w$)}nameIndexKey(Z){return this.key(j$+X(Z))}externalIdIndexKey(Z){return this.key(m$+X(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return a($,this.log)}async getByExternalId(Z){let $=await this.redis.get(this.externalIdIndexKey(Z));return $?this.get($):void 0}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?a(W,this.log)??null:null,G=Q.userName?.toLowerCase(),B=V?.userName?.toLowerCase(),U=Q.externalId,J=V?.externalId,O=new D;if(G)O.claimUnique(this.nameIndexKey(G),$);if(U)O.claimUnique(this.externalIdIndexKey(U),$);if(B&&B!==G)O.del(this.nameIndexKey(B));if(J&&J!==U)O.del(this.externalIdIndexKey(J));if(O.set(Y,I$(Q)),G)O.set(this.nameIndexKey(G),$);if(U)O.set(this.externalIdIndexKey(U),$);if(O.sadd(this.idsSetKey(),$),await O.exec(this.redis)==="conflict"){if(U){let A=await this.redis.get(this.externalIdIndexKey(U));if(A&&A!==$)throw new xZ(`externalId "${U}" is already in use`,"unique_violation","externalId")}throw new xZ(`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 D;if(Q.del(this.entityKey(Z)),$.userName)Q.del(this.nameIndexKey($.userName.toLowerCase()));if($.externalId)Q.del(this.externalIdIndexKey($.externalId));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 T$(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 K(V,$)}}import{list as b$,StoreConflictError as y$}from"@ionite/core";var g$="workload-identity:ids",l$="workload-identity:client:",u$="workload-identity:external:";function p$(Z){let $=Z.createdAt??new Date,Q=Z.updatedAt??$;return JSON.stringify({...Z,createdAt:$.toISOString(),updatedAt:Q.toISOString()})}function s(Z,$){let Q=L(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 TZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"workload-identity-store"),this.log=F(Z)}key(...Z){return C(this.prefix,...Z)}entityKey(Z){return this.key("workload-identity",X(Z))}idsSetKey(){return this.key(g$)}clientIndexKey(Z){return this.key(l$+X(Z))}externalIdIndexKey(Z){return this.key(u$+X(Z))}async get(Z){let $=await this.redis.get(this.entityKey(Z));if($==null)return;return s($,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($)}if(Z.externalId)return this.getByExternalId(Z.externalId);return}async getByExternalId(Z){let $=await this.redis.get(this.externalIdIndexKey(Z));if($==null)return;return this.get($)}async upsert(Z){let $=Z.id?.trim()||crypto.randomUUID(),Q=this.entityKey($),Y=await this.redis.get(Q),W=null;if(Y!=null)W=s(Y,this.log)??null;let V=new Date,G={...Z,id:$,createdAt:W?.createdAt??Z.createdAt??V,updatedAt:V},B=new D;if(G.clientId)B.claimUnique(this.clientIndexKey(G.clientId),$);if(G.externalId)B.claimUnique(this.externalIdIndexKey(G.externalId),$);if(W?.clientId&&W.clientId!==G.clientId)B.del(this.clientIndexKey(W.clientId));if(W?.externalId&&W.externalId!==G.externalId)B.del(this.externalIdIndexKey(W.externalId));if(B.set(Q,p$(G)),G.clientId)B.set(this.clientIndexKey(G.clientId),$);if(G.externalId)B.set(this.externalIdIndexKey(G.externalId),$);if(B.sadd(this.idsSetKey(),$),await B.exec(this.redis)==="conflict")throw new y$(`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 D;if(Q.del(this.entityKey(Z)),$.clientId)Q.del(this.clientIndexKey($.clientId));if($.externalId)Q.del(this.externalIdIndexKey($.externalId));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 b$(0,[],$.start??0,$.limit);let Y=Q.map((G)=>this.entityKey(G)),V=(await this.redis.mget(Y)).filter((G)=>G!=null).map((G)=>s(G,this.log)).filter((G)=>G!=null);return K(V,$)}}function c$(Z){return JSON.stringify({...Z,expires_at:Z.expires_at.toISOString(),created_at:Z.created_at.toISOString()})}function d$(Z,$){let Q=L(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 wZ{redis;prefix;log;constructor(Z){this.redis=Z.redis,this.prefix=_(Z,"workload-token-store"),this.log=F(Z)}key(Z){return C(this.prefix,"workload","token",X(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,c$(Z))}async get(Z){let $=await this.redis.get(this.key(Z));if($==null)return null;let Q=d$($,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{E as supportsRunScript,L$ as splitIonRedisSecret,LZ as scriptSha1,C as prefixedKey,F as getLogger,_ as getKeyPrefix,X as encodeKeyId,N$ as createRedisLfvTransport,f$ as createReactiveRedis,D$ as createEvalshaRunner,U$ as chunkedMget,m as atomicCreate,c as atomicCompareAndSet,wZ as RedisWorkloadTokenStore,TZ as RedisWorkloadIdentityStore,hZ as RedisUserStore,D as RedisTxn,kZ as RedisTenantStore,fZ as RedisSessionStore,XZ as RedisResourceStore,AZ as RedisMagicLinkStore,DZ as RedisCustomerStore,FZ as RedisAccessIndex,zZ as RedisAccessCatalogStore,JZ as RedisAccessAssignmentStore,QZ as MAX_LIST_LIMIT,B$ as DEFAULT_MGET_CHUNK_SIZE,$Z 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",
3
+ "version": "0.0.24-beta.20260907.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"
27
+ "@ionite/core": "0.0.24-beta.20260907.2"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^26.1.0"