chore: more refactorings and optimizations
This commit is contained in:
72
package-lock.json
generated
72
package-lock.json
generated
@@ -178,7 +178,6 @@
|
|||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@@ -751,7 +750,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.1.tgz",
|
||||||
"integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==",
|
"integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
"@codemirror/view": "^6.23.0",
|
"@codemirror/view": "^6.23.0",
|
||||||
@@ -828,7 +826,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz",
|
||||||
"integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==",
|
"integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@marijn/find-cluster-break": "^1.0.0"
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -850,7 +847,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.13.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.13.tgz",
|
||||||
"integrity": "sha512-QBO8ZsgJLCbI28KdY0/oDy5NQLqOQVZCozBknxc2/7L98V+TVYFHnfaCsnGh1U+alpd2LOkStVwYY7nW2R1xbw==",
|
"integrity": "sha512-QBO8ZsgJLCbI28KdY0/oDy5NQLqOQVZCozBknxc2/7L98V+TVYFHnfaCsnGh1U+alpd2LOkStVwYY7nW2R1xbw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
"crelt": "^1.0.6",
|
"crelt": "^1.0.6",
|
||||||
@@ -946,7 +942,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.19.0"
|
"node": ">=20.19.0"
|
||||||
},
|
},
|
||||||
@@ -987,7 +982,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.19.0"
|
"node": ">=20.19.0"
|
||||||
}
|
}
|
||||||
@@ -1382,6 +1376,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-dirname": "^0.1.0",
|
"cross-dirname": "^0.1.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
@@ -1403,6 +1398,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
@@ -1419,6 +1415,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1433,6 +1430,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
@@ -3980,7 +3978,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.17.0.tgz",
|
||||||
"integrity": "sha512-TLjSU9Otdpq0SpKHl1tD1Nc9MKhrsZbCFGot3EbCxRa8m1E5R1mMwoOjKMMM31IyF7fr+hPNHLpYfwbMKNusmg==",
|
"integrity": "sha512-TLjSU9Otdpq0SpKHl1tD1Nc9MKhrsZbCFGot3EbCxRa8m1E5R1mMwoOjKMMM31IyF7fr+hPNHLpYfwbMKNusmg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@libsql/core": "^0.17.0",
|
"@libsql/core": "^0.17.0",
|
||||||
"@libsql/hrana-client": "^0.9.0",
|
"@libsql/hrana-client": "^0.9.0",
|
||||||
@@ -5187,7 +5184,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
@@ -5416,7 +5414,6 @@
|
|||||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@@ -5427,7 +5424,6 @@
|
|||||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
@@ -5535,7 +5531,6 @@
|
|||||||
"integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==",
|
"integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.56.0",
|
"@typescript-eslint/scope-manager": "8.56.0",
|
||||||
"@typescript-eslint/types": "8.56.0",
|
"@typescript-eslint/types": "8.56.0",
|
||||||
@@ -5915,7 +5910,6 @@
|
|||||||
"integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==",
|
"integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "4.0.18",
|
"@vitest/utils": "4.0.18",
|
||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
@@ -6102,7 +6096,6 @@
|
|||||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -6136,7 +6129,6 @@
|
|||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@@ -6659,7 +6651,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -7355,7 +7346,8 @@
|
|||||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
@@ -7492,8 +7484,7 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/d3-cloud": {
|
"node_modules/d3-cloud": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
@@ -7771,7 +7762,6 @@
|
|||||||
"integrity": "sha512-uOOBA3f+kW3o4KpSoMQ6SNpdXU7WtxlJRb9vCZgOvqhTz4b3GjcoWKstdisizNZLsylhTMv8TLHFPFW0Uxsj/g==",
|
"integrity": "sha512-uOOBA3f+kW3o4KpSoMQ6SNpdXU7WtxlJRb9vCZgOvqhTz4b3GjcoWKstdisizNZLsylhTMv8TLHFPFW0Uxsj/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "26.7.0",
|
"app-builder-lib": "26.7.0",
|
||||||
"builder-util": "26.4.1",
|
"builder-util": "26.4.1",
|
||||||
@@ -7873,7 +7863,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/dompurify": {
|
"node_modules/dompurify": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
@@ -8346,6 +8337,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/asar": "^3.2.1",
|
"@electron/asar": "^3.2.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
@@ -8366,6 +8358,7 @@
|
|||||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
@@ -8392,6 +8385,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/encoding": {
|
||||||
|
"version": "0.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||||
|
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": "^0.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.5",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||||
@@ -8499,7 +8502,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
},
|
},
|
||||||
@@ -8577,7 +8579,6 @@
|
|||||||
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -10085,7 +10086,6 @@
|
|||||||
"integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==",
|
"integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@acemir/cssom": "^0.9.31",
|
"@acemir/cssom": "^0.9.31",
|
||||||
"@asamuzakjp/dom-selector": "^6.7.6",
|
"@asamuzakjp/dom-selector": "^6.7.6",
|
||||||
@@ -10421,6 +10421,7 @@
|
|||||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"lz-string": "bin/bin.js"
|
"lz-string": "bin/bin.js"
|
||||||
}
|
}
|
||||||
@@ -11690,6 +11691,7 @@
|
|||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
},
|
},
|
||||||
@@ -11702,7 +11704,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
|
||||||
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
|
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dompurify": "3.2.7",
|
"dompurify": "3.2.7",
|
||||||
"marked": "14.0.0"
|
"marked": "14.0.0"
|
||||||
@@ -12264,7 +12265,6 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -12340,6 +12340,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^9.4.0"
|
"commander": "^9.4.0"
|
||||||
},
|
},
|
||||||
@@ -12357,6 +12358,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
@@ -12377,6 +12379,7 @@
|
|||||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1",
|
"ansi-regex": "^5.0.1",
|
||||||
"ansi-styles": "^5.0.0",
|
"ansi-styles": "^5.0.0",
|
||||||
@@ -12392,6 +12395,7 @@
|
|||||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
@@ -12546,7 +12550,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"orderedmap": "^2.0.0"
|
"orderedmap": "^2.0.0"
|
||||||
}
|
}
|
||||||
@@ -12580,7 +12583,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-model": "^1.0.0",
|
"prosemirror-model": "^1.0.0",
|
||||||
"prosemirror-transform": "^1.0.0",
|
"prosemirror-transform": "^1.0.0",
|
||||||
@@ -12614,7 +12616,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz",
|
||||||
"integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==",
|
"integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-model": "^1.20.0",
|
"prosemirror-model": "^1.20.0",
|
||||||
"prosemirror-state": "^1.0.0",
|
"prosemirror-state": "^1.0.0",
|
||||||
@@ -12692,7 +12693,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -12758,7 +12758,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -12788,7 +12787,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.18.0",
|
"version": "0.18.0",
|
||||||
@@ -13093,6 +13093,7 @@
|
|||||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
},
|
},
|
||||||
@@ -13835,6 +13836,7 @@
|
|||||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"rimraf": "~2.6.2"
|
"rimraf": "~2.6.2"
|
||||||
@@ -14114,8 +14116,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "0BSD",
|
"license": "0BSD"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.21.0",
|
"version": "4.21.0",
|
||||||
@@ -14665,7 +14666,6 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -14873,7 +14873,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||||
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
@@ -14956,7 +14955,6 @@
|
|||||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -15516,7 +15514,6 @@
|
|||||||
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
|
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "4.0.18",
|
"@vitest/expect": "4.0.18",
|
||||||
"@vitest/mocker": "4.0.18",
|
"@vitest/mocker": "4.0.18",
|
||||||
@@ -15594,7 +15591,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.28.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.28.tgz",
|
||||||
"integrity": "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==",
|
"integrity": "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.28",
|
"@vue/compiler-dom": "3.5.28",
|
||||||
"@vue/compiler-sfc": "3.5.28",
|
"@vue/compiler-sfc": "3.5.28",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { getPicoStylesheetHref, sanitizePicoTheme, type PicoThemeName } from '..
|
|||||||
import type { MenuDocument } from './MenuEngine';
|
import type { MenuDocument } from './MenuEngine';
|
||||||
import type { ProjectMetadata } from './MetaEngine';
|
import type { ProjectMetadata } from './MetaEngine';
|
||||||
import { loadPublishedGenerationSets } from './GenerationPostSnapshotService';
|
import { loadPublishedGenerationSets } from './GenerationPostSnapshotService';
|
||||||
import { buildSitemapAndFeeds } from './GenerationSitemapFeedService';
|
import { buildSitemapAndFeeds, collectSitemapArchiveMetadata } from './GenerationSitemapFeedService';
|
||||||
import { buildTargetedValidationPlan, planMissingValidationPaths } from './ValidationApplyPlannerService';
|
import { buildTargetedValidationPlan, planMissingValidationPaths } from './ValidationApplyPlannerService';
|
||||||
import { compareSitemapToHtml } from './SiteValidationDiffService';
|
import { compareSitemapToHtml } from './SiteValidationDiffService';
|
||||||
import {
|
import {
|
||||||
@@ -218,30 +218,57 @@ export class BlogGenerationEngine {
|
|||||||
|
|
||||||
const generationPostIndex = buildGenerationPostIndex(publishedListPosts);
|
const generationPostIndex = buildGenerationPostIndex(publishedListPosts);
|
||||||
|
|
||||||
onProgress(5, 'Building sitemap XML...');
|
let allTags = new Set<string>();
|
||||||
const {
|
let allCategories = new Set<string>();
|
||||||
allTags,
|
let yearMonths = new Map<string, Date>();
|
||||||
allCategories,
|
let years = new Map<number, Date>();
|
||||||
yearMonths,
|
let yearMonthDays = new Map<string, Date>();
|
||||||
years,
|
let urls: string[] = [];
|
||||||
yearMonthDays,
|
let sitemapXml = '';
|
||||||
urls,
|
let rssXml = '';
|
||||||
sitemapXml,
|
let atomXml = '';
|
||||||
rssXml,
|
let feedPosts: PostData[] = [];
|
||||||
atomXml,
|
|
||||||
feedPosts,
|
|
||||||
} = buildSitemapAndFeeds({
|
|
||||||
baseUrl: options.baseUrl,
|
|
||||||
projectName: options.projectName,
|
|
||||||
projectDescription: options.projectDescription,
|
|
||||||
maxPostsPerPage,
|
|
||||||
publishedPosts,
|
|
||||||
publishedListPosts,
|
|
||||||
postIndex: generationPostIndex,
|
|
||||||
includeFeeds: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
onProgress(8, 'Building RSS and Atom feeds...');
|
if (includeCore) {
|
||||||
|
onProgress(5, 'Building sitemap XML...');
|
||||||
|
const sitemapAndFeedResult = buildSitemapAndFeeds({
|
||||||
|
baseUrl: options.baseUrl,
|
||||||
|
projectName: options.projectName,
|
||||||
|
projectDescription: options.projectDescription,
|
||||||
|
maxPostsPerPage,
|
||||||
|
publishedPosts,
|
||||||
|
publishedListPosts,
|
||||||
|
postIndex: generationPostIndex,
|
||||||
|
includeFeeds: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
allTags = sitemapAndFeedResult.allTags;
|
||||||
|
allCategories = sitemapAndFeedResult.allCategories;
|
||||||
|
yearMonths = sitemapAndFeedResult.yearMonths;
|
||||||
|
years = sitemapAndFeedResult.years;
|
||||||
|
yearMonthDays = sitemapAndFeedResult.yearMonthDays;
|
||||||
|
urls = sitemapAndFeedResult.urls;
|
||||||
|
sitemapXml = sitemapAndFeedResult.sitemapXml;
|
||||||
|
rssXml = sitemapAndFeedResult.rssXml;
|
||||||
|
atomXml = sitemapAndFeedResult.atomXml;
|
||||||
|
feedPosts = sitemapAndFeedResult.feedPosts;
|
||||||
|
|
||||||
|
onProgress(8, 'Building RSS and Atom feeds...');
|
||||||
|
} else if (includeCategory || includeTag || includeDate) {
|
||||||
|
const archiveMetadata = collectSitemapArchiveMetadata({
|
||||||
|
baseUrl: options.baseUrl,
|
||||||
|
maxPostsPerPage,
|
||||||
|
publishedPosts,
|
||||||
|
publishedListPosts,
|
||||||
|
});
|
||||||
|
|
||||||
|
allTags = archiveMetadata.allTags;
|
||||||
|
allCategories = archiveMetadata.allCategories;
|
||||||
|
yearMonths = archiveMetadata.yearMonths;
|
||||||
|
years = archiveMetadata.years;
|
||||||
|
yearMonthDays = archiveMetadata.yearMonthDays;
|
||||||
|
feedPosts = archiveMetadata.feedPosts;
|
||||||
|
}
|
||||||
|
|
||||||
const htmlDir = path.join(options.dataDir, 'html');
|
const htmlDir = path.join(options.dataDir, 'html');
|
||||||
await fs.mkdir(htmlDir, { recursive: true });
|
await fs.mkdir(htmlDir, { recursive: true });
|
||||||
@@ -321,11 +348,16 @@ export class BlogGenerationEngine {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const knownOutputDirectories = new Set<string>();
|
||||||
|
const generatedHashCache = new Map<string, string | null>();
|
||||||
|
|
||||||
const writePage = (projectId: string, urlPath: string, content: string) => writeHtmlPage({
|
const writePage = (projectId: string, urlPath: string, content: string) => writeHtmlPage({
|
||||||
projectId,
|
projectId,
|
||||||
htmlDir,
|
htmlDir,
|
||||||
urlPath,
|
urlPath,
|
||||||
content,
|
content,
|
||||||
|
knownDirectories: knownOutputDirectories,
|
||||||
|
hashCache: generatedHashCache,
|
||||||
});
|
});
|
||||||
|
|
||||||
let pagesGenerated = 0;
|
let pagesGenerated = 0;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export async function writeFileIfHashChanged(params: {
|
|||||||
filePath: string;
|
filePath: string;
|
||||||
relativePath: string;
|
relativePath: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
hashCache?: Map<string, string | null>;
|
||||||
getGeneratedFileHash?: (projectId: string, relativePath: string) => Promise<string | null>;
|
getGeneratedFileHash?: (projectId: string, relativePath: string) => Promise<string | null>;
|
||||||
setGeneratedFileHash?: (projectId: string, relativePath: string, hash: string) => Promise<void>;
|
setGeneratedFileHash?: (projectId: string, relativePath: string, hash: string) => Promise<void>;
|
||||||
computeHash?: (content: string) => string;
|
computeHash?: (content: string) => string;
|
||||||
@@ -42,13 +43,21 @@ export async function writeFileIfHashChanged(params: {
|
|||||||
const hashFn = params.computeHash ?? computeContentHash;
|
const hashFn = params.computeHash ?? computeContentHash;
|
||||||
|
|
||||||
const hash = hashFn(params.content);
|
const hash = hashFn(params.content);
|
||||||
const previousHash = await getHash(params.projectId, params.relativePath);
|
let previousHash: string | null;
|
||||||
|
if (params.hashCache && params.hashCache.has(params.relativePath)) {
|
||||||
|
previousHash = params.hashCache.get(params.relativePath) ?? null;
|
||||||
|
} else {
|
||||||
|
previousHash = await getHash(params.projectId, params.relativePath);
|
||||||
|
params.hashCache?.set(params.relativePath, previousHash);
|
||||||
|
}
|
||||||
|
|
||||||
if (previousHash === hash) {
|
if (previousHash === hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(params.filePath, params.content, 'utf-8');
|
await fs.writeFile(params.filePath, params.content, 'utf-8');
|
||||||
await setHash(params.projectId, params.relativePath, hash);
|
await setHash(params.projectId, params.relativePath, hash);
|
||||||
|
params.hashCache?.set(params.relativePath, hash);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +66,9 @@ export async function writeHtmlPage(params: {
|
|||||||
htmlDir: string;
|
htmlDir: string;
|
||||||
urlPath: string;
|
urlPath: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
knownDirectories?: Set<string>;
|
||||||
|
hashCache?: Map<string, string | null>;
|
||||||
|
ensureDirectory?: (dirPath: string) => Promise<void>;
|
||||||
getGeneratedFileHash?: (projectId: string, relativePath: string) => Promise<string | null>;
|
getGeneratedFileHash?: (projectId: string, relativePath: string) => Promise<string | null>;
|
||||||
setGeneratedFileHash?: (projectId: string, relativePath: string, hash: string) => Promise<void>;
|
setGeneratedFileHash?: (projectId: string, relativePath: string, hash: string) => Promise<void>;
|
||||||
computeHash?: (content: string) => string;
|
computeHash?: (content: string) => string;
|
||||||
@@ -66,14 +78,26 @@ export async function writeHtmlPage(params: {
|
|||||||
? path.join(params.htmlDir, normalizedPath, 'index.html')
|
? path.join(params.htmlDir, normalizedPath, 'index.html')
|
||||||
: path.join(params.htmlDir, 'index.html');
|
: path.join(params.htmlDir, 'index.html');
|
||||||
const relativePath = normalizedPath ? `${normalizedPath}/index.html` : 'index.html';
|
const relativePath = normalizedPath ? `${normalizedPath}/index.html` : 'index.html';
|
||||||
|
const directoryPath = path.dirname(filePath);
|
||||||
|
const ensureDirectory = params.ensureDirectory ?? (async (dirPath: string) => {
|
||||||
|
await fs.mkdir(dirPath, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
if (params.knownDirectories) {
|
||||||
|
if (!params.knownDirectories.has(directoryPath)) {
|
||||||
|
await ensureDirectory(directoryPath);
|
||||||
|
params.knownDirectories.add(directoryPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await ensureDirectory(directoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
return writeFileIfHashChanged({
|
return writeFileIfHashChanged({
|
||||||
projectId: params.projectId,
|
projectId: params.projectId,
|
||||||
filePath,
|
filePath,
|
||||||
relativePath,
|
relativePath,
|
||||||
content: params.content,
|
content: params.content,
|
||||||
|
hashCache: params.hashCache,
|
||||||
getGeneratedFileHash: params.getGeneratedFileHash,
|
getGeneratedFileHash: params.getGeneratedFileHash,
|
||||||
setGeneratedFileHash: params.setGeneratedFileHash,
|
setGeneratedFileHash: params.setGeneratedFileHash,
|
||||||
computeHash: params.computeHash,
|
computeHash: params.computeHash,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { CategoryRenderSettings } from './PageRenderer';
|
import type { CategoryRenderSettings } from './PageRenderer';
|
||||||
|
import { buildCanonicalPostPath } from './PageRenderer';
|
||||||
import type { MenuDocument } from './MenuEngine';
|
import type { MenuDocument } from './MenuEngine';
|
||||||
import type { ProjectMetadata } from './MetaEngine';
|
import type { ProjectMetadata } from './MetaEngine';
|
||||||
import type { PostData } from './PostEngine';
|
import type { PostData } from './PostEngine';
|
||||||
@@ -15,6 +16,7 @@ interface RenderContext {
|
|||||||
};
|
};
|
||||||
metadata?: ProjectMetadata | null;
|
metadata?: ProjectMetadata | null;
|
||||||
menu?: MenuDocument;
|
menu?: MenuDocument;
|
||||||
|
skipContextSetup?: boolean;
|
||||||
maxPostsPerPage?: number;
|
maxPostsPerPage?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +91,10 @@ export function createPreviewBackedGenerationRouteRenderer(params: {
|
|||||||
projectDescription: params.options.projectDescription,
|
projectDescription: params.options.projectDescription,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
params.engines.postEngine.setProjectContext(projectContext.projectId, projectContext.dataDir);
|
||||||
|
params.engines.mediaEngine.setProjectContext?.(projectContext.projectId, projectContext.dataDir, projectContext.dataDir);
|
||||||
|
params.engines.postMediaEngine.setProjectContext(projectContext.projectId);
|
||||||
|
|
||||||
const mediaItemsPromiseCache = new Map<string, Promise<unknown[]>>();
|
const mediaItemsPromiseCache = new Map<string, Promise<unknown[]>>();
|
||||||
const postsByFilterPromiseCache = new Map<string, Promise<PostData[]>>();
|
const postsByFilterPromiseCache = new Map<string, Promise<PostData[]>>();
|
||||||
const publishedSnapshotByIdPromiseCache = new Map<string, Promise<PostData | null>>();
|
const publishedSnapshotByIdPromiseCache = new Map<string, Promise<PostData | null>>();
|
||||||
@@ -201,12 +207,42 @@ export function createPreviewBackedGenerationRouteRenderer(params: {
|
|||||||
getActiveProjectContext: async () => projectContext,
|
getActiveProjectContext: async () => projectContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const htmlRewriteContextPromise: Promise<{ canonicalPostPathBySlug: Map<string, string>; canonicalMediaPathBySourcePath: Map<string, string> }> = (async () => {
|
||||||
|
const canonicalPostPathBySlug = new Map<string, string>();
|
||||||
|
for (const post of params.publishedPostsForLookup) {
|
||||||
|
canonicalPostPathBySlug.set(post.slug, buildCanonicalPostPath(post));
|
||||||
|
}
|
||||||
|
|
||||||
|
const canonicalMediaPathBySourcePath = new Map<string, string>();
|
||||||
|
const mediaItems = await cachedMediaEngine.getAllMedia();
|
||||||
|
for (const media of mediaItems as Array<{ createdAt: Date; filename: string; originalName: string }>) {
|
||||||
|
const year = media.createdAt.getFullYear();
|
||||||
|
const month = String(media.createdAt.getMonth() + 1).padStart(2, '0');
|
||||||
|
const canonicalPath = `/media/${year}/${month}/${media.filename}`;
|
||||||
|
|
||||||
|
const originalNameKey = `media/${year}/${month}/${media.originalName}`.toLowerCase();
|
||||||
|
const filenameKey = `media/${year}/${month}/${media.filename}`.toLowerCase();
|
||||||
|
|
||||||
|
canonicalMediaPathBySourcePath.set(originalNameKey, canonicalPath);
|
||||||
|
canonicalMediaPathBySourcePath.set(filenameKey, canonicalPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
canonicalPostPathBySlug,
|
||||||
|
canonicalMediaPathBySourcePath,
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
return createGenerationRouteRenderer({
|
return createGenerationRouteRenderer({
|
||||||
renderWithContext: (pathname, context) => previewServer.renderRouteForContext(pathname, context),
|
renderWithContext: async (pathname, context) => previewServer.renderRouteForContext(pathname, {
|
||||||
|
...context,
|
||||||
|
htmlRewriteContext: await htmlRewriteContextPromise,
|
||||||
|
}),
|
||||||
context: {
|
context: {
|
||||||
projectContext,
|
projectContext,
|
||||||
metadata,
|
metadata,
|
||||||
menu,
|
menu,
|
||||||
|
skipContextSetup: true,
|
||||||
maxPostsPerPage: params.maxPostsPerPage,
|
maxPostsPerPage: params.maxPostsPerPage,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,6 +32,18 @@ export interface SitemapFeedBuildResult {
|
|||||||
feedPosts: PostData[];
|
feedPosts: PostData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SitemapArchiveMetadata {
|
||||||
|
allTags: Set<string>;
|
||||||
|
allCategories: Set<string>;
|
||||||
|
yearMonths: Map<string, Date>;
|
||||||
|
years: Map<number, Date>;
|
||||||
|
yearMonthDays: Map<string, Date>;
|
||||||
|
feedPosts: PostData[];
|
||||||
|
postUrls: Array<{ loc: string; lastmod: string }>;
|
||||||
|
pageUrls: Array<{ loc: string; lastmod: string }>;
|
||||||
|
latestPostUpdatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
function resolvePostCreatedAt(post: { createdAt: Date | string }): Date {
|
function resolvePostCreatedAt(post: { createdAt: Date | string }): Date {
|
||||||
if (post.createdAt instanceof Date) {
|
if (post.createdAt instanceof Date) {
|
||||||
return post.createdAt;
|
return post.createdAt;
|
||||||
@@ -142,19 +154,19 @@ function escapeCdata(value: string): string {
|
|||||||
return value.replace(/]]>/g, ']]]]><![CDATA[>');
|
return value.replace(/]]>/g, ']]]]><![CDATA[>');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildSitemapAndFeeds(params: BuildSitemapAndFeedsParams): SitemapFeedBuildResult {
|
export function collectSitemapArchiveMetadata(params: {
|
||||||
|
baseUrl: string;
|
||||||
|
maxPostsPerPage: number;
|
||||||
|
publishedPosts: PostData[];
|
||||||
|
publishedListPosts: PostData[];
|
||||||
|
}): SitemapArchiveMetadata {
|
||||||
const {
|
const {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
projectName,
|
|
||||||
projectDescription,
|
|
||||||
maxPostsPerPage,
|
maxPostsPerPage,
|
||||||
publishedPosts,
|
publishedPosts,
|
||||||
publishedListPosts,
|
publishedListPosts,
|
||||||
postIndex,
|
|
||||||
includeFeeds,
|
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
const now = new Date().toISOString();
|
|
||||||
const allTags = new Set<string>();
|
const allTags = new Set<string>();
|
||||||
const allCategories = new Set<string>();
|
const allCategories = new Set<string>();
|
||||||
const yearMonths = new Map<string, Date>();
|
const yearMonths = new Map<string, Date>();
|
||||||
@@ -206,7 +218,53 @@ export function buildSitemapAndFeeds(params: BuildSitemapAndFeedsParams): Sitema
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const now = new Date().toISOString();
|
||||||
const latestPostUpdatedAt = publishedListPosts[0]?.updatedAt.toISOString() || now;
|
const latestPostUpdatedAt = publishedListPosts[0]?.updatedAt.toISOString() || now;
|
||||||
|
const feedPosts = publishedListPosts.slice(0, maxPostsPerPage);
|
||||||
|
|
||||||
|
return {
|
||||||
|
allTags,
|
||||||
|
allCategories,
|
||||||
|
yearMonths,
|
||||||
|
years,
|
||||||
|
yearMonthDays,
|
||||||
|
feedPosts,
|
||||||
|
postUrls,
|
||||||
|
pageUrls,
|
||||||
|
latestPostUpdatedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildSitemapAndFeeds(params: BuildSitemapAndFeedsParams): SitemapFeedBuildResult {
|
||||||
|
const {
|
||||||
|
baseUrl,
|
||||||
|
projectName,
|
||||||
|
projectDescription,
|
||||||
|
maxPostsPerPage,
|
||||||
|
publishedPosts,
|
||||||
|
publishedListPosts,
|
||||||
|
postIndex,
|
||||||
|
includeFeeds,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const archiveMetadata = collectSitemapArchiveMetadata({
|
||||||
|
baseUrl,
|
||||||
|
maxPostsPerPage,
|
||||||
|
publishedPosts,
|
||||||
|
publishedListPosts,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
allTags,
|
||||||
|
allCategories,
|
||||||
|
yearMonths,
|
||||||
|
years,
|
||||||
|
yearMonthDays,
|
||||||
|
postUrls,
|
||||||
|
pageUrls,
|
||||||
|
latestPostUpdatedAt,
|
||||||
|
feedPosts,
|
||||||
|
} = archiveMetadata;
|
||||||
|
|
||||||
const urls: string[] = [];
|
const urls: string[] = [];
|
||||||
urls.push(buildSitemapUrl(`${baseUrl}/`, latestPostUpdatedAt, 'daily', '1.0'));
|
urls.push(buildSitemapUrl(`${baseUrl}/`, latestPostUpdatedAt, 'daily', '1.0'));
|
||||||
@@ -259,7 +317,6 @@ export function buildSitemapAndFeeds(params: BuildSitemapAndFeedsParams): Sitema
|
|||||||
'',
|
'',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
const feedPosts = publishedListPosts.slice(0, maxPostsPerPage);
|
|
||||||
if (!includeFeeds) {
|
if (!includeFeeds) {
|
||||||
return {
|
return {
|
||||||
allTags,
|
allTags,
|
||||||
|
|||||||
@@ -155,6 +155,10 @@ function normalizeCategorySettings(value: unknown): Record<string, CategoryRende
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isJsonParseError(error: unknown): boolean {
|
||||||
|
return error instanceof SyntaxError;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MetaEngine manages project metadata like available tags and categories.
|
* MetaEngine manages project metadata like available tags and categories.
|
||||||
*
|
*
|
||||||
@@ -447,6 +451,11 @@ export class MetaEngine extends EventEmitter {
|
|||||||
const parsed = JSON.parse(content) as ProjectMetadata;
|
const parsed = JSON.parse(content) as ProjectMetadata;
|
||||||
this.projectMetadata = normalizeProjectMetadata(parsed);
|
this.projectMetadata = normalizeProjectMetadata(parsed);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (isJsonParseError(error)) {
|
||||||
|
console.warn('[MetaEngine] Failed to parse project metadata JSON, using null metadata:', error);
|
||||||
|
this.projectMetadata = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||||
console.error('[MetaEngine] Failed to load project metadata:', error);
|
console.error('[MetaEngine] Failed to load project metadata:', error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -466,6 +475,10 @@ export class MetaEngine extends EventEmitter {
|
|||||||
const parsed = JSON.parse(content) as Record<string, unknown>;
|
const parsed = JSON.parse(content) as Record<string, unknown>;
|
||||||
return normalizeCategoryMetadata(parsed);
|
return normalizeCategoryMetadata(parsed);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (isJsonParseError(error)) {
|
||||||
|
console.warn('[MetaEngine] Failed to parse category metadata JSON, using default metadata merge:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||||
console.error('[MetaEngine] Failed to load category metadata:', error);
|
console.error('[MetaEngine] Failed to load category metadata:', error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -490,6 +503,11 @@ export class MetaEngine extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (isJsonParseError(error)) {
|
||||||
|
console.warn('[MetaEngine] Failed to parse categories JSON, treating as empty and rebuilding from DB/defaults:', error);
|
||||||
|
this.categories.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||||
console.error('[MetaEngine] Failed to load categories:', error);
|
console.error('[MetaEngine] Failed to load categories:', error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -653,6 +671,18 @@ export class MetaEngine extends EventEmitter {
|
|||||||
// Handle project metadata
|
// Handle project metadata
|
||||||
if (projectMetadataFileExists) {
|
if (projectMetadataFileExists) {
|
||||||
await this.loadProjectMetadata();
|
await this.loadProjectMetadata();
|
||||||
|
if (!this.projectMetadata) {
|
||||||
|
const projectData = await this.fetchProjectFromDatabase();
|
||||||
|
if (!projectData) {
|
||||||
|
throw new Error(`Project not found in database: ${this.currentProjectId}`);
|
||||||
|
}
|
||||||
|
this.projectMetadata = {
|
||||||
|
name: projectData.name,
|
||||||
|
description: projectData.description || undefined,
|
||||||
|
maxPostsPerPage: DEFAULT_MAX_POSTS_PER_PAGE,
|
||||||
|
};
|
||||||
|
await this.saveProjectMetadata();
|
||||||
|
}
|
||||||
if (this.projectMetadata?.dataPath !== undefined) {
|
if (this.projectMetadata?.dataPath !== undefined) {
|
||||||
const { dataPath: _dataPath, ...metadataWithoutDataPath } = this.projectMetadata;
|
const { dataPath: _dataPath, ...metadataWithoutDataPath } = this.projectMetadata;
|
||||||
this.projectMetadata = metadataWithoutDataPath;
|
this.projectMetadata = metadataWithoutDataPath;
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ export class PreviewServer {
|
|||||||
projectContext: ActiveProjectContext;
|
projectContext: ActiveProjectContext;
|
||||||
metadata?: ProjectMetadata | null;
|
metadata?: ProjectMetadata | null;
|
||||||
menu?: MenuDocument;
|
menu?: MenuDocument;
|
||||||
|
htmlRewriteContext?: HtmlRewriteContext;
|
||||||
|
skipContextSetup?: boolean;
|
||||||
maxPostsPerPage?: number;
|
maxPostsPerPage?: number;
|
||||||
requestTheme?: string | null;
|
requestTheme?: string | null;
|
||||||
htmlThemeAttribute?: string;
|
htmlThemeAttribute?: string;
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export interface SharedRouteRenderOptions {
|
|||||||
projectContext: SharedActiveProjectContext;
|
projectContext: SharedActiveProjectContext;
|
||||||
metadata?: ProjectMetadata | null;
|
metadata?: ProjectMetadata | null;
|
||||||
menu?: MenuDocument;
|
menu?: MenuDocument;
|
||||||
|
htmlRewriteContext?: HtmlRewriteContext;
|
||||||
|
skipContextSetup?: boolean;
|
||||||
maxPostsPerPage?: number;
|
maxPostsPerPage?: number;
|
||||||
requestTheme?: string | null;
|
requestTheme?: string | null;
|
||||||
htmlThemeAttribute?: string;
|
htmlThemeAttribute?: string;
|
||||||
@@ -267,11 +269,13 @@ export async function renderRouteWithSharedContext<TCategoryMetadata>(
|
|||||||
options: SharedRouteRenderOptions,
|
options: SharedRouteRenderOptions,
|
||||||
services: SharedRouteRenderServices<TCategoryMetadata>,
|
services: SharedRouteRenderServices<TCategoryMetadata>,
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
services.postEngine.setProjectContext(options.projectContext.projectId, options.projectContext.dataDir);
|
if (!options.skipContextSetup) {
|
||||||
services.mediaEngine.setProjectContext?.(options.projectContext.projectId, options.projectContext.dataDir, options.projectContext.dataDir);
|
services.postEngine.setProjectContext(options.projectContext.projectId, options.projectContext.dataDir);
|
||||||
services.postMediaEngine.setProjectContext(options.projectContext.projectId);
|
services.mediaEngine.setProjectContext?.(options.projectContext.projectId, options.projectContext.dataDir, options.projectContext.dataDir);
|
||||||
services.settingsEngine.setProjectContext(options.projectContext.projectId, options.projectContext.dataDir);
|
services.postMediaEngine.setProjectContext(options.projectContext.projectId);
|
||||||
services.menuEngine.setProjectContext(options.projectContext.projectId, options.projectContext.dataDir);
|
services.settingsEngine.setProjectContext(options.projectContext.projectId, options.projectContext.dataDir);
|
||||||
|
services.menuEngine.setProjectContext(options.projectContext.projectId, options.projectContext.dataDir);
|
||||||
|
}
|
||||||
|
|
||||||
let metadata = options.metadata;
|
let metadata = options.metadata;
|
||||||
if (metadata === undefined) {
|
if (metadata === undefined) {
|
||||||
@@ -292,7 +296,7 @@ export async function renderRouteWithSharedContext<TCategoryMetadata>(
|
|||||||
const appliedTheme = sanitizePicoTheme(options.requestTheme)
|
const appliedTheme = sanitizePicoTheme(options.requestTheme)
|
||||||
?? sanitizePicoTheme((metadata as { picoTheme?: unknown } | null)?.picoTheme);
|
?? sanitizePicoTheme((metadata as { picoTheme?: unknown } | null)?.picoTheme);
|
||||||
const picoStylesheetHref = getPicoStylesheetHref(appliedTheme);
|
const picoStylesheetHref = getPicoStylesheetHref(appliedTheme);
|
||||||
const htmlRewriteContext = await services.buildHtmlRewriteContext();
|
const htmlRewriteContext = options.htmlRewriteContext ?? await services.buildHtmlRewriteContext();
|
||||||
const normalizedPathname = decodeURIComponent(pathname.replace(/\/+$/, '') || '/');
|
const normalizedPathname = decodeURIComponent(pathname.replace(/\/+$/, '') || '/');
|
||||||
|
|
||||||
return resolveRouteWithSharedServices(normalizedPathname, maxPostsPerPage, htmlRewriteContext, {
|
return resolveRouteWithSharedServices(normalizedPathname, maxPostsPerPage, htmlRewriteContext, {
|
||||||
|
|||||||
@@ -640,6 +640,118 @@ describe('BlogGenerationEngine', () => {
|
|||||||
expect(filteredCallCount).toBeLessThanOrEqual(8);
|
expect(filteredCallCount).toBeLessThanOrEqual(8);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('skips core sitemap and feed build phases for single-only generation', async () => {
|
||||||
|
const posts: PostData[] = [];
|
||||||
|
for (let i = 0; i < 4; i += 1) {
|
||||||
|
posts.push(makePost({
|
||||||
|
id: `single-phase-${i}`,
|
||||||
|
slug: `single-phase-${i}`,
|
||||||
|
createdAt: new Date(`2025-${String(i + 1).padStart(2, '0')}-10T10:00:00Z`),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
setupPosts(posts);
|
||||||
|
|
||||||
|
const { BlogGenerationEngine } = await import('../../src/main/engine/BlogGenerationEngine');
|
||||||
|
const engine = new BlogGenerationEngine();
|
||||||
|
const onProgress = vi.fn();
|
||||||
|
|
||||||
|
await engine.generate({
|
||||||
|
projectId: 'test',
|
||||||
|
projectName: 'Test Blog',
|
||||||
|
dataDir: tempDir,
|
||||||
|
baseUrl: 'https://example.com',
|
||||||
|
sections: ['single'],
|
||||||
|
}, onProgress);
|
||||||
|
|
||||||
|
const progressMessages = onProgress.mock.calls.map((call) => String(call[1] ?? ''));
|
||||||
|
expect(progressMessages).not.toContain('Building sitemap XML...');
|
||||||
|
expect(progressMessages).not.toContain('Building RSS and Atom feeds...');
|
||||||
|
expect(progressMessages).not.toContain('Writing sitemap and feeds...');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips sitemap XML build phase for archive-only generation sections', async () => {
|
||||||
|
const posts: PostData[] = [];
|
||||||
|
for (let i = 0; i < 8; i += 1) {
|
||||||
|
posts.push(makePost({
|
||||||
|
id: `archive-only-${i}`,
|
||||||
|
slug: `archive-only-${i}`,
|
||||||
|
categories: [`cat-${i % 2}`],
|
||||||
|
tags: [`tag-${i % 3}`],
|
||||||
|
createdAt: new Date(`2025-${String((i % 4) + 1).padStart(2, '0')}-10T10:00:00Z`),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
setupPosts(posts);
|
||||||
|
|
||||||
|
const { BlogGenerationEngine } = await import('../../src/main/engine/BlogGenerationEngine');
|
||||||
|
const engine = new BlogGenerationEngine();
|
||||||
|
const onProgress = vi.fn();
|
||||||
|
|
||||||
|
await engine.generate({
|
||||||
|
projectId: 'test',
|
||||||
|
projectName: 'Test Blog',
|
||||||
|
dataDir: tempDir,
|
||||||
|
baseUrl: 'https://example.com',
|
||||||
|
sections: ['category', 'tag', 'date'],
|
||||||
|
}, onProgress);
|
||||||
|
|
||||||
|
const progressMessages = onProgress.mock.calls.map((call) => String(call[1] ?? ''));
|
||||||
|
expect(progressMessages).not.toContain('Building sitemap XML...');
|
||||||
|
expect(progressMessages).not.toContain('Building RSS and Atom feeds...');
|
||||||
|
expect(progressMessages).not.toContain('Writing sitemap and feeds...');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not rebuild canonical rewrite context for every generated html file', async () => {
|
||||||
|
const posts = [
|
||||||
|
makePost({ id: '1', slug: 'p1', categories: ['news'], tags: ['t1'], createdAt: new Date('2025-01-15T10:00:00Z') }),
|
||||||
|
makePost({ id: '2', slug: 'p2', categories: ['news'], tags: ['t2'], createdAt: new Date('2025-01-14T10:00:00Z') }),
|
||||||
|
makePost({ id: '3', slug: 'p3', categories: ['news'], tags: ['t3'], createdAt: new Date('2025-01-13T10:00:00Z') }),
|
||||||
|
];
|
||||||
|
|
||||||
|
setupPosts(posts);
|
||||||
|
|
||||||
|
const pageRendererModule = await import('../../src/main/engine/PageRenderer');
|
||||||
|
const canonicalPathSpy = vi.spyOn(pageRendererModule, 'buildCanonicalPostPath');
|
||||||
|
|
||||||
|
const { BlogGenerationEngine } = await import('../../src/main/engine/BlogGenerationEngine');
|
||||||
|
const engine = new BlogGenerationEngine();
|
||||||
|
|
||||||
|
await engine.generate({
|
||||||
|
projectId: 'test',
|
||||||
|
projectName: 'Test Blog',
|
||||||
|
dataDir: tempDir,
|
||||||
|
baseUrl: 'https://example.com',
|
||||||
|
maxPostsPerPage: 1,
|
||||||
|
}, vi.fn());
|
||||||
|
|
||||||
|
expect(canonicalPathSpy.mock.calls.length).toBeLessThanOrEqual(6);
|
||||||
|
canonicalPathSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not re-setup engine project context for every rendered html file', async () => {
|
||||||
|
const posts = [
|
||||||
|
makePost({ id: '1', slug: 'ctx-1', categories: ['news'], tags: ['t1'], createdAt: new Date('2025-01-15T10:00:00Z') }),
|
||||||
|
makePost({ id: '2', slug: 'ctx-2', categories: ['news'], tags: ['t2'], createdAt: new Date('2025-01-14T10:00:00Z') }),
|
||||||
|
makePost({ id: '3', slug: 'ctx-3', categories: ['news'], tags: ['t3'], createdAt: new Date('2025-01-13T10:00:00Z') }),
|
||||||
|
];
|
||||||
|
|
||||||
|
setupPosts(posts);
|
||||||
|
|
||||||
|
const { BlogGenerationEngine } = await import('../../src/main/engine/BlogGenerationEngine');
|
||||||
|
const engine = new BlogGenerationEngine();
|
||||||
|
|
||||||
|
await engine.generate({
|
||||||
|
projectId: 'test',
|
||||||
|
projectName: 'Test Blog',
|
||||||
|
dataDir: tempDir,
|
||||||
|
baseUrl: 'https://example.com',
|
||||||
|
maxPostsPerPage: 1,
|
||||||
|
}, vi.fn());
|
||||||
|
|
||||||
|
expect(mockPostEngine.setProjectContext.mock.calls.length).toBeLessThanOrEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
it('reduces repeated in-memory filtering across category tag and date generation', async () => {
|
it('reduces repeated in-memory filtering across category tag and date generation', async () => {
|
||||||
const posts: PostData[] = [];
|
const posts: PostData[] = [];
|
||||||
for (let i = 0; i < 30; i += 1) {
|
for (let i = 0; i < 30; i += 1) {
|
||||||
|
|||||||
@@ -72,4 +72,77 @@ describe('BlogGenerationOutputService', () => {
|
|||||||
const saved = await readFile(path.join(htmlDir, 'section', 'page', 'index.html'), 'utf-8');
|
const saved = await readFile(path.join(htmlDir, 'section', 'page', 'index.html'), 'utf-8');
|
||||||
expect(saved).toBe('<html/>');
|
expect(saved).toBe('<html/>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reuses in-run hash cache to avoid repeated hash reads for same file', async () => {
|
||||||
|
const tempRoot = path.join('/tmp', makeTempName());
|
||||||
|
await mkdir(tempRoot, { recursive: true });
|
||||||
|
const filePath = path.join(tempRoot, 'cached.txt');
|
||||||
|
const hashCache = new Map<string, string | null>();
|
||||||
|
|
||||||
|
const getHash = vi.fn().mockResolvedValue(null);
|
||||||
|
const setHash = vi.fn().mockResolvedValue(undefined);
|
||||||
|
const hashFn = vi.fn().mockReturnValue('h1');
|
||||||
|
|
||||||
|
await writeFileIfHashChanged({
|
||||||
|
projectId: 'p',
|
||||||
|
filePath,
|
||||||
|
relativePath: 'cached.txt',
|
||||||
|
content: 'hello',
|
||||||
|
getGeneratedFileHash: getHash,
|
||||||
|
setGeneratedFileHash: setHash,
|
||||||
|
computeHash: hashFn,
|
||||||
|
hashCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
await writeFileIfHashChanged({
|
||||||
|
projectId: 'p',
|
||||||
|
filePath,
|
||||||
|
relativePath: 'cached.txt',
|
||||||
|
content: 'hello',
|
||||||
|
getGeneratedFileHash: getHash,
|
||||||
|
setGeneratedFileHash: setHash,
|
||||||
|
computeHash: hashFn,
|
||||||
|
hashCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getHash).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('avoids repeated directory ensure calls when known directory cache is provided', async () => {
|
||||||
|
const tempRoot = path.join('/tmp', makeTempName());
|
||||||
|
const htmlDir = path.join(tempRoot, 'html');
|
||||||
|
await mkdir(htmlDir, { recursive: true });
|
||||||
|
|
||||||
|
const ensureDirectory = vi.fn(async (dirPath: string) => {
|
||||||
|
await mkdir(dirPath, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
const knownDirectories = new Set<string>();
|
||||||
|
|
||||||
|
await writeHtmlPage({
|
||||||
|
projectId: 'p',
|
||||||
|
htmlDir,
|
||||||
|
urlPath: 'section/page',
|
||||||
|
content: '<html/>',
|
||||||
|
getGeneratedFileHash: async () => null,
|
||||||
|
setGeneratedFileHash: async () => undefined,
|
||||||
|
computeHash: () => 'h',
|
||||||
|
ensureDirectory,
|
||||||
|
knownDirectories,
|
||||||
|
});
|
||||||
|
|
||||||
|
await writeHtmlPage({
|
||||||
|
projectId: 'p',
|
||||||
|
htmlDir,
|
||||||
|
urlPath: 'section/page',
|
||||||
|
content: '<html/>',
|
||||||
|
getGeneratedFileHash: async () => 'h',
|
||||||
|
setGeneratedFileHash: async () => undefined,
|
||||||
|
computeHash: () => 'h',
|
||||||
|
ensureDirectory,
|
||||||
|
knownDirectories,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(ensureDirectory).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -861,6 +861,47 @@ describe('MetaEngine', () => {
|
|||||||
expect(metadata?.categoryMetadata?.news?.title).toBe('Newsroom');
|
expect(metadata?.categoryMetadata?.news?.title).toBe('Newsroom');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should continue syncOnStartup when categories.json is malformed and recover from database categories', async () => {
|
||||||
|
const metaDir = metaEngine.getMetaDir();
|
||||||
|
mockFiles.set(normalizePath(`${metaDir}/project.json`), JSON.stringify({
|
||||||
|
name: 'Synced Project',
|
||||||
|
}));
|
||||||
|
mockFiles.set(normalizePath(`${metaDir}/categories.json`), '["news",');
|
||||||
|
|
||||||
|
mockPosts = [
|
||||||
|
{ categories: JSON.stringify(['db-cat']) },
|
||||||
|
];
|
||||||
|
|
||||||
|
await expect(metaEngine.syncOnStartup()).resolves.toBeUndefined();
|
||||||
|
|
||||||
|
const categories = await metaEngine.getCategories();
|
||||||
|
expect(categories).toContain('db-cat');
|
||||||
|
|
||||||
|
const metadata = await metaEngine.getProjectMetadata();
|
||||||
|
expect(metadata?.name).toBe('Synced Project');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should continue syncOnStartup when category-meta.json is malformed and keep valid project metadata', async () => {
|
||||||
|
const metaDir = metaEngine.getMetaDir();
|
||||||
|
mockFiles.set(normalizePath(`${metaDir}/project.json`), JSON.stringify({
|
||||||
|
name: 'Synced Project',
|
||||||
|
}));
|
||||||
|
mockFiles.set(normalizePath(`${metaDir}/categories.json`), JSON.stringify(['news']));
|
||||||
|
mockFiles.set(normalizePath(`${metaDir}/category-meta.json`), '{"news":');
|
||||||
|
|
||||||
|
await expect(metaEngine.syncOnStartup()).resolves.toBeUndefined();
|
||||||
|
|
||||||
|
const metadata = await metaEngine.getProjectMetadata() as any;
|
||||||
|
expect(metadata?.name).toBe('Synced Project');
|
||||||
|
expect(metadata?.categoryMetadata?.news).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
renderInLists: true,
|
||||||
|
showTitle: true,
|
||||||
|
title: 'news',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should create project.json with data from database during syncOnStartup if file does not exist', async () => {
|
it('should create project.json with data from database during syncOnStartup if file does not exist', async () => {
|
||||||
const metaDir = metaEngine.getMetaDir();
|
const metaDir = metaEngine.getMetaDir();
|
||||||
const projectPath = normalizePath(`${metaDir}/project.json`);
|
const projectPath = normalizePath(`${metaDir}/project.json`);
|
||||||
|
|||||||
Reference in New Issue
Block a user