@aiquants/auth-directory-drizzle 0.6.0 → 0.6.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.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var F=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var pe=Object.getOwnPropertyNames;var ce=Object.prototype.hasOwnProperty;var me=(t,o)=>{for(var g in o)F(t,g,{get:o[g],enumerable:!0})},ye=(t,o,g,h)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of pe(o))!ce.call(t,s)&&s!==g&&F(t,s,{get:()=>o[s],enumerable:!(h=le(o,s))||h.enumerable});return t};var ge=t=>ye(F({},"__esModule",{value:!0}),t);var Re={};me(Re,{AUTH_DIRECTORY_PROVIDER_COLUMN:()=>ae,AUTH_DIRECTORY_TENANT_SCOPED_TABLES:()=>Te,MAX_IN_LIST:()=>qe,MAX_PARAMETERS:()=>se,MEMBERSHIP_MODES:()=>W,UNHEALTHY_SYNC_OUTCOMES:()=>Z,assertGroupMayBeLinked:()=>we,assertMembershipMode:()=>K,chunk:()=>L,chunkByParameters:()=>N,createDirectoryLinkStore:()=>fe,createDirectorySync:()=>xe,createDirectorySyncRepository:()=>Me,createPromotionRepository:()=>ve,createSyncRequestQueue:()=>ke,defaultExternalGroupGuardMessage:()=>he,defaultSyncMessages:()=>Q,defineAuthDirectorySchema:()=>De,findLedgerRowsForIdentity:()=>z,promoteLedgerForUser:()=>Ie,withExternalGroupGuards:()=>be});module.exports=ge(Re);var te=require("@aiquants/authz-core"),he=(t,o)=>`\u5916\u90E8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u9023\u643A\u30B0\u30EB\u30FC\u30D7 (${o}) \u306B\u306F\u7BA1\u7406\u30ED\u30FC\u30EB (${t}) \u3092\u6301\u305F\u305B\u3089\u308C\u307E\u305B\u3093\u3002\u4E0A\u6D41\u5074\u306E\u6240\u5C5E\u5909\u66F4\u304C\u305D\u306E\u307E\u307E\u7BA1\u7406\u8005\u306E\u5897\u6E1B\u306B\u306A\u308B\u305F\u3081\u3001\u7BA1\u7406\u8005\u7D4C\u8DEF\u306F\u30ED\u30FC\u30AB\u30EB\u7BA1\u7406\u306E\u30B0\u30EB\u30FC\u30D7\u306B\u9650\u5B9A\u3057\u3066\u3044\u307E\u3059\u3002`;function H(t,o,g,h){return new Set(t.filter(s=>s.roleId===o&&s.appKey===g&&s.resourceKey===h).map(s=>s.action))}var J=t=>te.AUTHZ_ACTIONS.every(o=>t.has(o)),V=(t,o)=>t.find(g=>g.roleId===o)?.roleKey??`#${o}`;function be(t,o){return ne(t,o)}function ne(t,o){let g=t;async function h(s,a,p,y,w){if(y.size===0)return;let n=await s.listGroupRoles(p),b=new Set;for(let e of n){if(!y.has(e.roleId)||b.has(e.groupId))continue;b.add(e.groupId);let l=await o.linkLookup.externalGroupKey(p,e.groupId,a);if(l!==null)throw new Error(o.message(e.roleKey||V(w,e.roleId),l))}}return{...t,withTransaction:(s,a)=>g.withTransaction((p,y)=>s(ne(p,o),y),a),async assignGroupRole(s,a,p,y){await g.withTransaction(async(w,n)=>{let b=await w.listGrants(s);if(J(H(b,p,o.adminAppKey,o.adminResourceKey))){let e=await o.linkLookup.externalGroupKey(s,a,n);if(e!==null)throw new Error(o.message(V(b,p),e))}await w.assignGroupRole(s,a,p,y)})},async createGrant(s,a,p){await g.withTransaction(async(y,w)=>{let b=(await y.listResources(s)).find(e=>e.id===a.resourceId);if(b?.appKey===o.adminAppKey&&b?.resourceKey===o.adminResourceKey){let e=await y.listGrants(s),l=H(e,a.roleId,o.adminAppKey,o.adminResourceKey);l.add(a.action),J(l)&&await h(y,w,s,new Set([a.roleId]),e)}await y.createGrant(s,a,p)})}}}async function we(t,o,g,h){let s=(await t.listGroupRoles(o)).filter(p=>p.groupId===g);if(s.length===0)return;let a=await t.listGrants(o);for(let p of s)if(J(H(a,p.roleId,h.adminAppKey,h.adminResourceKey)))throw new Error(h.message(p.roleKey||V(a,p.roleId),p.groupKey))}var $=require("drizzle-orm");async function z(t,o,g){let{ledger:h,group:s}=o,{tenantId:a,externalUserId:p,email:y}=g;if(p===null&&y==="")return[];let w={ledgerId:h.id,groupId:h.groupId,tenantId:s.tenantId,externalUserId:h.externalUserId},n=async d=>(await t.select(w).from(h).innerJoin(s,(0,$.and)((0,$.eq)(s.id,h.groupId),(0,$.eq)(s.tenantId,a),(0,$.eq)(s.isActive,!0))).where(d)).map(I=>({ledgerId:Number(I.ledgerId),groupId:Number(I.groupId),tenantId:String(I.tenantId),externalUserId:I.externalUserId===null?null:String(I.externalUserId)})),b=d=>{let v=new Map;for(let I of d)v.set(I.ledgerId,{ledgerId:I.ledgerId,groupId:I.groupId,tenantId:I.tenantId});return[...v.values()].sort((I,T)=>I.ledgerId-T.ledgerId)},e=p===null?[]:await n((0,$.eq)(h.externalUserId,p));if(y==="")return b(e);let l=await n((0,$.eq)(h.email,y)),A=d=>p!==null&&d.externalUserId!==null&&d.externalUserId!==p;return b([...e,...l.filter(d=>!A(d))])}var O=require("@aiquants/auth-core"),oe=require("@aiquants/auth-directory-core"),x=require("@aiquants/authz-core"),u=require("drizzle-orm");var W=["direct","transitive"];function K(t){if(!W.includes(t))throw new TypeError(`[@aiquants/auth-directory-drizzle] membershipMode must be one of ${W.join(" | ")}, received ${JSON.stringify(t)}`);return t}function fe(t,o,g,h){let s=t,a=h.ensureConnection??(async()=>{}),p=h.policyCacheTtlMs??0;if(!Number.isFinite(p)||p<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] policyCacheTtlMs must be a non-negative number, received ${String(h.policyCacheTtlMs)}`);let y=new Map,w=r=>y.delete(r),{TDGroupExternalLink:n,TDGroupMemberEmail:b,TDTenantMembershipGroup:e,TDAllowlistGroup:l,TDDirectorySyncRequest:A}=o,{TMGroup:d,TDGroupMember:v}=g;async function I(r,i,m){if((await r.select({groupId:e.groupId}).from(e).where((0,u.and)((0,u.eq)(e.tenantId,i),(0,u.eq)(e.groupId,m),(0,u.eq)(e.isBreakGlass,!0)))).length>0)throw new Error(`Group ${m} is a break-glass proof group; a break-glass proof path must be one the synchronization can never touch.`)}async function T(r,i,m){let[f]=await r.select({id:d.id}).from(d).where((0,u.and)((0,u.eq)(d.id,m),(0,u.eq)(d.tenantId,i)));if(!f)throw new Error(`Group not found in tenant: ${m}`)}function G(r,i){return{linkId:Number(r.linkId),groupId:Number(r.groupId),tenantId:String(r.tenantId),provider:String(r.provider),externalId:String(r.externalId),externalKey:String(r.externalKey),membershipMode:K(String(r.membershipMode)),isSyncEnabled:!!r.isSyncEnabled,...r.syncedAt?{syncedAt:new Date(r.syncedAt).toISOString()}:{},...r.syncError?{syncError:String(r.syncError)}:{},unresolvedCount:i}}async function _(r,i){let m=await r.select({groupId:b.groupId,n:(0,u.count)()}).from(b).innerJoin(d,(0,u.and)((0,u.eq)(d.id,b.groupId),(0,u.eq)(d.tenantId,i))).where((0,u.isNull)(b.resolvedUserId)).groupBy(b.groupId);return new Map(m.map(f=>[Number(f.groupId),Number(f.n)]))}return{async listLinks(r){(0,x.assertTenantId)(r,"listLinks(tenantId)"),await a();let i=await s.select({linkId:n.id,groupId:n.groupId,tenantId:n.tenantId,provider:n.provider,externalId:n.externalId,externalKey:n.externalKey,membershipMode:n.membershipMode,isSyncEnabled:n.isSyncEnabled,syncedAt:n.syncedAt,syncError:n.syncError}).from(n).where((0,u.eq)(n.tenantId,r)).orderBy((0,u.asc)(n.groupId)),m=await _(s,r);return i.map(f=>((0,x.assertTenantMatch)(r,String(f.tenantId),"listLinks(row.tenantId)"),G(f,m.get(Number(f.groupId))??0)))},async externalGroupKey(r,i,m){(0,x.assertTenantId)(r,"externalGroupKey(tenantId)"),await a();let[f]=await(m??s).select({externalKey:n.externalKey}).from(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)));return f?String(f.externalKey):null},async linkGroup(r,i,m,f){(0,x.assertTenantId)(r,"linkGroup(tenantId)");let D=K(m.membershipMode),k=m.externalId.trim(),E=(0,O.normalizeAuthEmail)(m.externalKey);if(k===""||E==="")throw new TypeError("[@aiquants/auth-directory-drizzle] externalId and externalKey must be non-empty");await a(),await h.linkable.assertLinkableAndWrite(r,i,async j=>{let M=j;await T(M,r,i),await I(M,r,i),await M.insert(n).output().values({groupId:i,tenantId:r,provider:m.provider,externalId:k,externalKey:E,membershipMode:D,isSyncEnabled:!0,vanishedObservations:0,createdAt:new Date,createdBy:f})}),w(r)},async unlinkGroup(r,i){(0,x.assertTenantId)(r,"unlinkGroup(tenantId)"),await a(),await s.transaction(async m=>{await T(m,r,i),await m.delete(b).where((0,u.eq)(b.groupId,i)),await m.delete(l).where((0,u.and)((0,u.eq)(l.groupId,i),(0,u.eq)(l.tenantId,r))),await m.delete(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)))}),w(r)},async setSyncEnabled(r,i,m,f){(0,x.assertTenantId)(r,"setSyncEnabled(tenantId)"),await a(),await s.update(n).set({isSyncEnabled:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)))},async requestSync(r,i,m){(0,x.assertTenantId)(r,"requestSync(tenantId)"),await a(),await s.transaction(async f=>{i!==null&&await T(f,r,i),await f.insert(A).output().values({tenantId:r,groupId:i,requestedBy:m,requestedAt:new Date,createdAt:new Date,createdBy:m})})},async getStatus(r){(0,x.assertTenantId)(r,"getStatus(tenantId)"),await a();let i=await s.select({groupId:n.groupId,isSyncEnabled:n.isSyncEnabled,syncedAt:n.syncedAt,syncError:n.syncError}).from(n).where((0,u.eq)(n.tenantId,r)),[m]=await s.select({n:(0,u.count)()}).from(A).where((0,u.and)((0,u.eq)(A.tenantId,r),(0,u.isNull)(A.finishedAt))),f=i.filter(M=>!!M.isSyncEnabled),D=f.map(M=>M.syncedAt?new Date(M.syncedAt).getTime():null),k=D.length>0&&D.every(M=>M!==null)?Math.min(...D):null,j=i.length>0&&f.length===0||f.length>0&&(k===null||Date.now()-k>h.stalenessThresholdSec*1e3);return{isEnabled:h.isEnabled,provider:h.provider,linkedGroupCount:i.length,...k!==null?{oldestSyncedAt:new Date(k).toISOString()}:{},isStale:j,pausedGroupCount:i.length-f.length,stalenessThresholdSec:h.stalenessThresholdSec,erroredGroupIds:i.filter(M=>!!M.syncError).map(M=>Number(M.groupId)).sort((M,ue)=>M-ue),pendingRequestCount:Number(m?.n??0)}},async loadTenantMembershipPolicy(r){(0,x.assertTenantId)(r,"loadTenantMembershipPolicy(tenantId)");let i=p>0?y.get(r):void 0;if(i&&Date.now()-i.loadedAt<p)return i.policy;await a();let m=await s.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass,externalLinkId:n.id}).from(e).innerJoin(d,(0,u.and)((0,u.eq)(d.id,e.groupId),(0,u.eq)(d.tenantId,r))).leftJoin(n,(0,u.eq)(n.groupId,e.groupId)).where((0,u.eq)(e.tenantId,r));if(m.length===0){let E={kind:"no-proof-required"};return p>0&&y.set(r,{policy:E,loadedAt:Date.now()}),E}let f=m.map(E=>Number(E.groupId)),D=m.filter(E=>!!E.isBreakGlass);for(let E of D)if(E.externalLinkId!==null)throw new Error(`Break-glass group ${Number(E.groupId)} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`);let k={kind:"require-proof",proofGroupIds:f,breakGlassGroupIds:D.map(E=>Number(E.groupId))};return(0,oe.assertTenantMembershipPolicy)(k,`tenant ${r}`),p>0&&y.set(r,{policy:k,loadedAt:Date.now()}),k},async setTenantMembershipGroup(r,i,m,f){(0,x.assertTenantId)(r,"setTenantMembershipGroup(tenantId)"),await a(),await s.transaction(async D=>{if(await T(D,r,i),m){let[E]=await D.select({id:n.id}).from(n).where((0,u.and)((0,u.eq)(n.tenantId,r),(0,u.eq)(n.groupId,i)));if(E)throw new Error(`Group ${i} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`)}if((await D.select({groupId:e.groupId}).from(e).where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i)))).length>0){await D.update(e).set({isBreakGlass:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i))),w(r);return}await D.insert(e).output().values({tenantId:r,groupId:i,isBreakGlass:m,createdAt:new Date,createdBy:f})}),w(r)},async removeTenantMembershipGroup(r,i){(0,x.assertTenantId)(r,"removeTenantMembershipGroup(tenantId)"),await a(),await s.transaction(async m=>{let D=(await m.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass}).from(e).where((0,u.eq)(e.tenantId,r))).filter(k=>Number(k.groupId)!==i);if(D.length>0&&!D.some(k=>!!k.isBreakGlass))throw new Error(`Removing group ${i} would leave tenant ${r} with a membership proof but no break-glass path.`);await m.delete(e).where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i)))}),w(r)},async setGroupActive(r,i,m,f){(0,x.assertTenantId)(r,"setGroupActive(tenantId)"),await a(),await s.update(d).set({isActive:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(d.id,i),(0,u.eq)(d.tenantId,r)))},async listLedgerGroupIds(r,i){if((0,x.assertTenantId)(r,"listLedgerGroupIds(tenantId)"),!h.isEnabled)return[];await a();let m=await z(s,{ledger:b,group:d},{tenantId:r,externalUserId:i.externalUserId?.trim()||null,email:(0,O.normalizeAuthEmail)(i.email)});return[...new Set(m.map(f=>f.groupId))].sort((f,D)=>f-D)},async isEmailAllowedByGroup(r){if(!h.isEnabled)return!1;let i=(0,O.normalizeAuthEmail)(r);if(i==="")return!1;await a();let[m]=await s.select({id:b.id}).from(b).innerJoin(l,(0,u.eq)(l.groupId,b.groupId)).innerJoin(d,(0,u.and)((0,u.eq)(d.id,b.groupId),(0,u.eq)(d.isActive,!0))).where((0,u.eq)(b.email,i));return!!m},async listAllowlistGroups(r){return(0,x.assertTenantId)(r,"listAllowlistGroups(tenantId)"),await a(),(await s.select({id:l.id,groupId:l.groupId,groupKey:d.groupKey,groupName:d.name,description:l.description}).from(l).innerJoin(d,(0,u.and)((0,u.eq)(d.id,l.groupId),(0,u.eq)(d.tenantId,r))).where((0,u.eq)(l.tenantId,r)).orderBy((0,u.asc)(d.name))).map(m=>({id:Number(m.id),type:"group",groupId:Number(m.groupId),groupKey:String(m.groupKey),groupName:String(m.groupName),...m.description?{description:String(m.description)}:{},owner:{kind:"tenant",tenantId:r},readOnly:!1}))},async addAllowlistGroup(r,i,m,f){(0,x.assertTenantId)(r,"addAllowlistGroup(tenantId)"),await a(),await s.transaction(async D=>{await T(D,r,i);let[k]=await D.select({id:n.id}).from(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)));if(!k)throw new Error(`Group ${i} is locally managed; only externally-sourced groups can gate sign-in, because the decision reads the upstream member ledger.`);await D.insert(l).output().values({tenantId:r,groupId:i,description:m??null,createdAt:new Date,createdBy:f})})},async removeAllowlistGroup(r,i){(0,x.assertTenantId)(r,"removeAllowlistGroup(tenantId)"),await a(),await s.delete(l).where((0,u.and)((0,u.eq)(l.id,i),(0,u.eq)(l.tenantId,r)))},async countProjectedMembers(r,i){(0,x.assertTenantId)(r,"countProjectedMembers(tenantId)"),await a();let[m]=await s.select({n:(0,u.count)()}).from(v).innerJoin(d,(0,u.and)((0,u.eq)(d.id,v.groupId),(0,u.eq)(d.tenantId,r))).where((0,u.eq)(v.groupId,i));return Number(m?.n??0)}}}var ie=require("@aiquants/auth-core"),B=require("@aiquants/authz-core");async function Ie(t,o){let g=(0,B.assertTenantId)(o.tenantId,"promoteLedgerForUser(tenantId)");if(!Number.isInteger(o.userId)||o.userId<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] userId must be a positive integer, received ${String(o.userId)}`);let h=(0,ie.normalizeAuthEmail)(o.email),s=o.externalUserId?.trim()||null;if(s===null&&h==="")return{addedGroupIds:[],matchedLedgerIds:[]};let a=await t.findLedgerMatches({tenantId:g,externalUserId:s,email:h});if(a.length===0)return{addedGroupIds:[],matchedLedgerIds:[]};for(let n of a)(0,B.assertTenantMatch)(g,n.tenantId,"promoteLedgerForUser(findLedgerMatches row)");let p=[...new Set(a.map(n=>n.ledgerId))].sort((n,b)=>n-b),y=[...new Set(a.map(n=>n.groupId))].sort((n,b)=>n-b);return{addedGroupIds:[...await t.attachUserToGroups({tenantId:g,userId:o.userId,ledgerIds:p,groupIds:y})].sort((n,b)=>n-b),matchedLedgerIds:p}}var Y=require("@aiquants/authz-core"),R=require("drizzle-orm");var se=2100;var qe=500;function L(t,o=500){if(!Number.isInteger(o)||o<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] chunk size must be a positive integer, received ${String(o)}`);let g=[];for(let h=0;h<t.length;h+=o)g.push(t.slice(h,h+o));return g}function N(t,o,g=100){if(!Number.isInteger(o)||o<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] parametersPerItem must be a positive integer, received ${String(o)}`);if(!Number.isInteger(g)||g<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] reservedParameters must be a non-negative integer, received ${String(g)}`);let h=2100-g,s=Math.floor(h/o);if(s<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] a single item binds ${o} parameters, which exceeds the ${h} available in one statement`);return L(t,s)}var Ae=4,Se=100;function ve(t,o,g,h){let s=t,a=h.ensureConnection??(async()=>{}),p=h.now??(()=>new Date),y=o.TDGroupMemberEmail,{TMGroup:w,TDGroupMember:n}=g;return{async findLedgerMatches(b){return(0,Y.assertTenantId)(b.tenantId,"findLedgerMatches(tenantId)"),await a(),z(s,{ledger:y,group:w},b)},async attachUserToGroups({tenantId:b,userId:e,ledgerIds:l,groupIds:A}){return(0,Y.assertTenantId)(b,"attachUserToGroups(tenantId)"),await a(),l.length===0?[]:s.transaction(async d=>{let v=new Set;for(let r of L(A)){let i=await d.select({id:w.id}).from(w).where((0,R.and)((0,R.inArray)(w.id,r),(0,R.eq)(w.tenantId,b),(0,R.eq)(w.isActive,!0)));for(let m of i)v.add(Number(m.id))}let I=A.filter(r=>v.has(r));if(I.length===0)return[];let T=new Set;for(let r of L(I)){let i=await d.select({groupId:n.groupId}).from(n).where((0,R.and)((0,R.eq)(n.userId,e),(0,R.inArray)(n.groupId,r)));for(let m of i)T.add(Number(m.groupId))}let G=I.filter(r=>!T.has(r)),_=Math.max(1,2100-Se-I.length);for(let r of L(l,_))await d.update(y).set({resolvedUserId:e}).output().where((0,R.and)((0,R.inArray)(y.id,r),(0,R.inArray)(y.groupId,I)));if(G.length>0){let r=p();for(let i of N(G,Ae))await d.insert(n).output().values(i.map(m=>({groupId:m,userId:e,createdAt:r,createdBy:h.actor})))}return G})}}}var U=require("@aiquants/authz-drizzle"),c=require("drizzle-orm/mssql-core"),ae="provider",Te=["TDGroupExternalLink","TDTenantMembershipGroup","TDAllowlistGroup","TDDirectorySyncRequest","TMAllowlist"];function De(t,o){let g=(0,c.mssqlSchema)(t),h=()=>({createdAt:(0,c.datetime2)("created_at"),createdBy:(0,c.nvarchar)("created_by",{length:50}),updatedAt:(0,c.datetime2)("updated_at"),updatedBy:(0,c.nvarchar)("updated_by",{length:50})}),s=()=>({createdAt:(0,c.datetime2)("created_at"),createdBy:(0,c.nvarchar)("created_by",{length:50})}),a=g.table("TDGroupExternalLink",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,c.nvarchar)(ae,{length:32}).notNull(),externalId:(0,c.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,c.nvarchar)("external_key",{length:320}).notNull(),membershipMode:(0,c.nvarchar)("membership_mode",{length:16}).notNull(),isSyncEnabled:(0,c.bit)("is_sync_enabled").notNull(),syncedAt:(0,c.datetime2)("synced_at"),syncError:(0,c.nvarchar)("sync_error",{length:1e3}),vanishedObservations:(0,c.int)("vanished_observations").notNull(),...h()},e=>[(0,c.primaryKey)({name:`${t}_TDGroupExternalLink_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDGroupExternalLink_group_uq`).on(e.groupId),(0,c.uniqueIndex)(`${t}_TDGroupExternalLink_external_uq`).on(e.tenantId,e.provider,e.externalId),(0,c.foreignKey)({name:`${t}_TDGroupExternalLink_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),p=g.table("TDGroupMemberEmail",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),email:(0,c.nvarchar)("email",{length:320}).notNull(),externalUserId:(0,c.nvarchar)("external_user_id",{length:128}),resolvedUserId:(0,c.bigint)("resolved_user_id",{mode:"number"}),observedAt:(0,c.datetime2)("observed_at").notNull(),...h()},e=>[(0,c.primaryKey)({name:`${t}_TDGroupMemberEmail_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDGroupMemberEmail_group_email_uq`).on(e.groupId,e.email),(0,c.foreignKey)({name:`${t}_TDGroupMemberEmail_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),y=g.table("TDTenantMembershipGroup",{tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),isBreakGlass:(0,c.bit)("is_break_glass").notNull(),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDTenantMembershipGroup_pk`,columns:[e.tenantId,e.groupId]}),(0,c.foreignKey)({name:`${t}_TDTenantMembershipGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),w=g.table("TDAllowlistGroup",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),description:(0,c.nvarchar)("description",{length:512}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDAllowlistGroup_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDAllowlistGroup_tenant_group_uq`).on(e.tenantId,e.groupId),(0,c.foreignKey)({name:`${t}_TDAllowlistGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),n=g.table("TDDirectorySyncRequest",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}),requestedBy:(0,c.nvarchar)("requested_by",{length:320}).notNull(),requestedAt:(0,c.datetime2)("requested_at").notNull(),pickedAt:(0,c.datetime2)("picked_at"),finishedAt:(0,c.datetime2)("finished_at"),result:(0,c.nvarchar)("result",{length:32}),error:(0,c.nvarchar)("error",{length:2e3}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDDirectorySyncRequest_pk`,columns:[e.id]})]),b=g.table("TMAllowlist",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),pattern:(0,c.nvarchar)("pattern",{length:256}).notNull(),type:(0,c.nvarchar)("type",{length:32}).notNull(),description:(0,c.nvarchar)("description",{length:512}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TMAllowlist_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TMAllowlist_pattern_uq`).on(e.tenantId,e.pattern)]);return{TDGroupExternalLink:a,TDGroupMemberEmail:p,TDTenantMembershipGroup:y,TDAllowlistGroup:w,TDDirectorySyncRequest:n,TMAllowlist:b}}var C=require("@aiquants/auth-directory-core");var X=(t,o=20)=>t.length<=o?t.join(", "):`${t.slice(0,o).join(", ")} \u307B\u304B ${t.length-o} \u4EF6`,Q={upstreamUnreadable:t=>`\u4E0A\u6D41\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${t}`,localWriteFailed:t=>`\u30ED\u30FC\u30AB\u30EB\u3078\u306E\u66F8\u304D\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${t}`,resolverIncomplete:(t,o)=>`\u4E0A\u6D41\u304C ${t} \u4EF6\u306E\u30E1\u30F3\u30D0\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F\u304C\u3001\u7A81\u5408\u7D50\u679C\u306F ${o} \u4EF6\u3067\u3057\u305F\u3002\u4EF6\u6570\u304C\u6E1B\u308B\u7A81\u5408\u306F\u3001\u89E3\u6C7A\u3067\u304D\u306A\u304B\u3063\u305F\u4EBA\u7269\u3092\u300C\u4E0A\u6D41\u304B\u3089\u6D88\u3048\u305F\u300D\u3068\u3057\u3066\u524A\u9664\u3057\u3066\u3057\u307E\u3046\u305F\u3081\u4E2D\u65AD\u3057\u307E\u3057\u305F\u3002\u89E3\u6C7A\u3067\u304D\u306A\u3044\u30E1\u30F3\u30D0\u30FC\u306F\u7701\u7565\u305B\u305A resolvedUserId: null \u3067\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`,groupMissing:(t,o,g)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 (${t})\u3002\u9023\u7D9A ${o} \u56DE\u76EE\u3067\u3059\u3002${g} \u56DE\u306B\u9054\u3057\u305F\u6642\u70B9\u3067\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u3002`,groupVanished:(t,o)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C ${o} \u56DE\u9023\u7D9A\u3067\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F (${t})\u3002\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3057\u305F\u3002\u4E0A\u6D41\u304C\u5FA9\u65E7\u3059\u308C\u3070\u6B21\u56DE\u306E\u540C\u671F\u3067\u81EA\u52D5\u7684\u306B\u518D\u6709\u52B9\u5316\u3055\u308C\u307E\u3059\u3002`,planRefused:t=>{if(t.reason==="invalid-upstream-data")return`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: \u4E0A\u6D41\u306E\u30E1\u30F3\u30D0\u30FC\u4E00\u89A7\u306B\u6271\u3048\u306A\u3044\u30A2\u30C9\u30EC\u30B9\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059 (${X(t.offendingEmails)})\u3002`;let o=t.axis==="membership"?"\u6A29\u9650\u3092\u5931\u3046\u4EBA\u6570":"\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u304D\u306A\u304F\u306A\u308B\u4EBA\u6570",g=t.axis==="membership"?t.withheldProjectionRemoves.map(String):t.withheldLedgerDeletes;return[`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: ${o}\u304C ${t.population} \u4EBA\u4E2D ${t.removalCount} \u4EBA\u306B\u306A\u308A\u307E\u3059`,`(\u4E0A\u9650: ${t.limits.maxRemovalCount} \u4EF6 / \u6BD4\u7387 ${t.limits.maxRemovalRatio})\u3002`,`\u5DEE\u3057\u6B62\u3081\u305F\u5BFE\u8C61: ${X(g)}\u3002`,"\u4E0A\u6D41\u5074\u306E\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u610F\u56F3\u3057\u305F\u5909\u66F4\u3067\u3042\u308C\u3070\u3001\u3053\u306E\u8EF8\u306E\u4E0A\u9650\u3060\u3051\u3092\u4E00\u6642\u7684\u306B\u5F15\u304D\u4E0A\u3052\u3066\u518D\u5B9F\u884C\u3057\u307E\u3059\u3002"].join(" ")},removalsRefused:t=>`${t.length} \u4EF6\u306E\u6240\u5C5E\u89E3\u9664\u304C\u6700\u5F8C\u306E\u7BA1\u7406\u8005\u30AC\u30FC\u30C9\u306B\u3088\u308A\u62D2\u5426\u3055\u308C\u307E\u3057\u305F (\u30E6\u30FC\u30B6\u30FC ${X(t.map(o=>String(o.userId)))})\u3002\u6B8B\u308A\u306E\u5909\u66F4\u306F\u9069\u7528\u6E08\u307F\u3067\u3059\u3002`,foreignProvider:(t,o)=>`\u3053\u306E\u30EA\u30F3\u30AF\u306E\u4F9B\u7D66\u5143\u306F ${t} \u3067\u3059\u304C\u3001\u5B9F\u884C\u4E2D\u306E\u540C\u671F\u306F ${o} \u7528\u3067\u3059\u3002\u5BFE\u5FDC\u3059\u308B\u540C\u671F\u30B8\u30E7\u30D6\u304B\u3089\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`};var Z=["aborted","missing","vanished","upstream-failed","local-failed","foreign-provider"];function xe(t){let{directory:o,repository:g,policy:h,now:s,logger:a}=t,p=t.messages??Q;if((0,C.assertBlastRadiusPolicy)(h),!Number.isInteger(t.vanishedThreshold)||t.vanishedThreshold<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] vanishedThreshold must be a positive integer, received ${String(t.vanishedThreshold)}`);if(typeof t.provider!="string"||t.provider.trim()==="")throw new TypeError("[@aiquants/auth-directory-drizzle] provider must be a non-empty string so links of other providers can be told apart");async function y(e,l){try{await g.recordFailure(e,l)}catch(A){a.error(`[auth-directory] could not record the failure for ${e.externalKey}:`,A)}}async function w(e){try{let l=await o.getGroup(e.externalId);return l===null?{kind:"missing"}:{kind:"ok",group:l,members:await o.listGroupMembers(l,e.membershipMode)}}catch(l){return{kind:"failed",error:l instanceof Error?l:new Error(String(l))}}}async function n(e){let l=await g.recordVanishedObservation(e,p.groupMissing(e.externalKey,e.vanishedObservations+1,t.vanishedThreshold));return l<t.vanishedThreshold?(a.warn(`[auth-directory] ${e.externalKey} missing upstream (${l}/${t.vanishedThreshold})`),{kind:"missing",link:e,observations:l}):(await g.markGroupInactive(e),await y(e,p.groupVanished(e.externalKey,l)),a.warn(`[auth-directory] ${e.externalKey} deactivated after ${l} consecutive absences`),{kind:"vanished",link:e})}async function b(e){if(e.provider!==t.provider)return await y(e,p.foreignProvider(e.provider,t.provider)),a.warn(`[auth-directory] skipping ${e.externalKey}: belongs to provider ${e.provider}`),{kind:"foreign-provider",link:e};let l=await w(e);if(l.kind==="missing")return n(e);if(l.kind==="failed")return await y(e,p.upstreamUnreadable(l.error.message)),a.error(`[auth-directory] upstream read failed for ${e.externalKey}:`,l.error),{kind:"upstream-failed",link:e,error:l.error};try{let[A,d]=await Promise.all([g.loadGroupSnapshot(e),g.resolveMembers(l.members)]);if(d.length!==l.members.length){let _=p.resolverIncomplete(l.members.length,d.length);return await y(e,_),a.error(`[auth-directory] ${_}`),{kind:"local-failed",link:e,error:new Error(_)}}let v=(0,C.reconcile)(A,d,h);if(v.aborted)return await y(e,p.planRefused(v.aborted)),a.warn(`[auth-directory] plan refused for ${e.externalKey}: ${v.aborted.reason}`),{kind:"aborted",link:e,plan:v};let I=v.ledgerUpserts.length>0||v.ledgerDeletes.length>0||v.projectionAdds.length>0||v.projectionRemoves.length>0,T=s(),G={refusals:[]};return I&&(G=await g.applyPlan(e,v,T)),await g.recordSuccess(e,l.group,T),G.refusals.length>0&&(await y(e,p.removalsRefused(G.refusals)),a.warn(`[auth-directory] ${G.refusals.length} removal(s) refused for ${e.externalKey}`)),v.unresolvedEmails.length>0&&a.info(`[auth-directory] ${e.externalKey}: ${v.unresolvedEmails.length} upstream member(s) have no local user yet`),I?{kind:"applied",link:e,plan:v}:{kind:"unchanged",link:e}}catch(A){let d=A instanceof Error?A:new Error(String(A));return await y(e,p.localWriteFailed(d.message)),a.error(`[auth-directory] local write failed for ${e.externalKey}:`,d),{kind:"local-failed",link:e,error:d}}}return{syncOne:b,async run(e={}){let l=await g.listSyncableLinks(e),A=[];for(let v of l)try{A.push(await b(v))}catch(I){let T=I instanceof Error?I:new Error(String(I));a.error(`[auth-directory] ${v.externalKey} ended abnormally:`,T),A.push({kind:"local-failed",link:v,error:T})}let d=A.filter(v=>Z.includes(v.kind)).length;return a.info(`[auth-directory] run finished: ${A.length} link(s), ${d} unhealthy`),{outcomes:A,unhealthyCount:d}}}}var de=require("@aiquants/authz-core"),P=require("drizzle-orm"),ee=2e3;function ke(t,o,g){let h=t,s=g.ensureConnection??(async()=>{}),a=o.TDDirectorySyncRequest,p=g.maxErrorLength??ee;if(!Number.isInteger(p)||p<1||p>ee)throw new TypeError(`[@aiquants/auth-directory-drizzle] maxErrorLength must be an integer within 1..${ee}, received ${String(g.maxErrorLength)}`);return{async claim(){await s();let y=await h.select({id:a.id}).from(a).where((0,P.isNull)(a.pickedAt)).orderBy((0,P.asc)(a.id));for(let w of y){let b=(await h.update(a).set({pickedAt:g.now()}).output().where((0,P.and)((0,P.eq)(a.id,Number(w.id)),(0,P.isNull)(a.pickedAt))))?.[0];if(!b)continue;let e=String(b.tenant_id??b.tenantId??""),l=b.group_id??b.groupId??null;return{id:Number(b.id),tenantId:(0,de.assertTenantId)(e,"claim(row.tenantId)"),groupId:l===null?null:Number(l),requestedBy:String(b.requested_by??b.requestedBy??"")}}return null},async finish(y,w){await s();let n=w.error?w.error.slice(0,p):null;await h.update(a).set({finishedAt:g.now(),result:w.result,error:n}).output().where((0,P.eq)(a.id,y))},async listStranded(y){return await s(),(await h.select({id:a.id,pickedAt:a.pickedAt}).from(a).where((0,P.isNull)(a.finishedAt)).orderBy((0,P.asc)(a.id))).filter(n=>n.pickedAt!==null&&new Date(n.pickedAt)<y).map(n=>Number(n.id))}}}var re=require("@aiquants/auth-core"),q=require("@aiquants/authz-core"),S=require("drizzle-orm");var Ge=7,Ee=4;function Me(t,o,g,h){let s=t,a=h.ensureConnection??(async()=>{}),{TDGroupExternalLink:p,TDGroupMemberEmail:y}=o,{TMGroup:w,TDGroupMember:n}=g,b=h.actor;return{async listSyncableLinks(e){await a();let l=[(0,S.eq)(p.isSyncEnabled,!0)];return e.tenantId!==void 0&&l.push((0,S.eq)(p.tenantId,(0,q.assertTenantId)(e.tenantId,"listSyncableLinks(tenantId)"))),e.groupId!==void 0&&l.push((0,S.eq)(p.groupId,e.groupId)),(await s.select({linkId:p.id,groupId:p.groupId,linkTenantId:p.tenantId,groupTenantId:w.tenantId,groupKey:w.groupKey,isGroupActive:w.isActive,provider:p.provider,externalId:p.externalId,externalKey:p.externalKey,membershipMode:p.membershipMode,vanishedObservations:p.vanishedObservations}).from(p).innerJoin(w,(0,S.eq)(w.id,p.groupId)).where((0,S.and)(...l))).map(d=>((0,q.assertTenantMatch)(String(d.groupTenantId),String(d.linkTenantId),"listSyncableLinks(link.tenantId vs TMGroup.tenant_id)"),{linkId:Number(d.linkId),groupId:Number(d.groupId),groupKey:String(d.groupKey),tenantId:String(d.groupTenantId),provider:String(d.provider),externalId:String(d.externalId),externalKey:String(d.externalKey),membershipMode:K(String(d.membershipMode)),isGroupActive:!!d.isGroupActive,vanishedObservations:Number(d.vanishedObservations??0)}))},async loadGroupSnapshot(e){await a();let[l,A]=await Promise.all([s.select({email:y.email,externalUserId:y.externalUserId,resolvedUserId:y.resolvedUserId}).from(y).where((0,S.eq)(y.groupId,e.groupId)),s.select({userId:n.userId}).from(n).where((0,S.eq)(n.groupId,e.groupId))]);return{ledger:l.map(d=>({email:String(d.email),...d.externalUserId?{externalUserId:String(d.externalUserId)}:{},resolvedUserId:d.resolvedUserId===null?null:Number(d.resolvedUserId)})),projectedUserIds:A.map(d=>Number(d.userId))}},resolveMembers(e){return h.memberResolver.resolve(e)},async applyPlan(e,l,A){(0,q.assertTenantId)(e.tenantId,"applyPlan(link.tenantId)"),await a(),(l.ledgerUpserts.length>0||l.projectionAdds.length>0)&&await s.transaction(async I=>{let T=l.ledgerUpserts.map(r=>({...r,email:(0,re.normalizeAuthEmail)(r.email)})),G=new Map;for(let r of L(T.map(i=>i.email))){let i=await I.select({id:y.id,email:y.email}).from(y).where((0,S.and)((0,S.eq)(y.groupId,e.groupId),(0,S.inArray)(y.email,r)));for(let m of i)G.set(String(m.email),Number(m.id))}let _=T.filter(r=>!G.has(r.email));for(let r of N(_,Ge))await I.insert(y).output().values(r.map(i=>({groupId:e.groupId,email:i.email,externalUserId:i.externalUserId??null,resolvedUserId:i.resolvedUserId,observedAt:A,createdAt:A,createdBy:b})));for(let r of T){let i=G.get(r.email);i!==void 0&&await I.update(y).set({externalUserId:r.externalUserId??null,resolvedUserId:r.resolvedUserId,observedAt:A,updatedAt:A,updatedBy:b}).output().where((0,S.eq)(y.id,i))}for(let r of N(l.projectionAdds,Ee)){let i=new Set((await I.select({userId:n.userId}).from(n).where((0,S.and)((0,S.eq)(n.groupId,e.groupId),(0,S.inArray)(n.userId,r)))).map(f=>Number(f.userId))),m=r.filter(f=>!i.has(f));m.length>0&&await I.insert(n).output().values(m.map(f=>({groupId:e.groupId,userId:f,createdAt:A,createdBy:b})))}});let d=[];for(let I of l.projectionRemoves)try{await h.lockoutGuard.guardedWrite(e.tenantId,{removeGroupMembership:{groupId:e.groupId,userId:I}},"remove-group-member",T=>T.delete(n).where((0,S.and)((0,S.eq)(n.groupId,e.groupId),(0,S.eq)(n.userId,I))))}catch(T){d.push({userId:I,applied:!1,refusal:T instanceof Error?T.message:String(T)})}let v=d.map(I=>I.userId);return l.ledgerDeletes.length>0&&await s.transaction(async I=>{for(let T of L(l.ledgerDeletes)){let G=v.length>0?(0,S.or)((0,S.isNull)(y.resolvedUserId),(0,S.notInArray)(y.resolvedUserId,v)):void 0;await I.delete(y).where((0,S.and)((0,S.eq)(y.groupId,e.groupId),(0,S.inArray)(y.email,T),...G?[G]:[]))}}),{refusals:d}},async recordSuccess(e,l,A){await a(),await s.transaction(async d=>{await d.update(p).set({syncedAt:A,syncError:null,vanishedObservations:0,externalId:l.externalId,externalKey:(0,re.normalizeAuthEmail)(l.externalKey),updatedAt:A,updatedBy:b}).output().where((0,S.eq)(p.id,e.linkId)),e.isGroupActive||await d.update(w).set({isActive:!0,updatedAt:A,updatedBy:b}).output().where((0,S.and)((0,S.eq)(w.id,e.groupId),(0,S.eq)(w.tenantId,e.tenantId)))})},async recordFailure(e,l){await a(),await s.update(p).set({syncError:l.slice(0,1e3),updatedAt:new Date,updatedBy:b}).output().where((0,S.eq)(p.id,e.linkId))},async recordVanishedObservation(e,l){await a();let A=e.vanishedObservations+1;return await s.update(p).set({vanishedObservations:A,syncError:l.slice(0,1e3),updatedAt:new Date,updatedBy:b}).output().where((0,S.eq)(p.id,e.linkId)),A},async markGroupInactive(e){await a(),await s.update(w).set({isActive:!1,updatedAt:new Date,updatedBy:b}).output().where((0,S.and)((0,S.eq)(w.id,e.groupId),(0,S.eq)(w.tenantId,e.tenantId)))}}}0&&(module.exports={AUTH_DIRECTORY_PROVIDER_COLUMN,AUTH_DIRECTORY_TENANT_SCOPED_TABLES,MAX_IN_LIST,MAX_PARAMETERS,MEMBERSHIP_MODES,UNHEALTHY_SYNC_OUTCOMES,assertGroupMayBeLinked,assertMembershipMode,chunk,chunkByParameters,createDirectoryLinkStore,createDirectorySync,createDirectorySyncRepository,createPromotionRepository,createSyncRequestQueue,defaultExternalGroupGuardMessage,defaultSyncMessages,defineAuthDirectorySchema,findLedgerRowsForIdentity,promoteLedgerForUser,withExternalGroupGuards});
1
+ "use strict";var J=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames;var ye=Object.prototype.hasOwnProperty;var ge=(t,n)=>{for(var g in n)J(t,g,{get:n[g],enumerable:!0})},he=(t,n,g,u)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of me(n))!ye.call(t,i)&&i!==g&&J(t,i,{get:()=>n[i],enumerable:!(u=ce(n,i))||u.enumerable});return t};var be=t=>he(J({},"__esModule",{value:!0}),t);var _e={};ge(_e,{AUTH_DIRECTORY_PROVIDER_COLUMN:()=>ue,AUTH_DIRECTORY_TENANT_SCOPED_TABLES:()=>xe,MAX_IN_LIST:()=>Be,MAX_PARAMETERS:()=>de,MEMBERSHIP_MODES:()=>X,UNHEALTHY_SYNC_OUTCOMES:()=>re,assertGroupMayBeLinked:()=>Ie,assertMembershipMode:()=>K,chunk:()=>L,chunkByParameters:()=>N,createDirectoryLinkStore:()=>Se,createDirectorySync:()=>Ge,createDirectorySyncRepository:()=>Pe,createPromotionRepository:()=>De,createSyncRequestQueue:()=>Re,defaultExternalGroupGuardMessage:()=>we,defaultSyncMessages:()=>ee,defineAuthDirectorySchema:()=>ke,findLedgerRowsForIdentity:()=>q,promoteLedgerForUser:()=>Ae,withExternalGroupGuards:()=>fe});module.exports=be(_e);var oe=require("@aiquants/authz-core"),we=(t,n)=>`\u5916\u90E8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u9023\u643A\u30B0\u30EB\u30FC\u30D7 (${n}) \u306B\u306F\u7BA1\u7406\u30ED\u30FC\u30EB (${t}) \u3092\u6301\u305F\u305B\u3089\u308C\u307E\u305B\u3093\u3002\u4E0A\u6D41\u5074\u306E\u6240\u5C5E\u5909\u66F4\u304C\u305D\u306E\u307E\u307E\u7BA1\u7406\u8005\u306E\u5897\u6E1B\u306B\u306A\u308B\u305F\u3081\u3001\u7BA1\u7406\u8005\u7D4C\u8DEF\u306F\u30ED\u30FC\u30AB\u30EB\u7BA1\u7406\u306E\u30B0\u30EB\u30FC\u30D7\u306B\u9650\u5B9A\u3057\u3066\u3044\u307E\u3059\u3002`;function z(t,n,g,u){return new Set(t.filter(i=>n.has(i.roleId)&&i.appKey===g&&i.resourceKey===u).map(i=>i.action))}function V(t,n){return new Set(t.filter(g=>g.groupId===n).map(g=>g.roleId))}var B=t=>oe.AUTHZ_ACTIONS.every(n=>t.has(n)),W=(t,n)=>t.find(g=>g.roleId===n)?.roleKey??`#${n}`;function Y(t,n,g,u){let i=[...n].find(c=>B(z(t,new Set([c]),g,u)));return i!==void 0?W(t,i):[...n].filter(c=>z(t,new Set([c]),g,u).size>0).map(c=>W(t,c)).join(" + ")}function fe(t,n){return ie(t,n)}function ie(t,n){let g=t;return{...t,withTransaction:(u,i)=>g.withTransaction((l,c)=>u(ie(l,n),c),i),async assignGroupRole(u,i,l,c){await g.withTransaction(async(b,f)=>{let o=await b.listGrants(u),h=V(await b.listGroupRoles(u),i);if(h.add(l),B(z(o,h,n.adminAppKey,n.adminResourceKey))){let e=await n.linkLookup.externalGroupKey(u,i,f);if(e!==null)throw new Error(n.message(Y(o,h,n.adminAppKey,n.adminResourceKey),e))}await b.assignGroupRole(u,i,l,c)})},async createGrant(u,i,l){await g.withTransaction(async(c,b)=>{let o=(await c.listResources(u)).find(h=>h.id===i.resourceId);if(o?.appKey===n.adminAppKey&&o?.resourceKey===n.adminResourceKey){let h=await c.listGrants(u),e=await c.listGroupRoles(u);for(let p of new Set(e.filter(w=>w.roleId===i.roleId).map(w=>w.groupId))){let w=z(h,V(e,p),n.adminAppKey,n.adminResourceKey);if(w.add(i.action),!B(w))continue;let a=await n.linkLookup.externalGroupKey(u,p,b);if(a!==null)throw new Error(n.message(Y(h,V(e,p),n.adminAppKey,n.adminResourceKey)||W(h,i.roleId),a))}}await c.createGrant(u,i,l)})}}}async function Ie(t,n,g,u){let i=(await t.listGroupRoles(n)).filter(f=>f.groupId===g);if(i.length===0)return;let l=await t.listGrants(n),c=new Set(i.map(f=>f.roleId));if(!B(z(l,c,u.adminAppKey,u.adminResourceKey)))return;let b=i[0];if(b)throw new Error(u.message(Y(l,c,u.adminAppKey,u.adminResourceKey),b.groupKey))}var $=require("drizzle-orm");async function q(t,n,g){let{ledger:u,group:i}=n,{tenantId:l,externalUserId:c,email:b}=g;if(c===null&&b==="")return[];let f={ledgerId:u.id,groupId:u.groupId,tenantId:i.tenantId,externalUserId:u.externalUserId},o=async a=>(await t.select(f).from(u).innerJoin(i,(0,$.and)((0,$.eq)(i.id,u.groupId),(0,$.eq)(i.tenantId,l),(0,$.eq)(i.isActive,!0))).where(a)).map(S=>({ledgerId:Number(S.ledgerId),groupId:Number(S.groupId),tenantId:String(S.tenantId),externalUserId:S.externalUserId===null?null:String(S.externalUserId)})),h=a=>{let v=new Map;for(let S of a)v.set(S.ledgerId,{ledgerId:S.ledgerId,groupId:S.groupId,tenantId:S.tenantId});return[...v.values()].sort((S,T)=>S.ledgerId-T.ledgerId)},e=c===null?[]:await o((0,$.eq)(u.externalUserId,c));if(b==="")return h(e);let p=await o((0,$.eq)(u.email,b)),w=a=>c!==null&&a.externalUserId!==null&&a.externalUserId!==c;return h([...e,...p.filter(a=>!w(a))])}var C=require("@aiquants/auth-core"),se=require("@aiquants/auth-directory-core"),x=require("@aiquants/authz-core"),d=require("drizzle-orm");var X=["direct","transitive"];function K(t){if(!X.includes(t))throw new TypeError(`[@aiquants/auth-directory-drizzle] membershipMode must be one of ${X.join(" | ")}, received ${JSON.stringify(t)}`);return t}function Se(t,n,g,u){let i=t,l=u.ensureConnection??(async()=>{}),c=u.policyCacheTtlMs??0;if(!Number.isFinite(c)||c<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] policyCacheTtlMs must be a non-negative number, received ${String(u.policyCacheTtlMs)}`);let b=new Map,f=r=>b.delete(r),{TDGroupExternalLink:o,TDGroupMemberEmail:h,TDTenantMembershipGroup:e,TDAllowlistGroup:p,TDDirectorySyncRequest:w}=n,{TMGroup:a,TDGroupMember:v}=g;async function S(r,s,y){if((await r.select({groupId:e.groupId}).from(e).where((0,d.and)((0,d.eq)(e.tenantId,s),(0,d.eq)(e.groupId,y),(0,d.eq)(e.isBreakGlass,!0)))).length>0)throw new Error(`Group ${y} is a break-glass proof group; a break-glass proof path must be one the synchronization can never touch.`)}async function T(r,s,y){let[I]=await r.select({id:a.id}).from(a).where((0,d.and)((0,d.eq)(a.id,y),(0,d.eq)(a.tenantId,s)));if(!I)throw new Error(`Group not found in tenant: ${y}`)}function G(r,s){return{linkId:Number(r.linkId),groupId:Number(r.groupId),tenantId:String(r.tenantId),provider:String(r.provider),externalId:String(r.externalId),externalKey:String(r.externalKey),membershipMode:K(String(r.membershipMode)),isSyncEnabled:!!r.isSyncEnabled,...r.syncedAt?{syncedAt:new Date(r.syncedAt).toISOString()}:{},...r.syncError?{syncError:String(r.syncError)}:{},unresolvedCount:s}}async function _(r,s){let y=await r.select({groupId:h.groupId,n:(0,d.count)()}).from(h).innerJoin(a,(0,d.and)((0,d.eq)(a.id,h.groupId),(0,d.eq)(a.tenantId,s))).where((0,d.isNull)(h.resolvedUserId)).groupBy(h.groupId);return new Map(y.map(I=>[Number(I.groupId),Number(I.n)]))}return{async listLinks(r){(0,x.assertTenantId)(r,"listLinks(tenantId)"),await l();let s=await i.select({linkId:o.id,groupId:o.groupId,tenantId:o.tenantId,provider:o.provider,externalId:o.externalId,externalKey:o.externalKey,membershipMode:o.membershipMode,isSyncEnabled:o.isSyncEnabled,syncedAt:o.syncedAt,syncError:o.syncError}).from(o).where((0,d.eq)(o.tenantId,r)).orderBy((0,d.asc)(o.groupId)),y=await _(i,r);return s.map(I=>((0,x.assertTenantMatch)(r,String(I.tenantId),"listLinks(row.tenantId)"),G(I,y.get(Number(I.groupId))??0)))},async externalGroupKey(r,s,y){(0,x.assertTenantId)(r,"externalGroupKey(tenantId)"),await l();let[I]=await(y??i).select({externalKey:o.externalKey}).from(o).where((0,d.and)((0,d.eq)(o.groupId,s),(0,d.eq)(o.tenantId,r)));return I?String(I.externalKey):null},async linkGroup(r,s,y,I){(0,x.assertTenantId)(r,"linkGroup(tenantId)");let D=K(y.membershipMode),k=y.externalId.trim(),R=(0,C.normalizeAuthEmail)(y.externalKey);if(k===""||R==="")throw new TypeError("[@aiquants/auth-directory-drizzle] externalId and externalKey must be non-empty");await l(),await u.linkable.assertLinkableAndWrite(r,s,async H=>{let E=H;await T(E,r,s),await S(E,r,s),await E.insert(o).output().values({groupId:s,tenantId:r,provider:y.provider,externalId:k,externalKey:R,membershipMode:D,isSyncEnabled:!0,vanishedObservations:0,createdAt:new Date,createdBy:I})}),f(r)},async unlinkGroup(r,s){(0,x.assertTenantId)(r,"unlinkGroup(tenantId)"),await l(),await i.transaction(async y=>{await T(y,r,s),await y.delete(h).where((0,d.eq)(h.groupId,s)),await y.delete(p).where((0,d.and)((0,d.eq)(p.groupId,s),(0,d.eq)(p.tenantId,r))),await y.delete(o).where((0,d.and)((0,d.eq)(o.groupId,s),(0,d.eq)(o.tenantId,r)))}),f(r)},async setSyncEnabled(r,s,y,I){(0,x.assertTenantId)(r,"setSyncEnabled(tenantId)"),await l(),await i.update(o).set({isSyncEnabled:y,updatedAt:new Date,updatedBy:I}).output().where((0,d.and)((0,d.eq)(o.groupId,s),(0,d.eq)(o.tenantId,r)))},async requestSync(r,s,y){(0,x.assertTenantId)(r,"requestSync(tenantId)"),await l(),await i.transaction(async I=>{s!==null&&await T(I,r,s),await I.insert(w).output().values({tenantId:r,groupId:s,requestedBy:y,requestedAt:new Date,createdAt:new Date,createdBy:y})})},async getStatus(r){(0,x.assertTenantId)(r,"getStatus(tenantId)"),await l();let s=await i.select({groupId:o.groupId,isSyncEnabled:o.isSyncEnabled,syncedAt:o.syncedAt,syncError:o.syncError}).from(o).where((0,d.eq)(o.tenantId,r)),[y]=await i.select({n:(0,d.count)()}).from(w).where((0,d.and)((0,d.eq)(w.tenantId,r),(0,d.isNull)(w.finishedAt))),I=s.filter(E=>!!E.isSyncEnabled),D=I.map(E=>E.syncedAt?new Date(E.syncedAt).getTime():null),k=D.length>0&&D.every(E=>E!==null)?Math.min(...D):null,H=s.length>0&&I.length===0||I.length>0&&(k===null||Date.now()-k>u.stalenessThresholdSec*1e3);return{isEnabled:u.isEnabled,provider:u.provider,linkedGroupCount:s.length,...k!==null?{oldestSyncedAt:new Date(k).toISOString()}:{},isStale:H,pausedGroupCount:s.length-I.length,stalenessThresholdSec:u.stalenessThresholdSec,erroredGroupIds:s.filter(E=>!!E.syncError).map(E=>Number(E.groupId)).sort((E,pe)=>E-pe),pendingRequestCount:Number(y?.n??0)}},async loadTenantMembershipPolicy(r){(0,x.assertTenantId)(r,"loadTenantMembershipPolicy(tenantId)");let s=c>0?b.get(r):void 0;if(s&&Date.now()-s.loadedAt<c)return s.policy;await l();let y=await i.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass,externalLinkId:o.id}).from(e).innerJoin(a,(0,d.and)((0,d.eq)(a.id,e.groupId),(0,d.eq)(a.tenantId,r))).leftJoin(o,(0,d.eq)(o.groupId,e.groupId)).where((0,d.eq)(e.tenantId,r));if(y.length===0){let R={kind:"no-proof-required"};return c>0&&b.set(r,{policy:R,loadedAt:Date.now()}),R}let I=y.map(R=>Number(R.groupId)),D=y.filter(R=>!!R.isBreakGlass);for(let R of D)if(R.externalLinkId!==null)throw new Error(`Break-glass group ${Number(R.groupId)} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`);let k={kind:"require-proof",proofGroupIds:I,breakGlassGroupIds:D.map(R=>Number(R.groupId))};return(0,se.assertTenantMembershipPolicy)(k,`tenant ${r}`),c>0&&b.set(r,{policy:k,loadedAt:Date.now()}),k},async setTenantMembershipGroup(r,s,y,I){(0,x.assertTenantId)(r,"setTenantMembershipGroup(tenantId)"),await l(),await i.transaction(async D=>{if(await T(D,r,s),y){let[R]=await D.select({id:o.id}).from(o).where((0,d.and)((0,d.eq)(o.tenantId,r),(0,d.eq)(o.groupId,s)));if(R)throw new Error(`Group ${s} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`)}if((await D.select({groupId:e.groupId}).from(e).where((0,d.and)((0,d.eq)(e.tenantId,r),(0,d.eq)(e.groupId,s)))).length>0){await D.update(e).set({isBreakGlass:y,updatedAt:new Date,updatedBy:I}).output().where((0,d.and)((0,d.eq)(e.tenantId,r),(0,d.eq)(e.groupId,s))),f(r);return}await D.insert(e).output().values({tenantId:r,groupId:s,isBreakGlass:y,createdAt:new Date,createdBy:I})}),f(r)},async removeTenantMembershipGroup(r,s){(0,x.assertTenantId)(r,"removeTenantMembershipGroup(tenantId)"),await l(),await i.transaction(async y=>{let D=(await y.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass}).from(e).where((0,d.eq)(e.tenantId,r))).filter(k=>Number(k.groupId)!==s);if(D.length>0&&!D.some(k=>!!k.isBreakGlass))throw new Error(`Removing group ${s} would leave tenant ${r} with a membership proof but no break-glass path.`);await y.delete(e).where((0,d.and)((0,d.eq)(e.tenantId,r),(0,d.eq)(e.groupId,s)))}),f(r)},async setGroupActive(r,s,y,I){(0,x.assertTenantId)(r,"setGroupActive(tenantId)"),await l(),await i.update(a).set({isActive:y,updatedAt:new Date,updatedBy:I}).output().where((0,d.and)((0,d.eq)(a.id,s),(0,d.eq)(a.tenantId,r)))},async listLedgerGroupIds(r,s){if((0,x.assertTenantId)(r,"listLedgerGroupIds(tenantId)"),!u.isEnabled)return[];await l();let y=await q(i,{ledger:h,group:a},{tenantId:r,externalUserId:s.externalUserId?.trim()||null,email:(0,C.normalizeAuthEmail)(s.email)});return[...new Set(y.map(I=>I.groupId))].sort((I,D)=>I-D)},async isEmailAllowedByGroup(r){if(!u.isEnabled)return!1;let s=(0,C.normalizeAuthEmail)(r);if(s==="")return!1;await l();let[y]=await i.select({id:h.id}).from(h).innerJoin(p,(0,d.eq)(p.groupId,h.groupId)).innerJoin(a,(0,d.and)((0,d.eq)(a.id,h.groupId),(0,d.eq)(a.isActive,!0))).where((0,d.eq)(h.email,s));return!!y},async listAllowlistGroups(r){return(0,x.assertTenantId)(r,"listAllowlistGroups(tenantId)"),await l(),(await i.select({id:p.id,groupId:p.groupId,groupKey:a.groupKey,groupName:a.name,description:p.description}).from(p).innerJoin(a,(0,d.and)((0,d.eq)(a.id,p.groupId),(0,d.eq)(a.tenantId,r))).where((0,d.eq)(p.tenantId,r)).orderBy((0,d.asc)(a.name))).map(y=>({id:Number(y.id),type:"group",groupId:Number(y.groupId),groupKey:String(y.groupKey),groupName:String(y.groupName),...y.description?{description:String(y.description)}:{},owner:{kind:"tenant",tenantId:r},readOnly:!1}))},async addAllowlistGroup(r,s,y,I){(0,x.assertTenantId)(r,"addAllowlistGroup(tenantId)"),await l(),await i.transaction(async D=>{await T(D,r,s);let[k]=await D.select({id:o.id}).from(o).where((0,d.and)((0,d.eq)(o.groupId,s),(0,d.eq)(o.tenantId,r)));if(!k)throw new Error(`Group ${s} is locally managed; only externally-sourced groups can gate sign-in, because the decision reads the upstream member ledger.`);await D.insert(p).output().values({tenantId:r,groupId:s,description:y??null,createdAt:new Date,createdBy:I})})},async removeAllowlistGroup(r,s){(0,x.assertTenantId)(r,"removeAllowlistGroup(tenantId)"),await l(),await i.delete(p).where((0,d.and)((0,d.eq)(p.id,s),(0,d.eq)(p.tenantId,r)))},async countProjectedMembers(r,s){(0,x.assertTenantId)(r,"countProjectedMembers(tenantId)"),await l();let[y]=await i.select({n:(0,d.count)()}).from(v).innerJoin(a,(0,d.and)((0,d.eq)(a.id,v.groupId),(0,d.eq)(a.tenantId,r))).where((0,d.eq)(v.groupId,s));return Number(y?.n??0)}}}var ae=require("@aiquants/auth-core"),j=require("@aiquants/authz-core");async function Ae(t,n){let g=(0,j.assertTenantId)(n.tenantId,"promoteLedgerForUser(tenantId)");if(!Number.isInteger(n.userId)||n.userId<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] userId must be a positive integer, received ${String(n.userId)}`);let u=(0,ae.normalizeAuthEmail)(n.email),i=n.externalUserId?.trim()||null;if(i===null&&u==="")return{addedGroupIds:[],matchedLedgerIds:[]};let l=await t.findLedgerMatches({tenantId:g,externalUserId:i,email:u});if(l.length===0)return{addedGroupIds:[],matchedLedgerIds:[]};for(let o of l)(0,j.assertTenantMatch)(g,o.tenantId,"promoteLedgerForUser(findLedgerMatches row)");let c=[...new Set(l.map(o=>o.ledgerId))].sort((o,h)=>o-h),b=[...new Set(l.map(o=>o.groupId))].sort((o,h)=>o-h);return{addedGroupIds:[...await t.attachUserToGroups({tenantId:g,userId:n.userId,ledgerIds:c,groupIds:b})].sort((o,h)=>o-h),matchedLedgerIds:c}}var Q=require("@aiquants/authz-core"),M=require("drizzle-orm");var de=2100;var Be=500;function L(t,n=500){if(!Number.isInteger(n)||n<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] chunk size must be a positive integer, received ${String(n)}`);let g=[];for(let u=0;u<t.length;u+=n)g.push(t.slice(u,u+n));return g}function N(t,n,g=100){if(!Number.isInteger(n)||n<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] parametersPerItem must be a positive integer, received ${String(n)}`);if(!Number.isInteger(g)||g<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] reservedParameters must be a non-negative integer, received ${String(g)}`);let u=2100-g,i=Math.floor(u/n);if(i<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] a single item binds ${n} parameters, which exceeds the ${u} available in one statement`);return L(t,i)}var ve=4,Te=100;function De(t,n,g,u){let i=t,l=u.ensureConnection??(async()=>{}),c=u.now??(()=>new Date),b=n.TDGroupMemberEmail,{TMGroup:f,TDGroupMember:o}=g;return{async findLedgerMatches(h){return(0,Q.assertTenantId)(h.tenantId,"findLedgerMatches(tenantId)"),await l(),q(i,{ledger:b,group:f},h)},async attachUserToGroups({tenantId:h,userId:e,ledgerIds:p,groupIds:w}){return(0,Q.assertTenantId)(h,"attachUserToGroups(tenantId)"),await l(),p.length===0?[]:i.transaction(async a=>{let v=new Set;for(let r of L(w)){let s=await a.select({id:f.id}).from(f).where((0,M.and)((0,M.inArray)(f.id,r),(0,M.eq)(f.tenantId,h),(0,M.eq)(f.isActive,!0)));for(let y of s)v.add(Number(y.id))}let S=w.filter(r=>v.has(r));if(S.length===0)return[];let T=new Set;for(let r of L(S)){let s=await a.select({groupId:o.groupId}).from(o).where((0,M.and)((0,M.eq)(o.userId,e),(0,M.inArray)(o.groupId,r)));for(let y of s)T.add(Number(y.groupId))}let G=S.filter(r=>!T.has(r)),_=Math.max(1,2100-Te-S.length);for(let r of L(p,_))await a.update(b).set({resolvedUserId:e}).output().where((0,M.and)((0,M.inArray)(b.id,r),(0,M.inArray)(b.groupId,S)));if(G.length>0){let r=c();for(let s of N(G,ve))await a.insert(o).output().values(s.map(y=>({groupId:y,userId:e,createdAt:r,createdBy:u.actor})))}return G})}}}var U=require("@aiquants/authz-drizzle"),m=require("drizzle-orm/mssql-core"),ue="provider",xe=["TDGroupExternalLink","TDTenantMembershipGroup","TDAllowlistGroup","TDDirectorySyncRequest","TMAllowlist"];function ke(t,n){let g=(0,m.mssqlSchema)(t),u=()=>({createdAt:(0,m.datetime2)("created_at"),createdBy:(0,m.nvarchar)("created_by",{length:50}),updatedAt:(0,m.datetime2)("updated_at"),updatedBy:(0,m.nvarchar)("updated_by",{length:50})}),i=()=>({createdAt:(0,m.datetime2)("created_at"),createdBy:(0,m.nvarchar)("created_by",{length:50})}),l=g.table("TDGroupExternalLink",{id:(0,m.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,m.bigint)("group_id",{mode:"number"}).notNull(),tenantId:(0,m.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,m.nvarchar)(ue,{length:32}).notNull(),externalId:(0,m.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,m.nvarchar)("external_key",{length:320}).notNull(),membershipMode:(0,m.nvarchar)("membership_mode",{length:16}).notNull(),isSyncEnabled:(0,m.bit)("is_sync_enabled").notNull(),syncedAt:(0,m.datetime2)("synced_at"),syncError:(0,m.nvarchar)("sync_error",{length:1e3}),vanishedObservations:(0,m.int)("vanished_observations").notNull(),...u()},e=>[(0,m.primaryKey)({name:`${t}_TDGroupExternalLink_pk`,columns:[e.id]}),(0,m.uniqueIndex)(`${t}_TDGroupExternalLink_group_uq`).on(e.groupId),(0,m.uniqueIndex)(`${t}_TDGroupExternalLink_external_uq`).on(e.tenantId,e.provider,e.externalId),(0,m.foreignKey)({name:`${t}_TDGroupExternalLink_group_id_fk`,columns:[e.groupId],foreignColumns:[n.groupTable.id]})]),c=g.table("TDGroupMemberEmail",{id:(0,m.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,m.bigint)("group_id",{mode:"number"}).notNull(),email:(0,m.nvarchar)("email",{length:320}).notNull(),externalUserId:(0,m.nvarchar)("external_user_id",{length:128}),resolvedUserId:(0,m.bigint)("resolved_user_id",{mode:"number"}),observedAt:(0,m.datetime2)("observed_at").notNull(),...u()},e=>[(0,m.primaryKey)({name:`${t}_TDGroupMemberEmail_pk`,columns:[e.id]}),(0,m.uniqueIndex)(`${t}_TDGroupMemberEmail_group_email_uq`).on(e.groupId,e.email),(0,m.foreignKey)({name:`${t}_TDGroupMemberEmail_group_id_fk`,columns:[e.groupId],foreignColumns:[n.groupTable.id]})]),b=g.table("TDTenantMembershipGroup",{tenantId:(0,m.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,m.bigint)("group_id",{mode:"number"}).notNull(),isBreakGlass:(0,m.bit)("is_break_glass").notNull(),...i()},e=>[(0,m.primaryKey)({name:`${t}_TDTenantMembershipGroup_pk`,columns:[e.tenantId,e.groupId]}),(0,m.foreignKey)({name:`${t}_TDTenantMembershipGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[n.groupTable.id]})]),f=g.table("TDAllowlistGroup",{id:(0,m.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,m.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,m.bigint)("group_id",{mode:"number"}).notNull(),description:(0,m.nvarchar)("description",{length:512}),...i()},e=>[(0,m.primaryKey)({name:`${t}_TDAllowlistGroup_pk`,columns:[e.id]}),(0,m.uniqueIndex)(`${t}_TDAllowlistGroup_tenant_group_uq`).on(e.tenantId,e.groupId),(0,m.foreignKey)({name:`${t}_TDAllowlistGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[n.groupTable.id]})]),o=g.table("TDDirectorySyncRequest",{id:(0,m.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,m.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,m.bigint)("group_id",{mode:"number"}),requestedBy:(0,m.nvarchar)("requested_by",{length:320}).notNull(),requestedAt:(0,m.datetime2)("requested_at").notNull(),pickedAt:(0,m.datetime2)("picked_at"),finishedAt:(0,m.datetime2)("finished_at"),result:(0,m.nvarchar)("result",{length:32}),error:(0,m.nvarchar)("error",{length:2e3}),...i()},e=>[(0,m.primaryKey)({name:`${t}_TDDirectorySyncRequest_pk`,columns:[e.id]})]),h=g.table("TMAllowlist",{id:(0,m.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,m.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),pattern:(0,m.nvarchar)("pattern",{length:256}).notNull(),type:(0,m.nvarchar)("type",{length:32}).notNull(),description:(0,m.nvarchar)("description",{length:512}),...i()},e=>[(0,m.primaryKey)({name:`${t}_TMAllowlist_pk`,columns:[e.id]}),(0,m.uniqueIndex)(`${t}_TMAllowlist_pattern_uq`).on(e.tenantId,e.pattern)]);return{TDGroupExternalLink:l,TDGroupMemberEmail:c,TDTenantMembershipGroup:b,TDAllowlistGroup:f,TDDirectorySyncRequest:o,TMAllowlist:h}}var F=require("@aiquants/auth-directory-core");var Z=(t,n=20)=>t.length<=n?t.join(", "):`${t.slice(0,n).join(", ")} \u307B\u304B ${t.length-n} \u4EF6`,ee={upstreamUnreadable:t=>`\u4E0A\u6D41\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${t}`,localWriteFailed:t=>`\u30ED\u30FC\u30AB\u30EB\u3078\u306E\u66F8\u304D\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${t}`,resolverIncomplete:(t,n)=>`\u4E0A\u6D41\u304C ${t} \u4EF6\u306E\u30E1\u30F3\u30D0\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F\u304C\u3001\u7A81\u5408\u7D50\u679C\u306F ${n} \u4EF6\u3067\u3057\u305F\u3002\u4EF6\u6570\u304C\u6E1B\u308B\u7A81\u5408\u306F\u3001\u89E3\u6C7A\u3067\u304D\u306A\u304B\u3063\u305F\u4EBA\u7269\u3092\u300C\u4E0A\u6D41\u304B\u3089\u6D88\u3048\u305F\u300D\u3068\u3057\u3066\u524A\u9664\u3057\u3066\u3057\u307E\u3046\u305F\u3081\u4E2D\u65AD\u3057\u307E\u3057\u305F\u3002\u89E3\u6C7A\u3067\u304D\u306A\u3044\u30E1\u30F3\u30D0\u30FC\u306F\u7701\u7565\u305B\u305A resolvedUserId: null \u3067\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`,groupMissing:(t,n,g)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 (${t})\u3002\u9023\u7D9A ${n} \u56DE\u76EE\u3067\u3059\u3002${g} \u56DE\u306B\u9054\u3057\u305F\u6642\u70B9\u3067\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u3002`,groupVanished:(t,n)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C ${n} \u56DE\u9023\u7D9A\u3067\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F (${t})\u3002\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3057\u305F\u3002\u4E0A\u6D41\u304C\u5FA9\u65E7\u3059\u308C\u3070\u6B21\u56DE\u306E\u540C\u671F\u3067\u81EA\u52D5\u7684\u306B\u518D\u6709\u52B9\u5316\u3055\u308C\u307E\u3059\u3002`,planRefused:t=>{if(t.reason==="invalid-upstream-data")return`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: \u4E0A\u6D41\u306E\u30E1\u30F3\u30D0\u30FC\u4E00\u89A7\u306B\u6271\u3048\u306A\u3044\u30A2\u30C9\u30EC\u30B9\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059 (${Z(t.offendingEmails)})\u3002`;let n=t.axis==="membership"?"\u6A29\u9650\u3092\u5931\u3046\u4EBA\u6570":"\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u304D\u306A\u304F\u306A\u308B\u4EBA\u6570",g=t.axis==="membership"?t.withheldProjectionRemoves.map(String):t.withheldLedgerDeletes;return[`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: ${n}\u304C ${t.population} \u4EBA\u4E2D ${t.removalCount} \u4EBA\u306B\u306A\u308A\u307E\u3059`,`(\u4E0A\u9650: ${t.limits.maxRemovalCount} \u4EF6 / \u6BD4\u7387 ${t.limits.maxRemovalRatio})\u3002`,`\u5DEE\u3057\u6B62\u3081\u305F\u5BFE\u8C61: ${Z(g)}\u3002`,"\u4E0A\u6D41\u5074\u306E\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u610F\u56F3\u3057\u305F\u5909\u66F4\u3067\u3042\u308C\u3070\u3001\u3053\u306E\u8EF8\u306E\u4E0A\u9650\u3060\u3051\u3092\u4E00\u6642\u7684\u306B\u5F15\u304D\u4E0A\u3052\u3066\u518D\u5B9F\u884C\u3057\u307E\u3059\u3002"].join(" ")},removalsRefused:t=>`${t.length} \u4EF6\u306E\u6240\u5C5E\u89E3\u9664\u304C\u6700\u5F8C\u306E\u7BA1\u7406\u8005\u30AC\u30FC\u30C9\u306B\u3088\u308A\u62D2\u5426\u3055\u308C\u307E\u3057\u305F (\u30E6\u30FC\u30B6\u30FC ${Z(t.map(n=>String(n.userId)))})\u3002\u6B8B\u308A\u306E\u5909\u66F4\u306F\u9069\u7528\u6E08\u307F\u3067\u3059\u3002`,foreignProvider:(t,n)=>`\u3053\u306E\u30EA\u30F3\u30AF\u306E\u4F9B\u7D66\u5143\u306F ${t} \u3067\u3059\u304C\u3001\u5B9F\u884C\u4E2D\u306E\u540C\u671F\u306F ${n} \u7528\u3067\u3059\u3002\u5BFE\u5FDC\u3059\u308B\u540C\u671F\u30B8\u30E7\u30D6\u304B\u3089\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`};var re=["aborted","missing","vanished","upstream-failed","local-failed","foreign-provider"];function Ge(t){let{directory:n,repository:g,policy:u,now:i,logger:l}=t,c=t.messages??ee;if((0,F.assertBlastRadiusPolicy)(u),!Number.isInteger(t.vanishedThreshold)||t.vanishedThreshold<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] vanishedThreshold must be a positive integer, received ${String(t.vanishedThreshold)}`);if(typeof t.provider!="string"||t.provider.trim()==="")throw new TypeError("[@aiquants/auth-directory-drizzle] provider must be a non-empty string so links of other providers can be told apart");async function b(e,p){try{await g.recordFailure(e,p)}catch(w){l.error(`[auth-directory] could not record the failure for ${e.externalKey}:`,w)}}async function f(e){try{let p=await n.getGroup(e.externalId);return p===null?{kind:"missing"}:{kind:"ok",group:p,members:await n.listGroupMembers(p,e.membershipMode)}}catch(p){return{kind:"failed",error:p instanceof Error?p:new Error(String(p))}}}async function o(e){let p=await g.recordVanishedObservation(e,c.groupMissing(e.externalKey,e.vanishedObservations+1,t.vanishedThreshold));return p<t.vanishedThreshold?(l.warn(`[auth-directory] ${e.externalKey} missing upstream (${p}/${t.vanishedThreshold})`),{kind:"missing",link:e,observations:p}):(await g.markGroupInactive(e),await b(e,c.groupVanished(e.externalKey,p)),l.warn(`[auth-directory] ${e.externalKey} deactivated after ${p} consecutive absences`),{kind:"vanished",link:e})}async function h(e){if(e.provider!==t.provider)return await b(e,c.foreignProvider(e.provider,t.provider)),l.warn(`[auth-directory] skipping ${e.externalKey}: belongs to provider ${e.provider}`),{kind:"foreign-provider",link:e};let p=await f(e);if(p.kind==="missing")return o(e);if(p.kind==="failed")return await b(e,c.upstreamUnreadable(p.error.message)),l.error(`[auth-directory] upstream read failed for ${e.externalKey}:`,p.error),{kind:"upstream-failed",link:e,error:p.error};try{let[w,a]=await Promise.all([g.loadGroupSnapshot(e),g.resolveMembers(p.members)]);if(a.length!==p.members.length){let _=c.resolverIncomplete(p.members.length,a.length);return await b(e,_),l.error(`[auth-directory] ${_}`),{kind:"local-failed",link:e,error:new Error(_)}}let v=(0,F.reconcile)(w,a,u);if(v.aborted)return await b(e,c.planRefused(v.aborted)),l.warn(`[auth-directory] plan refused for ${e.externalKey}: ${v.aborted.reason}`),{kind:"aborted",link:e,plan:v};let S=v.ledgerUpserts.length>0||v.ledgerDeletes.length>0||v.projectionAdds.length>0||v.projectionRemoves.length>0,T=i(),G={refusals:[]};return S&&(G=await g.applyPlan(e,v,T)),await g.recordSuccess(e,p.group,T),G.refusals.length>0&&(await b(e,c.removalsRefused(G.refusals)),l.warn(`[auth-directory] ${G.refusals.length} removal(s) refused for ${e.externalKey}`)),v.unresolvedEmails.length>0&&l.info(`[auth-directory] ${e.externalKey}: ${v.unresolvedEmails.length} upstream member(s) have no local user yet`),S?{kind:"applied",link:e,plan:v}:{kind:"unchanged",link:e}}catch(w){let a=w instanceof Error?w:new Error(String(w));return await b(e,c.localWriteFailed(a.message)),l.error(`[auth-directory] local write failed for ${e.externalKey}:`,a),{kind:"local-failed",link:e,error:a}}}return{syncOne:h,async run(e={}){let p=await g.listSyncableLinks(e),w=[];for(let v of p)try{w.push(await h(v))}catch(S){let T=S instanceof Error?S:new Error(String(S));l.error(`[auth-directory] ${v.externalKey} ended abnormally:`,T),w.push({kind:"local-failed",link:v,error:T})}let a=w.filter(v=>re.includes(v.kind)).length;return l.info(`[auth-directory] run finished: ${w.length} link(s), ${a} unhealthy`),{outcomes:w,unhealthyCount:a}}}}var le=require("@aiquants/authz-core"),P=require("drizzle-orm"),te=2e3;function Re(t,n,g){let u=t,i=g.ensureConnection??(async()=>{}),l=n.TDDirectorySyncRequest,c=g.maxErrorLength??te;if(!Number.isInteger(c)||c<1||c>te)throw new TypeError(`[@aiquants/auth-directory-drizzle] maxErrorLength must be an integer within 1..${te}, received ${String(g.maxErrorLength)}`);return{async claim(){await i();let b=await u.select({id:l.id}).from(l).where((0,P.isNull)(l.pickedAt)).orderBy((0,P.asc)(l.id));for(let f of b){let h=(await u.update(l).set({pickedAt:g.now()}).output().where((0,P.and)((0,P.eq)(l.id,Number(f.id)),(0,P.isNull)(l.pickedAt))))?.[0];if(!h)continue;let e=String(h.tenant_id??h.tenantId??""),p=h.group_id??h.groupId??null;return{id:Number(h.id),tenantId:(0,le.assertTenantId)(e,"claim(row.tenantId)"),groupId:p===null?null:Number(p),requestedBy:String(h.requested_by??h.requestedBy??"")}}return null},async finish(b,f){await i();let o=f.error?f.error.slice(0,c):null;await u.update(l).set({finishedAt:g.now(),result:f.result,error:o}).output().where((0,P.eq)(l.id,b))},async listStranded(b){return await i(),(await u.select({id:l.id,pickedAt:l.pickedAt}).from(l).where((0,P.isNull)(l.finishedAt)).orderBy((0,P.asc)(l.id))).filter(o=>o.pickedAt!==null&&new Date(o.pickedAt)<b).map(o=>Number(o.id))}}}var ne=require("@aiquants/auth-core"),O=require("@aiquants/authz-core"),A=require("drizzle-orm");var Ee=7,Me=4;function Pe(t,n,g,u){let i=t,l=u.ensureConnection??(async()=>{}),{TDGroupExternalLink:c,TDGroupMemberEmail:b}=n,{TMGroup:f,TDGroupMember:o}=g,h=u.actor;return{async listSyncableLinks(e){await l();let p=[(0,A.eq)(c.isSyncEnabled,!0)];return e.tenantId!==void 0&&p.push((0,A.eq)(c.tenantId,(0,O.assertTenantId)(e.tenantId,"listSyncableLinks(tenantId)"))),e.groupId!==void 0&&p.push((0,A.eq)(c.groupId,e.groupId)),(await i.select({linkId:c.id,groupId:c.groupId,linkTenantId:c.tenantId,groupTenantId:f.tenantId,groupKey:f.groupKey,isGroupActive:f.isActive,provider:c.provider,externalId:c.externalId,externalKey:c.externalKey,membershipMode:c.membershipMode,vanishedObservations:c.vanishedObservations}).from(c).innerJoin(f,(0,A.eq)(f.id,c.groupId)).where((0,A.and)(...p))).map(a=>((0,O.assertTenantMatch)(String(a.groupTenantId),String(a.linkTenantId),"listSyncableLinks(link.tenantId vs TMGroup.tenant_id)"),{linkId:Number(a.linkId),groupId:Number(a.groupId),groupKey:String(a.groupKey),tenantId:String(a.groupTenantId),provider:String(a.provider),externalId:String(a.externalId),externalKey:String(a.externalKey),membershipMode:K(String(a.membershipMode)),isGroupActive:!!a.isGroupActive,vanishedObservations:Number(a.vanishedObservations??0)}))},async loadGroupSnapshot(e){await l();let[p,w]=await Promise.all([i.select({email:b.email,externalUserId:b.externalUserId,resolvedUserId:b.resolvedUserId}).from(b).where((0,A.eq)(b.groupId,e.groupId)),i.select({userId:o.userId}).from(o).where((0,A.eq)(o.groupId,e.groupId))]);return{ledger:p.map(a=>({email:String(a.email),...a.externalUserId?{externalUserId:String(a.externalUserId)}:{},resolvedUserId:a.resolvedUserId===null?null:Number(a.resolvedUserId)})),projectedUserIds:w.map(a=>Number(a.userId))}},resolveMembers(e){return u.memberResolver.resolve(e)},async applyPlan(e,p,w){(0,O.assertTenantId)(e.tenantId,"applyPlan(link.tenantId)"),await l(),(p.ledgerUpserts.length>0||p.projectionAdds.length>0)&&await i.transaction(async S=>{let T=p.ledgerUpserts.map(r=>({...r,email:(0,ne.normalizeAuthEmail)(r.email)})),G=new Map;for(let r of L(T.map(s=>s.email))){let s=await S.select({id:b.id,email:b.email}).from(b).where((0,A.and)((0,A.eq)(b.groupId,e.groupId),(0,A.inArray)(b.email,r)));for(let y of s)G.set(String(y.email),Number(y.id))}let _=T.filter(r=>!G.has(r.email));for(let r of N(_,Ee))await S.insert(b).output().values(r.map(s=>({groupId:e.groupId,email:s.email,externalUserId:s.externalUserId??null,resolvedUserId:s.resolvedUserId,observedAt:w,createdAt:w,createdBy:h})));for(let r of T){let s=G.get(r.email);s!==void 0&&await S.update(b).set({externalUserId:r.externalUserId??null,resolvedUserId:r.resolvedUserId,observedAt:w,updatedAt:w,updatedBy:h}).output().where((0,A.eq)(b.id,s))}for(let r of N(p.projectionAdds,Me)){let s=new Set((await S.select({userId:o.userId}).from(o).where((0,A.and)((0,A.eq)(o.groupId,e.groupId),(0,A.inArray)(o.userId,r)))).map(I=>Number(I.userId))),y=r.filter(I=>!s.has(I));y.length>0&&await S.insert(o).output().values(y.map(I=>({groupId:e.groupId,userId:I,createdAt:w,createdBy:h})))}});let a=[];for(let S of p.projectionRemoves)try{await u.lockoutGuard.guardedWrite(e.tenantId,{removeGroupMembership:{groupId:e.groupId,userId:S}},"remove-group-member",T=>T.delete(o).where((0,A.and)((0,A.eq)(o.groupId,e.groupId),(0,A.eq)(o.userId,S))))}catch(T){a.push({userId:S,applied:!1,refusal:T instanceof Error?T.message:String(T)})}let v=a.map(S=>S.userId);return p.ledgerDeletes.length>0&&await i.transaction(async S=>{for(let T of L(p.ledgerDeletes)){let G=v.length>0?(0,A.or)((0,A.isNull)(b.resolvedUserId),(0,A.notInArray)(b.resolvedUserId,v)):void 0;await S.delete(b).where((0,A.and)((0,A.eq)(b.groupId,e.groupId),(0,A.inArray)(b.email,T),...G?[G]:[]))}}),{refusals:a}},async recordSuccess(e,p,w){await l(),await i.transaction(async a=>{await a.update(c).set({syncedAt:w,syncError:null,vanishedObservations:0,externalId:p.externalId,externalKey:(0,ne.normalizeAuthEmail)(p.externalKey),updatedAt:w,updatedBy:h}).output().where((0,A.eq)(c.id,e.linkId)),e.isGroupActive||await a.update(f).set({isActive:!0,updatedAt:w,updatedBy:h}).output().where((0,A.and)((0,A.eq)(f.id,e.groupId),(0,A.eq)(f.tenantId,e.tenantId)))})},async recordFailure(e,p){await l(),await i.update(c).set({syncError:p.slice(0,1e3),updatedAt:new Date,updatedBy:h}).output().where((0,A.eq)(c.id,e.linkId))},async recordVanishedObservation(e,p){await l();let w=e.vanishedObservations+1;return await i.update(c).set({vanishedObservations:w,syncError:p.slice(0,1e3),updatedAt:new Date,updatedBy:h}).output().where((0,A.eq)(c.id,e.linkId)),w},async markGroupInactive(e){await l(),await i.update(f).set({isActive:!1,updatedAt:new Date,updatedBy:h}).output().where((0,A.and)((0,A.eq)(f.id,e.groupId),(0,A.eq)(f.tenantId,e.tenantId)))}}}0&&(module.exports={AUTH_DIRECTORY_PROVIDER_COLUMN,AUTH_DIRECTORY_TENANT_SCOPED_TABLES,MAX_IN_LIST,MAX_PARAMETERS,MEMBERSHIP_MODES,UNHEALTHY_SYNC_OUTCOMES,assertGroupMayBeLinked,assertMembershipMode,chunk,chunkByParameters,createDirectoryLinkStore,createDirectorySync,createDirectorySyncRepository,createPromotionRepository,createSyncRequestQueue,defaultExternalGroupGuardMessage,defaultSyncMessages,defineAuthDirectorySchema,findLedgerRowsForIdentity,promoteLedgerForUser,withExternalGroupGuards});
2
2
  //# sourceMappingURL=index.js.map