@authhero/multi-tenancy 14.23.1 → 14.24.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.
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("hono"),t=require("authhero"),n=require("@hono/zod-openapi");function r(e){let{controlPlaneTenantId:t,requireOrganizationMatch:n=!0}=e;return{async onTenantAccessValidation(e,r){if(r===t)return!0;if(n){let t=e.var.org_name,n=e.var.organization_id,i=t||n;return i?i.toLowerCase()===r.toLowerCase():!1}return!0}}}function i(e,t,n,r){if(t===n)return!0;let i=r||e;return i?i.toLowerCase()===t.toLowerCase():!1}function a(e){return{async resolveDataAdapters(t){try{return await e.getAdapters(t)}catch(e){console.error(`Failed to resolve data adapters for tenant ${t}:`,e);return}}}}function o(e){return`urn:authhero:tenant:${e.toLowerCase()}`}function s(e){return{async beforeCreate(e,t){return!t.audience&&t.id?{...t,audience:o(t.id)}:t},async afterCreate(t,n){let{accessControl:r,databaseIsolation:i}=e;r&&t.ctx&&await c(t,n,r),i?.onProvision&&await i.onProvision(n.id)},async beforeDelete(t,n){let{accessControl:r,databaseIsolation:i}=e;if(r)try{let e=(await t.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find(e=>e.name===n);e&&await t.adapters.organizations.remove(r.controlPlaneTenantId,e.id)}catch(e){console.warn(`Failed to remove organization for tenant ${n}:`,e)}if(i?.onDeprovision)try{await i.onDeprovision(n)}catch(e){console.warn(`Failed to deprovision database for tenant ${n}:`,e)}}}}async function c(e,t,n){let{controlPlaneTenantId:r,defaultPermissions:i,defaultRoles:a,issuer:o,adminRoleName:s=`Tenant Admin`,adminRoleDescription:c=`Full access to all tenant management operations`,addCreatorToOrganization:d=!0}=n,f=await e.adapters.organizations.create(r,{name:t.id,display_name:t.friendly_name||t.id}),p;if(o&&(p=await u(e,r,s,c)),d&&e.ctx){let t=e.ctx.var.user;if(t?.sub&&!await l(e,r,t.sub))try{await e.adapters.userOrganizations.create(r,{user_id:t.sub,organization_id:f.id}),p&&await e.adapters.userRoles.create(r,t.sub,p,f.id)}catch(e){console.warn(`Failed to add creator ${t.sub} to organization ${f.id}:`,e)}}a&&a.length>0&&console.log(`Would assign roles ${a.join(`, `)} to organization ${f.id}`),i&&i.length>0&&console.log(`Would grant permissions ${i.join(`, `)} to organization ${f.id}`)}async function l(e,t,n){let r=await e.adapters.userRoles.list(t,n,void 0,``);for(let n of r)if((await e.adapters.rolePermissions.list(t,n.id,{per_page:1e3})).some(e=>e.permission_name===`admin:organizations`))return!0;return!1}async function u(e,n,r,i){let a=(await e.adapters.roles.list(n,{})).roles.find(e=>e.name===r);if(a)return a.id;let o=await e.adapters.roles.create(n,{name:r,description:i}),s=t.MANAGEMENT_API_AUDIENCE,c=t.MANAGEMENT_API_SCOPES.map(e=>({role_id:o.id,resource_server_identifier:s,permission_name:e.value}));return await e.adapters.rolePermissions.assign(n,o.id,c),o.id}function d(e,t,n=()=>!0){let{controlPlaneTenantId:r,getChildTenantIds:i,getAdapters:a}=e,o=new Map;async function s(e,n,r){return(await t(e).list(n,{q:`name:${r}`,per_page:1}))[0]??null}async function c(e){let n=await i(),o=t(await a(r));await Promise.all(n.map(async n=>{try{let r=await a(n),i=t(r),c={...o.transform(e),is_system:!0},l=await s(r,n,e.name),u=l?i.getId(l):void 0;if(l&&u){let e=i.preserveOnUpdate?i.preserveOnUpdate(l,c):c;await i.update(n,u,e)}else await i.create(n,c)}catch(t){console.error(`Failed to sync ${o.listKey} "${e.name}" to tenant "${n}":`,t)}}))}async function l(e){let n=await i();await Promise.all(n.map(async n=>{try{let r=await a(n),i=t(r),o=await s(r,n,e),c=o?i.getId(o):void 0;o&&c&&await i.remove(n,c)}catch(t){console.error(`Failed to delete entity "${e}" from tenant "${n}":`,t)}}))}return{afterCreate:async(e,t)=>{e.tenantId===r&&n(t)&&await c(t)},afterUpdate:async(e,t,i)=>{e.tenantId===r&&n(i)&&await c(i)},beforeDelete:async(e,i)=>{if(e.tenantId!==r)return;let a=await t(e.adapters).get(e.tenantId,i);a&&n(a)&&o.set(i,a)},afterDelete:async(e,t)=>{if(e.tenantId!==r)return;let n=o.get(t);n&&(o.delete(t),await l(n.name))}}}function f(e,n,r=()=>!0){let{controlPlaneTenantId:i,getControlPlaneAdapters:a,getAdapters:o}=e;return{async afterCreate(e,s){if(s.id!==i)try{let e=await a(),c=await o(s.id),l=n(e),u=n(c),d=await(0,t.fetchAll)(e=>l.listPaginated(i,e),l.listKey,{cursorField:`id`,pageSize:100});await Promise.all(d.filter(e=>r(e)).map(async e=>{try{let t=l.transform(e);await u.create(s.id,{...t,is_system:!0})}catch(e){console.error(`Failed to sync entity to new tenant "${s.id}":`,e)}}))}catch(e){console.error(`Failed to sync entities to new tenant "${s.id}":`,e)}}}}var p=e=>({list:async(t,n)=>(await e.resourceServers.list(t,n)).resource_servers,listPaginated:(t,n)=>e.resourceServers.list(t,n),get:(t,n)=>e.resourceServers.get(t,n),create:(t,n)=>e.resourceServers.create(t,n),update:(t,n,r)=>e.resourceServers.update(t,n,r),remove:(t,n)=>e.resourceServers.remove(t,n),listKey:`resource_servers`,getId:e=>e.id,transform:e=>({id:e.id,name:e.name,identifier:e.identifier,scopes:e.scopes,signing_alg:e.signing_alg,token_lifetime:e.token_lifetime,token_lifetime_for_web:e.token_lifetime_for_web})}),m=e=>({list:async(t,n)=>(await e.roles.list(t,n)).roles,listPaginated:(t,n)=>e.roles.list(t,n),get:(t,n)=>e.roles.get(t,n),create:(t,n)=>e.roles.create(t,n),update:(t,n,r)=>e.roles.update(t,n,r),remove:(t,n)=>e.roles.remove(t,n),listKey:`roles`,getId:e=>e.id,transform:e=>({id:e.id,name:e.name,description:e.description})});function h(e){return e.metadata?.sync!==!1}function g(e){let{sync:n={},filters:r={}}=e,i=n.resourceServers??!0,a=n.roles??!0,o=e=>h(e)?r.resourceServers?r.resourceServers(e):!0:!1,s=e=>h(e)?r.roles?r.roles(e):!0:!1,c=i?d(e,p,o):void 0,l=a?d(e,m,s):void 0,u=i?f(e,p,o):void 0,g=a?f(e,m,s):void 0,_=a?{async afterCreate(n,i){if(i.id!==e.controlPlaneTenantId){await g?.afterCreate?.(n,i);try{let n=await e.getControlPlaneAdapters(),a=await e.getAdapters(i.id),o=await(0,t.fetchAll)(t=>n.roles.list(e.controlPlaneTenantId,t),`roles`,{cursorField:`id`,pageSize:100}),s=new Map;for(let e of o.filter(e=>r.roles?.(e)??!0)){let t=await v(a,i.id,e.name);t&&s.set(e.name,t.id)}for(let t of o.filter(e=>r.roles?.(e)??!0)){let r=s.get(t.name);if(r)try{let o=await n.rolePermissions.list(e.controlPlaneTenantId,t.id,{});o.length>0&&await a.rolePermissions.assign(i.id,r,o.map(e=>({role_id:r,resource_server_identifier:e.resource_server_identifier,permission_name:e.permission_name})))}catch(e){console.error(`Failed to sync permissions for role "${t.name}" to tenant "${i.id}":`,e)}}}catch(e){console.error(`Failed to sync role permissions to tenant "${i.id}":`,e)}}}}:void 0;async function v(e,t,n){return(await e.roles.list(t,{q:`name:${n}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:c,roles:l},tenantHooks:{async afterCreate(e,t){let n=[u?.afterCreate,_?.afterCreate??g?.afterCreate],r=[];for(let i of n)if(i)try{await i(e,t)}catch(e){r.push(e instanceof Error?e:Error(String(e)))}if(r.length===1)throw r[0];if(r.length>1)throw AggregateError(r,r.map(e=>e.message).join(`; `))}}}}var _=class extends Error{res;status;constructor(e=500,t){super(t?.message,{cause:t?.cause}),this.res=t?.res,this.status=e}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function v(e,r){let i=new n.OpenAPIHono;return i.openapi((0,n.createRoute)({tags:[`tenants`],method:`get`,path:`/`,request:{query:t.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:n.z.object({tenants:n.z.array(t.tenantSchema),start:n.z.number().optional(),limit:n.z.number().optional(),length:n.z.number().optional()})}},description:`List of tenants`}}}),async n=>{let{page:r,per_page:i,include_totals:a,q:o}=n.req.valid(`query`),s=n.var.user,c=s?.permissions||[];if(!(s?.org_id??n.var.organization_id)&&c.includes(`admin:organizations`)){let e=await n.env.data.tenants.list({page:r,per_page:i,include_totals:a,q:o});return a?n.json({tenants:e.tenants,start:e.totals?.start??0,limit:e.totals?.limit??i,length:e.tenants.length}):n.json({tenants:e.tenants})}let l=e.accessControl?.controlPlaneTenantId??n.env.data.multiTenancyConfig?.controlPlaneTenantId;if(l&&!s?.sub)throw new _(403,{message:`Access denied: token has no subject`});if(l&&s?.sub){let e=(await(0,t.fetchAll)(e=>n.env.data.userOrganizations.listUserOrganizations(l,s.sub,e),`organizations`)).map(e=>e.name);if(e.length===0)return a?n.json({tenants:[],start:0,limit:i??50,length:0}):n.json({tenants:[]});let c=e.length,u=r??0,d=i??50,f=u*d,p=e.slice(f,f+d);if(p.length===0)return a?n.json({tenants:[],start:f,limit:d,length:c}):n.json({tenants:[]});let m=p.map(e=>`id:${e}`).join(` OR `),h=o?`(${m}) AND (${o})`:m,g=await n.env.data.tenants.list({q:h,per_page:d,include_totals:!1});return a?n.json({tenants:g.tenants,start:f,limit:d,length:c}):n.json({tenants:g.tenants})}let u=await n.env.data.tenants.list({page:r,per_page:i,include_totals:a,q:o});return a?n.json({tenants:u.tenants,start:u.totals?.start??0,limit:u.totals?.limit??i,length:u.tenants.length}):n.json({tenants:u.tenants})}),i.openapi((0,n.createRoute)({tags:[`tenants`],method:`post`,path:`/`,request:{body:{content:{"application/json":{schema:t.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:t.tenantSchema}},description:`Tenant created`},400:{description:`Validation error`},409:{description:`Tenant with this ID already exists`}}}),async e=>{if(!e.var.user?.sub)throw new _(401,{message:`Authentication required to create tenants`});let t=e.req.valid(`json`),n={adapters:e.env.data,ctx:e};r.tenants?.beforeCreate&&(t=await r.tenants.beforeCreate(n,t));let i=await e.env.data.tenants.create(t);return r.tenants?.afterCreate&&await r.tenants.afterCreate(n,i),e.json(i,201)}),i.openapi((0,n.createRoute)({tags:[`tenants`],method:`delete`,path:`/{id}`,request:{params:n.z.object({id:n.z.string()})},security:[{Bearer:[`delete:tenants`]}],responses:{204:{description:`Tenant deleted`},403:{description:`Access denied or cannot delete the control plane`},404:{description:`Tenant not found`}}}),async n=>{let{id:i}=n.req.valid(`param`),a=e.accessControl?.controlPlaneTenantId??n.env.data.multiTenancyConfig?.controlPlaneTenantId;if(a){let e=n.var.user;if(!e?.sub)throw new _(401,{message:`Authentication required`});if(i===a)throw new _(403,{message:`Cannot delete the control plane`});let r=n.var.org_name,o=i.toLowerCase(),s=!!r&&r.toLowerCase()===o;if(s||=(await(0,t.fetchAll)(t=>n.env.data.userOrganizations.listUserOrganizations(a,e.sub,t),`organizations`)).some(e=>e.name?.toLowerCase()===o),!s)throw new _(403,{message:`Access denied to this tenant`})}if(!await n.env.data.tenants.get(i))throw new _(404,{message:`Tenant not found`});let o={adapters:n.env.data,ctx:n};return r.tenants?.beforeDelete&&await r.tenants.beforeDelete(o,i),await n.env.data.tenants.remove(i),r.tenants?.afterDelete&&await r.tenants.afterDelete(o,i),n.body(null,204)}),i.openapi((0,n.createRoute)({tags:[`tenants`,`settings`],method:`get`,path:`/settings`,request:{headers:n.z.object({"tenant-id":n.z.string().optional()})},security:[{Bearer:[`read:tenants`]}],responses:{200:{content:{"application/json":{schema:t.tenantSchema}},description:`Current tenant settings`}}}),async e=>{let t=await e.env.data.tenants.get(e.var.tenant_id);if(!t)throw new _(404,{message:`Tenant not found`});return e.json(t)}),i.openapi((0,n.createRoute)({tags:[`tenants`,`settings`],method:`patch`,path:`/settings`,request:{headers:n.z.object({"tenant-id":n.z.string().optional()}),body:{content:{"application/json":{schema:n.z.object(t.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:[`update:tenants`]}],responses:{200:{content:{"application/json":{schema:t.tenantSchema}},description:`Updated tenant settings`}}}),async e=>{let{id:n,...r}=e.req.valid(`json`),i=await e.env.data.tenants.get(e.var.tenant_id);if(!i)throw new _(404,{message:`Tenant not found`});let a=(0,t.deepMergePatch)(i,r);await e.env.data.tenants.update(e.var.tenant_id,a);let o=await e.env.data.tenants.get(e.var.tenant_id);if(!o)throw new _(500,{message:`Failed to retrieve updated tenant`});return e.json(o)}),i}function y(e){for(let{pattern:t,type:n}of[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:`resource_server`},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:`role`},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:`connection`}]){let r=e.match(t);if(r&&r[1])return{type:n,id:r[1]}}return null}async function b(e,t,n){try{switch(n.type){case`resource_server`:return(await e.resourceServers.get(t,n.id))?.is_system===!0;case`role`:return(await e.roles.get(t,n.id))?.is_system===!0;case`connection`:return(await e.connections.get(t,n.id))?.is_system===!0;default:return!1}}catch{return!1}}function x(e){return{resource_server:`resource server`,role:`role`,connection:`connection`}[e]}function S(){return async(e,t)=>{if(![`PATCH`,`PUT`,`DELETE`].includes(e.req.method))return t();let n=y(e.req.path);if(!n)return t();let r=e.var.tenant_id||e.req.header(`x-tenant-id`)||e.req.header(`tenant-id`);if(!r)return t();if(await b(e.env.data,r,n))throw new _(403,{message:`This ${x(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return t()}}function C(e,n){let r=n.find(t=>t.strategy===e.strategy);if(!r?.options)return e;let i=t.connectionSchema.passthrough().parse({...r,...e});return i.options=t.connectionOptionsSchema.passthrough().parse({...r.options||{},...e.options}),i}function w(e,t){let n=[...t||[],...e||[]];return[...new Set(n)]}function T(e,t){if(!t?.length)return e||[];if(!e?.length)return t;let n=new Map;for(let e of t)n.set(e.value,e);for(let t of e)n.set(t.value,t);return Array.from(n.values())}function E(e,t){return t?{...e,scopes:T(e.scopes,t.scopes)}:e}function D(e,t){return t?{...e,callbacks:w(e.callbacks,t.callbacks),web_origins:w(e.web_origins,t.web_origins),allowed_logout_urls:w(e.allowed_logout_urls,t.allowed_logout_urls),allowed_origins:w(e.allowed_origins,t.allowed_origins)}:e}function O(e){let{controlPlaneTenantId:t,controlPlaneClientId:n,resolveControlPlane:r}=e;if(r)return async e=>r({tenant_id:e});if(!t)return async()=>void 0;let i={tenantId:t,clientId:n};return async()=>i}function k(e,t){return{...e.resourceServers,get:async(n,r)=>{let i=await e.resourceServers.get(n,r);if(!i)return i;let a=await t(n);return!a||n===a.tenantId||!i.is_system?i:E(i,await e.resourceServers.get(a.tenantId,r))},list:async(n,r)=>{let i=await e.resourceServers.list(n,r),a=await t(n);if(!a||n===a.tenantId)return i;let o=a.tenantId,s=i.resource_servers.filter(e=>!!(e.is_system&&e.id)).map(e=>e.id);if(s.length===0)return i;let c=new Map;await Promise.all(s.map(async t=>{let n=await e.resourceServers.get(o,t);n&&c.set(t,n)}));let l=i.resource_servers.map(e=>e.is_system&&e.id?E(e,c.get(e.id)??null):e);return{...i,resource_servers:l}}}}function A(e,t){let n=O({controlPlaneTenantId:t.controlPlaneTenantId,resolveControlPlane:t.resolveControlPlane});return{...e,resourceServers:k(e,n)}}function j(e,t){let{controlPlaneTenantId:n,controlPlaneClientId:r,resolveControlPlane:i}=t,a=O({controlPlaneTenantId:n,controlPlaneClientId:r,resolveControlPlane:i});return{...e,multiTenancyConfig:{controlPlaneTenantId:n,controlPlaneClientId:r,resolveControlPlane:i},connections:{...e.connections,get:async(t,n)=>{let r=await e.connections.get(t,n);if(!r)return r;let i=await a(t);return!i||t===i.tenantId?r:C(r,(await e.connections.list(i.tenantId)).connections||[])},list:async(t,n)=>{let r=await e.connections.list(t,n),i=await a(t);if(!i||t===i.tenantId)return r;let o=await e.connections.list(i.tenantId),s=r.connections.map(e=>C(e,o.connections||[]));return{...r,connections:s}}},clientConnections:{...e.clientConnections,listByClient:async(t,n)=>{let r=await e.clientConnections.listByClient(t,n);r.length===0&&(r=(await e.connections.list(t)).connections||[]);let i=await a(t);if(!i||t===i.tenantId)return r;let o=await e.connections.list(i.tenantId);return r.map(e=>C(e,o.connections||[]))}},emailProviders:{...e.emailProviders,get:async t=>{let n=await e.emailProviders.get(t);if(n)return n;let r=await a(t);return!r||t===r.tenantId?null:e.emailProviders.get(r.tenantId)}},resourceServers:k(e,a),hooks:N(e,a)}}function M(e){if(!e||typeof e!=`object`)return!1;let t=e.metadata;return!t||typeof t!=`object`?!1:t.inheritable===!0}function N(e,t){return{...e.hooks,list:async(n,r)=>{let i=await e.hooks.list(n,r),a=await t(n);if(!a||n===a.tenantId)return i;let o=((await e.hooks.list(a.tenantId,r)).hooks||[]).filter(M);if(o.length===0)return i;let s=new Set((i.hooks||[]).map(e=>e.hook_id)),c=o.filter(e=>!s.has(e.hook_id));return{...i,hooks:[...i.hooks||[],...c],length:typeof i.length==`number`?i.length+c.length:i.length}},get:async(n,r)=>{let i=await e.hooks.get(n,r);if(i)return i;let a=await t(n);if(!a||n===a.tenantId)return i;let o=await e.hooks.get(a.tenantId,r);return o&&M(o)?o:null}}}function P(e,t){return j(e,t)}function F(e){return async(t,n)=>{let r=t.var.user;return r?.tenant_id===e&&r.org_name&&t.set(`tenant_id`,r.org_name),n()}}function I(e){return async(n,r)=>{if(!e.accessControl)return r();let{controlPlaneTenantId:a}=e.accessControl,o=n.var.org_name,s=n.var.organization_id,c=o||s,l=n.var.tenant_id,u=n.var.user,d=(u?.aud?Array.isArray(u.aud)?u.aud:[u.aud]:[]).includes(t.MANAGEMENT_API_AUDIENCE);if(!l&&c&&d&&(n.set(`tenant_id`,c),l=c),!l)throw new _(400,{message:`Tenant ID not found in request`});if(!i(s,l,a,o))throw new _(403,{message:`Access denied to tenant ${l}`});return r()}}function L(e){return async(t,n)=>{if(!e.subdomainRouting)return n();let{baseDomain:r,reservedSubdomains:i=[],resolveSubdomain:a}=e.subdomainRouting,o=t.req.header(`x-forwarded-host`)||t.req.header(`host`)||``,s=null;if(o.endsWith(r)){let e=o.slice(0,-(r.length+1));e&&!e.includes(`.`)&&(s=e)}if(s&&i.includes(s)&&(s=null),!s)return e.accessControl&&t.set(`tenant_id`,e.accessControl.controlPlaneTenantId),n();let c=null;if(a)c=await a(s);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{let n=await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId,s);n&&(c=n.id)}catch{}if(!c)throw new _(404,{message:`Tenant not found for subdomain: ${s}`});return t.set(`tenant_id`,c),n()}}function R(e){return async(t,n)=>{if(!e.databaseIsolation)return n();let r=t.var.tenant_id;if(!r)throw new _(400,{message:`Tenant ID not found in request`});try{let n=await e.databaseIsolation.getAdapters(r);t.env.data=n}catch(e){throw console.error(`Failed to resolve database for tenant ${r}:`,e),new _(500,{message:`Failed to resolve tenant database`})}return n()}}function z(e){let t=L(e),n=I(e),r=R(e);return async(e,i)=>(await t(e,async()=>{}),await n(e,async()=>{}),await r(e,async()=>{}),i())}function B(e){let{dataAdapter:n,controlPlane:r,controlPlane:{tenantId:i=`control_plane`,clientId:a}={},resolveControlPlane:o,sync:c={resourceServers:!0,roles:!0},defaultPermissions:l=[`tenant:admin`],requireOrganizationMatch:u=!1,managementApiExtensions:d=[],entityHooks:f,getChildTenantIds:p,getAdapters:m,...h}=e;if(o&&!r)throw Error("initMultiTenant: `resolveControlPlane` requires `controlPlane` to be set. The static `controlPlane.tenantId` is used for access control, sync direction, and tenant management routing; the resolver only overrides per-tenant runtime inheritance lookups on top of it.");let _=n,y=n;r&&(_=P(n,{controlPlaneTenantId:i,controlPlaneClientId:a,resolveControlPlane:o}),y={...A(n,{controlPlaneTenantId:i,resolveControlPlane:o}),multiTenancyConfig:{controlPlaneTenantId:i,controlPlaneClientId:a,resolveControlPlane:o}});let b=c!==!1,x=b?{resourceServers:c.resourceServers??!0,roles:c.roles??!0}:{resourceServers:!1,roles:!1},{entityHooks:C,tenantHooks:w}=g({controlPlaneTenantId:i,getChildTenantIds:p??(async()=>(await(0,t.fetchAll)(e=>_.tenants.list(e),`tenants`,{cursorField:`id`,pageSize:100})).filter(e=>e.id!==i).map(e=>e.id)),getAdapters:m??(async()=>_),getControlPlaneAdapters:async()=>_,sync:x}),T={resourceServers:[C.resourceServers,...f?.resourceServers??[]],roles:[C.roles,...f?.roles??[]],connections:f?.connections??[],tenants:f?.tenants??[],rolePermissions:f?.rolePermissions??[]},E=s({accessControl:{controlPlaneTenantId:i,requireOrganizationMatch:u,defaultPermissions:l}}),D=v({accessControl:{controlPlaneTenantId:i,requireOrganizationMatch:u,defaultPermissions:l}},{tenants:{async beforeCreate(e,t){return E.beforeCreate&&(t=await E.beforeCreate(e,t)),w.beforeCreate&&(t=await w.beforeCreate(e,t)),t},async afterCreate(e,t){await E.afterCreate?.(e,t),await w.afterCreate?.(e,t)},async beforeDelete(e,t){await E.beforeDelete?.(e,t),await w.beforeDelete?.(e,t)}}}),{app:O}=(0,t.init)({dataAdapter:_,managementDataAdapter:y,...h,entityHooks:T,managementApiExtensions:[...d,{path:`/tenants`,router:D}]});return O.use(`/api/v2/*`,F(i)),b&&O.use(`/api/v2/*`,S()),{app:O,controlPlaneTenantId:i}}function V(e){let t=H(e);return{name:`multi-tenancy`,middleware:z(e),hooks:t,routes:[{path:`/management`,handler:v(e,t)}],onRegister:async()=>{console.log(`Multi-tenancy plugin registered`),e.accessControl&&console.log(` - Access control enabled (control plane: ${e.accessControl.controlPlaneTenantId})`),e.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`),e.databaseIsolation&&console.log(` - Database isolation enabled`)}}}function H(e){let t=e.accessControl?r(e.accessControl):{},n=e.databaseIsolation?a(e.databaseIsolation):{},i=s(e);return{...t,...n,tenants:i}}function U(t){let n=new e.Hono,r=H(t);return n.route(`/tenants`,v(t,r)),n}function W(e){return{hooks:H(e),middleware:z(e),app:U(e),config:e,wrapAdapters:(t,n)=>P(t,{controlPlaneTenantId:e.accessControl?.controlPlaneTenantId,controlPlaneClientId:n?.controlPlaneClientId})}}exports.createAccessControlHooks=r,exports.createAccessControlMiddleware=I,exports.createControlPlaneTenantMiddleware=F,exports.createDatabaseHooks=a,exports.createDatabaseMiddleware=R,exports.createMultiTenancy=U,exports.createMultiTenancyHooks=H,exports.createMultiTenancyMiddleware=z,exports.createMultiTenancyPlugin=V,exports.createProtectSyncedMiddleware=S,exports.createProvisioningHooks=s,exports.createRuntimeFallbackAdapter=j,exports.createSubdomainMiddleware=L,exports.createSyncHooks=g,exports.createTenantsOpenAPIRouter=v,exports.initMultiTenant=B,exports.mergeClientWithFallback=D,exports.setupMultiTenancy=W,exports.validateTenantAccess=i,exports.withRuntimeFallback=P,exports.withSystemResourceServerInheritance=A;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("hono"),t=require("authhero"),n=require("@hono/zod-openapi"),r=require("hono/http-exception");function i(e){let{controlPlaneTenantId:t,requireOrganizationMatch:n=!0}=e;return{async onTenantAccessValidation(e,r){if(r===t)return!0;if(n){let t=e.var.org_name,n=e.var.organization_id,i=t||n;return i?i.toLowerCase()===r.toLowerCase():!1}return!0}}}function a(e,t,n,r){if(t===n)return!0;let i=r||e;return i?i.toLowerCase()===t.toLowerCase():!1}function o(e){return{async resolveDataAdapters(t){try{return await e.getAdapters(t)}catch(e){console.error(`Failed to resolve data adapters for tenant ${t}:`,e);return}}}}function s(e){return`urn:authhero:tenant:${e.toLowerCase()}`}function c(e){return{async beforeCreate(e,t){return!t.audience&&t.id?{...t,audience:s(t.id)}:t},async afterCreate(t,n){let{accessControl:r,databaseIsolation:i}=e;r&&t.ctx&&await l(t,n,r),i?.onProvision&&await i.onProvision(n.id)},async beforeDelete(t,n){let{accessControl:r,databaseIsolation:i}=e;if(r)try{let e=(await t.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find(e=>e.name===n);e&&await t.adapters.organizations.remove(r.controlPlaneTenantId,e.id)}catch(e){console.warn(`Failed to remove organization for tenant ${n}:`,e)}if(i?.onDeprovision)try{await i.onDeprovision(n)}catch(e){console.warn(`Failed to deprovision database for tenant ${n}:`,e)}}}}async function l(e,t,n){let{controlPlaneTenantId:r,defaultPermissions:i,defaultRoles:a,issuer:o,adminRoleName:s=`Tenant Admin`,adminRoleDescription:c=`Full access to all tenant management operations`,addCreatorToOrganization:l=!0}=n,f=await e.adapters.organizations.create(r,{name:t.id,display_name:t.friendly_name||t.id}),p;if(o&&(p=await d(e,r,s,c)),l&&e.ctx){let t=e.ctx.var.user;if(t?.sub&&!await u(e,r,t.sub))try{await e.adapters.userOrganizations.create(r,{user_id:t.sub,organization_id:f.id}),p&&await e.adapters.userRoles.create(r,t.sub,p,f.id)}catch(e){console.warn(`Failed to add creator ${t.sub} to organization ${f.id}:`,e)}}a&&a.length>0&&console.log(`Would assign roles ${a.join(`, `)} to organization ${f.id}`),i&&i.length>0&&console.log(`Would grant permissions ${i.join(`, `)} to organization ${f.id}`)}async function u(e,t,n){let r=await e.adapters.userRoles.list(t,n,void 0,``);for(let n of r)if((await e.adapters.rolePermissions.list(t,n.id,{per_page:1e3})).some(e=>e.permission_name===`admin:organizations`))return!0;return!1}async function d(e,n,r,i){let a=(await e.adapters.roles.list(n,{})).roles.find(e=>e.name===r);if(a)return a.id;let o=await e.adapters.roles.create(n,{name:r,description:i}),s=t.MANAGEMENT_API_AUDIENCE,c=t.MANAGEMENT_API_SCOPES.map(e=>({role_id:o.id,resource_server_identifier:s,permission_name:e.value}));return await e.adapters.rolePermissions.assign(n,o.id,c),o.id}function f(e,t,n=()=>!0){let{controlPlaneTenantId:r,getChildTenantIds:i,getAdapters:a}=e,o=new Map;async function s(e,n,r){return(await t(e).list(n,{q:`name:${r}`,per_page:1}))[0]??null}async function c(e){let n=await i(),o=t(await a(r));await Promise.all(n.map(async n=>{try{let r=await a(n),i=t(r),c={...o.transform(e),is_system:!0},l=await s(r,n,e.name),u=l?i.getId(l):void 0;if(l&&u){let e=i.preserveOnUpdate?i.preserveOnUpdate(l,c):c;await i.update(n,u,e)}else await i.create(n,c)}catch(t){console.error(`Failed to sync ${o.listKey} "${e.name}" to tenant "${n}":`,t)}}))}async function l(e){let n=await i();await Promise.all(n.map(async n=>{try{let r=await a(n),i=t(r),o=await s(r,n,e),c=o?i.getId(o):void 0;o&&c&&await i.remove(n,c)}catch(t){console.error(`Failed to delete entity "${e}" from tenant "${n}":`,t)}}))}return{afterCreate:async(e,t)=>{e.tenantId===r&&n(t)&&await c(t)},afterUpdate:async(e,t,i)=>{e.tenantId===r&&n(i)&&await c(i)},beforeDelete:async(e,i)=>{if(e.tenantId!==r)return;let a=await t(e.adapters).get(e.tenantId,i);a&&n(a)&&o.set(i,a)},afterDelete:async(e,t)=>{if(e.tenantId!==r)return;let n=o.get(t);n&&(o.delete(t),await l(n.name))}}}function p(e,n,r=()=>!0){let{controlPlaneTenantId:i,getControlPlaneAdapters:a,getAdapters:o}=e;return{async afterCreate(e,s){if(s.id!==i)try{let e=await a(),c=await o(s.id),l=n(e),u=n(c),d=await(0,t.fetchAll)(e=>l.listPaginated(i,e),l.listKey,{cursorField:`id`,pageSize:100});await Promise.all(d.filter(e=>r(e)).map(async e=>{try{let t=l.transform(e);await u.create(s.id,{...t,is_system:!0})}catch(e){console.error(`Failed to sync entity to new tenant "${s.id}":`,e)}}))}catch(e){console.error(`Failed to sync entities to new tenant "${s.id}":`,e)}}}}var m=e=>({list:async(t,n)=>(await e.resourceServers.list(t,n)).resource_servers,listPaginated:(t,n)=>e.resourceServers.list(t,n),get:(t,n)=>e.resourceServers.get(t,n),create:(t,n)=>e.resourceServers.create(t,n),update:(t,n,r)=>e.resourceServers.update(t,n,r),remove:(t,n)=>e.resourceServers.remove(t,n),listKey:`resource_servers`,getId:e=>e.id,transform:e=>({id:e.id,name:e.name,identifier:e.identifier,scopes:e.scopes,signing_alg:e.signing_alg,token_lifetime:e.token_lifetime,token_lifetime_for_web:e.token_lifetime_for_web})}),h=e=>({list:async(t,n)=>(await e.roles.list(t,n)).roles,listPaginated:(t,n)=>e.roles.list(t,n),get:(t,n)=>e.roles.get(t,n),create:(t,n)=>e.roles.create(t,n),update:(t,n,r)=>e.roles.update(t,n,r),remove:(t,n)=>e.roles.remove(t,n),listKey:`roles`,getId:e=>e.id,transform:e=>({id:e.id,name:e.name,description:e.description})});function g(e){return e.metadata?.sync!==!1}function _(e){let{sync:n={},filters:r={}}=e,i=n.resourceServers??!0,a=n.roles??!0,o=e=>g(e)?r.resourceServers?r.resourceServers(e):!0:!1,s=e=>g(e)?r.roles?r.roles(e):!0:!1,c=i?f(e,m,o):void 0,l=a?f(e,h,s):void 0,u=i?p(e,m,o):void 0,d=a?p(e,h,s):void 0,_=a?{async afterCreate(n,i){if(i.id!==e.controlPlaneTenantId){await d?.afterCreate?.(n,i);try{let n=await e.getControlPlaneAdapters(),a=await e.getAdapters(i.id),o=await(0,t.fetchAll)(t=>n.roles.list(e.controlPlaneTenantId,t),`roles`,{cursorField:`id`,pageSize:100}),s=new Map;for(let e of o.filter(e=>r.roles?.(e)??!0)){let t=await v(a,i.id,e.name);t&&s.set(e.name,t.id)}for(let t of o.filter(e=>r.roles?.(e)??!0)){let r=s.get(t.name);if(r)try{let o=await n.rolePermissions.list(e.controlPlaneTenantId,t.id,{});o.length>0&&await a.rolePermissions.assign(i.id,r,o.map(e=>({role_id:r,resource_server_identifier:e.resource_server_identifier,permission_name:e.permission_name})))}catch(e){console.error(`Failed to sync permissions for role "${t.name}" to tenant "${i.id}":`,e)}}}catch(e){console.error(`Failed to sync role permissions to tenant "${i.id}":`,e)}}}}:void 0;async function v(e,t,n){return(await e.roles.list(t,{q:`name:${n}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:c,roles:l},tenantHooks:{async afterCreate(e,t){let n=[u?.afterCreate,_?.afterCreate??d?.afterCreate],r=[];for(let i of n)if(i)try{await i(e,t)}catch(e){r.push(e instanceof Error?e:Error(String(e)))}if(r.length===1)throw r[0];if(r.length>1)throw AggregateError(r,r.map(e=>e.message).join(`; `))}}}}var v=n.z.object({sub:n.z.string(),tenant_id:n.z.string().optional(),org_id:n.z.string().optional(),scope:n.z.string().optional(),permissions:n.z.array(n.z.string()).optional()}).passthrough();function y(e){let t=v.safeParse(e);return t.success?t.data:void 0}function b(e){let t=e.permissions??[],n=e.scope?e.scope.split(` `).filter(Boolean):[],r=new Set([...t,...n]);return r.has(`delete:tenants`)||r.has(`admin:organizations`)}function x(e,i){let a=new n.OpenAPIHono;return a.openapi((0,n.createRoute)({tags:[`tenants`],method:`get`,path:`/`,request:{query:t.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:n.z.object({tenants:n.z.array(t.tenantSchema),start:n.z.number().optional(),limit:n.z.number().optional(),length:n.z.number().optional()})}},description:`List of tenants`}}}),async n=>{let{page:i,per_page:a,include_totals:o,q:s}=n.req.valid(`query`),c=n.var.user,l=c?.permissions||[];if(!(c?.org_id??n.var.organization_id)&&l.includes(`admin:organizations`)){let e=await n.env.data.tenants.list({page:i,per_page:a,include_totals:o,q:s});return o?n.json({tenants:e.tenants,start:e.totals?.start??0,limit:e.totals?.limit??a,length:e.tenants.length}):n.json({tenants:e.tenants})}let u=e.accessControl?.controlPlaneTenantId??n.env.data.multiTenancyConfig?.controlPlaneTenantId;if(u&&!c?.sub)throw new r.HTTPException(403,{message:`Access denied: token has no subject`});if(u&&c?.sub){let e=(await(0,t.fetchAll)(e=>n.env.data.userOrganizations.listUserOrganizations(u,c.sub,e),`organizations`)).map(e=>e.name);if(e.length===0)return o?n.json({tenants:[],start:0,limit:a??50,length:0}):n.json({tenants:[]});let r=e.length,l=i??0,d=a??50,f=l*d,p=e.slice(f,f+d);if(p.length===0)return o?n.json({tenants:[],start:f,limit:d,length:r}):n.json({tenants:[]});let m=p.map(e=>`id:${e}`).join(` OR `),h=s?`(${m}) AND (${s})`:m,g=await n.env.data.tenants.list({q:h,per_page:d,include_totals:!1});return o?n.json({tenants:g.tenants,start:f,limit:d,length:r}):n.json({tenants:g.tenants})}let d=await n.env.data.tenants.list({page:i,per_page:a,include_totals:o,q:s});return o?n.json({tenants:d.tenants,start:d.totals?.start??0,limit:d.totals?.limit??a,length:d.tenants.length}):n.json({tenants:d.tenants})}),a.openapi((0,n.createRoute)({tags:[`tenants`],method:`post`,path:`/`,request:{body:{content:{"application/json":{schema:t.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:t.tenantSchema}},description:`Tenant created`},400:{description:`Validation error`},409:{description:`Tenant with this ID already exists`}}}),async e=>{if(!e.var.user?.sub)throw new r.HTTPException(401,{message:`Authentication required to create tenants`});let t=e.req.valid(`json`),n={adapters:e.env.data,ctx:e};i.tenants?.beforeCreate&&(t=await i.tenants.beforeCreate(n,t));let a=await e.env.data.tenants.create(t);return i.tenants?.afterCreate&&await i.tenants.afterCreate(n,a),e.json(a,201)}),a.openapi((0,n.createRoute)({tags:[`tenants`],method:`delete`,path:`/{id}`,request:{params:n.z.object({id:n.z.string()})},security:[{Bearer:[`delete:tenants`]}],responses:{204:{description:`Tenant deleted`},403:{description:`Access denied or cannot delete the control plane`},404:{description:`Tenant not found`}}}),async n=>{let{id:a}=n.req.valid(`param`),o=e.accessControl?.controlPlaneTenantId??n.env.data.multiTenancyConfig?.controlPlaneTenantId;if(o){let e=y(n.var.user);if(!e?.sub)throw new r.HTTPException(401,{message:`Authentication required`});if(a===o)throw new r.HTTPException(403,{message:`Cannot delete the control plane`});let i=n.var.org_name,s=a.toLowerCase(),c=!!i&&i.toLowerCase()===s;if(!c){let t=!!(e.org_id??n.var.organization_id??i),r=!e.tenant_id||e.tenant_id===o;!t&&r&&b(e)&&(c=!0)}if(c||=(await(0,t.fetchAll)(t=>n.env.data.userOrganizations.listUserOrganizations(o,e.sub,t),`organizations`)).some(e=>e.name?.toLowerCase()===s),!c)throw new r.HTTPException(403,{message:`Access denied to this tenant`})}if(!await n.env.data.tenants.get(a))throw new r.HTTPException(404,{message:`Tenant not found`});let s={adapters:n.env.data,ctx:n};return i.tenants?.beforeDelete&&await i.tenants.beforeDelete(s,a),await n.env.data.tenants.remove(a),i.tenants?.afterDelete&&await i.tenants.afterDelete(s,a),n.body(null,204)}),a.openapi((0,n.createRoute)({tags:[`tenants`,`settings`],method:`get`,path:`/settings`,request:{headers:n.z.object({"tenant-id":n.z.string().optional()})},security:[{Bearer:[`read:tenants`]}],responses:{200:{content:{"application/json":{schema:t.tenantSchema}},description:`Current tenant settings`}}}),async e=>{let t=await e.env.data.tenants.get(e.var.tenant_id);if(!t)throw new r.HTTPException(404,{message:`Tenant not found`});return e.json(t)}),a.openapi((0,n.createRoute)({tags:[`tenants`,`settings`],method:`patch`,path:`/settings`,request:{headers:n.z.object({"tenant-id":n.z.string().optional()}),body:{content:{"application/json":{schema:n.z.object(t.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:[`update:tenants`]}],responses:{200:{content:{"application/json":{schema:t.tenantSchema}},description:`Updated tenant settings`}}}),async e=>{let{id:n,...i}=e.req.valid(`json`),a=await e.env.data.tenants.get(e.var.tenant_id);if(!a)throw new r.HTTPException(404,{message:`Tenant not found`});let o=(0,t.deepMergePatch)(a,i);await e.env.data.tenants.update(e.var.tenant_id,o);let s=await e.env.data.tenants.get(e.var.tenant_id);if(!s)throw new r.HTTPException(500,{message:`Failed to retrieve updated tenant`});return e.json(s)}),a}function S(e){for(let{pattern:t,type:n}of[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:`resource_server`},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:`role`},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:`connection`}]){let r=e.match(t);if(r&&r[1])return{type:n,id:r[1]}}return null}async function C(e,t,n){try{switch(n.type){case`resource_server`:return(await e.resourceServers.get(t,n.id))?.is_system===!0;case`role`:return(await e.roles.get(t,n.id))?.is_system===!0;case`connection`:return(await e.connections.get(t,n.id))?.is_system===!0;default:return!1}}catch{return!1}}function w(e){return{resource_server:`resource server`,role:`role`,connection:`connection`}[e]}function T(){return async(e,t)=>{if(![`PATCH`,`PUT`,`DELETE`].includes(e.req.method))return t();let n=S(e.req.path);if(!n)return t();let i=e.var.tenant_id||e.req.header(`x-tenant-id`)||e.req.header(`tenant-id`);if(!i)return t();if(await C(e.env.data,i,n))throw new r.HTTPException(403,{message:`This ${w(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return t()}}function E(e,n){let r=n.find(t=>t.strategy===e.strategy);if(!r?.options)return e;let i=t.connectionSchema.passthrough().parse({...r,...e});return i.options=t.connectionOptionsSchema.passthrough().parse({...r.options||{},...e.options}),i}function D(e,t){let n=[...t||[],...e||[]];return[...new Set(n)]}function O(e,t){if(!t?.length)return e||[];if(!e?.length)return t;let n=new Map;for(let e of t)n.set(e.value,e);for(let t of e)n.set(t.value,t);return Array.from(n.values())}function k(e,t){return t?{...e,scopes:O(e.scopes,t.scopes)}:e}function A(e,t){return t?{...e,callbacks:D(e.callbacks,t.callbacks),web_origins:D(e.web_origins,t.web_origins),allowed_logout_urls:D(e.allowed_logout_urls,t.allowed_logout_urls),allowed_origins:D(e.allowed_origins,t.allowed_origins)}:e}function j(e){let{controlPlaneTenantId:t,controlPlaneClientId:n,resolveControlPlane:r}=e;if(r)return async e=>r({tenant_id:e});if(!t)return async()=>void 0;let i={tenantId:t,clientId:n};return async()=>i}function M(e,t){return{...e.resourceServers,get:async(n,r)=>{let i=await e.resourceServers.get(n,r);if(!i)return i;let a=await t(n);return!a||n===a.tenantId||!i.is_system?i:k(i,await e.resourceServers.get(a.tenantId,r))},list:async(n,r)=>{let i=await e.resourceServers.list(n,r),a=await t(n);if(!a||n===a.tenantId)return i;let o=a.tenantId,s=i.resource_servers.filter(e=>!!(e.is_system&&e.id)).map(e=>e.id);if(s.length===0)return i;let c=new Map;await Promise.all(s.map(async t=>{let n=await e.resourceServers.get(o,t);n&&c.set(t,n)}));let l=i.resource_servers.map(e=>e.is_system&&e.id?k(e,c.get(e.id)??null):e);return{...i,resource_servers:l}}}}function N(e,t){let n=j({controlPlaneTenantId:t.controlPlaneTenantId,resolveControlPlane:t.resolveControlPlane});return{...e,resourceServers:M(e,n)}}function P(e,t){let{controlPlaneTenantId:n,controlPlaneClientId:r,resolveControlPlane:i}=t,a=j({controlPlaneTenantId:n,controlPlaneClientId:r,resolveControlPlane:i});return{...e,multiTenancyConfig:{controlPlaneTenantId:n,controlPlaneClientId:r,resolveControlPlane:i},connections:{...e.connections,get:async(t,n)=>{let r=await e.connections.get(t,n);if(!r)return r;let i=await a(t);return!i||t===i.tenantId?r:E(r,(await e.connections.list(i.tenantId)).connections||[])},list:async(t,n)=>{let r=await e.connections.list(t,n),i=await a(t);if(!i||t===i.tenantId)return r;let o=await e.connections.list(i.tenantId),s=r.connections.map(e=>E(e,o.connections||[]));return{...r,connections:s}}},clientConnections:{...e.clientConnections,listByClient:async(t,n)=>{let r=await e.clientConnections.listByClient(t,n);r.length===0&&(r=(await e.connections.list(t)).connections||[]);let i=await a(t);if(!i||t===i.tenantId)return r;let o=await e.connections.list(i.tenantId);return r.map(e=>E(e,o.connections||[]))}},emailProviders:{...e.emailProviders,get:async t=>{let n=await e.emailProviders.get(t);if(n)return n;let r=await a(t);return!r||t===r.tenantId?null:e.emailProviders.get(r.tenantId)}},resourceServers:M(e,a),hooks:I(e,a)}}function F(e){if(!e||typeof e!=`object`)return!1;let t=e.metadata;return!t||typeof t!=`object`?!1:t.inheritable===!0}function I(e,t){return{...e.hooks,list:async(n,r)=>{let i=await e.hooks.list(n,r),a=await t(n);if(!a||n===a.tenantId)return i;let o=((await e.hooks.list(a.tenantId,r)).hooks||[]).filter(F);if(o.length===0)return i;let s=new Set((i.hooks||[]).map(e=>e.hook_id)),c=o.filter(e=>!s.has(e.hook_id));return{...i,hooks:[...i.hooks||[],...c],length:typeof i.length==`number`?i.length+c.length:i.length}},get:async(n,r)=>{let i=await e.hooks.get(n,r);if(i)return i;let a=await t(n);if(!a||n===a.tenantId)return i;let o=await e.hooks.get(a.tenantId,r);return o&&F(o)?o:null}}}function L(e,t){return P(e,t)}function R(e){return async(t,n)=>{let r=t.var.user;return r?.tenant_id===e&&r.org_name&&t.set(`tenant_id`,r.org_name),n()}}function z(e){return async(n,i)=>{if(!e.accessControl)return i();let{controlPlaneTenantId:o}=e.accessControl,s=n.var.org_name,c=n.var.organization_id,l=s||c,u=n.var.tenant_id,d=n.var.user,f=(d?.aud?Array.isArray(d.aud)?d.aud:[d.aud]:[]).includes(t.MANAGEMENT_API_AUDIENCE);if(!u&&l&&f&&(n.set(`tenant_id`,l),u=l),!u)throw new r.HTTPException(400,{message:`Tenant ID not found in request`});if(!a(c,u,o,s))throw new r.HTTPException(403,{message:`Access denied to tenant ${u}`});return i()}}function B(e){return async(t,n)=>{if(!e.subdomainRouting)return n();let{baseDomain:i,reservedSubdomains:a=[],resolveSubdomain:o}=e.subdomainRouting,s=t.req.header(`x-forwarded-host`)||t.req.header(`host`)||``,c=null;if(s.endsWith(i)){let e=s.slice(0,-(i.length+1));e&&!e.includes(`.`)&&(c=e)}if(c&&a.includes(c)&&(c=null),!c)return e.accessControl&&t.set(`tenant_id`,e.accessControl.controlPlaneTenantId),n();let l=null;if(o)l=await o(c);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{let n=await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId,c);n&&(l=n.id)}catch{}if(!l)throw new r.HTTPException(404,{message:`Tenant not found for subdomain: ${c}`});return t.set(`tenant_id`,l),n()}}function V(e){return async(t,n)=>{if(!e.databaseIsolation)return n();let i=t.var.tenant_id;if(!i)throw new r.HTTPException(400,{message:`Tenant ID not found in request`});try{let n=await e.databaseIsolation.getAdapters(i);t.env.data=n}catch(e){throw console.error(`Failed to resolve database for tenant ${i}:`,e),new r.HTTPException(500,{message:`Failed to resolve tenant database`})}return n()}}function H(e){let t=B(e),n=z(e),r=V(e);return async(e,i)=>(await t(e,async()=>{}),await n(e,async()=>{}),await r(e,async()=>{}),i())}function U(e){let{dataAdapter:n,controlPlane:r,controlPlane:{tenantId:i=`control_plane`,clientId:a}={},resolveControlPlane:o,sync:s={resourceServers:!0,roles:!0},defaultPermissions:l=[`tenant:admin`],requireOrganizationMatch:u=!1,managementApiExtensions:d=[],entityHooks:f,getChildTenantIds:p,getAdapters:m,...h}=e;if(o&&!r)throw Error("initMultiTenant: `resolveControlPlane` requires `controlPlane` to be set. The static `controlPlane.tenantId` is used for access control, sync direction, and tenant management routing; the resolver only overrides per-tenant runtime inheritance lookups on top of it.");let g=n,v=n;r&&(g=L(n,{controlPlaneTenantId:i,controlPlaneClientId:a,resolveControlPlane:o}),v={...N(n,{controlPlaneTenantId:i,resolveControlPlane:o}),multiTenancyConfig:{controlPlaneTenantId:i,controlPlaneClientId:a,resolveControlPlane:o}});let y=s!==!1,b=y?{resourceServers:s.resourceServers??!0,roles:s.roles??!0}:{resourceServers:!1,roles:!1},{entityHooks:S,tenantHooks:C}=_({controlPlaneTenantId:i,getChildTenantIds:p??(async()=>(await(0,t.fetchAll)(e=>g.tenants.list(e),`tenants`,{cursorField:`id`,pageSize:100})).filter(e=>e.id!==i).map(e=>e.id)),getAdapters:m??(async()=>g),getControlPlaneAdapters:async()=>g,sync:b}),w={resourceServers:[S.resourceServers,...f?.resourceServers??[]],roles:[S.roles,...f?.roles??[]],connections:f?.connections??[],tenants:f?.tenants??[],rolePermissions:f?.rolePermissions??[]},E=c({accessControl:{controlPlaneTenantId:i,requireOrganizationMatch:u,defaultPermissions:l}}),D=x({accessControl:{controlPlaneTenantId:i,requireOrganizationMatch:u,defaultPermissions:l}},{tenants:{async beforeCreate(e,t){return E.beforeCreate&&(t=await E.beforeCreate(e,t)),C.beforeCreate&&(t=await C.beforeCreate(e,t)),t},async afterCreate(e,t){await E.afterCreate?.(e,t),await C.afterCreate?.(e,t)},async beforeDelete(e,t){await E.beforeDelete?.(e,t),await C.beforeDelete?.(e,t)}}}),{app:O}=(0,t.init)({dataAdapter:g,managementDataAdapter:v,...h,entityHooks:w,managementApiExtensions:[...d,{path:`/tenants`,router:D}]});return O.use(`/api/v2/*`,R(i)),y&&O.use(`/api/v2/*`,T()),{app:O,controlPlaneTenantId:i}}function W(){return{upserted:0,errors:[]}}function G(e){let t=e.metadata;return!!(t&&t.inheritable===!0)}async function K(e,t,n,r){try{await r()}catch(r){let i=`${t}: ${r instanceof Error?r.message:String(r)}`;if(!n)throw Error(i,{cause:r});e.errors.push(i)}}async function q(e,n){let{controlPlaneTenantId:r,getControlPlaneAdapters:i,getAdapters:a,entities:o={},continueOnError:s=!1}=e,c={connections:o.connections??!0,resourceServers:o.resourceServers??!0,hooks:o.hooks??!0,emailProvider:o.emailProvider??!0,branding:o.branding??!0,promptSettings:o.promptSettings??!0},l=await i(),u=await a(n),d={tenantId:n,connections:W(),resourceServers:W(),hooks:W(),emailProvider:W(),branding:W(),promptSettings:W()};if(c.connections){let e=await(0,t.fetchAll)(e=>l.connections.list(r,e),`connections`,{cursorField:`id`,pageSize:100});for(let n of e){let e=n.id;e&&await K(d.connections,`connection ${e}`,s,async()=>{let i=t.connectionInsertSchema.parse(n);await u.connections.get(r,e)?await u.connections.update(r,e,i):await u.connections.create(r,i),d.connections.upserted+=1})}}if(c.resourceServers){let e=await(0,t.fetchAll)(e=>l.resourceServers.list(r,e),`resource_servers`,{cursorField:`id`,pageSize:100});for(let n of e)!n.is_system||!n.id||await K(d.resourceServers,`resource_server ${n.id}`,s,async()=>{let e=t.resourceServerInsertSchema.parse(n);await u.resourceServers.get(r,n.id)?await u.resourceServers.update(r,n.id,e):await u.resourceServers.create(r,e),d.resourceServers.upserted+=1})}if(c.hooks){let e=await(0,t.fetchAll)(e=>l.hooks.list(r,e),`hooks`,{cursorField:`hook_id`,pageSize:100});for(let n of e)!G(n)||!n.hook_id||await K(d.hooks,`hook ${n.hook_id}`,s,async()=>{let e=t.hookInsertSchema.parse(n);await u.hooks.get(r,n.hook_id)?await u.hooks.update(r,n.hook_id,e):await u.hooks.create(r,e),d.hooks.upserted+=1})}return c.emailProvider&&await K(d.emailProvider,`email_provider`,s,async()=>{let e=await l.emailProviders.get(r);e&&(await u.emailProviders.get(r)?await u.emailProviders.update(r,e):await u.emailProviders.create(r,e),d.emailProvider.upserted+=1)}),c.branding&&await K(d.branding,`branding`,s,async()=>{let e=await l.branding.get(r);e&&(await u.branding.set(r,e),d.branding.upserted+=1)}),c.promptSettings&&await K(d.promptSettings,`prompt_settings`,s,async()=>{let e=await l.promptSettings.get(r);e&&(await u.promptSettings.set(r,e),d.promptSettings.upserted+=1)}),d}function J(e){return{syncDefaults:t=>q(e,t),syncDefaultsToTenants:async t=>{let n=[];for(let r of t)n.push(await q(e,r));return n}}}function Y(e){let t=X(e);return{name:`multi-tenancy`,middleware:H(e),hooks:t,routes:[{path:`/management`,handler:x(e,t)}],onRegister:async()=>{console.log(`Multi-tenancy plugin registered`),e.accessControl&&console.log(` - Access control enabled (control plane: ${e.accessControl.controlPlaneTenantId})`),e.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`),e.databaseIsolation&&console.log(` - Database isolation enabled`)}}}function X(e){let t=e.accessControl?i(e.accessControl):{},n=e.databaseIsolation?o(e.databaseIsolation):{},r=c(e);return{...t,...n,tenants:r}}function Z(t){let n=new e.Hono,r=X(t);return n.route(`/tenants`,x(t,r)),n}function Q(e){return{hooks:X(e),middleware:H(e),app:Z(e),config:e,wrapAdapters:(t,n)=>L(t,{controlPlaneTenantId:e.accessControl?.controlPlaneTenantId,controlPlaneClientId:n?.controlPlaneClientId})}}exports.createAccessControlHooks=i,exports.createAccessControlMiddleware=z,exports.createControlPlaneTenantMiddleware=R,exports.createDatabaseHooks=o,exports.createDatabaseMiddleware=V,exports.createDirectRolloutAdapter=J,exports.createMultiTenancy=Z,exports.createMultiTenancyHooks=X,exports.createMultiTenancyMiddleware=H,exports.createMultiTenancyPlugin=Y,exports.createProtectSyncedMiddleware=T,exports.createProvisioningHooks=c,exports.createRuntimeFallbackAdapter=P,exports.createSubdomainMiddleware=B,exports.createSyncHooks=_,exports.createTenantsOpenAPIRouter=x,exports.initMultiTenant=U,exports.mergeClientWithFallback=A,exports.projectControlPlaneDefaults=q,exports.setupMultiTenancy=Q,exports.validateTenantAccess=a,exports.withRuntimeFallback=L,exports.withSystemResourceServerInheritance=N;
@@ -1,8 +1,9 @@
1
1
  import { Hono as e } from "hono";
2
- import { MANAGEMENT_API_AUDIENCE as t, MANAGEMENT_API_SCOPES as n, auth0QuerySchema as r, connectionOptionsSchema as i, connectionSchema as a, deepMergePatch as o, fetchAll as s, init as c, tenantInsertSchema as l, tenantSchema as u } from "authhero";
3
- import { OpenAPIHono as d, createRoute as f, z as p } from "@hono/zod-openapi";
2
+ import { MANAGEMENT_API_AUDIENCE as t, MANAGEMENT_API_SCOPES as n, auth0QuerySchema as r, connectionInsertSchema as i, connectionOptionsSchema as a, connectionSchema as o, deepMergePatch as s, fetchAll as c, hookInsertSchema as l, init as u, resourceServerInsertSchema as d, tenantInsertSchema as f, tenantSchema as p } from "authhero";
3
+ import { OpenAPIHono as m, createRoute as h, z as g } from "@hono/zod-openapi";
4
+ import { HTTPException as _ } from "hono/http-exception";
4
5
  //#region src/hooks/access-control.ts
5
- function m(e) {
6
+ function v(e) {
6
7
  let { controlPlaneTenantId: t, requireOrganizationMatch: n = !0 } = e;
7
8
  return { async onTenantAccessValidation(e, r) {
8
9
  if (r === t) return !0;
@@ -13,14 +14,14 @@ function m(e) {
13
14
  return !0;
14
15
  } };
15
16
  }
16
- function h(e, t, n, r) {
17
+ function y(e, t, n, r) {
17
18
  if (t === n) return !0;
18
19
  let i = r || e;
19
20
  return i ? i.toLowerCase() === t.toLowerCase() : !1;
20
21
  }
21
22
  //#endregion
22
23
  //#region src/hooks/database.ts
23
- function g(e) {
24
+ function b(e) {
24
25
  return { async resolveDataAdapters(t) {
25
26
  try {
26
27
  return await e.getAdapters(t);
@@ -32,20 +33,20 @@ function g(e) {
32
33
  }
33
34
  //#endregion
34
35
  //#region src/hooks/provisioning.ts
35
- function _(e) {
36
+ function x(e) {
36
37
  return `urn:authhero:tenant:${e.toLowerCase()}`;
37
38
  }
38
- function v(e) {
39
+ function S(e) {
39
40
  return {
40
41
  async beforeCreate(e, t) {
41
42
  return !t.audience && t.id ? {
42
43
  ...t,
43
- audience: _(t.id)
44
+ audience: x(t.id)
44
45
  } : t;
45
46
  },
46
47
  async afterCreate(t, n) {
47
48
  let { accessControl: r, databaseIsolation: i } = e;
48
- r && t.ctx && await y(t, n, r), i?.onProvision && await i.onProvision(n.id);
49
+ r && t.ctx && await C(t, n, r), i?.onProvision && await i.onProvision(n.id);
49
50
  },
50
51
  async beforeDelete(t, n) {
51
52
  let { accessControl: r, databaseIsolation: i } = e;
@@ -63,14 +64,14 @@ function v(e) {
63
64
  }
64
65
  };
65
66
  }
66
- async function y(e, t, n) {
67
+ async function C(e, t, n) {
67
68
  let { controlPlaneTenantId: r, defaultPermissions: i, defaultRoles: a, issuer: o, adminRoleName: s = "Tenant Admin", adminRoleDescription: c = "Full access to all tenant management operations", addCreatorToOrganization: l = !0 } = n, u = await e.adapters.organizations.create(r, {
68
69
  name: t.id,
69
70
  display_name: t.friendly_name || t.id
70
71
  }), d;
71
- if (o && (d = await x(e, r, s, c)), l && e.ctx) {
72
+ if (o && (d = await T(e, r, s, c)), l && e.ctx) {
72
73
  let t = e.ctx.var.user;
73
- if (t?.sub && !await b(e, r, t.sub)) try {
74
+ if (t?.sub && !await w(e, r, t.sub)) try {
74
75
  await e.adapters.userOrganizations.create(r, {
75
76
  user_id: t.sub,
76
77
  organization_id: u.id
@@ -81,12 +82,12 @@ async function y(e, t, n) {
81
82
  }
82
83
  a && a.length > 0 && console.log(`Would assign roles ${a.join(", ")} to organization ${u.id}`), i && i.length > 0 && console.log(`Would grant permissions ${i.join(", ")} to organization ${u.id}`);
83
84
  }
84
- async function b(e, t, n) {
85
+ async function w(e, t, n) {
85
86
  let r = await e.adapters.userRoles.list(t, n, void 0, "");
86
87
  for (let n of r) if ((await e.adapters.rolePermissions.list(t, n.id, { per_page: 1e3 })).some((e) => e.permission_name === "admin:organizations")) return !0;
87
88
  return !1;
88
89
  }
89
- async function x(e, r, i, a) {
90
+ async function T(e, r, i, a) {
90
91
  let o = (await e.adapters.roles.list(r, {})).roles.find((e) => e.name === i);
91
92
  if (o) return o.id;
92
93
  let s = await e.adapters.roles.create(r, {
@@ -101,7 +102,7 @@ async function x(e, r, i, a) {
101
102
  }
102
103
  //#endregion
103
104
  //#region src/hooks/sync.ts
104
- function S(e, t, n = () => !0) {
105
+ function E(e, t, n = () => !0) {
105
106
  let { controlPlaneTenantId: r, getChildTenantIds: i, getAdapters: a } = e, o = /* @__PURE__ */ new Map();
106
107
  async function s(e, n, r) {
107
108
  return (await t(e).list(n, {
@@ -156,11 +157,11 @@ function S(e, t, n = () => !0) {
156
157
  }
157
158
  };
158
159
  }
159
- function C(e, t, n = () => !0) {
160
+ function D(e, t, n = () => !0) {
160
161
  let { controlPlaneTenantId: r, getControlPlaneAdapters: i, getAdapters: a } = e;
161
162
  return { async afterCreate(e, o) {
162
163
  if (o.id !== r) try {
163
- let e = await i(), c = await a(o.id), l = t(e), u = t(c), d = await s((e) => l.listPaginated(r, e), l.listKey, {
164
+ let e = await i(), s = await a(o.id), l = t(e), u = t(s), d = await c((e) => l.listPaginated(r, e), l.listKey, {
164
165
  cursorField: "id",
165
166
  pageSize: 100
166
167
  });
@@ -180,7 +181,7 @@ function C(e, t, n = () => !0) {
180
181
  }
181
182
  } };
182
183
  }
183
- var w = (e) => ({
184
+ var O = (e) => ({
184
185
  list: async (t, n) => (await e.resourceServers.list(t, n)).resource_servers,
185
186
  listPaginated: (t, n) => e.resourceServers.list(t, n),
186
187
  get: (t, n) => e.resourceServers.get(t, n),
@@ -198,7 +199,7 @@ var w = (e) => ({
198
199
  token_lifetime: e.token_lifetime,
199
200
  token_lifetime_for_web: e.token_lifetime_for_web
200
201
  })
201
- }), T = (e) => ({
202
+ }), k = (e) => ({
202
203
  list: async (t, n) => (await e.roles.list(t, n)).roles,
203
204
  listPaginated: (t, n) => e.roles.list(t, n),
204
205
  get: (t, n) => e.roles.get(t, n),
@@ -213,15 +214,15 @@ var w = (e) => ({
213
214
  description: e.description
214
215
  })
215
216
  });
216
- function E(e) {
217
+ function A(e) {
217
218
  return e.metadata?.sync !== !1;
218
219
  }
219
- function D(e) {
220
- let { sync: t = {}, filters: n = {} } = e, r = t.resourceServers ?? !0, i = t.roles ?? !0, a = (e) => E(e) ? n.resourceServers ? n.resourceServers(e) : !0 : !1, o = (e) => E(e) ? n.roles ? n.roles(e) : !0 : !1, c = r ? S(e, w, a) : void 0, l = i ? S(e, T, o) : void 0, u = r ? C(e, w, a) : void 0, d = i ? C(e, T, o) : void 0, f = i ? { async afterCreate(t, r) {
220
+ function j(e) {
221
+ let { sync: t = {}, filters: n = {} } = e, r = t.resourceServers ?? !0, i = t.roles ?? !0, a = (e) => A(e) ? n.resourceServers ? n.resourceServers(e) : !0 : !1, o = (e) => A(e) ? n.roles ? n.roles(e) : !0 : !1, s = r ? E(e, O, a) : void 0, l = i ? E(e, k, o) : void 0, u = r ? D(e, O, a) : void 0, d = i ? D(e, k, o) : void 0, f = i ? { async afterCreate(t, r) {
221
222
  if (r.id !== e.controlPlaneTenantId) {
222
223
  await d?.afterCreate?.(t, r);
223
224
  try {
224
- let t = await e.getControlPlaneAdapters(), i = await e.getAdapters(r.id), a = await s((n) => t.roles.list(e.controlPlaneTenantId, n), "roles", {
225
+ let t = await e.getControlPlaneAdapters(), i = await e.getAdapters(r.id), a = await c((n) => t.roles.list(e.controlPlaneTenantId, n), "roles", {
225
226
  cursorField: "id",
226
227
  pageSize: 100
227
228
  }), o = /* @__PURE__ */ new Map();
@@ -255,7 +256,7 @@ function D(e) {
255
256
  }
256
257
  return {
257
258
  entityHooks: {
258
- resourceServers: c,
259
+ resourceServers: s,
259
260
  roles: l
260
261
  },
261
262
  tenantHooks: { async afterCreate(e, t) {
@@ -271,42 +272,42 @@ function D(e) {
271
272
  };
272
273
  }
273
274
  //#endregion
274
- //#region ../../node_modules/.pnpm/hono@4.12.25/node_modules/hono/dist/http-exception.js
275
- var O = class extends Error {
276
- res;
277
- status;
278
- constructor(e = 500, t) {
279
- super(t?.message, { cause: t?.cause }), this.res = t?.res, this.status = e;
280
- }
281
- getResponse() {
282
- return this.res ? new Response(this.res.body, {
283
- status: this.status,
284
- headers: this.res.headers
285
- }) : new Response(this.message, { status: this.status });
286
- }
287
- };
288
- //#endregion
289
275
  //#region src/routes/tenants.ts
290
- function k(e, t) {
291
- let n = new d();
292
- return n.openapi(f({
276
+ var ee = g.object({
277
+ sub: g.string(),
278
+ tenant_id: g.string().optional(),
279
+ org_id: g.string().optional(),
280
+ scope: g.string().optional(),
281
+ permissions: g.array(g.string()).optional()
282
+ }).passthrough();
283
+ function te(e) {
284
+ let t = ee.safeParse(e);
285
+ return t.success ? t.data : void 0;
286
+ }
287
+ function ne(e) {
288
+ let t = e.permissions ?? [], n = e.scope ? e.scope.split(" ").filter(Boolean) : [], r = new Set([...t, ...n]);
289
+ return r.has("delete:tenants") || r.has("admin:organizations");
290
+ }
291
+ function M(e, t) {
292
+ let n = new m();
293
+ return n.openapi(h({
293
294
  tags: ["tenants"],
294
295
  method: "get",
295
296
  path: "/",
296
297
  request: { query: r },
297
298
  security: [{ Bearer: [] }],
298
299
  responses: { 200: {
299
- content: { "application/json": { schema: p.object({
300
- tenants: p.array(u),
301
- start: p.number().optional(),
302
- limit: p.number().optional(),
303
- length: p.number().optional()
300
+ content: { "application/json": { schema: g.object({
301
+ tenants: g.array(p),
302
+ start: g.number().optional(),
303
+ limit: g.number().optional(),
304
+ length: g.number().optional()
304
305
  }) } },
305
306
  description: "List of tenants"
306
307
  } }
307
308
  }), async (t) => {
308
- let { page: n, per_page: r, include_totals: i, q: a } = t.req.valid("query"), o = t.var.user, c = o?.permissions || [];
309
- if (!(o?.org_id ?? t.var.organization_id) && c.includes("admin:organizations")) {
309
+ let { page: n, per_page: r, include_totals: i, q: a } = t.req.valid("query"), o = t.var.user, s = o?.permissions || [];
310
+ if (!(o?.org_id ?? t.var.organization_id) && s.includes("admin:organizations")) {
310
311
  let e = await t.env.data.tenants.list({
311
312
  page: n,
312
313
  per_page: r,
@@ -321,21 +322,21 @@ function k(e, t) {
321
322
  }) : t.json({ tenants: e.tenants });
322
323
  }
323
324
  let l = e.accessControl?.controlPlaneTenantId ?? t.env.data.multiTenancyConfig?.controlPlaneTenantId;
324
- if (l && !o?.sub) throw new O(403, { message: "Access denied: token has no subject" });
325
+ if (l && !o?.sub) throw new _(403, { message: "Access denied: token has no subject" });
325
326
  if (l && o?.sub) {
326
- let e = (await s((e) => t.env.data.userOrganizations.listUserOrganizations(l, o.sub, e), "organizations")).map((e) => e.name);
327
+ let e = (await c((e) => t.env.data.userOrganizations.listUserOrganizations(l, o.sub, e), "organizations")).map((e) => e.name);
327
328
  if (e.length === 0) return i ? t.json({
328
329
  tenants: [],
329
330
  start: 0,
330
331
  limit: r ?? 50,
331
332
  length: 0
332
333
  }) : t.json({ tenants: [] });
333
- let c = e.length, u = n ?? 0, d = r ?? 50, f = u * d, p = e.slice(f, f + d);
334
+ let s = e.length, u = n ?? 0, d = r ?? 50, f = u * d, p = e.slice(f, f + d);
334
335
  if (p.length === 0) return i ? t.json({
335
336
  tenants: [],
336
337
  start: f,
337
338
  limit: d,
338
- length: c
339
+ length: s
339
340
  }) : t.json({ tenants: [] });
340
341
  let m = p.map((e) => `id:${e}`).join(" OR "), h = a ? `(${m}) AND (${a})` : m, g = await t.env.data.tenants.list({
341
342
  q: h,
@@ -346,7 +347,7 @@ function k(e, t) {
346
347
  tenants: g.tenants,
347
348
  start: f,
348
349
  limit: d,
349
- length: c
350
+ length: s
350
351
  }) : t.json({ tenants: g.tenants });
351
352
  }
352
353
  let u = await t.env.data.tenants.list({
@@ -361,22 +362,22 @@ function k(e, t) {
361
362
  limit: u.totals?.limit ?? r,
362
363
  length: u.tenants.length
363
364
  }) : t.json({ tenants: u.tenants });
364
- }), n.openapi(f({
365
+ }), n.openapi(h({
365
366
  tags: ["tenants"],
366
367
  method: "post",
367
368
  path: "/",
368
- request: { body: { content: { "application/json": { schema: l } } } },
369
+ request: { body: { content: { "application/json": { schema: f } } } },
369
370
  security: [{ Bearer: [] }],
370
371
  responses: {
371
372
  201: {
372
- content: { "application/json": { schema: u } },
373
+ content: { "application/json": { schema: p } },
373
374
  description: "Tenant created"
374
375
  },
375
376
  400: { description: "Validation error" },
376
377
  409: { description: "Tenant with this ID already exists" }
377
378
  }
378
379
  }), async (e) => {
379
- if (!e.var.user?.sub) throw new O(401, { message: "Authentication required to create tenants" });
380
+ if (!e.var.user?.sub) throw new _(401, { message: "Authentication required to create tenants" });
380
381
  let n = e.req.valid("json"), r = {
381
382
  adapters: e.env.data,
382
383
  ctx: e
@@ -384,11 +385,11 @@ function k(e, t) {
384
385
  t.tenants?.beforeCreate && (n = await t.tenants.beforeCreate(r, n));
385
386
  let i = await e.env.data.tenants.create(n);
386
387
  return t.tenants?.afterCreate && await t.tenants.afterCreate(r, i), e.json(i, 201);
387
- }), n.openapi(f({
388
+ }), n.openapi(h({
388
389
  tags: ["tenants"],
389
390
  method: "delete",
390
391
  path: "/{id}",
391
- request: { params: p.object({ id: p.string() }) },
392
+ request: { params: g.object({ id: g.string() }) },
392
393
  security: [{ Bearer: ["delete:tenants"] }],
393
394
  responses: {
394
395
  204: { description: "Tenant deleted" },
@@ -398,58 +399,62 @@ function k(e, t) {
398
399
  }), async (n) => {
399
400
  let { id: r } = n.req.valid("param"), i = e.accessControl?.controlPlaneTenantId ?? n.env.data.multiTenancyConfig?.controlPlaneTenantId;
400
401
  if (i) {
401
- let e = n.var.user;
402
- if (!e?.sub) throw new O(401, { message: "Authentication required" });
403
- if (r === i) throw new O(403, { message: "Cannot delete the control plane" });
402
+ let e = te(n.var.user);
403
+ if (!e?.sub) throw new _(401, { message: "Authentication required" });
404
+ if (r === i) throw new _(403, { message: "Cannot delete the control plane" });
404
405
  let t = n.var.org_name, a = r.toLowerCase(), o = !!t && t.toLowerCase() === a;
405
- if (o ||= (await s((t) => n.env.data.userOrganizations.listUserOrganizations(i, e.sub, t), "organizations")).some((e) => e.name?.toLowerCase() === a), !o) throw new O(403, { message: "Access denied to this tenant" });
406
+ if (!o) {
407
+ let r = !!(e.org_id ?? n.var.organization_id ?? t), a = !e.tenant_id || e.tenant_id === i;
408
+ !r && a && ne(e) && (o = !0);
409
+ }
410
+ if (o ||= (await c((t) => n.env.data.userOrganizations.listUserOrganizations(i, e.sub, t), "organizations")).some((e) => e.name?.toLowerCase() === a), !o) throw new _(403, { message: "Access denied to this tenant" });
406
411
  }
407
- if (!await n.env.data.tenants.get(r)) throw new O(404, { message: "Tenant not found" });
412
+ if (!await n.env.data.tenants.get(r)) throw new _(404, { message: "Tenant not found" });
408
413
  let a = {
409
414
  adapters: n.env.data,
410
415
  ctx: n
411
416
  };
412
417
  return t.tenants?.beforeDelete && await t.tenants.beforeDelete(a, r), await n.env.data.tenants.remove(r), t.tenants?.afterDelete && await t.tenants.afterDelete(a, r), n.body(null, 204);
413
- }), n.openapi(f({
418
+ }), n.openapi(h({
414
419
  tags: ["tenants", "settings"],
415
420
  method: "get",
416
421
  path: "/settings",
417
- request: { headers: p.object({ "tenant-id": p.string().optional() }) },
422
+ request: { headers: g.object({ "tenant-id": g.string().optional() }) },
418
423
  security: [{ Bearer: ["read:tenants"] }],
419
424
  responses: { 200: {
420
- content: { "application/json": { schema: u } },
425
+ content: { "application/json": { schema: p } },
421
426
  description: "Current tenant settings"
422
427
  } }
423
428
  }), async (e) => {
424
429
  let t = await e.env.data.tenants.get(e.var.tenant_id);
425
- if (!t) throw new O(404, { message: "Tenant not found" });
430
+ if (!t) throw new _(404, { message: "Tenant not found" });
426
431
  return e.json(t);
427
- }), n.openapi(f({
432
+ }), n.openapi(h({
428
433
  tags: ["tenants", "settings"],
429
434
  method: "patch",
430
435
  path: "/settings",
431
436
  request: {
432
- headers: p.object({ "tenant-id": p.string().optional() }),
433
- body: { content: { "application/json": { schema: p.object(l.shape).partial() } } }
437
+ headers: g.object({ "tenant-id": g.string().optional() }),
438
+ body: { content: { "application/json": { schema: g.object(f.shape).partial() } } }
434
439
  },
435
440
  security: [{ Bearer: ["update:tenants"] }],
436
441
  responses: { 200: {
437
- content: { "application/json": { schema: u } },
442
+ content: { "application/json": { schema: p } },
438
443
  description: "Updated tenant settings"
439
444
  } }
440
445
  }), async (e) => {
441
446
  let { id: t, ...n } = e.req.valid("json"), r = await e.env.data.tenants.get(e.var.tenant_id);
442
- if (!r) throw new O(404, { message: "Tenant not found" });
443
- let i = o(r, n);
447
+ if (!r) throw new _(404, { message: "Tenant not found" });
448
+ let i = s(r, n);
444
449
  await e.env.data.tenants.update(e.var.tenant_id, i);
445
450
  let a = await e.env.data.tenants.get(e.var.tenant_id);
446
- if (!a) throw new O(500, { message: "Failed to retrieve updated tenant" });
451
+ if (!a) throw new _(500, { message: "Failed to retrieve updated tenant" });
447
452
  return e.json(a);
448
453
  }), n;
449
454
  }
450
455
  //#endregion
451
456
  //#region src/middleware/protect-synced.ts
452
- function A(e) {
457
+ function N(e) {
453
458
  for (let { pattern: t, type: n } of [
454
459
  {
455
460
  pattern: /\/api\/v2\/resource-servers\/([^/]+)$/,
@@ -472,7 +477,7 @@ function A(e) {
472
477
  }
473
478
  return null;
474
479
  }
475
- async function j(e, t, n) {
480
+ async function re(e, t, n) {
476
481
  try {
477
482
  switch (n.type) {
478
483
  case "resource_server": return (await e.resourceServers.get(t, n.id))?.is_system === !0;
@@ -484,47 +489,47 @@ async function j(e, t, n) {
484
489
  return !1;
485
490
  }
486
491
  }
487
- function M(e) {
492
+ function ie(e) {
488
493
  return {
489
494
  resource_server: "resource server",
490
495
  role: "role",
491
496
  connection: "connection"
492
497
  }[e];
493
498
  }
494
- function N() {
499
+ function P() {
495
500
  return async (e, t) => {
496
501
  if (![
497
502
  "PATCH",
498
503
  "PUT",
499
504
  "DELETE"
500
505
  ].includes(e.req.method)) return t();
501
- let n = A(e.req.path);
506
+ let n = N(e.req.path);
502
507
  if (!n) return t();
503
508
  let r = e.var.tenant_id || e.req.header("x-tenant-id") || e.req.header("tenant-id");
504
509
  if (!r) return t();
505
- if (await j(e.env.data, r, n)) throw new O(403, { message: `This ${M(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.` });
510
+ if (await re(e.env.data, r, n)) throw new _(403, { message: `This ${ie(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.` });
506
511
  return t();
507
512
  };
508
513
  }
509
514
  //#endregion
510
515
  //#region src/middleware/settings-inheritance.ts
511
- function P(e, t) {
516
+ function F(e, t) {
512
517
  let n = t.find((t) => t.strategy === e.strategy);
513
518
  if (!n?.options) return e;
514
- let r = a.passthrough().parse({
519
+ let r = o.passthrough().parse({
515
520
  ...n,
516
521
  ...e
517
522
  });
518
- return r.options = i.passthrough().parse({
523
+ return r.options = a.passthrough().parse({
519
524
  ...n.options || {},
520
525
  ...e.options
521
526
  }), r;
522
527
  }
523
- function F(e, t) {
528
+ function I(e, t) {
524
529
  let n = [...t || [], ...e || []];
525
530
  return [...new Set(n)];
526
531
  }
527
- function I(e, t) {
532
+ function ae(e, t) {
528
533
  if (!t?.length) return e || [];
529
534
  if (!e?.length) return t;
530
535
  let n = /* @__PURE__ */ new Map();
@@ -535,19 +540,19 @@ function I(e, t) {
535
540
  function L(e, t) {
536
541
  return t ? {
537
542
  ...e,
538
- scopes: I(e.scopes, t.scopes)
543
+ scopes: ae(e.scopes, t.scopes)
539
544
  } : e;
540
545
  }
541
- function R(e, t) {
546
+ function oe(e, t) {
542
547
  return t ? {
543
548
  ...e,
544
- callbacks: F(e.callbacks, t.callbacks),
545
- web_origins: F(e.web_origins, t.web_origins),
546
- allowed_logout_urls: F(e.allowed_logout_urls, t.allowed_logout_urls),
547
- allowed_origins: F(e.allowed_origins, t.allowed_origins)
549
+ callbacks: I(e.callbacks, t.callbacks),
550
+ web_origins: I(e.web_origins, t.web_origins),
551
+ allowed_logout_urls: I(e.allowed_logout_urls, t.allowed_logout_urls),
552
+ allowed_origins: I(e.allowed_origins, t.allowed_origins)
548
553
  } : e;
549
554
  }
550
- function z(e) {
555
+ function R(e) {
551
556
  let { controlPlaneTenantId: t, controlPlaneClientId: n, resolveControlPlane: r } = e;
552
557
  if (r) return async (e) => r({ tenant_id: e });
553
558
  if (!t) return async () => void 0;
@@ -557,7 +562,7 @@ function z(e) {
557
562
  };
558
563
  return async () => i;
559
564
  }
560
- function B(e, t) {
565
+ function z(e, t) {
561
566
  return {
562
567
  ...e.resourceServers,
563
568
  get: async (n, r) => {
@@ -584,18 +589,18 @@ function B(e, t) {
584
589
  }
585
590
  };
586
591
  }
587
- function V(e, t) {
588
- let n = z({
592
+ function B(e, t) {
593
+ let n = R({
589
594
  controlPlaneTenantId: t.controlPlaneTenantId,
590
595
  resolveControlPlane: t.resolveControlPlane
591
596
  });
592
597
  return {
593
598
  ...e,
594
- resourceServers: B(e, n)
599
+ resourceServers: z(e, n)
595
600
  };
596
601
  }
597
- function H(e, t) {
598
- let { controlPlaneTenantId: n, controlPlaneClientId: r, resolveControlPlane: i } = t, a = z({
602
+ function V(e, t) {
603
+ let { controlPlaneTenantId: n, controlPlaneClientId: r, resolveControlPlane: i } = t, a = R({
599
604
  controlPlaneTenantId: n,
600
605
  controlPlaneClientId: r,
601
606
  resolveControlPlane: i
@@ -613,12 +618,12 @@ function H(e, t) {
613
618
  let r = await e.connections.get(t, n);
614
619
  if (!r) return r;
615
620
  let i = await a(t);
616
- return !i || t === i.tenantId ? r : P(r, (await e.connections.list(i.tenantId)).connections || []);
621
+ return !i || t === i.tenantId ? r : F(r, (await e.connections.list(i.tenantId)).connections || []);
617
622
  },
618
623
  list: async (t, n) => {
619
624
  let r = await e.connections.list(t, n), i = await a(t);
620
625
  if (!i || t === i.tenantId) return r;
621
- let o = await e.connections.list(i.tenantId), s = r.connections.map((e) => P(e, o.connections || []));
626
+ let o = await e.connections.list(i.tenantId), s = r.connections.map((e) => F(e, o.connections || []));
622
627
  return {
623
628
  ...r,
624
629
  connections: s
@@ -633,7 +638,7 @@ function H(e, t) {
633
638
  let i = await a(t);
634
639
  if (!i || t === i.tenantId) return r;
635
640
  let o = await e.connections.list(i.tenantId);
636
- return r.map((e) => P(e, o.connections || []));
641
+ return r.map((e) => F(e, o.connections || []));
637
642
  }
638
643
  },
639
644
  emailProviders: {
@@ -645,22 +650,22 @@ function H(e, t) {
645
650
  return !r || t === r.tenantId ? null : e.emailProviders.get(r.tenantId);
646
651
  }
647
652
  },
648
- resourceServers: B(e, a),
649
- hooks: W(e, a)
653
+ resourceServers: z(e, a),
654
+ hooks: se(e, a)
650
655
  };
651
656
  }
652
- function U(e) {
657
+ function H(e) {
653
658
  if (!e || typeof e != "object") return !1;
654
659
  let t = e.metadata;
655
660
  return !t || typeof t != "object" ? !1 : t.inheritable === !0;
656
661
  }
657
- function W(e, t) {
662
+ function se(e, t) {
658
663
  return {
659
664
  ...e.hooks,
660
665
  list: async (n, r) => {
661
666
  let i = await e.hooks.list(n, r), a = await t(n);
662
667
  if (!a || n === a.tenantId) return i;
663
- let o = ((await e.hooks.list(a.tenantId, r)).hooks || []).filter(U);
668
+ let o = ((await e.hooks.list(a.tenantId, r)).hooks || []).filter(H);
664
669
  if (o.length === 0) return i;
665
670
  let s = new Set((i.hooks || []).map((e) => e.hook_id)), c = o.filter((e) => !s.has(e.hook_id));
666
671
  return {
@@ -675,31 +680,31 @@ function W(e, t) {
675
680
  let a = await t(n);
676
681
  if (!a || n === a.tenantId) return i;
677
682
  let o = await e.hooks.get(a.tenantId, r);
678
- return o && U(o) ? o : null;
683
+ return o && H(o) ? o : null;
679
684
  }
680
685
  };
681
686
  }
682
- function G(e, t) {
683
- return H(e, t);
687
+ function U(e, t) {
688
+ return V(e, t);
684
689
  }
685
690
  //#endregion
686
691
  //#region src/middleware/index.ts
687
- function K(e) {
692
+ function W(e) {
688
693
  return async (t, n) => {
689
694
  let r = t.var.user;
690
695
  return r?.tenant_id === e && r.org_name && t.set("tenant_id", r.org_name), n();
691
696
  };
692
697
  }
693
- function q(e) {
698
+ function G(e) {
694
699
  return async (n, r) => {
695
700
  if (!e.accessControl) return r();
696
701
  let { controlPlaneTenantId: i } = e.accessControl, a = n.var.org_name, o = n.var.organization_id, s = a || o, c = n.var.tenant_id, l = n.var.user, u = (l?.aud ? Array.isArray(l.aud) ? l.aud : [l.aud] : []).includes(t);
697
- if (!c && s && u && (n.set("tenant_id", s), c = s), !c) throw new O(400, { message: "Tenant ID not found in request" });
698
- if (!h(o, c, i, a)) throw new O(403, { message: `Access denied to tenant ${c}` });
702
+ if (!c && s && u && (n.set("tenant_id", s), c = s), !c) throw new _(400, { message: "Tenant ID not found in request" });
703
+ if (!y(o, c, i, a)) throw new _(403, { message: `Access denied to tenant ${c}` });
699
704
  return r();
700
705
  };
701
706
  }
702
- function J(e) {
707
+ function K(e) {
703
708
  return async (t, n) => {
704
709
  if (!e.subdomainRouting) return n();
705
710
  let { baseDomain: r, reservedSubdomains: i = [], resolveSubdomain: a } = e.subdomainRouting, o = t.req.header("x-forwarded-host") || t.req.header("host") || "", s = null;
@@ -714,43 +719,43 @@ function J(e) {
714
719
  let n = await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId, s);
715
720
  n && (c = n.id);
716
721
  } catch {}
717
- if (!c) throw new O(404, { message: `Tenant not found for subdomain: ${s}` });
722
+ if (!c) throw new _(404, { message: `Tenant not found for subdomain: ${s}` });
718
723
  return t.set("tenant_id", c), n();
719
724
  };
720
725
  }
721
- function Y(e) {
726
+ function q(e) {
722
727
  return async (t, n) => {
723
728
  if (!e.databaseIsolation) return n();
724
729
  let r = t.var.tenant_id;
725
- if (!r) throw new O(400, { message: "Tenant ID not found in request" });
730
+ if (!r) throw new _(400, { message: "Tenant ID not found in request" });
726
731
  try {
727
732
  let n = await e.databaseIsolation.getAdapters(r);
728
733
  t.env.data = n;
729
734
  } catch (e) {
730
- throw console.error(`Failed to resolve database for tenant ${r}:`, e), new O(500, { message: "Failed to resolve tenant database" });
735
+ throw console.error(`Failed to resolve database for tenant ${r}:`, e), new _(500, { message: "Failed to resolve tenant database" });
731
736
  }
732
737
  return n();
733
738
  };
734
739
  }
735
- function X(e) {
736
- let t = J(e), n = q(e), r = Y(e);
740
+ function J(e) {
741
+ let t = K(e), n = G(e), r = q(e);
737
742
  return async (e, i) => (await t(e, async () => {}), await n(e, async () => {}), await r(e, async () => {}), i());
738
743
  }
739
744
  //#endregion
740
745
  //#region src/init.ts
741
- function Z(e) {
746
+ function ce(e) {
742
747
  let { dataAdapter: t, controlPlane: n, controlPlane: { tenantId: r = "control_plane", clientId: i } = {}, resolveControlPlane: a, sync: o = {
743
748
  resourceServers: !0,
744
749
  roles: !0
745
- }, defaultPermissions: l = ["tenant:admin"], requireOrganizationMatch: u = !1, managementApiExtensions: d = [], entityHooks: f, getChildTenantIds: p, getAdapters: m, ...h } = e;
750
+ }, defaultPermissions: s = ["tenant:admin"], requireOrganizationMatch: l = !1, managementApiExtensions: d = [], entityHooks: f, getChildTenantIds: p, getAdapters: m, ...h } = e;
746
751
  if (a && !n) throw Error("initMultiTenant: `resolveControlPlane` requires `controlPlane` to be set. The static `controlPlane.tenantId` is used for access control, sync direction, and tenant management routing; the resolver only overrides per-tenant runtime inheritance lookups on top of it.");
747
752
  let g = t, _ = t;
748
- n && (g = G(t, {
753
+ n && (g = U(t, {
749
754
  controlPlaneTenantId: r,
750
755
  controlPlaneClientId: i,
751
756
  resolveControlPlane: a
752
757
  }), _ = {
753
- ...V(t, {
758
+ ...B(t, {
754
759
  controlPlaneTenantId: r,
755
760
  resolveControlPlane: a
756
761
  }),
@@ -760,46 +765,46 @@ function Z(e) {
760
765
  resolveControlPlane: a
761
766
  }
762
767
  });
763
- let y = o !== !1, b = y ? {
768
+ let v = o !== !1, y = v ? {
764
769
  resourceServers: o.resourceServers ?? !0,
765
770
  roles: o.roles ?? !0
766
771
  } : {
767
772
  resourceServers: !1,
768
773
  roles: !1
769
- }, { entityHooks: x, tenantHooks: S } = D({
774
+ }, { entityHooks: b, tenantHooks: x } = j({
770
775
  controlPlaneTenantId: r,
771
- getChildTenantIds: p ?? (async () => (await s((e) => g.tenants.list(e), "tenants", {
776
+ getChildTenantIds: p ?? (async () => (await c((e) => g.tenants.list(e), "tenants", {
772
777
  cursorField: "id",
773
778
  pageSize: 100
774
779
  })).filter((e) => e.id !== r).map((e) => e.id)),
775
780
  getAdapters: m ?? (async () => g),
776
781
  getControlPlaneAdapters: async () => g,
777
- sync: b
782
+ sync: y
778
783
  }), C = {
779
- resourceServers: [x.resourceServers, ...f?.resourceServers ?? []],
780
- roles: [x.roles, ...f?.roles ?? []],
784
+ resourceServers: [b.resourceServers, ...f?.resourceServers ?? []],
785
+ roles: [b.roles, ...f?.roles ?? []],
781
786
  connections: f?.connections ?? [],
782
787
  tenants: f?.tenants ?? [],
783
788
  rolePermissions: f?.rolePermissions ?? []
784
- }, w = v({ accessControl: {
789
+ }, w = S({ accessControl: {
785
790
  controlPlaneTenantId: r,
786
- requireOrganizationMatch: u,
787
- defaultPermissions: l
788
- } }), T = k({ accessControl: {
791
+ requireOrganizationMatch: l,
792
+ defaultPermissions: s
793
+ } }), T = M({ accessControl: {
789
794
  controlPlaneTenantId: r,
790
- requireOrganizationMatch: u,
791
- defaultPermissions: l
795
+ requireOrganizationMatch: l,
796
+ defaultPermissions: s
792
797
  } }, { tenants: {
793
798
  async beforeCreate(e, t) {
794
- return w.beforeCreate && (t = await w.beforeCreate(e, t)), S.beforeCreate && (t = await S.beforeCreate(e, t)), t;
799
+ return w.beforeCreate && (t = await w.beforeCreate(e, t)), x.beforeCreate && (t = await x.beforeCreate(e, t)), t;
795
800
  },
796
801
  async afterCreate(e, t) {
797
- await w.afterCreate?.(e, t), await S.afterCreate?.(e, t);
802
+ await w.afterCreate?.(e, t), await x.afterCreate?.(e, t);
798
803
  },
799
804
  async beforeDelete(e, t) {
800
- await w.beforeDelete?.(e, t), await S.beforeDelete?.(e, t);
805
+ await w.beforeDelete?.(e, t), await x.beforeDelete?.(e, t);
801
806
  }
802
- } }), { app: E } = c({
807
+ } }), { app: E } = u({
803
808
  dataAdapter: g,
804
809
  managementDataAdapter: _,
805
810
  ...h,
@@ -809,22 +814,116 @@ function Z(e) {
809
814
  router: T
810
815
  }]
811
816
  });
812
- return E.use("/api/v2/*", K(r)), y && E.use("/api/v2/*", N()), {
817
+ return E.use("/api/v2/*", W(r)), v && E.use("/api/v2/*", P()), {
813
818
  app: E,
814
819
  controlPlaneTenantId: r
815
820
  };
816
821
  }
817
822
  //#endregion
823
+ //#region src/rollout/defaults-projection.ts
824
+ function Y() {
825
+ return {
826
+ upserted: 0,
827
+ errors: []
828
+ };
829
+ }
830
+ function le(e) {
831
+ let t = e.metadata;
832
+ return !!(t && t.inheritable === !0);
833
+ }
834
+ async function X(e, t, n, r) {
835
+ try {
836
+ await r();
837
+ } catch (r) {
838
+ let i = `${t}: ${r instanceof Error ? r.message : String(r)}`;
839
+ if (!n) throw Error(i, { cause: r });
840
+ e.errors.push(i);
841
+ }
842
+ }
843
+ async function Z(e, t) {
844
+ let { controlPlaneTenantId: n, getControlPlaneAdapters: r, getAdapters: a, entities: o = {}, continueOnError: s = !1 } = e, u = {
845
+ connections: o.connections ?? !0,
846
+ resourceServers: o.resourceServers ?? !0,
847
+ hooks: o.hooks ?? !0,
848
+ emailProvider: o.emailProvider ?? !0,
849
+ branding: o.branding ?? !0,
850
+ promptSettings: o.promptSettings ?? !0
851
+ }, f = await r(), p = await a(t), m = {
852
+ tenantId: t,
853
+ connections: Y(),
854
+ resourceServers: Y(),
855
+ hooks: Y(),
856
+ emailProvider: Y(),
857
+ branding: Y(),
858
+ promptSettings: Y()
859
+ };
860
+ if (u.connections) {
861
+ let e = await c((e) => f.connections.list(n, e), "connections", {
862
+ cursorField: "id",
863
+ pageSize: 100
864
+ });
865
+ for (let t of e) {
866
+ let e = t.id;
867
+ e && await X(m.connections, `connection ${e}`, s, async () => {
868
+ let r = i.parse(t);
869
+ await p.connections.get(n, e) ? await p.connections.update(n, e, r) : await p.connections.create(n, r), m.connections.upserted += 1;
870
+ });
871
+ }
872
+ }
873
+ if (u.resourceServers) {
874
+ let e = await c((e) => f.resourceServers.list(n, e), "resource_servers", {
875
+ cursorField: "id",
876
+ pageSize: 100
877
+ });
878
+ for (let t of e) !t.is_system || !t.id || await X(m.resourceServers, `resource_server ${t.id}`, s, async () => {
879
+ let e = d.parse(t);
880
+ await p.resourceServers.get(n, t.id) ? await p.resourceServers.update(n, t.id, e) : await p.resourceServers.create(n, e), m.resourceServers.upserted += 1;
881
+ });
882
+ }
883
+ if (u.hooks) {
884
+ let e = await c((e) => f.hooks.list(n, e), "hooks", {
885
+ cursorField: "hook_id",
886
+ pageSize: 100
887
+ });
888
+ for (let t of e) !le(t) || !t.hook_id || await X(m.hooks, `hook ${t.hook_id}`, s, async () => {
889
+ let e = l.parse(t);
890
+ await p.hooks.get(n, t.hook_id) ? await p.hooks.update(n, t.hook_id, e) : await p.hooks.create(n, e), m.hooks.upserted += 1;
891
+ });
892
+ }
893
+ return u.emailProvider && await X(m.emailProvider, "email_provider", s, async () => {
894
+ let e = await f.emailProviders.get(n);
895
+ e && (await p.emailProviders.get(n) ? await p.emailProviders.update(n, e) : await p.emailProviders.create(n, e), m.emailProvider.upserted += 1);
896
+ }), u.branding && await X(m.branding, "branding", s, async () => {
897
+ let e = await f.branding.get(n);
898
+ e && (await p.branding.set(n, e), m.branding.upserted += 1);
899
+ }), u.promptSettings && await X(m.promptSettings, "prompt_settings", s, async () => {
900
+ let e = await f.promptSettings.get(n);
901
+ e && (await p.promptSettings.set(n, e), m.promptSettings.upserted += 1);
902
+ }), m;
903
+ }
904
+ //#endregion
905
+ //#region src/rollout/index.ts
906
+ function ue(e) {
907
+ return {
908
+ syncDefaults: (t) => Z(e, t),
909
+ syncDefaultsToTenants: async (t) => {
910
+ let n = [];
911
+ for (let r of t) n.push(await Z(e, r));
912
+ return n;
913
+ }
914
+ };
915
+ }
916
+ //#endregion
818
917
  //#region src/plugin.ts
819
- function ee(e) {
918
+ function de(e) {
820
919
  let t = Q(e);
821
920
  return {
822
921
  name: "multi-tenancy",
823
- middleware: X(e),
922
+ middleware: J(e),
824
923
  hooks: t,
825
924
  routes: [{
826
925
  path: "/management",
827
- handler: k(e, t)
926
+ handler: M(e, t)
828
927
  }],
829
928
  onRegister: async () => {
830
929
  console.log("Multi-tenancy plugin registered"), e.accessControl && console.log(` - Access control enabled (control plane: ${e.accessControl.controlPlaneTenantId})`), e.subdomainRouting && console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`), e.databaseIsolation && console.log(" - Database isolation enabled");
@@ -834,7 +933,7 @@ function ee(e) {
834
933
  //#endregion
835
934
  //#region src/index.ts
836
935
  function Q(e) {
837
- let t = e.accessControl ? m(e.accessControl) : {}, n = e.databaseIsolation ? g(e.databaseIsolation) : {}, r = v(e);
936
+ let t = e.accessControl ? v(e.accessControl) : {}, n = e.databaseIsolation ? b(e.databaseIsolation) : {}, r = S(e);
838
937
  return {
839
938
  ...t,
840
939
  ...n,
@@ -843,19 +942,19 @@ function Q(e) {
843
942
  }
844
943
  function $(t) {
845
944
  let n = new e(), r = Q(t);
846
- return n.route("/tenants", k(t, r)), n;
945
+ return n.route("/tenants", M(t, r)), n;
847
946
  }
848
- function te(e) {
947
+ function fe(e) {
849
948
  return {
850
949
  hooks: Q(e),
851
- middleware: X(e),
950
+ middleware: J(e),
852
951
  app: $(e),
853
952
  config: e,
854
- wrapAdapters: (t, n) => G(t, {
953
+ wrapAdapters: (t, n) => U(t, {
855
954
  controlPlaneTenantId: e.accessControl?.controlPlaneTenantId,
856
955
  controlPlaneClientId: n?.controlPlaneClientId
857
956
  })
858
957
  };
859
958
  }
860
959
  //#endregion
861
- export { m as createAccessControlHooks, q as createAccessControlMiddleware, K as createControlPlaneTenantMiddleware, g as createDatabaseHooks, Y as createDatabaseMiddleware, $ as createMultiTenancy, Q as createMultiTenancyHooks, X as createMultiTenancyMiddleware, ee as createMultiTenancyPlugin, N as createProtectSyncedMiddleware, v as createProvisioningHooks, H as createRuntimeFallbackAdapter, J as createSubdomainMiddleware, D as createSyncHooks, k as createTenantsOpenAPIRouter, Z as initMultiTenant, R as mergeClientWithFallback, te as setupMultiTenancy, h as validateTenantAccess, G as withRuntimeFallback, V as withSystemResourceServerInheritance };
960
+ export { v as createAccessControlHooks, G as createAccessControlMiddleware, W as createControlPlaneTenantMiddleware, b as createDatabaseHooks, q as createDatabaseMiddleware, ue as createDirectRolloutAdapter, $ as createMultiTenancy, Q as createMultiTenancyHooks, J as createMultiTenancyMiddleware, de as createMultiTenancyPlugin, P as createProtectSyncedMiddleware, S as createProvisioningHooks, V as createRuntimeFallbackAdapter, K as createSubdomainMiddleware, j as createSyncHooks, M as createTenantsOpenAPIRouter, ce as initMultiTenant, oe as mergeClientWithFallback, Z as projectControlPlaneDefaults, fe as setupMultiTenancy, y as validateTenantAccess, U as withRuntimeFallback, B as withSystemResourceServerInheritance };
@@ -3,6 +3,8 @@ import { MultiTenancyConfig, MultiTenancyHooks, MultiTenancyBindings, MultiTenan
3
3
  export * from "./types";
4
4
  export { initMultiTenant } from "./init";
5
5
  export type { MultiTenantConfig, MultiTenantResult, ControlPlaneConfig, } from "./init";
6
+ export { createDirectRolloutAdapter, projectControlPlaneDefaults, } from "./rollout";
7
+ export type { ControlPlaneRolloutAdapter, DefaultsProjectionConfig, DefaultsProjectionEntities, DefaultsProjectionResult, EntityProjectionOutcome, } from "./rollout";
6
8
  export { createSyncHooks } from "./hooks/sync";
7
9
  export type { EntitySyncConfig, SyncHooksResult } from "./hooks/sync";
8
10
  export { createTenantsOpenAPIRouter } from "./routes";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAajB,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACzC,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAEtD,OAAO,EACL,4BAA4B,EAC5B,6BAA6B,EAC7B,kCAAkC,EAClC,yBAAyB,EACzB,wBAAwB,EACxB,6BAA6B,EAC7B,4BAA4B,EAC5B,mBAAmB,EACnB,mCAAmC,EACnC,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAgBnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB;cAE/C,oBAAoB;eACnB,qBAAqB;0CASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB;;;;;;;kBAnD9C,oBAAoB;mBACnB,qBAAqB;;;IAwDhC;;;;;;;OAOG;6BAES,OAAO,UAAU,EAAE,YAAY,qBACtB;QAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;KAAE;EAOzD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAajB,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACzC,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,QAAQ,CAAC;AAKhB,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAEtD,OAAO,EACL,4BAA4B,EAC5B,6BAA6B,EAC7B,kCAAkC,EAClC,yBAAyB,EACzB,wBAAwB,EACxB,6BAA6B,EAC7B,4BAA4B,EAC5B,mBAAmB,EACnB,mCAAmC,EACnC,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAgBnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB;cAE/C,oBAAoB;eACnB,qBAAqB;0CASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB;;;;;;;kBAnD9C,oBAAoB;mBACnB,qBAAqB;;;IAwDhC;;;;;;;OAOG;6BAES,OAAO,UAAU,EAAE,YAAY,qBACtB;QAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;KAAE;EAOzD"}
@@ -0,0 +1,74 @@
1
+ import { DataAdapters } from "authhero";
2
+ /**
3
+ * Which control plane entities to project into a tenant's own database. Every
4
+ * entity defaults to `true`.
5
+ *
6
+ * The projected rows are the same set the runtime fallback
7
+ * (`withRuntimeFallback`) reads from the control plane tenant: connections,
8
+ * `is_system` resource servers, `inheritable` hooks and the email provider.
9
+ * `branding` and `promptSettings` are projected too so a WFP tenant can render
10
+ * the control plane's defaults; consuming them on read still depends on the
11
+ * tenant resolving those singletons against the control plane tenant id.
12
+ */
13
+ export interface DefaultsProjectionEntities {
14
+ connections?: boolean;
15
+ resourceServers?: boolean;
16
+ hooks?: boolean;
17
+ emailProvider?: boolean;
18
+ branding?: boolean;
19
+ promptSettings?: boolean;
20
+ }
21
+ export interface DefaultsProjectionConfig {
22
+ /**
23
+ * The control plane tenant id. Projected rows are written into the target
24
+ * tenant's database under THIS id, so the existing runtime fallback resolves
25
+ * them exactly as it does in a shared database.
26
+ */
27
+ controlPlaneTenantId: string;
28
+ /**
29
+ * Adapters for reading the control plane tenant's rows. Secrets are returned
30
+ * decrypted (this should be the encrypted adapter), and are re-encrypted at
31
+ * rest by the target adapter on write.
32
+ */
33
+ getControlPlaneAdapters: () => Promise<DataAdapters>;
34
+ /**
35
+ * Adapters for the target tenant's own database. For a WFP tenant this is the
36
+ * adapter over the tenant's D1, ideally wrapped with a key ring that tags
37
+ * control-plane-tenant rows with a control-plane-only key id so the tenant
38
+ * operator cannot read the inherited secrets.
39
+ */
40
+ getAdapters: (tenantId: string) => Promise<DataAdapters>;
41
+ /** Which entities to project. All default to true. */
42
+ entities?: DefaultsProjectionEntities;
43
+ /**
44
+ * When false (default) the first failing entity throws, so a pilot rollout
45
+ * fails loudly. When true, every entity is attempted and errors are collected
46
+ * into the result instead.
47
+ */
48
+ continueOnError?: boolean;
49
+ }
50
+ export interface EntityProjectionOutcome {
51
+ /** Rows created or updated. */
52
+ upserted: number;
53
+ /** Non-fatal errors, populated only when `continueOnError` is true. */
54
+ errors: string[];
55
+ }
56
+ export interface DefaultsProjectionResult {
57
+ tenantId: string;
58
+ connections: EntityProjectionOutcome;
59
+ resourceServers: EntityProjectionOutcome;
60
+ hooks: EntityProjectionOutcome;
61
+ emailProvider: EntityProjectionOutcome;
62
+ branding: EntityProjectionOutcome;
63
+ promptSettings: EntityProjectionOutcome;
64
+ }
65
+ /**
66
+ * Projects the control plane tenant's inheritable defaults into a single target
67
+ * tenant's database, writing the rows under the control plane tenant id so the
68
+ * existing runtime fallback resolves them with no read-path change.
69
+ *
70
+ * Idempotent: every row is upserted by its stable id, so re-running the
71
+ * projection (a re-sync, or a later rollout) converges rather than duplicating.
72
+ */
73
+ export declare function projectControlPlaneDefaults(config: DefaultsProjectionConfig, targetTenantId: string): Promise<DefaultsProjectionResult>;
74
+ //# sourceMappingURL=defaults-projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults-projection.d.ts","sourceRoot":"","sources":["../../../src/rollout/defaults-projection.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAQb,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;GAUG;AACH,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;;;OAIG;IACH,uBAAuB,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IAErD;;;;;OAKG;IACH,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzD,sDAAsD;IACtD,QAAQ,CAAC,EAAE,0BAA0B,CAAC;IAEtC;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,uBAAuB,CAAC;IACrC,eAAe,EAAE,uBAAuB,CAAC;IACzC,KAAK,EAAE,uBAAuB,CAAC;IAC/B,aAAa,EAAE,uBAAuB,CAAC;IACvC,QAAQ,EAAE,uBAAuB,CAAC;IAClC,cAAc,EAAE,uBAAuB,CAAC;CACzC;AAgCD;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,wBAAwB,EAChC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,wBAAwB,CAAC,CAyJnC"}
@@ -0,0 +1,30 @@
1
+ import { DefaultsProjectionConfig, DefaultsProjectionResult } from "./defaults-projection";
2
+ export type { DefaultsProjectionConfig, DefaultsProjectionEntities, DefaultsProjectionResult, EntityProjectionOutcome, } from "./defaults-projection";
3
+ export { projectControlPlaneDefaults } from "./defaults-projection";
4
+ /**
5
+ * A control plane rollout applies the control plane's state to one or more
6
+ * tenant databases. `syncDefaults` is the only operation today; schema
7
+ * migrations and tenant worker code deploys are the same shape (enumerate
8
+ * tenants, apply per-tenant, retry, resume) and will become sibling methods.
9
+ *
10
+ * The interface is the seam for execution strategy. The direct implementation
11
+ * runs inline — right for a single pilot tenant. A future Cloudflare Workflows
12
+ * implementation will satisfy the same interface with durable, retryable,
13
+ * resumable fan-out, with no change to callers.
14
+ */
15
+ export interface ControlPlaneRolloutAdapter {
16
+ /** Project the control plane defaults into a single tenant's database. */
17
+ syncDefaults(targetTenantId: string): Promise<DefaultsProjectionResult>;
18
+ /**
19
+ * Project the defaults into several tenants. The direct implementation runs
20
+ * them sequentially; a Workflows implementation fans out durably.
21
+ */
22
+ syncDefaultsToTenants(targetTenantIds: string[]): Promise<DefaultsProjectionResult[]>;
23
+ }
24
+ /**
25
+ * Creates a rollout adapter that executes projections inline in the current
26
+ * process. Use this to validate the model with a single tenant before moving to
27
+ * a durable Cloudflare Workflows implementation.
28
+ */
29
+ export declare function createDirectRolloutAdapter(config: DefaultsProjectionConfig): ControlPlaneRolloutAdapter;
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rollout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EAEzB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,0BAA0B;IACzC,0EAA0E;IAC1E,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAExE;;;OAGG;IACH,qBAAqB,CACnB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,wBAAwB,GAC/B,0BAA0B,CAa5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"tenants.d.ts","sourceRoot":"","sources":["../../../src/routes/tenants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AAUhE,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB;cAGZ,oBAAoB;eACnB,qBAAqB;YAuenC"}
1
+ {"version":3,"file":"tenants.d.ts","sourceRoot":"","sources":["../../../src/routes/tenants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AAUhE,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,UAAU,CAAC;AAwClB;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB;cAGZ,oBAAoB;eACnB,qBAAqB;YAggBnC"}
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "git",
12
12
  "url": "https://github.com/markusahlstrand/authhero"
13
13
  },
14
- "version": "14.23.1",
14
+ "version": "14.24.1",
15
15
  "description": "Multi-tenancy support for AuthHero with organization-based access control and per-tenant database isolation",
16
16
  "files": [
17
17
  "dist"
@@ -37,8 +37,8 @@
37
37
  "vite": "^8.0.14",
38
38
  "vitest": "^4.1.7",
39
39
  "@authhero/adapter-interfaces": "3.1.1",
40
- "@authhero/kysely-adapter": "11.8.8",
41
- "authhero": "7.2.2"
40
+ "@authhero/kysely-adapter": "11.8.9",
41
+ "authhero": "8.4.1"
42
42
  },
43
43
  "dependencies": {
44
44
  "zod": "^4.4.3"