drizzle-multitenant 1.0.7 → 1.0.9

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/roadmap.md DELETED
@@ -1,921 +0,0 @@
1
- # drizzle-multitenant - Roadmap
2
-
3
- > Multi-tenancy toolkit for Drizzle ORM with schema isolation, tenant context, and parallel migrations.
4
-
5
- ---
6
-
7
- ## Versões Completas
8
-
9
- ### v0.1.0 - Core
10
- - [x] `defineConfig` e tipos
11
- - [x] `createTenantManager` com pool management
12
- - [x] `getDb()` e `getSharedDb()`
13
- - [x] Cleanup automático de pools (LRU)
14
- - [x] Testes unitários
15
-
16
- ### v0.2.0 - Context
17
- - [x] `createTenantContext` com AsyncLocalStorage
18
- - [x] Express middleware
19
- - [x] Fastify plugin
20
- - [x] Testes de integração
21
-
22
- ### v0.3.0 - Migrations
23
- - [x] CLI base (generate, migrate, status)
24
- - [x] Parallel migration engine
25
- - [x] tenant:create e tenant:drop
26
- - [x] Hooks de migration
27
-
28
- ### v0.4.0 - Cross-Schema
29
- - [x] `createCrossSchemaQuery`
30
- - [x] `withSharedLookup` helper
31
- - [x] Type inference completo
32
-
33
- ### v0.5.0 - NestJS
34
- - [x] `TenantModule.forRoot()`
35
- - [x] `@InjectTenantDb()` decorator
36
- - [x] `@InjectTenantContext()` decorator
37
- - [x] Guards e interceptors
38
-
39
- ### v1.0.0 - Production Ready
40
- - [x] Documentação completa (README.md)
41
- - [x] Package publicado no npm
42
- - [x] 148 testes passando
43
- - [x] Licença MIT
44
-
45
- ### v1.0.3 - NestJS DX Improvements
46
- - [x] `TenantDbFactory` para singleton services (cron jobs, event handlers)
47
- - [x] `@InjectTenantDbFactory()` decorator
48
- - [x] Debug utilities para proxies (`__debug`, `__tenantId`, `__isProxy`)
49
- - [x] `console.log(tenantDb)` mostra informações úteis
50
- - [x] CLI `migrationsTable` config support
51
- - [x] 154 testes passando
52
-
53
- ---
54
-
55
- ## Próximas Versões
56
-
57
- ### v1.1.0 - Resiliência e Observabilidade
58
-
59
- #### Retry Logic para Conexões
60
- Conexões podem falhar temporariamente. Adicionar retry automático com backoff exponencial.
61
-
62
- ```typescript
63
- import { defineConfig } from 'drizzle-multitenant';
64
-
65
- export default defineConfig({
66
- connection: {
67
- url: process.env.DATABASE_URL!,
68
- retry: {
69
- maxAttempts: 3,
70
- initialDelayMs: 100,
71
- maxDelayMs: 5000,
72
- backoffMultiplier: 2,
73
- },
74
- },
75
- // ...
76
- });
77
- ```
78
-
79
- #### Health Checks
80
- Verificar saúde dos pools e conexões.
81
-
82
- ```typescript
83
- const manager = createTenantManager(config);
84
-
85
- // Verificar saúde de todos os pools
86
- const health = await manager.healthCheck();
87
- // {
88
- // healthy: true,
89
- // pools: [
90
- // { tenantId: 'abc', status: 'ok', connections: 5 },
91
- // { tenantId: 'def', status: 'degraded', connections: 1 },
92
- // ],
93
- // sharedDb: 'ok',
94
- // timestamp: '2024-01-15T10:30:00Z'
95
- // }
96
-
97
- // Endpoint para load balancers
98
- app.get('/health', async (req, res) => {
99
- const health = await manager.healthCheck();
100
- res.status(health.healthy ? 200 : 503).json(health);
101
- });
102
- ```
103
-
104
- #### Métricas Prometheus
105
- Expor métricas no formato Prometheus para monitoramento.
106
-
107
- ```typescript
108
- import { defineConfig } from 'drizzle-multitenant';
109
-
110
- export default defineConfig({
111
- // ...
112
- metrics: {
113
- enabled: true,
114
- prefix: 'drizzle_multitenant',
115
- },
116
- });
117
-
118
- // Endpoint para Prometheus
119
- app.get('/metrics', async (req, res) => {
120
- const metrics = manager.getMetrics();
121
- res.set('Content-Type', 'text/plain');
122
- res.send(metrics);
123
- });
124
- ```
125
-
126
- Métricas expostas:
127
- ```
128
- drizzle_multitenant_pool_count 15
129
- drizzle_multitenant_pool_connections_active{tenant="abc"} 3
130
- drizzle_multitenant_pool_connections_idle{tenant="abc"} 7
131
- drizzle_multitenant_query_duration_seconds_bucket{le="0.1"} 1024
132
- drizzle_multitenant_pool_evictions_total 42
133
- drizzle_multitenant_errors_total{type="connection"} 3
134
- ```
135
-
136
- #### Structured Logging
137
- Integração com loggers populares (pino, winston).
138
-
139
- ```typescript
140
- import pino from 'pino';
141
-
142
- const logger = pino({ level: 'info' });
143
-
144
- export default defineConfig({
145
- // ...
146
- hooks: {
147
- logger: {
148
- provider: logger,
149
- level: 'info',
150
- // Logs estruturados automaticamente
151
- // { tenant: 'abc', event: 'pool_created', duration: 45 }
152
- },
153
- onPoolCreated: (tenantId) => {
154
- logger.info({ tenant: tenantId }, 'Pool created');
155
- },
156
- },
157
- });
158
- ```
159
-
160
- **Checklist v1.1.0:**
161
- - [ ] Retry logic com backoff exponencial
162
- - [ ] `manager.healthCheck()` API
163
- - [ ] Métricas Prometheus
164
- - [ ] Integração com pino/winston
165
- - [ ] Testes unitários e integração
166
-
167
- ---
168
-
169
- ### v1.2.0 - Segurança
170
-
171
- #### Schema Name Sanitization
172
- Validar e sanitizar nomes de schema para prevenir SQL injection.
173
-
174
- ```typescript
175
- import { defineConfig } from 'drizzle-multitenant';
176
-
177
- export default defineConfig({
178
- isolation: {
179
- strategy: 'schema',
180
- schemaNameTemplate: (tenantId) => `tenant_${tenantId}`,
181
- // Sanitização automática habilitada por padrão
182
- sanitize: {
183
- enabled: true,
184
- maxLength: 63, // Limite PostgreSQL
185
- allowedChars: /^[a-z0-9_]+$/,
186
- reservedNames: ['public', 'pg_catalog', 'information_schema'],
187
- },
188
- },
189
- });
190
-
191
- // Throws error se nome inválido
192
- manager.getDb('tenant; DROP TABLE users;--'); // Error: Invalid tenant ID
193
- ```
194
-
195
- #### Rate Limiting por Tenant
196
- Limitar queries por tenant para prevenir abuso.
197
-
198
- ```typescript
199
- export default defineConfig({
200
- // ...
201
- rateLimit: {
202
- enabled: true,
203
- maxQueriesPerSecond: 100,
204
- maxConnectionsPerTenant: 5,
205
- onLimitExceeded: (tenantId, limit) => {
206
- logger.warn({ tenant: tenantId, limit }, 'Rate limit exceeded');
207
- // 'throttle' | 'reject' | 'queue'
208
- return 'throttle';
209
- },
210
- },
211
- });
212
- ```
213
-
214
- #### Tenant Isolation Audit
215
- Auditoria para garantir isolamento entre tenants.
216
-
217
- ```typescript
218
- export default defineConfig({
219
- // ...
220
- audit: {
221
- enabled: true,
222
- logQueries: true,
223
- detectCrossSchemaAccess: true,
224
- onViolation: async (event) => {
225
- // {
226
- // type: 'cross_schema_access',
227
- // tenant: 'abc',
228
- // query: 'SELECT * FROM tenant_def.users',
229
- // timestamp: Date
230
- // }
231
- await sendAlert(event);
232
- },
233
- },
234
- });
235
- ```
236
-
237
- **Checklist v1.2.0:**
238
- - [ ] Schema name sanitization
239
- - [ ] Rate limiting por tenant
240
- - [ ] Audit logging
241
- - [ ] Detecção de cross-schema access
242
- - [ ] Testes de segurança
243
-
244
- ---
245
-
246
- ### v1.3.0 - Performance
247
-
248
- #### Connection Queue
249
- Gerenciar overflow de pools com queue de espera.
250
-
251
- ```typescript
252
- export default defineConfig({
253
- isolation: {
254
- maxPools: 50,
255
- pooling: {
256
- strategy: 'queue', // 'queue' | 'reject' | 'evict-lru'
257
- queueTimeout: 5000,
258
- maxWaitingRequests: 100,
259
- },
260
- },
261
- });
262
-
263
- // Eventos de queue
264
- hooks: {
265
- onQueueFull: (tenantId) => {
266
- logger.warn({ tenant: tenantId }, 'Connection queue full');
267
- },
268
- onQueueTimeout: (tenantId, waitTime) => {
269
- logger.error({ tenant: tenantId, waitTime }, 'Queue timeout');
270
- },
271
- }
272
- ```
273
-
274
- #### Query Caching
275
- Cache opcional para queries repetidas.
276
-
277
- ```typescript
278
- import { createTenantManager } from 'drizzle-multitenant';
279
- import Redis from 'ioredis';
280
-
281
- const redis = new Redis();
282
-
283
- const manager = createTenantManager({
284
- ...config,
285
- cache: {
286
- enabled: true,
287
- provider: redis, // ou 'memory'
288
- defaultTtlMs: 60000,
289
- maxSize: 10000, // para memory provider
290
- keyPrefix: 'dmt:',
291
- },
292
- });
293
-
294
- // Uso no código
295
- const db = manager.getDb('tenant-123');
296
-
297
- // Query com cache
298
- const users = await db
299
- .select()
300
- .from(schema.users)
301
- .where(eq(schema.users.active, true))
302
- .$cache({ ttl: 5000, key: 'active-users' });
303
-
304
- // Invalidar cache
305
- await manager.invalidateCache('tenant-123', 'active-users');
306
- await manager.invalidateTenantCache('tenant-123'); // todo cache do tenant
307
- ```
308
-
309
- #### Prepared Statements Pool
310
- Reutilizar prepared statements entre requests.
311
-
312
- ```typescript
313
- export default defineConfig({
314
- connection: {
315
- url: process.env.DATABASE_URL!,
316
- preparedStatements: {
317
- enabled: true,
318
- maxPerTenant: 100,
319
- ttlMs: 3600000, // 1 hora
320
- },
321
- },
322
- });
323
- ```
324
-
325
- **Checklist v1.3.0:**
326
- - [ ] Connection queue com timeout
327
- - [ ] Query caching (memory + Redis)
328
- - [ ] Cache invalidation API
329
- - [ ] Prepared statements pool
330
- - [ ] Benchmarks de performance
331
-
332
- ---
333
-
334
- ### v1.4.0 - Novas Estratégias de Isolamento
335
-
336
- #### Row-Level Security (RLS)
337
- Isolamento por linha usando RLS do PostgreSQL.
338
-
339
- ```typescript
340
- export default defineConfig({
341
- isolation: {
342
- strategy: 'row',
343
- tenantColumn: 'tenant_id',
344
- enableRLS: true,
345
- },
346
- schemas: {
347
- tenant: tenantSchema,
348
- },
349
- });
350
-
351
- // Gera automaticamente:
352
- // CREATE POLICY tenant_isolation ON users
353
- // USING (tenant_id = current_setting('app.tenant_id')::uuid);
354
-
355
- // Uso transparente
356
- const db = manager.getDb('tenant-123');
357
- const users = await db.select().from(schema.users);
358
- // WHERE tenant_id = 'tenant-123' aplicado automaticamente
359
- ```
360
-
361
- #### Database-per-Tenant
362
- Isolamento completo por database.
363
-
364
- ```typescript
365
- export default defineConfig({
366
- isolation: {
367
- strategy: 'database',
368
- databaseNameTemplate: (tenantId) => `db_${tenantId}`,
369
- createDatabase: true, // criar automaticamente
370
- },
371
- });
372
-
373
- // Cada tenant tem seu próprio database
374
- // db_tenant_abc, db_tenant_def, etc.
375
- ```
376
-
377
- #### Hybrid Strategy
378
- Combinar estratégias para diferentes tiers de tenants.
379
-
380
- ```typescript
381
- export default defineConfig({
382
- isolation: {
383
- strategy: 'hybrid',
384
- default: 'row', // tenants pequenos
385
- rules: [
386
- {
387
- condition: async (tenantId) => {
388
- const plan = await getTenantPlan(tenantId);
389
- return plan === 'enterprise';
390
- },
391
- strategy: 'schema', // tenants enterprise
392
- },
393
- {
394
- condition: async (tenantId) => {
395
- const rows = await getTenantRowCount(tenantId);
396
- return rows > 100000;
397
- },
398
- strategy: 'schema', // tenants grandes
399
- },
400
- ],
401
- onPromotion: async (tenantId, from, to) => {
402
- // Migrar dados de RLS para schema dedicado
403
- await migrateDataToSchema(tenantId);
404
- logger.info({ tenant: tenantId, from, to }, 'Tenant promoted');
405
- },
406
- },
407
- });
408
- ```
409
-
410
- **Checklist v1.4.0:**
411
- - [ ] Row-Level Security (RLS)
412
- - [ ] Geração automática de policies
413
- - [ ] Database-per-tenant strategy
414
- - [ ] Hybrid strategy com regras
415
- - [ ] Migração entre estratégias
416
-
417
- ---
418
-
419
- ### v1.5.0 - Developer Experience
420
-
421
- #### CLI Interativo
422
- Modo interativo para operações comuns.
423
-
424
- ```bash
425
- $ npx drizzle-multitenant
426
-
427
- ? What do you want to do? (Use arrow keys)
428
- ❯ Migrate all tenants
429
- Check migration status
430
- Create new tenant
431
- Drop tenant
432
- View pool statistics
433
- Generate migration
434
- Exit
435
-
436
- ? Select tenants to migrate:
437
- [x] tenant_abc (2 pending)
438
- [x] tenant_def (2 pending)
439
- [ ] tenant_ghi (up to date)
440
- ```
441
-
442
- #### Tenant Seeding
443
- Popular dados iniciais em tenants.
444
-
445
- ```typescript
446
- // seeds/initial.ts
447
- import { SeedFunction } from 'drizzle-multitenant';
448
-
449
- export const seed: SeedFunction = async (db, tenantId) => {
450
- await db.insert(roles).values([
451
- { name: 'admin', permissions: ['*'] },
452
- { name: 'user', permissions: ['read'] },
453
- ]);
454
-
455
- await db.insert(settings).values({
456
- tenantId,
457
- theme: 'light',
458
- language: 'pt-BR',
459
- });
460
- };
461
- ```
462
-
463
- ```bash
464
- # CLI
465
- npx drizzle-multitenant seed --tenant=abc --file=./seeds/initial.ts
466
- npx drizzle-multitenant seed --all --file=./seeds/initial.ts
467
-
468
- # Programático
469
- await migrator.seedTenant('abc', seed);
470
- await migrator.seedAll(seed, { concurrency: 10 });
471
- ```
472
-
473
- #### Schema Drift Detection
474
- Detectar divergências entre schema esperado e atual.
475
-
476
- ```bash
477
- $ npx drizzle-multitenant diff --tenant=abc
478
-
479
- Schema drift detected in tenant_abc:
480
-
481
- Missing columns:
482
- - users.avatar_url (varchar)
483
- - users.last_login (timestamp)
484
-
485
- Extra columns:
486
- - users.legacy_field (will be removed)
487
-
488
- Index differences:
489
- - Missing: idx_users_email
490
- - Extra: idx_legacy_lookup
491
-
492
- Run 'drizzle-multitenant migrate --tenant=abc' to fix.
493
- ```
494
-
495
- #### Tenant Cloning
496
- Clonar tenant para desenvolvimento/teste.
497
-
498
- ```bash
499
- # CLI
500
- npx drizzle-multitenant tenant:clone \
501
- --from=production-tenant \
502
- --to=dev-tenant \
503
- --include-data \
504
- --anonymize # GDPR compliance
505
- ```
506
-
507
- ```typescript
508
- // Programático
509
- await migrator.cloneTenant('source', 'target', {
510
- includeData: true,
511
- anonymize: {
512
- enabled: true,
513
- rules: {
514
- users: {
515
- email: (val) => `user-${hash(val)}@example.com`,
516
- name: () => faker.person.fullName(),
517
- phone: () => null,
518
- },
519
- },
520
- },
521
- });
522
- ```
523
-
524
- **Checklist v1.5.0:**
525
- - [ ] CLI interativo com inquirer
526
- - [ ] Tenant seeding API
527
- - [ ] Schema drift detection
528
- - [ ] Tenant cloning com anonymization
529
- - [ ] Documentação interativa
530
-
531
- ---
532
-
533
- ### v1.6.0 - Integrações Avançadas
534
-
535
- #### tRPC Integration
536
- Middleware para tRPC.
537
-
538
- ```typescript
539
- import { initTRPC } from '@trpc/server';
540
- import { createTRPCMiddleware } from 'drizzle-multitenant/trpc';
541
-
542
- const t = initTRPC.context<Context>().create();
543
-
544
- const tenantMiddleware = createTRPCMiddleware({
545
- manager,
546
- extractTenantId: (ctx) => ctx.req.headers['x-tenant-id'],
547
- });
548
-
549
- export const protectedProcedure = t.procedure.use(tenantMiddleware);
550
-
551
- // Uso
552
- export const userRouter = router({
553
- list: protectedProcedure.query(async ({ ctx }) => {
554
- // ctx.tenantDb já configurado
555
- return ctx.tenantDb.select().from(users);
556
- }),
557
- });
558
- ```
559
-
560
- #### GraphQL Context
561
- Integração com Apollo Server e GraphQL Yoga.
562
-
563
- ```typescript
564
- import { ApolloServer } from '@apollo/server';
565
- import { createGraphQLContext } from 'drizzle-multitenant/graphql';
566
-
567
- const server = new ApolloServer({
568
- typeDefs,
569
- resolvers,
570
- });
571
-
572
- const { url } = await startStandaloneServer(server, {
573
- context: createGraphQLContext({
574
- manager,
575
- extractTenantId: (req) => req.headers['x-tenant-id'],
576
- }),
577
- });
578
-
579
- // Nos resolvers
580
- const resolvers = {
581
- Query: {
582
- users: async (_, __, { tenantDb }) => {
583
- return tenantDb.select().from(users);
584
- },
585
- },
586
- };
587
- ```
588
-
589
- #### BullMQ Integration
590
- Contexto de tenant em jobs de background.
591
-
592
- ```typescript
593
- import { Queue, Worker } from 'bullmq';
594
- import { createBullMQPlugin } from 'drizzle-multitenant/bullmq';
595
-
596
- const queue = new Queue('emails');
597
-
598
- // Job sempre inclui tenantId
599
- await queue.add('send-welcome', {
600
- tenantId: 'abc', // obrigatório
601
- userId: '123',
602
- });
603
-
604
- // Worker com contexto automático
605
- const worker = new Worker(
606
- 'emails',
607
- async (job) => {
608
- // tenantDb configurado automaticamente via job.data.tenantId
609
- const { tenantDb } = job;
610
- const user = await tenantDb
611
- .select()
612
- .from(users)
613
- .where(eq(users.id, job.data.userId));
614
-
615
- await sendEmail(user.email);
616
- },
617
- {
618
- plugins: [createBullMQPlugin({ manager })],
619
- }
620
- );
621
- ```
622
-
623
- **Checklist v1.6.0:**
624
- - [ ] tRPC middleware
625
- - [ ] Apollo Server context
626
- - [ ] GraphQL Yoga context
627
- - [ ] BullMQ plugin
628
- - [ ] Exemplos e documentação
629
-
630
- ---
631
-
632
- ### v2.0.0 - Enterprise Features
633
-
634
- #### Multi-Region Support
635
- Suporte para tenants em diferentes regiões.
636
-
637
- ```typescript
638
- export default defineConfig({
639
- regions: {
640
- 'us-east': {
641
- url: process.env.US_EAST_DB_URL!,
642
- default: true,
643
- },
644
- 'eu-west': {
645
- url: process.env.EU_WEST_DB_URL!,
646
- },
647
- 'ap-south': {
648
- url: process.env.AP_SOUTH_DB_URL!,
649
- },
650
- },
651
- tenantRegion: async (tenantId) => {
652
- const tenant = await getTenant(tenantId);
653
- return tenant.region; // 'us-east' | 'eu-west' | 'ap-south'
654
- },
655
- });
656
-
657
- // Transparente para o código
658
- const db = manager.getDb('tenant-123'); // conecta na região correta
659
- ```
660
-
661
- #### Backup & Restore
662
- Backup e restore por tenant.
663
-
664
- ```typescript
665
- // Backup
666
- await migrator.backupTenant('abc', {
667
- output: './backups/abc-2024-01-15.sql',
668
- format: 'sql', // 'sql' | 'custom' | 'directory'
669
- compress: true,
670
- });
671
-
672
- // Backup para S3
673
- await migrator.backupTenant('abc', {
674
- output: 's3://my-bucket/backups/abc.sql.gz',
675
- s3: { region: 'us-east-1' },
676
- });
677
-
678
- // Restore
679
- await migrator.restoreTenant('abc', {
680
- input: './backups/abc-2024-01-15.sql',
681
- dropExisting: true,
682
- });
683
-
684
- // Restore para novo tenant
685
- await migrator.restoreTenant('abc-copy', {
686
- input: './backups/abc-2024-01-15.sql',
687
- createSchema: true,
688
- });
689
- ```
690
-
691
- #### Tenant Quotas
692
- Limites de recursos por tenant.
693
-
694
- ```typescript
695
- export default defineConfig({
696
- quotas: {
697
- enabled: true,
698
- defaults: {
699
- maxRows: 1000000,
700
- maxStorageMb: 1024,
701
- maxTablesRows: {
702
- users: 10000,
703
- logs: 100000,
704
- },
705
- },
706
- perTenant: async (tenantId) => {
707
- const plan = await getTenantPlan(tenantId);
708
- return quotasByPlan[plan];
709
- },
710
- onQuotaExceeded: async (tenantId, quota, current) => {
711
- // 'warn' | 'block' | 'notify'
712
- await notifyTenantAdmin(tenantId, quota);
713
- return 'warn';
714
- },
715
- },
716
- });
717
- ```
718
-
719
- #### Encryption at Rest
720
- Criptografia de colunas sensíveis.
721
-
722
- ```typescript
723
- export default defineConfig({
724
- encryption: {
725
- enabled: true,
726
- keyProvider: {
727
- type: 'aws-kms',
728
- keyId: process.env.KMS_KEY_ID!,
729
- region: 'us-east-1',
730
- },
731
- // ou
732
- keyProvider: {
733
- type: 'vault',
734
- url: process.env.VAULT_URL!,
735
- token: process.env.VAULT_TOKEN!,
736
- },
737
- columns: [
738
- 'users.ssn',
739
- 'users.tax_id',
740
- 'payments.card_number',
741
- 'payments.cvv',
742
- ],
743
- },
744
- });
745
-
746
- // Transparente para o código
747
- const user = await db.select().from(users).where(eq(users.id, '123'));
748
- // user.ssn já descriptografado
749
- ```
750
-
751
- #### Cross-Tenant Admin Queries
752
- Queries agregadas para dashboards administrativos.
753
-
754
- ```typescript
755
- import { createAdminQuery } from 'drizzle-multitenant';
756
-
757
- const adminQuery = createAdminQuery({ manager });
758
-
759
- // Agregação cross-tenant
760
- const stats = await adminQuery
761
- .fromAllTenants(users)
762
- .select({
763
- tenantId: sql`current_schema()`,
764
- count: count(),
765
- activeUsers: count(sql`CASE WHEN active THEN 1 END`),
766
- })
767
- .groupBy(sql`current_schema()`);
768
-
769
- // Resultado:
770
- // [
771
- // { tenantId: 'tenant_abc', count: 1500, activeUsers: 1200 },
772
- // { tenantId: 'tenant_def', count: 3000, activeUsers: 2800 },
773
- // ]
774
- ```
775
-
776
- **Checklist v2.0.0:**
777
- - [ ] Multi-region support
778
- - [ ] Backup/Restore API
779
- - [ ] S3 integration
780
- - [ ] Tenant quotas
781
- - [ ] Column encryption (KMS/Vault)
782
- - [ ] Cross-tenant admin queries
783
- - [ ] Breaking changes migration guide
784
-
785
- ---
786
-
787
- ## Priorização
788
-
789
- | Versão | Tema | Impacto | Esforço | ETA |
790
- |--------|------|---------|---------|-----|
791
- | v1.1.0 | Resiliência | Alto | Médio | - |
792
- | v1.2.0 | Segurança | Alto | Médio | - |
793
- | v1.3.0 | Performance | Médio | Alto | - |
794
- | v1.4.0 | Estratégias | Alto | Alto | - |
795
- | v1.5.0 | DX | Médio | Médio | - |
796
- | v1.6.0 | Integrações | Médio | Médio | - |
797
- | v2.0.0 | Enterprise | Alto | Muito Alto | - |
798
-
799
- ---
800
-
801
- ## Quick Wins (Podem entrar em qualquer versão)
802
-
803
- | Feature | Esforço | Versão | Status |
804
- |---------|---------|--------|--------|
805
- | Health check API | 2h | v1.1.0 | Pendente |
806
- | Schema name sanitization | 1h | v1.2.0 | Pendente |
807
- | CLI interativo básico | 4h | v1.5.0 | Pendente |
808
- | Structured logging hook | 2h | v1.1.0 | Pendente |
809
- | Tenant clone (schema only) | 4h | v1.5.0 | Pendente |
810
- | ~~CLI migrationsTable config~~ | 1h | v1.0.3 | **Concluído** |
811
- | ~~TenantDbFactory para singletons~~ | 2h | v1.0.3 | **Concluído** |
812
- | ~~Debug utilities para proxies~~ | 1h | v1.0.3 | **Concluído** |
813
-
814
- ---
815
-
816
- ## v1.0.4 - CLI migrationsTable Support
817
-
818
- ### Problema
819
-
820
- A CLI do `drizzle-multitenant` usa a tabela `__drizzle_migrations` para tracking de migrations, mas não permite configurar um nome diferente. Isso causa incompatibilidade com projetos que já usam outra tabela de tracking (ex: `__drizzle_tenant_migrations`).
821
-
822
- ### Solução
823
-
824
- Ler o campo `migrationsTable` do objeto `migrations` na config e passá-lo para o `Migrator`.
825
-
826
- ### Mudanças Necessárias
827
-
828
- #### 1. Atualizar `loadConfig` em `src/cli/utils.ts`
829
-
830
- ```typescript
831
- export async function loadConfig(configPath?: string) {
832
- // ... código existente ...
833
-
834
- return {
835
- config: exported,
836
- migrationsFolder: exported.migrations?.tenantFolder,
837
- tenantDiscovery: exported.migrations?.tenantDiscovery,
838
- migrationsTable: exported.migrations?.migrationsTable, // NOVO
839
- };
840
- }
841
- ```
842
-
843
- #### 2. Atualizar comandos em `src/cli/commands/`
844
-
845
- Passar `migrationsTable` para o `createMigrator`:
846
-
847
- ```typescript
848
- // migrate.ts, status.ts, tenant-create.ts, tenant-drop.ts
849
- const { config, migrationsFolder, tenantDiscovery, migrationsTable } = await loadConfig(options.config);
850
-
851
- const migrator = createMigrator(config, {
852
- migrationsFolder: folder,
853
- tenantDiscovery: discoveryFn,
854
- migrationsTable, // NOVO - passa undefined se não configurado (usa default)
855
- });
856
- ```
857
-
858
- #### 3. Atualizar tipos
859
-
860
- ```typescript
861
- // types.ts ou onde apropriado
862
- interface MigrationsConfig {
863
- tenantFolder: string;
864
- tenantDiscovery: () => Promise<string[]>;
865
- migrationsTable?: string; // NOVO
866
- }
867
- ```
868
-
869
- ### Exemplo de Uso
870
-
871
- ```typescript
872
- // tenant.config.ts
873
- export default {
874
- ...config,
875
- migrations: {
876
- tenantFolder: "./drizzle/tenant",
877
- tenantDiscovery: discoverTenants,
878
- migrationsTable: "__drizzle_tenant_migrations", // NOVO - usa tabela customizada
879
- },
880
- };
881
- ```
882
-
883
- ### Compatibilidade
884
-
885
- - **Backward compatible**: Se não configurado, usa `__drizzle_migrations` (comportamento atual)
886
- - **Migration path**: Projetos existentes podem:
887
- 1. Configurar a tabela antiga na config
888
- 2. Ou renomear a tabela no banco para o novo padrão
889
-
890
- ### Checklist
891
-
892
- - [x] Atualizar `loadConfig` para extrair `migrationsTable`
893
- - [x] Atualizar `migrate` command
894
- - [x] Atualizar `status` command
895
- - [x] Atualizar `tenant:create` command
896
- - [x] Atualizar `tenant:drop` command
897
- - [x] Adicionar teste unitário
898
- - [x] Atualizar README com exemplo
899
- - [x] ~~Publicar v1.0.4~~ (incluído em v1.0.3)
900
-
901
- ---
902
-
903
- ## Breaking Changes Planejados (v2.0.0)
904
-
905
- 1. **Namespace de hooks**: Mover hooks para objeto dedicado
906
- 2. **Métricas opt-out**: Métricas habilitadas por padrão
907
- 3. **Config validation**: Validação mais estrita em runtime
908
- 4. **Import paths**: Consolidar exports
909
-
910
- ```typescript
911
- // v1.x
912
- import { createTenantManager } from 'drizzle-multitenant';
913
- import { createExpressMiddleware } from 'drizzle-multitenant/express';
914
-
915
- // v2.0 (proposta)
916
- import {
917
- createTenantManager,
918
- createExpressMiddleware,
919
- createFastifyPlugin,
920
- } from 'drizzle-multitenant';
921
- ```