drizzle-multitenant 1.2.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 +1809 -5949
- 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 +4 -4
- package/dist/index.js +34 -4745
- 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 -11006
- 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 +116 -4
- package/dist/migrator/index.js +34 -2990
- package/dist/{migrator-BDgFzSh8.d.ts → migrator-B7oPKe73.d.ts} +245 -2
- 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,277 @@
|
|
|
1
|
+
import {existsSync}from'fs';import {mkdir,writeFile,readdir}from'fs/promises';import {join,resolve,dirname}from'path';function h(t){let{tableName:e,tableNamePascal:n,tableNameCamel:r,type:i}=t,a=w(t),o=F(t),d=k(t),s=z(t),m=M(t);return `/**
|
|
2
|
+
* ${n} schema
|
|
3
|
+
* Type: ${i}
|
|
4
|
+
*
|
|
5
|
+
* @module schema/${i}/${r}
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
${a}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ${n} table definition
|
|
12
|
+
*/
|
|
13
|
+
export const ${r} = pgTable('${e}', {
|
|
14
|
+
${o}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
${d}
|
|
18
|
+
${s}
|
|
19
|
+
${m}
|
|
20
|
+
`}function w(t){let{includeTimestamps:e,includeSoftDelete:n,useUuid:r,includeExample:i}=t,a=["pgTable"];return r?a.push("uuid"):a.push("serial"),a.push("text"),i&&a.push("varchar","boolean"),(e||n)&&a.push("timestamp"),a.push("index"),`import { ${[...new Set(a)].sort().join(", ")} } from 'drizzle-orm/pg-core';
|
|
21
|
+
import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
|
|
22
|
+
import { z } from 'zod';`}function F(t){let{includeTimestamps:e,includeSoftDelete:n,useUuid:r,includeExample:i}=t,a=[];return r?a.push(" id: uuid('id').primaryKey().defaultRandom(),"):a.push(" id: serial('id').primaryKey(),"),i&&(a.push(" name: varchar('name', { length: 255 }).notNull(),"),a.push(" description: text('description'),"),a.push(" isActive: boolean('is_active').notNull().default(true),")),e&&(a.push(" createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),"),a.push(" updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),")),n&&a.push(" deletedAt: timestamp('deleted_at', { withTimezone: true }),"),a.join(`
|
|
23
|
+
`)}function k(t){let{tableNameCamel:e,tableName:n,includeExample:r,includeTimestamps:i}=t,a=[];return r&&(a.push(` nameIdx: index('${n}_name_idx').on(${e}.name),`),a.push(` isActiveIdx: index('${n}_is_active_idx').on(${e}.isActive),`)),i&&a.push(` createdAtIdx: index('${n}_created_at_idx').on(${e}.createdAt),`),a.length===0?"":`/**
|
|
24
|
+
* ${t.tableNamePascal} table indexes
|
|
25
|
+
*/
|
|
26
|
+
export const ${e}Indexes = {
|
|
27
|
+
${a.join(`
|
|
28
|
+
`)}
|
|
29
|
+
};
|
|
30
|
+
`}function z(t){return `// Uncomment and modify to add relations
|
|
31
|
+
// import { relations } from 'drizzle-orm';
|
|
32
|
+
//
|
|
33
|
+
// export const ${t.tableNameCamel}Relations = relations(${t.tableNameCamel}, ({ one, many }) => ({
|
|
34
|
+
// // Add your relations here
|
|
35
|
+
// // user: one(users, {
|
|
36
|
+
// // fields: [${t.tableNameCamel}.userId],
|
|
37
|
+
// // references: [users.id],
|
|
38
|
+
// // }),
|
|
39
|
+
// }));
|
|
40
|
+
`}function M(t){let{tableNameCamel:e,tableNamePascal:n}=t;return `/**
|
|
41
|
+
* Zod schemas for validation
|
|
42
|
+
*/
|
|
43
|
+
export const insert${n}Schema = createInsertSchema(${e});
|
|
44
|
+
export const select${n}Schema = createSelectSchema(${e});
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* TypeScript types inferred from schema
|
|
48
|
+
*/
|
|
49
|
+
export type ${n} = typeof ${e}.$inferSelect;
|
|
50
|
+
export type New${n} = typeof ${e}.$inferInsert;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Zod types
|
|
54
|
+
*/
|
|
55
|
+
export type Insert${n} = z.infer<typeof insert${n}Schema>;
|
|
56
|
+
export type Select${n} = z.infer<typeof select${n}Schema>;
|
|
57
|
+
`}function S(t){let{seedName:e,type:n,tableName:r}=t;return n==="shared"?P(e,r):v(e,r)}function v(t,e){let n=e?`import { ${e} } from '../../src/db/schema/tenant/${e}.js';`:"// import { yourTable } from '../../src/db/schema/tenant/yourTable.js';",r=e?G(e):X();return `/**
|
|
58
|
+
* Tenant seed: ${t}
|
|
59
|
+
*
|
|
60
|
+
* This seed runs in the context of each tenant's schema.
|
|
61
|
+
* Use this to populate initial data for each tenant.
|
|
62
|
+
*
|
|
63
|
+
* @module seeds/tenant/${t}
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
import type { SeedFunction } from 'drizzle-multitenant';
|
|
67
|
+
${n}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Seed function that populates tenant data
|
|
71
|
+
*
|
|
72
|
+
* @param db - Drizzle database instance scoped to the tenant
|
|
73
|
+
* @param tenantId - The tenant ID being seeded
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* \`\`\`bash
|
|
77
|
+
* npx drizzle-multitenant seed --file=./drizzle/seeds/tenant/${t}.ts --tenant=my-tenant
|
|
78
|
+
* \`\`\`
|
|
79
|
+
*/
|
|
80
|
+
export const seed: SeedFunction = async (db, tenantId) => {
|
|
81
|
+
console.log(\`Seeding tenant: \${tenantId}\`);
|
|
82
|
+
|
|
83
|
+
${r}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default seed;
|
|
87
|
+
`}function P(t,e){let n=e?`import { ${e} } from '../../src/db/schema/shared/${e}.js';`:"// import { yourTable } from '../../src/db/schema/shared/yourTable.js';",r=e?B(e):j();return `/**
|
|
88
|
+
* Shared seed: ${t}
|
|
89
|
+
*
|
|
90
|
+
* This seed runs against the shared/public schema.
|
|
91
|
+
* Use this to populate global data like plans, roles, permissions, etc.
|
|
92
|
+
*
|
|
93
|
+
* @module seeds/shared/${t}
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
import type { SharedSeedFunction } from 'drizzle-multitenant';
|
|
97
|
+
${n}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Seed function that populates shared data
|
|
101
|
+
*
|
|
102
|
+
* @param db - Drizzle database instance for the shared schema
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* \`\`\`bash
|
|
106
|
+
* npx drizzle-multitenant seed:shared --file=./drizzle/seeds/shared/${t}.ts
|
|
107
|
+
* \`\`\`
|
|
108
|
+
*/
|
|
109
|
+
export const seed: SharedSeedFunction = async (db) => {
|
|
110
|
+
console.log('Seeding shared schema...');
|
|
111
|
+
|
|
112
|
+
${r}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default seed;
|
|
116
|
+
`}function G(t){return ` // Insert data with conflict handling (idempotent)
|
|
117
|
+
await db.insert(${t}).values([
|
|
118
|
+
{
|
|
119
|
+
name: 'Example Item 1',
|
|
120
|
+
// Add your columns here
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'Example Item 2',
|
|
124
|
+
// Add your columns here
|
|
125
|
+
},
|
|
126
|
+
]).onConflictDoNothing();
|
|
127
|
+
|
|
128
|
+
console.log(\`Seeded ${t} for tenant: \${tenantId}\`);`}function B(t){return ` // Insert data with conflict handling (idempotent)
|
|
129
|
+
await db.insert(${t}).values([
|
|
130
|
+
{
|
|
131
|
+
name: 'Example Item 1',
|
|
132
|
+
// Add your columns here
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'Example Item 2',
|
|
136
|
+
// Add your columns here
|
|
137
|
+
},
|
|
138
|
+
]).onConflictDoNothing();
|
|
139
|
+
|
|
140
|
+
console.log('Seeded ${t} in shared schema');`}function X(){return ` // Example: Insert initial data
|
|
141
|
+
// await db.insert(yourTable).values([
|
|
142
|
+
// { name: 'Item 1', description: 'Description 1' },
|
|
143
|
+
// { name: 'Item 2', description: 'Description 2' },
|
|
144
|
+
// ]).onConflictDoNothing();
|
|
145
|
+
|
|
146
|
+
// You can use tenantId to customize data per tenant
|
|
147
|
+
// if (tenantId === 'demo-tenant') {
|
|
148
|
+
// await db.insert(yourTable).values([
|
|
149
|
+
// { name: 'Demo Item', description: 'Only for demo tenant' },
|
|
150
|
+
// ]);
|
|
151
|
+
// }
|
|
152
|
+
|
|
153
|
+
console.log(\`Seed completed for tenant: \${tenantId}\`);`}function j(){return ` // Example: Insert shared data like plans, roles, permissions
|
|
154
|
+
// await db.insert(plans).values([
|
|
155
|
+
// { id: 'free', name: 'Free', price: 0 },
|
|
156
|
+
// { id: 'pro', name: 'Pro', price: 29 },
|
|
157
|
+
// { id: 'enterprise', name: 'Enterprise', price: 99 },
|
|
158
|
+
// ]).onConflictDoNothing();
|
|
159
|
+
|
|
160
|
+
// await db.insert(roles).values([
|
|
161
|
+
// { id: 'admin', name: 'Administrator', permissions: ['*'] },
|
|
162
|
+
// { id: 'user', name: 'User', permissions: ['read'] },
|
|
163
|
+
// ]).onConflictDoNothing();
|
|
164
|
+
|
|
165
|
+
console.log('Shared seed completed');`}function N(t){let{migrationName:e,type:n,template:r,tableName:i}=t,a=new Date().toISOString(),o=`-- Migration: ${e}
|
|
166
|
+
-- Type: ${n}
|
|
167
|
+
-- Created at: ${a}
|
|
168
|
+
-- Template: ${r}
|
|
169
|
+
|
|
170
|
+
`;switch(r){case "create-table":return o+K(i);case "add-column":return o+W(i);case "add-index":return o+q(i);case "add-foreign-key":return o+Y(i);default:return o+H()}}function K(t){let e=t||"table_name";return `-- Create table: ${e}
|
|
171
|
+
CREATE TABLE IF NOT EXISTS "${e}" (
|
|
172
|
+
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
173
|
+
"name" VARCHAR(255) NOT NULL,
|
|
174
|
+
"description" TEXT,
|
|
175
|
+
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
|
176
|
+
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
177
|
+
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
-- Create indexes
|
|
181
|
+
CREATE INDEX IF NOT EXISTS "${e}_name_idx" ON "${e}" ("name");
|
|
182
|
+
CREATE INDEX IF NOT EXISTS "${e}_created_at_idx" ON "${e}" ("created_at");
|
|
183
|
+
|
|
184
|
+
-- Add updated_at trigger (optional but recommended)
|
|
185
|
+
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
186
|
+
RETURNS TRIGGER AS $$
|
|
187
|
+
BEGIN
|
|
188
|
+
NEW.updated_at = NOW();
|
|
189
|
+
RETURN NEW;
|
|
190
|
+
END;
|
|
191
|
+
$$ LANGUAGE plpgsql;
|
|
192
|
+
|
|
193
|
+
DROP TRIGGER IF EXISTS update_${e}_updated_at ON "${e}";
|
|
194
|
+
CREATE TRIGGER update_${e}_updated_at
|
|
195
|
+
BEFORE UPDATE ON "${e}"
|
|
196
|
+
FOR EACH ROW
|
|
197
|
+
EXECUTE FUNCTION update_updated_at_column();
|
|
198
|
+
`}function W(t){let e=t||"table_name";return `-- Add column to: ${e}
|
|
199
|
+
-- ALTER TABLE "${e}" ADD COLUMN "column_name" data_type [constraints];
|
|
200
|
+
|
|
201
|
+
-- Examples:
|
|
202
|
+
|
|
203
|
+
-- Add a nullable text column
|
|
204
|
+
-- ALTER TABLE "${e}" ADD COLUMN "notes" TEXT;
|
|
205
|
+
|
|
206
|
+
-- Add a non-null column with default
|
|
207
|
+
-- ALTER TABLE "${e}" ADD COLUMN "status" VARCHAR(50) NOT NULL DEFAULT 'pending';
|
|
208
|
+
|
|
209
|
+
-- Add a column with foreign key reference
|
|
210
|
+
-- ALTER TABLE "${e}" ADD COLUMN "user_id" UUID REFERENCES "users" ("id");
|
|
211
|
+
|
|
212
|
+
-- Add a column with check constraint
|
|
213
|
+
-- ALTER TABLE "${e}" ADD COLUMN "priority" INTEGER CHECK (priority >= 1 AND priority <= 5);
|
|
214
|
+
|
|
215
|
+
-- Write your column additions below:
|
|
216
|
+
|
|
217
|
+
`}function q(t){let e=t||"table_name";return `-- Add index to: ${e}
|
|
218
|
+
-- CREATE INDEX [CONCURRENTLY] "index_name" ON "${e}" ("column_name");
|
|
219
|
+
|
|
220
|
+
-- Examples:
|
|
221
|
+
|
|
222
|
+
-- Simple index on single column
|
|
223
|
+
-- CREATE INDEX "${e}_column_idx" ON "${e}" ("column_name");
|
|
224
|
+
|
|
225
|
+
-- Composite index on multiple columns
|
|
226
|
+
-- CREATE INDEX "${e}_col1_col2_idx" ON "${e}" ("column1", "column2");
|
|
227
|
+
|
|
228
|
+
-- Partial index (filtered)
|
|
229
|
+
-- CREATE INDEX "${e}_active_idx" ON "${e}" ("created_at") WHERE "is_active" = true;
|
|
230
|
+
|
|
231
|
+
-- Unique index
|
|
232
|
+
-- CREATE UNIQUE INDEX "${e}_email_unique_idx" ON "${e}" ("email");
|
|
233
|
+
|
|
234
|
+
-- GIN index for full-text search or JSONB
|
|
235
|
+
-- CREATE INDEX "${e}_data_gin_idx" ON "${e}" USING GIN ("data");
|
|
236
|
+
|
|
237
|
+
-- CONCURRENT index (doesn't lock table, recommended for production)
|
|
238
|
+
-- CREATE INDEX CONCURRENTLY "${e}_column_idx" ON "${e}" ("column_name");
|
|
239
|
+
|
|
240
|
+
-- Write your indexes below:
|
|
241
|
+
|
|
242
|
+
`}function Y(t){let e=t||"table_name";return `-- Add foreign key to: ${e}
|
|
243
|
+
-- ALTER TABLE "${e}" ADD CONSTRAINT "fk_name" FOREIGN KEY ("column") REFERENCES "other_table" ("id");
|
|
244
|
+
|
|
245
|
+
-- Examples:
|
|
246
|
+
|
|
247
|
+
-- Basic foreign key
|
|
248
|
+
-- ALTER TABLE "${e}" ADD CONSTRAINT "${e}_user_id_fk"
|
|
249
|
+
-- FOREIGN KEY ("user_id") REFERENCES "users" ("id");
|
|
250
|
+
|
|
251
|
+
-- Foreign key with ON DELETE action
|
|
252
|
+
-- ALTER TABLE "${e}" ADD CONSTRAINT "${e}_user_id_fk"
|
|
253
|
+
-- FOREIGN KEY ("user_id") REFERENCES "users" ("id")
|
|
254
|
+
-- ON DELETE CASCADE;
|
|
255
|
+
|
|
256
|
+
-- Foreign key with ON UPDATE action
|
|
257
|
+
-- ALTER TABLE "${e}" ADD CONSTRAINT "${e}_category_id_fk"
|
|
258
|
+
-- FOREIGN KEY ("category_id") REFERENCES "categories" ("id")
|
|
259
|
+
-- ON DELETE SET NULL
|
|
260
|
+
-- ON UPDATE CASCADE;
|
|
261
|
+
|
|
262
|
+
-- Cross-schema foreign key (to shared/public schema)
|
|
263
|
+
-- ALTER TABLE "${e}" ADD CONSTRAINT "${e}_plan_id_fk"
|
|
264
|
+
-- FOREIGN KEY ("plan_id") REFERENCES "public"."plans" ("id");
|
|
265
|
+
|
|
266
|
+
-- Write your foreign keys below:
|
|
267
|
+
|
|
268
|
+
`}function H(){return `-- Write your SQL migration here
|
|
269
|
+
|
|
270
|
+
-- Up migration (apply changes)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
-- Remember to consider:
|
|
274
|
+
-- 1. Idempotency: Use IF NOT EXISTS / IF EXISTS where possible
|
|
275
|
+
-- 2. Transactions: This will run in a transaction
|
|
276
|
+
-- 3. Rollback: Plan how to undo these changes if needed
|
|
277
|
+
`}function x(t){let e=[/^create[-_](.+)$/i,/^add[-_](.+?)[-_]?(?:table)?$/i,/^(?:add|create)[-_](?:index|fk|constraint)[-_](?:to|on)[-_](.+)$/i,/^(.+?)[-_](?:table|schema)$/i];for(let n of e){let r=t.match(n);if(r?.[1])return r[1].toLowerCase().replace(/[-\s]+/g,"_").replace(/s$/,"")}}function $(t){let e=t.toLowerCase();return e.includes("create")||e.includes("table")?"create-table":e.includes("index")||e.includes("idx")?"add-index":e.includes("fk")||e.includes("foreign")||e.includes("reference")?"add-foreign-key":e.includes("add")||e.includes("column")?"add-column":"blank"}function y(t){let e=t.replace(/([a-z])([A-Z])/g,"$1_$2").replace(/[-\s]+/g,"_").toLowerCase(),n=e.split("_").map(i=>i.charAt(0).toUpperCase()+i.slice(1)).join(""),r=n.charAt(0).toLowerCase()+n.slice(1);return {snake:e,pascal:n,camel:r}}var f={schemaDir:"src/db/schema",seedDir:"drizzle/seeds",tenantMigrationsDir:"drizzle/tenant-migrations",sharedMigrationsDir:"drizzle/shared-migrations"};async function Q(t){let{name:e,type:n,outputDir:r,includeExample:i=true,includeTimestamps:a=true,includeSoftDelete:o=false,useUuid:d=true}=t;try{let s=y(e),m={tableName:s.snake,tableNamePascal:s.pascal,tableNameCamel:s.camel,type:n,includeTimestamps:a,includeSoftDelete:o,useUuid:d,includeExample:i},l=h(m),E=r||join(f.schemaDir,n),p=`${s.camel}.ts`,c=resolve(process.cwd(),E,p);return await mkdir(dirname(c),{recursive:!0}),existsSync(c)?{success:!1,filePath:c,fileName:p,kind:"schema",type:n,error:`File already exists: ${c}`}:(await writeFile(c,l,"utf-8"),{success:!0,filePath:c,fileName:p,kind:"schema",type:n})}catch(s){return {success:false,filePath:"",fileName:"",kind:"schema",type:n,error:s instanceof Error?s.message:String(s)}}}async function V(t){let{name:e,type:n,outputDir:r,tableName:i}=t;try{let a=y(e),o={seedName:a.camel,type:n,...i!==void 0&&{tableName:i}},d=S(o),s=r||join(f.seedDir,n),m=`${a.camel}.ts`,l=resolve(process.cwd(),s,m);return await mkdir(dirname(l),{recursive:!0}),existsSync(l)?{success:!1,filePath:l,fileName:m,kind:"seed",type:n,error:`File already exists: ${l}`}:(await writeFile(l,d,"utf-8"),{success:!0,filePath:l,fileName:m,kind:"seed",type:n})}catch(a){return {success:false,filePath:"",fileName:"",kind:"seed",type:n,error:a instanceof Error?a.message:String(a)}}}async function J(t){let{name:e,type:n,outputDir:r,template:i}=t;try{let a=n==="shared"?f.sharedMigrationsDir:f.tenantMigrationsDir,o=r||a,d=resolve(process.cwd(),o);await mkdir(d,{recursive:!0});let m=(existsSync(d)?await readdir(d):[]).filter(g=>g.endsWith(".sql")),l=0;for(let g of m){let R=g.match(/^(\d+)_/);R?.[1]&&(l=Math.max(l,parseInt(R[1],10)));}let E=(l+1).toString().padStart(4,"0"),p=e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,""),c=`${E}_${p}.sql`,u=join(d,c),O=i||$(e),I=x(e),L={migrationName:e,type:n,template:O,...I!==void 0&&{tableName:I}},U=N(L);return existsSync(u)?{success:!1,filePath:u,fileName:c,kind:"migration",type:n,error:`File already exists: ${u}`}:(await writeFile(u,U,"utf-8"),{success:!0,filePath:u,fileName:c,kind:"migration",type:n})}catch(a){return {success:false,filePath:"",fileName:"",kind:"migration",type:n,error:a instanceof Error?a.message:String(a)}}}function ee(){return [{value:"create-table",label:"Create Table",description:"Template for creating a new table with common columns"},{value:"add-column",label:"Add Column",description:"Template for adding columns to an existing table"},{value:"add-index",label:"Add Index",description:"Template for creating indexes"},{value:"add-foreign-key",label:"Add Foreign Key",description:"Template for adding foreign key constraints"},{value:"blank",label:"Blank",description:"Empty migration with basic comments"}]}export{f as DEFAULT_DIRS,N as generateMigrationTemplate,h as generateSchemaTemplate,S as generateSeedTemplate,ee as getMigrationTemplates,$ as inferMigrationTemplate,x as inferTableName,J as scaffoldMigration,Q as scaffoldSchema,V as scaffoldSeed,y as toCase};
|
|
@@ -111,6 +111,52 @@ interface DebugContext {
|
|
|
111
111
|
/** Additional metadata */
|
|
112
112
|
metadata?: Record<string, unknown>;
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Lint rule configuration - can be severity only or [severity, options]
|
|
116
|
+
*/
|
|
117
|
+
type LintRuleConfig<TOptions = Record<string, unknown>> = 'off' | 'warn' | 'error' | ['off' | 'warn' | 'error', TOptions];
|
|
118
|
+
/**
|
|
119
|
+
* All available lint rules for configuration
|
|
120
|
+
*/
|
|
121
|
+
interface LintRulesConfig {
|
|
122
|
+
/** Enforce table naming convention (snake_case by default) */
|
|
123
|
+
'table-naming'?: LintRuleConfig<{
|
|
124
|
+
style?: 'snake_case' | 'camelCase' | 'PascalCase' | 'kebab-case';
|
|
125
|
+
exceptions?: string[];
|
|
126
|
+
}>;
|
|
127
|
+
/** Enforce column naming convention (snake_case by default) */
|
|
128
|
+
'column-naming'?: LintRuleConfig<{
|
|
129
|
+
style?: 'snake_case' | 'camelCase' | 'PascalCase' | 'kebab-case';
|
|
130
|
+
exceptions?: string[];
|
|
131
|
+
}>;
|
|
132
|
+
/** Require every table to have a primary key */
|
|
133
|
+
'require-primary-key'?: LintRuleConfig;
|
|
134
|
+
/** Prefer UUID over serial/integer for primary keys */
|
|
135
|
+
'prefer-uuid-pk'?: LintRuleConfig;
|
|
136
|
+
/** Require created_at/updated_at columns */
|
|
137
|
+
'require-timestamps'?: LintRuleConfig<{
|
|
138
|
+
columns?: string[];
|
|
139
|
+
}>;
|
|
140
|
+
/** Require indexes on foreign key columns */
|
|
141
|
+
'index-foreign-keys'?: LintRuleConfig;
|
|
142
|
+
/** Warn about CASCADE DELETE on foreign keys */
|
|
143
|
+
'no-cascade-delete'?: LintRuleConfig;
|
|
144
|
+
/** Require soft delete column on tables */
|
|
145
|
+
'require-soft-delete'?: LintRuleConfig<{
|
|
146
|
+
column?: string;
|
|
147
|
+
}>;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Lint configuration
|
|
151
|
+
*/
|
|
152
|
+
interface LintConfig {
|
|
153
|
+
/** Lint rules configuration */
|
|
154
|
+
rules?: LintRulesConfig;
|
|
155
|
+
/** Glob patterns for schema files to include */
|
|
156
|
+
include?: string[];
|
|
157
|
+
/** Glob patterns for schema files to exclude */
|
|
158
|
+
exclude?: string[];
|
|
159
|
+
}
|
|
114
160
|
/**
|
|
115
161
|
* Main configuration interface
|
|
116
162
|
*/
|
|
@@ -127,6 +173,8 @@ interface Config<TTenantSchema extends Record<string, unknown> = Record<string,
|
|
|
127
173
|
metrics?: MetricsConfig;
|
|
128
174
|
/** Debug configuration */
|
|
129
175
|
debug?: DebugConfig;
|
|
176
|
+
/** Schema linting configuration */
|
|
177
|
+
lint?: LintConfig;
|
|
130
178
|
}
|
|
131
179
|
/**
|
|
132
180
|
* Internal pool entry for LRU cache
|
|
@@ -346,4 +394,4 @@ declare const DEFAULT_CONFIG: {
|
|
|
346
394
|
};
|
|
347
395
|
};
|
|
348
396
|
|
|
349
|
-
export { type Config as C, type DebugConfig as D, type Hooks as H, type IsolationConfig as I, type MetricsConfig as M, type PoolEntry as P, type RetryConfig as R, type SharedDb as S, type TenantManager as T, type WarmupOptions as W, type TenantDb as a, type ConnectionConfig as b, type IsolationStrategy as c, type SchemasConfig as d, type DebugContext as e, type WarmupResult as f, type TenantWarmupResult as g, type HealthCheckOptions as h, type HealthCheckResult as i, type PoolHealth as j, type PoolHealthStatus as k, type MetricsResult as l, type TenantPoolMetrics as m, type ConnectionMetrics as n,
|
|
397
|
+
export { type Config as C, type DebugConfig as D, type Hooks as H, type IsolationConfig as I, type LintRuleConfig as L, type MetricsConfig as M, type PoolEntry as P, type RetryConfig as R, type SharedDb as S, type TenantManager as T, type WarmupOptions as W, type TenantDb as a, type ConnectionConfig as b, type IsolationStrategy as c, type SchemasConfig as d, type DebugContext as e, type WarmupResult as f, type TenantWarmupResult as g, type HealthCheckOptions as h, type HealthCheckResult as i, type PoolHealth as j, type PoolHealthStatus as k, type MetricsResult as l, type TenantPoolMetrics as m, type ConnectionMetrics as n, type LintRulesConfig as o, type LintConfig as p, DEFAULT_CONFIG as q };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drizzle-multitenant",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Multi-tenancy toolkit for Drizzle ORM with schema isolation, tenant context, and parallel migrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,6 +34,22 @@
|
|
|
34
34
|
"./cross-schema": {
|
|
35
35
|
"import": "./dist/cross-schema/index.js",
|
|
36
36
|
"types": "./dist/cross-schema/index.d.ts"
|
|
37
|
+
},
|
|
38
|
+
"./scaffold": {
|
|
39
|
+
"import": "./dist/scaffold/index.js",
|
|
40
|
+
"types": "./dist/scaffold/index.d.ts"
|
|
41
|
+
},
|
|
42
|
+
"./lint": {
|
|
43
|
+
"import": "./dist/lint/index.js",
|
|
44
|
+
"types": "./dist/lint/index.d.ts"
|
|
45
|
+
},
|
|
46
|
+
"./metrics": {
|
|
47
|
+
"import": "./dist/metrics/index.js",
|
|
48
|
+
"types": "./dist/metrics/index.d.ts"
|
|
49
|
+
},
|
|
50
|
+
"./export": {
|
|
51
|
+
"import": "./dist/export/index.js",
|
|
52
|
+
"types": "./dist/export/index.d.ts"
|
|
37
53
|
}
|
|
38
54
|
},
|
|
39
55
|
"bin": {
|
|
@@ -73,6 +89,7 @@
|
|
|
73
89
|
"cli-progress": "^3.12.0",
|
|
74
90
|
"cli-table3": "^0.6.5",
|
|
75
91
|
"commander": "^12.1.0",
|
|
92
|
+
"glob": "^13.0.0",
|
|
76
93
|
"lru-cache": "^10.4.3",
|
|
77
94
|
"ora": "^8.2.0"
|
|
78
95
|
},
|