From 3b0c31671e5adbdb9d162a85dbf6a960a85b041a Mon Sep 17 00:00:00 2001 From: Jason Benson Date: Thu, 25 Jun 2026 13:31:29 -0400 Subject: [PATCH 1/7] fixes src/models/Category.test.ts and src/jobs/Derivative.test.ts --- api/package-lock.json | 235 +++++++++++++++++-------------- api/package.json | 8 +- api/src/jobs/Derivative.test.ts | 4 +- api/src/jobs/Derivative.ts | 2 +- api/src/models/AudioFile.ts | 2 +- api/src/models/Category.ts | 2 +- api/src/models/Config.ts | 4 +- api/src/models/ImageFile.ts | 7 +- api/src/models/Job.ts | 18 ++- api/src/models/JobMetadata.ts | 14 +- api/src/models/PageOrder.ts | 2 +- api/src/models/VideoFile.ts | 2 +- api/src/services/QueueManager.ts | 8 +- api/src/services/SolrCache.ts | 4 +- api/tsconfig.json | 4 +- 15 files changed, 176 insertions(+), 140 deletions(-) diff --git a/api/package-lock.json b/api/package-lock.json index c05c07c4a..a06cd76b2 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -44,8 +44,10 @@ "devDependencies": { "@types/express": "^5.0.6", "@types/express-session": "^1.18.2", - "@types/jest": "^30", - "@types/node": "^25", + "@types/ini": "^4.1.1", + "@types/jest": "^30.0.0", + "@types/node": "^26", + "@types/pdfkit": "^0.17.6", "@typescript-eslint/eslint-plugin": "^8.52", "@typescript-eslint/parser": "^8.52", "eslint": "^10", @@ -57,7 +59,7 @@ "prettier": "^3", "supertest": "^7", "ts-jest": "^29", - "typescript": "^5", + "typescript": "^6", "typescript-eslint": "^8.52" } }, @@ -2305,9 +2307,9 @@ "license": "MIT" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -2441,6 +2443,13 @@ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "license": "MIT" }, + "node_modules/@types/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -2499,12 +2508,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", - "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", + "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/passport": { @@ -2526,6 +2535,16 @@ "@types/passport": "*" } }, + "node_modules/@types/pdfkit": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.17.6.tgz", + "integrity": "sha512-tIwzxk2uWKp0Cq9JIluQXJid77lYhF52EsIOwhsMF4iWLA6YneoBR1xVKYYdAysHuepUB0OX4tdwMiUDdGKmig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/qs": { "version": "6.15.1", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", @@ -2606,17 +2625,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz", - "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.0.tgz", + "integrity": "sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/type-utils": "8.61.1", - "@typescript-eslint/utils": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/scope-manager": "8.62.0", + "@typescript-eslint/type-utils": "8.62.0", + "@typescript-eslint/utils": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2629,22 +2648,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.61.1", + "@typescript-eslint/parser": "^8.62.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", - "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.0.tgz", + "integrity": "sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/scope-manager": "8.62.0", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/typescript-estree": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0", "debug": "^4.4.3" }, "engines": { @@ -2660,14 +2679,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", - "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.0.tgz", + "integrity": "sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.61.1", - "@typescript-eslint/types": "^8.61.1", + "@typescript-eslint/tsconfig-utils": "^8.62.0", + "@typescript-eslint/types": "^8.62.0", "debug": "^4.4.3" }, "engines": { @@ -2682,14 +2701,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", - "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.0.tgz", + "integrity": "sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1" + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2700,9 +2719,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", - "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz", + "integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==", "dev": true, "license": "MIT", "engines": { @@ -2717,15 +2736,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz", - "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.0.tgz", + "integrity": "sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/utils": "8.61.1", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/typescript-estree": "8.62.0", + "@typescript-eslint/utils": "8.62.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -2742,9 +2761,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", - "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz", + "integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==", "dev": true, "license": "MIT", "engines": { @@ -2756,16 +2775,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", - "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.0.tgz", + "integrity": "sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.61.1", - "@typescript-eslint/tsconfig-utils": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/project-service": "8.62.0", + "@typescript-eslint/tsconfig-utils": "8.62.0", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2784,16 +2803,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz", - "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.0.tgz", + "integrity": "sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1" + "@typescript-eslint/scope-manager": "8.62.0", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/typescript-estree": "8.62.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2808,13 +2827,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", - "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.0.tgz", + "integrity": "sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/types": "8.62.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2839,9 +2858,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.2.tgz", + "integrity": "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==", "dev": true, "license": "ISC" }, @@ -3700,9 +3719,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", + "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", "dev": true, "funding": [ { @@ -3720,10 +3739,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", + "baseline-browser-mapping": "^2.10.38", + "caniuse-lite": "^1.0.30001799", + "electron-to-chromium": "^1.5.376", + "node-releases": "^2.0.48", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -4103,9 +4122,9 @@ } }, "node_modules/bullmq": { - "version": "5.79.0", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.79.0.tgz", - "integrity": "sha512-sg+kYGn7PIDI/AAkSWINPNz0vMp745YaBYMyo3AtcfXk5iCvjEPU+XMbU3yf7rSf1KsU+OfU+GH8FhxUa+WDNA==", + "version": "5.79.1", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.79.1.tgz", + "integrity": "sha512-cteoHRr1FGOTUgzFrnMyBNGtQhNeVR8Ej6nImNSHQDJi4tj6GMD0p9ZG65ZsTnvR9RVf18dhRxWu4kFl634QGA==", "license": "MIT", "dependencies": { "cron-parser": "4.9.0", @@ -4808,9 +4827,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.375", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.375.tgz", - "integrity": "sha512-ZWP5eB4BVPW/ZYo9252hQZHZ5XavtsTgpbhcmMmRwymavC5AsLWQWBPaKMeNd2LW0KGby5HPXvj7+sr4ta5j/Q==", + "version": "1.5.377", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.377.tgz", + "integrity": "sha512-cH1jZgJHoezfTnKfKwnScpHywTFVnJUNITDPREFdhNjiuD502+QFpG0Qk7G8jhsV/f+CEAFlIrzP1fT+IMb92g==", "dev": true, "license": "ISC" }, @@ -5028,9 +5047,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "63.0.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.0.6.tgz", - "integrity": "sha512-qXMUdwQf+igjSLe/DZqyY1baVR0+snEJ3l/+2odJjeoAG43PrcpA0tSaLw2dC1iHvfKzo1YnZLGvgxrvAViyfQ==", + "version": "63.0.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.0.7.tgz", + "integrity": "sha512-pxrqGO733F7xmVYB5vQOiciiT9uddxqehawnbPjZmW2YaJR6fT5cP3UQd2BNoE85ATspCMtNL8w/a5WDGX3Qwg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -7840,9 +7859,9 @@ } }, "node_modules/n3": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/n3/-/n3-2.0.4.tgz", - "integrity": "sha512-d2Sr4vvqombySfOARjw5jQky0EYMwkEXuQQjYe2glasDdI3hAT3l00aJZbAmR+AA59eFNjJbDkHCq9l5Dm2DIg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/n3/-/n3-2.1.0.tgz", + "integrity": "sha512-+05H/h40wRyROglcVGrNZAwBu0Nc87luKhiTV95aGBGX1YyITtpwftHwyhT5YoZr4soXJWrzxqC1CHPdGqV63g==", "license": "MIT", "dependencies": { "buffer": "^6.0.3", @@ -7865,9 +7884,9 @@ } }, "node_modules/nanoid": { - "version": "5.1.14", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.14.tgz", - "integrity": "sha512-5c8l8kVzqpnDPaicbEop/fV0Q1w16FmbWtVhMqugTozAwYdlIQojWH5a/M7UfziFmGdQRrUdV+EPzc9Xng3VAQ==", + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.15.tgz", + "integrity": "sha512-kBg3RpGtIe+RpTbyXwoI6pk5yD7KUiI3sygUqgeBMRst42KmhB4RZC7eiO9Wa1HIpaCCtpE2DJ6OI4Wi5ebwFw==", "funding": [ { "type": "github", @@ -9077,9 +9096,9 @@ } }, "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -10162,9 +10181,9 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -10175,16 +10194,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz", - "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.0.tgz", + "integrity": "sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.61.1", - "@typescript-eslint/parser": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/utils": "8.61.1" + "@typescript-eslint/eslint-plugin": "8.62.0", + "@typescript-eslint/parser": "8.62.0", + "@typescript-eslint/typescript-estree": "8.62.0", + "@typescript-eslint/utils": "8.62.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10241,9 +10260,9 @@ } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "license": "MIT" }, "node_modules/unicode-properties": { @@ -10688,9 +10707,9 @@ "license": "ISC" }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/api/package.json b/api/package.json index 89593d7cc..e299eb7a8 100644 --- a/api/package.json +++ b/api/package.json @@ -57,7 +57,11 @@ "@types/express": "^5.0.6", "@types/express-session": "^1.18.2", "@types/jest": "^30", - "@types/node": "^25", + "@types/node": "^26", + "@types/ini": "^4.1.1", + "@types/jest": "^30.0.0", + "@types/node": "^26", + "@types/pdfkit": "^0.17.6", "@typescript-eslint/eslint-plugin": "^8.52", "@typescript-eslint/parser": "^8.52", "eslint": "^10", @@ -69,7 +73,7 @@ "prettier": "^3", "supertest": "^7", "ts-jest": "^29", - "typescript": "^5", + "typescript": "^6", "typescript-eslint": "^8.52" } } diff --git a/api/src/jobs/Derivative.test.ts b/api/src/jobs/Derivative.test.ts index 7df537189..e614509ec 100644 --- a/api/src/jobs/Derivative.test.ts +++ b/api/src/jobs/Derivative.test.ts @@ -16,8 +16,8 @@ describe("Derivative", () => { describe("run", () => { let job: Job; - let consoleErrorSpy; - let consoleLogSpy; + let consoleErrorSpy: jest.SpyInstance; + let consoleLogSpy: jest.SpyInstance; beforeEach(() => { job = { data: { diff --git a/api/src/jobs/Derivative.ts b/api/src/jobs/Derivative.ts index 829310b47..ec37f745e 100644 --- a/api/src/jobs/Derivative.ts +++ b/api/src/jobs/Derivative.ts @@ -12,7 +12,7 @@ class Derivative implements QueueJob { // For each page const order = PageOrder.fromJob(job.data); - const generatingPromises = []; + const generatingPromises: Promise[] = []; order.raw.forEach((page) => { // For each size const image = ImageFile.build(`${job.data.dir}/${page.filename}`); diff --git a/api/src/models/AudioFile.ts b/api/src/models/AudioFile.ts index 4d8f8744f..40e26eb3a 100644 --- a/api/src/models/AudioFile.ts +++ b/api/src/models/AudioFile.ts @@ -8,7 +8,7 @@ class AudioFile extends AbstractAVFile { return new AudioFile(filename, dir, Config.getInstance()); } - static fromRaw(raw: Record, config: Config = null): AudioFile { + static fromRaw(raw: Record, config: Config | null = null): AudioFile { return new AudioFile(raw.filename, raw.label, config ?? Config.getInstance()); } } diff --git a/api/src/models/Category.ts b/api/src/models/Category.ts index 96f215a15..a4b9e22ba 100644 --- a/api/src/models/Category.ts +++ b/api/src/models/Category.ts @@ -5,7 +5,7 @@ import path = require("path"); import Job from "./Job"; -export class CategoryRaw { +export interface CategoryRaw { category: string; jobs: Array; } diff --git a/api/src/models/Config.ts b/api/src/models/Config.ts index 49f7bc6e0..05b9ea473 100644 --- a/api/src/models/Config.ts +++ b/api/src/models/Config.ts @@ -10,9 +10,9 @@ interface ConfigRecord { class Config { private static instance: Config; - protected ini; + protected ini: Record; - constructor(ini: ConfigRecord) { + constructor(ini: Record) { this.ini = ini; } diff --git a/api/src/models/ImageFile.ts b/api/src/models/ImageFile.ts index 9c59bc700..c05fe9927 100644 --- a/api/src/models/ImageFile.ts +++ b/api/src/models/ImageFile.ts @@ -1,10 +1,7 @@ -import Sharp = require("sharp"); +import sharp from "sharp"; import path = require("path"); - import { execSync } from "child_process"; - import Config from "./Config"; - import fs = require("fs"); class ImageFile { @@ -42,7 +39,7 @@ class ImageFile { return deriv; } - const image = Sharp(this.filename, this.config.sharpOptions); + const image = sharp(this.filename, this.config.sharpOptions); const constraint = this.constraintForSize(size); const metadata = await image.metadata(); diff --git a/api/src/models/Job.ts b/api/src/models/Job.ts index 2d3ea81b7..bc8103329 100644 --- a/api/src/models/Job.ts +++ b/api/src/models/Job.ts @@ -1,5 +1,19 @@ import { createWriteStream, openSync, closeSync, existsSync as fileExists, statSync } from "fs"; import PDFDocument = require("pdfkit"); + +declare global { + namespace PDFKit { + interface PDFKitImage { + width: number; + height: number; + } + interface PDFDocument { + openImage(src: string | Buffer): PDFKitImage; + image(src: PDFKitImage, x?: number, y?: number, options?: Mixins.ImageOption): this; + image(src: PDFKitImage, options?: Mixins.ImageOption): this; + } + } +} import path = require("path"); import Config from "./Config"; @@ -11,7 +25,7 @@ import QueueManager from "../services/QueueManager"; class Job { dir: string; name: string; - _metadata: JobMetadata = null; + _metadata: JobMetadata | null = null; config: Config; queue: QueueManager; @@ -71,7 +85,7 @@ class Job { protected async getLargeJpegs(): Promise> { const pages = this.metadata.order.pages; - const jpegs = []; + const jpegs: string[] = []; for (const i in pages) { const image = ImageFile.build(this.dir + "/" + pages[i].filename); jpegs[i] = await image.derivative("LARGE"); diff --git a/api/src/models/JobMetadata.ts b/api/src/models/JobMetadata.ts index 698405b66..a43d2a535 100644 --- a/api/src/models/JobMetadata.ts +++ b/api/src/models/JobMetadata.ts @@ -16,12 +16,12 @@ interface JobMetadataRaw { class JobMetadata { job: Job; - page: PageRaw; + page: PageRaw | null = null; _filename: string; - _order: PageOrder = null; - _documents: DocumentOrder = null; - _audio: AudioOrder = null; - _video: VideoOrder = null; + _order: PageOrder | null = null; + _documents: DocumentOrder | null = null; + _audio: AudioOrder | null = null; + _video: VideoOrder | null = null; published = false; constructor(job: Job) { @@ -58,7 +58,7 @@ class JobMetadata { return this.job.dir + "/derivatives.lock"; } - get derivativeStatus(): Record { + get derivativeStatus(): { expected: number; processed: number; building: boolean } { const lockfileExists: boolean = fs.existsSync(this.derivativeLockfile); const status = { expected: 0, @@ -121,7 +121,7 @@ class JobMetadata { get ingestInfo(): string { const logfile: string = this.job.dir + "/ingest.log"; - return fs.existsSync(logfile) ? fs.readFileSync(logfile, "utf-8").split("\n").filter(Boolean).pop() : ""; + return fs.existsSync(logfile) ? fs.readFileSync(logfile, "utf-8").split("\n").filter(Boolean).pop() ?? "" : ""; } get order(): PageOrder { diff --git a/api/src/models/PageOrder.ts b/api/src/models/PageOrder.ts index 3f1992734..9fc1bde37 100644 --- a/api/src/models/PageOrder.ts +++ b/api/src/models/PageOrder.ts @@ -30,7 +30,7 @@ class PageOrder { return firstPartResults === 0 ? a.localeCompare(b) : firstPartResults; }); const pages = files.map((file) => { - return new Page(path.basename(file), null); + return new Page(path.basename(file), ""); }); return new PageOrder(pages); } diff --git a/api/src/models/VideoFile.ts b/api/src/models/VideoFile.ts index 713116d47..9bc12b3d2 100644 --- a/api/src/models/VideoFile.ts +++ b/api/src/models/VideoFile.ts @@ -9,7 +9,7 @@ class VideoFile extends AbstractAVFile { return new VideoFile(filename, dir, Config.getInstance()); } - static fromRaw(raw: Record, config: Config = null): VideoFile { + static fromRaw(raw: Record, config: Config | null = null): VideoFile { return new VideoFile(raw.filename, raw.label, config ?? Config.getInstance()); } diff --git a/api/src/services/QueueManager.ts b/api/src/services/QueueManager.ts index 1b8d52b53..8e1f6e5d6 100644 --- a/api/src/services/QueueManager.ts +++ b/api/src/services/QueueManager.ts @@ -29,11 +29,11 @@ class QueueManager { }; } - protected getQueue(queueName: string = null): Queue { + protected getQueue(queueName: string = ""): Queue { return new Queue(queueName ?? this.config.redisDefaultQueueName, this.queueBaseOptions); } - public getWorker(callback: Processor, queueName: string = null): Worker { + public getWorker(callback: Processor, queueName: string = ""): Worker { const options: WorkerOptions = this.queueBaseOptions; options.lockDuration = this.config.redisLockDuration; return new Worker(queueName ?? this.config.redisDefaultQueueName, callback, options); @@ -61,7 +61,7 @@ class QueueManager { return await this.addToQueue("ingest", { dir }); } - public async sendNotification(body: string, channel: string | null = null): Promise { + public async sendNotification(body: string, channel: string = ""): Promise { return await this.addToQueue("notify", { body, channel }); } @@ -69,7 +69,7 @@ class QueueManager { return await this.addToQueue("reindex", { file }); } - public async hasPendingIndexJob(q, queueJob): Promise { + public async hasPendingIndexJob(q: Queue, queueJob: { pid: string; action: string }): Promise { if (this.cache.isEnabled()) { return this.cache.isPidLocked(queueJob.pid, queueJob.action); } diff --git a/api/src/services/SolrCache.ts b/api/src/services/SolrCache.ts index dff416205..05eeef78c 100644 --- a/api/src/services/SolrCache.ts +++ b/api/src/services/SolrCache.ts @@ -159,7 +159,7 @@ export class SolrCache { } let document: Array = []; - let currentBatch: { file: string; size: number } = { file: null, size: 0 }; + let currentBatch: { file: string; size: number } = { file: "", size: 0 }; docs.forEach((file) => { const nextObject = this.readSolrAddDocFromFile(file); if (nextObject?.add?.doc === undefined) { @@ -175,7 +175,7 @@ export class SolrCache { if (currentBatch.size == batchSize) { this.writeFile(currentBatch.file, JSON.stringify(document)); document = []; - currentBatch = { file: null, size: 0 }; + currentBatch = { file: "", size: 0 }; } }); if (currentBatch.size > 0) { diff --git a/api/tsconfig.json b/api/tsconfig.json index 0d5405de2..f139d83b8 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -4,7 +4,9 @@ "allowJs": true, "target": "es5", "outDir": "./dist", - "skipLibCheck": true + "skipLibCheck": true, + "esModuleInterop": true, + "types": ["jest"] }, "include": [ "./src/**/*" From 16e7a3b99f5117b026822a43dd763ff8ed7e0885 Mon Sep 17 00:00:00 2001 From: Jason Benson Date: Tue, 7 Jul 2026 15:07:17 -0400 Subject: [PATCH 2/7] fix for FedoraDataCollection.test.ts --- api/package.json | 2 -- api/src/models/FedoraDataCollection.test.ts | 2 +- api/src/models/FedoraDataCollection.ts | 24 ++++++++++----------- api/tsconfig.json | 4 +++- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/package.json b/api/package.json index e299eb7a8..a11409c76 100644 --- a/api/package.json +++ b/api/package.json @@ -56,8 +56,6 @@ "devDependencies": { "@types/express": "^5.0.6", "@types/express-session": "^1.18.2", - "@types/jest": "^30", - "@types/node": "^26", "@types/ini": "^4.1.1", "@types/jest": "^30.0.0", "@types/node": "^26", diff --git a/api/src/models/FedoraDataCollection.test.ts b/api/src/models/FedoraDataCollection.test.ts index 55c107f42..d88ab26dd 100644 --- a/api/src/models/FedoraDataCollection.test.ts +++ b/api/src/models/FedoraDataCollection.test.ts @@ -1,7 +1,7 @@ import Config from "./Config"; import FedoraDataCollection from "./FedoraDataCollection"; -let fedoraData; +let fedoraData: FedoraDataCollection; beforeEach(() => { Config.setInstance(new Config({})); fedoraData = FedoraDataCollection.build("foo:123"); diff --git a/api/src/models/FedoraDataCollection.ts b/api/src/models/FedoraDataCollection.ts index b02e4357c..d288f20d1 100644 --- a/api/src/models/FedoraDataCollection.ts +++ b/api/src/models/FedoraDataCollection.ts @@ -42,10 +42,10 @@ class FedoraDataCollection { metadata: Record> = {}, fedoraDetails: Record> = {}, fedoraDatastreams: Array = [], - fedora: Fedora = null, - extractor: MetadataExtractor = null, - tika: TikaExtractor = null, - config: Config = null, + fedora: Fedora | null = null, + extractor: MetadataExtractor | null = null, + tika: TikaExtractor | null = null, + config: Config | null = null, ): FedoraDataCollection { return new FedoraDataCollection( pid, @@ -91,7 +91,7 @@ class FedoraDataCollection { * Create a flattened list of all PIDs "above" the current one. */ getAllParents(): Array { - const results = []; + const results: Array = []; this.parents.forEach((parent) => { const parentPids = [parent.pid].concat(parent.getAllParents()); parentPids.forEach((pid) => { @@ -116,7 +116,7 @@ class FedoraDataCollection { }; } - async getThumbnailHash(type: string): Promise { + async getThumbnailHash(type: string): Promise { const hashes = (await this.datastreamDetails.getThumbnails()).hasMessageDigest ?? []; for (const hash of hashes) { const parts = hash.split(":"); @@ -132,7 +132,7 @@ class FedoraDataCollection { return fitsData[name] ?? []; } - async getFitsValueAsString(name: string): Promise { + async getFitsValueAsString(name: string): Promise { const fitsData = await this.datastreamDetails.getFitsData(); if (typeof fitsData[name] === "undefined") { return null; @@ -140,12 +140,12 @@ class FedoraDataCollection { return fitsData[name][0] ?? null; } - async getFileSize(): Promise { + async getFileSize(): Promise { return await this.getFitsValueAsString("size"); } async getFullText(): Promise> { - let fullText = []; + let fullText: Array = []; const rawFullText = await this.datastreamDetails.getFullText(); for (const current in rawFullText) { fullText = fullText.concat(rawFullText[current]); @@ -156,11 +156,11 @@ class FedoraDataCollection { }); } - async getImageHeight(): Promise { + async getImageHeight(): Promise { return await this.getFitsValueAsString("imageHeight"); } - async getImageWidth(): Promise { + async getImageWidth(): Promise { return await this.getFitsValueAsString("imageWidth"); } @@ -176,7 +176,7 @@ class FedoraDataCollection { get models(): Array { // Separate identifier from URI prefix return (this.fedoraDetails.hasModel ?? []).map((model) => { - return model.split("/").pop(); + return model.split("/").pop() as string; }); } diff --git a/api/tsconfig.json b/api/tsconfig.json index f139d83b8..c1c4c64c3 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -2,7 +2,9 @@ "compilerOptions": { "sourceMap": true, "allowJs": true, - "target": "es5", + "target": "es2022", + "module": "commonjs", + "strict": false, "outDir": "./dist", "skipLibCheck": true, "esModuleInterop": true, From 8e9c6abf075138ef882dec63fa4f02d959f89661 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 19:10:03 +0000 Subject: [PATCH 3/7] Commit from GitHub Actions (Lint Pull Requests) --- api/src/models/JobMetadata.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/src/models/JobMetadata.ts b/api/src/models/JobMetadata.ts index a43d2a535..c8cf8b4fc 100644 --- a/api/src/models/JobMetadata.ts +++ b/api/src/models/JobMetadata.ts @@ -121,7 +121,9 @@ class JobMetadata { get ingestInfo(): string { const logfile: string = this.job.dir + "/ingest.log"; - return fs.existsSync(logfile) ? fs.readFileSync(logfile, "utf-8").split("\n").filter(Boolean).pop() ?? "" : ""; + return fs.existsSync(logfile) + ? (fs.readFileSync(logfile, "utf-8").split("\n").filter(Boolean).pop() ?? "") + : ""; } get order(): PageOrder { From 8cce52a1f84660b471c8913a44e14d47120c3d95 Mon Sep 17 00:00:00 2001 From: Jason Benson Date: Wed, 8 Jul 2026 15:57:31 -0400 Subject: [PATCH 4/7] Updates for auth and index tests. --- api/src/app.ts | 6 +- api/src/models/Config.ts | 126 +++++++++++++++++------------------ api/src/models/Job.ts | 1 + api/src/routes/auth.test.ts | 2 +- api/src/routes/index.test.ts | 2 +- api/src/server.ts | 2 +- 6 files changed, 70 insertions(+), 69 deletions(-) diff --git a/api/src/app.ts b/api/src/app.ts index bc3c8a7d5..cd752cf13 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -1,7 +1,7 @@ -import * as express from "express"; -import * as cookieParser from "cookie-parser"; +import express from "express"; +import cookieParser from "cookie-parser"; import * as path from "path"; -import * as logger from "morgan"; +import logger from "morgan"; import Config from "./models/Config"; const app = express(); diff --git a/api/src/models/Config.ts b/api/src/models/Config.ts index 05b9ea473..f0c1a95ff 100644 --- a/api/src/models/Config.ts +++ b/api/src/models/Config.ts @@ -10,9 +10,9 @@ interface ConfigRecord { class Config { private static instance: Config; - protected ini: Record; + protected ini: ConfigRecord; - constructor(ini: Record) { + constructor(ini: ConfigRecord) { this.ini = ini; } @@ -37,117 +37,117 @@ class Config { } get backendUrl(): string { - return this.ini["backend_url"] ?? "http://localhost:9000"; + return (this.ini["backend_url"] as string) ?? "http://localhost:9000"; } get clientUrl(): string { - return this.ini["client_url"]; + return this.ini["client_url"] as string; } get fedoraUsername(): string { - return this.ini["fedora_username"]; + return this.ini["fedora_username"] as string; } get fedoraPassword(): string { - return this.ini["fedora_password"]; + return this.ini["fedora_password"] as string; } get fedoraPidNameSpace(): string { - return this.ini["fedora_pid_namespace"]; + return this.ini["fedora_pid_namespace"] as string; } get ffmpegPath(): string { - return this.ini["ffmpeg_path"]; + return this.ini["ffmpeg_path"] as string; } get fitsCommand(): string { - return this.ini["fits_command"]; + return this.ini["fits_command"] as string; } get sessionKey(): string { - return this.ini["session_key"] ?? "vanilla hot cocoa"; + return (this.ini["session_key"] as string) ?? "vanilla hot cocoa"; } get tesseractPath(): string { - return this.ini["tesseract_path"]; + return this.ini["tesseract_path"] as string; } get tesseractAllowedChars(): string { - return this.ini["tesseract_allowed_characters"]; + return this.ini["tesseract_allowed_characters"] as string; } get vufindUrl(): string { - return this.ini["vufind_url"] ?? ""; + return (this.ini["vufind_url"] as string) ?? ""; } get pdfDirectory(): string { - return this.ini["pdf_directory"]; + return this.ini["pdf_directory"] as string; } get textcleanerPath(): string { - return this.ini["textcleaner_path"]; + return this.ini["textcleaner_path"] as string; } get textcleanerSwitches(): string { - return this.ini["textcleaner_switches"]; + return this.ini["textcleaner_switches"] as string; } get holdingArea(): string { - const holdingArea = this.ini["holding_area_path"]; + const holdingArea = this.ini["holding_area_path"] as string; return holdingArea.endsWith("/") ? holdingArea : holdingArea + "/"; } get ocrmypdfPath(): string { - return this.ini["ocrmypdf_path"]; + return this.ini["ocrmypdf_path"] as string; } get processedAreaPath(): string { - return this.ini["processed_area_path"]; + return this.ini["processed_area_path"] as string; } get restBaseUrl(): string { - return this.ini["base_url"]; + return this.ini["base_url"] as string; } get javaPath(): string { - return this.ini["java_path"] ?? "java"; + return (this.ini["java_path"] as string) ?? "java"; } - get tikaConfigFile(): string { - return this.ini["tika_config_file"] ?? null; + get tikaConfigFile(): string | null { + return (this.ini["tika_config_file"] as string) ?? null; } get tikaPath(): string { - return this.ini["tika_path"]; + return this.ini["tika_path"] as string; } get solrCore(): string { - return this.ini["solr_core"] ?? "biblio"; + return (this.ini["solr_core"] as string) ?? "biblio"; } get solrUrl(): string { - return this.ini["solr_url"] ?? "http://localhost:8983/solr"; + return (this.ini["solr_url"] as string) ?? "http://localhost:8983/solr"; } get solrDocumentCacheDir(): boolean | string { - return this.ini["solr_document_cache_dir"] ?? false; + return (this.ini["solr_document_cache_dir"] as string) ?? false; } get allowedOrigins(): string[] { - return this.ini["allowed_origins"] ?? []; + return (this.ini["allowed_origins"] as string[]) ?? []; } get pidNamespace(): string { - return this.ini["fedora_pid_namespace"] ?? "vudl"; + return (this.ini["fedora_pid_namespace"] as string) ?? "vudl"; } get initialPidValue(): number { - return parseInt(this.ini["fedora_initial_pid"] ?? "0"); + return parseInt((this.ini["fedora_initial_pid"] as string) ?? "0"); } get dataModels(): Record { return ( - this.ini["data_models"] ?? { + (this.ini["data_models"] as Record) ?? { Image: "vudl-system:ImageData", PDF: "vudl-system:PDFData", DOC: "vudl-system:DOCData", @@ -161,7 +161,7 @@ class Config { get collectionModels(): Record { return ( - this.ini["collection_models"] ?? { + (this.ini["collection_models"] as Record) ?? { List: "vudl-system:ListCollection", Resource: "vudl-system:ResourceCollection", Folder: "vudl-system:FolderCollection", @@ -170,27 +170,27 @@ class Config { } get institution(): string { - return this.ini["institution"] ?? "My University"; + return (this.ini["institution"] as string) ?? "My University"; } get collection(): string { - return this.ini["collection"] ?? "Digital Library"; + return (this.ini["collection"] as string) ?? "Digital Library"; } get topLevelPids(): Array { - return this.ini["top_level_pids"] ?? []; + return (this.ini["top_level_pids"] as string[]) ?? []; } get articlesToStrip(): Array { - return this.ini["articles_to_strip"] ?? []; + return (this.ini["articles_to_strip"] as string[]) ?? []; } get trashPid(): string | null { - return this.ini["trash_pid"] ?? null; + return (this.ini["trash_pid"] as string) ?? null; } get favoritePids(): Array { - const favorites = this.ini["favorite_pids"] ?? []; + const favorites = (this.ini["favorite_pids"] as string[]) ?? []; const trash = this.trashPid; if (trash && !favorites.includes(trash)) { favorites.push(trash); @@ -199,19 +199,19 @@ class Config { } get languageMap(): Record { - return this.ini["LanguageMap"] ?? {}; + return (this.ini["LanguageMap"] as Record) ?? {}; } get minimumValidYear(): number { - return parseInt(this.ini["minimum_valid_year"] ?? 1000); + return parseInt((this.ini["minimum_valid_year"] as string) ?? "1000"); } get models(): Record { - return this.ini["models"] || {}; + return (this.ini["models"] as unknown as Record) || {}; } get databaseSettings(): ConfigRecord { - return this.ini["Database"] ?? {}; + return (this.ini["Database"] as ConfigRecord) ?? {}; } get databaseClient(): string { @@ -223,7 +223,7 @@ class Config { } get authenticationSettings(): ConfigRecord { - return this.ini["Authentication"] ?? []; + return (this.ini["Authentication"] as ConfigRecord) ?? {}; } get authenticationStrategy(): string { @@ -263,88 +263,88 @@ class Config { } get licenses(): Record { - return this.ini["licenses"] ?? {}; + return (this.ini["licenses"] as unknown as Record) ?? {}; } get agentDefaults(): Record { - return this.ini?.["agent"]?.["defaults"] ?? {}; + return ((this.ini["agent"] as ConfigRecord)?.["defaults"] as Record) ?? {}; } get agentRoles(): Array { - return this.ini?.["agent"]?.["roles"] ?? []; + return ((this.ini["agent"] as ConfigRecord)?.["roles"] as string[]) ?? []; } get agentTypes(): Array { - return this.ini?.["agent"]?.["types"] ?? []; + return ((this.ini["agent"] as ConfigRecord)?.["types"] as string[]) ?? []; } get dublinCoreFields(): Record>> { - return this.ini?.["dublin_core"] ?? {}; + return (this.ini["dublin_core"] as Record>>) ?? {}; } get redisConnectionSettings(): Record { - return this.ini?.["queue"]?.["connection"] ?? {}; + return ((this.ini["queue"] as ConfigRecord)?.["connection"] as Record) ?? {}; } get redisDefaultQueueName(): string { - return this.ini?.["queue"]?.["defaultQueueName"] ?? "vudl"; + return ((this.ini["queue"] as ConfigRecord)?.["defaultQueueName"] as string) ?? "vudl"; } get redisQueueJobMap(): Record { - return this.ini?.["queue"]?.["jobMap"] ?? {}; + return ((this.ini["queue"] as ConfigRecord)?.["jobMap"] as Record) ?? {}; } get redisLockDuration(): number { - return parseInt(this.ini?.["queue"]?.["lockDuration"] ?? "30000"); + return parseInt(((this.ini["queue"] as ConfigRecord)?.["lockDuration"] as string) ?? "30000"); } get processMetadataDefaults(): Record { - return this.ini?.["process_metadata_defaults"] ?? {}; + return (this.ini["process_metadata_defaults"] as Record) ?? {}; } get toolPresets(): Array> { - return this.ini?.["tool_presets"] ?? []; + return (this.ini["tool_presets"] as unknown as Array>) ?? []; } get sharpOptions(): Record { - const pixelLimit = this.ini?.["sharp"]?.["limitInputPixels"] ?? "268402689"; + const pixelLimit = ((this.ini["sharp"] as ConfigRecord)?.["limitInputPixels"] as string) ?? "268402689"; return { limitInputPixels: parseInt(pixelLimit), }; } get max409Retries(): number { - return this.ini["max_409_retries"] ?? 3; + return (this.ini["max_409_retries"] as unknown as number) ?? 3; } get maxUploadSize(): number { - return this.ini?.["upload"]?.["sizeLimit"] ?? 200 * 1024 * 1024; + return ((this.ini["upload"] as ConfigRecord)?.["sizeLimit"] as unknown as number) ?? 200 * 1024 * 1024; } get notifyMethod(): string { - return this.ini?.["notify"]?.["method"] ?? "ntfy"; + return ((this.ini["notify"] as ConfigRecord)?.["method"] as string) ?? "ntfy"; } get ntfyConfig(): Record { return { - defaultChannel: this.ini?.["notify"]?.["ntfy_defaultChannel"] ?? "vudl-ntfy", + defaultChannel: ((this.ini["notify"] as ConfigRecord)?.["ntfy_defaultChannel"] as string) ?? "vudl-ntfy", }; } get indexerLockRetries(): number { - return parseInt(this.ini?.["indexer"]?.["lockRetries"] ?? 60); + return parseInt(((this.ini["indexer"] as ConfigRecord)?.["lockRetries"] as string) ?? "60"); } get indexerLockWaitMs(): number { - return parseInt(this.ini?.["indexer"]?.["lockWaitMs"] ?? 1000); + return parseInt(((this.ini["indexer"] as ConfigRecord)?.["lockWaitMs"] as string) ?? "1000"); } get indexerExceptionRetries(): number { - return parseInt(this.ini?.["indexer"]?.["exceptionRetries"] ?? 10); + return parseInt(((this.ini["indexer"] as ConfigRecord)?.["exceptionRetries"] as string) ?? "10"); } get indexerExceptionWaitMs(): number { - return parseInt(this.ini?.["indexer"]?.["exceptionWaitMs"] ?? 500); + return parseInt(((this.ini["indexer"] as ConfigRecord)?.["exceptionWaitMs"] as string) ?? "500"); } } diff --git a/api/src/models/Job.ts b/api/src/models/Job.ts index bc8103329..b2733cdd9 100644 --- a/api/src/models/Job.ts +++ b/api/src/models/Job.ts @@ -2,6 +2,7 @@ import { createWriteStream, openSync, closeSync, existsSync as fileExists, statS import PDFDocument = require("pdfkit"); declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace -- augmenting @types/pdfkit's ambient PDFKit namespace, not organizing our own code namespace PDFKit { interface PDFKitImage { width: number; diff --git a/api/src/routes/auth.test.ts b/api/src/routes/auth.test.ts index 6890ce9a7..e0a1172d4 100644 --- a/api/src/routes/auth.test.ts +++ b/api/src/routes/auth.test.ts @@ -1,5 +1,5 @@ import * as request from "supertest"; -import * as session from "express-session"; +import session from "express-session"; import { StatusCodes } from "http-status-codes"; import app from "../app"; import { getAuthRouter } from "./auth"; diff --git a/api/src/routes/index.test.ts b/api/src/routes/index.test.ts index 9b2ba0bf5..a848f2c0e 100644 --- a/api/src/routes/index.test.ts +++ b/api/src/routes/index.test.ts @@ -1,4 +1,4 @@ -import * as request from "supertest"; +import request from "supertest"; import { StatusCodes } from "http-status-codes"; import app from "../app"; import index from "./index"; diff --git a/api/src/server.ts b/api/src/server.ts index b9ccab462..e3618e448 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -4,7 +4,7 @@ import * as http from "http"; import * as passport from "passport"; -import * as session from "express-session"; +import session from "express-session"; import debug from "debug"; import app from "./app"; From dd8139d222c5907946c9449be98b8e4208703a82 Mon Sep 17 00:00:00 2001 From: Jason Benson Date: Thu, 9 Jul 2026 10:43:33 -0400 Subject: [PATCH 5/7] Changed typsescript version to 5 and implemented suggested changes to config.ts --- api/package-lock.json | 8 ++++---- api/package.json | 2 +- api/src/models/Config.ts | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/package-lock.json b/api/package-lock.json index a06cd76b2..e9b6a7bf1 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -59,7 +59,7 @@ "prettier": "^3", "supertest": "^7", "ts-jest": "^29", - "typescript": "^6", + "typescript": "^5", "typescript-eslint": "^8.52" } }, @@ -10181,9 +10181,9 @@ } }, "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/api/package.json b/api/package.json index a11409c76..6e1f0166d 100644 --- a/api/package.json +++ b/api/package.json @@ -71,7 +71,7 @@ "prettier": "^3", "supertest": "^7", "ts-jest": "^29", - "typescript": "^6", + "typescript": "^5", "typescript-eslint": "^8.52" } } diff --git a/api/src/models/Config.ts b/api/src/models/Config.ts index f0c1a95ff..a83283399 100644 --- a/api/src/models/Config.ts +++ b/api/src/models/Config.ts @@ -37,7 +37,7 @@ class Config { } get backendUrl(): string { - return (this.ini["backend_url"] as string) ?? "http://localhost:9000"; + return (this.ini["backend_url"] ?? "http://localhost:9000") as string; } get clientUrl(): string { @@ -114,7 +114,7 @@ class Config { } get tikaConfigFile(): string | null { - return (this.ini["tika_config_file"] as string) ?? null; + return (this.ini["tika_config_file"] ?? null) as string | null; } get tikaPath(): string { @@ -207,7 +207,7 @@ class Config { } get models(): Record { - return (this.ini["models"] as unknown as Record) || {}; + return (this.ini["models"] as Record) || {}; } get databaseSettings(): ConfigRecord { @@ -267,7 +267,7 @@ class Config { } get agentDefaults(): Record { - return ((this.ini["agent"] as ConfigRecord)?.["defaults"] as Record) ?? {}; + return ((this.ini["agent"] as ConfigRecord | undefined)?.["defaults"] as Record) ?? {}; } get agentRoles(): Array { From efc42b1ca144bd9e31042a05d72ea60245198244 Mon Sep 17 00:00:00 2001 From: Jason Benson Date: Thu, 9 Jul 2026 11:01:27 -0400 Subject: [PATCH 6/7] 1 line fixes for messenger, edit, and auth tests --- api/src/routes/auth.test.ts | 2 +- api/src/routes/edit.test.ts | 2 +- api/src/routes/messenger.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/routes/auth.test.ts b/api/src/routes/auth.test.ts index e0a1172d4..31457428e 100644 --- a/api/src/routes/auth.test.ts +++ b/api/src/routes/auth.test.ts @@ -1,4 +1,4 @@ -import * as request from "supertest"; +import request from "supertest"; import session from "express-session"; import { StatusCodes } from "http-status-codes"; import app from "../app"; diff --git a/api/src/routes/edit.test.ts b/api/src/routes/edit.test.ts index a67617fdf..f8e28d30b 100644 --- a/api/src/routes/edit.test.ts +++ b/api/src/routes/edit.test.ts @@ -1,4 +1,4 @@ -import * as request from "supertest"; +import request from "supertest"; import { StatusCodes } from "http-status-codes"; import app from "../app"; import edit from "./edit"; diff --git a/api/src/routes/messenger.test.ts b/api/src/routes/messenger.test.ts index 819bec02e..1939b1fcb 100644 --- a/api/src/routes/messenger.test.ts +++ b/api/src/routes/messenger.test.ts @@ -1,4 +1,4 @@ -import * as request from "supertest"; +import request from "supertest"; import { StatusCodes } from "http-status-codes"; import app from "../app"; import messenger from "./messenger"; From 2a4bc1fe228195936fb8a08d4272eecefe70e7a6 Mon Sep 17 00:00:00 2001 From: Jason Benson Date: Thu, 9 Jul 2026 12:09:29 -0400 Subject: [PATCH 7/7] fix for src/services/JobQueue.test.ts, jest was not accounting for esModuleInterop setting in tsconfig --- api/src/services/JobQueue.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/services/JobQueue.test.ts b/api/src/services/JobQueue.test.ts index 5531bb9c9..28f737f99 100644 --- a/api/src/services/JobQueue.test.ts +++ b/api/src/services/JobQueue.test.ts @@ -5,19 +5,19 @@ const mockDerivative = { run: jest.fn(), }; jest.mock("../jobs/Derivative", () => { - return { default: jest.fn().mockImplementation(() => mockDerivative) }; + return { __esModule: true, default: jest.fn().mockImplementation(() => mockDerivative) }; }); jest.mock("../jobs/GeneratePdf", () => { - return { default: jest.fn() }; + return { __esModule: true, default: jest.fn() }; }); jest.mock("../jobs/Index", () => { - return { default: jest.fn() }; + return { __esModule: true, default: jest.fn() }; }); jest.mock("../jobs/Ingest", () => { - return { default: jest.fn() }; + return { __esModule: true, default: jest.fn() }; }); jest.mock("../jobs/Metadata", () => { - return { default: jest.fn() }; + return { __esModule: true, default: jest.fn() }; }); describe("JobQueue", () => {