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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/context.ts","../../src/integrations/express.ts"],"names":[],"mappings":";;;AA2FO,SAAS,oBAKd,OAAA,EACsD;AACtD,EAAA,MAAM,OAAA,GAAU,IAAI,iBAAA,EAA8C;AAElE,EAAA,SAAS,eAAA,GAA0D;AACjE,IAAA,OAAO,QAAQ,QAAA,EAAS;AAAA,EAC1B;AAEA,EAAA,SAAS,SAAA,GAAwC;AAC/C,IAAA,MAAM,UAAU,eAAA,EAAgB;AAChC,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OAEF;AAAA,IACF;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,SAAS,WAAA,GAAsB;AAC7B,IAAA,OAAO,WAAU,CAAE,QAAA;AAAA,EACrB;AAEA,EAAA,SAAS,WAAA,GAAuC;AAC9C,IAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,IAAA,OAAO,OAAA,CAAQ,MAAM,QAAQ,CAAA;AAAA,EAC/B;AAEA,EAAA,SAAS,WAAA,GAAuC;AAC9C,IAAA,OAAO,QAAQ,WAAA,EAAY;AAAA,EAC7B;AAEA,EAAA,SAAS,iBAAA,GAA6B;AACpC,IAAA,OAAO,iBAAgB,KAAM,MAAA;AAAA,EAC/B;AAEA,EAAA,SAAS,aAAA,CACP,SACA,QAAA,EACgB;AAChB,IAAA,IAAI,CAAC,QAAQ,QAAA,EAAU;AACrB,MAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,IACzE;AACA,IAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,OAAA,EAAS,QAAQ,CAAA;AAAA,EACtC;AAEA,EAAA,OAAO;AAAA,IACL,aAAA;AAAA,IACA,SAAA;AAAA,IACA,eAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACF;;;ACtGO,IAAM,mBAAA,GAAN,cAAkC,KAAA,CAAM;AAAA,EAC7C,WAAA,CAAY,UAAU,kBAAA,EAAoB;AACxC,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAKO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA,EAC/C,WAAA,CAAY,UAAU,0BAAA,EAA4B;AAChD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AAAA,EACd;AACF;AAwCO,SAAS,wBAKd,OAAA,EAGA;AACA,EAAA,MAAM,EAAE,OAAA,EAAS,eAAA,EAAiB,cAAA,EAAgB,aAAA,EAAe,SAAQ,GAAI,OAAA;AAE7E,EAAA,MAAM,aAAA,GAAgB,oBAA2D,OAAO,CAAA;AAExF,EAAA,MAAM,mBAAA,GAAsB,CAC1B,KAAA,EACA,IAAA,EACA,KACA,KAAA,KACS;AACT,IAAA,IAAI,iBAAiB,mBAAA,EAAqB;AACxC,MAAA,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,yBAAyB,CAAA;AACvD,MAAA;AAAA,IACF;AACA,IAAA,IAAI,iBAAiB,qBAAA,EAAuB;AAC1C,MAAA,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,kBAAkB,CAAA;AAChD,MAAA;AAAA,IACF;AACA,IAAA,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,yBAAyB,CAAA;AAAA,EACzD,CAAA;AAEA,EAAA,MAAM,eAAe,OAAA,IAAW,mBAAA;AAEhC,EAAA,MAAM,UAAA,GAA6B,OACjC,GAAA,EACA,GAAA,EACA,IAAA,KACkB;AAClB,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,eAAA,CAAgB,GAAG,CAAA;AAE1C,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,MAAM,IAAI,oBAAoB,gCAAgC,CAAA;AAAA,MAChE;AAEA,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,MAAM,OAAA,GAAU,MAAM,cAAA,CAAe,QAAA,EAAU,GAAG,CAAA;AAClD,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,MAAM,IAAI,qBAAA,CAAsB,CAAA,OAAA,EAAU,QAAQ,CAAA,kBAAA,CAAoB,CAAA;AAAA,QACxE;AAAA,MACF;AAEA,MAAA,MAAM,gBAAgB,aAAA,GAAgB,MAAM,cAAc,QAAA,EAAU,GAAG,IAAK,EAAC;AAE7E,MAAA,MAAM,OAAA,GAAsC;AAAA,QAC1C,QAAA;AAAA,QACA,GAAG;AAAA,OACL;AAEA,MAAA,GAAA,CAAI,aAAA,GAAgB,OAAA;AAEpB,MAAA,MAAM,aAAA,CAAc,aAAA,CAAc,OAAA,EAAS,YAAY;AACrD,QAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,UAAA,IAAA,EAAK;AACL,UAAA,GAAA,CAAI,EAAA,CAAG,UAAU,OAAO,CAAA;AACxB,UAAA,GAAA,CAAI,EAAA,CAAG,SAAS,MAAM,CAAA;AAAA,QACxB,CAAC,CAAA;AAAA,MACH,CAAC,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,YAAA,CAAa,KAAA,EAAgB,GAAA,EAAK,GAAA,EAAK,IAAI,CAAA;AAAA,IAC7C;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,OAAO,MAAA,CAAO,UAAA,EAAY,EAAE,OAAA,EAAS,eAAe,CAAA;AAC7D","file":"express.js","sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { TenantManager, TenantDb, SharedDb } from './types.js';\n\n/**\n * Base tenant context data\n */\nexport interface BaseTenantContext {\n tenantId: string;\n}\n\n/**\n * Tenant context with optional custom data\n */\nexport type TenantContextData<TCustom extends Record<string, unknown> = Record<string, unknown>> =\n BaseTenantContext & TCustom;\n\n/**\n * Tenant context API\n */\nexport interface TenantContext<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown>,\n TCustom extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Run a callback with tenant context\n */\n runWithTenant<T>(\n context: TenantContextData<TCustom>,\n callback: () => T | Promise<T>\n ): T | Promise<T>;\n\n /**\n * Get current tenant context (throws if not in context)\n */\n getTenant(): TenantContextData<TCustom>;\n\n /**\n * Get current tenant context or undefined\n */\n getTenantOrNull(): TenantContextData<TCustom> | undefined;\n\n /**\n * Get current tenant ID (throws if not in context)\n */\n getTenantId(): string;\n\n /**\n * Get database for current tenant (throws if not in context)\n */\n getTenantDb(): TenantDb<TTenantSchema>;\n\n /**\n * Get shared database\n */\n getSharedDb(): SharedDb<TSharedSchema>;\n\n /**\n * Check if currently running within a tenant context\n */\n isInTenantContext(): boolean;\n}\n\n/**\n * Create a tenant context with AsyncLocalStorage\n *\n * @example\n * ```typescript\n * import { createTenantContext, createTenantManager } from 'drizzle-multitenant';\n *\n * const manager = createTenantManager(config);\n *\n * const {\n * runWithTenant,\n * getTenant,\n * getTenantDb,\n * getSharedDb,\n * } = createTenantContext(manager);\n *\n * // Use in request handler\n * app.get('/users', async (req, res) => {\n * const tenantId = req.headers['x-tenant-id'];\n *\n * await runWithTenant({ tenantId }, async () => {\n * const db = getTenantDb();\n * const users = await db.select().from(schema.users);\n * res.json(users);\n * });\n * });\n * ```\n */\nexport function createTenantContext<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown> = Record<string, unknown>,\n TCustom extends Record<string, unknown> = Record<string, unknown>,\n>(\n manager: TenantManager<TTenantSchema, TSharedSchema>\n): TenantContext<TTenantSchema, TSharedSchema, TCustom> {\n const storage = new AsyncLocalStorage<TenantContextData<TCustom>>();\n\n function getTenantOrNull(): TenantContextData<TCustom> | undefined {\n return storage.getStore();\n }\n\n function getTenant(): TenantContextData<TCustom> {\n const context = getTenantOrNull();\n if (!context) {\n throw new Error(\n '[drizzle-multitenant] No tenant context found. ' +\n 'Make sure you are calling this within runWithTenant().'\n );\n }\n return context;\n }\n\n function getTenantId(): string {\n return getTenant().tenantId;\n }\n\n function getTenantDb(): TenantDb<TTenantSchema> {\n const tenantId = getTenantId();\n return manager.getDb(tenantId);\n }\n\n function getSharedDb(): SharedDb<TSharedSchema> {\n return manager.getSharedDb();\n }\n\n function isInTenantContext(): boolean {\n return getTenantOrNull() !== undefined;\n }\n\n function runWithTenant<T>(\n context: TenantContextData<TCustom>,\n callback: () => T | Promise<T>\n ): T | Promise<T> {\n if (!context.tenantId) {\n throw new Error('[drizzle-multitenant] tenantId is required in context');\n }\n return storage.run(context, callback);\n }\n\n return {\n runWithTenant,\n getTenant,\n getTenantOrNull,\n getTenantId,\n getTenantDb,\n getSharedDb,\n isInTenantContext,\n };\n}\n","import type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport type { TenantManager } from '../types.js';\nimport { createTenantContext, type TenantContext, type TenantContextData } from '../context.js';\n\n/**\n * Express middleware options\n */\nexport interface ExpressMiddlewareOptions<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown>,\n TCustom extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Tenant manager instance */\n manager: TenantManager<TTenantSchema, TSharedSchema>;\n\n /**\n * Extract tenant ID from request\n * @example\n * // From header\n * extractTenantId: (req) => req.headers['x-tenant-id'] as string\n *\n * // From path param\n * extractTenantId: (req) => req.params.tenantId\n *\n * // From subdomain\n * extractTenantId: (req) => req.hostname.split('.')[0]\n */\n extractTenantId: (req: Request) => string | undefined | Promise<string | undefined>;\n\n /**\n * Optional tenant validation\n * Throw an error or return false to reject the request\n */\n validateTenant?: (tenantId: string, req: Request) => boolean | Promise<boolean>;\n\n /**\n * Enrich context with additional data\n */\n enrichContext?: (tenantId: string, req: Request) => TCustom | Promise<TCustom>;\n\n /**\n * Custom error handler\n */\n onError?: (error: Error, req: Request, res: Response, next: NextFunction) => void;\n}\n\n/**\n * Tenant not found error\n */\nexport class TenantNotFoundError extends Error {\n constructor(message = 'Tenant not found') {\n super(message);\n this.name = 'TenantNotFoundError';\n }\n}\n\n/**\n * Tenant validation error\n */\nexport class TenantValidationError extends Error {\n constructor(message = 'Tenant validation failed') {\n super(message);\n this.name = 'TenantValidationError';\n }\n}\n\n/**\n * Extended Express Request with tenant context\n */\nexport interface TenantRequest<TCustom extends Record<string, unknown> = Record<string, unknown>>\n extends Request {\n tenantContext?: TenantContextData<TCustom>;\n}\n\n/**\n * Create Express middleware for tenant context\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createTenantManager } from 'drizzle-multitenant';\n * import { createExpressMiddleware } from 'drizzle-multitenant/express';\n *\n * const app = express();\n * const manager = createTenantManager(config);\n *\n * const tenantMiddleware = createExpressMiddleware({\n * manager,\n * extractTenantId: (req) => req.headers['x-tenant-id'] as string,\n * validateTenant: async (tenantId) => {\n * // Check if tenant exists in database\n * return true;\n * },\n * });\n *\n * app.use('/api', tenantMiddleware);\n *\n * app.get('/api/users', async (req, res) => {\n * const db = getTenantDb();\n * const users = await db.select().from(schema.users);\n * res.json(users);\n * });\n * ```\n */\nexport function createExpressMiddleware<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown>,\n TCustom extends Record<string, unknown> = Record<string, unknown>,\n>(\n options: ExpressMiddlewareOptions<TTenantSchema, TSharedSchema, TCustom>\n): RequestHandler & {\n context: TenantContext<TTenantSchema, TSharedSchema, TCustom>;\n} {\n const { manager, extractTenantId, validateTenant, enrichContext, onError } = options;\n\n const tenantContext = createTenantContext<TTenantSchema, TSharedSchema, TCustom>(manager);\n\n const defaultErrorHandler = (\n error: Error,\n _req: Request,\n res: Response,\n _next: NextFunction\n ): void => {\n if (error instanceof TenantNotFoundError) {\n res.status(400).json({ error: 'Tenant ID is required' });\n return;\n }\n if (error instanceof TenantValidationError) {\n res.status(403).json({ error: 'Invalid tenant' });\n return;\n }\n res.status(500).json({ error: 'Internal server error' });\n };\n\n const errorHandler = onError ?? defaultErrorHandler;\n\n const middleware: RequestHandler = async (\n req: TenantRequest<TCustom>,\n res: Response,\n next: NextFunction\n ): Promise<void> => {\n try {\n const tenantId = await extractTenantId(req);\n\n if (!tenantId) {\n throw new TenantNotFoundError('Tenant ID not found in request');\n }\n\n if (validateTenant) {\n const isValid = await validateTenant(tenantId, req);\n if (!isValid) {\n throw new TenantValidationError(`Tenant ${tenantId} validation failed`);\n }\n }\n\n const customContext = enrichContext ? await enrichContext(tenantId, req) : ({} as TCustom);\n\n const context: TenantContextData<TCustom> = {\n tenantId,\n ...customContext,\n };\n\n req.tenantContext = context;\n\n await tenantContext.runWithTenant(context, async () => {\n await new Promise<void>((resolve, reject) => {\n next();\n res.on('finish', resolve);\n res.on('error', reject);\n });\n });\n } catch (error) {\n errorHandler(error as Error, req, res, next);\n }\n };\n\n return Object.assign(middleware, { context: tenantContext });\n}\n\n/**\n * Re-export context utilities for convenience\n */\nexport { createTenantContext, type TenantContext, type TenantContextData } from '../context.js';\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../node_modules/fastify-plugin/lib/getPluginName.js","../../node_modules/fastify-plugin/lib/toCamelCase.js","../../node_modules/fastify-plugin/plugin.js","../../src/integrations/fastify.ts","../../src/context.ts"],"names":["exports","fp"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAA,qBAAA,GAAA,UAAA,CAAA;AAAA,EAAA,kDAAA,CAAAA,SAAA,EAAA,MAAA,EAAA;AAEA,IAAA,IAAM,mBAAA,GAAsB,kCAAA;AAC5B,IAAA,IAAM,eAAA,GAAkB,mBAAA;AAExB,IAAA,MAAA,CAAO,OAAA,GAAU,SAAS,aAAA,CAAe,EAAA,EAAI;AAC3C,MAAA,IAAI,EAAA,CAAG,IAAA,CAAK,MAAA,GAAS,CAAA,SAAU,EAAA,CAAG,IAAA;AAElC,MAAA,MAAM,kBAAkB,KAAA,CAAM,eAAA;AAC9B,MAAA,KAAA,CAAM,eAAA,GAAkB,EAAA;AACxB,MAAA,IAAI;AACF,QAAA,MAAM,IAAI,MAAM,oBAAoB,CAAA;AAAA,MACtC,SAAS,CAAA,EAAG;AACV,QAAA,KAAA,CAAM,eAAA,GAAkB,eAAA;AACxB,QAAA,OAAO,iBAAA,CAAkB,EAAE,KAAK,CAAA;AAAA,MAClC;AAAA,IACF,CAAA;AAEA,IAAA,SAAS,kBAAmB,KAAA,EAAO;AACjC,MAAA,MAAM,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,mBAAmB,CAAA;AAGzC,MAAA,OAAO,IAAI,CAAA,CAAE,CAAC,CAAA,CAAE,KAAA,CAAM,OAAO,CAAA,CAAE,KAAA,CAAM,EAAE,CAAA,CAAE,CAAC,CAAA,CAAE,KAAA,CAAM,eAAe,CAAA,CAAE,CAAC,CAAA,GAAI,WAAA;AAAA,IAC1E;AACA,IAAA,MAAA,CAAO,QAAQ,iBAAA,GAAoB,iBAAA;AAAA,EAAA;AAAA,CAAA,CAAA;;;ACxBnC,IAAA,mBAAA,GAAA,UAAA,CAAA;AAAA,EAAA,gDAAA,CAAAA,SAAA,EAAA,MAAA,EAAA;AAEA,IAAA,MAAA,CAAO,OAAA,GAAU,SAAS,WAAA,CAAa,IAAA,EAAM;AAC3C,MAAA,IAAI,IAAA,CAAK,CAAC,CAAA,KAAM,GAAA,EAAK;AACnB,QAAA,IAAA,GAAO,KAAK,KAAA,CAAM,CAAC,CAAA,CAAE,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,MACvC;AACA,MAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,SAAU,OAAO,EAAA,EAAI;AAChD,QAAA,OAAO,GAAG,WAAA,EAAY;AAAA,MACxB,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,EAAA;AAAA,CAAA,CAAA;;;ACTA,IAAA,cAAA,GAAA,UAAA,CAAA;AAAA,EAAA,uCAAA,CAAAA,SAAA,EAAA,MAAA,EAAA;AAEA,IAAA,IAAM,aAAA,GAAgB,qBAAA,EAAA;AACtB,IAAA,IAAM,WAAA,GAAc,mBAAA,EAAA;AAEpB,IAAA,IAAI,KAAA,GAAQ,CAAA;AAEZ,IAAA,SAAS,MAAA,CAAQ,EAAA,EAAI,OAAA,GAAU,EAAC,EAAG;AACjC,MAAA,IAAI,QAAA,GAAW,KAAA;AAEf,MAAA,IAAI,EAAA,CAAG,YAAY,MAAA,EAAW;AAE5B,QAAA,EAAA,GAAK,EAAA,CAAG,OAAA;AAAA,MACV;AAEA,MAAA,IAAI,OAAO,OAAO,UAAA,EAAY;AAC5B,QAAA,MAAM,IAAI,SAAA;AAAA,UACR,CAAA,kDAAA,EAAqD,OAAO,EAAE,CAAA,CAAA;AAAA,SAChE;AAAA,MACF;AAEA,MAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,QAAA,OAAA,GAAU;AAAA,UACR,OAAA,EAAS;AAAA,SACX;AAAA,MACF;AAEA,MAAA,IACE,OAAO,YAAY,QAAA,IACnB,KAAA,CAAM,QAAQ,OAAO,CAAA,IACrB,YAAY,IAAA,EACZ;AACA,QAAA,MAAM,IAAI,UAAU,wCAAwC,CAAA;AAAA,MAC9D;AAEA,MAAA,IAAI,QAAQ,OAAA,KAAY,MAAA,IAAa,OAAO,OAAA,CAAQ,YAAY,QAAA,EAAU;AACxE,QAAA,MAAM,IAAI,SAAA,CAAU,CAAA,sDAAA,EAAyD,OAAO,OAAA,CAAQ,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,MACxG;AAEA,MAAA,IAAI,CAAC,QAAQ,IAAA,EAAM;AACjB,QAAA,QAAA,GAAW,IAAA;AACX,QAAA,OAAA,CAAQ,IAAA,GAAO,aAAA,CAAc,EAAE,CAAA,GAAI,QAAA,GAAW,KAAA,EAAA;AAAA,MAChD;AAEA,MAAA,EAAA,wBAAU,GAAA,CAAI,eAAe,CAAC,CAAA,GAAI,QAAQ,WAAA,KAAgB,IAAA;AAC1D,MAAA,EAAA,iBAAG,MAAA,CAAO,GAAA,CAAI,sBAAsB,CAAC,IAAI,OAAA,CAAQ,IAAA;AACjD,MAAA,EAAA,iBAAG,MAAA,CAAO,GAAA,CAAI,aAAa,CAAC,CAAA,GAAI,OAAA;AAGhC,MAAA,IAAI,CAAC,GAAG,OAAA,EAAS;AACf,QAAA,EAAA,CAAG,OAAA,GAAU,EAAA;AAAA,MACf;AAKA,MAAA,MAAM,SAAA,GAAY,WAAA,CAAY,OAAA,CAAQ,IAAI,CAAA;AAC1C,MAAA,IAAI,CAAC,QAAA,IAAY,CAAC,EAAA,CAAG,SAAS,CAAA,EAAG;AAC/B,QAAA,EAAA,CAAG,SAAS,CAAA,GAAI,EAAA;AAAA,MAClB;AAEA,MAAA,OAAO,EAAA;AAAA,IACT;AAEA,IAAA,MAAA,CAAO,OAAA,GAAU,MAAA;AACjB,IAAA,MAAA,CAAO,QAAQ,OAAA,GAAU,MAAA;AACzB,IAAA,MAAA,CAAO,QAAQ,aAAA,GAAgB,MAAA;AAAA,EAAA;AAAA,CAAA,CAAA;;;AC5D/B,IAAA,qBAAA,GAAe,OAAA,CAAA,cAAA,EAAA,CAAA;ACqFR,SAAS,oBAKd,OAAA,EACsD;AACtD,EAAA,MAAM,OAAA,GAAU,IAAI,iBAAA,EAA8C;AAElE,EAAA,SAAS,eAAA,GAA0D;AACjE,IAAA,OAAO,QAAQ,QAAA,EAAS;AAAA,EAC1B;AAEA,EAAA,SAAS,SAAA,GAAwC;AAC/C,IAAA,MAAM,UAAU,eAAA,EAAgB;AAChC,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OAEF;AAAA,IACF;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,SAAS,WAAA,GAAsB;AAC7B,IAAA,OAAO,WAAU,CAAE,QAAA;AAAA,EACrB;AAEA,EAAA,SAAS,WAAA,GAAuC;AAC9C,IAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,IAAA,OAAO,OAAA,CAAQ,MAAM,QAAQ,CAAA;AAAA,EAC/B;AAEA,EAAA,SAAS,WAAA,GAAuC;AAC9C,IAAA,OAAO,QAAQ,WAAA,EAAY;AAAA,EAC7B;AAEA,EAAA,SAAS,iBAAA,GAA6B;AACpC,IAAA,OAAO,iBAAgB,KAAM,MAAA;AAAA,EAC/B;AAEA,EAAA,SAAS,aAAA,CACP,SACA,QAAA,EACgB;AAChB,IAAA,IAAI,CAAC,QAAQ,QAAA,EAAU;AACrB,MAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,IACzE;AACA,IAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,OAAA,EAAS,QAAQ,CAAA;AAAA,EACtC;AAEA,EAAA,OAAO;AAAA,IACL,aAAA;AAAA,IACA,SAAA;AAAA,IACA,eAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACF;;;ADhGO,IAAM,mBAAA,GAAN,cAAkC,KAAA,CAAM;AAAA,EAC7C,WAAA,CAAY,UAAU,kBAAA,EAAoB;AACxC,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAKO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA,EAC/C,WAAA,CAAY,UAAU,0BAAA,EAA4B;AAChD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AAAA,EACd;AACF;AAqCO,SAAS,oBAKd,OAAA,EAIA;AACA,EAAA,MAAM,EAAE,OAAA,EAAS,eAAA,EAAiB,cAAA,EAAgB,aAAA,EAAe,SAAQ,GAAI,OAAA;AAE7E,EAAA,MAAM,aAAA,GAAgB,oBAA2D,OAAO,CAAA;AAExF,EAAA,MAAM,mBAAA,GAAsB,OAC1B,KAAA,EACA,IAAA,EACA,KAAA,KACkB;AAClB,IAAA,IAAI,iBAAiB,mBAAA,EAAqB;AACxC,MAAA,KAAA,CAAM,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,yBAAyB,CAAA;AACzD,MAAA;AAAA,IACF;AACA,IAAA,IAAI,iBAAiB,qBAAA,EAAuB;AAC1C,MAAA,KAAA,CAAM,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,kBAAkB,CAAA;AAClD,MAAA;AAAA,IACF;AACA,IAAA,KAAA,CAAM,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,yBAAyB,CAAA;AAAA,EAC3D,CAAA;AAEA,EAAA,MAAM,eAAe,OAAA,IAAW,mBAAA;AAEhC,EAAA,MAAM,UAAA,GAAiC,OAAO,OAAA,KAA4C;AAExF,IAAA,OAAA,CAAQ,eAAA,CAAgB,iBAAiB,MAAS,CAAA;AAGlD,IAAA,OAAA,CAAQ,OAAA,CAAQ,YAAA,EAAc,OAAO,GAAA,EAAqB,KAAA,KAAwB;AAChF,MAAA,IAAI,QAAA;AAEJ,MAAA,IAAI;AACF,QAAA,QAAA,GAAW,MAAM,gBAAgB,GAAG,CAAA;AAAA,MACtC,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,YAAA,CAAa,KAAA,EAAgB,GAAA,EAAK,KAAK,CAAA;AAC7C,QAAA,MAAM,KAAA;AAAA,MACR;AAEA,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,MAAM,KAAA,GAAQ,IAAI,mBAAA,CAAoB,gCAAgC,CAAA;AACtE,QAAA,MAAM,YAAA,CAAa,KAAA,EAAO,GAAA,EAAK,KAAK,CAAA;AACpC,QAAA,MAAM,KAAA;AAAA,MACR;AAEA,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,IAAI;AACF,UAAA,MAAM,OAAA,GAAU,MAAM,cAAA,CAAe,QAAA,EAAU,GAAG,CAAA;AAClD,UAAA,IAAI,CAAC,OAAA,EAAS;AACZ,YAAA,MAAM,KAAA,GAAQ,IAAI,qBAAA,CAAsB,CAAA,OAAA,EAAU,QAAQ,CAAA,kBAAA,CAAoB,CAAA;AAC9E,YAAA,MAAM,YAAA,CAAa,KAAA,EAAO,GAAA,EAAK,KAAK,CAAA;AACpC,YAAA,MAAM,KAAA;AAAA,UACR;AAAA,QACF,SAAS,KAAA,EAAO;AACd,UAAA,IAAI,EAAE,iBAAiB,qBAAA,CAAA,EAAwB;AAC7C,YAAA,MAAM,YAAA,CAAa,KAAA,EAAgB,GAAA,EAAK,KAAK,CAAA;AAAA,UAC/C;AACA,UAAA,MAAM,KAAA;AAAA,QACR;AAAA,MACF;AAEA,MAAA,MAAM,gBAAgB,aAAA,GAAgB,MAAM,cAAc,QAAA,EAAU,GAAG,IAAK,EAAC;AAE7E,MAAA,MAAM,OAAA,GAAsC;AAAA,QAC1C,QAAA;AAAA,QACA,GAAG;AAAA,OACL;AAEA,MAAA,GAAA,CAAI,aAAA,GAAgB,OAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACH,CAAA;AAGA,EAAA,MAAM,MAAA,GAAA,IAAS,qBAAA,CAAAC,OAAAA,EAAG,UAAA,EAAY;AAAA,IAC5B,IAAA,EAAM;AAAA,GACP,CAAA;AAED,EAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,aAAA,EAAc;AAC1C","file":"fastify.js","sourcesContent":["'use strict'\n\nconst fpStackTracePattern = /at\\s(?:.*\\.)?plugin\\s.*\\n\\s*(.*)/\nconst fileNamePattern = /(\\w*(\\.\\w*)*)\\..*/\n\nmodule.exports = function getPluginName (fn) {\n if (fn.name.length > 0) return fn.name\n\n const stackTraceLimit = Error.stackTraceLimit\n Error.stackTraceLimit = 10\n try {\n throw new Error('anonymous function')\n } catch (e) {\n Error.stackTraceLimit = stackTraceLimit\n return extractPluginName(e.stack)\n }\n}\n\nfunction extractPluginName (stack) {\n const m = stack.match(fpStackTracePattern)\n\n // get last section of path and match for filename\n return m ? m[1].split(/[/\\\\]/).slice(-1)[0].match(fileNamePattern)[1] : 'anonymous'\n}\nmodule.exports.extractPluginName = extractPluginName\n","'use strict'\n\nmodule.exports = function toCamelCase (name) {\n if (name[0] === '@') {\n name = name.slice(1).replace('/', '-')\n }\n return name.replace(/-(.)/g, function (match, g1) {\n return g1.toUpperCase()\n })\n}\n","'use strict'\n\nconst getPluginName = require('./lib/getPluginName')\nconst toCamelCase = require('./lib/toCamelCase')\n\nlet count = 0\n\nfunction plugin (fn, options = {}) {\n let autoName = false\n\n if (fn.default !== undefined) {\n // Support for 'export default' behaviour in transpiled ECMAScript module\n fn = fn.default\n }\n\n if (typeof fn !== 'function') {\n throw new TypeError(\n `fastify-plugin expects a function, instead got a '${typeof fn}'`\n )\n }\n\n if (typeof options === 'string') {\n options = {\n fastify: options\n }\n }\n\n if (\n typeof options !== 'object' ||\n Array.isArray(options) ||\n options === null\n ) {\n throw new TypeError('The options object should be an object')\n }\n\n if (options.fastify !== undefined && typeof options.fastify !== 'string') {\n throw new TypeError(`fastify-plugin expects a version string, instead got '${typeof options.fastify}'`)\n }\n\n if (!options.name) {\n autoName = true\n options.name = getPluginName(fn) + '-auto-' + count++\n }\n\n fn[Symbol.for('skip-override')] = options.encapsulate !== true\n fn[Symbol.for('fastify.display-name')] = options.name\n fn[Symbol.for('plugin-meta')] = options\n\n // Faux modules support\n if (!fn.default) {\n fn.default = fn\n }\n\n // TypeScript support for named imports\n // See https://github.com/fastify/fastify/issues/2404 for more details\n // The type definitions would have to be update to match this.\n const camelCase = toCamelCase(options.name)\n if (!autoName && !fn[camelCase]) {\n fn[camelCase] = fn\n }\n\n return fn\n}\n\nmodule.exports = plugin\nmodule.exports.default = plugin\nmodule.exports.fastifyPlugin = plugin\n","import type {\n FastifyPluginAsync,\n FastifyRequest,\n FastifyReply,\n FastifyInstance,\n} from 'fastify';\nimport fp from 'fastify-plugin';\nimport type { TenantManager } from '../types.js';\nimport { createTenantContext, type TenantContext, type TenantContextData } from '../context.js';\n\n/**\n * Fastify plugin options\n */\nexport interface FastifyPluginOptions<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown>,\n TCustom extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Tenant manager instance */\n manager: TenantManager<TTenantSchema, TSharedSchema>;\n\n /**\n * Extract tenant ID from request\n * @example\n * // From header\n * extractTenantId: (req) => req.headers['x-tenant-id'] as string\n *\n * // From path param\n * extractTenantId: (req) => (req.params as any).tenantId\n *\n * // From subdomain\n * extractTenantId: (req) => req.hostname.split('.')[0]\n */\n extractTenantId: (req: FastifyRequest) => string | undefined | Promise<string | undefined>;\n\n /**\n * Optional tenant validation\n * Throw an error or return false to reject the request\n */\n validateTenant?: (tenantId: string, req: FastifyRequest) => boolean | Promise<boolean>;\n\n /**\n * Enrich context with additional data\n */\n enrichContext?: (tenantId: string, req: FastifyRequest) => TCustom | Promise<TCustom>;\n\n /**\n * Custom error handler\n */\n onError?: (error: Error, req: FastifyRequest, reply: FastifyReply) => void | Promise<void>;\n}\n\n/**\n * Tenant not found error\n */\nexport class TenantNotFoundError extends Error {\n constructor(message = 'Tenant not found') {\n super(message);\n this.name = 'TenantNotFoundError';\n }\n}\n\n/**\n * Tenant validation error\n */\nexport class TenantValidationError extends Error {\n constructor(message = 'Tenant validation failed') {\n super(message);\n this.name = 'TenantValidationError';\n }\n}\n\n/**\n * Fastify request decorator\n */\ndeclare module 'fastify' {\n interface FastifyRequest {\n tenantContext?: TenantContextData<Record<string, unknown>>;\n }\n}\n\n/**\n * Create Fastify plugin for tenant context\n *\n * @example\n * ```typescript\n * import Fastify from 'fastify';\n * import { createTenantManager } from 'drizzle-multitenant';\n * import { createFastifyPlugin } from 'drizzle-multitenant/fastify';\n *\n * const fastify = Fastify();\n * const manager = createTenantManager(config);\n *\n * const { plugin, context } = createFastifyPlugin({\n * manager,\n * extractTenantId: (req) => req.headers['x-tenant-id'] as string,\n * });\n *\n * await fastify.register(plugin);\n *\n * fastify.get('/users', async (req, reply) => {\n * const db = context.getTenantDb();\n * const users = await db.select().from(schema.users);\n * return users;\n * });\n * ```\n */\nexport function createFastifyPlugin<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown>,\n TCustom extends Record<string, unknown> = Record<string, unknown>,\n>(\n options: FastifyPluginOptions<TTenantSchema, TSharedSchema, TCustom>\n): {\n plugin: FastifyPluginAsync;\n context: TenantContext<TTenantSchema, TSharedSchema, TCustom>;\n} {\n const { manager, extractTenantId, validateTenant, enrichContext, onError } = options;\n\n const tenantContext = createTenantContext<TTenantSchema, TSharedSchema, TCustom>(manager);\n\n const defaultErrorHandler = async (\n error: Error,\n _req: FastifyRequest,\n reply: FastifyReply\n ): Promise<void> => {\n if (error instanceof TenantNotFoundError) {\n reply.status(400).send({ error: 'Tenant ID is required' });\n return;\n }\n if (error instanceof TenantValidationError) {\n reply.status(403).send({ error: 'Invalid tenant' });\n return;\n }\n reply.status(500).send({ error: 'Internal server error' });\n };\n\n const errorHandler = onError ?? defaultErrorHandler;\n\n const pluginImpl: FastifyPluginAsync = async (fastify: FastifyInstance): Promise<void> => {\n // Decorate request with tenantContext\n fastify.decorateRequest('tenantContext', undefined);\n\n // Add preHandler hook to set tenant context\n fastify.addHook('preHandler', async (req: FastifyRequest, reply: FastifyReply) => {\n let tenantId: string | undefined;\n\n try {\n tenantId = await extractTenantId(req);\n } catch (error) {\n await errorHandler(error as Error, req, reply);\n throw error; // Re-throw to stop processing\n }\n\n if (!tenantId) {\n const error = new TenantNotFoundError('Tenant ID not found in request');\n await errorHandler(error, req, reply);\n throw error;\n }\n\n if (validateTenant) {\n try {\n const isValid = await validateTenant(tenantId, req);\n if (!isValid) {\n const error = new TenantValidationError(`Tenant ${tenantId} validation failed`);\n await errorHandler(error, req, reply);\n throw error;\n }\n } catch (error) {\n if (!(error instanceof TenantValidationError)) {\n await errorHandler(error as Error, req, reply);\n }\n throw error;\n }\n }\n\n const customContext = enrichContext ? await enrichContext(tenantId, req) : ({} as TCustom);\n\n const context: TenantContextData<TCustom> = {\n tenantId,\n ...customContext,\n };\n\n req.tenantContext = context;\n });\n };\n\n // Use fastify-plugin to make the plugin global (not encapsulated)\n const plugin = fp(pluginImpl, {\n name: 'drizzle-multitenant',\n });\n\n return { plugin, context: tenantContext };\n}\n\n/**\n * Re-export context utilities for convenience\n */\nexport { createTenantContext, type TenantContext, type TenantContextData } from '../context.js';\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { TenantManager, TenantDb, SharedDb } from './types.js';\n\n/**\n * Base tenant context data\n */\nexport interface BaseTenantContext {\n tenantId: string;\n}\n\n/**\n * Tenant context with optional custom data\n */\nexport type TenantContextData<TCustom extends Record<string, unknown> = Record<string, unknown>> =\n BaseTenantContext & TCustom;\n\n/**\n * Tenant context API\n */\nexport interface TenantContext<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown>,\n TCustom extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Run a callback with tenant context\n */\n runWithTenant<T>(\n context: TenantContextData<TCustom>,\n callback: () => T | Promise<T>\n ): T | Promise<T>;\n\n /**\n * Get current tenant context (throws if not in context)\n */\n getTenant(): TenantContextData<TCustom>;\n\n /**\n * Get current tenant context or undefined\n */\n getTenantOrNull(): TenantContextData<TCustom> | undefined;\n\n /**\n * Get current tenant ID (throws if not in context)\n */\n getTenantId(): string;\n\n /**\n * Get database for current tenant (throws if not in context)\n */\n getTenantDb(): TenantDb<TTenantSchema>;\n\n /**\n * Get shared database\n */\n getSharedDb(): SharedDb<TSharedSchema>;\n\n /**\n * Check if currently running within a tenant context\n */\n isInTenantContext(): boolean;\n}\n\n/**\n * Create a tenant context with AsyncLocalStorage\n *\n * @example\n * ```typescript\n * import { createTenantContext, createTenantManager } from 'drizzle-multitenant';\n *\n * const manager = createTenantManager(config);\n *\n * const {\n * runWithTenant,\n * getTenant,\n * getTenantDb,\n * getSharedDb,\n * } = createTenantContext(manager);\n *\n * // Use in request handler\n * app.get('/users', async (req, res) => {\n * const tenantId = req.headers['x-tenant-id'];\n *\n * await runWithTenant({ tenantId }, async () => {\n * const db = getTenantDb();\n * const users = await db.select().from(schema.users);\n * res.json(users);\n * });\n * });\n * ```\n */\nexport function createTenantContext<\n TTenantSchema extends Record<string, unknown>,\n TSharedSchema extends Record<string, unknown> = Record<string, unknown>,\n TCustom extends Record<string, unknown> = Record<string, unknown>,\n>(\n manager: TenantManager<TTenantSchema, TSharedSchema>\n): TenantContext<TTenantSchema, TSharedSchema, TCustom> {\n const storage = new AsyncLocalStorage<TenantContextData<TCustom>>();\n\n function getTenantOrNull(): TenantContextData<TCustom> | undefined {\n return storage.getStore();\n }\n\n function getTenant(): TenantContextData<TCustom> {\n const context = getTenantOrNull();\n if (!context) {\n throw new Error(\n '[drizzle-multitenant] No tenant context found. ' +\n 'Make sure you are calling this within runWithTenant().'\n );\n }\n return context;\n }\n\n function getTenantId(): string {\n return getTenant().tenantId;\n }\n\n function getTenantDb(): TenantDb<TTenantSchema> {\n const tenantId = getTenantId();\n return manager.getDb(tenantId);\n }\n\n function getSharedDb(): SharedDb<TSharedSchema> {\n return manager.getSharedDb();\n }\n\n function isInTenantContext(): boolean {\n return getTenantOrNull() !== undefined;\n }\n\n function runWithTenant<T>(\n context: TenantContextData<TCustom>,\n callback: () => T | Promise<T>\n ): T | Promise<T> {\n if (!context.tenantId) {\n throw new Error('[drizzle-multitenant] tenantId is required in context');\n }\n return storage.run(context, callback);\n }\n\n return {\n runWithTenant,\n getTenant,\n getTenantOrNull,\n getTenantId,\n getTenantDb,\n getSharedDb,\n isInTenantContext,\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"hono.js"}
|