@parsrun/auth 0.1.39 → 0.2.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,9 +1,9 @@
1
1
  import 'hono/types';
2
2
  import 'hono';
3
- export { Q as AuthVariables, W as HonoAdapterConfig, w as createAuthMiddleware, y as createAuthRoutes, z as createHonoAuth, x as createOptionalAuthMiddleware, K as requireAdmin, N as requireAll, O as requireAny, G as requireAnyPermission, L as requireOwnerOrPermission, F as requirePermission, E as requireRole, H as requireTenant, J as requireTenantAccess } from '../index-DOGcetyD.js';
3
+ export { Q as AuthVariables, W as HonoAdapterConfig, w as createAuthMiddleware, y as createAuthRoutes, z as createHonoAuth, x as createOptionalAuthMiddleware, K as requireAdmin, N as requireAll, O as requireAny, G as requireAnyPermission, L as requireOwnerOrPermission, F as requirePermission, E as requireRole, H as requireTenant, J as requireTenantAccess } from '../index-kATaLVps.js';
4
4
  import '../authorization-By1Xp8Za.js';
5
5
  import '../jwt-manager-CH8H0kmm.js';
6
6
  import 'jose';
7
7
  import '../types-DSjafxJ4.js';
8
- import '../base-BKyR8rcE.js';
9
- import '../index-C3kz9XqE.js';
8
+ import '../base-BI-SilX-.js';
9
+ import '../index-DC7A0IbX.js';
@@ -1,9 +1,9 @@
1
- export { X as AuthContext, Z as AuthResponse, Q as AuthVariables, Y as CookieOptions, W as HonoAdapterConfig, a1 as RefreshBody, _ as RequestOtpBody, a0 as SignInBody, $ as VerifyOtpBody, B as createAuthCookies, w as createAuthMiddleware, y as createAuthRoutes, z as createHonoAuth, D as createLogoutCookies, x as createOptionalAuthMiddleware, K as requireAdmin, N as requireAll, O as requireAny, G as requireAnyPermission, L as requireOwnerOrPermission, F as requirePermission, E as requireRole, H as requireTenant, J as requireTenantAccess } from '../index-DOGcetyD.js';
1
+ export { X as AuthContext, Z as AuthResponse, Q as AuthVariables, Y as CookieOptions, W as HonoAdapterConfig, a1 as RefreshBody, _ as RequestOtpBody, a0 as SignInBody, $ as VerifyOtpBody, B as createAuthCookies, w as createAuthMiddleware, y as createAuthRoutes, z as createHonoAuth, D as createLogoutCookies, x as createOptionalAuthMiddleware, K as requireAdmin, N as requireAll, O as requireAny, G as requireAnyPermission, L as requireOwnerOrPermission, F as requirePermission, E as requireRole, H as requireTenant, J as requireTenantAccess } from '../index-kATaLVps.js';
2
2
  import '../jwt-manager-CH8H0kmm.js';
3
3
  import 'jose';
4
4
  import 'hono/types';
5
5
  import 'hono';
6
6
  import '../types-DSjafxJ4.js';
7
- import '../base-BKyR8rcE.js';
8
- import '../index-C3kz9XqE.js';
7
+ import '../base-BI-SilX-.js';
8
+ import '../index-DC7A0IbX.js';
9
9
  import '../authorization-By1Xp8Za.js';
@@ -266,6 +266,12 @@ interface AuthAdapter {
266
266
  deleteAuthMethod(id: string): Promise<void>;
267
267
  findTenantById?(id: string): Promise<AdapterTenant | null>;
268
268
  findTenantBySlug?(slug: string): Promise<AdapterTenant | null>;
269
+ createTenant?(data: CreateTenantInput): Promise<AdapterTenant>;
270
+ updateTenant?(id: string, data: Partial<AdapterTenant>): Promise<AdapterTenant>;
271
+ deleteTenant?(id: string): Promise<void>;
272
+ findTenantsByParentId?(parentId: string | null): Promise<AdapterTenant[]>;
273
+ findTenantsByPath?(pathPrefix: string): Promise<AdapterTenant[]>;
274
+ updateTenantPath?(tenantId: string, path: string, depth: number): Promise<void>;
269
275
  findMembership?(userId: string, tenantId: string): Promise<AdapterMembership | null>;
270
276
  findMembershipsByUserId?(userId: string): Promise<AdapterMembership[]>;
271
277
  createMembership?(data: CreateMembershipInput): Promise<AdapterMembership>;
@@ -314,6 +320,12 @@ interface AdapterTenant {
314
320
  name: string;
315
321
  slug: string;
316
322
  status: 'active' | 'suspended' | 'inactive';
323
+ /** Parent tenant ID for hierarchical tenants */
324
+ parentId?: string | null;
325
+ /** Materialized path for efficient ancestor/descendant queries (e.g., "/root-id/parent-id/id/") */
326
+ path?: string | null;
327
+ /** Hierarchy depth (0 = root tenant) */
328
+ depth?: number | null;
317
329
  createdAt: Date;
318
330
  updatedAt: Date;
319
331
  }
@@ -358,6 +370,14 @@ interface CreateMembershipInput {
358
370
  role: string;
359
371
  permissions?: string[];
360
372
  }
373
+ interface CreateTenantInput {
374
+ name: string;
375
+ slug: string;
376
+ status?: 'active' | 'suspended' | 'inactive';
377
+ parentId?: string | null;
378
+ path?: string | null;
379
+ depth?: number | null;
380
+ }
361
381
  /**
362
382
  * Main Pars Auth Configuration
363
383
  */
@@ -1,5 +1,5 @@
1
1
  import { K as KVStorage } from './types-DSjafxJ4.js';
2
- import { A as AuthProvider, g as OtpConfig, V as VerifyInput, d as VerifyResult, b as AuthInput, c as AuthResult, a as ProviderInfo } from './base-BKyR8rcE.js';
2
+ import { A as AuthProvider, g as OtpConfig, V as VerifyInput, d as VerifyResult, b as AuthInput, c as AuthResult, a as ProviderInfo } from './base-BI-SilX-.js';
3
3
 
4
4
  /**
5
5
  * OTP Manager
@@ -2,8 +2,8 @@ import { T as TokenPair, d as JwtPayload, J as JwtManager } from './jwt-manager-
2
2
  import * as hono_types from 'hono/types';
3
3
  import { Context, MiddlewareHandler, Hono } from 'hono';
4
4
  import { K as KVStorage } from './types-DSjafxJ4.js';
5
- import { h as AuthAdapter, u as AdapterTenant, v as AdapterMembership, l as TenantResolutionStrategy, i as ParsAuthConfig, a as ProviderInfo, r as AdapterUser, s as AdapterSession } from './base-BKyR8rcE.js';
6
- import { R as RequestOTPInput, d as RequestOTPResult } from './index-C3kz9XqE.js';
5
+ import { h as AuthAdapter, u as AdapterTenant, v as AdapterMembership, l as TenantResolutionStrategy, i as ParsAuthConfig, a as ProviderInfo, r as AdapterUser, s as AdapterSession } from './base-BI-SilX-.js';
6
+ import { R as RequestOTPInput, d as RequestOTPResult } from './index-DC7A0IbX.js';
7
7
  import { f as PermissionPattern } from './authorization-By1Xp8Za.js';
8
8
 
9
9
  /**
@@ -25,6 +25,8 @@ interface CreateTenantInput {
25
25
  ownerRole?: string;
26
26
  /** Initial status (default: 'active') */
27
27
  status?: 'active' | 'suspended' | 'inactive';
28
+ /** Parent tenant ID for creating child tenants */
29
+ parentId?: string;
28
30
  }
29
31
  /**
30
32
  * Tenant update input
@@ -95,6 +97,46 @@ declare class TenantManager {
95
97
  * Get tenant by slug
96
98
  */
97
99
  getTenantBySlug(slug: string): Promise<AdapterTenant | null>;
100
+ /**
101
+ * Get direct children of a tenant
102
+ */
103
+ getChildren(tenantId: string): Promise<AdapterTenant[]>;
104
+ /**
105
+ * Get parent of a tenant
106
+ */
107
+ getParent(tenantId: string): Promise<AdapterTenant | null>;
108
+ /**
109
+ * Get all ancestors of a tenant (from immediate parent to root)
110
+ */
111
+ getAncestors(tenantId: string): Promise<AdapterTenant[]>;
112
+ /**
113
+ * Get all descendants of a tenant (all levels)
114
+ */
115
+ getDescendants(tenantId: string): Promise<AdapterTenant[]>;
116
+ /**
117
+ * Get siblings of a tenant (same parent)
118
+ */
119
+ getSiblings(tenantId: string): Promise<AdapterTenant[]>;
120
+ /**
121
+ * Get root tenant of a hierarchy
122
+ */
123
+ getRootTenant(tenantId: string): Promise<AdapterTenant | null>;
124
+ /**
125
+ * Move a tenant to a new parent
126
+ */
127
+ moveToParent(tenantId: string, newParentId: string | null): Promise<void>;
128
+ /**
129
+ * Check if a tenant is an ancestor of another tenant
130
+ */
131
+ isAncestorOf(ancestorId: string, descendantId: string): Promise<boolean>;
132
+ /**
133
+ * Check if a tenant is a descendant of another tenant
134
+ */
135
+ isDescendantOf(descendantId: string, ancestorId: string): Promise<boolean>;
136
+ /**
137
+ * Get all root tenants (tenants without parent)
138
+ */
139
+ getRootTenants(): Promise<AdapterTenant[]>;
98
140
  /**
99
141
  * Check if user is member of tenant
100
142
  */
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { A as AuthProvider, b as AuthInput, c as AuthResult, a as ProviderInfo, T as TwoFactorProvider, e as TwoFactorSetupResult, h as AuthAdapter, i as ParsAuthConfig } from './base-BKyR8rcE.js';
2
- export { t as AdapterAuthMethod, v as AdapterMembership, s as AdapterSession, u as AdapterTenant, r as AdapterUser, q as AuthCallbacks, C as CookieConfig, y as CreateAuthMethodInput, z as CreateMembershipInput, x as CreateSessionInput, w as CreateUserInput, j as CsrfConfig, J as JwtConfig, M as MagicLinkConfig, O as OAuthProvider, o as OAuthProviderConfig, f as OAuthUserInfo, g as OtpConfig, n as PasswordConfig, P as ProviderType, p as SecurityConfig, S as SessionConfig, k as TenantConfig, l as TenantResolutionStrategy, m as TotpConfig, V as VerifyInput, d as VerifyResult, W as WebAuthnConfig, D as defaultConfig, E as mergeConfig, F as validateConfig } from './base-BKyR8rcE.js';
3
- import { P as ParsAuthEngine } from './index-DOGcetyD.js';
4
- export { t as AcceptInvitationInput, u as AcceptInvitationResult, X as AdapterAuthContext, k as AddMemberInput, A as AuthContext, Z as AuthResponse, Q as AuthVariables, Y as CookieOptions, C as CreateTenantInput, W as HonoAdapterConfig, p as InvitationConfig, q as InvitationRecord, I as InvitationService, v as InvitationStatusResult, M as MultiStrategyTenantResolver, R as RefreshTokenResult, r as SendInvitationInput, s as SendInvitationResult, d as SessionInfo, S as SignInInput, a as SignInResult, b as SignUpInput, c as SignUpResult, i as TenantManager, h as TenantResolutionResult, T as TenantResolver, g as TenantResolverConfig, m as TenantWithMembers, l as UpdateMemberInput, U as UpdateTenantInput, n as UserTenantMembership, V as VerifyTokenResult, B as createAuthCookies, w as createAuthMiddleware, y as createAuthRoutes, z as createHonoAuth, o as createInvitationService, D as createLogoutCookies, f as createMultiStrategyResolver, x as createOptionalAuthMiddleware, j as createTenantManager, e as createTenantResolver, K as requireAdmin, N as requireAll, O as requireAny, G as requireAnyPermission, L as requireOwnerOrPermission, F as requirePermission, E as requireRole, H as requireTenant, J as requireTenantAccess } from './index-DOGcetyD.js';
1
+ import { A as AuthProvider, b as AuthInput, c as AuthResult, a as ProviderInfo, T as TwoFactorProvider, e as TwoFactorSetupResult, h as AuthAdapter, i as ParsAuthConfig } from './base-BI-SilX-.js';
2
+ export { t as AdapterAuthMethod, v as AdapterMembership, s as AdapterSession, u as AdapterTenant, r as AdapterUser, q as AuthCallbacks, C as CookieConfig, y as CreateAuthMethodInput, z as CreateMembershipInput, x as CreateSessionInput, w as CreateUserInput, j as CsrfConfig, J as JwtConfig, M as MagicLinkConfig, O as OAuthProvider, o as OAuthProviderConfig, f as OAuthUserInfo, g as OtpConfig, n as PasswordConfig, P as ProviderType, p as SecurityConfig, S as SessionConfig, k as TenantConfig, l as TenantResolutionStrategy, m as TotpConfig, V as VerifyInput, d as VerifyResult, W as WebAuthnConfig, D as defaultConfig, E as mergeConfig, F as validateConfig } from './base-BI-SilX-.js';
3
+ import { P as ParsAuthEngine } from './index-kATaLVps.js';
4
+ export { t as AcceptInvitationInput, u as AcceptInvitationResult, X as AdapterAuthContext, k as AddMemberInput, A as AuthContext, Z as AuthResponse, Q as AuthVariables, Y as CookieOptions, C as CreateTenantInput, W as HonoAdapterConfig, p as InvitationConfig, q as InvitationRecord, I as InvitationService, v as InvitationStatusResult, M as MultiStrategyTenantResolver, R as RefreshTokenResult, r as SendInvitationInput, s as SendInvitationResult, d as SessionInfo, S as SignInInput, a as SignInResult, b as SignUpInput, c as SignUpResult, i as TenantManager, h as TenantResolutionResult, T as TenantResolver, g as TenantResolverConfig, m as TenantWithMembers, l as UpdateMemberInput, U as UpdateTenantInput, n as UserTenantMembership, V as VerifyTokenResult, B as createAuthCookies, w as createAuthMiddleware, y as createAuthRoutes, z as createHonoAuth, o as createInvitationService, D as createLogoutCookies, f as createMultiStrategyResolver, x as createOptionalAuthMiddleware, j as createTenantManager, e as createTenantResolver, K as requireAdmin, N as requireAll, O as requireAny, G as requireAnyPermission, L as requireOwnerOrPermission, F as requirePermission, E as requireRole, H as requireTenant, J as requireTenantAccess } from './index-kATaLVps.js';
5
5
  export { ProviderRegistry } from './providers/index.js';
6
- export { e as OTPConfig, a as OTPManager, O as OTPProvider, f as OTPRecord, g as RateLimitCheck, R as RequestOTPInput, d as RequestOTPResult, S as StoreOTPResult, V as VerifyOTPResult, b as createOTPManager, c as createOTPProvider } from './index-C3kz9XqE.js';
6
+ export { e as OTPConfig, a as OTPManager, O as OTPProvider, f as OTPRecord, g as RateLimitCheck, R as RequestOTPInput, d as RequestOTPResult, S as StoreOTPResult, V as VerifyOTPResult, b as createOTPManager, c as createOTPProvider } from './index-DC7A0IbX.js';
7
7
  import { K as KVStorage } from './types-DSjafxJ4.js';
8
8
  export { C as CloudflareKVConfig, D as DenoKVConfig, M as MemoryConfig, R as RedisConfig, S as StorageConfig, a as StorageType } from './types-DSjafxJ4.js';
9
9
  export { a as JwtError, J as JwtManager, b as JwtManagerConfig, d as JwtPayload, K as KeyRotationResult, T as TokenPair, c as createJwtManager, e as extractBearerToken, p as parseDuration } from './jwt-manager-CH8H0kmm.js';
@@ -962,6 +962,12 @@ interface DrizzleTenant {
962
962
  status: string;
963
963
  subscriptionPlan: string;
964
964
  settings: Record<string, unknown>;
965
+ /** Parent tenant ID for hierarchical tenants */
966
+ parentId: string | null;
967
+ /** Materialized path for efficient ancestor/descendant queries (e.g., "/root-id/parent-id/id/") */
968
+ path: string | null;
969
+ /** Hierarchy depth (0 = root tenant) */
970
+ depth: number | null;
965
971
  insertedAt: Date;
966
972
  updatedAt: Date;
967
973
  deletedAt: Date | null;
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { OTPProvider } from './chunk-IB4WUQDZ.js';
11
11
  export { OTPManager, OTPProvider, createOTPManager, createOTPProvider } from './chunk-IB4WUQDZ.js';
12
12
  import { createStorage } from './chunk-42MGHABB.js';
13
13
  export { MemoryStorage, StorageKeys, createMemoryStorage, createStorage, createStorageSync, detectRuntime, getEnv, isBun, isCloudflare, isDeno, isEdge, isNode } from './chunk-42MGHABB.js';
14
- import { eq, and, isNull, desc } from 'drizzle-orm';
14
+ import { eq, and, isNull, like, desc } from 'drizzle-orm';
15
15
 
16
16
  // src/config.ts
17
17
  var defaultConfig = {
@@ -165,17 +165,55 @@ var TenantManager = class {
165
165
  * Create a new tenant with owner
166
166
  */
167
167
  async createTenant(input) {
168
- if (!this.adapter.findTenantById) {
168
+ if (!this.adapter.findTenantById || !this.adapter.createTenant) {
169
169
  throw new Error("Tenant operations not supported by adapter");
170
170
  }
171
+ if (!this.adapter.createMembership) {
172
+ throw new Error("Membership operations not supported by adapter");
173
+ }
171
174
  const slug = input.slug ?? this.generateSlug(input.name);
172
175
  const existingTenant = await this.adapter.findTenantBySlug?.(slug);
173
176
  if (existingTenant) {
174
177
  throw new Error(`Tenant with slug '${slug}' already exists`);
175
178
  }
176
- throw new Error(
177
- "createTenant not implemented in adapter. Please implement createTenant in your adapter or use direct database access."
178
- );
179
+ let parentId = null;
180
+ let path = null;
181
+ let depth = 0;
182
+ if (input.parentId) {
183
+ const parent = await this.adapter.findTenantById(input.parentId);
184
+ if (!parent) {
185
+ throw new Error(`Parent tenant '${input.parentId}' not found`);
186
+ }
187
+ parentId = input.parentId;
188
+ depth = (parent.depth ?? 0) + 1;
189
+ }
190
+ const tenant = await this.adapter.createTenant({
191
+ name: input.name,
192
+ slug,
193
+ status: input.status ?? "active",
194
+ parentId,
195
+ path: null,
196
+ // Will be updated after creation
197
+ depth
198
+ });
199
+ if (input.parentId) {
200
+ const parent = await this.adapter.findTenantById(input.parentId);
201
+ const parentPath = parent?.path ?? `/${input.parentId}/`;
202
+ path = `${parentPath.replace(/\/$/, "")}/${tenant.id}/`;
203
+ } else {
204
+ path = `/${tenant.id}/`;
205
+ }
206
+ if (this.adapter.updateTenantPath) {
207
+ await this.adapter.updateTenantPath(tenant.id, path, depth);
208
+ tenant.path = path;
209
+ tenant.depth = depth;
210
+ }
211
+ const membership = await this.adapter.createMembership({
212
+ userId: input.ownerId,
213
+ tenantId: tenant.id,
214
+ role: input.ownerRole ?? "owner"
215
+ });
216
+ return { tenant, membership };
179
217
  }
180
218
  /**
181
219
  * Get tenant by ID
@@ -195,6 +233,185 @@ var TenantManager = class {
195
233
  }
196
234
  return this.adapter.findTenantBySlug(slug);
197
235
  }
236
+ // ============================================
237
+ // Tenant Hierarchy Operations
238
+ // ============================================
239
+ /**
240
+ * Get direct children of a tenant
241
+ */
242
+ async getChildren(tenantId) {
243
+ if (!this.adapter.findTenantsByParentId) {
244
+ return [];
245
+ }
246
+ return this.adapter.findTenantsByParentId(tenantId);
247
+ }
248
+ /**
249
+ * Get parent of a tenant
250
+ */
251
+ async getParent(tenantId) {
252
+ if (!this.adapter.findTenantById) {
253
+ return null;
254
+ }
255
+ const tenant = await this.adapter.findTenantById(tenantId);
256
+ if (!tenant || !tenant.parentId) {
257
+ return null;
258
+ }
259
+ return this.adapter.findTenantById(tenant.parentId);
260
+ }
261
+ /**
262
+ * Get all ancestors of a tenant (from immediate parent to root)
263
+ */
264
+ async getAncestors(tenantId) {
265
+ if (!this.adapter.findTenantById) {
266
+ return [];
267
+ }
268
+ const tenant = await this.adapter.findTenantById(tenantId);
269
+ if (!tenant || !tenant.path) {
270
+ return [];
271
+ }
272
+ const pathParts = tenant.path.split("/").filter(Boolean);
273
+ const ancestorIds = pathParts.slice(0, -1);
274
+ const ancestors = [];
275
+ for (const ancestorId of ancestorIds) {
276
+ const ancestor = await this.adapter.findTenantById(ancestorId);
277
+ if (ancestor) {
278
+ ancestors.push(ancestor);
279
+ }
280
+ }
281
+ return ancestors;
282
+ }
283
+ /**
284
+ * Get all descendants of a tenant (all levels)
285
+ */
286
+ async getDescendants(tenantId) {
287
+ if (!this.adapter.findTenantById || !this.adapter.findTenantsByPath) {
288
+ return [];
289
+ }
290
+ const tenant = await this.adapter.findTenantById(tenantId);
291
+ if (!tenant) {
292
+ return [];
293
+ }
294
+ const path = tenant.path ?? `/${tenantId}/`;
295
+ const allDescendants = await this.adapter.findTenantsByPath(path);
296
+ return allDescendants.filter((t) => t.id !== tenantId);
297
+ }
298
+ /**
299
+ * Get siblings of a tenant (same parent)
300
+ */
301
+ async getSiblings(tenantId) {
302
+ if (!this.adapter.findTenantById || !this.adapter.findTenantsByParentId) {
303
+ return [];
304
+ }
305
+ const tenant = await this.adapter.findTenantById(tenantId);
306
+ if (!tenant) {
307
+ return [];
308
+ }
309
+ const siblings = await this.adapter.findTenantsByParentId(tenant.parentId ?? null);
310
+ return siblings.filter((t) => t.id !== tenantId);
311
+ }
312
+ /**
313
+ * Get root tenant of a hierarchy
314
+ */
315
+ async getRootTenant(tenantId) {
316
+ if (!this.adapter.findTenantById) {
317
+ return null;
318
+ }
319
+ const tenant = await this.adapter.findTenantById(tenantId);
320
+ if (!tenant) {
321
+ return null;
322
+ }
323
+ if (!tenant.parentId) {
324
+ return tenant;
325
+ }
326
+ if (tenant.path) {
327
+ const pathParts = tenant.path.split("/").filter(Boolean);
328
+ const rootId = pathParts[0];
329
+ if (rootId) {
330
+ return this.adapter.findTenantById(rootId);
331
+ }
332
+ }
333
+ let current = tenant;
334
+ while (current.parentId) {
335
+ const parent = await this.adapter.findTenantById(current.parentId);
336
+ if (!parent) break;
337
+ current = parent;
338
+ }
339
+ return current;
340
+ }
341
+ /**
342
+ * Move a tenant to a new parent
343
+ */
344
+ async moveToParent(tenantId, newParentId) {
345
+ if (!this.adapter.findTenantById || !this.adapter.updateTenant || !this.adapter.updateTenantPath) {
346
+ throw new Error("Tenant hierarchy operations not supported by adapter");
347
+ }
348
+ const tenant = await this.adapter.findTenantById(tenantId);
349
+ if (!tenant) {
350
+ throw new Error("Tenant not found");
351
+ }
352
+ if (newParentId) {
353
+ const isDescendant = await this.isDescendantOf(newParentId, tenantId);
354
+ if (isDescendant) {
355
+ throw new Error("Cannot move tenant to its own descendant");
356
+ }
357
+ }
358
+ const oldPath = tenant.path ?? `/${tenantId}/`;
359
+ const descendants = await this.getDescendants(tenantId);
360
+ let newPath;
361
+ let newDepth;
362
+ if (newParentId) {
363
+ const newParent = await this.adapter.findTenantById(newParentId);
364
+ if (!newParent) {
365
+ throw new Error("New parent tenant not found");
366
+ }
367
+ const parentPath = newParent.path ?? `/${newParentId}/`;
368
+ newPath = `${parentPath.replace(/\/$/, "")}/${tenantId}/`;
369
+ newDepth = (newParent.depth ?? 0) + 1;
370
+ } else {
371
+ newPath = `/${tenantId}/`;
372
+ newDepth = 0;
373
+ }
374
+ await this.adapter.updateTenant(tenantId, { parentId: newParentId });
375
+ await this.adapter.updateTenantPath(tenantId, newPath, newDepth);
376
+ for (const descendant of descendants) {
377
+ if (descendant.path) {
378
+ const descendantNewPath = descendant.path.replace(oldPath, newPath);
379
+ const descendantNewDepth = (descendant.depth ?? 0) - (tenant.depth ?? 0) + newDepth;
380
+ await this.adapter.updateTenantPath(descendant.id, descendantNewPath, descendantNewDepth);
381
+ }
382
+ }
383
+ }
384
+ /**
385
+ * Check if a tenant is an ancestor of another tenant
386
+ */
387
+ async isAncestorOf(ancestorId, descendantId) {
388
+ if (!this.adapter.findTenantById) {
389
+ return false;
390
+ }
391
+ const descendant = await this.adapter.findTenantById(descendantId);
392
+ if (!descendant || !descendant.path) {
393
+ return false;
394
+ }
395
+ return descendant.path.includes(`/${ancestorId}/`);
396
+ }
397
+ /**
398
+ * Check if a tenant is a descendant of another tenant
399
+ */
400
+ async isDescendantOf(descendantId, ancestorId) {
401
+ return this.isAncestorOf(ancestorId, descendantId);
402
+ }
403
+ /**
404
+ * Get all root tenants (tenants without parent)
405
+ */
406
+ async getRootTenants() {
407
+ if (!this.adapter.findTenantsByParentId) {
408
+ return [];
409
+ }
410
+ return this.adapter.findTenantsByParentId(null);
411
+ }
412
+ // ============================================
413
+ // Membership Operations
414
+ // ============================================
198
415
  /**
199
416
  * Check if user is member of tenant
200
417
  */
@@ -3195,6 +3412,9 @@ function toAdapterTenant(tenant) {
3195
3412
  name: tenant.name,
3196
3413
  slug: tenant.slug,
3197
3414
  status: tenant.status,
3415
+ parentId: tenant.parentId,
3416
+ path: tenant.path,
3417
+ depth: tenant.depth,
3198
3418
  createdAt: tenant.insertedAt,
3199
3419
  updatedAt: tenant.updatedAt
3200
3420
  };
@@ -3399,6 +3619,73 @@ function createDrizzleAdapter(config) {
3399
3619
  if (!tenant) return null;
3400
3620
  return toAdapterTenant(tenant);
3401
3621
  },
3622
+ async createTenant(input) {
3623
+ if (!tenants) {
3624
+ throw new Error("Tenants table not configured");
3625
+ }
3626
+ const [tenant] = await db.insert(tenants).values({
3627
+ name: input.name,
3628
+ slug: input.slug,
3629
+ status: input.status ?? "active",
3630
+ parentId: input.parentId ?? null,
3631
+ path: input.path ?? null,
3632
+ depth: input.depth ?? null,
3633
+ subscriptionPlan: "free",
3634
+ settings: {}
3635
+ }).returning();
3636
+ return toAdapterTenant(tenant);
3637
+ },
3638
+ async updateTenant(id, data) {
3639
+ if (!tenants) {
3640
+ throw new Error("Tenants table not configured");
3641
+ }
3642
+ const updateData = {
3643
+ updatedAt: /* @__PURE__ */ new Date()
3644
+ };
3645
+ if (data.name !== void 0) updateData["name"] = data.name;
3646
+ if (data.slug !== void 0) updateData["slug"] = data.slug;
3647
+ if (data.status !== void 0) updateData["status"] = data.status;
3648
+ if (data.parentId !== void 0) updateData["parentId"] = data.parentId;
3649
+ if (data.path !== void 0) updateData["path"] = data.path;
3650
+ if (data.depth !== void 0) updateData["depth"] = data.depth;
3651
+ const [tenant] = await db.update(tenants).set(updateData).where(eq(tenants.id, id)).returning();
3652
+ return toAdapterTenant(tenant);
3653
+ },
3654
+ async deleteTenant(id) {
3655
+ if (!tenants) return;
3656
+ if (softDelete) {
3657
+ await db.update(tenants).set({ deletedAt: /* @__PURE__ */ new Date() }).where(eq(tenants.id, id));
3658
+ } else {
3659
+ await db.delete(tenants).where(eq(tenants.id, id));
3660
+ }
3661
+ },
3662
+ // ============================================
3663
+ // Tenant Hierarchy Operations (Optional)
3664
+ // ============================================
3665
+ async findTenantsByParentId(parentId) {
3666
+ if (!tenants) return [];
3667
+ const result = await db.select().from(tenants).where(
3668
+ and(
3669
+ parentId === null ? isNull(tenants.parentId) : eq(tenants.parentId, parentId),
3670
+ softDelete ? isNull(tenants.deletedAt) : void 0
3671
+ )
3672
+ );
3673
+ return result.map(toAdapterTenant);
3674
+ },
3675
+ async findTenantsByPath(pathPrefix) {
3676
+ if (!tenants) return [];
3677
+ const result = await db.select().from(tenants).where(
3678
+ and(
3679
+ like(tenants.path, `${pathPrefix}%`),
3680
+ softDelete ? isNull(tenants.deletedAt) : void 0
3681
+ )
3682
+ );
3683
+ return result.map(toAdapterTenant);
3684
+ },
3685
+ async updateTenantPath(tenantId, path, depth) {
3686
+ if (!tenants) return;
3687
+ await db.update(tenants).set({ path, depth, updatedAt: /* @__PURE__ */ new Date() }).where(eq(tenants.id, tenantId));
3688
+ },
3402
3689
  // ============================================
3403
3690
  // Membership Operations (Optional)
3404
3691
  // ============================================