@carecard/auth-util 3.18.0 → 3.20.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/.agents/skills/carecard-workspace-standards/SKILL.md +49 -20
- package/.agents/skills/github-pr-create-update/SKILL.md +22 -1
- package/.agents/skills/github-pr-merge-cleanup/SKILL.md +22 -1
- package/.agents/skills/pkg-auth-util-auth-crypto-library/SKILL.md +40 -15
- package/.agents/skills/pkg-auth-util-coding-standards-and-best-practices/SKILL.md +42 -7
- package/.agents/skills/pkg-publish/SKILL.md +22 -1
- package/.agents/skills/software-design-patterns-and-clean-code/SKILL.md +23 -3
- package/.prettierrc.js +4 -2
- package/AGENTS.md +19 -0
- package/eslint.config.mjs +52 -0
- package/index.d.ts +30 -6
- package/index.js +2 -1
- package/lib/jwtUtilAuth.js +59 -13
- package/lib/pwdUtilAuth.js +50 -21
- package/lib/stringUtilAuth.js +9 -3
- package/lint-staged.config.mjs +36 -0
- package/package.json +19 -13
- package/readme.md +22 -1
- package/scripts/canonicalTestCommand.test.mjs +33 -0
- package/scripts/packageTaskRunner.audit.mjs +37 -0
- package/scripts/packageTaskRunner.test.mjs +4 -26
- package/scripts/runPackageTask.mjs +63 -16
- package/scripts/testOrder/randomizeTestOrder.cjs +15 -5
- package/scripts/testOrder/{testOrderPolicy.test.mjs → testOrderPolicy.audit.mjs} +8 -2
- package/scripts/testParallel/{parallelTestPolicy.test.mjs → parallelTestPolicy.audit.mjs} +28 -9
- package/scripts/testParallel/runIndexedMochaTests.cjs +3 -1
- package/scripts/testParallel/runIndexedMochaTests.test.mjs +8 -2
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
const MAX_TEST_ORDER_SEED = 2_147_483_647;
|
|
4
4
|
|
|
5
5
|
function resolveTestOrderSeed(configuredSeed) {
|
|
6
|
-
if (configuredSeed === undefined)
|
|
7
|
-
|
|
6
|
+
if (configuredSeed === undefined) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
if (!/^[1-9]\d*$/.test(configuredSeed)) {
|
|
10
|
+
throw new Error('TEST_ORDER_SEED must be a positive 32-bit integer.');
|
|
11
|
+
}
|
|
8
12
|
const seed = Number(configuredSeed);
|
|
9
|
-
if (!Number.isSafeInteger(seed) || seed > MAX_TEST_ORDER_SEED)
|
|
13
|
+
if (!Number.isSafeInteger(seed) || seed > MAX_TEST_ORDER_SEED) {
|
|
14
|
+
throw new Error('TEST_ORDER_SEED must be a positive 32-bit integer.');
|
|
15
|
+
}
|
|
10
16
|
return seed;
|
|
11
17
|
}
|
|
12
18
|
function createSeededRandom(seed) {
|
|
@@ -25,14 +31,18 @@ function shuffleValues(values, random) {
|
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
function shuffleSuiteTree(suite, random) {
|
|
28
|
-
for (const childSuite of suite.suites)
|
|
34
|
+
for (const childSuite of suite.suites) {
|
|
35
|
+
shuffleSuiteTree(childSuite, random);
|
|
36
|
+
}
|
|
29
37
|
shuffleValues(suite.tests, random);
|
|
30
38
|
shuffleValues(suite.suites, random);
|
|
31
39
|
}
|
|
32
40
|
const mochaHooks = {
|
|
33
41
|
beforeAll() {
|
|
34
42
|
const seed = resolveTestOrderSeed(process.env.TEST_ORDER_SEED);
|
|
35
|
-
if (seed === undefined)
|
|
43
|
+
if (seed === undefined) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
36
46
|
console.log(`Test order seed: ${seed} (reproduce with TEST_ORDER_SEED=${seed})`);
|
|
37
47
|
shuffleSuiteTree(this.test.parent, createSeededRandom(seed));
|
|
38
48
|
},
|
|
@@ -7,7 +7,9 @@ const TEST_ORDER_INVARIANCE_RULE =
|
|
|
7
7
|
"Non-negotiable test order invariance rule: Every test must pass independently of which tests run before or after it, and the suite must pass in every execution order. Each test must establish the state it needs, isolate mutable state, and clean up state it owns; it must never rely on another test's setup, mutations, or cleanup. Default test, CI, and Husky commands must use the test framework's ordinary ordering and must not force randomized ordering. Random-order execution is an explicit diagnostic only, and every failure it exposes must be fixed at the root cause.";
|
|
8
8
|
|
|
9
9
|
function listRepositoryFiles() {
|
|
10
|
-
return execFileSync('git', ['ls-files', '--cached', '--others', '--exclude-standard'], {
|
|
10
|
+
return execFileSync('git', ['ls-files', '--cached', '--others', '--exclude-standard'], {
|
|
11
|
+
encoding: 'utf8',
|
|
12
|
+
})
|
|
11
13
|
.trim()
|
|
12
14
|
.split('\n')
|
|
13
15
|
.filter(Boolean);
|
|
@@ -43,6 +45,10 @@ test('keeps default package scripts on the test framework ordinary ordering', ()
|
|
|
43
45
|
|
|
44
46
|
for (const [scriptName, command] of Object.entries(packageJson.scripts ?? {})) {
|
|
45
47
|
assert.equal(typeof command, 'string', `${scriptName} must be a string command.`);
|
|
46
|
-
assert.doesNotMatch(
|
|
48
|
+
assert.doesNotMatch(
|
|
49
|
+
command,
|
|
50
|
+
/--test-randomize|--test-random-seed/,
|
|
51
|
+
`${scriptName} must not force randomized test ordering.`,
|
|
52
|
+
);
|
|
47
53
|
}
|
|
48
54
|
});
|
|
@@ -6,15 +6,22 @@ import test from 'node:test';
|
|
|
6
6
|
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
8
8
|
const repositoryRoot = resolve(import.meta.dirname, '../..');
|
|
9
|
-
const packageJson = JSON.parse(
|
|
10
|
-
|
|
9
|
+
const packageJson = JSON.parse(
|
|
10
|
+
readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),
|
|
11
|
+
);
|
|
12
|
+
const packageTaskRunnerSource = readFileSync(
|
|
13
|
+
new URL('../runPackageTask.mjs', import.meta.url),
|
|
14
|
+
'utf8',
|
|
15
|
+
);
|
|
11
16
|
const testIndexSource = readFileSync(new URL('../../test/index.test.js', import.meta.url), 'utf8');
|
|
12
17
|
const { parallelTestFiles } = require('../../test/index.test.js');
|
|
13
18
|
|
|
14
19
|
function listRuntimeTestFiles(directoryPath) {
|
|
15
20
|
return readdirSync(directoryPath, { withFileTypes: true }).flatMap(entry => {
|
|
16
21
|
const entryPath = join(directoryPath, entry.name);
|
|
17
|
-
if (entry.isDirectory())
|
|
22
|
+
if (entry.isDirectory()) {
|
|
23
|
+
return listRuntimeTestFiles(entryPath);
|
|
24
|
+
}
|
|
18
25
|
if (!/\.test\.(?:js|mjs)$/.test(entry.name) || entry.name === 'index.test.js') {
|
|
19
26
|
return [];
|
|
20
27
|
}
|
|
@@ -24,20 +31,32 @@ function listRuntimeTestFiles(directoryPath) {
|
|
|
24
31
|
|
|
25
32
|
test('keeps runtime test selection in the index and package scripts short', () => {
|
|
26
33
|
assert.equal(packageJson.scripts.test, 'node scripts/runPackageTask.mjs test');
|
|
27
|
-
assert.equal(
|
|
34
|
+
assert.equal(
|
|
35
|
+
packageJson.scripts['test:coverage'],
|
|
36
|
+
'node scripts/runPackageTask.mjs test:coverage',
|
|
37
|
+
);
|
|
28
38
|
assert.match(packageTaskRunnerSource, /arguments: \['run', 'test:order'\]/);
|
|
29
|
-
assert.match(packageTaskRunnerSource, /arguments: \['test\/index\.test\.js'\]/);
|
|
39
|
+
assert.match(packageTaskRunnerSource, /arguments: \['node', 'test\/index\.test\.js'\]/);
|
|
30
40
|
assert.match(packageTaskRunnerSource, /command: 'nyc'/);
|
|
31
41
|
assert.match(testIndexSource, /parallelTestFiles/);
|
|
32
42
|
assert.match(testIndexSource, /runIndexedMochaTests/);
|
|
33
43
|
assert.match(testIndexSource, /if \(require\.main === module\)/);
|
|
34
44
|
});
|
|
35
45
|
|
|
36
|
-
test('
|
|
37
|
-
assert.match(
|
|
38
|
-
|
|
46
|
+
test('keeps parallel behavior tests in the test-order gate and static policy checks in the audit gate', () => {
|
|
47
|
+
assert.match(
|
|
48
|
+
packageJson.scripts['test:order'],
|
|
49
|
+
/scripts\/testParallel\/runIndexedMochaTests\.test\.mjs/,
|
|
50
|
+
);
|
|
51
|
+
assert.equal(
|
|
52
|
+
packageJson.scripts['validate:audits'],
|
|
53
|
+
'node scripts/runPackageTask.mjs validate:audits',
|
|
54
|
+
);
|
|
39
55
|
});
|
|
40
56
|
|
|
41
57
|
test('selects every runtime test file exactly once', () => {
|
|
42
|
-
assert.deepEqual(
|
|
58
|
+
assert.deepEqual(
|
|
59
|
+
[...parallelTestFiles].sort(),
|
|
60
|
+
listRuntimeTestFiles(resolve(repositoryRoot, 'test')).sort(),
|
|
61
|
+
);
|
|
43
62
|
});
|
|
@@ -15,7 +15,9 @@ function resolveParallelJobCount(
|
|
|
15
15
|
availableJobCount = availableParallelism(),
|
|
16
16
|
) {
|
|
17
17
|
const requestedJobCount =
|
|
18
|
-
configuredJobCount === undefined
|
|
18
|
+
configuredJobCount === undefined
|
|
19
|
+
? Math.min(availableJobCount, defaultMaximum)
|
|
20
|
+
: Number.parseInt(configuredJobCount, 10);
|
|
19
21
|
|
|
20
22
|
if (!Number.isInteger(requestedJobCount) || requestedJobCount < 1) {
|
|
21
23
|
throw new Error('TEST_PARALLEL_JOBS must be a positive integer.');
|
|
@@ -12,10 +12,16 @@ test('uses bounded Mocha file workers without randomized default ordering', () =
|
|
|
12
12
|
const argumentsList = buildMochaArguments(['test/example.test.js'], 2);
|
|
13
13
|
|
|
14
14
|
assert.ok(argumentsList.includes('--parallel'));
|
|
15
|
-
assert.deepEqual(
|
|
15
|
+
assert.deepEqual(
|
|
16
|
+
argumentsList.slice(argumentsList.indexOf('--jobs'), argumentsList.indexOf('--jobs') + 2),
|
|
17
|
+
['--jobs', '2'],
|
|
18
|
+
);
|
|
16
19
|
assert.ok(argumentsList.includes('test/example.test.js'));
|
|
17
20
|
});
|
|
18
21
|
|
|
19
22
|
test('rejects invalid worker configuration instead of changing execution silently', () => {
|
|
20
|
-
assert.throws(
|
|
23
|
+
assert.throws(
|
|
24
|
+
() => resolveParallelJobCount('0', 8, 4, 12),
|
|
25
|
+
/TEST_PARALLEL_JOBS must be a positive integer/,
|
|
26
|
+
);
|
|
21
27
|
});
|