Skip to content
Merged
8 changes: 5 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
node-version: [10.x, 12.x, 14.x, 15.x]
node-version: [22.x, 23.x, 24.x]

steps:
- uses: actions/checkout@v2
Expand All @@ -28,7 +28,9 @@ jobs:
run: npm test

- name: Code Coverage on Node LTS version
if: ${{ matrix.node-version == '14.x' }}
uses: codecov/codecov-action@v1
if: ${{ matrix.node-version == '22.x' }}
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default async (): Promise<Config.InitialOptions> => {
testEnvironment: 'node',
coverageDirectory: './coverage/',
collectCoverage: true,
testPathIgnorePatterns: ['<rootDir>/src/Aclatraz.performance.test.ts'],
coverageThreshold: {
global: {
branches: 100,
Expand Down
21,430 changes: 10,297 additions & 11,133 deletions package-lock.json

Large diffs are not rendered by default.

31 changes: 17 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "0.1.4",
"description": "Simple dependency-free package for ACL. It can handle hundreds of roles easily.",
"main": "lib/index.js",
"engines": {
"node": ">=20.0.0"
},
"scripts": {
"build": "rimraf ./lib && tsc",
"test": "jest",
Expand Down Expand Up @@ -35,18 +38,18 @@
},
"homepage": "https://github.com/PoOwAa/aclatraz#readme",
"devDependencies": {
"@types/jest": "^26.0.20",
"@types/node": "^14.14.29",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"eslint": "^7.20.0",
"eslint-config-prettier": "^7.2.0",
"eslint-plugin-jest": "^24.1.5",
"jest": "^26.6.3",
"np": "^7.4.0",
"rimraf": "^3.0.2",
"ts-jest": "^26.5.1",
"ts-node": "^9.1.1",
"typescript": "^4.1.5"
"@types/jest": "^29.5.11",
"@types/node": "^20.11.30",
"@typescript-eslint/eslint-plugin": "^7.7.0",
"@typescript-eslint/parser": "^7.7.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^27.9.0",
"jest": "^29.7.0",
"np": "^8.0.4",
"rimraf": "^5.0.5",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
Comment on lines +41 to +53
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify npm compatibility with major dependency upgrades.

The significant version bumps across all development dependencies are necessary to support ES2020 features like BigInt, but may require adjustments to CI/CD workflows or developer environments.


🏁 Script executed:

#!/bin/bash
# Check compatibility with current Node.js LTS versions
echo "Checking Node.js compatibility information"
grep -A 5 "engines" package.json || echo "No engines field specified in package.json"

# Check if any dependencies have compatibility warnings
npm ls --depth=0 || echo "Some dependencies might have compatibility issues"

Length of output: 315


🏁 Script executed:

#!/bin/bash
# Locate the package.json in the repository
fd --max-depth 2 package.json || find . -maxdepth 2 -name package.json

# Inspect for an "engines" field to lock Node.js versions
grep -R '"engines"' -n package.json || echo "No engines field in package.json"

# Check GitHub Actions workflows for Node version pins
grep -R "node-version" -n .github/workflows/ || echo "No node-version specification in workflows"

Length of output: 522


Add a Node.js engines field and update CI Node versions

To ensure developers and CI use a compatible Node.js version for these upgraded dev dependencies:

• In package.json, add an "engines" section (e.g. "node": ">=16") to lock the minimum Node.js version.
• In .github/workflows/test.yml, bump the node-version matrix to current LTS releases (e.g. 16.x, 18.x, 20.x), replacing the old 10.x, 12.x, 14.x, 15.x entries.
• Verify local environments are running Node.js ≥16 before installing.

These changes will align your CI/CD and developer machines with the requirements of TypeScript 5.4+, ESLint 8.x, and other modern tooling.

}
}
}
144 changes: 144 additions & 0 deletions src/Aclatraz.performance.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { performance } from 'perf_hooks';
import { Aclatraz } from './Aclatraz';

describe('Aclatraz performance test', () => {
test('generate permission code for 10,000 rules', () => {
const rules = [];
for (let i = 1; i <= 10000; i++) {
rules.push({ id: i, slug: `rule${i}` });
}
const acl = new Aclatraz(rules);

const ruleIds = Array.from({ length: 10000 }, (_, i) => i + 1);

const start = performance.now();
const code = acl.generateAclCode(ruleIds);
const end = performance.now();

console.log(`Generated code length: ${code.length}`);
console.log(`Time taken: ${(end - start).toFixed(2)} ms`);

expect(typeof code).toBe('string');
});

test('grantPermission 100,000 times', () => {
const rules = [];
for (let i = 1; i <= 100; i++) {
rules.push({ id: i, slug: `rule${i}` });
}
const acl = new Aclatraz(rules);
const ruleIds = Array.from({ length: 100 }, (_, i) => i + 1);

const start = performance.now();
for (let i = 0; i < 100000; i++) {
acl.grantPermission('', ruleIds);
}
const end = performance.now();
console.log(
`grantPermission 100,000 times took: ${(end - start).toFixed(2)} ms`
);
expect(true).toBe(true);
});

test('verify (decode) 100,000 times', () => {
const rules = [];
for (let i = 1; i <= 100; i++) {
rules.push({ id: i, slug: `rule${i}` });
}
const acl = new Aclatraz(rules);
const ruleIds = Array.from({ length: 100 }, (_, i) => i + 1);
const permission = acl.grantPermission('', ruleIds);

const start = performance.now();
for (let i = 0; i < 100000; i++) {
acl.verify(permission, 50); // Arbitrary ruleId in the middle
}
const end = performance.now();
console.log(
`verify (decode) 100,000 times took: ${(end - start).toFixed(2)} ms`
);
expect(true).toBe(true);
});

test('revokePermission 100,000 times', () => {
const rules = [];
for (let i = 1; i <= 100; i++) {
rules.push({ id: i, slug: `rule${i}` });
}
const acl = new Aclatraz(rules);
const ruleIds = Array.from({ length: 100 }, (_, i) => i + 1);
const permission = acl.grantPermission('', ruleIds);

const start = performance.now();
for (let i = 0; i < 100000; i++) {
acl.revokePermission(permission, [50]); // Arbitrary ruleId
}
const end = performance.now();
console.log(
`revokePermission 100,000 times took: ${(end - start).toFixed(2)} ms`
);
expect(true).toBe(true);
});

test('grantPermission with 100,000 rules', () => {
const rules = [];
for (let i = 1; i <= 100000; i++) {
rules.push({ id: i, slug: `rule${i}` });
}
const acl = new Aclatraz(rules);
const ruleIds = Array.from({ length: 100000 }, (_, i) => i + 1);

const start = performance.now();
const permission = acl.grantPermission('', ruleIds);
const end = performance.now();
console.log(
`grantPermission with 100,000 rules took: ${(end - start).toFixed(
2
)} ms, token length: ${permission.length}`
);
expect(typeof permission).toBe('string');
});

test('verify (decode) with 100,000 rules', () => {
const rules = [];
for (let i = 1; i <= 100000; i++) {
rules.push({ id: i, slug: `rule${i}` });
}
const acl = new Aclatraz(rules);
const ruleIds = Array.from({ length: 100000 }, (_, i) => i + 1);
const permission = acl.grantPermission('', ruleIds);

const start = performance.now();
let count = 0;
for (let i = 1; i <= 100000; i += 10000) {
if (acl.verify(permission, i)) count++;
}
const end = performance.now();
console.log(
`verify (decode) with 100,000 rules (checked every 10,000th): ${(
end - start
).toFixed(2)} ms, found: ${count}`
);
expect(count).toBe(10);
});

test('revokePermission with 100,000 rules', () => {
const rules = [];
for (let i = 1; i <= 100000; i++) {
rules.push({ id: i, slug: `rule${i}` });
}
const acl = new Aclatraz(rules);
const ruleIds = Array.from({ length: 100000 }, (_, i) => i + 1);
const permission = acl.grantPermission('', ruleIds);

const start = performance.now();
const revoked = acl.revokePermission(permission, [50000, 99999, 100000]);
const end = performance.now();
console.log(
`revokePermission with 100,000 rules took: ${(end - start).toFixed(
2
)} ms, token length: ${revoked.length}`
);
expect(typeof revoked).toBe('string');
});
});
Loading