wrote a usbip module

This commit is contained in:
2025-01-27 15:53:20 +03:00
parent 5cbe6dcf61
commit 4eb62f688a
484 changed files with 115213 additions and 499 deletions

View File

@ -23,5 +23,6 @@
"fmt": {
"indentWidth": 4
},
"nodeModulesDir": "auto"
"nodeModulesDir": "auto",
"vendor": true
}

437
deno.lock generated
View File

@ -36,18 +36,18 @@
"jsr:@std/path@^1.0.6": "1.0.8",
"jsr:@std/path@^1.0.8": "1.0.8",
"jsr:@std/streams@^1.0.8": "1.0.8",
"npm:@ryanflorence/sqlite-typegen@0.2": "0.2.0",
"npm:@minify-html/node-linux-x64@*": "0.15.0",
"npm:@minify-html/node-linux-x64@0.15": "0.15.0",
"npm:@minify-html/wasm@*": "0.15.0",
"npm:@tauri-apps/api@2": "2.2.0",
"npm:@tauri-apps/cli@2": "2.2.5",
"npm:@tauri-apps/plugin-shell@2": "2.2.0",
"npm:better-sqlite3@^11.8.0": "11.8.1",
"npm:esbuild-plugin-tsc@*": "0.4.0_typescript@5.7.3",
"npm:esbuild-plugin-tsc@0.4": "0.4.0_typescript@5.7.3",
"npm:esbuild@*": "0.24.2",
"npm:esbuild@~0.24.2": "0.24.2",
"npm:fast-glob@*": "3.3.3",
"npm:fast-glob@^3.3.3": "3.3.3",
"npm:mariadb@^3.4.0": "3.4.0",
"npm:typescript@^5.2.2": "5.7.3",
"npm:typescript@^5.7.3": "5.7.3",
"npm:vite@^5.3.1": "5.4.13"
@ -332,6 +332,12 @@
"@esbuild/win32-x64@0.24.2": {
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="
},
"@minify-html/node-linux-x64@0.15.0": {
"integrity": "sha512-cO893EV6O9ZHUFX+2Yge546OCo/eCiatjzJDmUmrPP56fQ7pzTRquHs4ko3t8Rg6tMKG7RT49mBuF09JWPnrgg=="
},
"@minify-html/wasm@0.15.0": {
"integrity": "sha512-xsd4FFypUayUNS4LvokyfiRQwYbyKVITnGHCaGsvlu36jZTYMSeQ7vn5LpEjW20Tbw7CYBQmD2JWCak/V+na5w=="
},
"@nodelib/fs.scandir@2.1.5": {
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dependencies": [
@ -406,16 +412,6 @@
"@rollup/rollup-win32-x64-msvc@4.31.0": {
"integrity": "sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw=="
},
"@ryanflorence/sqlite-typegen@0.2.0": {
"integrity": "sha512-5zDtou8+wd0Qoz5COkX2Nf8JgrSFukjMZVEZdoXct2q5EmH1TcrQc1vjngniDNAKIMDQBZHSIaMEyvtDBtkpXA==",
"dependencies": [
"arg",
"better-sqlite3@11.6.0",
"cli-highlight",
"picocolors",
"tiny-invariant"
]
},
"@tauri-apps/api@2.2.0": {
"integrity": "sha512-R8epOeZl1eJEl603aUMIGb4RXlhPjpgxbGVEaqY+0G5JG9vzV/clNlzTeqc+NLYXVqXcn8mb4c5b9pJIUDEyAg=="
},
@ -473,136 +469,12 @@
"@types/estree@1.0.6": {
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
},
"@types/geojson@7946.0.15": {
"integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA=="
},
"@types/node@22.10.7": {
"integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==",
"dependencies": [
"undici-types"
]
},
"ansi-regex@5.0.1": {
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles@4.3.0": {
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": [
"color-convert"
]
},
"any-promise@1.3.0": {
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
},
"arg@5.0.2": {
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
},
"base64-js@1.5.1": {
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"better-sqlite3@11.6.0": {
"integrity": "sha512-2J6k/eVxcFYY2SsTxsXrj6XylzHWPxveCn4fKPKZFv/Vqn/Cd7lOuX4d7rGQXT5zL+97MkNL3nSbCrIoe3LkgA==",
"dependencies": [
"bindings",
"prebuild-install"
]
},
"better-sqlite3@11.8.1": {
"integrity": "sha512-9BxNaBkblMjhJW8sMRZxnxVTRgbRmssZW0Oxc1MPBTfiR+WW21e2Mk4qu8CzrcZb1LwPCnFsfDEzq+SNcBU8eg==",
"dependencies": [
"bindings",
"prebuild-install"
]
},
"bindings@1.5.0": {
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dependencies": [
"file-uri-to-path"
]
},
"bl@4.1.0": {
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": [
"buffer",
"inherits",
"readable-stream"
]
},
"braces@3.0.3": {
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dependencies": [
"fill-range"
]
},
"buffer@5.7.1": {
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dependencies": [
"base64-js",
"ieee754"
]
},
"chalk@4.1.2": {
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dependencies": [
"ansi-styles",
"supports-color"
]
},
"chownr@1.1.4": {
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"cli-highlight@2.1.11": {
"integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
"dependencies": [
"chalk",
"highlight.js",
"mz",
"parse5@5.1.1",
"parse5-htmlparser2-tree-adapter",
"yargs"
]
},
"cliui@7.0.4": {
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dependencies": [
"string-width",
"strip-ansi",
"wrap-ansi"
]
},
"color-convert@2.0.1": {
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": [
"color-name"
]
},
"color-name@1.1.4": {
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"decompress-response@6.0.0": {
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": [
"mimic-response"
]
},
"deep-extend@0.6.0": {
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"denque@2.1.0": {
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="
},
"detect-libc@2.0.3": {
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="
},
"emoji-regex@8.0.0": {
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"end-of-stream@1.4.4": {
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": [
"once"
]
},
"esbuild-plugin-tsc@0.4.0_typescript@5.7.3": {
"integrity": "sha512-q9gWIovt1nkwchMLc2zhyksaiHOv3kDK4b0AUol8lkMCRhJ1zavgfb2fad6BKp7FT9rh/OHmEBXVjczLoi/0yw==",
"dependencies": [
@ -668,12 +540,6 @@
"@esbuild/win32-x64@0.24.2"
]
},
"escalade@3.2.0": {
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
},
"expand-template@2.0.3": {
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
},
"fast-glob@3.3.3": {
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"dependencies": [
@ -690,60 +556,24 @@
"reusify"
]
},
"file-uri-to-path@1.0.0": {
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"fill-range@7.1.1": {
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dependencies": [
"to-regex-range"
]
},
"fs-constants@1.0.0": {
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"fsevents@2.3.3": {
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
},
"get-caller-file@2.0.5": {
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"github-from-package@0.0.0": {
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
},
"glob-parent@5.1.2": {
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dependencies": [
"is-glob"
]
},
"has-flag@4.0.0": {
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"highlight.js@10.7.3": {
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
},
"iconv-lite@0.6.3": {
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": [
"safer-buffer"
]
},
"ieee754@1.2.1": {
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"inherits@2.0.4": {
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini@1.3.8": {
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"is-extglob@2.1.1": {
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
},
"is-fullwidth-code-point@3.0.0": {
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"is-glob@4.0.3": {
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dependencies": [
@ -753,19 +583,6 @@
"is-number@7.0.0": {
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
"lru-cache@10.4.3": {
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
},
"mariadb@3.4.0": {
"integrity": "sha512-hdRPcAzs+MTxK5VG1thBW18gGTlw6yWBe9YnLB65GLo7q0fO5DWsgomIevV/pXSaWRmD3qi6ka4oSFRTExRiEQ==",
"dependencies": [
"@types/geojson",
"@types/node",
"denque",
"iconv-lite",
"lru-cache"
]
},
"merge2@1.4.1": {
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
},
@ -776,56 +593,9 @@
"picomatch"
]
},
"mimic-response@3.1.0": {
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
},
"minimist@1.2.8": {
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
},
"mkdirp-classic@0.5.3": {
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"mz@2.7.0": {
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dependencies": [
"any-promise",
"object-assign",
"thenify-all"
]
},
"nanoid@3.3.8": {
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="
},
"napi-build-utils@1.0.2": {
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
},
"node-abi@3.73.0": {
"integrity": "sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg==",
"dependencies": [
"semver"
]
},
"object-assign@4.1.1": {
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"once@1.4.0": {
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": [
"wrappy"
]
},
"parse5-htmlparser2-tree-adapter@6.0.1": {
"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
"dependencies": [
"parse5@6.0.1"
]
},
"parse5@5.1.1": {
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="
},
"parse5@6.0.1": {
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
},
"picocolors@1.1.1": {
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
@ -840,53 +610,9 @@
"source-map-js"
]
},
"prebuild-install@7.1.2": {
"integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
"dependencies": [
"detect-libc",
"expand-template",
"github-from-package",
"minimist",
"mkdirp-classic",
"napi-build-utils",
"node-abi",
"pump",
"rc",
"simple-get",
"tar-fs",
"tunnel-agent"
]
},
"pump@3.0.2": {
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
"dependencies": [
"end-of-stream",
"once"
]
},
"queue-microtask@1.2.3": {
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
},
"rc@1.2.8": {
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dependencies": [
"deep-extend",
"ini",
"minimist",
"strip-json-comments"
]
},
"readable-stream@3.6.2": {
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": [
"inherits",
"string_decoder",
"util-deprecate"
]
},
"require-directory@2.1.1": {
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
},
"reusify@1.0.4": {
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
},
@ -922,116 +648,21 @@
"queue-microtask"
]
},
"safe-buffer@5.2.1": {
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"safer-buffer@2.1.2": {
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver@7.6.3": {
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
},
"simple-concat@1.0.1": {
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get@4.0.1": {
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"dependencies": [
"decompress-response",
"once",
"simple-concat"
]
},
"source-map-js@1.2.1": {
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
},
"string-width@4.2.3": {
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": [
"emoji-regex",
"is-fullwidth-code-point",
"strip-ansi"
]
},
"string_decoder@1.3.0": {
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": [
"safe-buffer"
]
},
"strip-ansi@6.0.1": {
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": [
"ansi-regex"
]
},
"strip-comments@2.0.1": {
"integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw=="
},
"strip-json-comments@2.0.1": {
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
},
"supports-color@7.2.0": {
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dependencies": [
"has-flag"
]
},
"tar-fs@2.1.2": {
"integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==",
"dependencies": [
"chownr",
"mkdirp-classic",
"pump",
"tar-stream"
]
},
"tar-stream@2.2.0": {
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": [
"bl",
"end-of-stream",
"fs-constants",
"inherits",
"readable-stream"
]
},
"thenify-all@1.6.0": {
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dependencies": [
"thenify"
]
},
"thenify@3.3.1": {
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dependencies": [
"any-promise"
]
},
"tiny-invariant@1.3.3": {
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
},
"to-regex-range@5.0.1": {
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": [
"is-number"
]
},
"tunnel-agent@0.6.0": {
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dependencies": [
"safe-buffer"
]
},
"typescript@5.7.3": {
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="
},
"undici-types@6.20.0": {
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
},
"util-deprecate@1.0.2": {
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"vite@5.4.13": {
"integrity": "sha512-7zp3N4YSjXOSAFfdBe9pPD3FrO398QlJ/5QpFGm3L8xDP1IxDn1XRxArPw4ZKk5394MM8rcTVPY4y1Hvo62bog==",
"dependencies": [
@ -1040,37 +671,23 @@
"postcss",
"rollup"
]
},
"wrap-ansi@7.0.0": {
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dependencies": [
"ansi-styles",
"string-width",
"strip-ansi"
]
},
"wrappy@1.0.2": {
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"y18n@5.0.8": {
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
},
"yargs-parser@20.2.9": {
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
},
"yargs@16.2.0": {
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dependencies": [
"cliui",
"escalade",
"get-caller-file",
"require-directory",
"string-width",
"y18n",
"yargs-parser"
]
}
},
"remote": {
"https://deno.land/std@0.203.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
"https://deno.land/std@0.203.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
"https://deno.land/std@0.203.0/async/abortable.ts": "fd682fa46f3b7b16b4606a5ab52a7ce309434b76f820d3221bdfb862719a15d7",
"https://deno.land/std@0.203.0/async/deadline.ts": "58f72a3cc0fcb731b2cc055ba046f4b5be3349ff6bf98f2e793c3b969354aab2",
"https://deno.land/std@0.203.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332",
"https://deno.land/std@0.203.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8",
"https://deno.land/std@0.203.0/async/delay.ts": "a6142eb44cdd856b645086af2b811b1fcce08ec06bb7d50969e6a872ee9b8659",
"https://deno.land/std@0.203.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576",
"https://deno.land/std@0.203.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9",
"https://deno.land/std@0.203.0/async/pool.ts": "47c1841cfa9c036144943d11747ddd44064f5baf8cb7ece25473ba873c6aceb0",
"https://deno.land/std@0.203.0/async/retry.ts": "296fb9c323e1325a69bee14ba947e7da7409a8dd9dd646d70cb51ea0d301f24e",
"https://deno.land/std@0.203.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757",
"https://wilsonl.in/minify-html/deno/0.15.0/index.js": "8e7ee5067ca84fb5d5a1f33118cac4998de0b7d80b3f56cc5c6728b84e6bfb70"
},
"workspace": {
"dependencies": [
"jsr:@luca/esbuild-deno-loader@~0.11.1",
@ -1101,10 +718,8 @@
"jsr:@std/crypto@^1.0.3",
"jsr:@std/dotenv@~0.225.3",
"jsr:@std/http@^1.0.12",
"npm:@ryanflorence/sqlite-typegen@0.2",
"npm:better-sqlite3@^11.8.0",
"npm:esbuild@~0.24.2",
"npm:mariadb@^3.4.0"
"npm:@minify-html/node-linux-x64@0.15",
"npm:esbuild@~0.24.2"
]
},
"shared": {

View File

@ -6,6 +6,7 @@ import authMiddleware from "@src/middleware/auth.ts";
import { ok, ResultFromJSON } from "@shared/utils/result.ts";
import { ResultResponseFromJSON } from "@src/lib/context.ts";
import admin from "@src/lib/admin.ts";
import UsbipManager from "@shared/utils/usbip.ts";
const router = new HttpRouter();

View File

@ -5,6 +5,7 @@ import { AdminPasswordNotSetError, QueryExecutionError } from "@lib/errors.ts";
import { AdminRaw, AdminSessionRaw } from "@lib/db/types/index.ts";
import { generateRandomString, passwd } from "@lib/utils.ts";
import { errAsync, ResultAsync } from "@shared/utils/resultasync.ts";
import log from "@shared/utils/logger.ts";
const TOKEN_LENGTH = 128;
const EXPIRED_TOKENS_DELETION_INTERVAL = 120 * 60 * 1000;
@ -49,7 +50,12 @@ class Admin {
password: string,
): ResultAsync<boolean, QueryExecutionError | AdminPasswordNotSetError> {
const result = this.getPasswordHash().flattenOption(
() => new AdminPasswordNotSetError("Admin password is not set"),
() => {
log.warn("Tried to verify password when it is not set");
return new AdminPasswordNotSetError(
"Admin password is not set",
);
},
);
if (result.isErr()) {
@ -75,8 +81,7 @@ class Admin {
this.statements.insertPasswordHash(hash);
this.passwordHash = hash;
},
)
.toAsync(),
),
);
}
@ -121,8 +126,7 @@ class AdminSessions {
);
}
},
(error) =>
console.error(`failed to clear expired tokens: ${error}`),
() => log.error("Failed to clear expired tokens!"),
);
}, EXPIRED_TOKENS_DELETION_INTERVAL);
}

View File

@ -42,6 +42,7 @@ export class Context<S extends string = string> {
private _port?: number;
private _cookies?: Record<string, string>;
private _responseHeaders: Headers = new Headers();
public res: Response = new Response();
constructor(
public readonly req: Request,

View File

@ -2,6 +2,7 @@ import { Database, RestBindParameters } from "@db/sqlite";
import { err, getMessageFromError, ok, Result } from "@shared/utils/result.ts";
import { QueryExecutionError } from "@lib/errors.ts";
import { fromNullableVal, none, Option, some } from "@shared/utils/option.ts";
import log from "@shared/utils/logger.ts";
export class DatabaseClient {
constructor(private readonly db: Database) {}
@ -11,6 +12,7 @@ export class DatabaseClient {
return ok(fn());
} catch (e) {
const message = getMessageFromError(e);
log.error(`Failed to execute sql! Error: ${e}`);
return err(new QueryExecutionError(message));
}
}

View File

@ -1,4 +1,5 @@
import { Database } from "jsr:@db/sqlite";
import log from "@shared/utils/logger.ts";
const MIGRATION_TABLE = "_keyborgMigrations";
@ -39,7 +40,7 @@ export class MigrationManager {
public init() {
if (!this.doesMigrationTableExist()) {
if (this.hasExistingTables()) {
console.error(
log.critical(
"Attempting to initialize migrations on a non-empty database.",
);
Deno.exit(1);
@ -49,7 +50,7 @@ export class MigrationManager {
}
if (!this.areMigrationsSequential()) {
console.error("Migrations are not applied in sequential order!");
log.critical("Migrations are not applied in sequential order!");
Deno.exit(1);
}
this.applyPendingMigrations();
@ -182,7 +183,9 @@ export class MigrationManager {
try {
this.db.exec(sql, params);
} catch (e) {
console.error(e);
log.critical(
`Failed to execute sql while applying migrations! Error: ${e}`,
);
Deno.exit(1);
}
}
@ -194,7 +197,9 @@ export class MigrationManager {
try {
return this.db.prepare(sql).all<T>(params);
} catch (e) {
console.error(e);
log.critical(
`Failed to execute sql while applying migrations! Error: ${e}`,
);
Deno.exit(1);
}
}
@ -206,7 +211,9 @@ export class MigrationManager {
try {
return this.db.prepare(sql).get<T>(params);
} catch (e) {
console.error(e);
log.critical(
`Failed to execute sql while applying migrations! Error: ${e}`,
);
Deno.exit(1);
}
}

View File

@ -5,10 +5,9 @@ import { Context } from "@lib/context.ts";
type RequestHandler<S extends string> = (
c: Context<S>,
) => Promise<Response> | Response;
export type Middleware = (
c: Context<string>,
next: () => Promise<Response>,
next: () => Promise<void>,
) => Promise<Response | undefined> | Response | undefined;
type MethodHandlers<S extends string> = Partial<
@ -100,12 +99,8 @@ class HttpRouter {
): Promise<Response> {
const c = new Context(req, connInfo, {});
for (const mw of this.middlewareChain) {
const res = await mw(c);
if (res) {
return res;
}
}
let i = 0;
const mw = this.middlewareChain[i++];
const path = this.pathPreprocessor
? this.pathPreprocessor(c.path)

5
server/src/lib/usbip.ts Normal file
View File

@ -0,0 +1,5 @@
import UsbipManager from "@shared/utils/usbip.ts";
const usbip = new UsbipManager();
export default usbip;

View File

@ -1,4 +1,5 @@
import { Middleware } from "@lib/router.ts";
import log from "@shared/utils/logger.ts";
const requestCounts: Partial<
Record<string, { count: number; lastReset: number }>
@ -27,6 +28,7 @@ const rateLimitMiddleware: Middleware = (c) => {
}
if (c.preferredType.isSome()) {
log.info(`client ${hostname} is rate limeted`);
switch (c.preferredType.value) {
case "html": {
return c.html("429 Too Many Requests", {

3
server/test.tex Normal file
View File

@ -0,0 +1,3 @@
quantile
quntile

29
shared/utils/' Normal file
View File

@ -0,0 +1,29 @@
const shellCmd = new Deno.Command("/bin/sh", {
stdin: "piped",
stdout: "piped",
stderr: "piped",
});
const shell = shellCmd.spawn();
const writer = shell.stdin.getWriter();
const stdoutR = shell.stdout.pipeThrough(new TextDecoderStream()).getReader();
const stderrR = shell.stderr.pipeThrough(new TextDecoderStream()).getReader();
const encode = new TextEncoder();
//
//await writer.write(encode.encode("usbip list -l\n"));
//
//const value = await stdoutR.read();
//
//await stdoutR.cancel();
//
//console.log(value);
await writer.write(encode.encode("usbip port\n"));
while (true) {
const { value, done } = stderrR.read();
}
shell.kill("SIGTERM");

1
shared/utils/2 Normal file
View File

@ -0,0 +1 @@
error_

2
shared/utils/2usbip Normal file
View File

@ -0,0 +1,2 @@
1 list -l

View File

@ -2,6 +2,8 @@ import { some } from "@shared/utils/option.ts";
import { None, type Option, Some } from "@shared/utils/option.ts";
import { errAsync, okAsync, ResultAsync } from "@shared/utils/resultasync.ts";
type ResultJSON<T, E> = { tag: "ok"; value: T } | { tag: "err"; error: E };
//#region Ok, Err and Result
interface IResult<T, E> {
isOk(): this is Ok<T, E>;
@ -14,8 +16,11 @@ interface IResult<T, E> {
unwrapOrElse<U>(fn: () => U): T | U;
match<A, B = A>(ok: (value: T) => A, err: (error: E) => B): A | B;
map<U>(fn: (value: T) => U): Result<U, E>;
mapAsync<U>(fn: (value: T) => Promise<U>): ResultAsync<U, E>;
mapErr<U>(fn: (err: E) => U): Result<T, U>;
mapErrAsync<U>(fn: (err: E) => Promise<U>): ResultAsync<T, U>;
andThen<U, F>(fn: (value: T) => Result<U, F>): Result<U, E | F>;
andThenAsync<U, F>(fn: (value: T) => ResultAsync<U, F>): ResultAsync<U, F>;
flatten(): FlattenResult<Result<T, E>>;
flattenOption<U>(errFn: () => U): Result<UnwrapOption<T>, U | E>;
flattenOptionOr<D = UnwrapOption<T>>(
@ -29,6 +34,8 @@ interface IResult<T, E> {
toNullable(): T | null;
toAsync(): ResultAsync<T, E>;
void(): Result<void, E>;
toJSON(): ResultJSON<T, E>;
}
export class Ok<T, E> implements IResult<T, E> {
@ -93,6 +100,10 @@ export class Ok<T, E> implements IResult<T, E> {
return new Ok<U, E>(mappedValue);
}
mapAsync<U>(fn: (value: T) => Promise<U>): ResultAsync<U, E> {
return ResultAsync.fromSafePromise(fn(this.value));
}
mapOption<U>(fn: (value: UnwrapOption<T>) => U): Result<Option<U>, E> {
if (this.value instanceof None || this.value instanceof Some) {
return ok(this.value.map(fn));
@ -104,10 +115,18 @@ export class Ok<T, E> implements IResult<T, E> {
return fn(this.value) as Result<U, E | F>;
}
andThenAsync<U, F>(fn: (value: T) => ResultAsync<U, F>): ResultAsync<U, F> {
return fn(this.value);
}
mapErr<U>(fn: (err: E) => U): Result<T, U> {
return new Ok<T, U>(this.value);
}
mapErrAsync<U>(fn: (err: E) => Promise<U>): ResultAsync<T, U> {
return okAsync(this.value);
}
flatten(): FlattenResult<Result<T, E>> {
return flattenResult(this);
}
@ -149,6 +168,13 @@ export class Ok<T, E> implements IResult<T, E> {
void(): Result<void, E> {
return ok();
}
toJSON(): ResultJSON<T, E> {
return {
tag: "ok",
value: this.value,
};
}
}
export class Err<T, E> implements IResult<T, E> {
@ -199,10 +225,23 @@ export class Err<T, E> implements IResult<T, E> {
map<U>(fn: (value: T) => U): Result<U, E> {
return new Err<U, E>(this.error);
}
mapAsync<U>(fn: (value: T) => Promise<U>): ResultAsync<U, E> {
return errAsync(this.error);
}
mapErr<U>(fn: (err: E) => U): Result<T, U> {
const mappedError = fn(this.error);
return new Err<T, U>(mappedError);
}
mapErrAsync<U>(fn: (err: E) => Promise<U>): ResultAsync<T, U> {
return ResultAsync.fromPromise(
new Promise(() => {
throw "";
}),
() => {
return fn(this.error);
},
);
}
mapOption<U>(fn: (value: UnwrapOption<T>) => U): Result<Option<U>, E> {
return err(this.error);
}
@ -236,6 +275,12 @@ export class Err<T, E> implements IResult<T, E> {
void(): Result<void, E> {
return err(this.error);
}
toJSON(): { tag: "ok"; value: T } | { tag: "err"; error: E } {
return {
tag: "err",
error: this.error,
};
}
}
export type Result<T, E> = Ok<T, E> | Err<T, E>;
@ -314,20 +359,6 @@ export function flattenResult<R extends Result<any, any>>(
return currentResult as FlattenResult<R>;
}
export function ResultFromJSON<T = unknown, E = unknown>(
str: string,
): Result<T, E> {
const result: { value: T } | { error: E } = JSON.parse(str);
if (obj.value) {
return ok(obj.value);
}
if (obj.error) {
return err(obj.error);
}
}
export type UnwrapOption<T> = T extends Option<infer V> ? V : T;
export type FlattenResult<R> = R extends Result<infer T, infer E>
@ -337,3 +368,59 @@ export type FlattenResult<R> = R extends Result<infer T, infer E>
: never
: R
: never;
class FailedToParseResult extends Error {
constructor(json: string) {
super(`Failed to parse ${json} as result`);
}
}
export function ResultFromJSON<T = unknown, E = unknown>(
input: string | unknown,
): Result<T, E | FailedToParseResult> {
let data: unknown;
if (typeof input === "string") {
try {
data = JSON.parse(input);
} catch (e) {
return err(
new FailedToParseResult(getMessageFromError(e)),
);
}
} else {
data = input;
}
if (typeof data !== "object" || data === null) {
return err(
new FailedToParseResult(
"Expected an object but received type ${typeof data}.",
),
);
}
const resultObj = data as ResultJSON<T, E>;
if ("tag" in resultObj) {
switch (resultObj.tag) {
case "ok": {
if ("value" in resultObj) {
return ok(resultObj.value as T);
}
break;
}
case "err": {
if ("error" in resultObj) {
return err(resultObj.error as E);
}
break;
}
}
}
return err(
new FailedToParseResult(
"Object does not contain 'tag' and 'value' or 'error' property",
),
);
}

View File

@ -1,11 +1,11 @@
import {
Err,
type UnwrapOption,
FlattenResult,
Ok,
type Result,
FlattenResult,
type UnwrapOption,
} from "@shared/utils/result.ts";
import { none, None, Option, some, Some } from "@shared/utils/option.ts";
import { None, none, Option, Some, some } from "@shared/utils/option.ts";
export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
constructor(private readonly _promise: Promise<Result<T, E>>) {
@ -37,22 +37,33 @@ export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
return new ResultAsync<T, never>(promiseOfResult);
}
static fromThrowable<Fn extends (...args: readonly any[]) => any, E>(
static fromThrowable<
Fn extends (...args: readonly any[]) => Promise<any>,
Em extends ErrorMapper<any>,
>(
fn: Fn,
errorMapper?: (e: unknown) => E,
): (...args: Parameters<Fn>) => ResultAsync<ReturnType<Fn>, E> {
return (...args: Parameters<Fn>): ResultAsync<ReturnType<Fn>, E> => {
try {
return okAsync(fn(args));
} catch (e) {
return errAsync(errorMapper ? errorMapper(e) : e);
}
errorMapper?: Em,
): (
...args: Parameters<Fn>
) => ResultAsync<
UnwrapPromise<ReturnType<Fn>>,
ExtractErrorFromMapper<Em>
> {
return (
...args: Parameters<Fn>
): ResultAsync<
UnwrapPromise<ReturnType<Fn>>,
ExtractErrorFromMapper<Em>
> => {
return ResultAsync.fromPromise(
fn(args),
(e) => errorMapper ? errorMapper(e) : e,
);
};
}
async unwrap(): Promise<T> {
const result = await this._promise;
if (result.isErr()) {
throw result.error;
}
@ -124,7 +135,23 @@ export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
);
}
andThen<U, F>(fn: (value: T) => ResultAsync<U, F>): ResultAsync<U, E | F> {
andThen<U, F>(fn: (value: T) => Result<U, F>): ResultAsync<U, E | F> {
return new ResultAsync(
this._promise.then(
(result: Result<T, E>): ResultAsync<U, E | F> => {
if (result.isErr()) {
return errAsync(result.error);
}
return fn(result.value).toAsync() as ResultAsync<U, E | F>;
},
),
);
}
andThenAsync<U, F>(
fn: (value: T) => ResultAsync<U, F>,
): ResultAsync<U, E | F> {
return new ResultAsync(
this._promise.then(
(result: Result<T, E>): ResultAsync<U, E | F> => {
@ -227,11 +254,19 @@ export function errAsync<E, T = never>(err: E): ResultAsync<T, E> {
return new ResultAsync(Promise.resolve(new Err<T, E>(err)));
}
export type FlattenResultAsync<R> =
R extends ResultAsync<infer T, infer E>
export type FlattenResultAsync<R> = R extends ResultAsync<infer T, infer E>
? T extends ResultAsync<any, any>
? FlattenResultAsync<T> extends ResultAsync<infer V, infer innerE>
? ResultAsync<V, E | innerE>
: never
: R
: never;
type UnwrapPromise<Pr extends Promise<unknown>> = Pr extends Promise<infer U>
? U
: never;
type ErrorMapper<E> = ((e: unknown) => E) | undefined;
type ExtractErrorFromMapper<Em extends ErrorMapper<unknown>> = Em extends
(e: unknown) => infer E ? E : unknown;

283
shared/utils/usbip.ts Normal file
View File

@ -0,0 +1,283 @@
import { okAsync, ResultAsync } from "@shared/utils/resultasync.ts";
import { err, getMessageFromError, ok } from "@shared/utils/result.ts";
import { errAsync } from "@shared/utils/index.ts";
import log from "@shared/utils/logger.ts";
import {
fromNullableVal,
none,
type Option,
some,
} from "@shared/utils/option.ts";
class CommandExecutionError extends Error {
code = "CommandExecutionError";
constructor(msg: string) {
super(msg);
}
}
class DeviceDoesNotExistError extends Error {
code = "DeviceDoesNotExist";
constructor(msg: string) {
super(msg);
}
}
class DeviceAlreadyBoundError extends Error {
code = "DeviceAlreadyBound";
constructor(msg: string) {
super(msg);
}
}
class DeviceNotBound extends Error {
code = "DeviceNotBound";
constructor(msg: string) {
super(msg);
}
}
class UsbipUknownError extends Error {
code = "UsbipUknownError";
constructor(msg: string) {
super(msg);
}
}
type UsbipCommonError = DeviceDoesNotExistError | UsbipUknownError;
class UsbipManager {
private readonly listDeatiledCmd = new Deno.Command("usbip", {
args: ["list", "-l"],
});
private readonly listParsableCmd = new Deno.Command("usbip", {
args: ["list", "-pl"],
});
private readonly decoder = new TextDecoder();
private readonly usbidRegex = /[0-9abcdef]{4}:[0-9abcdef]{4}/;
private readonly busidRegex =
/(?:[0-9]+(?:\.[0-9]+)*-)*[0-9]+(?:\.[0-9]+)*/;
private executeCommand(
cmd: Deno.Command,
): ResultAsync<CommandOutput, CommandExecutionError> {
const promise = cmd.output();
return ResultAsync.fromPromise(
promise,
(e) => new CommandExecutionError(getMessageFromError(e)),
)
.map(({ stdout, stderr, code }) =>
new CommandOutput(
this.decoder.decode(stdout).trim(),
this.decoder.decode(stderr).trim(),
code,
)
);
}
private handleCommonErrors(stderr: string): UsbipCommonError {
if (
stderr.includes("device with the specified bus ID does not exist")
) {
return new DeviceDoesNotExistError(stderr);
}
return new UsbipUknownError(stderr);
}
private parseDetailedList(stdout: string): Option<DeviceDetailed[]> {
const devices: DeviceDetailed[] = [];
const deviceEntries = stdout.trim().split("\n\n");
for (const deviceEntry of deviceEntries) {
const busid = deviceEntry.match(this.busidRegex)?.shift();
if (!busid) {
log.error(
`Failed to parse busid of a device:\n ${deviceEntry}`,
);
continue;
}
const usbid = fromNullableVal(
deviceEntry.match(this.usbidRegex)?.shift(),
);
const [_, line2] = deviceEntry.split("\n");
const [vendorVal, nameVal] = line2
? line2.split(" : ").map((s) => s.trim())
: [undefined, undefined];
const vendor = fromNullableVal(vendorVal);
const name = nameVal
? some(
nameVal.replace(
usbid.isSome() ? usbid.value : this.usbidRegex,
"",
).replace("()", "")
.trim(),
)
: none;
[["usbid", usbid], ["vendor", vendor], ["name", name]].filter((v) =>
(v[1] as Option<string>).isNone()
).map((v) => log.warn(`Failed to parse ${v[0]}:\n ${deviceEntry}`));
devices.push({
busid,
usbid,
vendor,
name,
});
}
return devices.length > 0 ? some(devices) : none;
}
public getDevicesDetailed(): ResultAsync<
Option<DeviceDetailed[]>,
CommandExecutionError | UsbipUknownError
> {
return this.executeCommand(this.listDeatiledCmd).andThen(
({ stdout, stderr, success }) => {
if (success) {
if (stderr) {
log.warn(
`usbip list -l succeeded but encountered an error: ${stderr}`,
);
}
return ok(this.parseDetailedList(stdout));
}
return err(new UsbipUknownError(stderr));
},
);
}
private parseParsableList(stdout: string): Option<Device[]> {
const devices: Device[] = [];
const devicesEntries = stdout.trim().split("\n");
for (const deviceEntry of devicesEntries) {
const [busid, usbid] = deviceEntry
.slice(0, -1)
.split("#")
.map((v) => v.split("=")[1].trim() || undefined);
if (!busid) {
log.error(
`Failed to parse busid of a device:\n ${deviceEntry}`,
);
continue;
}
if (!usbid) {
log.warn(
`Failed to parse usbid of a device:\n ${deviceEntry}`,
);
}
devices.push({
busid,
usbid: fromNullableVal(usbid),
});
}
return devices.length > 0 ? some(devices) : none;
}
public getDevices(): ResultAsync<
Option<Device[]>,
CommandExecutionError | UsbipUknownError
> {
return this.executeCommand(this.listParsableCmd).andThenAsync(
({ stdout, stderr, success }) => {
if (success) {
if (stderr) {
log.warn(
`usbip list -lp succeeded but encountered an error: ${stderr}`,
);
}
return okAsync(this.parseParsableList(stdout));
}
return errAsync(new UsbipUknownError(stderr));
},
);
}
public bindDevice(
busid: string,
): ResultAsync<
string,
UsbipCommonError | DeviceAlreadyBoundError | CommandExecutionError
> {
const cmd = new Deno.Command("usbip", { args: ["bind", "-b", busid] });
return this.executeCommand(cmd).andThen(
({ stderr, success }) => {
if (success) {
return ok(stderr.trim() || "Device bound successfully");
}
if (stderr.includes("is already bound to usbip-host")) {
return err(new DeviceAlreadyBoundError(stderr));
}
return err(this.handleCommonErrors(stderr));
},
);
}
public unbindDevice(
busid: string,
): ResultAsync<
string,
CommandExecutionError | DeviceNotBound | UsbipCommonError
> {
const cmd = new Deno.Command("usbip", {
args: ["unbind", "-b", busid],
});
return this.executeCommand(cmd).andThen(({ stderr, success }) => {
if (success) {
return ok(stderr.trim() || "Device unbound successfully");
}
if (stderr.includes("device is not bound to usbip-host driver")) {
return err(new DeviceNotBound(stderr));
}
return err(this.handleCommonErrors(stderr));
});
}
}
class CommandOutput {
constructor(
public readonly stdout: string,
public readonly stderr: string,
public readonly code: number,
) {}
get success(): boolean {
return this.code === 0;
}
}
interface DeviceDetailed {
busid: string;
usbid: Option<string>;
vendor: Option<string>;
name: Option<string>;
}
interface Device {
busid: string;
usbid: Option<string>;
}
export default UsbipManager;

107
shared/utils/usbipTest.ts Normal file
View File

@ -0,0 +1,107 @@
import { ResultAsync } from "@shared/utils/resultasync.ts";
import { getMessageFromError } from "@shared/utils/result.ts";
const shellCmd = new Deno.Command("/bin/sh", {
stdin: "piped",
stdout: "piped",
stderr: "piped",
});
class UsbipController {
private shell = shellCmd.spawn();
private stdinWriter = this.shell.stdin.getWriter();
private stdoutReader = this.shell.stdout.pipeThrough(
new TextDecoderStream(),
).getReader();
private stderrReader = this.shell.stderr.pipeThrough(
new TextDecoderStream(),
).getReader();
private readonly encoder = new TextEncoder();
private readonly marker = "___FINISHED___";
constructor() {}
run(cmd: string): ResultAsync<CommandOutput, string> {
return ResultAsync.fromPromise(
(async () => {
cmd = `${cmd}\necho ${this.marker}\necho ${this.marker} >&2\n`;
await this.stdinWriter.write(this.encoder.encode(
cmd,
));
let outFinished = false, errFinished = false;
let stdout = "", stderr = "";
while (!outFinished || !errFinished) {
if (!outFinished) {
const { value } = await this.stdoutReader.read();
if (!outFinished) {
if (
value?.includes(this.marker)
) {
stdout = stdout.trim();
outFinished = true;
} else {
stdout += value;
}
}
}
if (!errFinished) {
const { value } = await this.stderrReader.read();
if (!errFinished) {
if (
value?.includes(this.marker)
) {
stderr = stderr.trim();
errFinished = true;
} else {
stderr += value;
}
}
}
}
return {
stdout: stdout.trim(),
stderr: stderr.trim(),
};
})(),
(e) => getMessageFromError(e),
);
}
}
interface CommandOutput {
stdout: string;
stderr: string;
}
const usbip = new UsbipController();
let a = 0;
for (let i = 0; i < 100; i++) {
const t = performance.now();
console.log(await usbip.run("usbip list -l"));
a += performance.now() - t;
}
console.log(a / 100);
//await usbip.run("usbip list -l");
//await usbip.run("usbip list -l");
//await usbip.run("usbip list -l");
//await usbip.run("usbip list -l");
//new Deno.Command("usbip", { args: ["list", "-l"] }).outputSync();
//new Deno.Command("usbip", { args: ["list", "-l"] }).outputSync();
//new Deno.Command("usbip", { args: ["list", "-l"] }).outputSync();
//new Deno.Command("usbip", { args: ["list", "-l"] }).outputSync();
//new Deno.Command("usbip", { args: ["list", "-l"] }).outputSync();
//const decoder = new TextDecoder();
//
//const now1 = new Date().getTime();
//
Deno.exit(0);

View File

@ -0,0 +1,9 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
export function assert(expr: unknown, msg = ""): asserts expr {
if (!expr) {
throw new AssertionError(msg);
}
}

View File

@ -0,0 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
export class AssertionError extends Error {
override name = "AssertionError";
constructor(message: string) {
super(message);
}
}

View File

@ -0,0 +1,151 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { deferred } from "./deferred.ts";
/**
* Make Promise abortable with the given signal.
*
* @example
* ```typescript
* import { abortable } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
* import { delay } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
*
* const p = delay(1000);
* const c = new AbortController();
* setTimeout(() => c.abort(), 100);
*
* // Below throws `DOMException` after 100 ms
* await abortable(p, c.signal);
* ```
*/
export function abortable<T>(p: Promise<T>, signal: AbortSignal): Promise<T>;
/**
* Make AsyncIterable abortable with the given signal.
*
* @example
* ```typescript
* import { abortable } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
* import { delay } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
*
* const p = async function* () {
* yield "Hello";
* await delay(1000);
* yield "World";
* };
* const c = new AbortController();
* setTimeout(() => c.abort(), 100);
*
* // Below throws `DOMException` after 100 ms
* // and items become `["Hello"]`
* const items: string[] = [];
* for await (const item of abortable(p(), c.signal)) {
* items.push(item);
* }
* ```
*/
export function abortable<T>(
p: AsyncIterable<T>,
signal: AbortSignal,
): AsyncGenerator<T>;
export function abortable<T>(
p: Promise<T> | AsyncIterable<T>,
signal: AbortSignal,
): Promise<T> | AsyncIterable<T> {
if (p instanceof Promise) {
return abortablePromise(p, signal);
} else {
return abortableAsyncIterable(p, signal);
}
}
/**
* Make Promise abortable with the given signal.
*
* @example
* ```typescript
* import { abortablePromise } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
*
* const request = fetch("https://example.com");
*
* const c = new AbortController();
* setTimeout(() => c.abort(), 100);
*
* const p = abortablePromise(request, c.signal);
*
* // The below throws if the request didn't resolve in 100ms
* await p;
* ```
*/
export function abortablePromise<T>(
p: Promise<T>,
signal: AbortSignal,
): Promise<T> {
if (signal.aborted) {
return Promise.reject(createAbortError(signal.reason));
}
const waiter = deferred<never>();
const abort = () => waiter.reject(createAbortError(signal.reason));
signal.addEventListener("abort", abort, { once: true });
return Promise.race([
waiter,
p.finally(() => {
signal.removeEventListener("abort", abort);
}),
]);
}
/**
* Make AsyncIterable abortable with the given signal.
*
* @example
* ```typescript
* import { abortableAsyncIterable } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
* import { delay } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
*
* const p = async function* () {
* yield "Hello";
* await delay(1000);
* yield "World";
* };
* const c = new AbortController();
* setTimeout(() => c.abort(), 100);
*
* // Below throws `DOMException` after 100 ms
* // and items become `["Hello"]`
* const items: string[] = [];
* for await (const item of abortableAsyncIterable(p(), c.signal)) {
* items.push(item);
* }
* ```
*/
export async function* abortableAsyncIterable<T>(
p: AsyncIterable<T>,
signal: AbortSignal,
): AsyncGenerator<T> {
if (signal.aborted) {
throw createAbortError(signal.reason);
}
const waiter = deferred<never>();
const abort = () => waiter.reject(createAbortError(signal.reason));
signal.addEventListener("abort", abort, { once: true });
const it = p[Symbol.asyncIterator]();
while (true) {
const { done, value } = await Promise.race([waiter, it.next()]);
if (done) {
signal.removeEventListener("abort", abort);
return;
}
yield value;
}
}
// This `reason` comes from `AbortSignal` thus must be `any`.
// deno-lint-ignore no-explicit-any
function createAbortError(reason?: any): DOMException {
return new DOMException(
reason ? `Aborted: ${reason}` : "Aborted",
"AbortError",
);
}

View File

@ -0,0 +1,48 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { delay } from "./delay.ts";
export interface DeadlineOptions {
/** Signal used to abort the deadline. */
signal?: AbortSignal;
}
export class DeadlineError extends Error {
constructor() {
super("Deadline");
this.name = this.constructor.name;
}
}
/**
* Create a promise which will be rejected with {@linkcode DeadlineError} when a given delay is exceeded.
*
* NOTE: Prefer to use `AbortSignal.timeout` instead for the APIs accept `AbortSignal`.
*
* @example
* ```typescript
* import { deadline } from "https://deno.land/std@$STD_VERSION/async/deadline.ts";
* import { delay } from "https://deno.land/std@$STD_VERSION/async/delay.ts";
*
* const delayedPromise = delay(1000);
* // Below throws `DeadlineError` after 10 ms
* const result = await deadline(delayedPromise, 10);
* ```
*/
export function deadline<T>(
p: Promise<T>,
ms: number,
options: DeadlineOptions = {},
): Promise<T> {
const controller = new AbortController();
const { signal } = options;
if (signal?.aborted) {
return Promise.reject(new DeadlineError());
}
signal?.addEventListener("abort", () => controller.abort(signal.reason));
const d = delay(ms, { signal: controller.signal })
.catch(() => {}) // Do NOTHING on abort.
.then(() => Promise.reject(new DeadlineError()));
return Promise.race([p.finally(() => controller.abort()), d]);
}

View File

@ -0,0 +1,79 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/**
* A debounced function that will be delayed by a given `wait`
* time in milliseconds. If the method is called again before
* the timeout expires, the previous call will be aborted.
*/
export interface DebouncedFunction<T extends Array<unknown>> {
(...args: T): void;
/** Clears the debounce timeout and omits calling the debounced function. */
clear(): void;
/** Clears the debounce timeout and calls the debounced function immediately. */
flush(): void;
/** Returns a boolean whether a debounce call is pending or not. */
readonly pending: boolean;
}
/**
* Creates a debounced function that delays the given `func`
* by a given `wait` time in milliseconds. If the method is called
* again before the timeout expires, the previous call will be
* aborted.
*
* @example
* ```
* import { debounce } from "https://deno.land/std@$STD_VERSION/async/debounce.ts";
*
* const log = debounce(
* (event: Deno.FsEvent) =>
* console.log("[%s] %s", event.kind, event.paths[0]),
* 200,
* );
*
* for await (const event of Deno.watchFs("./")) {
* log(event);
* }
* // wait 200ms ...
* // output: Function debounced after 200ms with baz
* ```
*
* @param fn The function to debounce.
* @param wait The time in milliseconds to delay the function.
*/
// deno-lint-ignore no-explicit-any
export function debounce<T extends Array<any>>(
fn: (this: DebouncedFunction<T>, ...args: T) => void,
wait: number,
): DebouncedFunction<T> {
let timeout: number | null = null;
let flush: (() => void) | null = null;
const debounced: DebouncedFunction<T> = ((...args: T) => {
debounced.clear();
flush = () => {
debounced.clear();
fn.call(debounced, ...args);
};
timeout = setTimeout(flush, wait);
}) as DebouncedFunction<T>;
debounced.clear = () => {
if (typeof timeout === "number") {
clearTimeout(timeout);
timeout = null;
flush = null;
}
};
debounced.flush = () => {
flush?.();
};
Object.defineProperty(debounced, "pending", {
get: () => typeof timeout === "number",
});
return debounced;
}

View File

@ -0,0 +1,48 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
// TODO(ry) It'd be better to make Deferred a class that inherits from
// Promise, rather than an interface. This is possible in ES2016, however
// typescript produces broken code when targeting ES5 code.
// See https://github.com/Microsoft/TypeScript/issues/15202
// At the time of writing, the github issue is closed but the problem remains.
export interface Deferred<T> extends Promise<T> {
readonly state: "pending" | "fulfilled" | "rejected";
resolve(value?: T | PromiseLike<T>): void;
// deno-lint-ignore no-explicit-any
reject(reason?: any): void;
}
/**
* Creates a Promise with the `reject` and `resolve` functions placed as methods
* on the promise object itself.
*
* @example
* ```typescript
* import { deferred } from "https://deno.land/std@$STD_VERSION/async/deferred.ts";
*
* const p = deferred<number>();
* // ...
* p.resolve(42);
* ```
*/
export function deferred<T>(): Deferred<T> {
let methods;
let state = "pending";
const promise = new Promise<T>((resolve, reject) => {
methods = {
async resolve(value: T | PromiseLike<T>) {
await value;
state = "fulfilled";
resolve(value);
},
// deno-lint-ignore no-explicit-any
reject(reason?: any) {
state = "rejected";
reject(reason);
},
};
});
Object.defineProperty(promise, "state", { get: () => state });
return Object.assign(promise, methods) as Deferred<T>;
}

View File

@ -0,0 +1,65 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
export interface DelayOptions {
/** Signal used to abort the delay. */
signal?: AbortSignal;
/** Indicates whether the process should continue to run as long as the timer exists.
*
* @default {true}
*/
persistent?: boolean;
}
/**
* Resolve a Promise after a given amount of milliseconds.
*
* @example
*
* ```typescript
* import { delay } from "https://deno.land/std@$STD_VERSION/async/delay.ts";
*
* // ...
* const delayedPromise = delay(100);
* const result = await delayedPromise;
* // ...
* ```
*
* To allow the process to continue to run as long as the timer exists. Requires
* `--unstable` flag.
*
* ```typescript
* import { delay } from "https://deno.land/std@$STD_VERSION/async/delay.ts";
*
* // ...
* await delay(100, { persistent: false });
* // ...
* ```
*/
export function delay(ms: number, options: DelayOptions = {}): Promise<void> {
const { signal, persistent } = options;
if (signal?.aborted) return Promise.reject(signal.reason);
return new Promise((resolve, reject) => {
const abort = () => {
clearTimeout(i);
reject(signal?.reason);
};
const done = () => {
signal?.removeEventListener("abort", abort);
resolve();
};
const i = setTimeout(done, ms);
signal?.addEventListener("abort", abort, { once: true });
if (persistent === false) {
try {
// @ts-ignore For browser compatibility
Deno.unrefTimer(i);
} catch (error) {
if (!(error instanceof ReferenceError)) {
throw error;
}
console.error("`persistent` option is only available in Deno");
}
}
});
}

View File

@ -0,0 +1,18 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
/**
* Provide help with asynchronous tasks like delays, debouncing, deferring, or
* pooling.
*
* @module
*/
export * from "./abortable.ts";
export * from "./deadline.ts";
export * from "./debounce.ts";
export * from "./deferred.ts";
export * from "./delay.ts";
export * from "./mux_async_iterator.ts";
export * from "./pool.ts";
export * from "./tee.ts";
export * from "./retry.ts";

View File

@ -0,0 +1,97 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { Deferred, deferred } from "./deferred.ts";
interface TaggedYieldedValue<T> {
iterator: AsyncIterator<T>;
value: T;
}
/**
* The MuxAsyncIterator class multiplexes multiple async iterators into a single
* stream. It currently makes an assumption that the final result (the value
* returned and not yielded from the iterator) does not matter; if there is any
* result, it is discarded.
*
* @example
* ```typescript
* import { MuxAsyncIterator } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
*
* async function* gen123(): AsyncIterableIterator<number> {
* yield 1;
* yield 2;
* yield 3;
* }
*
* async function* gen456(): AsyncIterableIterator<number> {
* yield 4;
* yield 5;
* yield 6;
* }
*
* const mux = new MuxAsyncIterator<number>();
* mux.add(gen123());
* mux.add(gen456());
* for await (const value of mux) {
* // ...
* }
* // ..
* ```
*/
export class MuxAsyncIterator<T> implements AsyncIterable<T> {
#iteratorCount = 0;
#yields: Array<TaggedYieldedValue<T>> = [];
// deno-lint-ignore no-explicit-any
#throws: any[] = [];
#signal: Deferred<void> = deferred();
add(iterable: AsyncIterable<T>) {
++this.#iteratorCount;
this.#callIteratorNext(iterable[Symbol.asyncIterator]());
}
async #callIteratorNext(
iterator: AsyncIterator<T>,
) {
try {
const { value, done } = await iterator.next();
if (done) {
--this.#iteratorCount;
} else {
this.#yields.push({ iterator, value });
}
} catch (e) {
this.#throws.push(e);
}
this.#signal.resolve();
}
async *iterate(): AsyncIterableIterator<T> {
while (this.#iteratorCount > 0) {
// Sleep until any of the wrapped iterators yields.
await this.#signal;
// Note that while we're looping over `yields`, new items may be added.
for (let i = 0; i < this.#yields.length; i++) {
const { iterator, value } = this.#yields[i];
yield value;
this.#callIteratorNext(iterator);
}
if (this.#throws.length) {
for (const e of this.#throws) {
throw e;
}
this.#throws.length = 0;
}
// Clear the `yields` list and reset the `signal` promise.
this.#yields.length = 0;
this.#signal = deferred();
}
}
[Symbol.asyncIterator](): AsyncIterator<T> {
return this.iterate();
}
}

View File

@ -0,0 +1,108 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
export const ERROR_WHILE_MAPPING_MESSAGE = "Threw while mapping.";
/**
* pooledMap transforms values from an (async) iterable into another async
* iterable. The transforms are done concurrently, with a max concurrency
* defined by the poolLimit.
*
* If an error is thrown from `iterableFn`, no new transformations will begin.
* All currently executing transformations are allowed to finish and still
* yielded on success. After that, the rejections among them are gathered and
* thrown by the iterator in an `AggregateError`.
*
* @example
* ```typescript
* import { pooledMap } from "https://deno.land/std@$STD_VERSION/async/pool.ts";
*
* const results = pooledMap(
* 2,
* [1, 2, 3],
* (i) => new Promise((r) => setTimeout(() => r(i), 1000)),
* );
*
* for await (const value of results) {
* // ...
* }
* ```
*
* @param poolLimit The maximum count of items being processed concurrently.
* @param array The input array for mapping.
* @param iteratorFn The function to call for every item of the array.
*/
export function pooledMap<T, R>(
poolLimit: number,
array: Iterable<T> | AsyncIterable<T>,
iteratorFn: (data: T) => Promise<R>,
): AsyncIterableIterator<R> {
// Create the async iterable that is returned from this function.
const res = new TransformStream<Promise<R>, R>({
async transform(
p: Promise<R>,
controller: TransformStreamDefaultController<R>,
) {
try {
const s = await p;
controller.enqueue(s);
} catch (e) {
if (
e instanceof AggregateError &&
e.message === ERROR_WHILE_MAPPING_MESSAGE
) {
controller.error(e as unknown);
}
}
},
});
// Start processing items from the iterator
(async () => {
const writer = res.writable.getWriter();
const executing: Array<Promise<unknown>> = [];
try {
for await (const item of array) {
const p = Promise.resolve().then(() => iteratorFn(item));
// Only write on success. If we `writer.write()` a rejected promise,
// that will end the iteration. We don't want that yet. Instead let it
// fail the race, taking us to the catch block where all currently
// executing jobs are allowed to finish and all rejections among them
// can be reported together.
writer.write(p);
const e: Promise<unknown> = p.then(() =>
executing.splice(executing.indexOf(e), 1)
);
executing.push(e);
if (executing.length >= poolLimit) {
await Promise.race(executing);
}
}
// Wait until all ongoing events have processed, then close the writer.
await Promise.all(executing);
writer.close();
} catch {
const errors = [];
for (const result of await Promise.allSettled(executing)) {
if (result.status === "rejected") {
errors.push(result.reason);
}
}
writer.write(Promise.reject(
new AggregateError(errors, ERROR_WHILE_MAPPING_MESSAGE),
)).catch(() => {});
}
})();
// Feature test until browser coverage is adequate
return Symbol.asyncIterator in res.readable &&
typeof res.readable[Symbol.asyncIterator] === "function"
? (res.readable[Symbol.asyncIterator] as () => AsyncIterableIterator<R>)()
: (async function* () {
const reader = res.readable.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
yield value;
}
reader.releaseLock();
})();
}

View File

@ -0,0 +1,129 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { assert } from "../assert/assert.ts";
export class RetryError extends Error {
constructor(cause: unknown, attempts: number) {
super(`Retrying exceeded the maxAttempts (${attempts}).`);
this.name = "RetryError";
this.cause = cause;
}
}
export interface RetryOptions {
/** How much to backoff after each retry. This is `2` by default. */
multiplier?: number;
/** The maximum milliseconds between attempts. This is `60000` by default. */
maxTimeout?: number;
/** The maximum amount of attempts until failure. This is `5` by default. */
maxAttempts?: number;
/** The initial and minimum amount of milliseconds between attempts. This is `1000` by default. */
minTimeout?: number;
/** Amount of jitter to introduce to the time between attempts. This is `1` for full jitter by default. */
jitter?: number;
}
const defaultRetryOptions: Required<RetryOptions> = {
multiplier: 2,
maxTimeout: 60000,
maxAttempts: 5,
minTimeout: 1000,
jitter: 1,
};
/**
* Calls the given (possibly asynchronous) function up to `maxAttempts` times.
* Retries as long as the given function throws.
* If the attempts are exhausted, throws an `RetryError` with `cause` set to the inner exception.
*
* The backoff is calculated by multiplying `minTimeout` with `multiplier` to the power of the current attempt counter (starting at 0 up to `maxAttempts - 1`). It is capped at `maxTimeout` however.
* How long the actual delay is, depends on `jitter`.
*
* When `jitter` is the default value of `1`, waits between two attempts for a randomized amount between 0 and the backoff time.
* With the default options the maximal delay will be `15s = 1s + 2s + 4s + 8s`. If all five attempts are exhausted the mean delay will be `9.5s = ½(4s + 15s)`.
*
* When `jitter` is `0`, waits the full backoff time.
*
* @example
* ```typescript
* import { retry } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
* const req = async () => {
* // some function that throws sometimes
* };
*
* // Below resolves to the first non-error result of `req`
* const retryPromise = await retry(req, {
* multiplier: 2,
* maxTimeout: 60000,
* maxAttempts: 5,
* minTimeout: 100,
* jitter: 1,
* });
* ```
*
* @example
* ```typescript
* import { retry } from "https://deno.land/std@$STD_VERSION/async/mod.ts";
* const req = async () => {
* // some function that throws sometimes
* };
*
* // Make sure we wait at least 1 minute, but at most 2 minutes
* const retryPromise = await retry(req, {
* multiplier: 2.34,
* maxTimeout: 80000,
* maxAttempts: 7,
* minTimeout: 1000,
* jitter: 0.5,
* });
* ```
*/
export async function retry<T>(
fn: (() => Promise<T>) | (() => T),
opts?: RetryOptions,
) {
const options: Required<RetryOptions> = {
...defaultRetryOptions,
...opts,
};
assert(options.maxTimeout >= 0, "maxTimeout is less than 0");
assert(
options.minTimeout <= options.maxTimeout,
"minTimeout is greater than maxTimeout",
);
assert(options.jitter <= 1, "jitter is greater than 1");
let attempt = 0;
while (true) {
try {
return await fn();
} catch (error) {
if (attempt + 1 >= options.maxAttempts) {
throw new RetryError(error, options.maxAttempts);
}
const timeout = _exponentialBackoffWithJitter(
options.maxTimeout,
options.minTimeout,
attempt,
options.multiplier,
options.jitter,
);
await new Promise((r) => setTimeout(r, timeout));
}
attempt++;
}
}
export function _exponentialBackoffWithJitter(
cap: number,
base: number,
attempt: number,
multiplier: number,
jitter: number,
) {
const exp = Math.min(cap, base * multiplier ** attempt);
return (1 - jitter * Math.random()) * exp;
}

View File

@ -0,0 +1,100 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
// Utility for representing n-tuple
type Tuple<T, N extends number> = N extends N
? number extends N ? T[] : TupleOf<T, N, []>
: never;
type TupleOf<T, N extends number, R extends unknown[]> = R["length"] extends N
? R
: TupleOf<T, N, [T, ...R]>;
interface QueueNode<T> {
value: T;
next: QueueNode<T> | undefined;
}
class Queue<T> {
#source: AsyncIterator<T>;
#queue: QueueNode<T>;
head: QueueNode<T>;
done: boolean;
constructor(iterable: AsyncIterable<T>) {
this.#source = iterable[Symbol.asyncIterator]();
this.#queue = {
value: undefined!,
next: undefined,
};
this.head = this.#queue;
this.done = false;
}
async next() {
const result = await this.#source.next();
if (!result.done) {
const nextNode: QueueNode<T> = {
value: result.value,
next: undefined,
};
this.#queue.next = nextNode;
this.#queue = nextNode;
} else {
this.done = true;
}
}
}
/**
* Branches the given async iterable into the n branches.
*
* @example
* ```ts
* import { tee } from "https://deno.land/std@$STD_VERSION/async/tee.ts";
*
* const gen = async function* gen() {
* yield 1;
* yield 2;
* yield 3;
* };
*
* const [branch1, branch2] = tee(gen());
*
* for await (const n of branch1) {
* console.log(n); // => 1, 2, 3
* }
*
* for await (const n of branch2) {
* console.log(n); // => 1, 2, 3
* }
* ```
*/
export function tee<T, N extends number = 2>(
iterable: AsyncIterable<T>,
n: N = 2 as N,
): Tuple<AsyncIterable<T>, N> {
const queue = new Queue<T>(iterable);
async function* generator(): AsyncGenerator<T> {
let buffer = queue.head;
while (true) {
if (buffer.next) {
buffer = buffer.next;
yield buffer.value;
} else if (queue.done) {
return;
} else {
await queue.next();
}
}
}
const branches = Array.from({ length: n }).map(
() => generator(),
) as Tuple<
AsyncIterable<T>,
N
>;
return branches;
}

49
vendor/jsr.io/@db/sqlite/0.12.0/deno.json generated vendored Normal file
View File

@ -0,0 +1,49 @@
{
"name": "@db/sqlite",
"version": "0.12.0",
"github": "https://github.com/denodrivers/sqlite3",
"exports": "./mod.ts",
"exclude": [
"sqlite",
"scripts"
],
"tasks": {
"test": "deno test --unstable-ffi -A test/test.ts",
"build": "deno run -A scripts/build.ts",
"bench-deno": "deno run -A --unstable-ffi bench/bench_deno.js 50 1000000",
"bench-deno-ffi": "deno run -A --unstable-ffi bench/bench_deno_ffi.js 50 1000000",
"bench-deno-wasm": "deno run -A --unstable-ffi bench/bench_deno_wasm.js 50 1000000",
"bench-node": "node bench/bench_node.js 50 1000000",
"bench-bun": "bun run bench/bench_bun.js 50 1000000",
"bench-bun-ffi": "bun run bench/bench_bun_ffi.js 50 1000000",
"bench-c": "./bench/bench 50 1000000",
"bench-python": "python ./bench/bench_python.py",
"bench:northwind": "deno bench -A --unstable-ffi bench/northwind/deno.js",
"bench-wasm:northwind": "deno run -A --unstable-ffi bench/northwind/deno_wasm.js",
"bench-node:northwind": "node bench/northwind/node.mjs",
"bench-bun:northwind": "bun run bench/northwind/bun.js"
},
"fmt": {
"exclude": [
"sqlite"
]
},
"lint": {
"rules": {
"exclude": [
"camelcase",
"no-explicit-any"
],
"include": [
"explicit-function-return-type",
"eqeqeq",
"explicit-module-boundary-types"
]
}
}
}

2
vendor/jsr.io/@db/sqlite/0.12.0/deps.ts generated vendored Normal file
View File

@ -0,0 +1,2 @@
export { fromFileUrl } from "jsr:@std/path@0.217";
export { dlopen } from "jsr:@denosaurs/plug@1";

18
vendor/jsr.io/@db/sqlite/0.12.0/mod.ts generated vendored Normal file
View File

@ -0,0 +1,18 @@
export {
type AggregateFunctionOptions,
Database,
type DatabaseOpenOptions,
type FunctionOptions,
isComplete,
SQLITE_SOURCEID,
SQLITE_VERSION,
type Transaction,
} from "./src/database.ts";
export { type BlobOpenOptions, SQLBlob } from "./src/blob.ts";
export {
type BindParameters,
type BindValue,
type RestBindParameters,
Statement,
} from "./src/statement.ts";
export { SqliteError } from "./src/util.ts";

149
vendor/jsr.io/@db/sqlite/0.12.0/src/blob.ts generated vendored Normal file
View File

@ -0,0 +1,149 @@
import type { Database } from "./database.ts";
import ffi from "./ffi.ts";
import { toCString, unwrap } from "./util.ts";
const {
sqlite3_blob_open,
sqlite3_blob_bytes,
sqlite3_blob_close,
sqlite3_blob_read,
sqlite3_blob_write,
} = ffi;
/** Various options that can be configured when opening a Blob via `Database#openBlob`. */
export interface BlobOpenOptions {
/** Whether to open Blob in readonly mode. True by default. */
readonly?: boolean;
/** Database to open Blob from, "main" by default. */
db?: string;
/** Table the Blob is in */
table: string;
/** Column name of the Blob Field */
column: string;
/** Row ID of which column to select */
row: number;
}
/**
* Enumerates SQLite3 Blob opened for streamed I/O.
*
* BLOB columns still return a `Uint8Array` of the data.
* You can instead open this from `Database.openBlob()`.
*
* @see https://www.sqlite.org/c3ref/blob_open.html
*/
export class SQLBlob {
#handle: Deno.PointerValue;
constructor(db: Database, options: BlobOpenOptions) {
options = Object.assign({
readonly: true,
db: "main",
}, options);
const pHandle = new BigUint64Array(1);
unwrap(sqlite3_blob_open(
db.unsafeHandle,
toCString(options.db ?? "main"),
toCString(options.table),
toCString(options.column),
BigInt(options.row),
options.readonly === false ? 1 : 0,
pHandle,
));
this.#handle = Deno.UnsafePointer.create(pHandle[0]);
}
/** Byte size of the Blob */
get byteLength(): number {
return sqlite3_blob_bytes(this.#handle);
}
/** Read from the Blob at given offset into a buffer (Uint8Array) */
readSync(offset: number, p: Uint8Array): void {
unwrap(sqlite3_blob_read(this.#handle, p, p.byteLength, offset));
}
/** Write a buffer (Uint8Array) at given offset in the Blob */
writeSync(offset: number, p: Uint8Array): void {
unwrap(sqlite3_blob_write(this.#handle, p, p.byteLength, offset));
}
/** Close the Blob. It **must** be called to prevent leaks. */
close(): void {
unwrap(sqlite3_blob_close(this.#handle));
}
/** Obtains Web Stream for reading the Blob */
get readable(): ReadableStream<Uint8Array> {
const length = this.byteLength;
let offset = 0;
return new ReadableStream({
type: "bytes",
pull: (ctx) => {
try {
const byob = ctx.byobRequest;
if (byob) {
const toRead = Math.min(
length - offset,
byob.view!.byteLength,
);
this.readSync(
offset,
(byob.view as Uint8Array).subarray(0, toRead),
);
offset += toRead;
byob.respond(toRead);
} else {
const toRead = Math.min(
length - offset,
ctx.desiredSize || 1024 * 16,
);
if (toRead === 0) {
ctx.close();
return;
}
const buffer = new Uint8Array(toRead);
this.readSync(offset, buffer);
offset += toRead;
ctx.enqueue(buffer);
}
} catch (e) {
ctx.error(e);
ctx.byobRequest?.respond(0);
}
},
});
}
/** Obtains Web Stream for writing to the Blob */
get writable(): WritableStream<Uint8Array> {
const length = this.byteLength;
let offset = 0;
return new WritableStream({
write: (chunk, ctx) => {
if (offset + chunk.byteLength > length) {
ctx.error(new Error("Write exceeds blob length"));
return;
}
this.writeSync(offset, chunk);
offset += chunk.byteLength;
},
});
}
*[Symbol.iterator](): IterableIterator<Uint8Array> {
const length = this.byteLength;
let offset = 0;
while (offset < length) {
const toRead = Math.min(length - offset, 1024 * 16);
const buffer = new Uint8Array(toRead);
this.readSync(offset, buffer);
offset += toRead;
yield buffer;
}
}
[Symbol.for("Deno.customInspect")](): string {
return `SQLite3.Blob(0x${this.byteLength.toString(16)})`;
}
}

67
vendor/jsr.io/@db/sqlite/0.12.0/src/constants.ts generated vendored Normal file
View File

@ -0,0 +1,67 @@
// Result Codes
export const SQLITE3_OK = 0;
export const SQLITE3_ERROR = 1;
export const SQLITE3_INTERNAL = 2;
export const SQLITE3_PERM = 3;
export const SQLITE3_ABORT = 4;
export const SQLITE3_BUSY = 5;
export const SQLITE3_LOCKED = 6;
export const SQLITE3_NOMEM = 7;
export const SQLITE3_READONLY = 8;
export const SQLITE3_INTERRUPT = 9;
export const SQLITE3_IOERR = 10;
export const SQLITE3_CORRUPT = 11;
export const SQLITE3_NOTFOUND = 12;
export const SQLITE3_FULL = 13;
export const SQLITE3_CANTOPEN = 14;
export const SQLITE3_PROTOCOL = 15;
export const SQLITE3_EMPTY = 16;
export const SQLITE3_SCHEMA = 17;
export const SQLITE3_TOOBIG = 18;
export const SQLITE3_CONSTRAINT = 19;
export const SQLITE3_MISMATCH = 20;
export const SQLITE3_MISUSE = 21;
export const SQLITE3_NOLFS = 22;
export const SQLITE3_AUTH = 23;
export const SQLITE3_FORMAT = 24;
export const SQLITE3_RANGE = 25;
export const SQLITE3_NOTADB = 26;
export const SQLITE3_NOTICE = 27;
export const SQLITE3_WARNING = 28;
export const SQLITE3_ROW = 100;
export const SQLITE3_DONE = 101;
// Open Flags
export const SQLITE3_OPEN_READONLY = 0x00000001;
export const SQLITE3_OPEN_READWRITE = 0x00000002;
export const SQLITE3_OPEN_CREATE = 0x00000004;
export const SQLITE3_OPEN_DELETEONCLOSE = 0x00000008;
export const SQLITE3_OPEN_EXCLUSIVE = 0x00000010;
export const SQLITE3_OPEN_AUTOPROXY = 0x00000020;
export const SQLITE3_OPEN_URI = 0x00000040;
export const SQLITE3_OPEN_MEMORY = 0x00000080;
export const SQLITE3_OPEN_MAIN_DB = 0x00000100;
export const SQLITE3_OPEN_TEMP_DB = 0x00000200;
export const SQLITE3_OPEN_TRANSIENT_DB = 0x00000400;
export const SQLITE3_OPEN_MAIN_JOURNAL = 0x00000800;
export const SQLITE3_OPEN_TEMP_JOURNAL = 0x00001000;
export const SQLITE3_OPEN_SUBJOURNAL = 0x00002000;
export const SQLITE3_OPEN_SUPER_JOURNAL = 0x00004000;
export const SQLITE3_OPEN_NONMUTEX = 0x00008000;
export const SQLITE3_OPEN_FULLMUTEX = 0x00010000;
export const SQLITE3_OPEN_SHAREDCACHE = 0x00020000;
export const SQLITE3_OPEN_PRIVATECACHE = 0x00040000;
export const SQLITE3_OPEN_WAL = 0x00080000;
export const SQLITE3_OPEN_NOFOLLOW = 0x01000000;
// Prepare Flags
export const SQLITE3_PREPARE_PERSISTENT = 0x00000001;
export const SQLITE3_PREPARE_NORMALIZE = 0x00000002;
export const SQLITE3_PREPARE_NO_VTAB = 0x00000004;
// Fundamental Datatypes
export const SQLITE_INTEGER = 1;
export const SQLITE_FLOAT = 2;
export const SQLITE_TEXT = 3;
export const SQLITE_BLOB = 4;
export const SQLITE_NULL = 5;

884
vendor/jsr.io/@db/sqlite/0.12.0/src/database.ts generated vendored Normal file
View File

@ -0,0 +1,884 @@
import ffi from "./ffi.ts";
import { fromFileUrl } from "../deps.ts";
import {
SQLITE3_OPEN_CREATE,
SQLITE3_OPEN_MEMORY,
SQLITE3_OPEN_READONLY,
SQLITE3_OPEN_READWRITE,
SQLITE_BLOB,
SQLITE_FLOAT,
SQLITE_INTEGER,
SQLITE_NULL,
SQLITE_TEXT,
} from "./constants.ts";
import { readCstr, toCString, unwrap } from "./util.ts";
import {
type RestBindParameters,
Statement,
STATEMENTS_TO_DB,
} from "./statement.ts";
import { type BlobOpenOptions, SQLBlob } from "./blob.ts";
/** Various options that can be configured when opening Database connection. */
export interface DatabaseOpenOptions {
/** Whether to open database only in read-only mode. By default, this is false. */
readonly?: boolean;
/** Whether to create a new database file at specified path if one does not exist already. By default this is true. */
create?: boolean;
/** Raw SQLite C API flags. Specifying this ignores all other options. */
flags?: number;
/** Opens an in-memory database. */
memory?: boolean;
/** Whether to support BigInt columns. False by default, integers larger than 32 bit will be inaccurate. */
int64?: boolean;
/** Apply agressive optimizations that are not possible with concurrent clients. */
unsafeConcurrency?: boolean;
/** Enable or disable extension loading */
enableLoadExtension?: boolean;
}
/** Transaction function created using `Database#transaction`. */
export type Transaction<T extends (...args: any[]) => void> =
& ((...args: Parameters<T>) => ReturnType<T>)
& {
/** BEGIN */
default: Transaction<T>;
/** BEGIN DEFERRED */
deferred: Transaction<T>;
/** BEGIN IMMEDIATE */
immediate: Transaction<T>;
/** BEGIN EXCLUSIVE */
exclusive: Transaction<T>;
database: Database;
};
/**
* Options for user-defined functions.
*
* @link https://www.sqlite.org/c3ref/c_deterministic.html
*/
export interface FunctionOptions {
varargs?: boolean;
deterministic?: boolean;
directOnly?: boolean;
innocuous?: boolean;
subtype?: boolean;
}
/**
* Options for user-defined aggregate functions.
*/
export interface AggregateFunctionOptions extends FunctionOptions {
start: any | (() => any);
step: (aggregate: any, ...args: any[]) => void;
final?: (aggregate: any) => any;
}
const {
sqlite3_open_v2,
sqlite3_close_v2,
sqlite3_changes,
sqlite3_total_changes,
sqlite3_last_insert_rowid,
sqlite3_get_autocommit,
sqlite3_exec,
sqlite3_free,
sqlite3_libversion,
sqlite3_sourceid,
sqlite3_complete,
sqlite3_finalize,
sqlite3_result_blob,
sqlite3_result_double,
sqlite3_result_error,
sqlite3_result_int64,
sqlite3_result_null,
sqlite3_result_text,
sqlite3_value_blob,
sqlite3_value_bytes,
sqlite3_value_double,
sqlite3_value_int64,
sqlite3_value_text,
sqlite3_value_type,
sqlite3_create_function,
sqlite3_result_int,
sqlite3_aggregate_context,
sqlite3_enable_load_extension,
sqlite3_load_extension,
sqlite3_backup_init,
sqlite3_backup_step,
sqlite3_backup_finish,
sqlite3_errcode,
} = ffi;
/** SQLite version string */
export const SQLITE_VERSION: string = readCstr(sqlite3_libversion()!);
/** SQLite source ID string */
export const SQLITE_SOURCEID: string = readCstr(sqlite3_sourceid()!);
/**
* Whether the given SQL statement is complete.
*
* @param statement SQL statement string
*/
export function isComplete(statement: string): boolean {
return Boolean(sqlite3_complete(toCString(statement)));
}
const BIG_MAX = BigInt(Number.MAX_SAFE_INTEGER);
/**
* Represents a SQLite3 database connection.
*
* Example:
* ```ts
* // Open a database from file, creates if doesn't exist.
* const db = new Database("myfile.db");
*
* // Open an in-memory database.
* const db = new Database(":memory:");
*
* // Open a read-only database.
* const db = new Database("myfile.db", { readonly: true });
*
* // Or open using File URL
* const db = new Database(new URL("./myfile.db", import.meta.url));
* ```
*/
export class Database {
#path: string;
#handle: Deno.PointerValue;
#open = true;
#enableLoadExtension = false;
/** Whether to support BigInt columns. False by default, integers larger than 32 bit will be inaccurate. */
int64: boolean;
unsafeConcurrency: boolean;
/** Whether DB connection is open */
get open(): boolean {
return this.#open;
}
/** Unsafe Raw (pointer) to the sqlite object */
get unsafeHandle(): Deno.PointerValue {
return this.#handle;
}
/** Path of the database file. */
get path(): string {
return this.#path;
}
/** Number of rows changed by the last executed statement. */
get changes(): number {
return sqlite3_changes(this.#handle);
}
/** Number of rows changed since the database connection was opened. */
get totalChanges(): number {
return sqlite3_total_changes(this.#handle);
}
/** Gets last inserted Row ID */
get lastInsertRowId(): number {
return Number(sqlite3_last_insert_rowid(this.#handle));
}
/** Whether autocommit is enabled. Enabled by default, can be disabled using BEGIN statement. */
get autocommit(): boolean {
return sqlite3_get_autocommit(this.#handle) === 1;
}
/** Whether DB is in mid of a transaction */
get inTransaction(): boolean {
return this.#open && !this.autocommit;
}
get enableLoadExtension(): boolean {
return this.#enableLoadExtension;
}
set enableLoadExtension(enabled: boolean) {
if (sqlite3_enable_load_extension === null) {
throw new Error(
"Extension loading is not supported by the shared library that was used.",
);
}
const result = sqlite3_enable_load_extension(this.#handle, Number(enabled));
unwrap(result, this.#handle);
this.#enableLoadExtension = enabled;
}
constructor(path: string | URL, options: DatabaseOpenOptions = {}) {
this.#path = path instanceof URL ? fromFileUrl(path) : path;
let flags = 0;
this.int64 = options.int64 ?? false;
this.unsafeConcurrency = options.unsafeConcurrency ?? false;
if (options.flags !== undefined) {
flags = options.flags;
} else {
if (options.memory) {
flags |= SQLITE3_OPEN_MEMORY;
}
if (options.readonly ?? false) {
flags |= SQLITE3_OPEN_READONLY;
} else {
flags |= SQLITE3_OPEN_READWRITE;
}
if ((options.create ?? true) && !options.readonly) {
flags |= SQLITE3_OPEN_CREATE;
}
}
const pHandle = new BigUint64Array(1);
const result = sqlite3_open_v2(toCString(this.#path), pHandle, flags, null);
this.#handle = Deno.UnsafePointer.create(pHandle[0]);
if (result !== 0) sqlite3_close_v2(this.#handle);
unwrap(result);
if (options.enableLoadExtension) {
this.enableLoadExtension = options.enableLoadExtension;
}
}
/**
* Creates a new Prepared Statement from the given SQL statement.
*
* Example:
* ```ts
* const stmt = db.prepare("SELECT * FROM mytable WHERE id = ?");
*
* for (const row of stmt.all(1)) {
* console.log(row);
* }
* ```
*
* Bind parameters can be either provided as an array of values, or as an object
* mapping the parameter name to the value.
*
* Example:
* ```ts
* const stmt = db.prepare("SELECT * FROM mytable WHERE id = ?");
* const row = stmt.get(1);
*
* // or
*
* const stmt = db.prepare("SELECT * FROM mytable WHERE id = :id");
* const row = stmt.get({ id: 1 });
* ```
*
* Statements are automatically freed once GC catches them, however
* you can also manually free using `finalize` method.
*
* @param sql SQL statement string
* @returns Statement object
*/
prepare(sql: string): Statement {
return new Statement(this, sql);
}
/**
* Open a Blob for incremental I/O.
*
* Make sure to close the blob after you are done with it,
* otherwise you will have memory leaks.
*/
openBlob(options: BlobOpenOptions): SQLBlob {
return new SQLBlob(this, options);
}
/**
* Simply executes the SQL statement (supports multiple statements separated by semicolon).
* Returns the number of changes made by last statement.
*
* Example:
* ```ts
* // Create table
* db.exec("create table users (id integer not null, username varchar(20) not null)");
*
* // Inserts
* db.exec("insert into users (id, username) values(?, ?)", id, username);
*
* // Insert with named parameters
* db.exec("insert into users (id, username) values(:id, :username)", { id, username });
*
* // Pragma statements
* db.exec("pragma journal_mode = WAL");
* db.exec("pragma synchronous = normal");
* db.exec("pragma temp_store = memory");
* ```
*
* Under the hood, it uses `sqlite3_exec` if no parameters are given to bind
* with the SQL statement, a prepared statement otherwise.
*/
exec(sql: string, ...params: RestBindParameters): number {
if (params.length === 0) {
const pErr = new BigUint64Array(1);
sqlite3_exec(
this.#handle,
toCString(sql),
null,
null,
new Uint8Array(pErr.buffer),
);
const errPtr = Deno.UnsafePointer.create(pErr[0]);
if (errPtr !== null) {
const err = readCstr(errPtr);
sqlite3_free(errPtr);
throw new Error(err);
}
return sqlite3_changes(this.#handle);
}
const stmt = this.prepare(sql);
stmt.run(...params);
return sqlite3_changes(this.#handle);
}
/** Alias for `exec`. */
run(sql: string, ...params: RestBindParameters): number {
return this.exec(sql, ...params);
}
/** Safely execute SQL with parameters using a tagged template */
sql<T extends Record<string, unknown> = Record<string, any>>(
strings: TemplateStringsArray,
...parameters: RestBindParameters
): T[] {
const sql = strings.join("?");
const stmt = this.prepare(sql);
return stmt.all(...parameters);
}
/**
* Wraps a callback function in a transaction.
*
* - When function is called, the transaction is started.
* - When function returns, the transaction is committed.
* - When function throws an error, the transaction is rolled back.
*
* Example:
* ```ts
* const stmt = db.prepare("insert into users (id, username) values(?, ?)");
*
* interface User {
* id: number;
* username: string;
* }
*
* const insertUsers = db.transaction((data: User[]) => {
* for (const user of data) {
* stmt.run(user);
* }
* });
*
* insertUsers([
* { id: 1, username: "alice" },
* { id: 2, username: "bob" },
* ]);
*
* // May also use `insertUsers.deferred`, `immediate`, or `exclusive`.
* // They corresspond to using `BEGIN DEFERRED`, `BEGIN IMMEDIATE`, and `BEGIN EXCLUSIVE`.
* // For eg.
*
* insertUsers.deferred([
* { id: 1, username: "alice" },
* { id: 2, username: "bob" },
* ]);
* ```
*/
transaction<T extends (this: Transaction<T>, ...args: any[]) => void>(
fn: T,
): Transaction<T> {
// Based on https://github.com/WiseLibs/better-sqlite3/blob/master/lib/methods/transaction.js
const controller = getController(this);
// Each version of the transaction function has these same properties
const properties = {
default: { value: wrapTransaction(fn, this, controller.default) },
deferred: { value: wrapTransaction(fn, this, controller.deferred) },
immediate: { value: wrapTransaction(fn, this, controller.immediate) },
exclusive: { value: wrapTransaction(fn, this, controller.exclusive) },
database: { value: this, enumerable: true },
};
Object.defineProperties(properties.default.value, properties);
Object.defineProperties(properties.deferred.value, properties);
Object.defineProperties(properties.immediate.value, properties);
Object.defineProperties(properties.exclusive.value, properties);
// Return the default version of the transaction function
return properties.default.value as Transaction<T>;
}
#callbacks = new Set<Deno.UnsafeCallback>();
/**
* Creates a new user-defined function.
*
* Example:
* ```ts
* db.function("add", (a: number, b: number) => a + b);
* db.prepare("select add(1, 2)").value<[number]>()!; // [3]
* ```
*/
function(
name: string,
fn: CallableFunction,
options?: FunctionOptions,
): void {
if (sqlite3_create_function === null) {
throw new Error(
"User-defined functions are not supported by the shared library that was used.",
);
}
const cb = new Deno.UnsafeCallback(
{
parameters: ["pointer", "i32", "pointer"],
result: "void",
} as const,
(ctx, nArgs, pArgs) => {
const argptr = new Deno.UnsafePointerView(pArgs!);
const args: any[] = [];
for (let i = 0; i < nArgs; i++) {
const arg = Deno.UnsafePointer.create(
argptr.getBigUint64(i * 8),
);
const type = sqlite3_value_type(arg);
switch (type) {
case SQLITE_INTEGER: {
const value = sqlite3_value_int64(arg);
if (value < -BIG_MAX || value > BIG_MAX) {
args.push(value);
} else {
args.push(Number(value));
}
break;
}
case SQLITE_FLOAT:
args.push(sqlite3_value_double(arg));
break;
case SQLITE_TEXT:
args.push(
new TextDecoder().decode(
new Uint8Array(
Deno.UnsafePointerView.getArrayBuffer(
sqlite3_value_text(arg)!,
sqlite3_value_bytes(arg),
),
),
),
);
break;
case SQLITE_BLOB:
args.push(
new Uint8Array(
Deno.UnsafePointerView.getArrayBuffer(
sqlite3_value_blob(arg)!,
sqlite3_value_bytes(arg),
),
),
);
break;
case SQLITE_NULL:
args.push(null);
break;
default:
throw new Error(`Unknown type: ${type}`);
}
}
let result: any;
try {
result = fn(...args);
} catch (err) {
const buf = new TextEncoder().encode(err.message);
sqlite3_result_error(ctx, buf, buf.byteLength);
return;
}
if (result === undefined || result === null) {
sqlite3_result_null(ctx);
} else if (typeof result === "boolean") {
sqlite3_result_int(ctx, result ? 1 : 0);
} else if (typeof result === "number") {
if (Number.isSafeInteger(result)) {
sqlite3_result_int64(ctx, BigInt(result));
} else sqlite3_result_double(ctx, result);
} else if (typeof result === "bigint") {
sqlite3_result_int64(ctx, result);
} else if (typeof result === "string") {
const buffer = new TextEncoder().encode(result);
sqlite3_result_text(ctx, buffer, buffer.byteLength, 0n);
} else if (result instanceof Uint8Array) {
sqlite3_result_blob(ctx, result, result.length, -1n);
} else {
const buffer = new TextEncoder().encode(
`Invalid return value: ${Deno.inspect(result)}`,
);
sqlite3_result_error(ctx, buffer, buffer.byteLength);
}
},
);
let flags = 1;
if (options?.deterministic) {
flags |= 0x000000800;
}
if (options?.directOnly) {
flags |= 0x000080000;
}
if (options?.subtype) {
flags |= 0x000100000;
}
if (options?.directOnly) {
flags |= 0x000200000;
}
const err = sqlite3_create_function(
this.#handle,
toCString(name),
options?.varargs ? -1 : fn.length,
flags,
null,
cb.pointer,
null,
null,
);
unwrap(err, this.#handle);
this.#callbacks.add(cb as Deno.UnsafeCallback);
}
/**
* Creates a new user-defined aggregate function.
*/
aggregate(name: string, options: AggregateFunctionOptions): void {
if (
sqlite3_aggregate_context === null || sqlite3_create_function === null
) {
throw new Error(
"User-defined functions are not supported by the shared library that was used.",
);
}
const contexts = new Map<number | bigint, any>();
const cb = new Deno.UnsafeCallback(
{
parameters: ["pointer", "i32", "pointer"],
result: "void",
} as const,
(ctx, nArgs, pArgs) => {
const aggrCtx = sqlite3_aggregate_context(ctx, 8);
const aggrPtr = Deno.UnsafePointer.value(aggrCtx);
let aggregate;
if (contexts.has(aggrPtr)) {
aggregate = contexts.get(aggrPtr);
} else {
aggregate = typeof options.start === "function"
? options.start()
: options.start;
contexts.set(aggrPtr, aggregate);
}
const argptr = new Deno.UnsafePointerView(pArgs!);
const args: any[] = [];
for (let i = 0; i < nArgs; i++) {
const arg = Deno.UnsafePointer.create(
argptr.getBigUint64(i * 8),
);
const type = sqlite3_value_type(arg);
switch (type) {
case SQLITE_INTEGER: {
const value = sqlite3_value_int64(arg);
if (value < -BIG_MAX || value > BIG_MAX) {
args.push(value);
} else {
args.push(Number(value));
}
break;
}
case SQLITE_FLOAT:
args.push(sqlite3_value_double(arg));
break;
case SQLITE_TEXT:
args.push(
new TextDecoder().decode(
new Uint8Array(
Deno.UnsafePointerView.getArrayBuffer(
sqlite3_value_text(arg)!,
sqlite3_value_bytes(arg),
),
),
),
);
break;
case SQLITE_BLOB:
args.push(
new Uint8Array(
Deno.UnsafePointerView.getArrayBuffer(
sqlite3_value_blob(arg)!,
sqlite3_value_bytes(arg),
),
),
);
break;
case SQLITE_NULL:
args.push(null);
break;
default:
throw new Error(`Unknown type: ${type}`);
}
}
let result: any;
try {
result = options.step(aggregate, ...args);
} catch (err) {
const buf = new TextEncoder().encode(err.message);
sqlite3_result_error(ctx, buf, buf.byteLength);
return;
}
contexts.set(aggrPtr, result);
},
);
const cbFinal = new Deno.UnsafeCallback(
{
parameters: ["pointer"],
result: "void",
} as const,
(ctx) => {
const aggrCtx = sqlite3_aggregate_context(ctx, 0);
const aggrPtr = Deno.UnsafePointer.value(aggrCtx);
const aggregate = contexts.get(aggrPtr);
contexts.delete(aggrPtr);
let result: any;
try {
result = options.final ? options.final(aggregate) : aggregate;
} catch (err) {
const buf = new TextEncoder().encode(err.message);
sqlite3_result_error(ctx, buf, buf.byteLength);
return;
}
if (result === undefined || result === null) {
sqlite3_result_null(ctx);
} else if (typeof result === "boolean") {
sqlite3_result_int(ctx, result ? 1 : 0);
} else if (typeof result === "number") {
if (Number.isSafeInteger(result)) {
sqlite3_result_int64(ctx, BigInt(result));
} else sqlite3_result_double(ctx, result);
} else if (typeof result === "bigint") {
sqlite3_result_int64(ctx, result);
} else if (typeof result === "string") {
const buffer = new TextEncoder().encode(result);
sqlite3_result_text(ctx, buffer, buffer.byteLength, 0n);
} else if (result instanceof Uint8Array) {
sqlite3_result_blob(ctx, result, result.length, -1n);
} else {
const buffer = new TextEncoder().encode(
`Invalid return value: ${Deno.inspect(result)}`,
);
sqlite3_result_error(ctx, buffer, buffer.byteLength);
}
},
);
let flags = 1;
if (options?.deterministic) {
flags |= 0x000000800;
}
if (options?.directOnly) {
flags |= 0x000080000;
}
if (options?.subtype) {
flags |= 0x000100000;
}
if (options?.directOnly) {
flags |= 0x000200000;
}
const err = sqlite3_create_function(
this.#handle,
toCString(name),
options?.varargs ? -1 : options.step.length - 1,
flags,
null,
null,
cb.pointer,
cbFinal.pointer,
);
unwrap(err, this.#handle);
this.#callbacks.add(cb as Deno.UnsafeCallback);
this.#callbacks.add(cbFinal as Deno.UnsafeCallback);
}
/**
* Loads an SQLite extension library from the named file.
*/
loadExtension(file: string, entryPoint?: string): void {
if (sqlite3_load_extension === null) {
throw new Error(
"Extension loading is not supported by the shared library that was used.",
);
}
if (!this.enableLoadExtension) {
throw new Error("Extension loading is not enabled");
}
const pzErrMsg = new BigUint64Array(1);
const result = sqlite3_load_extension(
this.#handle,
toCString(file),
entryPoint ? toCString(entryPoint) : null,
pzErrMsg,
);
const pzErrPtr = Deno.UnsafePointer.create(
pzErrMsg[0],
);
if (pzErrPtr !== null) {
const pzErr = readCstr(pzErrPtr);
sqlite3_free(pzErrPtr);
throw new Error(pzErr);
}
unwrap(result, this.#handle);
}
/**
* Closes the database connection.
*
* Calling this method more than once is no-op.
*/
close(): void {
if (!this.#open) return;
for (const [stmt, db] of STATEMENTS_TO_DB) {
if (db === this.#handle) {
sqlite3_finalize(stmt);
STATEMENTS_TO_DB.delete(stmt);
}
}
for (const cb of this.#callbacks) {
cb.close();
}
unwrap(sqlite3_close_v2(this.#handle));
this.#open = false;
}
/**
* @param dest The destination database connection.
* @param name Destination database name. "main" for main database, "temp" for temporary database, or the name specified after the AS keyword in an ATTACH statement for an attached database.
* @param pages The number of pages to copy. If it is negative, all remaining pages are copied (default).
*/
backup(dest: Database, name = "main", pages = -1): void {
const backup = sqlite3_backup_init(
dest.#handle,
toCString(name),
this.#handle,
toCString("main"),
);
if (backup) {
unwrap(sqlite3_backup_step(backup, pages));
unwrap(sqlite3_backup_finish(backup));
} else {
unwrap(sqlite3_errcode(dest.#handle), dest.#handle);
}
}
[Symbol.for("Deno.customInspect")](): string {
return `SQLite3.Database { path: ${this.path} }`;
}
}
const controllers = new WeakMap();
// Return the database's cached transaction controller, or create a new one
const getController = (db: Database) => {
let controller = controllers.get(db);
if (!controller) {
const shared = {
commit: db.prepare("COMMIT"),
rollback: db.prepare("ROLLBACK"),
savepoint: db.prepare("SAVEPOINT `\t_bs3.\t`"),
release: db.prepare("RELEASE `\t_bs3.\t`"),
rollbackTo: db.prepare("ROLLBACK TO `\t_bs3.\t`"),
};
controllers.set(
db,
controller = {
default: Object.assign(
{ begin: db.prepare("BEGIN") },
shared,
),
deferred: Object.assign(
{ begin: db.prepare("BEGIN DEFERRED") },
shared,
),
immediate: Object.assign(
{ begin: db.prepare("BEGIN IMMEDIATE") },
shared,
),
exclusive: Object.assign(
{ begin: db.prepare("BEGIN EXCLUSIVE") },
shared,
),
},
);
}
return controller;
};
// Return a new transaction function by wrapping the given function
const wrapTransaction = <T extends (...args: any[]) => void>(
fn: T,
db: Database,
{ begin, commit, rollback, savepoint, release, rollbackTo }: any,
) =>
function sqliteTransaction(...args: Parameters<T>): ReturnType<T> {
const { apply } = Function.prototype;
let before, after, undo;
if (db.inTransaction) {
before = savepoint;
after = release;
undo = rollbackTo;
} else {
before = begin;
after = commit;
undo = rollback;
}
before.run();
try {
// @ts-ignore An outer value of 'this' is shadowed by this container.
const result = apply.call(fn, this, args);
after.run();
return result;
} catch (ex) {
if (!db.autocommit) {
undo.run();
if (undo !== rollback) after.run();
}
throw ex;
}
};

641
vendor/jsr.io/@db/sqlite/0.12.0/src/ffi.ts generated vendored Normal file
View File

@ -0,0 +1,641 @@
import meta from "../deno.json" with { type: "json" };
import { dlopen } from "../deps.ts";
const symbols = {
sqlite3_open_v2: {
parameters: [
"buffer", // const char *filename
"buffer", // sqlite3 **ppDb
"i32", // int flags
"pointer", // const char *zVfs
],
result: "i32",
},
sqlite3_close_v2: {
parameters: [
"pointer", // sqlite3 *db
],
result: "i32",
},
sqlite3_changes: {
parameters: [
"pointer", // sqlite3 *db
],
result: "i32",
},
sqlite3_total_changes: {
parameters: [
"pointer", // sqlite3 *db
],
result: "i32",
},
sqlite3_last_insert_rowid: {
parameters: [
"pointer", // sqlite3 *db
],
result: "i32",
},
sqlite3_get_autocommit: {
parameters: [
"pointer", // sqlite3 *db
],
result: "i32",
},
sqlite3_prepare_v2: {
parameters: [
"pointer", // sqlite3 *db
"buffer", // const char *zSql
"i32", // int nByte
"buffer", // sqlite3_stmt **ppStmt
"pointer", // const char **pzTail
],
result: "i32",
},
sqlite3_reset: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "i32",
},
sqlite3_clear_bindings: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "i32",
},
sqlite3_step: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "i32",
},
sqlite3_column_count: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "i32",
},
sqlite3_column_type: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "i32",
},
sqlite3_column_text: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "pointer",
},
sqlite3_column_value: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "pointer",
},
sqlite3_finalize: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "i32",
},
sqlite3_exec: {
parameters: [
"pointer", // sqlite3 *db
"buffer", // const char *sql
"pointer", // sqlite3_callback callback
"pointer", // void *arg
"buffer", // char **errmsg
],
result: "i32",
},
sqlite3_free: {
parameters: [
"pointer", // void *p
],
result: "void",
},
sqlite3_column_int: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "i32",
},
sqlite3_column_double: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "f64",
},
sqlite3_column_blob: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "pointer",
},
sqlite3_column_bytes: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "i32",
},
sqlite3_column_name: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "pointer",
},
sqlite3_column_decltype: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "u64",
},
sqlite3_bind_parameter_index: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"buffer", // const char *zName
],
result: "i32",
},
sqlite3_bind_text: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
"buffer", // const char *zData
"i32", // int nData
"pointer", // void (*xDel)(void*)
],
result: "i32",
},
sqlite3_bind_blob: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
"buffer", // const void *zData
"i32", // int nData
"pointer", // void (*xDel)(void*)
],
result: "i32",
},
sqlite3_bind_double: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
"f64", // double rValue
],
result: "i32",
},
sqlite3_bind_int: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
"i32", // int iValue
],
result: "i32",
},
sqlite3_bind_int64: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
"i64", // i64 iValue
],
result: "i32",
},
sqlite3_bind_null: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "i32",
},
sqlite3_expanded_sql: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "pointer",
},
sqlite3_bind_parameter_count: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "i32",
},
sqlite3_complete: {
parameters: [
"buffer", // const char *sql
],
result: "i32",
},
sqlite3_sourceid: {
parameters: [],
result: "pointer",
},
sqlite3_libversion: {
parameters: [],
result: "pointer",
},
sqlite3_blob_open: {
parameters: [
"pointer", /* sqlite3 *db */
"buffer", /* const char *zDb */
"buffer", /* const char *zTable */
"buffer", /* const char *zColumn */
"i64", /* sqlite3_int64 iRow */
"i32", /* int flags */
"buffer", /* sqlite3_blob **ppBlob */
],
result: "i32",
},
sqlite3_blob_read: {
parameters: [
"pointer", /* sqlite3_blob *blob */
"buffer", /* void *Z */
"i32", /* int N */
"i32", /* int iOffset */
],
result: "i32",
},
sqlite3_blob_write: {
parameters: [
"pointer", /* sqlite3_blob *blob */
"buffer", /* const void *z */
"i32", /* int n */
"i32", /* int iOffset */
],
result: "i32",
},
sqlite3_blob_bytes: {
parameters: ["pointer" /* sqlite3_blob *blob */],
result: "i32",
},
sqlite3_blob_close: {
parameters: ["pointer" /* sqlite3_blob *blob */],
result: "i32",
},
sqlite3_sql: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "pointer",
},
sqlite3_stmt_readonly: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
],
result: "i32",
},
sqlite3_bind_parameter_name: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "pointer",
},
sqlite3_errcode: {
parameters: [
"pointer", // sqlite3 *db
],
result: "i32",
},
sqlite3_errmsg: {
parameters: [
"pointer", // sqlite3 *db
],
result: "pointer",
},
sqlite3_errstr: {
parameters: [
"i32", // int rc
],
result: "pointer",
},
sqlite3_column_int64: {
parameters: [
"pointer", // sqlite3_stmt *pStmt
"i32", // int iCol
],
result: "i64",
},
sqlite3_backup_init: {
parameters: [
"pointer", // sqlite3 *pDest
"buffer", // const char *zDestName
"pointer", // sqlite3 *pSource
"buffer", // const char *zSourceName
],
result: "pointer",
},
sqlite3_backup_step: {
parameters: [
"pointer", // sqlite3_backup *p
"i32", // int nPage
],
result: "i32",
},
sqlite3_backup_finish: {
parameters: [
"pointer", // sqlite3_backup *p
],
result: "i32",
},
sqlite3_backup_remaining: {
parameters: [
"pointer", // sqlite3_backup *p
],
result: "i32",
},
sqlite3_backup_pagecount: {
parameters: [
"pointer", // sqlite3_backup *p
],
result: "i32",
},
sqlite3_create_function: {
parameters: [
"pointer", // sqlite3 *db
"buffer", // const char *zFunctionName
"i32", // int nArg
"i32", // int eTextRep
"pointer", // void *pApp
"pointer", // void (*xFunc)(sqlite3_context*,int,sqlite3_value**)
"pointer", // void (*xStep)(sqlite3_context*,int,sqlite3_value**)
"pointer", // void (*xFinal)(sqlite3_context*)
],
result: "i32",
optional: true,
},
sqlite3_result_blob: {
parameters: [
"pointer", // sqlite3_context *p
"buffer", // const void *z
"i32", // int n
"isize", // void (*xDel)(void*)
],
result: "void",
},
sqlite3_result_double: {
parameters: [
"pointer", // sqlite3_context *p
"f64", // double rVal
],
result: "void",
},
sqlite3_result_error: {
parameters: [
"pointer", // sqlite3_context *p
"buffer", // const char *z
"i32", // int n
],
result: "void",
},
sqlite3_result_int: {
parameters: [
"pointer", // sqlite3_context *p
"i32", // int iVal
],
result: "void",
},
sqlite3_result_int64: {
parameters: [
"pointer", // sqlite3_context *p
"i64", // sqlite3_int64 iVal
],
result: "void",
},
sqlite3_result_null: {
parameters: [
"pointer", // sqlite3_context *p
],
result: "void",
},
sqlite3_result_text: {
parameters: [
"pointer", // sqlite3_context *p
"buffer", // const char *z
"i32", // int n
"isize", // void (*xDel)(void*)
],
result: "void",
},
sqlite3_value_type: {
parameters: [
"pointer", // sqlite3_value *pVal
],
result: "i32",
},
sqlite3_value_subtype: {
parameters: [
"pointer", // sqlite3_value *pVal
],
result: "i32",
},
sqlite3_value_blob: {
parameters: [
"pointer", // sqlite3_value *pVal
],
result: "pointer",
},
sqlite3_value_double: {
parameters: [
"pointer", // sqlite3_value *pVal
],
result: "f64",
},
sqlite3_value_int: {
parameters: [
"pointer", // sqlite3_value *pVal
],
result: "i32",
},
sqlite3_value_int64: {
parameters: [
"pointer", // sqlite3_value *pVal
],
result: "i64",
},
sqlite3_value_text: {
parameters: [
"pointer", // sqlite3_value *pVal
],
result: "pointer",
},
sqlite3_value_bytes: {
parameters: [
"pointer", // sqlite3_value *pVal
],
result: "i32",
},
sqlite3_aggregate_context: {
parameters: [
"pointer", // sqlite3_context *p
"i32", // int nBytes
],
result: "pointer",
optional: true,
},
sqlite3_enable_load_extension: {
parameters: [
"pointer", // sqlite3 *db
"i32", // int onoff
],
result: "i32",
optional: true,
},
sqlite3_load_extension: {
parameters: [
"pointer", // sqlite3 *db
"buffer", // const char *zFile
"buffer", // const char *zProc
"buffer", // const char **pzErrMsg
],
result: "i32",
optional: true,
},
sqlite3_initialize: {
parameters: [],
result: "i32",
},
} as const satisfies Deno.ForeignLibraryInterface;
let lib: Deno.DynamicLibrary<typeof symbols>["symbols"];
function tryGetEnv(key: string): string | undefined {
try {
return Deno.env.get(key);
} catch (e) {
if (e instanceof Deno.errors.PermissionDenied) {
return undefined;
}
throw e;
}
}
try {
const customPath = tryGetEnv("DENO_SQLITE_PATH");
const sqliteLocal = tryGetEnv("DENO_SQLITE_LOCAL");
if (sqliteLocal === "1") {
lib = Deno.dlopen(
new URL(
`../build/${Deno.build.os === "windows" ? "" : "lib"}sqlite3${
Deno.build.arch !== "x86_64" ? `_${Deno.build.arch}` : ""
}.${
Deno.build.os === "windows"
? "dll"
: Deno.build.os === "darwin"
? "dylib"
: "so"
}`,
import.meta.url,
),
symbols,
).symbols;
} else if (customPath) {
lib = Deno.dlopen(customPath, symbols).symbols;
} else {
lib = (
await dlopen(
{
name: "sqlite3",
url: `${meta.github}/releases/download/${meta.version}/`,
suffixes: {
aarch64: "_aarch64",
},
},
symbols,
)
).symbols;
}
} catch (e) {
if (e instanceof Deno.errors.PermissionDenied) {
throw e;
}
throw new Error("Failed to load SQLite3 Dynamic Library", { cause: e });
}
const init = lib.sqlite3_initialize();
if (init !== 0) {
throw new Error(`Failed to initialize SQLite3: ${init}`);
}
export default lib;

727
vendor/jsr.io/@db/sqlite/0.12.0/src/statement.ts generated vendored Normal file
View File

@ -0,0 +1,727 @@
import type { Database } from "./database.ts";
import { readCstr, toCString, unwrap } from "./util.ts";
import ffi from "./ffi.ts";
import {
SQLITE3_DONE,
SQLITE3_ROW,
SQLITE_BLOB,
SQLITE_FLOAT,
SQLITE_INTEGER,
SQLITE_TEXT,
} from "./constants.ts";
const {
sqlite3_prepare_v2,
sqlite3_reset,
sqlite3_clear_bindings,
sqlite3_step,
sqlite3_column_count,
sqlite3_column_type,
sqlite3_column_value,
sqlite3_value_subtype,
sqlite3_column_text,
sqlite3_finalize,
sqlite3_column_int64,
sqlite3_column_double,
sqlite3_column_blob,
sqlite3_column_bytes,
sqlite3_column_name,
sqlite3_expanded_sql,
sqlite3_bind_parameter_count,
sqlite3_bind_int,
sqlite3_bind_int64,
sqlite3_bind_text,
sqlite3_bind_blob,
sqlite3_bind_double,
sqlite3_bind_parameter_index,
sqlite3_sql,
sqlite3_stmt_readonly,
sqlite3_bind_parameter_name,
sqlite3_changes,
sqlite3_column_int,
} = ffi;
/** Types that can be possibly serialized as SQLite bind values */
export type BindValue =
| number
| string
| symbol
| bigint
| boolean
| null
| undefined
| Date
| Uint8Array
| BindValue[]
| { [key: string]: BindValue };
export type BindParameters = BindValue[] | Record<string, BindValue>;
export type RestBindParameters = BindValue[] | [BindParameters];
/** Maps sqlite_stmt* pointers to sqlite* db pointers. */
export const STATEMENTS_TO_DB = new Map<Deno.PointerValue, Deno.PointerValue>();
const emptyStringBuffer = new Uint8Array(1);
const statementFinalizer = new FinalizationRegistry(
(ptr: Deno.PointerValue) => {
if (STATEMENTS_TO_DB.has(ptr)) {
sqlite3_finalize(ptr);
STATEMENTS_TO_DB.delete(ptr);
}
},
);
// https://github.com/sqlite/sqlite/blob/195611d8e6fc0bba559a49e91e6ceb42e4bdd6ba/src/json.c#L125-L126
const JSON_SUBTYPE = 74;
const BIG_MAX = BigInt(Number.MAX_SAFE_INTEGER);
function getColumn(handle: Deno.PointerValue, i: number, int64: boolean): any {
const ty = sqlite3_column_type(handle, i);
if (ty === SQLITE_INTEGER && !int64) return sqlite3_column_int(handle, i);
switch (ty) {
case SQLITE_TEXT: {
const ptr = sqlite3_column_text(handle, i);
if (ptr === null) return null;
const text = readCstr(ptr, 0);
const value = sqlite3_column_value(handle, i);
const subtype = sqlite3_value_subtype(value);
if (subtype === JSON_SUBTYPE) {
try {
return JSON.parse(text);
} catch (_error) {
return text;
}
}
return text;
}
case SQLITE_INTEGER: {
const val = sqlite3_column_int64(handle, i);
if (val < -BIG_MAX || val > BIG_MAX) {
return val;
}
return Number(val);
}
case SQLITE_FLOAT: {
return sqlite3_column_double(handle, i);
}
case SQLITE_BLOB: {
const ptr = sqlite3_column_blob(handle, i);
if (ptr === null) {
return new Uint8Array();
}
const bytes = sqlite3_column_bytes(handle, i);
return new Uint8Array(
Deno.UnsafePointerView.getArrayBuffer(ptr, bytes).slice(0),
);
}
default: {
return null;
}
}
}
/**
* Represents a prepared statement.
*
* See `Database#prepare` for more information.
*/
export class Statement {
#handle: Deno.PointerValue;
#finalizerToken: { handle: Deno.PointerValue };
#bound = false;
#hasNoArgs = false;
#unsafeConcurrency;
/**
* Whether the query might call into JavaScript or not.
*
* Must enable if the query makes use of user defined functions,
* otherwise there can be V8 crashes.
*
* Off by default. Causes performance degradation.
*/
callback = false;
/** Unsafe Raw (pointer) to the sqlite object */
get unsafeHandle(): Deno.PointerValue {
return this.#handle;
}
/** SQL string including bindings */
get expandedSql(): string {
return readCstr(sqlite3_expanded_sql(this.#handle)!);
}
/** The SQL string that we passed when creating statement */
get sql(): string {
return readCstr(sqlite3_sql(this.#handle)!);
}
/** Whether this statement doesn't make any direct changes to the DB */
get readonly(): boolean {
return sqlite3_stmt_readonly(this.#handle) !== 0;
}
/** Simply run the query without retrieving any output there may be. */
run(...args: RestBindParameters): number {
return this.#runWithArgs(...args);
}
/**
* Run the query and return the resulting rows where rows are array of columns.
*/
values<T extends unknown[] = any[]>(...args: RestBindParameters): T[] {
return this.#valuesWithArgs(...args);
}
/**
* Run the query and return the resulting rows where rows are objects
* mapping column name to their corresponding values.
*/
all<T extends object = Record<string, any>>(
...args: RestBindParameters
): T[] {
return this.#allWithArgs(...args);
}
#bindParameterCount: number;
/** Number of parameters (to be) bound */
get bindParameterCount(): number {
return this.#bindParameterCount;
}
constructor(public db: Database, sql: string) {
const pHandle = new BigUint64Array(1);
unwrap(
sqlite3_prepare_v2(
db.unsafeHandle,
toCString(sql),
sql.length,
pHandle,
null,
),
db.unsafeHandle,
);
this.#handle = Deno.UnsafePointer.create(pHandle[0]);
STATEMENTS_TO_DB.set(this.#handle, db.unsafeHandle);
this.#unsafeConcurrency = db.unsafeConcurrency;
this.#finalizerToken = { handle: this.#handle };
statementFinalizer.register(this, this.#handle, this.#finalizerToken);
if (
(this.#bindParameterCount = sqlite3_bind_parameter_count(
this.#handle,
)) === 0
) {
this.#hasNoArgs = true;
this.all = this.#allNoArgs;
this.values = this.#valuesNoArgs;
this.run = this.#runNoArgs;
this.value = this.#valueNoArgs;
this.get = this.#getNoArgs;
}
}
/** Shorthand for `this.callback = true`. Enables calling user defined functions. */
enableCallback(): this {
this.callback = true;
return this;
}
/** Get bind parameter name by index */
bindParameterName(i: number): string {
return readCstr(sqlite3_bind_parameter_name(this.#handle, i)!);
}
/** Get bind parameter index by name */
bindParameterIndex(name: string): number {
if (name[0] !== ":" && name[0] !== "@" && name[0] !== "$") {
name = ":" + name;
}
return sqlite3_bind_parameter_index(this.#handle, toCString(name));
}
#begin(): void {
sqlite3_reset(this.#handle);
if (!this.#bound && !this.#hasNoArgs) {
sqlite3_clear_bindings(this.#handle);
this.#bindRefs.clear();
}
}
#bindRefs: Set<any> = new Set();
#bind(i: number, param: BindValue): void {
switch (typeof param) {
case "number": {
if (Number.isInteger(param)) {
if (
Number.isSafeInteger(param) && param >= -(2 ** 31) &&
param < 2 ** 31
) {
unwrap(sqlite3_bind_int(this.#handle, i + 1, param));
} else {
unwrap(sqlite3_bind_int64(this.#handle, i + 1, BigInt(param)));
}
} else {
unwrap(sqlite3_bind_double(this.#handle, i + 1, param));
}
break;
}
case "string": {
if (param === "") {
// Empty string is encoded as empty buffer in Deno. And as of
// right now (Deno 1.29.1), ffi layer converts it to NULL pointer,
// which causes sqlite3_bind_text to bind the NULL value instead
// of an empty string. As a workaround let's use a special
// non-empty buffer, but specify zero length.
unwrap(
sqlite3_bind_text(this.#handle, i + 1, emptyStringBuffer, 0, null),
);
} else {
const str = new TextEncoder().encode(param);
this.#bindRefs.add(str);
unwrap(
sqlite3_bind_text(this.#handle, i + 1, str, str.byteLength, null),
);
}
break;
}
case "object": {
if (param === null) {
// pass
} else if (param instanceof Uint8Array) {
this.#bindRefs.add(param);
unwrap(
sqlite3_bind_blob(
this.#handle,
i + 1,
param.byteLength === 0 ? emptyStringBuffer : param,
param.byteLength,
null,
),
);
} else if (param instanceof Date) {
const cstring = toCString(param.toISOString());
this.#bindRefs.add(cstring);
unwrap(
sqlite3_bind_text(
this.#handle,
i + 1,
cstring,
-1,
null,
),
);
} else {
const cstring = toCString(JSON.stringify(param));
this.#bindRefs.add(cstring);
unwrap(
sqlite3_bind_text(
this.#handle,
i + 1,
cstring,
-1,
null,
),
);
}
break;
}
case "bigint": {
unwrap(sqlite3_bind_int64(this.#handle, i + 1, param));
break;
}
case "boolean":
unwrap(sqlite3_bind_int(
this.#handle,
i + 1,
param ? 1 : 0,
));
break;
default: {
throw new Error(`Value of unsupported type: ${Deno.inspect(param)}`);
}
}
}
/**
* Bind parameters to the statement. This method can only be called once
* to set the parameters to be same throughout the statement. You cannot
* change the parameters after this method is called.
*
* This method is merely just for optimization to avoid binding parameters
* each time in prepared statement.
*/
bind(...params: RestBindParameters): this {
this.#bindAll(params);
this.#bound = true;
return this;
}
#bindAll(params: RestBindParameters | BindParameters): void {
if (this.#bound) throw new Error("Statement already bound to values");
if (
typeof params[0] === "object" && params[0] !== null &&
!(params[0] instanceof Uint8Array) && !(params[0] instanceof Date)
) {
params = params[0];
}
if (Array.isArray(params)) {
for (let i = 0; i < params.length; i++) {
this.#bind(i, (params as BindValue[])[i]);
}
} else {
for (const [name, param] of Object.entries(params)) {
const i = this.bindParameterIndex(name);
if (i === 0) {
throw new Error(`No such parameter "${name}"`);
}
this.#bind(i - 1, param as BindValue);
}
}
}
#runNoArgs(): number {
const handle = this.#handle;
this.#begin();
const status = sqlite3_step(handle);
if (status !== SQLITE3_ROW && status !== SQLITE3_DONE) {
unwrap(status, this.db.unsafeHandle);
}
sqlite3_reset(handle);
return sqlite3_changes(this.db.unsafeHandle);
}
#runWithArgs(...params: RestBindParameters): number {
const handle = this.#handle;
this.#begin();
this.#bindAll(params);
const status = sqlite3_step(handle);
if (!this.#hasNoArgs && !this.#bound && params.length) {
this.#bindRefs.clear();
}
if (status !== SQLITE3_ROW && status !== SQLITE3_DONE) {
unwrap(status, this.db.unsafeHandle);
}
sqlite3_reset(handle);
return sqlite3_changes(this.db.unsafeHandle);
}
#valuesNoArgs<T extends Array<unknown>>(): T[] {
const handle = this.#handle;
this.#begin();
const columnCount = sqlite3_column_count(handle);
const result: T[] = [];
const getRowArray = new Function(
"getColumn",
`
return function(h) {
return [${
Array.from({ length: columnCount }).map((_, i) =>
`getColumn(h, ${i}, ${this.db.int64})`
)
.join(", ")
}];
};
`,
)(getColumn);
let status = sqlite3_step(handle);
while (status === SQLITE3_ROW) {
result.push(getRowArray(handle));
status = sqlite3_step(handle);
}
if (status !== SQLITE3_DONE) {
unwrap(status, this.db.unsafeHandle);
}
sqlite3_reset(handle);
return result as T[];
}
#valuesWithArgs<T extends Array<unknown>>(
...params: RestBindParameters
): T[] {
const handle = this.#handle;
this.#begin();
this.#bindAll(params);
const columnCount = sqlite3_column_count(handle);
const result: T[] = [];
const getRowArray = new Function(
"getColumn",
`
return function(h) {
return [${
Array.from({ length: columnCount }).map((_, i) =>
`getColumn(h, ${i}, ${this.db.int64})`
)
.join(", ")
}];
};
`,
)(getColumn);
let status = sqlite3_step(handle);
while (status === SQLITE3_ROW) {
result.push(getRowArray(handle));
status = sqlite3_step(handle);
}
if (!this.#hasNoArgs && !this.#bound && params.length) {
this.#bindRefs.clear();
}
if (status !== SQLITE3_DONE) {
unwrap(status, this.db.unsafeHandle);
}
sqlite3_reset(handle);
return result as T[];
}
#rowObjectFn: ((h: Deno.PointerValue) => any) | undefined;
getRowObject(): (h: Deno.PointerValue) => any {
if (!this.#rowObjectFn || !this.#unsafeConcurrency) {
const columnNames = this.columnNames();
const getRowObject = new Function(
"getColumn",
`
return function(h) {
return {
${
columnNames.map((name, i) =>
`"${name}": getColumn(h, ${i}, ${this.db.int64})`
).join(",\n")
}
};
};
`,
)(getColumn);
this.#rowObjectFn = getRowObject;
}
return this.#rowObjectFn!;
}
#allNoArgs<T extends object>(): T[] {
const handle = this.#handle;
this.#begin();
const getRowObject = this.getRowObject();
const result: T[] = [];
let status = sqlite3_step(handle);
while (status === SQLITE3_ROW) {
result.push(getRowObject(handle));
status = sqlite3_step(handle);
}
if (status !== SQLITE3_DONE) {
unwrap(status, this.db.unsafeHandle);
}
sqlite3_reset(handle);
return result as T[];
}
#allWithArgs<T extends object>(
...params: RestBindParameters
): T[] {
const handle = this.#handle;
this.#begin();
this.#bindAll(params);
const getRowObject = this.getRowObject();
const result: T[] = [];
let status = sqlite3_step(handle);
while (status === SQLITE3_ROW) {
result.push(getRowObject(handle));
status = sqlite3_step(handle);
}
if (!this.#hasNoArgs && !this.#bound && params.length) {
this.#bindRefs.clear();
}
if (status !== SQLITE3_DONE) {
unwrap(status, this.db.unsafeHandle);
}
sqlite3_reset(handle);
return result as T[];
}
/** Fetch only first row as an array, if any. */
value<T extends Array<unknown>>(
...params: RestBindParameters
): T | undefined {
const handle = this.#handle;
const int64 = this.db.int64;
const arr = new Array(sqlite3_column_count(handle));
sqlite3_reset(handle);
if (!this.#hasNoArgs && !this.#bound) {
sqlite3_clear_bindings(handle);
this.#bindRefs.clear();
if (params.length) {
this.#bindAll(params);
}
}
const status = sqlite3_step(handle);
if (!this.#hasNoArgs && !this.#bound && params.length) {
this.#bindRefs.clear();
}
if (status === SQLITE3_ROW) {
for (let i = 0; i < arr.length; i++) {
arr[i] = getColumn(handle, i, int64);
}
sqlite3_reset(this.#handle);
return arr as T;
} else if (status === SQLITE3_DONE) {
return;
} else {
unwrap(status, this.db.unsafeHandle);
}
}
#valueNoArgs<T extends Array<unknown>>(): T | undefined {
const handle = this.#handle;
const int64 = this.db.int64;
const cc = sqlite3_column_count(handle);
const arr = new Array(cc);
sqlite3_reset(handle);
const status = sqlite3_step(handle);
if (status === SQLITE3_ROW) {
for (let i = 0; i < cc; i++) {
arr[i] = getColumn(handle, i, int64);
}
sqlite3_reset(this.#handle);
return arr as T;
} else if (status === SQLITE3_DONE) {
return;
} else {
unwrap(status, this.db.unsafeHandle);
}
}
#columnNames: string[] | undefined;
#rowObject: Record<string, unknown> = {};
columnNames(): string[] {
if (!this.#columnNames || !this.#unsafeConcurrency) {
const columnCount = sqlite3_column_count(this.#handle);
const columnNames = new Array(columnCount);
for (let i = 0; i < columnCount; i++) {
columnNames[i] = readCstr(sqlite3_column_name(this.#handle, i)!);
}
this.#columnNames = columnNames;
this.#rowObject = {};
for (const name of columnNames) {
this.#rowObject![name] = undefined;
}
}
return this.#columnNames!;
}
/** Fetch only first row as an object, if any. */
get<T extends object>(
...params: RestBindParameters
): T | undefined {
const handle = this.#handle;
const int64 = this.db.int64;
const columnNames = this.columnNames();
const row: Record<string, unknown> = {};
sqlite3_reset(handle);
if (!this.#hasNoArgs && !this.#bound) {
sqlite3_clear_bindings(handle);
this.#bindRefs.clear();
if (params.length) {
this.#bindAll(params);
}
}
const status = sqlite3_step(handle);
if (!this.#hasNoArgs && !this.#bound && params.length) {
this.#bindRefs.clear();
}
if (status === SQLITE3_ROW) {
for (let i = 0; i < columnNames.length; i++) {
row[columnNames[i]] = getColumn(handle, i, int64);
}
sqlite3_reset(this.#handle);
return row as T;
} else if (status === SQLITE3_DONE) {
return;
} else {
unwrap(status, this.db.unsafeHandle);
}
}
#getNoArgs<T extends object>(): T | undefined {
const handle = this.#handle;
const int64 = this.db.int64;
const columnNames = this.columnNames();
const row: Record<string, unknown> = this.#rowObject;
sqlite3_reset(handle);
const status = sqlite3_step(handle);
if (status === SQLITE3_ROW) {
for (let i = 0; i < columnNames?.length; i++) {
row[columnNames[i]] = getColumn(handle, i, int64);
}
sqlite3_reset(handle);
return row as T;
} else if (status === SQLITE3_DONE) {
return;
} else {
unwrap(status, this.db.unsafeHandle);
}
}
/** Free up the statement object. */
finalize(): void {
if (!STATEMENTS_TO_DB.has(this.#handle)) return;
this.#bindRefs.clear();
statementFinalizer.unregister(this.#finalizerToken);
STATEMENTS_TO_DB.delete(this.#handle);
unwrap(sqlite3_finalize(this.#handle));
}
/** Coerces the statement to a string, which in this case is expanded SQL. */
toString(): string {
return readCstr(sqlite3_expanded_sql(this.#handle)!);
}
/** Iterate over resultant rows from query. */
*iter(...params: RestBindParameters): IterableIterator<any> {
this.#begin();
this.#bindAll(params);
const getRowObject = this.getRowObject();
let status = sqlite3_step(this.#handle);
while (status === SQLITE3_ROW) {
yield getRowObject(this.#handle);
status = sqlite3_step(this.#handle);
}
if (status !== SQLITE3_DONE) {
unwrap(status, this.db.unsafeHandle);
}
sqlite3_reset(this.#handle);
}
[Symbol.iterator](): IterableIterator<any> {
return this.iter();
}
[Symbol.dispose](): void {
this.finalize();
}
[Symbol.for("Deno.customInspect")](): string {
return `Statement { ${this.expandedSql} }`;
}
}

48
vendor/jsr.io/@db/sqlite/0.12.0/src/util.ts generated vendored Normal file
View File

@ -0,0 +1,48 @@
import { SQLITE3_DONE, SQLITE3_MISUSE, SQLITE3_OK } from "./constants.ts";
import ffi from "./ffi.ts";
const {
sqlite3_errmsg,
sqlite3_errstr,
} = ffi;
export const encoder = new TextEncoder();
export function toCString(str: string): Uint8Array {
return encoder.encode(str + "\0");
}
export function isObject(value: unknown): boolean {
return typeof value === "object" && value !== null;
}
export class SqliteError extends Error {
name = "SqliteError";
constructor(
public code: number = 1,
message: string = "Unknown Error",
) {
super(`${code}: ${message}`);
}
}
export function unwrap(code: number, db?: Deno.PointerValue): void {
if (code === SQLITE3_OK || code === SQLITE3_DONE) return;
if (code === SQLITE3_MISUSE) {
throw new SqliteError(code, "SQLite3 API misuse");
} else if (db !== undefined) {
const errmsg = sqlite3_errmsg(db);
if (errmsg === null) throw new SqliteError(code);
throw new Error(Deno.UnsafePointerView.getCString(errmsg));
} else {
throw new SqliteError(
code,
Deno.UnsafePointerView.getCString(sqlite3_errstr(code)!),
);
}
}
export const buf = Deno.UnsafePointerView.getArrayBuffer;
export const readCstr = Deno.UnsafePointerView.getCString;

532
vendor/jsr.io/@db/sqlite/0.12.0_meta.json generated vendored Normal file
View File

@ -0,0 +1,532 @@
{
"manifest": {
"/src/statement.ts": {
"size": 19811,
"checksum": "sha256-e8ccde898aef47c7a2514953aca5359a44a285bc3dc0de5819d66f891f477be1"
},
"/src/ffi.ts": {
"size": 12263,
"checksum": "sha256-795b598eeae4d12f182e7bcdab524b74b0f01d6deae7f4d8ce63f25c06a46154"
},
"/bench/northwind/deno.js": {
"size": 993,
"checksum": "sha256-8009f531181554cc0b7e00f2e0a32f00aa5debad08347bc0c340959034155b0c"
},
"/src/util.ts": {
"size": 1252,
"checksum": "sha256-c6604183d2ec5fb17fa0a018572ed5f2317b319dbd7bf48d88a5d06ff25b2cc3"
},
"/bench/northwind/deno_old.js": {
"size": 845,
"checksum": "sha256-2c739731eff0b14572b71480fea6daa1a7233733998a9b2fbe82457116d25c08"
},
"/.gitmodules": {
"size": 76,
"checksum": "sha256-5f8d6b06ad75304ae8f1cac12dee879165b7a49f4e5a716b06f5ff1ddcee0418"
},
"/src/constants.ts": {
"size": 2478,
"checksum": "sha256-85fd27aa6e199093f25f5f437052e16fd0e0870b96ca9b24a98e04ddc8b7d006"
},
"/.github/FUNDING.yml": {
"size": 23,
"checksum": "sha256-bb4e0cedf30e330abf572013ee6220f4928273d8cc8dd90242b5b6888d4c8e6a"
},
"/bench/northwind/deno_wasm.js": {
"size": 1028,
"checksum": "sha256-f91a88e2eb8b6e10b4ca9a86eb8f36453792d4ef075ccbf7a756e701fab20ff0"
},
"/test/test.ts": {
"size": 14434,
"checksum": "sha256-6ef55635b1143b7dbf121eb9d3458a91537334f058f6d57c603bc413e72caa17"
},
"/bench/northwind/node.mjs": {
"size": 1091,
"checksum": "sha256-cd3759ab841b5f0c1c0406dfd5c169f63a535f748b0a2a11fe25822e6bc12db8"
},
"/bench/makefile": {
"size": 79,
"checksum": "sha256-58559ec25fcf986ce9250db85784722e622332ab854611428d7c717abd896b7b"
},
"/bench/northwind/bun.js": {
"size": 968,
"checksum": "sha256-9433ae459a7acbd9f78c7d37ba0ff8fd247186f3aed0c6a64a6073be24ca9a52"
},
"/LICENSE": {
"size": 11543,
"checksum": "sha256-23583fa34c041cdeb82b28bb117d8697cd5c212effff70efb831f173c0c68b66"
},
"/mod.ts": {
"size": 432,
"checksum": "sha256-3169f246c0eddd6ed82862758f4109f167b7ba5538236240fbb26a129f1bc16c"
},
"/bench/bench.js": {
"size": 3409,
"checksum": "sha256-f54929af49c5c1d5825ba3689064161717315e35df25117daf0167da672a3f72"
},
"/bench/bench_python.py": {
"size": 666,
"checksum": "sha256-c85d12f7a1d88e54418b58fcb20b1c9628dd721997d74bfdcce4e38194bd5293"
},
"/bench/bench_node.js": {
"size": 740,
"checksum": "sha256-2524a7f6c2c9350c5a8ab1a05df9977075350f97a9cad2640b9bd51da43e897e"
},
"/bench/bench_deno_ffi.js": {
"size": 1826,
"checksum": "sha256-53c02fc0ae9bd7b84b5d06220d0a6fff9d0daac1abf117d1dc30a2ded46f23e2"
},
"/bench/download.sh": {
"size": 375,
"checksum": "sha256-07c52368f7a13765303430ec1797bcb098dcc66482191219bb154ea07441928a"
},
"/bench/bench_bun.js": {
"size": 828,
"checksum": "sha256-03a7c603a5c55b21b3f6febeaf5061f245875be47ba76dbd474024ac4a926105"
},
"/bench/bench.c": {
"size": 1359,
"checksum": "sha256-275247e3bcd62935376369940abc8617787113b31d76911a1431b65650ef2547"
},
"/test/deps.ts": {
"size": 66,
"checksum": "sha256-5da2bf33a7a61aa2680f1aba05b07f1a687a682d7b8f4f8a8a1481ee6163260a"
},
"/bench/bench_deno.js": {
"size": 729,
"checksum": "sha256-2ca30ec55aac5b48bfc4bca27305c7171a111743783dc138f45a56827dae46f7"
},
"/src/blob.ts": {
"size": 4161,
"checksum": "sha256-330886fae9714e4a612786f44d8117d65f91e778cf3f40de59b34879fc7ca9ab"
},
"/package.json": {
"size": 1093,
"checksum": "sha256-062ecb8336c9921ba20523b172fe4fcd0547bcb885b482fe3089ee6d84b7a177"
},
"/src/database.ts": {
"size": 25465,
"checksum": "sha256-4d380d7f0e5a2cf74635a9fcd2b4e27373533f2816cde5357067e51fd22ad8d0"
},
"/deno.json": {
"size": 1415,
"checksum": "sha256-b03d6de05f953886662ea987212539af8456a91352684c84af2188520449d42a"
},
"/.github/workflows/ci.yml": {
"size": 2949,
"checksum": "sha256-9d5758dae05df104fcc5252808b6b23347aa588ac8a833624a064205dffe57e3"
},
"/.github/workflows/publish.yml": {
"size": 238,
"checksum": "sha256-8b6aee1b0b7b5c7a50623ce94e54b936219ea23e319c5d448551ce1d8691ff12"
},
"/bench/bench_bun_ffi.js": {
"size": 3100,
"checksum": "sha256-62ce7a93d735becb71188cb642d5ed07f9860631ccd8aed2d5b5e58770b4cc25"
},
"/README.md": {
"size": 2679,
"checksum": "sha256-769ae29ef3e5235160b53a7d1e2a3746bbdc1c8a0c6d715e721a262040ef7941"
},
"/bench/bench_deno_wasm.js": {
"size": 818,
"checksum": "sha256-90e7cb8198452efb5bd1a14e720a77ea3bac76be38249406ad8208d9cbe12b03"
},
"/doc.md": {
"size": 12287,
"checksum": "sha256-5052824d485ec61794dca20fe904900f48f47dab096b3282b9eba86ac0015a26"
},
"/bench/results.png": {
"size": 42888,
"checksum": "sha256-b6fd903c2f4977e2ad89ccc22988495eb21bb51b6876665ad22b02d24266d16a"
},
"/deps.ts": {
"size": 99,
"checksum": "sha256-d2f23a4489d27ed7ba1f601b86a85ff488a87603e4be7a15f3ea15154fc288ec"
}
},
"moduleGraph2": {
"/src/util.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./constants.ts",
"specifierRange": [
[
0,
57
],
[
0,
73
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./ffi.ts",
"specifierRange": [
[
1,
16
],
[
1,
26
]
]
}
]
},
"/deps.ts": {
"dependencies": [
{
"type": "static",
"kind": "export",
"specifier": "jsr:@std/path@0.217",
"specifierRange": [
[
0,
28
],
[
0,
49
]
]
},
{
"type": "static",
"kind": "export",
"specifier": "jsr:@denosaurs/plug@1",
"specifierRange": [
[
1,
23
],
[
1,
46
]
]
}
]
},
"/src/database.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./ffi.ts",
"specifierRange": [
[
0,
16
],
[
0,
26
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "../deps.ts",
"specifierRange": [
[
1,
28
],
[
1,
40
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./constants.ts",
"specifierRange": [
[
12,
7
],
[
12,
23
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./util.ts",
"specifierRange": [
[
13,
44
],
[
13,
55
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./statement.ts",
"specifierRange": [
[
18,
7
],
[
18,
23
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./blob.ts",
"specifierRange": [
[
19,
46
],
[
19,
57
]
]
}
]
},
"/src/blob.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./database.ts",
"specifierRange": [
[
0,
30
],
[
0,
45
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./ffi.ts",
"specifierRange": [
[
1,
16
],
[
1,
26
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./util.ts",
"specifierRange": [
[
2,
34
],
[
2,
45
]
]
}
]
},
"/src/statement.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./database.ts",
"specifierRange": [
[
0,
30
],
[
0,
45
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./util.ts",
"specifierRange": [
[
1,
44
],
[
1,
55
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./ffi.ts",
"specifierRange": [
[
2,
16
],
[
2,
26
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./constants.ts",
"specifierRange": [
[
10,
7
],
[
10,
23
]
]
}
]
},
"/mod.ts": {
"dependencies": [
{
"type": "static",
"kind": "export",
"specifier": "./src/database.ts",
"specifierRange": [
[
9,
7
],
[
9,
26
]
]
},
{
"type": "static",
"kind": "export",
"specifier": "./src/blob.ts",
"specifierRange": [
[
10,
46
],
[
10,
61
]
]
},
{
"type": "static",
"kind": "export",
"specifier": "./src/statement.ts",
"specifierRange": [
[
16,
7
],
[
16,
27
]
]
},
{
"type": "static",
"kind": "export",
"specifier": "./src/util.ts",
"specifierRange": [
[
17,
28
],
[
17,
43
]
]
}
]
},
"/src/constants.ts": {},
"/src/ffi.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "../deno.json",
"specifierRange": [
[
0,
17
],
[
0,
31
]
],
"importAttributes": {
"known": {
"type": "json"
}
}
},
{
"type": "static",
"kind": "import",
"specifier": "../deps.ts",
"specifierRange": [
[
1,
23
],
[
1,
35
]
]
}
]
}
},
"exports": {
".": "./mod.ts"
}
}

10
vendor/jsr.io/@db/sqlite/meta.json generated vendored Normal file
View File

@ -0,0 +1,10 @@
{
"scope": "db",
"name": "sqlite",
"latest": "0.12.0",
"versions": {
"0.11.0": {},
"0.12.0": {},
"0.11.1": {}
}
}

322
vendor/jsr.io/@denosaurs/plug/1.0.6/download.ts generated vendored Normal file
View File

@ -0,0 +1,322 @@
/**
* This module contains the common types used in plug.
*
* @module
*/
import {
dirname,
extname,
fromFileUrl,
join,
normalize,
resolve,
} from "jsr:@std/path@^0.221.0";
import { ensureDir } from "jsr:@std/fs@^0.221.0";
import { green } from "jsr:@std/fmt@^0.221.0/colors";
import type {
ArchRecord,
CacheLocation,
FetchOptions,
NestedCrossRecord,
OsRecord,
} from "./types.ts";
import {
cacheDir,
denoCacheDir,
isFile,
stringToURL,
urlToFilename,
} from "./util.ts";
/**
* A list of all possible system architectures.
*
* This should match the {@link Deno.build.arch} type.
*/
export const ALL_ARCHS: (typeof Deno.build.arch)[] = [
"x86_64",
"aarch64",
];
/**
* A list of all possible system operating systems.
*
* This should match the {@link Deno.build.os} type.
*/
export const ALL_OSS: (typeof Deno.build.os)[] = [
"darwin",
"linux",
"android",
"windows",
"freebsd",
"netbsd",
"aix",
"solaris",
"illumos",
];
/**
* The default file extensions for dynamic libraries in the different operating
* systems.
*/
export const defaultExtensions: OsRecord<string> = {
darwin: "dylib",
linux: "so",
windows: "dll",
freebsd: "so",
netbsd: "so",
aix: "so",
solaris: "so",
illumos: "so",
android: "so",
};
/**
* The default file prefixes for dynamic libraries in the different operating
* systems.
*/
export const defaultPrefixes: OsRecord<string> = {
darwin: "lib",
linux: "lib",
netbsd: "lib",
freebsd: "lib",
aix: "lib",
solaris: "lib",
illumos: "lib",
windows: "",
android: "lib",
};
function getCrossOption<T>(record?: NestedCrossRecord<T>): T | undefined {
if (record === undefined) {
return;
}
if (ALL_OSS.some((os) => os in record)) {
const subrecord = (record as OsRecord<T>)[Deno.build.os];
if (
subrecord &&
typeof subrecord === "object" &&
ALL_ARCHS.some((arch) => arch in subrecord)
) {
return (subrecord as ArchRecord<T>)[Deno.build.arch];
} else {
return subrecord as T;
}
}
if (ALL_ARCHS.some((arch) => arch in record)) {
const subrecord = (record as ArchRecord<T>)[Deno.build.arch];
if (
subrecord &&
typeof subrecord === "object" &&
ALL_OSS.some((os) => os in subrecord)
) {
return (subrecord as OsRecord<T>)[Deno.build.os];
} else {
return subrecord as T;
}
}
}
/**
* Creates a cross-platform url for the specified options
*
* @param options See {@link FetchOptions}
* @returns A fully specified url to the specified file
*/
export function createDownloadURL(options: FetchOptions): URL {
if (typeof options === "string" || options instanceof URL) {
options = { url: options };
}
// Initialize default options
options.extensions ??= defaultExtensions;
options.prefixes ??= defaultPrefixes;
// Clean extensions to not contain a leading dot
for (const key in options.extensions) {
const os = key as typeof Deno.build.os;
if (options.extensions[os] !== undefined) {
options.extensions[os] = options.extensions[os].replace(/\.?(.+)/, "$1");
}
}
// Get the os-specific url
let url: URL;
if (options.url instanceof URL) {
url = options.url;
} else if (typeof options.url === "string") {
url = stringToURL(options.url);
} else {
const tmpUrl = getCrossOption(options.url);
if (tmpUrl === undefined) {
throw new TypeError(
`An URL for the "${Deno.build.os}-${Deno.build.arch}" target was not provided.`,
);
}
if (typeof tmpUrl === "string") {
url = stringToURL(tmpUrl);
} else {
url = tmpUrl;
}
}
// Assemble automatic cross-platform named urls here
if (
"name" in options &&
!Object.values(options.extensions).includes(extname(url.pathname))
) {
if (!url.pathname.endsWith("/")) {
url.pathname = `${url.pathname}/`;
}
const prefix = getCrossOption(options.prefixes) ?? "";
const suffix = getCrossOption(options.suffixes) ?? "";
const extension = options.extensions[Deno.build.os];
if (options.name === undefined) {
throw new TypeError(
`Expected the "name" property for an automatically assembled URL.`,
);
}
const filename = `${prefix}${options.name}${suffix}.${extension}`;
url = new URL(filename, url);
}
return url;
}
/**
* Return the path to the cache location along with ensuring its existance
*
* @param location See the {@link CacheLocation} type
* @returns The cache location path
*/
export async function ensureCacheLocation(
location: CacheLocation = "deno",
): Promise<string> {
if (location === "deno") {
const dir = denoCacheDir();
if (dir === undefined) {
throw new Error(
"Could not get the deno cache directory, try using another CacheLocation in the plug options.",
);
}
location = join(dir, "plug");
} else if (location === "cache") {
const dir = cacheDir();
if (dir === undefined) {
throw new Error(
"Could not get the cache directory, try using another CacheLocation in the plug options.",
);
}
location = join(dir, "plug");
} else if (location === "cwd") {
location = join(Deno.cwd(), "plug");
} else if (location === "tmp") {
location = await Deno.makeTempDir({ prefix: "plug" });
} else if (typeof location === "string" && location.startsWith("file://")) {
location = fromFileUrl(location);
} else if (location instanceof URL) {
if (location?.protocol !== "file:") {
throw new TypeError(
"Cannot use any other protocol than file:// for an URL cache location.",
);
}
location = fromFileUrl(location);
}
location = resolve(normalize(location));
await ensureDir(location);
return location;
}
/**
* Downloads a file using the specified {@link FetchOptions}
*
* @param options See {@link FetchOptions}
* @returns The path to the downloaded file in its cached location
*/
export async function download(options: FetchOptions): Promise<string> {
const location =
(typeof options === "object" && "location" in options
? options.location
: undefined) ?? "deno";
const setting =
(typeof options === "object" && "cache" in options
? options.cache
: undefined) ?? "use";
const url = createDownloadURL(options);
const directory = await ensureCacheLocation(location);
const cacheBasePath = join(directory, await urlToFilename(url));
const cacheFilePath = `${cacheBasePath}${extname(url.pathname)}`;
const cacheMetaPath = `${cacheBasePath}.metadata.json`;
const cached = setting === "use"
? await isFile(cacheFilePath)
: setting === "only" || setting !== "reloadAll";
await ensureDir(dirname(cacheBasePath));
if (!cached) {
const meta = { url };
switch (url.protocol) {
case "http:":
case "https:": {
console.log(`${green("Downloading")} ${url}`);
const response = await fetch(url.toString());
if (!response.ok) {
if (response.status === 404) {
throw new Error(`Could not find ${url}`);
} else {
throw new Deno.errors.Http(
`${response.status} ${response.statusText}`,
);
}
}
await Deno.writeFile(
cacheFilePath,
new Uint8Array(await response.arrayBuffer()),
);
break;
}
case "file:": {
console.log(`${green("Copying")} ${url}`);
await Deno.copyFile(fromFileUrl(url), cacheFilePath);
if (Deno.build.os !== "windows") {
await Deno.chmod(cacheFilePath, 0o644);
}
break;
}
default: {
throw new TypeError(
`Cannot fetch to cache using the "${url.protocol}" protocol`,
);
}
}
await Deno.writeTextFile(cacheMetaPath, JSON.stringify(meta));
}
if (!(await isFile(cacheFilePath))) {
throw new Error(`Could not find "${url}" in cache.`);
}
return cacheFilePath;
}

159
vendor/jsr.io/@denosaurs/plug/1.0.6/mod.ts generated vendored Normal file
View File

@ -0,0 +1,159 @@
/**
* Plug is a drop in extension for using remote dynamic libraries in deno. It
* automatically handles caching and loading with minimal overhead. It can
* automatically create the URL for your cross-operating-system, cross-architecture
* libraries if you so wish using a simple configuration which deviates from
* the standard URL/string path input.
*
* @example
* ```ts
* import { dlopen } from "@denosaurs/plug";
*
* // Drop-in replacement for `Deno.dlopen` which fetches the following depending
* // on operating system:
* // * darwin: "https://example.com/some/path/libexample.dylib"
* // * windows: "https://example.com/some/path/example.dll"
* // * linux: "https://example.com/some/path/libexample.so"
* const library = await dlopen("https://example.com/some/path/", {
* noop: { parameters: [], result: "void" },
* });
*
* library.symbols.noop();
* ```
*
* @example
* ```ts
* import { dlopen, FetchOptions } from "@denosaurs/plug";
*
* // If you want plug to guess your binary names
* const options: FetchOptions = {
* name: "example",
* url: "https://example.com/some/path/",
* // Becomes:
* // darwin: "https://example.com/some/path/libexample.dylib"
* // windows: "https://example.com/some/path/example.dll"
* // linux: "https://example.com/some/path/libexample.so"
* };
*
* const library = await dlopen(options, {
* noop: { parameters: [], result: "void" },
* });
*
* library.symbols.noop();
* ```
*
* @example
* ```ts
* import { dlopen, FetchOptions } from "@denosaurs/plug";
*
* // Also you can specify the path for certain architecture
* const options: FetchOptions = {
* name: "example",
* url: {
* darwin: {
* aarch64: `https://example.com/some/path/libexample.aarch64.dylib`,
* x86_64: `https://example.com/some/path/libexample.x86_64.dylib`,
* },
* windows: `https://example.com/some/path/example.dll`,
* linux: `https://example.com/some/path/libexample.so`,
* },
* };
*
* await dlopen(options, {});
* ```
*
* @example
* ```ts
* import { dlopen, FetchOptions } from "@denosaurs/plug";
*
* // Or even configure plug to automatically guess the binary names for you,
* // even when there are special rules for naming on specific architectures
* const options: FetchOptions = {
* name: "test",
* url: "https://example.com/some/path/",
* suffixes: {
* darwin: {
* aarch64: ".aarch64",
* x86_64: ".x86_64",
* },
* },
* // Becomes:
* // darwin-aarch64: "https://example.com/some/path/libexample.aarch64.dylib"
* // darwin-x86_64: "https://example.com/some/path/libexample.x86_64.dylib"
* };
*
* await dlopen(options, {});
* ```
*
* @module
*/
import { download } from "./download.ts";
import type { FetchOptions } from "./types.ts";
export type {
ArchRecord,
CacheLocation,
CacheOptions,
CacheSetting,
CrossOptions,
FetchOptions,
NamedOptions,
NestedCrossRecord,
OsRecord,
URLOptions,
} from "./types.ts";
export { download } from "./download.ts";
/* Magic types from deno which help implement better FFI type checking */
type Cast<A, B> = A extends B ? A : B;
type Const<T> = Cast<
T,
| (T extends string | number | bigint | boolean ? T : never)
| { [K in keyof T]: Const<T[K]> }
| []
>;
/**
* Opens a dynamic library and registers symbols, compatible with
* {@link Deno.dlopen} yet with extended functionality allowing you to use
* remote (or local) binaries, automatically building the binary name and
* controlling the caching policy.
*
* @example
* ```ts
* import { dlopen, FetchOptions } from "@denosaurs/plug";
*
* // Configure plug to automatically guess the binary names for you, even when
* // there for example are special rules for naming on specific architectures
* const options: FetchOptions = {
* name: "test",
* url: "https://example.com/some/path/",
* suffixes: {
* darwin: {
* aarch64: ".aarch64",
* x86_64: ".x86_64",
* },
* },
* // Becomes:
* // darwin-aarch64: "https://example.com/some/path/libexample.aarch64.dylib"
* // darwin-x86_64: "https://example.com/some/path/libexample.x86_64.dylib"
* };
*
* await dlopen(options, {});
* ```
*
* @param options See {@link FetchOptions}
* @param symbols A record extending {@link Deno.ForeignLibraryInterface}
* @returns An opened {@link Deno.DynamicLibrary}
*/
export async function dlopen<S extends Deno.ForeignLibraryInterface>(
options: FetchOptions,
symbols: Const<S>,
): Promise<Deno.DynamicLibrary<S>> {
if (Deno.dlopen === undefined) {
throw new Error("`--unstable-ffi` is required");
}
// deno-lint-ignore no-explicit-any
return Deno.dlopen<S>(await download(options), symbols as any);
}

152
vendor/jsr.io/@denosaurs/plug/1.0.6/types.ts generated vendored Normal file
View File

@ -0,0 +1,152 @@
/**
* This module contains the common types used in plug.
*
* @module
*/
/**
* A record keyed by possible operating system identifiers
*/
export type OsRecord<T> = { [os in typeof Deno.build.os]: T };
/**
* A record keyed by possible system architecture identifiers
*/
export type ArchRecord<T> = { [os in typeof Deno.build.arch]: T };
/**
* An optionally nested record of either an {@link OsRecord} or
* {@link ArchRecord} containing either the generic T or the opposite record
* type from the parent. That way we can query for the record entry of a target
* keyed by both an architecture and operating system in the ordered entered in
* this record.
*/
export type NestedCrossRecord<T> = Partial<
| OsRecord<T | Partial<ArchRecord<T>>>
| ArchRecord<T | Partial<OsRecord<T>>>
>;
/**
* Where the plug cache is stored:
*
* | Option | Description |
* | ------- | ----------------------------------------------------------------------------------------- |
* | `deno` | The location of the default deno cache, this is the default option. |
* | `cwd` | A `plug` folder in the current working directory. |
* | `cache` | A `plug` folder in default cache directory for the current os. |
* | `tmp` | A temporary `plug` prefixed folder in the default temporary directory for the current os. |
* | string | A file path pointing to the folder where the plug cache should be stored. |
* | URL | A file protocol URL pointing to the folder where the plug cache should be stored. |
*/
export type CacheLocation = "deno" | "cwd" | "cache" | "tmp" | string | URL;
/** A setting that determines how the cache is handled for remote dependencies.
*
* | Option | Description |
* | ----------- | ------------------------------------------------------------------------------------------------------------ |
* | `use` | The cache will be used, meaning existing remote files will not be reloaded, this is the default option. |
* | `only` | Only the cache will be re-used, and any remote files not in the cache will error. |
* | `reloadAll` | Any cached modules will be ignored and their values will be fetched. |
*/
export type CacheSetting = "use" | "only" | "reloadAll";
/**
* Options for controlling how plug caches files
*/
export interface CacheOptions {
/**
* The location where plug should cache the fetched file
*/
location?: CacheLocation;
/**
* The cache policy plug should use, see {@link CacheSetting}
*/
cache?: CacheSetting;
}
/**
* Options for using a single url as the source for either creating a
* {@link NamedOptions named url} or using it simply as is if the platforms
* extension is specified or no name is specified.
*/
export interface URLOptions {
/**
* The url to either a dynamic library or its directory if {@link NamedOptions named}
*/
url: string | URL;
}
/**
* Options for fetching cross-platform urls.
*/
export interface CrossOptions {
/**
* See {@link NestedCrossRecord}, basically a record optionally keyed by
* either or operating-system and architecture letting us get the correct
* url for fetching the right file for the platform.
*/
url: NestedCrossRecord<string | URL>;
}
/**
* Options used for automatically assembling an os and arch specific file name
*/
export interface NamedOptions {
/**
* The base name of the library.
*
* ```
* libplug.x86_64.dll
* ^^^^
* ```
*/
name: string;
/**
* A {@link OsRecord} containing the extensions for the respective
* operating-systems. By default this is `.so` for linux, freebsd, netbsd, aix, solaris and illumos, `.dylib` for darwin
* and `.dll` for windows.
*
* ```
* libplug.x86_64.dll
* ^^^
* ```
*/
extensions?: OsRecord<string>;
/**
* A {@link NestedCrossRecord} containing the prefixes for the respective
* operating-systems and architectures. By default this is `lib` for all
* architectures on linux, darwin, freebsd, netbsd, aix, solaris and illumos and empty for windows.
*
* ```
* libplug.x86_64.dll
* ^^^
* ```
*/
prefixes?: NestedCrossRecord<string>;
/**
* A {@link NestedCrossRecord} containing the suffixes for the respective
* operating-systems and architectures. By default this is empty for all
* architectures and operating-systems. An idea would be to use this to
* automatically select the suffix for the correct architecture.
*
* ```
* libplug.x86_64.dll
* ^^^^^^^
* ```
*/
suffixes?: NestedCrossRecord<string>;
}
/**
* Options for fetching files (usually being dynamic libraries, but could
* possibly also be other dependencies) using plug. This can be either a
* string or an URL. All urls in plug can be either local or remote. If it is
* not an string or URL it can be some combination of the following options:
*
* * {@link URLOptions} or {@link CrossOptions} for controlling the source url
* * {@link NamedOptions} for automatically creating cross-platform binary names
* * {@link CacheOptions} for controlling the cache behaviour
*/
export type FetchOptions =
| string
| URL
| ((((URLOptions | CrossOptions) & Partial<NamedOptions>)) & CacheOptions);

161
vendor/jsr.io/@denosaurs/plug/1.0.6/util.ts generated vendored Normal file
View File

@ -0,0 +1,161 @@
/**
* This file contains useful utility functions used by plug.
*
* @module
*/
import { isAbsolute, join, normalize, resolve, toFileUrl } from "jsr:@std/path@^0.221.0";
import { encodeHex } from "jsr:@std/encoding@^0.221.0/hex";
const encoder = new TextEncoder();
function baseUrlToFilename(url: URL): string {
const out = [];
const protocol = url.protocol.replace(":", "");
out.push(protocol);
switch (protocol) {
case "http":
case "https": {
const host = url.hostname;
const hostPort = url.port;
out.push(hostPort ? `${host}_PORT${hostPort}` : host);
break;
}
case "file":
case "data":
case "blob":
break;
default:
throw new TypeError(
`Don't know how to create cache name for protocol: ${protocol}`,
);
}
return join(...out);
}
/**
* Transforms a string into a URL.
*
* @private
*/
export function stringToURL(url: string): URL {
// deno-fmt-ignore
return url.startsWith("file://")
|| url.startsWith("http://")
|| url.startsWith("https://")
? new URL(url)
: toFileUrl(resolve(url));
}
/**
* SHA-256 hashes a string. Used internally to hash URLs for cache filenames.
*
* @private
*/
export async function hash(value: string): Promise<string> {
return encodeHex(
new Uint8Array(
await crypto.subtle.digest("SHA-256", encoder.encode(value)),
),
);
}
/**
* Transforms a URL into a filename for the cache.
*
* @private
*/
export async function urlToFilename(url: URL): Promise<string> {
const cacheFilename = baseUrlToFilename(url);
const hashedFilename = await hash(url.pathname + url.search);
return join(cacheFilename, hashedFilename);
}
/**
* Checks if a file exists.
*
* @private
*/
export async function isFile(filePath: string): Promise<boolean> {
try {
const stats = await Deno.lstat(filePath);
return stats.isFile;
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
return false;
}
throw err;
}
}
// The rest of is based on code from denoland/deno_cache by the Deno authors
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
/**
* @returns The home directory of the user.
*/
export function homeDir(): string | undefined {
switch (Deno.build.os) {
case "windows":
return Deno.env.get("USERPROFILE");
case "linux":
case "darwin":
case "freebsd":
case "netbsd":
case "aix":
case "solaris":
case "illumos":
return Deno.env.get("HOME");
default:
throw Error("unreachable");
}
}
/**
* @returns The cache directory of the user.
*/
export function cacheDir(): string | undefined {
if (Deno.build.os === "darwin") {
const home = homeDir();
if (home) {
return join(home, "Library/Caches");
}
} else if (Deno.build.os === "windows") {
return Deno.env.get("LOCALAPPDATA");
} else {
const cacheHome = Deno.env.get("XDG_CACHE_HOME");
if (cacheHome) {
return cacheHome;
} else {
const home = homeDir();
if (home) {
return join(home, ".cache");
}
}
}
}
/**
* @returns The cache directory for Deno.
*/
export function denoCacheDir(): string | undefined {
const dd = Deno.env.get("DENO_DIR");
let root;
if (dd) {
root = normalize(isAbsolute(dd) ? dd : join(Deno.cwd(), dd));
} else {
const cd = cacheDir();
if (cd) {
root = join(cd, "deno");
} else {
const hd = homeDir();
if (hd) {
root = join(hd, ".deno");
}
}
}
return root;
}

246
vendor/jsr.io/@denosaurs/plug/1.0.6_meta.json generated vendored Normal file
View File

@ -0,0 +1,246 @@
{
"manifest": {
"/download_test.ts": {
"size": 9300,
"checksum": "sha256-9dbe825f543096cc20b1f6cc70d1f239fcad6444f25f8e95ccadfb95a421beb0"
},
"/README.md": {
"size": 4633,
"checksum": "sha256-2d58d95ce1e2614dd3e74cb9e55566f5f03dfcd621ccaa181557426083fff73a"
},
"/mod.ts": {
"size": 4760,
"checksum": "sha256-15c683abe87ed5df6cdccd5ff45ff46438ef68b825729f8463a6c8b605a9d19b"
},
"/.github/workflows/publish.yml": {
"size": 267,
"checksum": "sha256-a3f12d6833d9a43bc8453e63c95610a950538191899852bddadd2cf31c071ad7"
},
"/LICENSE": {
"size": 1080,
"checksum": "sha256-0546a91da726c46e6d54a352c707ca46e4a47924d13518fa3140c4f71bb4f3e3"
},
"/.github/FUNDING.yml": {
"size": 45,
"checksum": "sha256-f7d5bd53b9a6add2d05771a6edeb3192701e92fb60c0725cbd861ebadd5e4181"
},
"/deno.json": {
"size": 417,
"checksum": "sha256-79aeb6b285543f6c96f3900359467d80aaeb8d89a1a820aa2e847a8e701dd34e"
},
"/util.ts": {
"size": 3562,
"checksum": "sha256-91fb019b500ee81c5cb50754dd598abdab15bc22c80610790d9fe05eed8f948e"
},
"/.editorconfig": {
"size": 99,
"checksum": "sha256-561e2d9d8035e8b9861e31b3934557da651481d95dfd8992e12dbbee03c285c2"
},
"/download.ts": {
"size": 8041,
"checksum": "sha256-a0dc7084ed428cd95d66840176c9c4318a3a70270fc92a4ada5530adc7f52860"
},
"/types.ts": {
"size": 5585,
"checksum": "sha256-944a7abaded38515f0b06a4499d462c99d1c018ced7b33c2ee32607224e4e039"
},
"/test_import_map.json": {
"size": 472,
"checksum": "sha256-3db76688daeb0658d136afa66271844953a56b82bac9e786c6337b6ac7e899a2"
},
"/.github/workflows/checks.yml": {
"size": 566,
"checksum": "sha256-c9dbd47a446d6756355521e9d98cde8168854e915996b345b4a050605c15a56a"
},
"/util_test.ts": {
"size": 11546,
"checksum": "sha256-fcc4302200b7608be814957e147ca4b4fe162551404050939fc5f37cf7fd1753"
}
},
"moduleGraph1": {
"/mod.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./download.ts",
"specifierRange": [
[
90,
25
],
[
90,
40
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./types.ts",
"specifierRange": [
[
91,
34
],
[
91,
46
]
]
},
{
"type": "static",
"kind": "exportType",
"specifier": "./types.ts",
"specifierRange": [
[
104,
7
],
[
104,
19
]
]
},
{
"type": "static",
"kind": "export",
"specifier": "./download.ts",
"specifierRange": [
[
105,
25
],
[
105,
40
]
]
}
]
},
"/download.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/path@^0.221.0",
"specifierRange": [
[
13,
7
],
[
13,
31
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/fs@^0.221.0",
"specifierRange": [
[
14,
26
],
[
14,
48
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/fmt@^0.221.0/colors",
"specifierRange": [
[
15,
22
],
[
15,
52
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./types.ts",
"specifierRange": [
[
22,
7
],
[
22,
19
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./util.ts",
"specifierRange": [
[
29,
7
],
[
29,
18
]
]
}
]
},
"/util.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/path@^0.221.0",
"specifierRange": [
[
6,
64
],
[
6,
88
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/encoding@^0.221.0/hex",
"specifierRange": [
[
7,
26
],
[
7,
58
]
]
}
]
},
"/types.ts": {}
},
"exports": {
".": "./mod.ts",
"./types": "./types.ts",
"./download": "./download.ts",
"./util": "./util.ts"
}
}

11
vendor/jsr.io/@denosaurs/plug/meta.json generated vendored Normal file
View File

@ -0,0 +1,11 @@
{
"scope": "denosaurs",
"name": "plug",
"latest": "1.0.6",
"versions": {
"1.0.4": {},
"1.0.5": {},
"1.0.6": {},
"1.0.3": {}
}
}

131
vendor/jsr.io/@eta-dev/eta/3.5.0/src/compile-string.ts generated vendored Normal file
View File

@ -0,0 +1,131 @@
/* TYPES */
import type { Options } from "./config.ts";
import type { AstObject } from "./parse.ts";
import type { Eta } from "./core.ts";
/* END TYPES */
/**
* Compiles a template string to a function string. Most often users just use `compile()`, which calls `compileToString` and creates a new function using the result
*/
export function compileToString(
this: Eta,
str: string,
options?: Partial<Options>,
): string {
const config = this.config;
const isAsync = options && options.async;
const compileBody = this.compileBody;
const buffer: Array<AstObject> = this.parse.call(this, str);
// note: when the include function passes through options, the only parameter that matters is the filepath parameter
let res = `${config.functionHeader}
let include = (template, data) => this.render(template, data, options);
let includeAsync = (template, data) => this.renderAsync(template, data, options);
let __eta = {res: "", e: this.config.escapeFunction, f: this.config.filterFunction${
config.debug
? ', line: 1, templateStr: "' +
str.replace(/\\|"/g, "\\$&").replace(/\r\n|\n|\r/g, "\\n") +
'"'
: ""
}};
function layout(path, data) {
__eta.layout = path;
__eta.layoutData = data;
}${config.debug ? "try {" : ""}${
config.useWith ? "with(" + config.varName + "||{}){" : ""
}
${compileBody.call(this, buffer)}
if (__eta.layout) {
__eta.res = ${
isAsync ? "await includeAsync" : "include"
} (__eta.layout, {...${config.varName}, body: __eta.res, ...__eta.layoutData});
}
${config.useWith ? "}" : ""}${
config.debug
? "} catch (e) { this.RuntimeErr(e, __eta.templateStr, __eta.line, options.filepath) }"
: ""
}
return __eta.res;
`;
if (config.plugins) {
for (let i = 0; i < config.plugins.length; i++) {
const plugin = config.plugins[i];
if (plugin.processFnString) {
res = plugin.processFnString(res, config);
}
}
}
return res;
}
/**
* Loops through the AST generated by `parse` and transform each item into JS calls
*
* **Example**
*
* ```js
* let templateAST = ['Hi ', { val: 'it.name', t: 'i' }]
* compileBody.call(Eta, templateAST)
* // => "__eta.res+='Hi '\n__eta.res+=__eta.e(it.name)\n"
* ```
*/
export function compileBody(this: Eta, buff: Array<AstObject>): string {
const config = this.config;
let i = 0;
const buffLength = buff.length;
let returnStr = "";
for (i; i < buffLength; i++) {
const currentBlock = buff[i];
if (typeof currentBlock === "string") {
const str = currentBlock;
// we know string exists
returnStr += "__eta.res+='" + str + "'\n";
} else {
const type = currentBlock.t; // "r", "e", or "i"
let content = currentBlock.val || "";
if (config.debug) returnStr += "__eta.line=" + currentBlock.lineNo + "\n";
if (type === "r") {
// raw
if (config.autoFilter) {
content = "__eta.f(" + content + ")";
}
returnStr += "__eta.res+=" + content + "\n";
} else if (type === "i") {
// interpolate
if (config.autoFilter) {
content = "__eta.f(" + content + ")";
}
if (config.autoEscape) {
content = "__eta.e(" + content + ")";
}
returnStr += "__eta.res+=" + content + "\n";
} else if (type === "e") {
// execute
returnStr += content + "\n";
}
}
}
return returnStr;
}

59
vendor/jsr.io/@eta-dev/eta/3.5.0/src/compile.ts generated vendored Normal file
View File

@ -0,0 +1,59 @@
import { EtaParseError } from "./err.ts";
/* TYPES */
import type { Eta } from "./core.ts";
import type { EtaConfig, Options } from "./config.ts";
export type TemplateFunction = (
this: Eta,
data?: object,
options?: Partial<Options>,
) => string;
/* END TYPES */
/* istanbul ignore next */
const AsyncFunction = async function () {}.constructor; // eslint-disable-line @typescript-eslint/no-empty-function
/**
* Takes a template string and returns a template function that can be called with (data, config)
*
* @param str - The template string
* @param config - A custom configuration object (optional)
*/
export function compile(
this: Eta,
str: string,
options?: Partial<Options>,
): TemplateFunction {
const config: EtaConfig = this.config;
/* ASYNC HANDLING */
// code gratefully taken from https://github.com/mde/ejs and adapted
const ctor = options && options.async
? (AsyncFunction as FunctionConstructor)
: Function;
/* END ASYNC HANDLING */
try {
return new ctor(
config.varName,
"options",
this.compileToString.call(this, str, options),
) as TemplateFunction; // eslint-disable-line no-new-func
} catch (e) {
if (e instanceof SyntaxError) {
throw new EtaParseError(
"Bad template syntax\n\n" +
e.message +
"\n" +
Array(e.message.length + 1).join("=") +
"\n" +
this.compileToString.call(this, str, options) +
"\n", // This will put an extra newline before the callstack for extra readability
);
} else {
throw e;
}
}
}

110
vendor/jsr.io/@eta-dev/eta/3.5.0/src/config.ts generated vendored Normal file
View File

@ -0,0 +1,110 @@
import { XMLEscape } from "./utils.ts";
/* TYPES */
type trimConfig = "nl" | "slurp" | false;
export interface Options {
/** Compile to async function */
async?: boolean;
/** Absolute path to template file */
filepath?: string;
}
export interface EtaConfig {
/** Whether or not to automatically XML-escape interpolations. Default true */
autoEscape: boolean;
/** Apply a filter function defined on the class to every interpolation or raw interpolation */
autoFilter: boolean;
/** Configure automatic whitespace trimming. Default `[false, 'nl']` */
autoTrim: trimConfig | [trimConfig, trimConfig];
/** Whether or not to cache templates if `name` or `filename` is passed */
cache: boolean;
/** Holds cache of resolved filepaths. Set to `false` to disable. */
cacheFilepaths: boolean;
/** Whether to pretty-format error messages (introduces runtime penalties) */
debug: boolean;
/** Function to XML-sanitize interpolations */
escapeFunction: (str: unknown) => string;
/** Function applied to all interpolations when autoFilter is true */
filterFunction: (val: unknown) => string;
/** Raw JS code inserted in the template function. Useful for declaring global variables for user templates */
functionHeader: string;
/** Parsing options */
parse: {
/** Which prefix to use for evaluation. Default `""`, does not support `"-"` or `"_"` */
exec: string;
/** Which prefix to use for interpolation. Default `"="`, does not support `"-"` or `"_"` */
interpolate: string;
/** Which prefix to use for raw interpolation. Default `"~"`, does not support `"-"` or `"_"` */
raw: string;
};
/** Array of plugins */
plugins: Array<
{
processFnString?: Function;
processAST?: Function;
processTemplate?: Function;
}
>;
/** Remove all safe-to-remove whitespace */
rmWhitespace: boolean;
/** Delimiters: by default `['<%', '%>']` */
tags: [string, string];
/** Make data available on the global object instead of varName */
useWith: boolean;
/** Name of the data object. Default `it` */
varName: string;
/** Directory that contains templates */
views?: string;
/** Control template file extension defaults. Default `.eta` */
defaultExtension?: string;
}
/* END TYPES */
/** Eta's base (global) configuration */
const defaultConfig: EtaConfig = {
autoEscape: true,
autoFilter: false,
autoTrim: [false, "nl"],
cache: false,
cacheFilepaths: true,
debug: false,
escapeFunction: XMLEscape,
// default filter function (not used unless enables) just stringifies the input
filterFunction: (val) => String(val),
functionHeader: "",
parse: {
exec: "",
interpolate: "=",
raw: "~",
},
plugins: [],
rmWhitespace: false,
tags: ["<%", "%>"],
useWith: false,
varName: "it",
defaultExtension: ".eta",
};
export { defaultConfig };

89
vendor/jsr.io/@eta-dev/eta/3.5.0/src/core.ts generated vendored Normal file
View File

@ -0,0 +1,89 @@
import { Cacher } from "./storage.ts";
import { compile } from "./compile.ts";
import { compileBody, compileToString } from "./compile-string.ts";
import { defaultConfig } from "./config.ts";
import { parse } from "./parse.ts";
import {
render,
renderAsync,
renderString,
renderStringAsync,
} from "./render.ts";
import { EtaError, RuntimeErr } from "./err.ts";
import { TemplateFunction } from "./compile.ts";
/* TYPES */
import type { EtaConfig, Options } from "./config.ts";
/* END TYPES */
export class Eta {
constructor(customConfig?: Partial<EtaConfig>) {
if (customConfig) {
this.config = { ...defaultConfig, ...customConfig };
} else {
this.config = { ...defaultConfig };
}
}
config: EtaConfig;
RuntimeErr = RuntimeErr;
compile = compile;
compileToString = compileToString;
compileBody = compileBody;
parse = parse;
render = render;
renderAsync = renderAsync;
renderString = renderString;
renderStringAsync = renderStringAsync;
filepathCache: Record<string, string> = {};
templatesSync: Cacher<TemplateFunction> = new Cacher<TemplateFunction>({});
templatesAsync: Cacher<TemplateFunction> = new Cacher<TemplateFunction>({});
// resolvePath takes a relative path from the "views" directory
resolvePath:
| null
| ((this: Eta, template: string, options?: Partial<Options>) => string) =
null;
readFile: null | ((this: Eta, path: string) => string) = null;
// METHODS
configure(customConfig: Partial<EtaConfig>) {
this.config = { ...this.config, ...customConfig };
}
withConfig(customConfig: Partial<EtaConfig>): this & { config: EtaConfig } {
return { ...this, config: { ...this.config, ...customConfig } };
}
loadTemplate(
name: string,
template: string | TemplateFunction, // template string or template function
options?: { async: boolean },
): void {
if (typeof template === "string") {
const templates = options && options.async
? this.templatesAsync
: this.templatesSync;
templates.define(name, this.compile(template, options));
} else {
let templates = this.templatesSync;
if (
template.constructor.name === "AsyncFunction" ||
(options && options.async)
) {
templates = this.templatesAsync;
}
templates.define(name, template);
}
}
}
// for instance checking against thrown errors
export { EtaError };

91
vendor/jsr.io/@eta-dev/eta/3.5.0/src/err.ts generated vendored Normal file
View File

@ -0,0 +1,91 @@
export class EtaError extends Error {
constructor(message: string) {
super(message);
this.name = "Eta Error";
}
}
export class EtaParseError extends EtaError {
constructor(message: string) {
super(message);
this.name = "EtaParser Error";
}
}
export class EtaRuntimeError extends EtaError {
constructor(message: string) {
super(message);
this.name = "EtaRuntime Error";
}
}
export class EtaFileResolutionError extends EtaError {
constructor(message: string) {
super(message);
this.name = "EtaFileResolution Error";
}
}
export class EtaNameResolutionError extends EtaError {
constructor(message: string) {
super(message);
this.name = "EtaNameResolution Error";
}
}
/**
* Throws an EtaError with a nicely formatted error and message showing where in the template the error occurred.
*/
export function ParseErr(message: string, str: string, indx: number): never {
const whitespace = str.slice(0, indx).split(/\n/);
const lineNo = whitespace.length;
const colNo = whitespace[lineNo - 1].length + 1;
message += " at line " +
lineNo +
" col " +
colNo +
":\n\n" +
" " +
str.split(/\n/)[lineNo - 1] +
"\n" +
" " +
Array(colNo).join(" ") +
"^";
throw new EtaParseError(message);
}
export function RuntimeErr(
originalError: Error,
str: string,
lineNo: number,
path: string,
): never {
// code gratefully taken from https://github.com/mde/ejs and adapted
const lines = str.split("\n");
const start = Math.max(lineNo - 3, 0);
const end = Math.min(lines.length, lineNo + 3);
const filename = path;
// Error context
const context = lines
.slice(start, end)
.map(function (line, i) {
const curr = i + start + 1;
return (curr == lineNo ? " >> " : " ") + curr + "| " + line;
})
.join("\n");
const header = filename
? filename + ":" + lineNo + "\n"
: "line " + lineNo + "\n";
const err = new EtaRuntimeError(
header + context + "\n\n" + originalError.message,
);
err.name = originalError.name; // the original name (e.g. ReferenceError) may be useful
throw err;
}

95
vendor/jsr.io/@eta-dev/eta/3.5.0/src/file-handling.ts generated vendored Normal file
View File

@ -0,0 +1,95 @@
import { EtaFileResolutionError } from "./err.ts";
import * as path from "node:path";
import * as fs from "node:fs";
/* TYPES */
import type { Eta as EtaCore } from "./core.ts";
import type { Options } from "./config.ts";
/* END TYPES */
export function readFile(this: EtaCore, path: string): string {
let res = "";
try {
res = fs.readFileSync(path, "utf8");
// eslint-disable-line @typescript-eslint/no-explicit-any
} catch (err: any) {
if (err?.code === "ENOENT") {
throw new EtaFileResolutionError(`Could not find template: ${path}`);
} else {
throw err;
}
}
return res;
}
export function resolvePath(
this: EtaCore,
templatePath: string,
options?: Partial<Options>,
): string {
let resolvedFilePath = "";
const views = this.config.views;
if (!views) {
throw new EtaFileResolutionError("Views directory is not defined");
}
const baseFilePath = options && options.filepath;
const defaultExtension = this.config.defaultExtension === undefined
? ".eta"
: this.config.defaultExtension;
// how we index cached template paths
const cacheIndex = JSON.stringify({
filename: baseFilePath, // filename of the template which called includeFile()
path: templatePath,
views: this.config.views,
});
templatePath += path.extname(templatePath) ? "" : defaultExtension;
// if the file was included from another template
if (baseFilePath) {
// check the cache
if (this.config.cacheFilepaths && this.filepathCache[cacheIndex]) {
return this.filepathCache[cacheIndex];
}
const absolutePathTest = absolutePathRegExp.exec(templatePath);
if (absolutePathTest && absolutePathTest.length) {
const formattedPath = templatePath.replace(/^\/*|^\\*/, "");
resolvedFilePath = path.join(views, formattedPath);
} else {
resolvedFilePath = path.join(path.dirname(baseFilePath), templatePath);
}
} else {
resolvedFilePath = path.join(views, templatePath);
}
if (dirIsChild(views, resolvedFilePath)) {
// add resolved path to the cache
if (baseFilePath && this.config.cacheFilepaths) {
this.filepathCache[cacheIndex] = resolvedFilePath;
}
return resolvedFilePath;
} else {
throw new EtaFileResolutionError(
`Template '${templatePath}' is not in the views directory`,
);
}
}
function dirIsChild(parent: string, dir: string) {
const relative = path.relative(parent, dir);
return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
}
const absolutePathRegExp = /^\\|^\//;

16
vendor/jsr.io/@eta-dev/eta/3.5.0/src/index.ts generated vendored Normal file
View File

@ -0,0 +1,16 @@
import { Eta as EtaCore } from "./core.ts";
import { readFile, resolvePath } from "./file-handling.ts";
export {
EtaError,
EtaFileResolutionError,
EtaNameResolutionError,
EtaParseError,
EtaRuntimeError,
} from "./err.ts";
export { type EtaConfig, type Options } from "./config.ts";
export class Eta extends EtaCore {
readFile = readFile;
resolvePath = resolvePath;
}

215
vendor/jsr.io/@eta-dev/eta/3.5.0/src/parse.ts generated vendored Normal file
View File

@ -0,0 +1,215 @@
import { ParseErr } from "./err.ts";
import { trimWS } from "./utils.ts";
/* TYPES */
import type { Eta } from "./core.ts";
export type TagType = "r" | "e" | "i" | "";
export interface TemplateObject {
t: TagType;
val: string;
lineNo?: number;
}
export type AstObject = string | TemplateObject;
/* END TYPES */
const templateLitReg =
/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g;
const singleQuoteReg = /'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g;
const doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;
/** Escape special regular expression characters inside a string */
function escapeRegExp(string: string) {
// From MDN
return string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
function getLineNo(str: string, index: number) {
return str.slice(0, index).split("\n").length;
}
export function parse(this: Eta, str: string): Array<AstObject> {
const config = this.config;
let buffer: Array<AstObject> = [];
let trimLeftOfNextStr: string | false = false;
let lastIndex = 0;
const parseOptions = config.parse;
if (config.plugins) {
for (let i = 0; i < config.plugins.length; i++) {
const plugin = config.plugins[i];
if (plugin.processTemplate) {
str = plugin.processTemplate(str, config);
}
}
}
/* Adding for EJS compatibility */
if (config.rmWhitespace) {
// Code taken directly from EJS
// Have to use two separate replaces here as `^` and `$` operators don't
// work well with `\r` and empty lines don't work well with the `m` flag.
// Essentially, this replaces the whitespace at the beginning and end of
// each line and removes multiple newlines.
str = str.replace(/[\r\n]+/g, "\n").replace(/^\s+|\s+$/gm, "");
}
/* End rmWhitespace option */
templateLitReg.lastIndex = 0;
singleQuoteReg.lastIndex = 0;
doubleQuoteReg.lastIndex = 0;
function pushString(strng: string, shouldTrimRightOfString?: string | false) {
if (strng) {
// if string is truthy it must be of type 'string'
strng = trimWS(
strng,
config,
trimLeftOfNextStr, // this will only be false on the first str, the next ones will be null or undefined
shouldTrimRightOfString,
);
if (strng) {
// replace \ with \\, ' with \'
// we're going to convert all CRLF to LF so it doesn't take more than one replace
strng = strng.replace(/\\|'/g, "\\$&").replace(/\r\n|\n|\r/g, "\\n");
buffer.push(strng);
}
}
}
const prefixes = [
parseOptions.exec,
parseOptions.interpolate,
parseOptions.raw,
].reduce(function (
accumulator,
prefix,
) {
if (accumulator && prefix) {
return accumulator + "|" + escapeRegExp(prefix);
} else if (prefix) {
// accumulator is falsy
return escapeRegExp(prefix);
} else {
// prefix and accumulator are both falsy
return accumulator;
}
}, "");
const parseOpenReg = new RegExp(
escapeRegExp(config.tags[0]) + "(-|_)?\\s*(" + prefixes + ")?\\s*",
"g",
);
const parseCloseReg = new RegExp(
"'|\"|`|\\/\\*|(\\s*(-|_)?" + escapeRegExp(config.tags[1]) + ")",
"g",
);
let m;
while ((m = parseOpenReg.exec(str))) {
const precedingString = str.slice(lastIndex, m.index);
lastIndex = m[0].length + m.index;
const wsLeft = m[1];
const prefix = m[2] || ""; // by default either ~, =, or empty
pushString(precedingString, wsLeft);
parseCloseReg.lastIndex = lastIndex;
let closeTag;
let currentObj: AstObject | false = false;
while ((closeTag = parseCloseReg.exec(str))) {
if (closeTag[1]) {
const content = str.slice(lastIndex, closeTag.index);
parseOpenReg.lastIndex = lastIndex = parseCloseReg.lastIndex;
trimLeftOfNextStr = closeTag[2];
const currentType: TagType = prefix === parseOptions.exec
? "e"
: prefix === parseOptions.raw
? "r"
: prefix === parseOptions.interpolate
? "i"
: "";
currentObj = { t: currentType, val: content };
break;
} else {
const char = closeTag[0];
if (char === "/*") {
const commentCloseInd = str.indexOf("*/", parseCloseReg.lastIndex);
if (commentCloseInd === -1) {
ParseErr("unclosed comment", str, closeTag.index);
}
parseCloseReg.lastIndex = commentCloseInd;
} else if (char === "'") {
singleQuoteReg.lastIndex = closeTag.index;
const singleQuoteMatch = singleQuoteReg.exec(str);
if (singleQuoteMatch) {
parseCloseReg.lastIndex = singleQuoteReg.lastIndex;
} else {
ParseErr("unclosed string", str, closeTag.index);
}
} else if (char === '"') {
doubleQuoteReg.lastIndex = closeTag.index;
const doubleQuoteMatch = doubleQuoteReg.exec(str);
if (doubleQuoteMatch) {
parseCloseReg.lastIndex = doubleQuoteReg.lastIndex;
} else {
ParseErr("unclosed string", str, closeTag.index);
}
} else if (char === "`") {
templateLitReg.lastIndex = closeTag.index;
const templateLitMatch = templateLitReg.exec(str);
if (templateLitMatch) {
parseCloseReg.lastIndex = templateLitReg.lastIndex;
} else {
ParseErr("unclosed string", str, closeTag.index);
}
}
}
}
if (currentObj) {
if (config.debug) {
currentObj.lineNo = getLineNo(str, m.index);
}
buffer.push(currentObj);
} else {
ParseErr("unclosed tag", str, m.index);
}
}
pushString(str.slice(lastIndex, str.length), false);
if (config.plugins) {
for (let i = 0; i < config.plugins.length; i++) {
const plugin = config.plugins[i];
if (plugin.processAST) {
buffer = plugin.processAST(buffer, config);
}
}
}
return buffer;
}

114
vendor/jsr.io/@eta-dev/eta/3.5.0/src/render.ts generated vendored Normal file
View File

@ -0,0 +1,114 @@
import { EtaNameResolutionError } from "./err.ts";
/* TYPES */
import type { Options } from "./config.ts";
import type { TemplateFunction } from "./compile.ts";
import type { Eta } from "./core.ts";
/* END TYPES */
function handleCache(
this: Eta,
template: string,
options: Partial<Options>,
): TemplateFunction {
const templateStore = options && options.async
? this.templatesAsync
: this.templatesSync;
if (this.resolvePath && this.readFile && !template.startsWith("@")) {
const templatePath = options.filepath as string;
const cachedTemplate = templateStore.get(templatePath);
if (this.config.cache && cachedTemplate) {
return cachedTemplate;
} else {
const templateString = this.readFile(templatePath);
const templateFn = this.compile(templateString, options);
if (this.config.cache) templateStore.define(templatePath, templateFn);
return templateFn;
}
} else {
const cachedTemplate = templateStore.get(template);
if (cachedTemplate) {
return cachedTemplate;
} else {
throw new EtaNameResolutionError(
"Failed to get template '" + template + "'",
);
}
}
}
export function render<T extends object>(
this: Eta,
template: string | TemplateFunction, // template name or template function
data: T,
meta?: { filepath: string },
): string {
let templateFn: TemplateFunction;
const options = { ...meta, async: false };
if (typeof template === "string") {
if (this.resolvePath && this.readFile && !template.startsWith("@")) {
options.filepath = this.resolvePath(template, options);
}
templateFn = handleCache.call(this, template, options);
} else {
templateFn = template;
}
const res = templateFn.call(this, data, options);
return res;
}
export function renderAsync<T extends object>(
this: Eta,
template: string | TemplateFunction, // template name or template function
data: T,
meta?: { filepath: string },
): Promise<string> {
let templateFn: TemplateFunction;
const options = { ...meta, async: true };
if (typeof template === "string") {
if (this.resolvePath && this.readFile && !template.startsWith("@")) {
options.filepath = this.resolvePath(template, options);
}
templateFn = handleCache.call(this, template, options);
} else {
templateFn = template;
}
const res = templateFn.call(this, data, options);
// Return a promise
return Promise.resolve(res);
}
export function renderString<T extends object>(
this: Eta,
template: string,
data: T,
): string {
const templateFn = this.compile(template, { async: false });
return render.call(this, templateFn, data);
}
export function renderStringAsync<T extends object>(
this: Eta,
template: string,
data: T,
): Promise<string> {
const templateFn = this.compile(template, { async: true });
return renderAsync.call(this, templateFn, data);
}

25
vendor/jsr.io/@eta-dev/eta/3.5.0/src/storage.ts generated vendored Normal file
View File

@ -0,0 +1,25 @@
/**
* Handles storage and accessing of values
*
* In this case, we use it to store compiled template functions
* Indexed by their `name` or `filename`
*/
export class Cacher<T> {
constructor(private cache: Record<string, T>) {}
define(key: string, val: T): void {
this.cache[key] = val;
}
get(key: string): T {
return this.cache[key];
}
remove(key: string): void {
delete this.cache[key];
}
reset(): void {
this.cache = {};
}
load(cacheObj: Record<string, T>): void {
this.cache = { ...this.cache, ...cacheObj };
}
}

91
vendor/jsr.io/@eta-dev/eta/3.5.0/src/utils.ts generated vendored Normal file
View File

@ -0,0 +1,91 @@
import type { EtaConfig } from "./config.ts";
/**
* Takes a string within a template and trims it, based on the preceding tag's whitespace control and `config.autoTrim`
*/
export function trimWS(
str: string,
config: EtaConfig,
wsLeft: string | false,
wsRight?: string | false,
): string {
let leftTrim;
let rightTrim;
if (Array.isArray(config.autoTrim)) {
// Slightly confusing,
// but _}} will trim the left side of the following string
leftTrim = config.autoTrim[1];
rightTrim = config.autoTrim[0];
} else {
leftTrim = rightTrim = config.autoTrim;
}
if (wsLeft || wsLeft === false) {
leftTrim = wsLeft;
}
if (wsRight || wsRight === false) {
rightTrim = wsRight;
}
if (!rightTrim && !leftTrim) {
return str;
}
if (leftTrim === "slurp" && rightTrim === "slurp") {
return str.trim();
}
if (leftTrim === "_" || leftTrim === "slurp") {
// full slurp
str = str.trimStart();
} else if (leftTrim === "-" || leftTrim === "nl") {
// nl trim
str = str.replace(/^(?:\r\n|\n|\r)/, "");
}
if (rightTrim === "_" || rightTrim === "slurp") {
// full slurp
str = str.trimEnd();
} else if (rightTrim === "-" || rightTrim === "nl") {
// nl trim
str = str.replace(/(?:\r\n|\n|\r)$/, "");
}
return str;
}
/**
* A map of special HTML characters to their XML-escaped equivalents
*/
const escMap: { [key: string]: string } = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#39;",
};
function replaceChar(s: string): string {
return escMap[s];
}
/**
* XML-escapes an input value after converting it to a string
*
* @param str - Input value (usually a string)
* @returns XML-escaped string
*/
export function XMLEscape(str: unknown): string {
// To deal with XSS. Based on Escape implementations of Mustache.JS and Marko, then customized.
const newStr = String(str);
if (/[&<>"']/.test(newStr)) {
return newStr.replace(/[&<>"']/g, replaceChar);
} else {
return newStr;
}
}

602
vendor/jsr.io/@eta-dev/eta/3.5.0_meta.json generated vendored Normal file
View File

@ -0,0 +1,602 @@
{
"manifest": {
"/jsr.json": {
"size": 181,
"checksum": "sha256-a5fd262244bc73cbd184534361d324caf2c2c823000b2207bfedb363b42a3c1c"
},
"/src/config.ts": {
"size": 2896,
"checksum": "sha256-5b13139a94c07a13d9467712d754fb02f6b5245d30c0bab792f79b22277d4da8"
},
"/src/err.ts": {
"size": 2149,
"checksum": "sha256-7354a040b6aa49650d564c1f9791d9efe67499c1772632472f1cb91e93eb3af0"
},
"/README.md": {
"size": 10359,
"checksum": "sha256-7673f007986cfe047328cbd9dd40f488eb77a681d23b701063f9293a87a53540"
},
"/src/parse.ts": {
"size": 6041,
"checksum": "sha256-a029cc6b95e8c1393f1f4d25f107f770e6ab41b8dbb820e2c26f6808b8ef74a5"
},
"/LICENSE": {
"size": 1072,
"checksum": "sha256-ffae00b51ffea9ff5ba139166c89119d6ee3810f1e04e427dd6820a728b43915"
},
"/src/storage.ts": {
"size": 565,
"checksum": "sha256-c40bd31cdd6f1218c86f70067282709e2f2c7fa7eea67547be950e1ed5d4bd4c"
},
"/src/file-handling.ts": {
"size": 2577,
"checksum": "sha256-eeabf27135ec1fa1b513cafc61dc0ef6fbe385b0c10004d250f514e718469b6a"
},
"/src/render.ts": {
"size": 2932,
"checksum": "sha256-e63acccd1b3a0dfcf67c712c172f8175e71d30f0d3c6a07f2f7555e52f7b6cf1"
},
"/src/browser.ts": {
"size": 81,
"checksum": "sha256-d76f77a2adaa2f0d987c4c65132c65a7fa7b895221b75aee00a52ba48e663bf5"
},
"/src/index.ts": {
"size": 383,
"checksum": "sha256-d2bacfd6fc5ab8f41fc2802e2191863b5c630256648b0d2e8a2962412d63b1ac"
},
"/src/utils.ts": {
"size": 2035,
"checksum": "sha256-8e651cf1f30dfc90de15242079b7dd1a6f53789c5c8d65e1280cf15381847937"
},
"/src/compile.ts": {
"size": 1600,
"checksum": "sha256-6e519d6341664d552da844bb8356e818d3b644b59070fb6a918b8fed0f5e0e6c"
},
"/src/compile-string.ts": {
"size": 3457,
"checksum": "sha256-ee1de98e5349dc70652e944316cb7fe1d32fd4065fef323b9e6e78db65d3e20c"
},
"/src/core.ts": {
"size": 2439,
"checksum": "sha256-79ca3d073fde31f3d38f09048c9da7519c688563d48e05e014d85db26d9d7402"
}
},
"moduleGraph2": {
"/src/file-handling.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./err.ts",
"specifierRange": [
[
0,
39
],
[
0,
49
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "node:path",
"specifierRange": [
[
2,
22
],
[
2,
33
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "node:fs",
"specifierRange": [
[
4,
20
],
[
4,
29
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./core.ts",
"specifierRange": [
[
7,
36
],
[
7,
47
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./config.ts",
"specifierRange": [
[
8,
29
],
[
8,
42
]
]
}
]
},
"/src/core.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./storage.ts",
"specifierRange": [
[
0,
23
],
[
0,
37
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./compile.ts",
"specifierRange": [
[
1,
24
],
[
1,
38
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./compile-string.ts",
"specifierRange": [
[
2,
45
],
[
2,
66
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./config.ts",
"specifierRange": [
[
3,
30
],
[
3,
43
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./parse.ts",
"specifierRange": [
[
4,
22
],
[
4,
34
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./render.ts",
"specifierRange": [
[
10,
7
],
[
10,
20
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./err.ts",
"specifierRange": [
[
11,
37
],
[
11,
47
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./compile.ts",
"specifierRange": [
[
12,
33
],
[
12,
47
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./config.ts",
"specifierRange": [
[
15,
40
],
[
15,
53
]
]
}
]
},
"/src/parse.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./err.ts",
"specifierRange": [
[
0,
25
],
[
0,
35
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./utils.ts",
"specifierRange": [
[
1,
23
],
[
1,
35
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./core.ts",
"specifierRange": [
[
5,
25
],
[
5,
36
]
]
}
]
},
"/src/index.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./core.ts",
"specifierRange": [
[
0,
31
],
[
0,
42
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./file-handling.ts",
"specifierRange": [
[
1,
38
],
[
1,
58
]
]
},
{
"type": "static",
"kind": "export",
"specifier": "./err.ts",
"specifierRange": [
[
8,
7
],
[
8,
17
]
]
},
{
"type": "static",
"kind": "export",
"specifier": "./config.ts",
"specifierRange": [
[
9,
45
],
[
9,
58
]
]
}
]
},
"/src/err.ts": {},
"/src/config.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./utils.ts",
"specifierRange": [
[
0,
26
],
[
0,
38
]
]
}
]
},
"/src/compile-string.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./config.ts",
"specifierRange": [
[
2,
29
],
[
2,
42
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./parse.ts",
"specifierRange": [
[
3,
31
],
[
3,
43
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./core.ts",
"specifierRange": [
[
4,
25
],
[
4,
36
]
]
}
]
},
"/src/render.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./err.ts",
"specifierRange": [
[
0,
39
],
[
0,
49
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./config.ts",
"specifierRange": [
[
3,
29
],
[
3,
42
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./compile.ts",
"specifierRange": [
[
4,
38
],
[
4,
52
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./core.ts",
"specifierRange": [
[
5,
25
],
[
5,
36
]
]
}
]
},
"/src/storage.ts": {},
"/src/utils.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./config.ts",
"specifierRange": [
[
0,
31
],
[
0,
44
]
]
}
]
},
"/src/compile.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./err.ts",
"specifierRange": [
[
0,
30
],
[
0,
40
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./core.ts",
"specifierRange": [
[
3,
25
],
[
3,
36
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./config.ts",
"specifierRange": [
[
4,
40
],
[
4,
53
]
]
}
]
}
},
"exports": {
".": "./src/index.ts"
}
}

9
vendor/jsr.io/@eta-dev/eta/meta.json generated vendored Normal file
View File

@ -0,0 +1,9 @@
{
"scope": "eta-dev",
"name": "eta",
"latest": "3.5.0",
"versions": {
"3.4.0": {},
"3.5.0": {}
}
}

89
vendor/jsr.io/@felix/bcrypt/1.0.5/bindings/bindings.ts generated vendored Normal file
View File

@ -0,0 +1,89 @@
import { dlopen } from "jsr:@denosaurs/plug@^1.0.6";
export const VERSION = "1.0.5";
// Auto-generated with deno_bindgen
function encode(v: string | Uint8Array): Uint8Array {
if (typeof v !== "string") return v;
return new TextEncoder().encode(v);
}
function decode(v: Uint8Array): string {
return new TextDecoder().decode(v);
}
// deno-lint-ignore no-explicit-any
function readPointer(v: any): Uint8Array {
const ptr = new Deno.UnsafePointerView(v);
const lengthBe = new Uint8Array(4);
const view = new DataView(lengthBe.buffer);
ptr.copyInto(lengthBe, 0);
const buf = new Uint8Array(view.getUint32(0));
ptr.copyInto(buf, 4);
return buf;
}
function getLocalUrl(): string {
const url = new URL("../target/release", import.meta.url);
let uri = url.pathname;
if (!uri.endsWith("/")) uri += "/";
// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya#parameters
if (Deno.build.os === "windows") {
uri = uri.replace(/\//g, "\\");
// Remove leading slash
if (uri.startsWith("\\")) {
uri = uri.slice(1);
}
}
return uri;
}
const SYMBOLS = {
hash: {
parameters: ["buffer", "usize"],
result: "buffer",
nonblocking: true,
},
verify: {
parameters: ["buffer", "usize", "buffer", "usize"],
result: "i8",
nonblocking: true,
},
} as const;
const LOCAL = Deno.env.get("LOCAL");
const { symbols } = await dlopen(
{
name: "deno_bcrypt",
url: LOCAL
? getLocalUrl()
: `https://github.com/felix-schindler/deno-bcrypt/releases/download/v${VERSION}/`,
cache: LOCAL ? "reloadAll" : "use",
},
SYMBOLS,
);
export function hash(a0: string) {
const a0_buf = encode(a0);
const rawResult = symbols.hash(a0_buf, BigInt(a0_buf.byteLength));
const result = rawResult.then(readPointer);
return result.then(decode);
}
export function verify(a0: string, a1: string) {
const a0_buf = encode(a0);
const a1_buf = encode(a1);
const rawResult = symbols.verify(
a0_buf,
BigInt(a0_buf.byteLength),
a1_buf,
BigInt(a1_buf.byteLength),
);
const result = rawResult;
return result;
}

18
vendor/jsr.io/@felix/bcrypt/1.0.5/helper.ts generated vendored Normal file
View File

@ -0,0 +1,18 @@
import * as internal from "./bindings/bindings.ts";
/**
* @param password Clear text password
* @returns Hashed password
*/
export async function hash(password: string): Promise<string> {
return await internal.hash(password);
}
/**
* @param password Clear text password
* @param hash Hashed password
* @returns Whether the password matches the hash
*/
export async function verify(password: string, hash: string): Promise<boolean> {
return (await internal.verify(password, hash) === 1);
}

1
vendor/jsr.io/@felix/bcrypt/1.0.5/mod.ts generated vendored Normal file
View File

@ -0,0 +1 @@
export * from "./helper.ts";

94
vendor/jsr.io/@felix/bcrypt/1.0.5_meta.json generated vendored Normal file
View File

@ -0,0 +1,94 @@
{
"manifest": {
"/mod.ts": {
"size": 29,
"checksum": "sha256-2dc3991ca869eccc6a831a13b7d3488fd139ceead2868263bbf73a97ffcb6fa7"
},
"/bindings.json": {
"size": 216,
"checksum": "sha256-334c4ab9ae8029a60ef0d534d49794841e3193b7d082361a795aacceb37e71a0"
},
"/bindings/bindings.ts": {
"size": 2068,
"checksum": "sha256-b391e47854e78a54ab044c1c3846a9a4a73330bb953d99658e7c915b72359e1b"
},
"/helper.ts": {
"size": 500,
"checksum": "sha256-70a035fd0cebb70a680b28dca6e7f949e91ecc301aa299844374c5706d72a7c3"
},
"/deno.json": {
"size": 802,
"checksum": "sha256-cdad19aaf9007e051f72f13def1ac0a18734e6d5d51478feb9d483d9bb25a049"
},
"/README.md": {
"size": 1621,
"checksum": "sha256-ee89ab95a010b212cc242cb6bef5c3e596e6312942664a586b2efef8443b4dfd"
},
"/LICENSE": {
"size": 34523,
"checksum": "sha256-8486a10c4393cee1c25392769ddd3b2d6c242d6ec7928e1414efff7dfb2f07ef"
}
},
"moduleGraph2": {
"/helper.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./bindings/bindings.ts",
"specifierRange": [
[
0,
26
],
[
0,
50
]
]
}
]
},
"/bindings/bindings.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "jsr:@denosaurs/plug@^1.0.6",
"specifierRange": [
[
0,
23
],
[
0,
51
]
]
}
]
},
"/mod.ts": {
"dependencies": [
{
"type": "static",
"kind": "export",
"specifier": "./helper.ts",
"specifierRange": [
[
0,
14
],
[
0,
27
]
]
}
]
}
},
"exports": {
".": "./mod.ts"
}
}

9
vendor/jsr.io/@felix/bcrypt/meta.json generated vendored Normal file
View File

@ -0,0 +1,9 @@
{
"scope": "felix",
"name": "bcrypt",
"latest": "1.0.5",
"versions": {
"0.3.0": {},
"1.0.5": {}
}
}

89
vendor/jsr.io/@luca/esbuild-deno-loader/0.11.1/mod.ts generated vendored Normal file
View File

@ -0,0 +1,89 @@
import type * as esbuild from "./src/esbuild_types.ts";
import {
denoResolverPlugin,
type DenoResolverPluginOptions,
} from "./src/plugin_deno_resolver.ts";
export { denoResolverPlugin, type DenoResolverPluginOptions };
import {
DEFAULT_LOADER,
denoLoaderPlugin,
type DenoLoaderPluginOptions,
} from "./src/plugin_deno_loader.ts";
export { DEFAULT_LOADER, denoLoaderPlugin, type DenoLoaderPluginOptions };
export {
type EsbuildResolution,
esbuildResolutionToURL,
urlToEsbuildResolution,
} from "./src/shared.ts";
/** Options for the {@link denoPlugins} function. */
export interface DenoPluginsOptions {
/**
* Specify which loader to use. By default this will use the `native` loader,
* unless the `--allow-run` permission has not been given.
*
* See {@link denoLoaderPlugin} for more information on the different loaders.
*/
loader?: "native" | "portable";
/**
* Specify the path to a deno.json config file to use. This is equivalent to
* the `--config` flag to the Deno executable. This path must be absolute.
*
* If not specified, the plugin will attempt to find the nearest deno.json and
* use that. If the deno.json is part of a workspace, the plugin will
* automatically find the workspace root.
*/
configPath?: string;
/**
* Specify a URL to an import map file to use when resolving import
* specifiers. This is equivalent to the `--import-map` flag to the Deno
* executable. This URL may be remote or a local file URL.
*
* If this option is not specified, the deno.json config file is consulted to
* determine what import map to use, if any.
*/
importMapURL?: string;
/**
* Specify the path to a deno.lock file to use. This is equivalent to the
* `--lock` flag to the Deno executable. This path must be absolute.
*
* If this option is not specified, the deno.json config file is consulted to
* determine what lock file to use, if any.
*
* A lockfile must be present to resolve `jsr:` specifiers with the `portable`
* loader. When using the `native` loader, a lockfile is not required, but to
* ensure dependencies are de-duplicated correctly, it is recommended to use a
* lockfile.
*/
lockPath?: string;
/**
* Specify how the loader should handle NPM packages. By default and if this
* option is set to `none`, the loader will use the global cache to resolve
* NPM packages. If this option is set to `manual`, the loader will use a
* manually managed `node_modules` directory. If this option is set to `auto`,
* the loader will use a local `node_modules` directory.
*
* If this option is not specified, the deno.json config file is consulted to
* determine which mode to use.
*
* This option is ignored when using the `portable` loader, as the portable
* loader always uses a manual `node_modules` directory (equivalent of
* `nodeModulesDir: "manual"`).
*/
nodeModulesDir?: "auto" | "manual" | "none";
}
/**
* A convenience function to enable both the Deno resolver plugin, and Deno
* loader plugin.
*/
export function denoPlugins(opts: DenoPluginsOptions = {}): esbuild.Plugin[] {
return [
denoResolverPlugin(opts),
denoLoaderPlugin(opts),
];
}

View File

@ -0,0 +1,283 @@
export interface RootInfoOutput {
denoDir: string;
npmCache: string;
}
export async function rootInfo(): Promise<RootInfoOutput> {
if (!tmpDir) tmpDir = Deno.makeTempDirSync();
const opts = {
args: ["info", "--json", "--no-config", "--no-lock"],
cwd: tmpDir,
env: { DENO_NO_PACKAGE_JSON: "true" } as Record<string, string>,
stdout: "piped",
stderr: "inherit",
};
const output = await new Deno.Command(
Deno.execPath(),
opts as Deno.CommandOptions,
).output();
if (!output.success) {
throw new Error(`Failed to call 'deno info'`);
}
const txt = new TextDecoder().decode(output.stdout);
return JSON.parse(txt);
}
// Lifted from https://raw.githubusercontent.com/denoland/deno_graph/89affe43c9d3d5c9165c8089687c107d53ed8fe1/lib/media_type.ts
export type MediaType =
| "JavaScript"
| "Mjs"
| "Cjs"
| "JSX"
| "TypeScript"
| "Mts"
| "Cts"
| "Dts"
| "Dmts"
| "Dcts"
| "TSX"
| "Json"
| "Wasm"
| "TsBuildInfo"
| "SourceMap"
| "Unknown";
interface InfoOutput {
roots: string[];
modules: ModuleEntry[];
redirects: Record<string, string>;
npmPackages?: Record<string, NpmPackage>;
}
export type ModuleEntry =
| ModuleEntryError
| ModuleEntryEsm
| ModuleEntryJson
| ModuleEntryNpm
| ModuleEntryNode;
export interface ModuleEntryBase {
specifier: string;
}
export interface ModuleEntryError extends ModuleEntryBase {
error: string;
}
export interface ModuleEntryEsm extends ModuleEntryBase {
kind: "esm";
local: string | null;
emit: string | null;
map: string | null;
mediaType: MediaType;
size: number;
}
export interface ModuleEntryJson extends ModuleEntryBase {
kind: "asserted" | "json";
local: string | null;
mediaType: MediaType;
size: number;
}
export interface ModuleEntryNpm extends ModuleEntryBase {
kind: "npm";
npmPackage: string;
}
export interface ModuleEntryNode extends ModuleEntryBase {
kind: "node";
moduleName: string;
}
export interface NpmPackage {
name: string;
version: string;
dependencies: string[];
registryUrl?: string;
}
export interface InfoOptions {
cwd?: string;
config?: string;
importMap?: string;
lock?: string;
nodeModulesDir?: "auto" | "manual" | "none";
}
let tmpDir: string | undefined;
async function info(
specifier: string,
options: InfoOptions,
): Promise<InfoOutput> {
const args = ["info", "--json"];
if (!Deno.version.deno.startsWith("1.")) {
args.push("--allow-import");
}
const opts = {
args,
cwd: undefined as string | undefined,
env: { DENO_NO_PACKAGE_JSON: "true" } as Record<string, string>,
stdout: "piped",
stderr: "inherit",
};
if (typeof options.config === "string") {
opts.args.push("--config", options.config);
} else {
opts.args.push("--no-config");
}
if (options.importMap) {
opts.args.push("--import-map", options.importMap);
}
if (typeof options.lock === "string") {
opts.args.push("--lock", options.lock);
} else if (!options.cwd) {
opts.args.push("--no-lock");
}
if (options.nodeModulesDir !== undefined) {
if (Deno.version.deno.startsWith("1.")) {
if (options.nodeModulesDir === "auto") {
opts.args.push("--node-modules-dir");
} else if (options.nodeModulesDir === "manual") {
opts.args.push("--unstable-byonm");
}
} else {
opts.args.push(`--node-modules-dir=${options.nodeModulesDir}`);
}
}
if (options.cwd) {
opts.cwd = options.cwd;
} else {
if (!tmpDir) tmpDir = Deno.makeTempDirSync();
opts.cwd = tmpDir;
}
opts.args.push(specifier);
const output = await new Deno.Command(
Deno.execPath(),
opts as Deno.CommandOptions,
).output();
if (!output.success) {
throw new Error(`Failed to call 'deno info' on '${specifier}'`);
}
const txt = new TextDecoder().decode(output.stdout);
return JSON.parse(txt);
}
export class InfoCache {
#options: InfoOptions;
#pending: { done: Promise<void>; specifiers: Set<string> | null } | null =
null;
#modules: Map<string, ModuleEntry> = new Map();
#redirects: Map<string, string> = new Map();
#npmPackages: Map<string, NpmPackage> = new Map();
constructor(options: InfoOptions = {}) {
this.#options = options;
}
async get(specifier: string): Promise<ModuleEntry> {
let entry = this.#getCached(specifier);
if (entry !== undefined) return entry;
await this.#queueLoad(specifier);
entry = this.#getCached(specifier);
if (entry === undefined) {
throw new Error(`Unreachable: '${specifier}' loaded but not reachable`);
}
return entry;
}
getNpmPackage(id: string): NpmPackage | undefined {
return this.#npmPackages.get(id);
}
#resolve(specifier: string): string {
const original = specifier;
let counter = 0;
while (counter++ < 10) {
const redirect = this.#redirects.get(specifier);
if (redirect === undefined) return specifier;
specifier = redirect;
}
throw new Error(`Too many redirects for '${original}'`);
}
#getCached(specifier: string): ModuleEntry | undefined {
specifier = this.#resolve(specifier);
return this.#modules.get(specifier);
}
async #queueLoad(specifier: string) {
while (true) {
if (this.#pending === null) {
this.#pending = {
specifiers: new Set([specifier]),
done: (async () => {
await new Promise((r) => setTimeout(r, 5));
const specifiers = this.#pending!.specifiers!;
this.#pending!.specifiers = null;
await this.#load([...specifiers]);
this.#pending = null;
})(),
};
await this.#pending.done;
return;
} else if (this.#pending.specifiers !== null) {
this.#pending.specifiers.add(specifier);
await this.#pending.done;
return;
} else {
await this.#pending.done;
}
}
}
async #load(specifiers: string[]): Promise<void> {
await this.#populate(specifiers);
for (let specifier of specifiers) {
specifier = this.#resolve(specifier);
const entry = this.#modules.get(specifier);
if (entry === undefined && specifier.startsWith("npm:")) {
// we hit https://github.com/denoland/deno/issues/18043, so we have to
// perform another load to get the actual data of the redirected specifier
await this.#populate([specifier]);
}
}
}
async #populate(specifiers: string[]) {
let specifier;
if (specifiers.length === 1) {
specifier = specifiers[0];
} else {
specifier = `data:application/javascript,${
encodeURIComponent(
specifiers.map((s) => `import ${JSON.stringify(s)};`).join(""),
)
}`;
}
const { modules, redirects, npmPackages } = await info(
specifier,
this.#options,
);
for (const module of modules) {
if (specifiers.length !== 1 && module.specifier === specifier) continue;
this.#modules.set(module.specifier, module);
}
for (const [from, to] of Object.entries(redirects)) {
this.#redirects.set(from, to);
}
if (npmPackages !== undefined) {
for (const [id, npmPackage] of Object.entries(npmPackages)) {
this.#npmPackages.set(id, npmPackage);
}
}
}
}

View File

@ -0,0 +1,143 @@
/**
* This is a copy of the esbuild types that `deno_esbuild_loader` uses. This is
* necessary because the `esbuild` package is not available on JSR yet.
*
* @module
*/
/** the type of import */
export type ImportKind =
| "entry-point"
// JS
| "import-statement"
| "require-call"
| "dynamic-import"
| "require-resolve"
// CSS
| "import-rule"
| "composes-from"
| "url-token";
/** Documentation: https://esbuild.github.io/api/#loader */
export type Loader =
| "base64"
| "binary"
| "copy"
| "css"
| "dataurl"
| "default"
| "empty"
| "file"
| "js"
| "json"
| "jsx"
| "local-css"
| "text"
| "ts"
| "tsx";
/** Documentation: https://esbuild.github.io/plugins */
export interface Plugin {
name: string;
setup: (build: PluginBuild) => void | Promise<void>;
}
/** Documentation: https://esbuild.github.io/plugins */
export interface PluginBuild {
/** Documentation: https://esbuild.github.io/plugins/#build-options */
initialOptions: BuildOptions;
/** Documentation: https://esbuild.github.io/plugins/#resolve */
resolve(path: string, options?: ResolveOptions): Promise<ResolveResult>;
/** Documentation: https://esbuild.github.io/plugins/#on-start */
onStart(callback: () => void | Promise<void>): void;
/** Documentation: https://esbuild.github.io/plugins/#on-resolve */
onResolve(
options: OnResolveOptions,
callback: (args: OnResolveArgs) => Promise<OnResolveResult | undefined>,
): void;
/** Documentation: https://esbuild.github.io/plugins/#on-load */
onLoad(
options: OnLoadOptions,
callback: (
args: OnLoadArgs,
) => Promise<OnLoadResult | null | undefined> | undefined,
): void;
}
/** Documentation: https://esbuild.github.io/api */
export interface BuildOptions {
/** Documentation: https://esbuild.github.io/api/#external */
external?: string[];
/** Documentation: https://esbuild.github.io/api/#working-directory */
absWorkingDir?: string;
/** Documentation: https://esbuild.github.io/api/#entry-points */
entryPoints?: string[] | Record<string, string> | {
in: string;
out: string;
}[];
}
/** Documentation: https://esbuild.github.io/plugins/#resolve-options */
export interface ResolveOptions {
importer?: string;
resolveDir?: string;
namespace?: string;
kind?: ImportKind;
}
/** Documentation: https://esbuild.github.io/plugins/#resolve-results */
export interface ResolveResult {
path: string;
namespace: string;
}
/** Documentation: https://esbuild.github.io/plugins/#on-resolve-options */
export interface OnResolveOptions {
filter: RegExp;
namespace?: string;
}
/** Documentation: https://esbuild.github.io/plugins/#on-resolve-arguments */
export interface OnResolveArgs {
path: string;
importer: string;
namespace: string;
resolveDir: string;
kind: ImportKind;
}
export interface OnResolveResult {
path?: string;
external?: boolean;
namespace?: string;
}
/** Documentation: https://esbuild.github.io/plugins/#on-load-options */
export interface OnLoadOptions {
filter: RegExp;
namespace?: string;
}
/** Documentation: https://esbuild.github.io/plugins/#on-load-arguments */
export interface OnLoadArgs {
path: string;
namespace: string;
}
/** Documentation: https://esbuild.github.io/plugins/#on-load-results */
export interface OnLoadResult {
contents?: string | Uint8Array;
resolveDir?: string;
loader?: Loader;
watchFiles?: string[];
}
/** Documentation: https://esbuild.github.io/plugins/#on-start-results */
// deno-lint-ignore no-empty-interface
export interface OnStartResult {
}

View File

@ -0,0 +1,219 @@
import type * as esbuild from "./esbuild_types.ts";
import { dirname, fromFileUrl, join } from "jsr:@std/path@^1.0.6";
import { encodeBase32 } from "jsr:/@std/encoding@^1.0.5/base32";
import { lastIndexOfNeedle } from "jsr:@std/bytes@^1.0.2";
import * as deno from "./deno.ts";
import { rootInfo, type RootInfoOutput } from "./deno.ts";
import {
type Loader,
type LoaderResolution,
mapContentType,
mediaTypeFromSpecifier,
mediaTypeToLoader,
parseNpmSpecifier,
} from "./shared.ts";
let ROOT_INFO_OUTPUT: Promise<RootInfoOutput> | RootInfoOutput | undefined;
export const DENO_CACHE_METADATA = new TextEncoder()
.encode("\n// denoCacheMetadata=");
export interface NativeLoaderOptions {
infoOptions?: deno.InfoOptions;
}
export class NativeLoader implements Loader {
#nodeModulesDirManual: boolean;
#infoCache: deno.InfoCache;
#linkDirCache: Map<string, string> = new Map(); // mapping from package id -> link dir
constructor(options: NativeLoaderOptions) {
this.#nodeModulesDirManual =
options.infoOptions?.nodeModulesDir === "manual";
this.#infoCache = new deno.InfoCache(options.infoOptions);
}
async resolve(specifier: URL): Promise<LoaderResolution> {
// Workaround for https://github.com/denoland/deno/issues/25903
if (this.#nodeModulesDirManual && specifier.protocol === "npm:") {
const npmSpecifier = parseNpmSpecifier(specifier);
return {
kind: "npm",
packageId: "",
packageName: npmSpecifier.name,
path: npmSpecifier.path ?? "",
};
}
const entry = await this.#infoCache.get(specifier.href);
if ("error" in entry) {
if (
specifier.protocol === "file:" &&
mediaTypeFromSpecifier(specifier) === "Unknown"
) {
return { kind: "esm", specifier: new URL(entry.specifier) };
}
throw new Error(entry.error);
}
if (entry.kind === "npm") {
// TODO(lucacasonato): remove parsing once https://github.com/denoland/deno/issues/18043 is resolved
const parsed = parseNpmSpecifier(new URL(entry.specifier));
return {
kind: "npm",
packageId: entry.npmPackage,
packageName: parsed.name,
path: parsed.path ?? "",
};
} else if (entry.kind === "node") {
return {
kind: "node",
path: entry.specifier,
};
}
return { kind: "esm", specifier: new URL(entry.specifier) };
}
async loadEsm(specifier: URL): Promise<esbuild.OnLoadResult | undefined> {
if (specifier.protocol === "data:") {
const resp = await fetch(specifier);
const contents = new Uint8Array(await resp.arrayBuffer());
const contentType = resp.headers.get("content-type");
const mediaType = mapContentType(specifier, contentType);
const loader = mediaTypeToLoader(mediaType);
if (loader === null) return undefined;
return { contents, loader };
}
const entry = await this.#infoCache.get(specifier.href);
if (
"error" in entry && specifier.protocol !== "file:" &&
mediaTypeFromSpecifier(specifier) !== "Unknown"
) throw new Error(entry.error);
if (!("local" in entry)) {
throw new Error("[unreachable] Not an ESM module.");
}
if (!entry.local) throw new Error("Module not downloaded yet.");
const loader = mediaTypeToLoader(entry.mediaType);
if (loader === null) return undefined;
let contents = await Deno.readFile(entry.local);
const denoCacheMetadata = lastIndexOfNeedle(contents, DENO_CACHE_METADATA);
if (denoCacheMetadata !== -1) {
contents = contents.subarray(0, denoCacheMetadata);
}
const res: esbuild.OnLoadResult = { contents, loader };
if (specifier.protocol === "file:") {
res.watchFiles = [fromFileUrl(specifier)];
}
return res;
}
async nodeModulesDirForPackage(npmPackageId: string): Promise<string> {
const npmPackage = this.#infoCache.getNpmPackage(npmPackageId);
if (!npmPackage) throw new Error("NPM package not found.");
let linkDir = this.#linkDirCache.get(npmPackageId);
if (!linkDir) {
linkDir = await this.#nodeModulesDirForPackageInner(
npmPackageId,
npmPackage,
);
this.#linkDirCache.set(npmPackageId, linkDir);
}
return linkDir;
}
async #nodeModulesDirForPackageInner(
npmPackageId: string,
npmPackage: deno.NpmPackage,
): Promise<string> {
let name = npmPackage.name;
if (name.toLowerCase() !== name) {
name = `_${encodeBase32(new TextEncoder().encode(name))}`;
}
if (ROOT_INFO_OUTPUT === undefined) {
ROOT_INFO_OUTPUT = rootInfo();
}
if (ROOT_INFO_OUTPUT instanceof Promise) {
ROOT_INFO_OUTPUT = await ROOT_INFO_OUTPUT;
}
const { denoDir, npmCache } = ROOT_INFO_OUTPUT;
const registryUrl = npmPackage.registryUrl ?? "https://registry.npmjs.org";
const registry = new URL(registryUrl);
const packageDir = join(
npmCache,
registry.hostname,
name,
npmPackage.version,
);
const linkDir = join(
denoDir,
"deno_esbuild",
registry.hostname,
npmPackageId,
"node_modules",
name,
);
const linkDirParent = dirname(linkDir);
const tmpDirParent = join(denoDir, "deno_esbuild_tmp");
// check if the package is already linked, if so, return the link and skip
// a bunch of work
try {
await Deno.stat(linkDir);
this.#linkDirCache.set(npmPackageId, linkDir);
return linkDir;
} catch {
// directory does not yet exist
}
// create a temporary directory, recursively hardlink the package contents
// into it, and then rename it to the final location
await Deno.mkdir(tmpDirParent, { recursive: true });
const tmpDir = await Deno.makeTempDir({ dir: tmpDirParent });
await linkRecursive(packageDir, tmpDir);
try {
await Deno.mkdir(linkDirParent, { recursive: true });
await Deno.rename(tmpDir, linkDir);
} catch (err) {
// the directory may already have been created by someone else - check if so
try {
await Deno.stat(linkDir);
} catch {
throw err;
}
}
return linkDir;
}
packageIdFromNameInPackage(
name: string,
parentPackageId: string,
): string | null {
const parentPackage = this.#infoCache.getNpmPackage(parentPackageId);
if (!parentPackage) throw new Error("NPM package not found.");
if (parentPackage.name === name) return parentPackageId;
for (const dep of parentPackage.dependencies) {
const depPackage = this.#infoCache.getNpmPackage(dep);
if (!depPackage) throw new Error("NPM package not found.");
if (depPackage.name === name) return dep;
}
return null;
}
}
async function linkRecursive(from: string, to: string) {
const fromStat = await Deno.stat(from);
if (fromStat.isDirectory) {
await Deno.mkdir(to, { recursive: true });
for await (const entry of Deno.readDir(from)) {
await linkRecursive(join(from, entry.name), join(to, entry.name));
}
} else {
await Deno.link(from, to);
}
}

View File

@ -0,0 +1,245 @@
import type * as esbuild from "./esbuild_types.ts";
import { fromFileUrl } from "jsr:@std/path@^1.0.6";
import type * as deno from "./deno.ts";
import {
type Loader,
type LoaderResolution,
mapContentType,
mediaTypeToLoader,
parseJsrSpecifier,
parseNpmSpecifier,
} from "./shared.ts";
import { instantiate, type WasmLockfile } from "./wasm/loader.generated.js";
interface Module {
specifier: string;
mediaType: deno.MediaType;
data: Uint8Array;
}
const JSR_URL = Deno.env.get("JSR_URL") ?? "https://jsr.io";
async function readLockfile(path: string): Promise<WasmLockfile | null> {
try {
const data = await Deno.readTextFile(path);
const instance = instantiate();
return new instance.WasmLockfile(path, data);
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
return null;
}
throw err;
}
}
interface PortableLoaderOptions {
lock?: string;
}
export class PortableLoader implements Loader, Disposable {
#options: PortableLoaderOptions;
#fetchOngoing = new Map<string, Promise<void>>();
#lockfile: Promise<WasmLockfile | null> | WasmLockfile | null | undefined;
#fetchModules = new Map<string, Module>();
#fetchRedirects = new Map<string, string>();
constructor(options: PortableLoaderOptions) {
this.#options = options;
}
[Symbol.dispose]() {
if (this.#lockfile != null && "free" in this.#lockfile) {
this.#lockfile.free();
}
}
async resolve(specifier: URL): Promise<LoaderResolution> {
switch (specifier.protocol) {
case "file:": {
return { kind: "esm", specifier };
}
case "http:":
case "https:":
case "data:": {
const module = await this.#loadRemote(specifier.href);
return { kind: "esm", specifier: new URL(module.specifier) };
}
case "npm:": {
const npmSpecifier = parseNpmSpecifier(specifier);
return {
kind: "npm",
packageId: "",
packageName: npmSpecifier.name,
path: npmSpecifier.path ?? "",
};
}
case "node:": {
return { kind: "node", path: specifier.pathname };
}
case "jsr:": {
const resolvedSpecifier = await this.#resolveJsrSpecifier(specifier);
return { kind: "esm", specifier: resolvedSpecifier };
}
default:
throw new Error(`Unsupported scheme: '${specifier.protocol}'`);
}
}
async #resolveJsrSpecifier(specifier: URL): Promise<URL> {
// parse the JSR specifier.
const jsrSpecifier = parseJsrSpecifier(specifier);
// Attempt to load the lockfile.
if (this.#lockfile === undefined) {
this.#lockfile = typeof this.#options.lock === "string"
? readLockfile(this.#options.lock)
: null;
}
if (this.#lockfile instanceof Promise) {
this.#lockfile = await this.#lockfile;
}
if (this.#lockfile === null) {
throw new Error(
"jsr: specifiers are not supported in the portable loader without a lockfile",
);
}
const lockfile = this.#lockfile;
// Look up the package + constraint in the lockfile.
const id = `jsr:${jsrSpecifier.name}${
jsrSpecifier.version ? `@${jsrSpecifier.version}` : ""
}`;
const resolvedVersion = lockfile.package_version(id);
if (!resolvedVersion) {
throw new Error(`Specifier not found in lockfile: ${id}`);
}
// Load the JSR manifest to find the export path.
const manifestUrl = new URL(
`./${jsrSpecifier.name}/${resolvedVersion}_meta.json`,
JSR_URL,
);
const manifest = await this.#loadRemote(manifestUrl.href);
if (manifest.mediaType !== "Json") {
throw new Error(
`Expected JSON media type for JSR manifest, got: ${manifest.mediaType}`,
);
}
const manifestData = new TextDecoder().decode(manifest.data);
const manifestJson = JSON.parse(manifestData);
// Look up the export path in the manifest.
const exportEntry = `.${jsrSpecifier.path ?? ""}`;
const exportPath = manifestJson.exports[exportEntry];
if (!exportPath) {
throw new Error(
`Package 'jsr:${jsrSpecifier.name}@${resolvedVersion}' has no export named '${exportEntry}'`,
);
}
// Return the resolved URL.
return new URL(
`./${jsrSpecifier.name}/${resolvedVersion}/${exportPath}`,
JSR_URL,
);
}
async loadEsm(url: URL): Promise<esbuild.OnLoadResult | undefined> {
let module: Module;
switch (url.protocol) {
case "file:": {
module = await this.#loadLocal(url);
break;
}
case "http:":
case "https:":
case "data:": {
module = await this.#loadRemote(url.href);
break;
}
default:
throw new Error("[unreachable] unsupported esm scheme " + url.protocol);
}
const loader = mediaTypeToLoader(module.mediaType);
if (loader === null) return undefined;
const res: esbuild.OnLoadResult = { contents: module.data, loader };
if (url.protocol === "file:") {
res.watchFiles = [fromFileUrl(module.specifier)];
}
return res;
}
#resolveRemote(specifier: string): string {
return this.#fetchRedirects.get(specifier) ?? specifier;
}
async #loadRemote(specifier: string): Promise<Module> {
for (let i = 0; i < 10; i++) {
specifier = this.#resolveRemote(specifier);
const module = this.#fetchModules.get(specifier);
if (module) return module;
let promise = this.#fetchOngoing.get(specifier);
if (!promise) {
promise = this.#fetch(specifier);
this.#fetchOngoing.set(specifier, promise);
}
await promise;
}
throw new Error("Too many redirects. Last one: " + specifier);
}
async #fetch(specifier: string): Promise<void> {
const resp = await fetch(specifier, {
redirect: "manual",
});
if (resp.status < 200 && resp.status >= 400) {
throw new Error(
`Encountered status code ${resp.status} while fetching ${specifier}.`,
);
}
if (resp.status >= 300 && resp.status < 400) {
await resp.body?.cancel();
const location = resp.headers.get("location");
if (!location) {
throw new Error(
`Redirected without location header while fetching ${specifier}.`,
);
}
const url = new URL(location, specifier);
if (url.protocol !== "https:" && url.protocol !== "http:") {
throw new Error(
`Redirected to unsupported protocol '${url.protocol}' while fetching ${specifier}.`,
);
}
this.#fetchRedirects.set(specifier, url.href);
return;
}
const contentType = resp.headers.get("content-type");
const mediaType = mapContentType(new URL(specifier), contentType);
const data = new Uint8Array(await resp.arrayBuffer());
this.#fetchModules.set(specifier, {
specifier,
mediaType,
data,
});
}
async #loadLocal(specifier: URL): Promise<Module> {
const path = fromFileUrl(specifier);
const mediaType = mapContentType(specifier, null);
const data = await Deno.readFile(path);
return { specifier: specifier.href, mediaType, data };
}
}

View File

@ -0,0 +1,401 @@
import type * as esbuild from "./esbuild_types.ts";
import { dirname, join } from "jsr:@std/path@^1.0.6";
import { NativeLoader } from "./loader_native.ts";
import { PortableLoader } from "./loader_portable.ts";
import { findWorkspace, isInNodeModules } from "./shared.ts";
import {
esbuildResolutionToURL,
isNodeModulesResolution,
type Loader,
urlToEsbuildResolution,
} from "./shared.ts";
/** Options for the {@link denoLoaderPlugin}. */
export interface DenoLoaderPluginOptions {
/**
* Specify which loader to use. By default this will use the `native` loader,
* unless the `--allow-run` permission has not been given.
*
* See {@link denoLoaderPlugin} for more information on the different loaders.
*/
loader?: "native" | "portable";
/**
* Specify the path to a deno.json config file to use. This is equivalent to
* the `--config` flag to the Deno executable. This path must be absolute.
*
* NOTE: Import maps in the config file are not used to inform resolution, as
* this has already been done by the `denoResolverPlugin`. This option is only
* used when specifying `loader: "native"` to more efficiently load modules
* from the cache. When specifying `loader: "native"`, this option must be in
* sync with the `configPath` option for `denoResolverPlugin`.
*
* If not specified, the plugin will attempt to find the nearest deno.json and
* use that. If the deno.json is part of a workspace, the plugin will
* automatically find the workspace root.
*/
configPath?: string;
/**
* Specify a URL to an import map file to use when resolving import
* specifiers. This is equivalent to the `--import-map` flag to the Deno
* executable. This URL may be remote or a local file URL.
*
* If this option is not specified, the deno.json config file is consulted to
* determine what import map to use, if any.
*
* NOTE: Import maps in the config file are not used to inform resolution, as
* this has already been done by the `denoResolverPlugin`. This option is only
* used when specifying `loader: "native"` to more efficiently load modules
* from the cache. When specifying `loader: "native"`, this option must be in
* sync with the `importMapURL` option for `denoResolverPlugin`.
*/
importMapURL?: string;
/**
* Specify the path to a lock file to use. This is equivalent to the `--lock`
* flag to the Deno executable. This path must be absolute.
*
* If this option is not specified, the deno.json config file is consulted to
* determine what import map to use, if any.
*
* A lockfile must be present to resolve `jsr:` specifiers with the `portable`
* loader. When using the `native` loader, a lockfile is not required, but to
* ensure dependencies are de-duplicated correctly, it is recommended to use a
* lockfile.
*
* NOTE: when using `loader: "portable"`, integrity checks are not performed
* for ESM modules.
*/
lockPath?: string;
/**
* Specify how the loader should handle NPM packages. By default and if this
* option is set to `none`, the loader will use the global cache to resolve
* NPM packages. If this option is set to `manual`, the loader will use a
* manually managed `node_modules` directory. If this option is set to `auto`,
* the loader will use a local `node_modules` directory.
*
* If this option is not specified, the deno.json config file is consulted to
* determine which mode to use. If no config file is present, or the config
* file does not specify this option, the default is `none` if no package.json
* is present, and `auto` if a package.json is present.
*
* This option is ignored when using the `portable` loader, as the portable
* loader always uses a manual `node_modules` directory (equivalent of
* `nodeModulesDir: "manual"`).
*/
nodeModulesDir?: "auto" | "manual" | "none";
}
const LOADERS = ["native", "portable"] as const;
/** The default loader to use. */
export const DEFAULT_LOADER: "native" | "portable" =
await Deno.permissions.query({ name: "run" })
.then((res) => res.state !== "granted")
? "portable"
: "native";
const BUILTIN_NODE_MODULES = new Set([
"assert",
"assert/strict",
"async_hooks",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"diagnostics_channel",
"dns",
"dns/promises",
"domain",
"events",
"fs",
"fs/promises",
"http",
"http2",
"https",
"module",
"net",
"os",
"path",
"path/posix",
"path/win32",
"perf_hooks",
"process",
"punycode",
"querystring",
"repl",
"readline",
"stream",
"stream/consumers",
"stream/promises",
"stream/web",
"string_decoder",
"sys",
"test",
"timers",
"timers/promises",
"tls",
"tty",
"url",
"util",
"util/types",
"v8",
"vm",
"worker_threads",
"zlib",
]);
/**
* The Deno loader plugin for esbuild. This plugin will load fully qualified
* `file`, `http`, `https`, and `data` URLs.
*
* **Note** that this plugin does not do relative->absolute specifier
* resolution, or import map resolution. You must use the `denoResolverPlugin`
* _before_ the `denoLoaderPlugin` to do that.
*
* This plugin can be backed by two different loaders, the `native` loader and
* the `portable` loader.
*
* ### Native Loader
*
* The native loader shells out to the Deno executable under the hood to load
* files. Requires `--allow-read` and `--allow-run`. In this mode the download
* cache is shared with the Deno executable. This mode respects deno.lock,
* DENO_DIR, DENO_AUTH_TOKENS, and all similar loading configuration. Files are
* cached on disk in the same Deno cache as the Deno executable, and will not be
* re-downloaded on subsequent builds.
*
* NPM specifiers can be used in the native loader without requiring a local
* `node_modules` directory. NPM packages are resolved, downloaded, cached, and
* loaded in the same way as the Deno executable does.
*
* JSR specifiers can be used without restrictions in the native loader. To
* ensure dependencies are de-duplicated correctly, it is recommended to use a
* lockfile.
*
* ### Portable Loader
*
* The portable loader does module downloading and caching with only Web APIs.
* Requires `--allow-read` and/or `--allow-net`. This mode does not respect
* deno.lock, DENO_DIR, DENO_AUTH_TOKENS, or any other loading configuration. It
* does not cache downloaded files. It will re-download files on every build.
*
* NPM specifiers can be used in the portable loader, but require a local
* `node_modules` directory. The `node_modules` directory must be created prior
* using Deno's `--node-modules-dir` flag.
*
* JSR specifiers require a lockfile to be present to resolve.
*/
export function denoLoaderPlugin(
options: DenoLoaderPluginOptions = {},
): esbuild.Plugin {
const loader = options.loader ?? DEFAULT_LOADER;
if (LOADERS.indexOf(loader) === -1) {
throw new Error(`Invalid loader: ${loader}`);
}
return {
name: "deno-loader",
setup(build) {
const cwd = build.initialOptions.absWorkingDir ?? Deno.cwd();
let nodeModulesDir: string | null = null;
let loaderImpl: Loader | undefined;
const packageIdByNodeModules = new Map<string, string>();
build.onStart(function onStart() {
loaderImpl?.[Symbol.dispose]?.();
loaderImpl = undefined;
packageIdByNodeModules.clear();
let nodeModulesDirOpt: "auto" | "manual" | "none" | undefined =
options.nodeModulesDir;
let lockPath: string | undefined = options.lockPath;
if (
(nodeModulesDirOpt === undefined ||
(loader === "portable" && lockPath === undefined))
) {
const workspace = findWorkspace(
cwd,
build.initialOptions.entryPoints,
options.configPath,
);
try {
if (nodeModulesDirOpt === undefined) {
nodeModulesDirOpt = workspace.node_modules_dir() as
| "auto"
| "manual"
| "none";
}
if (loader === "portable" && lockPath === undefined) {
lockPath = workspace.lock_path();
}
} finally {
workspace.free();
}
}
if (
nodeModulesDirOpt === "auto" ||
nodeModulesDirOpt === "manual"
) {
nodeModulesDir = join(cwd, "node_modules");
}
switch (loader) {
case "native":
loaderImpl = new NativeLoader({
infoOptions: {
cwd,
config: options.configPath,
importMap: options.importMapURL,
lock: options.lockPath,
nodeModulesDir: nodeModulesDirOpt,
},
});
break;
case "portable": {
loaderImpl = new PortableLoader({
lock: lockPath,
});
}
}
});
async function onResolve(
args: esbuild.OnResolveArgs,
): Promise<esbuild.OnResolveResult | undefined> {
if (isNodeModulesResolution(args)) {
if (
BUILTIN_NODE_MODULES.has(args.path) ||
BUILTIN_NODE_MODULES.has("node:" + args.path)
) {
return {
path: args.path,
external: true,
};
}
if (nodeModulesDir !== null) {
return undefined;
} else if (
loaderImpl!.nodeModulesDirForPackage &&
loaderImpl!.packageIdFromNameInPackage
) {
let parentPackageId: string | undefined;
let path = args.importer;
while (true) {
const packageId = packageIdByNodeModules.get(path);
if (packageId) {
parentPackageId = packageId;
break;
}
const pathBefore = path;
path = dirname(path);
if (path === pathBefore) break;
}
if (!parentPackageId) {
throw new Error(
`Could not find package ID for importer: ${args.importer}`,
);
}
if (args.path.startsWith(".")) {
return undefined;
} else {
let packageName: string;
let pathParts: string[];
if (args.path.startsWith("@")) {
const [scope, name, ...rest] = args.path.split("/");
packageName = `${scope}/${name}`;
pathParts = rest;
} else {
const [name, ...rest] = args.path.split("/");
packageName = name;
pathParts = rest;
}
const packageId = loaderImpl!.packageIdFromNameInPackage(
packageName,
parentPackageId,
);
const id = packageId ?? parentPackageId;
const resolveDir = await loaderImpl!.nodeModulesDirForPackage(id);
packageIdByNodeModules.set(resolveDir, id);
const path = [packageName, ...pathParts].join("/");
return await build.resolve(path, {
kind: args.kind,
resolveDir,
importer: args.importer,
});
}
} else {
throw new Error(
`To use "npm:" specifiers, you must specify 'nodeModulesDir: "manual"', or use 'loader: "native"'.`,
);
}
}
const specifier = esbuildResolutionToURL(args);
// Once we have an absolute path, let the loader resolver figure out
// what to do with it.
const res = await loaderImpl!.resolve(specifier);
switch (res.kind) {
case "esm": {
const { specifier } = res;
return urlToEsbuildResolution(specifier);
}
case "npm": {
let resolveDir: string;
if (nodeModulesDir !== null) {
resolveDir = nodeModulesDir;
} else if (loaderImpl!.nodeModulesDirForPackage) {
resolveDir = await loaderImpl!.nodeModulesDirForPackage(
res.packageId,
);
packageIdByNodeModules.set(resolveDir, res.packageId);
} else {
throw new Error(
`To use "npm:" specifiers, you must specify 'nodeModulesDir: "manual"', or use 'loader: "native"'.`,
);
}
const path = `${res.packageName}${res.path ?? ""}`;
return await build.resolve(path, {
kind: args.kind,
resolveDir,
importer: args.importer,
});
}
case "node": {
return {
path: res.path,
external: true,
};
}
}
}
build.onResolve({ filter: /.*/, namespace: "file" }, onResolve);
build.onResolve({ filter: /.*/, namespace: "http" }, onResolve);
build.onResolve({ filter: /.*/, namespace: "https" }, onResolve);
build.onResolve({ filter: /.*/, namespace: "data" }, onResolve);
build.onResolve({ filter: /.*/, namespace: "npm" }, onResolve);
build.onResolve({ filter: /.*/, namespace: "jsr" }, onResolve);
build.onResolve({ filter: /.*/, namespace: "node" }, onResolve);
function onLoad(
args: esbuild.OnLoadArgs,
): Promise<esbuild.OnLoadResult | null | undefined> | undefined {
if (args.namespace === "file" && isInNodeModules(args.path)) {
// inside node_modules, just let esbuild do it's thing
return undefined;
}
const specifier = esbuildResolutionToURL(args);
return loaderImpl!.loadEsm(specifier);
}
// TODO(lucacasonato): once https://github.com/evanw/esbuild/pull/2968 is fixed, remove the catch all "file" handler
build.onLoad({ filter: /.*/, namespace: "file" }, onLoad);
build.onLoad({ filter: /.*/, namespace: "http" }, onLoad);
build.onLoad({ filter: /.*/, namespace: "https" }, onLoad);
build.onLoad({ filter: /.*/, namespace: "data" }, onLoad);
},
};
}

View File

@ -0,0 +1,136 @@
import type * as esbuild from "./esbuild_types.ts";
import { toFileUrl } from "jsr:@std/path@^1.0.6";
import {
findWorkspace,
isNodeModulesResolution,
urlToEsbuildResolution,
} from "./shared.ts";
import type { WasmWorkspaceResolver } from "./wasm/loader.generated.js";
/** Options for the {@link denoResolverPlugin}. */
export interface DenoResolverPluginOptions {
/**
* Specify the path to a deno.json config file to use. This is equivalent to
* the `--config` flag to the Deno executable. This path must be absolute.
*
* If not specified, the plugin will attempt to find the nearest deno.json and
* use that. If the deno.json is part of a workspace, the plugin will
* automatically find the workspace root.
*/
configPath?: string;
/**
* Specify a URL to an import map file to use when resolving import
* specifiers. This is equivalent to the `--import-map` flag to the Deno
* executable. This URL may be remote or a local file URL.
*
* If this option is not specified, the deno.json config file is consulted to
* determine what import map to use, if any.
*/
importMapURL?: string;
}
/**
* The Deno resolver plugin performs relative->absolute specifier resolution
* and import map resolution.
*
* If using the {@link denoLoaderPlugin}, this plugin must be used before the
* loader plugin.
*/
export function denoResolverPlugin(
options: DenoResolverPluginOptions = {},
): esbuild.Plugin {
return {
name: "deno-resolver",
setup(build) {
let resolver: WasmWorkspaceResolver | null = null;
const externalRegexps: RegExp[] = (build.initialOptions.external ?? [])
.map((external) => {
const regexp = new RegExp(
"^" + external.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&").replace(
/\*/g,
".*",
) + "$",
);
return regexp;
});
build.onStart(async function onStart() {
const cwd = build.initialOptions.absWorkingDir ?? Deno.cwd();
const workspace = findWorkspace(
cwd,
build.initialOptions.entryPoints,
options.configPath,
);
try {
const importMapURL: string | undefined = options.importMapURL;
let importMapValue: unknown | undefined;
if (importMapURL !== undefined) {
// If we have an import map URL, fetch it and parse it.
const resp = await fetch(importMapURL);
importMapValue = await resp.json();
}
resolver?.free();
resolver = null;
resolver = workspace.resolver(importMapURL, importMapValue);
} finally {
workspace.free();
}
});
build.onResolve({ filter: /.*/ }, async function onResolve(args) {
// Pass through any node_modules internal resolution.
if (isNodeModulesResolution(args)) {
return undefined;
}
// The first pass resolver performs synchronous resolution. This
// includes relative to absolute specifier resolution and import map
// resolution.
// We have to first determine the referrer URL to use when resolving
// the specifier. This is either the importer URL, or the resolveDir
// URL if the importer is not specified (ie if the specifier is at the
// root).
let referrer: URL;
if (args.importer !== "") {
if (args.namespace === "") {
throw new Error("[assert] namespace is empty");
}
referrer = new URL(`${args.namespace}:${args.importer}`);
} else if (args.resolveDir !== "") {
referrer = new URL(`${toFileUrl(args.resolveDir).href}/`);
} else {
return undefined;
}
for (const externalRegexp of externalRegexps) {
if (externalRegexp.test(args.path)) {
return {
path: args.path,
external: true,
};
}
}
// We can then resolve the specifier relative to the referrer URL, using
// the workspace resolver.
const resolved = new URL(
resolver!.resolve(args.path, referrer.href),
);
// Now pass the resolved specifier back into the resolver, for a second
// pass. Now plugins can perform any resolution they want on the fully
// resolved specifier.
const { path, namespace } = urlToEsbuildResolution(resolved);
const res = await build.resolve(path, {
namespace,
kind: args.kind,
});
return res;
});
},
};
}

View File

@ -0,0 +1,387 @@
import { dirname, extname, fromFileUrl, SEPARATOR, toFileUrl } from "jsr:@std/path@^1.0.6";
import type { MediaType } from "./deno.ts";
import type * as esbuild from "./esbuild_types.ts";
import { instantiate, WasmWorkspace } from "./wasm/loader.generated.js";
import type { BuildOptions } from "./esbuild_types.ts";
export interface Loader {
resolve(specifier: URL): Promise<LoaderResolution>;
loadEsm(specifier: URL): Promise<esbuild.OnLoadResult | undefined>;
packageIdFromNameInPackage?(
name: string,
parentPackageId: string,
): string | null;
nodeModulesDirForPackage?(npmPackageId?: string): Promise<string>;
[Symbol.dispose]?(): void;
}
export function findWorkspace(
cwd: string,
entryPoints: BuildOptions["entryPoints"],
configPath: string | undefined,
): WasmWorkspace {
const cwdFileUrl = toFileUrl(cwd);
if (!cwdFileUrl.pathname.endsWith("/")) {
cwdFileUrl.pathname += "/";
}
let entrypoints: Array<string>;
let isConfigFile = false;
if (configPath !== undefined) {
entrypoints = [configPath];
isConfigFile = true;
} else if (Array.isArray(entryPoints)) {
entrypoints = entryPoints.flatMap(
(entrypoint) => {
let specifier: string;
if (typeof entrypoint === "string") {
specifier = entrypoint;
} else {
specifier = entrypoint.in;
}
const url = new URL(specifier, cwdFileUrl.href);
if (url.protocol === "file:") {
return [dirname(fromFileUrl(url.href))];
} else {
return [];
}
},
);
} else if (typeof entryPoints === "object") {
entrypoints = Object.values(entryPoints).flatMap(
(entrypoint) => {
const url = new URL(entrypoint, cwdFileUrl.href);
if (url.protocol === "file:") {
return [dirname(fromFileUrl(url.href))];
} else {
return [];
}
},
);
} else {
entrypoints = [];
}
if (entrypoints.length === 0) {
entrypoints = [cwd];
}
instantiate();
return WasmWorkspace.discover(entrypoints, isConfigFile);
}
export type LoaderResolution =
| LoaderResolutionEsm
| LoaderResolutionNpm
| LoaderResolutionNode;
export interface LoaderResolutionEsm {
kind: "esm";
specifier: URL;
}
export interface LoaderResolutionNpm {
kind: "npm";
packageId: string;
packageName: string;
path: string;
}
export interface LoaderResolutionNode {
kind: "node";
path: string;
}
export function mediaTypeToLoader(mediaType: MediaType): esbuild.Loader | null {
switch (mediaType) {
case "JavaScript":
case "Mjs":
return "js";
case "JSX":
return "jsx";
case "TypeScript":
case "Mts":
return "ts";
case "TSX":
return "tsx";
case "Json":
return "json";
default:
return null;
}
}
/** Esbuild's representation of a module specifier. */
export interface EsbuildResolution {
/** The namespace, like `file`, `https`, or `npm`. */
namespace: string;
/** The path. When the namespace is `file`, this is a file path. Otherwise
* this is everything in a URL with the namespace as the scheme, after the
* `:` of the scheme. */
path: string;
}
/**
* Turn a URL into an {@link EsbuildResolution} by splitting the URL into a
* namespace and path.
*
* For file URLs, the path returned is a file path not a URL path representing a
* file.
*/
export function urlToEsbuildResolution(url: URL): EsbuildResolution {
if (url.protocol === "file:") {
return { path: fromFileUrl(url), namespace: "file" };
}
const namespace = url.protocol.slice(0, -1);
const path = url.href.slice(namespace.length + 1);
return { path, namespace };
}
/**
* Turn an {@link EsbuildResolution} into a URL by joining the namespace and
* path into a URL string.
*
* For file URLs, the path is interpreted as a file path not as a URL path
* representing a file.
*/
export function esbuildResolutionToURL(specifier: EsbuildResolution): URL {
if (specifier.namespace === "file") {
return toFileUrl(specifier.path);
}
return new URL(`${specifier.namespace}:${specifier.path}`);
}
export function mapContentType(
specifier: URL,
contentType: string | null,
): MediaType {
if (contentType !== null) {
const contentTypes = contentType.split(";");
const mediaType = contentTypes[0].toLowerCase();
switch (mediaType) {
case "application/typescript":
case "text/typescript":
case "video/vnd.dlna.mpeg-tts":
case "video/mp2t":
case "application/x-typescript":
return mapJsLikeExtension(specifier, "TypeScript");
case "application/javascript":
case "text/javascript":
case "application/ecmascript":
case "text/ecmascript":
case "application/x-javascript":
case "application/node":
return mapJsLikeExtension(specifier, "JavaScript");
case "text/jsx":
return "JSX";
case "text/tsx":
return "TSX";
case "application/json":
case "text/json":
return "Json";
case "application/wasm":
return "Wasm";
case "text/plain":
case "application/octet-stream":
return mediaTypeFromSpecifier(specifier);
default:
return "Unknown";
}
} else {
return mediaTypeFromSpecifier(specifier);
}
}
function mapJsLikeExtension(
specifier: URL,
defaultType: MediaType,
): MediaType {
const path = specifier.pathname;
switch (extname(path)) {
case ".jsx":
return "JSX";
case ".mjs":
return "Mjs";
case ".cjs":
return "Cjs";
case ".tsx":
return "TSX";
case ".ts":
if (path.endsWith(".d.ts")) {
return "Dts";
} else {
return defaultType;
}
case ".mts": {
if (path.endsWith(".d.mts")) {
return "Dmts";
} else {
return defaultType == "JavaScript" ? "Mjs" : "Mts";
}
}
case ".cts": {
if (path.endsWith(".d.cts")) {
return "Dcts";
} else {
return defaultType == "JavaScript" ? "Cjs" : "Cts";
}
}
default:
return defaultType;
}
}
export function mediaTypeFromSpecifier(specifier: URL): MediaType {
const path = specifier.pathname;
switch (extname(path)) {
case "":
if (path.endsWith("/.tsbuildinfo")) {
return "TsBuildInfo";
} else {
return "Unknown";
}
case ".ts":
if (path.endsWith(".d.ts")) {
return "Dts";
} else {
return "TypeScript";
}
case ".mts":
if (path.endsWith(".d.mts")) {
return "Dmts";
} else {
return "Mts";
}
case ".cts":
if (path.endsWith(".d.cts")) {
return "Dcts";
} else {
return "Cts";
}
case ".tsx":
return "TSX";
case ".js":
return "JavaScript";
case ".jsx":
return "JSX";
case ".mjs":
return "Mjs";
case ".cjs":
return "Cjs";
case ".json":
return "Json";
case ".wasm":
return "Wasm";
case ".tsbuildinfo":
return "TsBuildInfo";
case ".map":
return "SourceMap";
default:
return "Unknown";
}
}
export interface NpmSpecifier {
name: string;
version: string | null;
path: string | null;
}
export function parseNpmSpecifier(specifier: URL): NpmSpecifier {
if (specifier.protocol !== "npm:") throw new Error("Invalid npm specifier");
const path = specifier.pathname;
const startIndex = path[0] === "/" ? 1 : 0;
let pathStartIndex;
let versionStartIndex;
if (path[startIndex] === "@") {
const firstSlash = path.indexOf("/", startIndex);
if (firstSlash === -1) {
throw new Error(`Invalid npm specifier: ${specifier}`);
}
pathStartIndex = path.indexOf("/", firstSlash + 1);
versionStartIndex = path.indexOf("@", firstSlash + 1);
} else {
pathStartIndex = path.indexOf("/", startIndex);
versionStartIndex = path.indexOf("@", startIndex);
}
if (pathStartIndex === -1) pathStartIndex = path.length;
if (versionStartIndex === -1) versionStartIndex = path.length;
if (versionStartIndex > pathStartIndex) {
versionStartIndex = pathStartIndex;
}
if (startIndex === versionStartIndex) {
throw new Error(`Invalid npm specifier: ${specifier}`);
}
return {
name: path.slice(startIndex, versionStartIndex),
version: versionStartIndex === pathStartIndex
? null
: path.slice(versionStartIndex + 1, pathStartIndex),
path: pathStartIndex === path.length ? null : path.slice(pathStartIndex),
};
}
export interface JsrSpecifier {
name: string;
version: string | null;
path: string | null;
}
export function parseJsrSpecifier(specifier: URL): JsrSpecifier {
if (specifier.protocol !== "jsr:") throw new Error("Invalid jsr specifier");
const path = specifier.pathname;
const startIndex = path[0] === "/" ? 1 : 0;
if (path[startIndex] !== "@") {
throw new Error(`Invalid jsr specifier: ${specifier}`);
}
const firstSlash = path.indexOf("/", startIndex);
if (firstSlash === -1) {
throw new Error(`Invalid jsr specifier: ${specifier}`);
}
let pathStartIndex = path.indexOf("/", firstSlash + 1);
let versionStartIndex = path.indexOf("@", firstSlash + 1);
if (pathStartIndex === -1) pathStartIndex = path.length;
if (versionStartIndex === -1) versionStartIndex = path.length;
if (versionStartIndex > pathStartIndex) {
versionStartIndex = pathStartIndex;
}
if (startIndex === versionStartIndex) {
throw new Error(`Invalid jsr specifier: ${specifier}`);
}
return {
name: path.slice(startIndex, versionStartIndex),
version: versionStartIndex === pathStartIndex
? null
: path.slice(versionStartIndex + 1, pathStartIndex),
path: pathStartIndex === path.length ? null : path.slice(pathStartIndex),
};
}
const SLASH_NODE_MODULES_SLASH = `${SEPARATOR}node_modules${SEPARATOR}`;
const SLASH_NODE_MODULES = `${SEPARATOR}node_modules`;
export function isInNodeModules(path: string): boolean {
return path.includes(SLASH_NODE_MODULES_SLASH) ||
path.endsWith(SLASH_NODE_MODULES);
}
export function isNodeModulesResolution(args: esbuild.OnResolveArgs) {
return (
(args.namespace === "" || args.namespace === "file") &&
(isInNodeModules(args.resolveDir) || isInNodeModules(args.path) ||
isInNodeModules(args.importer))
);
}

View File

@ -0,0 +1,77 @@
// deno-lint-ignore-file
// deno-fmt-ignore-file
export interface InstantiateResult {
instance: WebAssembly.Instance;
exports: {
WasmLockfile : typeof WasmLockfile ;
WasmWorkspace : typeof WasmWorkspace ;
WasmWorkspaceResolver : typeof WasmWorkspaceResolver
};
}
/** Gets if the Wasm module has been instantiated. */
export function isInstantiated(): boolean;
/** Instantiates an instance of the Wasm module returning its functions.
* @remarks It is safe to call this multiple times and once successfully
* loaded it will always return a reference to the same object. */
export function instantiate(): InstantiateResult["exports"];
/** Instantiates an instance of the Wasm module along with its exports.
* @remarks It is safe to call this multiple times and once successfully
* loaded it will always return a reference to the same object. */
export function instantiateWithInstance(): InstantiateResult;
/**
*/
export class WasmLockfile {
free(): void;
/**
* @param {string} file_path
* @param {string} content
*/
constructor(file_path: string, content: string);
/**
* @param {string} specifier
* @returns {string | undefined}
*/
package_version(specifier: string): string | undefined;
}
/**
*/
export class WasmWorkspace {
free(): void;
/**
* @param {(string)[]} entrypoints
* @param {boolean} is_config_file
* @returns {WasmWorkspace}
*/
static discover(entrypoints: (string)[], is_config_file: boolean): WasmWorkspace;
/**
* @returns {string | undefined}
*/
lock_path(): string | undefined;
/**
* @returns {string}
*/
node_modules_dir(): string;
/**
* @param {string | undefined} import_map_url
* @param {any} import_map_value
* @returns {WasmWorkspaceResolver}
*/
resolver(import_map_url: string | undefined, import_map_value: any): WasmWorkspaceResolver;
}
/**
*/
export class WasmWorkspaceResolver {
free(): void;
/**
* @param {string} specifier
* @param {string} referrer
* @returns {string}
*/
resolve(specifier: string, referrer: string): string;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
/** @param {string} path */
export function stat_sync(path) {
const stat = Deno.statSync(path);
return {
is_file: stat.isFile,
is_directory: stat.isDirectory,
is_symlink: stat.isSymlink,
};
}
/** @param {string} path */
export function read_to_string_lossy(path) {
return Deno.readTextFileSync(path);
}
/** @param {string} path */
export function read_dir(path) {
return Iterator.from(Deno.readDirSync(path)).map((entry) => {
return {
name: entry.name,
is_file: entry.isFile,
is_directory: entry.isDirectory,
is_symlink: entry.isSymlink,
};
}).toArray();
}

View File

@ -0,0 +1,646 @@
{
"manifest": {
"/src/wasm/fs.js": {
"size": 615,
"checksum": "sha256-63bedcea54fa2494f106586325ada9dcd5eccf0dcba82caa2fc4a54d9419a14b"
},
"/src/wasm/src/lib.rs": {
"size": 7914,
"checksum": "sha256-38e1590caf09b74751963aed2e880faa2bb397ee8ed4b09b5ae525834c1c5ba1"
},
"/src/wasm/loader.generated.js": {
"size": 2343392,
"checksum": "sha256-c766177855693201055b3a762929a16b85fd5d5dd8c90d530a928d3e03a1551c"
},
"/.vscode/settings.json": {
"size": 377,
"checksum": "sha256-fd130ffd6c737a7e713d5b07a628a7551da177330ff98881b7ce51d4075be47d"
},
"/examples/custom_scheme_plugin.ts": {
"size": 1081,
"checksum": "sha256-ff3a9ecb3d9cd4367280e6cf8f0782a1ec5c86ad5b8a521eade12f18accc5930"
},
"/README.md": {
"size": 4470,
"checksum": "sha256-31e895507af1a69a840d5ab0655e11aa8ec2c16777e5fa46d63fb6675c73a8eb"
},
"/LICENSE": {
"size": 1070,
"checksum": "sha256-75b6120f639f83c46bfaec3a6c97516e015b5ab18053c39bfbaf72f1d76e6c87"
},
"/src/shared.ts": {
"size": 10396,
"checksum": "sha256-3237112e33ca33db036cb5d7d5c1064c8bfe65bb3bcbbfb328ecff21629fcd2f"
},
"/src/wasm/loader.generated.d.ts": {
"size": 2005,
"checksum": "sha256-5a81527107d6b8fe0ec24477314d3c68ea575d119aa40823cec4ceacd54aede7"
},
"/src/wasm/snippets/loader-ace679fc3fd6566c/fs.js": {
"size": 615,
"checksum": "sha256-63bedcea54fa2494f106586325ada9dcd5eccf0dcba82caa2fc4a54d9419a14b"
},
"/src/plugin_deno_resolver.ts": {
"size": 4631,
"checksum": "sha256-c0d7d4e4e1cf69313cdf904430824852ff309e628759c3d871b421b40037aa82"
},
"/src/loader_portable.ts": {
"size": 7189,
"checksum": "sha256-c600a1e83af419ba72424318d9bb64691fbe8a0155f6577f1aa9ac118babe5ac"
},
"/.github/workflows/ci.yml": {
"size": 1257,
"checksum": "sha256-c8bb36bf8002583a645563788905e6e6f2e2a8cfbd2babe9257dc017319dcd92"
},
"/src/shared_test.ts": {
"size": 3926,
"checksum": "sha256-b2beb7e9394b1a21d64b57d807d5aa83e5e86ee4eeac755395d6d7ec32bc360d"
},
"/Cargo.lock": {
"size": 17503,
"checksum": "sha256-3e2fa77a49a3ac5d2e23a025a64005745ce54c15acb9943b958129d1f146837d"
},
"/src/esbuild_types.ts": {
"size": 3597,
"checksum": "sha256-7ecffd69706f9fbe60dcf75a07b94b835b269aedaab270307f2373c246e5fa14"
},
"/mod.ts": {
"size": 3240,
"checksum": "sha256-8d0e10d3bd5046246761ae7de21f8087a3d43498cc04aaa34d0b9502956a1c7e"
},
"/.vscode/launch.json": {
"size": 381,
"checksum": "sha256-e282e7a86214382bb864dc270b55ef5727bba745506bcc2affa11de235f041cf"
},
"/examples/bundle.ts": {
"size": 307,
"checksum": "sha256-df6f7046ef78d8441bb5d77d29735e465900fc5e7741ed84324f42c71323515f"
},
"/src/deno.ts": {
"size": 7321,
"checksum": "sha256-11922034c4f35eb98f0787adf5556d07ffb20ba2e743101370a05ee1c9fcc386"
},
"/rust-toolchain.toml": {
"size": 87,
"checksum": "sha256-1b36c3d65ba81d090a75fefe18229bf1fd3d4c643973d15dadb271c7f3c38129"
},
"/deno.json": {
"size": 973,
"checksum": "sha256-e885193f06c915db9912ec3be459d2d9e79207beaf3c3651108c403dd7907879"
},
"/Cargo.toml": {
"size": 119,
"checksum": "sha256-1c3994d06f7d0530b84ae3bed3d5ac92d2d75f8c2a034ec2e67ecb1020a8d8fa"
},
"/src/loader_native.ts": {
"size": 7117,
"checksum": "sha256-639560af228e34f165ef69bf4cc3b548e75c5ff4d93a587704a3bdf894e09d5e"
},
"/src/wasm/Cargo.toml": {
"size": 337,
"checksum": "sha256-af62a342f0b8d7c002ad57fbcf6edc2ef110ae19c49b7881e166b3a5eaede35b"
},
"/.rustfmt.toml": {
"size": 47,
"checksum": "sha256-5c9b47730885a7e03b6afb73eed16e6ccc07ff666e82974559bfc6a7feb61031"
},
"/src/plugin_deno_loader.ts": {
"size": 14302,
"checksum": "sha256-df8823e5b3dbe554d8a7d2dafc9af4c564c654dd07ebbb528bb00ce3f38f38f4"
}
},
"moduleGraph2": {
"/src/plugin_deno_loader.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./esbuild_types.ts",
"specifierRange": [
[
0,
30
],
[
0,
50
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/path@^1.0.6",
"specifierRange": [
[
1,
30
],
[
1,
52
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./loader_native.ts",
"specifierRange": [
[
2,
29
],
[
2,
49
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./loader_portable.ts",
"specifierRange": [
[
3,
31
],
[
3,
53
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./shared.ts",
"specifierRange": [
[
4,
47
],
[
4,
60
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./shared.ts",
"specifierRange": [
[
10,
7
],
[
10,
20
]
]
}
]
},
"/src/loader_native.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./esbuild_types.ts",
"specifierRange": [
[
0,
30
],
[
0,
50
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/path@^1.0.6",
"specifierRange": [
[
1,
43
],
[
1,
65
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:/@std/encoding@^1.0.5/base32",
"specifierRange": [
[
2,
29
],
[
2,
63
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/bytes@^1.0.2",
"specifierRange": [
[
3,
34
],
[
3,
57
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./deno.ts",
"specifierRange": [
[
4,
22
],
[
4,
33
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./deno.ts",
"specifierRange": [
[
5,
46
],
[
5,
57
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./shared.ts",
"specifierRange": [
[
13,
7
],
[
13,
20
]
]
}
]
},
"/src/wasm/loader.generated.d.ts": {},
"/src/wasm/loader.generated.js": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "./snippets/loader-ace679fc3fd6566c/fs.js",
"specifierRange": [
[
13,
7
],
[
13,
49
]
]
}
],
"tsReferences": [
{
"type": "types",
"text": "./loader.generated.d.ts",
"range": [
[
4,
21
],
[
4,
46
]
]
}
]
},
"/mod.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./src/esbuild_types.ts",
"specifierRange": [
[
0,
30
],
[
0,
54
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./src/plugin_deno_resolver.ts",
"specifierRange": [
[
5,
7
],
[
5,
38
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./src/plugin_deno_loader.ts",
"specifierRange": [
[
12,
7
],
[
12,
36
]
]
},
{
"type": "static",
"kind": "export",
"specifier": "./src/shared.ts",
"specifierRange": [
[
19,
7
],
[
19,
24
]
]
}
]
},
"/src/plugin_deno_resolver.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./esbuild_types.ts",
"specifierRange": [
[
0,
30
],
[
0,
50
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/path@^1.0.6",
"specifierRange": [
[
1,
26
],
[
1,
48
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./shared.ts",
"specifierRange": [
[
6,
7
],
[
6,
20
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./wasm/loader.generated.js",
"specifierRange": [
[
7,
43
],
[
7,
71
]
]
}
]
},
"/src/deno.ts": {},
"/src/shared.ts": {
"dependencies": [
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/path@^1.0.6",
"specifierRange": [
[
0,
68
],
[
0,
90
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./deno.ts",
"specifierRange": [
[
1,
31
],
[
1,
42
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./esbuild_types.ts",
"specifierRange": [
[
2,
30
],
[
2,
50
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./wasm/loader.generated.js",
"specifierRange": [
[
3,
43
],
[
3,
71
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./esbuild_types.ts",
"specifierRange": [
[
4,
34
],
[
4,
54
]
]
}
]
},
"/src/loader_portable.ts": {
"dependencies": [
{
"type": "static",
"kind": "importType",
"specifier": "./esbuild_types.ts",
"specifierRange": [
[
0,
30
],
[
0,
50
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "jsr:@std/path@^1.0.6",
"specifierRange": [
[
1,
28
],
[
1,
50
]
]
},
{
"type": "static",
"kind": "importType",
"specifier": "./deno.ts",
"specifierRange": [
[
2,
27
],
[
2,
38
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./shared.ts",
"specifierRange": [
[
10,
7
],
[
10,
20
]
]
},
{
"type": "static",
"kind": "import",
"specifier": "./wasm/loader.generated.js",
"specifierRange": [
[
11,
47
],
[
11,
75
]
]
}
]
},
"/src/esbuild_types.ts": {},
"/src/wasm/snippets/loader-ace679fc3fd6566c/fs.js": {}
},
"exports": {
".": "./mod.ts",
"./esbuild_types": "./src/esbuild_types.ts"
}
}

13
vendor/jsr.io/@luca/esbuild-deno-loader/meta.json generated vendored Normal file
View File

@ -0,0 +1,13 @@
{
"scope": "luca",
"name": "esbuild-deno-loader",
"latest": "0.11.1",
"versions": {
"0.10.2": {},
"0.11.0-rc.1": {},
"0.11.1": {},
"0.9.0": {},
"0.11.0": {},
"0.10.3": {}
}
}

19
vendor/jsr.io/@std/assert/0.217.0/assert.ts generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion, error will be thrown if `expr` does not have truthy value.
*
* @example
* ```ts
* import { assert } from "@std/assert/assert";
*
* assert("hello".includes("ello")); // Doesn't throw
* assert("hello".includes("world")); // Throws
* ```
*/
export function assert(expr: unknown, msg = ""): asserts expr {
if (!expr) {
throw new AssertionError(msg);
}
}

19
vendor/jsr.io/@std/assert/0.217.0/assertion_error.ts generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
/**
* Error thrown when an assertion fails.
*
* @example
* ```ts
* import { AssertionError } from "@std/assert/assertion_error";
*
* throw new AssertionError("Assertion failed");
* ```
*/
export class AssertionError extends Error {
/** Constructs a new instance. */
constructor(message: string) {
super(message);
this.name = "AssertionError";
}
}

2669
vendor/jsr.io/@std/assert/0.217.0_meta.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

19
vendor/jsr.io/@std/assert/0.221.0/assert.ts generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion, error will be thrown if `expr` does not have truthy value.
*
* @example
* ```ts
* import { assert } from "@std/assert/assert";
*
* assert("hello".includes("ello")); // Doesn't throw
* assert("hello".includes("world")); // Throws
* ```
*/
export function assert(expr: unknown, msg = ""): asserts expr {
if (!expr) {
throw new AssertionError(msg);
}
}

19
vendor/jsr.io/@std/assert/0.221.0/assertion_error.ts generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
/**
* Error thrown when an assertion fails.
*
* @example
* ```ts
* import { AssertionError } from "@std/assert/assertion-error";
*
* throw new AssertionError("Assertion failed");
* ```
*/
export class AssertionError extends Error {
/** Constructs a new instance. */
constructor(message: string) {
super(message);
this.name = "AssertionError";
}
}

1498
vendor/jsr.io/@std/assert/0.221.0_meta.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

55
vendor/jsr.io/@std/assert/1.0.10/almost_equals.ts generated vendored Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` and `expected` are almost equal numbers
* through a given tolerance. It can be used to take into account IEEE-754
* double-precision floating-point representation limitations. If the values
* are not almost equal then throw.
*
* The default tolerance is one hundred thousandth of a percent of the
* expected value.
*
* @example Usage
* ```ts ignore
* import { assertAlmostEquals } from "@std/assert";
*
* assertAlmostEquals(0.01, 0.02); // Throws
* assertAlmostEquals(1e-8, 1e-9); // Throws
* assertAlmostEquals(1.000000001e-8, 1.000000002e-8); // Doesn't throw
* assertAlmostEquals(0.01, 0.02, 0.1); // Doesn't throw
* assertAlmostEquals(0.1 + 0.2, 0.3, 1e-16); // Doesn't throw
* assertAlmostEquals(0.1 + 0.2, 0.3, 1e-17); // Throws
* ```
*
* @param actual The actual value to compare.
* @param expected The expected value to compare.
* @param tolerance The tolerance to consider the values almost equal. The
* default is one hundred thousandth of a percent of the expected value.
* @param msg The optional message to include in the error.
*/
export function assertAlmostEquals(
actual: number,
expected: number,
tolerance?: number,
msg?: string,
) {
if (Object.is(actual, expected)) {
return;
}
const delta = Math.abs(expected - actual);
if (tolerance === undefined) {
tolerance = isFinite(expected) ? Math.abs(expected * 1e-7) : 1e-7;
}
if (delta <= tolerance) {
return;
}
const msgSuffix = msg ? `: ${msg}` : ".";
const f = (n: number) => Number.isInteger(n) ? n : n.toExponential();
throw new AssertionError(
`Expected actual: "${f(actual)}" to be close to "${f(expected)}": \
delta "${f(delta)}" is greater than "${f(tolerance)}"${msgSuffix}`,
);
}

57
vendor/jsr.io/@std/assert/1.0.10/array_includes.ts generated vendored Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { equal } from "./equal.ts";
import { format } from "jsr:@std/internal@^1.0.5/format";
import { AssertionError } from "./assertion_error.ts";
/** An array-like object (`Array`, `Uint8Array`, `NodeList`, etc.) that is not a string */
export type ArrayLikeArg<T> = ArrayLike<T> & object;
/**
* Make an assertion that `actual` includes the `expected` values. If not then
* an error will be thrown.
*
* Type parameter can be specified to ensure values under comparison have the
* same type.
*
* @example Usage
* ```ts ignore
* import { assertArrayIncludes } from "@std/assert";
*
* assertArrayIncludes([1, 2], [2]); // Doesn't throw
* assertArrayIncludes([1, 2], [3]); // Throws
* ```
*
* @typeParam T The type of the elements in the array to compare.
* @param actual The array-like object to check for.
* @param expected The array-like object to check for.
* @param msg The optional message to display if the assertion fails.
*/
export function assertArrayIncludes<T>(
actual: ArrayLikeArg<T>,
expected: ArrayLikeArg<T>,
msg?: string,
) {
const missing: unknown[] = [];
for (let i = 0; i < expected.length; i++) {
let found = false;
for (let j = 0; j < actual.length; j++) {
if (equal(expected[i], actual[j])) {
found = true;
break;
}
}
if (!found) {
missing.push(expected[i]);
}
}
if (missing.length === 0) {
return;
}
const msgSuffix = msg ? `: ${msg}` : ".";
msg = `Expected actual: "${format(actual)}" to include: "${
format(expected)
}"${msgSuffix}\nmissing: ${format(missing)}`;
throw new AssertionError(msg);
}

23
vendor/jsr.io/@std/assert/1.0.10/assert.ts generated vendored Normal file
View File

@ -0,0 +1,23 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion, error will be thrown if `expr` does not have truthy value.
*
* @example Usage
* ```ts ignore
* import { assert } from "@std/assert";
*
* assert("hello".includes("ello")); // Doesn't throw
* assert("hello".includes("world")); // Throws
* ```
*
* @param expr The expression to test.
* @param msg The optional message to display if the assertion fails.
*/
export function assert(expr: unknown, msg = ""): asserts expr {
if (!expr) {
throw new AssertionError(msg);
}
}

31
vendor/jsr.io/@std/assert/1.0.10/assertion_error.ts generated vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/**
* Error thrown when an assertion fails.
*
* @example Usage
* ```ts ignore
* import { AssertionError } from "@std/assert";
*
* try {
* throw new AssertionError("foo", { cause: "bar" });
* } catch (error) {
* if (error instanceof AssertionError) {
* error.message === "foo"; // true
* error.cause === "bar"; // true
* }
* }
* ```
*/
export class AssertionError extends Error {
/** Constructs a new instance.
*
* @param message The error message.
* @param options Additional options. This argument is still unstable. It may change in the future release.
*/
constructor(message: string, options?: ErrorOptions) {
super(message, options);
this.name = "AssertionError";
}
}

208
vendor/jsr.io/@std/assert/1.0.10/equal.ts generated vendored Normal file
View File

@ -0,0 +1,208 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
type KeyedCollection = Set<unknown> | Map<unknown, unknown>;
function isKeyedCollection(x: unknown): x is KeyedCollection {
return x instanceof Set || x instanceof Map;
}
function prototypesEqual(a: object, b: object) {
const pa = Object.getPrototypeOf(a);
const pb = Object.getPrototypeOf(b);
return pa === pb ||
pa === Object.prototype && pb === null ||
pa === null && pb === Object.prototype;
}
function isBasicObjectOrArray(obj: object) {
const proto = Object.getPrototypeOf(obj);
return proto === null || proto === Object.prototype ||
proto === Array.prototype;
}
// Slightly faster than Reflect.ownKeys in V8 as of 12.9.202.13-rusty (2024-10-28)
function ownKeys(obj: object) {
return [
...Object.getOwnPropertyNames(obj),
...Object.getOwnPropertySymbols(obj),
];
}
function getKeysDeep(obj: object) {
const keys = new Set<string | symbol>();
while (obj !== Object.prototype && obj !== Array.prototype && obj != null) {
for (const key of ownKeys(obj)) {
keys.add(key);
}
obj = Object.getPrototypeOf(obj);
}
return keys;
}
// deno-lint-ignore no-explicit-any
const Temporal: any = (globalThis as any).Temporal ??
new Proxy({}, { get: () => {} });
/** A non-exhaustive list of prototypes that can be accurately fast-path compared with `String(instance)` */
const stringComparablePrototypes = new Set<unknown>(
[
Intl.Locale,
RegExp,
Temporal.Duration,
Temporal.Instant,
Temporal.PlainDate,
Temporal.PlainDateTime,
Temporal.PlainTime,
Temporal.PlainYearMonth,
Temporal.PlainMonthDay,
Temporal.ZonedDateTime,
URL,
URLSearchParams,
].filter((x) => x != null).map((x) => x.prototype),
);
function isPrimitive(x: unknown) {
return typeof x === "string" ||
typeof x === "number" ||
typeof x === "boolean" ||
typeof x === "bigint" ||
typeof x === "symbol" ||
x == null;
}
type TypedArray = Pick<Uint8Array | BigUint64Array, "length" | number>;
const TypedArray = Object.getPrototypeOf(Uint8Array);
function compareTypedArrays(a: TypedArray, b: TypedArray) {
if (a.length !== b.length) return false;
for (let i = 0; i < b.length; i++) {
if (!sameValueZero(a[i], b[i])) return false;
}
return true;
}
/** Check both strict equality (`0 == -0`) and `Object.is` (`NaN == NaN`) */
function sameValueZero(a: unknown, b: unknown) {
return a === b || Object.is(a, b);
}
/**
* Deep equality comparison used in assertions.
*
* @param a The actual value
* @param b The expected value
* @returns `true` if the values are deeply equal, `false` otherwise
*
* @example Usage
* ```ts
* import { equal } from "@std/assert/equal";
*
* equal({ foo: "bar" }, { foo: "bar" }); // Returns `true`
* equal({ foo: "bar" }, { foo: "baz" }); // Returns `false`
* ```
*/
export function equal(a: unknown, b: unknown): boolean {
const seen = new Map<unknown, unknown>();
return (function compare(a: unknown, b: unknown): boolean {
if (sameValueZero(a, b)) return true;
if (isPrimitive(a) || isPrimitive(b)) return false;
if (a instanceof Date && b instanceof Date) {
return Object.is(a.getTime(), b.getTime());
}
if (a && typeof a === "object" && b && typeof b === "object") {
if (!prototypesEqual(a, b)) {
return false;
}
if (a instanceof TypedArray) {
return compareTypedArrays(a as TypedArray, b as TypedArray);
}
if (a instanceof WeakMap) {
throw new TypeError("cannot compare WeakMap instances");
}
if (a instanceof WeakSet) {
throw new TypeError("cannot compare WeakSet instances");
}
if (a instanceof WeakRef) {
return compare(a.deref(), (b as WeakRef<WeakKey>).deref());
}
if (seen.get(a) === b) {
return true;
}
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
seen.set(a, b);
if (isKeyedCollection(a) && isKeyedCollection(b)) {
if (a.size !== b.size) {
return false;
}
const aKeys = [...a.keys()];
const primitiveKeysFastPath = aKeys.every(isPrimitive);
if (primitiveKeysFastPath) {
if (a instanceof Set) {
return a.symmetricDifference(b).size === 0;
}
for (const key of aKeys) {
if (
!b.has(key) ||
!compare(a.get(key), (b as Map<unknown, unknown>).get(key))
) {
return false;
}
}
return true;
}
let unmatchedEntries = a.size;
for (const [aKey, aValue] of a.entries()) {
for (const [bKey, bValue] of b.entries()) {
/* Given that Map keys can be references, we need
* to ensure that they are also deeply equal */
if (!compare(aKey, bKey)) continue;
if (
(aKey === aValue && bKey === bValue) ||
(compare(aValue, bValue))
) {
unmatchedEntries--;
break;
}
}
}
return unmatchedEntries === 0;
}
let keys: Iterable<string | symbol>;
if (isBasicObjectOrArray(a)) {
// fast path
keys = ownKeys({ ...a, ...b });
} else if (stringComparablePrototypes.has(Object.getPrototypeOf(a))) {
// medium path
return String(a) === String(b);
} else {
// slow path
keys = getKeysDeep(a).union(getKeysDeep(b));
}
for (const key of keys) {
type Key = keyof typeof a;
if (!compare(a[key as Key], b[key as Key])) {
return false;
}
if (((key in a) && (!(key in b))) || ((key in b) && (!(key in a)))) {
return false;
}
}
return true;
}
return false;
})(a, b);
}

65
vendor/jsr.io/@std/assert/1.0.10/equals.ts generated vendored Normal file
View File

@ -0,0 +1,65 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { equal } from "./equal.ts";
import { buildMessage } from "jsr:@std/internal@^1.0.5/build-message";
import { diff } from "jsr:@std/internal@^1.0.5/diff";
import { diffStr } from "jsr:@std/internal@^1.0.5/diff-str";
import { format } from "jsr:@std/internal@^1.0.5/format";
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` and `expected` are equal, deeply. If not
* deeply equal, then throw.
*
* Type parameter can be specified to ensure values under comparison have the
* same type.
*
* Note: When comparing `Blob` objects, you should first convert them to
* `Uint8Array` using the `Blob.bytes()` method and then compare their
* contents.
*
* @example Usage
* ```ts ignore
* import { assertEquals } from "@std/assert";
*
* assertEquals("world", "world"); // Doesn't throw
* assertEquals("hello", "world"); // Throws
* ```
* @example Compare `Blob` objects
* ```ts ignore
* import { assertEquals } from "@std/assert";
*
* const bytes1 = await new Blob(["foo"]).bytes();
* const bytes2 = await new Blob(["foo"]).bytes();
*
* assertEquals(bytes1, bytes2);
* ```
*
* @typeParam T The type of the values to compare. This is usually inferred.
* @param actual The actual value to compare.
* @param expected The expected value to compare.
* @param msg The optional message to display if the assertion fails.
*/
export function assertEquals<T>(
actual: T,
expected: T,
msg?: string,
) {
if (equal(actual, expected)) {
return;
}
const msgSuffix = msg ? `: ${msg}` : ".";
let message = `Values are not equal${msgSuffix}`;
const actualString = format(actual);
const expectedString = format(expected);
const stringDiff = (typeof actual === "string") &&
(typeof expected === "string");
const diffResult = stringDiff
? diffStr(actual as string, expected as string)
: diff(actualString.split("\n"), expectedString.split("\n"));
const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n");
message = `${message}\n${diffMsg}`;
throw new AssertionError(message);
}

31
vendor/jsr.io/@std/assert/1.0.10/exists.ts generated vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that actual is not null or undefined.
* If not then throw.
*
* @example Usage
* ```ts ignore
* import { assertExists } from "@std/assert";
*
* assertExists("something"); // Doesn't throw
* assertExists(undefined); // Throws
* ```
*
* @typeParam T The type of the actual value.
* @param actual The actual value to check.
* @param msg The optional message to include in the error if the assertion fails.
*/
export function assertExists<T>(
actual: T,
msg?: string,
): asserts actual is NonNullable<T> {
if (actual === undefined || actual === null) {
const msgSuffix = msg ? `: ${msg}` : ".";
msg =
`Expected actual: "${actual}" to not be null or undefined${msgSuffix}`;
throw new AssertionError(msg);
}
}

21
vendor/jsr.io/@std/assert/1.0.10/fail.ts generated vendored Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { AssertionError } from "./assertion_error.ts";
/**
* Forcefully throws a failed assertion.
*
* @example Usage
* ```ts ignore
* import { fail } from "@std/assert";
*
* fail("Deliberately failed!"); // Throws
* ```
*
* @param msg Optional message to include in the error.
* @returns Never returns, always throws.
*/
export function fail(msg?: string): never {
const msgSuffix = msg ? `: ${msg}` : ".";
throw new AssertionError(`Failed assertion${msgSuffix}`);
}

26
vendor/jsr.io/@std/assert/1.0.10/false.ts generated vendored Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { AssertionError } from "./assertion_error.ts";
/** Assertion condition for {@linkcode assertFalse}. */
export type Falsy = false | 0 | 0n | "" | null | undefined;
/**
* Make an assertion, error will be thrown if `expr` have truthy value.
*
* @example Usage
* ```ts ignore
* import { assertFalse } from "@std/assert";
*
* assertFalse(false); // Doesn't throw
* assertFalse(true); // Throws
* ```
*
* @param expr The expression to test.
* @param msg The optional message to display if the assertion fails.
*/
export function assertFalse(expr: unknown, msg = ""): asserts expr is Falsy {
if (expr) {
throw new AssertionError(msg);
}
}

30
vendor/jsr.io/@std/assert/1.0.10/greater.ts generated vendored Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { format } from "jsr:@std/internal@^1.0.5/format";
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` is greater than `expected`.
* If not then throw.
*
* @example Usage
* ```ts ignore
* import { assertGreater } from "@std/assert";
*
* assertGreater(2, 1); // Doesn't throw
* assertGreater(1, 1); // Throws
* assertGreater(0, 1); // Throws
* ```
*
* @typeParam T The type of the values to compare.
* @param actual The actual value to compare.
* @param expected The expected value to compare.
* @param msg The optional message to display if the assertion fails.
*/
export function assertGreater<T>(actual: T, expected: T, msg?: string) {
if (actual > expected) return;
const actualString = format(actual);
const expectedString = format(expected);
throw new AssertionError(msg ?? `Expect ${actualString} > ${expectedString}`);
}

36
vendor/jsr.io/@std/assert/1.0.10/greater_or_equal.ts generated vendored Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { format } from "jsr:@std/internal@^1.0.5/format";
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` is greater than or equal to `expected`.
* If not then throw.
*
* @example Usage
* ```ts ignore
* import { assertGreaterOrEqual } from "@std/assert";
*
* assertGreaterOrEqual(2, 1); // Doesn't throw
* assertGreaterOrEqual(1, 1); // Doesn't throw
* assertGreaterOrEqual(0, 1); // Throws
* ```
*
* @typeParam T The type of the values to compare.
* @param actual The actual value to compare.
* @param expected The expected value to compare.
* @param msg The optional message to display if the assertion fails.
*/
export function assertGreaterOrEqual<T>(
actual: T,
expected: T,
msg?: string,
) {
if (actual >= expected) return;
const actualString = format(actual);
const expectedString = format(expected);
throw new AssertionError(
msg ?? `Expect ${actualString} >= ${expectedString}`,
);
}

64
vendor/jsr.io/@std/assert/1.0.10/instance_of.ts generated vendored Normal file
View File

@ -0,0 +1,64 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { AssertionError } from "./assertion_error.ts";
/** Any constructor */
// deno-lint-ignore no-explicit-any
export type AnyConstructor = new (...args: any[]) => any;
/** Gets constructor type */
export type GetConstructorType<T extends AnyConstructor> = InstanceType<T>;
/**
* Make an assertion that `obj` is an instance of `type`.
* If not then throw.
*
* @example Usage
* ```ts ignore
* import { assertInstanceOf } from "@std/assert";
*
* assertInstanceOf(new Date(), Date); // Doesn't throw
* assertInstanceOf(new Date(), Number); // Throws
* ```
*
* @typeParam T The expected type of the object.
* @param actual The object to check.
* @param expectedType The expected class constructor.
* @param msg The optional message to display if the assertion fails.
*/
export function assertInstanceOf<
// deno-lint-ignore no-explicit-any
T extends abstract new (...args: any[]) => any,
>(
actual: unknown,
expectedType: T,
msg = "",
): asserts actual is InstanceType<T> {
if (actual instanceof expectedType) return;
const msgSuffix = msg ? `: ${msg}` : ".";
const expectedTypeStr = expectedType.name;
let actualTypeStr = "";
if (actual === null) {
actualTypeStr = "null";
} else if (actual === undefined) {
actualTypeStr = "undefined";
} else if (typeof actual === "object") {
actualTypeStr = actual.constructor?.name ?? "Object";
} else {
actualTypeStr = typeof actual;
}
if (expectedTypeStr === actualTypeStr) {
msg =
`Expected object to be an instance of "${expectedTypeStr}"${msgSuffix}`;
} else if (actualTypeStr === "function") {
msg =
`Expected object to be an instance of "${expectedTypeStr}" but was not an instanced object${msgSuffix}`;
} else {
msg =
`Expected object to be an instance of "${expectedTypeStr}" but was "${actualTypeStr}"${msgSuffix}`;
}
throw new AssertionError(msg);
}

65
vendor/jsr.io/@std/assert/1.0.10/is_error.ts generated vendored Normal file
View File

@ -0,0 +1,65 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { AssertionError } from "./assertion_error.ts";
import { stripAnsiCode } from "jsr:@std/internal@^1.0.5/styles";
/**
* Make an assertion that `error` is an `Error`.
* If not then an error will be thrown.
* An error class and a string that should be included in the
* error message can also be asserted.
*
* @example Usage
* ```ts ignore
* import { assertIsError } from "@std/assert";
*
* assertIsError(null); // Throws
* assertIsError(new RangeError("Out of range")); // Doesn't throw
* assertIsError(new RangeError("Out of range"), SyntaxError); // Throws
* assertIsError(new RangeError("Out of range"), SyntaxError, "Out of range"); // Doesn't throw
* assertIsError(new RangeError("Out of range"), SyntaxError, "Within range"); // Throws
* ```
*
* @typeParam E The type of the error to assert.
* @param error The error to assert.
* @param ErrorClass The optional error class to assert.
* @param msgMatches The optional string or RegExp to assert in the error message.
* @param msg The optional message to display if the assertion fails.
*/
export function assertIsError<E extends Error = Error>(
error: unknown,
// deno-lint-ignore no-explicit-any
ErrorClass?: abstract new (...args: any[]) => E,
msgMatches?: string | RegExp,
msg?: string,
): asserts error is E {
const msgSuffix = msg ? `: ${msg}` : ".";
if (!(error instanceof Error)) {
throw new AssertionError(
`Expected "error" to be an Error object${msgSuffix}`,
);
}
if (ErrorClass && !(error instanceof ErrorClass)) {
msg =
`Expected error to be instance of "${ErrorClass.name}", but was "${error?.constructor?.name}"${msgSuffix}`;
throw new AssertionError(msg);
}
let msgCheck;
if (typeof msgMatches === "string") {
msgCheck = stripAnsiCode(error.message).includes(
stripAnsiCode(msgMatches),
);
}
if (msgMatches instanceof RegExp) {
msgCheck = msgMatches.test(stripAnsiCode(error.message));
}
if (msgMatches && !msgCheck) {
msg = `Expected error message to include ${
msgMatches instanceof RegExp
? msgMatches.toString()
: JSON.stringify(msgMatches)
}, but got ${JSON.stringify(error?.message)}${msgSuffix}`;
throw new AssertionError(msg);
}
}

29
vendor/jsr.io/@std/assert/1.0.10/less.ts generated vendored Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { format } from "jsr:@std/internal@^1.0.5/format";
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` is less than `expected`.
* If not then throw.
*
* @example Usage
* ```ts ignore
* import { assertLess } from "@std/assert";
*
* assertLess(1, 2); // Doesn't throw
* assertLess(2, 1); // Throws
* ```
*
* @typeParam T The type of the values to compare.
* @param actual The actual value to compare.
* @param expected The expected value to compare.
* @param msg The optional message to display if the assertion fails.
*/
export function assertLess<T>(actual: T, expected: T, msg?: string) {
if (actual < expected) return;
const actualString = format(actual);
const expectedString = format(expected);
throw new AssertionError(msg ?? `Expect ${actualString} < ${expectedString}`);
}

36
vendor/jsr.io/@std/assert/1.0.10/less_or_equal.ts generated vendored Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { format } from "jsr:@std/internal@^1.0.5/format";
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` is less than or equal to `expected`.
* If not then throw.
*
* @example Usage
* ```ts ignore
* import { assertLessOrEqual } from "@std/assert";
*
* assertLessOrEqual(1, 2); // Doesn't throw
* assertLessOrEqual(1, 1); // Doesn't throw
* assertLessOrEqual(1, 0); // Throws
* ```
*
* @typeParam T The type of the values to compare.
* @param actual The actual value to compare.
* @param expected The expected value to compare.
* @param msg The optional message to display if the assertion fails.
*/
export function assertLessOrEqual<T>(
actual: T,
expected: T,
msg?: string,
) {
if (actual <= expected) return;
const actualString = format(actual);
const expectedString = format(expected);
throw new AssertionError(
msg ?? `Expect ${actualString} <= ${expectedString}`,
);
}

30
vendor/jsr.io/@std/assert/1.0.10/match.ts generated vendored Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` match RegExp `expected`. If not
* then throw.
*
* @example Usage
* ```ts ignore
* import { assertMatch } from "@std/assert";
*
* assertMatch("Raptor", /Raptor/); // Doesn't throw
* assertMatch("Denosaurus", /Raptor/); // Throws
* ```
*
* @param actual The actual value to be matched.
* @param expected The expected pattern to match.
* @param msg The optional message to display if the assertion fails.
*/
export function assertMatch(
actual: string,
expected: RegExp,
msg?: string,
) {
if (expected.test(actual)) return;
const msgSuffix = msg ? `: ${msg}` : ".";
msg = `Expected actual: "${actual}" to match: "${expected}"${msgSuffix}`;
throw new AssertionError(msg);
}

Some files were not shown because too many files have changed in this diff Show More