From ad6ba8b6805f34f6532bcb17e5e70997068a37c0 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 25 Apr 2026 18:46:49 +0200 Subject: [PATCH 1/3] #362 support drizzle-orm v1 --- lib/RateLimiterDrizzle.js | 83 ++++++++++++++++++++++++++++++++------- package.json | 3 +- 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/lib/RateLimiterDrizzle.js b/lib/RateLimiterDrizzle.js index 80e6422..1d46ec7 100644 --- a/lib/RateLimiterDrizzle.js +++ b/lib/RateLimiterDrizzle.js @@ -9,23 +9,76 @@ class RateLimiterDrizzleError extends Error { } } -async function getDrizzleOperators() { +async function loadDrizzleOperators() { if (drizzleOperators) return drizzleOperators; - try { - // Use dynamic import to prevent static analysis tools from detecting the import - function getPackageName() { - return ['drizzle', 'orm'].join('-'); + const requiredOperators = ['and', 'or', 'gt', 'lt', 'eq', 'isNull', 'sql']; + + // Use dynamic import to prevent static analysis tools from detecting the import + function getPackageName() { + return ['drizzle', 'orm'].join('-'); + } + + const collectModuleLayers = (mod) => { + const layers = []; + const seen = new Set(); + let current = mod; + + while (current && (typeof current === 'object' || typeof current === 'function') && !seen.has(current)) { + layers.push(current); + seen.add(current); + current = current.default; + } + + return layers; + }; + + const importModule = async (specifier, { optional = false } = {}) => { + try { + return await import(/* @vite-ignore */ specifier); + } catch (error) { + if (optional) { + return null; + } + throw new RateLimiterDrizzleError( + 'drizzle-orm is not installed. Please install drizzle-orm to use RateLimiterDrizzle.' + ); + } + }; + + const mainModule = await importModule(`${getPackageName()}`); + const sqlModule = await importModule(`${getPackageName()}/sql`, { optional: true }); + + const mainCandidates = collectModuleLayers(mainModule); + const sqlCandidates = sqlModule ? collectModuleLayers(sqlModule) : []; + const resolvedOperators = {}; + const missingOperators = []; + + for (const operatorName of requiredOperators) { + const candidates = operatorName === 'sql' + ? [...sqlCandidates, ...mainCandidates] + : mainCandidates; + + const operator = candidates + .map((candidate) => candidate?.[operatorName]) + .find((value) => typeof value === 'function'); + + if (!operator) { + missingOperators.push(operatorName); + continue; } - const drizzleOrm = await import(`${getPackageName()}`); - const { and, or, gt, lt, eq, isNull, sql } = drizzleOrm.default || drizzleOrm; - drizzleOperators = { and, or, gt, lt, eq, isNull, sql }; - return drizzleOperators; - } catch (error) { + + resolvedOperators[operatorName] = operator; + } + + if (missingOperators.length > 0) { throw new RateLimiterDrizzleError( - 'drizzle-orm is not installed. Please install drizzle-orm to use RateLimiterDrizzle.' + `Failed to load drizzle-orm operators: missing ${missingOperators.join(', ')}` ); } + + drizzleOperators = resolvedOperators; + return drizzleOperators; } const RateLimiterStoreAbstract = require('./RateLimiterStoreAbstract'); @@ -71,7 +124,7 @@ class RateLimiterDrizzle extends RateLimiterStoreAbstract { return Promise.reject(new RateLimiterDrizzleError('Drizzle client is not established')) } - const { eq, sql } = await getDrizzleOperators(); + const { eq, sql } = await loadDrizzleOperators(); const now = new Date(); const newExpire = msDuration > 0 ? new Date(now.getTime() + msDuration) : null; @@ -117,7 +170,7 @@ class RateLimiterDrizzle extends RateLimiterStoreAbstract { return Promise.reject(new RateLimiterDrizzleError('Drizzle client is not established')) } - const { and, or, gt, eq, isNull } = await getDrizzleOperators(); + const { and, or, gt, eq, isNull } = await loadDrizzleOperators(); const [response] = await this.drizzleClient .select() @@ -139,7 +192,7 @@ class RateLimiterDrizzle extends RateLimiterStoreAbstract { return Promise.reject(new RateLimiterDrizzleError('Drizzle client is not established')) } - const { eq } = await getDrizzleOperators(); + const { eq } = await loadDrizzleOperators(); const [result] = await this.drizzleClient .delete(this.schema) @@ -156,7 +209,7 @@ class RateLimiterDrizzle extends RateLimiterStoreAbstract { this._clearExpiredTimeoutId = setTimeout(async () => { try { - const { lt } = await getDrizzleOperators(); + const { lt } = await loadDrizzleOperators(); await this.drizzleClient .delete(this.schema) .where(lt(this.schema.expire, new Date(Date.now() - EXPIRED_THRESHOLD_MS))); diff --git a/package.json b/package.json index dbd4c05..cca4f9d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "test:valkey-cluster": "VALKEY_CLUSTER_PORT=7001 mocha test/RateLimiterValkeyGlide.test.js -- -g 'RateLimiterValkeyGlide with cluster client'", "prisma:postgres": "prisma generate --schema=./test/RateLimiterPrisma/Postgres/schema.prisma && prisma db push --schema=./test/RateLimiterPrisma/Postgres/schema.prisma", "drizzle:postgres": "cd ./test/RateLimiterDrizzle/Postgres && drizzle-kit push", - "test": "npm run prisma:postgres && npm run drizzle:postgres && nyc --reporter=html --reporter=text mocha \"test/**/*.test.js\"", + "test:drizzle:postgres:v1": "npm i --no-save --no-package-lock drizzle-orm@beta && mocha test/RateLimiterDrizzle/Postgres/RateLimiterDrizzlePostgres.test.js && ( [ \"$GITHUB_ACTIONS\" = \"true\" ] || npm i )", + "test": "npm run prisma:postgres && npm run drizzle:postgres && nyc --reporter=html --reporter=text mocha \"test/**/*.test.js\" && npm run test:drizzle:postgres:v1", "debug-test": "mocha --inspect-brk lib/**/**.test.js", "coveralls": "cat ./coverage/lcov.info | coveralls", "eslint": "eslint --quiet lib/**/**.js test/**/**.js", From f2d3429f54a7fa83176ffc618276b37ab436295e Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 25 Apr 2026 19:06:23 +0200 Subject: [PATCH 2/3] #362 pin test drizzle-orm v1 to ^1.0.0-beta.23 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cca4f9d..1d9cdf9 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "test:valkey-cluster": "VALKEY_CLUSTER_PORT=7001 mocha test/RateLimiterValkeyGlide.test.js -- -g 'RateLimiterValkeyGlide with cluster client'", "prisma:postgres": "prisma generate --schema=./test/RateLimiterPrisma/Postgres/schema.prisma && prisma db push --schema=./test/RateLimiterPrisma/Postgres/schema.prisma", "drizzle:postgres": "cd ./test/RateLimiterDrizzle/Postgres && drizzle-kit push", - "test:drizzle:postgres:v1": "npm i --no-save --no-package-lock drizzle-orm@beta && mocha test/RateLimiterDrizzle/Postgres/RateLimiterDrizzlePostgres.test.js && ( [ \"$GITHUB_ACTIONS\" = \"true\" ] || npm i )", + "test:drizzle:postgres:v1": "npm i --no-save --no-package-lock drizzle-orm@^1.0.0-beta.23 && mocha test/RateLimiterDrizzle/Postgres/RateLimiterDrizzlePostgres.test.js && ( [ \"$GITHUB_ACTIONS\" = \"true\" ] || npm i )", "test": "npm run prisma:postgres && npm run drizzle:postgres && nyc --reporter=html --reporter=text mocha \"test/**/*.test.js\" && npm run test:drizzle:postgres:v1", "debug-test": "mocha --inspect-brk lib/**/**.test.js", "coveralls": "cat ./coverage/lcov.info | coveralls", From c3141d9af8767eff063c2e2b09c06efcb7a91385 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 2 May 2026 15:01:01 +0200 Subject: [PATCH 3/3] #362 return error with original message --- lib/RateLimiterDrizzle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/RateLimiterDrizzle.js b/lib/RateLimiterDrizzle.js index 1d46ec7..ea0e384 100644 --- a/lib/RateLimiterDrizzle.js +++ b/lib/RateLimiterDrizzle.js @@ -41,7 +41,7 @@ async function loadDrizzleOperators() { return null; } throw new RateLimiterDrizzleError( - 'drizzle-orm is not installed. Please install drizzle-orm to use RateLimiterDrizzle.' + error.message || 'Failed to import drizzle-orm package.' ); } };