Merge setup/test-infrastructure: vitest config, shared fixtures, 30 tests passing
This commit is contained in:
863
package-lock.json
generated
863
package-lock.json
generated
@@ -19,6 +19,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"typescript": "^5.7.0",
|
||||
"vitest": "^3.1.0"
|
||||
},
|
||||
@@ -32,6 +33,80 @@
|
||||
"integrity": "sha512-hMidpI6GlMgQMlW9KEd8I3ywgewV6mva9iJaDuBfGtgeRAGrB8yyu6T/fHmgmyQineZ8l4/1PdH/VNr3S2er2g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
|
||||
"integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.29.0"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.28.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@bcoe/v8-coverage": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",
|
||||
"integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.7",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
|
||||
@@ -474,6 +549,55 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
"strip-ansi": "^7.0.1",
|
||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/schema": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz",
|
||||
"integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
@@ -481,6 +605,28 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.31",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.60.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz",
|
||||
@@ -905,6 +1051,40 @@
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz",
|
||||
"integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.3.0",
|
||||
"@bcoe/v8-coverage": "^1.0.2",
|
||||
"ast-v8-to-istanbul": "^0.3.3",
|
||||
"debug": "^4.4.1",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"istanbul-lib-report": "^3.0.1",
|
||||
"istanbul-lib-source-maps": "^5.0.6",
|
||||
"istanbul-reports": "^3.1.7",
|
||||
"magic-string": "^0.30.17",
|
||||
"magicast": "^0.3.5",
|
||||
"std-env": "^3.9.0",
|
||||
"test-exclude": "^7.0.1",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "3.2.4",
|
||||
"vitest": "3.2.4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
|
||||
@@ -1026,6 +1206,32 @@
|
||||
"integrity": "sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||
@@ -1036,6 +1242,48 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/ast-v8-to-istanbul": {
|
||||
"version": "0.3.12",
|
||||
"resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz",
|
||||
"integrity": "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.31",
|
||||
"estree-walker": "^3.0.3",
|
||||
"js-tokens": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ast-v8-to-istanbul/node_modules/js-tokens": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz",
|
||||
"integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/cac": {
|
||||
"version": "6.7.14",
|
||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||
@@ -1073,6 +1321,41 @@
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
@@ -1101,6 +1384,20 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||
@@ -1197,6 +1494,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.6",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -1212,6 +1526,61 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^3.1.2",
|
||||
"minimatch": "^9.0.4",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^1.11.1"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/graphology": {
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/graphology/-/graphology-0.26.0.tgz",
|
||||
@@ -1322,6 +1691,110 @@
|
||||
"graphology-types": ">=0.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/istanbul-lib-coverage": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
|
||||
"integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/istanbul-lib-report": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
|
||||
"integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"istanbul-lib-coverage": "^3.0.0",
|
||||
"make-dir": "^4.0.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/istanbul-lib-source-maps": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
|
||||
"integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.23",
|
||||
"debug": "^4.1.1",
|
||||
"istanbul-lib-coverage": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/istanbul-reports": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
|
||||
"integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"html-escaper": "^2.0.0",
|
||||
"istanbul-lib-report": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
|
||||
@@ -1336,6 +1809,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -1346,6 +1826,60 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/magicast": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz",
|
||||
"integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.25.4",
|
||||
"@babel/types": "^7.25.4",
|
||||
"source-map-js": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
|
||||
"integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
||||
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/mnemonist": {
|
||||
"version": "0.39.8",
|
||||
"resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz",
|
||||
@@ -1387,6 +1921,13 @@
|
||||
"integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/package-json-from-dist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/pandemonium": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/pandemonium/-/pandemonium-2.4.1.tgz",
|
||||
@@ -1396,6 +1937,33 @@
|
||||
"mnemonist": "^0.39.2"
|
||||
}
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^10.2.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
@@ -1507,6 +2075,42 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/siginfo": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
|
||||
@@ -1514,6 +2118,19 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@@ -1538,6 +2155,110 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs": {
|
||||
"name": "string-width",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
|
||||
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs": {
|
||||
"name": "strip-ansi",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-literal": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz",
|
||||
@@ -1551,6 +2272,34 @@
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/test-exclude": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz",
|
||||
"integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@istanbuljs/schema": "^0.1.2",
|
||||
"glob": "^10.4.1",
|
||||
"minimatch": "^10.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
||||
@@ -1804,6 +2553,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/why-is-node-running": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
|
||||
@@ -1821,6 +2586,104 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs": {
|
||||
"name": "wrap-ansi",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
||||
|
||||
12
package.json
12
package.json
@@ -25,6 +25,7 @@
|
||||
"build": "tsc",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"lint": "tsc --noEmit",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
@@ -37,20 +38,21 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alkdev/typebox": "^0.34.49",
|
||||
"graphology": "^0.26.0",
|
||||
"graphology-components": "^1.5.4",
|
||||
"graphology-dag": "^0.4.1",
|
||||
"graphology-metrics": "^2.4.0",
|
||||
"graphology-components": "^1.5.4",
|
||||
"graphology-operators": "^1.6.1",
|
||||
"@alkdev/typebox": "^0.34.49",
|
||||
"yaml": "^2.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"typescript": "^5.7.0",
|
||||
"vitest": "^3.1.0",
|
||||
"@types/node": "^22.0.0"
|
||||
"vitest": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: setup/test-infrastructure
|
||||
name: Configure test runner and shared test fixtures
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on:
|
||||
- setup/project-init
|
||||
scope: narrow
|
||||
@@ -16,17 +16,17 @@ Set up the test infrastructure: configure Vitest (or chosen runner), create shar
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Test runner configured in `package.json` scripts (`"test"`, `"test:watch"`, `"test:coverage"`)
|
||||
- [ ] Vitest config (or equivalent) exists with ESM support and TypeScript path resolution
|
||||
- [ ] Shared test fixture file created (e.g., `test/fixtures/graphs.ts`) with:
|
||||
- [x] Test runner configured in `package.json` scripts (`"test"`, `"test:watch"`, `"test:coverage"`)
|
||||
- [x] Vitest config (or equivalent) exists with ESM support and TypeScript path resolution
|
||||
- [x] Shared test fixture file created (e.g., `test/fixtures/graphs.ts`) with:
|
||||
- A simple linear chain graph (3-4 tasks, A→B→C→D)
|
||||
- A diamond dependency graph (A→B, A→C, B→D, C→D)
|
||||
- A graph with mixed categorical fields (some assessed, some null)
|
||||
- A graph with cycles for testing cycle detection
|
||||
- A larger graph (20+ nodes) for performance/bottleneck testing
|
||||
- [ ] Helper function to create a `TaskGraph` from `TaskInput[]` for one-liner test setup
|
||||
- [ ] Test runner executes successfully against placeholder test files
|
||||
- [ ] CI-compatible output format (no watch mode in default script)
|
||||
- [x] Helper function to create a `TaskGraph` from `TaskInput[]` for one-liner test setup
|
||||
- [x] Test runner executes successfully against placeholder test files
|
||||
- [x] CI-compatible output format (no watch mode in default script)
|
||||
|
||||
## References
|
||||
|
||||
@@ -35,8 +35,17 @@ Set up the test infrastructure: configure Vitest (or chosen runner), create shar
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
- Added `@vitest/coverage-v8@^3.2.4` as a dev dependency to support `npm run test:coverage`
|
||||
- `createTaskGraph()` builds a graphology `DirectedGraph` directly using `graph.import()` (bulk construction per architecture recommendation) with deterministic edge keys (`source->target`) and default `qualityRetention: 0.9`
|
||||
- The `@/` path alias resolves to `src/` for convenient test imports of source modules
|
||||
- Cycle detection uses `hasCycle()` from `graphology-dag` (standalone function, not a method on the graph instance)
|
||||
- All fixture types (`TaskInput`, `TaskGraphNodeAttributes`, etc.) are defined inline until the schema module is implemented
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Configured Vitest test infrastructure and created shared test fixtures for graph construction.
|
||||
- Modified: package.json (added `test:coverage` script, `@vitest/coverage-v8` dev dependency)
|
||||
- Modified: vitest.config.ts (added `@/` path alias, `include` pattern, coverage config with v8 provider)
|
||||
- Modified: test/graph.test.ts (expanded with 26 fixture validation tests)
|
||||
- Created: test/fixtures/graphs.ts (shared fixtures: linearChain, diamond, mixedCategory, cyclic, largeGraph + createTaskGraph helper + allGraphs/allTasks convenience exports)
|
||||
- Tests: 30, all passing (5 placeholder + 25 fixture validation + 1 retained placeholder)
|
||||
313
test/fixtures/graphs.ts
vendored
Normal file
313
test/fixtures/graphs.ts
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* Shared test fixtures for graph construction.
|
||||
*
|
||||
* Provides:
|
||||
* - TaskInput type matching the architecture spec (inline until schema module is implemented)
|
||||
* - createTaskGraph() helper for one-liner graph setup from TaskInput[]
|
||||
* - Pre-built graph fixtures for common test patterns
|
||||
*
|
||||
* When the schema and graph modules are implemented, these fixtures will work
|
||||
* with TaskGraph.fromTasks() directly. Until then, createTaskGraph() builds
|
||||
* a graphology DirectedGraph with the same semantics.
|
||||
*/
|
||||
|
||||
import Graph from 'graphology';
|
||||
import { hasCycle } from 'graphology-dag';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types — mirrors src/schema/task.ts and src/schema/graph.ts architecture
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Categorical enum values matching DB and frontmatter conventions */
|
||||
export type TaskScope = 'single' | 'narrow' | 'moderate' | 'broad' | 'system';
|
||||
export type TaskRisk = 'trivial' | 'low' | 'medium' | 'high' | 'critical';
|
||||
export type TaskImpact = 'isolated' | 'component' | 'phase' | 'project';
|
||||
export type TaskLevel = 'planning' | 'decomposition' | 'implementation' | 'review' | 'research';
|
||||
export type TaskPriority = 'low' | 'medium' | 'high' | 'critical';
|
||||
export type TaskStatus = 'pending' | 'in-progress' | 'completed' | 'failed' | 'blocked';
|
||||
|
||||
/**
|
||||
* Universal input shape for a task.
|
||||
* Mirrors the TaskInput schema from docs/architecture/schemas.md.
|
||||
* Categorical fields are optional and nullable (null = "not yet assessed").
|
||||
*/
|
||||
export interface TaskInput {
|
||||
id: string;
|
||||
name: string;
|
||||
dependsOn: string[];
|
||||
status?: TaskStatus | null;
|
||||
scope?: TaskScope | null;
|
||||
risk?: TaskRisk | null;
|
||||
impact?: TaskImpact | null;
|
||||
level?: TaskLevel | null;
|
||||
priority?: TaskPriority | null;
|
||||
tags?: string[];
|
||||
assignee?: string | null;
|
||||
due?: string | null;
|
||||
created?: string | null;
|
||||
modified?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Node attributes stored on the graphology graph.
|
||||
* Mirrors TaskGraphNodeAttributes from docs/architecture/schemas.md.
|
||||
* After construction, null values are stripped to undefined (absent = "not assessed").
|
||||
*/
|
||||
export interface TaskGraphNodeAttributes {
|
||||
name: string;
|
||||
scope?: TaskScope;
|
||||
risk?: TaskRisk;
|
||||
impact?: TaskImpact;
|
||||
level?: TaskLevel;
|
||||
priority?: TaskPriority;
|
||||
status?: TaskStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edge attributes on graph edges.
|
||||
* qualityRetention defaults to 0.9 if not specified.
|
||||
*/
|
||||
export interface TaskGraphEdgeAttributes {
|
||||
qualityRetention?: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: strip null → undefined for node attributes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function stripNulls(input: TaskInput): TaskGraphNodeAttributes {
|
||||
const attrs: TaskGraphNodeAttributes = { name: input.name };
|
||||
if (input.scope != null) attrs.scope = input.scope;
|
||||
if (input.risk != null) attrs.risk = input.risk;
|
||||
if (input.impact != null) attrs.impact = input.impact;
|
||||
if (input.level != null) attrs.level = input.level;
|
||||
if (input.priority != null) attrs.priority = input.priority;
|
||||
if (input.status != null) attrs.status = input.status;
|
||||
return attrs;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: create a graphology DirectedGraph from TaskInput[]
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Build a graphology DirectedGraph from an array of TaskInputobjects.
|
||||
*
|
||||
* Uses graph.import(serializedData) for bulk construction (faster than N
|
||||
* individual addNode/addEdge calls per architecture recommendation).
|
||||
*
|
||||
* Edges use deterministic `${source}->${target}` keys per ADR-006.
|
||||
* Edge qualityRetention defaults to 0.9 (matching fromTasks convention).
|
||||
*
|
||||
* @param tasks - Array of TaskInput objects
|
||||
* @returns A graphology DirectedGraph with nodes and edges populated
|
||||
*/
|
||||
export function createTaskGraph(tasks: TaskInput[]): Graph<TaskGraphNodeAttributes, TaskGraphEdgeAttributes> {
|
||||
const graph = new Graph<TaskGraphNodeAttributes, TaskGraphEdgeAttributes>({ type: 'directed' });
|
||||
|
||||
// Build node map for lookups
|
||||
const taskMap = new Map<string, TaskInput>();
|
||||
for (const task of tasks) {
|
||||
taskMap.set(task.id, task);
|
||||
}
|
||||
|
||||
// Build serialized format for bulk import
|
||||
const nodes: Array<{ key: string; attributes: TaskGraphNodeAttributes }> = [];
|
||||
const edges: Array<{ key: string; source: string; target: string; attributes: TaskGraphEdgeAttributes }> = [];
|
||||
|
||||
// Edge set to prevent duplicates
|
||||
const edgeSet = new Set<string>();
|
||||
|
||||
for (const task of tasks) {
|
||||
nodes.push({ key: task.id, attributes: stripNulls(task) });
|
||||
|
||||
for (const dep of task.dependsOn) {
|
||||
const edgeKey = `${dep}->${task.id}`;
|
||||
if (!edgeSet.has(edgeKey)) {
|
||||
edgeSet.add(edgeKey);
|
||||
edges.push({
|
||||
key: edgeKey,
|
||||
source: dep,
|
||||
target: task.id,
|
||||
attributes: { qualityRetention: 0.9 },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Import in bulk
|
||||
graph.import({ nodes, edges });
|
||||
|
||||
// Handle dangling references: fromTasks silently creates orphan nodes
|
||||
// for dependsOn targets not in the tasks array (per architecture spec).
|
||||
// We need to add those nodes now.
|
||||
for (const task of tasks) {
|
||||
for (const dep of task.dependsOn) {
|
||||
if (!graph.hasNode(dep)) {
|
||||
graph.addNode(dep, { name: dep });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fixture Graphs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Simple linear chain graph: A → B → C → D
|
||||
*
|
||||
* All tasks have medium risk, narrow scope, isolated impact.
|
||||
* Useful for testing topological ordering, basic path traversal.
|
||||
*/
|
||||
export const linearChainTasks: TaskInput[] = [
|
||||
{ id: 'A', name: 'Task A', dependsOn: [] },
|
||||
{ id: 'B', name: 'Task B', dependsOn: ['A'] },
|
||||
{ id: 'C', name: 'Task C', dependsOn: ['B'] },
|
||||
{ id: 'D', name: 'Task D', dependsOn: ['C'] },
|
||||
];
|
||||
|
||||
/** Pre-built linear chain graph */
|
||||
export const linearChain = createTaskGraph(linearChainTasks);
|
||||
|
||||
/**
|
||||
* Diamond dependency graph:
|
||||
* A
|
||||
* / \
|
||||
* B C
|
||||
* \ /
|
||||
* D
|
||||
*
|
||||
* A → B, A → C, B → D, C → D
|
||||
* Useful for testing parallel groups, bottleneck detection, merge points.
|
||||
*/
|
||||
export const diamondTasks: TaskInput[] = [
|
||||
{ id: 'A', name: 'Task A', dependsOn: [] },
|
||||
{ id: 'B', name: 'Task B', dependsOn: ['A'] },
|
||||
{ id: 'C', name: 'Task C', dependsOn: ['A'] },
|
||||
{ id: 'D', name: 'Task D', dependsOn: ['B', 'C'] },
|
||||
];
|
||||
|
||||
/** Pre-built diamond graph */
|
||||
export const diamond = createTaskGraph(diamondTasks);
|
||||
|
||||
/**
|
||||
* Mixed categorical fields graph:
|
||||
* Some tasks have assessed fields, some have null (not assessed).
|
||||
* Useful for testing resolveDefaults, riskDistribution, and other
|
||||
* analysis functions that handle nullable categorical fields.
|
||||
*/
|
||||
export const mixedCategoryTasks: TaskInput[] = [
|
||||
{ id: 'auth', name: 'Auth module', dependsOn: [], risk: 'high', scope: 'broad', impact: 'phase', status: 'pending' },
|
||||
{ id: 'db', name: 'Database setup', dependsOn: [], risk: 'medium', scope: 'moderate', impact: null, status: 'completed' },
|
||||
{ id: 'api', name: 'API layer', dependsOn: ['auth', 'db'], risk: null, scope: null, impact: 'component', status: null },
|
||||
{ id: 'tests', name: 'Test suite', dependsOn: ['api'], risk: 'low', scope: null, impact: null, status: null },
|
||||
{ id: 'deploy', name: 'Deploy pipeline', dependsOn: ['tests'], risk: 'critical', scope: 'system', impact: 'project', status: 'blocked' },
|
||||
];
|
||||
|
||||
/** Pre-built mixed category graph */
|
||||
export const mixedCategory = createTaskGraph(mixedCategoryTasks);
|
||||
|
||||
/**
|
||||
* Graph with cycles for testing cycle detection:
|
||||
* A → B → C → A (cycle)
|
||||
* A → D (non-cyclic branch)
|
||||
*
|
||||
* Useful for testing hasCycles(), findCycles(), and that
|
||||
* topologicalOrder() throws CircularDependencyError.
|
||||
*/
|
||||
export const cyclicTasks: TaskInput[] = [
|
||||
{ id: 'A', name: 'Task A', dependsOn: ['C'] }, // A depends on C (creates cycle)
|
||||
{ id: 'B', name: 'Task B', dependsOn: ['A'] }, // B depends on A
|
||||
{ id: 'C', name: 'Task C', dependsOn: ['B'] }, // C depends on B
|
||||
{ id: 'D', name: 'Task D', dependsOn: ['A'] }, // D depends on A (non-cyclic)
|
||||
];
|
||||
|
||||
/** Pre-built cyclic graph */
|
||||
export const cyclic = createTaskGraph(cyclicTasks);
|
||||
|
||||
/**
|
||||
* Larger graph (20+ nodes) for performance and bottleneck testing.
|
||||
* Represents a realistic project structure with multiple parallel
|
||||
* workstreams converging.
|
||||
*
|
||||
* Structure:
|
||||
* - 3 foundation tasks (no deps)
|
||||
* - 3 core services (depend on foundation)
|
||||
* - 5 feature tasks (depend on core services)
|
||||
* - 3 integration tasks (merge features)
|
||||
* - 2 testing tasks
|
||||
* - 5 polish tasks
|
||||
* - 1 release task (depends on everything)
|
||||
*/
|
||||
export const largeGraphTasks: TaskInput[] = [
|
||||
// Foundation (3 tasks)
|
||||
{ id: 'infra-setup', name: 'Infrastructure setup', dependsOn: [], scope: 'broad', risk: 'high', impact: 'project', level: 'implementation' },
|
||||
{ id: 'db-schema', name: 'Database schema design', dependsOn: [], scope: 'moderate', risk: 'medium', impact: 'phase', level: 'planning' },
|
||||
{ id: 'auth-design', name: 'Auth system design', dependsOn: [], scope: 'moderate', risk: 'high', impact: 'component', level: 'planning' },
|
||||
|
||||
// Core services (3 tasks)
|
||||
{ id: 'auth-impl', name: 'Auth implementation', dependsOn: ['infra-setup', 'auth-design'], scope: 'broad', risk: 'high', impact: 'phase', level: 'implementation' },
|
||||
{ id: 'data-layer', name: 'Data access layer', dependsOn: ['db-schema', 'infra-setup'], scope: 'moderate', risk: 'medium', impact: 'component', level: 'implementation' },
|
||||
{ id: 'api-gateway', name: 'API gateway', dependsOn: ['auth-impl', 'data-layer'], scope: 'broad', risk: 'medium', impact: 'phase', level: 'implementation' },
|
||||
|
||||
// Feature tasks (4 tasks)
|
||||
{ id: 'feature-users', name: 'User management', dependsOn: ['auth-impl', 'data-layer'], scope: 'narrow', risk: 'low', impact: 'component', level: 'implementation' },
|
||||
{ id: 'feature-notifications', name: 'Notification system', dependsOn: ['api-gateway', 'data-layer'], scope: 'narrow', risk: 'low', impact: 'isolated', level: 'implementation' },
|
||||
{ id: 'feature-search', name: 'Search functionality', dependsOn: ['data-layer'], scope: 'moderate', risk: 'medium', impact: 'component', level: 'implementation' },
|
||||
{ id: 'feature-permissions', name: 'Permissions system', dependsOn: ['auth-impl'], scope: 'moderate', risk: 'high', impact: 'phase', level: 'implementation' },
|
||||
{ id: 'feature-analytics', name: 'Analytics dashboard', dependsOn: ['data-layer', 'api-gateway'], scope: 'moderate', risk: 'medium', impact: 'component', level: 'implementation' },
|
||||
|
||||
// Integration tasks (3 tasks)
|
||||
{ id: 'integrate-auth', name: 'Auth integration test', dependsOn: ['feature-users', 'feature-permissions'], scope: 'narrow', risk: 'medium', impact: 'component', level: 'review' },
|
||||
{ id: 'integrate-api', name: 'API integration test', dependsOn: ['feature-notifications', 'feature-search', 'api-gateway'], scope: 'moderate', risk: 'medium', impact: 'phase', level: 'review' },
|
||||
{ id: 'integrate-e2e', name: 'End-to-end integration', dependsOn: ['integrate-auth', 'integrate-api'], scope: 'broad', risk: 'high', impact: 'project', level: 'review' },
|
||||
|
||||
// Testing tasks (2 tasks)
|
||||
{ id: 'perf-tests', name: 'Performance testing', dependsOn: ['integrate-e2e'], scope: 'moderate', risk: 'medium', impact: 'component', level: 'review' },
|
||||
{ id: 'security-audit', name: 'Security audit', dependsOn: ['auth-impl', 'integrate-auth'], scope: 'broad', risk: 'critical', impact: 'project', level: 'review' },
|
||||
|
||||
// Polish tasks (3 tasks)
|
||||
{ id: 'docs-api', name: 'API documentation', dependsOn: ['api-gateway'], scope: 'moderate', risk: 'trivial', impact: 'isolated', level: 'implementation' },
|
||||
{ id: 'docs-user', name: 'User documentation', dependsOn: ['feature-users'], scope: 'narrow', risk: 'trivial', impact: 'isolated', level: 'implementation' },
|
||||
{ id: 'i18n', name: 'Internationalization', dependsOn: ['feature-users', 'feature-notifications'], scope: 'moderate', risk: 'low', impact: 'component', level: 'implementation' },
|
||||
{ id: 'accessibility', name: 'Accessibility compliance', dependsOn: ['feature-users', 'feature-analytics'], scope: 'moderate', risk: 'low', impact: 'component', level: 'implementation' },
|
||||
{ id: 'error-handling', name: 'Error handling polish', dependsOn: ['api-gateway', 'data-layer'], scope: 'narrow', risk: 'low', impact: 'isolated', level: 'implementation' },
|
||||
{ id: 'config-system', name: 'Configuration system', dependsOn: ['data-layer'], scope: 'narrow', risk: 'low', impact: 'isolated', level: 'implementation' },
|
||||
|
||||
// Release (1 task)
|
||||
{ id: 'release', name: 'Production release', dependsOn: ['perf-tests', 'security-audit', 'docs-api', 'docs-user', 'i18n', 'accessibility', 'error-handling', 'config-system'], scope: 'system', risk: 'critical', impact: 'project', level: 'implementation' },
|
||||
];
|
||||
|
||||
/** Pre-built large graph (23 nodes) */
|
||||
export const largeGraph = createTaskGraph(largeGraphTasks);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Re-exports for convenience
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* All fixture graphs as a record for iteration in tests.
|
||||
* Keys: 'linearChain', 'diamond', 'mixedCategory', 'cyclic', 'large'
|
||||
*/
|
||||
export const allGraphs: Record<string, Graph<TaskGraphNodeAttributes, TaskGraphEdgeAttributes>> = {
|
||||
linearChain,
|
||||
diamond,
|
||||
mixedCategory,
|
||||
cyclic,
|
||||
large: largeGraph,
|
||||
};
|
||||
|
||||
/**
|
||||
* All fixture TaskInput arrays as a record for iteration in tests.
|
||||
* Keys: 'linearChain', 'diamond', 'mixedCategory', 'cyclic', 'large'
|
||||
*/
|
||||
export const allTasks: Record<string, TaskInput[]> = {
|
||||
linearChain: linearChainTasks,
|
||||
diamond: diamondTasks,
|
||||
mixedCategory: mixedCategoryTasks,
|
||||
cyclic: cyclicTasks,
|
||||
large: largeGraphTasks,
|
||||
};
|
||||
@@ -1,7 +1,171 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { hasCycle } from 'graphology-dag';
|
||||
import {
|
||||
createTaskGraph,
|
||||
linearChainTasks,
|
||||
linearChain,
|
||||
diamondTasks,
|
||||
diamond,
|
||||
mixedCategoryTasks,
|
||||
mixedCategory,
|
||||
cyclicTasks,
|
||||
cyclic,
|
||||
largeGraphTasks,
|
||||
largeGraph,
|
||||
allGraphs,
|
||||
allTasks,
|
||||
} from './fixtures/graphs.js';
|
||||
|
||||
describe('TaskGraph', () => {
|
||||
it('placeholder — construction and queries', () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test Fixtures', () => {
|
||||
describe('linearChain', () => {
|
||||
it('has 4 nodes', () => {
|
||||
expect(linearChain.order).toBe(4);
|
||||
});
|
||||
|
||||
it('has 3 edges (A→B, B→C, C→D)', () => {
|
||||
expect(linearChain.size).toBe(3);
|
||||
});
|
||||
|
||||
it('has correct task IDs', () => {
|
||||
expect(linearChainTasks.map(t => t.id)).toEqual(['A', 'B', 'C', 'D']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('diamond', () => {
|
||||
it('has 4 nodes', () => {
|
||||
expect(diamond.order).toBe(4);
|
||||
});
|
||||
|
||||
it('has 4 edges (A→B, A→C, B→D, C→D)', () => {
|
||||
expect(diamond.size).toBe(4);
|
||||
});
|
||||
|
||||
it('A has two dependents (B, C)', () => {
|
||||
expect(diamond.outNeighbors('A')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('D has two prerequisites (B, C)', () => {
|
||||
expect(diamond.inNeighbors('D')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mixedCategory', () => {
|
||||
it('has 5 nodes', () => {
|
||||
expect(mixedCategory.order).toBe(5);
|
||||
});
|
||||
|
||||
it('stores assessed categorical fields', () => {
|
||||
const authAttrs = mixedCategory.getNodeAttributes('auth');
|
||||
expect(authAttrs.risk).toBe('high');
|
||||
expect(authAttrs.scope).toBe('broad');
|
||||
});
|
||||
|
||||
it('strips null categorical fields (absent = not assessed)', () => {
|
||||
const apiAttrs = mixedCategory.getNodeAttributes('api');
|
||||
expect(apiAttrs.risk).toBeUndefined();
|
||||
expect(apiAttrs.scope).toBeUndefined();
|
||||
});
|
||||
|
||||
it('preserves non-null optional fields', () => {
|
||||
const apiAttrs = mixedCategory.getNodeAttributes('api');
|
||||
expect(apiAttrs.impact).toBe('component');
|
||||
});
|
||||
});
|
||||
|
||||
describe('cyclic', () => {
|
||||
it('has 4 nodes', () => {
|
||||
expect(cyclic.order).toBe(4);
|
||||
});
|
||||
|
||||
it('has 4 edges (C→A, A→B, B→C, A→D)', () => {
|
||||
expect(cyclic.size).toBe(4);
|
||||
});
|
||||
|
||||
it('contains a cycle', () => {
|
||||
// graphology-dag hasCycle check
|
||||
expect(hasCycle(cyclic)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('largeGraph', () => {
|
||||
it('has 23 nodes (20+ for performance testing)', () => {
|
||||
expect(largeGraph.order).toBe(23);
|
||||
});
|
||||
|
||||
it('has more than 20 edges', () => {
|
||||
expect(largeGraph.size).toBeGreaterThan(20);
|
||||
});
|
||||
|
||||
it('release node has 8 prerequisites', () => {
|
||||
expect(largeGraph.inNeighbors('release')).toHaveLength(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTaskGraph helper', () => {
|
||||
it('builds a graph from TaskInput[]', () => {
|
||||
const tasks = [
|
||||
{ id: 'x', name: 'Task X', dependsOn: [] },
|
||||
{ id: 'y', name: 'Task Y', dependsOn: ['x'] },
|
||||
];
|
||||
const graph = createTaskGraph(tasks);
|
||||
expect(graph.order).toBe(2);
|
||||
expect(graph.size).toBe(1);
|
||||
});
|
||||
|
||||
it('handles empty task array', () => {
|
||||
const graph = createTaskGraph([]);
|
||||
expect(graph.order).toBe(0);
|
||||
expect(graph.size).toBe(0);
|
||||
});
|
||||
|
||||
it('uses deterministic edge keys', () => {
|
||||
const graph = createTaskGraph([
|
||||
{ id: 'a', name: 'A', dependsOn: [] },
|
||||
{ id: 'b', name: 'B', dependsOn: ['a'] },
|
||||
]);
|
||||
expect(graph.hasEdge('a->b')).toBe(true);
|
||||
});
|
||||
|
||||
it('sets default qualityRetention 0.9 on edges', () => {
|
||||
const graph = createTaskGraph([
|
||||
{ id: 'a', name: 'A', dependsOn: [] },
|
||||
{ id: 'b', name: 'B', dependsOn: ['a'] },
|
||||
]);
|
||||
const edgeAttrs = graph.getEdgeAttributes('a->b');
|
||||
expect(edgeAttrs.qualityRetention).toBe(0.9);
|
||||
});
|
||||
|
||||
it('deduplicates edges when same dependency appears twice', () => {
|
||||
const tasks = [
|
||||
{ id: 'a', name: 'A', dependsOn: [] },
|
||||
{ id: 'b', name: 'B', dependsOn: ['a', 'a'] },
|
||||
];
|
||||
const graph = createTaskGraph(tasks);
|
||||
expect(graph.size).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('allGraphs / allTasks convenience exports', () => {
|
||||
it('allGraphs contains 5 fixtures', () => {
|
||||
expect(Object.keys(allGraphs)).toHaveLength(5);
|
||||
});
|
||||
|
||||
it('allTasks contains 5 task arrays', () => {
|
||||
expect(Object.keys(allTasks)).toHaveLength(5);
|
||||
});
|
||||
|
||||
it('each graph has matching task array', () => {
|
||||
expect(allGraphs.linearChain.order).toBe(linearChainTasks.length);
|
||||
expect(allGraphs.diamond.order).toBe(diamondTasks.length);
|
||||
expect(allGraphs.mixedCategory.order).toBe(mixedCategoryTasks.length);
|
||||
expect(allGraphs.cyclic.order).toBe(cyclicTasks.length);
|
||||
expect(allGraphs.large.order).toBe(largeGraphTasks.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,19 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import path from 'node:path';
|
||||
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
include: ['test/**/*.test.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['src/**/index.ts'],
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user