diff --git a/docs/components/vectordbs/dbs/s3_vectors.mdx b/docs/components/vectordbs/dbs/s3_vectors.mdx
index 3bf37e9dd..442971a26 100644
--- a/docs/components/vectordbs/dbs/s3_vectors.mdx
+++ b/docs/components/vectordbs/dbs/s3_vectors.mdx
@@ -9,15 +9,22 @@ description: "Use Amazon S3 Vectors as a cost-optimized vector storage service i
S3 Vectors support requires additional dependencies. Install them with:
-```bash
+
+```bash Python
pip install boto3
```
+```bash TypeScript
+npm install @aws-sdk/client-s3vectors
+```
+
+
### Usage
To use Amazon S3 Vectors with Mem0, you need to have an AWS account and the necessary IAM permissions (`s3vectors:*`). Ensure your environment is configured with AWS credentials (e.g., via `~/.aws/credentials` or environment variables).
-```python
+
+```python Python
import os
from mem0 import Memory
@@ -47,6 +54,36 @@ messages = [
m.add(messages, user_id="alice", metadata={"category": "movies"})
```
+```typescript TypeScript
+import { Memory } from 'mem0ai/oss';
+
+// Ensure your AWS credentials are configured in your environment
+// e.g., by setting AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_DEFAULT_REGION
+
+const config = {
+ vectorStore: {
+ provider: 's3_vectors',
+ config: {
+ vectorBucketName: 'my-mem0-vector-bucket',
+ collectionName: 'my-memories-index',
+ embeddingModelDims: 1536,
+ distanceMetric: 'cosine',
+ region: 'us-east-1',
+ },
+ },
+};
+
+const memory = new Memory(config);
+const messages = [
+ {"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
+ {"role": "assistant", "content": "How about a thriller movie? They can be quite engaging."},
+ {"role": "user", "content": "I'm not a big fan of thriller movies but I love sci-fi movies."},
+ {"role": "assistant", "content": "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future."}
+]
+await memory.add(messages, { userId: "alice", metadata: { category: "movies" } });
+```
+
+
### Config
Here are the parameters available for configuring Amazon S3 Vectors:
diff --git a/docs/components/vectordbs/overview.mdx b/docs/components/vectordbs/overview.mdx
index 6b0b86f6b..cc00af3ef 100644
--- a/docs/components/vectordbs/overview.mdx
+++ b/docs/components/vectordbs/overview.mdx
@@ -10,7 +10,7 @@ Mem0 includes built-in support for various popular databases. Memory can utilize
See the list of supported vector databases below.
- The following vector databases are supported in the Python implementation. The TypeScript implementation currently supports Qdrant, Redis, PGVector, Supabase, LangChain, Azure AI Search, Vectorize, and an in-memory store.
+ The following vector databases are supported in the Python implementation. The TypeScript implementation currently supports Qdrant, Redis, PGVector, Supabase, LangChain, Azure AI Search, Vectorize, Amazon S3 Vectors, and an in-memory store.
diff --git a/mem0-ts/package.json b/mem0-ts/package.json
index 4c44cbfbd..f69438052 100644
--- a/mem0-ts/package.json
+++ b/mem0-ts/package.json
@@ -107,6 +107,7 @@
},
"peerDependencies": {
"@anthropic-ai/sdk": "^0.40.1",
+ "@aws-sdk/client-s3vectors": "3.967.0",
"@azure/identity": "^4.0.0",
"@azure/search-documents": "^12.0.0",
"@cloudflare/workers-types": "^4.20250504.0",
diff --git a/mem0-ts/pnpm-lock.yaml b/mem0-ts/pnpm-lock.yaml
index a2c094030..2e26095ad 100644
--- a/mem0-ts/pnpm-lock.yaml
+++ b/mem0-ts/pnpm-lock.yaml
@@ -32,6 +32,9 @@ importers:
'@anthropic-ai/sdk':
specifier: ^0.40.1
version: 0.40.1
+ '@aws-sdk/client-s3vectors':
+ specifier: 3.967.0
+ version: 3.967.0
'@azure/identity':
specifier: ^4.0.0
version: 4.13.1
@@ -144,6 +147,131 @@ packages:
'@anthropic-ai/sdk@0.40.1':
resolution: {integrity: sha512-DJMWm8lTEM9Lk/MSFL+V+ugF7jKOn0M2Ujvb5fN8r2nY14aHbGPZ1k6sgjL+tpJ3VuOGJNG+4R83jEpOuYPv8w==}
+ '@aws-crypto/crc32@5.2.0':
+ resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
+ engines: {node: '>=16.0.0'}
+
+ '@aws-crypto/sha256-browser@5.2.0':
+ resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==}
+
+ '@aws-crypto/sha256-js@5.2.0':
+ resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==}
+ engines: {node: '>=16.0.0'}
+
+ '@aws-crypto/supports-web-crypto@5.2.0':
+ resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==}
+
+ '@aws-crypto/util@5.2.0':
+ resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
+
+ '@aws-sdk/client-s3vectors@3.967.0':
+ resolution: {integrity: sha512-DDmq8Jm/4IwliVEYVUbTub0RhcYJXVexRklhsIOdIWM42zf4ZMWPeeNvHJBSI+DMNbpqRN57TLCfH5tgiPdyVQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/client-sso@3.967.0':
+ resolution: {integrity: sha512-7RgUwHcRMJtWme6kCHGUVT+Rn9GmNH+FHm34N9UgMXzUqQlzFMweE7T5E9O8nv3wIp7xFNB20ADaCw9Xdnox1Q==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/core@3.967.0':
+ resolution: {integrity: sha512-sJmuP7GrVmlbO6DpXkuf9Mbn6jGNNvy6PLawvaxVF150c8bpNk3w39rerRls6q1dot1dBFV2D29hBXMY1agNMg==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/credential-provider-env@3.967.0':
+ resolution: {integrity: sha512-+XWw0+f/txeMbEVRtTFZhgSw1ymH1ffaVKkdMBSnw48rfSohJElKmitCqdihagRTZpzh7m8qI6tIQ5t3OUqugw==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/credential-provider-http@3.967.0':
+ resolution: {integrity: sha512-0/GIAEv5pY5htg6IBMuYccBgzz3oS2DqHjHi396ziTrwlhbrCNX96AbNhQhzAx3LBZUk13sPfeapjyQ7G57Ekg==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/credential-provider-ini@3.967.0':
+ resolution: {integrity: sha512-U8dMpaM6Qf6+2Qvp1uG6OcWv1RlrZW7tQkpmzEVWH8HZTGrVHIXXju64NMtIOr7yOnNwd0CKcytuD1QG+phCwQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/credential-provider-login@3.967.0':
+ resolution: {integrity: sha512-kbvZsZL6CBlfnb71zuJdJmBUFZN5utNrcziZr/DZ2olEOkA9vlmizE8i9BUIbmS7ptjgvRnmcY1A966yfhiblw==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/credential-provider-node@3.967.0':
+ resolution: {integrity: sha512-WuNbHs9rfKKSVok4+OBrZf0AHfzDgFYYMxN2G/q6ZfUmY4QmiPyxV5HkNFh1rqDxS9VV6kAZPo0EBmry10idSg==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/credential-provider-process@3.967.0':
+ resolution: {integrity: sha512-sNCY5JDV0whsfsZ6c2+6eUwH33H7UhKbqvCPbEYlIIa8wkGjCtCyFI3zZIJHVcMKJJ3117vSUFHEkNA7g+8rtw==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/credential-provider-sso@3.967.0':
+ resolution: {integrity: sha512-0K6kITKNytFjk1UYabYUsTThgU6TQkyW6Wmt8S5zd1A/up7NSQGpp58Rpg9GIf4amQDQwb+p9FGG7emmV8FEeA==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/credential-provider-web-identity@3.967.0':
+ resolution: {integrity: sha512-Vkr7S2ec7q/v8i/MzkHcBEdqqfWz3lyb8FDjb+NjslEwdxC3f6XwADRZzWwV1pChfx6SbsvJXKfkcF/pKAelhA==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/middleware-host-header@3.965.0':
+ resolution: {integrity: sha512-SfpSYqoPOAmdb3DBsnNsZ0vix+1VAtkUkzXM79JL3R5IfacpyKE2zytOgVAQx/FjhhlpSTwuXd+LRhUEVb3MaA==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/middleware-logger@3.965.0':
+ resolution: {integrity: sha512-gjUvJRZT1bUABKewnvkj51LAynFrfz2h5DYAg5/2F4Utx6UOGByTSr9Rq8JCLbURvvzAbCtcMkkIJRxw+8Zuzw==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/middleware-recursion-detection@3.965.0':
+ resolution: {integrity: sha512-6dvD+18Ni14KCRu+tfEoNxq1sIGVp9tvoZDZ7aMvpnA7mDXuRLrOjRQ/TAZqXwr9ENKVGyxcPl0cRK8jk1YWjA==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/middleware-user-agent@3.967.0':
+ resolution: {integrity: sha512-2qzJzZj5u+cZiG7kz3XJPaTH4ssUY/aet1kwJsUTFKrWeHUf7mZZkDFfkXP5cOffgiOyR5ZkrmJoLKAde9hshg==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/nested-clients@3.967.0':
+ resolution: {integrity: sha512-PYa7V8w0gaNux6Sz/Z7zrHmPloEE+EKpRxQIOG/D0askTr5Yd4oO2KGgcInf65uHK3f0Z9U4CTUGHZvQvABypA==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/region-config-resolver@3.965.0':
+ resolution: {integrity: sha512-RoMhu9ly2B0coxn8ctXosPP2WmDD0MkQlZGLjoYHQUOCBmty5qmCxOqBmBDa6wbWbB8xKtMQ/4VXloQOgzjHXg==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/token-providers@3.967.0':
+ resolution: {integrity: sha512-Qnd/nJ0CgeUa7zQczgmdQm0vYUF7pD1G0C+dR1T7huHQHRIsgCWIsCV9wNKzOFluqtcr6YAeuTwvY0+l8XWxnA==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/types@3.965.0':
+ resolution: {integrity: sha512-jvodoJdMavvg8faN7co58vVJRO5MVep4JFPRzUNCzpJ98BDqWDk/ad045aMJcmxkLzYLS2UAnUmqjJ/tUPNlzQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/types@3.973.13':
+ resolution: {integrity: sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==}
+ engines: {node: '>=20.0.0'}
+
+ '@aws-sdk/util-endpoints@3.965.0':
+ resolution: {integrity: sha512-WqSCB0XIsGUwZWvrYkuoofi2vzoVHqyeJ2kN+WyoOsxPLTiQSBIoqm/01R/qJvoxwK/gOOF7su9i84Vw2NQQpQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/util-locate-window@3.965.8':
+ resolution: {integrity: sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g==}
+ engines: {node: '>=20.0.0'}
+
+ '@aws-sdk/util-user-agent-browser@3.965.0':
+ resolution: {integrity: sha512-Xiza/zMntQGpkd2dETQeAK8So1pg5+STTzpcdGWxj5q0jGO5ayjqT/q1Q7BrsX5KIr6PvRkl9/V7lLCv04wGjQ==}
+
+ '@aws-sdk/util-user-agent-node@3.967.0':
+ resolution: {integrity: sha512-yUz6pCGxyG4+QaDg0dkdIBphjQp8A9rrbZa/+U3RJgRrW47hy64clFQUROzj5Poy1Ur8ICVXEUpBsSqRuYEU2g==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ aws-crt: '>=1.0.0'
+ peerDependenciesMeta:
+ aws-crt:
+ optional: true
+
+ '@aws-sdk/xml-builder@3.965.0':
+ resolution: {integrity: sha512-Tcod25/BTupraQwtb+Q+GX8bmEZfxIFjjJ/AvkhUZsZlkPeVluzq1uu3Oeqf145DCdMjzLIN6vab5MrykbDP+g==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws/lambda-invoke-store@0.2.4':
+ resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==}
+ engines: {node: '>=18.0.0'}
+
'@azure/abort-controller@2.1.2':
resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==}
engines: {node: '>=18.0.0'}
@@ -905,6 +1033,138 @@ packages:
'@sinonjs/fake-timers@10.3.0':
resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
+ '@smithy/config-resolver@4.6.2':
+ resolution: {integrity: sha512-kUY1m60FASy0ijf+0vEuZ7v+WqFlCAyyhvLkO/A15wY0hK2BLEN9Wszf/Uc3PDJWlqnrwRf+lNXOmzyds3Eflw==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/core@3.26.0':
+ resolution: {integrity: sha512-mLUktFAn+Pa2agl1J7VgtYNFWCX8/b4GMJSK1hCu4YCvtBfM6F8Os3EP4ry+DFFlXOf3wyvlgXhuUdFoy52D3g==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/credential-provider-imds@4.4.2':
+ resolution: {integrity: sha512-18UMDMyrAbDcpmL1gLUA7ww0fRTcdCrSjSJOi2Sbld+tVjwD/pW+OAwjlScFLR7vvBnhZrIPQ7kVuTf1mnJLug==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/fetch-http-handler@5.5.2':
+ resolution: {integrity: sha512-Ei/UK/QMhq0rKaMqGPlOAkE2yS9DZeYmZdk1RAKc3vp3zxgleZHZyBLlZv8yLsxljX4svCRuMTD6u3LLIcU4Bg==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/hash-node@4.4.2':
+ resolution: {integrity: sha512-M/8PUgwzekn0GcMBjcrTXV4PGFIe1AKuMuHjkHNBfofCPQ9+9Nr35lCvRfQK9BwYHQM+LKiKj8GY0pXSwYm6qQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/invalid-dependency@4.4.2':
+ resolution: {integrity: sha512-GjmewKjtK4GQlWmKTIXABHTCW6z14kebGnW1P3CTvP9WWp0nmydkb4yhjkuIWlpXkAJfcAO3cEfRLeSATaEKSQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/is-array-buffer@2.2.0':
+ resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==}
+ engines: {node: '>=14.0.0'}
+
+ '@smithy/middleware-content-length@4.4.2':
+ resolution: {integrity: sha512-yaZdk8sbKxDell/mN1OPwEwnZ0m7sTGkUK6OKlvWyCvPykAqojHEMjfLZlHdaMhIaJ2E4Jr2qMzLQHn9gp02DA==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/middleware-endpoint@4.6.2':
+ resolution: {integrity: sha512-uCYuVrw65beEpzQszGK7uiDK3Gk0HlzsZuN3JNjDM05/dwwEheg7gF4DKYWH8dpXK7c634GUfkH/vG5XuR9z2w==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/middleware-retry@4.7.2':
+ resolution: {integrity: sha512-QldB+7GcqKGVyjxTq5tpuHRFQKQJRnIETx8tdpEwYu5fHZR7z5+SM5eGd/TZqkUkV/6vUHgiiDVmmqjsobzPLg==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/middleware-serde@4.4.2':
+ resolution: {integrity: sha512-Nx6wuSMasSazyIC02cmmSF+jEGnBpVL6u/Ra9J+AoKQyX1Uwxqf96fSoD6G+D1SKqw51GVNC1V08GtJPe8Yhaw==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/middleware-stack@4.4.2':
+ resolution: {integrity: sha512-yWvlZxqgmk0ZiRd0QVzSAqvdkREeiw2boqT+DTbyi03ylx3LyYGs5hNvRjks5ay0cDotZ7H0AdG2tJDMy+IxgA==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/node-config-provider@4.5.2':
+ resolution: {integrity: sha512-kGnGhU7v/VSlKjcUHUNhMQ1eDiGKjDF6OlGlOWcilgsyR6Yyq4sDbEV6vs1RD8+xqj1OPpCYt817KAM44ymweQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/node-http-handler@4.8.2':
+ resolution: {integrity: sha512-wfl1uwrAqMH9/pi4kqBo5LBcFwrJLxuDLqL7p7qNcJIFcyZDUc6pzhYk4CYv+DP7fIUpQCZumwNnkhPKS52osQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/property-provider@4.4.2':
+ resolution: {integrity: sha512-V74/UQ+fPPIqZHzeXIUd0H+k/MF+WpeO3I+5wrMCMgsllLt6uqSNbWnLhJs6pMeXZGu6ry+g1bpIdcHNeL+Oww==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/protocol-http@5.5.2':
+ resolution: {integrity: sha512-ZCQGk/UNS+YF5D+Jmhr8+PQX2PJKe4QzR6zuOR5sUinxO/ZiKNA7nyrbeO/Ds2hXLts3fZAtgLPKLJgvjCX9uQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/shared-ini-file-loader@4.6.2':
+ resolution: {integrity: sha512-9Xti1yhj7Em9t4ZQ1E8YRA6wbhAqx1nfLu0XSAqv82HED43TMUvn3nOO2xBISxZc0hcOg7wvuHEypUy4L4XF2w==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/signature-v4@5.5.2':
+ resolution: {integrity: sha512-7xHpmPY4rt0IOmeAA8EfjgEH8isT+587TCdy9H6a7d4OMi5CQ0oEHhWllunvPu4j4Cq0vTFwdxXN/kABWPjdyA==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/smithy-client@4.14.2':
+ resolution: {integrity: sha512-H3fp0F/IhG8a6qnoWHeGTH1qDPRR9VVZjcxM9NMXPB6nqzaqcoYIX8D4FFI1Ap2siGr+yvYgKqHC76+afBgF1A==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/types@4.15.0':
+ resolution: {integrity: sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/url-parser@4.4.2':
+ resolution: {integrity: sha512-vx7FUYJMRfQqdqPRLYwpisOuYU6e1PaabcPUTEc7xG6X0VEr4B6PnjZloHz67qva0pQ9/eNs3xPxvQ6w5gRGAw==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-base64@4.5.2':
+ resolution: {integrity: sha512-OQKnJft8J4KPJyL4K3w3XpOvstpZUbPdcs840dH+JTp2w+v8Lce8nxzUoeOiAIRIhnWFQd7FxAjkZ0OkpX6jzw==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-body-length-browser@4.4.2':
+ resolution: {integrity: sha512-PTdWf5ApVEJQvnCaAJJPFHkJcUUY5yImYEcfzPqFP8ytet2oI0P/2nCF2t0600MkAH/MC9ne8SvgcRKh8Ooqhw==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-body-length-node@4.4.2':
+ resolution: {integrity: sha512-KaT3bRb0khPrjXsXzyAi47F5aKOqBLypDhhvVC7p65AoZCPPAQ4j8KB1oaK+TKf6k5XFgkAY4gMK1EFr7xh5Wg==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-buffer-from@2.2.0':
+ resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==}
+ engines: {node: '>=14.0.0'}
+
+ '@smithy/util-defaults-mode-browser@4.5.2':
+ resolution: {integrity: sha512-VexZx4D0vhOeXNLpmxgXK0s9CNV/jCSfjBDWS7YNjZ47K87rTzmgovr8s0CgOLASIkCAS4aAbTqIG4YzU6zduA==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-defaults-mode-node@4.4.2':
+ resolution: {integrity: sha512-nhmz4D2s2FGFTgWf8M2bQHCLKjJbCQBS91waDofexbptBESTExtpEgiaI0rWIP4MhAarCCdFapHdvlCoknBE6w==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-endpoints@3.6.2':
+ resolution: {integrity: sha512-K50/sbl8HRnBJt5+UYvIG8HCdEDNA9rhCmkWTDftzGMlJ4XlPgtqI1V6jTSi9tmK7MLfgw7CfGvzSKFSWOteOQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-middleware@4.4.2':
+ resolution: {integrity: sha512-Xk039bCOxEZfasaHSHPj0QS8EY2npd0z3nxLkDvczg9ueF7eboiKw0HEa5pILLwjb+sjSlgzSjoCOVAM715TzA==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-retry@4.5.2':
+ resolution: {integrity: sha512-iinqHq7WnWWKQUCObyu4YBzXROJO8dHMQrM+pvut92rKgjhcLNHel8QVJNsIswjPLOEJffq6sYjOdq/ndOrIAA==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-stream@4.7.2':
+ resolution: {integrity: sha512-/JZLMlF/pzY7QIA5M70bKSq5Bz/JpGLHiLlttvrcZpfLyGSCR3BvIebbipmCvSsnLXnU2pGDsDgD513N1/GGVg==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/util-utf8@2.3.0':
+ resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==}
+ engines: {node: '>=14.0.0'}
+
+ '@smithy/util-utf8@4.4.2':
+ resolution: {integrity: sha512-N5CpfaL+/LPQU9PFdOT55ayUo5T0QypG4Almzd1/efJvoDypuT1shkgJk1+hhg/02scYluW6Q2JGnSHIPwCEGQ==}
+ engines: {node: '>=18.0.0'}
+
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
@@ -1082,6 +1342,9 @@ packages:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
+ anynum@1.0.1:
+ resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==}
+
apparatus@0.0.10:
resolution: {integrity: sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg==}
engines: {node: '>=0.2.6'}
@@ -1158,6 +1421,9 @@ packages:
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+ bowser@2.14.1:
+ resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==}
+
brace-expansion@1.1.15:
resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==}
@@ -1503,6 +1769,10 @@ packages:
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+ fast-xml-parser@5.2.5:
+ resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==}
+ hasBin: true
+
fastq@1.20.1:
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
@@ -2741,6 +3011,9 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
+ strnum@2.4.1:
+ resolution: {integrity: sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==}
+
sucrase@3.35.1:
resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -3080,6 +3353,384 @@ snapshots:
transitivePeerDependencies:
- encoding
+ '@aws-crypto/crc32@5.2.0':
+ dependencies:
+ '@aws-crypto/util': 5.2.0
+ '@aws-sdk/types': 3.973.13
+ tslib: 2.8.1
+
+ '@aws-crypto/sha256-browser@5.2.0':
+ dependencies:
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-crypto/supports-web-crypto': 5.2.0
+ '@aws-crypto/util': 5.2.0
+ '@aws-sdk/types': 3.973.13
+ '@aws-sdk/util-locate-window': 3.965.8
+ '@smithy/util-utf8': 2.3.0
+ tslib: 2.8.1
+
+ '@aws-crypto/sha256-js@5.2.0':
+ dependencies:
+ '@aws-crypto/util': 5.2.0
+ '@aws-sdk/types': 3.973.13
+ tslib: 2.8.1
+
+ '@aws-crypto/supports-web-crypto@5.2.0':
+ dependencies:
+ tslib: 2.8.1
+
+ '@aws-crypto/util@5.2.0':
+ dependencies:
+ '@aws-sdk/types': 3.973.13
+ '@smithy/util-utf8': 2.3.0
+ tslib: 2.8.1
+
+ '@aws-sdk/client-s3vectors@3.967.0':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/credential-provider-node': 3.967.0
+ '@aws-sdk/middleware-host-header': 3.965.0
+ '@aws-sdk/middleware-logger': 3.965.0
+ '@aws-sdk/middleware-recursion-detection': 3.965.0
+ '@aws-sdk/middleware-user-agent': 3.967.0
+ '@aws-sdk/region-config-resolver': 3.965.0
+ '@aws-sdk/types': 3.965.0
+ '@aws-sdk/util-endpoints': 3.965.0
+ '@aws-sdk/util-user-agent-browser': 3.965.0
+ '@aws-sdk/util-user-agent-node': 3.967.0
+ '@smithy/config-resolver': 4.6.2
+ '@smithy/core': 3.26.0
+ '@smithy/fetch-http-handler': 5.5.2
+ '@smithy/hash-node': 4.4.2
+ '@smithy/invalid-dependency': 4.4.2
+ '@smithy/middleware-content-length': 4.4.2
+ '@smithy/middleware-endpoint': 4.6.2
+ '@smithy/middleware-retry': 4.7.2
+ '@smithy/middleware-serde': 4.4.2
+ '@smithy/middleware-stack': 4.4.2
+ '@smithy/node-config-provider': 4.5.2
+ '@smithy/node-http-handler': 4.8.2
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/smithy-client': 4.14.2
+ '@smithy/types': 4.15.0
+ '@smithy/url-parser': 4.4.2
+ '@smithy/util-base64': 4.5.2
+ '@smithy/util-body-length-browser': 4.4.2
+ '@smithy/util-body-length-node': 4.4.2
+ '@smithy/util-defaults-mode-browser': 4.5.2
+ '@smithy/util-defaults-mode-node': 4.4.2
+ '@smithy/util-endpoints': 3.6.2
+ '@smithy/util-middleware': 4.4.2
+ '@smithy/util-retry': 4.5.2
+ '@smithy/util-utf8': 4.4.2
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/client-sso@3.967.0':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/middleware-host-header': 3.965.0
+ '@aws-sdk/middleware-logger': 3.965.0
+ '@aws-sdk/middleware-recursion-detection': 3.965.0
+ '@aws-sdk/middleware-user-agent': 3.967.0
+ '@aws-sdk/region-config-resolver': 3.965.0
+ '@aws-sdk/types': 3.965.0
+ '@aws-sdk/util-endpoints': 3.965.0
+ '@aws-sdk/util-user-agent-browser': 3.965.0
+ '@aws-sdk/util-user-agent-node': 3.967.0
+ '@smithy/config-resolver': 4.6.2
+ '@smithy/core': 3.26.0
+ '@smithy/fetch-http-handler': 5.5.2
+ '@smithy/hash-node': 4.4.2
+ '@smithy/invalid-dependency': 4.4.2
+ '@smithy/middleware-content-length': 4.4.2
+ '@smithy/middleware-endpoint': 4.6.2
+ '@smithy/middleware-retry': 4.7.2
+ '@smithy/middleware-serde': 4.4.2
+ '@smithy/middleware-stack': 4.4.2
+ '@smithy/node-config-provider': 4.5.2
+ '@smithy/node-http-handler': 4.8.2
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/smithy-client': 4.14.2
+ '@smithy/types': 4.15.0
+ '@smithy/url-parser': 4.4.2
+ '@smithy/util-base64': 4.5.2
+ '@smithy/util-body-length-browser': 4.4.2
+ '@smithy/util-body-length-node': 4.4.2
+ '@smithy/util-defaults-mode-browser': 4.5.2
+ '@smithy/util-defaults-mode-node': 4.4.2
+ '@smithy/util-endpoints': 3.6.2
+ '@smithy/util-middleware': 4.4.2
+ '@smithy/util-retry': 4.5.2
+ '@smithy/util-utf8': 4.4.2
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/core@3.967.0':
+ dependencies:
+ '@aws-sdk/types': 3.965.0
+ '@aws-sdk/xml-builder': 3.965.0
+ '@smithy/core': 3.26.0
+ '@smithy/node-config-provider': 4.5.2
+ '@smithy/property-provider': 4.4.2
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/signature-v4': 5.5.2
+ '@smithy/smithy-client': 4.14.2
+ '@smithy/types': 4.15.0
+ '@smithy/util-base64': 4.5.2
+ '@smithy/util-middleware': 4.4.2
+ '@smithy/util-utf8': 4.4.2
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-env@3.967.0':
+ dependencies:
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/property-provider': 4.4.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-http@3.967.0':
+ dependencies:
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/fetch-http-handler': 5.5.2
+ '@smithy/node-http-handler': 4.8.2
+ '@smithy/property-provider': 4.4.2
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/smithy-client': 4.14.2
+ '@smithy/types': 4.15.0
+ '@smithy/util-stream': 4.7.2
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-ini@3.967.0':
+ dependencies:
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/credential-provider-env': 3.967.0
+ '@aws-sdk/credential-provider-http': 3.967.0
+ '@aws-sdk/credential-provider-login': 3.967.0
+ '@aws-sdk/credential-provider-process': 3.967.0
+ '@aws-sdk/credential-provider-sso': 3.967.0
+ '@aws-sdk/credential-provider-web-identity': 3.967.0
+ '@aws-sdk/nested-clients': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/credential-provider-imds': 4.4.2
+ '@smithy/property-provider': 4.4.2
+ '@smithy/shared-ini-file-loader': 4.6.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/credential-provider-login@3.967.0':
+ dependencies:
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/nested-clients': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/property-provider': 4.4.2
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/shared-ini-file-loader': 4.6.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/credential-provider-node@3.967.0':
+ dependencies:
+ '@aws-sdk/credential-provider-env': 3.967.0
+ '@aws-sdk/credential-provider-http': 3.967.0
+ '@aws-sdk/credential-provider-ini': 3.967.0
+ '@aws-sdk/credential-provider-process': 3.967.0
+ '@aws-sdk/credential-provider-sso': 3.967.0
+ '@aws-sdk/credential-provider-web-identity': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/credential-provider-imds': 4.4.2
+ '@smithy/property-provider': 4.4.2
+ '@smithy/shared-ini-file-loader': 4.6.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/credential-provider-process@3.967.0':
+ dependencies:
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/property-provider': 4.4.2
+ '@smithy/shared-ini-file-loader': 4.6.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-sso@3.967.0':
+ dependencies:
+ '@aws-sdk/client-sso': 3.967.0
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/token-providers': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/property-provider': 4.4.2
+ '@smithy/shared-ini-file-loader': 4.6.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/credential-provider-web-identity@3.967.0':
+ dependencies:
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/nested-clients': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/property-provider': 4.4.2
+ '@smithy/shared-ini-file-loader': 4.6.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/middleware-host-header@3.965.0':
+ dependencies:
+ '@aws-sdk/types': 3.965.0
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/middleware-logger@3.965.0':
+ dependencies:
+ '@aws-sdk/types': 3.965.0
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/middleware-recursion-detection@3.965.0':
+ dependencies:
+ '@aws-sdk/types': 3.965.0
+ '@aws/lambda-invoke-store': 0.2.4
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/middleware-user-agent@3.967.0':
+ dependencies:
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@aws-sdk/util-endpoints': 3.965.0
+ '@smithy/core': 3.26.0
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/nested-clients@3.967.0':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/middleware-host-header': 3.965.0
+ '@aws-sdk/middleware-logger': 3.965.0
+ '@aws-sdk/middleware-recursion-detection': 3.965.0
+ '@aws-sdk/middleware-user-agent': 3.967.0
+ '@aws-sdk/region-config-resolver': 3.965.0
+ '@aws-sdk/types': 3.965.0
+ '@aws-sdk/util-endpoints': 3.965.0
+ '@aws-sdk/util-user-agent-browser': 3.965.0
+ '@aws-sdk/util-user-agent-node': 3.967.0
+ '@smithy/config-resolver': 4.6.2
+ '@smithy/core': 3.26.0
+ '@smithy/fetch-http-handler': 5.5.2
+ '@smithy/hash-node': 4.4.2
+ '@smithy/invalid-dependency': 4.4.2
+ '@smithy/middleware-content-length': 4.4.2
+ '@smithy/middleware-endpoint': 4.6.2
+ '@smithy/middleware-retry': 4.7.2
+ '@smithy/middleware-serde': 4.4.2
+ '@smithy/middleware-stack': 4.4.2
+ '@smithy/node-config-provider': 4.5.2
+ '@smithy/node-http-handler': 4.8.2
+ '@smithy/protocol-http': 5.5.2
+ '@smithy/smithy-client': 4.14.2
+ '@smithy/types': 4.15.0
+ '@smithy/url-parser': 4.4.2
+ '@smithy/util-base64': 4.5.2
+ '@smithy/util-body-length-browser': 4.4.2
+ '@smithy/util-body-length-node': 4.4.2
+ '@smithy/util-defaults-mode-browser': 4.5.2
+ '@smithy/util-defaults-mode-node': 4.4.2
+ '@smithy/util-endpoints': 3.6.2
+ '@smithy/util-middleware': 4.4.2
+ '@smithy/util-retry': 4.5.2
+ '@smithy/util-utf8': 4.4.2
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/region-config-resolver@3.965.0':
+ dependencies:
+ '@aws-sdk/types': 3.965.0
+ '@smithy/config-resolver': 4.6.2
+ '@smithy/node-config-provider': 4.5.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/token-providers@3.967.0':
+ dependencies:
+ '@aws-sdk/core': 3.967.0
+ '@aws-sdk/nested-clients': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/property-provider': 4.4.2
+ '@smithy/shared-ini-file-loader': 4.6.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/types@3.965.0':
+ dependencies:
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/types@3.973.13':
+ dependencies:
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/util-endpoints@3.965.0':
+ dependencies:
+ '@aws-sdk/types': 3.965.0
+ '@smithy/types': 4.15.0
+ '@smithy/url-parser': 4.4.2
+ '@smithy/util-endpoints': 3.6.2
+ tslib: 2.8.1
+
+ '@aws-sdk/util-locate-window@3.965.8':
+ dependencies:
+ tslib: 2.8.1
+
+ '@aws-sdk/util-user-agent-browser@3.965.0':
+ dependencies:
+ '@aws-sdk/types': 3.965.0
+ '@smithy/types': 4.15.0
+ bowser: 2.14.1
+ tslib: 2.8.1
+
+ '@aws-sdk/util-user-agent-node@3.967.0':
+ dependencies:
+ '@aws-sdk/middleware-user-agent': 3.967.0
+ '@aws-sdk/types': 3.965.0
+ '@smithy/node-config-provider': 4.5.2
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/xml-builder@3.965.0':
+ dependencies:
+ '@smithy/types': 4.15.0
+ fast-xml-parser: 5.2.5
+ tslib: 2.8.1
+
+ '@aws/lambda-invoke-store@0.2.4': {}
+
'@azure/abort-controller@2.1.2':
dependencies:
tslib: 2.8.1
@@ -3883,6 +4534,175 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
+ '@smithy/config-resolver@4.6.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/core@3.26.0':
+ dependencies:
+ '@aws-crypto/crc32': 5.2.0
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@smithy/credential-provider-imds@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@smithy/fetch-http-handler@5.5.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@smithy/hash-node@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/invalid-dependency@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/is-array-buffer@2.2.0':
+ dependencies:
+ tslib: 2.8.1
+
+ '@smithy/middleware-content-length@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/middleware-endpoint@4.6.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/middleware-retry@4.7.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/middleware-serde@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/middleware-stack@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/node-config-provider@4.5.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/node-http-handler@4.8.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@smithy/property-provider@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/protocol-http@5.5.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/shared-ini-file-loader@4.6.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/signature-v4@5.5.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@smithy/smithy-client@4.14.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@smithy/types@4.15.0':
+ dependencies:
+ tslib: 2.8.1
+
+ '@smithy/url-parser@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-base64@4.5.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-body-length-browser@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-body-length-node@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-buffer-from@2.2.0':
+ dependencies:
+ '@smithy/is-array-buffer': 2.2.0
+ tslib: 2.8.1
+
+ '@smithy/util-defaults-mode-browser@4.5.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-defaults-mode-node@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-endpoints@3.6.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-middleware@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-retry@4.5.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-stream@4.7.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
+ '@smithy/util-utf8@2.3.0':
+ dependencies:
+ '@smithy/util-buffer-from': 2.2.0
+ tslib: 2.8.1
+
+ '@smithy/util-utf8@4.4.2':
+ dependencies:
+ '@smithy/core': 3.26.0
+ tslib: 2.8.1
+
'@standard-schema/spec@1.1.0': {}
'@supabase/auth-js@2.108.1':
@@ -4067,6 +4887,8 @@ snapshots:
normalize-path: 3.0.0
picomatch: 2.3.2
+ anynum@1.0.1: {}
+
apparatus@0.0.10:
dependencies:
sylvester: 0.0.21
@@ -4171,6 +4993,8 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
+ bowser@2.14.1: {}
+
brace-expansion@1.1.15:
dependencies:
balanced-match: 1.0.2
@@ -4513,6 +5337,10 @@ snapshots:
fast-json-stable-stringify@2.1.0: {}
+ fast-xml-parser@5.2.5:
+ dependencies:
+ strnum: 2.4.1
+
fastq@1.20.1:
dependencies:
reusify: 1.1.0
@@ -5937,6 +6765,10 @@ snapshots:
strip-json-comments@3.1.1: {}
+ strnum@2.4.1:
+ dependencies:
+ anynum: 1.0.1
+
sucrase@3.35.1:
dependencies:
'@jridgewell/gen-mapping': 0.3.13
diff --git a/mem0-ts/src/oss/src/index.ts b/mem0-ts/src/oss/src/index.ts
index 141edcacf..290872800 100644
--- a/mem0-ts/src/oss/src/index.ts
+++ b/mem0-ts/src/oss/src/index.ts
@@ -28,4 +28,5 @@ export * from "./vector_stores/langchain";
export * from "./vector_stores/vectorize";
export * from "./vector_stores/azure_ai_search";
export * from "./vector_stores/pgvector";
+export * from "./vector_stores/s3_vectors";
export * from "./utils/factory";
diff --git a/mem0-ts/src/oss/src/utils/factory.ts b/mem0-ts/src/oss/src/utils/factory.ts
index 09a2db2db..228a6c418 100644
--- a/mem0-ts/src/oss/src/utils/factory.ts
+++ b/mem0-ts/src/oss/src/utils/factory.ts
@@ -38,6 +38,7 @@ import { LangchainEmbedder } from "../embeddings/langchain";
import { LangchainVectorStore } from "../vector_stores/langchain";
import { AzureAISearch } from "../vector_stores/azure_ai_search";
import { PGVector } from "../vector_stores/pgvector";
+import { S3Vectors } from "../vector_stores/s3_vectors";
export class EmbedderFactory {
static create(provider: string, config: EmbeddingConfig): Embedder {
@@ -116,6 +117,9 @@ export class VectorStoreFactory {
return new AzureAISearch(config as any);
case "pgvector":
return new PGVector(config as any);
+ case "s3-vectors":
+ case "s3_vectors":
+ return new S3Vectors(config as any);
default:
throw new Error(`Unsupported vector store provider: ${provider}`);
}
diff --git a/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts b/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts
new file mode 100644
index 000000000..cc2edf6b5
--- /dev/null
+++ b/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts
@@ -0,0 +1,841 @@
+import {
+ CreateIndexCommand,
+ CreateVectorBucketCommand,
+ DeleteIndexCommand,
+ DeleteVectorsCommand,
+ GetIndexCommand,
+ GetVectorBucketCommand,
+ GetVectorsCommand,
+ ListVectorsCommand,
+ PutVectorsCommand,
+ QueryVectorsCommand,
+ S3VectorsClient,
+ type DistanceMetric,
+ type GetOutputVector,
+ type ListOutputVector,
+ type QueryOutputVector,
+ type S3VectorsClientConfig,
+ type VectorData,
+} from "@aws-sdk/client-s3vectors";
+import { VectorStore } from "./base";
+import { SearchFilters, VectorStoreConfig, VectorStoreResult } from "../types";
+
+const MIGRATION_INDEX_NAME = "memory_migrations";
+const MIGRATION_VECTOR_KEY = "mem0-user";
+const DEFAULT_PAGE_SIZE = 500;
+const ALWAYS_FALSE_FILTER_KEY = "$alwaysFalse";
+
+type S3Filter = Record;
+
+interface S3VectorsConfig extends VectorStoreConfig {
+ vectorBucketName: string;
+ collectionName: string;
+ embeddingModelDims?: number;
+ dimension?: number;
+ distanceMetric?: DistanceMetric | "cosine" | "euclidean";
+ region?: string;
+ regionName?: string;
+ client?: S3VectorsClientLike;
+ clientConfig?: S3VectorsClientConfig;
+}
+
+interface S3VectorsClientLike {
+ send(command: any): Promise;
+}
+
+export class S3Vectors implements VectorStore {
+ private readonly client: S3VectorsClientLike;
+ private readonly vectorBucketName: string;
+ private readonly collectionName: string;
+ private readonly dimension: number;
+ private readonly distanceMetric: DistanceMetric | "cosine" | "euclidean";
+ private _initPromise?: Promise;
+ private cachedUserId?: string;
+
+ constructor(config: S3VectorsConfig) {
+ if (!config.vectorBucketName) {
+ throw new Error("vectorBucketName is required");
+ }
+ if (!config.collectionName) {
+ throw new Error("collectionName is required");
+ }
+
+ const dimension = config.embeddingModelDims ?? config.dimension;
+ if (!dimension || dimension < 1) {
+ throw new Error("embeddingModelDims or dimension is required");
+ }
+
+ this.vectorBucketName = config.vectorBucketName;
+ this.collectionName = config.collectionName;
+ this.dimension = dimension;
+ this.distanceMetric = config.distanceMetric || "cosine";
+ this.client =
+ config.client ||
+ new S3VectorsClient({
+ ...(config.clientConfig || {}),
+ ...(config.region || config.regionName
+ ? { region: config.region || config.regionName }
+ : {}),
+ });
+
+ void this.initialize().catch(console.error);
+ }
+
+ async initialize(): Promise {
+ if (!this._initPromise) {
+ this._initPromise = this._doInitialize();
+ }
+ return this._initPromise;
+ }
+
+ private async _doInitialize(): Promise {
+ await this.ensureBucketExists();
+ await this.ensureIndexExists(
+ this.collectionName,
+ this.dimension,
+ this.distanceMetric,
+ );
+ }
+
+ async insert(
+ vectors: number[][],
+ ids: string[],
+ payloads: Record[],
+ ): Promise {
+ await this.initialize();
+ this.assertBatchDimensions(vectors, "Insert");
+
+ await this.client.send(
+ new PutVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: this.collectionName,
+ vectors: vectors.map((vector, index) => ({
+ key: ids[index],
+ data: this.toVectorData(vector),
+ metadata: payloads[index] || {},
+ })),
+ }),
+ );
+ }
+
+ async keywordSearch(): Promise {
+ return null;
+ }
+
+ async search(
+ query: number[],
+ topK: number = 5,
+ filters?: SearchFilters,
+ ): Promise {
+ await this.initialize();
+ this.assertVectorDimension(query, "Query");
+
+ const filter = this.convertFilters(filters);
+ if (filter && this.isAlwaysFalseFilter(filter)) {
+ return [];
+ }
+ const response = await this.client.send(
+ new QueryVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: this.collectionName,
+ queryVector: this.toVectorData(query),
+ topK,
+ filter,
+ returnMetadata: true,
+ returnDistance: true,
+ }),
+ );
+
+ const responseDistanceMetric =
+ response.distanceMetric || this.distanceMetric;
+ return (response.vectors || [])
+ .map((vector: QueryOutputVector) =>
+ this.normalizeQueryVector(vector, responseDistanceMetric),
+ )
+ .slice(0, topK);
+ }
+
+ async get(vectorId: string): Promise {
+ await this.initialize();
+
+ try {
+ const response = await this.client.send(
+ new GetVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: this.collectionName,
+ keys: [vectorId],
+ returnMetadata: true,
+ }),
+ );
+
+ const vector = response.vectors?.[0];
+ if (!vector) {
+ return null;
+ }
+
+ return this.normalizeStoredVector(vector);
+ } catch (error) {
+ if (this.isNotFound(error)) {
+ return null;
+ }
+ throw error;
+ }
+ }
+
+ async update(
+ vectorId: string,
+ vector: number[],
+ payload: Record,
+ ): Promise {
+ await this.initialize();
+
+ let nextVector = vector;
+ let nextPayload = payload || {};
+
+ if (vector.length === 0 || !payload) {
+ const existing = await this.fetchStoredVector(vectorId);
+ if (!existing) {
+ throw new Error(`Vector with ID ${vectorId} not found`);
+ }
+ nextVector = vector.length > 0 ? vector : existing.vector;
+ nextPayload = payload || existing.payload;
+ }
+
+ this.assertVectorDimension(nextVector, "Vector");
+
+ await this.client.send(
+ new PutVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: this.collectionName,
+ vectors: [
+ {
+ key: vectorId,
+ data: this.toVectorData(nextVector),
+ metadata: nextPayload,
+ },
+ ],
+ }),
+ );
+ }
+
+ async delete(vectorId: string): Promise {
+ await this.initialize();
+
+ await this.client.send(
+ new DeleteVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: this.collectionName,
+ keys: [vectorId],
+ }),
+ );
+ }
+
+ async deleteCol(): Promise {
+ await this.initialize();
+
+ try {
+ await this.client.send(
+ new DeleteIndexCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: this.collectionName,
+ }),
+ );
+ } catch (error) {
+ if (this.isNotFound(error)) {
+ return;
+ }
+ throw error;
+ }
+ }
+
+ async list(
+ filters?: SearchFilters,
+ topK: number = 100,
+ ): Promise<[VectorStoreResult[], number]> {
+ await this.initialize();
+
+ const filter = this.convertFilters(filters);
+ const results: VectorStoreResult[] = [];
+ let nextToken: string | undefined;
+
+ // Stop paginating once we have topK matches. Both callers of list()
+ // discard the count, so scanning the whole index just to total every
+ // match is wasted round-trips (O(index size) on getAll/deleteAll).
+ // Return the page length like qdrant does.
+ do {
+ const response = await this.client.send(
+ new ListVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: this.collectionName,
+ maxResults: DEFAULT_PAGE_SIZE,
+ nextToken,
+ returnMetadata: true,
+ }),
+ );
+
+ for (const vector of response.vectors || []) {
+ if (results.length >= topK) {
+ break;
+ }
+ const normalized = this.normalizeListedVector(vector);
+ if (filter && !this.matchesFilter(normalized.payload, filter)) {
+ continue;
+ }
+ results.push(normalized);
+ }
+
+ nextToken = response.nextToken;
+ } while (nextToken && results.length < topK);
+
+ return [results, results.length];
+ }
+
+ async getUserId(): Promise {
+ await this.initialize();
+ if (this.cachedUserId) {
+ return this.cachedUserId;
+ }
+
+ await this.ensureMigrationIndex();
+
+ const response = await this.client.send(
+ new GetVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: MIGRATION_INDEX_NAME,
+ keys: [MIGRATION_VECTOR_KEY],
+ returnMetadata: true,
+ }),
+ );
+
+ const metadata = this.normalizeMetadata(response.vectors?.[0]?.metadata);
+ const userId = metadata.user_id;
+ if (typeof userId === "string" && userId.length > 0) {
+ this.cachedUserId = userId;
+ return userId;
+ }
+
+ const randomUserId =
+ Math.random().toString(36).substring(2, 15) +
+ Math.random().toString(36).substring(2, 15);
+ await this.setUserId(randomUserId);
+ return randomUserId;
+ }
+
+ async setUserId(userId: string): Promise {
+ await this.initialize();
+ await this.ensureMigrationIndex();
+
+ await this.client.send(
+ new PutVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: MIGRATION_INDEX_NAME,
+ vectors: [
+ {
+ key: MIGRATION_VECTOR_KEY,
+ data: this.toVectorData([0]),
+ metadata: { user_id: userId },
+ },
+ ],
+ }),
+ );
+
+ this.cachedUserId = userId;
+ }
+
+ private async ensureBucketExists(): Promise {
+ try {
+ await this.client.send(
+ new GetVectorBucketCommand({
+ vectorBucketName: this.vectorBucketName,
+ }),
+ );
+ } catch (error) {
+ if (!this.isNotFound(error)) {
+ throw error;
+ }
+
+ try {
+ await this.client.send(
+ new CreateVectorBucketCommand({
+ vectorBucketName: this.vectorBucketName,
+ }),
+ );
+ } catch (createError) {
+ if (!this.isConflict(createError)) {
+ throw createError;
+ }
+ }
+ }
+ }
+
+ private async ensureIndexExists(
+ indexName: string,
+ dimension: number,
+ distanceMetric: DistanceMetric | "cosine" | "euclidean",
+ ): Promise {
+ try {
+ await this.client.send(
+ new GetIndexCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName,
+ }),
+ );
+ } catch (error) {
+ if (!this.isNotFound(error)) {
+ throw error;
+ }
+
+ try {
+ await this.client.send(
+ new CreateIndexCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName,
+ dataType: "float32",
+ dimension,
+ distanceMetric,
+ }),
+ );
+ } catch (createError) {
+ if (!this.isConflict(createError)) {
+ throw createError;
+ }
+ }
+ }
+ }
+
+ private async ensureMigrationIndex(): Promise {
+ await this.ensureIndexExists(MIGRATION_INDEX_NAME, 1, "euclidean");
+ }
+
+ private async fetchStoredVector(
+ vectorId: string,
+ ): Promise<{ vector: number[]; payload: Record } | null> {
+ const response = await this.client.send(
+ new GetVectorsCommand({
+ vectorBucketName: this.vectorBucketName,
+ indexName: this.collectionName,
+ keys: [vectorId],
+ returnData: true,
+ returnMetadata: true,
+ }),
+ );
+
+ const vector = response.vectors?.[0];
+ const data = this.fromVectorData(vector?.data);
+ if (!vector || !data) {
+ return null;
+ }
+
+ return {
+ vector: data,
+ payload: this.normalizeMetadata(vector.metadata),
+ };
+ }
+
+ private convertFilters(filters?: SearchFilters): S3Filter | undefined {
+ if (!filters || Object.keys(filters).length === 0) {
+ return undefined;
+ }
+ const normalized = this.convertFilterNode(filters);
+ return this.isNoOpFilter(normalized) ? undefined : normalized;
+ }
+
+ private convertFilterNode(node: Record): S3Filter {
+ const clauses: S3Filter[] = [];
+
+ for (const [key, value] of Object.entries(node)) {
+ if (key === "$and" || key === "$or") {
+ if (!Array.isArray(value) || value.length === 0) {
+ throw new Error(`${key} filter requires a non-empty list`);
+ }
+ const normalizedEntries = value.map((entry) =>
+ this.convertFilterNode(entry),
+ );
+ const entries = normalizedEntries.filter(
+ (entry) => !this.isNoOpFilter(entry),
+ );
+ if (key === "$and") {
+ if (entries.some((entry) => this.isAlwaysFalseFilter(entry))) {
+ return { [ALWAYS_FALSE_FILTER_KEY]: true };
+ }
+ if (entries.length === 0) {
+ continue;
+ }
+ } else {
+ if (normalizedEntries.some((entry) => this.isNoOpFilter(entry))) {
+ continue;
+ }
+ const viableEntries = entries.filter(
+ (entry) => !this.isAlwaysFalseFilter(entry),
+ );
+ if (viableEntries.length === 0) {
+ return { [ALWAYS_FALSE_FILTER_KEY]: true };
+ }
+ clauses.push(
+ viableEntries.length === 1
+ ? viableEntries[0]
+ : { [key]: viableEntries },
+ );
+ continue;
+ }
+ clauses.push(entries.length === 1 ? entries[0] : { [key]: entries });
+ continue;
+ }
+
+ if (key === "$not") {
+ if (!Array.isArray(value) || value.length === 0) {
+ throw new Error("$not filter requires a non-empty list");
+ }
+
+ const normalizedEntries = value.map((entry) =>
+ this.convertFilterNode(entry),
+ );
+ if (normalizedEntries.some((entry) => this.isNoOpFilter(entry))) {
+ return { [ALWAYS_FALSE_FILTER_KEY]: true };
+ }
+ const negated = normalizedEntries.filter(
+ (entry) => !this.isAlwaysFalseFilter(entry),
+ );
+ if (negated.length === 0) {
+ continue;
+ }
+ const negatedFilters = negated.map((entry) => this.negateFilter(entry));
+ clauses.push(
+ negatedFilters.length === 1
+ ? negatedFilters[0]
+ : { $and: negatedFilters },
+ );
+ continue;
+ }
+
+ const fieldFilter = this.convertFieldFilter(key, value);
+ if (this.isAlwaysFalseFilter(fieldFilter)) {
+ return fieldFilter;
+ }
+ if (this.isNoOpFilter(fieldFilter)) {
+ continue;
+ }
+ clauses.push(fieldFilter);
+ }
+
+ if (clauses.some((entry) => this.isAlwaysFalseFilter(entry))) {
+ return { [ALWAYS_FALSE_FILTER_KEY]: true };
+ }
+ if (clauses.length === 1) {
+ return clauses[0];
+ }
+ if (clauses.length === 0) {
+ return {};
+ }
+
+ return { $and: clauses };
+ }
+
+ private convertFieldFilter(key: string, value: any): S3Filter {
+ if (value === "*") {
+ return { [key]: { $exists: true } };
+ }
+
+ if (Array.isArray(value)) {
+ if (value.length === 0) {
+ return { [ALWAYS_FALSE_FILTER_KEY]: true };
+ }
+ return { [key]: { $in: value } };
+ }
+
+ if (typeof value !== "object" || value === null) {
+ return { [key]: { $eq: value } };
+ }
+
+ const operators: Record = {};
+
+ for (const [operator, operand] of Object.entries(value)) {
+ switch (operator) {
+ case "eq":
+ operators.$eq = operand;
+ break;
+ case "ne":
+ operators.$ne = operand;
+ break;
+ case "gt":
+ operators.$gt = operand;
+ break;
+ case "gte":
+ operators.$gte = operand;
+ break;
+ case "lt":
+ operators.$lt = operand;
+ break;
+ case "lte":
+ operators.$lte = operand;
+ break;
+ case "in":
+ if (!Array.isArray(operand) || operand.length === 0) {
+ return { [ALWAYS_FALSE_FILTER_KEY]: true };
+ }
+ operators.$in = operand;
+ break;
+ case "nin":
+ if (!Array.isArray(operand) || operand.length === 0) {
+ break;
+ }
+ operators.$nin = operand;
+ break;
+ case "contains":
+ case "icontains":
+ case "startsWith":
+ throw new Error(
+ `S3 Vectors does not support '${operator}' metadata filters.`,
+ );
+ default:
+ throw new Error(
+ `Unsupported S3 Vectors filter operator: ${operator}`,
+ );
+ }
+ }
+
+ if (Object.keys(operators).length === 0) {
+ return {};
+ }
+ return { [key]: operators };
+ }
+
+ private negateFilter(filter: S3Filter): S3Filter {
+ if (Array.isArray(filter.$and)) {
+ return {
+ $or: filter.$and.map((entry: S3Filter) => this.negateFilter(entry)),
+ };
+ }
+
+ if (Array.isArray(filter.$or)) {
+ return {
+ $and: filter.$or.map((entry: S3Filter) => this.negateFilter(entry)),
+ };
+ }
+
+ const entries = Object.entries(filter);
+ if (entries.length !== 1) {
+ throw new Error("S3 Vectors cannot negate this filter shape.");
+ }
+
+ const [key, rawValue] = entries[0];
+ if (key.startsWith("$")) {
+ throw new Error("S3 Vectors cannot negate this filter shape.");
+ }
+
+ const value =
+ typeof rawValue === "object" && rawValue !== null
+ ? rawValue
+ : { $eq: rawValue };
+ const negated: Record = {};
+
+ for (const [operator, operand] of Object.entries(value)) {
+ switch (operator) {
+ case "$eq":
+ negated.$ne = operand;
+ break;
+ case "$ne":
+ negated.$eq = operand;
+ break;
+ case "$gt":
+ negated.$lte = operand;
+ break;
+ case "$gte":
+ negated.$lt = operand;
+ break;
+ case "$lt":
+ negated.$gte = operand;
+ break;
+ case "$lte":
+ negated.$gt = operand;
+ break;
+ case "$in":
+ negated.$nin = operand;
+ break;
+ case "$nin":
+ negated.$in = operand;
+ break;
+ case "$exists":
+ negated.$exists = !operand;
+ break;
+ default:
+ throw new Error("S3 Vectors cannot negate this filter shape.");
+ }
+ }
+
+ return { [key]: negated };
+ }
+
+ private matchesFilter(
+ metadata: Record,
+ filter: S3Filter,
+ ): boolean {
+ if (this.isAlwaysFalseFilter(filter)) {
+ return false;
+ }
+ if (Array.isArray(filter.$and)) {
+ return filter.$and.every((entry: S3Filter) =>
+ this.matchesFilter(metadata, entry),
+ );
+ }
+
+ if (Array.isArray(filter.$or)) {
+ return filter.$or.some((entry: S3Filter) =>
+ this.matchesFilter(metadata, entry),
+ );
+ }
+
+ return Object.entries(filter).every(([key, rawValue]) =>
+ this.matchesFieldFilter(metadata[key], rawValue),
+ );
+ }
+
+ private matchesFieldFilter(metadataValue: any, rawValue: any): boolean {
+ const value =
+ typeof rawValue === "object" && rawValue !== null
+ ? rawValue
+ : { $eq: rawValue };
+
+ return Object.entries(value).every(([operator, operand]) =>
+ this.evaluateOperator(metadataValue, operator, operand),
+ );
+ }
+
+ private evaluateOperator(
+ metadataValue: any,
+ operator: string,
+ operand: any,
+ ): boolean {
+ switch (operator) {
+ case "$eq":
+ if (Array.isArray(metadataValue)) {
+ return metadataValue.some((entry) => entry === operand);
+ }
+ return metadataValue === operand;
+ case "$ne":
+ if (Array.isArray(metadataValue)) {
+ return !metadataValue.some((entry) => entry === operand);
+ }
+ return metadataValue !== operand;
+ case "$gt":
+ return typeof metadataValue === "number" && metadataValue > operand;
+ case "$gte":
+ return typeof metadataValue === "number" && metadataValue >= operand;
+ case "$lt":
+ return typeof metadataValue === "number" && metadataValue < operand;
+ case "$lte":
+ return typeof metadataValue === "number" && metadataValue <= operand;
+ case "$in":
+ if (!Array.isArray(operand) || operand.length === 0) {
+ return false;
+ }
+ if (Array.isArray(metadataValue)) {
+ return metadataValue.some((entry) => operand.includes(entry));
+ }
+ return operand.includes(metadataValue);
+ case "$nin":
+ if (!Array.isArray(operand) || operand.length === 0) {
+ return true;
+ }
+ if (Array.isArray(metadataValue)) {
+ return metadataValue.every((entry) => !operand.includes(entry));
+ }
+ return !operand.includes(metadataValue);
+ case "$exists":
+ return operand
+ ? metadataValue !== undefined
+ : metadataValue === undefined;
+ default:
+ throw new Error(`Unsupported S3 Vectors filter operator: ${operator}`);
+ }
+ }
+
+ private toVectorData(vector: number[]): VectorData {
+ return { float32: vector };
+ }
+
+ private fromVectorData(data?: VectorData): number[] | undefined {
+ if (!data || !("float32" in data) || !Array.isArray(data.float32)) {
+ return undefined;
+ }
+ return data.float32;
+ }
+
+ private normalizeMetadata(metadata: any): Record {
+ if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
+ return { ...(metadata as Record) };
+ }
+ return {};
+ }
+
+ private normalizeQueryVector(
+ vector: QueryOutputVector,
+ distanceMetric: DistanceMetric | "cosine" | "euclidean",
+ ): VectorStoreResult {
+ return {
+ id: String(vector.key),
+ payload: this.normalizeMetadata(vector.metadata),
+ score: this.normalizeScore(vector.distance, distanceMetric),
+ };
+ }
+
+ private normalizeStoredVector(
+ vector: GetOutputVector | ListOutputVector,
+ ): VectorStoreResult {
+ return {
+ id: String(vector.key),
+ payload: this.normalizeMetadata(vector.metadata),
+ };
+ }
+
+ private normalizeListedVector(vector: ListOutputVector): VectorStoreResult {
+ return this.normalizeStoredVector(vector);
+ }
+
+ private normalizeScore(
+ distance?: number,
+ distanceMetric: DistanceMetric | "cosine" | "euclidean" = this
+ .distanceMetric,
+ ): number | undefined {
+ if (distance === undefined || distance === null) {
+ return undefined;
+ }
+ if (!Number.isFinite(distance)) {
+ return undefined;
+ }
+ if (distanceMetric === "euclidean") {
+ return 1 / (1 + distance);
+ }
+ return Math.max(0, Math.min(1, 1 - distance));
+ }
+
+ private assertVectorDimension(vector: number[], context: string): void {
+ if (vector.length !== this.dimension) {
+ throw new Error(
+ `${context} dimension mismatch. Expected ${this.dimension}, got ${vector.length}`,
+ );
+ }
+ }
+
+ private assertBatchDimensions(vectors: number[][], context: string): void {
+ for (const vector of vectors) {
+ this.assertVectorDimension(vector, context);
+ }
+ }
+
+ private isNotFound(error: any): boolean {
+ return error?.name === "NotFoundException";
+ }
+
+ private isConflict(error: any): boolean {
+ return error?.name === "ConflictException";
+ }
+
+ private isAlwaysFalseFilter(filter: S3Filter): boolean {
+ return filter[ALWAYS_FALSE_FILTER_KEY] === true;
+ }
+
+ private isNoOpFilter(filter: S3Filter): boolean {
+ return Object.keys(filter).length === 0;
+ }
+}
diff --git a/mem0-ts/src/oss/tests/factory.unit.test.ts b/mem0-ts/src/oss/tests/factory.unit.test.ts
index c1651abe9..99a735e7c 100644
--- a/mem0-ts/src/oss/tests/factory.unit.test.ts
+++ b/mem0-ts/src/oss/tests/factory.unit.test.ts
@@ -138,6 +138,11 @@ jest.mock("../src/vector_stores/pgvector", () => ({
.fn()
.mockImplementation((config) => ({ type: "pgvector", config })),
}));
+jest.mock("../src/vector_stores/s3_vectors", () => ({
+ S3Vectors: jest
+ .fn()
+ .mockImplementation((config) => ({ type: "s3-vectors", config })),
+}));
jest.mock("../src/storage/SupabaseHistoryManager", () => ({
SupabaseHistoryManager: jest
.fn()
@@ -260,6 +265,8 @@ describe("VectorStoreFactory", () => {
["vectorize"],
["azure-ai-search"],
["pgvector"],
+ ["s3-vectors"],
+ ["s3_vectors"],
])("creates vector store for provider '%s'", (provider) => {
expect(() =>
VectorStoreFactory.create(provider, dummyVSConfig),
diff --git a/mem0-ts/src/oss/tests/tsup-externals.test.ts b/mem0-ts/src/oss/tests/tsup-externals.test.ts
index 9206aa27d..0512a4ee2 100644
--- a/mem0-ts/src/oss/tests/tsup-externals.test.ts
+++ b/mem0-ts/src/oss/tests/tsup-externals.test.ts
@@ -14,7 +14,6 @@ describe("tsup.config.ts externals", () => {
beforeAll(() => {
const pkgPath = path.resolve(__dirname, "../../../package.json");
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
- // Filter out @types/* packages — they are type-only and not bundled at runtime
peerDeps = Object.keys(pkg.peerDependencies || {}).filter(
(dep) => !dep.startsWith("@types/"),
);
@@ -22,16 +21,16 @@ describe("tsup.config.ts externals", () => {
const tsupConfigPath = path.resolve(__dirname, "../../../tsup.config.ts");
const tsupContent = fs.readFileSync(tsupConfigPath, "utf-8");
-
- // Extract strings from the external array (supports double, single, and backtick quotes)
const externalMatch = tsupContent.match(
/const external\s*=\s*\[([\s\S]*?)\];/,
);
+
if (!externalMatch) {
throw new Error("Could not find external array in tsup.config.ts");
}
+
const matches = externalMatch[1].match(/["'`]([^"'`]+)["'`]/g);
- externalDeps = (matches || []).map((m) => m.replace(/["'`]/g, ""));
+ externalDeps = (matches || []).map((value) => value.replace(/["'`]/g, ""));
});
it("should have every peerDependency in the external array", () => {
diff --git a/mem0-ts/src/oss/tests/vector-stores-compat.test.ts b/mem0-ts/src/oss/tests/vector-stores-compat.test.ts
index 81c9c7f34..5c8225c1f 100644
--- a/mem0-ts/src/oss/tests/vector-stores-compat.test.ts
+++ b/mem0-ts/src/oss/tests/vector-stores-compat.test.ts
@@ -720,7 +720,358 @@ describe("AzureAISearch – backward compat with mocked client", () => {
});
// ───────────────────────────────────────────────────────────────────────────
-// 6. Vectorize — mock Cloudflare client, test idempotent init
+// 6. S3 Vectors — mock AWS client, test interface + init
+// ───────────────────────────────────────────────────────────────────────────
+describe("S3 Vectors – backward compat with mocked client", () => {
+ function createMockS3VectorsClient(options?: {
+ queryDistance?: number;
+ queryDistanceMetric?: "cosine" | "euclidean";
+ }) {
+ const buckets = new Set();
+ const indexes = new Map<
+ string,
+ {
+ dimension: number;
+ distanceMetric: string;
+ }
+ >();
+ const vectors = new Map<
+ string,
+ {
+ key: string;
+ data: { float32: number[] };
+ metadata: Record;
+ }
+ >();
+
+ const vectorMapKey = (indexName: string, key: string) =>
+ `${indexName}:${key}`;
+
+ return {
+ send: jest.fn().mockImplementation(async (command: any) => {
+ const name = command.constructor.name;
+ const input = command.input;
+
+ switch (name) {
+ case "GetVectorBucketCommand":
+ if (!buckets.has(input.vectorBucketName)) {
+ const error: any = new Error("Bucket not found");
+ error.name = "NotFoundException";
+ throw error;
+ }
+ return {};
+ case "CreateVectorBucketCommand":
+ if (buckets.has(input.vectorBucketName)) {
+ const error: any = new Error("Bucket exists");
+ error.name = "ConflictException";
+ throw error;
+ }
+ buckets.add(input.vectorBucketName);
+ return {};
+ case "GetIndexCommand":
+ if (!indexes.has(input.indexName)) {
+ const error: any = new Error("Index not found");
+ error.name = "NotFoundException";
+ throw error;
+ }
+ return {};
+ case "CreateIndexCommand":
+ if (indexes.has(input.indexName)) {
+ const error: any = new Error("Index exists");
+ error.name = "ConflictException";
+ throw error;
+ }
+ indexes.set(input.indexName, {
+ dimension: input.dimension,
+ distanceMetric: input.distanceMetric,
+ });
+ return {};
+ case "PutVectorsCommand":
+ for (const vector of input.vectors || []) {
+ vectors.set(vectorMapKey(input.indexName, vector.key), {
+ key: vector.key,
+ data: vector.data,
+ metadata: vector.metadata || {},
+ });
+ }
+ return {};
+ case "QueryVectorsCommand":
+ return {
+ distanceMetric: options?.queryDistanceMetric ?? "cosine",
+ vectors: [
+ {
+ key: "doc-1",
+ metadata: { user_id: "u1", topic: "alpha" },
+ distance: options?.queryDistance ?? 0.25,
+ },
+ ],
+ };
+ case "GetVectorsCommand":
+ return {
+ vectors: (input.keys || [])
+ .map((key: string) =>
+ vectors.get(vectorMapKey(input.indexName, key)),
+ )
+ .filter(Boolean),
+ };
+ case "DeleteVectorsCommand":
+ for (const key of input.keys || []) {
+ vectors.delete(vectorMapKey(input.indexName, key));
+ }
+ return {};
+ case "DeleteIndexCommand":
+ indexes.delete(input.indexName);
+ for (const key of Array.from(vectors.keys())) {
+ if (key.startsWith(`${input.indexName}:`)) {
+ vectors.delete(key);
+ }
+ }
+ return {};
+ case "ListVectorsCommand":
+ return {
+ vectors: Array.from(vectors.values())
+ .filter((entry) =>
+ vectorMapKey(input.indexName, entry.key).startsWith(
+ `${input.indexName}:`,
+ ),
+ )
+ .map((entry) => ({
+ key: entry.key,
+ metadata: entry.metadata,
+ })),
+ };
+ default:
+ throw new Error(`Unexpected S3Vectors command: ${name}`);
+ }
+ }),
+ };
+ }
+
+ function findCommandInput(
+ client: { send: jest.Mock },
+ commandName: string,
+ ): Record | undefined {
+ const match = client.send.mock.calls.find(
+ ([command]) => command.constructor.name === commandName,
+ );
+ return match?.[0]?.input;
+ }
+
+ it("implements full VectorStore interface", () => {
+ const { S3Vectors } = require("../src/vector_stores/s3_vectors");
+ const store = new S3Vectors({
+ client: createMockS3VectorsClient(),
+ vectorBucketName: "test-bucket",
+ collectionName: "test-index",
+ embeddingModelDims: 3,
+ });
+ expect(typeof store.insert).toBe("function");
+ expect(typeof store.search).toBe("function");
+ expect(typeof store.get).toBe("function");
+ expect(typeof store.update).toBe("function");
+ expect(typeof store.delete).toBe("function");
+ expect(typeof store.deleteCol).toBe("function");
+ expect(typeof store.list).toBe("function");
+ expect(typeof store.getUserId).toBe("function");
+ expect(typeof store.setUserId).toBe("function");
+ expect(typeof store.initialize).toBe("function");
+ });
+
+ it("initialize() is idempotent (same promise returned)", async () => {
+ const { S3Vectors } = require("../src/vector_stores/s3_vectors");
+ const mockClient = createMockS3VectorsClient();
+ const store = new S3Vectors({
+ client: mockClient,
+ vectorBucketName: "test-bucket",
+ collectionName: "test-index",
+ embeddingModelDims: 3,
+ });
+
+ const p1 = store.initialize();
+ const p2 = store.initialize();
+ const p3 = store.initialize();
+
+ await Promise.all([p1, p2, p3]);
+
+ expect(
+ mockClient.send.mock.calls.filter(
+ ([command]) => command.constructor.name === "CreateVectorBucketCommand",
+ ),
+ ).toHaveLength(1);
+ expect(
+ mockClient.send.mock.calls.filter(
+ ([command]) => command.constructor.name === "CreateIndexCommand",
+ ),
+ ).toHaveLength(1);
+ expect(
+ mockClient.send.mock.calls.filter(
+ ([command]) => command.constructor.name === "GetVectorBucketCommand",
+ ),
+ ).toHaveLength(1);
+ expect(
+ mockClient.send.mock.calls.filter(
+ ([command]) => command.constructor.name === "GetIndexCommand",
+ ),
+ ).toHaveLength(1);
+ });
+
+ it("shapes S3 write requests and normalizes search results", async () => {
+ const { S3Vectors } = require("../src/vector_stores/s3_vectors");
+ const mockClient = createMockS3VectorsClient();
+ const store = new S3Vectors({
+ client: mockClient,
+ vectorBucketName: "test-bucket",
+ collectionName: "test-index",
+ embeddingModelDims: 3,
+ });
+
+ await store.initialize();
+ await store.insert(
+ [[0.1, 0.2, 0.3]],
+ ["doc-1"],
+ [{ user_id: "u1", topic: "alpha" }],
+ );
+
+ const putInput = findCommandInput(mockClient, "PutVectorsCommand");
+ expect(putInput).toMatchObject({
+ vectorBucketName: "test-bucket",
+ indexName: "test-index",
+ vectors: [
+ {
+ key: "doc-1",
+ data: { float32: [0.1, 0.2, 0.3] },
+ metadata: { user_id: "u1", topic: "alpha" },
+ },
+ ],
+ });
+
+ const results = await store.search([0.1, 0.2, 0.3], 5, { user_id: "u1" });
+
+ const queryInput = findCommandInput(mockClient, "QueryVectorsCommand");
+ expect(queryInput).toMatchObject({
+ vectorBucketName: "test-bucket",
+ indexName: "test-index",
+ queryVector: { float32: [0.1, 0.2, 0.3] },
+ topK: 5,
+ filter: { user_id: { $eq: "u1" } },
+ returnMetadata: true,
+ returnDistance: true,
+ });
+ expect(results).toEqual([
+ {
+ id: "doc-1",
+ payload: { user_id: "u1", topic: "alpha" },
+ score: 0.75,
+ },
+ ]);
+ });
+
+ it("normalizes euclidean distances without collapsing scores to zero", async () => {
+ const { S3Vectors } = require("../src/vector_stores/s3_vectors");
+ const store = new S3Vectors({
+ client: createMockS3VectorsClient({
+ queryDistance: 1.5,
+ queryDistanceMetric: "euclidean",
+ }),
+ vectorBucketName: "test-bucket",
+ collectionName: "test-index",
+ embeddingModelDims: 3,
+ distanceMetric: "cosine",
+ });
+
+ const [result] = await store.search([0.1, 0.2, 0.3], 1);
+
+ expect(result).toEqual({
+ id: "doc-1",
+ payload: { user_id: "u1", topic: "alpha" },
+ score: 0.4,
+ });
+ });
+
+ it("normalizes empty in and nin operands before search hits AWS", async () => {
+ const { S3Vectors } = require("../src/vector_stores/s3_vectors");
+ const mockClient = createMockS3VectorsClient();
+ const store = new S3Vectors({
+ client: mockClient,
+ vectorBucketName: "test-bucket",
+ collectionName: "test-index",
+ embeddingModelDims: 3,
+ });
+
+ const baselineQueryCalls = mockClient.send.mock.calls.filter(
+ ([command]) => command.constructor.name === "QueryVectorsCommand",
+ ).length;
+ const emptyInResults = await store.search([0.1, 0.2, 0.3], 5, {
+ topic: { in: [] },
+ });
+ expect(emptyInResults).toEqual([]);
+ expect(
+ mockClient.send.mock.calls.filter(
+ ([command]) => command.constructor.name === "QueryVectorsCommand",
+ ),
+ ).toHaveLength(baselineQueryCalls);
+
+ await store.search([0.1, 0.2, 0.3], 5, {
+ topic: { nin: [] },
+ });
+
+ const queryInput = findCommandInput(mockClient, "QueryVectorsCommand");
+ expect(queryInput).toMatchObject({
+ vectorBucketName: "test-bucket",
+ indexName: "test-index",
+ queryVector: { float32: [0.1, 0.2, 0.3] },
+ topK: 5,
+ returnMetadata: true,
+ returnDistance: true,
+ });
+ expect(queryInput.filter).toBeUndefined();
+ });
+ it("applies client-side list filters, including empty $nin operands", async () => {
+ const { S3Vectors } = require("../src/vector_stores/s3_vectors");
+ const mockClient = createMockS3VectorsClient();
+ const store = new S3Vectors({
+ client: mockClient,
+ vectorBucketName: "test-bucket",
+ collectionName: "test-index",
+ embeddingModelDims: 3,
+ });
+
+ await store.initialize();
+ await store.insert(
+ [
+ [0.1, 0.2, 0.3],
+ [0.3, 0.2, 0.1],
+ ],
+ ["doc-1", "doc-2"],
+ [
+ { user_id: "u1", topic: "alpha", tags: ["keep"] },
+ { user_id: "u2", topic: "beta", tags: ["skip"] },
+ ],
+ );
+
+ const [allRows, allCount] = await store.list({ topic: { nin: [] } }, 10);
+ expect(allCount).toBe(2);
+ expect(allRows.map((row) => row.id)).toEqual(["doc-1", "doc-2"]);
+
+ const [filteredRows, filteredCount] = await store.list(
+ {
+ $and: [{ topic: { nin: ["beta"] } }, { tags: { in: ["keep"] } }],
+ },
+ 10,
+ );
+
+ expect(filteredCount).toBe(1);
+ expect(filteredRows).toEqual([
+ {
+ id: "doc-1",
+ payload: { user_id: "u1", topic: "alpha", tags: ["keep"] },
+ },
+ ]);
+ });
+});
+
+// ───────────────────────────────────────────────────────────────────────────
+// 7. Vectorize — mock Cloudflare client, test idempotent init
// ───────────────────────────────────────────────────────────────────────────
describe("Vectorize – backward compat with mocked client", () => {
let VectorizeDB: any;
diff --git a/mem0-ts/tsup.config.ts b/mem0-ts/tsup.config.ts
index 76ea4e777..86cc7789a 100644
--- a/mem0-ts/tsup.config.ts
+++ b/mem0-ts/tsup.config.ts
@@ -4,6 +4,7 @@ import pkg from "./package.json";
const external = [
"openai",
"@anthropic-ai/sdk",
+ "@aws-sdk/client-s3vectors",
"groq-sdk",
"uuid",
"pg",