@authhero/multi-tenancy 13.17.0 → 13.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,15 +9,19 @@ Multi-tenancy support for AuthHero with organization-based access control, per-t
9
9
  - ⚙️ **Settings Inheritance** - Inherit configuration from main tenant to child tenants
10
10
  - 🌐 **Subdomain Routing** - Automatic subdomain-to-tenant resolution
11
11
  - 🔄 **Tenant Lifecycle** - Automated provisioning and deprovisioning
12
- - 🪝 **Hooks Integration** - Seamless integration with AuthHero hooks system
13
- - 📡 **Resource Server Sync** - Automatically sync resource servers from main tenant to all child tenants
12
+ - 🪝 **Composable Architecture** - Combine multi-tenancy features with the base AuthHero package
13
+ - 📡 **Entity Sync** - Automatically sync resource servers, roles, and connections from control plane to all child tenants
14
14
 
15
15
  ## Installation
16
16
 
17
17
  ```bash
18
- pnpm add @authhero/multi-tenancy
18
+ pnpm add authhero @authhero/multi-tenancy
19
19
  ```
20
20
 
21
+ ::: tip Peer Dependency
22
+ `@authhero/multi-tenancy` requires `authhero` as a peer dependency. Both packages must be installed.
23
+ :::
24
+
21
25
  ## Documentation
22
26
 
23
27
  📚 **Full documentation**: [https://authhero.net/packages/multi-tenancy/](https://authhero.net/packages/multi-tenancy/)
@@ -29,33 +33,71 @@ pnpm add @authhero/multi-tenancy
29
33
  ## Quick Start
30
34
 
31
35
  ```typescript
32
- import { Hono } from "hono";
33
- import { createAuthhero } from "authhero";
34
- import { setupMultiTenancy } from "@authhero/multi-tenancy";
35
-
36
- const multiTenancy = setupMultiTenancy({
37
- accessControl: {
38
- controlPlaneTenantId: "control_plane",
39
- defaultPermissions: ["tenant:admin"],
36
+ import { initMultiTenant } from "@authhero/multi-tenancy";
37
+ import createAdapters from "@authhero/kysely-adapter";
38
+
39
+ const dataAdapter = createAdapters(db);
40
+
41
+ const { app } = initMultiTenant({
42
+ dataAdapter,
43
+ // That's it! Everything else has sensible defaults:
44
+ // - controlPlaneTenantId: "control_plane"
45
+ // - Resource servers, roles, and connections sync enabled
46
+ // - Tenants API mounted at /tenants
47
+ // - Protected synced entities middleware applied
48
+ });
49
+
50
+ export default app;
51
+ ```
52
+
53
+ ### Customization
54
+
55
+ ```typescript
56
+ const { app } = initMultiTenant({
57
+ dataAdapter,
58
+ controlPlaneTenantId: "main",
59
+ sync: {
60
+ resourceServers: true,
61
+ roles: true,
62
+ connections: false, // Don't sync connections
40
63
  },
64
+ defaultPermissions: ["tenant:admin", "tenant:read"],
41
65
  });
66
+ ```
67
+
68
+ ### Advanced Setup
42
69
 
43
- const app = new Hono();
70
+ For more control, use the lower-level APIs:
44
71
 
45
- // Apply middleware
46
- app.use("*", multiTenancy.middleware);
72
+ ```typescript
73
+ import { init, fetchAll } from "authhero";
74
+ import {
75
+ createSyncHooks,
76
+ createTenantsOpenAPIRouter,
77
+ createProtectSyncedMiddleware,
78
+ } from "@authhero/multi-tenancy";
47
79
 
48
- // Mount management routes
49
- app.route("/management", multiTenancy.app);
80
+ const { entityHooks, tenantHooks } = createSyncHooks({
81
+ controlPlaneTenantId: "control_plane",
82
+ getChildTenantIds: async () => {
83
+ /* ... */
84
+ },
85
+ getAdapters: async () => dataAdapter,
86
+ getControlPlaneAdapters: async () => dataAdapter,
87
+ });
50
88
 
51
- // Mount AuthHero with hooks
52
- app.route(
53
- "/",
54
- createAuthhero({
55
- dataAdapter: env.data,
56
- hooks: multiTenancy.hooks,
57
- }),
89
+ const tenantsRouter = createTenantsOpenAPIRouter(
90
+ { accessControl: { controlPlaneTenantId: "control_plane" } },
91
+ { tenants: tenantHooks },
58
92
  );
93
+
94
+ const { app } = init({
95
+ dataAdapter,
96
+ entityHooks,
97
+ managementApiExtensions: [{ path: "/tenants", router: tenantsRouter }],
98
+ });
99
+
100
+ app.use("/api/v2/*", createProtectSyncedMiddleware());
59
101
  ```
60
102
 
61
103
  ## Key Concepts
@@ -112,7 +154,7 @@ const resourceServerHooks = createResourceServerSyncHooks({
112
154
  const config: AuthHeroConfig = {
113
155
  dataAdapter,
114
156
  entityHooks: {
115
- resourceServers: resourceServerHooks,
157
+ resourceServers: [resourceServerHooks],
116
158
  },
117
159
  };
118
160
  ```
@@ -1 +1 @@
1
- "use strict";var Z=Object.defineProperty;var x=(t,e,n)=>e in t?Z(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var k=(t,e,n)=>x(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ee=require("hono"),P=require("authhero"),S=require("@hono/zod-openapi"),I=require("@authhero/adapter-interfaces");function B(t){const{controlPlaneTenantId:e,requireOrganizationMatch:n=!0}=t;return{async onTenantAccessValidation(s,a){if(a===e)return!0;if(n){const o=s.var.org_name,r=s.var.organization_id,i=o||r;return i?i===a:!1}return!0}}}function G(t,e,n,s){if(e===n)return!0;const a=s||t;return a?a===e:!1}function K(t){return{async resolveDataAdapters(e){try{return await t.getAdapters(e)}catch(n){console.error(`Failed to resolve data adapters for tenant ${e}:`,n);return}}}}function L(t){return{async beforeCreate(e,n){return!n.audience&&n.id?{...n,audience:P.getTenantAudience(n.id)}:n},async afterCreate(e,n){const{accessControl:s,databaseIsolation:a}=t;s&&e.ctx&&await te(e,n,s),a!=null&&a.onProvision&&await a.onProvision(n.id)},async beforeDelete(e,n){const{accessControl:s,databaseIsolation:a}=t;if(s)try{const r=(await e.adapters.organizations.list(s.controlPlaneTenantId)).organizations.find(i=>i.name===n);r&&await e.adapters.organizations.remove(s.controlPlaneTenantId,r.id)}catch(o){console.warn(`Failed to remove organization for tenant ${n}:`,o)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(n)}catch(o){console.warn(`Failed to deprovision database for tenant ${n}:`,o)}}}}async function te(t,e,n){const{controlPlaneTenantId:s,defaultPermissions:a,defaultRoles:o,issuer:r,adminRoleName:i="Tenant Admin",adminRoleDescription:m="Full access to all tenant management operations",addCreatorToOrganization:d=!0}=n,c=await t.adapters.organizations.create(s,{name:e.id,display_name:e.friendly_name||e.id});let p;if(r&&(p=await se(t,s,i,m)),d&&t.ctx){const l=t.ctx.var.user;if(l!=null&&l.sub&&!await ne(t,s,l.sub))try{await t.adapters.userOrganizations.create(s,{user_id:l.sub,organization_id:c.id}),p&&await t.adapters.userRoles.create(s,l.sub,p,c.id)}catch(f){console.warn(`Failed to add creator ${l.sub} to organization ${c.id}:`,f)}}o&&o.length>0&&console.log(`Would assign roles ${o.join(", ")} to organization ${c.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${c.id}`)}async function ne(t,e,n){const s=await t.adapters.userRoles.list(e,n,void 0,"");for(const a of s)if((await t.adapters.rolePermissions.list(e,a.id,{per_page:1e3})).some(i=>i.permission_name==="admin:organizations"))return!0;return!1}async function se(t,e,n,s){const o=(await t.adapters.roles.list(e,{})).roles.find(d=>d.name===n);if(o)return o.id;const r=await t.adapters.roles.create(e,{name:n,description:s}),i=P.MANAGEMENT_API_AUDIENCE,m=P.MANAGEMENT_API_SCOPES.map(d=>({role_id:r.id,resource_server_identifier:i,permission_name:d.value}));return await t.adapters.rolePermissions.assign(e,r.id,m),r.id}const ae=["client_id","client_secret","app_secret","kid","team_id","twilio_sid","twilio_token"];function R(t,e,n=()=>!0){const{controlPlaneTenantId:s,getChildTenantIds:a,getAdapters:o}=t,r=new Map;async function i(c,p,l){return(await e(c).list(p,{q:`name:${l}`,per_page:1}))[0]??null}async function m(c){const p=await a(),l=e(await o(s));await Promise.all(p.map(async u=>{try{const f=await o(u),g=e(f),h={...l.transform(c),is_system:!0},y=await i(f,u,c.name),v=y?g.getId(y):void 0;if(y&&v){const A=g.preserveOnUpdate?g.preserveOnUpdate(y,h):h;await g.update(u,v,A)}else await g.create(u,h)}catch(f){console.error(`Failed to sync ${l.listKey} "${c.name}" to tenant "${u}":`,f)}}))}async function d(c){const p=await a();await Promise.all(p.map(async l=>{try{const u=await o(l),f=e(u),g=await i(u,l,c),w=g?f.getId(g):void 0;g&&w&&await f.remove(l,w)}catch(u){console.error(`Failed to delete entity "${c}" from tenant "${l}":`,u)}}))}return{afterCreate:async(c,p)=>{c.tenantId===s&&n(p)&&await m(p)},afterUpdate:async(c,p,l)=>{c.tenantId===s&&n(l)&&await m(l)},beforeDelete:async(c,p)=>{if(c.tenantId!==s)return;const u=await e(c.adapters).get(c.tenantId,p);u&&n(u)&&r.set(p,u)},afterDelete:async(c,p)=>{if(c.tenantId!==s)return;const l=r.get(p);l&&(r.delete(p),await d(l.name))}}}function q(t,e,n=()=>!0){const{controlPlaneTenantId:s,getControlPlaneAdapters:a,getAdapters:o}=t;return{async afterCreate(r,i){if(i.id!==s)try{const m=await a(),d=await o(i.id),c=e(m),p=e(d),l=await P.fetchAll(u=>c.listPaginated(s,u),c.listKey,{cursorField:"id",pageSize:100});await Promise.all(l.filter(u=>n(u)).map(async u=>{try{const f=c.transform(u);await p.create(i.id,{...f,is_system:!0})}catch(f){console.error(`Failed to sync entity to new tenant "${i.id}":`,f)}}))}catch(m){console.error(`Failed to sync entities to new tenant "${i.id}":`,m)}}}}const H=t=>({list:async(e,n)=>(await t.resourceServers.list(e,n)).resource_servers,listPaginated:(e,n)=>t.resourceServers.list(e,n),get:(e,n)=>t.resourceServers.get(e,n),create:(e,n)=>t.resourceServers.create(e,n),update:(e,n,s)=>t.resourceServers.update(e,n,s),remove:(e,n)=>t.resourceServers.remove(e,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})}),N=t=>({list:async(e,n)=>(await t.roles.list(e,n)).roles,listPaginated:(e,n)=>t.roles.list(e,n),get:(e,n)=>t.roles.get(e,n),create:(e,n)=>t.roles.create(e,n),update:(e,n,s)=>t.roles.update(e,n,s),remove:(e,n)=>t.roles.remove(e,n),listKey:"roles",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,description:e.description})}),U=t=>({list:async(e,n)=>(await t.connections.list(e,n)).connections,listPaginated:(e,n)=>t.connections.list(e,n),get:(e,n)=>t.connections.get(e,n),create:(e,n)=>t.connections.create(e,n),update:(e,n,s)=>t.connections.update(e,n,s),remove:(e,n)=>t.connections.remove(e,n),listKey:"connections",getId:e=>e.id,transform:e=>{const n=e.options?{...e.options}:{};for(const s of ae)delete n[s];return{id:e.id,name:e.name,display_name:e.display_name,strategy:e.strategy,options:n,response_type:e.response_type,response_mode:e.response_mode,is_domain_connection:e.is_domain_connection,show_as_button:e.show_as_button,metadata:e.metadata}},preserveOnUpdate:(e,n)=>{const s=e.options||{};return{...n,options:{...n.options,client_id:s.client_id,client_secret:s.client_secret,app_secret:s.app_secret,kid:s.kid,team_id:s.team_id,twilio_sid:s.twilio_sid,twilio_token:s.twilio_token}}}});function re(t){const{sync:e={},filters:n={}}=t,s=e.resourceServers??!0,a=e.roles??!0,o=e.connections??!0,r=s?R(t,H,n.resourceServers):void 0,i=a?R(t,N,n.roles):void 0,m=o?R(t,U,n.connections):void 0,d=s?q(t,H,n.resourceServers):void 0,c=a?q(t,N,n.roles):void 0,p=o?q(t,U,n.connections):void 0,l=a?{async afterCreate(g,w){var h;if(w.id!==t.controlPlaneTenantId){await((h=c==null?void 0:c.afterCreate)==null?void 0:h.call(c,g,w));try{const y=await t.getControlPlaneAdapters(),v=await t.getAdapters(w.id),A=await P.fetchAll(b=>y.roles.list(t.controlPlaneTenantId,b),"roles",{cursorField:"id",pageSize:100}),M=new Map;for(const b of A.filter(_=>{var T;return((T=n.roles)==null?void 0:T.call(n,_))??!0})){const _=await u(v,w.id,b.name);_&&M.set(b.name,_.id)}for(const b of A.filter(_=>{var T;return((T=n.roles)==null?void 0:T.call(n,_))??!0})){const _=M.get(b.name);if(_)try{const T=await y.rolePermissions.list(t.controlPlaneTenantId,b.id,{});T.length>0&&await v.rolePermissions.assign(w.id,_,T.map(z=>({role_id:_,resource_server_identifier:z.resource_server_identifier,permission_name:z.permission_name})))}catch(T){console.error(`Failed to sync permissions for role "${b.name}" to tenant "${w.id}":`,T)}}}catch(y){console.error(`Failed to sync role permissions to tenant "${w.id}":`,y)}}}}:void 0;async function u(g,w,h){return(await g.roles.list(w,{q:`name:${h}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:r,roles:i,connections:m},tenantHooks:{async afterCreate(g,w){const h=[d==null?void 0:d.afterCreate,(l==null?void 0:l.afterCreate)??(c==null?void 0:c.afterCreate),p==null?void 0:p.afterCreate],y=[];for(const v of h)if(v)try{await v(g,w)}catch(A){y.push(A instanceof Error?A:new Error(String(A)))}if(y.length===1)throw y[0];if(y.length>1)throw new AggregateError(y,y.map(v=>v.message).join("; "))}}}}var C=class extends Error{constructor(e=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});k(this,"res");k(this,"status");this.res=n==null?void 0:n.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 D(t,e){const n=new S.OpenAPIHono;return n.openapi(S.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:I.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:S.z.object({tenants:S.z.array(I.tenantSchema),start:S.z.number().optional(),limit:S.z.number().optional(),length:S.z.number().optional()})}},description:"List of tenants"}}}),async s=>{var u,f,g,w;const a=s.req.valid("query"),{page:o,per_page:r,include_totals:i,q:m}=a,d=s.var.user,c=(d==null?void 0:d.permissions)||[];if(c.includes("auth:read")||c.includes("admin:organizations")){const h=await s.env.data.tenants.list({page:o,per_page:r,include_totals:i,q:m});return i?s.json({tenants:h.tenants,start:((u=h.totals)==null?void 0:u.start)??0,limit:((f=h.totals)==null?void 0:f.limit)??r,length:h.tenants.length}):s.json({tenants:h.tenants})}if(t.accessControl&&(d!=null&&d.sub)){const h=t.accessControl.controlPlaneTenantId,v=(await P.fetchAll(O=>s.env.data.userOrganizations.listUserOrganizations(h,d.sub,O),"organizations")).map(O=>O.name);if(v.length===0)return i?s.json({tenants:[],start:0,limit:r??50,length:0}):s.json({tenants:[]});const A=v.length,M=o??0,b=r??50,_=M*b,T=v.slice(_,_+b);if(T.length===0)return i?s.json({tenants:[],start:_,limit:b,length:A}):s.json({tenants:[]});const z=T.map(O=>`id:${O}`).join(" OR "),Y=m?`(${z}) AND (${m})`:z,j=await s.env.data.tenants.list({q:Y,per_page:b,include_totals:!1});return i?s.json({tenants:j.tenants,start:_,limit:b,length:A}):s.json({tenants:j.tenants})}const l=await s.env.data.tenants.list({page:o,per_page:r,include_totals:i,q:m});return i?s.json({tenants:l.tenants,start:((g=l.totals)==null?void 0:g.start)??0,limit:((w=l.totals)==null?void 0:w.limit)??r,length:l.tenants.length}):s.json({tenants:l.tenants})}),n.openapi(S.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:I.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:I.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async s=>{var m,d;const a=s.var.user;if(!(a!=null&&a.sub))throw new C(401,{message:"Authentication required to create tenants"});let o=s.req.valid("json");const r={adapters:s.env.data,ctx:s};(m=e.tenants)!=null&&m.beforeCreate&&(o=await e.tenants.beforeCreate(r,o));const i=await s.env.data.tenants.create(o);return(d=e.tenants)!=null&&d.afterCreate&&await e.tenants.afterCreate(r,i),s.json(i,201)}),n.openapi(S.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:S.z.object({id:S.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 s=>{var i,m;const{id:a}=s.req.valid("param");if(t.accessControl){const d=s.var.user,c=t.accessControl.controlPlaneTenantId;if(!(d!=null&&d.sub))throw new C(401,{message:"Authentication required"});if(a===c)throw new C(403,{message:"Cannot delete the control plane"});if(!(await P.fetchAll(u=>s.env.data.userOrganizations.listUserOrganizations(c,d.sub,u),"organizations")).some(u=>u.name===a))throw new C(403,{message:"Access denied to this tenant"})}if(!await s.env.data.tenants.get(a))throw new C(404,{message:"Tenant not found"});const r={adapters:s.env.data,ctx:s};return(i=e.tenants)!=null&&i.beforeDelete&&await e.tenants.beforeDelete(r,a),await s.env.data.tenants.remove(a),(m=e.tenants)!=null&&m.afterDelete&&await e.tenants.afterDelete(r,a),s.body(null,204)}),n}function oe(t){const e=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:n,type:s}of e){const a=t.match(n);if(a&&a[1])return{type:s,id:a[1]}}return null}async function ie(t,e,n){try{switch(n.type){case"resource_server":{const s=await t.resourceServers.get(e,n.id);return(s==null?void 0:s.is_system)===!0}case"role":{const s=await t.roles.get(e,n.id);return(s==null?void 0:s.is_system)===!0}case"connection":{const s=await t.connections.get(e,n.id);return(s==null?void 0:s.is_system)===!0}default:return!1}}catch{return!1}}function ce(t){return{resource_server:"resource server",role:"role",connection:"connection"}[t]}function le(){return async(t,e)=>{if(!["PATCH","PUT","DELETE"].includes(t.req.method))return e();const n=oe(t.req.path);if(!n)return e();const s=t.var.tenant_id||t.req.header("x-tenant-id")||t.req.header("tenant-id");if(!s)return e();if(await ie(t.env.data,s,n))throw new C(403,{message:`This ${ce(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return e()}}function F(t,e){const{controlPlaneTenantId:n,controlPlaneClientId:s}=e;return{...t,legacyClients:{...t.legacyClients,get:async a=>{var p;const o=await t.legacyClients.get(a);if(!o)return null;const r=s?await t.legacyClients.get(s):void 0,i=await t.connections.list(o.tenant.id),m=n?await t.connections.list(n):{connections:[]},d=i.connections.map(l=>{var g;const u=(g=m.connections)==null?void 0:g.find(w=>w.name===l.name);if(!(u!=null&&u.options))return l;const f=I.connectionSchema.parse({...u||{},...l});return f.options=I.connectionOptionsSchema.parse({...u.options||{},...l.options}),f}).filter(l=>l),c={...(r==null?void 0:r.tenant)||{},...o.tenant};return!o.tenant.audience&&((p=r==null?void 0:r.tenant)!=null&&p.audience)&&(c.audience=r.tenant.audience),{...o,web_origins:[...(r==null?void 0:r.web_origins)||[],...o.web_origins||[]],allowed_logout_urls:[...(r==null?void 0:r.allowed_logout_urls)||[],...o.allowed_logout_urls||[]],callbacks:[...(r==null?void 0:r.callbacks)||[],...o.callbacks||[]],connections:d,tenant:c}}},connections:{...t.connections,get:async(a,o)=>{const r=await t.connections.get(a,o);if(!r||!n)return r;const i=await t.connections.get(n,o);if(!i)return r;const m=I.connectionSchema.parse({...i,...r});return m.options=I.connectionOptionsSchema.parse({...i.options||{},...r.options}),m},list:async(a,o)=>{const r=await t.connections.list(a,o);if(!n||a===n)return r;const i=await t.connections.list(n),m=r.connections.map(d=>{var l;const c=(l=i.connections)==null?void 0:l.find(u=>u.name===d.name);if(!(c!=null&&c.options))return d;const p=I.connectionSchema.parse({...c,...d});return p.options=I.connectionOptionsSchema.parse({...c.options||{},...d.options}),p});return{...r,connections:m}}}}}function V(t,e){return F(t,e)}const de=F,ue=V;function W(t){return async(e,n)=>{if(!t.accessControl)return n();const s=e.var.tenant_id,a=e.var.organization_id;if(!s)throw new C(400,{message:"Tenant ID not found in request"});if(!G(a,s,t.accessControl.controlPlaneTenantId))throw new C(403,{message:`Access denied to tenant ${s}`});return n()}}function Q(t){return async(e,n)=>{if(!t.subdomainRouting)return n();const{baseDomain:s,reservedSubdomains:a=[],resolveSubdomain:o}=t.subdomainRouting,r=e.req.header("host")||"";let i=null;if(r.endsWith(s)){const d=r.slice(0,-(s.length+1));d&&!d.includes(".")&&(i=d)}if(i&&a.includes(i)&&(i=null),!i)return t.accessControl&&e.set("tenant_id",t.accessControl.controlPlaneTenantId),n();let m=null;if(o)m=await o(i);else if(t.subdomainRouting.useOrganizations!==!1&&t.accessControl)try{const d=await e.env.data.organizations.get(t.accessControl.controlPlaneTenantId,i);d&&(m=d.id)}catch{}if(!m)throw new C(404,{message:`Tenant not found for subdomain: ${i}`});return e.set("tenant_id",m),n()}}function J(t){return async(e,n)=>{if(!t.databaseIsolation)return n();const s=e.var.tenant_id;if(!s)throw new C(400,{message:"Tenant ID not found in request"});try{const a=await t.databaseIsolation.getAdapters(s);e.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${s}:`,a),new C(500,{message:"Failed to resolve tenant database"})}return n()}}function E(t){const e=Q(t),n=W(t),s=J(t);return async(a,o)=>(await e(a,async()=>{}),await n(a,async()=>{}),await s(a,async()=>{}),o())}function me(t){const e=$(t);return{name:"multi-tenancy",middleware:E(t),hooks:e,routes:[{path:"/management",handler:D(t,e)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),t.accessControl&&console.log(` - Access control enabled (control plane: ${t.accessControl.controlPlaneTenantId})`),t.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${t.subdomainRouting.baseDomain})`),t.databaseIsolation&&console.log(" - Database isolation enabled")}}}function $(t){const e=t.accessControl?B(t.accessControl):{},n=t.databaseIsolation?K(t.databaseIsolation):{},s=L(t);return{...e,...n,tenants:s}}function X(t){const e=new ee.Hono,n=$(t);return e.route("/tenants",D(t,n)),e}function pe(t){return{hooks:$(t),middleware:E(t),app:X(t),config:t}}exports.createAccessControlHooks=B;exports.createAccessControlMiddleware=W;exports.createDatabaseHooks=K;exports.createDatabaseMiddleware=J;exports.createMultiTenancy=X;exports.createMultiTenancyHooks=$;exports.createMultiTenancyMiddleware=E;exports.createMultiTenancyPlugin=me;exports.createProtectSyncedMiddleware=le;exports.createProvisioningHooks=L;exports.createRuntimeFallbackAdapter=F;exports.createSettingsInheritanceAdapter=de;exports.createSubdomainMiddleware=Q;exports.createSyncHooks=re;exports.createTenantsOpenAPIRouter=D;exports.setupMultiTenancy=pe;exports.validateTenantAccess=G;exports.withRuntimeFallback=V;exports.withSettingsInheritance=ue;
1
+ "use strict";var ee=Object.defineProperty;var te=(n,e,t)=>e in n?ee(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var k=(n,e,t)=>te(n,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ne=require("hono"),P=require("authhero"),C=require("@hono/zod-openapi"),I=require("@authhero/adapter-interfaces");function U(n){const{controlPlaneTenantId:e,requireOrganizationMatch:t=!0}=n;return{async onTenantAccessValidation(s,a){if(a===e)return!0;if(t){const i=s.var.org_name,o=s.var.organization_id,r=i||o;return r?r===a:!1}return!0}}}function B(n,e,t,s){if(e===t)return!0;const a=s||n;return a?a===e:!1}function K(n){return{async resolveDataAdapters(e){try{return await n.getAdapters(e)}catch(t){console.error(`Failed to resolve data adapters for tenant ${e}:`,t);return}}}}function L(n){return{async beforeCreate(e,t){return!t.audience&&t.id?{...t,audience:P.getTenantAudience(t.id)}:t},async afterCreate(e,t){const{accessControl:s,databaseIsolation:a}=n;s&&e.ctx&&await se(e,t,s),a!=null&&a.onProvision&&await a.onProvision(t.id)},async beforeDelete(e,t){const{accessControl:s,databaseIsolation:a}=n;if(s)try{const o=(await e.adapters.organizations.list(s.controlPlaneTenantId)).organizations.find(r=>r.name===t);o&&await e.adapters.organizations.remove(s.controlPlaneTenantId,o.id)}catch(i){console.warn(`Failed to remove organization for tenant ${t}:`,i)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(t)}catch(i){console.warn(`Failed to deprovision database for tenant ${t}:`,i)}}}}async function se(n,e,t){const{controlPlaneTenantId:s,defaultPermissions:a,defaultRoles:i,issuer:o,adminRoleName:r="Tenant Admin",adminRoleDescription:m="Full access to all tenant management operations",addCreatorToOrganization:d=!0}=t,c=await n.adapters.organizations.create(s,{name:e.id,display_name:e.friendly_name||e.id});let p;if(o&&(p=await re(n,s,r,m)),d&&n.ctx){const l=n.ctx.var.user;if(l!=null&&l.sub&&!await ae(n,s,l.sub))try{await n.adapters.userOrganizations.create(s,{user_id:l.sub,organization_id:c.id}),p&&await n.adapters.userRoles.create(s,l.sub,p,c.id)}catch(w){console.warn(`Failed to add creator ${l.sub} to organization ${c.id}:`,w)}}i&&i.length>0&&console.log(`Would assign roles ${i.join(", ")} to organization ${c.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${c.id}`)}async function ae(n,e,t){const s=await n.adapters.userRoles.list(e,t,void 0,"");for(const a of s)if((await n.adapters.rolePermissions.list(e,a.id,{per_page:1e3})).some(r=>r.permission_name==="admin:organizations"))return!0;return!1}async function re(n,e,t,s){const i=(await n.adapters.roles.list(e,{})).roles.find(d=>d.name===t);if(i)return i.id;const o=await n.adapters.roles.create(e,{name:t,description:s}),r=P.MANAGEMENT_API_AUDIENCE,m=P.MANAGEMENT_API_SCOPES.map(d=>({role_id:o.id,resource_server_identifier:r,permission_name:d.value}));return await n.adapters.rolePermissions.assign(e,o.id,m),o.id}const oe=["client_id","client_secret","app_secret","kid","team_id","twilio_sid","twilio_token"];function q(n,e,t=()=>!0){const{controlPlaneTenantId:s,getChildTenantIds:a,getAdapters:i}=n,o=new Map;async function r(c,p,l){return(await e(c).list(p,{q:`name:${l}`,per_page:1}))[0]??null}async function m(c){const p=await a(),l=e(await i(s));await Promise.all(p.map(async u=>{try{const w=await i(u),g=e(w),h={...l.transform(c),is_system:!0},y=await r(w,u,c.name),_=y?g.getId(y):void 0;if(y&&_){const b=g.preserveOnUpdate?g.preserveOnUpdate(y,h):h;await g.update(u,_,b)}else await g.create(u,h)}catch(w){console.error(`Failed to sync ${l.listKey} "${c.name}" to tenant "${u}":`,w)}}))}async function d(c){const p=await a();await Promise.all(p.map(async l=>{try{const u=await i(l),w=e(u),g=await r(u,l,c),f=g?w.getId(g):void 0;g&&f&&await w.remove(l,f)}catch(u){console.error(`Failed to delete entity "${c}" from tenant "${l}":`,u)}}))}return{afterCreate:async(c,p)=>{c.tenantId===s&&t(p)&&await m(p)},afterUpdate:async(c,p,l)=>{c.tenantId===s&&t(l)&&await m(l)},beforeDelete:async(c,p)=>{if(c.tenantId!==s)return;const u=await e(c.adapters).get(c.tenantId,p);u&&t(u)&&o.set(p,u)},afterDelete:async(c,p)=>{if(c.tenantId!==s)return;const l=o.get(p);l&&(o.delete(p),await d(l.name))}}}function D(n,e,t=()=>!0){const{controlPlaneTenantId:s,getControlPlaneAdapters:a,getAdapters:i}=n;return{async afterCreate(o,r){if(r.id!==s)try{const m=await a(),d=await i(r.id),c=e(m),p=e(d),l=await P.fetchAll(u=>c.listPaginated(s,u),c.listKey,{cursorField:"id",pageSize:100});await Promise.all(l.filter(u=>t(u)).map(async u=>{try{const w=c.transform(u);await p.create(r.id,{...w,is_system:!0})}catch(w){console.error(`Failed to sync entity to new tenant "${r.id}":`,w)}}))}catch(m){console.error(`Failed to sync entities to new tenant "${r.id}":`,m)}}}}const N=n=>({list:async(e,t)=>(await n.resourceServers.list(e,t)).resource_servers,listPaginated:(e,t)=>n.resourceServers.list(e,t),get:(e,t)=>n.resourceServers.get(e,t),create:(e,t)=>n.resourceServers.create(e,t),update:(e,t,s)=>n.resourceServers.update(e,t,s),remove:(e,t)=>n.resourceServers.remove(e,t),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=n=>({list:async(e,t)=>(await n.roles.list(e,t)).roles,listPaginated:(e,t)=>n.roles.list(e,t),get:(e,t)=>n.roles.get(e,t),create:(e,t)=>n.roles.create(e,t),update:(e,t,s)=>n.roles.update(e,t,s),remove:(e,t)=>n.roles.remove(e,t),listKey:"roles",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,description:e.description})}),G=n=>({list:async(e,t)=>(await n.connections.list(e,t)).connections,listPaginated:(e,t)=>n.connections.list(e,t),get:(e,t)=>n.connections.get(e,t),create:(e,t)=>n.connections.create(e,t),update:(e,t,s)=>n.connections.update(e,t,s),remove:(e,t)=>n.connections.remove(e,t),listKey:"connections",getId:e=>e.id,transform:e=>{const t=e.options?{...e.options}:{};for(const s of oe)delete t[s];return{id:e.id,name:e.name,display_name:e.display_name,strategy:e.strategy,options:t,response_type:e.response_type,response_mode:e.response_mode,is_domain_connection:e.is_domain_connection,show_as_button:e.show_as_button,metadata:e.metadata}},preserveOnUpdate:(e,t)=>{const s=e.options||{};return{...t,options:{...t.options,client_id:s.client_id,client_secret:s.client_secret,app_secret:s.app_secret,kid:s.kid,team_id:s.team_id,twilio_sid:s.twilio_sid,twilio_token:s.twilio_token}}}});function V(n){const{sync:e={},filters:t={}}=n,s=e.resourceServers??!0,a=e.roles??!0,i=e.connections??!0,o=s?q(n,N,t.resourceServers):void 0,r=a?q(n,H,t.roles):void 0,m=i?q(n,G,t.connections):void 0,d=s?D(n,N,t.resourceServers):void 0,c=a?D(n,H,t.roles):void 0,p=i?D(n,G,t.connections):void 0,l=a?{async afterCreate(g,f){var h;if(f.id!==n.controlPlaneTenantId){await((h=c==null?void 0:c.afterCreate)==null?void 0:h.call(c,g,f));try{const y=await n.getControlPlaneAdapters(),_=await n.getAdapters(f.id),b=await P.fetchAll(v=>y.roles.list(n.controlPlaneTenantId,v),"roles",{cursorField:"id",pageSize:100}),z=new Map;for(const v of b.filter(T=>{var A;return((A=t.roles)==null?void 0:A.call(t,T))??!0})){const T=await u(_,f.id,v.name);T&&z.set(v.name,T.id)}for(const v of b.filter(T=>{var A;return((A=t.roles)==null?void 0:A.call(t,T))??!0})){const T=z.get(v.name);if(T)try{const A=await y.rolePermissions.list(n.controlPlaneTenantId,v.id,{});A.length>0&&await _.rolePermissions.assign(f.id,T,A.map(M=>({role_id:T,resource_server_identifier:M.resource_server_identifier,permission_name:M.permission_name})))}catch(A){console.error(`Failed to sync permissions for role "${v.name}" to tenant "${f.id}":`,A)}}}catch(y){console.error(`Failed to sync role permissions to tenant "${f.id}":`,y)}}}}:void 0;async function u(g,f,h){return(await g.roles.list(f,{q:`name:${h}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:o,roles:r,connections:m},tenantHooks:{async afterCreate(g,f){const h=[d==null?void 0:d.afterCreate,(l==null?void 0:l.afterCreate)??(c==null?void 0:c.afterCreate),p==null?void 0:p.afterCreate],y=[];for(const _ of h)if(_)try{await _(g,f)}catch(b){y.push(b instanceof Error?b:new Error(String(b)))}if(y.length===1)throw y[0];if(y.length>1)throw new AggregateError(y,y.map(_=>_.message).join("; "))}}}}var S=class extends Error{constructor(e=500,t){super(t==null?void 0:t.message,{cause:t==null?void 0:t.cause});k(this,"res");k(this,"status");this.res=t==null?void 0: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 R(n,e){const t=new C.OpenAPIHono;return t.openapi(C.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:I.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:C.z.object({tenants:C.z.array(I.tenantSchema),start:C.z.number().optional(),limit:C.z.number().optional(),length:C.z.number().optional()})}},description:"List of tenants"}}}),async s=>{var u,w,g,f;const a=s.req.valid("query"),{page:i,per_page:o,include_totals:r,q:m}=a,d=s.var.user,c=(d==null?void 0:d.permissions)||[];if(c.includes("auth:read")||c.includes("admin:organizations")){const h=await s.env.data.tenants.list({page:i,per_page:o,include_totals:r,q:m});return r?s.json({tenants:h.tenants,start:((u=h.totals)==null?void 0:u.start)??0,limit:((w=h.totals)==null?void 0:w.limit)??o,length:h.tenants.length}):s.json({tenants:h.tenants})}if(n.accessControl&&(d!=null&&d.sub)){const h=n.accessControl.controlPlaneTenantId,_=(await P.fetchAll(O=>s.env.data.userOrganizations.listUserOrganizations(h,d.sub,O),"organizations")).map(O=>O.name);if(_.length===0)return r?s.json({tenants:[],start:0,limit:o??50,length:0}):s.json({tenants:[]});const b=_.length,z=i??0,v=o??50,T=z*v,A=_.slice(T,T+v);if(A.length===0)return r?s.json({tenants:[],start:T,limit:v,length:b}):s.json({tenants:[]});const M=A.map(O=>`id:${O}`).join(" OR "),x=m?`(${M}) AND (${m})`:M,E=await s.env.data.tenants.list({q:x,per_page:v,include_totals:!1});return r?s.json({tenants:E.tenants,start:T,limit:v,length:b}):s.json({tenants:E.tenants})}const l=await s.env.data.tenants.list({page:i,per_page:o,include_totals:r,q:m});return r?s.json({tenants:l.tenants,start:((g=l.totals)==null?void 0:g.start)??0,limit:((f=l.totals)==null?void 0:f.limit)??o,length:l.tenants.length}):s.json({tenants:l.tenants})}),t.openapi(C.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:I.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:I.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async s=>{var m,d;const a=s.var.user;if(!(a!=null&&a.sub))throw new S(401,{message:"Authentication required to create tenants"});let i=s.req.valid("json");const o={adapters:s.env.data,ctx:s};(m=e.tenants)!=null&&m.beforeCreate&&(i=await e.tenants.beforeCreate(o,i));const r=await s.env.data.tenants.create(i);return(d=e.tenants)!=null&&d.afterCreate&&await e.tenants.afterCreate(o,r),s.json(r,201)}),t.openapi(C.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:C.z.object({id:C.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 s=>{var r,m;const{id:a}=s.req.valid("param");if(n.accessControl){const d=s.var.user,c=n.accessControl.controlPlaneTenantId;if(!(d!=null&&d.sub))throw new S(401,{message:"Authentication required"});if(a===c)throw new S(403,{message:"Cannot delete the control plane"});if(!(await P.fetchAll(u=>s.env.data.userOrganizations.listUserOrganizations(c,d.sub,u),"organizations")).some(u=>u.name===a))throw new S(403,{message:"Access denied to this tenant"})}if(!await s.env.data.tenants.get(a))throw new S(404,{message:"Tenant not found"});const o={adapters:s.env.data,ctx:s};return(r=e.tenants)!=null&&r.beforeDelete&&await e.tenants.beforeDelete(o,a),await s.env.data.tenants.remove(a),(m=e.tenants)!=null&&m.afterDelete&&await e.tenants.afterDelete(o,a),s.body(null,204)}),t}function ie(n){const e=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:t,type:s}of e){const a=n.match(t);if(a&&a[1])return{type:s,id:a[1]}}return null}async function ce(n,e,t){try{switch(t.type){case"resource_server":{const s=await n.resourceServers.get(e,t.id);return(s==null?void 0:s.is_system)===!0}case"role":{const s=await n.roles.get(e,t.id);return(s==null?void 0:s.is_system)===!0}case"connection":{const s=await n.connections.get(e,t.id);return(s==null?void 0:s.is_system)===!0}default:return!1}}catch{return!1}}function le(n){return{resource_server:"resource server",role:"role",connection:"connection"}[n]}function W(){return async(n,e)=>{if(!["PATCH","PUT","DELETE"].includes(n.req.method))return e();const t=ie(n.req.path);if(!t)return e();const s=n.var.tenant_id||n.req.header("x-tenant-id")||n.req.header("tenant-id");if(!s)return e();if(await ce(n.env.data,s,t))throw new S(403,{message:`This ${le(t.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return e()}}function F(n,e){const{controlPlaneTenantId:t,controlPlaneClientId:s}=e;return{...n,legacyClients:{...n.legacyClients,get:async a=>{var p;const i=await n.legacyClients.get(a);if(!i)return null;const o=s?await n.legacyClients.get(s):void 0,r=await n.connections.list(i.tenant.id),m=t?await n.connections.list(t):{connections:[]},d=r.connections.map(l=>{var g;const u=(g=m.connections)==null?void 0:g.find(f=>f.name===l.name);if(!(u!=null&&u.options))return l;const w=I.connectionSchema.parse({...u||{},...l});return w.options=I.connectionOptionsSchema.parse({...u.options||{},...l.options}),w}).filter(l=>l),c={...(o==null?void 0:o.tenant)||{},...i.tenant};return!i.tenant.audience&&((p=o==null?void 0:o.tenant)!=null&&p.audience)&&(c.audience=o.tenant.audience),{...i,web_origins:[...(o==null?void 0:o.web_origins)||[],...i.web_origins||[]],allowed_logout_urls:[...(o==null?void 0:o.allowed_logout_urls)||[],...i.allowed_logout_urls||[]],callbacks:[...(o==null?void 0:o.callbacks)||[],...i.callbacks||[]],connections:d,tenant:c}}},connections:{...n.connections,get:async(a,i)=>{const o=await n.connections.get(a,i);if(!o||!t)return o;const r=await n.connections.get(t,i);if(!r)return o;const m=I.connectionSchema.parse({...r,...o});return m.options=I.connectionOptionsSchema.parse({...r.options||{},...o.options}),m},list:async(a,i)=>{const o=await n.connections.list(a,i);if(!t||a===t)return o;const r=await n.connections.list(t),m=o.connections.map(d=>{var l;const c=(l=r.connections)==null?void 0:l.find(u=>u.name===d.name);if(!(c!=null&&c.options))return d;const p=I.connectionSchema.parse({...c,...d});return p.options=I.connectionOptionsSchema.parse({...c.options||{},...d.options}),p});return{...o,connections:m}}}}}function Q(n,e){return F(n,e)}const de=F,ue=Q;function J(n){return async(e,t)=>{if(!n.accessControl)return t();const s=e.var.tenant_id,a=e.var.organization_id;if(!s)throw new S(400,{message:"Tenant ID not found in request"});if(!B(a,s,n.accessControl.controlPlaneTenantId))throw new S(403,{message:`Access denied to tenant ${s}`});return t()}}function X(n){return async(e,t)=>{if(!n.subdomainRouting)return t();const{baseDomain:s,reservedSubdomains:a=[],resolveSubdomain:i}=n.subdomainRouting,o=e.req.header("host")||"";let r=null;if(o.endsWith(s)){const d=o.slice(0,-(s.length+1));d&&!d.includes(".")&&(r=d)}if(r&&a.includes(r)&&(r=null),!r)return n.accessControl&&e.set("tenant_id",n.accessControl.controlPlaneTenantId),t();let m=null;if(i)m=await i(r);else if(n.subdomainRouting.useOrganizations!==!1&&n.accessControl)try{const d=await e.env.data.organizations.get(n.accessControl.controlPlaneTenantId,r);d&&(m=d.id)}catch{}if(!m)throw new S(404,{message:`Tenant not found for subdomain: ${r}`});return e.set("tenant_id",m),t()}}function Y(n){return async(e,t)=>{if(!n.databaseIsolation)return t();const s=e.var.tenant_id;if(!s)throw new S(400,{message:"Tenant ID not found in request"});try{const a=await n.databaseIsolation.getAdapters(s);e.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${s}:`,a),new S(500,{message:"Failed to resolve tenant database"})}return t()}}function j(n){const e=X(n),t=J(n),s=Y(n);return async(a,i)=>(await e(a,async()=>{}),await t(a,async()=>{}),await s(a,async()=>{}),i())}function me(n){const{dataAdapter:e,controlPlaneTenantId:t="control_plane",sync:s={resourceServers:!0,roles:!0,connections:!0},defaultPermissions:a=["tenant:admin"],requireOrganizationMatch:i=!1,managementApiExtensions:o=[],entityHooks:r,getChildTenantIds:m,getAdapters:d,...c}=n,p=s!==!1,l=p?{resourceServers:s.resourceServers??!0,roles:s.roles??!0,connections:s.connections??!0}:{resourceServers:!1,roles:!1,connections:!1},g={controlPlaneTenantId:t,getChildTenantIds:m??(async()=>(await P.fetchAll(v=>e.tenants.list(v),"tenants",{cursorField:"id",pageSize:100})).filter(v=>v.id!==t).map(v=>v.id)),getAdapters:d??(async()=>e),getControlPlaneAdapters:async()=>e,sync:l},{entityHooks:f,tenantHooks:h}=V(g),y={resourceServers:[f.resourceServers,...(r==null?void 0:r.resourceServers)??[]],roles:[f.roles,...(r==null?void 0:r.roles)??[]],connections:[f.connections,...(r==null?void 0:r.connections)??[]],tenants:(r==null?void 0:r.tenants)??[],rolePermissions:(r==null?void 0:r.rolePermissions)??[]},_=R({accessControl:{controlPlaneTenantId:t,requireOrganizationMatch:i,defaultPermissions:a}},{tenants:h}),{app:b}=P.init({dataAdapter:e,...c,entityHooks:y,managementApiExtensions:[...o,{path:"/tenants",router:_}]});return p&&b.use("/api/v2/*",W()),{app:b,controlPlaneTenantId:t}}function pe(n){const e=$(n);return{name:"multi-tenancy",middleware:j(n),hooks:e,routes:[{path:"/management",handler:R(n,e)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),n.accessControl&&console.log(` - Access control enabled (control plane: ${n.accessControl.controlPlaneTenantId})`),n.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${n.subdomainRouting.baseDomain})`),n.databaseIsolation&&console.log(" - Database isolation enabled")}}}function $(n){const e=n.accessControl?U(n.accessControl):{},t=n.databaseIsolation?K(n.databaseIsolation):{},s=L(n);return{...e,...t,tenants:s}}function Z(n){const e=new ne.Hono,t=$(n);return e.route("/tenants",R(n,t)),e}function fe(n){return{hooks:$(n),middleware:j(n),app:Z(n),config:n}}exports.createAccessControlHooks=U;exports.createAccessControlMiddleware=J;exports.createDatabaseHooks=K;exports.createDatabaseMiddleware=Y;exports.createMultiTenancy=Z;exports.createMultiTenancyHooks=$;exports.createMultiTenancyMiddleware=j;exports.createMultiTenancyPlugin=pe;exports.createProtectSyncedMiddleware=W;exports.createProvisioningHooks=L;exports.createRuntimeFallbackAdapter=F;exports.createSettingsInheritanceAdapter=de;exports.createSubdomainMiddleware=X;exports.createSyncHooks=V;exports.createTenantsOpenAPIRouter=R;exports.initMultiTenant=me;exports.setupMultiTenancy=fe;exports.validateTenantAccess=B;exports.withRuntimeFallback=Q;exports.withSettingsInheritance=ue;