@aiquants/auth-directory-drizzle 0.5.0 → 0.6.0

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.mts CHANGED
@@ -1190,6 +1190,14 @@ type DirectoryLinkStoreOptions = {
1190
1190
  stalenessThresholdSec: number;
1191
1191
  /** False when directory integration is not wired for this deployment. 未結線なら false。 */
1192
1192
  isEnabled: boolean;
1193
+ /**
1194
+ * Upstream system this deployment synchronizes against, e.g. `"google"`. 同期先の上流システム。
1195
+ *
1196
+ * 新しい連携を作る面へそのまま渡る。UI に供給元を焼き込ませないための注入点であり、
1197
+ * 同期ランナーに設定する値と同一でなければならない — 食い違うと、作られたリンクは
1198
+ * 同期側で「別プロバイダ」として恒久的に拒否される。
1199
+ */
1200
+ provider: string;
1193
1201
  /**
1194
1202
  * Milliseconds to reuse a loaded tenant-membership policy. 帰属方針を再利用するミリ秒。
1195
1203
  *
package/dist/index.d.ts CHANGED
@@ -1190,6 +1190,14 @@ type DirectoryLinkStoreOptions = {
1190
1190
  stalenessThresholdSec: number;
1191
1191
  /** False when directory integration is not wired for this deployment. 未結線なら false。 */
1192
1192
  isEnabled: boolean;
1193
+ /**
1194
+ * Upstream system this deployment synchronizes against, e.g. `"google"`. 同期先の上流システム。
1195
+ *
1196
+ * 新しい連携を作る面へそのまま渡る。UI に供給元を焼き込ませないための注入点であり、
1197
+ * 同期ランナーに設定する値と同一でなければならない — 食い違うと、作られたリンクは
1198
+ * 同期側で「別プロバイダ」として恒久的に拒否される。
1199
+ */
1200
+ provider: string;
1193
1201
  /**
1194
1202
  * Milliseconds to reuse a loaded tenant-membership policy. 帰属方針を再利用するミリ秒。
1195
1203
  *
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,b)=>{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:!(b=le(o,s))||b.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,b){return new Set(t.filter(s=>s.roleId===o&&s.appKey===g&&s.resourceKey===b).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 b(s,a,p,y,w){if(y.size===0)return;let n=await s.listGroupRoles(p),h=new Set;for(let e of n){if(!y.has(e.roleId)||h.has(e.groupId))continue;h.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 h=await w.listGrants(s);if(J(H(h,p,o.adminAppKey,o.adminResourceKey))){let e=await o.linkLookup.externalGroupKey(s,a,n);if(e!==null)throw new Error(o.message(V(h,p),e))}await w.assignGroupRole(s,a,p,y)})},async createGrant(s,a,p){await g.withTransaction(async(y,w)=>{let h=(await y.listResources(s)).find(e=>e.id===a.resourceId);if(h?.appKey===o.adminAppKey&&h?.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 b(y,w,s,new Set([a.roleId]),e)}await y.createGrant(s,a,p)})}}}async function we(t,o,g,b){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,b.adminAppKey,b.adminResourceKey)))throw new Error(b.message(p.roleKey||V(a,p.roleId),p.groupKey))}var $=require("drizzle-orm");async function z(t,o,g){let{ledger:b,group:s}=o,{tenantId:a,externalUserId:p,email:y}=g;if(p===null&&y==="")return[];let w={ledgerId:b.id,groupId:b.groupId,tenantId:s.tenantId,externalUserId:b.externalUserId},n=async d=>(await t.select(w).from(b).innerJoin(s,(0,$.and)((0,$.eq)(s.id,b.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)})),h=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)(b.externalUserId,p));if(y==="")return h(e);let l=await n((0,$.eq)(b.email,y)),A=d=>p!==null&&d.externalUserId!==null&&d.externalUserId!==p;return h([...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,b){let s=t,a=b.ensureConnection??(async()=>{}),p=b.policyCacheTtlMs??0;if(!Number.isFinite(p)||p<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] policyCacheTtlMs must be a non-negative number, received ${String(b.policyCacheTtlMs)}`);let y=new Map,w=r=>y.delete(r),{TDGroupExternalLink:n,TDGroupMemberEmail:h,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:h.groupId,n:(0,u.count)()}).from(h).innerJoin(d,(0,u.and)((0,u.eq)(d.id,h.groupId),(0,u.eq)(d.tenantId,i))).where((0,u.isNull)(h.resolvedUserId)).groupBy(h.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 b.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(h).where((0,u.eq)(h.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>b.stalenessThresholdSec*1e3);return{isEnabled:b.isEnabled,linkedGroupCount:i.length,...k!==null?{oldestSyncedAt:new Date(k).toISOString()}:{},isStale:j,pausedGroupCount:i.length-f.length,stalenessThresholdSec:b.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)"),!b.isEnabled)return[];await a();let m=await z(s,{ledger:h,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(!b.isEnabled)return!1;let i=(0,O.normalizeAuthEmail)(r);if(i==="")return!1;await a();let[m]=await s.select({id:h.id}).from(h).innerJoin(l,(0,u.eq)(l.groupId,h.groupId)).innerJoin(d,(0,u.and)((0,u.eq)(d.id,h.groupId),(0,u.eq)(d.isActive,!0))).where((0,u.eq)(h.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 b=(0,ie.normalizeAuthEmail)(o.email),s=o.externalUserId?.trim()||null;if(s===null&&b==="")return{addedGroupIds:[],matchedLedgerIds:[]};let a=await t.findLedgerMatches({tenantId:g,externalUserId:s,email:b});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,h)=>n-h),y=[...new Set(a.map(n=>n.groupId))].sort((n,h)=>n-h);return{addedGroupIds:[...await t.attachUserToGroups({tenantId:g,userId:o.userId,ledgerIds:p,groupIds:y})].sort((n,h)=>n-h),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 b=0;b<t.length;b+=o)g.push(t.slice(b,b+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 b=2100-g,s=Math.floor(b/o);if(s<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] a single item binds ${o} parameters, which exceeds the ${b} available in one statement`);return L(t,s)}var Ae=4,Se=100;function ve(t,o,g,b){let s=t,a=b.ensureConnection??(async()=>{}),p=b.now??(()=>new Date),y=o.TDGroupMemberEmail,{TMGroup:w,TDGroupMember:n}=g;return{async findLedgerMatches(h){return(0,Y.assertTenantId)(h.tenantId,"findLedgerMatches(tenantId)"),await a(),z(s,{ledger:y,group:w},h)},async attachUserToGroups({tenantId:h,userId:e,ledgerIds:l,groupIds:A}){return(0,Y.assertTenantId)(h,"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,h),(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:b.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),b=()=>({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(),...b()},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(),...b()},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]})]),h=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:h}}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:b,now:s,logger:a}=t,p=t.messages??Q;if((0,C.assertBlastRadiusPolicy)(b),!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 h(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,b);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:h,async run(e={}){let l=await g.listSyncableLinks(e),A=[];for(let v of l)try{A.push(await h(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 b=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 b.select({id:a.id}).from(a).where((0,P.isNull)(a.pickedAt)).orderBy((0,P.asc)(a.id));for(let w of y){let h=(await b.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(!h)continue;let e=String(h.tenant_id??h.tenantId??""),l=h.group_id??h.groupId??null;return{id:Number(h.id),tenantId:(0,de.assertTenantId)(e,"claim(row.tenantId)"),groupId:l===null?null:Number(l),requestedBy:String(h.requested_by??h.requestedBy??"")}}return null},async finish(y,w){await s();let n=w.error?w.error.slice(0,p):null;await b.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 b.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,b){let s=t,a=b.ensureConnection??(async()=>{}),{TDGroupExternalLink:p,TDGroupMemberEmail:y}=o,{TMGroup:w,TDGroupMember:n}=g,h=b.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 b.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:h})));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:h}).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:h})))}});let d=[];for(let I of l.projectionRemoves)try{await b.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:h}).output().where((0,S.eq)(p.id,e.linkId)),e.isGroupActive||await d.update(w).set({isActive:!0,updatedAt:A,updatedBy:h}).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:h}).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:h}).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:h}).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 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});
2
2
  //# sourceMappingURL=index.js.map