@parsrun/auth 0.1.38 → 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.
- package/dist/adapters/hono.d.ts +3 -3
- package/dist/adapters/index.d.ts +3 -3
- package/dist/{base-BKyR8rcE.d.ts → base-BI-SilX-.d.ts} +20 -0
- package/dist/{index-C3kz9XqE.d.ts → index-DC7A0IbX.d.ts} +1 -1
- package/dist/{index-DOGcetyD.d.ts → index-kATaLVps.d.ts} +44 -2
- package/dist/index.d.ts +11 -5
- package/dist/index.js +292 -5
- package/dist/index.js.map +1 -1
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/otp/index.d.ts +2 -2
- package/package.json +3 -3
package/dist/adapters/hono.d.ts
CHANGED
|
@@ -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-
|
|
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-
|
|
9
|
-
import '../index-
|
|
8
|
+
import '../base-BI-SilX-.js';
|
|
9
|
+
import '../index-DC7A0IbX.js';
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -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-
|
|
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-
|
|
8
|
-
import '../index-
|
|
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-
|
|
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-
|
|
6
|
-
import { R as RequestOTPInput, d as RequestOTPResult } from './index-
|
|
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-
|
|
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-
|
|
3
|
-
import { P as ParsAuthEngine } from './index-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
// ============================================
|