@lark-apaas/express-datapaas 0.0.3 → 1.0.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
@@ -36,6 +36,92 @@ __export(index_exports, {
36
36
  });
37
37
  module.exports = __toCommonJS(index_exports);
38
38
 
39
+ // src/runtime-patch.ts
40
+ var import_node_async_hooks = require("async_hooks");
41
+ var import_node_module = require("module");
42
+ var import_node_url = require("url");
43
+ var import_meta = {};
44
+ var _require = (0, import_node_module.createRequire)(typeof __filename !== "undefined" ? __filename : (0, import_node_url.fileURLToPath)(import_meta.url));
45
+ var asyncLocalStorage = new import_node_async_hooks.AsyncLocalStorage();
46
+ function getAuthContextStore() {
47
+ return asyncLocalStorage.getStore();
48
+ }
49
+ __name(getAuthContextStore, "getAuthContextStore");
50
+ function runWithAuthContext(store, fn) {
51
+ asyncLocalStorage.run(store, fn);
52
+ }
53
+ __name(runWithAuthContext, "runWithAuthContext");
54
+ var _applied = false;
55
+ function applyRuntimePatch() {
56
+ if (_applied) return;
57
+ const path = _require("node:path");
58
+ const drizzleEntry = _require.resolve("drizzle-orm");
59
+ const drizzleDir = drizzleEntry.slice(0, drizzleEntry.lastIndexOf(`${path.sep}drizzle-orm${path.sep}`) + `${path.sep}drizzle-orm`.length);
60
+ const session = _require(path.join(drizzleDir, "postgres-js", "session.cjs"));
61
+ const tracing = _require(path.join(drizzleDir, "tracing.cjs"));
62
+ const utils = _require(path.join(drizzleDir, "utils.cjs"));
63
+ const PreparedQuery = session.PostgresJsPreparedQuery;
64
+ if (!PreparedQuery) {
65
+ throw new Error("Cannot find PostgresJsPreparedQuery in drizzle-orm/postgres-js/session.cjs");
66
+ }
67
+ const originalExecute = PreparedQuery.prototype.execute;
68
+ const originalInit = PreparedQuery.prototype.constructor;
69
+ const patchedConstructor = /* @__PURE__ */ __name(function(...args) {
70
+ originalInit.apply(this, args);
71
+ const authContext = getAuthContextStore();
72
+ this.preSql = authContext?.preSql;
73
+ this.postSql = authContext?.postSql;
74
+ }, "patchedConstructor");
75
+ PreparedQuery.prototype.constructor = patchedConstructor;
76
+ PreparedQuery.prototype.execute = async function(params) {
77
+ const { preSql, postSql } = this;
78
+ if (!preSql && !postSql) {
79
+ return originalExecute.call(this, params);
80
+ }
81
+ const { fields, client, joinsNotNullableMap, customResultMapper } = this;
82
+ const tracer = tracing.tracer;
83
+ return tracer.startActiveSpan("drizzle.driver.execute.transactional", async (_span) => {
84
+ const execInContext = /* @__PURE__ */ __name(async (sql) => {
85
+ if (preSql) {
86
+ await tracer.startActiveSpan("drizzle.driver.execute.pre", () => sql.unsafe(preSql));
87
+ }
88
+ let result;
89
+ if (!fields && !customResultMapper) {
90
+ result = await tracer.startActiveSpan("drizzle.driver.execute.main", () => {
91
+ return this.queryWithCache(this.queryString, params, async () => {
92
+ return await sql.unsafe(this.queryString, params);
93
+ });
94
+ });
95
+ } else {
96
+ const rows = await tracer.startActiveSpan("drizzle.driver.execute.main", (innerSpan) => {
97
+ innerSpan?.setAttributes({
98
+ "drizzle.query.text": this.queryString,
99
+ "drizzle.query.params": JSON.stringify(params)
100
+ });
101
+ return this.queryWithCache(this.queryString, params, async () => {
102
+ return await sql.unsafe(this.queryString, params).values();
103
+ });
104
+ });
105
+ result = tracer.startActiveSpan("drizzle.mapResponse", () => {
106
+ return customResultMapper ? customResultMapper(rows) : rows.map((row) => utils.mapResultRow(fields, row, joinsNotNullableMap));
107
+ });
108
+ }
109
+ if (postSql) {
110
+ await tracer.startActiveSpan("drizzle.driver.execute.post", () => sql.unsafe(postSql));
111
+ }
112
+ return result;
113
+ }, "execInContext");
114
+ if (typeof client.begin === "function") {
115
+ return client.begin((tx) => execInContext(tx));
116
+ }
117
+ return execInContext(client);
118
+ });
119
+ };
120
+ _applied = true;
121
+ }
122
+ __name(applyRuntimePatch, "applyRuntimePatch");
123
+ applyRuntimePatch();
124
+
39
125
  // src/create-database.ts
40
126
  var import_postgres_js = require("drizzle-orm/postgres-js");
41
127
  var import_postgres = __toESM(require("postgres"), 1);
@@ -99,21 +185,7 @@ function _getRoleSchema() {
99
185
  return _cachedRoleSchema;
100
186
  }
101
187
  __name(_getRoleSchema, "_getRoleSchema");
102
- function verifyDrizzlePatched() {
103
- try {
104
- const session2 = require("drizzle-orm/postgres-js");
105
- const ok = session2 && typeof session2.getAuthContextStore === "function" && typeof session2.runWithAuthContext === "function";
106
- if (!ok) {
107
- throw new Error("drizzle-orm \u672A\u68C0\u6D4B\u5230\u8865\u4E01\u5BFC\u51FA\uFF1AgetAuthContextStore/runWithAuthContext");
108
- }
109
- } catch (err) {
110
- const reason = err instanceof Error ? err.message : String(err);
111
- throw new Error(`drizzle-orm \u8865\u4E01\u672A\u751F\u6548\u6216\u7248\u672C\u4E0D\u5339\u914D\uFF1A${reason}`);
112
- }
113
- }
114
- __name(verifyDrizzlePatched, "verifyDrizzlePatched");
115
188
  function createDatabase(config) {
116
- verifyDrizzlePatched();
117
189
  const { roleSchema, sslModeRequired } = parseRoleSchema(config.connectionString);
118
190
  _cachedRoleSchema = roleSchema;
119
191
  const resolvedSsl = config.ssl || (sslModeRequired ? "require" : false);
@@ -139,7 +211,6 @@ function createDatabase(config) {
139
211
  __name(createDatabase, "createDatabase");
140
212
 
141
213
  // src/create-rls-middleware.ts
142
- var session = __toESM(require("drizzle-orm/postgres-js"), 1);
143
214
  function createRlsMiddleware(options) {
144
215
  return /* @__PURE__ */ __name(function rlsMiddleware(req, _res, next) {
145
216
  const roleSchema = options?.roleSchema ?? _getRoleSchema();
@@ -157,7 +228,7 @@ function createRlsMiddleware(options) {
157
228
  roles ? `SET LOCAL app.role_ids = '${roles}'` : `SET LOCAL app.role_ids = ''`,
158
229
  userType ? `SET LOCAL app.user_type = '${userType}'` : `SET LOCAL app.user_type = ''`
159
230
  ].filter(Boolean).join(";");
160
- session.runWithAuthContext({
231
+ runWithAuthContext({
161
232
  preSql: sqls
162
233
  }, () => {
163
234
  next();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/create-database.ts","../src/utils.ts","../src/create-rls-middleware.ts"],"sourcesContent":["export { 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","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\nfunction verifyDrizzlePatched(): void {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const session = require('drizzle-orm/postgres-js');\n const ok =\n session &&\n typeof session.getAuthContextStore === 'function' &&\n typeof session.runWithAuthContext === 'function';\n if (!ok) {\n throw new Error(\n 'drizzle-orm 未检测到补丁导出:getAuthContextStore/runWithAuthContext',\n );\n }\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`drizzle-orm 补丁未生效或版本不匹配:${reason}`);\n }\n}\n\nexport function createDatabase(config: CreateDatabaseConfig): DatabaseInstance {\n verifyDrizzlePatched();\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 * as session from 'drizzle-orm/postgres-js';\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 (session as unknown as { runWithAuthContext: (store: { preSql: string }, fn: () => void) => void })\n .runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n };\n}\n"],"mappings":"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","names":["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","verifyDrizzlePatched","session","require","ok","getAuthContextStore","runWithAuthContext","Error","err","reason","message","String","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","session","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 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"]}
package/dist/index.js CHANGED
@@ -1,11 +1,90 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
3
+
4
+ // src/runtime-patch.ts
5
+ import { AsyncLocalStorage } from "async_hooks";
6
+ import { createRequire } from "module";
7
+ import { fileURLToPath } from "url";
8
+ var _require = createRequire(typeof __filename !== "undefined" ? __filename : fileURLToPath(import.meta.url));
9
+ var asyncLocalStorage = new AsyncLocalStorage();
10
+ function getAuthContextStore() {
11
+ return asyncLocalStorage.getStore();
12
+ }
13
+ __name(getAuthContextStore, "getAuthContextStore");
14
+ function runWithAuthContext(store, fn) {
15
+ asyncLocalStorage.run(store, fn);
16
+ }
17
+ __name(runWithAuthContext, "runWithAuthContext");
18
+ var _applied = false;
19
+ function applyRuntimePatch() {
20
+ if (_applied) return;
21
+ const path = _require("node:path");
22
+ const drizzleEntry = _require.resolve("drizzle-orm");
23
+ const drizzleDir = drizzleEntry.slice(0, drizzleEntry.lastIndexOf(`${path.sep}drizzle-orm${path.sep}`) + `${path.sep}drizzle-orm`.length);
24
+ const session = _require(path.join(drizzleDir, "postgres-js", "session.cjs"));
25
+ const tracing = _require(path.join(drizzleDir, "tracing.cjs"));
26
+ const utils = _require(path.join(drizzleDir, "utils.cjs"));
27
+ const PreparedQuery = session.PostgresJsPreparedQuery;
28
+ if (!PreparedQuery) {
29
+ throw new Error("Cannot find PostgresJsPreparedQuery in drizzle-orm/postgres-js/session.cjs");
30
+ }
31
+ const originalExecute = PreparedQuery.prototype.execute;
32
+ const originalInit = PreparedQuery.prototype.constructor;
33
+ const patchedConstructor = /* @__PURE__ */ __name(function(...args) {
34
+ originalInit.apply(this, args);
35
+ const authContext = getAuthContextStore();
36
+ this.preSql = authContext?.preSql;
37
+ this.postSql = authContext?.postSql;
38
+ }, "patchedConstructor");
39
+ PreparedQuery.prototype.constructor = patchedConstructor;
40
+ PreparedQuery.prototype.execute = async function(params) {
41
+ const { preSql, postSql } = this;
42
+ if (!preSql && !postSql) {
43
+ return originalExecute.call(this, params);
44
+ }
45
+ const { fields, client, joinsNotNullableMap, customResultMapper } = this;
46
+ const tracer = tracing.tracer;
47
+ return tracer.startActiveSpan("drizzle.driver.execute.transactional", async (_span) => {
48
+ const execInContext = /* @__PURE__ */ __name(async (sql) => {
49
+ if (preSql) {
50
+ await tracer.startActiveSpan("drizzle.driver.execute.pre", () => sql.unsafe(preSql));
51
+ }
52
+ let result;
53
+ if (!fields && !customResultMapper) {
54
+ result = await tracer.startActiveSpan("drizzle.driver.execute.main", () => {
55
+ return this.queryWithCache(this.queryString, params, async () => {
56
+ return await sql.unsafe(this.queryString, params);
57
+ });
58
+ });
59
+ } else {
60
+ const rows = await tracer.startActiveSpan("drizzle.driver.execute.main", (innerSpan) => {
61
+ innerSpan?.setAttributes({
62
+ "drizzle.query.text": this.queryString,
63
+ "drizzle.query.params": JSON.stringify(params)
64
+ });
65
+ return this.queryWithCache(this.queryString, params, async () => {
66
+ return await sql.unsafe(this.queryString, params).values();
67
+ });
68
+ });
69
+ result = tracer.startActiveSpan("drizzle.mapResponse", () => {
70
+ return customResultMapper ? customResultMapper(rows) : rows.map((row) => utils.mapResultRow(fields, row, joinsNotNullableMap));
71
+ });
72
+ }
73
+ if (postSql) {
74
+ await tracer.startActiveSpan("drizzle.driver.execute.post", () => sql.unsafe(postSql));
75
+ }
76
+ return result;
77
+ }, "execInContext");
78
+ if (typeof client.begin === "function") {
79
+ return client.begin((tx) => execInContext(tx));
80
+ }
81
+ return execInContext(client);
82
+ });
83
+ };
84
+ _applied = true;
85
+ }
86
+ __name(applyRuntimePatch, "applyRuntimePatch");
87
+ applyRuntimePatch();
9
88
 
10
89
  // src/create-database.ts
11
90
  import { drizzle } from "drizzle-orm/postgres-js";
@@ -70,21 +149,7 @@ function _getRoleSchema() {
70
149
  return _cachedRoleSchema;
71
150
  }
72
151
  __name(_getRoleSchema, "_getRoleSchema");
73
- function verifyDrizzlePatched() {
74
- try {
75
- const session2 = __require("drizzle-orm/postgres-js");
76
- const ok = session2 && typeof session2.getAuthContextStore === "function" && typeof session2.runWithAuthContext === "function";
77
- if (!ok) {
78
- throw new Error("drizzle-orm \u672A\u68C0\u6D4B\u5230\u8865\u4E01\u5BFC\u51FA\uFF1AgetAuthContextStore/runWithAuthContext");
79
- }
80
- } catch (err) {
81
- const reason = err instanceof Error ? err.message : String(err);
82
- throw new Error(`drizzle-orm \u8865\u4E01\u672A\u751F\u6548\u6216\u7248\u672C\u4E0D\u5339\u914D\uFF1A${reason}`);
83
- }
84
- }
85
- __name(verifyDrizzlePatched, "verifyDrizzlePatched");
86
152
  function createDatabase(config) {
87
- verifyDrizzlePatched();
88
153
  const { roleSchema, sslModeRequired } = parseRoleSchema(config.connectionString);
89
154
  _cachedRoleSchema = roleSchema;
90
155
  const resolvedSsl = config.ssl || (sslModeRequired ? "require" : false);
@@ -110,7 +175,6 @@ function createDatabase(config) {
110
175
  __name(createDatabase, "createDatabase");
111
176
 
112
177
  // src/create-rls-middleware.ts
113
- import * as session from "drizzle-orm/postgres-js";
114
178
  function createRlsMiddleware(options) {
115
179
  return /* @__PURE__ */ __name(function rlsMiddleware(req, _res, next) {
116
180
  const roleSchema = options?.roleSchema ?? _getRoleSchema();
@@ -128,7 +192,7 @@ function createRlsMiddleware(options) {
128
192
  roles ? `SET LOCAL app.role_ids = '${roles}'` : `SET LOCAL app.role_ids = ''`,
129
193
  userType ? `SET LOCAL app.user_type = '${userType}'` : `SET LOCAL app.user_type = ''`
130
194
  ].filter(Boolean).join(";");
131
- session.runWithAuthContext({
195
+ runWithAuthContext({
132
196
  preSql: sqls
133
197
  }, () => {
134
198
  next();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-database.ts","../src/utils.ts","../src/create-rls-middleware.ts"],"sourcesContent":["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\nfunction verifyDrizzlePatched(): void {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const session = require('drizzle-orm/postgres-js');\n const ok =\n session &&\n typeof session.getAuthContextStore === 'function' &&\n typeof session.runWithAuthContext === 'function';\n if (!ok) {\n throw new Error(\n 'drizzle-orm 未检测到补丁导出:getAuthContextStore/runWithAuthContext',\n );\n }\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`drizzle-orm 补丁未生效或版本不匹配:${reason}`);\n }\n}\n\nexport function createDatabase(config: CreateDatabaseConfig): DatabaseInstance {\n verifyDrizzlePatched();\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 * as session from 'drizzle-orm/postgres-js';\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 (session as unknown as { runWithAuthContext: (store: { preSql: string }, fn: () => void) => void })\n .runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n };\n}\n"],"mappings":"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","names":["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","verifyDrizzlePatched","session","require","ok","getAuthContextStore","runWithAuthContext","Error","err","reason","message","String","createDatabase","config","roleSchema","sslModeRequired","parseRoleSchema","connectionString","resolvedSsl","ssl","connectionConfig","parseConnectionInfo","maxConnections","idleTimeout","connectionTimeout","logger","sql","postgres","db","drizzle","schema","close","end","session","createRlsMiddleware","options","rlsMiddleware","req","_res","next","roleSchema","_getRoleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","session","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 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/express-datapaas",
3
- "version": "0.0.3",
3
+ "version": "1.0.0",
4
4
  "description": "Drizzle ORM + PostgreSQL with RLS support for Express",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,9 +21,7 @@
21
21
  "node": ">=18.0.0"
22
22
  },
23
23
  "files": [
24
- "dist",
25
- "patches",
26
- "scripts"
24
+ "dist"
27
25
  ],
28
26
  "exports": {
29
27
  ".": {
@@ -34,8 +32,6 @@
34
32
  },
35
33
  "scripts": {
36
34
  "build": "tsup",
37
- "postinstall": "node scripts/apply-patch.js",
38
- "apply-patch": "node scripts/apply-patch.js",
39
35
  "dev": "tsup --watch",
40
36
  "clean": "rm -rf dist",
41
37
  "lint": "eslint src/**/*.ts",
@@ -45,7 +41,6 @@
45
41
  "prepublishOnly": "npm run build"
46
42
  },
47
43
  "dependencies": {
48
- "patch-package": "^8.0.0",
49
44
  "postgres": "^3.4.3"
50
45
  },
51
46
  "devDependencies": {
@@ -1,178 +0,0 @@
1
- diff --git a/node_modules/drizzle-orm/postgres-js/session.cjs b/node_modules/drizzle-orm/postgres-js/session.cjs
2
- index dac222a..7708081 100644
3
- --- a/node_modules/drizzle-orm/postgres-js/session.cjs
4
- +++ b/node_modules/drizzle-orm/postgres-js/session.cjs
5
- @@ -20,9 +20,12 @@ var session_exports = {};
6
- __export(session_exports, {
7
- PostgresJsPreparedQuery: () => PostgresJsPreparedQuery,
8
- PostgresJsSession: () => PostgresJsSession,
9
- - PostgresJsTransaction: () => PostgresJsTransaction
10
- + PostgresJsTransaction: () => PostgresJsTransaction,
11
- + getAuthContextStore: () => getAuthContextStore,
12
- + runWithAuthContext: () => runWithAuthContext,
13
- });
14
- module.exports = __toCommonJS(session_exports);
15
- +var { AsyncLocalStorage } = require('node:async_hooks');
16
- var import_core = require("../cache/core/index.cjs");
17
- var import_entity = require("../entity.cjs");
18
- var import_logger = require("../logger.cjs");
19
- @@ -31,10 +34,24 @@ var import_session = require("../pg-core/session.cjs");
20
- var import_sql = require("../sql/sql.cjs");
21
- var import_tracing = require("../tracing.cjs");
22
- var import_utils = require("../utils.cjs");
23
- +
24
- +const asyncLocalStorage = new AsyncLocalStorage();
25
- +
26
- +function getAuthContextStore() {
27
- + return asyncLocalStorage.getStore();
28
- +}
29
- +
30
- +function runWithAuthContext(store, fn) {
31
- + return asyncLocalStorage.run(store, fn);
32
- +}
33
- +
34
- class PostgresJsPreparedQuery extends import_session.PgPreparedQuery {
35
- constructor(client, queryString, params, logger, cache, queryMetadata, cacheConfig, fields, _isResponseInArrayMode, customResultMapper) {
36
- super({ sql: queryString, params }, cache, queryMetadata, cacheConfig);
37
- this.client = client;
38
- + const authContextStore = getAuthContextStore();
39
- + this.preSql = authContextStore?.preSql;
40
- + this.postSql = authContextStore?.postSql;
41
- this.queryString = queryString;
42
- this.params = params;
43
- this.logger = logger;
44
- @@ -51,21 +68,103 @@ class PostgresJsPreparedQuery extends import_session.PgPreparedQuery {
45
- "drizzle.query.params": JSON.stringify(params)
46
- });
47
- this.logger.logQuery(this.queryString, params);
48
- - const { fields, queryString: query, client, joinsNotNullableMap, customResultMapper } = this;
49
- + const { fields, client, joinsNotNullableMap, customResultMapper, preSql, postSql } = this;
50
- +
51
- + // 在同一事务中依次执行:preSql(可选)→ 主查询 → postSql(可选)
52
- + if (preSql || postSql) {
53
- + return import_tracing.tracer.startActiveSpan("drizzle.driver.execute.transactional", async () => {
54
- + // 若当前不在事务中(client 有 begin 方法),则开启事务;否则复用当前事务
55
- + if (typeof client.begin === 'function') {
56
- + return client.begin(async (tx) => {
57
- + // 若存在 preSql,则执行
58
- + if (preSql) {
59
- + await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.pre", () => tx.unsafe(preSql));
60
- + }
61
- +
62
- + // 执行主查询
63
- + let result;
64
- + if (!fields && !customResultMapper) {
65
- + result = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.main", () => {
66
- + return this.queryWithCache(this.queryString, params, async () => {
67
- + return await tx.unsafe(this.queryString, params);
68
- + });
69
- + });
70
- + } else {
71
- + const rows = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.main", () => {
72
- + span?.setAttributes({
73
- + "drizzle.query.text": this.queryString,
74
- + "drizzle.query.params": JSON.stringify(params)
75
- + });
76
- + return this.queryWithCache(this.queryString, params, async () => {
77
- + return await tx.unsafe(this.queryString, params).values();
78
- + });;
79
- + });
80
- + result = import_tracing.tracer.startActiveSpan("drizzle.mapResponse", () => {
81
- + return customResultMapper ? customResultMapper(rows) : rows.map((row) => (0, import_utils.mapResultRow)(fields, row, joinsNotNullableMap));
82
- + });
83
- + }
84
- +
85
- + // 若存在 postSql,则执行
86
- + if (postSql) {
87
- + await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.post", () => tx.unsafe(postSql));
88
- + }
89
- +
90
- + return result;
91
- + });
92
- + } else {
93
- + // 已在事务中,直接执行
94
- + // 若存在 preSql,则执行
95
- + if (preSql) {
96
- + await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.pre", () => client.unsafe(preSql));
97
- + }
98
- +
99
- + // 执行主查询
100
- + let result;
101
- + if (!fields && !customResultMapper) {
102
- + result = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.main", () => {
103
- + return this.queryWithCache(this.queryString, params, async () => {
104
- + return await client.unsafe(this.queryString, params);
105
- + });
106
- + });
107
- + } else {
108
- + const rows = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.main", () => {
109
- + span?.setAttributes({
110
- + "drizzle.query.text": this.queryString,
111
- + "drizzle.query.params": JSON.stringify(params)
112
- + });
113
- + return this.queryWithCache(this.queryString, params, async () => {
114
- + return await client.unsafe(this.queryString, params).values();
115
- + });
116
- + });
117
- + result = import_tracing.tracer.startActiveSpan("drizzle.mapResponse", () => {
118
- + return customResultMapper ? customResultMapper(rows) : rows.map((row) => (0, import_utils.mapResultRow)(fields, row, joinsNotNullableMap));
119
- + });
120
- + }
121
- +
122
- + // 若存在 postSql,则执行
123
- + if (postSql) {
124
- + await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.post", () => client.unsafe(postSql));
125
- + }
126
- +
127
- + return result;
128
- + }
129
- + });
130
- + }
131
- + // 单语句执行(原始逻辑)
132
- if (!fields && !customResultMapper) {
133
- return import_tracing.tracer.startActiveSpan("drizzle.driver.execute", () => {
134
- - return this.queryWithCache(query, params, async () => {
135
- - return await client.unsafe(query, params);
136
- + return this.queryWithCache(this.queryString, params, async () => {
137
- + return await client.unsafe(this.queryString, params);
138
- });
139
- });
140
- }
141
- const rows = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute", () => {
142
- span?.setAttributes({
143
- - "drizzle.query.text": query,
144
- + "drizzle.query.text": this.queryString,
145
- "drizzle.query.params": JSON.stringify(params)
146
- });
147
- - return this.queryWithCache(query, params, async () => {
148
- - return await client.unsafe(query, params).values();
149
- + return this.queryWithCache(this.queryString, params, async () => {
150
- + return await client.unsafe(this.queryString, params).values();
151
- });
152
- });
153
- return import_tracing.tracer.startActiveSpan("drizzle.mapResponse", () => {
154
- @@ -169,6 +268,8 @@ class PostgresJsTransaction extends import_pg_core.PgTransaction {
155
- 0 && (module.exports = {
156
- PostgresJsPreparedQuery,
157
- PostgresJsSession,
158
- - PostgresJsTransaction
159
- + PostgresJsTransaction,
160
- + getAuthContextStore,
161
- + runWithAuthContext,
162
- });
163
- //# sourceMappingURL=session.cjs.map
164
-
165
- diff --git a/node_modules/drizzle-orm/postgres-js/session.d.ts b/node_modules/drizzle-orm/postgres-js/session.d.ts
166
- index bccaa53..b07425b 100644
167
- --- a/node_modules/drizzle-orm/postgres-js/session.d.ts
168
- +++ b/node_modules/drizzle-orm/postgres-js/session.d.ts
169
- @@ -58,3 +58,8 @@ export declare class PostgresJsTransaction<TFullSchema extends Record<string, un
170
- export interface PostgresJsQueryResultHKT extends PgQueryResultHKT {
171
- type: RowList<Assume<this['row'], Row>[]>;
172
- }
173
- +export interface RequestSessionContext {
174
- + preSql?: string;
175
- +}
176
- +export declare function getAuthContextStore(): RequestSessionContext | undefined;
177
- +export declare function runWithAuthContext(store: RequestSessionContext, fn: () => void): void;
178
-
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env node
2
- import { execSync } from "child_process";
3
- import { createRequire } from "node:module";
4
- import fs from "fs";
5
- import path from "path";
6
- import { fileURLToPath } from "url";
7
-
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
- const sdkRoot = path.resolve(__dirname, "..");
10
- const patchDir = path.resolve(sdkRoot, "patches");
11
- const esmRequire = createRequire(import.meta.url);
12
-
13
- /**
14
- * 通过 Node 模块解析定位 drizzle-orm 所在的 node_modules 父目录。
15
- * 兼容 npm(扁平)、yarn(扁平)、pnpm(隔离 + symlink)。
16
- *
17
- * 注意:drizzle-orm 的 exports 字段未导出 ./package.json,
18
- * 因此使用 resolve("drizzle-orm") 获取入口文件路径,
19
- * 再通过 "node_modules/drizzle-orm/" marker 截取 hostRoot。
20
- */
21
- function resolveHostRoot() {
22
- const resolved = fs.realpathSync(esmRequire.resolve("drizzle-orm"));
23
- // resolved: .../node_modules/drizzle-orm/index.cjs (npm/yarn)
24
- // 或: .../.pnpm/drizzle-orm@0.44.6/node_modules/drizzle-orm/index.cjs (pnpm)
25
- const marker = `${path.sep}node_modules${path.sep}drizzle-orm${path.sep}`;
26
- const idx = resolved.lastIndexOf(marker);
27
- if (idx === -1) {
28
- throw new Error(
29
- `Cannot locate node_modules/drizzle-orm in resolved path: ${resolved}`,
30
- );
31
- }
32
- return resolved.substring(0, idx);
33
- }
34
-
35
- /**
36
- * 解析 patch-package CLI 入口的绝对路径,避免依赖 npx 在错误 cwd 下查找。
37
- */
38
- function resolvePatchBin() {
39
- const pkgPath = esmRequire.resolve("patch-package/package.json");
40
- const pkgDir = path.dirname(pkgPath);
41
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
42
- const bin =
43
- typeof pkg.bin === "string" ? pkg.bin : pkg.bin["patch-package"];
44
- return path.resolve(pkgDir, bin);
45
- }
46
-
47
- // ── main ──
48
- const hostRoot = resolveHostRoot();
49
- const patchBin = resolvePatchBin();
50
-
51
- // patch-package 要求 --patch-dir 为相对路径(相对于 cwd)
52
- const relativePatchDir = path.relative(hostRoot, patchDir);
53
-
54
- console.log(`[patch] host root : ${hostRoot}`);
55
- console.log(`[patch] patch-package : ${patchBin}`);
56
- console.log(`[patch] patch dir : ${patchDir} (relative: ${relativePatchDir})`);
57
-
58
- // pnpm 的虚拟目录下可能没有 package.json,patch-package 需要它才能工作
59
- const hostPkgPath = path.join(hostRoot, "package.json");
60
- const needsTempPkg = !fs.existsSync(hostPkgPath);
61
- if (needsTempPkg) {
62
- fs.writeFileSync(hostPkgPath, JSON.stringify({ private: true }));
63
- }
64
-
65
- try {
66
- execSync(`node "${patchBin}" --patch-dir "${relativePatchDir}"`, {
67
- stdio: "inherit",
68
- cwd: hostRoot,
69
- });
70
- console.log("[patch] drizzle-orm patched ✅");
71
- } catch (err) {
72
- console.error("[patch] Failed to apply drizzle-orm patch ❌", err);
73
- process.exit(1);
74
- } finally {
75
- if (needsTempPkg) {
76
- try { fs.unlinkSync(hostPkgPath); } catch { /* ignore */ }
77
- }
78
- }