@venturekit-pro/tenancy 0.0.0-dev.20260323005827

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.
Files changed (47) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +86 -0
  3. package/dist/context/index.d.ts +8 -0
  4. package/dist/context/index.d.ts.map +1 -0
  5. package/dist/context/index.js +8 -0
  6. package/dist/context/index.js.map +1 -0
  7. package/dist/context/resolver.d.ts +22 -0
  8. package/dist/context/resolver.d.ts.map +1 -0
  9. package/dist/context/resolver.js +116 -0
  10. package/dist/context/resolver.js.map +1 -0
  11. package/dist/context/tenant-context.d.ts +46 -0
  12. package/dist/context/tenant-context.d.ts.map +1 -0
  13. package/dist/context/tenant-context.js +88 -0
  14. package/dist/context/tenant-context.js.map +1 -0
  15. package/dist/index.d.ts +10 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +13 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/middleware/index.d.ts +6 -0
  20. package/dist/middleware/index.d.ts.map +1 -0
  21. package/dist/middleware/index.js +6 -0
  22. package/dist/middleware/index.js.map +1 -0
  23. package/dist/middleware/quota-middleware.d.ts +48 -0
  24. package/dist/middleware/quota-middleware.d.ts.map +1 -0
  25. package/dist/middleware/quota-middleware.js +63 -0
  26. package/dist/middleware/quota-middleware.js.map +1 -0
  27. package/dist/middleware/tenant-middleware.d.ts +51 -0
  28. package/dist/middleware/tenant-middleware.d.ts.map +1 -0
  29. package/dist/middleware/tenant-middleware.js +74 -0
  30. package/dist/middleware/tenant-middleware.js.map +1 -0
  31. package/dist/types/config.d.ts +69 -0
  32. package/dist/types/config.d.ts.map +1 -0
  33. package/dist/types/config.js +5 -0
  34. package/dist/types/config.js.map +1 -0
  35. package/dist/types/index.d.ts +7 -0
  36. package/dist/types/index.d.ts.map +1 -0
  37. package/dist/types/index.js +7 -0
  38. package/dist/types/index.js.map +1 -0
  39. package/dist/types/isolation.d.ts +61 -0
  40. package/dist/types/isolation.d.ts.map +1 -0
  41. package/dist/types/isolation.js +5 -0
  42. package/dist/types/isolation.js.map +1 -0
  43. package/dist/types/tenant.d.ts +129 -0
  44. package/dist/types/tenant.d.ts.map +1 -0
  45. package/dist/types/tenant.js +5 -0
  46. package/dist/types/tenant.js.map +1 -0
  47. package/package.json +39 -0
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ Copyright 2024 VentureKit
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @venturekit-pro/tenancy
2
+
3
+ > **Warning:** This package is in active development and not production-ready. APIs may change without notice.
4
+
5
+ Multi-tenant utilities for [VentureKit](https://venturekit.dev) — tenant resolution, context management, data isolation, and quota enforcement.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @venturekit-pro/tenancy@dev
11
+ ```
12
+
13
+ ## Overview
14
+
15
+ `@venturekit-pro/tenancy` provides:
16
+
17
+ - **Tenant resolution** — resolve tenants from subdomain, custom domain, path, header, or JWT
18
+ - **Tenant context** — `createTenantContext()`, `getCurrentTenant()`, `resolveTenant()`
19
+ - **Tenant middleware** — `createTenantMiddleware()` for automatic tenant resolution per request
20
+ - **Quota enforcement** — `createQuotaMiddleware()`, `checkQuotas()` for usage limits
21
+ - **Error types** — `TenantNotFoundError`, `TenantSuspendedError`, `TenantInactiveError`, `QuotaExceededError`
22
+
23
+ ## Tenant Resolution
24
+
25
+ VentureKit supports multiple strategies for identifying the current tenant:
26
+
27
+ | Strategy | Example |
28
+ |----------|---------|
29
+ | Subdomain | `acme.app.example.com` → tenant `acme` |
30
+ | Custom domain | `app.acme.com` → lookup in domain table |
31
+ | Path prefix | `/t/acme/api/tasks` → tenant `acme` |
32
+ | Header | `X-Tenant-ID: acme` |
33
+ | JWT claim | `tenant_id` claim in access token |
34
+
35
+ ## Middleware
36
+
37
+ Add tenant resolution to your handlers:
38
+
39
+ ```typescript
40
+ import { createTenantMiddleware, createQuotaMiddleware } from '@venturekit-pro/tenancy';
41
+ import { handler } from '@venturekit/runtime';
42
+
43
+ export const main = handler(async (_body, ctx, logger) => {
44
+ logger.info('Current tenant', { tenantId: ctx.tenant?.id });
45
+ return { tenantId: ctx.tenant?.id };
46
+ }, {
47
+ scopes: ['api.read'],
48
+ middleware: [
49
+ createTenantMiddleware({ strategy: 'subdomain' }),
50
+ createQuotaMiddleware(),
51
+ ],
52
+ });
53
+ ```
54
+
55
+ ## Quota Enforcement
56
+
57
+ Define per-tenant quotas and enforce them automatically:
58
+
59
+ ```typescript
60
+ import { checkQuotas } from '@venturekit-pro/tenancy';
61
+
62
+ // Throws QuotaExceededError if over limit
63
+ await checkQuotas(tenantId, {
64
+ apiRequests: { limit: 10000, period: 'month' },
65
+ storage: { limit: 5_000_000_000 }, // 5 GB
66
+ });
67
+ ```
68
+
69
+ ## Context
70
+
71
+ Access the current tenant anywhere in your handler:
72
+
73
+ ```typescript
74
+ import { getCurrentTenant } from '@venturekit-pro/tenancy';
75
+
76
+ const tenant = getCurrentTenant(ctx);
77
+ // { id: 'acme', slug: 'acme', metadata: { ... } }
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ See the [API reference](https://venturekit.dev/api-reference/tenancy) for full documentation.
83
+
84
+ ## License
85
+
86
+ Apache-2.0 — see [LICENSE](../../LICENSE) for details.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Tenant Context
3
+ *
4
+ * Utilities for managing tenant context in requests.
5
+ */
6
+ export { TenantContext, createTenantContext, getCurrentTenant } from './tenant-context.js';
7
+ export { resolveTenant } from './resolver.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Tenant Context
3
+ *
4
+ * Utilities for managing tenant context in requests.
5
+ */
6
+ export { createTenantContext, getCurrentTenant } from './tenant-context.js';
7
+ export { resolveTenant } from './resolver.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAiB,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Tenant Resolver
3
+ *
4
+ * Resolves tenant from request based on configured strategy.
5
+ */
6
+ import type { IsolationConfig } from '../types/index.js';
7
+ /**
8
+ * Request-like object for tenant resolution
9
+ */
10
+ export interface TenantRequest {
11
+ headers?: Record<string, string | string[] | undefined>;
12
+ url?: string;
13
+ hostname?: string;
14
+ query?: Record<string, string | string[] | undefined>;
15
+ path?: string;
16
+ claims?: Record<string, unknown>;
17
+ }
18
+ /**
19
+ * Resolve tenant ID from request
20
+ */
21
+ export declare function resolveTenant(request: TenantRequest, config: IsolationConfig): Promise<string | null>;
22
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/context/resolver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,mBAAmB,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBxB"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Tenant Resolver
3
+ *
4
+ * Resolves tenant from request based on configured strategy.
5
+ */
6
+ /**
7
+ * Resolve tenant ID from request
8
+ */
9
+ export async function resolveTenant(request, config) {
10
+ // Check if route is public
11
+ if (config.publicRoutes && request.path) {
12
+ const isPublic = config.publicRoutes.some(route => {
13
+ if (route.endsWith('*')) {
14
+ return request.path.startsWith(route.slice(0, -1));
15
+ }
16
+ return request.path === route;
17
+ });
18
+ if (isPublic) {
19
+ return config.defaultTenantId || null;
20
+ }
21
+ }
22
+ const tenantId = await resolveByStrategy(request, config);
23
+ return tenantId || config.defaultTenantId || null;
24
+ }
25
+ /**
26
+ * Resolve tenant by configured strategy
27
+ */
28
+ async function resolveByStrategy(request, config) {
29
+ switch (config.resolution) {
30
+ case 'subdomain':
31
+ return resolveFromSubdomain(request);
32
+ case 'custom_domain':
33
+ return resolveFromCustomDomain(request, config.domainLookup);
34
+ case 'path':
35
+ return resolveFromPath(request);
36
+ case 'header':
37
+ return resolveFromHeader(request, config.headerName || 'X-Tenant-ID');
38
+ case 'query':
39
+ return resolveFromQuery(request, config.queryParam || 'tenant');
40
+ case 'jwt':
41
+ return resolveFromJwt(request, config.jwtClaim || 'tenant_id');
42
+ case 'custom':
43
+ if (config.customResolver) {
44
+ return config.customResolver(request);
45
+ }
46
+ return null;
47
+ default:
48
+ return null;
49
+ }
50
+ }
51
+ /**
52
+ * Resolve from subdomain (tenant.example.com)
53
+ */
54
+ function resolveFromSubdomain(request) {
55
+ const hostname = request.hostname || '';
56
+ const parts = hostname.split('.');
57
+ // Need at least 3 parts (tenant.example.com)
58
+ if (parts.length < 3)
59
+ return null;
60
+ // Skip www
61
+ const subdomain = parts[0];
62
+ if (subdomain === 'www')
63
+ return null;
64
+ return subdomain;
65
+ }
66
+ /**
67
+ * Resolve from path (example.com/tenant/...)
68
+ */
69
+ function resolveFromPath(request) {
70
+ const path = request.path || request.url || '';
71
+ const parts = path.split('/').filter(Boolean);
72
+ if (parts.length === 0)
73
+ return null;
74
+ return parts[0];
75
+ }
76
+ /**
77
+ * Resolve from header
78
+ */
79
+ function resolveFromHeader(request, headerName) {
80
+ const headers = request.headers || {};
81
+ const value = headers[headerName.toLowerCase()] || headers[headerName];
82
+ if (Array.isArray(value))
83
+ return value[0] || null;
84
+ return value || null;
85
+ }
86
+ /**
87
+ * Resolve from query parameter
88
+ */
89
+ function resolveFromQuery(request, paramName) {
90
+ const query = request.query || {};
91
+ const value = query[paramName];
92
+ if (Array.isArray(value))
93
+ return value[0] || null;
94
+ return value || null;
95
+ }
96
+ /**
97
+ * Resolve from JWT claims
98
+ */
99
+ function resolveFromJwt(request, claimName) {
100
+ const claims = request.claims || {};
101
+ const value = claims[claimName];
102
+ if (typeof value === 'string')
103
+ return value;
104
+ return null;
105
+ }
106
+ /**
107
+ * Resolve from custom domain (white-label)
108
+ * Looks up tenant ID by the full hostname
109
+ */
110
+ async function resolveFromCustomDomain(request, domainLookup) {
111
+ const hostname = request.hostname || '';
112
+ if (!hostname || !domainLookup)
113
+ return null;
114
+ return domainLookup(hostname);
115
+ }
116
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/context/resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Tenant Context
3
+ *
4
+ * Manages tenant context for the current request.
5
+ */
6
+ import type { Tenant, TenantMember } from '../types/index.js';
7
+ /**
8
+ * Tenant context for current request
9
+ */
10
+ export interface TenantContext {
11
+ /** Current tenant */
12
+ tenant: Tenant;
13
+ /** Current user's membership (if authenticated) */
14
+ membership?: TenantMember;
15
+ /** Is this a system/admin context */
16
+ isSystem?: boolean;
17
+ }
18
+ /**
19
+ * Create tenant context for a request
20
+ */
21
+ export declare function createTenantContext(tenant: Tenant, membership?: TenantMember): TenantContext;
22
+ /**
23
+ * Get current tenant from context
24
+ */
25
+ export declare function getCurrentTenant(): Tenant | null;
26
+ /**
27
+ * Get current tenant context
28
+ */
29
+ export declare function getTenantContext(): TenantContext | null;
30
+ /**
31
+ * Run function with tenant context
32
+ */
33
+ export declare function withTenantContext<T>(context: TenantContext, fn: () => T | Promise<T>): Promise<T>;
34
+ /**
35
+ * Run function with system context (bypasses tenant checks)
36
+ */
37
+ export declare function withSystemContext<T>(fn: () => T | Promise<T>): Promise<T>;
38
+ /**
39
+ * Assert tenant context exists
40
+ */
41
+ export declare function requireTenantContext(): TenantContext;
42
+ /**
43
+ * Assert current tenant exists
44
+ */
45
+ export declare function requireTenant(): Tenant;
46
+ //# sourceMappingURL=tenant-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-context.d.ts","sourceRoot":"","sources":["../../src/context/tenant-context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IAEf,mDAAmD;IACnD,UAAU,CAAC,EAAE,YAAY,CAAC;IAE1B,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAgBD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,YAAY,GAAG,aAAa,CAM5F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAGhD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,GAAG,IAAI,CAEvD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,OAAO,EAAE,aAAa,EACtB,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAgB/E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,CAMpD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAMtC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Tenant Context
3
+ *
4
+ * Manages tenant context for the current request.
5
+ */
6
+ // AsyncLocalStorage for tenant context (Node.js)
7
+ let asyncLocalStorage = null;
8
+ /**
9
+ * Initialize async local storage (call once at app startup)
10
+ */
11
+ async function initAsyncLocalStorage() {
12
+ if (!asyncLocalStorage) {
13
+ const { AsyncLocalStorage } = await import('node:async_hooks');
14
+ asyncLocalStorage = new AsyncLocalStorage();
15
+ }
16
+ return asyncLocalStorage;
17
+ }
18
+ /**
19
+ * Create tenant context for a request
20
+ */
21
+ export function createTenantContext(tenant, membership) {
22
+ return {
23
+ tenant,
24
+ membership,
25
+ isSystem: false,
26
+ };
27
+ }
28
+ /**
29
+ * Get current tenant from context
30
+ */
31
+ export function getCurrentTenant() {
32
+ const store = asyncLocalStorage?.getStore();
33
+ return store?.tenant || null;
34
+ }
35
+ /**
36
+ * Get current tenant context
37
+ */
38
+ export function getTenantContext() {
39
+ return asyncLocalStorage?.getStore() || null;
40
+ }
41
+ /**
42
+ * Run function with tenant context
43
+ */
44
+ export async function withTenantContext(context, fn) {
45
+ const storage = await initAsyncLocalStorage();
46
+ return storage.run(context, fn);
47
+ }
48
+ /**
49
+ * Run function with system context (bypasses tenant checks)
50
+ */
51
+ export async function withSystemContext(fn) {
52
+ const storage = await initAsyncLocalStorage();
53
+ const systemContext = {
54
+ tenant: {
55
+ id: 'system',
56
+ name: 'System',
57
+ slug: 'system',
58
+ status: 'active',
59
+ plan: 'enterprise',
60
+ ownerId: 'system',
61
+ createdAt: new Date(),
62
+ updatedAt: new Date(),
63
+ },
64
+ isSystem: true,
65
+ };
66
+ return storage.run(systemContext, fn);
67
+ }
68
+ /**
69
+ * Assert tenant context exists
70
+ */
71
+ export function requireTenantContext() {
72
+ const context = getTenantContext();
73
+ if (!context) {
74
+ throw new Error('Tenant context required but not found');
75
+ }
76
+ return context;
77
+ }
78
+ /**
79
+ * Assert current tenant exists
80
+ */
81
+ export function requireTenant() {
82
+ const tenant = getCurrentTenant();
83
+ if (!tenant) {
84
+ throw new Error('Tenant required but not found in context');
85
+ }
86
+ return tenant;
87
+ }
88
+ //# sourceMappingURL=tenant-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-context.js","sourceRoot":"","sources":["../../src/context/tenant-context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,iDAAiD;AACjD,IAAI,iBAAiB,GAA2G,IAAI,CAAC;AAErI;;GAEG;AACH,KAAK,UAAU,qBAAqB;IAClC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC/D,iBAAiB,GAAG,IAAI,iBAAiB,EAAiB,CAAC;IAC7D,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,UAAyB;IAC3E,OAAO;QACL,MAAM;QACN,UAAU;QACV,QAAQ,EAAE,KAAK;KAChB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,KAAK,GAAG,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAC5C,OAAO,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,iBAAiB,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAsB,EACtB,EAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAC9C,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAI,EAAwB;IACjE,MAAM,OAAO,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAC9C,MAAM,aAAa,GAAkB;QACnC,MAAM,EAAE;YACN,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB;QACD,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @venturekit-pro/tenancy
3
+ *
4
+ * Multi-tenant utilities for VentureKit.
5
+ * Documentation: https://venturekit.dev
6
+ */
7
+ export * from './types/index.js';
8
+ export { TenantContext, createTenantContext, getCurrentTenant, resolveTenant, } from './context/index.js';
9
+ export { createTenantMiddleware, createQuotaMiddleware, checkQuotas, TenantNotFoundError, TenantSuspendedError, TenantInactiveError, QuotaExceededError, } from './middleware/index.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @venturekit-pro/tenancy
3
+ *
4
+ * Multi-tenant utilities for VentureKit.
5
+ * Documentation: https://venturekit.dev
6
+ */
7
+ // Types
8
+ export * from './types/index.js';
9
+ // Context
10
+ export { createTenantContext, getCurrentTenant, resolveTenant, } from './context/index.js';
11
+ // Middleware
12
+ export { createTenantMiddleware, createQuotaMiddleware, checkQuotas, TenantNotFoundError, TenantSuspendedError, TenantInactiveError, QuotaExceededError, } from './middleware/index.js';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,UAAU;AACV,OAAO,EAEL,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,GACd,MAAM,oBAAoB,CAAC;AAE5B,aAAa;AACb,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tenancy Middleware
3
+ */
4
+ export { createTenantMiddleware, TenantNotFoundError, TenantSuspendedError, TenantInactiveError, } from './tenant-middleware.js';
5
+ export { createQuotaMiddleware, QuotaExceededError, checkQuotas } from './quota-middleware.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tenancy Middleware
3
+ */
4
+ export { createTenantMiddleware, TenantNotFoundError, TenantSuspendedError, TenantInactiveError, } from './tenant-middleware.js';
5
+ export { createQuotaMiddleware, QuotaExceededError, checkQuotas } from './quota-middleware.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Quota Middleware
3
+ *
4
+ * Middleware for enforcing tenant quotas.
5
+ */
6
+ import type { TenantQuotas } from '../types/index.js';
7
+ /**
8
+ * Quota check function type
9
+ */
10
+ export type QuotaCheck = (tenantId: string, quotaKey: string) => Promise<{
11
+ current: number;
12
+ limit: number;
13
+ }>;
14
+ /**
15
+ * Quota middleware options
16
+ */
17
+ export interface QuotaMiddlewareOptions {
18
+ /** Function to check current usage */
19
+ checkUsage: QuotaCheck;
20
+ /** Quota key to check */
21
+ quotaKey: keyof TenantQuotas;
22
+ /** Called when quota exceeded */
23
+ onQuotaExceeded?: (tenantId: string, current: number, limit: number) => void;
24
+ }
25
+ /**
26
+ * Create quota enforcement middleware
27
+ */
28
+ export declare function createQuotaMiddleware(options: QuotaMiddlewareOptions): <T>(next: () => Promise<T>) => Promise<T>;
29
+ /**
30
+ * Quota exceeded error
31
+ */
32
+ export declare class QuotaExceededError extends Error {
33
+ tenantId: string;
34
+ quotaKey: string;
35
+ current: number;
36
+ limit: number;
37
+ constructor(tenantId: string, quotaKey: string, current: number, limit: number);
38
+ }
39
+ /**
40
+ * Check multiple quotas at once
41
+ */
42
+ export declare function checkQuotas(tenantId: string, quotas: TenantQuotas, checkUsage: QuotaCheck): Promise<{
43
+ key: string;
44
+ current: number;
45
+ limit: number;
46
+ exceeded: boolean;
47
+ }[]>;
48
+ //# sourceMappingURL=quota-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/quota-middleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,sCAAsC;IACtC,UAAU,EAAE,UAAU,CAAC;IAEvB,yBAAyB;IACzB,QAAQ,EAAE,MAAM,YAAY,CAAC;IAE7B,iCAAiC;IACjC,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,IAG7B,CAAC,EAAE,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC,CAmB7E;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAElC,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,MAAM;IACf,KAAK,EAAE,MAAM;gBAHb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM;CAKvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,EAAE,CAAC,CAgB/E"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Quota Middleware
3
+ *
4
+ * Middleware for enforcing tenant quotas.
5
+ */
6
+ import { requireTenant } from '../context/tenant-context.js';
7
+ /**
8
+ * Create quota enforcement middleware
9
+ */
10
+ export function createQuotaMiddleware(options) {
11
+ const { checkUsage, quotaKey, onQuotaExceeded } = options;
12
+ return async function quotaMiddleware(next) {
13
+ const tenant = requireTenant();
14
+ const limit = tenant.quotas?.[quotaKey];
15
+ // No limit set, allow
16
+ if (limit === undefined) {
17
+ return next();
18
+ }
19
+ // Check current usage
20
+ const { current } = await checkUsage(tenant.id, quotaKey);
21
+ if (current >= limit) {
22
+ onQuotaExceeded?.(tenant.id, current, limit);
23
+ throw new QuotaExceededError(tenant.id, quotaKey, current, limit);
24
+ }
25
+ return next();
26
+ };
27
+ }
28
+ /**
29
+ * Quota exceeded error
30
+ */
31
+ export class QuotaExceededError extends Error {
32
+ tenantId;
33
+ quotaKey;
34
+ current;
35
+ limit;
36
+ constructor(tenantId, quotaKey, current, limit) {
37
+ super(`Quota exceeded for ${quotaKey}: ${current}/${limit}`);
38
+ this.tenantId = tenantId;
39
+ this.quotaKey = quotaKey;
40
+ this.current = current;
41
+ this.limit = limit;
42
+ this.name = 'QuotaExceededError';
43
+ }
44
+ }
45
+ /**
46
+ * Check multiple quotas at once
47
+ */
48
+ export async function checkQuotas(tenantId, quotas, checkUsage) {
49
+ const results = [];
50
+ for (const [key, limit] of Object.entries(quotas)) {
51
+ if (limit === undefined)
52
+ continue;
53
+ const { current } = await checkUsage(tenantId, key);
54
+ results.push({
55
+ key,
56
+ current,
57
+ limit,
58
+ exceeded: current >= limit,
59
+ });
60
+ }
61
+ return results;
62
+ }
63
+ //# sourceMappingURL=quota-middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-middleware.js","sourceRoot":"","sources":["../../src/middleware/quota-middleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAwB7D;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAA+B;IACnE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;IAE1D,OAAO,KAAK,UAAU,eAAe,CAAI,IAAsB;QAC7D,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC;QAExC,sBAAsB;QACtB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,sBAAsB;QACtB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,EAAE,EAAE,QAAkB,CAAC,CAAC;QAEpE,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;YACrB,eAAe,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YAC7C,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAAE,QAAkB,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9E,CAAC;QAED,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAElC;IACA;IACA;IACA;IAJT,YACS,QAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,KAAa;QAEpB,KAAK,CAAC,sBAAsB,QAAQ,KAAK,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC;QALtD,aAAQ,GAAR,QAAQ,CAAQ;QAChB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,YAAO,GAAP,OAAO,CAAQ;QACf,UAAK,GAAL,KAAK,CAAQ;QAGpB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB,EAChB,MAAoB,EACpB,UAAsB;IAEtB,MAAM,OAAO,GAAG,EAAE,CAAC;IAEnB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAElC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC;YACX,GAAG;YACH,OAAO;YACP,KAAK;YACL,QAAQ,EAAE,OAAO,IAAI,KAAK;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Tenant Middleware
3
+ *
4
+ * Middleware for resolving and setting tenant context.
5
+ */
6
+ import type { IsolationConfig, Tenant } from '../types/index.js';
7
+ import { TenantRequest } from '../context/resolver.js';
8
+ /**
9
+ * Tenant lookup function type
10
+ */
11
+ export type TenantLookup = (tenantId: string) => Promise<Tenant | null>;
12
+ /**
13
+ * Tenant middleware options
14
+ */
15
+ export interface TenantMiddlewareOptions {
16
+ /** Isolation config */
17
+ config: IsolationConfig;
18
+ /** Function to lookup tenant by ID */
19
+ lookupTenant: TenantLookup;
20
+ /** Called when tenant not found */
21
+ onTenantNotFound?: (tenantId: string) => void;
22
+ /** Called when tenant is suspended */
23
+ onTenantSuspended?: (tenant: Tenant) => void;
24
+ }
25
+ /**
26
+ * Create tenant middleware
27
+ */
28
+ export declare function createTenantMiddleware(options: TenantMiddlewareOptions): <T>(request: TenantRequest, next: () => Promise<T>) => Promise<T>;
29
+ /**
30
+ * Tenant not found error
31
+ */
32
+ export declare class TenantNotFoundError extends Error {
33
+ tenantId: string;
34
+ constructor(tenantId: string);
35
+ }
36
+ /**
37
+ * Tenant suspended error
38
+ */
39
+ export declare class TenantSuspendedError extends Error {
40
+ tenantId: string;
41
+ constructor(tenantId: string);
42
+ }
43
+ /**
44
+ * Tenant inactive error
45
+ */
46
+ export declare class TenantInactiveError extends Error {
47
+ tenantId: string;
48
+ status: string;
49
+ constructor(tenantId: string, status: string);
50
+ }
51
+ //# sourceMappingURL=tenant-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/tenant-middleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAiB,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGtE;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,uBAAuB;IACvB,MAAM,EAAE,eAAe,CAAC;IAExB,sCAAsC;IACtC,YAAY,EAAE,YAAY,CAAC;IAE3B,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9C,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,IAG9B,CAAC,EACtC,SAAS,aAAa,EACtB,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,KACrB,OAAO,CAAC,CAAC,CAAC,CA+Bd;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IACzB,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAIpC;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC1B,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAIpC;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IACzB,QAAQ,EAAE,MAAM;IAAS,MAAM,EAAE,MAAM;gBAAvC,QAAQ,EAAE,MAAM,EAAS,MAAM,EAAE,MAAM;CAI3D"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Tenant Middleware
3
+ *
4
+ * Middleware for resolving and setting tenant context.
5
+ */
6
+ import { resolveTenant } from '../context/resolver.js';
7
+ import { createTenantContext, withTenantContext } from '../context/tenant-context.js';
8
+ /**
9
+ * Create tenant middleware
10
+ */
11
+ export function createTenantMiddleware(options) {
12
+ const { config, lookupTenant, onTenantNotFound, onTenantSuspended } = options;
13
+ return async function tenantMiddleware(request, next) {
14
+ // Resolve tenant ID from request
15
+ const tenantId = await resolveTenant(request, config);
16
+ if (!tenantId) {
17
+ // No tenant required (public route or default)
18
+ return next();
19
+ }
20
+ // Lookup tenant
21
+ const tenant = await lookupTenant(tenantId);
22
+ if (!tenant) {
23
+ onTenantNotFound?.(tenantId);
24
+ throw new TenantNotFoundError(tenantId);
25
+ }
26
+ // Check tenant status
27
+ if (tenant.status === 'suspended') {
28
+ onTenantSuspended?.(tenant);
29
+ throw new TenantSuspendedError(tenant.id);
30
+ }
31
+ if (tenant.status !== 'active') {
32
+ throw new TenantInactiveError(tenant.id, tenant.status);
33
+ }
34
+ // Create context and run handler
35
+ const context = createTenantContext(tenant);
36
+ return withTenantContext(context, next);
37
+ };
38
+ }
39
+ /**
40
+ * Tenant not found error
41
+ */
42
+ export class TenantNotFoundError extends Error {
43
+ tenantId;
44
+ constructor(tenantId) {
45
+ super(`Tenant not found: ${tenantId}`);
46
+ this.tenantId = tenantId;
47
+ this.name = 'TenantNotFoundError';
48
+ }
49
+ }
50
+ /**
51
+ * Tenant suspended error
52
+ */
53
+ export class TenantSuspendedError extends Error {
54
+ tenantId;
55
+ constructor(tenantId) {
56
+ super(`Tenant suspended: ${tenantId}`);
57
+ this.tenantId = tenantId;
58
+ this.name = 'TenantSuspendedError';
59
+ }
60
+ }
61
+ /**
62
+ * Tenant inactive error
63
+ */
64
+ export class TenantInactiveError extends Error {
65
+ tenantId;
66
+ status;
67
+ constructor(tenantId, status) {
68
+ super(`Tenant inactive (${status}): ${tenantId}`);
69
+ this.tenantId = tenantId;
70
+ this.status = status;
71
+ this.name = 'TenantInactiveError';
72
+ }
73
+ }
74
+ //# sourceMappingURL=tenant-middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-middleware.js","sourceRoot":"","sources":["../../src/middleware/tenant-middleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,aAAa,EAAiB,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAwBtF;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACrE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC;IAE9E,OAAO,KAAK,UAAU,gBAAgB,CACpC,OAAsB,EACtB,IAAsB;QAEtB,iCAAiC;QACjC,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAEtD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,+CAA+C;YAC/C,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,gBAAgB;QAChB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAE5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,gBAAgB,EAAE,CAAC,QAAQ,CAAC,CAAC;YAC7B,MAAM,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAED,sBAAsB;QACtB,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,iBAAiB,EAAE,CAAC,MAAM,CAAC,CAAC;YAC5B,MAAM,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1D,CAAC;QAED,iCAAiC;QACjC,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5C,OAAO,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACzB;IAAnB,YAAmB,QAAgB;QACjC,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;QADtB,aAAQ,GAAR,QAAQ,CAAQ;QAEjC,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC1B;IAAnB,YAAmB,QAAgB;QACjC,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;QADtB,aAAQ,GAAR,QAAQ,CAAQ;QAEjC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACzB;IAAyB;IAA5C,YAAmB,QAAgB,EAAS,MAAc;QACxD,KAAK,CAAC,oBAAoB,MAAM,MAAM,QAAQ,EAAE,CAAC,CAAC;QADjC,aAAQ,GAAR,QAAQ,CAAQ;QAAS,WAAM,GAAN,MAAM,CAAQ;QAExD,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Tenancy Configuration Types
3
+ */
4
+ import type { IsolationConfig, DatabaseIsolationConfig, StorageIsolationConfig } from './isolation.js';
5
+ import type { TenantQuotas, TenantPlan } from './tenant.js';
6
+ /**
7
+ * Plan configuration
8
+ */
9
+ export interface PlanConfig {
10
+ /** Plan ID */
11
+ id: TenantPlan;
12
+ /** Plan name */
13
+ name: string;
14
+ /** Plan description */
15
+ description?: string;
16
+ /** Default quotas for this plan */
17
+ quotas: TenantQuotas;
18
+ /** Features enabled for this plan */
19
+ features?: Record<string, boolean>;
20
+ /** Monthly price (in cents) */
21
+ priceMonthly?: number;
22
+ /** Yearly price (in cents) */
23
+ priceYearly?: number;
24
+ }
25
+ /**
26
+ * Tenancy configuration
27
+ */
28
+ export interface TenancyConfig {
29
+ /** Enable multi-tenancy */
30
+ enabled: boolean;
31
+ /** Isolation configuration */
32
+ isolation: IsolationConfig;
33
+ /** Database isolation */
34
+ database?: DatabaseIsolationConfig;
35
+ /** Storage isolation */
36
+ storage?: StorageIsolationConfig;
37
+ /** Available plans */
38
+ plans?: PlanConfig[];
39
+ /** Default plan for new tenants */
40
+ defaultPlan?: TenantPlan;
41
+ /** Enforce quotas */
42
+ enforceQuotas?: boolean;
43
+ /** Allow custom domains */
44
+ allowCustomDomains?: boolean;
45
+ /** Require email verification for new tenants */
46
+ requireVerification?: boolean;
47
+ }
48
+ /**
49
+ * Tenancy config input (partial for overrides)
50
+ */
51
+ export interface TenancyConfigInput {
52
+ enabled?: boolean;
53
+ isolation?: Partial<IsolationConfig>;
54
+ database?: Partial<DatabaseIsolationConfig>;
55
+ storage?: Partial<StorageIsolationConfig>;
56
+ plans?: PlanConfig[];
57
+ defaultPlan?: TenantPlan;
58
+ enforceQuotas?: boolean;
59
+ allowCustomDomains?: boolean;
60
+ requireVerification?: boolean;
61
+ }
62
+ /**
63
+ * Tenancy environment config
64
+ */
65
+ export interface TenancyEnvConfig {
66
+ /** Base tenancy config */
67
+ tenancy: TenancyConfig;
68
+ }
69
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACvG,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,cAAc;IACd,EAAE,EAAE,UAAU,CAAC;IAEf,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IAEb,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,mCAAmC;IACnC,MAAM,EAAE,YAAY,CAAC;IAErB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,OAAO,EAAE,OAAO,CAAC;IAEjB,8BAA8B;IAC9B,SAAS,EAAE,eAAe,CAAC;IAE3B,yBAAyB;IACzB,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IAEnC,wBAAwB;IACxB,OAAO,CAAC,EAAE,sBAAsB,CAAC;IAEjC,sBAAsB;IACtB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IAErB,mCAAmC;IACnC,WAAW,CAAC,EAAE,UAAU,CAAC;IAEzB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,2BAA2B;IAC3B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B,iDAAiD;IACjD,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC1C,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0BAA0B;IAC1B,OAAO,EAAE,aAAa,CAAC;CACxB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tenancy Configuration Types
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tenancy Types
3
+ */
4
+ export * from './tenant.js';
5
+ export * from './isolation.js';
6
+ export * from './config.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tenancy Types
3
+ */
4
+ export * from './tenant.js';
5
+ export * from './isolation.js';
6
+ export * from './config.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Isolation Types
3
+ */
4
+ /**
5
+ * Data isolation strategy
6
+ */
7
+ export type IsolationStrategy = 'shared' | 'schema' | 'database' | 'hybrid';
8
+ /**
9
+ * Tenant resolution strategy
10
+ */
11
+ export type ResolutionStrategy = 'subdomain' | 'custom_domain' | 'path' | 'header' | 'query' | 'jwt' | 'custom';
12
+ /**
13
+ * Isolation configuration
14
+ */
15
+ export interface IsolationConfig {
16
+ /** Data isolation strategy */
17
+ strategy: IsolationStrategy;
18
+ /** Tenant resolution strategy */
19
+ resolution: ResolutionStrategy;
20
+ /** Header name for header-based resolution */
21
+ headerName?: string;
22
+ /** Query param name for query-based resolution */
23
+ queryParam?: string;
24
+ /** JWT claim name for JWT-based resolution */
25
+ jwtClaim?: string;
26
+ /** Domain lookup function for custom_domain resolution */
27
+ domainLookup?: (domain: string) => Promise<string | null>;
28
+ /** Custom resolver function */
29
+ customResolver?: (request: unknown) => Promise<string | null>;
30
+ /** Default tenant ID (for public routes) */
31
+ defaultTenantId?: string;
32
+ /** Routes that don't require tenant context */
33
+ publicRoutes?: string[];
34
+ }
35
+ /**
36
+ * Database isolation config
37
+ */
38
+ export interface DatabaseIsolationConfig {
39
+ /** Isolation strategy */
40
+ strategy: IsolationStrategy;
41
+ /** Schema prefix for schema-based isolation */
42
+ schemaPrefix?: string;
43
+ /** Database name template for database-based isolation */
44
+ databaseTemplate?: string;
45
+ /** Connection pool per tenant */
46
+ poolPerTenant?: boolean;
47
+ /** Max connections per tenant pool */
48
+ maxConnectionsPerTenant?: number;
49
+ }
50
+ /**
51
+ * Storage isolation config
52
+ */
53
+ export interface StorageIsolationConfig {
54
+ /** Use separate S3 prefix per tenant */
55
+ prefixByTenant: boolean;
56
+ /** Prefix template (e.g., "tenants/{tenantId}/") */
57
+ prefixTemplate?: string;
58
+ /** Use separate bucket per tenant */
59
+ bucketPerTenant?: boolean;
60
+ }
61
+ //# sourceMappingURL=isolation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../../src/types/isolation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,QAAQ,CAAC;AAEb;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,eAAe,GACf,MAAM,GACN,QAAQ,GACR,OAAO,GACP,KAAK,GACL,QAAQ,CAAC;AAEb;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,QAAQ,EAAE,iBAAiB,CAAC;IAE5B,iCAAiC;IACjC,UAAU,EAAE,kBAAkB,CAAC;IAE/B,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE1D,+BAA+B;IAC/B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE9D,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,yBAAyB;IACzB,QAAQ,EAAE,iBAAiB,CAAC;IAE5B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,iCAAiC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,sCAAsC;IACtC,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,wCAAwC;IACxC,cAAc,EAAE,OAAO,CAAC;IAExB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,qCAAqC;IACrC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Isolation Types
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=isolation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isolation.js","sourceRoot":"","sources":["../../src/types/isolation.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Tenant Types
3
+ */
4
+ /**
5
+ * Tenant status
6
+ */
7
+ export type TenantStatus = 'active' | 'suspended' | 'pending' | 'deleted';
8
+ /**
9
+ * Tenant plan identifier
10
+ */
11
+ export type TenantPlan = string;
12
+ /**
13
+ * Tenant entity
14
+ */
15
+ export interface Tenant {
16
+ /** Unique tenant ID */
17
+ id: string;
18
+ /** Tenant name */
19
+ name: string;
20
+ /** Tenant slug (for subdomain/URL) */
21
+ slug: string;
22
+ /** Tenant status */
23
+ status: TenantStatus;
24
+ /** Subscription plan (project-specific) */
25
+ plan?: string;
26
+ /** Owner user ID */
27
+ ownerId: string;
28
+ /** Custom domain (optional) */
29
+ customDomain?: string;
30
+ /** Tenant settings */
31
+ settings?: TenantSettings;
32
+ /** Quota limits */
33
+ quotas?: TenantQuotas;
34
+ /** Metadata */
35
+ metadata?: Record<string, unknown>;
36
+ /** Created timestamp */
37
+ createdAt: Date;
38
+ /** Updated timestamp */
39
+ updatedAt: Date;
40
+ }
41
+ /**
42
+ * Tenant settings
43
+ */
44
+ export interface TenantSettings {
45
+ /** Locale/language */
46
+ locale?: string;
47
+ /** Timezone */
48
+ timezone?: string;
49
+ /** Branding */
50
+ branding?: {
51
+ logo?: string;
52
+ primaryColor?: string;
53
+ secondaryColor?: string;
54
+ };
55
+ /** Feature flags */
56
+ features?: Record<string, boolean>;
57
+ }
58
+ /**
59
+ * Tenant quotas/limits
60
+ */
61
+ export interface TenantQuotas {
62
+ /** Max users */
63
+ maxUsers?: number;
64
+ /** Max storage (bytes) */
65
+ maxStorage?: number;
66
+ /** Max API requests per month */
67
+ maxApiRequests?: number;
68
+ /** Max projects/resources */
69
+ maxProjects?: number;
70
+ /** Custom quotas */
71
+ [key: string]: number | undefined;
72
+ }
73
+ /**
74
+ * Tenant member
75
+ */
76
+ export interface TenantMember {
77
+ /** User ID */
78
+ userId: string;
79
+ /** Tenant ID */
80
+ tenantId: string;
81
+ /** Role within tenant (project-specific) */
82
+ role: string;
83
+ /** Joined timestamp */
84
+ joinedAt: Date;
85
+ }
86
+ /**
87
+ * Tenant invitation
88
+ */
89
+ export interface TenantInvitation {
90
+ /** Invitation ID */
91
+ id: string;
92
+ /** Tenant ID */
93
+ tenantId: string;
94
+ /** Invitee email */
95
+ email: string;
96
+ /** Assigned role */
97
+ role: string;
98
+ /** Invitation token */
99
+ token: string;
100
+ /** Expiry timestamp */
101
+ expiresAt: Date;
102
+ /** Created timestamp */
103
+ createdAt: Date;
104
+ }
105
+ /**
106
+ * Input for creating a tenant
107
+ */
108
+ export interface CreateTenantInput {
109
+ name: string;
110
+ slug: string;
111
+ ownerId: string;
112
+ plan?: string;
113
+ settings?: TenantSettings;
114
+ metadata?: Record<string, unknown>;
115
+ }
116
+ /**
117
+ * Input for updating a tenant
118
+ */
119
+ export interface UpdateTenantInput {
120
+ name?: string;
121
+ slug?: string;
122
+ status?: TenantStatus;
123
+ plan?: string;
124
+ customDomain?: string;
125
+ settings?: Partial<TenantSettings>;
126
+ quotas?: Partial<TenantQuotas>;
127
+ metadata?: Record<string, unknown>;
128
+ }
129
+ //# sourceMappingURL=tenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/types/tenant.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tenant Types
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=tenant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../src/types/tenant.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@venturekit-pro/tenancy",
3
+ "version": "0.0.0-dev.20260323005827",
4
+ "description": "Multi-tenant utilities for VentureKit",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/venturekit-dev/venturekit-pro.git",
14
+ "directory": "packages/tenancy"
15
+ },
16
+ "publishConfig": {
17
+ "registry": "https://registry.npmjs.org",
18
+ "access": "restricted"
19
+ },
20
+ "license": "Apache-2.0",
21
+ "exports": {
22
+ ".": {
23
+ "import": "./dist/index.js",
24
+ "types": "./dist/index.d.ts"
25
+ }
26
+ },
27
+ "dependencies": {
28
+ "@venturekit/core": "0.0.0-dev.20260312012510"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.10.0",
32
+ "typescript": "^5.3.0"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc",
36
+ "dev": "tsc --watch",
37
+ "clean": "rm -rf dist"
38
+ }
39
+ }