@ionite/redis-core 0.0.22 → 0.0.23-beta.20260729.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ interface RedisStoreOptions {
|
|
|
67
67
|
redis: RedisAdapter;
|
|
68
68
|
/**
|
|
69
69
|
* Key prefix for all keys. REQUIRED: there is no implicit default. In multi-tenant deployments
|
|
70
|
-
* each tenant MUST use a distinct prefix (e.g. `
|
|
70
|
+
* each tenant MUST use a distinct prefix (e.g. `ion:t:<tenantId>:`) so the global index sets
|
|
71
71
|
* (`user:ids`, `tenant:ids`, etc.) auto-namespace and never collide across tenants.
|
|
72
72
|
*/
|
|
73
73
|
keyPrefix: string;
|
|
@@ -78,7 +78,7 @@ interface RedisStoreOptions {
|
|
|
78
78
|
* Validate the required `keyPrefix` and warn (at most once per fingerprint) when the same
|
|
79
79
|
* `(storeKind, keyPrefix)` is constructed more than once in a process. A duplicate almost always
|
|
80
80
|
* means two tenants share a Redis namespace; distinct tenants MUST use distinct prefixes (e.g.
|
|
81
|
-
* `
|
|
81
|
+
* `ion:t:<tenantId>:`). This is a warning, not a throw, because legitimate re-initialization (same
|
|
82
82
|
* tenant, same process — notably an LRU evict→recreate cycle) can reconstruct a store; the once-per
|
|
83
83
|
* -fingerprint dedup keeps that from becoming recurring false-positive noise while still surfacing an
|
|
84
84
|
* accidental shared keyspace loudly the first time.
|
package/dist/index.js
CHANGED
|
@@ -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. "es: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. "es: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 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]
|
|
47
47
|
local A=tonumber(ARGV[2])
|
|
48
48
|
local R=tonumber(ARGV[3])
|
|
49
49
|
local rootKey=KEYS[1]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ionite/redis-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23-beta.20260729.1",
|
|
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": "
|
|
27
|
+
"@ionite/core": "0.0.23-beta.20260729.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^26.1.0"
|