@happyvertical/smrt-tenancy 0.30.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/AGENTS.md +71 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +122 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/adapters/cli.d.ts +178 -0
- package/dist/adapters/cli.d.ts.map +1 -0
- package/dist/adapters/express.d.ts +115 -0
- package/dist/adapters/express.d.ts.map +1 -0
- package/dist/adapters/index.d.ts +22 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +7 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/sveltekit.d.ts +123 -0
- package/dist/adapters/sveltekit.d.ts.map +1 -0
- package/dist/chunks/context-B5CKsmMi.js +190 -0
- package/dist/chunks/context-B5CKsmMi.js.map +1 -0
- package/dist/chunks/sveltekit-9eRH1RLw.js +153 -0
- package/dist/chunks/sveltekit-9eRH1RLw.js.map +1 -0
- package/dist/chunks/testing-C_tV23JW.js +487 -0
- package/dist/chunks/testing-C_tV23JW.js.map +1 -0
- package/dist/context.d.ts +435 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/decorators.d.ts +126 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/enabled-state.d.ts +25 -0
- package/dist/enabled-state.d.ts.map +1 -0
- package/dist/entry-point.d.ts +83 -0
- package/dist/entry-point.d.ts.map +1 -0
- package/dist/fields.d.ts +104 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptor.d.ts +156 -0
- package/dist/interceptor.d.ts.map +1 -0
- package/dist/manifest.json +11 -0
- package/dist/playground.d.ts +2 -0
- package/dist/playground.d.ts.map +1 -0
- package/dist/playground.js +80 -0
- package/dist/playground.js.map +1 -0
- package/dist/registry.d.ts +145 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +65 -0
- package/dist/svelte/components/TenantCard.svelte +272 -0
- package/dist/svelte/components/TenantCard.svelte.d.ts +18 -0
- package/dist/svelte/components/TenantCard.svelte.d.ts.map +1 -0
- package/dist/svelte/components/TenantSwitcher.svelte +68 -0
- package/dist/svelte/components/TenantSwitcher.svelte.d.ts +11 -0
- package/dist/svelte/components/TenantSwitcher.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +5 -0
- package/dist/svelte/i18n.d.ts.map +1 -0
- package/dist/svelte/i18n.js +9 -0
- package/dist/svelte/index.d.ts +15 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +19 -0
- package/dist/svelte/playground.d.ts +70 -0
- package/dist/svelte/playground.d.ts.map +1 -0
- package/dist/svelte/playground.js +75 -0
- package/dist/testing.d.ts +145 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +11 -0
- package/dist/testing.js.map +1 -0
- package/dist/ui.d.ts +21 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +33 -0
- package/dist/ui.js.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import { DatabaseInterface } from '@happyvertical/sql';
|
|
2
|
+
/**
|
|
3
|
+
* Full data stored in tenant context for the current async execution scope.
|
|
4
|
+
*
|
|
5
|
+
* Created by `withTenant()` / `enterTenantContext()` and read by `getCurrentTenant()`,
|
|
6
|
+
* `getTenantId()`, and the interceptor hooks. All fields except `tenantId` and
|
|
7
|
+
* `permissions` are optional and may be populated lazily by higher-level packages
|
|
8
|
+
* (e.g., `smrt-users`).
|
|
9
|
+
*
|
|
10
|
+
* @see withTenant
|
|
11
|
+
* @see MinimalTenantContext
|
|
12
|
+
*/
|
|
13
|
+
export interface TenantContextData {
|
|
14
|
+
/** Current tenant ID (required) */
|
|
15
|
+
tenantId: string;
|
|
16
|
+
/** Current tenant object (lazy-loaded if smrt-users is available) */
|
|
17
|
+
tenant?: unknown;
|
|
18
|
+
/** Current user ID (optional) */
|
|
19
|
+
userId?: string;
|
|
20
|
+
/** Current user object (lazy-loaded if smrt-users is available) */
|
|
21
|
+
user?: unknown;
|
|
22
|
+
/** Resolved permissions for this user in this tenant */
|
|
23
|
+
permissions: Set<string>;
|
|
24
|
+
/** Database connection for this tenant (if database-per-tenant strategy) */
|
|
25
|
+
database?: DatabaseInterface;
|
|
26
|
+
/** Super admin bypass enabled - allows cross-tenant operations */
|
|
27
|
+
superAdminBypass?: boolean;
|
|
28
|
+
/** Custom metadata for application-specific data */
|
|
29
|
+
metadata?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Minimal context accepted by `withTenant()` and `withTenantSync()` when only a
|
|
33
|
+
* tenant ID is known.
|
|
34
|
+
*
|
|
35
|
+
* `permissions` defaults to an empty `Set` when omitted. Use `TenantContextData`
|
|
36
|
+
* when you also need to carry user info, database handles, or resolved permissions.
|
|
37
|
+
*
|
|
38
|
+
* @see TenantContextData
|
|
39
|
+
* @see withTenant
|
|
40
|
+
*/
|
|
41
|
+
export interface MinimalTenantContext {
|
|
42
|
+
/** Tenant identifier. */
|
|
43
|
+
tenantId: string;
|
|
44
|
+
/** Resolved permissions; defaults to an empty Set when omitted. */
|
|
45
|
+
permissions?: Set<string>;
|
|
46
|
+
/** When `true`, tenant auto-filtering is skipped for classes that allow super admin bypass. */
|
|
47
|
+
superAdminBypass?: boolean;
|
|
48
|
+
/** Arbitrary application-specific metadata to carry through the context. */
|
|
49
|
+
metadata?: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the current tenant context for this async execution scope.
|
|
53
|
+
*
|
|
54
|
+
* Returns `undefined` when called outside any tenant scope or inside a
|
|
55
|
+
* `withSystemContext()` block (the system context marker is treated as "no
|
|
56
|
+
* tenant data"). Prefer `requireTenant()` when a context is mandatory.
|
|
57
|
+
*
|
|
58
|
+
* @returns The active `TenantContextData`, or `undefined` if none is set.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const ctx = getCurrentTenant();
|
|
63
|
+
* if (ctx) {
|
|
64
|
+
* console.log('Current tenant:', ctx.tenantId);
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @see requireTenant
|
|
69
|
+
* @see hasTenantContext
|
|
70
|
+
*/
|
|
71
|
+
export declare function getCurrentTenant(): TenantContextData | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Get the current tenant context or throw if one is not available.
|
|
74
|
+
*
|
|
75
|
+
* Use this in business-logic code that must run inside a tenant scope.
|
|
76
|
+
* For a non-throwing alternative use `getCurrentTenant()`.
|
|
77
|
+
*
|
|
78
|
+
* @returns The active `TenantContextData`.
|
|
79
|
+
* @throws {TenantContextError} When no tenant context is set (code is outside
|
|
80
|
+
* any `withTenant()` call or the enclosing middleware has not run).
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* const { tenantId, permissions } = requireTenant();
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @see getCurrentTenant
|
|
88
|
+
* @see requireTenantId
|
|
89
|
+
*/
|
|
90
|
+
export declare function requireTenant(): TenantContextData;
|
|
91
|
+
/**
|
|
92
|
+
* Get the current tenant ID or throw if no tenant context is available.
|
|
93
|
+
*
|
|
94
|
+
* Shorthand for `requireTenant().tenantId`.
|
|
95
|
+
*
|
|
96
|
+
* @returns The active tenant ID string.
|
|
97
|
+
* @throws {TenantContextError} When no tenant context is set.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const tenantId = requireTenantId();
|
|
102
|
+
* const rows = await db.query(`SELECT * FROM docs WHERE tenant_id = ?`, [tenantId]);
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @see getTenantId
|
|
106
|
+
* @see requireTenant
|
|
107
|
+
*/
|
|
108
|
+
export declare function requireTenantId(): string;
|
|
109
|
+
/**
|
|
110
|
+
* Get the current tenant ID without throwing.
|
|
111
|
+
*
|
|
112
|
+
* Returns `undefined` when called outside any tenant scope or inside a
|
|
113
|
+
* `withSystemContext()` block. Use `requireTenantId()` when a missing context
|
|
114
|
+
* should be treated as an error.
|
|
115
|
+
*
|
|
116
|
+
* @returns The active tenant ID, or `undefined` if none is set.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const tenantId = getTenantId();
|
|
121
|
+
* if (tenantId) {
|
|
122
|
+
* // Optional tenant-scoped logic
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @see requireTenantId
|
|
127
|
+
* @see hasTenantContext
|
|
128
|
+
*/
|
|
129
|
+
export declare function getTenantId(): string | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* Check whether the current async execution scope has an active tenant context.
|
|
132
|
+
*
|
|
133
|
+
* Returns `false` both when there is no context at all and when code is running
|
|
134
|
+
* inside `withSystemContext()` (the system marker is not a tenant context).
|
|
135
|
+
*
|
|
136
|
+
* @returns `true` if a `TenantContextData` is active, `false` otherwise.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* if (hasTenantContext()) {
|
|
141
|
+
* console.log('Tenant:', getTenantId());
|
|
142
|
+
* }
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* @see getTenantId
|
|
146
|
+
* @see isSystemContext
|
|
147
|
+
*/
|
|
148
|
+
export declare function hasTenantContext(): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Check whether the current async execution scope was entered via `withSystemContext()`.
|
|
151
|
+
*
|
|
152
|
+
* A system context is explicitly set to bypass all tenant checks; it is distinct
|
|
153
|
+
* from "no context" (undefined store). When the store is undefined the
|
|
154
|
+
* interceptor enforces tenant requirements; when it holds the system marker the
|
|
155
|
+
* interceptor skips all checks.
|
|
156
|
+
*
|
|
157
|
+
* @returns `true` if inside a `withSystemContext()` call, `false` otherwise.
|
|
158
|
+
*
|
|
159
|
+
* @see withSystemContext
|
|
160
|
+
* @see hasTenantContext
|
|
161
|
+
*/
|
|
162
|
+
export declare function isSystemContext(): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Check whether the super admin bypass flag is set in the current tenant context.
|
|
165
|
+
*
|
|
166
|
+
* When `true`, the interceptor skips tenant auto-filtering for classes that have
|
|
167
|
+
* `allowSuperAdminBypass: true` in their `@TenantScoped()` config. Returns
|
|
168
|
+
* `false` inside a system context (no tenant data is available).
|
|
169
|
+
*
|
|
170
|
+
* @returns `true` if super admin bypass is active, `false` otherwise.
|
|
171
|
+
*
|
|
172
|
+
* @see withSuperAdminBypass
|
|
173
|
+
* @see TenantScopedOptions.allowSuperAdminBypass
|
|
174
|
+
*/
|
|
175
|
+
export declare function isSuperAdminBypass(): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Run code within a tenant context (async version)
|
|
178
|
+
*
|
|
179
|
+
* @param context - Tenant context data (at minimum, tenantId)
|
|
180
|
+
* @param fn - Async function to run within the tenant context
|
|
181
|
+
* @returns Promise resolving to the function's return value
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* await withTenant({ tenantId: 'tenant-123' }, async () => {
|
|
186
|
+
* const id = requireTenantId(); // 'tenant-123'
|
|
187
|
+
* await doSomething();
|
|
188
|
+
* });
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export declare function withTenant<T>(context: TenantContextData | MinimalTenantContext, fn: () => Promise<T>): Promise<T>;
|
|
192
|
+
/**
|
|
193
|
+
* Run synchronous code within a tenant context.
|
|
194
|
+
*
|
|
195
|
+
* Prefer `withTenant()` for async code. Use this variant only when the
|
|
196
|
+
* callback must be synchronous (e.g., initializing a module-level value that
|
|
197
|
+
* is consumed synchronously downstream).
|
|
198
|
+
*
|
|
199
|
+
* @param context - Tenant context data (at minimum, `tenantId`).
|
|
200
|
+
* @param fn - Synchronous function to run within the tenant context.
|
|
201
|
+
* @returns The return value of `fn`.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* const result = withTenantSync({ tenantId: 'tenant-123' }, () => {
|
|
206
|
+
* return computeSomethingSync();
|
|
207
|
+
* });
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* @see withTenant
|
|
211
|
+
*/
|
|
212
|
+
export declare function withTenantSync<T>(context: TenantContextData | MinimalTenantContext, fn: () => T): T;
|
|
213
|
+
/**
|
|
214
|
+
* Enter tenant context for the remainder of the current async execution
|
|
215
|
+
*
|
|
216
|
+
* This uses AsyncLocalStorage.enterWith() to establish context that persists
|
|
217
|
+
* until the async resource completes. Useful for Express middleware where
|
|
218
|
+
* the route handler executes after the middleware returns.
|
|
219
|
+
*
|
|
220
|
+
* @param context - Tenant context data
|
|
221
|
+
*
|
|
222
|
+
* @example Express middleware
|
|
223
|
+
* ```typescript
|
|
224
|
+
* app.use((req, res, next) => {
|
|
225
|
+
* const tenantId = req.headers['x-tenant-id'] as string;
|
|
226
|
+
* enterTenantContext({ tenantId });
|
|
227
|
+
* next(); // Route handlers now have tenant context
|
|
228
|
+
* });
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
export declare function enterTenantContext(context: TenantContextData | MinimalTenantContext): void;
|
|
232
|
+
/**
|
|
233
|
+
* Run code in system context (bypasses tenant checks)
|
|
234
|
+
*
|
|
235
|
+
* Use this for:
|
|
236
|
+
* - Migration scripts
|
|
237
|
+
* - Admin tools that need cross-tenant access
|
|
238
|
+
* - Background jobs that process multiple tenants
|
|
239
|
+
*
|
|
240
|
+
* System context is explicitly different from "no context" - it signals
|
|
241
|
+
* that tenant checks should be bypassed, while no context means the
|
|
242
|
+
* interceptor should enforce tenant requirements.
|
|
243
|
+
*
|
|
244
|
+
* @param fn - Async function to run without tenant context
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* await withSystemContext(async () => {
|
|
249
|
+
* // No tenant context - can access all data
|
|
250
|
+
* const allDocuments = await documentCollection.list({});
|
|
251
|
+
* });
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
export declare function withSystemContext<T>(fn: () => Promise<T>): Promise<T>;
|
|
255
|
+
/**
|
|
256
|
+
* Run async code with the super admin bypass flag enabled on the current
|
|
257
|
+
* tenant context.
|
|
258
|
+
*
|
|
259
|
+
* Unlike `withSystemContext()`, this does **not** remove the tenant context —
|
|
260
|
+
* the caller's `tenantId` remains intact. The interceptor skips
|
|
261
|
+
* auto-filtering only for classes that have `allowSuperAdminBypass: true` in
|
|
262
|
+
* their `@TenantScoped()` config.
|
|
263
|
+
*
|
|
264
|
+
* A tenant context must already be active (i.e., this must be called from
|
|
265
|
+
* within a `withTenant()` scope). Use `withSystemContext()` if no tenant
|
|
266
|
+
* context is available at all.
|
|
267
|
+
*
|
|
268
|
+
* @param fn - Async function to run with super admin bypass enabled.
|
|
269
|
+
* @returns Promise resolving to the return value of `fn`.
|
|
270
|
+
* @throws {TenantContextError} If called outside any tenant context.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* await withTenant({ tenantId: 'admin-tenant' }, async () => {
|
|
275
|
+
* await withSuperAdminBypass(async () => {
|
|
276
|
+
* // Can read any tenant's AuditLog (if allowSuperAdminBypass: true)
|
|
277
|
+
* const logs = await auditLogCollection.list({});
|
|
278
|
+
* });
|
|
279
|
+
* });
|
|
280
|
+
* ```
|
|
281
|
+
*
|
|
282
|
+
* @see withSystemContext
|
|
283
|
+
* @see isSuperAdminBypass
|
|
284
|
+
*/
|
|
285
|
+
export declare function withSuperAdminBypass<T>(fn: () => Promise<T>): Promise<T>;
|
|
286
|
+
/**
|
|
287
|
+
* Namespace object providing advanced tenant context utilities.
|
|
288
|
+
*
|
|
289
|
+
* Contains helpers for binding callbacks, inspecting context state, and
|
|
290
|
+
* running code with the context stored in a queued job payload. These
|
|
291
|
+
* utilities supplement the standalone exported functions for situations where
|
|
292
|
+
* async context might otherwise be lost (e.g., `setTimeout`, event emitters,
|
|
293
|
+
* message queue consumers).
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* import { TenantContext } from '@happyvertical/smrt-tenancy';
|
|
298
|
+
*
|
|
299
|
+
* // Preserve context across a setTimeout
|
|
300
|
+
* setTimeout(TenantContext.bind(() => {
|
|
301
|
+
* console.log(getTenantId()); // context is intact
|
|
302
|
+
* }), 500);
|
|
303
|
+
*
|
|
304
|
+
* // Process a queued job
|
|
305
|
+
* await TenantContext.runWithJobContext(job, async () => {
|
|
306
|
+
* await processJob(job);
|
|
307
|
+
* });
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
export declare const TenantContext: {
|
|
311
|
+
/**
|
|
312
|
+
* Bind a callback to the current tenant context
|
|
313
|
+
*
|
|
314
|
+
* Use this when passing callbacks to setTimeout, event emitters,
|
|
315
|
+
* or other APIs that might lose the async context.
|
|
316
|
+
*
|
|
317
|
+
* @param fn - Function to bind to current context
|
|
318
|
+
* @returns Wrapped function that will run in the original context
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* // Without bind - context is lost
|
|
323
|
+
* setTimeout(() => {
|
|
324
|
+
* console.log(getTenantId()); // undefined!
|
|
325
|
+
* }, 1000);
|
|
326
|
+
*
|
|
327
|
+
* // With bind - context is preserved
|
|
328
|
+
* setTimeout(TenantContext.bind(() => {
|
|
329
|
+
* console.log(getTenantId()); // 'tenant-123'
|
|
330
|
+
* }), 1000);
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
bind<T extends (...args: unknown[]) => unknown>(fn: T): T;
|
|
334
|
+
/**
|
|
335
|
+
* Get the current context data (or undefined for system/no context)
|
|
336
|
+
*/
|
|
337
|
+
readonly current: TenantContextData | undefined;
|
|
338
|
+
/**
|
|
339
|
+
* Check if we're in system context
|
|
340
|
+
*/
|
|
341
|
+
readonly isSystem: boolean;
|
|
342
|
+
/**
|
|
343
|
+
* Run code with context from a job/message payload
|
|
344
|
+
*
|
|
345
|
+
* Useful for processing queued jobs that include tenant metadata.
|
|
346
|
+
*
|
|
347
|
+
* @param job - Job object with tenantId in metadata
|
|
348
|
+
* @param fn - Function to run in the job's tenant context
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```typescript
|
|
352
|
+
* const job = await queue.pop();
|
|
353
|
+
* await TenantContext.runWithJobContext(job, async () => {
|
|
354
|
+
* await processJob(job);
|
|
355
|
+
* });
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
runWithJobContext<T>(job: {
|
|
359
|
+
metadata?: {
|
|
360
|
+
tenantId?: string;
|
|
361
|
+
};
|
|
362
|
+
tenantId?: string;
|
|
363
|
+
}, fn: () => Promise<T>): Promise<T>;
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* Error thrown when a tenant context is required but not available.
|
|
367
|
+
*
|
|
368
|
+
* Raised by `requireTenant()`, `requireTenantId()`, and the tenant interceptor
|
|
369
|
+
* when a `@TenantScoped({ mode: 'required' })` operation is attempted outside
|
|
370
|
+
* any `withTenant()` scope.
|
|
371
|
+
*
|
|
372
|
+
* The `code` property is always `'TENANT_CONTEXT_REQUIRED'` and can be used for
|
|
373
|
+
* programmatic error handling.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* try {
|
|
378
|
+
* const tenantId = requireTenantId();
|
|
379
|
+
* } catch (err) {
|
|
380
|
+
* if (err instanceof TenantContextError) {
|
|
381
|
+
* // err.code === 'TENANT_CONTEXT_REQUIRED'
|
|
382
|
+
* }
|
|
383
|
+
* }
|
|
384
|
+
* ```
|
|
385
|
+
*
|
|
386
|
+
* @see requireTenant
|
|
387
|
+
* @see requireTenantId
|
|
388
|
+
* @see TenantIsolationError
|
|
389
|
+
*/
|
|
390
|
+
export declare class TenantContextError extends Error {
|
|
391
|
+
/** Stable error code; always `'TENANT_CONTEXT_REQUIRED'`. */
|
|
392
|
+
readonly code = "TENANT_CONTEXT_REQUIRED";
|
|
393
|
+
constructor(message: string);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Error thrown when a tenant isolation boundary is crossed.
|
|
397
|
+
*
|
|
398
|
+
* Raised by the tenant interceptor when:
|
|
399
|
+
* - A `list()` or `get()` query explicitly filters by a tenant ID that does not
|
|
400
|
+
* match the current context tenant.
|
|
401
|
+
* - A `save()` or `delete()` is attempted on an object whose `tenantId` field
|
|
402
|
+
* belongs to a different tenant than the current context.
|
|
403
|
+
* - A raw SQL query is executed against a tenant-scoped class without an
|
|
404
|
+
* explicit bypass (when `rawQueryPolicy` is `'throw'`).
|
|
405
|
+
*
|
|
406
|
+
* The `code` property is always `'TENANT_ISOLATION_VIOLATION'`.
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* ```typescript
|
|
410
|
+
* try {
|
|
411
|
+
* await collection.list({ where: { tenantId: 'other-tenant' } });
|
|
412
|
+
* } catch (err) {
|
|
413
|
+
* if (err instanceof TenantIsolationError) {
|
|
414
|
+
* // err.tenantId — context tenant
|
|
415
|
+
* // err.attemptedTenantId — tenant that was attempted
|
|
416
|
+
* }
|
|
417
|
+
* }
|
|
418
|
+
* ```
|
|
419
|
+
*
|
|
420
|
+
* @see TenantContextError
|
|
421
|
+
* @see createTenantInterceptor
|
|
422
|
+
*/
|
|
423
|
+
export declare class TenantIsolationError extends Error {
|
|
424
|
+
/** Stable error code; always `'TENANT_ISOLATION_VIOLATION'`. */
|
|
425
|
+
readonly code = "TENANT_ISOLATION_VIOLATION";
|
|
426
|
+
/** The tenant ID that is active in the current context. */
|
|
427
|
+
readonly tenantId?: string;
|
|
428
|
+
/** The tenant ID that was attempted (and rejected). */
|
|
429
|
+
readonly attemptedTenantId?: string;
|
|
430
|
+
constructor(message: string, details?: {
|
|
431
|
+
tenantId?: string;
|
|
432
|
+
attemptedTenantId?: string;
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IAEjB,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,mEAAmE;IACnE,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,wDAAwD;IACxD,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAEzB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAE7B,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,+FAA+F;IAC/F,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAWD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,IAAI,iBAAiB,GAAG,SAAS,CAOhE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,IAAI,iBAAiB,CASjD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,IAAI,MAAM,GAAG,SAAS,CAMhD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAI1C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAM5C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,OAAO,EAAE,iBAAiB,GAAG,oBAAoB,EACjD,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAMZ;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,iBAAiB,GAAG,oBAAoB,EACjD,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAMH;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,iBAAiB,GAAG,oBAAoB,GAChD,IAAI,CAMN;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAG3E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAeZ;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,aAAa;IACxB;;;;;;;;;;;;;;;;;;;;;OAqBG;SACE,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,MAAM,CAAC,GAAG,CAAC;IAazD;;OAEG;sBACY,iBAAiB,GAAG,SAAS;IAI5C;;OAEG;uBACa,OAAO;IAIvB;;;;;;;;;;;;;;;OAeG;sBACqB,CAAC,OAClB;QAAE,QAAQ,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,MACxD,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC;CAWd,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,6BAA6B;gBAE9B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,gEAAgE;IAChE,QAAQ,CAAC,IAAI,gCAAgC;IAC7C,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;gBAGlC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE;CAO9D"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { CompatiblePropertyDecorator } from '@happyvertical/smrt-core';
|
|
2
|
+
import { TenantIdFieldOptions } from './fields.js';
|
|
3
|
+
/**
|
|
4
|
+
* Options accepted by the `@TenantScoped()` class decorator.
|
|
5
|
+
*
|
|
6
|
+
* All fields are optional; defaults match the most restrictive safe behaviour
|
|
7
|
+
* (required mode, auto-filter and auto-populate enabled, no super-admin bypass).
|
|
8
|
+
*
|
|
9
|
+
* @see TenantScoped
|
|
10
|
+
* @see TenantScopedConfig
|
|
11
|
+
*/
|
|
12
|
+
export interface TenantScopedOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Tenancy mode for this class
|
|
15
|
+
* - 'required': Must have tenant context for all operations (default)
|
|
16
|
+
* - 'optional': Works with or without tenant context
|
|
17
|
+
*/
|
|
18
|
+
mode?: 'required' | 'optional';
|
|
19
|
+
/**
|
|
20
|
+
* Field name containing tenant ID
|
|
21
|
+
* @default 'tenantId'
|
|
22
|
+
*/
|
|
23
|
+
field?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Auto-filter all queries by tenant
|
|
26
|
+
* @default true
|
|
27
|
+
*/
|
|
28
|
+
autoFilter?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Auto-populate tenant ID from context on create
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
autoPopulate?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Allow super admin bypass for this class
|
|
36
|
+
* @default false - must be explicitly enabled
|
|
37
|
+
*/
|
|
38
|
+
allowSuperAdminBypass?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Mark a class as tenant-scoped
|
|
42
|
+
*
|
|
43
|
+
* This decorator registers the class with the tenancy system so that:
|
|
44
|
+
* - list()/get() queries are automatically filtered by tenant
|
|
45
|
+
* - save() validates tenant ID matches current context
|
|
46
|
+
* - delete() validates tenant ownership
|
|
47
|
+
* - Raw SQL queries trigger policy enforcement
|
|
48
|
+
*
|
|
49
|
+
* @param options - Configuration options
|
|
50
|
+
*
|
|
51
|
+
* @example Basic usage (required tenancy)
|
|
52
|
+
* ```typescript
|
|
53
|
+
* @smrt()
|
|
54
|
+
* @TenantScoped()
|
|
55
|
+
* class Document extends SmrtObject {
|
|
56
|
+
* @tenantId()
|
|
57
|
+
* tenantId: string = '';
|
|
58
|
+
*
|
|
59
|
+
* title: string = '';
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @example With super admin bypass enabled
|
|
64
|
+
* ```typescript
|
|
65
|
+
* @smrt()
|
|
66
|
+
* @TenantScoped({ allowSuperAdminBypass: true })
|
|
67
|
+
* class AuditLog extends SmrtObject {
|
|
68
|
+
* @tenantId()
|
|
69
|
+
* tenantId: string = '';
|
|
70
|
+
*
|
|
71
|
+
* action: string = '';
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @example Optional tenancy (works with or without context)
|
|
76
|
+
* ```typescript
|
|
77
|
+
* @smrt()
|
|
78
|
+
* @TenantScoped({ mode: 'optional' })
|
|
79
|
+
* class GlobalConfig extends SmrtObject {
|
|
80
|
+
* @tenantId({ nullable: true })
|
|
81
|
+
* tenantId: string | null = null; // null = global, string = tenant-specific
|
|
82
|
+
*
|
|
83
|
+
* key: string = '';
|
|
84
|
+
* value: string = '';
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function TenantScoped(options?: TenantScopedOptions): <T extends Function>(target: T, decoratorContext?: ClassDecoratorContext) => T;
|
|
89
|
+
/**
|
|
90
|
+
* Tenant ID property decorator
|
|
91
|
+
*
|
|
92
|
+
* Marks a property as the tenant identifier field. This decorator registers
|
|
93
|
+
* the field metadata with ObjectRegistry, keeping the property value clean
|
|
94
|
+
* (no descriptor objects that could be accidentally saved to the database).
|
|
95
|
+
*
|
|
96
|
+
* @param options - Field options (nullable, autoFilter, autoPopulate, etc.)
|
|
97
|
+
* @returns Property decorator
|
|
98
|
+
*
|
|
99
|
+
* @example Basic usage (required tenancy)
|
|
100
|
+
* ```typescript
|
|
101
|
+
* @smrt()
|
|
102
|
+
* @TenantScoped()
|
|
103
|
+
* class Document extends SmrtObject {
|
|
104
|
+
* @tenantId()
|
|
105
|
+
* tenantId: string = '';
|
|
106
|
+
*
|
|
107
|
+
* title: string = '';
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @example Nullable tenant ID (for global resources)
|
|
112
|
+
* ```typescript
|
|
113
|
+
* @smrt()
|
|
114
|
+
* @TenantScoped({ mode: 'optional' })
|
|
115
|
+
* class GlobalConfig extends SmrtObject {
|
|
116
|
+
* @tenantId({ nullable: true })
|
|
117
|
+
* tenantId: string | null = null; // null = global, string = tenant-specific
|
|
118
|
+
*
|
|
119
|
+
* key: string = '';
|
|
120
|
+
* }
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* @see https://github.com/happyvertical/smrt/issues/829 - Why decorators over field helpers
|
|
124
|
+
*/
|
|
125
|
+
export declare function tenantId(options?: TenantIdFieldOptions): CompatiblePropertyDecorator;
|
|
126
|
+
//# sourceMappingURL=decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAEL,KAAK,2BAA2B,EAKjC,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMxD;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IAE/B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,IACpD,CAAC,SAAS,QAAQ,EACxB,QAAQ,CAAC,EACT,mBAAmB,qBAAqB,KACvC,CAAC,CAoBL;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,oBAAyB,GA8BnD,2BAA2B,CAClC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tenancy-enabled flag.
|
|
3
|
+
*
|
|
4
|
+
* Holds the single boolean toggled by `enableTenancy()` / `disableTenancy()`.
|
|
5
|
+
* It lives in its own leaf module (importing nothing from the package) so that
|
|
6
|
+
* both `interceptor.ts` and `entry-point.ts` can read it without forming a
|
|
7
|
+
* circular import: `interceptor.ts` imports `runTenantScopedEntryPoint` from
|
|
8
|
+
* `entry-point.ts`, and `entry-point.ts` needs the enabled flag — routing the
|
|
9
|
+
* flag through here keeps that dependency one-directional.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Set the global tenancy-enabled flag. Internal — called by `enableTenancy()` /
|
|
13
|
+
* `disableTenancy()` in `interceptor.ts`.
|
|
14
|
+
*
|
|
15
|
+
* @param value - `true` to mark tenancy enabled, `false` to clear it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function setTenancyEnabled(value: boolean): void;
|
|
18
|
+
/**
|
|
19
|
+
* Return `true` if tenant enforcement is currently active.
|
|
20
|
+
*
|
|
21
|
+
* @returns Whether `enableTenancy()` has been called without a later
|
|
22
|
+
* `disableTenancy()`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isTenancyEnabled(): boolean;
|
|
25
|
+
//# sourceMappingURL=enabled-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enabled-state.d.ts","sourceRoot":"","sources":["../src/enabled-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed tenant-context establishment for non-web entry points (#1554).
|
|
3
|
+
*
|
|
4
|
+
* The SvelteKit/Express adapters establish tenant context from the authenticated
|
|
5
|
+
* request principal, so the web surface of a `@TenantScoped({ mode: 'optional' })`
|
|
6
|
+
* model never reads across tenants without an active context. The generated
|
|
7
|
+
* **CLI** and **MCP** entry points have no request principal, so an invocation
|
|
8
|
+
* with no active context would fall through the interceptor's optional-mode
|
|
9
|
+
* pass-through and return rows across **all** tenants.
|
|
10
|
+
*
|
|
11
|
+
* `runTenantScopedEntryPoint()` closes that gap. It is the single fail-closed
|
|
12
|
+
* gate both generated surfaces wrap their per-command/per-tool execution in.
|
|
13
|
+
*
|
|
14
|
+
* @see createCliContext for the richer CLI runner (resolveTenantId, super-admin).
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Inputs for {@link runTenantScopedEntryPoint}.
|
|
18
|
+
*
|
|
19
|
+
* Provide **either** `className` (the gate resolves tenant-scoping from the
|
|
20
|
+
* authoritative tenancy registry — the same source the interceptor uses, so it
|
|
21
|
+
* covers both `@TenantScoped` and `@smrt({ tenantScoped })` registrations) or an
|
|
22
|
+
* explicit `tenantScoped` boolean (when the caller already resolved it, e.g. a
|
|
23
|
+
* build-time generated surface). An explicit boolean wins when both are given.
|
|
24
|
+
*/
|
|
25
|
+
export interface TenantEntryPointOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Class name of the target model. When provided, tenant-scoping is resolved
|
|
28
|
+
* via `isTenantScopedClass(className)`.
|
|
29
|
+
*/
|
|
30
|
+
className?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Explicit tenant-scoping decision. Overrides `className` resolution when set.
|
|
33
|
+
* Non-scoped models always pass through unchanged — the gate is a no-op.
|
|
34
|
+
*/
|
|
35
|
+
tenantScoped?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Explicit operator-provided tenant selector (CLI `--tenant <id>`, MCP
|
|
38
|
+
* `context.tenantId`). When present (and no context is already active) the
|
|
39
|
+
* function runs inside this tenant's context.
|
|
40
|
+
*/
|
|
41
|
+
tenantId?: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Explicit operator opt-in to cross-tenant / system access (CLI
|
|
44
|
+
* `--all-tenants`, an MCP host that trusts the caller as an operator). When
|
|
45
|
+
* set the function runs in system context, bypassing tenant filtering.
|
|
46
|
+
*
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
allowCrossTenant?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Human-facing surface name used in the fail-closed error message, e.g.
|
|
52
|
+
* `'CLI'` or `'MCP'`.
|
|
53
|
+
*
|
|
54
|
+
* @default 'entry point'
|
|
55
|
+
*/
|
|
56
|
+
surface?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Run `fn` inside an appropriate tenant context for a generated CLI/MCP entry
|
|
60
|
+
* point, failing closed for tenant-scoped models when no authorized context can
|
|
61
|
+
* be established.
|
|
62
|
+
*
|
|
63
|
+
* Resolution order (tenant-scoped models only):
|
|
64
|
+
* 1. A tenant context is already active, or an explicit `withSystemContext()`
|
|
65
|
+
* bypass is in effect (e.g. `runAsSystem()`, migrations) → run as-is.
|
|
66
|
+
* 2. `allowCrossTenant` was explicitly set → run in system context. Checked
|
|
67
|
+
* before `tenantId` so an explicit cross-tenant opt-in wins over a default
|
|
68
|
+
* principal/host tenant rather than being silently scoped.
|
|
69
|
+
* 3. An explicit `tenantId` was provided → run inside that tenant.
|
|
70
|
+
* 4. Tenancy is enabled but none of the above → **throw** `TenantContextError`
|
|
71
|
+
* (the fail-closed branch — never silently read across tenants).
|
|
72
|
+
* 5. Tenancy is disabled (single-/no-tenant deployment) → pass through.
|
|
73
|
+
*
|
|
74
|
+
* Non-tenant-scoped models always pass straight through.
|
|
75
|
+
*
|
|
76
|
+
* @param options - {@link TenantEntryPointOptions}.
|
|
77
|
+
* @param fn - The command/tool body to execute.
|
|
78
|
+
* @returns The resolved value of `fn`.
|
|
79
|
+
* @throws {TenantContextError} When a tenant-scoped model is reached with
|
|
80
|
+
* tenancy enabled and no tenant/cross-tenant selector.
|
|
81
|
+
*/
|
|
82
|
+
export declare function runTenantScopedEntryPoint<T>(options: TenantEntryPointOptions, fn: () => Promise<T>): Promise<T>;
|
|
83
|
+
//# sourceMappingURL=entry-point.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-point.d.ts","sourceRoot":"","sources":["../src/entry-point.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAYH;;;;;;;;GAQG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,OAAO,EAAE,uBAAuB,EAChC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAgDZ"}
|