@lark-apaas/express-datapaas 1.0.0 → 1.0.1-alpha.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/dist/index.cjs CHANGED
@@ -136,7 +136,8 @@ function parseConnectionInfo(connectionString, config) {
136
136
  temp.searchParams.delete("schema");
137
137
  connectionString = temp.toString();
138
138
  } catch (error) {
139
- throw new Error("Failed to parse connection info");
139
+ const message = error instanceof Error ? error.message : "Unknown error";
140
+ throw new Error(`Failed to parse connection info: ${message}`);
140
141
  }
141
142
  const { username, password, hostname, port, pathname } = parsedConfigFromUrl;
142
143
  const connectionConfig = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/runtime-patch.ts","../src/create-database.ts","../src/utils.ts","../src/create-rls-middleware.ts"],"sourcesContent":["import './runtime-patch';\nexport { createDatabase } from './create-database';\nexport { createRlsMiddleware } from './create-rls-middleware';\nexport type { CreateDatabaseConfig, DatabaseInstance, UserContext } from './types';\nexport type { RlsMiddlewareOptions } from './create-rls-middleware';\nexport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\n","/* eslint-disable */\n/**\n * Runtime monkey-patch for drizzle-orm RLS support.\n *\n * Instead of patching files on disk (which breaks under pnpm v10's security policy),\n * this module patches drizzle-orm's PostgresJsPreparedQuery in memory at import time.\n *\n * Node.js CJS module cache guarantees that require('drizzle-orm/postgres-js/session.cjs')\n * returns the same object everywhere, so prototype mutations propagate to all consumers.\n */\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\n\n// Create a require function that works in both ESM and CJS contexts\n// In CJS bundle (tsup output), import.meta.url may be undefined\nconst _require = createRequire(\n typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url)\n);\n\nexport interface RequestSessionContext {\n preSql?: string;\n postSql?: string;\n}\n\n// ── AsyncLocalStorage for per-request RLS context ──\nconst asyncLocalStorage = new AsyncLocalStorage<RequestSessionContext>();\n\nexport function getAuthContextStore(): RequestSessionContext | undefined {\n return asyncLocalStorage.getStore();\n}\n\nexport function runWithAuthContext(store: RequestSessionContext, fn: () => void): void {\n asyncLocalStorage.run(store, fn);\n}\n\n// ── Monkey-patch drizzle-orm ──\nlet _applied = false;\n\nfunction applyRuntimePatch(): void {\n if (_applied) return;\n\n // Resolve drizzle-orm root dir from its main entry, then require internal CJS files\n // by absolute path (bypasses package.json exports restrictions that block deep imports)\n const path = _require('node:path') as typeof import('node:path');\n const drizzleEntry = _require.resolve('drizzle-orm');\n const drizzleDir = drizzleEntry.slice(0, drizzleEntry.lastIndexOf(`${path.sep}drizzle-orm${path.sep}`) + `${path.sep}drizzle-orm`.length);\n const session = _require(path.join(drizzleDir, 'postgres-js', 'session.cjs'));\n const tracing = _require(path.join(drizzleDir, 'tracing.cjs'));\n const utils = _require(path.join(drizzleDir, 'utils.cjs'));\n\n const PreparedQuery = session.PostgresJsPreparedQuery;\n if (!PreparedQuery) {\n throw new Error('Cannot find PostgresJsPreparedQuery in drizzle-orm/postgres-js/session.cjs');\n }\n\n const originalExecute = PreparedQuery.prototype.execute;\n\n // Patch constructor: capture preSql/postSql from AsyncLocalStorage at query creation time\n const originalInit = PreparedQuery.prototype.constructor;\n const patchedConstructor = function (this: any, ...args: any[]) {\n originalInit.apply(this, args);\n const authContext = getAuthContextStore();\n this.preSql = authContext?.preSql;\n this.postSql = authContext?.postSql;\n };\n PreparedQuery.prototype.constructor = patchedConstructor;\n\n // Patch execute: if preSql/postSql exist, wrap in transaction\n PreparedQuery.prototype.execute = async function (this: any, params?: unknown[]) {\n const { preSql, postSql } = this;\n\n if (!preSql && !postSql) {\n // No RLS context, use original execute\n return originalExecute.call(this, params);\n }\n\n // Execute with RLS context: preSql → main query → postSql in a transaction\n const { fields, client, joinsNotNullableMap, customResultMapper } = this;\n const tracer = tracing.tracer;\n\n return tracer.startActiveSpan('drizzle.driver.execute.transactional', async (_span: any) => {\n const execInContext = async (sql: any) => {\n if (preSql) {\n await tracer.startActiveSpan('drizzle.driver.execute.pre', () => sql.unsafe(preSql));\n }\n\n let result;\n if (!fields && !customResultMapper) {\n result = await tracer.startActiveSpan('drizzle.driver.execute.main', () => {\n return this.queryWithCache(this.queryString, params, async () => {\n return await sql.unsafe(this.queryString, params as any);\n });\n });\n } else {\n const rows = await tracer.startActiveSpan('drizzle.driver.execute.main', (innerSpan: any) => {\n innerSpan?.setAttributes({\n 'drizzle.query.text': this.queryString,\n 'drizzle.query.params': JSON.stringify(params),\n });\n return this.queryWithCache(this.queryString, params, async () => {\n return await sql.unsafe(this.queryString, params as any).values();\n });\n });\n result = tracer.startActiveSpan('drizzle.mapResponse', () => {\n return customResultMapper\n ? customResultMapper(rows)\n : rows.map((row: any) => utils.mapResultRow(fields, row, joinsNotNullableMap));\n });\n }\n\n if (postSql) {\n await tracer.startActiveSpan('drizzle.driver.execute.post', () => sql.unsafe(postSql));\n }\n\n return result;\n };\n\n // If client has begin(), we're not in a transaction yet — start one\n if (typeof client.begin === 'function') {\n return client.begin((tx: any) => execInContext(tx));\n }\n // Already in a transaction, execute directly\n return execInContext(client);\n });\n };\n\n _applied = true;\n}\n\n// Apply patch immediately on import\napplyRuntimePatch();\n","import { drizzle } from 'drizzle-orm/postgres-js';\nimport postgres from 'postgres';\nimport { parseConnectionInfo, parseRoleSchema } from './utils';\nimport type { CreateDatabaseConfig, DatabaseInstance } from './types';\n\n// 模块级缓存,供 createRlsMiddleware 零参数调用时读取\nlet _cachedRoleSchema = '';\n\nexport function _getRoleSchema(): string {\n return _cachedRoleSchema;\n}\n\nexport function createDatabase(config: CreateDatabaseConfig): DatabaseInstance {\n\n const { roleSchema, sslModeRequired } = parseRoleSchema(config.connectionString);\n _cachedRoleSchema = roleSchema;\n\n const resolvedSsl = config.ssl || (sslModeRequired ? 'require' : false);\n\n const { connectionString, connectionConfig } = parseConnectionInfo(config.connectionString, {\n maxConnections: config.maxConnections,\n idleTimeout: config.idleTimeout,\n connectionTimeout: config.connectionTimeout,\n ssl: resolvedSsl,\n logger: typeof config.logger === 'boolean' ? config.logger : false,\n });\n\n const sql = postgres(connectionString, connectionConfig);\n const db = drizzle(sql, { schema: config.schema, logger: config.logger });\n\n return {\n db,\n close: async () => {\n await sql.end();\n },\n };\n}\n","import { type PostgresOptions } from './types';\n\ninterface ConnectionConfig {\n maxConnections?: number;\n idleTimeout?: number;\n connectionTimeout?: number;\n ssl?: PostgresOptions['ssl'];\n logger?: boolean;\n}\n\nexport function parseConnectionInfo(connectionString: string, config?: ConnectionConfig) {\n const { maxConnections, idleTimeout, connectionTimeout, ssl, logger } = config || {};\n\n let parsedConfigFromUrl = null;\n try {\n parsedConfigFromUrl = new URL(connectionString);\n const temp = new URL(connectionString);\n temp.searchParams.delete('schema');\n connectionString = temp.toString();\n } catch (error) {\n throw new Error('Failed to parse connection info');\n }\n\n const { username, password, hostname, port, pathname } = parsedConfigFromUrl;\n const connectionConfig = {\n username: decodeURIComponent(username),\n password: decodeURIComponent(password),\n host: [hostname] as unknown as string,\n port: [parseInt(port) || 5432] as unknown as number,\n pathname: decodeURIComponent(pathname),\n max: maxConnections || 1,\n idle_timeout: idleTimeout || 20,\n connect_timeout: connectionTimeout || 10,\n ssl,\n onnotice: logger ? console.log : undefined,\n };\n\n return { connectionString, connectionConfig };\n}\n\nexport function parseRoleSchema(connectionString: string): { roleSchema: string; sslModeRequired: boolean } {\n try {\n const url = new URL(connectionString);\n const roleSchema = url.searchParams.get('schema') || '';\n const sslModeRequired = url.searchParams.get('sslmode') === 'require';\n return { roleSchema, sslModeRequired };\n } catch {\n return { roleSchema: '', sslModeRequired: false };\n }\n}\n","import { type Request, type Response, type NextFunction } from 'express';\nimport { runWithAuthContext } from './runtime-patch';\nimport { _getRoleSchema } from './create-database';\nimport type { UserContext } from './types';\n\nexport interface RlsMiddlewareOptions {\n roleSchema?: string;\n}\n\nexport function createRlsMiddleware(options?: RlsMiddlewareOptions) {\n return function rlsMiddleware(req: Request, _res: Response, next: NextFunction) {\n const roleSchema = options?.roleSchema ?? _getRoleSchema();\n const userContext = (req as unknown as { userContext: UserContext }).userContext;\n\n if (!userContext) {\n return next();\n }\n\n const roles = (userContext.roles || []).join(',');\n const userId = userContext.userId;\n const isSystemAccount = userContext.isSystemAccount;\n const userType = userContext.userType;\n\n const sqls = [\n userId\n ? `SET LOCAL app.user_id = '${userId}'`\n : `SET LOCAL app.user_id = ''`,\n isSystemAccount\n ? `SET LOCAL ROLE 'service_role_${roleSchema}'`\n : userId\n ? `SET LOCAL ROLE 'authenticated_${roleSchema}'`\n : `SET LOCAL ROLE 'anon_${roleSchema}'`,\n roles\n ? `SET LOCAL app.role_ids = '${roles}'`\n : `SET LOCAL app.role_ids = ''`,\n userType\n ? `SET LOCAL app.user_type = '${userType}'`\n : `SET LOCAL app.user_type = ''`,\n ]\n .filter(Boolean)\n .join(';');\n\n runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n };\n}\n"],"mappings":"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","names":["_require","createRequire","__filename","fileURLToPath","url","asyncLocalStorage","AsyncLocalStorage","getAuthContextStore","getStore","runWithAuthContext","store","fn","run","_applied","applyRuntimePatch","path","drizzleEntry","resolve","drizzleDir","slice","lastIndexOf","sep","length","session","join","tracing","utils","PreparedQuery","PostgresJsPreparedQuery","Error","originalExecute","prototype","execute","originalInit","patchedConstructor","args","apply","authContext","preSql","postSql","params","call","fields","client","joinsNotNullableMap","customResultMapper","tracer","startActiveSpan","_span","execInContext","sql","unsafe","result","queryWithCache","queryString","rows","innerSpan","setAttributes","JSON","stringify","values","map","row","mapResultRow","begin","tx","parseConnectionInfo","connectionString","config","maxConnections","idleTimeout","connectionTimeout","ssl","logger","parsedConfigFromUrl","URL","temp","searchParams","delete","toString","error","Error","username","password","hostname","port","pathname","connectionConfig","decodeURIComponent","host","parseInt","max","idle_timeout","connect_timeout","onnotice","console","log","undefined","parseRoleSchema","url","roleSchema","get","sslModeRequired","_cachedRoleSchema","_getRoleSchema","createDatabase","config","roleSchema","sslModeRequired","parseRoleSchema","connectionString","resolvedSsl","ssl","connectionConfig","parseConnectionInfo","maxConnections","idleTimeout","connectionTimeout","logger","sql","postgres","db","drizzle","schema","close","end","createRlsMiddleware","options","rlsMiddleware","req","_res","next","roleSchema","_getRoleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","runWithAuthContext","preSql"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/runtime-patch.ts","../src/create-database.ts","../src/utils.ts","../src/create-rls-middleware.ts"],"sourcesContent":["import './runtime-patch';\nexport { createDatabase } from './create-database';\nexport { createRlsMiddleware } from './create-rls-middleware';\nexport type { CreateDatabaseConfig, DatabaseInstance, UserContext } from './types';\nexport type { RlsMiddlewareOptions } from './create-rls-middleware';\nexport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\n","/* eslint-disable */\n/**\n * Runtime monkey-patch for drizzle-orm RLS support.\n *\n * Instead of patching files on disk (which breaks under pnpm v10's security policy),\n * this module patches drizzle-orm's PostgresJsPreparedQuery in memory at import time.\n *\n * Node.js CJS module cache guarantees that require('drizzle-orm/postgres-js/session.cjs')\n * returns the same object everywhere, so prototype mutations propagate to all consumers.\n */\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\n\n// Create a require function that works in both ESM and CJS contexts\n// In CJS bundle (tsup output), import.meta.url may be undefined\nconst _require = createRequire(\n typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url)\n);\n\nexport interface RequestSessionContext {\n preSql?: string;\n postSql?: string;\n}\n\n// ── AsyncLocalStorage for per-request RLS context ──\nconst asyncLocalStorage = new AsyncLocalStorage<RequestSessionContext>();\n\nexport function getAuthContextStore(): RequestSessionContext | undefined {\n return asyncLocalStorage.getStore();\n}\n\nexport function runWithAuthContext(store: RequestSessionContext, fn: () => void): void {\n asyncLocalStorage.run(store, fn);\n}\n\n// ── Monkey-patch drizzle-orm ──\nlet _applied = false;\n\nfunction applyRuntimePatch(): void {\n if (_applied) return;\n\n // Resolve drizzle-orm root dir from its main entry, then require internal CJS files\n // by absolute path (bypasses package.json exports restrictions that block deep imports)\n const path = _require('node:path') as typeof import('node:path');\n const drizzleEntry = _require.resolve('drizzle-orm');\n const drizzleDir = drizzleEntry.slice(0, drizzleEntry.lastIndexOf(`${path.sep}drizzle-orm${path.sep}`) + `${path.sep}drizzle-orm`.length);\n const session = _require(path.join(drizzleDir, 'postgres-js', 'session.cjs'));\n const tracing = _require(path.join(drizzleDir, 'tracing.cjs'));\n const utils = _require(path.join(drizzleDir, 'utils.cjs'));\n\n const PreparedQuery = session.PostgresJsPreparedQuery;\n if (!PreparedQuery) {\n throw new Error('Cannot find PostgresJsPreparedQuery in drizzle-orm/postgres-js/session.cjs');\n }\n\n const originalExecute = PreparedQuery.prototype.execute;\n\n // Patch constructor: capture preSql/postSql from AsyncLocalStorage at query creation time\n const originalInit = PreparedQuery.prototype.constructor;\n const patchedConstructor = function (this: any, ...args: any[]) {\n originalInit.apply(this, args);\n const authContext = getAuthContextStore();\n this.preSql = authContext?.preSql;\n this.postSql = authContext?.postSql;\n };\n PreparedQuery.prototype.constructor = patchedConstructor;\n\n // Patch execute: if preSql/postSql exist, wrap in transaction\n PreparedQuery.prototype.execute = async function (this: any, params?: unknown[]) {\n const { preSql, postSql } = this;\n\n if (!preSql && !postSql) {\n // No RLS context, use original execute\n return originalExecute.call(this, params);\n }\n\n // Execute with RLS context: preSql → main query → postSql in a transaction\n const { fields, client, joinsNotNullableMap, customResultMapper } = this;\n const tracer = tracing.tracer;\n\n return tracer.startActiveSpan('drizzle.driver.execute.transactional', async (_span: any) => {\n const execInContext = async (sql: any) => {\n if (preSql) {\n await tracer.startActiveSpan('drizzle.driver.execute.pre', () => sql.unsafe(preSql));\n }\n\n let result;\n if (!fields && !customResultMapper) {\n result = await tracer.startActiveSpan('drizzle.driver.execute.main', () => {\n return this.queryWithCache(this.queryString, params, async () => {\n return await sql.unsafe(this.queryString, params as any);\n });\n });\n } else {\n const rows = await tracer.startActiveSpan('drizzle.driver.execute.main', (innerSpan: any) => {\n innerSpan?.setAttributes({\n 'drizzle.query.text': this.queryString,\n 'drizzle.query.params': JSON.stringify(params),\n });\n return this.queryWithCache(this.queryString, params, async () => {\n return await sql.unsafe(this.queryString, params as any).values();\n });\n });\n result = tracer.startActiveSpan('drizzle.mapResponse', () => {\n return customResultMapper\n ? customResultMapper(rows)\n : rows.map((row: any) => utils.mapResultRow(fields, row, joinsNotNullableMap));\n });\n }\n\n if (postSql) {\n await tracer.startActiveSpan('drizzle.driver.execute.post', () => sql.unsafe(postSql));\n }\n\n return result;\n };\n\n // If client has begin(), we're not in a transaction yet — start one\n if (typeof client.begin === 'function') {\n return client.begin((tx: any) => execInContext(tx));\n }\n // Already in a transaction, execute directly\n return execInContext(client);\n });\n };\n\n _applied = true;\n}\n\n// Apply patch immediately on import\napplyRuntimePatch();\n","import { drizzle } from 'drizzle-orm/postgres-js';\nimport postgres from 'postgres';\nimport { parseConnectionInfo, parseRoleSchema } from './utils';\nimport type { CreateDatabaseConfig, DatabaseInstance } from './types';\n\n// 模块级缓存,供 createRlsMiddleware 零参数调用时读取\nlet _cachedRoleSchema = '';\n\nexport function _getRoleSchema(): string {\n return _cachedRoleSchema;\n}\n\nexport function createDatabase(config: CreateDatabaseConfig): DatabaseInstance {\n\n const { roleSchema, sslModeRequired } = parseRoleSchema(config.connectionString);\n _cachedRoleSchema = roleSchema;\n\n const resolvedSsl = config.ssl || (sslModeRequired ? 'require' : false);\n\n const { connectionString, connectionConfig } = parseConnectionInfo(config.connectionString, {\n maxConnections: config.maxConnections,\n idleTimeout: config.idleTimeout,\n connectionTimeout: config.connectionTimeout,\n ssl: resolvedSsl,\n logger: typeof config.logger === 'boolean' ? config.logger : false,\n });\n\n const sql = postgres(connectionString, connectionConfig);\n const db = drizzle(sql, { schema: config.schema, logger: config.logger });\n\n return {\n db,\n close: async () => {\n await sql.end();\n },\n };\n}\n","import { type PostgresOptions } from './types';\n\ninterface ConnectionConfig {\n maxConnections?: number;\n idleTimeout?: number;\n connectionTimeout?: number;\n ssl?: PostgresOptions['ssl'];\n logger?: boolean;\n}\n\nexport function parseConnectionInfo(connectionString: string, config?: ConnectionConfig) {\n const { maxConnections, idleTimeout, connectionTimeout, ssl, logger } = config || {};\n\n let parsedConfigFromUrl = null;\n try {\n parsedConfigFromUrl = new URL(connectionString);\n const temp = new URL(connectionString);\n temp.searchParams.delete('schema');\n connectionString = temp.toString();\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new Error(`Failed to parse connection info: ${message}`);\n }\n\n const { username, password, hostname, port, pathname } = parsedConfigFromUrl;\n const connectionConfig = {\n username: decodeURIComponent(username),\n password: decodeURIComponent(password),\n host: [hostname] as unknown as string,\n port: [parseInt(port) || 5432] as unknown as number,\n pathname: decodeURIComponent(pathname),\n max: maxConnections || 1,\n idle_timeout: idleTimeout || 20,\n connect_timeout: connectionTimeout || 10,\n ssl,\n onnotice: logger ? console.log : undefined,\n };\n\n return { connectionString, connectionConfig };\n}\n\nexport function parseRoleSchema(connectionString: string): { roleSchema: string; sslModeRequired: boolean } {\n try {\n const url = new URL(connectionString);\n const roleSchema = url.searchParams.get('schema') || '';\n const sslModeRequired = url.searchParams.get('sslmode') === 'require';\n return { roleSchema, sslModeRequired };\n } catch {\n return { roleSchema: '', sslModeRequired: false };\n }\n}\n","import { type Request, type Response, type NextFunction } from 'express';\nimport { runWithAuthContext } from './runtime-patch';\nimport { _getRoleSchema } from './create-database';\nimport type { UserContext } from './types';\n\nexport interface RlsMiddlewareOptions {\n roleSchema?: string;\n}\n\nexport function createRlsMiddleware(options?: RlsMiddlewareOptions) {\n return function rlsMiddleware(req: Request, _res: Response, next: NextFunction) {\n const roleSchema = options?.roleSchema ?? _getRoleSchema();\n const userContext = (req as unknown as { userContext: UserContext }).userContext;\n\n if (!userContext) {\n return next();\n }\n\n const roles = (userContext.roles || []).join(',');\n const userId = userContext.userId;\n const isSystemAccount = userContext.isSystemAccount;\n const userType = userContext.userType;\n\n const sqls = [\n userId\n ? `SET LOCAL app.user_id = '${userId}'`\n : `SET LOCAL app.user_id = ''`,\n isSystemAccount\n ? `SET LOCAL ROLE 'service_role_${roleSchema}'`\n : userId\n ? `SET LOCAL ROLE 'authenticated_${roleSchema}'`\n : `SET LOCAL ROLE 'anon_${roleSchema}'`,\n roles\n ? `SET LOCAL app.role_ids = '${roles}'`\n : `SET LOCAL app.role_ids = ''`,\n userType\n ? `SET LOCAL app.user_type = '${userType}'`\n : `SET LOCAL app.user_type = ''`,\n ]\n .filter(Boolean)\n .join(';');\n\n runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n };\n}\n"],"mappings":"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","names":["_require","createRequire","__filename","fileURLToPath","url","asyncLocalStorage","AsyncLocalStorage","getAuthContextStore","getStore","runWithAuthContext","store","fn","run","_applied","applyRuntimePatch","path","drizzleEntry","resolve","drizzleDir","slice","lastIndexOf","sep","length","session","join","tracing","utils","PreparedQuery","PostgresJsPreparedQuery","Error","originalExecute","prototype","execute","originalInit","patchedConstructor","args","apply","authContext","preSql","postSql","params","call","fields","client","joinsNotNullableMap","customResultMapper","tracer","startActiveSpan","_span","execInContext","sql","unsafe","result","queryWithCache","queryString","rows","innerSpan","setAttributes","JSON","stringify","values","map","row","mapResultRow","begin","tx","parseConnectionInfo","connectionString","config","maxConnections","idleTimeout","connectionTimeout","ssl","logger","parsedConfigFromUrl","URL","temp","searchParams","delete","toString","error","message","Error","username","password","hostname","port","pathname","connectionConfig","decodeURIComponent","host","parseInt","max","idle_timeout","connect_timeout","onnotice","console","log","undefined","parseRoleSchema","url","roleSchema","get","sslModeRequired","_cachedRoleSchema","_getRoleSchema","createDatabase","config","roleSchema","sslModeRequired","parseRoleSchema","connectionString","resolvedSsl","ssl","connectionConfig","parseConnectionInfo","maxConnections","idleTimeout","connectionTimeout","logger","sql","postgres","db","drizzle","schema","close","end","createRlsMiddleware","options","rlsMiddleware","req","_res","next","roleSchema","_getRoleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","runWithAuthContext","preSql"]}
package/dist/index.js CHANGED
@@ -100,7 +100,8 @@ function parseConnectionInfo(connectionString, config) {
100
100
  temp.searchParams.delete("schema");
101
101
  connectionString = temp.toString();
102
102
  } catch (error) {
103
- throw new Error("Failed to parse connection info");
103
+ const message = error instanceof Error ? error.message : "Unknown error";
104
+ throw new Error(`Failed to parse connection info: ${message}`);
104
105
  }
105
106
  const { username, password, hostname, port, pathname } = parsedConfigFromUrl;
106
107
  const connectionConfig = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime-patch.ts","../src/create-database.ts","../src/utils.ts","../src/create-rls-middleware.ts"],"sourcesContent":["/* eslint-disable */\n/**\n * Runtime monkey-patch for drizzle-orm RLS support.\n *\n * Instead of patching files on disk (which breaks under pnpm v10's security policy),\n * this module patches drizzle-orm's PostgresJsPreparedQuery in memory at import time.\n *\n * Node.js CJS module cache guarantees that require('drizzle-orm/postgres-js/session.cjs')\n * returns the same object everywhere, so prototype mutations propagate to all consumers.\n */\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\n\n// Create a require function that works in both ESM and CJS contexts\n// In CJS bundle (tsup output), import.meta.url may be undefined\nconst _require = createRequire(\n typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url)\n);\n\nexport interface RequestSessionContext {\n preSql?: string;\n postSql?: string;\n}\n\n// ── AsyncLocalStorage for per-request RLS context ──\nconst asyncLocalStorage = new AsyncLocalStorage<RequestSessionContext>();\n\nexport function getAuthContextStore(): RequestSessionContext | undefined {\n return asyncLocalStorage.getStore();\n}\n\nexport function runWithAuthContext(store: RequestSessionContext, fn: () => void): void {\n asyncLocalStorage.run(store, fn);\n}\n\n// ── Monkey-patch drizzle-orm ──\nlet _applied = false;\n\nfunction applyRuntimePatch(): void {\n if (_applied) return;\n\n // Resolve drizzle-orm root dir from its main entry, then require internal CJS files\n // by absolute path (bypasses package.json exports restrictions that block deep imports)\n const path = _require('node:path') as typeof import('node:path');\n const drizzleEntry = _require.resolve('drizzle-orm');\n const drizzleDir = drizzleEntry.slice(0, drizzleEntry.lastIndexOf(`${path.sep}drizzle-orm${path.sep}`) + `${path.sep}drizzle-orm`.length);\n const session = _require(path.join(drizzleDir, 'postgres-js', 'session.cjs'));\n const tracing = _require(path.join(drizzleDir, 'tracing.cjs'));\n const utils = _require(path.join(drizzleDir, 'utils.cjs'));\n\n const PreparedQuery = session.PostgresJsPreparedQuery;\n if (!PreparedQuery) {\n throw new Error('Cannot find PostgresJsPreparedQuery in drizzle-orm/postgres-js/session.cjs');\n }\n\n const originalExecute = PreparedQuery.prototype.execute;\n\n // Patch constructor: capture preSql/postSql from AsyncLocalStorage at query creation time\n const originalInit = PreparedQuery.prototype.constructor;\n const patchedConstructor = function (this: any, ...args: any[]) {\n originalInit.apply(this, args);\n const authContext = getAuthContextStore();\n this.preSql = authContext?.preSql;\n this.postSql = authContext?.postSql;\n };\n PreparedQuery.prototype.constructor = patchedConstructor;\n\n // Patch execute: if preSql/postSql exist, wrap in transaction\n PreparedQuery.prototype.execute = async function (this: any, params?: unknown[]) {\n const { preSql, postSql } = this;\n\n if (!preSql && !postSql) {\n // No RLS context, use original execute\n return originalExecute.call(this, params);\n }\n\n // Execute with RLS context: preSql → main query → postSql in a transaction\n const { fields, client, joinsNotNullableMap, customResultMapper } = this;\n const tracer = tracing.tracer;\n\n return tracer.startActiveSpan('drizzle.driver.execute.transactional', async (_span: any) => {\n const execInContext = async (sql: any) => {\n if (preSql) {\n await tracer.startActiveSpan('drizzle.driver.execute.pre', () => sql.unsafe(preSql));\n }\n\n let result;\n if (!fields && !customResultMapper) {\n result = await tracer.startActiveSpan('drizzle.driver.execute.main', () => {\n return this.queryWithCache(this.queryString, params, async () => {\n return await sql.unsafe(this.queryString, params as any);\n });\n });\n } else {\n const rows = await tracer.startActiveSpan('drizzle.driver.execute.main', (innerSpan: any) => {\n innerSpan?.setAttributes({\n 'drizzle.query.text': this.queryString,\n 'drizzle.query.params': JSON.stringify(params),\n });\n return this.queryWithCache(this.queryString, params, async () => {\n return await sql.unsafe(this.queryString, params as any).values();\n });\n });\n result = tracer.startActiveSpan('drizzle.mapResponse', () => {\n return customResultMapper\n ? customResultMapper(rows)\n : rows.map((row: any) => utils.mapResultRow(fields, row, joinsNotNullableMap));\n });\n }\n\n if (postSql) {\n await tracer.startActiveSpan('drizzle.driver.execute.post', () => sql.unsafe(postSql));\n }\n\n return result;\n };\n\n // If client has begin(), we're not in a transaction yet — start one\n if (typeof client.begin === 'function') {\n return client.begin((tx: any) => execInContext(tx));\n }\n // Already in a transaction, execute directly\n return execInContext(client);\n });\n };\n\n _applied = true;\n}\n\n// Apply patch immediately on import\napplyRuntimePatch();\n","import { drizzle } from 'drizzle-orm/postgres-js';\nimport postgres from 'postgres';\nimport { parseConnectionInfo, parseRoleSchema } from './utils';\nimport type { CreateDatabaseConfig, DatabaseInstance } from './types';\n\n// 模块级缓存,供 createRlsMiddleware 零参数调用时读取\nlet _cachedRoleSchema = '';\n\nexport function _getRoleSchema(): string {\n return _cachedRoleSchema;\n}\n\nexport function createDatabase(config: CreateDatabaseConfig): DatabaseInstance {\n\n const { roleSchema, sslModeRequired } = parseRoleSchema(config.connectionString);\n _cachedRoleSchema = roleSchema;\n\n const resolvedSsl = config.ssl || (sslModeRequired ? 'require' : false);\n\n const { connectionString, connectionConfig } = parseConnectionInfo(config.connectionString, {\n maxConnections: config.maxConnections,\n idleTimeout: config.idleTimeout,\n connectionTimeout: config.connectionTimeout,\n ssl: resolvedSsl,\n logger: typeof config.logger === 'boolean' ? config.logger : false,\n });\n\n const sql = postgres(connectionString, connectionConfig);\n const db = drizzle(sql, { schema: config.schema, logger: config.logger });\n\n return {\n db,\n close: async () => {\n await sql.end();\n },\n };\n}\n","import { type PostgresOptions } from './types';\n\ninterface ConnectionConfig {\n maxConnections?: number;\n idleTimeout?: number;\n connectionTimeout?: number;\n ssl?: PostgresOptions['ssl'];\n logger?: boolean;\n}\n\nexport function parseConnectionInfo(connectionString: string, config?: ConnectionConfig) {\n const { maxConnections, idleTimeout, connectionTimeout, ssl, logger } = config || {};\n\n let parsedConfigFromUrl = null;\n try {\n parsedConfigFromUrl = new URL(connectionString);\n const temp = new URL(connectionString);\n temp.searchParams.delete('schema');\n connectionString = temp.toString();\n } catch (error) {\n throw new Error('Failed to parse connection info');\n }\n\n const { username, password, hostname, port, pathname } = parsedConfigFromUrl;\n const connectionConfig = {\n username: decodeURIComponent(username),\n password: decodeURIComponent(password),\n host: [hostname] as unknown as string,\n port: [parseInt(port) || 5432] as unknown as number,\n pathname: decodeURIComponent(pathname),\n max: maxConnections || 1,\n idle_timeout: idleTimeout || 20,\n connect_timeout: connectionTimeout || 10,\n ssl,\n onnotice: logger ? console.log : undefined,\n };\n\n return { connectionString, connectionConfig };\n}\n\nexport function parseRoleSchema(connectionString: string): { roleSchema: string; sslModeRequired: boolean } {\n try {\n const url = new URL(connectionString);\n const roleSchema = url.searchParams.get('schema') || '';\n const sslModeRequired = url.searchParams.get('sslmode') === 'require';\n return { roleSchema, sslModeRequired };\n } catch {\n return { roleSchema: '', sslModeRequired: false };\n }\n}\n","import { type Request, type Response, type NextFunction } from 'express';\nimport { runWithAuthContext } from './runtime-patch';\nimport { _getRoleSchema } from './create-database';\nimport type { UserContext } from './types';\n\nexport interface RlsMiddlewareOptions {\n roleSchema?: string;\n}\n\nexport function createRlsMiddleware(options?: RlsMiddlewareOptions) {\n return function rlsMiddleware(req: Request, _res: Response, next: NextFunction) {\n const roleSchema = options?.roleSchema ?? _getRoleSchema();\n const userContext = (req as unknown as { userContext: UserContext }).userContext;\n\n if (!userContext) {\n return next();\n }\n\n const roles = (userContext.roles || []).join(',');\n const userId = userContext.userId;\n const isSystemAccount = userContext.isSystemAccount;\n const userType = userContext.userType;\n\n const sqls = [\n userId\n ? `SET LOCAL app.user_id = '${userId}'`\n : `SET LOCAL app.user_id = ''`,\n isSystemAccount\n ? `SET LOCAL ROLE 'service_role_${roleSchema}'`\n : userId\n ? `SET LOCAL ROLE 'authenticated_${roleSchema}'`\n : `SET LOCAL ROLE 'anon_${roleSchema}'`,\n roles\n ? `SET LOCAL app.role_ids = '${roles}'`\n : `SET LOCAL app.role_ids = ''`,\n userType\n ? `SET LOCAL app.user_type = '${userType}'`\n : `SET LOCAL app.user_type = ''`,\n ]\n .filter(Boolean)\n .join(';');\n\n runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n };\n}\n"],"mappings":"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","names":["AsyncLocalStorage","createRequire","fileURLToPath","_require","createRequire","__filename","fileURLToPath","url","asyncLocalStorage","AsyncLocalStorage","getAuthContextStore","getStore","runWithAuthContext","store","fn","run","_applied","applyRuntimePatch","path","drizzleEntry","resolve","drizzleDir","slice","lastIndexOf","sep","length","session","join","tracing","utils","PreparedQuery","PostgresJsPreparedQuery","Error","originalExecute","prototype","execute","originalInit","patchedConstructor","args","apply","authContext","preSql","postSql","params","call","fields","client","joinsNotNullableMap","customResultMapper","tracer","startActiveSpan","_span","execInContext","sql","unsafe","result","queryWithCache","queryString","rows","innerSpan","setAttributes","JSON","stringify","values","map","row","mapResultRow","begin","tx","drizzle","postgres","parseConnectionInfo","connectionString","config","maxConnections","idleTimeout","connectionTimeout","ssl","logger","parsedConfigFromUrl","URL","temp","searchParams","delete","toString","error","Error","username","password","hostname","port","pathname","connectionConfig","decodeURIComponent","host","parseInt","max","idle_timeout","connect_timeout","onnotice","console","log","undefined","parseRoleSchema","url","roleSchema","get","sslModeRequired","_cachedRoleSchema","_getRoleSchema","createDatabase","config","roleSchema","sslModeRequired","parseRoleSchema","connectionString","resolvedSsl","ssl","connectionConfig","parseConnectionInfo","maxConnections","idleTimeout","connectionTimeout","logger","sql","postgres","db","drizzle","schema","close","end","createRlsMiddleware","options","rlsMiddleware","req","_res","next","roleSchema","_getRoleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","runWithAuthContext","preSql"]}
1
+ {"version":3,"sources":["../src/runtime-patch.ts","../src/create-database.ts","../src/utils.ts","../src/create-rls-middleware.ts"],"sourcesContent":["/* eslint-disable */\n/**\n * Runtime monkey-patch for drizzle-orm RLS support.\n *\n * Instead of patching files on disk (which breaks under pnpm v10's security policy),\n * this module patches drizzle-orm's PostgresJsPreparedQuery in memory at import time.\n *\n * Node.js CJS module cache guarantees that require('drizzle-orm/postgres-js/session.cjs')\n * returns the same object everywhere, so prototype mutations propagate to all consumers.\n */\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\n\n// Create a require function that works in both ESM and CJS contexts\n// In CJS bundle (tsup output), import.meta.url may be undefined\nconst _require = createRequire(\n typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url)\n);\n\nexport interface RequestSessionContext {\n preSql?: string;\n postSql?: string;\n}\n\n// ── AsyncLocalStorage for per-request RLS context ──\nconst asyncLocalStorage = new AsyncLocalStorage<RequestSessionContext>();\n\nexport function getAuthContextStore(): RequestSessionContext | undefined {\n return asyncLocalStorage.getStore();\n}\n\nexport function runWithAuthContext(store: RequestSessionContext, fn: () => void): void {\n asyncLocalStorage.run(store, fn);\n}\n\n// ── Monkey-patch drizzle-orm ──\nlet _applied = false;\n\nfunction applyRuntimePatch(): void {\n if (_applied) return;\n\n // Resolve drizzle-orm root dir from its main entry, then require internal CJS files\n // by absolute path (bypasses package.json exports restrictions that block deep imports)\n const path = _require('node:path') as typeof import('node:path');\n const drizzleEntry = _require.resolve('drizzle-orm');\n const drizzleDir = drizzleEntry.slice(0, drizzleEntry.lastIndexOf(`${path.sep}drizzle-orm${path.sep}`) + `${path.sep}drizzle-orm`.length);\n const session = _require(path.join(drizzleDir, 'postgres-js', 'session.cjs'));\n const tracing = _require(path.join(drizzleDir, 'tracing.cjs'));\n const utils = _require(path.join(drizzleDir, 'utils.cjs'));\n\n const PreparedQuery = session.PostgresJsPreparedQuery;\n if (!PreparedQuery) {\n throw new Error('Cannot find PostgresJsPreparedQuery in drizzle-orm/postgres-js/session.cjs');\n }\n\n const originalExecute = PreparedQuery.prototype.execute;\n\n // Patch constructor: capture preSql/postSql from AsyncLocalStorage at query creation time\n const originalInit = PreparedQuery.prototype.constructor;\n const patchedConstructor = function (this: any, ...args: any[]) {\n originalInit.apply(this, args);\n const authContext = getAuthContextStore();\n this.preSql = authContext?.preSql;\n this.postSql = authContext?.postSql;\n };\n PreparedQuery.prototype.constructor = patchedConstructor;\n\n // Patch execute: if preSql/postSql exist, wrap in transaction\n PreparedQuery.prototype.execute = async function (this: any, params?: unknown[]) {\n const { preSql, postSql } = this;\n\n if (!preSql && !postSql) {\n // No RLS context, use original execute\n return originalExecute.call(this, params);\n }\n\n // Execute with RLS context: preSql → main query → postSql in a transaction\n const { fields, client, joinsNotNullableMap, customResultMapper } = this;\n const tracer = tracing.tracer;\n\n return tracer.startActiveSpan('drizzle.driver.execute.transactional', async (_span: any) => {\n const execInContext = async (sql: any) => {\n if (preSql) {\n await tracer.startActiveSpan('drizzle.driver.execute.pre', () => sql.unsafe(preSql));\n }\n\n let result;\n if (!fields && !customResultMapper) {\n result = await tracer.startActiveSpan('drizzle.driver.execute.main', () => {\n return this.queryWithCache(this.queryString, params, async () => {\n return await sql.unsafe(this.queryString, params as any);\n });\n });\n } else {\n const rows = await tracer.startActiveSpan('drizzle.driver.execute.main', (innerSpan: any) => {\n innerSpan?.setAttributes({\n 'drizzle.query.text': this.queryString,\n 'drizzle.query.params': JSON.stringify(params),\n });\n return this.queryWithCache(this.queryString, params, async () => {\n return await sql.unsafe(this.queryString, params as any).values();\n });\n });\n result = tracer.startActiveSpan('drizzle.mapResponse', () => {\n return customResultMapper\n ? customResultMapper(rows)\n : rows.map((row: any) => utils.mapResultRow(fields, row, joinsNotNullableMap));\n });\n }\n\n if (postSql) {\n await tracer.startActiveSpan('drizzle.driver.execute.post', () => sql.unsafe(postSql));\n }\n\n return result;\n };\n\n // If client has begin(), we're not in a transaction yet — start one\n if (typeof client.begin === 'function') {\n return client.begin((tx: any) => execInContext(tx));\n }\n // Already in a transaction, execute directly\n return execInContext(client);\n });\n };\n\n _applied = true;\n}\n\n// Apply patch immediately on import\napplyRuntimePatch();\n","import { drizzle } from 'drizzle-orm/postgres-js';\nimport postgres from 'postgres';\nimport { parseConnectionInfo, parseRoleSchema } from './utils';\nimport type { CreateDatabaseConfig, DatabaseInstance } from './types';\n\n// 模块级缓存,供 createRlsMiddleware 零参数调用时读取\nlet _cachedRoleSchema = '';\n\nexport function _getRoleSchema(): string {\n return _cachedRoleSchema;\n}\n\nexport function createDatabase(config: CreateDatabaseConfig): DatabaseInstance {\n\n const { roleSchema, sslModeRequired } = parseRoleSchema(config.connectionString);\n _cachedRoleSchema = roleSchema;\n\n const resolvedSsl = config.ssl || (sslModeRequired ? 'require' : false);\n\n const { connectionString, connectionConfig } = parseConnectionInfo(config.connectionString, {\n maxConnections: config.maxConnections,\n idleTimeout: config.idleTimeout,\n connectionTimeout: config.connectionTimeout,\n ssl: resolvedSsl,\n logger: typeof config.logger === 'boolean' ? config.logger : false,\n });\n\n const sql = postgres(connectionString, connectionConfig);\n const db = drizzle(sql, { schema: config.schema, logger: config.logger });\n\n return {\n db,\n close: async () => {\n await sql.end();\n },\n };\n}\n","import { type PostgresOptions } from './types';\n\ninterface ConnectionConfig {\n maxConnections?: number;\n idleTimeout?: number;\n connectionTimeout?: number;\n ssl?: PostgresOptions['ssl'];\n logger?: boolean;\n}\n\nexport function parseConnectionInfo(connectionString: string, config?: ConnectionConfig) {\n const { maxConnections, idleTimeout, connectionTimeout, ssl, logger } = config || {};\n\n let parsedConfigFromUrl = null;\n try {\n parsedConfigFromUrl = new URL(connectionString);\n const temp = new URL(connectionString);\n temp.searchParams.delete('schema');\n connectionString = temp.toString();\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new Error(`Failed to parse connection info: ${message}`);\n }\n\n const { username, password, hostname, port, pathname } = parsedConfigFromUrl;\n const connectionConfig = {\n username: decodeURIComponent(username),\n password: decodeURIComponent(password),\n host: [hostname] as unknown as string,\n port: [parseInt(port) || 5432] as unknown as number,\n pathname: decodeURIComponent(pathname),\n max: maxConnections || 1,\n idle_timeout: idleTimeout || 20,\n connect_timeout: connectionTimeout || 10,\n ssl,\n onnotice: logger ? console.log : undefined,\n };\n\n return { connectionString, connectionConfig };\n}\n\nexport function parseRoleSchema(connectionString: string): { roleSchema: string; sslModeRequired: boolean } {\n try {\n const url = new URL(connectionString);\n const roleSchema = url.searchParams.get('schema') || '';\n const sslModeRequired = url.searchParams.get('sslmode') === 'require';\n return { roleSchema, sslModeRequired };\n } catch {\n return { roleSchema: '', sslModeRequired: false };\n }\n}\n","import { type Request, type Response, type NextFunction } from 'express';\nimport { runWithAuthContext } from './runtime-patch';\nimport { _getRoleSchema } from './create-database';\nimport type { UserContext } from './types';\n\nexport interface RlsMiddlewareOptions {\n roleSchema?: string;\n}\n\nexport function createRlsMiddleware(options?: RlsMiddlewareOptions) {\n return function rlsMiddleware(req: Request, _res: Response, next: NextFunction) {\n const roleSchema = options?.roleSchema ?? _getRoleSchema();\n const userContext = (req as unknown as { userContext: UserContext }).userContext;\n\n if (!userContext) {\n return next();\n }\n\n const roles = (userContext.roles || []).join(',');\n const userId = userContext.userId;\n const isSystemAccount = userContext.isSystemAccount;\n const userType = userContext.userType;\n\n const sqls = [\n userId\n ? `SET LOCAL app.user_id = '${userId}'`\n : `SET LOCAL app.user_id = ''`,\n isSystemAccount\n ? `SET LOCAL ROLE 'service_role_${roleSchema}'`\n : userId\n ? `SET LOCAL ROLE 'authenticated_${roleSchema}'`\n : `SET LOCAL ROLE 'anon_${roleSchema}'`,\n roles\n ? `SET LOCAL app.role_ids = '${roles}'`\n : `SET LOCAL app.role_ids = ''`,\n userType\n ? `SET LOCAL app.user_type = '${userType}'`\n : `SET LOCAL app.user_type = ''`,\n ]\n .filter(Boolean)\n .join(';');\n\n runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n };\n}\n"],"mappings":"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","names":["AsyncLocalStorage","createRequire","fileURLToPath","_require","createRequire","__filename","fileURLToPath","url","asyncLocalStorage","AsyncLocalStorage","getAuthContextStore","getStore","runWithAuthContext","store","fn","run","_applied","applyRuntimePatch","path","drizzleEntry","resolve","drizzleDir","slice","lastIndexOf","sep","length","session","join","tracing","utils","PreparedQuery","PostgresJsPreparedQuery","Error","originalExecute","prototype","execute","originalInit","patchedConstructor","args","apply","authContext","preSql","postSql","params","call","fields","client","joinsNotNullableMap","customResultMapper","tracer","startActiveSpan","_span","execInContext","sql","unsafe","result","queryWithCache","queryString","rows","innerSpan","setAttributes","JSON","stringify","values","map","row","mapResultRow","begin","tx","drizzle","postgres","parseConnectionInfo","connectionString","config","maxConnections","idleTimeout","connectionTimeout","ssl","logger","parsedConfigFromUrl","URL","temp","searchParams","delete","toString","error","message","Error","username","password","hostname","port","pathname","connectionConfig","decodeURIComponent","host","parseInt","max","idle_timeout","connect_timeout","onnotice","console","log","undefined","parseRoleSchema","url","roleSchema","get","sslModeRequired","_cachedRoleSchema","_getRoleSchema","createDatabase","config","roleSchema","sslModeRequired","parseRoleSchema","connectionString","resolvedSsl","ssl","connectionConfig","parseConnectionInfo","maxConnections","idleTimeout","connectionTimeout","logger","sql","postgres","db","drizzle","schema","close","end","createRlsMiddleware","options","rlsMiddleware","req","_res","next","roleSchema","_getRoleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","runWithAuthContext","preSql"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/express-datapaas",
3
- "version": "1.0.0",
3
+ "version": "1.0.1-alpha.0",
4
4
  "description": "Drizzle ORM + PostgreSQL with RLS support for Express",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",