drizzle-multitenant 1.0.8 → 1.0.10

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.
@@ -1,4 +1,4 @@
1
- import { T as TenantManager, a as TenantDb, S as SharedDb } from './types-DKVaTaIb.js';
1
+ import { T as TenantManager, a as TenantDb, S as SharedDb } from './types-B5eSRLFW.js';
2
2
 
3
3
  /**
4
4
  * Base tenant context data
@@ -101,6 +101,30 @@ type ColumnSelection<T extends Table> = {
101
101
  type InferSelectedColumns<T extends Table, TSelection extends ColumnSelection<T>> = {
102
102
  [K in keyof TSelection as TSelection[K] extends true ? K : never]: T['_']['columns'][K extends keyof T['_']['columns'] ? K : never]['_']['data'];
103
103
  };
104
+ /**
105
+ * Configuration for withShared helper
106
+ */
107
+ interface WithSharedConfig<TTenantSchema extends Record<string, unknown> = Record<string, unknown>, TSharedSchema extends Record<string, unknown> = Record<string, unknown>> {
108
+ /** Tenant schema object containing table definitions */
109
+ tenant: TTenantSchema;
110
+ /** Shared schema object containing table definitions */
111
+ shared: TSharedSchema;
112
+ }
113
+ /**
114
+ * Options for withShared helper
115
+ */
116
+ interface WithSharedOptions {
117
+ /** Tenant schema name (default: derived from tenantDb) */
118
+ tenantSchema?: string;
119
+ /** Shared schema name (default: 'public') */
120
+ sharedSchema?: string;
121
+ }
122
+ /**
123
+ * Infer result type from select fields
124
+ */
125
+ type InferSelectResult<T extends Record<string, Column>> = {
126
+ [K in keyof T]: T[K]['_']['data'];
127
+ };
104
128
 
105
129
  /**
106
130
  * Cross-schema query builder for joining tenant and shared data
@@ -259,4 +283,131 @@ declare function buildCrossSchemaSelect<T extends Record<string, Column>>(fields
259
283
  getSchema: () => string;
260
284
  };
261
285
 
262
- export { type ColumnSelection, type CrossSchemaContext, CrossSchemaQueryBuilder, type CrossSchemaRawOptions, type InferSelectedColumns, type JoinCondition, type JoinDefinition, type JoinType, type LookupResult, type QueryBuilderState, type SchemaSource, type SelectField, type SharedLookupConfig, type TableReference, buildCrossSchemaSelect, createCrossSchemaQuery, crossSchemaRaw, withSharedLookup };
286
+ /**
287
+ * Simplified cross-schema query builder with automatic schema detection
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * const result = await withShared(tenantDb, sharedDb, { tenant: tenantSchema, shared: sharedSchema })
292
+ * .from(pedidos)
293
+ * .leftJoin(workflowSteps, eq(pedidos.workflowStepId, workflowSteps.id))
294
+ * .select({ pedidoId: pedidos.id, workflowNome: workflowSteps.nome })
295
+ * .execute();
296
+ * ```
297
+ */
298
+ declare class WithSharedQueryBuilder<TTenantSchema extends Record<string, unknown> = Record<string, unknown>, _TSharedSchema extends Record<string, unknown> = Record<string, unknown>> {
299
+ private readonly tenantDb;
300
+ private readonly sharedTables;
301
+ private readonly tenantSchemaName;
302
+ private readonly sharedSchemaName;
303
+ private fromTable;
304
+ private joins;
305
+ private selectFields;
306
+ private whereCondition;
307
+ private orderByFields;
308
+ private limitValue;
309
+ private offsetValue;
310
+ constructor(tenantDb: NodePgDatabase<TTenantSchema>, sharedTables: Set<Table>, tenantSchemaName: string, sharedSchemaName?: string);
311
+ /**
312
+ * Set the main table to query from
313
+ * Automatically detects if it's a tenant or shared table
314
+ */
315
+ from<T extends Table>(table: T): this;
316
+ /**
317
+ * Add a left join with automatic schema detection
318
+ */
319
+ leftJoin<T extends Table>(table: T, condition: JoinCondition): this;
320
+ /**
321
+ * Add an inner join with automatic schema detection
322
+ */
323
+ innerJoin<T extends Table>(table: T, condition: JoinCondition): this;
324
+ /**
325
+ * Add a right join with automatic schema detection
326
+ */
327
+ rightJoin<T extends Table>(table: T, condition: JoinCondition): this;
328
+ /**
329
+ * Add a full outer join with automatic schema detection
330
+ */
331
+ fullJoin<T extends Table>(table: T, condition: JoinCondition): this;
332
+ /**
333
+ * Select specific fields
334
+ */
335
+ select<T extends Record<string, Column>>(fields: T): this;
336
+ /**
337
+ * Add a WHERE condition
338
+ */
339
+ where(condition: SQL<unknown>): this;
340
+ /**
341
+ * Add ORDER BY
342
+ */
343
+ orderBy(...fields: SQL<unknown>[]): this;
344
+ /**
345
+ * Set LIMIT
346
+ */
347
+ limit(value: number): this;
348
+ /**
349
+ * Set OFFSET
350
+ */
351
+ offset(value: number): this;
352
+ /**
353
+ * Execute the query and return typed results
354
+ */
355
+ execute<TResult extends Record<string, unknown> = InferSelectResult<typeof this.selectFields>>(): Promise<TResult[]>;
356
+ /**
357
+ * Add a join to the query
358
+ */
359
+ private addJoin;
360
+ /**
361
+ * Build the SQL query
362
+ */
363
+ private buildSql;
364
+ /**
365
+ * Get table alias for a column (used in SELECT)
366
+ */
367
+ private getTableAliasForColumn;
368
+ /**
369
+ * Get SQL keyword for join type
370
+ */
371
+ private getJoinKeyword;
372
+ }
373
+ /**
374
+ * Create a simplified cross-schema query builder with automatic schema detection
375
+ *
376
+ * This helper automatically detects whether a table belongs to the tenant schema
377
+ * or the shared schema based on the schema configuration provided.
378
+ *
379
+ * @param tenantDb - The tenant database instance
380
+ * @param sharedDb - The shared database instance (unused but kept for API symmetry)
381
+ * @param schemas - Object containing tenant and shared schema definitions
382
+ * @param options - Optional configuration for schema names
383
+ * @returns A query builder with automatic schema detection
384
+ *
385
+ * @example
386
+ * ```typescript
387
+ * // Define your schemas
388
+ * const tenantSchema = { pedidos, clientes };
389
+ * const sharedSchema = { workflowSteps, plans };
390
+ *
391
+ * // Use withShared for cross-schema queries
392
+ * const result = await withShared(
393
+ * tenantDb,
394
+ * sharedDb,
395
+ * { tenant: tenantSchema, shared: sharedSchema }
396
+ * )
397
+ * .from(pedidos) // Auto-detected as tenant table
398
+ * .leftJoin(workflowSteps, // Auto-detected as shared table
399
+ * eq(pedidos.workflowStepId, workflowSteps.id)
400
+ * )
401
+ * .select({
402
+ * pedidoId: pedidos.id,
403
+ * workflowNome: workflowSteps.nome,
404
+ * })
405
+ * .where(eq(pedidos.status, 'active'))
406
+ * .orderBy(desc(pedidos.createdAt))
407
+ * .limit(10)
408
+ * .execute();
409
+ * ```
410
+ */
411
+ declare function withShared<TTenantSchema extends Record<string, unknown>, TSharedSchema extends Record<string, unknown>>(tenantDb: NodePgDatabase<TTenantSchema>, _sharedDb: NodePgDatabase<TSharedSchema>, schemas: WithSharedConfig<TTenantSchema, TSharedSchema>, options?: WithSharedOptions): WithSharedQueryBuilder<TTenantSchema, TSharedSchema>;
412
+
413
+ export { type ColumnSelection, type CrossSchemaContext, CrossSchemaQueryBuilder, type CrossSchemaRawOptions, type InferSelectResult, type InferSelectedColumns, type JoinCondition, type JoinDefinition, type JoinType, type LookupResult, type QueryBuilderState, type SchemaSource, type SelectField, type SharedLookupConfig, type TableReference, type WithSharedConfig, type WithSharedOptions, WithSharedQueryBuilder, buildCrossSchemaSelect, createCrossSchemaQuery, crossSchemaRaw, withShared, withSharedLookup };
@@ -213,7 +213,214 @@ function buildCrossSchemaSelect(fields, tenantSchema, _sharedSchema) {
213
213
  };
214
214
  return { columns, getSchema };
215
215
  }
216
+ function extractTablesFromSchema(schema) {
217
+ const tables = /* @__PURE__ */ new Set();
218
+ for (const value of Object.values(schema)) {
219
+ if (value && typeof value === "object" && "_" in value) {
220
+ const branded = value;
221
+ if (branded._?.brand === "Table") {
222
+ tables.add(value);
223
+ }
224
+ }
225
+ }
226
+ return tables;
227
+ }
228
+ function isSharedTable(table, sharedTables) {
229
+ return sharedTables.has(table);
230
+ }
231
+ var WithSharedQueryBuilder = class {
232
+ constructor(tenantDb, sharedTables, tenantSchemaName, sharedSchemaName = "public") {
233
+ this.tenantDb = tenantDb;
234
+ this.sharedTables = sharedTables;
235
+ this.tenantSchemaName = tenantSchemaName;
236
+ this.sharedSchemaName = sharedSchemaName;
237
+ }
238
+ fromTable = null;
239
+ joins = [];
240
+ selectFields = {};
241
+ whereCondition = null;
242
+ orderByFields = [];
243
+ limitValue = null;
244
+ offsetValue = null;
245
+ /**
246
+ * Set the main table to query from
247
+ * Automatically detects if it's a tenant or shared table
248
+ */
249
+ from(table) {
250
+ const isShared = isSharedTable(table, this.sharedTables);
251
+ this.fromTable = {
252
+ table,
253
+ isShared,
254
+ schemaName: isShared ? this.sharedSchemaName : this.tenantSchemaName
255
+ };
256
+ return this;
257
+ }
258
+ /**
259
+ * Add a left join with automatic schema detection
260
+ */
261
+ leftJoin(table, condition) {
262
+ return this.addJoin(table, condition, "left");
263
+ }
264
+ /**
265
+ * Add an inner join with automatic schema detection
266
+ */
267
+ innerJoin(table, condition) {
268
+ return this.addJoin(table, condition, "inner");
269
+ }
270
+ /**
271
+ * Add a right join with automatic schema detection
272
+ */
273
+ rightJoin(table, condition) {
274
+ return this.addJoin(table, condition, "right");
275
+ }
276
+ /**
277
+ * Add a full outer join with automatic schema detection
278
+ */
279
+ fullJoin(table, condition) {
280
+ return this.addJoin(table, condition, "full");
281
+ }
282
+ /**
283
+ * Select specific fields
284
+ */
285
+ select(fields) {
286
+ this.selectFields = fields;
287
+ return this;
288
+ }
289
+ /**
290
+ * Add a WHERE condition
291
+ */
292
+ where(condition) {
293
+ this.whereCondition = condition;
294
+ return this;
295
+ }
296
+ /**
297
+ * Add ORDER BY
298
+ */
299
+ orderBy(...fields) {
300
+ this.orderByFields = fields;
301
+ return this;
302
+ }
303
+ /**
304
+ * Set LIMIT
305
+ */
306
+ limit(value) {
307
+ this.limitValue = value;
308
+ return this;
309
+ }
310
+ /**
311
+ * Set OFFSET
312
+ */
313
+ offset(value) {
314
+ this.offsetValue = value;
315
+ return this;
316
+ }
317
+ /**
318
+ * Execute the query and return typed results
319
+ */
320
+ async execute() {
321
+ if (!this.fromTable) {
322
+ throw new Error("[drizzle-multitenant] No table specified. Use .from() first.");
323
+ }
324
+ const sqlQuery = this.buildSql();
325
+ const result = await this.tenantDb.execute(sqlQuery);
326
+ return result.rows;
327
+ }
328
+ /**
329
+ * Add a join to the query
330
+ */
331
+ addJoin(table, condition, type) {
332
+ const isShared = isSharedTable(table, this.sharedTables);
333
+ this.joins.push({
334
+ table,
335
+ isShared,
336
+ schemaName: isShared ? this.sharedSchemaName : this.tenantSchemaName,
337
+ condition,
338
+ type
339
+ });
340
+ return this;
341
+ }
342
+ /**
343
+ * Build the SQL query
344
+ */
345
+ buildSql() {
346
+ if (!this.fromTable) {
347
+ throw new Error("[drizzle-multitenant] No table specified");
348
+ }
349
+ const parts = [];
350
+ const selectParts = Object.entries(this.selectFields).map(([alias, column]) => {
351
+ const columnName = column.name;
352
+ const tableName = this.getTableAliasForColumn(column);
353
+ if (tableName) {
354
+ return sql`${sql.raw(`"${tableName}"."${columnName}"`)} as ${sql.raw(`"${alias}"`)}`;
355
+ }
356
+ return sql`${sql.raw(`"${columnName}"`)} as ${sql.raw(`"${alias}"`)}`;
357
+ });
358
+ if (selectParts.length === 0) {
359
+ parts.push(sql`SELECT *`);
360
+ } else {
361
+ parts.push(sql`SELECT ${sql.join(selectParts, sql`, `)}`);
362
+ }
363
+ const fromTableName = getTableName(this.fromTable.table);
364
+ const fromTableRef = `"${this.fromTable.schemaName}"."${fromTableName}"`;
365
+ parts.push(sql` FROM ${sql.raw(fromTableRef)} "${sql.raw(fromTableName)}"`);
366
+ for (const join of this.joins) {
367
+ const joinTableName = getTableName(join.table);
368
+ const joinTableRef = `"${join.schemaName}"."${joinTableName}"`;
369
+ const joinKeyword = this.getJoinKeyword(join.type);
370
+ parts.push(
371
+ sql` ${sql.raw(joinKeyword)} ${sql.raw(joinTableRef)} "${sql.raw(joinTableName)}" ON ${join.condition}`
372
+ );
373
+ }
374
+ if (this.whereCondition) {
375
+ parts.push(sql` WHERE ${this.whereCondition}`);
376
+ }
377
+ if (this.orderByFields.length > 0) {
378
+ parts.push(sql` ORDER BY ${sql.join(this.orderByFields, sql`, `)}`);
379
+ }
380
+ if (this.limitValue !== null) {
381
+ parts.push(sql` LIMIT ${sql.raw(this.limitValue.toString())}`);
382
+ }
383
+ if (this.offsetValue !== null) {
384
+ parts.push(sql` OFFSET ${sql.raw(this.offsetValue.toString())}`);
385
+ }
386
+ return sql.join(parts, sql``);
387
+ }
388
+ /**
389
+ * Get table alias for a column (used in SELECT)
390
+ */
391
+ getTableAliasForColumn(column) {
392
+ const columnTable = column.table;
393
+ if (columnTable) {
394
+ return getTableName(columnTable);
395
+ }
396
+ return null;
397
+ }
398
+ /**
399
+ * Get SQL keyword for join type
400
+ */
401
+ getJoinKeyword(type) {
402
+ switch (type) {
403
+ case "inner":
404
+ return "INNER JOIN";
405
+ case "left":
406
+ return "LEFT JOIN";
407
+ case "right":
408
+ return "RIGHT JOIN";
409
+ case "full":
410
+ return "FULL OUTER JOIN";
411
+ }
412
+ }
413
+ };
414
+ function withShared(tenantDb, _sharedDb, schemas, options) {
415
+ const sharedTables = extractTablesFromSchema(schemas.shared);
416
+ return new WithSharedQueryBuilder(
417
+ tenantDb,
418
+ sharedTables,
419
+ options?.tenantSchema ?? "tenant",
420
+ options?.sharedSchema ?? "public"
421
+ );
422
+ }
216
423
 
217
- export { CrossSchemaQueryBuilder, buildCrossSchemaSelect, createCrossSchemaQuery, crossSchemaRaw, withSharedLookup };
424
+ export { CrossSchemaQueryBuilder, WithSharedQueryBuilder, buildCrossSchemaSelect, createCrossSchemaQuery, crossSchemaRaw, withShared, withSharedLookup };
218
425
  //# sourceMappingURL=index.js.map
219
426
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cross-schema/cross-schema.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { sql, getTableName } from 'drizzle-orm';\nimport type { NodePgDatabase } from 'drizzle-orm/node-postgres';\nimport type { SQL, Table, Column } from 'drizzle-orm';\nimport type {\n CrossSchemaContext,\n SchemaSource,\n JoinType,\n JoinCondition,\n SharedLookupConfig,\n CrossSchemaRawOptions,\n} from './types.js';\n\n/**\n * Cross-schema query builder for joining tenant and shared data\n *\n * @example\n * ```typescript\n * const query = createCrossSchemaQuery({\n * tenantDb: tenants.getDb('tenant-uuid'),\n * sharedDb: tenants.getSharedDb(),\n * });\n *\n * const results = await query\n * .from('tenant', orders)\n * .leftJoin('shared', subscriptionPlans, eq(orders.planId, subscriptionPlans.id))\n * .select({\n * orderId: orders.id,\n * planName: subscriptionPlans.name,\n * })\n * .execute();\n * ```\n */\nexport class CrossSchemaQueryBuilder<\n TTenantSchema extends Record<string, unknown> = Record<string, unknown>,\n TSharedSchema extends Record<string, unknown> = Record<string, unknown>,\n> {\n private fromTable: { table: Table; source: SchemaSource; schemaName: string } | null = null;\n private joins: Array<{\n table: Table;\n source: SchemaSource;\n schemaName: string;\n condition: JoinCondition;\n type: JoinType;\n }> = [];\n private selectFields: Record<string, Column> = {};\n private whereCondition: SQL<unknown> | null = null;\n private orderByFields: SQL<unknown>[] = [];\n private limitValue: number | null = null;\n private offsetValue: number | null = null;\n\n constructor(private readonly context: CrossSchemaContext<TTenantSchema, TSharedSchema>) {}\n\n /**\n * Set the main table to query from\n */\n from<T extends Table>(source: SchemaSource, table: T): this {\n const schemaName = this.getSchemaName(source);\n this.fromTable = { table, source, schemaName };\n return this;\n }\n\n /**\n * Add an inner join\n */\n innerJoin<T extends Table>(source: SchemaSource, table: T, condition: JoinCondition): this {\n return this.addJoin(source, table, condition, 'inner');\n }\n\n /**\n * Add a left join\n */\n leftJoin<T extends Table>(source: SchemaSource, table: T, condition: JoinCondition): this {\n return this.addJoin(source, table, condition, 'left');\n }\n\n /**\n * Add a right join\n */\n rightJoin<T extends Table>(source: SchemaSource, table: T, condition: JoinCondition): this {\n return this.addJoin(source, table, condition, 'right');\n }\n\n /**\n * Add a full outer join\n */\n fullJoin<T extends Table>(source: SchemaSource, table: T, condition: JoinCondition): this {\n return this.addJoin(source, table, condition, 'full');\n }\n\n /**\n * Select specific fields\n */\n select<T extends Record<string, Column>>(fields: T): this {\n this.selectFields = fields;\n return this;\n }\n\n /**\n * Add a where condition\n */\n where(condition: SQL<unknown>): this {\n this.whereCondition = condition;\n return this;\n }\n\n /**\n * Add order by\n */\n orderBy(...fields: SQL<unknown>[]): this {\n this.orderByFields = fields;\n return this;\n }\n\n /**\n * Set limit\n */\n limit(value: number): this {\n this.limitValue = value;\n return this;\n }\n\n /**\n * Set offset\n */\n offset(value: number): this {\n this.offsetValue = value;\n return this;\n }\n\n /**\n * Execute the query and return typed results\n */\n async execute<TResult = Record<string, unknown>>(): Promise<TResult[]> {\n if (!this.fromTable) {\n throw new Error('[drizzle-multitenant] No table specified. Use .from() first.');\n }\n\n const sqlQuery = this.buildSql();\n\n // Use the tenant db to execute (it has access to both schemas via search_path)\n const result = await this.context.tenantDb.execute(sqlQuery);\n\n return result.rows as TResult[];\n }\n\n /**\n * Build the SQL query\n */\n private buildSql(): SQL<unknown> {\n if (!this.fromTable) {\n throw new Error('[drizzle-multitenant] No table specified');\n }\n\n const parts: SQL<unknown>[] = [];\n\n // SELECT clause\n const selectParts = Object.entries(this.selectFields).map(([alias, column]) => {\n const columnName = column.name;\n return sql`${sql.raw(`\"${columnName}\"`)} as ${sql.raw(`\"${alias}\"`)}`;\n });\n\n if (selectParts.length === 0) {\n parts.push(sql`SELECT *`);\n } else {\n parts.push(sql`SELECT ${sql.join(selectParts, sql`, `)}`);\n }\n\n // FROM clause\n const fromTableRef = this.getFullTableName(this.fromTable.schemaName, this.fromTable.table);\n parts.push(sql` FROM ${sql.raw(fromTableRef)}`);\n\n // JOIN clauses\n for (const join of this.joins) {\n const joinTableRef = this.getFullTableName(join.schemaName, join.table);\n const joinType = this.getJoinKeyword(join.type);\n parts.push(sql` ${sql.raw(joinType)} ${sql.raw(joinTableRef)} ON ${join.condition}`);\n }\n\n // WHERE clause\n if (this.whereCondition) {\n parts.push(sql` WHERE ${this.whereCondition}`);\n }\n\n // ORDER BY clause\n if (this.orderByFields.length > 0) {\n parts.push(sql` ORDER BY ${sql.join(this.orderByFields, sql`, `)}`);\n }\n\n // LIMIT clause\n if (this.limitValue !== null) {\n parts.push(sql` LIMIT ${sql.raw(this.limitValue.toString())}`);\n }\n\n // OFFSET clause\n if (this.offsetValue !== null) {\n parts.push(sql` OFFSET ${sql.raw(this.offsetValue.toString())}`);\n }\n\n return sql.join(parts, sql``);\n }\n\n /**\n * Add a join to the query\n */\n private addJoin<T extends Table>(\n source: SchemaSource,\n table: T,\n condition: JoinCondition,\n type: JoinType\n ): this {\n const schemaName = this.getSchemaName(source);\n this.joins.push({ table, source, schemaName, condition, type });\n return this;\n }\n\n /**\n * Get schema name for a source\n */\n private getSchemaName(source: SchemaSource): string {\n if (source === 'tenant') {\n return this.context.tenantSchema ?? 'tenant';\n }\n return this.context.sharedSchema ?? 'public';\n }\n\n /**\n * Get fully qualified table name\n */\n private getFullTableName(schemaName: string, table: Table): string {\n const tableName = getTableName(table);\n return `\"${schemaName}\".\"${tableName}\"`;\n }\n\n /**\n * Get SQL keyword for join type\n */\n private getJoinKeyword(type: JoinType): string {\n switch (type) {\n case 'inner':\n return 'INNER JOIN';\n case 'left':\n return 'LEFT JOIN';\n case 'right':\n return 'RIGHT JOIN';\n case 'full':\n return 'FULL OUTER JOIN';\n }\n }\n}\n\n/**\n * Create a cross-schema query builder\n */\nexport function createCrossSchemaQuery<\n TTenantSchema extends Record<string, unknown> = Record<string, unknown>,\n TSharedSchema extends Record<string, unknown> = Record<string, unknown>,\n>(\n context: CrossSchemaContext<TTenantSchema, TSharedSchema>\n): CrossSchemaQueryBuilder<TTenantSchema, TSharedSchema> {\n return new CrossSchemaQueryBuilder(context);\n}\n\n/**\n * Helper for common pattern: tenant table with shared lookup\n *\n * @example\n * ```typescript\n * const ordersWithPlans = await withSharedLookup({\n * tenantDb,\n * sharedDb,\n * tenantTable: orders,\n * sharedTable: subscriptionPlans,\n * foreignKey: 'planId',\n * sharedFields: ['name', 'features', 'price'],\n * });\n * ```\n */\nexport async function withSharedLookup<\n TTenantTable extends Table,\n TSharedTable extends Table,\n TSharedFields extends keyof TSharedTable['_']['columns'],\n>(config: SharedLookupConfig<TTenantTable, TSharedTable, TSharedFields>): Promise<Array<Record<string, unknown>>> {\n const {\n tenantDb,\n tenantTable,\n sharedTable,\n foreignKey,\n sharedKey = 'id' as keyof TSharedTable['_']['columns'],\n sharedFields,\n where: whereCondition,\n } = config;\n\n // Get table names using Drizzle's utility\n const tenantTableName = getTableName(tenantTable);\n const sharedTableName = getTableName(sharedTable);\n\n // Build field list for shared table\n const sharedFieldList = sharedFields\n .map((field) => `s.\"${String(field)}\"`)\n .join(', ');\n\n // Build the query\n const queryParts = [\n `SELECT t.*, ${sharedFieldList}`,\n `FROM \"${tenantTableName}\" t`,\n `LEFT JOIN \"public\".\"${sharedTableName}\" s ON t.\"${String(foreignKey)}\" = s.\"${String(sharedKey)}\"`,\n ];\n\n if (whereCondition) {\n queryParts.push('WHERE');\n // We'll need to use raw SQL for the where condition\n }\n\n const sqlQuery = sql.raw(queryParts.join(' '));\n\n const result = await tenantDb.execute(sqlQuery);\n\n return result.rows as Array<Record<string, unknown>>;\n}\n\n/**\n * Execute raw cross-schema SQL with type safety\n *\n * @example\n * ```typescript\n * const result = await crossSchemaRaw<{ userName: string; planName: string }>({\n * tenantSchema: 'tenant_abc123',\n * sharedSchema: 'public',\n * sql: `\n * SELECT u.name as \"userName\", p.name as \"planName\"\n * FROM $tenant.users u\n * JOIN $shared.plans p ON u.plan_id = p.id\n * `,\n * });\n * ```\n */\nexport async function crossSchemaRaw<TResult = Record<string, unknown>>(\n db: NodePgDatabase<Record<string, unknown>>,\n options: CrossSchemaRawOptions\n): Promise<TResult[]> {\n const { tenantSchema, sharedSchema, sql: rawSql } = options;\n\n // Replace $tenant and $shared placeholders\n const processedSql = rawSql\n .replace(/\\$tenant\\./g, `\"${tenantSchema}\".`)\n .replace(/\\$shared\\./g, `\"${sharedSchema}\".`);\n\n const query = sql.raw(processedSql);\n\n const result = await db.execute(query);\n\n return result.rows as TResult[];\n}\n\n/**\n * Create a typed cross-schema query using SQL template\n *\n * @example\n * ```typescript\n * const users = await crossSchemaSelect(tenantDb, {\n * tenantSchema: 'tenant_abc',\n * sharedSchema: 'public',\n * select: {\n * id: users.id,\n * name: users.name,\n * planName: plans.name,\n * },\n * from: { table: users, schema: 'tenant' },\n * joins: [\n * { table: plans, schema: 'shared', on: eq(users.planId, plans.id), type: 'left' },\n * ],\n * });\n * ```\n */\nexport function buildCrossSchemaSelect<T extends Record<string, Column>>(\n fields: T,\n tenantSchema: string,\n _sharedSchema: string\n): { columns: string[]; getSchema: () => string } {\n const columns = Object.entries(fields).map(([alias, column]) => {\n const columnName = column.name;\n return `\"${columnName}\" as \"${alias}\"`;\n });\n\n const getSchema = (): string => {\n // This would need more context to determine which schema a column belongs to\n // For now, return tenant schema as default\n return tenantSchema;\n };\n\n return { columns, getSchema };\n}\n"]}
1
+ {"version":3,"sources":["../../src/cross-schema/cross-schema.ts","../../src/cross-schema/with-shared.ts"],"names":["sql","getTableName"],"mappings":"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","file":"index.js","sourcesContent":["import { sql, getTableName } from 'drizzle-orm';\nimport type { NodePgDatabase } from 'drizzle-orm/node-postgres';\nimport type { SQL, Table, Column } from 'drizzle-orm';\nimport type {\n CrossSchemaContext,\n SchemaSource,\n JoinType,\n JoinCondition,\n SharedLookupConfig,\n CrossSchemaRawOptions,\n} from './types.js';\n\n/**\n * Cross-schema query builder for joining tenant and shared data\n *\n * @example\n * ```typescript\n * const query = createCrossSchemaQuery({\n * tenantDb: tenants.getDb('tenant-uuid'),\n * sharedDb: tenants.getSharedDb(),\n * });\n *\n * const results = await query\n * .from('tenant', orders)\n * .leftJoin('shared', subscriptionPlans, eq(orders.planId, subscriptionPlans.id))\n * .select({\n * orderId: orders.id,\n * planName: subscriptionPlans.name,\n * })\n * .execute();\n * ```\n */\nexport class CrossSchemaQueryBuilder<\n TTenantSchema extends Record<string, unknown> = Record<string, unknown>,\n TSharedSchema extends Record<string, unknown> = Record<string, unknown>,\n> {\n private fromTable: { table: Table; source: SchemaSource; schemaName: string } | null = null;\n private joins: Array<{\n table: Table;\n source: SchemaSource;\n schemaName: string;\n condition: JoinCondition;\n type: JoinType;\n }> = [];\n private selectFields: Record<string, Column> = {};\n private whereCondition: SQL<unknown> | null = null;\n private orderByFields: SQL<unknown>[] = [];\n private limitValue: number | null = null;\n private offsetValue: number | null = null;\n\n constructor(private readonly context: CrossSchemaContext<TTenantSchema, TSharedSchema>) {}\n\n /**\n * Set the main table to query from\n */\n from<T extends Table>(source: SchemaSource, table: T): this {\n const schemaName = this.getSchemaName(source);\n this.fromTable = { table, source, schemaName };\n return this;\n }\n\n /**\n * Add an inner join\n */\n innerJoin<T extends Table>(source: SchemaSource, table: T, condition: JoinCondition): this {\n return this.addJoin(source, table, condition, 'inner');\n }\n\n /**\n * Add a left join\n */\n leftJoin<T extends Table>(source: SchemaSource, table: T, condition: JoinCondition): this {\n return this.addJoin(source, table, condition, 'left');\n }\n\n /**\n * Add a right join\n */\n rightJoin<T extends Table>(source: SchemaSource, table: T, condition: JoinCondition): this {\n return this.addJoin(source, table, condition, 'right');\n }\n\n /**\n * Add a full outer join\n */\n fullJoin<T extends Table>(source: SchemaSource, table: T, condition: JoinCondition): this {\n return this.addJoin(source, table, condition, 'full');\n }\n\n /**\n * Select specific fields\n */\n select<T extends Record<string, Column>>(fields: T): this {\n this.selectFields = fields;\n return this;\n }\n\n /**\n * Add a where condition\n */\n where(condition: SQL<unknown>): this {\n this.whereCondition = condition;\n return this;\n }\n\n /**\n * Add order by\n */\n orderBy(...fields: SQL<unknown>[]): this {\n this.orderByFields = fields;\n return this;\n }\n\n /**\n * Set limit\n */\n limit(value: number): this {\n this.limitValue = value;\n return this;\n }\n\n /**\n * Set offset\n */\n offset(value: number): this {\n this.offsetValue = value;\n return this;\n }\n\n /**\n * Execute the query and return typed results\n */\n async execute<TResult = Record<string, unknown>>(): Promise<TResult[]> {\n if (!this.fromTable) {\n throw new Error('[drizzle-multitenant] No table specified. Use .from() first.');\n }\n\n const sqlQuery = this.buildSql();\n\n // Use the tenant db to execute (it has access to both schemas via search_path)\n const result = await this.context.tenantDb.execute(sqlQuery);\n\n return result.rows as TResult[];\n }\n\n /**\n * Build the SQL query\n */\n private buildSql(): SQL<unknown> {\n if (!this.fromTable) {\n throw new Error('[drizzle-multitenant] No table specified');\n }\n\n const parts: SQL<unknown>[] = [];\n\n // SELECT clause\n const selectParts = Object.entries(this.selectFields).map(([alias, column]) => {\n const columnName = column.name;\n return sql`${sql.raw(`\"${columnName}\"`)} as ${sql.raw(`\"${alias}\"`)}`;\n });\n\n if (selectParts.length === 0) {\n parts.push(sql`SELECT *`);\n } else {\n parts.push(sql`SELECT ${sql.join(selectParts, sql`, `)}`);\n }\n\n // FROM clause\n const fromTableRef = this.getFullTableName(this.fromTable.schemaName, this.fromTable.table);\n parts.push(sql` FROM ${sql.raw(fromTableRef)}`);\n\n // JOIN clauses\n for (const join of this.joins) {\n const joinTableRef = this.getFullTableName(join.schemaName, join.table);\n const joinType = this.getJoinKeyword(join.type);\n parts.push(sql` ${sql.raw(joinType)} ${sql.raw(joinTableRef)} ON ${join.condition}`);\n }\n\n // WHERE clause\n if (this.whereCondition) {\n parts.push(sql` WHERE ${this.whereCondition}`);\n }\n\n // ORDER BY clause\n if (this.orderByFields.length > 0) {\n parts.push(sql` ORDER BY ${sql.join(this.orderByFields, sql`, `)}`);\n }\n\n // LIMIT clause\n if (this.limitValue !== null) {\n parts.push(sql` LIMIT ${sql.raw(this.limitValue.toString())}`);\n }\n\n // OFFSET clause\n if (this.offsetValue !== null) {\n parts.push(sql` OFFSET ${sql.raw(this.offsetValue.toString())}`);\n }\n\n return sql.join(parts, sql``);\n }\n\n /**\n * Add a join to the query\n */\n private addJoin<T extends Table>(\n source: SchemaSource,\n table: T,\n condition: JoinCondition,\n type: JoinType\n ): this {\n const schemaName = this.getSchemaName(source);\n this.joins.push({ table, source, schemaName, condition, type });\n return this;\n }\n\n /**\n * Get schema name for a source\n */\n private getSchemaName(source: SchemaSource): string {\n if (source === 'tenant') {\n return this.context.tenantSchema ?? 'tenant';\n }\n return this.context.sharedSchema ?? 'public';\n }\n\n /**\n * Get fully qualified table name\n */\n private getFullTableName(schemaName: string, table: Table): string {\n const tableName = getTableName(table);\n return `\"${schemaName}\".\"${tableName}\"`;\n }\n\n /**\n * Get SQL keyword for join type\n */\n private getJoinKeyword(type: JoinType): string {\n switch (type) {\n case 'inner':\n return 'INNER JOIN';\n case 'left':\n return 'LEFT JOIN';\n case 'right':\n return 'RIGHT JOIN';\n case 'full':\n return 'FULL OUTER JOIN';\n }\n }\n}\n\n/**\n * Create a cross-schema query builder\n */\nexport function createCrossSchemaQuery<\n TTenantSchema extends Record<string, unknown> = Record<string, unknown>,\n TSharedSchema extends Record<string, unknown> = Record<string, unknown>,\n>(\n context: CrossSchemaContext<TTenantSchema, TSharedSchema>\n): CrossSchemaQueryBuilder<TTenantSchema, TSharedSchema> {\n return new CrossSchemaQueryBuilder(context);\n}\n\n/**\n * Helper for common pattern: tenant table with shared lookup\n *\n * @example\n * ```typescript\n * const ordersWithPlans = await withSharedLookup({\n * tenantDb,\n * sharedDb,\n * tenantTable: orders,\n * sharedTable: subscriptionPlans,\n * foreignKey: 'planId',\n * sharedFields: ['name', 'features', 'price'],\n * });\n * ```\n */\nexport async function withSharedLookup<\n TTenantTable extends Table,\n TSharedTable extends Table,\n TSharedFields extends keyof TSharedTable['_']['columns'],\n>(config: SharedLookupConfig<TTenantTable, TSharedTable, TSharedFields>): Promise<Array<Record<string, unknown>>> {\n const {\n tenantDb,\n tenantTable,\n sharedTable,\n foreignKey,\n sharedKey = 'id' as keyof TSharedTable['_']['columns'],\n sharedFields,\n where: whereCondition,\n } = config;\n\n // Get table names using Drizzle's utility\n const tenantTableName = getTableName(tenantTable);\n const sharedTableName = getTableName(sharedTable);\n\n // Build field list for shared table\n const sharedFieldList = sharedFields\n .map((field) => `s.\"${String(field)}\"`)\n .join(', ');\n\n // Build the query\n const queryParts = [\n `SELECT t.*, ${sharedFieldList}`,\n `FROM \"${tenantTableName}\" t`,\n `LEFT JOIN \"public\".\"${sharedTableName}\" s ON t.\"${String(foreignKey)}\" = s.\"${String(sharedKey)}\"`,\n ];\n\n if (whereCondition) {\n queryParts.push('WHERE');\n // We'll need to use raw SQL for the where condition\n }\n\n const sqlQuery = sql.raw(queryParts.join(' '));\n\n const result = await tenantDb.execute(sqlQuery);\n\n return result.rows as Array<Record<string, unknown>>;\n}\n\n/**\n * Execute raw cross-schema SQL with type safety\n *\n * @example\n * ```typescript\n * const result = await crossSchemaRaw<{ userName: string; planName: string }>({\n * tenantSchema: 'tenant_abc123',\n * sharedSchema: 'public',\n * sql: `\n * SELECT u.name as \"userName\", p.name as \"planName\"\n * FROM $tenant.users u\n * JOIN $shared.plans p ON u.plan_id = p.id\n * `,\n * });\n * ```\n */\nexport async function crossSchemaRaw<TResult = Record<string, unknown>>(\n db: NodePgDatabase<Record<string, unknown>>,\n options: CrossSchemaRawOptions\n): Promise<TResult[]> {\n const { tenantSchema, sharedSchema, sql: rawSql } = options;\n\n // Replace $tenant and $shared placeholders\n const processedSql = rawSql\n .replace(/\\$tenant\\./g, `\"${tenantSchema}\".`)\n .replace(/\\$shared\\./g, `\"${sharedSchema}\".`);\n\n const query = sql.raw(processedSql);\n\n const result = await db.execute(query);\n\n return result.rows as TResult[];\n}\n\n/**\n * Create a typed cross-schema query using SQL template\n *\n * @example\n * ```typescript\n * const users = await crossSchemaSelect(tenantDb, {\n * tenantSchema: 'tenant_abc',\n * sharedSchema: 'public',\n * select: {\n * id: users.id,\n * name: users.name,\n * planName: plans.name,\n * },\n * from: { table: users, schema: 'tenant' },\n * joins: [\n * { table: plans, schema: 'shared', on: eq(users.planId, plans.id), type: 'left' },\n * ],\n * });\n * ```\n */\nexport function buildCrossSchemaSelect<T extends Record<string, Column>>(\n fields: T,\n tenantSchema: string,\n _sharedSchema: string\n): { columns: string[]; getSchema: () => string } {\n const columns = Object.entries(fields).map(([alias, column]) => {\n const columnName = column.name;\n return `\"${columnName}\" as \"${alias}\"`;\n });\n\n const getSchema = (): string => {\n // This would need more context to determine which schema a column belongs to\n // For now, return tenant schema as default\n return tenantSchema;\n };\n\n return { columns, getSchema };\n}\n","import { sql, getTableName } from 'drizzle-orm';\nimport type { NodePgDatabase } from 'drizzle-orm/node-postgres';\nimport type { SQL, Column, Table } from 'drizzle-orm';\nimport type {\n JoinCondition,\n JoinType,\n WithSharedConfig,\n WithSharedOptions,\n InferSelectResult,\n} from './types.js';\n\n/**\n * Extract tables from a Drizzle schema object\n */\nfunction extractTablesFromSchema(schema: Record<string, unknown>): Set<Table> {\n const tables = new Set<Table>();\n\n for (const value of Object.values(schema)) {\n if (value && typeof value === 'object' && '_' in value) {\n const branded = value as { _?: { brand?: string } };\n if (branded._?.brand === 'Table') {\n tables.add(value as Table);\n }\n }\n }\n\n return tables;\n}\n\n/**\n * Check if a table belongs to the shared schema\n */\nfunction isSharedTable(table: Table, sharedTables: Set<Table>): boolean {\n return sharedTables.has(table);\n}\n\n/**\n * Simplified cross-schema query builder with automatic schema detection\n *\n * @example\n * ```typescript\n * const result = await withShared(tenantDb, sharedDb, { tenant: tenantSchema, shared: sharedSchema })\n * .from(pedidos)\n * .leftJoin(workflowSteps, eq(pedidos.workflowStepId, workflowSteps.id))\n * .select({ pedidoId: pedidos.id, workflowNome: workflowSteps.nome })\n * .execute();\n * ```\n */\nexport class WithSharedQueryBuilder<\n TTenantSchema extends Record<string, unknown> = Record<string, unknown>,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _TSharedSchema extends Record<string, unknown> = Record<string, unknown>,\n> {\n private fromTable: {\n table: Table;\n isShared: boolean;\n schemaName: string;\n } | null = null;\n\n private joins: Array<{\n table: Table;\n isShared: boolean;\n schemaName: string;\n condition: JoinCondition;\n type: JoinType;\n }> = [];\n\n private selectFields: Record<string, Column> = {};\n private whereCondition: SQL<unknown> | null = null;\n private orderByFields: SQL<unknown>[] = [];\n private limitValue: number | null = null;\n private offsetValue: number | null = null;\n\n constructor(\n private readonly tenantDb: NodePgDatabase<TTenantSchema>,\n private readonly sharedTables: Set<Table>,\n private readonly tenantSchemaName: string,\n private readonly sharedSchemaName: string = 'public'\n ) {}\n\n /**\n * Set the main table to query from\n * Automatically detects if it's a tenant or shared table\n */\n from<T extends Table>(table: T): this {\n const isShared = isSharedTable(table, this.sharedTables);\n this.fromTable = {\n table,\n isShared,\n schemaName: isShared ? this.sharedSchemaName : this.tenantSchemaName,\n };\n return this;\n }\n\n /**\n * Add a left join with automatic schema detection\n */\n leftJoin<T extends Table>(table: T, condition: JoinCondition): this {\n return this.addJoin(table, condition, 'left');\n }\n\n /**\n * Add an inner join with automatic schema detection\n */\n innerJoin<T extends Table>(table: T, condition: JoinCondition): this {\n return this.addJoin(table, condition, 'inner');\n }\n\n /**\n * Add a right join with automatic schema detection\n */\n rightJoin<T extends Table>(table: T, condition: JoinCondition): this {\n return this.addJoin(table, condition, 'right');\n }\n\n /**\n * Add a full outer join with automatic schema detection\n */\n fullJoin<T extends Table>(table: T, condition: JoinCondition): this {\n return this.addJoin(table, condition, 'full');\n }\n\n /**\n * Select specific fields\n */\n select<T extends Record<string, Column>>(fields: T): this {\n this.selectFields = fields;\n return this;\n }\n\n /**\n * Add a WHERE condition\n */\n where(condition: SQL<unknown>): this {\n this.whereCondition = condition;\n return this;\n }\n\n /**\n * Add ORDER BY\n */\n orderBy(...fields: SQL<unknown>[]): this {\n this.orderByFields = fields;\n return this;\n }\n\n /**\n * Set LIMIT\n */\n limit(value: number): this {\n this.limitValue = value;\n return this;\n }\n\n /**\n * Set OFFSET\n */\n offset(value: number): this {\n this.offsetValue = value;\n return this;\n }\n\n /**\n * Execute the query and return typed results\n */\n async execute<TResult extends Record<string, unknown> = InferSelectResult<typeof this.selectFields>>(): Promise<TResult[]> {\n if (!this.fromTable) {\n throw new Error('[drizzle-multitenant] No table specified. Use .from() first.');\n }\n\n const sqlQuery = this.buildSql();\n const result = await this.tenantDb.execute(sqlQuery);\n\n return result.rows as TResult[];\n }\n\n /**\n * Add a join to the query\n */\n private addJoin<T extends Table>(\n table: T,\n condition: JoinCondition,\n type: JoinType\n ): this {\n const isShared = isSharedTable(table, this.sharedTables);\n this.joins.push({\n table,\n isShared,\n schemaName: isShared ? this.sharedSchemaName : this.tenantSchemaName,\n condition,\n type,\n });\n return this;\n }\n\n /**\n * Build the SQL query\n */\n private buildSql(): SQL<unknown> {\n if (!this.fromTable) {\n throw new Error('[drizzle-multitenant] No table specified');\n }\n\n const parts: SQL<unknown>[] = [];\n\n // SELECT clause\n const selectParts = Object.entries(this.selectFields).map(([alias, column]) => {\n const columnName = column.name;\n // Try to get table name from column\n const tableName = this.getTableAliasForColumn(column);\n if (tableName) {\n return sql`${sql.raw(`\"${tableName}\".\"${columnName}\"`)} as ${sql.raw(`\"${alias}\"`)}`;\n }\n return sql`${sql.raw(`\"${columnName}\"`)} as ${sql.raw(`\"${alias}\"`)}`;\n });\n\n if (selectParts.length === 0) {\n parts.push(sql`SELECT *`);\n } else {\n parts.push(sql`SELECT ${sql.join(selectParts, sql`, `)}`);\n }\n\n // FROM clause with alias\n const fromTableName = getTableName(this.fromTable.table);\n const fromTableRef = `\"${this.fromTable.schemaName}\".\"${fromTableName}\"`;\n parts.push(sql` FROM ${sql.raw(fromTableRef)} \"${sql.raw(fromTableName)}\"`);\n\n // JOIN clauses with aliases\n for (const join of this.joins) {\n const joinTableName = getTableName(join.table);\n const joinTableRef = `\"${join.schemaName}\".\"${joinTableName}\"`;\n const joinKeyword = this.getJoinKeyword(join.type);\n parts.push(\n sql` ${sql.raw(joinKeyword)} ${sql.raw(joinTableRef)} \"${sql.raw(joinTableName)}\" ON ${join.condition}`\n );\n }\n\n // WHERE clause\n if (this.whereCondition) {\n parts.push(sql` WHERE ${this.whereCondition}`);\n }\n\n // ORDER BY clause\n if (this.orderByFields.length > 0) {\n parts.push(sql` ORDER BY ${sql.join(this.orderByFields, sql`, `)}`);\n }\n\n // LIMIT clause\n if (this.limitValue !== null) {\n parts.push(sql` LIMIT ${sql.raw(this.limitValue.toString())}`);\n }\n\n // OFFSET clause\n if (this.offsetValue !== null) {\n parts.push(sql` OFFSET ${sql.raw(this.offsetValue.toString())}`);\n }\n\n return sql.join(parts, sql``);\n }\n\n /**\n * Get table alias for a column (used in SELECT)\n */\n private getTableAliasForColumn(column: Column): string | null {\n // Drizzle columns have a reference to their table\n const columnTable = (column as unknown as { table?: Table }).table;\n if (columnTable) {\n return getTableName(columnTable);\n }\n return null;\n }\n\n /**\n * Get SQL keyword for join type\n */\n private getJoinKeyword(type: JoinType): string {\n switch (type) {\n case 'inner':\n return 'INNER JOIN';\n case 'left':\n return 'LEFT JOIN';\n case 'right':\n return 'RIGHT JOIN';\n case 'full':\n return 'FULL OUTER JOIN';\n }\n }\n}\n\n/**\n * Create a simplified cross-schema query builder with automatic schema detection\n *\n * This helper automatically detects whether a table belongs to the tenant schema\n * or the shared schema based on the schema configuration provided.\n *\n * @param tenantDb - The tenant database instance\n * @param sharedDb - The shared database instance (unused but kept for API symmetry)\n * @param schemas - Object containing tenant and shared schema definitions\n * @param options - Optional configuration for schema names\n * @returns A query builder with automatic schema detection\n *\n * @example\n * ```typescript\n * // Define your schemas\n * const tenantSchema = { pedidos, clientes };\n * const sharedSchema = { workflowSteps, plans };\n *\n * // Use withShared for cross-schema queries\n * const result = await withShared(\n * tenantDb,\n * sharedDb,\n * { tenant: tenantSchema, shared: sharedSchema }\n * )\n * .from(pedidos) // Auto-detected as tenant table\n * .leftJoin(workflowSteps, // Auto-detected as shared table\n * eq(pedidos.workflowStepId, workflowSteps.id)\n * )\n * .select({\n * pedidoId: pedidos.id,\n * workflowNome: workflowSteps.nome,\n * })\n * .where(eq(pedidos.status, 'active'))\n * .orderBy(desc(pedidos.createdAt))\n * .limit(10)\n * .execute();\n * ```\n */\nexport function withShared<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown>,\n>(\n tenantDb: NodePgDatabase<TTenantSchema>,\n _sharedDb: NodePgDatabase<TSharedSchema>,\n schemas: WithSharedConfig<TTenantSchema, TSharedSchema>,\n options?: WithSharedOptions\n): WithSharedQueryBuilder<TTenantSchema, TSharedSchema> {\n const sharedTables = extractTablesFromSchema(schemas.shared);\n\n return new WithSharedQueryBuilder(\n tenantDb,\n sharedTables,\n options?.tenantSchema ?? 'tenant',\n options?.sharedSchema ?? 'public'\n );\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { C as Config, T as TenantManager } from './types-DKVaTaIb.js';
2
- export { b as ConnectionConfig, D as DEFAULT_CONFIG, H as Hooks, I as IsolationConfig, c as IsolationStrategy, M as MetricsConfig, P as PoolEntry, d as SchemasConfig, S as SharedDb, a as TenantDb } from './types-DKVaTaIb.js';
3
- export { B as BaseTenantContext, a as TenantContext, T as TenantContextData, c as createTenantContext } from './context-DBerWr50.js';
1
+ import { C as Config, T as TenantManager, R as RetryConfig } from './types-B5eSRLFW.js';
2
+ export { b as ConnectionConfig, h as DEFAULT_CONFIG, D as DebugConfig, e as DebugContext, H as Hooks, I as IsolationConfig, c as IsolationStrategy, M as MetricsConfig, P as PoolEntry, d as SchemasConfig, S as SharedDb, a as TenantDb, g as TenantWarmupResult, W as WarmupOptions, f as WarmupResult } from './types-B5eSRLFW.js';
3
+ export { B as BaseTenantContext, a as TenantContext, T as TenantContextData, c as createTenantContext } from './context-DoHx79MS.js';
4
4
  export { AppliedMigration, CreateTenantOptions, DropTenantOptions, MigrateOptions, MigrationErrorHandler, MigrationFile, MigrationHooks, MigrationProgressCallback, MigrationResults, Migrator, MigratorConfig, TenantMigrationResult, TenantMigrationStatus, createMigrator } from './migrator/index.js';
5
- export { ColumnSelection, CrossSchemaContext, CrossSchemaQueryBuilder, CrossSchemaRawOptions, InferSelectedColumns, JoinCondition, JoinDefinition, JoinType, LookupResult, SchemaSource, SharedLookupConfig, TableReference, buildCrossSchemaSelect, createCrossSchemaQuery, crossSchemaRaw, withSharedLookup } from './cross-schema/index.js';
5
+ export { ColumnSelection, CrossSchemaContext, CrossSchemaQueryBuilder, CrossSchemaRawOptions, InferSelectResult, InferSelectedColumns, JoinCondition, JoinDefinition, JoinType, LookupResult, SchemaSource, SharedLookupConfig, TableReference, WithSharedConfig, WithSharedOptions, WithSharedQueryBuilder, buildCrossSchemaSelect, createCrossSchemaQuery, crossSchemaRaw, withShared, withSharedLookup } from './cross-schema/index.js';
6
6
  import 'pg';
7
7
  import 'drizzle-orm/node-postgres';
8
8
  import 'drizzle-orm';
@@ -68,4 +68,61 @@ declare function defineConfig<TTenantSchema extends Record<string, unknown>, TSh
68
68
  */
69
69
  declare function createTenantManager<TTenantSchema extends Record<string, unknown>, TSharedSchema extends Record<string, unknown> = Record<string, unknown>>(config: Config<TTenantSchema, TSharedSchema>): TenantManager<TTenantSchema, TSharedSchema>;
70
70
 
71
- export { Config, TenantManager, createTenantManager, defineConfig };
71
+ /**
72
+ * Default function to determine if an error is retryable
73
+ * Focuses on transient connection errors
74
+ */
75
+ declare function isRetryableError(error: Error): boolean;
76
+ /**
77
+ * Calculate delay with exponential backoff and optional jitter
78
+ */
79
+ declare function calculateDelay(attempt: number, config: Required<Pick<RetryConfig, 'initialDelayMs' | 'maxDelayMs' | 'backoffMultiplier' | 'jitter'>>): number;
80
+ /**
81
+ * Retry result with metadata
82
+ */
83
+ interface RetryResult<T> {
84
+ /** The result value if successful */
85
+ result: T;
86
+ /** Number of attempts made (1 = first try succeeded) */
87
+ attempts: number;
88
+ /** Total time spent including retries in ms */
89
+ totalTimeMs: number;
90
+ }
91
+ /**
92
+ * Retry an async operation with exponential backoff
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const result = await withRetry(
97
+ * () => pool.connect(),
98
+ * {
99
+ * maxAttempts: 3,
100
+ * initialDelayMs: 100,
101
+ * maxDelayMs: 5000,
102
+ * backoffMultiplier: 2,
103
+ * onRetry: (attempt, error, delay) => {
104
+ * console.log(`Retry ${attempt} after ${delay}ms: ${error.message}`);
105
+ * },
106
+ * }
107
+ * );
108
+ * ```
109
+ */
110
+ declare function withRetry<T>(operation: () => Promise<T>, config?: RetryConfig): Promise<RetryResult<T>>;
111
+ /**
112
+ * Create a retry wrapper with pre-configured options
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * const retrier = createRetrier({
117
+ * maxAttempts: 5,
118
+ * initialDelayMs: 200,
119
+ * });
120
+ *
121
+ * // Use the same config for multiple operations
122
+ * const result1 = await retrier(() => connectToDb());
123
+ * const result2 = await retrier(() => fetchData());
124
+ * ```
125
+ */
126
+ declare function createRetrier(config: RetryConfig): <T>(operation: () => Promise<T>) => Promise<RetryResult<T>>;
127
+
128
+ export { Config, RetryConfig, type RetryResult, TenantManager, calculateDelay, createRetrier, createTenantManager, defineConfig, isRetryableError, withRetry };