@authhero/multi-tenancy 13.9.0 → 13.10.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.
@@ -1 +1 @@
1
- "use strict";var H=Object.defineProperty;var k=(t,a,o)=>a in t?H(t,a,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[a]=o;var $=(t,a,o)=>k(t,typeof a!="symbol"?a+"":a,o);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("hono"),q=require("authhero"),x=require("zod"),P=require("@authhero/adapter-interfaces"),b=require("@hono/zod-openapi");function U(t){const{controlPlaneTenantId:a,requireOrganizationMatch:o=!0}=t;return{async onTenantAccessValidation(e,n){if(n===a)return!0;if(o){const r=e.var.org_name,d=e.var.organization_id,i=r||d;return i?i===n:!1}return!0}}}function N(t,a,o,e){if(a===o)return!0;const n=e||t;return n?n===a:!1}function E(t){return{async resolveDataAdapters(a){try{return await t.getAdapters(a)}catch(o){console.error(`Failed to resolve data adapters for tenant ${a}:`,o);return}}}}function K(t){return{async beforeCreate(a,o){return!o.audience&&o.id?{...o,audience:q.getTenantAudience(o.id)}:o},async afterCreate(a,o){const{accessControl:e,databaseIsolation:n,settingsInheritance:r}=t;e&&a.ctx&&await ee(a,o,e),n!=null&&n.onProvision&&await n.onProvision(o.id),(r==null?void 0:r.inheritFromControlPlane)!==!1&&a.ctx&&await ae(a,o,t)},async beforeDelete(a,o){const{accessControl:e,databaseIsolation:n}=t;if(e)try{const d=(await a.adapters.organizations.list(e.controlPlaneTenantId)).organizations.find(i=>i.name===o);d&&await a.adapters.organizations.remove(e.controlPlaneTenantId,d.id)}catch(r){console.warn(`Failed to remove organization for tenant ${o}:`,r)}if(n!=null&&n.onDeprovision)try{await n.onDeprovision(o)}catch(r){console.warn(`Failed to deprovision database for tenant ${o}:`,r)}}}}async function ee(t,a,o){const{controlPlaneTenantId:e,defaultPermissions:n,defaultRoles:r,issuer:d,adminRoleName:i="Tenant Admin",adminRoleDescription:c="Full access to all tenant management operations",addCreatorToOrganization:s=!0}=o,u=await t.adapters.organizations.create(e,{name:a.id,display_name:a.friendly_name||a.id}),f=d?await te(t,e,i,c):null;if(s&&t.ctx){const l=t.ctx.var.user;if(l!=null&&l.sub)try{await t.adapters.userOrganizations.create(e,{user_id:l.sub,organization_id:u.id}),f&&await t.adapters.userRoles.create(e,l.sub,f,u.id)}catch(p){console.warn(`Failed to add creator ${l.sub} to organization ${u.id}:`,p)}}r&&r.length>0&&console.log(`Would assign roles ${r.join(", ")} to organization ${u.id}`),n&&n.length>0&&console.log(`Would grant permissions ${n.join(", ")} to organization ${u.id}`)}async function te(t,a,o,e){const r=(await t.adapters.roles.list(a,{})).roles.find(s=>s.name===o);if(r)return r.id;const d=await t.adapters.roles.create(a,{name:o,description:e}),i=q.MANAGEMENT_API_AUDIENCE,c=q.MANAGEMENT_API_SCOPES.map(s=>({role_id:d.id,resource_server_identifier:i,permission_name:s.value}));return await t.adapters.rolePermissions.assign(a,d.id,c),d.id}async function ae(t,a,o){const{accessControl:e,settingsInheritance:n}=o;if(!e)return;const r=await t.adapters.tenants.get(e.controlPlaneTenantId);if(!r)return;let d={...r};const i=["id","created_at","updated_at","friendly_name","audience","sender_email","sender_name"];for(const c of i)delete d[c];if(n!=null&&n.inheritedKeys){const c={};for(const s of n.inheritedKeys)s in r&&!i.includes(s)&&(c[s]=r[s]);d=c}if(n!=null&&n.excludedKeys)for(const c of n.excludedKeys)delete d[c];n!=null&&n.transformSettings&&(d=n.transformSettings(d,a.id)),Object.keys(d).length>0&&await t.adapters.tenants.update(a.id,d)}async function z(t,a,o={}){const{cursorField:e="id",sortOrder:n="asc",pageSize:r=100,maxItems:d=1e4,q:i}=o,c=[];let s,u=!0;for(;u;){let f=i||"";if(s){const y=`${e}:${n==="asc"?">":"<"}${s}`;f=f?`(${f}) AND ${y}`:y}const l={per_page:r,page:0,sort:{sort_by:e,sort_order:n},...f&&{q:f}},h=(await t(l))[a]||[];if(h.length===0)u=!1;else{c.push(...h);const m=h[h.length-1];if(m&&typeof m=="object"){const y=m[e];y!=null&&(s=String(y))}h.length<r&&(u=!1),d!==-1&&c.length>=d&&(console.warn(`fetchAll: Reached maxItems limit (${d}). There may be more items.`),u=!1)}}return c}function B(t){const{controlPlaneTenantId:a,getChildTenantIds:o,getAdapters:e,shouldSync:n=()=>!0,transformForSync:r}=t;async function d(s,u,f){return(await s.resourceServers.list(u,{q:`identifier:${f}`,per_page:1})).resource_servers[0]??null}async function i(s,u){const f=await o();await Promise.all(f.map(async l=>{try{const p=await e(l),m={...r?r(s,l):{name:s.name,identifier:s.identifier,scopes:s.scopes,signing_alg:s.signing_alg,signing_secret:s.signing_secret,token_lifetime:s.token_lifetime,token_lifetime_for_web:s.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:s.skip_consent_for_verifiable_first_party_clients,allow_offline_access:s.allow_offline_access,verificationKey:s.verificationKey,options:s.options},is_system:!0};if(u==="create"){const y=await d(p,l,s.identifier);y&&y.id?await p.resourceServers.update(l,y.id,m):await p.resourceServers.create(l,m)}else{const y=await d(p,l,s.identifier);y&&y.id&&await p.resourceServers.update(l,y.id,m)}}catch(p){console.error(`Failed to sync resource server "${s.identifier}" to tenant "${l}":`,p)}}))}async function c(s){const u=await o();await Promise.all(u.map(async f=>{try{const l=await e(f),p=await d(l,f,s);p&&p.id&&await l.resourceServers.remove(f,p.id)}catch(l){console.error(`Failed to delete resource server "${s}" from tenant "${f}":`,l)}}))}return{afterCreate:async(s,u)=>{s.tenantId===a&&n(u)&&await i(u,"create")},afterUpdate:async(s,u,f)=>{s.tenantId===a&&n(f)&&await i(f,"update")},afterDelete:async(s,u)=>{s.tenantId===a&&await c(u)}}}function W(t){const{controlPlaneTenantId:a,getControlPlaneAdapters:o,getAdapters:e,shouldSync:n=()=>!0,transformForSync:r}=t;return{async afterCreate(d,i){if(i.id!==a)try{const c=await o(),s=await e(i.id),u=await z(f=>c.resourceServers.list(a,f),"resource_servers",{cursorField:"id",pageSize:100});await Promise.all(u.filter(f=>n(f)).map(async f=>{const l=f;try{const p=r?r(l,i.id):{name:l.name,identifier:l.identifier,scopes:l.scopes,signing_alg:l.signing_alg,signing_secret:l.signing_secret,token_lifetime:l.token_lifetime,token_lifetime_for_web:l.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:l.skip_consent_for_verifiable_first_party_clients,allow_offline_access:l.allow_offline_access,verificationKey:l.verificationKey,options:l.options};await s.resourceServers.create(i.id,{...p,is_system:!0})}catch(p){console.error(`Failed to sync resource server "${l.identifier}" to new tenant "${i.id}":`,p)}}))}catch(c){console.error(`Failed to sync resource servers to new tenant "${i.id}":`,c)}}}}function G(t){const{controlPlaneTenantId:a,getChildTenantIds:o,getAdapters:e,shouldSync:n=()=>!0,transformForSync:r}=t;async function d(c,s,u){return(await c.roles.list(s,{q:`name:${u}`,per_page:1})).roles[0]??null}async function i(c,s){const u=await o();await Promise.all(u.map(async f=>{try{const l=await e(f),h={...r?r(c,f):{name:c.name,description:c.description},is_system:!0};if(s==="create"){const m=await d(l,f,c.name);m&&m.id?await l.roles.update(f,m.id,h):await l.roles.create(f,h)}else{const m=await d(l,f,c.name);m&&m.id&&await l.roles.update(f,m.id,h)}}catch(l){console.error(`Failed to sync role "${c.name}" to tenant "${f}":`,l)}}))}return{afterCreate:async(c,s)=>{c.tenantId===a&&n(s)&&await i(s,"create")},afterUpdate:async(c,s,u)=>{c.tenantId===a&&n(u)&&await i(u,"update")},afterDelete:async(c,s)=>{c.tenantId===a&&console.warn(`Role ${s} was deleted from control plane. Child tenant roles with matching names should be deleted manually or implement role name tracking.`)}}}function Q(t){const{controlPlaneTenantId:a,getControlPlaneAdapters:o,getAdapters:e,shouldSync:n=()=>!0,transformForSync:r,syncPermissions:d=!0}=t;return{async afterCreate(i,c){if(c.id!==a)try{const s=await o(),u=await e(c.id),f=await z(p=>s.roles.list(a,p),"roles",{cursorField:"id",pageSize:100}),l=new Map;if(await Promise.all(f.filter(p=>n(p)).map(async p=>{const h=p;try{const m=r?r(h,c.id):{name:h.name,description:h.description},y=await u.roles.create(c.id,{...m,is_system:!0});l.set(h.id,y.id)}catch(m){console.error(`Failed to sync role "${h.name}" to new tenant "${c.id}":`,m)}})),d)for(const[p,h]of l)try{const m=await s.rolePermissions.list(a,p,{});m.length>0&&await u.rolePermissions.assign(c.id,h,m.map(y=>({role_id:h,resource_server_identifier:y.resource_server_identifier,permission_name:y.permission_name})))}catch(m){console.error(`Failed to sync permissions for role to new tenant "${c.id}":`,m)}}catch(s){console.error(`Failed to sync roles to new tenant "${c.id}":`,s)}}}}var g=class extends Error{constructor(a=500,o){super(o==null?void 0:o.message,{cause:o==null?void 0:o.cause});$(this,"res");$(this,"status");this.res=o==null?void 0:o.res,this.status=a}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,a){const o=new j.Hono;return o.get("/",async e=>{var f,l;const n=P.auth0QuerySchema.parse(e.req.query()),{page:r,per_page:d,include_totals:i,q:c}=n,s=e.var.user;if(t.accessControl&&(s!=null&&s.sub)){const p=t.accessControl.controlPlaneTenantId,m=(await e.env.data.userOrganizations.listUserOrganizations(p,s.sub,{})).organizations.map(C=>C.name),y=await e.env.data.tenants.list({page:r,per_page:d,include_totals:i,q:c}),S=y.tenants.filter(C=>m.includes(C.id));return i?e.json({tenants:S,start:((f=y.totals)==null?void 0:f.start)??0,limit:((l=y.totals)==null?void 0:l.limit)??d,length:S.length}):e.json(S)}const u=await e.env.data.tenants.list({page:r,per_page:d,include_totals:i,q:c});return i?e.json(u):e.json(u.tenants)}),o.get("/:id",async e=>{const n=e.req.param("id");if(t.accessControl){const d=e.var.user,i=t.accessControl.controlPlaneTenantId;if(n!==i){if(!(d!=null&&d.sub))throw new g(401,{message:"Authentication required"});if(!(await e.env.data.userOrganizations.listUserOrganizations(i,d.sub,{})).organizations.some(u=>u.name===n))throw new g(403,{message:"Access denied to this tenant"})}}const r=await e.env.data.tenants.get(n);if(!r)throw new g(404,{message:"Tenant not found"});return e.json(r)}),o.post("/",async e=>{var n,r,d;try{const i=e.var.user;if(!(i!=null&&i.sub))throw new g(401,{message:"Authentication required to create tenants"});let c=P.tenantInsertSchema.parse(await e.req.json());const s={adapters:e.env.data,ctx:e};(n=a.tenants)!=null&&n.beforeCreate&&(c=await a.tenants.beforeCreate(s,c));const u=await e.env.data.tenants.create(c);return(r=a.tenants)!=null&&r.afterCreate&&await a.tenants.afterCreate(s,u),e.json(u,201)}catch(i){throw i instanceof x.z.ZodError?new g(400,{message:"Validation error",cause:i}):i instanceof Error&&("code"in i&&i.code==="SQLITE_CONSTRAINT_PRIMARYKEY"||(d=i.message)!=null&&d.includes("UNIQUE constraint failed"))?new g(409,{message:"Tenant with this ID already exists"}):i}}),o.patch("/:id",async e=>{var l,p;const n=e.req.param("id");if(t.accessControl){const h=e.var.user;if(!(h!=null&&h.sub))throw new g(401,{message:"Authentication required to update tenants"});const m=t.accessControl.controlPlaneTenantId;if(n!==m&&!(await e.env.data.userOrganizations.listUserOrganizations(m,h.sub,{})).organizations.some(C=>C.name===n))throw new g(403,{message:"Access denied to update this tenant"})}const r=P.tenantInsertSchema.partial().parse(await e.req.json()),{id:d,...i}=r;if(!await e.env.data.tenants.get(n))throw new g(404,{message:"Tenant not found"});const s={adapters:e.env.data,ctx:e};let u=i;(l=a.tenants)!=null&&l.beforeUpdate&&(u=await a.tenants.beforeUpdate(s,n,i)),await e.env.data.tenants.update(n,u);const f=await e.env.data.tenants.get(n);if(!f)throw new g(404,{message:"Tenant not found after update"});return(p=a.tenants)!=null&&p.afterUpdate&&await a.tenants.afterUpdate(s,f),e.json(f)}),o.delete("/:id",async e=>{var i,c;const n=e.req.param("id");if(t.accessControl&&n===t.accessControl.controlPlaneTenantId)throw new g(400,{message:"Cannot delete the control plane"});if(t.accessControl){const s=e.var.user;if(!(s!=null&&s.sub))throw new g(401,{message:"Authentication required to delete tenants"});const u=t.accessControl.controlPlaneTenantId;if(!(await e.env.data.userOrganizations.listUserOrganizations(u,s.sub,{})).organizations.some(p=>p.name===n))throw new g(403,{message:"Access denied to delete this tenant"})}if(!await e.env.data.tenants.get(n))throw new g(404,{message:"Tenant not found"});const d={adapters:e.env.data,ctx:e};return(i=a.tenants)!=null&&i.beforeDelete&&await a.tenants.beforeDelete(d,n),await e.env.data.tenants.remove(n),(c=a.tenants)!=null&&c.afterDelete&&await a.tenants.afterDelete(d,n),e.body(null,204)}),o}function ne(t,a){const o=new b.OpenAPIHono;return o.openapi(b.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:P.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:b.z.object({tenants:b.z.array(P.tenantSchema),start:b.z.number().optional(),limit:b.z.number().optional(),length:b.z.number().optional()})}},description:"List of tenants"}}}),async e=>{var f,l,p,h;const n=e.req.valid("query"),{page:r,per_page:d,include_totals:i,q:c}=n,s=e.var.user;if(t.accessControl&&(s!=null&&s.sub)){const m=t.accessControl.controlPlaneTenantId,S=(await z(I=>e.env.data.userOrganizations.listUserOrganizations(m,s.sub,I),"organizations")).map(I=>I.name),C=await e.env.data.tenants.list({page:r,per_page:d,include_totals:i,q:c}),O=C.tenants.filter(I=>S.includes(I.id));return i?e.json({tenants:O,start:((f=C.totals)==null?void 0:f.start)??0,limit:((l=C.totals)==null?void 0:l.limit)??d,length:O.length}):e.json({tenants:O})}const u=await e.env.data.tenants.list({page:r,per_page:d,include_totals:i,q:c});return i?e.json({tenants:u.tenants,start:((p=u.totals)==null?void 0:p.start)??0,limit:((h=u.totals)==null?void 0:h.limit)??d,length:u.tenants.length}):e.json({tenants:u.tenants})}),o.openapi(b.createRoute({tags:["tenants"],method:"get",path:"/{id}",request:{params:b.z.object({id:b.z.string()})},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:P.tenantSchema}},description:"Tenant details"},404:{description:"Tenant not found"}}}),async e=>{const{id:n}=e.req.valid("param");if(t.accessControl){const d=e.var.user,i=t.accessControl.controlPlaneTenantId;if(n!==i){if(!(d!=null&&d.sub))throw new g(401,{message:"Authentication required"});if(!(await z(u=>e.env.data.userOrganizations.listUserOrganizations(i,d.sub,u),"organizations")).some(u=>u.name===n))throw new g(403,{message:"Access denied to this tenant"})}}const r=await e.env.data.tenants.get(n);if(!r)throw new g(404,{message:"Tenant not found"});return e.json(r)}),o.openapi(b.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:P.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:P.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"}}}),async e=>{var c,s;const n=e.var.user;if(!(n!=null&&n.sub))throw new g(401,{message:"Authentication required to create tenants"});let r=e.req.valid("json");const d={adapters:e.env.data,ctx:e};(c=a.tenants)!=null&&c.beforeCreate&&(r=await a.tenants.beforeCreate(d,r));const i=await e.env.data.tenants.create(r);return(s=a.tenants)!=null&&s.afterCreate&&await a.tenants.afterCreate(d,i),e.json(i,201)}),o.openapi(b.createRoute({tags:["tenants"],method:"patch",path:"/{id}",request:{params:b.z.object({id:b.z.string()}),body:{content:{"application/json":{schema:b.z.object(P.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:["update:tenants"]}],responses:{200:{content:{"application/json":{schema:P.tenantSchema}},description:"Tenant updated"},403:{description:"Access denied"},404:{description:"Tenant not found"}}}),async e=>{var u,f;const{id:n}=e.req.valid("param");if(t.accessControl){const l=e.var.user,p=t.accessControl.controlPlaneTenantId;if(!(l!=null&&l.sub))throw new g(401,{message:"Authentication required"});if(n!==p&&!(await z(y=>e.env.data.userOrganizations.listUserOrganizations(p,l.sub,y),"organizations")).some(y=>y.name===n))throw new g(403,{message:"Access denied to this tenant"})}if(!await e.env.data.tenants.get(n))throw new g(404,{message:"Tenant not found"});const d=e.req.valid("json"),i={adapters:e.env.data,ctx:e};let c=d;(u=a.tenants)!=null&&u.beforeUpdate&&(c=await a.tenants.beforeUpdate(i,n,d)),await e.env.data.tenants.update(n,c);const s=await e.env.data.tenants.get(n);if(!s)throw new g(500,{message:"Failed to retrieve updated tenant"});return(f=a.tenants)!=null&&f.afterUpdate&&await a.tenants.afterUpdate(i,s),e.json(s)}),o.openapi(b.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:b.z.object({id:b.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 e=>{var i,c;const{id:n}=e.req.valid("param");if(t.accessControl){const s=e.var.user,u=t.accessControl.controlPlaneTenantId;if(!(s!=null&&s.sub))throw new g(401,{message:"Authentication required"});if(n===u)throw new g(403,{message:"Cannot delete the control plane"});if(!(await z(p=>e.env.data.userOrganizations.listUserOrganizations(u,s.sub,p),"organizations")).some(p=>p.name===n))throw new g(403,{message:"Access denied to this tenant"})}if(!await e.env.data.tenants.get(n))throw new g(404,{message:"Tenant not found"});const d={adapters:e.env.data,ctx:e};return(i=a.tenants)!=null&&i.beforeDelete&&await a.tenants.beforeDelete(d,n),await e.env.data.tenants.remove(n),(c=a.tenants)!=null&&c.afterDelete&&await a.tenants.afterDelete(d,n),e.body(null,204)}),o}function se(t){const a=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:o,type:e}of a){const n=t.match(o);if(n&&n[1])return{type:e,id:n[1]}}return null}async function re(t,a,o){try{switch(o.type){case"resource_server":{const e=await t.resourceServers.get(a,o.id);return(e==null?void 0:e.is_system)===!0}case"role":{const e=await t.roles.get(a,o.id);return(e==null?void 0:e.is_system)===!0}case"connection":{const e=await t.connections.get(a,o.id);return(e==null?void 0:e.is_system)===!0}default:return!1}}catch{return!1}}function oe(t){return{resource_server:"resource server",role:"role",connection:"connection"}[t]}function V(){return async(t,a)=>{if(!["PATCH","PUT","DELETE"].includes(t.req.method))return a();const o=se(t.req.path);if(!o)return a();const e=t.var.tenant_id||t.req.header("x-tenant-id")||t.req.header("tenant-id");if(!e)return a();if(await re(t.env.data,e,o))throw new g(403,{message:`This ${oe(o.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return a()}}function L(t){return async(a,o)=>{if(!t.accessControl)return o();const e=a.var.tenant_id,n=a.var.organization_id;if(!e)throw new g(400,{message:"Tenant ID not found in request"});if(!N(n,e,t.accessControl.controlPlaneTenantId))throw new g(403,{message:`Access denied to tenant ${e}`});return o()}}function Y(t){return async(a,o)=>{if(!t.subdomainRouting)return o();const{baseDomain:e,reservedSubdomains:n=[],resolveSubdomain:r}=t.subdomainRouting,d=a.req.header("host")||"";let i=null;if(d.endsWith(e)){const s=d.slice(0,-(e.length+1));s&&!s.includes(".")&&(i=s)}if(i&&n.includes(i)&&(i=null),!i)return t.accessControl&&a.set("tenant_id",t.accessControl.controlPlaneTenantId),o();let c=null;if(r)c=await r(i);else if(t.subdomainRouting.useOrganizations!==!1&&t.accessControl)try{const s=await a.env.data.organizations.get(t.accessControl.controlPlaneTenantId,i);s&&(c=s.id)}catch{}if(!c)throw new g(404,{message:`Tenant not found for subdomain: ${i}`});return a.set("tenant_id",c),o()}}function Z(t){return async(a,o)=>{if(!t.databaseIsolation)return o();const e=a.var.tenant_id;if(!e)throw new g(400,{message:"Tenant ID not found in request"});try{const n=await t.databaseIsolation.getAdapters(e);a.env.data=n}catch(n){throw console.error(`Failed to resolve database for tenant ${e}:`,n),new g(500,{message:"Failed to resolve tenant database"})}return o()}}function F(t){const a=Y(t),o=L(t),e=Z(t);return async(n,r)=>(await a(n,async()=>{}),await o(n,async()=>{}),await e(n,async()=>{}),r())}function ie(t){const a=R(t);return{name:"multi-tenancy",middleware:F(t),hooks:a,routes:[{path:"/management",handler:D(t,a)}],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 R(t){const a=t.accessControl?U(t.accessControl):{},o=t.databaseIsolation?E(t.databaseIsolation):{},e=K(t);return{...a,...o,tenants:e}}function J(t){const a=new j.Hono,o=R(t);return a.route("/tenants",D(t,o)),a}function ce(t){return{hooks:R(t),middleware:F(t),app:J(t),config:t}}function le(t){const{controlPlaneTenantId:a="control_plane",syncResourceServers:o=!0,syncRoles:e=!0,multiTenancy:n,entityHooks:r,...d}=t,i={...n,accessControl:{controlPlaneTenantId:a,requireOrganizationMatch:!1,defaultPermissions:["tenant:admin"],...n==null?void 0:n.accessControl}},c=R(i);let s,u;o&&(s=B({controlPlaneTenantId:a,getChildTenantIds:async()=>(await z(w=>t.dataAdapter.tenants.list(w),"tenants",{cursorField:"id",pageSize:100})).filter(w=>w.id!==a).map(w=>w.id),getAdapters:async T=>t.dataAdapter}),u=W({controlPlaneTenantId:a,getControlPlaneAdapters:async()=>t.dataAdapter,getAdapters:async T=>t.dataAdapter}));let f,l;e&&(f=G({controlPlaneTenantId:a,getChildTenantIds:async()=>(await z(w=>t.dataAdapter.tenants.list(w),"tenants",{cursorField:"id",pageSize:100})).filter(w=>w.id!==a).map(w=>w.id),getAdapters:async T=>t.dataAdapter}),l=Q({controlPlaneTenantId:a,getControlPlaneAdapters:async()=>t.dataAdapter,getAdapters:async T=>t.dataAdapter,syncPermissions:!0}));const p=async(T,w,..._)=>{const v=[];if(T)try{await T(..._)}catch(A){v.push(A instanceof Error?A:new Error(String(A)))}if(w)try{await w(..._)}catch(A){v.push(A instanceof Error?A:new Error(String(A)))}if(v.length===1)throw v[0];if(v.length>1)throw new AggregateError(v,`Multiple hook errors: ${v.map(A=>A.message).join("; ")}`)},h=async(T,...w)=>{const _=[];for(const v of T)if(v)try{await v(...w)}catch(A){_.push(A instanceof Error?A:new Error(String(A)))}if(_.length===1)throw _[0];if(_.length>1)throw new AggregateError(_,`Multiple hook errors: ${_.map(v=>v.message).join("; ")}`)},m={...r,resourceServers:s?{...r==null?void 0:r.resourceServers,afterCreate:async(T,w)=>{var _;await p((_=r==null?void 0:r.resourceServers)==null?void 0:_.afterCreate,s==null?void 0:s.afterCreate,T,w)},afterUpdate:async(T,w,_)=>{var v;await p((v=r==null?void 0:r.resourceServers)==null?void 0:v.afterUpdate,s==null?void 0:s.afterUpdate,T,w,_)},afterDelete:async(T,w)=>{var _;await p((_=r==null?void 0:r.resourceServers)==null?void 0:_.afterDelete,s==null?void 0:s.afterDelete,T,w)}}:r==null?void 0:r.resourceServers,roles:f?{...r==null?void 0:r.roles,afterCreate:async(T,w)=>{var _;await p((_=r==null?void 0:r.roles)==null?void 0:_.afterCreate,f==null?void 0:f.afterCreate,T,w)},afterUpdate:async(T,w,_)=>{var v;await p((v=r==null?void 0:r.roles)==null?void 0:v.afterUpdate,f==null?void 0:f.afterUpdate,T,w,_)},afterDelete:async(T,w)=>{var _;await p((_=r==null?void 0:r.roles)==null?void 0:_.afterDelete,f==null?void 0:f.afterDelete,T,w)}}:r==null?void 0:r.roles,tenants:u||l?{...r==null?void 0:r.tenants,afterCreate:async(T,w)=>{var _;await h([(_=r==null?void 0:r.tenants)==null?void 0:_.afterCreate,u==null?void 0:u.afterCreate,l==null?void 0:l.afterCreate],T,w)}}:r==null?void 0:r.tenants},y={...c,tenants:u||l?{...c.tenants,afterCreate:async(T,w)=>{var _;(_=c.tenants)!=null&&_.afterCreate&&await c.tenants.afterCreate(T,w),await h([u==null?void 0:u.afterCreate,l==null?void 0:l.afterCreate],T,w)}}:c.tenants},S=ne(i,y),C=q.init({...d,entityHooks:m,managementApiExtensions:[...d.managementApiExtensions||[],{path:"/tenants",router:S}]}),{app:O,managementApp:I,...X}=C,M=new j.Hono;return M.use("/api/v2/*",V()),M.route("/",O),{app:M,managementApp:I,...X,multiTenancyConfig:i,multiTenancyHooks:c}}Object.defineProperty(exports,"MANAGEMENT_API_SCOPES",{enumerable:!0,get:()=>q.MANAGEMENT_API_SCOPES});Object.defineProperty(exports,"seed",{enumerable:!0,get:()=>q.seed});exports.createAccessControlHooks=U;exports.createAccessControlMiddleware=L;exports.createDatabaseHooks=E;exports.createDatabaseMiddleware=Z;exports.createMultiTenancy=J;exports.createMultiTenancyHooks=R;exports.createMultiTenancyMiddleware=F;exports.createMultiTenancyPlugin=ie;exports.createProtectSyncedMiddleware=V;exports.createProvisioningHooks=K;exports.createResourceServerSyncHooks=B;exports.createRoleSyncHooks=G;exports.createSubdomainMiddleware=Y;exports.createTenantResourceServerSyncHooks=W;exports.createTenantRoleSyncHooks=Q;exports.createTenantsRouter=D;exports.fetchAll=z;exports.init=le;exports.setupMultiTenancy=ce;exports.validateTenantAccess=N;
1
+ "use strict";var X=Object.defineProperty;var H=(t,a,i)=>a in t?X(t,a,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[a]=i;var M=(t,a,i)=>H(t,typeof a!="symbol"?a+"":a,i);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("hono"),A=require("authhero"),k=require("zod"),z=require("@authhero/adapter-interfaces"),b=require("@hono/zod-openapi");var w=class extends Error{constructor(a=500,i){super(i==null?void 0:i.message,{cause:i==null?void 0:i.cause});M(this,"res");M(this,"status");this.res=i==null?void 0:i.res,this.status=a}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function U(t){const{controlPlaneTenantId:a,requireOrganizationMatch:i=!0}=t;return{async onTenantAccessValidation(e,n){if(n===a)return!0;if(i){const r=e.var.org_name,d=e.var.organization_id,o=r||d;return o?o===n:!1}return!0}}}function F(t,a,i,e){if(a===i)return!0;const n=e||t;return n?n===a:!1}function N(t){return{async resolveDataAdapters(a){try{return await t.getAdapters(a)}catch(i){console.error(`Failed to resolve data adapters for tenant ${a}:`,i);return}}}}function E(t){return{async beforeCreate(a,i){return!i.audience&&i.id?{...i,audience:A.getTenantAudience(i.id)}:i},async afterCreate(a,i){const{accessControl:e,databaseIsolation:n,settingsInheritance:r}=t;e&&a.ctx&&await x(a,i,e),n!=null&&n.onProvision&&await n.onProvision(i.id),(r==null?void 0:r.inheritFromControlPlane)!==!1&&a.ctx&&await te(a,i,t)},async beforeDelete(a,i){const{accessControl:e,databaseIsolation:n}=t;if(e)try{const d=(await a.adapters.organizations.list(e.controlPlaneTenantId)).organizations.find(o=>o.name===i);d&&await a.adapters.organizations.remove(e.controlPlaneTenantId,d.id)}catch(r){console.warn(`Failed to remove organization for tenant ${i}:`,r)}if(n!=null&&n.onDeprovision)try{await n.onDeprovision(i)}catch(r){console.warn(`Failed to deprovision database for tenant ${i}:`,r)}}}}async function x(t,a,i){const{controlPlaneTenantId:e,defaultPermissions:n,defaultRoles:r,issuer:d,adminRoleName:o="Tenant Admin",adminRoleDescription:c="Full access to all tenant management operations",addCreatorToOrganization:s=!0}=i,u=await t.adapters.organizations.create(e,{name:a.id,display_name:a.friendly_name||a.id});let f;if(d&&(f=await ee(t,e,o,c)),s&&t.ctx){const l=t.ctx.var.user;if(l!=null&&l.sub)try{await t.adapters.userOrganizations.create(e,{user_id:l.sub,organization_id:u.id}),f&&await t.adapters.userRoles.create(e,l.sub,f,u.id)}catch(p){console.warn(`Failed to add creator ${l.sub} to organization ${u.id}:`,p)}}r&&r.length>0&&console.log(`Would assign roles ${r.join(", ")} to organization ${u.id}`),n&&n.length>0&&console.log(`Would grant permissions ${n.join(", ")} to organization ${u.id}`)}async function ee(t,a,i,e){const r=(await t.adapters.roles.list(a,{})).roles.find(s=>s.name===i);if(r)return r.id;const d=await t.adapters.roles.create(a,{name:i,description:e}),o=A.MANAGEMENT_API_AUDIENCE,c=A.MANAGEMENT_API_SCOPES.map(s=>({role_id:d.id,resource_server_identifier:o,permission_name:s.value}));return await t.adapters.rolePermissions.assign(a,d.id,c),d.id}async function te(t,a,i){const{accessControl:e,settingsInheritance:n}=i;if(!e)return;const r=await t.adapters.tenants.get(e.controlPlaneTenantId);if(!r)return;let d={...r};const o=["id","created_at","updated_at","friendly_name","audience","sender_email","sender_name"];for(const c of o)delete d[c];if(n!=null&&n.inheritedKeys){const c={};for(const s of n.inheritedKeys)s in r&&!o.includes(s)&&(c[s]=r[s]);d=c}if(n!=null&&n.excludedKeys)for(const c of n.excludedKeys)delete d[c];n!=null&&n.transformSettings&&(d=n.transformSettings(d,a.id)),Object.keys(d).length>0&&await t.adapters.tenants.update(a.id,d)}function K(t){const{controlPlaneTenantId:a,getChildTenantIds:i,getAdapters:e,shouldSync:n=()=>!0,transformForSync:r}=t;async function d(s,u,f){return(await s.resourceServers.list(u,{q:`identifier:${f}`,per_page:1})).resource_servers[0]??null}async function o(s,u){const f=await i();await Promise.all(f.map(async l=>{try{const p=await e(l),y={...r?r(s,l):{name:s.name,identifier:s.identifier,scopes:s.scopes,signing_alg:s.signing_alg,signing_secret:s.signing_secret,token_lifetime:s.token_lifetime,token_lifetime_for_web:s.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:s.skip_consent_for_verifiable_first_party_clients,allow_offline_access:s.allow_offline_access,verificationKey:s.verificationKey,options:s.options},is_system:!0};if(u==="create"){const T=await d(p,l,s.identifier);T&&T.id?await p.resourceServers.update(l,T.id,y):await p.resourceServers.create(l,y)}else{const T=await d(p,l,s.identifier);T&&T.id&&await p.resourceServers.update(l,T.id,y)}}catch(p){console.error(`Failed to sync resource server "${s.identifier}" to tenant "${l}":`,p)}}))}async function c(s){const u=await i();await Promise.all(u.map(async f=>{try{const l=await e(f),p=await d(l,f,s);p&&p.id&&await l.resourceServers.remove(f,p.id)}catch(l){console.error(`Failed to delete resource server "${s}" from tenant "${f}":`,l)}}))}return{afterCreate:async(s,u)=>{s.tenantId===a&&n(u)&&await o(u,"create")},afterUpdate:async(s,u,f)=>{s.tenantId===a&&n(f)&&await o(f,"update")},afterDelete:async(s,u)=>{s.tenantId===a&&await c(u)}}}function B(t){const{controlPlaneTenantId:a,getControlPlaneAdapters:i,getAdapters:e,shouldSync:n=()=>!0,transformForSync:r}=t;return{async afterCreate(d,o){if(o.id!==a)try{const c=await i(),s=await e(o.id),u=await A.fetchAll(f=>c.resourceServers.list(a,f),"resource_servers",{cursorField:"id",pageSize:100});await Promise.all(u.filter(f=>n(f)).map(async f=>{const l=f;try{const p=r?r(l,o.id):{name:l.name,identifier:l.identifier,scopes:l.scopes,signing_alg:l.signing_alg,signing_secret:l.signing_secret,token_lifetime:l.token_lifetime,token_lifetime_for_web:l.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:l.skip_consent_for_verifiable_first_party_clients,allow_offline_access:l.allow_offline_access,verificationKey:l.verificationKey,options:l.options};await s.resourceServers.create(o.id,{...p,is_system:!0})}catch(p){console.error(`Failed to sync resource server "${l.identifier}" to new tenant "${o.id}":`,p)}}))}catch(c){console.error(`Failed to sync resource servers to new tenant "${o.id}":`,c)}}}}function W(t){const{controlPlaneTenantId:a,getChildTenantIds:i,getAdapters:e,shouldSync:n=()=>!0,transformForSync:r}=t;async function d(c,s,u){return(await c.roles.list(s,{q:`name:${u}`,per_page:1})).roles[0]??null}async function o(c,s){const u=await i();await Promise.all(u.map(async f=>{try{const l=await e(f),_={...r?r(c,f):{name:c.name,description:c.description},is_system:!0};if(s==="create"){const y=await d(l,f,c.name);y&&y.id?await l.roles.update(f,y.id,_):await l.roles.create(f,_)}else{const y=await d(l,f,c.name);y&&y.id&&await l.roles.update(f,y.id,_)}}catch(l){console.error(`Failed to sync role "${c.name}" to tenant "${f}":`,l)}}))}return{afterCreate:async(c,s)=>{c.tenantId===a&&n(s)&&await o(s,"create")},afterUpdate:async(c,s,u)=>{c.tenantId===a&&n(u)&&await o(u,"update")},afterDelete:async(c,s)=>{c.tenantId===a&&console.warn(`Role ${s} was deleted from control plane. Child tenant roles with matching names should be deleted manually or implement role name tracking.`)}}}function G(t){const{controlPlaneTenantId:a,getControlPlaneAdapters:i,getAdapters:e,shouldSync:n=()=>!0,transformForSync:r,syncPermissions:d=!0}=t;return{async afterCreate(o,c){if(c.id!==a)try{const s=await i(),u=await e(c.id),f=await A.fetchAll(p=>s.roles.list(a,p),"roles",{cursorField:"id",pageSize:100}),l=new Map;if(await Promise.all(f.filter(p=>n(p)).map(async p=>{const _=p;try{const y=r?r(_,c.id):{name:_.name,description:_.description},T=await u.roles.create(c.id,{...y,is_system:!0});l.set(_.id,T.id)}catch(y){console.error(`Failed to sync role "${_.name}" to new tenant "${c.id}":`,y)}})),d)for(const[p,_]of l)try{const y=await s.rolePermissions.list(a,p,{});y.length>0&&await u.rolePermissions.assign(c.id,_,y.map(T=>({role_id:_,resource_server_identifier:T.resource_server_identifier,permission_name:T.permission_name})))}catch(y){console.error(`Failed to sync permissions for role to new tenant "${c.id}":`,y)}}catch(s){console.error(`Failed to sync roles to new tenant "${c.id}":`,s)}}}}function $(t,a){const i=new j.Hono;return i.get("/",async e=>{var f,l;const n=z.auth0QuerySchema.parse(e.req.query()),{page:r,per_page:d,include_totals:o,q:c}=n,s=e.var.user;if(t.accessControl&&(s!=null&&s.sub)){const p=t.accessControl.controlPlaneTenantId,y=(await e.env.data.userOrganizations.listUserOrganizations(p,s.sub,{})).organizations.map(P=>P.name),T=await e.env.data.tenants.list({page:r,per_page:d,include_totals:o,q:c}),S=T.tenants.filter(P=>y.includes(P.id));return o?e.json({tenants:S,start:((f=T.totals)==null?void 0:f.start)??0,limit:((l=T.totals)==null?void 0:l.limit)??d,length:S.length}):e.json(S)}const u=await e.env.data.tenants.list({page:r,per_page:d,include_totals:o,q:c});return o?e.json(u):e.json(u.tenants)}),i.get("/:id",async e=>{const n=e.req.param("id");if(t.accessControl){const d=e.var.user,o=t.accessControl.controlPlaneTenantId;if(n!==o){if(!(d!=null&&d.sub))throw new w(401,{message:"Authentication required"});if(!(await e.env.data.userOrganizations.listUserOrganizations(o,d.sub,{})).organizations.some(u=>u.name===n))throw new w(403,{message:"Access denied to this tenant"})}}const r=await e.env.data.tenants.get(n);if(!r)throw new w(404,{message:"Tenant not found"});return e.json(r)}),i.post("/",async e=>{var n,r,d;try{const o=e.var.user;if(!(o!=null&&o.sub))throw new w(401,{message:"Authentication required to create tenants"});let c=z.tenantInsertSchema.parse(await e.req.json());const s={adapters:e.env.data,ctx:e};(n=a.tenants)!=null&&n.beforeCreate&&(c=await a.tenants.beforeCreate(s,c));const u=await e.env.data.tenants.create(c);return(r=a.tenants)!=null&&r.afterCreate&&await a.tenants.afterCreate(s,u),e.json(u,201)}catch(o){throw o instanceof k.z.ZodError?new w(400,{message:"Validation error",cause:o}):o instanceof Error&&("code"in o&&o.code==="SQLITE_CONSTRAINT_PRIMARYKEY"||(d=o.message)!=null&&d.includes("UNIQUE constraint failed"))?new w(409,{message:"Tenant with this ID already exists"}):o}}),i.patch("/:id",async e=>{var l,p;const n=e.req.param("id");if(t.accessControl){const _=e.var.user;if(!(_!=null&&_.sub))throw new w(401,{message:"Authentication required to update tenants"});const y=t.accessControl.controlPlaneTenantId;if(n!==y&&!(await e.env.data.userOrganizations.listUserOrganizations(y,_.sub,{})).organizations.some(P=>P.name===n))throw new w(403,{message:"Access denied to update this tenant"})}const r=z.tenantInsertSchema.partial().parse(await e.req.json()),{id:d,...o}=r;if(!await e.env.data.tenants.get(n))throw new w(404,{message:"Tenant not found"});const s={adapters:e.env.data,ctx:e};let u=o;(l=a.tenants)!=null&&l.beforeUpdate&&(u=await a.tenants.beforeUpdate(s,n,o)),await e.env.data.tenants.update(n,u);const f=await e.env.data.tenants.get(n);if(!f)throw new w(404,{message:"Tenant not found after update"});return(p=a.tenants)!=null&&p.afterUpdate&&await a.tenants.afterUpdate(s,f),e.json(f)}),i.delete("/:id",async e=>{var o,c;const n=e.req.param("id");if(t.accessControl&&n===t.accessControl.controlPlaneTenantId)throw new w(400,{message:"Cannot delete the control plane"});if(t.accessControl){const s=e.var.user;if(!(s!=null&&s.sub))throw new w(401,{message:"Authentication required to delete tenants"});const u=t.accessControl.controlPlaneTenantId;if(!(await e.env.data.userOrganizations.listUserOrganizations(u,s.sub,{})).organizations.some(p=>p.name===n))throw new w(403,{message:"Access denied to delete this tenant"})}if(!await e.env.data.tenants.get(n))throw new w(404,{message:"Tenant not found"});const d={adapters:e.env.data,ctx:e};return(o=a.tenants)!=null&&o.beforeDelete&&await a.tenants.beforeDelete(d,n),await e.env.data.tenants.remove(n),(c=a.tenants)!=null&&c.afterDelete&&await a.tenants.afterDelete(d,n),e.body(null,204)}),i}function ae(t,a){const i=new b.OpenAPIHono;return i.openapi(b.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:z.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:b.z.object({tenants:b.z.array(z.tenantSchema),start:b.z.number().optional(),limit:b.z.number().optional(),length:b.z.number().optional()})}},description:"List of tenants"}}}),async e=>{var f,l,p,_;const n=e.req.valid("query"),{page:r,per_page:d,include_totals:o,q:c}=n,s=e.var.user;if(t.accessControl&&(s!=null&&s.sub)){const y=t.accessControl.controlPlaneTenantId,S=(await A.fetchAll(I=>e.env.data.userOrganizations.listUserOrganizations(y,s.sub,I),"organizations")).map(I=>I.name),P=await e.env.data.tenants.list({page:r,per_page:d,include_totals:o,q:c}),O=P.tenants.filter(I=>S.includes(I.id));return o?e.json({tenants:O,start:((f=P.totals)==null?void 0:f.start)??0,limit:((l=P.totals)==null?void 0:l.limit)??d,length:O.length}):e.json({tenants:O})}const u=await e.env.data.tenants.list({page:r,per_page:d,include_totals:o,q:c});return o?e.json({tenants:u.tenants,start:((p=u.totals)==null?void 0:p.start)??0,limit:((_=u.totals)==null?void 0:_.limit)??d,length:u.tenants.length}):e.json({tenants:u.tenants})}),i.openapi(b.createRoute({tags:["tenants"],method:"get",path:"/{id}",request:{params:b.z.object({id:b.z.string()})},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:z.tenantSchema}},description:"Tenant details"},404:{description:"Tenant not found"}}}),async e=>{const{id:n}=e.req.valid("param");if(t.accessControl){const d=e.var.user,o=t.accessControl.controlPlaneTenantId;if(n!==o){if(!(d!=null&&d.sub))throw new w(401,{message:"Authentication required"});if(!(await A.fetchAll(u=>e.env.data.userOrganizations.listUserOrganizations(o,d.sub,u),"organizations")).some(u=>u.name===n))throw new w(403,{message:"Access denied to this tenant"})}}const r=await e.env.data.tenants.get(n);if(!r)throw new w(404,{message:"Tenant not found"});return e.json(r)}),i.openapi(b.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:z.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:z.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"}}}),async e=>{var c,s;const n=e.var.user;if(!(n!=null&&n.sub))throw new w(401,{message:"Authentication required to create tenants"});let r=e.req.valid("json");const d={adapters:e.env.data,ctx:e};(c=a.tenants)!=null&&c.beforeCreate&&(r=await a.tenants.beforeCreate(d,r));const o=await e.env.data.tenants.create(r);return(s=a.tenants)!=null&&s.afterCreate&&await a.tenants.afterCreate(d,o),e.json(o,201)}),i.openapi(b.createRoute({tags:["tenants"],method:"patch",path:"/{id}",request:{params:b.z.object({id:b.z.string()}),body:{content:{"application/json":{schema:b.z.object(z.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:["update:tenants"]}],responses:{200:{content:{"application/json":{schema:z.tenantSchema}},description:"Tenant updated"},403:{description:"Access denied"},404:{description:"Tenant not found"}}}),async e=>{var u,f;const{id:n}=e.req.valid("param");if(t.accessControl){const l=e.var.user,p=t.accessControl.controlPlaneTenantId;if(!(l!=null&&l.sub))throw new w(401,{message:"Authentication required"});if(n!==p&&!(await A.fetchAll(T=>e.env.data.userOrganizations.listUserOrganizations(p,l.sub,T),"organizations")).some(T=>T.name===n))throw new w(403,{message:"Access denied to this tenant"})}if(!await e.env.data.tenants.get(n))throw new w(404,{message:"Tenant not found"});const d=e.req.valid("json"),o={adapters:e.env.data,ctx:e};let c=d;(u=a.tenants)!=null&&u.beforeUpdate&&(c=await a.tenants.beforeUpdate(o,n,d)),await e.env.data.tenants.update(n,c);const s=await e.env.data.tenants.get(n);if(!s)throw new w(500,{message:"Failed to retrieve updated tenant"});return(f=a.tenants)!=null&&f.afterUpdate&&await a.tenants.afterUpdate(o,s),e.json(s)}),i.openapi(b.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:b.z.object({id:b.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 e=>{var o,c;const{id:n}=e.req.valid("param");if(t.accessControl){const s=e.var.user,u=t.accessControl.controlPlaneTenantId;if(!(s!=null&&s.sub))throw new w(401,{message:"Authentication required"});if(n===u)throw new w(403,{message:"Cannot delete the control plane"});if(!(await A.fetchAll(p=>e.env.data.userOrganizations.listUserOrganizations(u,s.sub,p),"organizations")).some(p=>p.name===n))throw new w(403,{message:"Access denied to this tenant"})}if(!await e.env.data.tenants.get(n))throw new w(404,{message:"Tenant not found"});const d={adapters:e.env.data,ctx:e};return(o=a.tenants)!=null&&o.beforeDelete&&await a.tenants.beforeDelete(d,n),await e.env.data.tenants.remove(n),(c=a.tenants)!=null&&c.afterDelete&&await a.tenants.afterDelete(d,n),e.body(null,204)}),i}function ne(t){const a=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:i,type:e}of a){const n=t.match(i);if(n&&n[1])return{type:e,id:n[1]}}return null}async function se(t,a,i){try{switch(i.type){case"resource_server":{const e=await t.resourceServers.get(a,i.id);return(e==null?void 0:e.is_system)===!0}case"role":{const e=await t.roles.get(a,i.id);return(e==null?void 0:e.is_system)===!0}case"connection":{const e=await t.connections.get(a,i.id);return(e==null?void 0:e.is_system)===!0}default:return!1}}catch{return!1}}function re(t){return{resource_server:"resource server",role:"role",connection:"connection"}[t]}function Q(){return async(t,a)=>{if(!["PATCH","PUT","DELETE"].includes(t.req.method))return a();const i=ne(t.req.path);if(!i)return a();const e=t.var.tenant_id||t.req.header("x-tenant-id")||t.req.header("tenant-id");if(!e)return a();if(await se(t.env.data,e,i))throw new w(403,{message:`This ${re(i.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return a()}}function L(t){return async(a,i)=>{if(!t.accessControl)return i();const e=a.var.tenant_id,n=a.var.organization_id;if(!e)throw new w(400,{message:"Tenant ID not found in request"});if(!F(n,e,t.accessControl.controlPlaneTenantId))throw new w(403,{message:`Access denied to tenant ${e}`});return i()}}function V(t){return async(a,i)=>{if(!t.subdomainRouting)return i();const{baseDomain:e,reservedSubdomains:n=[],resolveSubdomain:r}=t.subdomainRouting,d=a.req.header("host")||"";let o=null;if(d.endsWith(e)){const s=d.slice(0,-(e.length+1));s&&!s.includes(".")&&(o=s)}if(o&&n.includes(o)&&(o=null),!o)return t.accessControl&&a.set("tenant_id",t.accessControl.controlPlaneTenantId),i();let c=null;if(r)c=await r(o);else if(t.subdomainRouting.useOrganizations!==!1&&t.accessControl)try{const s=await a.env.data.organizations.get(t.accessControl.controlPlaneTenantId,o);s&&(c=s.id)}catch{}if(!c)throw new w(404,{message:`Tenant not found for subdomain: ${o}`});return a.set("tenant_id",c),i()}}function Y(t){return async(a,i)=>{if(!t.databaseIsolation)return i();const e=a.var.tenant_id;if(!e)throw new w(400,{message:"Tenant ID not found in request"});try{const n=await t.databaseIsolation.getAdapters(e);a.env.data=n}catch(n){throw console.error(`Failed to resolve database for tenant ${e}:`,n),new w(500,{message:"Failed to resolve tenant database"})}return i()}}function D(t){const a=V(t),i=L(t),e=Y(t);return async(n,r)=>(await a(n,async()=>{}),await i(n,async()=>{}),await e(n,async()=>{}),r())}function ie(t){const a=q(t);return{name:"multi-tenancy",middleware:D(t),hooks:a,routes:[{path:"/management",handler:$(t,a)}],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 q(t){const a=t.accessControl?U(t.accessControl):{},i=t.databaseIsolation?N(t.databaseIsolation):{},e=E(t);return{...a,...i,tenants:e}}function Z(t){const a=new j.Hono,i=q(t);return a.route("/tenants",$(t,i)),a}function oe(t){return{hooks:q(t),middleware:D(t),app:Z(t),config:t}}function ce(t){const{controlPlaneTenantId:a="control_plane",syncResourceServers:i=!0,syncRoles:e=!0,multiTenancy:n,entityHooks:r,...d}=t,o={...n,accessControl:{controlPlaneTenantId:a,requireOrganizationMatch:!1,defaultPermissions:["tenant:admin"],...n==null?void 0:n.accessControl}},c=q(o);let s,u;i&&(s=K({controlPlaneTenantId:a,getChildTenantIds:async()=>(await A.fetchAll(m=>t.dataAdapter.tenants.list(m),"tenants",{cursorField:"id",pageSize:100})).filter(m=>m.id!==a).map(m=>m.id),getAdapters:async h=>t.dataAdapter}),u=B({controlPlaneTenantId:a,getControlPlaneAdapters:async()=>t.dataAdapter,getAdapters:async h=>t.dataAdapter}));let f,l;e&&(f=W({controlPlaneTenantId:a,getChildTenantIds:async()=>(await A.fetchAll(m=>t.dataAdapter.tenants.list(m),"tenants",{cursorField:"id",pageSize:100})).filter(m=>m.id!==a).map(m=>m.id),getAdapters:async h=>t.dataAdapter}),l=G({controlPlaneTenantId:a,getControlPlaneAdapters:async()=>t.dataAdapter,getAdapters:async h=>t.dataAdapter,syncPermissions:!0}));const p=async(h,m,...g)=>{const v=[];if(h)try{await h(...g)}catch(C){v.push(C instanceof Error?C:new Error(String(C)))}if(m)try{await m(...g)}catch(C){v.push(C instanceof Error?C:new Error(String(C)))}if(v.length===1)throw v[0];if(v.length>1)throw new AggregateError(v,`Multiple hook errors: ${v.map(C=>C.message).join("; ")}`)},_=async(h,...m)=>{const g=[];for(const v of h)if(v)try{await v(...m)}catch(C){g.push(C instanceof Error?C:new Error(String(C)))}if(g.length===1)throw g[0];if(g.length>1)throw new AggregateError(g,`Multiple hook errors: ${g.map(v=>v.message).join("; ")}`)},y={...r,resourceServers:s?{...r==null?void 0:r.resourceServers,afterCreate:async(h,m)=>{var g;await p((g=r==null?void 0:r.resourceServers)==null?void 0:g.afterCreate,s==null?void 0:s.afterCreate,h,m)},afterUpdate:async(h,m,g)=>{var v;await p((v=r==null?void 0:r.resourceServers)==null?void 0:v.afterUpdate,s==null?void 0:s.afterUpdate,h,m,g)},afterDelete:async(h,m)=>{var g;await p((g=r==null?void 0:r.resourceServers)==null?void 0:g.afterDelete,s==null?void 0:s.afterDelete,h,m)}}:r==null?void 0:r.resourceServers,roles:f?{...r==null?void 0:r.roles,afterCreate:async(h,m)=>{var g;await p((g=r==null?void 0:r.roles)==null?void 0:g.afterCreate,f==null?void 0:f.afterCreate,h,m)},afterUpdate:async(h,m,g)=>{var v;await p((v=r==null?void 0:r.roles)==null?void 0:v.afterUpdate,f==null?void 0:f.afterUpdate,h,m,g)},afterDelete:async(h,m)=>{var g;await p((g=r==null?void 0:r.roles)==null?void 0:g.afterDelete,f==null?void 0:f.afterDelete,h,m)}}:r==null?void 0:r.roles,tenants:u||l?{...r==null?void 0:r.tenants,afterCreate:async(h,m)=>{var g;await _([(g=r==null?void 0:r.tenants)==null?void 0:g.afterCreate,u==null?void 0:u.afterCreate,l==null?void 0:l.afterCreate],h,m)}}:r==null?void 0:r.tenants},T={...c,tenants:u||l?{...c.tenants,afterCreate:async(h,m)=>{var g;(g=c.tenants)!=null&&g.afterCreate&&await c.tenants.afterCreate(h,m),await _([u==null?void 0:u.afterCreate,l==null?void 0:l.afterCreate],h,m)}}:c.tenants},S=ae(o,T),P=A.init({...d,entityHooks:y,managementApiExtensions:[...d.managementApiExtensions||[],{path:"/tenants",router:S}]}),{app:O,managementApp:I,...J}=P,R=new j.Hono;return R.onError((h,m)=>h instanceof w?h.getResponse():(console.error(h),m.json({message:"Internal Server Error"},500))),R.use("/api/v2/*",Q()),R.route("/",O),{app:R,managementApp:I,...J,multiTenancyConfig:o,multiTenancyHooks:c}}Object.defineProperty(exports,"MANAGEMENT_API_SCOPES",{enumerable:!0,get:()=>A.MANAGEMENT_API_SCOPES});Object.defineProperty(exports,"fetchAll",{enumerable:!0,get:()=>A.fetchAll});Object.defineProperty(exports,"seed",{enumerable:!0,get:()=>A.seed});exports.createAccessControlHooks=U;exports.createAccessControlMiddleware=L;exports.createDatabaseHooks=N;exports.createDatabaseMiddleware=Y;exports.createMultiTenancy=Z;exports.createMultiTenancyHooks=q;exports.createMultiTenancyMiddleware=D;exports.createMultiTenancyPlugin=ie;exports.createProtectSyncedMiddleware=Q;exports.createProvisioningHooks=E;exports.createResourceServerSyncHooks=K;exports.createRoleSyncHooks=W;exports.createSubdomainMiddleware=V;exports.createTenantResourceServerSyncHooks=B;exports.createTenantRoleSyncHooks=G;exports.createTenantsRouter=$;exports.init=ce;exports.setupMultiTenancy=oe;exports.validateTenantAccess=F;
@@ -9786,6 +9786,65 @@ export interface SeedResult {
9786
9786
  * ```
9787
9787
  */
9788
9788
  export declare function seed(adapters: DataAdapters, options: SeedOptions): Promise<SeedResult>;
9789
+ /**
9790
+ * Options for fetching all resources with pagination.
9791
+ */
9792
+ export interface FetchAllOptions {
9793
+ /**
9794
+ * The field to use for cursor-based pagination.
9795
+ * This field should be unique and sortable (e.g., 'id', 'created_at').
9796
+ * @default 'id'
9797
+ */
9798
+ cursorField?: string;
9799
+ /**
9800
+ * The sort order for the cursor field.
9801
+ * @default 'asc'
9802
+ */
9803
+ sortOrder?: "asc" | "desc";
9804
+ /**
9805
+ * Maximum number of items to fetch per page.
9806
+ * @default 100
9807
+ */
9808
+ pageSize?: number;
9809
+ /**
9810
+ * Maximum total items to fetch (for safety).
9811
+ * Set to -1 for unlimited.
9812
+ * @default 10000
9813
+ */
9814
+ maxItems?: number;
9815
+ /**
9816
+ * Optional filter query (Lucene-style).
9817
+ */
9818
+ q?: string;
9819
+ }
9820
+ /**
9821
+ * Fetches all resources from a paginated list endpoint by iterating through pages.
9822
+ *
9823
+ * Uses cursor-based pagination by filtering on a sortable field (like 'id') to ensure
9824
+ * consistent results even if data changes between requests.
9825
+ *
9826
+ * @param listFn - The list function from the adapter (e.g., adapters.tenants.list)
9827
+ * @param itemsKey - The key in the response that contains the array of items (e.g., 'tenants', 'users')
9828
+ * @param options - Pagination options
9829
+ * @returns Promise resolving to an array of all items
9830
+ *
9831
+ * @example
9832
+ * ```typescript
9833
+ * // Fetch all tenants
9834
+ * const allTenants = await fetchAll(
9835
+ * (params) => adapters.tenants.list(params),
9836
+ * 'tenants'
9837
+ * );
9838
+ *
9839
+ * // Fetch all users for a tenant with custom options
9840
+ * const allUsers = await fetchAll(
9841
+ * (params) => adapters.users.list(tenantId, params),
9842
+ * 'users',
9843
+ * { cursorField: 'user_id', pageSize: 50 }
9844
+ * );
9845
+ * ```
9846
+ */
9847
+ export declare function fetchAll<T>(listFn: (params: ListParams) => Promise<any>, itemsKey: string, options?: FetchAllOptions): Promise<T[]>;
9789
9848
  declare const flowInsertSchema$1: z.ZodObject<{
9790
9849
  name: z.ZodString;
9791
9850
  actions: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<[
@@ -19805,65 +19864,6 @@ export declare function createMultiTenancyMiddleware(config: MultiTenancyConfig)
19805
19864
  Bindings: MultiTenancyBindings;
19806
19865
  Variables: MultiTenancyVariables;
19807
19866
  }>;
19808
- /**
19809
- * Options for fetching all resources with pagination.
19810
- */
19811
- export interface FetchAllOptions {
19812
- /**
19813
- * The field to use for cursor-based pagination.
19814
- * This field should be unique and sortable (e.g., 'id', 'created_at').
19815
- * @default 'id'
19816
- */
19817
- cursorField?: string;
19818
- /**
19819
- * The sort order for the cursor field.
19820
- * @default 'asc'
19821
- */
19822
- sortOrder?: "asc" | "desc";
19823
- /**
19824
- * Maximum number of items to fetch per page.
19825
- * @default 100
19826
- */
19827
- pageSize?: number;
19828
- /**
19829
- * Maximum total items to fetch (for safety).
19830
- * Set to -1 for unlimited.
19831
- * @default 10000
19832
- */
19833
- maxItems?: number;
19834
- /**
19835
- * Optional filter query (Lucene-style).
19836
- */
19837
- q?: string;
19838
- }
19839
- /**
19840
- * Fetches all resources from a paginated list endpoint by iterating through pages.
19841
- *
19842
- * Uses cursor-based pagination by filtering on a sortable field (like 'id') to ensure
19843
- * consistent results even if data changes between requests.
19844
- *
19845
- * @param listFn - The list function from the adapter (e.g., adapters.tenants.list)
19846
- * @param itemsKey - The key in the response that contains the array of items (e.g., 'tenants', 'users')
19847
- * @param options - Pagination options
19848
- * @returns Promise resolving to an array of all items
19849
- *
19850
- * @example
19851
- * ```typescript
19852
- * // Fetch all tenants
19853
- * const allTenants = await fetchAll(
19854
- * (params) => adapters.tenants.list(params),
19855
- * 'tenants'
19856
- * );
19857
- *
19858
- * // Fetch all users for a tenant with custom options
19859
- * const allUsers = await fetchAll(
19860
- * (params) => adapters.users.list(tenantId, params),
19861
- * 'users',
19862
- * { cursorField: 'user_id', pageSize: 50 }
19863
- * );
19864
- * ```
19865
- */
19866
- export declare function fetchAll<T>(listFn: (params: ListParams$1) => Promise<any>, itemsKey: string, options?: FetchAllOptions): Promise<T[]>;
19867
19867
  export type MultiTenancyEnv = {
19868
19868
  Bindings: MultiTenancyBindings;
19869
19869
  Variables: MultiTenancyVariables;