diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c8b2d79 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true +[*] +indent_style = space +tab_width = 2 +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/README.md b/README.md index 4da888e..c1a7ee2 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,112 @@ or using `docker`: docker run -it --rm -p 3000:3000 pqnet/minivec ``` -add new documents to the store with a HTTP POST: +## Creating Indices + +Minivec now supports multiple indices for different document properties. First, create an index: + +```bash +# Create default index for document content +curl -X POST -H "Content-Type: application/json" -d '{"name":"default","indexedPropertyPath":"$.content","description":"Default content index"}' localhost:3000/api/indices + +# Create another index for document titles +curl -X POST -H "Content-Type: application/json" -d '{"name":"title-index","indexedPropertyPath":"$.title","description":"Index for document titles"}' localhost:3000/api/indices +``` + +## Adding Documents + +Add new documents to the store with a HTTP POST. Minivec will automatically detect and use all applicable indices for your documents: + +```bash +# Add a document - will automatically be indexed in all applicable indices +curl -X POST -H "Content-Type: application/json" -d '{ + "documents": [ + { + "content": "Artificial intelligence is transforming industries across the globe.", + "title": "AI Revolution", + "metadata": { "tags": ["ai", "technology"] } + } + ] +}' localhost:3000/api/documents +``` + +You can also specify which indices you want to use (any non-applicable indices will be ignored): + +```bash +# Index document only in the title index (if applicable) +curl -X POST -H "Content-Type: application/json" -d '{ + "documents": [ + { + "content": "Artificial intelligence is transforming industries across the globe.", + "title": "AI Revolution", + "metadata": { "tags": ["ai", "technology"] } + } + ], + "indices": ["title-index"] +}' localhost:3000/api/documents +``` + +The response includes a summary of which indices were used: + +```json +{ + "success": true, + "count": 1, + "indices": "default (1/1), title-index (1/1)" +} +``` + +## Document Structure + +Documents in Minivec have flexible JSON structures. The only requirement is that the property referenced by the index's `indexedPropertyPath` must exist as a string when you want to index the document. + +Examples of valid documents: + +```json +// Simple document with content property +{ "content": "This is the document content" } + +// Document with nested properties +{ + "title": "My Document", + "details": { + "content": "This is the document content", + "author": "John Doe" + }, + "metadata": { + "tags": ["example", "documentation"] + } +} +``` + +You can create indices with appropriate JSON paths like `$.content`, `$.title`, or `$.details.content` to target different properties in your documents. + +## Searching Documents + +Search using HTTP GET, specifying which index to use: + ```bash -curl -H "Content-Type: application/json" -d '{ "documents": [{ "content": "hello world", "metadata":{} }]}' localhost:3000/api/documents +# Search using the default content index +curl 'localhost:3000/api/documents?q=artificial%20intelligence' + +# Search using the title index +curl 'localhost:3000/api/documents?q=revolution&index=title-index' ``` -And search them using HTTP GET: +## Listing Available Indices + +List all available indices: + ```bash -curl 'localhost:3000/api/documents?q=hello' +curl 'localhost:3000/api/indices' +``` + +## Building Indices for Existing Documents + +If you add new indices after inserting documents, you can build embeddings for existing documents: + +```bash +curl -X POST 'localhost:3000/api/indices/title-index/build' ``` ## Persistence @@ -39,6 +137,7 @@ docker run -it --rm -p 3000:3000 -v minivec-models-cache:/models -v minivec-data ## Configuration Use environment variables to configure which models to load. see [nitro.config.ts](nitro.config.ts) for a full list of the usable variables + ### Model choice `bge-m3` (for embedding) and `bge-reranker-v2-m3` (for reranking) are automatically downloaded and used by the container. It is possible to choose different models by specifying a local file name, an http/https URL or an huggingface repository to download the models automatically. diff --git a/nitro.config.ts b/nitro.config.ts index f822988..9a12156 100644 --- a/nitro.config.ts +++ b/nitro.config.ts @@ -6,6 +6,12 @@ export default defineNitroConfig({ experimental: { database: true, }, + database: { + default: { + connector: "better-sqlite3", + options: {}, + }, + }, runtimeConfig: { disableWrite: false, localModels: { diff --git a/package.json b/package.json index 2067bbb..c986d8c 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "description": "", "type": "module", "scripts": { + "test": "tsx --test ./test/**/*.test.ts", + "test:integration": "node ./test/api-integration.test.js", "dev": "nitro dev", "build": "nitro build", "build-container": "podman build . -t docker.io/pqnet/minivec:latest" @@ -11,10 +13,10 @@ "keywords": [], "author": "", "license": "ISC", - "packageManager": "pnpm@10.4.1", + "packageManager": "pnpm@10.6.3+sha512.bb45e34d50a9a76e858a95837301bfb6bd6d35aea2c5d52094fa497a467c43f5c440103ce2511e9e0a2f89c3d6071baac3358fc68ac6fb75e2ceb3d2736065e6", "devDependencies": { "@types/better-sqlite3": "^7.6.12", - "nitropack": "^2.10.4", + "nitropack": "^2.11.6", "tsx": "^4.19.3" }, "pnpm": { @@ -38,8 +40,8 @@ ] }, "dependencies": { - "better-sqlite3": "^11.8.1", - "db0": "^0.2.4", + "better-sqlite3": "^11.9.0", + "db0": "^0.3.1", "node-llama-cpp": "^3.6.0", "sqlite-vec": "0.1.7-alpha.2" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11b0a11..f80555b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,11 +21,11 @@ importers: .: dependencies: better-sqlite3: - specifier: ^11.8.1 - version: 11.8.1 + specifier: ^11.9.0 + version: 11.9.0 db0: - specifier: ^0.2.4 - version: 0.2.4(better-sqlite3@11.8.1) + specifier: ^0.3.1 + version: 0.3.1(better-sqlite3@11.9.0) node-llama-cpp: specifier: ^3.6.0 version: 3.6.0(typescript@5.7.3) @@ -37,48 +37,18 @@ importers: specifier: ^7.6.12 version: 7.6.12 nitropack: - specifier: ^2.10.4 - version: 2.10.4(better-sqlite3@11.8.1)(typescript@5.7.3) + specifier: ^2.11.6 + version: 2.11.6(better-sqlite3@11.9.0)(typescript@5.7.3) tsx: specifier: ^4.19.3 version: 4.19.3 packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.8': - resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.26.9': - resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.26.9': - resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.26.5': - resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-string-parser@7.25.9': resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} @@ -87,31 +57,11 @@ packages: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.26.9': - resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==} - engines: {node: '>=6.9.0'} - '@babel/parser@7.26.9': resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/standalone@7.26.9': - resolution: {integrity: sha512-UTeQKy0kzJwWRe55kT1uK4G9H6D0lS6G4207hCU/bDaOhA5t2aC0qHN6GmID0Axv3OFLNXm27NdqcWp+BXcGtA==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.26.9': - resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.26.9': - resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} - engines: {node: '>=6.9.0'} - '@babel/types@7.26.9': resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} @@ -120,300 +70,150 @@ packages: resolution: {integrity: sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==} engines: {node: '>=16.13'} - '@esbuild/aix-ppc64@0.24.2': - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.0': resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.24.2': - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.0': resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.24.2': - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.0': resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.24.2': - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.0': resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.24.2': - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.0': resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.24.2': - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.0': resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.24.2': - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.0': resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.2': - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.0': resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.24.2': - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.0': resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.24.2': - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.0': resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.24.2': - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.0': resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.24.2': - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.0': resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.24.2': - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.0': resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.24.2': - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.0': resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.24.2': - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.0': resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.24.2': - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.0': resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.24.2': - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.0': resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.24.2': - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.25.0': resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.2': - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.0': resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.24.2': - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.25.0': resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.2': - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.0': resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.24.2': - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.0': resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.24.2': - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.0': resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.24.2': - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.25.0': resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.24.2': - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.0': resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} engines: {node: '>=18'} @@ -467,16 +267,16 @@ packages: engines: {node: '>=18'} hasBin: true - '@netlify/functions@2.8.2': - resolution: {integrity: sha512-DeoAQh8LuNPvBE4qsKlezjKj0PyXDryOFJfJKo3Z1qZLKzQ21sT314KQKPVjfvw6knqijj+IO+0kHXy/TJiqNA==} - engines: {node: '>=14.0.0'} + '@netlify/functions@3.0.0': + resolution: {integrity: sha512-XXf9mNw4+fkxUzukDpJtzc32bl1+YlXZwEhc5ZgMcTbJPLpgRLDs5WWSPJ4eY/Mv1ZFvtxmMwmfgoQYVt68Qog==} + engines: {node: '>=18.0.0'} '@netlify/node-cookies@0.1.0': resolution: {integrity: sha512-OAs1xG+FfLX0LoRASpqzVntVV/RpYkgpI0VrUnw2u0Q1qiZUzcPffxRK8HF3gc4GjuhG5ahOEMJ9bswBiZPq0g==} engines: {node: ^14.16.0 || >=16.0.0} - '@netlify/serverless-functions-api@1.26.1': - resolution: {integrity: sha512-q3L9i3HoNfz0SGpTIS4zTcKBbRkxzCRpd169eyiTuk3IwcPC3/85mzLHranlKo2b+HYT0gu37YxGB45aD8A3Tw==} + '@netlify/serverless-functions-api@1.30.1': + resolution: {integrity: sha512-JkbaWFeydQdeDHz1mAy4rw+E3bl9YtbCgkntfTxq+IlNX/aIMv2/b1kZnQZcil4/sPoZGL831Dq6E374qRpU1A==} engines: {node: '>=18.0.0'} '@node-llama-cpp/linux-x64@3.6.0': @@ -696,6 +496,17 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@poppinss/colors@4.1.4': + resolution: {integrity: sha512-FA+nTU8p6OcSH4tLDY5JilGYr1bVWHpNmcLr7xmMEdbWmKHa+3QZ+DqefrXKmdjO/brHTnQZo20lLSjaO7ydog==} + engines: {node: '>=18.16.0'} + + '@poppinss/dumper@0.6.3': + resolution: {integrity: sha512-iombbn8ckOixMtuV1p3f8jN6vqhXefNjJttoPaJDMeIk/yIGhkkL3OrHkEjE9SRsgoAx1vBUU2GtgggjvA5hCA==} + + '@poppinss/exception@1.2.1': + resolution: {integrity: sha512-aQypoot0HPSJa6gDPEPTntc1GT6QINrSbgRlRhadGW2WaYqUK3tK4Bw9SBMZXhmxd3GeAlZjVcODHgiu+THY7A==} + engines: {node: '>=18'} + '@redocly/ajv@8.11.2': resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} @@ -767,8 +578,8 @@ packages: rollup: optional: true - '@rollup/plugin-commonjs@28.0.2': - resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==} + '@rollup/plugin-commonjs@28.0.3': + resolution: {integrity: sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: rollup: ^2.68.0||^3.0.0||^4.0.0 @@ -794,8 +605,8 @@ packages: rollup: optional: true - '@rollup/plugin-node-resolve@15.3.1': - resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} + '@rollup/plugin-node-resolve@16.0.1': + resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.78.0||^3.0.0||^4.0.0 @@ -830,105 +641,112 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.34.8': - resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} + '@rollup/rollup-android-arm-eabi@4.36.0': + resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.34.8': - resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} + '@rollup/rollup-android-arm64@4.36.0': + resolution: {integrity: sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.34.8': - resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} + '@rollup/rollup-darwin-arm64@4.36.0': + resolution: {integrity: sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.34.8': - resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} + '@rollup/rollup-darwin-x64@4.36.0': + resolution: {integrity: sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.34.8': - resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} + '@rollup/rollup-freebsd-arm64@4.36.0': + resolution: {integrity: sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.34.8': - resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} + '@rollup/rollup-freebsd-x64@4.36.0': + resolution: {integrity: sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.34.8': - resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} + '@rollup/rollup-linux-arm-gnueabihf@4.36.0': + resolution: {integrity: sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.34.8': - resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} + '@rollup/rollup-linux-arm-musleabihf@4.36.0': + resolution: {integrity: sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.34.8': - resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} + '@rollup/rollup-linux-arm64-gnu@4.36.0': + resolution: {integrity: sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.34.8': - resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} + '@rollup/rollup-linux-arm64-musl@4.36.0': + resolution: {integrity: sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.34.8': - resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.36.0': + resolution: {integrity: sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': - resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.36.0': + resolution: {integrity: sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.34.8': - resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} + '@rollup/rollup-linux-riscv64-gnu@4.36.0': + resolution: {integrity: sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.34.8': - resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} + '@rollup/rollup-linux-s390x-gnu@4.36.0': + resolution: {integrity: sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.34.8': - resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} + '@rollup/rollup-linux-x64-gnu@4.36.0': + resolution: {integrity: sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.34.8': - resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} + '@rollup/rollup-linux-x64-musl@4.36.0': + resolution: {integrity: sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.34.8': - resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} + '@rollup/rollup-win32-arm64-msvc@4.36.0': + resolution: {integrity: sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.34.8': - resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} + '@rollup/rollup-win32-ia32-msvc@4.36.0': + resolution: {integrity: sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.34.8': - resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} + '@rollup/rollup-win32-x64-msvc@4.36.0': + resolution: {integrity: sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==} cpu: [x64] os: [win32] + '@sindresorhus/is@7.0.1': + resolution: {integrity: sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==} + engines: {node: '>=18'} + '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@speed-highlight/core@1.2.7': + resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==} + '@tinyhttp/content-disposition@2.2.2': resolution: {integrity: sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==} engines: {node: '>=12.20.0'} @@ -951,9 +769,9 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@vercel/nft@0.27.10': - resolution: {integrity: sha512-zbaF9Wp/NsZtKLE4uVmL3FyfFwlpDyuymQM1kPbeT0mVOHKDQQNjnnfslB3REg3oZprmNFJuh3pkHBk2qAaizg==} - engines: {node: '>=16'} + '@vercel/nft@0.29.2': + resolution: {integrity: sha512-A/Si4mrTkQqJ6EXJKv5EYCDQ3NL6nJXxG8VGXePsaiQigsomHYQC9xSpX8qGk7AEZk4b1ssbYIqJ0ISQQ7bfcA==} + engines: {node: '>=18'} hasBin: true abbrev@3.0.0: @@ -1055,12 +873,8 @@ packages: before-after-hook@3.0.2: resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} - better-sqlite3@11.8.1: - resolution: {integrity: sha512-9BxNaBkblMjhJW8sMRZxnxVTRgbRmssZW0Oxc1MPBTfiR+WW21e2Mk4qu8CzrcZb1LwPCnFsfDEzq+SNcBU8eg==} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} + better-sqlite3@11.9.0: + resolution: {integrity: sha512-4b9xYnoaskj8eIkke9ZCB42p5bOPabptSku8Rl4Yww70Jf+aHeLvrIjXDJrKQxUEjdppsFb+fdJSjoH4TklROA==} bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} @@ -1071,9 +885,6 @@ packages: bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -1081,11 +892,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - buffer-crc32@1.0.0: resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} engines: {node: '>=8.0.0'} @@ -1103,8 +909,8 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - c12@2.0.1: - resolution: {integrity: sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==} + c12@3.0.2: + resolution: {integrity: sha512-6Tzk1/TNeI3WBPpK0j/Ss4+gPj3PUJYbWl/MWDJBThFvwNGNkXtd7Cz8BJtD4aRwoGHtzQD0SnxamgUiBH0/Nw==} peerDependencies: magicast: ^0.3.5 peerDependenciesMeta: @@ -1115,9 +921,6 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - caniuse-lite@1.0.30001701: - resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==} - chalk@5.4.1: resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -1128,10 +931,6 @@ packages: chmodrp@1.0.2: resolution: {integrity: sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w==} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1214,12 +1013,12 @@ packages: resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} engines: {node: '>= 14'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + confbox@0.2.1: + resolution: {integrity: sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==} + consola@3.4.0: resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -1227,12 +1026,16 @@ packages: console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-es@1.2.2: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + cookie-es@2.0.0: + resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -1261,8 +1064,8 @@ packages: crossws@0.3.4: resolution: {integrity: sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw==} - db0@0.2.4: - resolution: {integrity: sha512-hIzftLH1nMsF95zSLjDLYLbE9huOXnLYUTAQ5yKF5amp0FpeD+B15XJa8BvGYSOeSCH4gl2WahB/y1FcUByQSg==} + db0@0.3.1: + resolution: {integrity: sha512-3RogPLE2LLq6t4YiFCREyl572aBjkfMvfwPyN51df00TbPbryL3XqBYuJ/j6mgPssPK8AKfYdLxizaO5UG10sA==} peerDependencies: '@electric-sql/pglite': '*' '@libsql/client': '*' @@ -1372,9 +1175,6 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.105: - resolution: {integrity: sha512-ccp7LocdXx3yBhwiG0qTQ7XFrK48Ua2pxIxBdJO8cbddp/MvbBtPFzvnTchtyHQTsgqqczO8cdmAIbpMa0u2+g==} - emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -1399,6 +1199,9 @@ packages: resolution: {integrity: sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==} engines: {node: '>=10'} + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1415,11 +1218,6 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.25.0: resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} engines: {node: '>=18'} @@ -1465,6 +1263,9 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + exsolve@1.0.4: + resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==} + fast-content-type-parse@2.0.1: resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} @@ -1536,9 +1337,6 @@ packages: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1552,10 +1350,6 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This package is no longer supported. - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1582,8 +1376,8 @@ packages: get-tsconfig@4.10.0: resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} - giget@1.2.5: - resolution: {integrity: sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==} + giget@2.0.0: + resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true github-from-package@0.0.0: @@ -1597,14 +1391,6 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - globby@14.1.0: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} @@ -1671,18 +1457,14 @@ packages: resolution: {integrity: sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==} engines: {node: '>=18'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ioredis@5.5.0: - resolution: {integrity: sha512-7CutT89g23FfSa8MDoIFs2GYYa0PaNiW/OrT+nRyjRXHDZd17HmIgy+reOQ/yhh72NznNjGuS8kbCAcA4Ro4mw==} + ioredis@5.6.0: + resolution: {integrity: sha512-tBZlIIWbndeWBWCXWZiqtOF/yxf6yZX3tAlTJ7nfo5jhd6dctNxF7QnYlZLZ1a0o0pDoen7CgZqO+zjNaFbJAg==} engines: {node: '>=12.22.0'} ipull@3.9.2: @@ -1693,10 +1475,6 @@ packages: iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -1805,22 +1583,16 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + klona@2.0.6: resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} engines: {node: '>= 8'} @@ -1876,9 +1648,6 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -1938,9 +1707,6 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} @@ -2002,8 +1768,8 @@ packages: napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - nitropack@2.10.4: - resolution: {integrity: sha512-sJiG/MIQlZCVSw2cQrFG1H6mLeSqHlYfFerRjLKz69vUfdu0EL2l0WdOxlQbzJr3mMv/l4cOlCCLzVRzjzzF/g==} + nitropack@2.11.6: + resolution: {integrity: sha512-iaLzOKYxsNL8G6h9cMFTC/hAN4RfhZsrFzFFzemr6Vfn57MooYEz6KLeUoRyTposlAeEWTVejz8naYOORIrnDg==} engines: {node: ^16.11.0 || >=17.0.0} hasBin: true peerDependencies: @@ -2059,9 +1825,6 @@ packages: node-mock-http@1.0.0: resolution: {integrity: sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - nopt@8.1.0: resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} engines: {node: ^18.17.0 || >=20.5.0} @@ -2080,8 +1843,8 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This package is no longer supported. - nypm@0.5.4: - resolution: {integrity: sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==} + nypm@0.6.0: + resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} engines: {node: ^14.16.0 || >=16.10.0} hasBin: true @@ -2092,8 +1855,8 @@ packages: ofetch@1.4.1: resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} - ohash@1.1.4: - resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} @@ -2143,10 +1906,6 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2189,6 +1948,9 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + pkg-types@2.1.0: + resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -2260,10 +2022,6 @@ packages: readdir-glob@1.1.3: resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -2329,8 +2087,8 @@ packages: rollup: optional: true - rollup@4.34.8: - resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} + rollup@4.36.0: + resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2346,10 +2104,6 @@ packages: scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.7.1: resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} @@ -2463,8 +2217,8 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - std-env@3.8.0: - resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + std-env@3.8.1: + resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==} stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} @@ -2515,8 +2269,12 @@ packages: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} - strip-literal@2.1.1: - resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + + supports-color@10.0.0: + resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==} + engines: {node: '>=18'} supports-color@9.4.0: resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} @@ -2559,6 +2317,10 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.12: + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2594,6 +2356,9 @@ packages: ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + ultrahtml@1.5.3: + resolution: {integrity: sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==} + uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} @@ -2603,15 +2368,16 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} - unenv@1.10.0: - resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==} + unenv@2.0.0-rc.14: + resolution: {integrity: sha512-od496pShMen7nOy5VmVJCnq8rptd45vh6Nx/r2iPbrba6pa6p+tS2ywuIHRZ/OBvSbQZB0kWvpO9XBNVFXHD3Q==} unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - unimport@3.14.6: - resolution: {integrity: sha512-CYvbDaTT04Rh8bmD8jz3WPmHYZRG/NnvYVzwD6V1YAlvvKROlAeNDUBhkBGzNav2RKaeuXvlWYaa1V4Lfi/O0g==} + unimport@4.1.2: + resolution: {integrity: sha512-oVUL7PSlyVV3QRhsdcyYEMaDX8HJyS/CnUonEJTYA3//bWO+o/4gG8F7auGWWWkrrxBQBYOO8DKe+C53ktpRXw==} + engines: {node: '>=18.12.0'} universal-github-app-jwt@2.2.0: resolution: {integrity: sha512-G5o6f95b5BggDGuUfKDApKaCgNYy2x7OdHY0zSMF081O0EJobw+1130VONhrA7ezGSV2FNOGyM+KQpQZAr9bIQ==} @@ -2623,6 +2389,10 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + unplugin-utils@0.2.4: + resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==} + engines: {node: '>=18.12.0'} + unplugin@1.16.1: resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} engines: {node: '>=14.0.0'} @@ -2694,19 +2464,13 @@ packages: resolution: {integrity: sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==} hasBin: true - untyped@1.5.2: - resolution: {integrity: sha512-eL/8PlhLcMmlMDtNPKhyyz9kEBDS3Uk4yMu/ewlkT2WFbtzScjHWPJLdQLmaGPUKjXzwe9MumOtOgc4Fro96Kg==} + untyped@2.0.0: + resolution: {integrity: sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==} hasBin: true unwasm@0.3.9: resolution: {integrity: sha512-LDxTx/2DkFURUd+BU1vUsF/moj0JsoTvl+2tcg2AUOiEzVturhGGx17/IMgGvKUYdZwr33EJHtChCJuhu9Ouvg==} - update-browserslist-db@1.1.2: - resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - uqr@0.1.2: resolution: {integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==} @@ -2763,9 +2527,6 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -2788,112 +2549,34 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} + youch-core@0.3.2: + resolution: {integrity: sha512-fusrlIMLeRvTFYLUjJ9KzlGC3N+6MOPJ68HNj/yJv2nz7zq8t4HEviLms2gkdRPUS7F5rZ5n+pYx9r88m6IE1g==} + engines: {node: '>=18'} + + youch@4.1.0-beta.6: + resolution: {integrity: sha512-y1aNsEeoLXnWb6pI9TvfNPIxySyo4Un3OGxKn7rsNj8+tgSquzXEWkzfA5y6gU0fvzmQgvx3JBn/p51qQ8Xg9A==} + engines: {node: '>=18'} + zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} snapshots: - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.8': {} - - '@babel/core@7.26.9': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.9 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9) - '@babel/helpers': 7.26.9 - '@babel/parser': 7.26.9 - '@babel/template': 7.26.9 - '@babel/traverse': 7.26.9 - '@babel/types': 7.26.9 - convert-source-map: 2.0.0 - debug: 4.4.0(supports-color@9.4.0) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.26.9': - dependencies: - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 3.1.0 - - '@babel/helper-compilation-targets@7.26.5': - dependencies: - '@babel/compat-data': 7.26.8 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.4 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-module-imports@7.25.9': - dependencies: - '@babel/traverse': 7.26.9 - '@babel/types': 7.26.9 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)': - dependencies: - '@babel/core': 7.26.9 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.9 - transitivePeerDependencies: - - supports-color - '@babel/helper-string-parser@7.25.9': {} '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-option@7.25.9': {} - - '@babel/helpers@7.26.9': - dependencies: - '@babel/template': 7.26.9 - '@babel/types': 7.26.9 - '@babel/parser@7.26.9': dependencies: '@babel/types': 7.26.9 - '@babel/standalone@7.26.9': {} - - '@babel/template@7.26.9': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 - - '@babel/traverse@7.26.9': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.9 - '@babel/parser': 7.26.9 - '@babel/template': 7.26.9 - '@babel/types': 7.26.9 - debug: 4.4.0(supports-color@9.4.0) - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - '@babel/types@7.26.9': dependencies: '@babel/helper-string-parser': 7.25.9 @@ -2903,153 +2586,78 @@ snapshots: dependencies: mime: 3.0.0 - '@esbuild/aix-ppc64@0.24.2': - optional: true - '@esbuild/aix-ppc64@0.25.0': optional: true - '@esbuild/android-arm64@0.24.2': - optional: true - '@esbuild/android-arm64@0.25.0': optional: true - '@esbuild/android-arm@0.24.2': - optional: true - '@esbuild/android-arm@0.25.0': optional: true - '@esbuild/android-x64@0.24.2': - optional: true - '@esbuild/android-x64@0.25.0': optional: true - '@esbuild/darwin-arm64@0.24.2': - optional: true - '@esbuild/darwin-arm64@0.25.0': optional: true - '@esbuild/darwin-x64@0.24.2': - optional: true - '@esbuild/darwin-x64@0.25.0': optional: true - '@esbuild/freebsd-arm64@0.24.2': - optional: true - '@esbuild/freebsd-arm64@0.25.0': optional: true - '@esbuild/freebsd-x64@0.24.2': - optional: true - '@esbuild/freebsd-x64@0.25.0': optional: true - '@esbuild/linux-arm64@0.24.2': - optional: true - '@esbuild/linux-arm64@0.25.0': optional: true - '@esbuild/linux-arm@0.24.2': - optional: true - '@esbuild/linux-arm@0.25.0': optional: true - '@esbuild/linux-ia32@0.24.2': - optional: true - '@esbuild/linux-ia32@0.25.0': optional: true - '@esbuild/linux-loong64@0.24.2': - optional: true - '@esbuild/linux-loong64@0.25.0': optional: true - '@esbuild/linux-mips64el@0.24.2': - optional: true - '@esbuild/linux-mips64el@0.25.0': optional: true - '@esbuild/linux-ppc64@0.24.2': - optional: true - '@esbuild/linux-ppc64@0.25.0': optional: true - '@esbuild/linux-riscv64@0.24.2': - optional: true - '@esbuild/linux-riscv64@0.25.0': optional: true - '@esbuild/linux-s390x@0.24.2': - optional: true - '@esbuild/linux-s390x@0.25.0': optional: true - '@esbuild/linux-x64@0.24.2': - optional: true - '@esbuild/linux-x64@0.25.0': optional: true - '@esbuild/netbsd-arm64@0.24.2': - optional: true - '@esbuild/netbsd-arm64@0.25.0': optional: true - '@esbuild/netbsd-x64@0.24.2': - optional: true - '@esbuild/netbsd-x64@0.25.0': optional: true - '@esbuild/openbsd-arm64@0.24.2': - optional: true - '@esbuild/openbsd-arm64@0.25.0': optional: true - '@esbuild/openbsd-x64@0.24.2': - optional: true - '@esbuild/openbsd-x64@0.25.0': optional: true - '@esbuild/sunos-x64@0.24.2': - optional: true - '@esbuild/sunos-x64@0.25.0': optional: true - '@esbuild/win32-arm64@0.24.2': - optional: true - '@esbuild/win32-arm64@0.25.0': optional: true - '@esbuild/win32-ia32@0.24.2': - optional: true - '@esbuild/win32-ia32@0.25.0': optional: true - '@esbuild/win32-x64@0.24.2': - optional: true - '@esbuild/win32-x64@0.25.0': optional: true @@ -3113,13 +2721,13 @@ snapshots: - encoding - supports-color - '@netlify/functions@2.8.2': + '@netlify/functions@3.0.0': dependencies: - '@netlify/serverless-functions-api': 1.26.1 + '@netlify/serverless-functions-api': 1.30.1 '@netlify/node-cookies@0.1.0': {} - '@netlify/serverless-functions-api@1.26.1': + '@netlify/serverless-functions-api@1.30.1': dependencies: '@netlify/node-cookies': 0.1.0 urlpattern-polyfill: 8.0.2 @@ -3354,6 +2962,18 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@poppinss/colors@4.1.4': + dependencies: + kleur: 4.1.5 + + '@poppinss/dumper@0.6.3': + dependencies: + '@poppinss/colors': 4.1.4 + '@sindresorhus/is': 7.0.1 + supports-color: 10.0.0 + + '@poppinss/exception@1.2.1': {} + '@redocly/ajv@8.11.2': dependencies: fast-deep-equal: 3.1.3 @@ -3413,13 +3033,13 @@ snapshots: '@reflink/reflink-win32-x64-msvc': 0.1.19 optional: true - '@rollup/plugin-alias@5.1.1(rollup@4.34.8)': + '@rollup/plugin-alias@5.1.1(rollup@4.36.0)': optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - '@rollup/plugin-commonjs@28.0.2(rollup@4.34.8)': + '@rollup/plugin-commonjs@28.0.3(rollup@4.36.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.34.8) + '@rollup/pluginutils': 5.1.4(rollup@4.36.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.4.3(picomatch@4.0.2) @@ -3427,114 +3047,118 @@ snapshots: magic-string: 0.30.17 picomatch: 4.0.2 optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - '@rollup/plugin-inject@5.0.5(rollup@4.34.8)': + '@rollup/plugin-inject@5.0.5(rollup@4.36.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.34.8) + '@rollup/pluginutils': 5.1.4(rollup@4.36.0) estree-walker: 2.0.2 magic-string: 0.30.17 optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - '@rollup/plugin-json@6.1.0(rollup@4.34.8)': + '@rollup/plugin-json@6.1.0(rollup@4.36.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.34.8) + '@rollup/pluginutils': 5.1.4(rollup@4.36.0) optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.34.8)': + '@rollup/plugin-node-resolve@16.0.1(rollup@4.36.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.34.8) + '@rollup/pluginutils': 5.1.4(rollup@4.36.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.10 optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - '@rollup/plugin-replace@6.0.2(rollup@4.34.8)': + '@rollup/plugin-replace@6.0.2(rollup@4.36.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.34.8) + '@rollup/pluginutils': 5.1.4(rollup@4.36.0) magic-string: 0.30.17 optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - '@rollup/plugin-terser@0.4.4(rollup@4.34.8)': + '@rollup/plugin-terser@0.4.4(rollup@4.36.0)': dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 terser: 5.39.0 optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - '@rollup/pluginutils@5.1.4(rollup@4.34.8)': + '@rollup/pluginutils@5.1.4(rollup@4.36.0)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - '@rollup/rollup-android-arm-eabi@4.34.8': + '@rollup/rollup-android-arm-eabi@4.36.0': optional: true - '@rollup/rollup-android-arm64@4.34.8': + '@rollup/rollup-android-arm64@4.36.0': optional: true - '@rollup/rollup-darwin-arm64@4.34.8': + '@rollup/rollup-darwin-arm64@4.36.0': optional: true - '@rollup/rollup-darwin-x64@4.34.8': + '@rollup/rollup-darwin-x64@4.36.0': optional: true - '@rollup/rollup-freebsd-arm64@4.34.8': + '@rollup/rollup-freebsd-arm64@4.36.0': optional: true - '@rollup/rollup-freebsd-x64@4.34.8': + '@rollup/rollup-freebsd-x64@4.36.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + '@rollup/rollup-linux-arm-gnueabihf@4.36.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.34.8': + '@rollup/rollup-linux-arm-musleabihf@4.36.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.34.8': + '@rollup/rollup-linux-arm64-gnu@4.36.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.34.8': + '@rollup/rollup-linux-arm64-musl@4.36.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + '@rollup/rollup-linux-loongarch64-gnu@4.36.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + '@rollup/rollup-linux-powerpc64le-gnu@4.36.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.34.8': + '@rollup/rollup-linux-riscv64-gnu@4.36.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.34.8': + '@rollup/rollup-linux-s390x-gnu@4.36.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.34.8': + '@rollup/rollup-linux-x64-gnu@4.36.0': optional: true - '@rollup/rollup-linux-x64-musl@4.34.8': + '@rollup/rollup-linux-x64-musl@4.36.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.34.8': + '@rollup/rollup-win32-arm64-msvc@4.36.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.34.8': + '@rollup/rollup-win32-ia32-msvc@4.36.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.34.8': + '@rollup/rollup-win32-x64-msvc@4.36.0': optional: true + '@sindresorhus/is@7.0.1': {} + '@sindresorhus/merge-streams@2.3.0': {} + '@speed-highlight/core@1.2.7': {} + '@tinyhttp/content-disposition@2.2.2': {} '@types/aws-lambda@8.10.147': {} @@ -3555,16 +3179,16 @@ snapshots: '@types/resolve@1.20.2': {} - '@vercel/nft@0.27.10(rollup@4.34.8)': + '@vercel/nft@0.29.2(rollup@4.36.0)': dependencies: '@mapbox/node-pre-gyp': 2.0.0 - '@rollup/pluginutils': 5.1.4(rollup@4.34.8) + '@rollup/pluginutils': 5.1.4(rollup@4.36.0) acorn: 8.14.0 acorn-import-attributes: 1.9.5(acorn@8.14.0) async-sema: 3.1.1 bindings: 1.5.0 estree-walker: 2.0.2 - glob: 7.2.3 + glob: 10.4.5 graceful-fs: 4.2.11 node-gyp-build: 4.8.4 picomatch: 4.0.2 @@ -3665,13 +3289,11 @@ snapshots: before-after-hook@3.0.2: {} - better-sqlite3@11.8.1: + better-sqlite3@11.9.0: dependencies: bindings: 1.5.0 prebuild-install: 7.1.3 - binary-extensions@2.3.0: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 @@ -3684,11 +3306,6 @@ snapshots: bottleneck@2.19.5: {} - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 @@ -3697,13 +3314,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.4: - dependencies: - caniuse-lite: 1.0.30001701 - electron-to-chromium: 1.5.105 - node-releases: 2.0.19 - update-browserslist-db: 1.1.2(browserslist@4.24.4) - buffer-crc32@1.0.0: {} buffer-from@1.1.2: {} @@ -3720,19 +3330,19 @@ snapshots: bytes@3.1.2: {} - c12@2.0.1(magicast@0.3.5): + c12@3.0.2(magicast@0.3.5): dependencies: chokidar: 4.0.3 confbox: 0.1.8 defu: 6.1.4 dotenv: 16.4.7 - giget: 1.2.5 + exsolve: 1.0.4 + giget: 2.0.0 jiti: 2.4.2 - mlly: 1.7.4 - ohash: 1.1.4 - pathe: 1.1.2 + ohash: 2.0.11 + pathe: 2.0.3 perfect-debounce: 1.0.0 - pkg-types: 1.3.1 + pkg-types: 2.1.0 rc9: 2.1.2 optionalDependencies: magicast: 0.3.5 @@ -3742,26 +3352,12 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - caniuse-lite@1.0.30001701: {} - chalk@5.4.1: {} change-case@5.4.4: {} chmodrp@1.0.2: {} - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -3846,18 +3442,20 @@ snapshots: normalize-path: 3.0.0 readable-stream: 4.7.0 - concat-map@0.0.1: {} - confbox@0.1.8: {} + confbox@0.2.1: {} + consola@3.4.0: {} console-control-strings@1.1.0: {} - convert-source-map@2.0.0: {} - cookie-es@1.2.2: {} + cookie-es@2.0.0: {} + + cookie@1.0.2: {} + core-util-is@1.0.3: {} crc-32@1.2.2: {} @@ -3883,9 +3481,9 @@ snapshots: dependencies: uncrypto: 0.1.3 - db0@0.2.4(better-sqlite3@11.8.1): + db0@0.3.1(better-sqlite3@11.9.0): optionalDependencies: - better-sqlite3: 11.8.1 + better-sqlite3: 11.9.0 debug@2.6.9: dependencies: @@ -3943,8 +3541,6 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.105: {} - emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} @@ -3961,6 +3557,8 @@ snapshots: env-var@7.5.0: {} + error-stack-parser-es@1.0.5: {} + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -3976,34 +3574,6 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - esbuild@0.24.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 - esbuild@0.25.0: optionalDependencies: '@esbuild/aix-ppc64': 0.25.0 @@ -4066,6 +3636,8 @@ snapshots: expand-template@2.0.3: {} + exsolve@1.0.4: {} + fast-content-type-parse@2.0.1: {} fast-deep-equal@3.1.3: {} @@ -4130,8 +3702,6 @@ snapshots: dependencies: minipass: 3.3.6 - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true @@ -4148,8 +3718,6 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} get-east-asian-width@1.3.0: {} @@ -4180,15 +3748,14 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - giget@1.2.5: + giget@2.0.0: dependencies: citty: 0.1.6 consola: 3.4.0 defu: 6.1.4 node-fetch-native: 1.6.6 - nypm: 0.5.4 + nypm: 0.6.0 pathe: 2.0.3 - tar: 6.2.1 github-from-package@0.0.0: {} @@ -4205,17 +3772,6 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globals@11.12.0: {} - globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -4286,16 +3842,11 @@ snapshots: index-to-position@0.1.2: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} ini@1.3.8: {} - ioredis@5.5.0: + ioredis@5.6.0: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 @@ -4335,10 +3886,6 @@ snapshots: iron-webcrypto@1.2.1: {} - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -4417,18 +3964,16 @@ snapshots: dependencies: argparse: 2.0.1 - jsesc@3.1.0: {} - json-schema-traverse@1.0.0: {} - json5@2.2.3: {} - jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 + kleur@4.1.5: {} + klona@2.0.6: {} knitwork@1.2.0: {} @@ -4457,7 +4002,7 @@ snapshots: mlly: 1.7.4 node-forge: 1.3.1 pathe: 1.1.2 - std-env: 3.8.0 + std-env: 3.8.1 ufo: 1.5.4 untun: 0.1.3 uqr: 0.1.2 @@ -4493,10 +4038,6 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -4540,10 +4081,6 @@ snapshots: mimic-response@3.1.0: {} - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 - minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 @@ -4593,44 +4130,44 @@ snapshots: napi-build-utils@2.0.0: {} - nitropack@2.10.4(better-sqlite3@11.8.1)(typescript@5.7.3): + nitropack@2.11.6(better-sqlite3@11.9.0)(typescript@5.7.3): dependencies: '@cloudflare/kv-asset-handler': 0.3.4 - '@netlify/functions': 2.8.2 - '@rollup/plugin-alias': 5.1.1(rollup@4.34.8) - '@rollup/plugin-commonjs': 28.0.2(rollup@4.34.8) - '@rollup/plugin-inject': 5.0.5(rollup@4.34.8) - '@rollup/plugin-json': 6.1.0(rollup@4.34.8) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.34.8) - '@rollup/plugin-replace': 6.0.2(rollup@4.34.8) - '@rollup/plugin-terser': 0.4.4(rollup@4.34.8) - '@rollup/pluginutils': 5.1.4(rollup@4.34.8) + '@netlify/functions': 3.0.0 + '@rollup/plugin-alias': 5.1.1(rollup@4.36.0) + '@rollup/plugin-commonjs': 28.0.3(rollup@4.36.0) + '@rollup/plugin-inject': 5.0.5(rollup@4.36.0) + '@rollup/plugin-json': 6.1.0(rollup@4.36.0) + '@rollup/plugin-node-resolve': 16.0.1(rollup@4.36.0) + '@rollup/plugin-replace': 6.0.2(rollup@4.36.0) + '@rollup/plugin-terser': 0.4.4(rollup@4.36.0) '@types/http-proxy': 1.17.16 - '@vercel/nft': 0.27.10(rollup@4.34.8) + '@vercel/nft': 0.29.2(rollup@4.36.0) archiver: 7.0.1 - c12: 2.0.1(magicast@0.3.5) - chokidar: 3.6.0 + c12: 3.0.2(magicast@0.3.5) + chokidar: 4.0.3 citty: 0.1.6 compatx: 0.1.8 - confbox: 0.1.8 + confbox: 0.2.1 consola: 3.4.0 - cookie-es: 1.2.2 + cookie-es: 2.0.0 croner: 9.0.0 crossws: 0.3.4 - db0: 0.2.4(better-sqlite3@11.8.1) + db0: 0.3.1(better-sqlite3@11.9.0) defu: 6.1.4 destr: 2.0.3 dot-prop: 9.0.0 - esbuild: 0.24.2 + esbuild: 0.25.0 escape-string-regexp: 5.0.0 etag: 1.8.1 + exsolve: 1.0.4 fs-extra: 11.3.0 globby: 14.1.0 gzip-size: 7.0.0 h3: 1.15.1 hookable: 5.5.3 httpxy: 0.1.7 - ioredis: 5.5.0 + ioredis: 5.6.0 jiti: 2.4.2 klona: 2.0.6 knitwork: 1.2.0 @@ -4640,29 +4177,35 @@ snapshots: mime: 4.0.6 mlly: 1.7.4 node-fetch-native: 1.6.6 + node-mock-http: 1.0.0 ofetch: 1.4.1 - ohash: 1.1.4 + ohash: 2.0.11 openapi-typescript: 7.6.1(typescript@5.7.3) - pathe: 1.1.2 + pathe: 2.0.3 perfect-debounce: 1.0.0 - pkg-types: 1.3.1 + pkg-types: 2.1.0 pretty-bytes: 6.1.1 radix3: 1.1.2 - rollup: 4.34.8 - rollup-plugin-visualizer: 5.14.0(rollup@4.34.8) + rollup: 4.36.0 + rollup-plugin-visualizer: 5.14.0(rollup@4.36.0) scule: 1.3.0 semver: 7.7.1 serve-placeholder: 2.0.2 serve-static: 1.16.2 - std-env: 3.8.0 + source-map: 0.7.4 + std-env: 3.8.1 ufo: 1.5.4 + ultrahtml: 1.5.3 uncrypto: 0.1.3 unctx: 2.4.1 - unenv: 1.10.0 - unimport: 3.14.6(rollup@4.34.8) - unstorage: 1.15.0(db0@0.2.4(better-sqlite3@11.8.1))(ioredis@5.5.0) - untyped: 1.5.2 + unenv: 2.0.0-rc.14 + unimport: 4.1.2 + unplugin-utils: 0.2.4 + unstorage: 1.15.0(db0@0.3.1(better-sqlite3@11.9.0))(ioredis@5.6.0) + untyped: 2.0.0 unwasm: 0.3.9 + youch: 4.1.0-beta.6 + youch-core: 0.3.2 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -4751,8 +4294,6 @@ snapshots: node-mock-http@1.0.0: {} - node-releases@2.0.19: {} - nopt@8.1.0: dependencies: abbrev: 3.0.0 @@ -4770,14 +4311,13 @@ snapshots: gauge: 4.0.4 set-blocking: 2.0.0 - nypm@0.5.4: + nypm@0.6.0: dependencies: citty: 0.1.6 consola: 3.4.0 pathe: 2.0.3 - pkg-types: 1.3.1 + pkg-types: 2.1.0 tinyexec: 0.3.2 - ufo: 1.5.4 octokit@4.1.2: dependencies: @@ -4798,7 +4338,7 @@ snapshots: node-fetch-native: 1.6.6 ufo: 1.5.4 - ohash@1.1.4: {} + ohash@2.0.11: {} on-finished@2.4.1: dependencies: @@ -4858,8 +4398,6 @@ snapshots: parseurl@1.3.3: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} path-key@4.0.0: {} @@ -4891,6 +4429,12 @@ snapshots: mlly: 1.7.4 pathe: 2.0.3 + pkg-types@2.1.0: + dependencies: + confbox: 0.2.1 + exsolve: 1.0.4 + pathe: 2.0.3 + pluralize@8.0.0: {} prebuild-install@7.1.3: @@ -4985,10 +4529,6 @@ snapshots: dependencies: minimatch: 5.1.6 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - readdirp@4.1.2: {} redis-errors@1.2.0: {} @@ -5026,38 +4566,38 @@ snapshots: dependencies: glob: 10.4.5 - rollup-plugin-visualizer@5.14.0(rollup@4.34.8): + rollup-plugin-visualizer@5.14.0(rollup@4.36.0): dependencies: open: 8.4.2 picomatch: 4.0.2 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.34.8 + rollup: 4.36.0 - rollup@4.34.8: + rollup@4.36.0: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.34.8 - '@rollup/rollup-android-arm64': 4.34.8 - '@rollup/rollup-darwin-arm64': 4.34.8 - '@rollup/rollup-darwin-x64': 4.34.8 - '@rollup/rollup-freebsd-arm64': 4.34.8 - '@rollup/rollup-freebsd-x64': 4.34.8 - '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 - '@rollup/rollup-linux-arm-musleabihf': 4.34.8 - '@rollup/rollup-linux-arm64-gnu': 4.34.8 - '@rollup/rollup-linux-arm64-musl': 4.34.8 - '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 - '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 - '@rollup/rollup-linux-riscv64-gnu': 4.34.8 - '@rollup/rollup-linux-s390x-gnu': 4.34.8 - '@rollup/rollup-linux-x64-gnu': 4.34.8 - '@rollup/rollup-linux-x64-musl': 4.34.8 - '@rollup/rollup-win32-arm64-msvc': 4.34.8 - '@rollup/rollup-win32-ia32-msvc': 4.34.8 - '@rollup/rollup-win32-x64-msvc': 4.34.8 + '@rollup/rollup-android-arm-eabi': 4.36.0 + '@rollup/rollup-android-arm64': 4.36.0 + '@rollup/rollup-darwin-arm64': 4.36.0 + '@rollup/rollup-darwin-x64': 4.36.0 + '@rollup/rollup-freebsd-arm64': 4.36.0 + '@rollup/rollup-freebsd-x64': 4.36.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.36.0 + '@rollup/rollup-linux-arm-musleabihf': 4.36.0 + '@rollup/rollup-linux-arm64-gnu': 4.36.0 + '@rollup/rollup-linux-arm64-musl': 4.36.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.36.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.36.0 + '@rollup/rollup-linux-riscv64-gnu': 4.36.0 + '@rollup/rollup-linux-s390x-gnu': 4.36.0 + '@rollup/rollup-linux-x64-gnu': 4.36.0 + '@rollup/rollup-linux-x64-musl': 4.36.0 + '@rollup/rollup-win32-arm64-msvc': 4.36.0 + '@rollup/rollup-win32-ia32-msvc': 4.36.0 + '@rollup/rollup-win32-x64-msvc': 4.36.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -5070,8 +4610,6 @@ snapshots: scule@1.3.0: {} - semver@6.3.1: {} - semver@7.7.1: {} send@0.19.0: @@ -5188,7 +4726,7 @@ snapshots: statuses@2.0.1: {} - std-env@3.8.0: {} + std-env@3.8.1: {} stdin-discarder@0.2.2: {} @@ -5246,10 +4784,12 @@ snapshots: strip-json-comments@2.0.1: {} - strip-literal@2.1.1: + strip-literal@3.0.0: dependencies: js-tokens: 9.0.1 + supports-color@10.0.0: {} + supports-color@9.4.0: {} supports-preserve-symlinks-flag@1.0.0: {} @@ -5308,6 +4848,11 @@ snapshots: tinyexec@0.3.2: {} + tinyglobby@0.2.12: + dependencies: + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -5335,6 +4880,8 @@ snapshots: ufo@1.5.4: {} + ultrahtml@1.5.3: {} + uncrypto@0.1.3: {} unctx@2.4.1: @@ -5346,23 +4893,21 @@ snapshots: undici-types@6.20.0: {} - unenv@1.10.0: + unenv@2.0.0-rc.14: dependencies: - consola: 3.4.0 defu: 6.1.4 - mime: 3.0.0 - node-fetch-native: 1.6.6 - pathe: 1.1.2 + exsolve: 1.0.4 + ohash: 2.0.11 + pathe: 2.0.3 + ufo: 1.5.4 unicorn-magic@0.3.0: {} - unimport@3.14.6(rollup@4.34.8): + unimport@4.1.2: dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.34.8) acorn: 8.14.0 escape-string-regexp: 5.0.0 estree-walker: 3.0.3 - fast-glob: 3.3.3 local-pkg: 1.0.0 magic-string: 0.30.17 mlly: 1.7.4 @@ -5370,10 +4915,10 @@ snapshots: picomatch: 4.0.2 pkg-types: 1.3.1 scule: 1.3.0 - strip-literal: 2.1.1 - unplugin: 1.16.1 - transitivePeerDependencies: - - rollup + strip-literal: 3.0.0 + tinyglobby: 0.2.12 + unplugin: 2.2.0 + unplugin-utils: 0.2.4 universal-github-app-jwt@2.2.0: {} @@ -5381,6 +4926,11 @@ snapshots: universalify@2.0.1: {} + unplugin-utils@0.2.4: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.2 + unplugin@1.16.1: dependencies: acorn: 8.14.0 @@ -5391,7 +4941,7 @@ snapshots: acorn: 8.14.0 webpack-virtual-modules: 0.6.2 - unstorage@1.15.0(db0@0.2.4(better-sqlite3@11.8.1))(ioredis@5.5.0): + unstorage@1.15.0(db0@0.3.1(better-sqlite3@11.9.0))(ioredis@5.6.0): dependencies: anymatch: 3.1.3 chokidar: 4.0.3 @@ -5402,8 +4952,8 @@ snapshots: ofetch: 1.4.1 ufo: 1.5.4 optionalDependencies: - db0: 0.2.4(better-sqlite3@11.8.1) - ioredis: 5.5.0 + db0: 0.3.1(better-sqlite3@11.9.0) + ioredis: 5.6.0 untun@0.1.3: dependencies: @@ -5411,18 +4961,13 @@ snapshots: consola: 3.4.0 pathe: 1.1.2 - untyped@1.5.2: + untyped@2.0.0: dependencies: - '@babel/core': 7.26.9 - '@babel/standalone': 7.26.9 - '@babel/types': 7.26.9 citty: 0.1.6 defu: 6.1.4 jiti: 2.4.2 knitwork: 1.2.0 scule: 1.3.0 - transitivePeerDependencies: - - supports-color unwasm@0.3.9: dependencies: @@ -5433,12 +4978,6 @@ snapshots: pkg-types: 1.3.1 unplugin: 1.16.1 - update-browserslist-db@1.1.2(browserslist@4.24.4): - dependencies: - browserslist: 4.24.4 - escalade: 3.2.0 - picocolors: 1.1.1 - uqr@0.1.2: {} uri-js-replace@1.0.1: {} @@ -5488,8 +5027,6 @@ snapshots: y18n@5.0.8: {} - yallist@3.1.1: {} - yallist@4.0.0: {} yallist@5.0.0: {} @@ -5510,6 +5047,18 @@ snapshots: yoctocolors@2.1.1: {} + youch-core@0.3.2: + dependencies: + '@poppinss/exception': 1.2.1 + error-stack-parser-es: 1.0.5 + + youch@4.1.0-beta.6: + dependencies: + '@poppinss/dumper': 0.6.3 + '@speed-highlight/core': 1.2.7 + cookie: 1.0.2 + youch-core: 0.3.2 + zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 diff --git a/server/api/documents/index.get.ts b/server/api/documents/index.get.ts index de6545c..dfbb3cb 100644 --- a/server/api/documents/index.get.ts +++ b/server/api/documents/index.get.ts @@ -1,13 +1,15 @@ import { Primitive } from "db0"; export default defineLazyEventHandler(async () => { - const db = await getDb(); + const dbService = await getDatabaseService(); const { embeddingContext, rankingContext } = await useAiContext(); + return defineEventHandler(async (event) => { // Get query parameters const query = getQuery(event); const q = [query.q].flat().join(" "); const n = parseInt(query.n as string) || 5; // Default to 5 if not provided + const indexName = query.index as string || 'default'; // Default to the default index if (!q) { return { @@ -17,65 +19,73 @@ export default defineLazyEventHandler(async () => { } try { - // Calculate k for retrieval (n+1) * 5 - const k = (n + 1) * 5; - - // Calculate embedding for the query console.log( `[${new Date().toISOString()}] Calculating embedding for query:`, q ); const embedding = await embeddingContext.getEmbeddingFor(q); - // Find closest documents in the database using cosine distance + // Calculate k for retrieval (n+1) * 5 for reranking + const k = (n + 1) * 5; + console.log( `[${new Date().toISOString()}] Searching for documents similar to:`, - q + q, + `using index: ${indexName}` ); - const documents = await db.sql<{ - rows: Array<{ id: number; content: string; metadata: string }>; - }>` - SELECT id, content, json(metadata) metadata FROM documents - ORDER BY vec_distance_cosine(embedding, ${ - new Float32Array(embedding.vector) as unknown as Primitive - }) ASC - LIMIT ${k} - `; - if (!documents || documents.rows.length === 0) { + // Get candidate documents + const documents = await dbService.searchDocumentsByVector( + embedding.vector, + indexName, + k + ); + + if (!documents.length) { return { results: [] }; } - // Rerank the results using the reranker + // Get the index for property path + const index = await dbService.getIndexByName(indexName); + if (!index) { + throw new Error(`Index "${indexName}" not found`); + } + + // Extract content for reranking + const contents: string[] = []; + for (const doc of documents) { + const text = await dbService.extractTextByPropertyPath(doc.id, index.indexedPropertyPath); + contents.push(text || ''); + } + + // Rerank the results console.log( `[${new Date().toISOString()}] Reranking ${ - documents.rows.length + documents.length } documents` ); - const scores = await rankingContext.rankAll( - q, - documents.rows.map((doc) => doc.content) - ); - const results = documents.rows - .map((document, i) => ({ - document: { ...document, metadata: JSON.parse(document.metadata) }, - score: scores[i], + const scores = await rankingContext.rankAll(q, contents); + + // Combine and return results + const results = documents + .map((doc, i) => ({ + document: doc.data, + score: scores[i] })) .slice(0, n); - // Return the n highest ranked results + console.log( `[${new Date().toISOString()}] Found ${results.length} results` ); - return { - results, - }; + + return { results }; } catch (error) { console.error("Error searching documents:", error); throw createError({ - statusCode: 500, - statusMessage: "Internal Server Error", + statusCode: error.message?.includes('not found') ? 400 : 500, + statusMessage: error.message?.includes('not found') ? "Bad Request" : "Internal Server Error", cause: error, - message: "Failed to search documents", + message: error.message || "Failed to search documents", }); } }); diff --git a/server/api/documents/index.post.ts b/server/api/documents/index.post.ts index 3ae88bb..d87e40a 100644 --- a/server/api/documents/index.post.ts +++ b/server/api/documents/index.post.ts @@ -1,8 +1,9 @@ import { Primitive } from "db0"; export default defineLazyEventHandler(async () => { - const db = await getDb(); + const dbService = await getDatabaseService(); const { embeddingContext } = await useAiContext(); + return defineEventHandler(async (event) => { const { disableWrite } = useRuntimeConfig(event); if (disableWrite) { @@ -12,9 +13,12 @@ export default defineLazyEventHandler(async () => { message: "Write operations are disabled", }); } - const { documents } = await readBody<{ - documents: { content: string; metadata: unknown }[]; + + const { documents, indices } = await readBody<{ + documents: Record[]; + indices?: string[]; }>(event); + if (!Array.isArray(documents) || documents.length === 0) { throw createError({ statusCode: 400, @@ -23,26 +27,83 @@ export default defineLazyEventHandler(async () => { }); } - const embeddedDocuments = await Promise.all( - documents.map(async (document) => { - const embedding = await embeddingContext.getEmbeddingFor( - document.content - ); - return { ...document, embedding }; - }) - ); - const prepst = db.prepare( - `insert into documents (content, metadata, embedding) values ( ?, jsonb(?), ?)` - ); - for (const { content, metadata, embedding } of embeddedDocuments) { - const { success } = await prepst.run( - content, - JSON.stringify(metadata), - new Float32Array(embedding.vector) as unknown as Primitive - ); - if (!success) { - throw new Error("Failed to insert document"); + try { + // Insert each document and its embeddings + let insertedCount = 0; + + // Track which indices were used + const indexUsageSummary: Record = {}; + + for (const document of documents) { + // Insert the document + const documentId = await dbService.addDocument(document); + + // Find all applicable indices for this document + const applicableIndices = await dbService.findApplicableIndices(document); + + // If indices parameter was provided, intersect with applicable indices + // Otherwise, use all applicable indices + const indicesToUse = indices && indices.length > 0 + ? applicableIndices.filter(idx => indices.includes(idx.name)) + : applicableIndices; + + if (indicesToUse.length === 0) { + console.warn(`Document ${documentId} has no applicable indices${ + indices ? ' among the requested indices' : '' + }`); + } + + // Create embeddings for each applicable index + const embeddingEntries = []; + + for (const index of indicesToUse) { + // Extract text using property path + const textContent = await dbService.extractTextByPropertyPath( + documentId, + index.indexedPropertyPath + ); + + if (textContent) { + const embedding = await embeddingContext.getEmbeddingFor(textContent); + + embeddingEntries.push({ + documentId, + indexId: index.id, + vector: embedding.vector + }); + + // Update usage summary + indexUsageSummary[index.name] = (indexUsageSummary[index.name] || 0) + 1; + } + } + + // Add all embeddings in one batch + if (embeddingEntries.length > 0) { + await dbService.addEmbeddings(embeddingEntries); + } + + insertedCount++; } + + // Create a summary of which indices were used + const indexSummary = Object.entries(indexUsageSummary) + .map(([name, count]) => `${name} (${count}/${insertedCount})`) + .join(', '); + + return { + success: true, + count: insertedCount, + indices: indexSummary || "No applicable indices found" + }; + + } catch (error) { + console.error("Error inserting documents:", error); + throw createError({ + statusCode: 500, + statusMessage: "Internal Server Error", + cause: error, + message: error.message || "Failed to insert documents", + }); } }); }); diff --git a/server/api/indices/index.ts b/server/api/indices/index.ts new file mode 100644 index 0000000..38c9dc0 --- /dev/null +++ b/server/api/indices/index.ts @@ -0,0 +1,97 @@ +export default defineLazyEventHandler(async () => { + const dbService = await getDatabaseService(); + const { embeddingContext } = await useAiContext(); + + return defineEventHandler(async (event) => { + const method = event.method; + const { disableWrite } = useRuntimeConfig(event); + + // GET - List all indices + if (method === "GET") { + const indices = await dbService.listIndices(); + return indices; + } + + // POST - Create a new index + if (method === "POST") { + if (disableWrite) { + throw createError({ + statusCode: 403, + statusMessage: "Forbidden", + message: "Write operations are disabled", + }); + } + + const body = await readBody(event); + + if (!body.name || !body.indexedPropertyPath) { + throw createError({ + statusCode: 400, + statusMessage: "Bad Request", + message: "Name and indexedPropertyPath are required", + }); + } + + try { + const result = await dbService.createIndex( + body.name, + body.indexedPropertyPath, + body.description + ); + + while (true) { + const documentsForIndexing = await dbService.getDocumentsForIndexing( + body.name + ); + if (!documentsForIndexing.length) { + break; + } + // Index the documents + const embeddingEntries = await Promise.all( + documentsForIndexing.map(async (d) => { + const embedding = await embeddingContext.getEmbeddingFor( + d.textContent + ); + return { + documentId: d.id, + indexId: result.id, + vector: embedding.vector, + }; + }) + ); + await dbService.addEmbeddings(embeddingEntries); + } + return result; + } catch (error) { + if (error.message?.includes("UNIQUE constraint failed")) { + throw createError({ + statusCode: 409, + statusMessage: "Conflict", + message: `Index with name "${body.name}" already exists`, + }); + } + + if (error.message?.includes("Invalid property path")) { + throw createError({ + statusCode: 400, + statusMessage: "Bad Request", + message: error.message, + }); + } + + throw createError({ + statusCode: 500, + statusMessage: "Internal Server Error", + message: error.message || "Failed to create index", + cause: error, + }); + } + } + + throw createError({ + statusCode: 405, + statusMessage: "Method Not Allowed", + message: `Method ${method} not allowed`, + }); + }); +}); diff --git a/server/database/migrations/001-initialize-schema/index.ts b/server/database/migrations/001-initialize-schema/index.ts new file mode 100644 index 0000000..f2d5f86 --- /dev/null +++ b/server/database/migrations/001-initialize-schema/index.ts @@ -0,0 +1,148 @@ +import type { Connector, Database } from "db0"; +import type { Database as BetterSqlite3Database } from "better-sqlite3"; +import { Migration } from "../../schema"; +type Db = Database>; +export const migration: Migration = { + name: "initialize_multi_index_schema", + version: 1, + async up(db: Db) { + // Check if we need to migrate from the old schema + const needsMigration = await checkIfOldSchemaExists(db); + + if (needsMigration) { + await migrateFromOldSchema(db); + } else { + await initializeSchema(db); + } + }, + + // Down method is not implemented as we don't plan to support reverting to old schema + async down() { + throw new Error("Downgrade not supported for this migration"); + }, +}; + +async function checkIfOldSchemaExists(db: Db) { + try { + // Check if the documents table exists with the old schema + const tableInfo = await db.sql`select * from pragma_table_info('documents')`; + + if (tableInfo.rows.length === 0) { + // Table doesn't exist yet, no migration needed + return false; + } + + // Check if this is the old schema (has content, metadata, embedding columns) + const hasOldColumns = + tableInfo.rows.some((col) => col.name === "content") && + tableInfo.rows.some((col) => col.name === "metadata") && + tableInfo.rows.some((col) => col.name === "embedding"); + + return hasOldColumns; + } catch (error) { + // If there's an error checking the schema, assume no migration needed + console.error("Error checking schema:", error); + return false; + } +} + +async function migrateFromOldSchema(db: Db) { + console.log( + "[Migration] Starting migration from old schema to multi-index schema" + ); + + // Start transaction - note that we don't need a transaction here as the parent already started one + + try { + // Temporarily rename the old table + await db.sql`ALTER TABLE documents RENAME TO documents_old`; + + // Create the new schema + await initializeSchema(db); + + // Create the default index + const indexResult = await db.sql` + INSERT INTO indices (name, indexed_property_path, description) + VALUES ('default', '$.content', 'Default index migrated from previous schema') + RETURNING id + `; + + if (!indexResult.rows.length) { + throw new Error("Failed to create default index during migration"); + } + + const defaultIndexId = indexResult.rows[0].id; + + // Migrate the documents + console.log("[Migration] Migrating documents and embeddings"); + const oldDocs = + await db.sql`SELECT id, content, metadata, embedding FROM documents_old`; + + for (const doc of oldDocs.rows) { + let metadata = {}; + try { + // Parse the metadata JSON + metadata = JSON.parse(String(doc.metadata)); + } catch (e) { + console.warn( + `[Migration] Warning: Failed to parse metadata for document ${doc.id}` + ); + } + + // Create new document with content merged into metadata + const newData = { content: doc.content, ...metadata }; + + // Insert the document + await db.sql` + INSERT INTO documents (id, data) + VALUES (${doc.id}, ${JSON.stringify(newData)}) + `; + + // Insert the embedding if it exists + if (doc.embedding) { + await db.sql` + INSERT INTO embeddings (document_id, index_id, vector) + VALUES (CAST(${doc.id} as integer), CAST(${defaultIndexId} as integer), ${doc.embedding as any}) + `; + } + } + + // Drop the old table + await db.sql`DROP TABLE documents_old`; + + console.log("[Migration] Migration completed successfully"); + } catch (error) { + console.error("[Migration] Migration failed:", error); + throw error; + } +} + +async function initializeSchema(db: Db) { + await db.sql` + -- Create the core tables if they don't exist + CREATE TABLE IF NOT EXISTS documents ( + id INTEGER PRIMARY KEY, + data JSONB NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + );`; + + await db.sql` + -- Table for index definitions + CREATE TABLE IF NOT EXISTS indices ( + id INTEGER PRIMARY KEY, + name TEXT UNIQUE NOT NULL, + indexed_property_path TEXT NOT NULL, + description TEXT + );`; + + await db.sql`-- Virtual table for embeddings using sqlite-vec + CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0( + id INTEGER PRIMARY KEY, + document_id INTEGER NOT NULL, + index_id INTEGER partition key, + vector float[1024] + -- foreign keys are not supported by vec0 virtual tables + -- FOREIGN KEY(document_id) REFERENCES documents(id) ON DELETE CASCADE, + -- FOREIGN KEY(index_id) REFERENCES indices(id) ON DELETE CASCADE + );`; +} diff --git a/server/database/schema.ts b/server/database/schema.ts new file mode 100644 index 0000000..ae85796 --- /dev/null +++ b/server/database/schema.ts @@ -0,0 +1,68 @@ +import type { Connector, Database } from "db0"; +import type { Database as BetterSqlite3Database } from "better-sqlite3"; + +// Define Migration interface +export interface Migration { + name: string; + version: number; + up: (db: Database>) => Promise; + down?: (db: Database>) => Promise; +} + +// List of migrations (imported lazily) +export const migrations = [ + () => import('./migrations/001-initialize-schema').then(m => m.migration), + // Add more migrations as they are created +]; + +export async function migrate(db: Database>) { + // Create migrations table if it doesn't exist + await db.sql` + CREATE TABLE IF NOT EXISTS migrations ( + id INTEGER PRIMARY KEY, + version INTEGER NOT NULL, + name TEXT NOT NULL, + applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + `; + + // Get the currently applied migrations + const appliedMigrations = await db.sql` + SELECT version, name FROM migrations ORDER BY version ASC + `; + + const appliedVersions = new Set(appliedMigrations.rows.map(m => m.version)); + + // Apply pending migrations in order + for (const getMigration of migrations) { + const migration = await getMigration(); + + if (!appliedVersions.has(migration.version)) { + console.log(`[Migration] Applying migration: ${migration.name} (v${migration.version})`); + + // Start transaction + await db.sql`BEGIN TRANSACTION`; + + try { + // Run the migration + await migration.up(db); + + // Record the migration + await db.sql` + INSERT INTO migrations (version, name) + VALUES (${migration.version}, ${migration.name}) + `; + + await db.sql`COMMIT`; + console.log(`[Migration] Successfully applied migration: ${migration.name}`); + } catch (error) { + // Rollback on failure + await db.sql`ROLLBACK`; + console.error(`[Migration] Failed to apply migration ${migration.name}:`, error); + throw error; + } + } + } + + console.log('[Migration] Database is up to date'); +} \ No newline at end of file diff --git a/server/utils/DatabaseService.ts b/server/utils/DatabaseService.ts new file mode 100644 index 0000000..3dc33e6 --- /dev/null +++ b/server/utils/DatabaseService.ts @@ -0,0 +1,251 @@ +import type { Connector, Database, Primitive } from "db0"; +import type { Database as BetterSqlite3Database } from "better-sqlite3"; + +type Db = Database>; + +export interface DocumentIndex { + id: number; + name: string; + indexedPropertyPath: string; + description?: string; + documentCount?: number; +} + +export interface DocumentWithData { + id: number; + data: any; +} + +export interface DocumentForIndexing { + id: number; + textContent: string; +} + +export interface EmbeddingEntry { + documentId: number; + indexId: number; + vector: readonly number[] | Float32Array; +} + +export class DatabaseService { + private db: Db; + + constructor(db: Db) { + this.db = db; + } + + // Document operations + async addDocument(data: any): Promise { + const result = await this.db.sql` + INSERT INTO documents (data) VALUES (jsonb(${JSON.stringify(data)})) + RETURNING id + `; + + if (!result || !result.rows.length) { + throw new Error("Failed to insert document"); + } + + return +result.rows[0].id; + } + + async addEmbeddings(entries: EmbeddingEntry[]): Promise { + let insertedCount = 0; + + // Process each embedding entry + for (const entry of entries) { + await this.db.sql` + INSERT INTO embeddings (document_id, index_id, vector) + VALUES ( + CAST(${entry.documentId} as integer), + CAST(${entry.indexId} as integer), + ${new Float32Array(entry.vector) as unknown as Primitive} + ) + `; + + insertedCount++; + } + + return insertedCount; + } + + async searchDocumentsByVector( + vector: number[] | Float32Array, + indexName: string, + limit: number = 10 + ): Promise { + // First get the index ID + const indexQuery = await this.db.sql` + SELECT id FROM indices WHERE name = ${indexName} LIMIT 1 + `; + + if (!indexQuery.rows.length) { + throw new Error(`Index "${indexName}" not found`); + } + + const indexId = indexQuery.rows[0].id; + + // Search for documents by vector similarity + const documents = await this.db.sql` + SELECT d.id, JSON(d.data) as data + FROM documents d + JOIN embeddings e ON d.id = e.document_id + WHERE e.index_id = ${indexId} + ORDER BY vec_distance_cosine(e.vector, ${ + new Float32Array(vector) as unknown as Primitive + }) ASC + LIMIT ${limit} + `; + + // Parse the document data + return documents.rows.map(row => ({ + id: +row.id, + data: JSON.parse(String(row.data)) + })); + } + + // Index operations + async createIndex(name: string, indexedPropertyPath: string, description?: string): Promise { + // TODO Validate the property path by making sure it's a valid JSON path + try { + await this.db.sql`SELECT json_extract('{"test": "value"}', ${indexedPropertyPath})`; + } catch (error) { + throw new Error(`Invalid property path: ${indexedPropertyPath}`); + } + + const result = await this.db.sql` + INSERT INTO indices (name, indexed_property_path, description) + VALUES (${name}, ${indexedPropertyPath}, ${description || null}) + RETURNING + id, + name, + indexed_property_path as indexedPropertyPath, + description + `; + + if (!result.rows.length) { + throw new Error(`Failed to create index "${name}"`); + } + + return result.rows[0] as unknown as DocumentIndex; + } + + async listIndices(): Promise { + const result = await this.db.sql` + SELECT + id, + name, + indexed_property_path as indexedPropertyPath, + description, + (SELECT COUNT(*) FROM embeddings WHERE index_id = indices.id) as documentCount + FROM indices + ORDER BY name + `; + + return result.rows as unknown as DocumentIndex[]; + } + + async getIndexByName(name: string): Promise { + const result = await this.db.sql` + SELECT + id, + name, + indexed_property_path as indexedPropertyPath, + description + FROM indices + WHERE name = ${name} + LIMIT 1 + `; + + return result.rows.length ? (result.rows[0] as unknown as DocumentIndex) : null; + } + + async getDocumentsForIndexing( + indexName: string, + batchSize: number = 100 + ): Promise { + // Get the index + const index = await this.getIndexByName(indexName); + if (!index) { + throw new Error(`Index "${indexName}" not found`); + } + + // Get documents that don't have embeddings for this index + const query = await this.db.sql` + SELECT + d.id, + JSON_EXTRACT(d.data, ${index.indexedPropertyPath}) as textContent + FROM documents d + WHERE + NOT EXISTS ( + SELECT 1 FROM embeddings e + WHERE e.document_id = d.id AND e.index_id = ${index.id} + ) + AND JSON_EXTRACT(d.data, ${index.indexedPropertyPath}) IS NOT NULL + AND JSON_TYPE(d.data, ${index.indexedPropertyPath}) = 'text' + LIMIT ${batchSize} + `; + + return query.rows as unknown as DocumentForIndexing[]; + } + + async extractTextByPropertyPath(documentId: number, propertyPath: string): Promise { + const result = await this.db.sql` + SELECT JSON_EXTRACT(data, ${propertyPath}) as textContent + FROM documents + WHERE id = ${documentId} + `; + + return result.rows.length ? String(result.rows[0].textContent) : null; + } + + /** + * Find all indices that can be applied to a document based on its structure + * @param document Document ID or document object + * @returns Array of applicable indices + */ + async findApplicableIndices(document: number | any): Promise { + // If document is a number (ID), use it directly in the SQL query + // Otherwise, we'll need to pass the JSON data + if (typeof document === 'number') { + const result = await this.db.sql` + SELECT + i.id, + i.name, + i.indexed_property_path as indexedPropertyPath, + i.description, + (SELECT COUNT(*) FROM embeddings WHERE index_id = i.id) as documentCount + FROM indices i + WHERE EXISTS ( + SELECT 1 + FROM documents d + WHERE d.id = ${document} + AND JSON_TYPE(d.data, i.indexed_property_path) = 'text' + ) + ORDER BY i.name + `; + + return result.rows as unknown as DocumentIndex[]; + } else { + // Document is an object, we'll stringify it + const docJson = JSON.stringify(document); + + const result = await this.db.sql` + SELECT + i.id, + i.name, + i.indexed_property_path as indexedPropertyPath, + i.description, + 0 as documentCount + FROM indices i + WHERE JSON_TYPE(${docJson}, i.indexed_property_path) = 'text' + ORDER BY i.name + `; + + return result.rows as unknown as DocumentIndex[]; + } + } +} + +export function createDatabaseService(db: Db): DatabaseService { + return new DatabaseService(db); +} diff --git a/server/utils/getDatabaseService.ts b/server/utils/getDatabaseService.ts new file mode 100644 index 0000000..fc6b91a --- /dev/null +++ b/server/utils/getDatabaseService.ts @@ -0,0 +1,14 @@ +import { createDatabaseService, DatabaseService } from './DatabaseService'; +import getDb from './getDb'; + +let service: DatabaseService | null = null; + +export async function getDatabaseService(): Promise { + if (service) return service; + + const db = await getDb(); + service = createDatabaseService(db); + return service; +} + +export default getDatabaseService; diff --git a/server/utils/getDb.ts b/server/utils/getDb.ts index b42ad83..a17186d 100644 --- a/server/utils/getDb.ts +++ b/server/utils/getDb.ts @@ -1,20 +1,24 @@ import { getLoadablePath, load } from "sqlite-vec"; import type { Connector, Database } from "db0"; import type { Database as BetterSqlite3Database } from "better-sqlite3"; +import { migrate } from "../database/schema"; +import { load as sqliteVecLoad } from "sqlite-vec"; + let initialized = false; + export async function getDb() { const db: Database> = useDatabase(); - if (!initialized) { - initialized = true; - const instance = - (await db.getInstance()) as unknown as BetterSqlite3Database; - load(instance); - await db.sql`create table if not exists documents ( - id integer primary key autoincrement, - content text not null, - metadata blob not null, - embedding blob not null check(vec_length(embedding) > 0));`; - } + + if (initialized) return db; + + // Load sqlite-vec extension + sqliteVecLoad(await db.getInstance()); + + // Run migrations + await migrate(db); + + initialized = true; return db; } + export default getDb; diff --git a/test/api-compatibility.test.ts b/test/api-compatibility.test.ts new file mode 100644 index 0000000..4dcd98c --- /dev/null +++ b/test/api-compatibility.test.ts @@ -0,0 +1,208 @@ +import { describe, it, beforeEach, after } from "node:test"; +import assert from "node:assert"; +import { Connector, createDatabase, Database, Primitive } from "db0"; +import sqlite from "db0/connectors/better-sqlite3"; +import { migration } from "../server/database/migrations/001-initialize-schema"; +import { Database as BetterSqlite3Database } from "better-sqlite3"; +import { load as sqliteVecLoad } from "sqlite-vec"; + +type Db = Database>; +// Mock AiContext for tests +class MockAiContext { + async getEmbeddingFor(text: string) { + // Return a fake embedding + return { + vector: new Array(1024).fill(0.25), + text, + }; + } + + async rankAll(query: string, texts: string[]) { + // Return mock scores + return texts.map((_, i) => 1.0 - i * 0.1); + } +} + +describe("API Compatibility Tests", () => { + let db: Db; + let mockEmbeddingContext; + + beforeEach(async () => { + // Create a fresh in-memory database for each test + db = createDatabase(sqlite({ name: ":memory:" })); + sqliteVecLoad(await db.getInstance()); + + // Apply migration + await migration.up(db); + + // Create mock AI context + mockEmbeddingContext = new MockAiContext(); + }); + + after(async () => { + if (db) { + // DB0 does not support connection closing. + // await db.close(); + } + }); + + it("should handle documents with content field format", async () => { + try { + // Test document in the old format (content in root) + const oldFormatDoc = { + content: "This is a test document", + metadata: { title: "Test" }, + }; + + // Insert the document + const docResult = await db.sql` + INSERT INTO documents (data) + VALUES (jsonb(${JSON.stringify(oldFormatDoc)})) + RETURNING id + `; + + const docId = docResult.rows[0].id; + + // Create the default index + const indexResult = await db.sql` + INSERT INTO indices (name, indexed_property_path, description) + VALUES ('default', '$.content', 'Default index migrated from previous schema') + RETURNING id + `; + const indexId = indexResult.rows[0].id; + + // Create an embedding + const embedding = await mockEmbeddingContext.getEmbeddingFor( + oldFormatDoc.content + ); + + // Insert the embedding + await db.sql` + INSERT INTO embeddings (document_id, index_id, vector) + VALUES (CAST(${docId} as integer), CAST(${indexId} as integer), ${ + new Float32Array(embedding.vector) as unknown as Primitive + }) + `; + + // Now test that we can search and retrieve this document + // This would test the GET endpoint functionality + + // First, generate a query embedding + const queryEmbedding = await mockEmbeddingContext.getEmbeddingFor( + "test document" + ); + + // Search + const searchResults = await db.sql` + SELECT d.id, JSON(d.data) as data + FROM documents d + JOIN embeddings e ON d.id = e.document_id + WHERE e.index_id = ${indexId} + ORDER BY vec_distance_cosine(e.vector, ${ + new Float32Array(queryEmbedding.vector) as unknown as Primitive + }) ASC + LIMIT 5 + `; + + // Verify results + assert.strictEqual( + searchResults.rows.length, + 1, + "should find the document" + ); + + // Extract content for reranking (simulating API endpoint behavior) + const content = JSON.parse(String(searchResults.rows[0].data)).content; + assert.strictEqual( + content, + "This is a test document", + "should extract content correctly" + ); + } catch (e) { + console.error(e); + throw e; + } + }); + + it("should handle documents with nested content field", async () => { + try { + // Test document with nested content structure + const nestedDoc = { + details: { + content: "This is a nested document", + language: "en", + }, + }; + + // Insert the document + const docResult = await db.sql` + INSERT INTO documents (data) + VALUES (jsonb(${JSON.stringify(nestedDoc)})) + RETURNING id + `; + + const docId = docResult.rows[0].id; + + // Create a custom index for the nested path + const newIndexResult = await db.sql` + INSERT INTO indices (name, indexed_property_path, description) + VALUES ('nested_content', '$.details.content', 'Index for nested content field') + RETURNING id + `; + + const nestedIndexId = newIndexResult.rows[0].id; + + // Create an embedding for the nested content + const embedding = await mockEmbeddingContext.getEmbeddingFor( + nestedDoc.details.content + ); + + // Insert the embedding + await db.sql` + INSERT INTO embeddings (document_id, index_id, vector) + VALUES (CAST(${docId} as integer), CAST(${nestedIndexId} as integer), ${ + new Float32Array(embedding.vector) as unknown as Primitive + }) + `; + + // Search using the nested index + const queryEmbedding = await mockEmbeddingContext.getEmbeddingFor( + "nested document" + ); + + const searchResults = await db.sql` + SELECT d.id, JSON(d.data) as data + FROM documents d + JOIN embeddings e ON d.id = e.document_id + WHERE e.index_id = ${nestedIndexId} + ORDER BY vec_distance_cosine(e.vector, ${ + new Float32Array(queryEmbedding.vector) as unknown as Primitive + }) ASC + LIMIT 5 + `; + + // Verify results + assert.strictEqual( + searchResults.rows.length, + 1, + "should find the document" + ); + + // Extract nested content (simulating API endpoint behavior) + const contentQuery = await db.sql` + SELECT ${String( + searchResults.rows[0].data + )} ->> '$.details.content' as text_content + `; + + assert.strictEqual( + contentQuery.rows[0].text_content, + "This is a nested document", + "should extract nested content correctly" + ); + } catch (e) { + console.error(e); + throw e; + } + }); +}); diff --git a/test/api-integration.test.js b/test/api-integration.test.js new file mode 100644 index 0000000..e95dd97 --- /dev/null +++ b/test/api-integration.test.js @@ -0,0 +1,300 @@ +import { describe, it, before, after } from "node:test"; +import { spawn, execSync } from "node:child_process"; +import { mkdir, rm } from "node:fs/promises"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import assert from "node:assert"; + +describe("API Integration Tests", { timeout: 120000 }, () => { + let serverProcess; + let tempDbDir; + const serverPort = 3456; // Using a non-default port to avoid conflicts + + // Helper function to execute curl commands with improved argument handling + const curl = (...args) => { + try { + // Quote each non-empty argument to prevent shell interpretation issues + const quotedArgs = args + .filter((arg) => arg.trim() !== "") // Skip empty arguments + .map((arg) => `'${arg}'`); // Wrap each in single quotes + + const command = `curl -s -H "Content-Type: application/json" ${quotedArgs.join( + " " + )}`; + console.log(`Executing: ${command}`); + + return JSON.parse( + execSync(command, { + encoding: "utf-8", + }) + ); + } catch (error) { + console.error(`Failed curl command: ${error.message}`); + console.error(`Arguments: ${args.join(" ")}`); + throw error; + } + }; + + // Setup before all tests + before(async () => { + // Create temporary directory for database + tempDbDir = join(tmpdir(), `minivec-test-${Date.now()}`); + await mkdir(tempDbDir, { recursive: true }); + + console.log(`Using temporary database directory: ${tempDbDir}`); + + // // Start the Nitro server with the temporary database directory + // serverProcess = spawn("pnpm", ["dev", "--port", serverPort], { + // env: { + // ...process.env, + // NITRO_PORT: serverPort, + // }, + // stdio: ["ignore", "pipe", "pipe"], + // }); + + // // Log server output for debugging + // serverProcess.stdout.on("data", (data) => { + // console.log(`[SERVER]: ${data.toString().trim()}`); + // }); + + // serverProcess.stderr.on("data", (data) => { + // console.error(`[SERVER ERROR]: ${data.toString().trim()}`); + // }); + + // // Wait for server to start - allow more time for model loading + // await new Promise((resolve) => { + // const checkServer = () => { + // try { + // execSync(`curl -s http://localhost:${serverPort}/`, { + // stdio: "ignore", + // }); + // // Wait a bit longer to ensure models are fully loaded + // setTimeout(resolve, 2000); + // } catch (e) { + // // Server not ready yet, retry after delay + // setTimeout(checkServer, 500); + // } + // }; + + // setTimeout(checkServer, 3000); // Initial delay to let server start + // }); + + console.log(`Server started on port ${serverPort}`); + }); + + // Cleanup after all tests + after(async () => { + if (serverProcess) { + console.log("Shutting down server..."); + serverProcess.kill("SIGTERM"); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Give server time to shutdown gracefully + } + + // Remove temporary directory + try { + await rm(tempDbDir, { recursive: true, force: true }); + console.log(`Removed temporary directory: ${tempDbDir}`); + } catch (err) { + console.error(`Failed to remove temp directory: ${err}`); + } + }); + + // Test creating indices + it("should create and list indices", async () => { + // Create a test index + const createResponse = curl( + "-X", + "POST", + "-d", + JSON.stringify({ + name: "test-index", + indexedPropertyPath: "$.text", + description: "Test Index", + }), + `http://localhost:${serverPort}/api/indices` + ); + + assert.strictEqual(createResponse.name, "test-index"); + assert.strictEqual(createResponse.indexedPropertyPath, "$.text"); + + // List indices + const listResponse = curl(`http://localhost:${serverPort}/api/indices`); + + assert.ok(Array.isArray(listResponse), "Index list should be an array"); + assert.ok( + listResponse.some((idx) => idx.name === "test-index"), + "Should include our test index" + ); + }); + + // Test adding and searching documents + it("should add and search documents", async () => { + // Add documents + const docs = [ + { + text: "The quick brown fox jumps over the lazy dog", + title: "Fox and Dog", + }, + { + text: "A spacecraft landed on the moon in 1969", + title: "Moon Landing", + }, + { + text: "Machine learning algorithms can identify patterns in data", + title: "ML Intro", + }, + ]; + + const addResponse = curl( + "-X", + "POST", + "-d", + JSON.stringify({ documents: docs, indices: ["test-index"] }), + `http://localhost:${serverPort}/api/documents` + ); + + assert.strictEqual(addResponse.success, true); + assert.strictEqual(addResponse.count, 3); + + // Allow time for embedding generation (real models take longer) + await new Promise((resolve) => setTimeout(resolve, 5000)); + + // Search documents + const searchResponse = curl( + `http://localhost:${serverPort}/api/documents?q=fox+and+dog&index=test-index` + ); + + assert.ok( + Array.isArray(searchResponse.results), + "Results should be an array" + ); + assert.ok(searchResponse.results.length > 0, "Should return results"); + + // With real models, we can't deterministically know the exact order, + // but the fox document should be in the results somewhere + const hasFoxResult = searchResponse.results.some((result) => + result.document.text.toLowerCase().includes("fox") + ); + assert.ok(hasFoxResult, "Results should include the fox document"); + + // The scores should be between 0 and 1 + searchResponse.results.forEach((result) => { + assert.ok( + result.score >= 0 && result.score <= 1, + "Score should be between 0 and 1" + ); + }); + }); + + // Test building an index for existing documents + it("should build index for existing documents", async () => { + // Create another index + const createResponse = curl( + "-X", + "POST", + "-d", + JSON.stringify({ + name: "title-index", + indexedPropertyPath: "$.title", + description: "Title Index", + }), + `http://localhost:${serverPort}/api/indices` + ); + + assert.strictEqual(createResponse.name, "title-index"); + + // Build the index for existing documents + // const buildResponse = curl( + // "-X", + // "POST", + // `http://localhost:${serverPort}/api/indices/title-index/build` + // ); + + // assert.strictEqual(buildResponse.indexed, 3); + // assert.strictEqual(buildResponse.errors, 0); + + // Allow time for embedding generation (real models take longer) + // await new Promise((resolve) => setTimeout(resolve, 5000)); + + // Search using the new index + const searchResponse = curl( + `http://localhost:${serverPort}/api/documents?q=moon+landing&index=title-index` + ); + + assert.ok( + searchResponse.results.length > 0, + "Should return results from title index" + ); + + // The results should include the moon landing document somewhere + const hasMoonResult = searchResponse.results.some((result) => + result.document.title.toLowerCase().includes("moon") + ); + assert.ok( + hasMoonResult, + "Results should include the moon landing document" + ); + + // Try a different query to test semantic search capabilities + const spaceSearchResponse = curl( + `http://localhost:${serverPort}/api/documents?q=space+exploration&index=title-index` + ); + + // Even though "space exploration" isn't explicitly in the text, semantic search + // should still find the moon landing document + assert.ok( + spaceSearchResponse.results.length > 0, + "Should return semantic search results" + ); + }); + + // Test multi-index document handling + it("should support documents indexed in multiple indices", async () => { + // Add a document with content for both indices + const multiDoc = { + text: "SpaceX plans to send humans to Mars in the coming decade", + title: "Mars Mission Plans", + }; + + const addResponse = curl( + "-X", + "POST", + "-d", + JSON.stringify({ + documents: [multiDoc], + indices: ["test-index", "title-index"], + }), + `http://localhost:${serverPort}/api/documents` + ); + + assert.strictEqual(addResponse.success, true); + + // Allow time for embedding generation (real models take longer) + await new Promise((resolve) => setTimeout(resolve, 5000)); + + // Search in the text index + const textSearchResponse = curl( + `http://localhost:${serverPort}/api/documents?q=Mars+colonization&index=test-index` + ); + + // Search in the title index + const titleSearchResponse = curl( + `http://localhost:${serverPort}/api/documents?q=Mission+to+Mars&index=title-index` + ); + + // The document should be found in both indices + const foundInTextIndex = textSearchResponse.results.some((result) => + result.document.text.includes("Mars") + ); + + const foundInTitleIndex = titleSearchResponse.results.some((result) => + result.document.title.includes("Mars") + ); + + assert.ok(foundInTextIndex, "Document should be found through text index"); + assert.ok( + foundInTitleIndex, + "Document should be found through title index" + ); + }); +}); diff --git a/test/database-service.test.ts b/test/database-service.test.ts new file mode 100644 index 0000000..baac4cc --- /dev/null +++ b/test/database-service.test.ts @@ -0,0 +1,151 @@ +import { describe, it, beforeEach, after } from "node:test"; +import assert from "node:assert"; +import { createDatabase } from "db0"; +import sqlite from "db0/connectors/better-sqlite3"; +import { load as sqliteVecLoad } from "sqlite-vec"; +import { migration } from "../server/database/migrations/001-initialize-schema"; +import { createDatabaseService } from "../server/utils/DatabaseService"; + +describe("DatabaseService", () => { + let db; + let dbService; + + beforeEach(async () => { + // Create a fresh in-memory database for each test + db = createDatabase(sqlite({ name: ":memory:" })); + sqliteVecLoad(await db.getInstance()); + + // Apply migration + await migration.up(db); + + // Create database service + dbService = createDatabaseService(db); + + // Since the default index is no longer created automatically for fresh databases, + // we need to create it for our tests + await dbService.createIndex("default", "$.content", "Default index for tests"); + }); + + describe("Index operations", () => { + it("should list indices", async () => { + const indices = await dbService.listIndices(); + assert.strictEqual(indices.length, 1, "should have default index"); + assert.strictEqual(indices[0].name, "default", "should be named 'default'"); + }); + + it("should create new indices with valid paths", async () => { + await dbService.createIndex("test-index", "$.test", "Test index description"); + + const indices = await dbService.listIndices(); + assert.strictEqual(indices.length, 2, "should have two indices now"); + + const testIndex = indices.find(idx => idx.name === "test-index"); + assert.ok(testIndex, "should find the new index"); + assert.strictEqual(testIndex.indexedPropertyPath, "$.test", "should have correct path"); + }); + + it("should reject invalid property paths", async () => { + try { + await dbService.createIndex("invalid-index", "invalid-path", "Bad path"); + assert.fail("Should have rejected invalid path"); + } catch (error) { + assert.ok(error.message.includes("Invalid property path"), "should have proper error message"); + } + }); + }); + + describe("Document operations", () => { + it("should add documents and retrieve them by vector search", async () => { + // Add a document + const docId = await dbService.addDocument({ content: "Test content" }); + + // Add an embedding for it + const mockVector = new Array(1024).fill(0.1); + const indexId = (await dbService.getIndexByName("default"))!.id; + + await dbService.addEmbeddings([{ + documentId: docId, + indexId, + vector: mockVector + }]); + + // Search using the same vector + const results = await dbService.searchDocumentsByVector(mockVector, "default"); + + assert.strictEqual(results.length, 1, "should find one document"); + assert.strictEqual(results[0].id, docId, "should be the same document"); + assert.strictEqual(results[0].data.content, "Test content", "should have correct content"); + }); + + it("should identify documents that need indexing", async () => { + // Create a new index + const newIndex = await dbService.createIndex("summary-index", "$.summary", "Summary index"); + + // Add documents with both content and summary + const docId1 = await dbService.addDocument({ content: "Content 1", summary: "Summary 1" }); + const docId2 = await dbService.addDocument({ content: "Content 2" }); // No summary + const docId3 = await dbService.addDocument({ content: "Content 3", summary: "Summary 3" }); + + // Only add embedding for first document + await dbService.addEmbeddings([{ + documentId: docId1, + indexId: newIndex!.id, + vector: new Array(1024).fill(0.1) + }]); + + // Check which documents need indexing for summary-index + const docsNeedingIndexing = await dbService.getDocumentsForIndexing("summary-index"); + + // Should only return doc3 (doc1 already has embedding, doc2 has no summary field) + assert.strictEqual(docsNeedingIndexing.length, 1, "should find one document needing indexing"); + assert.strictEqual(docsNeedingIndexing[0].id, docId3, "should be the third document"); + assert.strictEqual(docsNeedingIndexing[0].textContent, "Summary 3", "should extract correct content"); + + // check also the content index + const docsNeedingIndexingContent = await dbService.getDocumentsForIndexing("default"); + assert.strictEqual(docsNeedingIndexingContent.length, 3, "should find three documents needing indexing"); + assert.strictEqual(docsNeedingIndexingContent[0].id, docId1, "should be the first document"); + assert.strictEqual(docsNeedingIndexingContent[1].id, docId2, "should be the second document"); + assert.strictEqual(docsNeedingIndexingContent[2].id, docId3, "should be the third document"); + }); + + it('should find applicable indices for a document', async () => { + // Create multiple indices + await dbService.createIndex('content-index', '$.content', 'Content index'); + await dbService.createIndex('title-index', '$.title', 'Title index'); + await dbService.createIndex('nested-index', '$.details.text', 'Nested property index'); + + // Add a document with multiple applicable fields + const docId = await dbService.addDocument({ + content: 'Document content', + title: 'Document title', + details: { + text: 'Nested text content', + number: 42 + }, + tags: ['test', 'document'] + }); + + // Find applicable indices by document ID + const indicesById = await dbService.findApplicableIndices(docId); + assert.strictEqual(indicesById.length, 3, 'Should find 3 applicable indices for document ID'); + + const indexNames = indicesById.map(idx => idx.name).sort(); + assert.deepStrictEqual(indexNames, ['content-index', 'nested-index', 'title-index'].sort()); + + // Find applicable indices by document object + const docObject = { + content: 'Another document', + details: { + text: 'More nested content' + } + }; + + const indicesByObject = await dbService.findApplicableIndices(docObject); + assert.strictEqual(indicesByObject.length, 2, 'Should find 2 applicable indices for document object'); + assert.ok(indicesByObject.some(idx => idx.name === 'content-index'), 'Should include content index'); + assert.ok(indicesByObject.some(idx => idx.name === 'nested-index'), 'Should include nested index'); + assert.ok(!indicesByObject.some(idx => idx.name === 'title-index'), 'Should not include title index'); + }); + }); +}); diff --git a/test/database/migrations/001-initialize-schema.test.ts b/test/database/migrations/001-initialize-schema.test.ts new file mode 100644 index 0000000..7a6a80f --- /dev/null +++ b/test/database/migrations/001-initialize-schema.test.ts @@ -0,0 +1,204 @@ +import { describe, it, beforeEach, after } from "node:test"; +import assert from "node:assert"; +import { Connector, createDatabase, Database, Primitive } from "db0"; +import sqlite from "db0/connectors/better-sqlite3"; +import { migration } from "../../../server/database/migrations/001-initialize-schema"; +import { Database as BetterSqlite3Database } from "better-sqlite3"; +import { load as sqliteVecLoad } from "sqlite-vec"; + +const example_vec = new Float32Array(new Array(1024).map((_, i) => i % 256)); + +describe("Database Migrations", () => { + // Test database initialization on empty database + describe("Initialize Schema on Empty Database", () => { + let db; + + beforeEach(async () => { + // Create a fresh in-memory database for each test + db = createDatabase(sqlite({ name: ":memory:" })); + sqliteVecLoad(await db.getInstance()); + }); + + after(async () => { + // Clean up + if (db) { + // DB0 does not support connection closing. + // await db.close(); + } + }); + + it("should create tables and default index", async () => { + // Apply the migration + await migration.up(db); + + // Verify documents table exists with correct schema + const docTableInfo = + await db.sql`select * from pragma_table_info('documents')`; + assert.strictEqual( + docTableInfo.rows.length > 0, + true, + "documents table should exist" + ); + + // Check for id and data columns + const hasId = docTableInfo.rows.some((col) => col.name === "id"); + const hasData = docTableInfo.rows.some((col) => col.name === "data"); + assert.strictEqual(hasId, true, "documents table should have id column"); + assert.strictEqual( + hasData, + true, + "documents table should have data column" + ); + + // Verify indices table exists with default index + const indices = + await db.sql`SELECT name, indexed_property_path FROM indices`; + assert.strictEqual(indices.rows.length, 0, "should not create default index on empty database"); + + // Verify embeddings table exists + const embedTableExists = await db.sql` + SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings' + `; + assert.strictEqual( + embedTableExists.rows.length, + 1, + "embeddings table should exist" + ); + }); + }); + + // Test migration from old schema + describe("Migration from Old Schema", () => { + let db: Database>; + + beforeEach(async () => { + // Create a fresh in-memory database for each test + db = createDatabase(sqlite({ name: ":memory:" })); + sqliteVecLoad(await db.getInstance()); + + // Set up the old schema + await db.sql` + CREATE TABLE documents ( + id integer primary key autoincrement, + content text not null, + metadata text not null, + embedding blob not null + ) + `; + + // Insert some test data + await db.sql` + INSERT INTO documents (content, metadata, embedding) + VALUES ( + 'Test document content', + '{"title": "Test Document", "tags": ["test", "document"]}', + ${example_vec as unknown as Primitive} + ) + `; + }); + + after(async () => { + // Clean up + if (db) { + // DB0 does not support connection closing. We will have to leak the connection. + // await db.close(); + } + }); + + it("should migrate data from old schema to new schema", async () => { + // Apply the migration + await migration.up(db); + + // Check that old table is gone + const oldTableExists = await db.sql` + SELECT name FROM sqlite_master WHERE type='table' AND name='documents_old' + `; + assert.strictEqual( + oldTableExists.rows.length, + 0, + "documents_old table should be removed" + ); + + // Verify the new tables exist + const docTableInfo = + await db.sql`select * from pragma_table_info('documents')`; + assert.strictEqual( + docTableInfo.rows.length > 0, + true, + "documents table should exist" + ); + + // Check that data was migrated + const migratedDocs = await db.sql`SELECT json(data) as data FROM documents`; + assert.strictEqual( + migratedDocs.rows.length, + 1, + "document should be migrated" + ); + + // Parse the JSON data and check structure + const docData = JSON.parse(String(migratedDocs.rows[0].data)); + assert.strictEqual( + docData.content, + "Test document content", + "content should be preserved" + ); + assert.strictEqual( + docData.title, + "Test Document", + "metadata fields should be merged" + ); + assert.deepStrictEqual( + docData.tags, + ["test", "document"], + "complex metadata should be preserved" + ); + + // Verify embeddings were migrated + const embeddings = await db.sql`SELECT * FROM embeddings`; + assert.strictEqual( + embeddings.rows.length, + 1, + "embedding should be migrated" + ); + }); + + it("should handle malformed metadata gracefully", async () => { + // Insert document with invalid JSON metadata + await db.sql` + INSERT INTO documents (content, metadata, embedding) + VALUES ( + 'Document with bad metadata', + 'not valid json', + ${example_vec as unknown as Primitive} + ) + `; + + // Apply the migration + await migration.up(db); + + // Check that both documents were migrated + const migratedDocs = await db.sql` + SELECT json(data) as data FROM documents ORDER BY id + `; + assert.strictEqual( + migratedDocs.rows.length, + 2, + "both documents should be migrated" + ); + + // Parse the JSON data of the second document + const docData = JSON.parse(String(migratedDocs.rows[1].data)); + assert.strictEqual( + docData.content, + "Document with bad metadata", + "content should be preserved" + ); + assert.deepStrictEqual( + Object.keys(docData).length, + 1, + "no metadata fields should be merged when JSON is invalid" + ); + }); + }); +});