Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 68 additions & 15 deletions lib/RateLimiterDrizzle.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
error.message || 'Failed to import drizzle-orm package.'
);
}
Comment thread
animir marked this conversation as resolved.
};

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');
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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()
Expand All @@ -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)
Expand All @@ -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)));
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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@^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",
"eslint": "eslint --quiet lib/**/**.js test/**/**.js",
Expand Down
Loading