@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 +21 -3
- package/dist/index.js +42 -9
- package/package.json +2 -2
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
|
|
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?:
|
|
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
|
|
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
|
-
${
|
|
4
|
-
return 'created'`,B=
|
|
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,
|
|
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'`,
|
|
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'`,
|
|
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`,
|
|
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.
|
|
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.
|
|
27
|
+
"@ionite/core": "0.0.23-beta.20260809.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^26.1.0"
|