215
package-lock.json
generated
215
package-lock.json
generated
@@ -153,7 +153,7 @@
|
||||
"typescript": "^5.4.4",
|
||||
"unist-util-remove": "^4.0.0",
|
||||
"unist-util-visit-parents": "6.0.1",
|
||||
"vitest": "1.5.0",
|
||||
"vitest": "1.6.0",
|
||||
"website-scraper": "^5.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2669,9 +2669,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz",
|
||||
"integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
|
||||
"integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2682,9 +2682,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz",
|
||||
"integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
|
||||
"integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2695,9 +2695,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz",
|
||||
"integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
|
||||
"integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2708,9 +2708,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz",
|
||||
"integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
|
||||
"integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2721,9 +2721,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz",
|
||||
"integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
|
||||
"integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2734,9 +2734,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz",
|
||||
"integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
|
||||
"integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2747,9 +2747,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2760,9 +2760,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz",
|
||||
"integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
|
||||
"integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2773,9 +2773,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -2786,9 +2786,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -2799,9 +2799,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -2812,9 +2812,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2825,9 +2825,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz",
|
||||
"integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
|
||||
"integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2838,9 +2838,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz",
|
||||
"integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
|
||||
"integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2851,9 +2851,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz",
|
||||
"integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
|
||||
"integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -2864,9 +2864,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz",
|
||||
"integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
|
||||
"integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3682,13 +3682,13 @@
|
||||
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz",
|
||||
"integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz",
|
||||
"integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vitest/spy": "1.5.0",
|
||||
"@vitest/utils": "1.5.0",
|
||||
"@vitest/spy": "1.6.0",
|
||||
"@vitest/utils": "1.6.0",
|
||||
"chai": "^4.3.10"
|
||||
},
|
||||
"funding": {
|
||||
@@ -3696,12 +3696,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz",
|
||||
"integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz",
|
||||
"integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vitest/utils": "1.5.0",
|
||||
"@vitest/utils": "1.6.0",
|
||||
"p-limit": "^5.0.0",
|
||||
"pathe": "^1.1.1"
|
||||
},
|
||||
@@ -3737,9 +3737,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz",
|
||||
"integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz",
|
||||
"integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"magic-string": "^0.30.5",
|
||||
@@ -3751,9 +3751,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz",
|
||||
"integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz",
|
||||
"integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tinyspy": "^2.2.0"
|
||||
@@ -3763,9 +3763,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz",
|
||||
"integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz",
|
||||
"integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"diff-sequences": "^29.6.3",
|
||||
@@ -9266,15 +9266,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.9",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
|
||||
"integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
|
||||
"version": "0.30.10",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
|
||||
"integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string/node_modules/@jridgewell/sourcemap-codec": {
|
||||
@@ -12588,9 +12585,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz",
|
||||
"integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
|
||||
"integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
@@ -12603,22 +12600,22 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.14.3",
|
||||
"@rollup/rollup-android-arm64": "4.14.3",
|
||||
"@rollup/rollup-darwin-arm64": "4.14.3",
|
||||
"@rollup/rollup-darwin-x64": "4.14.3",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.14.3",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.14.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.14.3",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-x64-musl": "4.14.3",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.14.3",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.14.3",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.14.3",
|
||||
"@rollup/rollup-android-arm-eabi": "4.17.2",
|
||||
"@rollup/rollup-android-arm64": "4.17.2",
|
||||
"@rollup/rollup-darwin-arm64": "4.17.2",
|
||||
"@rollup/rollup-darwin-x64": "4.17.2",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.17.2",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.17.2",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.17.2",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-x64-musl": "4.17.2",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.17.2",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.17.2",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.17.2",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -14409,9 +14406,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.2.8",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
|
||||
"integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
|
||||
"version": "5.2.11",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
|
||||
"integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.20.1",
|
||||
@@ -14464,9 +14461,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz",
|
||||
"integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz",
|
||||
"integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cac": "^6.7.14",
|
||||
@@ -14934,16 +14931,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz",
|
||||
"integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz",
|
||||
"integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "1.5.0",
|
||||
"@vitest/runner": "1.5.0",
|
||||
"@vitest/snapshot": "1.5.0",
|
||||
"@vitest/spy": "1.5.0",
|
||||
"@vitest/utils": "1.5.0",
|
||||
"@vitest/expect": "1.6.0",
|
||||
"@vitest/runner": "1.6.0",
|
||||
"@vitest/snapshot": "1.6.0",
|
||||
"@vitest/spy": "1.6.0",
|
||||
"@vitest/utils": "1.6.0",
|
||||
"acorn-walk": "^8.3.2",
|
||||
"chai": "^4.3.10",
|
||||
"debug": "^4.3.4",
|
||||
@@ -14957,7 +14954,7 @@
|
||||
"tinybench": "^2.5.1",
|
||||
"tinypool": "^0.8.3",
|
||||
"vite": "^5.0.0",
|
||||
"vite-node": "1.5.0",
|
||||
"vite-node": "1.6.0",
|
||||
"why-is-node-running": "^2.2.2"
|
||||
},
|
||||
"bin": {
|
||||
@@ -14972,8 +14969,8 @@
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/node": "^18.0.0 || >=20.0.0",
|
||||
"@vitest/browser": "1.5.0",
|
||||
"@vitest/ui": "1.5.0",
|
||||
"@vitest/browser": "1.6.0",
|
||||
"@vitest/ui": "1.6.0",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"fixture-test": "cross-env ROOT=src/fixtures/fixtures npm test -- src/fixtures/tests",
|
||||
"index": "tsx src/search/scripts/index/index.ts",
|
||||
"index-elasticsearch": "node src/search/scripts/index-elasticsearch.js",
|
||||
"index-test-fixtures": "npm run index-elasticsearch -- -l en -l ja -V ghec -V dotcom --index-prefix tests -- src/search/tests/fixtures/search-indexes",
|
||||
"index-test-fixtures": "./src/search/scripts/index-test-fixtures.sh",
|
||||
"lint": "eslint '**/*.{js,mjs,ts,tsx}'",
|
||||
"lint-content": "node src/content-linter/scripts/lint-content.js",
|
||||
"lint-translation": "vitest src/content-linter/tests/lint-files.js",
|
||||
@@ -339,7 +339,7 @@
|
||||
"typescript": "^5.4.4",
|
||||
"unist-util-remove": "^4.0.0",
|
||||
"unist-util-visit-parents": "6.0.1",
|
||||
"vitest": "1.5.0",
|
||||
"vitest": "1.6.0",
|
||||
"website-scraper": "^5.3.1"
|
||||
},
|
||||
"overrides": {},
|
||||
|
||||
@@ -183,6 +183,48 @@ export async function getSearchResults({
|
||||
return { meta, hits }
|
||||
}
|
||||
|
||||
export async function getAutocompleteSearchResults({ indexName, query, size }) {
|
||||
const client = getClient()
|
||||
|
||||
const matchQueries = getAutocompleteMatchQueries(query.trim(), {
|
||||
fuzzy: {
|
||||
minLength: 3,
|
||||
maxLength: 20,
|
||||
},
|
||||
})
|
||||
const matchQuery = {
|
||||
bool: {
|
||||
should: matchQueries,
|
||||
},
|
||||
}
|
||||
|
||||
const highlight = getHighlightConfiguration(query, ['term'])
|
||||
|
||||
const searchQuery = {
|
||||
index: indexName,
|
||||
highlight,
|
||||
size,
|
||||
query: matchQuery,
|
||||
// Send absolutely minimal from Elasticsearch to here. Less data => faster.
|
||||
_source_includes: ['term'],
|
||||
}
|
||||
const result = await client.search(searchQuery)
|
||||
|
||||
const hitsAll = result.hits
|
||||
const hits = hitsAll.hits.map((hit) => {
|
||||
return {
|
||||
term: hit._source.term,
|
||||
highlights: (hit.highlight && hit.highlight.term) || [],
|
||||
}
|
||||
})
|
||||
|
||||
const meta = {
|
||||
found: hitsAll.total,
|
||||
}
|
||||
|
||||
return { meta, hits }
|
||||
}
|
||||
|
||||
function getMatchQueries(query, { usePrefixSearch, fuzzy }) {
|
||||
const BOOST_PHRASE = 10.0
|
||||
const BOOST_TITLE = 4.0
|
||||
@@ -371,6 +413,46 @@ function getMatchQueries(query, { usePrefixSearch, fuzzy }) {
|
||||
return matchQueries
|
||||
}
|
||||
|
||||
function getAutocompleteMatchQueries(query, { fuzzy }) {
|
||||
const BOOST_PHRASE = 4.0
|
||||
const BOOST_REGULAR = 2.0
|
||||
const BOOST_FUZZY = 0.1 // make it always last in ranking
|
||||
const matchQueries = []
|
||||
|
||||
// If the query input is multiple words, it's good to know because you can
|
||||
// make the query do `match_phrase` and you can make `match` query
|
||||
// with the `AND` operator (`OR` is the default).
|
||||
const isMultiWordQuery = query.includes(' ') || query.includes('-')
|
||||
|
||||
if (isMultiWordQuery) {
|
||||
matchQueries.push({
|
||||
match_phrase_prefix: {
|
||||
term: {
|
||||
query,
|
||||
boost: BOOST_PHRASE,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
matchQueries.push({
|
||||
match_bool_prefix: {
|
||||
term: {
|
||||
query,
|
||||
boost: BOOST_REGULAR,
|
||||
},
|
||||
},
|
||||
})
|
||||
if (query.length > fuzzy.minLength && query.length < fuzzy.maxLength) {
|
||||
matchQueries.push({
|
||||
fuzzy: {
|
||||
term: { value: query, boost: BOOST_FUZZY, fuzziness: 'AUTO' },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return matchQueries
|
||||
}
|
||||
|
||||
function getHits(hits, { indexName, debug, includeTopics, highlightFields, include }) {
|
||||
return hits.map((hit) => {
|
||||
// Return `hit.highlights[...]` based on the highlight fields requested.
|
||||
@@ -464,7 +546,16 @@ function getHighlightConfiguration(query, highlights) {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (highlights.includes('term')) {
|
||||
fields.term = {
|
||||
// Fast Vector Highlighter
|
||||
// Using this requires that you first index these fields
|
||||
// with {term_vector: 'with_positions_offsets'}
|
||||
type: 'fvh',
|
||||
// fragment_size: 200,
|
||||
// number_of_fragments: 1,
|
||||
}
|
||||
}
|
||||
return {
|
||||
pre_tags: ['<mark>'],
|
||||
post_tags: ['</mark>'],
|
||||
|
||||
@@ -5,7 +5,9 @@ import { allVersions } from '#src/versions/lib/all-versions.js'
|
||||
import { POSSIBLE_HIGHLIGHT_FIELDS, DEFAULT_HIGHLIGHT_FIELDS } from './es-search.js'
|
||||
|
||||
const DEFAULT_SIZE = 10
|
||||
const DEFAULT_AUTOCOMPLETE_SIZE = 8
|
||||
const MAX_SIZE = 50 // How much you return has a strong impact on performance
|
||||
const MAX_AUTOCOMPLETE_SIZE = 10
|
||||
const DEFAULT_PAGE = 1
|
||||
const POSSIBLE_SORTS = ['best', 'relevance']
|
||||
const DEFAULT_SORT = POSSIBLE_SORTS[0]
|
||||
@@ -23,6 +25,7 @@ const V1_ADDITIONAL_INCLUDES = ['intro', 'headings']
|
||||
// In some distant future we can clean up any client enough that this
|
||||
// aliasing won't be necessary.
|
||||
const versionAliases = {}
|
||||
const prefixVersionAliases = {}
|
||||
Object.values(allVersions).forEach((info) => {
|
||||
if (info.hasNumberedReleases) {
|
||||
versionAliases[info.currentRelease] = info.miscVersionName
|
||||
@@ -30,6 +33,11 @@ Object.values(allVersions).forEach((info) => {
|
||||
versionAliases[info.version] = info.miscVersionName
|
||||
versionAliases[info.miscVersionName] = info.miscVersionName
|
||||
}
|
||||
// This makes it so you can search for `?version=enterprise-server`
|
||||
// and that actually means `?version=ghes` because there's an index
|
||||
// called `github-autocomplete-en-ghes`.
|
||||
prefixVersionAliases[info.plan] = info.shortName
|
||||
prefixVersionAliases[info.shortName] = info.shortName
|
||||
})
|
||||
|
||||
function getIndexPrefix() {
|
||||
@@ -102,11 +110,44 @@ const PARAMS = [
|
||||
},
|
||||
]
|
||||
|
||||
export function getSearchFromRequest(req, force = {}) {
|
||||
const AUTOCOMPLETE_PARAMS = [
|
||||
{ key: 'query' },
|
||||
{ key: 'language', default_: 'en', validate: (v) => v in languages },
|
||||
{
|
||||
key: 'version',
|
||||
default_: 'free-pro-team',
|
||||
validate: (v) => {
|
||||
if (prefixVersionAliases[v] || allVersions[v]) return true
|
||||
if (Object.values(prefixVersionAliases).includes(v)) return true
|
||||
const valid = [
|
||||
...Object.keys(prefixVersionAliases),
|
||||
...Object.values(prefixVersionAliases),
|
||||
...Object.keys(allVersions),
|
||||
]
|
||||
throw new ValidationError(`'${v}' not in ${valid.join(', ')}`)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'size',
|
||||
default_: DEFAULT_AUTOCOMPLETE_SIZE,
|
||||
cast: (v) => parseInt(v, 10),
|
||||
validate: (v) => v >= 0 && v <= MAX_AUTOCOMPLETE_SIZE,
|
||||
},
|
||||
]
|
||||
export function getAutocompleteSearchFromRequest(req, force = {}) {
|
||||
const { search, validationErrors } = getSearchFromRequest(req, {}, AUTOCOMPLETE_PARAMS)
|
||||
if (validationErrors.length === 0) {
|
||||
const version = prefixVersionAliases[search.version] || allVersions[search.version].shortName
|
||||
search.indexName = `${getIndexPrefix()}github-autocomplete-${search.language}-${version}`
|
||||
}
|
||||
return { search, validationErrors }
|
||||
}
|
||||
|
||||
export function getSearchFromRequest(req, force = {}, params = PARAMS) {
|
||||
const search = {}
|
||||
const validationErrors = []
|
||||
|
||||
for (const { key, default_, cast, validate, multiple } of PARAMS) {
|
||||
for (const { key, default_, cast, validate, multiple } of params) {
|
||||
// This is necessary because when the version or language comes from
|
||||
// the pathname, we don't want pick these up from the query string.
|
||||
// This function gets used by /$locale/$version/search
|
||||
@@ -153,7 +194,10 @@ export function getSearchFromRequest(req, force = {}) {
|
||||
}
|
||||
|
||||
if (!validationErrors.length) {
|
||||
const version = versionAliases[search.version] || allVersions[search.version].miscVersionName
|
||||
const version =
|
||||
prefixVersionAliases[search.version] ||
|
||||
versionAliases[search.version] ||
|
||||
allVersions[search.version].miscVersionName
|
||||
search.indexName = `${getIndexPrefix()}github-docs-${version}-${search.language}` // github-docs-ghes-3.5-en
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
setFastlySurrogateKey,
|
||||
SURROGATE_ENUMS,
|
||||
} from '#src/frame/middleware/set-fastly-surrogate-key.js'
|
||||
import { getSearchResults } from './es-search.js'
|
||||
import { getSearchFromRequest } from './get-search-request.js'
|
||||
import { getAutocompleteSearchResults, getSearchResults } from './es-search.js'
|
||||
import { getAutocompleteSearchFromRequest, getSearchFromRequest } from './get-search-request.js'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
@@ -69,6 +69,52 @@ router.get(
|
||||
}),
|
||||
)
|
||||
|
||||
export const autocompleteValidationMiddleware = (req, res, next) => {
|
||||
const { search, validationErrors } = getAutocompleteSearchFromRequest(req)
|
||||
if (validationErrors.length) {
|
||||
// There might be multiple things bad about the query parameters,
|
||||
// but we send a 400 on the first possible one in the API.
|
||||
return res.status(400).json(validationErrors[0])
|
||||
}
|
||||
|
||||
req.search = search
|
||||
return next()
|
||||
}
|
||||
|
||||
router.get(
|
||||
'/autocomplete/v1',
|
||||
autocompleteValidationMiddleware,
|
||||
catchMiddlewareError(async (req, res) => {
|
||||
const { indexName, query, size } = req.search
|
||||
|
||||
const options = {
|
||||
indexName,
|
||||
query,
|
||||
size,
|
||||
}
|
||||
try {
|
||||
const { meta, hits } = await getAutocompleteSearchResults(options)
|
||||
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
searchCacheControl(res)
|
||||
// We can cache this without purging it after every deploy
|
||||
// because the API search is only used as a proxy for local
|
||||
// and preview environments.
|
||||
setFastlySurrogateKey(res, SURROGATE_ENUMS.MANUAL)
|
||||
}
|
||||
|
||||
// The v1 version of the output matches perfectly what comes out
|
||||
// of the getSearchResults() function.
|
||||
res.status(200).json({ meta, hits })
|
||||
} catch (error) {
|
||||
// If getSearchResult() throws an error that might be 404 inside
|
||||
// elasticsearch, if we don't capture that here, it will propagate
|
||||
// to the next middleware.
|
||||
await handleGetSearchResultsError(req, res, error, options)
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
// We have more than one place where we do `try{...} catch error( THIS )`
|
||||
// which is slightly different depending on the "sub-version" (e.g. /legacy)
|
||||
// This function is a single place to take care of all of these error handlings
|
||||
@@ -93,4 +139,9 @@ router.get('/', (req, res) => {
|
||||
res.redirect(307, req.originalUrl.replace('/search', '/search/v1'))
|
||||
})
|
||||
|
||||
// Alias for the latest autocomplete version
|
||||
router.get('/autocomplete', (req, res) => {
|
||||
res.redirect(307, req.originalUrl.replace('/search/autocomplete', '/search/autocomplete/v1'))
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
12
src/search/scripts/index-test-fixtures.sh
Executable file
12
src/search/scripts/index-test-fixtures.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This exists as a bash script because the commands are a bit too long
|
||||
# and complex to express inside `package.json`.
|
||||
|
||||
set -e
|
||||
|
||||
# For general site-search
|
||||
npm run index-elasticsearch -- -l en -l ja -V ghec -V dotcom --index-prefix tests -- src/search/tests/fixtures/search-indexes
|
||||
|
||||
# For autocomplete search
|
||||
npm run index -- autocomplete src/search/tests/fixtures/data -l en -l ja -v fpt -v ghec --index-prefix tests
|
||||
@@ -24,6 +24,7 @@ type Options = {
|
||||
retries?: number
|
||||
sleepTime?: number
|
||||
verbose?: boolean
|
||||
indexPrefix?: string
|
||||
}
|
||||
|
||||
export async function indexAutocomplete(options: Options) {
|
||||
@@ -38,7 +39,12 @@ export async function indexAutocomplete(options: Options) {
|
||||
for (const language of languages) {
|
||||
for (const version of versions) {
|
||||
const records = loadRecords({ version, language, dataRepoRoot })
|
||||
const { alias, name } = await createIndex(client, language, version)
|
||||
const { alias, name } = await createIndex(
|
||||
client,
|
||||
language,
|
||||
version,
|
||||
options.indexPrefix || '',
|
||||
)
|
||||
await populate(client, records, {
|
||||
alias,
|
||||
name,
|
||||
@@ -109,7 +115,12 @@ type IndexInfo = {
|
||||
name: string
|
||||
}
|
||||
|
||||
async function createIndex(client: Client, language: string, version: Version): Promise<IndexInfo> {
|
||||
async function createIndex(
|
||||
client: Client,
|
||||
language: string,
|
||||
version: Version,
|
||||
indexPrefix: string,
|
||||
): Promise<IndexInfo> {
|
||||
const settings: estypes.IndicesIndexSettings = {
|
||||
analysis: {
|
||||
analyzer: {
|
||||
@@ -126,7 +137,11 @@ async function createIndex(client: Client, language: string, version: Version):
|
||||
// XXX SNOWBALL?
|
||||
}
|
||||
|
||||
const indexName = `github-autocomplete-${language}-${shortVersionNames[version] || version}`
|
||||
if (indexPrefix && !indexPrefix.endsWith('_')) {
|
||||
indexPrefix += '_'
|
||||
}
|
||||
|
||||
const indexName = `${indexPrefix}github-autocomplete-${language}-${shortVersionNames[version] || version}`
|
||||
const thisAlias = `${indexName}__${utcTimestamp()}`
|
||||
|
||||
const mappings: estypes.MappingTypeMapping = {
|
||||
|
||||
@@ -25,6 +25,7 @@ program
|
||||
]),
|
||||
)
|
||||
.option('--verbose', 'Verbose output')
|
||||
.option('--index-prefix <prefix>', 'Prefix for the index names', '')
|
||||
.argument('<data-root>', 'path to the docs-internal-data repo')
|
||||
.action((root: string, options) => {
|
||||
const languages = options.language ? options.language : languageKeys
|
||||
@@ -36,7 +37,8 @@ program
|
||||
versions.push(v)
|
||||
}
|
||||
}
|
||||
return indexAutocomplete({ dataRepoRoot: root, languages, versions })
|
||||
const indexPrefix = options.indexPrefix || ''
|
||||
return indexAutocomplete({ dataRepoRoot: root, languages, versions, indexPrefix })
|
||||
})
|
||||
|
||||
program.parse(process.argv)
|
||||
|
||||
136
src/search/tests/api-autocomplete-search.js
Normal file
136
src/search/tests/api-autocomplete-search.js
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* To be able to run these tests you need to index the fixtures!
|
||||
* And you need to have an Elasticsearch URL to connect to for the server.
|
||||
*
|
||||
* To index the fixtures, run:
|
||||
*
|
||||
* ELASTICSEARCH_URL=http://localhost:9200 npm run index-test-fixtures
|
||||
*
|
||||
* This will replace any "real" Elasticsearch indexes you might have so
|
||||
* once you're done working on vitest tests you need to index real
|
||||
* content again.
|
||||
*/
|
||||
|
||||
import { expect, test, vi } from 'vitest'
|
||||
|
||||
import { describeIfElasticsearchURL } from '#src/tests/helpers/conditional-runs.js'
|
||||
import { get } from '#src/tests/helpers/e2etest.js'
|
||||
|
||||
if (!process.env.ELASTICSEARCH_URL) {
|
||||
console.warn(
|
||||
'None of the API search middleware tests are run because ' +
|
||||
"the environment variable 'ELASTICSEARCH_URL' is currently not set.",
|
||||
)
|
||||
}
|
||||
|
||||
// This suite only runs if $ELASTICSEARCH_URL is set.
|
||||
describeIfElasticsearchURL('search/autocomplete v1 middleware', () => {
|
||||
vi.setConfig({ testTimeout: 60 * 1000 })
|
||||
|
||||
test('basic search', async () => {
|
||||
const sp = new URLSearchParams()
|
||||
// To see why this will work,
|
||||
// see src/search/tests/fixtures/data
|
||||
sp.set('query', 'fo')
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(200)
|
||||
const results = JSON.parse(res.body)
|
||||
|
||||
expect(results.meta).toBeTruthy()
|
||||
expect(results.meta.found.value).toBeGreaterThanOrEqual(1)
|
||||
expect(results.meta.found.relation).toBeTruthy()
|
||||
|
||||
// Might be empty but at least an array
|
||||
expect(results.hits).toBeTruthy()
|
||||
// The work "fork" matches "fo"
|
||||
const hit = results.hits[0]
|
||||
expect(hit.term).toBe('fork')
|
||||
expect(hit.highlights).toBeTruthy()
|
||||
expect(hit.highlights[0]).toBe('<mark>fork</mark>')
|
||||
|
||||
// Check that it can be cached at the CDN
|
||||
expect(res.headers['set-cookie']).toBeUndefined()
|
||||
expect(res.headers['cache-control']).toContain('public')
|
||||
expect(res.headers['cache-control']).toMatch(/max-age=[1-9]/)
|
||||
expect(res.headers['surrogate-control']).toContain('public')
|
||||
expect(res.headers['surrogate-control']).toMatch(/max-age=[1-9]/)
|
||||
expect(res.headers['surrogate-key']).toBe('manual-purge')
|
||||
})
|
||||
|
||||
test('invalid version', async () => {
|
||||
const sp = new URLSearchParams()
|
||||
sp.set('query', 'fo')
|
||||
sp.set('version', 'never-heard-of')
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(400)
|
||||
expect(JSON.parse(res.body).error).toBeTruthy()
|
||||
})
|
||||
|
||||
test('variations on version name', async () => {
|
||||
const sp = new URLSearchParams()
|
||||
sp.set('query', 'fo')
|
||||
sp.set('version', 'enterprise-cloud')
|
||||
{
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(200)
|
||||
}
|
||||
sp.set('version', 'ghec')
|
||||
{
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(200)
|
||||
}
|
||||
sp.set('version', 'fpt')
|
||||
{
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(200)
|
||||
}
|
||||
sp.set('version', 'free-pro-team@latest')
|
||||
{
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(200)
|
||||
}
|
||||
})
|
||||
|
||||
test('invalid language', async () => {
|
||||
const sp = new URLSearchParams()
|
||||
sp.set('query', 'fo')
|
||||
sp.set('language', 'xx')
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(400)
|
||||
expect(JSON.parse(res.body).error).toBeTruthy()
|
||||
})
|
||||
|
||||
test('fuzzy autocomplete search', async () => {
|
||||
const sp = new URLSearchParams()
|
||||
sp.set('query', 'forc')
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(200)
|
||||
const results = JSON.parse(res.body)
|
||||
// The work "fork" matches "fo"
|
||||
const hit = results.hits[0]
|
||||
expect(hit.term).toBe('fork')
|
||||
expect(hit.highlights).toBeTruthy()
|
||||
expect(hit.highlights[0]).toBe('<mark>fork</mark>')
|
||||
})
|
||||
|
||||
test('invalid query', async () => {
|
||||
const sp = new URLSearchParams()
|
||||
// No query at all
|
||||
{
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(400)
|
||||
}
|
||||
// Empty query
|
||||
{
|
||||
sp.set('query', '')
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(400)
|
||||
}
|
||||
// Empty when trimmed
|
||||
{
|
||||
sp.set('query', ' ')
|
||||
const res = await get('/api/search/autocomplete/v1?' + sp)
|
||||
expect(res.statusCode).toBe(400)
|
||||
}
|
||||
})
|
||||
})
|
||||
26
src/search/tests/fixtures/data/all-documents/terms/en/enterprise-cloud/terms.json
vendored
Normal file
26
src/search/tests/fixtures/data/all-documents/terms/en/enterprise-cloud/terms.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"github": 631,
|
||||
"api": 213,
|
||||
"rest": 198,
|
||||
"rest api": 196,
|
||||
"endpoints": 176,
|
||||
"api endpoints": 172,
|
||||
"rest api endpoints": 172,
|
||||
"managing": 165,
|
||||
"organization": 155,
|
||||
"repository": 144,
|
||||
"using": 126,
|
||||
"code": 81,
|
||||
"app": 79,
|
||||
"actions": 76,
|
||||
"creating": 76,
|
||||
"security": 75,
|
||||
"codespaces": 70,
|
||||
"pull": 70,
|
||||
"configuring": 64,
|
||||
"project": 61,
|
||||
"copilot": 58,
|
||||
"github actions": 57,
|
||||
"access": 56,
|
||||
"codeql": 54
|
||||
}
|
||||
26
src/search/tests/fixtures/data/all-documents/terms/en/free-pro-team/terms.json
vendored
Normal file
26
src/search/tests/fixtures/data/all-documents/terms/en/free-pro-team/terms.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"github": 631,
|
||||
"api": 213,
|
||||
"rest": 198,
|
||||
"rest api": 196,
|
||||
"endpoints": 176,
|
||||
"api endpoints": 172,
|
||||
"rest api endpoints": 172,
|
||||
"managing": 165,
|
||||
"organization": 155,
|
||||
"repository": 144,
|
||||
"using": 126,
|
||||
"code": 81,
|
||||
"app": 79,
|
||||
"actions": 76,
|
||||
"creating": 76,
|
||||
"security": 75,
|
||||
"codespaces": 70,
|
||||
"pull": 70,
|
||||
"configuring": 64,
|
||||
"project": 61,
|
||||
"copilot": 58,
|
||||
"github actions": 57,
|
||||
"access": 56,
|
||||
"codeql": 54
|
||||
}
|
||||
52
src/search/tests/fixtures/data/all-documents/terms/ja/enterprise-cloud/terms.json
vendored
Normal file
52
src/search/tests/fixtures/data/all-documents/terms/ja/enterprise-cloud/terms.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"github": 608,
|
||||
"api": 204,
|
||||
"rest": 192,
|
||||
"rest api": 192,
|
||||
"api \u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8": 169,
|
||||
"rest api \u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8": 169,
|
||||
"\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8": 169,
|
||||
"organization": 110,
|
||||
"codespaces": 58,
|
||||
"codeql": 54,
|
||||
"copilot": 51,
|
||||
"actions": 49,
|
||||
"github codespaces": 46,
|
||||
"\u306b\u3064\u3044\u3066": 46,
|
||||
"github actions": 45,
|
||||
"github copilot": 42,
|
||||
"app": 41,
|
||||
"git": 41,
|
||||
"classic": 36,
|
||||
"project": 36,
|
||||
"github app": 35,
|
||||
"issue": 33,
|
||||
"codespace": 31,
|
||||
"dependabot": 31,
|
||||
"oauth": 31,
|
||||
"cli": 30,
|
||||
"code": 30,
|
||||
"desktop": 30,
|
||||
"github desktop": 30,
|
||||
"enterprise": 29,
|
||||
"projects": 28,
|
||||
"pull": 28,
|
||||
"github enterprise": 26,
|
||||
"\u3067\u306e": 26,
|
||||
"pages": 25,
|
||||
"pull request": 25,
|
||||
"request": 25,
|
||||
"github pages": 23,
|
||||
"project classic": 23,
|
||||
"azure": 21,
|
||||
"importer": 20,
|
||||
"database": 19,
|
||||
"webhook": 19,
|
||||
"team": 18,
|
||||
"\u30ea\u30dd\u30b8\u30c8\u30ea": 18,
|
||||
"marketplace": 17,
|
||||
"\u306e\u4f7f\u7528": 17,
|
||||
"github marketplace": 16,
|
||||
"ssh": 16,
|
||||
"\u30ab\u30b9\u30bf\u30e0": 16
|
||||
}
|
||||
52
src/search/tests/fixtures/data/all-documents/terms/ja/free-pro-team/terms.json
vendored
Normal file
52
src/search/tests/fixtures/data/all-documents/terms/ja/free-pro-team/terms.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"github": 608,
|
||||
"api": 204,
|
||||
"rest": 192,
|
||||
"rest api": 192,
|
||||
"api \u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8": 169,
|
||||
"rest api \u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8": 169,
|
||||
"\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8": 169,
|
||||
"organization": 110,
|
||||
"codespaces": 58,
|
||||
"codeql": 54,
|
||||
"copilot": 51,
|
||||
"actions": 49,
|
||||
"github codespaces": 46,
|
||||
"\u306b\u3064\u3044\u3066": 46,
|
||||
"github actions": 45,
|
||||
"github copilot": 42,
|
||||
"app": 41,
|
||||
"git": 41,
|
||||
"classic": 36,
|
||||
"project": 36,
|
||||
"github app": 35,
|
||||
"issue": 33,
|
||||
"codespace": 31,
|
||||
"dependabot": 31,
|
||||
"oauth": 31,
|
||||
"cli": 30,
|
||||
"code": 30,
|
||||
"desktop": 30,
|
||||
"github desktop": 30,
|
||||
"enterprise": 29,
|
||||
"projects": 28,
|
||||
"pull": 28,
|
||||
"github enterprise": 26,
|
||||
"\u3067\u306e": 26,
|
||||
"pages": 25,
|
||||
"pull request": 25,
|
||||
"request": 25,
|
||||
"github pages": 23,
|
||||
"project classic": 23,
|
||||
"azure": 21,
|
||||
"importer": 20,
|
||||
"database": 19,
|
||||
"webhook": 19,
|
||||
"team": 18,
|
||||
"\u30ea\u30dd\u30b8\u30c8\u30ea": 18,
|
||||
"marketplace": 17,
|
||||
"\u306e\u4f7f\u7528": 17,
|
||||
"github marketplace": 16,
|
||||
"ssh": 16,
|
||||
"\u30ab\u30b9\u30bf\u30e0": 16
|
||||
}
|
||||
51
src/search/tests/fixtures/data/hydro/rollups/user-searches/en/enterprise-cloud/rollup.json
vendored
Normal file
51
src/search/tests/fixtures/data/hydro/rollups/user-searches/en/enterprise-cloud/rollup.json
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"copilot": 141.3,
|
||||
"inputs": 98.3,
|
||||
"codeowners": 79.9,
|
||||
"personal access token": 73.7,
|
||||
"scim": 73.7,
|
||||
"emu": 67.6,
|
||||
"google": 61.4,
|
||||
"ssh": 55.3,
|
||||
"enterprise managed users": 55.3,
|
||||
"enterprise": 49.1,
|
||||
"audit log": 49.1,
|
||||
"octocat": 49.1,
|
||||
"rulesets": 43.0,
|
||||
"teams": 43.0,
|
||||
"saml": 43.0,
|
||||
"oidc": 43.0,
|
||||
"copilot api": 36.9,
|
||||
"input": 36.9,
|
||||
"release": 36.9,
|
||||
"ssh key": 36.9,
|
||||
"actions": 31.4,
|
||||
"public code": 30.7,
|
||||
"copilot enterprise": 30.7,
|
||||
"license": 30.7,
|
||||
"outside collaborators": 30.7,
|
||||
"enterprise managed user": 30.7,
|
||||
"github actions": 24.6,
|
||||
"invite": 24.6,
|
||||
"merge queue": 24.6,
|
||||
"repository roles": 24.6,
|
||||
"api": 24.6,
|
||||
"workflow_dispatch": 24.6,
|
||||
"github app": 24.6,
|
||||
"oauth": 24.6,
|
||||
"support": 24.6,
|
||||
"dependabot": 24.6,
|
||||
"personal access tokens": 24.6,
|
||||
"secrets": 24.6,
|
||||
"graphql": 24.6,
|
||||
"token": 24.6,
|
||||
"rest api": 24.6,
|
||||
"audit logs": 24.6,
|
||||
"pages": 18.4,
|
||||
"security manager": 18.4,
|
||||
"fork": 18.4,
|
||||
"push": 18.4,
|
||||
"enterprise policy": 18.4,
|
||||
"environments": 18.4,
|
||||
"okta": 18.4
|
||||
}
|
||||
51
src/search/tests/fixtures/data/hydro/rollups/user-searches/en/free-pro-team/rollup.json
vendored
Normal file
51
src/search/tests/fixtures/data/hydro/rollups/user-searches/en/free-pro-team/rollup.json
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"copilot": 141.3,
|
||||
"inputs": 98.3,
|
||||
"codeowners": 79.9,
|
||||
"personal access token": 73.7,
|
||||
"scim": 73.7,
|
||||
"emu": 67.6,
|
||||
"google": 61.4,
|
||||
"ssh": 55.3,
|
||||
"enterprise managed users": 55.3,
|
||||
"enterprise": 49.1,
|
||||
"audit log": 49.1,
|
||||
"octocat": 49.1,
|
||||
"rulesets": 43.0,
|
||||
"teams": 43.0,
|
||||
"saml": 43.0,
|
||||
"oidc": 43.0,
|
||||
"copilot api": 36.9,
|
||||
"input": 36.9,
|
||||
"release": 36.9,
|
||||
"ssh key": 36.9,
|
||||
"actions": 31.4,
|
||||
"public code": 30.7,
|
||||
"copilot enterprise": 30.7,
|
||||
"license": 30.7,
|
||||
"outside collaborators": 30.7,
|
||||
"enterprise managed user": 30.7,
|
||||
"github actions": 24.6,
|
||||
"invite": 24.6,
|
||||
"merge queue": 24.6,
|
||||
"repository roles": 24.6,
|
||||
"api": 24.6,
|
||||
"workflow_dispatch": 24.6,
|
||||
"github app": 24.6,
|
||||
"oauth": 24.6,
|
||||
"support": 24.6,
|
||||
"dependabot": 24.6,
|
||||
"personal access tokens": 24.6,
|
||||
"secrets": 24.6,
|
||||
"graphql": 24.6,
|
||||
"token": 24.6,
|
||||
"rest api": 24.6,
|
||||
"audit logs": 24.6,
|
||||
"pages": 18.4,
|
||||
"security manager": 18.4,
|
||||
"fork": 18.4,
|
||||
"push": 18.4,
|
||||
"enterprise policy": 18.4,
|
||||
"environments": 18.4,
|
||||
"okta": 18.4
|
||||
}
|
||||
23
src/search/tests/fixtures/data/hydro/rollups/user-searches/ja/enterprise-cloud/rollup.json
vendored
Normal file
23
src/search/tests/fixtures/data/hydro/rollups/user-searches/ja/enterprise-cloud/rollup.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"youtube": 43.0,
|
||||
"ssh": 20.6,
|
||||
"copilot": 18.4,
|
||||
"google": 14.0,
|
||||
"\u30ea\u30fc\u30b8\u30e7\u30f3": 13.7,
|
||||
"workflow_run": 13.7,
|
||||
"\u65e5\u672c\u8a9e": 13.7,
|
||||
"\u691c\u7d22": 13.7,
|
||||
"workflow_dispatch": 13.4,
|
||||
"\u8a00\u8a9e": 13.4,
|
||||
"\u5909\u6570": 13.4,
|
||||
"uses": 12.6,
|
||||
"\u30c8\u30fc\u30af\u30f3": 12.6,
|
||||
"fork": 12.6,
|
||||
"japanese": 12.6,
|
||||
"permissions": 12.3,
|
||||
"\u5bb9\u91cf": 12.3,
|
||||
"actions/checkout": 12.3,
|
||||
"github.event": 12.3,
|
||||
"homebrew": 12.3,
|
||||
"copilot workspace": 12.3
|
||||
}
|
||||
23
src/search/tests/fixtures/data/hydro/rollups/user-searches/ja/free-pro-team/rollup.json
vendored
Normal file
23
src/search/tests/fixtures/data/hydro/rollups/user-searches/ja/free-pro-team/rollup.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"youtube": 43.0,
|
||||
"ssh": 20.6,
|
||||
"copilot": 18.4,
|
||||
"google": 14.0,
|
||||
"\u30ea\u30fc\u30b8\u30e7\u30f3": 13.7,
|
||||
"workflow_run": 13.7,
|
||||
"\u65e5\u672c\u8a9e": 13.7,
|
||||
"\u691c\u7d22": 13.7,
|
||||
"workflow_dispatch": 13.4,
|
||||
"\u8a00\u8a9e": 13.4,
|
||||
"\u5909\u6570": 13.4,
|
||||
"uses": 12.6,
|
||||
"\u30c8\u30fc\u30af\u30f3": 12.6,
|
||||
"fork": 12.6,
|
||||
"japanese": 12.6,
|
||||
"permissions": 12.3,
|
||||
"\u5bb9\u91cf": 12.3,
|
||||
"actions/checkout": 12.3,
|
||||
"github.event": 12.3,
|
||||
"homebrew": 12.3,
|
||||
"copilot workspace": 12.3
|
||||
}
|
||||
Reference in New Issue
Block a user