drizzle-multitenant 1.1.0 → 1.3.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/README.md +28 -8
- package/dist/cli/index.js +2001 -2442
- package/dist/{context-Vki959ri.d.ts → context-BBLPNjmk.d.ts} +1 -1
- package/dist/cross-schema/index.js +1 -426
- package/dist/export/index.d.ts +395 -0
- package/dist/export/index.js +9 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +149 -2437
- package/dist/integrations/express.d.ts +3 -3
- package/dist/integrations/express.js +1 -110
- package/dist/integrations/fastify.d.ts +3 -3
- package/dist/integrations/fastify.js +1 -236
- package/dist/integrations/hono.js +0 -3
- package/dist/integrations/nestjs/index.d.ts +1 -1
- package/dist/integrations/nestjs/index.js +3 -10759
- package/dist/lint/index.d.ts +475 -0
- package/dist/lint/index.js +5 -0
- package/dist/metrics/index.d.ts +530 -0
- package/dist/metrics/index.js +3 -0
- package/dist/migrator/index.d.ts +1087 -270
- package/dist/migrator/index.js +149 -970
- package/dist/migrator-B7oPKe73.d.ts +1067 -0
- package/dist/scaffold/index.d.ts +330 -0
- package/dist/scaffold/index.js +277 -0
- package/dist/{types-BhK96FPC.d.ts → types-CGqsPe2Q.d.ts} +49 -1
- package/package.json +18 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cross-schema/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integrations/express.js.map +0 -1
- package/dist/integrations/fastify.js.map +0 -1
- package/dist/integrations/hono.js.map +0 -1
- package/dist/integrations/nestjs/index.js.map +0 -1
- package/dist/migrator/index.js.map +0 -1
|
@@ -0,0 +1,1067 @@
|
|
|
1
|
+
import { C as Config } from './types-CGqsPe2Q.js';
|
|
2
|
+
import { Pool } from 'pg';
|
|
3
|
+
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Table format for tracking migrations
|
|
7
|
+
* - "name": drizzle-multitenant native (filename-based)
|
|
8
|
+
* - "hash": SHA-256 hash with timestamp
|
|
9
|
+
* - "drizzle-kit": Exact drizzle-kit format (hash + bigint timestamp)
|
|
10
|
+
*/
|
|
11
|
+
type TableFormat = 'name' | 'hash' | 'drizzle-kit';
|
|
12
|
+
/**
|
|
13
|
+
* Detected table format information
|
|
14
|
+
*/
|
|
15
|
+
interface DetectedFormat {
|
|
16
|
+
/** The detected format type */
|
|
17
|
+
format: TableFormat;
|
|
18
|
+
/** The table name */
|
|
19
|
+
tableName: string;
|
|
20
|
+
/** Column configuration */
|
|
21
|
+
columns: {
|
|
22
|
+
/** Column used for identifying migrations */
|
|
23
|
+
identifier: 'name' | 'hash';
|
|
24
|
+
/** Column used for timestamp */
|
|
25
|
+
timestamp: 'applied_at' | 'created_at';
|
|
26
|
+
/** Data type of timestamp column */
|
|
27
|
+
timestampType: 'timestamp' | 'bigint';
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Default format configuration for new tables
|
|
32
|
+
*/
|
|
33
|
+
declare const DEFAULT_FORMAT: DetectedFormat;
|
|
34
|
+
/**
|
|
35
|
+
* drizzle-kit format configuration
|
|
36
|
+
*/
|
|
37
|
+
declare const DRIZZLE_KIT_FORMAT: DetectedFormat;
|
|
38
|
+
/**
|
|
39
|
+
* Detect the format of an existing migrations table
|
|
40
|
+
*
|
|
41
|
+
* @param pool - Database connection pool
|
|
42
|
+
* @param schemaName - Schema to check
|
|
43
|
+
* @param tableName - Migrations table name
|
|
44
|
+
* @returns Detected format or null if table doesn't exist
|
|
45
|
+
*/
|
|
46
|
+
declare function detectTableFormat(pool: Pool, schemaName: string, tableName: string): Promise<DetectedFormat | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Get the format configuration for a specific format type
|
|
49
|
+
*/
|
|
50
|
+
declare function getFormatConfig(format: TableFormat, tableName?: string): DetectedFormat;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Types for tenant cloning module
|
|
54
|
+
* @module clone/types
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Value to use for anonymization (v1: null or fixed value)
|
|
59
|
+
*/
|
|
60
|
+
type AnonymizeValue = null | string | number | boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Anonymization rules by table and column
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const rules: AnonymizeRules = {
|
|
67
|
+
* users: {
|
|
68
|
+
* email: null,
|
|
69
|
+
* phone: null,
|
|
70
|
+
* ssn: '000-00-0000',
|
|
71
|
+
* },
|
|
72
|
+
* payments: {
|
|
73
|
+
* card_number: null,
|
|
74
|
+
* },
|
|
75
|
+
* };
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
interface AnonymizeRules {
|
|
79
|
+
[tableName: string]: {
|
|
80
|
+
[columnName: string]: AnonymizeValue;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Anonymization options
|
|
85
|
+
*/
|
|
86
|
+
interface AnonymizeOptions {
|
|
87
|
+
/** Enable anonymization */
|
|
88
|
+
enabled: boolean;
|
|
89
|
+
/** Rules per table/column */
|
|
90
|
+
rules?: AnonymizeRules;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Options for cloning a tenant
|
|
94
|
+
*/
|
|
95
|
+
interface CloneTenantOptions {
|
|
96
|
+
/** Include data (default: false, schema only) */
|
|
97
|
+
includeData?: boolean;
|
|
98
|
+
/** Anonymize sensitive data */
|
|
99
|
+
anonymize?: AnonymizeOptions;
|
|
100
|
+
/** Tables to exclude from cloning */
|
|
101
|
+
excludeTables?: string[];
|
|
102
|
+
/** Progress callback */
|
|
103
|
+
onProgress?: CloneProgressCallback;
|
|
104
|
+
/** Error handler */
|
|
105
|
+
onError?: (error: Error) => 'continue' | 'abort';
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Progress status for cloning operation
|
|
109
|
+
*/
|
|
110
|
+
type CloneProgressStatus = 'starting' | 'introspecting' | 'creating_schema' | 'creating_tables' | 'creating_indexes' | 'creating_constraints' | 'copying_data' | 'completed' | 'failed';
|
|
111
|
+
/**
|
|
112
|
+
* Progress callback for cloning
|
|
113
|
+
*/
|
|
114
|
+
type CloneProgressCallback = (status: CloneProgressStatus, details?: {
|
|
115
|
+
table?: string;
|
|
116
|
+
progress?: number;
|
|
117
|
+
total?: number;
|
|
118
|
+
}) => void;
|
|
119
|
+
/**
|
|
120
|
+
* Result of cloning a tenant
|
|
121
|
+
*/
|
|
122
|
+
interface CloneTenantResult {
|
|
123
|
+
/** Source tenant ID */
|
|
124
|
+
sourceTenant: string;
|
|
125
|
+
/** Target tenant ID */
|
|
126
|
+
targetTenant: string;
|
|
127
|
+
/** Target schema name */
|
|
128
|
+
targetSchema: string;
|
|
129
|
+
/** Whether the operation was successful */
|
|
130
|
+
success: boolean;
|
|
131
|
+
/** Error message if failed */
|
|
132
|
+
error?: string;
|
|
133
|
+
/** Tables cloned */
|
|
134
|
+
tables: string[];
|
|
135
|
+
/** Number of rows copied (if includeData) */
|
|
136
|
+
rowsCopied?: number;
|
|
137
|
+
/** Duration in milliseconds */
|
|
138
|
+
durationMs: number;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Configuration for Cloner
|
|
142
|
+
*/
|
|
143
|
+
interface ClonerConfig {
|
|
144
|
+
/** Migrations table name (excluded from data copy) */
|
|
145
|
+
migrationsTable?: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Dependencies for Cloner
|
|
149
|
+
*/
|
|
150
|
+
interface ClonerDependencies {
|
|
151
|
+
/** Create pool for specific schema */
|
|
152
|
+
createPool: (schemaName: string) => Promise<Pool>;
|
|
153
|
+
/** Create pool without schema (root) */
|
|
154
|
+
createRootPool: () => Promise<Pool>;
|
|
155
|
+
/** Schema name template function */
|
|
156
|
+
schemaNameTemplate: (tenantId: string) => string;
|
|
157
|
+
/** Check if schema exists */
|
|
158
|
+
schemaExists: (tenantId: string) => Promise<boolean>;
|
|
159
|
+
/** Create schema */
|
|
160
|
+
createSchema: (tenantId: string) => Promise<void>;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Seed function signature
|
|
165
|
+
* Called with the tenant database instance and tenant ID
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const seed: SeedFunction = async (db, tenantId) => {
|
|
170
|
+
* await db.insert(roles).values([
|
|
171
|
+
* { name: 'admin', permissions: ['*'] },
|
|
172
|
+
* { name: 'user', permissions: ['read'] },
|
|
173
|
+
* ]);
|
|
174
|
+
* };
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
type SeedFunction<TSchema extends Record<string, unknown> = Record<string, unknown>> = (db: PostgresJsDatabase<TSchema>, tenantId: string) => Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Seed result for a single tenant
|
|
180
|
+
*/
|
|
181
|
+
interface TenantSeedResult {
|
|
182
|
+
tenantId: string;
|
|
183
|
+
schemaName: string;
|
|
184
|
+
success: boolean;
|
|
185
|
+
error?: string;
|
|
186
|
+
durationMs: number;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Aggregate seed results
|
|
190
|
+
*/
|
|
191
|
+
interface SeedResults {
|
|
192
|
+
total: number;
|
|
193
|
+
succeeded: number;
|
|
194
|
+
failed: number;
|
|
195
|
+
skipped: number;
|
|
196
|
+
details: TenantSeedResult[];
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Options for seed operations
|
|
200
|
+
*/
|
|
201
|
+
interface SeedOptions {
|
|
202
|
+
/** Number of concurrent seed operations */
|
|
203
|
+
concurrency?: number;
|
|
204
|
+
/** Progress callback */
|
|
205
|
+
onProgress?: (tenantId: string, status: 'starting' | 'seeding' | 'completed' | 'failed' | 'skipped') => void;
|
|
206
|
+
/** Error handler */
|
|
207
|
+
onError?: (tenantId: string, error: Error) => 'continue' | 'abort';
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Migration file metadata
|
|
211
|
+
*/
|
|
212
|
+
interface MigrationFile {
|
|
213
|
+
/** Migration file name */
|
|
214
|
+
name: string;
|
|
215
|
+
/** Full file path */
|
|
216
|
+
path: string;
|
|
217
|
+
/** SQL content */
|
|
218
|
+
sql: string;
|
|
219
|
+
/** Timestamp extracted from filename */
|
|
220
|
+
timestamp: number;
|
|
221
|
+
/** SHA-256 hash of file content (for drizzle-kit compatibility) */
|
|
222
|
+
hash: string;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Migration status for a tenant
|
|
226
|
+
*/
|
|
227
|
+
interface TenantMigrationStatus {
|
|
228
|
+
tenantId: string;
|
|
229
|
+
schemaName: string;
|
|
230
|
+
appliedCount: number;
|
|
231
|
+
pendingCount: number;
|
|
232
|
+
pendingMigrations: string[];
|
|
233
|
+
status: 'ok' | 'behind' | 'error';
|
|
234
|
+
error?: string;
|
|
235
|
+
/** Detected table format (null for new tenants without migrations table) */
|
|
236
|
+
format: TableFormat | null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Migration result for a single tenant
|
|
240
|
+
*/
|
|
241
|
+
interface TenantMigrationResult {
|
|
242
|
+
tenantId: string;
|
|
243
|
+
schemaName: string;
|
|
244
|
+
success: boolean;
|
|
245
|
+
appliedMigrations: string[];
|
|
246
|
+
error?: string;
|
|
247
|
+
durationMs: number;
|
|
248
|
+
/** Table format used for this migration */
|
|
249
|
+
format?: TableFormat;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Aggregate migration results
|
|
253
|
+
*/
|
|
254
|
+
interface MigrationResults {
|
|
255
|
+
total: number;
|
|
256
|
+
succeeded: number;
|
|
257
|
+
failed: number;
|
|
258
|
+
skipped: number;
|
|
259
|
+
details: TenantMigrationResult[];
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Progress callback for migrations
|
|
263
|
+
*/
|
|
264
|
+
type MigrationProgressCallback = (tenantId: string, status: 'starting' | 'migrating' | 'completed' | 'failed' | 'skipped', migrationName?: string) => void;
|
|
265
|
+
/**
|
|
266
|
+
* Error handler for migrations
|
|
267
|
+
*/
|
|
268
|
+
type MigrationErrorHandler = (tenantId: string, error: Error) => 'continue' | 'abort';
|
|
269
|
+
/**
|
|
270
|
+
* Migration hooks
|
|
271
|
+
*/
|
|
272
|
+
interface MigrationHooks {
|
|
273
|
+
/** Called before migrating a tenant */
|
|
274
|
+
beforeTenant?: (tenantId: string) => void | Promise<void>;
|
|
275
|
+
/** Called after migrating a tenant */
|
|
276
|
+
afterTenant?: (tenantId: string, result: TenantMigrationResult) => void | Promise<void>;
|
|
277
|
+
/** Called before applying a migration */
|
|
278
|
+
beforeMigration?: (tenantId: string, migrationName: string) => void | Promise<void>;
|
|
279
|
+
/** Called after applying a migration */
|
|
280
|
+
afterMigration?: (tenantId: string, migrationName: string, durationMs: number) => void | Promise<void>;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Migrator configuration
|
|
284
|
+
*/
|
|
285
|
+
interface MigratorConfig {
|
|
286
|
+
/** Path to tenant migrations folder */
|
|
287
|
+
migrationsFolder: string;
|
|
288
|
+
/** Table name for tracking migrations */
|
|
289
|
+
migrationsTable?: string;
|
|
290
|
+
/** Function to discover tenant IDs */
|
|
291
|
+
tenantDiscovery: () => Promise<string[]>;
|
|
292
|
+
/** Migration hooks */
|
|
293
|
+
hooks?: MigrationHooks;
|
|
294
|
+
/**
|
|
295
|
+
* Table format for tracking migrations
|
|
296
|
+
* - "auto": Auto-detect existing format, use defaultFormat for new tables
|
|
297
|
+
* - "name": Use filename (drizzle-multitenant native)
|
|
298
|
+
* - "hash": Use SHA-256 hash
|
|
299
|
+
* - "drizzle-kit": Exact drizzle-kit format (hash + bigint timestamp)
|
|
300
|
+
* @default "auto"
|
|
301
|
+
*/
|
|
302
|
+
tableFormat?: 'auto' | TableFormat;
|
|
303
|
+
/**
|
|
304
|
+
* When using "auto" format and no table exists, which format to create
|
|
305
|
+
* @default "name"
|
|
306
|
+
*/
|
|
307
|
+
defaultFormat?: TableFormat;
|
|
308
|
+
/**
|
|
309
|
+
* Path to shared schema migrations folder
|
|
310
|
+
* If provided, enables shared schema migration support
|
|
311
|
+
*/
|
|
312
|
+
sharedMigrationsFolder?: string;
|
|
313
|
+
/**
|
|
314
|
+
* Table name for tracking shared migrations
|
|
315
|
+
* @default "__drizzle_shared_migrations"
|
|
316
|
+
*/
|
|
317
|
+
sharedMigrationsTable?: string;
|
|
318
|
+
/**
|
|
319
|
+
* Hooks for shared schema migrations
|
|
320
|
+
*/
|
|
321
|
+
sharedHooks?: SharedMigrationHooks;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Migrate options
|
|
325
|
+
*/
|
|
326
|
+
interface MigrateOptions {
|
|
327
|
+
/** Number of concurrent migrations */
|
|
328
|
+
concurrency?: number;
|
|
329
|
+
/** Progress callback */
|
|
330
|
+
onProgress?: MigrationProgressCallback;
|
|
331
|
+
/** Error handler */
|
|
332
|
+
onError?: MigrationErrorHandler;
|
|
333
|
+
/** Dry run mode */
|
|
334
|
+
dryRun?: boolean;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Tenant creation options
|
|
338
|
+
*/
|
|
339
|
+
interface CreateTenantOptions {
|
|
340
|
+
/** Apply all migrations after creating schema */
|
|
341
|
+
migrate?: boolean;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Tenant drop options
|
|
345
|
+
*/
|
|
346
|
+
interface DropTenantOptions {
|
|
347
|
+
/** Skip confirmation (force drop) */
|
|
348
|
+
force?: boolean;
|
|
349
|
+
/** Cascade drop */
|
|
350
|
+
cascade?: boolean;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Applied migration record
|
|
354
|
+
*/
|
|
355
|
+
interface AppliedMigration {
|
|
356
|
+
id: number;
|
|
357
|
+
/** Migration identifier (name or hash depending on format) */
|
|
358
|
+
identifier: string;
|
|
359
|
+
/** Migration name (only available in name-based format) */
|
|
360
|
+
name?: string;
|
|
361
|
+
/** Migration hash (only available in hash-based format) */
|
|
362
|
+
hash?: string;
|
|
363
|
+
appliedAt: Date;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Sync status for a single tenant
|
|
367
|
+
*/
|
|
368
|
+
interface TenantSyncStatus {
|
|
369
|
+
tenantId: string;
|
|
370
|
+
schemaName: string;
|
|
371
|
+
/** Migrations in disk but not tracked in database */
|
|
372
|
+
missing: string[];
|
|
373
|
+
/** Migrations tracked in database but not found in disk */
|
|
374
|
+
orphans: string[];
|
|
375
|
+
/** Whether the tenant is in sync */
|
|
376
|
+
inSync: boolean;
|
|
377
|
+
/** Table format used */
|
|
378
|
+
format: TableFormat | null;
|
|
379
|
+
/** Error if any */
|
|
380
|
+
error?: string;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Aggregate sync status
|
|
384
|
+
*/
|
|
385
|
+
interface SyncStatus {
|
|
386
|
+
total: number;
|
|
387
|
+
inSync: number;
|
|
388
|
+
outOfSync: number;
|
|
389
|
+
error: number;
|
|
390
|
+
details: TenantSyncStatus[];
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Sync result for a single tenant
|
|
394
|
+
*/
|
|
395
|
+
interface TenantSyncResult {
|
|
396
|
+
tenantId: string;
|
|
397
|
+
schemaName: string;
|
|
398
|
+
success: boolean;
|
|
399
|
+
/** Migrations that were marked as applied */
|
|
400
|
+
markedMigrations: string[];
|
|
401
|
+
/** Orphan records that were removed */
|
|
402
|
+
removedOrphans: string[];
|
|
403
|
+
error?: string;
|
|
404
|
+
durationMs: number;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Aggregate sync results
|
|
408
|
+
*/
|
|
409
|
+
interface SyncResults {
|
|
410
|
+
total: number;
|
|
411
|
+
succeeded: number;
|
|
412
|
+
failed: number;
|
|
413
|
+
details: TenantSyncResult[];
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Options for sync operations
|
|
417
|
+
*/
|
|
418
|
+
interface SyncOptions {
|
|
419
|
+
/** Number of concurrent operations */
|
|
420
|
+
concurrency?: number;
|
|
421
|
+
/** Progress callback */
|
|
422
|
+
onProgress?: (tenantId: string, status: 'starting' | 'syncing' | 'completed' | 'failed') => void;
|
|
423
|
+
/** Error handler */
|
|
424
|
+
onError?: MigrationErrorHandler;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Column information from database introspection
|
|
428
|
+
*/
|
|
429
|
+
interface ColumnInfo {
|
|
430
|
+
/** Column name */
|
|
431
|
+
name: string;
|
|
432
|
+
/** PostgreSQL data type */
|
|
433
|
+
dataType: string;
|
|
434
|
+
/** Full data type (e.g., varchar(255)) */
|
|
435
|
+
udtName: string;
|
|
436
|
+
/** Whether column is nullable */
|
|
437
|
+
isNullable: boolean;
|
|
438
|
+
/** Default value expression */
|
|
439
|
+
columnDefault: string | null;
|
|
440
|
+
/** Character maximum length for varchar/char */
|
|
441
|
+
characterMaximumLength: number | null;
|
|
442
|
+
/** Numeric precision for numeric types */
|
|
443
|
+
numericPrecision: number | null;
|
|
444
|
+
/** Numeric scale for numeric types */
|
|
445
|
+
numericScale: number | null;
|
|
446
|
+
/** Ordinal position in table */
|
|
447
|
+
ordinalPosition: number;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Index information from database introspection
|
|
451
|
+
*/
|
|
452
|
+
interface IndexInfo {
|
|
453
|
+
/** Index name */
|
|
454
|
+
name: string;
|
|
455
|
+
/** Column names in the index */
|
|
456
|
+
columns: string[];
|
|
457
|
+
/** Whether index is unique */
|
|
458
|
+
isUnique: boolean;
|
|
459
|
+
/** Whether index is primary key */
|
|
460
|
+
isPrimary: boolean;
|
|
461
|
+
/** Index definition SQL */
|
|
462
|
+
definition: string;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Constraint information from database introspection
|
|
466
|
+
*/
|
|
467
|
+
interface ConstraintInfo {
|
|
468
|
+
/** Constraint name */
|
|
469
|
+
name: string;
|
|
470
|
+
/** Constraint type (PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK) */
|
|
471
|
+
type: 'PRIMARY KEY' | 'FOREIGN KEY' | 'UNIQUE' | 'CHECK';
|
|
472
|
+
/** Columns involved in constraint */
|
|
473
|
+
columns: string[];
|
|
474
|
+
/** Foreign table (for foreign keys) */
|
|
475
|
+
foreignTable?: string;
|
|
476
|
+
/** Foreign columns (for foreign keys) */
|
|
477
|
+
foreignColumns?: string[];
|
|
478
|
+
/** Check expression (for check constraints) */
|
|
479
|
+
checkExpression?: string;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Table schema information
|
|
483
|
+
*/
|
|
484
|
+
interface TableSchema {
|
|
485
|
+
/** Table name */
|
|
486
|
+
name: string;
|
|
487
|
+
/** Columns in the table */
|
|
488
|
+
columns: ColumnInfo[];
|
|
489
|
+
/** Indexes on the table */
|
|
490
|
+
indexes: IndexInfo[];
|
|
491
|
+
/** Constraints on the table */
|
|
492
|
+
constraints: ConstraintInfo[];
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Full schema for a tenant
|
|
496
|
+
*/
|
|
497
|
+
interface TenantSchema {
|
|
498
|
+
/** Tenant ID */
|
|
499
|
+
tenantId: string;
|
|
500
|
+
/** Schema name */
|
|
501
|
+
schemaName: string;
|
|
502
|
+
/** Tables in the schema */
|
|
503
|
+
tables: TableSchema[];
|
|
504
|
+
/** Introspection timestamp */
|
|
505
|
+
introspectedAt: Date;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Column drift details
|
|
509
|
+
*/
|
|
510
|
+
interface ColumnDrift {
|
|
511
|
+
/** Column name */
|
|
512
|
+
column: string;
|
|
513
|
+
/** Type of drift */
|
|
514
|
+
type: 'missing' | 'extra' | 'type_mismatch' | 'nullable_mismatch' | 'default_mismatch';
|
|
515
|
+
/** Expected value (from reference) */
|
|
516
|
+
expected?: string | boolean | null;
|
|
517
|
+
/** Actual value (from tenant) */
|
|
518
|
+
actual?: string | boolean | null;
|
|
519
|
+
/** Human-readable description */
|
|
520
|
+
description: string;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Index drift details
|
|
524
|
+
*/
|
|
525
|
+
interface IndexDrift {
|
|
526
|
+
/** Index name */
|
|
527
|
+
index: string;
|
|
528
|
+
/** Type of drift */
|
|
529
|
+
type: 'missing' | 'extra' | 'definition_mismatch';
|
|
530
|
+
/** Expected definition */
|
|
531
|
+
expected?: string;
|
|
532
|
+
/** Actual definition */
|
|
533
|
+
actual?: string;
|
|
534
|
+
/** Human-readable description */
|
|
535
|
+
description: string;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Constraint drift details
|
|
539
|
+
*/
|
|
540
|
+
interface ConstraintDrift {
|
|
541
|
+
/** Constraint name */
|
|
542
|
+
constraint: string;
|
|
543
|
+
/** Type of drift */
|
|
544
|
+
type: 'missing' | 'extra' | 'definition_mismatch';
|
|
545
|
+
/** Expected details */
|
|
546
|
+
expected?: string;
|
|
547
|
+
/** Actual details */
|
|
548
|
+
actual?: string;
|
|
549
|
+
/** Human-readable description */
|
|
550
|
+
description: string;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Table drift details
|
|
554
|
+
*/
|
|
555
|
+
interface TableDrift {
|
|
556
|
+
/** Table name */
|
|
557
|
+
table: string;
|
|
558
|
+
/** Whether the entire table is missing or extra */
|
|
559
|
+
status: 'ok' | 'missing' | 'extra' | 'drifted';
|
|
560
|
+
/** Column drifts */
|
|
561
|
+
columns: ColumnDrift[];
|
|
562
|
+
/** Index drifts */
|
|
563
|
+
indexes: IndexDrift[];
|
|
564
|
+
/** Constraints drifts */
|
|
565
|
+
constraints: ConstraintDrift[];
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Schema drift for a single tenant
|
|
569
|
+
*/
|
|
570
|
+
interface TenantSchemaDrift {
|
|
571
|
+
/** Tenant ID */
|
|
572
|
+
tenantId: string;
|
|
573
|
+
/** Schema name */
|
|
574
|
+
schemaName: string;
|
|
575
|
+
/** Whether schema has drift */
|
|
576
|
+
hasDrift: boolean;
|
|
577
|
+
/** Table-level drifts */
|
|
578
|
+
tables: TableDrift[];
|
|
579
|
+
/** Total number of issues */
|
|
580
|
+
issueCount: number;
|
|
581
|
+
/** Error if introspection failed */
|
|
582
|
+
error?: string;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Aggregate schema drift status
|
|
586
|
+
*/
|
|
587
|
+
interface SchemaDriftStatus {
|
|
588
|
+
/** Reference tenant used for comparison */
|
|
589
|
+
referenceTenant: string;
|
|
590
|
+
/** Total tenants checked */
|
|
591
|
+
total: number;
|
|
592
|
+
/** Tenants without drift */
|
|
593
|
+
noDrift: number;
|
|
594
|
+
/** Tenants with drift */
|
|
595
|
+
withDrift: number;
|
|
596
|
+
/** Tenants with errors */
|
|
597
|
+
error: number;
|
|
598
|
+
/** Detailed results per tenant */
|
|
599
|
+
details: TenantSchemaDrift[];
|
|
600
|
+
/** Timestamp of the check */
|
|
601
|
+
timestamp: string;
|
|
602
|
+
/** Duration of the check in ms */
|
|
603
|
+
durationMs: number;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Options for schema drift detection
|
|
607
|
+
*/
|
|
608
|
+
interface SchemaDriftOptions {
|
|
609
|
+
/** Tenant ID to use as reference (default: first tenant) */
|
|
610
|
+
referenceTenant?: string;
|
|
611
|
+
/** Specific tenant IDs to check (default: all tenants) */
|
|
612
|
+
tenantIds?: string[];
|
|
613
|
+
/** Number of concurrent checks */
|
|
614
|
+
concurrency?: number;
|
|
615
|
+
/** Whether to include index comparison */
|
|
616
|
+
includeIndexes?: boolean;
|
|
617
|
+
/** Whether to include constraint comparison */
|
|
618
|
+
includeConstraints?: boolean;
|
|
619
|
+
/** Tables to exclude from comparison */
|
|
620
|
+
excludeTables?: string[];
|
|
621
|
+
/** Progress callback */
|
|
622
|
+
onProgress?: (tenantId: string, status: 'starting' | 'introspecting' | 'comparing' | 'completed' | 'failed') => void;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Migration status for the shared schema (public)
|
|
627
|
+
*/
|
|
628
|
+
interface SharedMigrationStatus {
|
|
629
|
+
/** Schema name (usually 'public') */
|
|
630
|
+
schemaName: string;
|
|
631
|
+
/** Number of applied migrations */
|
|
632
|
+
appliedCount: number;
|
|
633
|
+
/** Number of pending migrations */
|
|
634
|
+
pendingCount: number;
|
|
635
|
+
/** Names of pending migrations */
|
|
636
|
+
pendingMigrations: string[];
|
|
637
|
+
/** Overall status */
|
|
638
|
+
status: 'ok' | 'behind' | 'error';
|
|
639
|
+
/** Error message if status is 'error' */
|
|
640
|
+
error?: string;
|
|
641
|
+
/** Detected table format */
|
|
642
|
+
format: TableFormat | null;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Migration result for the shared schema
|
|
646
|
+
*/
|
|
647
|
+
interface SharedMigrationResult {
|
|
648
|
+
/** Schema name (usually 'public') */
|
|
649
|
+
schemaName: string;
|
|
650
|
+
/** Whether migration was successful */
|
|
651
|
+
success: boolean;
|
|
652
|
+
/** List of applied migration names */
|
|
653
|
+
appliedMigrations: string[];
|
|
654
|
+
/** Error message if failed */
|
|
655
|
+
error?: string;
|
|
656
|
+
/** Duration in milliseconds */
|
|
657
|
+
durationMs: number;
|
|
658
|
+
/** Table format used */
|
|
659
|
+
format?: TableFormat;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Shared migration configuration (extends MigratorConfig)
|
|
663
|
+
*/
|
|
664
|
+
interface SharedMigratorConfig {
|
|
665
|
+
/** Path to shared schema migrations folder */
|
|
666
|
+
sharedMigrationsFolder: string;
|
|
667
|
+
/** Table name for tracking shared migrations (default: '__drizzle_shared_migrations') */
|
|
668
|
+
sharedMigrationsTable?: string;
|
|
669
|
+
/** Migration hooks for shared schema */
|
|
670
|
+
hooks?: SharedMigrationHooks;
|
|
671
|
+
/**
|
|
672
|
+
* Table format for tracking migrations
|
|
673
|
+
* @default "auto"
|
|
674
|
+
*/
|
|
675
|
+
tableFormat?: 'auto' | TableFormat;
|
|
676
|
+
/**
|
|
677
|
+
* Default format when creating new table
|
|
678
|
+
* @default "name"
|
|
679
|
+
*/
|
|
680
|
+
defaultFormat?: TableFormat;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Hooks for shared schema migrations
|
|
684
|
+
*/
|
|
685
|
+
interface SharedMigrationHooks {
|
|
686
|
+
/** Called before starting shared migration */
|
|
687
|
+
beforeMigration?: () => void | Promise<void>;
|
|
688
|
+
/** Called after shared migration completes */
|
|
689
|
+
afterMigration?: (result: SharedMigrationResult) => void | Promise<void>;
|
|
690
|
+
/** Called before applying a specific migration */
|
|
691
|
+
beforeApply?: (migrationName: string) => void | Promise<void>;
|
|
692
|
+
/** Called after applying a specific migration */
|
|
693
|
+
afterApply?: (migrationName: string, durationMs: number) => void | Promise<void>;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Options for shared migration operations
|
|
697
|
+
*/
|
|
698
|
+
interface SharedMigrateOptions {
|
|
699
|
+
/** Dry run mode - show what would be applied without executing */
|
|
700
|
+
dryRun?: boolean;
|
|
701
|
+
/** Progress callback */
|
|
702
|
+
onProgress?: (status: 'starting' | 'migrating' | 'completed' | 'failed', migrationName?: string) => void;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Seed function signature for shared schema
|
|
706
|
+
* Called with the shared database instance
|
|
707
|
+
*/
|
|
708
|
+
type SharedSeedFunction<TSchema extends Record<string, unknown> = Record<string, unknown>> = (db: PostgresJsDatabase<TSchema>) => Promise<void>;
|
|
709
|
+
/**
|
|
710
|
+
* Result of shared schema seeding
|
|
711
|
+
*/
|
|
712
|
+
interface SharedSeedResult {
|
|
713
|
+
/** Schema name (usually 'public') */
|
|
714
|
+
schemaName: string;
|
|
715
|
+
/** Whether seeding was successful */
|
|
716
|
+
success: boolean;
|
|
717
|
+
/** Error message if failed */
|
|
718
|
+
error?: string;
|
|
719
|
+
/** Duration in milliseconds */
|
|
720
|
+
durationMs: number;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Parallel migration engine for multi-tenant applications
|
|
725
|
+
*/
|
|
726
|
+
declare class Migrator<TTenantSchema extends Record<string, unknown>, TSharedSchema extends Record<string, unknown>> {
|
|
727
|
+
private readonly migratorConfig;
|
|
728
|
+
private readonly migrationsTable;
|
|
729
|
+
private readonly schemaManager;
|
|
730
|
+
private readonly driftDetector;
|
|
731
|
+
private readonly seeder;
|
|
732
|
+
private readonly syncManager;
|
|
733
|
+
private readonly migrationExecutor;
|
|
734
|
+
private readonly batchExecutor;
|
|
735
|
+
private readonly cloner;
|
|
736
|
+
private readonly sharedMigrationExecutor;
|
|
737
|
+
private readonly sharedSeeder;
|
|
738
|
+
constructor(tenantConfig: Config<TTenantSchema, TSharedSchema>, migratorConfig: MigratorConfig);
|
|
739
|
+
/**
|
|
740
|
+
* Migrate all tenants in parallel
|
|
741
|
+
*
|
|
742
|
+
* Delegates to BatchExecutor for parallel migration operations.
|
|
743
|
+
*/
|
|
744
|
+
migrateAll(options?: MigrateOptions): Promise<MigrationResults>;
|
|
745
|
+
/**
|
|
746
|
+
* Migrate a single tenant
|
|
747
|
+
*
|
|
748
|
+
* Delegates to MigrationExecutor for single tenant operations.
|
|
749
|
+
*/
|
|
750
|
+
migrateTenant(tenantId: string, migrations?: MigrationFile[], options?: {
|
|
751
|
+
dryRun?: boolean;
|
|
752
|
+
onProgress?: MigrateOptions['onProgress'];
|
|
753
|
+
}): Promise<TenantMigrationResult>;
|
|
754
|
+
/**
|
|
755
|
+
* Migrate specific tenants
|
|
756
|
+
*
|
|
757
|
+
* Delegates to BatchExecutor for parallel migration operations.
|
|
758
|
+
*/
|
|
759
|
+
migrateTenants(tenantIds: string[], options?: MigrateOptions): Promise<MigrationResults>;
|
|
760
|
+
/**
|
|
761
|
+
* Get migration status for all tenants
|
|
762
|
+
*
|
|
763
|
+
* Delegates to BatchExecutor for status operations.
|
|
764
|
+
*/
|
|
765
|
+
getStatus(): Promise<TenantMigrationStatus[]>;
|
|
766
|
+
/**
|
|
767
|
+
* Get migration status for a specific tenant
|
|
768
|
+
*
|
|
769
|
+
* Delegates to MigrationExecutor for single tenant operations.
|
|
770
|
+
*/
|
|
771
|
+
getTenantStatus(tenantId: string, migrations?: MigrationFile[]): Promise<TenantMigrationStatus>;
|
|
772
|
+
/**
|
|
773
|
+
* Create a new tenant schema and optionally apply migrations
|
|
774
|
+
*/
|
|
775
|
+
createTenant(tenantId: string, options?: CreateTenantOptions): Promise<void>;
|
|
776
|
+
/**
|
|
777
|
+
* Drop a tenant schema
|
|
778
|
+
*/
|
|
779
|
+
dropTenant(tenantId: string, options?: DropTenantOptions): Promise<void>;
|
|
780
|
+
/**
|
|
781
|
+
* Check if a tenant schema exists
|
|
782
|
+
*/
|
|
783
|
+
tenantExists(tenantId: string): Promise<boolean>;
|
|
784
|
+
/**
|
|
785
|
+
* Clone a tenant to a new tenant
|
|
786
|
+
*
|
|
787
|
+
* By default, clones only schema structure. Use includeData to copy data.
|
|
788
|
+
*
|
|
789
|
+
* @example
|
|
790
|
+
* ```typescript
|
|
791
|
+
* // Schema-only clone
|
|
792
|
+
* await migrator.cloneTenant('production', 'dev');
|
|
793
|
+
*
|
|
794
|
+
* // Clone with data
|
|
795
|
+
* await migrator.cloneTenant('production', 'dev', { includeData: true });
|
|
796
|
+
*
|
|
797
|
+
* // Clone with anonymization
|
|
798
|
+
* await migrator.cloneTenant('production', 'dev', {
|
|
799
|
+
* includeData: true,
|
|
800
|
+
* anonymize: {
|
|
801
|
+
* enabled: true,
|
|
802
|
+
* rules: {
|
|
803
|
+
* users: { email: null, phone: null },
|
|
804
|
+
* },
|
|
805
|
+
* },
|
|
806
|
+
* });
|
|
807
|
+
* ```
|
|
808
|
+
*/
|
|
809
|
+
cloneTenant(sourceTenantId: string, targetTenantId: string, options?: CloneTenantOptions): Promise<CloneTenantResult>;
|
|
810
|
+
/**
|
|
811
|
+
* Mark migrations as applied without executing SQL
|
|
812
|
+
* Useful for syncing tracking state with already-applied migrations
|
|
813
|
+
*
|
|
814
|
+
* Delegates to MigrationExecutor for single tenant operations.
|
|
815
|
+
*/
|
|
816
|
+
markAsApplied(tenantId: string, options?: {
|
|
817
|
+
onProgress?: MigrateOptions['onProgress'];
|
|
818
|
+
}): Promise<TenantMigrationResult>;
|
|
819
|
+
/**
|
|
820
|
+
* Mark migrations as applied for all tenants without executing SQL
|
|
821
|
+
* Useful for syncing tracking state with already-applied migrations
|
|
822
|
+
*
|
|
823
|
+
* Delegates to BatchExecutor for parallel operations.
|
|
824
|
+
*/
|
|
825
|
+
markAllAsApplied(options?: MigrateOptions): Promise<MigrationResults>;
|
|
826
|
+
/**
|
|
827
|
+
* Get sync status for all tenants
|
|
828
|
+
* Detects divergences between migrations on disk and tracking in database
|
|
829
|
+
*/
|
|
830
|
+
getSyncStatus(): Promise<SyncStatus>;
|
|
831
|
+
/**
|
|
832
|
+
* Get sync status for a specific tenant
|
|
833
|
+
*/
|
|
834
|
+
getTenantSyncStatus(tenantId: string, migrations?: MigrationFile[]): Promise<TenantSyncStatus>;
|
|
835
|
+
/**
|
|
836
|
+
* Mark missing migrations as applied for a tenant
|
|
837
|
+
*/
|
|
838
|
+
markMissing(tenantId: string): Promise<TenantSyncResult>;
|
|
839
|
+
/**
|
|
840
|
+
* Mark missing migrations as applied for all tenants
|
|
841
|
+
*/
|
|
842
|
+
markAllMissing(options?: SyncOptions): Promise<SyncResults>;
|
|
843
|
+
/**
|
|
844
|
+
* Remove orphan migration records for a tenant
|
|
845
|
+
*/
|
|
846
|
+
cleanOrphans(tenantId: string): Promise<TenantSyncResult>;
|
|
847
|
+
/**
|
|
848
|
+
* Remove orphan migration records for all tenants
|
|
849
|
+
*/
|
|
850
|
+
cleanAllOrphans(options?: SyncOptions): Promise<SyncResults>;
|
|
851
|
+
/**
|
|
852
|
+
* Seed a single tenant with initial data
|
|
853
|
+
*
|
|
854
|
+
* @example
|
|
855
|
+
* ```typescript
|
|
856
|
+
* const seed: SeedFunction = async (db, tenantId) => {
|
|
857
|
+
* await db.insert(roles).values([
|
|
858
|
+
* { name: 'admin', permissions: ['*'] },
|
|
859
|
+
* { name: 'user', permissions: ['read'] },
|
|
860
|
+
* ]);
|
|
861
|
+
* };
|
|
862
|
+
*
|
|
863
|
+
* await migrator.seedTenant('tenant-123', seed);
|
|
864
|
+
* ```
|
|
865
|
+
*/
|
|
866
|
+
seedTenant(tenantId: string, seedFn: SeedFunction<TTenantSchema>): Promise<TenantSeedResult>;
|
|
867
|
+
/**
|
|
868
|
+
* Seed all tenants with initial data in parallel
|
|
869
|
+
*
|
|
870
|
+
* @example
|
|
871
|
+
* ```typescript
|
|
872
|
+
* const seed: SeedFunction = async (db, tenantId) => {
|
|
873
|
+
* await db.insert(roles).values([
|
|
874
|
+
* { name: 'admin', permissions: ['*'] },
|
|
875
|
+
* ]);
|
|
876
|
+
* };
|
|
877
|
+
*
|
|
878
|
+
* await migrator.seedAll(seed, { concurrency: 10 });
|
|
879
|
+
* ```
|
|
880
|
+
*/
|
|
881
|
+
seedAll(seedFn: SeedFunction<TTenantSchema>, options?: SeedOptions): Promise<SeedResults>;
|
|
882
|
+
/**
|
|
883
|
+
* Seed specific tenants with initial data
|
|
884
|
+
*/
|
|
885
|
+
seedTenants(tenantIds: string[], seedFn: SeedFunction<TTenantSchema>, options?: SeedOptions): Promise<SeedResults>;
|
|
886
|
+
/**
|
|
887
|
+
* Check if shared schema seeding is available
|
|
888
|
+
*
|
|
889
|
+
* @returns True if shared schema is configured
|
|
890
|
+
*/
|
|
891
|
+
hasSharedSeeding(): boolean;
|
|
892
|
+
/**
|
|
893
|
+
* Seed the shared schema with initial data
|
|
894
|
+
*
|
|
895
|
+
* Seeds the public/shared schema with common data like plans, roles, permissions.
|
|
896
|
+
* Must have schemas.shared configured in the tenant config.
|
|
897
|
+
*
|
|
898
|
+
* @example
|
|
899
|
+
* ```typescript
|
|
900
|
+
* if (migrator.hasSharedSeeding()) {
|
|
901
|
+
* const result = await migrator.seedShared(async (db) => {
|
|
902
|
+
* await db.insert(plans).values([
|
|
903
|
+
* { id: 'free', name: 'Free', price: 0 },
|
|
904
|
+
* { id: 'pro', name: 'Pro', price: 29 },
|
|
905
|
+
* { id: 'enterprise', name: 'Enterprise', price: 99 },
|
|
906
|
+
* ]).onConflictDoNothing();
|
|
907
|
+
*
|
|
908
|
+
* await db.insert(roles).values([
|
|
909
|
+
* { name: 'admin', permissions: ['*'] },
|
|
910
|
+
* { name: 'user', permissions: ['read'] },
|
|
911
|
+
* ]).onConflictDoNothing();
|
|
912
|
+
* });
|
|
913
|
+
*
|
|
914
|
+
* if (result.success) {
|
|
915
|
+
* console.log(`Seeded shared schema in ${result.durationMs}ms`);
|
|
916
|
+
* }
|
|
917
|
+
* }
|
|
918
|
+
* ```
|
|
919
|
+
*/
|
|
920
|
+
seedShared(seedFn: SharedSeedFunction<TSharedSchema>): Promise<SharedSeedResult>;
|
|
921
|
+
/**
|
|
922
|
+
* Seed shared schema first, then all tenants
|
|
923
|
+
*
|
|
924
|
+
* Convenience method for the common pattern of seeding shared tables
|
|
925
|
+
* before tenant tables.
|
|
926
|
+
*
|
|
927
|
+
* @example
|
|
928
|
+
* ```typescript
|
|
929
|
+
* const result = await migrator.seedAllWithShared(
|
|
930
|
+
* async (db) => {
|
|
931
|
+
* await db.insert(plans).values([{ id: 'free', name: 'Free' }]);
|
|
932
|
+
* },
|
|
933
|
+
* async (db, tenantId) => {
|
|
934
|
+
* await db.insert(roles).values([{ name: 'admin' }]);
|
|
935
|
+
* },
|
|
936
|
+
* { concurrency: 10 }
|
|
937
|
+
* );
|
|
938
|
+
*
|
|
939
|
+
* if (result.shared.success) {
|
|
940
|
+
* console.log('Shared seeding completed');
|
|
941
|
+
* }
|
|
942
|
+
* console.log(`Tenants: ${result.tenants.succeeded}/${result.tenants.total}`);
|
|
943
|
+
* ```
|
|
944
|
+
*/
|
|
945
|
+
seedAllWithShared(sharedSeedFn: SharedSeedFunction<TSharedSchema>, tenantSeedFn: SeedFunction<TTenantSchema>, options?: SeedOptions): Promise<{
|
|
946
|
+
shared: SharedSeedResult;
|
|
947
|
+
tenants: SeedResults;
|
|
948
|
+
}>;
|
|
949
|
+
/**
|
|
950
|
+
* Load migration files from the migrations folder
|
|
951
|
+
*/
|
|
952
|
+
private loadMigrations;
|
|
953
|
+
/**
|
|
954
|
+
* Get or detect the format for a schema
|
|
955
|
+
* Returns the configured format or auto-detects from existing table
|
|
956
|
+
*
|
|
957
|
+
* Note: This method is shared with SyncManager and MigrationExecutor via dependency injection.
|
|
958
|
+
*/
|
|
959
|
+
private getOrDetectFormat;
|
|
960
|
+
/**
|
|
961
|
+
* Load shared migration files from the shared migrations folder
|
|
962
|
+
*/
|
|
963
|
+
private loadSharedMigrations;
|
|
964
|
+
/**
|
|
965
|
+
* Get or detect the format for the shared schema
|
|
966
|
+
*/
|
|
967
|
+
private getOrDetectSharedFormat;
|
|
968
|
+
/**
|
|
969
|
+
* Check if shared schema migrations are configured
|
|
970
|
+
*
|
|
971
|
+
* @returns True if sharedMigrationsFolder is configured and exists
|
|
972
|
+
*/
|
|
973
|
+
hasSharedMigrations(): boolean;
|
|
974
|
+
/**
|
|
975
|
+
* Migrate the shared schema (public)
|
|
976
|
+
*
|
|
977
|
+
* Applies pending migrations to the shared/public schema.
|
|
978
|
+
* Must have sharedMigrationsFolder configured.
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```typescript
|
|
982
|
+
* if (migrator.hasSharedMigrations()) {
|
|
983
|
+
* const result = await migrator.migrateShared();
|
|
984
|
+
* console.log(`Applied ${result.appliedMigrations.length} shared migrations`);
|
|
985
|
+
* }
|
|
986
|
+
* ```
|
|
987
|
+
*/
|
|
988
|
+
migrateShared(options?: SharedMigrateOptions): Promise<SharedMigrationResult>;
|
|
989
|
+
/**
|
|
990
|
+
* Get migration status for the shared schema
|
|
991
|
+
*
|
|
992
|
+
* @returns Status with applied/pending counts
|
|
993
|
+
*/
|
|
994
|
+
getSharedStatus(): Promise<SharedMigrationStatus>;
|
|
995
|
+
/**
|
|
996
|
+
* Mark shared schema migrations as applied without executing SQL
|
|
997
|
+
*
|
|
998
|
+
* Useful for syncing tracking state with already-applied migrations.
|
|
999
|
+
*/
|
|
1000
|
+
markSharedAsApplied(options?: {
|
|
1001
|
+
onProgress?: SharedMigrateOptions['onProgress'];
|
|
1002
|
+
}): Promise<SharedMigrationResult>;
|
|
1003
|
+
/**
|
|
1004
|
+
* Migrate shared schema first, then all tenants
|
|
1005
|
+
*
|
|
1006
|
+
* Convenience method for the common pattern of migrating shared tables
|
|
1007
|
+
* before tenant tables.
|
|
1008
|
+
*
|
|
1009
|
+
* @example
|
|
1010
|
+
* ```typescript
|
|
1011
|
+
* const result = await migrator.migrateAllWithShared({
|
|
1012
|
+
* concurrency: 10,
|
|
1013
|
+
* onProgress: (tenantId, status) => console.log(`${tenantId}: ${status}`),
|
|
1014
|
+
* });
|
|
1015
|
+
*
|
|
1016
|
+
* console.log(`Shared: ${result.shared.appliedMigrations.length} applied`);
|
|
1017
|
+
* console.log(`Tenants: ${result.tenants.succeeded}/${result.tenants.total} succeeded`);
|
|
1018
|
+
* ```
|
|
1019
|
+
*/
|
|
1020
|
+
migrateAllWithShared(options?: MigrateOptions & {
|
|
1021
|
+
sharedOptions?: SharedMigrateOptions;
|
|
1022
|
+
}): Promise<{
|
|
1023
|
+
shared: SharedMigrationResult;
|
|
1024
|
+
tenants: MigrationResults;
|
|
1025
|
+
}>;
|
|
1026
|
+
/**
|
|
1027
|
+
* Detect schema drift across all tenants
|
|
1028
|
+
* Compares each tenant's schema against a reference tenant (first tenant by default)
|
|
1029
|
+
*
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```typescript
|
|
1032
|
+
* const drift = await migrator.getSchemaDrift();
|
|
1033
|
+
* if (drift.withDrift > 0) {
|
|
1034
|
+
* console.log('Schema drift detected!');
|
|
1035
|
+
* for (const tenant of drift.details) {
|
|
1036
|
+
* if (tenant.hasDrift) {
|
|
1037
|
+
* console.log(`Tenant ${tenant.tenantId} has drift:`);
|
|
1038
|
+
* for (const table of tenant.tables) {
|
|
1039
|
+
* for (const col of table.columns) {
|
|
1040
|
+
* console.log(` - ${table.table}.${col.column}: ${col.description}`);
|
|
1041
|
+
* }
|
|
1042
|
+
* }
|
|
1043
|
+
* }
|
|
1044
|
+
* }
|
|
1045
|
+
* }
|
|
1046
|
+
* ```
|
|
1047
|
+
*/
|
|
1048
|
+
getSchemaDrift(options?: SchemaDriftOptions): Promise<SchemaDriftStatus>;
|
|
1049
|
+
/**
|
|
1050
|
+
* Get schema drift for a specific tenant compared to a reference
|
|
1051
|
+
*/
|
|
1052
|
+
getTenantSchemaDrift(tenantId: string, referenceTenantId: string, options?: Pick<SchemaDriftOptions, 'includeIndexes' | 'includeConstraints' | 'excludeTables'>): Promise<TenantSchemaDrift>;
|
|
1053
|
+
/**
|
|
1054
|
+
* Introspect the schema of a tenant
|
|
1055
|
+
*/
|
|
1056
|
+
introspectTenantSchema(tenantId: string, options?: {
|
|
1057
|
+
includeIndexes?: boolean;
|
|
1058
|
+
includeConstraints?: boolean;
|
|
1059
|
+
excludeTables?: string[];
|
|
1060
|
+
}): Promise<TenantSchema | null>;
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Create a migrator instance
|
|
1064
|
+
*/
|
|
1065
|
+
declare function createMigrator<TTenantSchema extends Record<string, unknown>, TSharedSchema extends Record<string, unknown>>(tenantConfig: Config<TTenantSchema, TSharedSchema>, migratorConfig: MigratorConfig): Migrator<TTenantSchema, TSharedSchema>;
|
|
1066
|
+
|
|
1067
|
+
export { type AnonymizeRules as $, type AppliedMigration as A, type SharedMigrationStatus as B, type CreateTenantOptions as C, type DropTenantOptions as D, detectTableFormat as E, getFormatConfig as F, DEFAULT_FORMAT as G, DRIZZLE_KIT_FORMAT as H, type TableFormat as I, type ColumnInfo as J, type IndexInfo as K, type ConstraintInfo as L, Migrator as M, type TableSchema as N, type TenantSchema as O, type ColumnDrift as P, type IndexDrift as Q, type ConstraintDrift as R, type SeedFunction as S, type TenantMigrationResult as T, type TableDrift as U, type TenantSchemaDrift as V, type SchemaDriftStatus as W, type SchemaDriftOptions as X, type CloneProgressCallback as Y, type CloneProgressStatus as Z, type AnonymizeOptions as _, type MigratorConfig as a, type AnonymizeValue as a0, type SharedMigratorConfig as a1, type SharedMigrationHooks as a2, type MigrationFile as b, createMigrator as c, type MigrateOptions as d, type MigrationResults as e, type TenantMigrationStatus as f, type MigrationHooks as g, type MigrationProgressCallback as h, type MigrationErrorHandler as i, type SeedOptions as j, type TenantSeedResult as k, type SeedResults as l, type SharedSeedFunction as m, type SharedSeedResult as n, type DetectedFormat as o, type SyncStatus as p, type TenantSyncStatus as q, type TenantSyncResult as r, type SyncOptions as s, type SyncResults as t, type ClonerConfig as u, type ClonerDependencies as v, type CloneTenantOptions as w, type CloneTenantResult as x, type SharedMigrateOptions as y, type SharedMigrationResult as z };
|