end of day
This commit is contained in:
@@ -0,0 +1 @@
|
||||
@sugoidogo:registry=https://gitea.sugoidogo.com/api/packages/sugoidogo/npm/
|
||||
Generated
+67
-246
@@ -9,13 +9,18 @@
|
||||
"version": "0.2.0",
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@sugoidogo/js-util": "^0.3.0",
|
||||
"commander": "^14.0.2",
|
||||
"confbox": "^0.2.2",
|
||||
"curseforge-v2": "^1.5.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"md5": "^2.3.0",
|
||||
"murmur2": "^1.0.2",
|
||||
"native-file-system-adapter": "file:../native-file-system-adapter/native-file-system-adapter-3.0.1.tgz",
|
||||
"workerless": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/md5": "^2.3.6",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/web": "^0.0.294",
|
||||
"bun": "^1.3.3",
|
||||
@@ -1343,25 +1348,10 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sindresorhus/is": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.1.tgz",
|
||||
"integrity": "sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@speed-highlight/core": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.12.tgz",
|
||||
"integrity": "sha512-uilwrK0Ygyri5dToHYdZSjcvpS2ZwX0w5aSt3GCEN9hrjxWCoeV4Z2DTXuxjwbntaLQIEEAlCeNQss5SoHvAEA==",
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
"node_modules/@sugoidogo/js-util": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://gitea.sugoidogo.com/api/packages/sugoidogo/npm/%40sugoidogo%2Fjs-util/-/0.3.0/js-util-0.3.0.tgz",
|
||||
"integrity": "sha512-ld9PHyjS03zSglQJzvS8CNXcDONDyvRygEEpkAggNywVbf5eGfoj7nimdNSLo2arA7Xs1s4/lHC2F1aWjRe+WQ=="
|
||||
},
|
||||
"node_modules/@types/hast": {
|
||||
"version": "3.0.4",
|
||||
@@ -1373,6 +1363,13 @@
|
||||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/md5": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.6.tgz",
|
||||
"integrity": "sha512-WD69gNXtRBnpknfZcb4TRQ0XJQbUPZcai/Qdhmka3sxUR3Et8NrXoeAoknG/LghYHTf4ve795rInVYHBTQdNVA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||
@@ -1427,6 +1424,23 @@
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -1485,51 +1499,6 @@
|
||||
"@oven/bun-windows-x64-baseline": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1",
|
||||
"color-string": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.5.0"
|
||||
}
|
||||
},
|
||||
"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/color-string": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "14.0.2",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz",
|
||||
@@ -1545,20 +1514,6 @@
|
||||
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
|
||||
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/deno": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/deno/-/deno-2.5.6.tgz",
|
||||
@@ -1600,6 +1555,20 @@
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
@@ -1613,110 +1582,6 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/error-stack-parser-es": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
|
||||
"integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz",
|
||||
"integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.27.0",
|
||||
"@esbuild/android-arm": "0.27.0",
|
||||
"@esbuild/android-arm64": "0.27.0",
|
||||
"@esbuild/android-x64": "0.27.0",
|
||||
"@esbuild/darwin-arm64": "0.27.0",
|
||||
"@esbuild/darwin-x64": "0.27.0",
|
||||
"@esbuild/freebsd-arm64": "0.27.0",
|
||||
"@esbuild/freebsd-x64": "0.27.0",
|
||||
"@esbuild/linux-arm": "0.27.0",
|
||||
"@esbuild/linux-arm64": "0.27.0",
|
||||
"@esbuild/linux-ia32": "0.27.0",
|
||||
"@esbuild/linux-loong64": "0.27.0",
|
||||
"@esbuild/linux-mips64el": "0.27.0",
|
||||
"@esbuild/linux-ppc64": "0.27.0",
|
||||
"@esbuild/linux-riscv64": "0.27.0",
|
||||
"@esbuild/linux-s390x": "0.27.0",
|
||||
"@esbuild/linux-x64": "0.27.0",
|
||||
"@esbuild/netbsd-arm64": "0.27.0",
|
||||
"@esbuild/netbsd-x64": "0.27.0",
|
||||
"@esbuild/openbsd-arm64": "0.27.0",
|
||||
"@esbuild/openbsd-x64": "0.27.0",
|
||||
"@esbuild/openharmony-arm64": "0.27.0",
|
||||
"@esbuild/sunos-x64": "0.27.0",
|
||||
"@esbuild/win32-arm64": "0.27.0",
|
||||
"@esbuild/win32-ia32": "0.27.0",
|
||||
"@esbuild/win32-x64": "0.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/exit-hook": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz",
|
||||
"integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-to-regexp": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
|
||||
"integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/kleur": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
||||
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
@@ -1752,6 +1617,26 @@
|
||||
"markdown-it": "bin/markdown-it.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/md5": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"charenc": "0.0.2",
|
||||
"crypt": "0.0.2",
|
||||
"is-buffer": "~1.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||
@@ -1759,46 +1644,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare": {
|
||||
"version": "4.20251217.0",
|
||||
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20251217.0.tgz",
|
||||
"integrity": "sha512-8xsTQbPS6YV+ABZl9qiJIbsum6hbpbhqiyKpOVdzZrhK+1N8EFpT8R6aBZff7kezGmxYZSntjgjqTwJmj3JLgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "0.8.1",
|
||||
"acorn": "8.14.0",
|
||||
"acorn-walk": "8.3.2",
|
||||
"exit-hook": "2.2.1",
|
||||
"glob-to-regexp": "0.4.1",
|
||||
"sharp": "^0.33.5",
|
||||
"stoppable": "1.1.0",
|
||||
"undici": "7.14.0",
|
||||
"workerd": "1.20251217.0",
|
||||
"ws": "8.18.0",
|
||||
"youch": "4.1.0-beta.10",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"bin": {
|
||||
"miniflare": "bootstrap.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
@@ -1821,20 +1666,6 @@
|
||||
"integrity": "sha512-r9dVsfnmykIYovQH9oP2JeZu5/DYa9rZpSQw2FWR/VDoI0oxm/u21NF46bFH5h+x2Xv4q5+jHzsX95N6m0UO3Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
|
||||
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||
@@ -2002,16 +1833,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unenv": {
|
||||
"version": "2.0.0-rc.24",
|
||||
"resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz",
|
||||
"integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pathe": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/web-worker": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz",
|
||||
|
||||
@@ -29,13 +29,18 @@
|
||||
"docs": "typedoc src/lib/packwiz.ts --tsconfig src/lib/tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sugoidogo/js-util": "^0.3.0",
|
||||
"commander": "^14.0.2",
|
||||
"confbox": "^0.2.2",
|
||||
"curseforge-v2": "^1.5.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"md5": "^2.3.0",
|
||||
"murmur2": "^1.0.2",
|
||||
"native-file-system-adapter": "file:../native-file-system-adapter/native-file-system-adapter-3.0.1.tgz",
|
||||
"workerless": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/md5": "^2.3.6",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/web": "^0.0.294",
|
||||
"bun": "^1.3.3",
|
||||
|
||||
+125
-97
@@ -305,130 +305,125 @@ export interface Mod {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Return type of all exported functions, describing how to apply the function result */
|
||||
export interface Result {
|
||||
/** files to be deleted */
|
||||
delete?: Path[]
|
||||
/** files to be written with TOML data */
|
||||
write?: { [key: Path]: Mod | Index | Pack }
|
||||
}
|
||||
|
||||
import WorkerlessPool from 'workerless'
|
||||
import { parseTOML } from 'confbox'
|
||||
import { parseTOML, stringifyTOML } from 'confbox'
|
||||
import { CFV2Client } from 'curseforge-v2'
|
||||
import { forAsync } from '@sugoidogo/js-util'
|
||||
|
||||
// copied from https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API/Non-cryptographic_uses_of_subtle_crypto#hashing_a_file
|
||||
async function sha1(arrayBuffer: BufferSource) {
|
||||
const hashAsArrayBuffer = await crypto.subtle.digest("SHA-1", arrayBuffer);
|
||||
const uint8ViewOfHash = new Uint8Array(hashAsArrayBuffer);
|
||||
// @ts-ignore
|
||||
if (uint8ViewOfHash.toHex) {
|
||||
export async function getHash(data: Uint8Array<ArrayBuffer>, format:HashFormat): Promise<Hash> {
|
||||
if (format.startsWith('sha')) { // copied from https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API/Non-cryptographic_uses_of_subtle_crypto#hashing_a_file
|
||||
const hashAsArrayBuffer = await crypto.subtle.digest("SHA-" + format.substring(3), data);
|
||||
const uint8ViewOfHash = new Uint8Array(hashAsArrayBuffer);
|
||||
// @ts-ignore
|
||||
return uint8ViewOfHash.toHex() as string;
|
||||
if (uint8ViewOfHash.toHex) {
|
||||
// @ts-ignore
|
||||
return uint8ViewOfHash.toHex() as string;
|
||||
}
|
||||
const hashAsString = Array.from(uint8ViewOfHash)
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
return hashAsString;
|
||||
}
|
||||
const hashAsString = Array.from(uint8ViewOfHash)
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
return hashAsString;
|
||||
}
|
||||
|
||||
async function fetch_toml(url: string, options?: RequestInit) {
|
||||
const response = await fetch_ok(url, options)
|
||||
const text = await response.text()
|
||||
return parseTOML(text) as any
|
||||
}
|
||||
|
||||
async function fetch_json(url: string, options?: RequestInit) {
|
||||
const response = await fetch_ok(url, options)
|
||||
const json = await response.json()
|
||||
return json as any
|
||||
}
|
||||
|
||||
async function fetch_bytes(url: string, options?: RequestInit) {
|
||||
const response = await fetch_ok(url, options)
|
||||
const bytes = await response.bytes()
|
||||
return bytes
|
||||
}
|
||||
|
||||
async function fetch_ok(url: string, options?: RequestInit) {
|
||||
try {
|
||||
new URL(url)
|
||||
} catch {
|
||||
// @ts-expect-error
|
||||
const fs = await import('node:fs/promises')
|
||||
const body = await fs.readFile(url)
|
||||
return new Response(body)
|
||||
if (format === "murmur2") {
|
||||
const { default: murmurHash } = await import('murmur2')
|
||||
return murmurHash(data, 1, true).toString()
|
||||
}
|
||||
const response = await fetch(url, options)
|
||||
if (!response.ok) {
|
||||
throw new Error(`${url}: ${response.statusText}`, { 'cause': response })
|
||||
if (format === "md5") {
|
||||
const md5 = await import('md5')
|
||||
return md5(data)
|
||||
}
|
||||
return response
|
||||
throw new Error('unknown hash format: '+format)
|
||||
}
|
||||
|
||||
function dirname(path: string) {
|
||||
if (!path.startsWith('http') && !path.startsWith('./')) path = './' + path
|
||||
function splitPath(path: string) {
|
||||
path=path.replaceAll('\\','/')
|
||||
const path_segments = path.split('/')
|
||||
path_segments.pop()
|
||||
path = path_segments.join('/')
|
||||
return path
|
||||
const basename = path_segments.pop()
|
||||
const dirname = path_segments.join('/')
|
||||
return {dirname,basename}
|
||||
}
|
||||
|
||||
async function getDirectoryHandle(dir: FileSystemDirectoryHandle, path: string) {
|
||||
if (!path) {
|
||||
return dir
|
||||
}
|
||||
path = path.replaceAll('\\', '/')
|
||||
const path_segments = path.split('/')
|
||||
for (const segment of path_segments) {
|
||||
dir = await dir.getDirectoryHandle(segment)
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
async function getFileHandle(dir: FileSystemDirectoryHandle, path: string, options?:FileSystemGetFileOptions) {
|
||||
const { dirname, basename } = splitPath(path)
|
||||
dir = await getDirectoryHandle(dir, dirname)
|
||||
return dir.getFileHandle(basename,options)
|
||||
}
|
||||
|
||||
async function removeEntry(dir: FileSystemDirectoryHandle, path: string) {
|
||||
const { dirname, basename } = splitPath(path)
|
||||
dir = await getDirectoryHandle(dir, dirname)
|
||||
return dir.removeEntry(basename)
|
||||
}
|
||||
|
||||
/**
|
||||
* detect files availible on curseforge
|
||||
* @param pack_url url or path to the pack.toml file
|
||||
* @param cfApiKey curseforge api key
|
||||
* @param size_min minimum size for a file to be checked
|
||||
*/
|
||||
export async function cfDetect(pack_url: string, cfApiKey: string, size_min = 4096): Promise<Result> {
|
||||
export async function cfDetect(pack_dir:FileSystemDirectoryHandle, cfApiKey: string, pack_file_name='pack.toml', size_min = 4096): Promise<void> {
|
||||
console.log('loading pack')
|
||||
const pack = await fetch_toml(pack_url) as Pack
|
||||
const pack_dir = dirname(pack_url)
|
||||
const index_url = `${pack_dir}/${pack.index.file}`
|
||||
const pack_file_handle = await getFileHandle(pack_dir,pack_file_name)
|
||||
const pack:Pack = await pack_file_handle.getFile()
|
||||
.then(file => file.text())
|
||||
.then(text => parseTOML(text))
|
||||
console.log('loading index')
|
||||
const index = await fetch_toml(index_url) as Index
|
||||
const index_dir = dirname(index_url)
|
||||
const { dirname: index_dir_name, basename: index_file_name } = splitPath(pack.index.file)
|
||||
let index_dir: FileSystemDirectoryHandle;
|
||||
if (!index_dir_name) index_dir = pack_dir
|
||||
else index_dir = await getDirectoryHandle(pack_dir,index_dir_name)
|
||||
const index_file_handle = await getFileHandle(index_dir,index_file_name)
|
||||
const index:Index = await index_file_handle.getFile()
|
||||
.then(file => file.text())
|
||||
.then(text => parseTOML(text))
|
||||
if (!index.files) {
|
||||
console.warn(`${index_url} has no files indexed`)
|
||||
return {}
|
||||
console.warn(`modpack has no files indexed`)
|
||||
return
|
||||
}
|
||||
const file_hash_map = new Map<number, Path>()
|
||||
console.log('hashing files')
|
||||
const workerlessPool = new WorkerlessPool()
|
||||
await Promise.all(index.files.map(async file => {
|
||||
if (file.metafile) return
|
||||
const file_url = `${index_dir}/${file.file}`
|
||||
const file_data = await fetch_bytes(file_url)
|
||||
if (file_data.length < size_min) return
|
||||
const hash = await workerlessPool.run(async function (buffer: Uint8Array) {
|
||||
const murmurHash = await import('murmur2').then(module => module.default)
|
||||
return murmurHash(buffer, 1, true)
|
||||
}, file_data)
|
||||
file_hash_map.set(hash, file_url)
|
||||
}))
|
||||
await forAsync(index.files, async function (entry) {
|
||||
if (entry.metafile) return
|
||||
const file = await getFileHandle(index_dir, entry.file)
|
||||
.then(handle => handle.getFile())
|
||||
if (file.size < size_min) return
|
||||
const file_data = await file.arrayBuffer().then(buffer => new Uint8Array(buffer))
|
||||
const hash = await workerlessPool.run(getHash, file_data, "murmur2").then(hash => Number(hash))
|
||||
file_hash_map.set(hash, entry.file)
|
||||
})
|
||||
workerlessPool.terminate()
|
||||
console.log('requesting matches from curseforge')
|
||||
const response = await fetch_json('https://api.curseforge.com/v1/fingerprints', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
'x-api-key': cfApiKey
|
||||
},
|
||||
body: JSON.stringify({ fingerprints: Array.from(file_hash_map.keys()) })
|
||||
})
|
||||
const result: Result = { delete: [], write: {} }
|
||||
for (const match of response.data.exactMatches) {
|
||||
let file_url = file_hash_map.get(match.file.fileFingerprint)
|
||||
if (!file_url) {
|
||||
const response = await new CFV2Client({ apiKey: cfApiKey }).getFingerprintMatches({ 'fingerprints': Array.from(file_hash_map.keys()) })
|
||||
const textEncoder = new TextEncoder()
|
||||
let resultCount=0
|
||||
await forAsync(Object.values(response.data.data.exactMatches),async function(match){
|
||||
let file_path = file_hash_map.get(match.file.fileFingerprint)
|
||||
if (!file_path) {
|
||||
for (const module of match.file.modules) {
|
||||
file_url = file_hash_map.get(module.fingerprint)
|
||||
file_path = file_hash_map.get(module.fingerprint)
|
||||
}
|
||||
if (file_url) {
|
||||
console.debug("skipping module match, this is probably a false positive", file_url, match.file)
|
||||
if (file_path) {
|
||||
console.debug("skipping module match, this is probably a false positive", file_path, match.file)
|
||||
} else {
|
||||
console.debug("curseforge returned a result we didn't ask for, skipping", match.file)
|
||||
}
|
||||
continue
|
||||
return
|
||||
}
|
||||
const { dirname: file_dir_name, basename: file_name } = splitPath(file_path)
|
||||
for (const hash of match.file.hashes) {
|
||||
if (hash.algo === 1) {
|
||||
const mod: Mod = {
|
||||
@@ -446,14 +441,47 @@ export async function cfDetect(pack_url: string, cfApiKey: string, size_min = 40
|
||||
}
|
||||
}
|
||||
}
|
||||
result.delete.push(file_url)
|
||||
result.write[index_dir+'/'+match.file.displayName.toLowerCase().replaceAll(' ', '-')] = mod
|
||||
const new_file_path = file_dir_name + '/' + encodeURI(match.file.displayName.toLowerCase().replaceAll(' ', '-') + '.pw.toml')
|
||||
const new_file_data = textEncoder.encode(stringifyTOML(mod))
|
||||
const new_file_hash = await getHash(new_file_data, index['hash-format'])
|
||||
await getFileHandle(index_dir,new_file_path,{'create':true})
|
||||
.then(handle => handle.createWritable())
|
||||
.then(stream => {
|
||||
stream.write(new_file_data)
|
||||
stream.close()
|
||||
index.files.push({
|
||||
"file": new_file_path,
|
||||
"hash": new_file_hash.toString(),
|
||||
"metafile": true
|
||||
})
|
||||
})
|
||||
await removeEntry(index_dir,file_path)
|
||||
.then(() => {
|
||||
for (let i = 0; i < index.files.length; i++){
|
||||
if (index.files[i].file != file_path) continue
|
||||
index.files.splice(i, 1)
|
||||
return
|
||||
}
|
||||
throw new Error("can't find removed file in index")
|
||||
})
|
||||
resultCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`found ${result.delete.length} matching files`)
|
||||
return result
|
||||
})
|
||||
const index_file_data=textEncoder.encode(stringifyTOML(index))
|
||||
const index_file_hash = await getHash(index_file_data, pack.index['hash-format'])
|
||||
await index_file_handle.createWritable()
|
||||
.then(async stream => {
|
||||
await stream.write(index_file_data)
|
||||
await stream.close()
|
||||
pack.index.hash = index_file_hash.toString()
|
||||
return pack_file_handle.createWritable()
|
||||
}).then(async stream => {
|
||||
await stream.write(stringifyTOML(pack))
|
||||
await stream.close()
|
||||
})
|
||||
console.log(`found ${resultCount} matching files`)
|
||||
}
|
||||
/**
|
||||
* detect files availible on modrinth
|
||||
|
||||
Reference in New Issue
Block a user