diff --git a/.cursor/rules/tests-nvm.mdc b/.cursor/rules/tests-nvm.mdc new file mode 100644 index 000000000..645ec639b --- /dev/null +++ b/.cursor/rules/tests-nvm.mdc @@ -0,0 +1,14 @@ +--- +description: Run nvm use before tests to avoid SyntaxError (e.g. from "with" or other Node-version-dependent syntax) +alwaysApply: true +--- + +# Running tests + +Before running any test commands in this project (e.g. `npm test`, `npm run mocha`, `npm run test:unit`), run: + +```bash +nvm use +``` + +This ensures the Node version from `.nvmrc` is active. Skipping it can cause SyntaxError or other version-related failures. diff --git a/CHANGELOG.md b/CHANGELOG.md index a63244a2b..3ebefa081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,57 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [v1.0.0](https://github.com/oceanprotocol/ocean-node/compare/v0.2.3...v1.0.0) +#### [v1.0.7](https://github.com/oceanprotocol/ocean-node/compare/v1.0.6...v1.0.7) + +- fix query [`#1252`](https://github.com/oceanprotocol/ocean-node/pull/1252) + +#### [v1.0.6](https://github.com/oceanprotocol/ocean-node/compare/v1.0.5...v1.0.6) + +> 3 March 2026 + +- fix engine hash creation [`#1250`](https://github.com/oceanprotocol/ocean-node/pull/1250) +- Release 1.0.6 [`b7a7f65`](https://github.com/oceanprotocol/ocean-node/commit/b7a7f655573903595f4ac76a917385b3a14ba60b) + +#### [v1.0.5](https://github.com/oceanprotocol/ocean-node/compare/v1.0.4...v1.0.5) + +> 3 March 2026 + +- Locks catch error [`#1248`](https://github.com/oceanprotocol/ocean-node/pull/1248) +- Release 1.0.5 [`080a370`](https://github.com/oceanprotocol/ocean-node/commit/080a370a9f5f436279247b43a6a254cacbeeccf1) + +#### [v1.0.4](https://github.com/oceanprotocol/ocean-node/compare/v1.0.3...v1.0.4) + +> 3 March 2026 + +- fix job deletion [`#1247`](https://github.com/oceanprotocol/ocean-node/pull/1247) +- Release 1.0.4 [`152f8d1`](https://github.com/oceanprotocol/ocean-node/commit/152f8d108c86a9d0031f15a042c0851902a83971) + +#### [v1.0.3](https://github.com/oceanprotocol/ocean-node/compare/v1.0.2...v1.0.3) + +> 2 March 2026 + +- fix access lists validation format [`#1244`](https://github.com/oceanprotocol/ocean-node/pull/1244) +- Release 1.0.3 [`188710b`](https://github.com/oceanprotocol/ocean-node/commit/188710b51ba461476974eb80bd9c5e38d71a8d9d) + +#### [v1.0.2](https://github.com/oceanprotocol/ocean-node/compare/v1.0.1...v1.0.2) + +> 2 March 2026 + +- allow custom cpu & ram [`#1242`](https://github.com/oceanprotocol/ocean-node/pull/1242) +- Release 1.0.2 [`3ae416e`](https://github.com/oceanprotocol/ocean-node/commit/3ae416efd0c6ac8014b0ccac4bf1ee399780d186) + +#### [v1.0.1](https://github.com/oceanprotocol/ocean-node/compare/v1.0.0...v1.0.1) + +> 1 March 2026 + +- update dns bootstraps [`#1239`](https://github.com/oceanprotocol/ocean-node/pull/1239) +- Do not abort on ping failure [`#1238`](https://github.com/oceanprotocol/ocean-node/pull/1238) +- Bump minimatch from 3.1.2 to 3.1.5 [`#1237`](https://github.com/oceanprotocol/ocean-node/pull/1237) +- Release 1.0.1 [`c79f33b`](https://github.com/oceanprotocol/ocean-node/commit/c79f33bbc4765aa5fdf73ad52cc0245f942a0068) + +### [v1.0.0](https://github.com/oceanprotocol/ocean-node/compare/v0.2.3...v1.0.0) + +> 27 February 2026 - Release 1 0 [`#1235`](https://github.com/oceanprotocol/ocean-node/pull/1235) - retry connection lost [`#1213`](https://github.com/oceanprotocol/ocean-node/pull/1213) @@ -177,36 +227,36 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Fix validUntil value for free c2d. [`#899`](https://github.com/oceanprotocol/ocean-node/pull/899) - Bump axios from 1.7.4 to 1.8.2 [`#892`](https://github.com/oceanprotocol/ocean-node/pull/892) - Bump serialize-javascript and mocha [`#891`](https://github.com/oceanprotocol/ocean-node/pull/891) +- Release 1.0.0 [`bbdf97b`](https://github.com/oceanprotocol/ocean-node/commit/bbdf97b55eb62a985650f0f044c7e4e300169c2d) - add integration tests for getJobs handler [`b4bdb40`](https://github.com/oceanprotocol/ocean-node/commit/b4bdb4058807c0315d6ed1c2982893ed5354aad8) - remove retrial. [`1190c46`](https://github.com/oceanprotocol/ocean-node/commit/1190c46f7bb676083e12c9741a477a7166b9165f) -- Fix retrial in worker. [`8d5ed53`](https://github.com/oceanprotocol/ocean-node/commit/8d5ed53825b5b1026c0b2968ae4e4222a7401829) #### [v0.2.3](https://github.com/oceanprotocol/ocean-node/compare/v0.2.1...v0.2.3) > 24 March 2025 -- Update node script - C2D [`#896`](https://github.com/oceanprotocol/ocean-node/pull/896) +- Update node script - C2D [`#896`](https://github.com/oceanprotocol/ocean-node/pull/896) - fix docker-compose [`#895`](https://github.com/oceanprotocol/ocean-node/pull/895) - re-indexing old DDOs [`#867`](https://github.com/oceanprotocol/ocean-node/pull/867) - Upgrade tsx dep to v4.x [`#893`](https://github.com/oceanprotocol/ocean-node/pull/893) - C2D Docker [`#705`](https://github.com/oceanprotocol/ocean-node/pull/705) - Updating codeowners [`#887`](https://github.com/oceanprotocol/ocean-node/pull/887) -- fix issue with empty nft fields [`#886`](https://github.com/oceanprotocol/ocean-node/pull/886) +- fix issue with empty nft fields [`#886`](https://github.com/oceanprotocol/ocean-node/pull/886) - add allowed admins access list [`#841`](https://github.com/oceanprotocol/ocean-node/pull/841) - Update error message for invalid peer connection [`#874`](https://github.com/oceanprotocol/ocean-node/pull/874) - add AUTHORIZED_DECRYPTERS_LIST [`#836`](https://github.com/oceanprotocol/ocean-node/pull/836) - fix status code if policy server not available [`#869`](https://github.com/oceanprotocol/ocean-node/pull/869) -- Fix DDO: Stats and Prices for exchanges/dispensers [`#774`](https://github.com/oceanprotocol/ocean-node/pull/774) +- Fix DDO: Stats and Prices for exchanges/dispensers [`#774`](https://github.com/oceanprotocol/ocean-node/pull/774) - move p2p getters as handlers [`#862`](https://github.com/oceanprotocol/ocean-node/pull/862) - always check remote peerId [`#864`](https://github.com/oceanprotocol/ocean-node/pull/864) - Test if dashboard changes are already committed [`#842`](https://github.com/oceanprotocol/ocean-node/pull/842) -- add AUTHORIZED_PUBLISHERS_*** env variables [`#826`](https://github.com/oceanprotocol/ocean-node/pull/826) +- add AUTHORIZED*PUBLISHERS*\*\*\* env variables [`#826`](https://github.com/oceanprotocol/ocean-node/pull/826) - Issue 814 credentials types [`#823`](https://github.com/oceanprotocol/ocean-node/pull/823) - remove echo command [`#839`](https://github.com/oceanprotocol/ocean-node/pull/839) - Issue 808 - new accesslist credentials type [`#819`](https://github.com/oceanprotocol/ocean-node/pull/819) - add ALLOWED_VALIDATORS_LIST [`#829`](https://github.com/oceanprotocol/ocean-node/pull/829) - update build files and hash [`#821`](https://github.com/oceanprotocol/ocean-node/pull/821) -- add * as match all rule for address types [`#837`](https://github.com/oceanprotocol/ocean-node/pull/837) +- add \* as match all rule for address types [`#837`](https://github.com/oceanprotocol/ocean-node/pull/837) - fix: return correct message on policy server call [`#834`](https://github.com/oceanprotocol/ocean-node/pull/834) - add policyServerPassthrough routes [`#832`](https://github.com/oceanprotocol/ocean-node/pull/832) - Bump fast-xml-parser from 4.3.6 to 4.5.0 in /dashboard [`#711`](https://github.com/oceanprotocol/ocean-node/pull/711) @@ -242,7 +292,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - fix system tests. Running old version of node? [`#733`](https://github.com/oceanprotocol/ocean-node/pull/733) - rm console.logs [`#731`](https://github.com/oceanprotocol/ocean-node/pull/731) - fix wrong block for log [`#727`](https://github.com/oceanprotocol/ocean-node/pull/727) -- nonce db sql lite [`#723`](https://github.com/oceanprotocol/ocean-node/pull/723) +- nonce db sql lite [`#723`](https://github.com/oceanprotocol/ocean-node/pull/723) - Bump version axios 1.6.0 -> 1.7.4. [`#716`](https://github.com/oceanprotocol/ocean-node/pull/716) - Bump version express 4.18.2 -> 4.21.0. [`#717`](https://github.com/oceanprotocol/ocean-node/pull/717) - Feature/ add Elasticsearch database alternative for typesense [`#599`](https://github.com/oceanprotocol/ocean-node/pull/599) @@ -251,7 +301,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Bump micromatch from 4.0.5 to 4.0.8 in /dashboard [`#649`](https://github.com/oceanprotocol/ocean-node/pull/649) - Bump undici from 5.27.0 to 5.28.4 [`#610`](https://github.com/oceanprotocol/ocean-node/pull/610) - testing changes [`#718`](https://github.com/oceanprotocol/ocean-node/pull/718) -- Policy Server [`#694`](https://github.com/oceanprotocol/ocean-node/pull/694) +- Policy Server [`#694`](https://github.com/oceanprotocol/ocean-node/pull/694) - fix missing/invalid db_url log message, put warn at startup [`#654`](https://github.com/oceanprotocol/ocean-node/pull/654) - move c2d engines under OceanNode class [`#702`](https://github.com/oceanprotocol/ocean-node/pull/702) - improve error message, transfer fees and tweak node response [`#701`](https://github.com/oceanprotocol/ocean-node/pull/701) @@ -381,7 +431,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Differentiate error messages indexer [`#570`](https://github.com/oceanprotocol/ocean-node/pull/570) - Issue 565 optimize get status [`#566`](https://github.com/oceanprotocol/ocean-node/pull/566) - fix get indexing queue [`#564`](https://github.com/oceanprotocol/ocean-node/pull/564) -- Changes on logging transports (.env var based locations) [`#553`](https://github.com/oceanprotocol/ocean-node/pull/553) +- Changes on logging transports (.env var based locations) [`#553`](https://github.com/oceanprotocol/ocean-node/pull/553) - Check if ddo state is active before executing node's commands. [`#542`](https://github.com/oceanprotocol/ocean-node/pull/542) - use static rpc provider [`#548`](https://github.com/oceanprotocol/ocean-node/pull/548) - Fix downloading full content of the file. [`#559`](https://github.com/oceanprotocol/ocean-node/pull/559) @@ -435,7 +485,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Remove chain id from get compute envs task. [`#460`](https://github.com/oceanprotocol/ocean-node/pull/460) - Issue 397 warn env db logs [`#457`](https://github.com/oceanprotocol/ocean-node/pull/457) - fix p2p peers [`#449`](https://github.com/oceanprotocol/ocean-node/pull/449) -- c2d v2 arhitecture [`#381`](https://github.com/oceanprotocol/ocean-node/pull/381) +- c2d v2 arhitecture [`#381`](https://github.com/oceanprotocol/ocean-node/pull/381) - Fix: dashboard failing build if NODE_ENV is changed [`#450`](https://github.com/oceanprotocol/ocean-node/pull/450) - Dashboard: get ocean peers polling [`#445`](https://github.com/oceanprotocol/ocean-node/pull/445) - Replace hardcoded values in Dashboard [`#444`](https://github.com/oceanprotocol/ocean-node/pull/444) @@ -514,7 +564,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Provider fees compute [`#252`](https://github.com/oceanprotocol/ocean-node/pull/252) - Updates to package.json bringing it in line with our other repositories [`#260`](https://github.com/oceanprotocol/ocean-node/pull/260) - Issue 205 ddo handling [`#239`](https://github.com/oceanprotocol/ocean-node/pull/239) -- fix error on publish + no signer/no account / metadata events error [`#255`](https://github.com/oceanprotocol/ocean-node/pull/255) +- fix error on publish + no signer/no account / metadata events error [`#255`](https://github.com/oceanprotocol/ocean-node/pull/255) - add config option for network interfaces, p2p and http [`#248`](https://github.com/oceanprotocol/ocean-node/pull/248) - Feature/ Add handle decrypt method [`#221`](https://github.com/oceanprotocol/ocean-node/pull/221) - Added checks for metadata events. [`#237`](https://github.com/oceanprotocol/ocean-node/pull/237) @@ -522,7 +572,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Move commands from constants.ts to @types/commands.ts [`#244`](https://github.com/oceanprotocol/ocean-node/pull/244) - Issue 227 get environments [`#238`](https://github.com/oceanprotocol/ocean-node/pull/238) - fix unit test on commands.ts - pick mismatches in both directions [`#246`](https://github.com/oceanprotocol/ocean-node/pull/246) -- Expose validateDDO on http [`#234`](https://github.com/oceanprotocol/ocean-node/pull/234) +- Expose validateDDO on http [`#234`](https://github.com/oceanprotocol/ocean-node/pull/234) - Missing param validation for directCommand DOWNLOAD [`#242`](https://github.com/oceanprotocol/ocean-node/pull/242) - add c2d in ci [`#241`](https://github.com/oceanprotocol/ocean-node/pull/241) - add C2C cluster env config [`#240`](https://github.com/oceanprotocol/ocean-node/pull/240) diff --git a/README.md b/README.md index e1e51708d..f6b404ac0 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ Your node is now running. To start additional nodes, repeat these steps in a new - [API Endpoints](docs/API.md) - [Environmental Variables](docs/env.md) - [Database Guide](docs/database.md) +- [Storage Types](docs/Storage.md) - [Testing Guide](docs/testing.md) - [Network Configuration](docs/networking.md) - [Logging & accessing logs](docs/networking.md) diff --git a/docs/Storage.md b/docs/Storage.md new file mode 100644 index 000000000..40f37e812 --- /dev/null +++ b/docs/Storage.md @@ -0,0 +1,173 @@ +# Storage Types + +Ocean Node supports four storage backends for assets (e.g. algorithm or data files). Each type is identified by a `type` field on the file object and has its own shape and validation rules. + +## Supported types + +| Type | `type` value | Description | +| ---------- | ------------- | ------------------------------------ | +| **URL** | `url` | File served via HTTP/HTTPS | +| **IPFS** | `ipfs` | File identified by IPFS CID | +| **Arweave**| `arweave` | File identified by Arweave transaction ID | +| **S3** | `s3` | File in S3-compatible storage (AWS, Ceph, MinIO, etc.) | + +All file objects can optionally include encryption metadata: `encryptedBy` and `encryptMethod` (e.g. `AES`, `ECIES`). + +--- + +## URL storage + +Files are fetched from a given URL using HTTP GET or POST. + +### File object shape + +```json +{ + "type": "url", + "url": "https://example.com/path/to/file.zip", + "method": "get", + "headers": {} +} +``` + +| Field | Required | Description | +| --------- | -------- | ------------------------------------------------ | +| `type` | Yes | Must be `"url"` | +| `url` | Yes | Full HTTP/HTTPS URL to the file | +| `method` | Yes | `"get"` or `"post"` | +| `headers` | No | Optional request headers (key-value object) | + +### Validation + +- `url` and `method` must be present. +- `method` must be `get` or `post` (case-insensitive). +- If the node config defines `unsafeURLs` (list of regex patterns), any URL matching a pattern is rejected. +- The URL must look like a real URL (`http://` or `https://`); path-like values are rejected. + +### Node configuration + +- Optional: `unsafeURLs` – array of regex strings; URLs matching any of them are considered unsafe and rejected. + +--- + +## IPFS storage + +Files are resolved via an IPFS gateway using a content identifier (CID). + +### File object shape + +```json +{ + "type": "ipfs", + "hash": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco" +} +``` + +| Field | Required | Description | +| ------ | -------- | ------------------------------ | +| `type` | Yes | Must be `"ipfs"` | +| `hash` | Yes | IPFS content identifier (CID) | + +The node builds the download URL as: `{ipfsGateway}/ipfs/{hash}` (e.g. `https://ipfs.io/ipfs/QmXoy...`). + +### Validation + +- `hash` (CID) must be present. +- The value must not look like an HTTP(S) URL (use URL storage for gateway URLs). +- The value must not look like a file path. + +### Node configuration + +- **Required**: `ipfsGateway` – base URL of the IPFS HTTP gateway (e.g. `https://ipfs.io`). + +--- + +## Arweave storage + +Files are identified by an Arweave transaction ID and fetched via an Arweave gateway. + +### File object shape + +```json +{ + "type": "arweave", + "transactionId": "abc123..." +} +``` + +| Field | Required | Description | +| --------------- | -------- | -------------------------- | +| `type` | Yes | Must be `"arweave"` | +| `transactionId` | Yes | Arweave transaction ID | + +The node builds the download URL as: `{arweaveGateway}/{transactionId}`. + +### Validation + +- `transactionId` must be present. +- The value must not look like an HTTP(S) URL (use URL storage for direct URLs). +- The value must not look like a file path. + +### Node configuration + +- **Required**: `arweaveGateway` – base URL of the Arweave gateway (e.g. `https://arweave.net`). + +--- + +## S3 storage + +Files are stored in S3-compatible object storage. The node uses the AWS SDK and works with Amazon S3, Ceph, MinIO, DigitalOcean Spaces, and other S3-compatible services. Credentials and endpoint are provided on the file object; no node-level S3 config is required. + +### File object shape + +```json +{ + "type": "s3", + "s3Access": { + "endpoint": "https://s3.amazonaws.com", + "region": "us-east-1", + "bucket": "my-bucket", + "objectKey": "path/to/file.zip", + "accessKeyId": "AKIAIOSFODNN7EXAMPLE", + "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + } +} +``` + +| Field | Required | Description | +| --------- | -------- | ----------- | +| `type` | Yes | Must be `"s3"` | +| `s3Access` | Yes | Object with endpoint, bucket, object key, and credentials (see below). | + +**`s3Access` fields:** + +| Field | Required | Description | +| ----------------- | -------- | ----------- | +| `endpoint` | Yes | S3 endpoint URL (e.g. `https://s3.amazonaws.com`, `https://nyc3.digitaloceanspaces.com`, or `https://my-ceph.example.com`) | +| `bucket` | Yes | Bucket name | +| `objectKey` | Yes | Object key (path within the bucket) | +| `accessKeyId` | Yes | Access key for the S3-compatible API | +| `secretAccessKey` | Yes | Secret key for the S3-compatible API | +| `region` | No | Region (e.g. `us-east-1`). Optional; defaults to `us-east-1` if omitted. Some backends (e.g. Ceph) may ignore it. | +| `forcePathStyle` | No | If `true`, use path-style addressing (e.g. `endpoint/bucket/key`). Required for some S3-compatible services (e.g. MinIO). Default `false` (virtual-host style, e.g. `bucket.endpoint/key`, standard for AWS S3). | + +### Validation + +- `s3Access` must be present. +- Within `s3Access`, `bucket`, `objectKey`, `endpoint`, `accessKeyId`, and `secretAccessKey` must be present and non-empty. +- `region` and `forcePathStyle` are optional; when provided they are used when creating the S3 client. + +### Node configuration + +- None. All S3 connection details (endpoint, credentials, bucket, key) come from the file object’s `s3Access`. + +--- + +## Summary + +- **URL**: flexible HTTP(S) endpoints; optional custom headers and `unsafeURLs` filtering. +- **IPFS**: CID-based; requires `ipfsGateway` in config. +- **Arweave**: transaction-ID-based; requires `arweaveGateway` in config. +- **S3**: S3-compatible object storage (AWS, Ceph, MinIO, etc.); credentials and endpoint in the file object; `region` optional (defaults to `us-east-1`). + +The storage implementation lives under `src/components/storage/`. The node selects the backend from the file object’s `type` (case-insensitive) and validates the shape and config before fetching or streaming the file. diff --git a/docs/env.md b/docs/env.md index 35ee04c5a..4f2445f03 100644 --- a/docs/env.md +++ b/docs/env.md @@ -64,6 +64,20 @@ Environmental variables are also tracked in `ENVIRONMENT_VARIABLES` within `src/ - `ELASTICSEARCH_SNIFF_ON_CONNECTION_FAULT`: Enable automatic cluster node discovery when connection faults occur. Default is `true`. Example: `true` - `ELASTICSEARCH_HEALTH_CHECK_INTERVAL`: Interval in milliseconds for proactive connection health monitoring. Default is `60000`. Example: `60000` +## Database + +- `DB_URL`: URL for connecting to the database. Required for running a database with the node. Example: `"http://localhost:8108/?apiKey=xyz"` +- `DB_USERNAME`: Username for database authentication. Optional if not using authentication. Example: `"elastic"` +- `DB_PASSWORD`: Password for database authentication. Optional if not using authentication. Example: `"password123"` +- `ELASTICSEARCH_REQUEST_TIMEOUT`: Request timeout in milliseconds for Elasticsearch operations. Default is `60000`. Example: `60000` +- `ELASTICSEARCH_PING_TIMEOUT`: Ping timeout in milliseconds for Elasticsearch health checks. Default is `5000`. Example: `5000` +- `ELASTICSEARCH_RESURRECT_STRATEGY`: Strategy for bringing failed Elasticsearch nodes back online. Options are 'ping', 'optimistic', or 'none'. Default is `ping`. Example: `"ping"` +- `ELASTICSEARCH_MAX_RETRIES`: Maximum number of retry attempts for failed Elasticsearch operations. Default is `5`. Example: `5` +- `ELASTICSEARCH_SNIFF_ON_START`: Enable cluster node discovery on Elasticsearch client startup. Default is `true`. Example: `true` +- `ELASTICSEARCH_SNIFF_INTERVAL`: Interval in milliseconds for periodic cluster health monitoring and node discovery. Set to 'false' to disable. Default is `30000`. Example: `30000` +- `ELASTICSEARCH_SNIFF_ON_CONNECTION_FAULT`: Enable automatic cluster node discovery when connection faults occur. Default is `true`. Example: `true` +- `ELASTICSEARCH_HEALTH_CHECK_INTERVAL`: Interval in milliseconds for proactive connection health monitoring. Default is `60000`. Example: `60000` + ## Payments - `ESCROW_CLAIM_TIMEOUT`: Amount of time reserved to claim a escrow payment, in seconds. Defaults to `3600`. Example: `3600` diff --git a/package-lock.json b/package-lock.json index 192d3b76d..fc47b8c8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "ocean-node", - "version": "1.0.3", + "version": "1.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocean-node", - "version": "1.0.3", + "version": "1.0.7", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-s3": "^3.554.0", + "@aws-sdk/client-s3": "^3.1002.0", "@chainsafe/libp2p-noise": "^17.0.0", "@chainsafe/libp2p-yamux": "^8.0.1", "@elastic/elasticsearch": "^8.14.0", @@ -36,7 +36,6 @@ "@multiformats/multiaddr": "^12.2.3", "@oceanprotocol/contracts": "^2.6.0", "@oceanprotocol/ddo-js": "^0.2.0", - "aws-sdk": "^2.1693.0", "axios": "^1.13.5", "base58-js": "^2.0.0", "cors": "^2.8.5", @@ -59,7 +58,7 @@ "node-cron": "^3.0.3", "sqlite3": "^5.1.7", "stream-concat": "^1.0.0", - "tar": "^7.5.8", + "tar": "^7.5.10", "uint8arrays": "^4.0.6", "url-join": "^5.0.0", "winston": "^3.11.0", @@ -355,114 +354,65 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.982.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.982.0.tgz", - "integrity": "sha512-k0ANYAtPiON9BwLXcDgJXkmmCAGEuSk2pZOvrMej2kNhs3xTXoPshIUR5UMCD9apYiWtXJJfXMZSgaME+iWNaQ==", + "version": "3.1004.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1004.0.tgz", + "integrity": "sha512-m0zNfpsona9jQdX1cHtHArOiuvSGZPsgp/KRZS2YjJhKah96G2UN3UNGZQ6aVjXIQjCY6UanCJo0uW9Xf2U41w==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/credential-provider-node": "^3.972.5", - "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", - "@aws-sdk/middleware-expect-continue": "^3.972.3", - "@aws-sdk/middleware-flexible-checksums": "^3.972.4", - "@aws-sdk/middleware-host-header": "^3.972.3", - "@aws-sdk/middleware-location-constraint": "^3.972.3", - "@aws-sdk/middleware-logger": "^3.972.3", - "@aws-sdk/middleware-recursion-detection": "^3.972.3", - "@aws-sdk/middleware-sdk-s3": "^3.972.6", - "@aws-sdk/middleware-ssec": "^3.972.3", - "@aws-sdk/middleware-user-agent": "^3.972.6", - "@aws-sdk/region-config-resolver": "^3.972.3", - "@aws-sdk/signature-v4-multi-region": "3.982.0", - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.982.0", - "@aws-sdk/util-user-agent-browser": "^3.972.3", - "@aws-sdk/util-user-agent-node": "^3.972.4", - "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.22.0", - "@smithy/eventstream-serde-browser": "^4.2.8", - "@smithy/eventstream-serde-config-resolver": "^4.3.8", - "@smithy/eventstream-serde-node": "^4.2.8", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/hash-blob-browser": "^4.2.9", - "@smithy/hash-node": "^4.2.8", - "@smithy/hash-stream-node": "^4.2.8", - "@smithy/invalid-dependency": "^4.2.8", - "@smithy/md5-js": "^4.2.8", - "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.12", - "@smithy/middleware-retry": "^4.4.29", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.1", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.28", - "@smithy/util-defaults-mode-node": "^4.2.31", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/util-stream": "^4.5.10", - "@smithy/util-utf8": "^4.2.0", - "@smithy/util-waiter": "^4.2.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.982.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.982.0.tgz", - "integrity": "sha512-qJrIiivmvujdGqJ0ldSUvhN3k3N7GtPesoOI1BSt0fNXovVnMz4C/JmnkhZihU7hJhDvxJaBROLYTU+lpild4w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/middleware-host-header": "^3.972.3", - "@aws-sdk/middleware-logger": "^3.972.3", - "@aws-sdk/middleware-recursion-detection": "^3.972.3", - "@aws-sdk/middleware-user-agent": "^3.972.6", - "@aws-sdk/region-config-resolver": "^3.972.3", - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.982.0", - "@aws-sdk/util-user-agent-browser": "^3.972.3", - "@aws-sdk/util-user-agent-node": "^3.972.4", - "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.22.0", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/hash-node": "^4.2.8", - "@smithy/invalid-dependency": "^4.2.8", - "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.12", - "@smithy/middleware-retry": "^4.4.29", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.1", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.28", - "@smithy/util-defaults-mode-node": "^4.2.31", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/credential-provider-node": "^3.972.18", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.7", + "@aws-sdk/middleware-expect-continue": "^3.972.7", + "@aws-sdk/middleware-flexible-checksums": "^3.973.4", + "@aws-sdk/middleware-host-header": "^3.972.7", + "@aws-sdk/middleware-location-constraint": "^3.972.7", + "@aws-sdk/middleware-logger": "^3.972.7", + "@aws-sdk/middleware-recursion-detection": "^3.972.7", + "@aws-sdk/middleware-sdk-s3": "^3.972.18", + "@aws-sdk/middleware-ssec": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.19", + "@aws-sdk/region-config-resolver": "^3.972.7", + "@aws-sdk/signature-v4-multi-region": "^3.996.6", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@aws-sdk/util-user-agent-browser": "^3.972.7", + "@aws-sdk/util-user-agent-node": "^3.973.4", + "@smithy/config-resolver": "^4.4.10", + "@smithy/core": "^3.23.8", + "@smithy/eventstream-serde-browser": "^4.2.11", + "@smithy/eventstream-serde-config-resolver": "^4.3.11", + "@smithy/eventstream-serde-node": "^4.2.11", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/hash-blob-browser": "^4.2.12", + "@smithy/hash-node": "^4.2.11", + "@smithy/hash-stream-node": "^4.2.11", + "@smithy/invalid-dependency": "^4.2.11", + "@smithy/md5-js": "^4.2.11", + "@smithy/middleware-content-length": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.22", + "@smithy/middleware-retry": "^4.4.39", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.38", + "@smithy/util-defaults-mode-node": "^4.2.41", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.11", "tslib": "^2.6.2" }, "engines": { @@ -470,23 +420,23 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.973.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.6.tgz", - "integrity": "sha512-pz4ZOw3BLG0NdF25HoB9ymSYyPbMiIjwQJ2aROXRhAzt+b+EOxStfFv8s5iZyP6Kiw7aYhyWxj5G3NhmkoOTKw==", + "version": "3.973.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.18.tgz", + "integrity": "sha512-GUIlegfcK2LO1J2Y98sCJy63rQSiLiDOgVw7HiHPRqfI2vb3XozTVqemwO0VSGXp54ngCnAQz0Lf0YPCBINNxA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/xml-builder": "^3.972.4", - "@smithy/core": "^3.22.0", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/signature-v4": "^5.3.8", - "@smithy/smithy-client": "^4.11.1", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/xml-builder": "^3.972.10", + "@smithy/core": "^3.23.8", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -494,12 +444,12 @@ } }, "node_modules/@aws-sdk/crc64-nvme": { - "version": "3.972.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.0.tgz", - "integrity": "sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw==", + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.4.tgz", + "integrity": "sha512-HKZIZLbRyvzo/bXZU7Zmk6XqU+1C9DjI56xd02vwuDIxedxBEqP17t9ExhbP9QFeNq/a3l9GOcyirFXxmbDhmw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -507,15 +457,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.4.tgz", - "integrity": "sha512-/8dnc7+XNMmViEom2xsNdArQxQPSgy4Z/lm6qaFPTrMFesT1bV3PsBhb19n09nmxHdrtQskYmViddUIjUQElXg==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.16.tgz", + "integrity": "sha512-HrdtnadvTGAQUr18sPzGlE5El3ICphnH6SU7UQOMOWFgRKbTRNN8msTxM4emzguUso9CzaHU2xy5ctSrmK5YNA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/types": "^3.973.1", - "@smithy/property-provider": "^4.2.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -523,20 +473,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.6.tgz", - "integrity": "sha512-5ERWqRljiZv44AIdvIRQ3k+EAV0Sq2WeJHvXuK7gL7bovSxOf8Al7MLH7Eh3rdovH4KHFnlIty7J71mzvQBl5Q==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.18.tgz", + "integrity": "sha512-NyB6smuZAixND5jZumkpkunQ0voc4Mwgkd+SZ6cvAzIB7gK8HV8Zd4rS8Kn5MmoGgusyNfVGG+RLoYc4yFiw+A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/types": "^3.973.1", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.1", - "@smithy/types": "^4.12.0", - "@smithy/util-stream": "^4.5.10", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" }, "engines": { @@ -544,24 +494,24 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.4.tgz", - "integrity": "sha512-eRUg+3HaUKuXWn/lEMirdiA5HOKmEl8hEHVuszIDt2MMBUKgVX5XNGmb3XmbgU17h6DZ+RtjbxQpjhz3SbTjZg==", + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.17.tgz", + "integrity": "sha512-dFqh7nfX43B8dO1aPQHOcjC0SnCJ83H3F+1LoCh3X1P7E7N09I+0/taID0asU6GCddfDExqnEvQtDdkuMe5tKQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/credential-provider-env": "^3.972.4", - "@aws-sdk/credential-provider-http": "^3.972.6", - "@aws-sdk/credential-provider-login": "^3.972.4", - "@aws-sdk/credential-provider-process": "^3.972.4", - "@aws-sdk/credential-provider-sso": "^3.972.4", - "@aws-sdk/credential-provider-web-identity": "^3.972.4", - "@aws-sdk/nested-clients": "3.982.0", - "@aws-sdk/types": "^3.973.1", - "@smithy/credential-provider-imds": "^4.2.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/credential-provider-env": "^3.972.16", + "@aws-sdk/credential-provider-http": "^3.972.18", + "@aws-sdk/credential-provider-login": "^3.972.17", + "@aws-sdk/credential-provider-process": "^3.972.16", + "@aws-sdk/credential-provider-sso": "^3.972.17", + "@aws-sdk/credential-provider-web-identity": "^3.972.17", + "@aws-sdk/nested-clients": "^3.996.7", + "@aws-sdk/types": "^3.973.5", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -569,18 +519,18 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.4.tgz", - "integrity": "sha512-nLGjXuvWWDlQAp505xIONI7Gam0vw2p7Qu3P6on/W2q7rjJXtYjtpHbcsaOjJ/pAju3eTvEQuSuRedcRHVQIAQ==", + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.17.tgz", + "integrity": "sha512-gf2E5b7LpKb+JX2oQsRIDxdRZjBFZt2olCGlWCdb3vBERbXIPgm2t1R5mEnwd4j0UEO/Tbg5zN2KJbHXttJqwA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/nested-clients": "3.982.0", - "@aws-sdk/types": "^3.973.1", - "@smithy/property-provider": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/nested-clients": "^3.996.7", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -588,22 +538,22 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.5.tgz", - "integrity": "sha512-VWXKgSISQCI2GKN3zakTNHSiZ0+mux7v6YHmmbLQp/o3fvYUQJmKGcLZZzg2GFA+tGGBStplra9VFNf/WwxpYg==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.18.tgz", + "integrity": "sha512-ZDJa2gd1xiPg/nBDGhUlat02O8obaDEnICBAVS8qieZ0+nDfaB0Z3ec6gjZj27OqFTjnB/Q5a0GwQwb7rMVViw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.4", - "@aws-sdk/credential-provider-http": "^3.972.6", - "@aws-sdk/credential-provider-ini": "^3.972.4", - "@aws-sdk/credential-provider-process": "^3.972.4", - "@aws-sdk/credential-provider-sso": "^3.972.4", - "@aws-sdk/credential-provider-web-identity": "^3.972.4", - "@aws-sdk/types": "^3.973.1", - "@smithy/credential-provider-imds": "^4.2.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@aws-sdk/credential-provider-env": "^3.972.16", + "@aws-sdk/credential-provider-http": "^3.972.18", + "@aws-sdk/credential-provider-ini": "^3.972.17", + "@aws-sdk/credential-provider-process": "^3.972.16", + "@aws-sdk/credential-provider-sso": "^3.972.17", + "@aws-sdk/credential-provider-web-identity": "^3.972.17", + "@aws-sdk/types": "^3.973.5", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -611,16 +561,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.4.tgz", - "integrity": "sha512-TCZpWUnBQN1YPk6grvd5x419OfXjHvhj5Oj44GYb84dOVChpg/+2VoEj+YVA4F4E/6huQPNnX7UYbTtxJqgihw==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.16.tgz", + "integrity": "sha512-n89ibATwnLEg0ZdZmUds5bq8AfBAdoYEDpqP3uzPLaRuGelsKlIvCYSNNvfgGLi8NaHPNNhs1HjJZYbqkW9b+g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/types": "^3.973.1", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -628,18 +578,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.4.tgz", - "integrity": "sha512-wzsGwv9mKlwJ3vHLyembBvGE/5nPUIwRR2I51B1cBV4Cb4ql9nIIfpmHzm050XYTY5fqTOKJQnhLj7zj89VG8g==", + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.17.tgz", + "integrity": "sha512-wGtte+48xnhnhHMl/MsxzacBPs5A+7JJedjiP452IkHY7vsbYKcvQBqFye8LwdTJVeHtBHv+JFeTscnwepoWGg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.982.0", - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/token-providers": "3.982.0", - "@aws-sdk/types": "^3.973.1", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/nested-clients": "^3.996.7", + "@aws-sdk/token-providers": "3.1004.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -647,17 +597,17 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.4.tgz", - "integrity": "sha512-hIzw2XzrG8jzsUSEatehmpkd5rWzASg5IHUfA+m01k/RtvfAML7ZJVVohuKdhAYx+wV2AThLiQJVzqn7F0khrw==", + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.17.tgz", + "integrity": "sha512-8aiVJh6fTdl8gcyL+sVNcNwTtWpmoFa1Sh7xlj6Z7L/cZ/tYMEBHq44wTYG8Kt0z/PpGNopD89nbj3FHl9QmTA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/nested-clients": "3.982.0", - "@aws-sdk/types": "^3.973.1", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/nested-clients": "^3.996.7", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -665,17 +615,17 @@ } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.3.tgz", - "integrity": "sha512-fmbgWYirF67YF1GfD7cg5N6HHQ96EyRNx/rDIrTF277/zTWVuPI2qS/ZHgofwR1NZPe/NWvoppflQY01LrbVLg==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.7.tgz", + "integrity": "sha512-goX+axlJ6PQlRnzE2bQisZ8wVrlm6dXJfBzMJhd8LhAIBan/w1Kl73fJnalM/S+18VnpzIHumyV6DtgmvqG5IA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-arn-parser": "^3.972.2", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-config-provider": "^4.2.0", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -683,14 +633,14 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.3.tgz", - "integrity": "sha512-4msC33RZsXQpUKR5QR4HnvBSNCPLGHmB55oDiROqqgyOc+TOfVu2xgi5goA7ms6MdZLeEh2905UfWMnMMF4mRg==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.7.tgz", + "integrity": "sha512-mvWqvm61bmZUKmmrtl2uWbokqpenY3Mc3Jf4nXB/Hse6gWxLPaCQThmhPBDzsPSV8/Odn8V6ovWt3pZ7vy4BFQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -698,24 +648,24 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.972.4.tgz", - "integrity": "sha512-xOxsUkF3O3BtIe3tf54OpPo94eZepjFm3z0Dd2TZKbsPxMiRTFXurC04wJ58o/wPW9YHVO9VqZik3MfoPfrKlw==", + "version": "3.973.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.973.4.tgz", + "integrity": "sha512-7CH2jcGmkvkHc5Buz9IGbdjq1729AAlgYJiAvGq7qhCHqYleCsriWdSnmsqWTwdAfXHMT+pkxX3w6v5tJNcSug==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/crc64-nvme": "3.972.0", - "@aws-sdk/types": "^3.973.1", - "@smithy/is-array-buffer": "^4.2.0", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-stream": "^4.5.10", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/crc64-nvme": "^3.972.4", + "@aws-sdk/types": "^3.973.5", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -723,14 +673,14 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.3.tgz", - "integrity": "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.7.tgz", + "integrity": "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -738,13 +688,13 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.3.tgz", - "integrity": "sha512-nIg64CVrsXp67vbK0U1/Is8rik3huS3QkRHn2DRDx4NldrEFMgdkZGI/+cZMKD9k4YOS110Dfu21KZLHrFA/1g==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.7.tgz", + "integrity": "sha512-vdK1LJfffBp87Lj0Bw3WdK1rJk9OLDYdQpqoKgmpIZPe+4+HawZ6THTbvjhJt4C4MNnRrHTKHQjkwBiIpDBoig==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -752,13 +702,13 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.3.tgz", - "integrity": "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.7.tgz", + "integrity": "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -766,15 +716,15 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.3.tgz", - "integrity": "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.7.tgz", + "integrity": "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", + "@aws-sdk/types": "^3.973.5", "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -782,24 +732,24 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.6.tgz", - "integrity": "sha512-Xq7wM6kbgJN1UO++8dvH/efPb1nTwWqFCpZCR7RCLOETP7xAUAhVo7JmsCnML5Di/iC4Oo5VrJ4QmkYcMZniLw==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.18.tgz", + "integrity": "sha512-5E3XxaElrdyk6ZJ0TjH7Qm6ios4b/qQCiLr6oQ8NK7e4Kn6JBTJCaYioQCQ65BpZ1+l1mK5wTAac2+pEz0Smpw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-arn-parser": "^3.972.2", - "@smithy/core": "^3.22.0", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/signature-v4": "^5.3.8", - "@smithy/smithy-client": "^4.11.1", - "@smithy/types": "^4.12.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-stream": "^4.5.10", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.8", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -807,13 +757,13 @@ } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.3.tgz", - "integrity": "sha512-dU6kDuULN3o3jEHcjm0c4zWJlY1zWVkjG9NPe9qxYLLpcbdj5kRYBS2DdWYD+1B9f910DezRuws7xDEqKkHQIg==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.7.tgz", + "integrity": "sha512-G9clGVuAml7d8DYzY6DnRi7TIIDRvZ3YpqJPz/8wnWS5fYx/FNWNmkO6iJVlVkQg9BfeMzd+bVPtPJOvC4B+nQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -821,17 +771,18 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.6.tgz", - "integrity": "sha512-TehLN8W/kivl0U9HcS+keryElEWORROpghDXZBLfnb40DXM7hx/i+7OOjkogXQOF3QtUraJVRkHQ07bPhrWKlw==", + "version": "3.972.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.19.tgz", + "integrity": "sha512-Km90fcXt3W/iqujHzuM6IaDkYCj73gsYufcuWXApWdzoTy6KGk8fnchAjePMARU0xegIR3K4N3yIo1vy7OVe8A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.982.0", - "@smithy/core": "^3.22.0", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@smithy/core": "^3.23.8", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-retry": "^4.2.11", "tslib": "^2.6.2" }, "engines": { @@ -839,48 +790,48 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.982.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.982.0.tgz", - "integrity": "sha512-VVkaH27digrJfdVrT64rjkllvOp4oRiZuuJvrylLXAKl18ujToJR7AqpDldL/LS63RVne3QWIpkygIymxFtliQ==", + "version": "3.996.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.7.tgz", + "integrity": "sha512-MlGWA8uPaOs5AiTZ5JLM4uuWDm9EEAnm9cqwvqQIc6kEgel/8s1BaOWm9QgUcfc9K8qd7KkC3n43yDbeXOA2tg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/middleware-host-header": "^3.972.3", - "@aws-sdk/middleware-logger": "^3.972.3", - "@aws-sdk/middleware-recursion-detection": "^3.972.3", - "@aws-sdk/middleware-user-agent": "^3.972.6", - "@aws-sdk/region-config-resolver": "^3.972.3", - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.982.0", - "@aws-sdk/util-user-agent-browser": "^3.972.3", - "@aws-sdk/util-user-agent-node": "^3.972.4", - "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.22.0", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/hash-node": "^4.2.8", - "@smithy/invalid-dependency": "^4.2.8", - "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.12", - "@smithy/middleware-retry": "^4.4.29", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.1", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.28", - "@smithy/util-defaults-mode-node": "^4.2.31", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/middleware-host-header": "^3.972.7", + "@aws-sdk/middleware-logger": "^3.972.7", + "@aws-sdk/middleware-recursion-detection": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.19", + "@aws-sdk/region-config-resolver": "^3.972.7", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@aws-sdk/util-user-agent-browser": "^3.972.7", + "@aws-sdk/util-user-agent-node": "^3.973.4", + "@smithy/config-resolver": "^4.4.10", + "@smithy/core": "^3.23.8", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/hash-node": "^4.2.11", + "@smithy/invalid-dependency": "^4.2.11", + "@smithy/middleware-content-length": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.22", + "@smithy/middleware-retry": "^4.4.39", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.38", + "@smithy/util-defaults-mode-node": "^4.2.41", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -888,15 +839,15 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.3.tgz", - "integrity": "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.7.tgz", + "integrity": "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/config-resolver": "^4.4.6", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/config-resolver": "^4.4.10", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -904,16 +855,16 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.982.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.982.0.tgz", - "integrity": "sha512-AWqjMAH848aNwnLCtIKM3WO00eHuUoYVfQMP4ccrUHhnEduGOusVgdHQ5mLNQZZNZzREuBwnPPhIP55cy0gFSg==", + "version": "3.996.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.6.tgz", + "integrity": "sha512-NnsOQsVmJXy4+IdPFUjRCWPn9qNH1TzS/f7MiWgXeoHs903tJpAWQWQtoFvLccyPoBgomKP9L89RRr2YsT/L0g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.6", - "@aws-sdk/types": "^3.973.1", - "@smithy/protocol-http": "^5.3.8", - "@smithy/signature-v4": "^5.3.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/middleware-sdk-s3": "^3.972.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -921,17 +872,17 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.982.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.982.0.tgz", - "integrity": "sha512-v3M0KYp2TVHYHNBT7jHD9lLTWAdS9CaWJ2jboRKt0WAB65bA7iUEpR+k4VqKYtpQN4+8kKSc4w+K6kUNZkHKQw==", + "version": "3.1004.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1004.0.tgz", + "integrity": "sha512-j9BwZZId9sFp+4GPhf6KrwO8Tben2sXibZA8D1vv2I1zBdvkUHcBA2g4pkqIpTRalMTLC0NPkBPX0gERxfy/iA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.6", - "@aws-sdk/nested-clients": "3.982.0", - "@aws-sdk/types": "^3.973.1", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/nested-clients": "^3.996.7", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -939,12 +890,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.973.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.1.tgz", - "integrity": "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==", + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.5.tgz", + "integrity": "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -952,9 +903,9 @@ } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.972.2", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.2.tgz", - "integrity": "sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -964,15 +915,15 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.982.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.982.0.tgz", - "integrity": "sha512-M27u8FJP7O0Of9hMWX5dipp//8iglmV9jr7R8SR8RveU+Z50/8TqH68Tu6wUWBGMfXjzbVwn1INIAO5lZrlxXQ==", + "version": "3.996.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", + "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-endpoints": "^3.2.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-endpoints": "^3.3.2", "tslib": "^2.6.2" }, "engines": { @@ -992,27 +943,27 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.972.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.3.tgz", - "integrity": "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.7.tgz", + "integrity": "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.4.tgz", - "integrity": "sha512-3WFCBLiM8QiHDfosQq3Py+lIMgWlFWwFQliUHUqwEiRqLnKyhgbU3AKa7AWJF7lW2Oc/2kFNY4MlAYVnVc0i8A==", + "version": "3.973.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.4.tgz", + "integrity": "sha512-uqKeLqZ9D3nQjH7HGIERNXK9qnSpUK08l4MlJ5/NZqSSdeJsVANYp437EM9sEzwU28c2xfj2V6qlkqzsgtKs6Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.6", - "@aws-sdk/types": "^3.973.1", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/middleware-user-agent": "^3.972.19", + "@aws-sdk/types": "^3.973.5", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -1028,13 +979,13 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.4.tgz", - "integrity": "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz", + "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", - "fast-xml-parser": "5.3.4", + "@smithy/types": "^4.13.0", + "fast-xml-parser": "5.4.1", "tslib": "^2.6.2" }, "engines": { @@ -6206,12 +6157,12 @@ "license": "(Unlicense OR Apache-2.0)" }, "node_modules/@smithy/abort-controller": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", - "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.11.tgz", + "integrity": "sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6219,9 +6170,9 @@ } }, "node_modules/@smithy/chunked-blob-reader": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", - "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.2.tgz", + "integrity": "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6231,12 +6182,12 @@ } }, "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", - "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.3.tgz", + "integrity": "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-base64": "^4.3.0", + "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -6244,16 +6195,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", - "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.10.tgz", + "integrity": "sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", "tslib": "^2.6.2" }, "engines": { @@ -6261,20 +6212,20 @@ } }, "node_modules/@smithy/core": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.22.1.tgz", - "integrity": "sha512-x3ie6Crr58MWrm4viHqqy2Du2rHYZjwu8BekasrQx4ca+Y24dzVAwq3yErdqIbc2G3I0kLQA13PQ+/rde+u65g==", + "version": "3.23.9", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.9.tgz", + "integrity": "sha512-1Vcut4LEL9HZsdpI0vFiRYIsaoPwZLjAxnVQDUMQK8beMS+EYPLDQCXtbzfxmM5GzSgjfe2Q9M7WaXwIMQllyQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.2.9", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-stream": "^4.5.11", - "@smithy/util-utf8": "^4.2.0", - "@smithy/uuid": "^1.1.0", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, "engines": { @@ -6282,15 +6233,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", - "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.11.tgz", + "integrity": "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", "tslib": "^2.6.2" }, "engines": { @@ -6298,14 +6249,14 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz", - "integrity": "sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.11.tgz", + "integrity": "sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.12.0", - "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6313,13 +6264,13 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz", - "integrity": "sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.11.tgz", + "integrity": "sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/eventstream-serde-universal": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6327,12 +6278,12 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz", - "integrity": "sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.11.tgz", + "integrity": "sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6340,13 +6291,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz", - "integrity": "sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.11.tgz", + "integrity": "sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/eventstream-serde-universal": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6354,13 +6305,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz", - "integrity": "sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.11.tgz", + "integrity": "sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/eventstream-codec": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6368,15 +6319,15 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", - "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", + "version": "5.3.13", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.13.tgz", + "integrity": "sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.8", - "@smithy/querystring-builder": "^4.2.8", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", + "@smithy/protocol-http": "^5.3.11", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -6384,14 +6335,14 @@ } }, "node_modules/@smithy/hash-blob-browser": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.9.tgz", - "integrity": "sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.12.tgz", + "integrity": "sha512-1wQE33DsxkM/waftAhCH9VtJbUGyt1PJ9YRDpOu+q9FUi73LLFUZ2fD8A61g2mT1UY9k7b99+V1xZ41Rz4SHRQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/chunked-blob-reader": "^5.2.0", - "@smithy/chunked-blob-reader-native": "^4.2.1", - "@smithy/types": "^4.12.0", + "@smithy/chunked-blob-reader": "^5.2.2", + "@smithy/chunked-blob-reader-native": "^4.2.3", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6399,14 +6350,14 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", - "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.11.tgz", + "integrity": "sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6414,13 +6365,13 @@ } }, "node_modules/@smithy/hash-stream-node": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.8.tgz", - "integrity": "sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.11.tgz", + "integrity": "sha512-hQsTjwPCRY8w9GK07w1RqJi3e+myh0UaOWBBhZ1UMSDgofH/Q1fEYzU1teaX6HkpX/eWDdm7tAGR0jBPlz9QEQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6428,12 +6379,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", - "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.11.tgz", + "integrity": "sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6441,9 +6392,9 @@ } }, "node_modules/@smithy/is-array-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", - "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6453,13 +6404,13 @@ } }, "node_modules/@smithy/md5-js": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.8.tgz", - "integrity": "sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.11.tgz", + "integrity": "sha512-350X4kGIrty0Snx2OWv7rPM6p6vM7RzryvFs6B/56Cux3w3sChOb3bymo5oidXJlPcP9fIRxGUCk7GqpiSOtng==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6467,13 +6418,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", - "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.11.tgz", + "integrity": "sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6481,18 +6432,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.13.tgz", - "integrity": "sha512-x6vn0PjYmGdNuKh/juUJJewZh7MoQ46jYaJ2mvekF4EesMuFfrl4LaW/k97Zjf8PTCPQmPgMvwewg7eNoH9n5w==", + "version": "4.4.23", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.23.tgz", + "integrity": "sha512-UEFIejZy54T1EJn2aWJ45voB7RP2T+IRzUqocIdM6GFFa5ClZncakYJfcYnoXt3UsQrZZ9ZRauGm77l9UCbBLw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.22.1", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-middleware": "^4.2.8", + "@smithy/core": "^3.23.9", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-middleware": "^4.2.11", "tslib": "^2.6.2" }, "engines": { @@ -6500,19 +6451,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.30", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.30.tgz", - "integrity": "sha512-CBGyFvN0f8hlnqKH/jckRDz78Snrp345+PVk8Ux7pnkUCW97Iinse59lY78hBt04h1GZ6hjBN94BRwZy1xC8Bg==", + "version": "4.4.40", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.40.tgz", + "integrity": "sha512-YhEMakG1Ae57FajERdHNZ4ShOPIY7DsgV+ZoAxo/5BT0KIe+f6DDU2rtIymNNFIj22NJfeeI6LWIifrwM0f+rA==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/service-error-classification": "^4.2.8", - "@smithy/smithy-client": "^4.11.2", - "@smithy/types": "^4.12.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/uuid": "^1.1.0", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/service-error-classification": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, "engines": { @@ -6520,13 +6471,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", - "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.12.tgz", + "integrity": "sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6534,12 +6485,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", - "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.11.tgz", + "integrity": "sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6547,14 +6498,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", - "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.11.tgz", + "integrity": "sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6562,15 +6513,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.9.tgz", - "integrity": "sha512-KX5Wml5mF+luxm1szW4QDz32e3NObgJ4Fyw+irhph4I/2geXwUy4jkIMUs5ZPGflRBeR6BUkC2wqIab4Llgm3w==", + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.14.tgz", + "integrity": "sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/querystring-builder": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/abort-controller": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6578,12 +6529,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", - "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.11.tgz", + "integrity": "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6591,12 +6542,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", - "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.11.tgz", + "integrity": "sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6604,13 +6555,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", - "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.11.tgz", + "integrity": "sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", - "@smithy/util-uri-escape": "^4.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6618,12 +6569,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", - "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.11.tgz", + "integrity": "sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6631,24 +6582,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", - "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.11.tgz", + "integrity": "sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0" + "@smithy/types": "^4.13.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", - "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.6.tgz", + "integrity": "sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6656,18 +6607,18 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", - "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.11.tgz", + "integrity": "sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-uri-escape": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6675,17 +6626,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.2.tgz", - "integrity": "sha512-SCkGmFak/xC1n7hKRsUr6wOnBTJ3L22Qd4e8H1fQIuKTAjntwgU8lrdMe7uHdiT2mJAOWA/60qaW9tiMu69n1A==", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.3.tgz", + "integrity": "sha512-7k4UxjSpHmPN2AxVhvIazRSzFQjWnud3sOsXcFStzagww17j1cFQYqTSiQ8xuYK3vKLR1Ni8FzuT3VlKr3xCNw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.22.1", - "@smithy/middleware-endpoint": "^4.4.13", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-stream": "^4.5.11", + "@smithy/core": "^3.23.9", + "@smithy/middleware-endpoint": "^4.4.23", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" }, "engines": { @@ -6693,9 +6644,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", - "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.0.tgz", + "integrity": "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6705,13 +6656,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", - "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.11.tgz", + "integrity": "sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/querystring-parser": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6719,13 +6670,13 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", - "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6733,9 +6684,9 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", - "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6745,9 +6696,9 @@ } }, "node_modules/@smithy/util-body-length-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", - "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6757,12 +6708,12 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", - "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", + "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6770,9 +6721,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", - "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6782,14 +6733,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.29", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.29.tgz", - "integrity": "sha512-nIGy3DNRmOjaYaaKcQDzmWsro9uxlaqUOhZDHQed9MW/GmkBZPtnU70Pu1+GT9IBmUXwRdDuiyaeiy9Xtpn3+Q==", + "version": "4.3.39", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.39.tgz", + "integrity": "sha512-ui7/Ho/+VHqS7Km2wBw4/Ab4RktoiSshgcgpJzC4keFPs6tLJS4IQwbeahxQS3E/w98uq6E1mirCH/id9xIXeQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.8", - "@smithy/smithy-client": "^4.11.2", - "@smithy/types": "^4.12.0", + "@smithy/property-provider": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6797,17 +6748,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.32", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.32.tgz", - "integrity": "sha512-7dtFff6pu5fsjqrVve0YMhrnzJtccCWDacNKOkiZjJ++fmjGExmmSu341x+WU6Oc1IccL7lDuaUj7SfrHpWc5Q==", + "version": "4.2.42", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.42.tgz", + "integrity": "sha512-QDA84CWNe8Akpj15ofLO+1N3Rfg8qa2K5uX0y6HnOp4AnRYRgWrKx/xzbYNbVF9ZsyJUYOfcoaN3y93wA/QJ2A==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.6", - "@smithy/credential-provider-imds": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/smithy-client": "^4.11.2", - "@smithy/types": "^4.12.0", + "@smithy/config-resolver": "^4.4.10", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6815,13 +6766,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", - "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.2.tgz", + "integrity": "sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6829,9 +6780,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", - "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6841,12 +6792,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", - "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.11.tgz", + "integrity": "sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6854,13 +6805,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", - "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.11.tgz", + "integrity": "sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/service-error-classification": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6868,18 +6819,18 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.11", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.11.tgz", - "integrity": "sha512-lKmZ0S/3Qj2OF5H1+VzvDLb6kRxGzZHq6f3rAsoSu5cTLGsn3v3VQBA8czkNNXlLjoFEtVu3OQT2jEeOtOE2CA==", + "version": "4.5.17", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.17.tgz", + "integrity": "sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/node-http-handler": "^4.4.9", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6887,9 +6838,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", - "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -6899,12 +6850,12 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", - "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -6912,13 +6863,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.8.tgz", - "integrity": "sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.11.tgz", + "integrity": "sha512-x7Rh2azQPs3XxbvCzcttRErKKvLnbZfqRf/gOjw2pb+ZscX88e5UkRPCB67bVnsFHxayvMvmePfKTqsRb+is1A==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/abort-controller": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -6926,9 +6877,9 @@ } }, "node_modules/@smithy/uuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", - "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -8508,28 +8459,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws-sdk": { - "version": "2.1693.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1693.0.tgz", - "integrity": "sha512-cJmb8xEnVLT+R6fBS5sn/EFJiX7tUnDaPtOPZ1vFbOJtd0fnZn/Ky2XGgsvvoeliWeH7mL3TWSX5zXXGSQV6gQ==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/axios": { "version": "1.13.5", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", @@ -8738,9 +8667,9 @@ "license": "MIT" }, "node_modules/bowser": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", - "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "license": "MIT" }, "node_modules/brace-expansion": { @@ -8856,17 +8785,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -11978,15 +11896,6 @@ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "license": "MIT", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -12224,10 +12133,22 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-builder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz", + "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/fast-xml-parser": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", - "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", + "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", "funding": [ { "type": "github", @@ -12236,7 +12157,8 @@ ], "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "fast-xml-builder": "^1.0.0", + "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" @@ -12738,6 +12660,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -13684,22 +13607,6 @@ "node": ">= 10" } }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -13918,6 +13825,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.4", @@ -14095,6 +14003,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -14703,15 +14612,6 @@ "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -18030,15 +17930,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -18967,6 +18858,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -19975,9 +19867,9 @@ } }, "node_modules/strnum": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", - "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", + "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", "funding": [ { "type": "github", @@ -20058,9 +19950,9 @@ } }, "node_modules/tar": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz", - "integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==", + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", + "integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -20844,16 +20736,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "license": "MIT", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, "node_modules/url-join": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", @@ -20863,12 +20745,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", - "license": "MIT" - }, "node_modules/utf8-byte-length": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", @@ -20881,19 +20757,6 @@ "integrity": "sha512-S/QSLezp3qvG4ld5PUfXiH7mCFxLKjSVZRFkB3DOjgwHuJPFDkInAXc/anf7BAbHt/D38ozDzL+QMZ6/7gsI6w==", "license": "MIT" }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -20909,15 +20772,6 @@ "node": ">= 0.4.0" } }, - "node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/validate-iri": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/validate-iri/-/validate-iri-1.0.1.tgz", diff --git a/package.json b/package.json index 5eb8bd52d..876f1d41b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocean-node", - "version": "1.0.3", + "version": "1.0.7", "description": "Ocean Node is used to run all core services in the Ocean stack", "author": "Ocean Protocol Foundation", "license": "Apache-2.0", @@ -48,7 +48,7 @@ "postinstall": "node scripts/fix-libp2p-http-utils.js" }, "dependencies": { - "@aws-sdk/client-s3": "^3.554.0", + "@aws-sdk/client-s3": "^3.1002.0", "@chainsafe/libp2p-noise": "^17.0.0", "@chainsafe/libp2p-yamux": "^8.0.1", "@elastic/elasticsearch": "^8.14.0", @@ -74,7 +74,6 @@ "@multiformats/multiaddr": "^12.2.3", "@oceanprotocol/contracts": "^2.6.0", "@oceanprotocol/ddo-js": "^0.2.0", - "aws-sdk": "^2.1693.0", "axios": "^1.13.5", "base58-js": "^2.0.0", "cors": "^2.8.5", @@ -97,7 +96,7 @@ "node-cron": "^3.0.3", "sqlite3": "^5.1.7", "stream-concat": "^1.0.0", - "tar": "^7.5.8", + "tar": "^7.5.10", "uint8arrays": "^4.0.6", "url-join": "^5.0.0", "winston": "^3.11.0", diff --git a/src/@types/fileObject.ts b/src/@types/fileObject.ts index 15e3485cf..4bfebfe2a 100644 --- a/src/@types/fileObject.ts +++ b/src/@types/fileObject.ts @@ -25,26 +25,36 @@ export interface UrlFileObject extends BaseFileObject { export interface IpfsFileObject extends BaseFileObject { hash: string } + +export interface ArweaveFileObject extends BaseFileObject { + transactionId: string +} + export interface S3Object { endpoint: string - region: string + region?: string objectKey: string bucket: string accessKeyId: string secretAccessKey: string + /** If true, use path-style addressing (e.g. endpoint/bucket/key). Required for some S3-compatible services (e.g. MinIO). Default false (virtual-host style, e.g. bucket.endpoint/key). */ + forcePathStyle?: boolean } export interface S3FileObject extends BaseFileObject { s3Access: S3Object } -export interface ArweaveFileObject extends BaseFileObject { - transactionId: string -} +export type StorageObject = + | UrlFileObject + | IpfsFileObject + | ArweaveFileObject + | S3FileObject export interface StorageReadable { stream: Readable httpStatus?: number - headers?: [any] + headers?: Record | undefined + error?: any } export enum FileObjectType { @@ -71,7 +81,7 @@ export interface FileInfoResponse { } export interface FileInfoHttpRequest { - type?: 'ipfs' | 'url' | 'arweave' + type?: FileObjectType did?: string hash?: string url?: string diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts old mode 100644 new mode 100755 index 0deb6d896..c43f19658 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -341,50 +341,9 @@ export class C2DEngineDocker extends C2DEngine { } private async claimPayments(): Promise { - const envs: string[] = [] - for (const env of this.envs) { - envs.push(env.id) - } - // get all jobs that are in the settle status - const jobs = await this.db.getJobs( - envs, - undefined, - undefined, - C2DStatusNumber.JobSettle - ) - - if (jobs.length === 0) { - return - } - - const providerAddress = this.getKeyManager().getEthAddress() - const chains: Set = new Set() - // get all unique chains - for (const job of jobs) { - if (job.payment && job.payment.token) { - chains.add(job.payment.chainId) - } - } - - // Get all locks for all chains - const locks: any[] = [] - for (const chain of chains) { - try { - const contractLocks = await this.escrow.getLocks( - chain, - ZeroAddress, - ZeroAddress, - providerAddress - ) - if (contractLocks) { - locks.push(...contractLocks) - } - } catch (e) { - CORE_LOGGER.error(`Failed to get locks for chain ${chain}: ${e.message}`) - } - } - const currentTimestamp = BigInt(Math.floor(Date.now() / 1000)) + const envs: string[] = [] + const envsChains: string[] = [] // Group jobs by operation type and chain for batch processing const jobsToClaim: Array<{ @@ -395,224 +354,318 @@ export class C2DEngineDocker extends C2DEngine { const jobsToCancel: DBComputeJob[] = [] const jobsWithoutLock: DBComputeJob[] = [] - // Process each job to determine what operation is needed - for (const job of jobs) { - // Calculate algo duration - const algoDuration = - parseFloat(job.algoStopTimestamp) - parseFloat(job.algoStartTimestamp) - job.algoDuration = algoDuration - - // Free jobs or jobs without payment info - mark as finished - if (job.isFree || !job.payment) { - jobsWithoutLock.push(job) - continue + for (const env of this.envs) { + envs.push(env.id) + for (const chain in env.fees) { + if (!envsChains.includes(chain)) envsChains.push(chain) } + } - // Find matching lock - const lock = locks.find( - (lock) => BigInt(lock.jobId.toString()) === BigInt(create256Hash(job.jobId)) - ) - - if (!lock) { - // No lock found, mark as finished - jobsWithoutLock.push(job) - continue + // get all jobs that are in the settle status + const jobs = await this.db.getJobs( + envs, + undefined, + undefined, + C2DStatusNumber.JobSettle + ) + CORE_LOGGER.info(`ClaimPayments: Got ${jobs.length} jobs to check`) + if (jobs.length > 0) { + const providerAddress = this.getKeyManager().getEthAddress() + const chains: Set = new Set() + // get all unique chains + for (const job of jobs) { + if (job.payment && job.payment.token) { + chains.add(job.payment.chainId) + } } - // Check if lock is expired - const lockExpiry = BigInt(lock.expiry.toString()) - if (currentTimestamp > lockExpiry) { - // Lock expired, cancel it - jobsToCancel.push(job) - continue + // Get all locks for all chains + const locks: any[] = [] + for (const chain of chains) { + try { + const contractLocks = await this.escrow.getLocks( + chain, + ZeroAddress, + ZeroAddress, + providerAddress + ) + if (contractLocks) { + locks.push(...contractLocks) + } + } catch (e) { + CORE_LOGGER.error(`Failed to get locks for chain ${chain}: ${e.message}`) + } } - // Get environment to calculate cost - const env = await this.getComputeEnvironment(job.payment.chainId, job.environment) + // Process each job to determine what operation is needed + for (const job of jobs) { + // Calculate algo duration + const algoDuration = + parseFloat(job.algoStopTimestamp) - parseFloat(job.algoStartTimestamp) + job.algoDuration = algoDuration - if (!env) { - CORE_LOGGER.warn( - `Environment not found for job ${job.jobId}, skipping payment claim` + // Free jobs or jobs without payment info - mark as finished + if (job.isFree || !job.payment) { + jobsWithoutLock.push(job) + continue + } + + // Find matching lock + const lock = locks.find( + (lock) => BigInt(lock.jobId.toString()) === BigInt(create256Hash(job.jobId)) ) - continue - } - // Calculate minimum duration - let minDuration = 0 - if (algoDuration < 0) minDuration += algoDuration * -1 - else minDuration += algoDuration - if ( - `minJobDuration` in env && - env.minJobDuration && - minDuration < env.minJobDuration - ) { - minDuration = env.minJobDuration - } + if (!lock) { + // No lock found, mark as finished + jobsWithoutLock.push(job) + continue + } - if (minDuration > 0) { - // We need to claim payment - const fee = env.fees?.[job.payment.chainId]?.find( - (fee) => fee.feeToken === job.payment.token - ) + // Check if lock is expired + const lockExpiry = BigInt(lock.expiry.toString()) + if (currentTimestamp > lockExpiry) { + // Lock expired, cancel it + jobsToCancel.push(job) + continue + } + + // Get environment to calculate cost + const env = await this.getComputeEnvironment(job.payment.chainId, job.environment) - if (!fee) { + if (!env) { CORE_LOGGER.warn( - `Fee not found for job ${job.jobId}, token ${job.payment.token}, skipping` + `Environment not found for job ${job.jobId}, skipping payment claim` ) continue } - const cost = this.getTotalCostOfJob(job.resources, minDuration, fee) - const proof = JSON.stringify(omitDBComputeFieldsFromComputeJob(job)) + // Calculate minimum duration + let minDuration = 0 + if (algoDuration < 0) minDuration += algoDuration * -1 + else minDuration += algoDuration + if ( + `minJobDuration` in env && + env.minJobDuration && + minDuration < env.minJobDuration + ) { + minDuration = env.minJobDuration + } - jobsToClaim.push({ job, cost, proof }) - } else { - // No payment due, cancel the lock - jobsToCancel.push(job) - } - } + if (minDuration > 0) { + // We need to claim payment + const fee = env.fees?.[job.payment.chainId]?.find( + (fee) => fee.feeToken === job.payment.token + ) - // Batch process claims by chain - const claimsByChain = new Map< - number, - Array<{ job: DBComputeJob; cost: number; proof: string }> - >() - for (const claim of jobsToClaim) { - const { chainId } = claim.job.payment! - if (!claimsByChain.has(chainId)) { - claimsByChain.set(chainId, []) + if (!fee) { + CORE_LOGGER.warn( + `Fee not found for job ${job.jobId}, token ${job.payment.token}, skipping` + ) + continue + } + + const cost = this.getTotalCostOfJob(job.resources, minDuration, fee) + const proof = JSON.stringify(omitDBComputeFieldsFromComputeJob(job)) + jobsToClaim.push({ job, cost, proof }) + } else { + // No payment due, cancel the lock + jobsToCancel.push(job) + } } - claimsByChain.get(chainId)!.push(claim) - } - // Process batch claims - for (const [chainId, claims] of claimsByChain.entries()) { - if (claims.length === 0) continue + // Batch process claims by chain + const claimsByChain = new Map< + number, + Array<{ job: DBComputeJob; cost: number; proof: string }> + >() + for (const claim of jobsToClaim) { + const { chainId } = claim.job.payment! + if (!claimsByChain.has(chainId)) { + claimsByChain.set(chainId, []) + } + claimsByChain.get(chainId)!.push(claim) + } - try { - const jobs = claims.map((c) => c.job) - const tokens = jobs.map((j) => j.payment!.token) - const payers = jobs.map((j) => j.owner) - const amounts = claims.map((c) => c.cost) - const proofs = claims.map((c) => c.proof) - - const txId = await this.escrow.claimLocks( - chainId, - jobs.map((j) => j.jobId), - tokens, - payers, - amounts, - proofs - ) + // Process batch claims + for (const [chainId, claims] of claimsByChain.entries()) { + if (claims.length === 0) continue - if (txId) { - // Update all jobs with the transaction ID - for (const claim of claims) { - claim.job.payment!.claimTx = txId - claim.job.payment!.cost = claim.cost - claim.job.status = C2DStatusNumber.JobFinished - claim.job.statusText = C2DStatusText.JobFinished - await this.db.updateJob(claim.job) - } - CORE_LOGGER.info( - `Successfully claimed ${claims.length} locks in batch transaction ${txId}` + try { + const jobs = claims.map((c) => c.job) + const tokens = jobs.map((j) => j.payment!.token) + const payers = jobs.map((j) => j.owner) + const amounts = claims.map((c) => c.cost) + const proofs = claims.map((c) => c.proof) + + const txId = await this.escrow.claimLocks( + chainId, + jobs.map((j) => j.jobId), + tokens, + payers, + amounts, + proofs ) - } - } catch (e) { - CORE_LOGGER.error( - `Failed to batch claim locks for chain ${chainId}: ${e.message}` - ) - // Fallback to individual processing on batch failure - for (const claim of claims) { - try { - const txId = await this.escrow.claimLock( - chainId, - claim.job.jobId, - claim.job.payment!.token, - claim.job.owner, - claim.cost, - claim.proof - ) - if (txId) { + if (txId) { + // Update all jobs with the transaction ID + for (const claim of claims) { claim.job.payment!.claimTx = txId claim.job.payment!.cost = claim.cost claim.job.status = C2DStatusNumber.JobFinished claim.job.statusText = C2DStatusText.JobFinished await this.db.updateJob(claim.job) } - } catch (err) { - CORE_LOGGER.error( - `Failed to claim lock for job ${claim.job.jobId}: ${err.message}` + CORE_LOGGER.info( + `Successfully claimed ${claims.length} locks in batch transaction ${txId}` ) } + } catch (e) { + CORE_LOGGER.error( + `Failed to batch claim locks for chain ${chainId}: ${e.message}` + ) + // Fallback to individual processing on batch failure + for (const claim of claims) { + try { + const txId = await this.escrow.claimLock( + chainId, + claim.job.jobId, + claim.job.payment!.token, + claim.job.owner, + claim.cost, + claim.proof + ) + if (txId) { + claim.job.payment!.claimTx = txId + claim.job.payment!.cost = claim.cost + claim.job.status = C2DStatusNumber.JobFinished + claim.job.statusText = C2DStatusText.JobFinished + await this.db.updateJob(claim.job) + } + } catch (err) { + CORE_LOGGER.error( + `Failed to claim lock for job ${claim.job.jobId}: ${err.message}` + ) + } + } } } - } - // Batch process cancellations by chain - const cancellationsByChain = new Map() - for (const job of jobsToCancel) { - const { chainId } = job.payment! - if (!cancellationsByChain.has(chainId)) { - cancellationsByChain.set(chainId, []) + // Batch process cancellations by chain + const cancellationsByChain = new Map() + for (const job of jobsToCancel) { + const { chainId } = job.payment! + if (!cancellationsByChain.has(chainId)) { + cancellationsByChain.set(chainId, []) + } + cancellationsByChain.get(chainId)!.push(job) } - cancellationsByChain.get(chainId)!.push(job) - } - - // Process batch cancellations - for (const [chainId, jobsToCancelBatch] of cancellationsByChain.entries()) { - if (jobsToCancelBatch.length === 0) continue - try { - const jobIds = jobsToCancelBatch.map((j) => j.jobId) - const tokens = jobsToCancelBatch.map((j) => j.payment!.token) - const payers = jobsToCancelBatch.map((j) => j.owner) + // Process batch cancellations + for (const [chainId, jobsToCancelBatch] of cancellationsByChain.entries()) { + if (jobsToCancelBatch.length === 0) continue - const txId = await this.escrow.cancelExpiredLocks(chainId, jobIds, tokens, payers) - - if (txId) { - // Update all jobs - for (const job of jobsToCancelBatch) { - job.status = C2DStatusNumber.JobFinished - job.statusText = C2DStatusText.JobFinished - await this.db.updateJob(job) - } - CORE_LOGGER.info( - `Successfully cancelled ${jobsToCancelBatch.length} expired locks in batch transaction ${txId}` + try { + const jobIds = jobsToCancelBatch.map((j) => j.jobId) + const tokens = jobsToCancelBatch.map((j) => j.payment!.token) + const payers = jobsToCancelBatch.map((j) => j.owner) + + const txId = await this.escrow.cancelExpiredLocks( + chainId, + jobIds, + tokens, + payers ) - } - } catch (e) { - CORE_LOGGER.error( - `Failed to batch cancel locks for chain ${chainId}: ${e.message}` - ) - // Fallback to individual processing on batch failure - for (const job of jobsToCancelBatch) { - try { - const txId = await this.escrow.cancelExpiredLock( - chainId, - job.jobId, - job.payment!.token, - job.owner - ) - if (txId) { + + if (txId) { + // Update all jobs + for (const job of jobsToCancelBatch) { job.status = C2DStatusNumber.JobFinished job.statusText = C2DStatusText.JobFinished await this.db.updateJob(job) } - } catch (err) { - CORE_LOGGER.error( - `Failed to cancel lock for job ${job.jobId}: ${err.message}` + CORE_LOGGER.info( + `Successfully cancelled ${jobsToCancelBatch.length} expired locks in batch transaction ${txId}` ) } + } catch (e) { + CORE_LOGGER.error( + `Failed to batch cancel locks for chain ${chainId}: ${e.message}` + ) + // Fallback to individual processing on batch failure + for (const job of jobsToCancelBatch) { + try { + const txId = await this.escrow.cancelExpiredLock( + chainId, + job.jobId, + job.payment!.token, + job.owner + ) + if (txId) { + job.status = C2DStatusNumber.JobFinished + job.statusText = C2DStatusText.JobFinished + await this.db.updateJob(job) + } + } catch (err) { + CORE_LOGGER.error( + `Failed to cancel lock for job ${job.jobId}: ${err.message}` + ) + } + } } } + + // Mark jobs without locks as finished + for (const job of jobsWithoutLock) { + job.status = C2DStatusNumber.JobFinished + job.statusText = C2DStatusText.JobFinished + await this.db.updateJob(job) + } } + // force clean of locks without jobs + // ideally, we should never have locks without jobs in db + // (handled above). This means somehow that db got deleted + for (const chain of envsChains) { + this.cleanUpUnknownLocks(chain, currentTimestamp) + } + } - // Mark jobs without locks as finished - for (const job of jobsWithoutLock) { - job.status = C2DStatusNumber.JobFinished - job.statusText = C2DStatusText.JobFinished - await this.db.updateJob(job) + private async cleanUpUnknownLocks(chain: string, currentTimestamp: bigint) { + try { + const nodeAddress = await this.getKeyManager().getEthAddress() + const jobIds: any[] = [] + const tokens: string[] = [] + const payer: string[] = [] + + const balocks = await this.escrow.getLocks( + parseInt(chain), + '0x0000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000', + nodeAddress + ) + for (const lock of balocks) { + const lockExpiry = BigInt(lock.expiry.toString()) + if (currentTimestamp > lockExpiry) { + jobIds.push(lock.jobId.toString()) + tokens.push(lock.token) + payer.push(lock.payer) + } + } + if (jobIds.length > 0) { + try { + const tx = await this.escrow.cancelExpiredLocks( + parseInt(chain), + jobIds, + tokens, + payer, + false + ) + CORE_LOGGER.warn(` Canceled locks on chain ${chain}, tx:${tx}`) + } catch (e) { + // not critical, since we will try to cancel them at next run + CORE_LOGGER.warn(`Tried to cancel some locks, errored: ${e.message}`) + } + } + } catch (e) { + CORE_LOGGER.error(`Error during cleanup of unknown locks: ${e.message}`) } } @@ -1998,6 +2051,7 @@ export class C2DEngineDocker extends C2DEngine { job.dateFinished = String(Date.now() / 1000) await this.db.updateJob(job) + await this.cleanupJob(job) CORE_LOGGER.info(`Job ${job.jobId} terminated - DISK QUOTA EXCEEDED`) return false diff --git a/src/components/core/handler/downloadHandler.ts b/src/components/core/handler/downloadHandler.ts index 650e00cf4..1b97d1792 100644 --- a/src/components/core/handler/downloadHandler.ts +++ b/src/components/core/handler/downloadHandler.ts @@ -531,6 +531,17 @@ export class DownloadHandler extends CommandHandler { CORE_LOGGER.info('Appended userData to file url: ' + decriptedFileObject.url) } + if (decriptedFileObject?.url && task.userData) { + const url = new URL(decriptedFileObject.url) + const userDataObj = + typeof task.userData === 'string' ? JSON.parse(task.userData) : task.userData + for (const [key, value] of Object.entries(userDataObj)) { + url.searchParams.append(key, String(value)) + } + decriptedFileObject.url = url.toString() + CORE_LOGGER.info('Appended userData to file url: ' + decriptedFileObject.url) + } + if (!validateFilesStructure(ddo, service, decryptedFileData)) { CORE_LOGGER.error( 'Unauthorized download operation. Decrypted "nftAddress" and "datatokenAddress" do not match the original DDO' diff --git a/src/components/core/utils/escrow.ts b/src/components/core/utils/escrow.ts index c49230679..49d64a9f0 100644 --- a/src/components/core/utils/escrow.ts +++ b/src/components/core/utils/escrow.ts @@ -359,19 +359,23 @@ export class Escrow { chain: number, jobs: string[], tokens: string[], - payers: string[] + payers: string[], + generateHash: boolean = true ): Promise { const blockchain = this.getBlockchain(chain) const signer = await blockchain.getSigner() + const ourAddress = await signer.getAddress() if (jobs.length !== tokens.length || jobs.length !== payers.length) { throw new Error('Invalid input: all arrays must have the same length') } const jobIds: string[] = [] const payersAddresses: string[] = [] for (let i = 0; i < jobs.length; i++) { - const jobId = create256Hash(jobs[i]) + let jobId + if (generateHash) jobId = create256Hash(jobs[i]) + else jobId = jobs[i] jobIds.push(jobId) - payersAddresses.push(await signer.getAddress()) + payersAddresses.push(ourAddress) } const contract = this.getContract(chain, signer) diff --git a/src/components/database/C2DDatabase.ts b/src/components/database/C2DDatabase.ts old mode 100644 new mode 100755 index 88ff5be2f..dee7c57cf --- a/src/components/database/C2DDatabase.ts +++ b/src/components/database/C2DDatabase.ts @@ -113,6 +113,8 @@ export class C2DDatabase extends AbstractDatabase { * @returns array of eexpired jobs */ async cleanStorageExpiredJobs(): Promise { + const allEnvironments: ComputeEnvironment[] = [] + const currentTimestamp = Date.now() / 1000 const config = await getConfiguration(true) const allEngines = await OceanNode.getInstance( config, @@ -121,15 +123,17 @@ export class C2DDatabase extends AbstractDatabase { let cleaned = 0 for (const engine of allEngines) { - const allEnvironments = await engine.getComputeEnvironments() - for (const computeEnvironment of allEnvironments) { + const engineEnvironments = await engine.getComputeEnvironments() + for (const computeEnvironment of engineEnvironments) { + allEnvironments.push(computeEnvironment) const finishedOrExpired: DBComputeJob[] = await this.provider.getFinishedJobs([ computeEnvironment.id ]) for (const job of finishedOrExpired) { if ( computeEnvironment && - computeEnvironment.storageExpiry > Date.now() / 1000 + computeEnvironment.storageExpiry < + currentTimestamp - parseInt(job.dateFinished) ) { if (await engine.cleanupExpiredStorage(job)) { cleaned++ @@ -137,8 +141,9 @@ export class C2DDatabase extends AbstractDatabase { } } } - cleaned += await this.cleanOrphanJobs(allEnvironments) } + // now let's clean jobs that have an unknown envs (not in our envs) + cleaned += await this.cleanOrphanJobs(allEnvironments) return cleaned } diff --git a/src/components/database/DatabaseFactory.ts b/src/components/database/DatabaseFactory.ts index e83549f9d..4d5d35603 100644 --- a/src/components/database/DatabaseFactory.ts +++ b/src/components/database/DatabaseFactory.ts @@ -20,7 +20,7 @@ import { TypesenseIndexerDatabase, TypesenseLogDatabase, TypesenseOrderDatabase -} from './TypenseDatabase.js' +} from './TypesenseDatabase.js' import { elasticSchemas } from './ElasticSchemas.js' import { IDdoStateQuery } from '../../@types/DDO/IDdoStateQuery.js' import { TypesenseDdoStateQuery } from './TypesenseDdoStateQuery.js' diff --git a/src/components/database/ElasticSearchDatabase.ts b/src/components/database/ElasticSearchDatabase.ts index 26a30ea73..65de69fec 100644 --- a/src/components/database/ElasticSearchDatabase.ts +++ b/src/components/database/ElasticSearchDatabase.ts @@ -11,7 +11,6 @@ import { OceanNodeDBConfig } from '../../@types' import { ElasticsearchSchema } from './ElasticSchemas.js' import { DATABASE_LOGGER } from '../../utils/logging/common.js' import { GENERIC_EMOJIS, LOG_LEVELS_STR } from '../../utils/logging/Logger.js' - import { DDOManager } from '@oceanprotocol/ddo-js' import { validateDDO } from '../../utils/asset.js' @@ -76,7 +75,8 @@ export class ElasticsearchIndexerDatabase extends AbstractIndexerDatabase { try { const result = await this.client.get({ index: this.index, - id: network.toString() + id: network.toString(), + refresh: true }) return result._source } catch (error) { @@ -240,8 +240,7 @@ export class ElasticsearchDdoStateDatabase extends AbstractDdoStateDatabase { index: this.index, body: { query - }, - preference: '_primary_first' + } }) console.log('Query result: ', result) return result.hits.hits.map((hit: any) => { diff --git a/src/components/database/TypenseDatabase.ts b/src/components/database/TypesenseDatabase.ts similarity index 99% rename from src/components/database/TypenseDatabase.ts rename to src/components/database/TypesenseDatabase.ts index 1dc5c3166..2e5645ea0 100644 --- a/src/components/database/TypenseDatabase.ts +++ b/src/components/database/TypesenseDatabase.ts @@ -372,6 +372,7 @@ export class TypesenseDdoDatabase extends AbstractDdoDatabase { getDDOSchema(ddo: Record): TypesenseSchema { // Find the schema based on the DDO version OR use the short DDO schema when state !== 0 let schemaName: string + const ddoInstance = DDOManager.getDDOClass(ddo) const ddoData = ddoInstance.getDDOData() if ('indexedMetadata' in ddoData && ddoData?.indexedMetadata?.nft.state !== 0) { diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index 4b1d0bebf..9d87bd16f 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -462,7 +462,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { if (consumerAddrs && consumerAddrs.length > 0) { const placeholders = consumerAddrs.map(() => '?').join(',') - conditions.push(`owner NOT IN (${placeholders})`) + conditions.push(`owner IN (${placeholders})`) params.push(...consumerAddrs) } diff --git a/src/components/storage/ArweaveStorage.ts b/src/components/storage/ArweaveStorage.ts new file mode 100644 index 000000000..b4333b6b8 --- /dev/null +++ b/src/components/storage/ArweaveStorage.ts @@ -0,0 +1,93 @@ +import { + ArweaveFileObject, + FileInfoResponse, + StorageReadable +} from '../../@types/fileObject.js' +import { OceanNodeConfig } from '../../@types/OceanNode.js' +import { fetchFileMetadata } from '../../utils/asset.js' +import urlJoin from 'url-join' +import axios from 'axios' +import { Storage } from './Storage.js' + +export class ArweaveStorage extends Storage { + public constructor(file: ArweaveFileObject, config: OceanNodeConfig) { + super(file, config) + + const [isValid, message] = this.validate() + if (isValid === false) { + throw new Error(`Error validating the Arweave file: ${message}`) + } + } + + validate(): [boolean, string] { + if (!this.config.arweaveGateway) { + return [false, 'Arweave gateway is not configured!'] + } + const file: ArweaveFileObject = this.getFile() as ArweaveFileObject + if (!file.transactionId) { + return [false, 'Missing transaction ID'] + } + if ( + file.transactionId.startsWith('http://') || + file.transactionId.startsWith('https://') + ) { + return [ + false, + 'Transaction ID looks like an URL. Please specify URL storage instead.' + ] + } + if (this.isFilePath() === true) { + return [false, 'Transaction ID looks like a file path'] + } + return [true, ''] + } + + isFilePath(): boolean { + const regex: RegExp = /^(.+)\/([^/]*)$/ // The transaction ID should not represent a path + const { transactionId } = this.getFile() + + return regex.test(transactionId) + } + + getDownloadUrl(): string { + return urlJoin(this.config.arweaveGateway, this.getFile().transactionId) + } + + override async getReadableStream(): Promise { + const input = this.getDownloadUrl() + const response = await axios({ + method: 'get', + url: input, + responseType: 'stream', + timeout: 30000 + }) + + return { + httpStatus: response.status, + stream: response.data, + headers: response.headers as any + } + } + + async fetchSpecificFileMetadata( + fileObject: ArweaveFileObject, + forceChecksum: boolean + ): Promise { + const url = urlJoin(this.config.arweaveGateway, fileObject.transactionId) + const { contentLength, contentType, contentChecksum } = await fetchFileMetadata( + url, + 'get', + forceChecksum + ) + return { + valid: true, + contentLength, + contentType, + checksum: contentChecksum, + name: '', // Never send the file name for Arweave as it may leak the transaction ID + type: 'arweave', + encryptedBy: fileObject.encryptedBy, + encryptMethod: fileObject.encryptMethod + } + } +} diff --git a/src/components/storage/IpfsStorage.ts b/src/components/storage/IpfsStorage.ts new file mode 100644 index 000000000..5a3070b31 --- /dev/null +++ b/src/components/storage/IpfsStorage.ts @@ -0,0 +1,87 @@ +import { + FileInfoResponse, + IpfsFileObject, + StorageReadable +} from '../../@types/fileObject.js' +import { OceanNodeConfig } from '../../@types/OceanNode.js' +import { fetchFileMetadata } from '../../utils/asset.js' +import urlJoin from 'url-join' +import axios from 'axios' +import { Storage } from './Storage.js' + +export class IpfsStorage extends Storage { + public constructor(file: IpfsFileObject, config: OceanNodeConfig) { + super(file, config) + + const [isValid, message] = this.validate() + if (isValid === false) { + throw new Error(`Error validating the IPFS file: ${message}`) + } + } + + validate(): [boolean, string] { + if (!this.config.ipfsGateway) { + return [false, 'IPFS gateway is not configured!'] + } + const file: IpfsFileObject = this.getFile() as IpfsFileObject + if (!file.hash) { + return [false, 'Missing CID'] + } + if (file.hash.startsWith('http://') || file.hash.startsWith('https://')) { + return [false, 'CID looks like an URL. Please specify URL storage instead.'] + } + if (this.isFilePath() === true) { + return [false, 'CID looks like a file path'] + } + return [true, ''] + } + + isFilePath(): boolean { + const regex: RegExp = /^(.+)\/([^/]*)$/ // The CID should not represent a path + const { hash } = this.getFile() + + return regex.test(hash) + } + + getDownloadUrl(): string { + return urlJoin(this.config.ipfsGateway, urlJoin('/ipfs', this.getFile().hash)) + } + + override async getReadableStream(): Promise { + const input = this.getDownloadUrl() + const response = await axios({ + method: 'get', + url: input, + responseType: 'stream', + timeout: 30000 + }) + + return { + httpStatus: response.status, + stream: response.data, + headers: response.headers as any + } + } + + async fetchSpecificFileMetadata( + fileObject: IpfsFileObject, + forceChecksum: boolean + ): Promise { + const url = urlJoin(this.config.ipfsGateway, urlJoin('/ipfs', fileObject.hash)) + const { contentLength, contentType, contentChecksum } = await fetchFileMetadata( + url, + 'get', + forceChecksum + ) + return { + valid: true, + contentLength, + contentType, + checksum: contentChecksum, + name: '', + type: 'ipfs', + encryptedBy: fileObject.encryptedBy, + encryptMethod: fileObject.encryptMethod + } + } +} diff --git a/src/components/storage/S3Storage.ts b/src/components/storage/S3Storage.ts new file mode 100644 index 000000000..2214d52b9 --- /dev/null +++ b/src/components/storage/S3Storage.ts @@ -0,0 +1,121 @@ +import { + FileInfoResponse, + S3FileObject, + StorageReadable +} from '../../@types/fileObject.js' +import { OceanNodeConfig } from '../../@types/OceanNode.js' +import { GetObjectCommand, HeadObjectCommand, S3Client } from '@aws-sdk/client-s3' +import { Readable } from 'stream' +import { CORE_LOGGER } from '../../utils/logging/common.js' + +import { Storage } from './Storage.js' + +function createS3Client(s3Access: S3FileObject['s3Access']): S3Client { + const endpoint = s3Access.endpoint.startsWith('http') + ? s3Access.endpoint + : `https://${s3Access.endpoint}` + return new S3Client({ + endpoint, + // Region is optional; default to us-east-1 if not provided + region: s3Access.region ?? 'us-east-1', + // Path-style (e.g. endpoint/bucket/key) required for some S3-compatible services (e.g. MinIO); default false for AWS virtual-host style + forcePathStyle: s3Access.forcePathStyle ?? false, + credentials: { + accessKeyId: s3Access.accessKeyId, + secretAccessKey: s3Access.secretAccessKey + } + }) +} + +export class S3Storage extends Storage { + public constructor(file: S3FileObject, config: OceanNodeConfig) { + super(file, config) + const [isValid, message] = this.validate() + if (isValid === false) { + throw new Error(`Error validating the S3 file: ${message}`) + } + } + + validate(): [boolean, string] { + const file: S3FileObject = this.getFile() as S3FileObject + if (!file.s3Access) { + return [false, 'Missing s3Access'] + } + const { bucket, objectKey, endpoint, accessKeyId, secretAccessKey } = file.s3Access + if (!bucket?.trim()) { + return [false, 'Missing bucket'] + } + if (!objectKey?.trim()) { + return [false, 'Missing objectKey'] + } + if (!endpoint?.trim()) { + return [false, 'Missing endpoint'] + } + if (!accessKeyId?.trim()) { + return [false, 'Missing accessKeyId'] + } + if (!secretAccessKey?.trim()) { + return [false, 'Missing secretAccessKey'] + } + return [true, ''] + } + + override async getReadableStream(): Promise { + const { s3Access } = this.getFile() as S3FileObject + const s3Client = createS3Client(s3Access) + + try { + const response = await s3Client.send( + new GetObjectCommand({ + Bucket: s3Access.bucket, + Key: s3Access.objectKey + }) + ) + + if (!response.Body) { + throw new Error('S3 GetObject returned no body') + } + + return { + httpStatus: response.$metadata.httpStatusCode ?? 200, + stream: response.Body as Readable, + headers: response.ContentType + ? { 'Content-Type': response.ContentType } + : undefined + } + } catch (err) { + CORE_LOGGER.error(`Error fetching object from S3: ${err}`) + throw err + } + } + + async fetchSpecificFileMetadata( + fileObject: S3FileObject, + _forceChecksum: boolean + ): Promise { + const { s3Access } = fileObject + const s3Client = createS3Client(s3Access) + + const data = await s3Client.send( + new HeadObjectCommand({ + Bucket: s3Access.bucket, + Key: s3Access.objectKey + }) + ) + + const contentLength = data.ContentLength != null ? String(data.ContentLength) : '0' + const contentType = data.ContentType ?? 'application/octet-stream' + const name = s3Access.objectKey.split('/').pop() ?? s3Access.objectKey + + return { + valid: true, + contentLength, + contentType, + checksum: data.ETag?.replace(/"/g, ''), + name, + type: 's3', + encryptedBy: fileObject.encryptedBy, + encryptMethod: fileObject.encryptMethod + } + } +} diff --git a/src/components/storage/Storage.ts b/src/components/storage/Storage.ts new file mode 100644 index 000000000..baf0b513e --- /dev/null +++ b/src/components/storage/Storage.ts @@ -0,0 +1,91 @@ +import { + FileInfoRequest, + FileInfoResponse, + FileObjectType, + StorageReadable, + StorageObject, + EncryptMethod +} from '../../@types/fileObject.js' +import { OceanNodeConfig } from '../../@types/OceanNode.js' + +import { CORE_LOGGER } from '../../utils/logging/common.js' + +export abstract class Storage { + // eslint-disable-next-line no-use-before-define -- static factory return type references this class + static getStorageClass: (file: any, config: OceanNodeConfig) => Storage + + private file: StorageObject + config: OceanNodeConfig + public constructor(file: StorageObject, config: OceanNodeConfig) { + this.file = file + this.config = config + } + + abstract validate(): [boolean, string] + + abstract fetchSpecificFileMetadata( + fileObject: any, + forceChecksum: boolean + ): Promise + + getFile(): any { + return this.file + } + + abstract getReadableStream(): Promise + + getStorageType(file: any): FileObjectType { + const { type } = file + return type + } + + async getFileInfo( + fileInfoRequest: FileInfoRequest, + forceChecksum: boolean = false + ): Promise { + if (!fileInfoRequest.type) { + throw new Error('Storage type is not provided') + } + + const response: FileInfoResponse[] = [] + + try { + const file = this.getFile() + + if (!file) { + throw new Error('Empty file object') + } else { + const fileInfo = await this.fetchSpecificFileMetadata(file, forceChecksum) + response.push(fileInfo) + } + } catch (error) { + CORE_LOGGER.error(error.message) + throw error + } + return response + } + + isEncrypted(): boolean { + if ( + this.file.encryptedBy && + (this.file.encryptMethod === EncryptMethod.AES || + this.file.encryptMethod === EncryptMethod.ECIES) + ) { + return true + } else { + return false + } + } + + canDecrypt(nodeId: string): boolean { + if ( + this.file.encryptedBy === nodeId && + (this.file.encryptMethod === EncryptMethod.AES || + this.file.encryptMethod === EncryptMethod.ECIES) + ) { + return true + } else { + return false + } + } +} diff --git a/src/components/storage/UrlStorage.ts b/src/components/storage/UrlStorage.ts new file mode 100644 index 000000000..2d0884eb9 --- /dev/null +++ b/src/components/storage/UrlStorage.ts @@ -0,0 +1,104 @@ +import { + FileInfoResponse, + StorageReadable, + UrlFileObject +} from '../../@types/fileObject.js' +import { OceanNodeConfig } from '../../@types/OceanNode.js' +import { fetchFileMetadata } from '../../utils/asset.js' +import axios from 'axios' + +import { Storage } from './Storage.js' + +export class UrlStorage extends Storage { + public constructor(file: UrlFileObject, config: OceanNodeConfig) { + super(file, config) + const [isValid, message] = this.validate() + if (isValid === false) { + throw new Error(`Error validating the URL file: ${message}`) + } + } + + async getReadableStream(): Promise { + const input = this.getDownloadUrl() + const file = this.getFile() + const { headers } = file + const response = await axios({ + method: 'get', + url: input, + headers, + responseType: 'stream', + timeout: 30000 + }) + + return { + httpStatus: response.status, + stream: response.data, + headers: response.headers as any + } + } + + validate(): [boolean, string] { + const file: UrlFileObject = this.getFile() as UrlFileObject + if (!file.url || !file.method) { + return [false, 'URL or method are missing'] + } + if (!['get', 'post'].includes(file.method?.toLowerCase())) { + return [false, 'Invalid method for URL'] + } + if (this.config && this.config.unsafeURLs) { + for (const regex of this.config.unsafeURLs) { + try { + // eslint-disable-next-line security/detect-non-literal-regexp + const pattern = new RegExp(regex) + if (pattern.test(file.url)) { + return [false, 'URL is marked as unsafe'] + } + } catch (e) {} + } + } + if (this.isFilePath() === true) { + return [false, 'URL looks like a file path'] + } + + return [true, ''] + } + + isFilePath(): boolean { + const regex: RegExp = /^(.+)\/([^/]*)$/ // The URL should not represent a path + const { url } = this.getFile() + if (url.startsWith('http://') || url.startsWith('https://')) { + return false + } + return regex.test(url) + } + + getDownloadUrl(): string { + if (this.validate()[0] === true) { + return this.getFile().url + } + return null + } + + async fetchSpecificFileMetadata( + fileObject: UrlFileObject, + forceChecksum: boolean + ): Promise { + const { url, method, headers } = fileObject + const { contentLength, contentType, contentChecksum } = await fetchFileMetadata( + url, + method || 'get', + forceChecksum, + headers + ) + return { + valid: true, + contentLength, + contentType, + checksum: contentChecksum, + name: new URL(url).pathname.split('/').pop() || '', + type: 'url', + encryptedBy: fileObject.encryptedBy, + encryptMethod: fileObject.encryptMethod + } + } +} diff --git a/src/components/storage/getStorageClass.ts b/src/components/storage/getStorageClass.ts new file mode 100644 index 000000000..e81120d31 --- /dev/null +++ b/src/components/storage/getStorageClass.ts @@ -0,0 +1,36 @@ +import { FileObjectType } from '../../@types/fileObject.js' +import { OceanNodeConfig } from '../../@types/OceanNode.js' +import { CORE_LOGGER } from '../../utils/logging/common.js' + +import { ArweaveStorage } from './ArweaveStorage.js' +import { IpfsStorage } from './IpfsStorage.js' +import { S3Storage } from './S3Storage.js' +import { UrlStorage } from './UrlStorage.js' + +export type StorageClass = UrlStorage | IpfsStorage | ArweaveStorage | S3Storage + +export function getStorageClass(file: any, config: OceanNodeConfig): StorageClass { + if (!file) { + throw new Error('Empty file object') + } + try { + const { type } = file + switch ( + type?.toLowerCase() // case insensitive + ) { + case FileObjectType.URL: + return new UrlStorage(file, config) + case FileObjectType.IPFS: + return new IpfsStorage(file, config) + case FileObjectType.ARWEAVE: + return new ArweaveStorage(file, config) + case FileObjectType.S3: + return new S3Storage(file, config) + default: + throw new Error(`Invalid storage type: ${type}`) + } + } catch (err) { + CORE_LOGGER.error(`Error in getStorageClass: ${err.message}`) + throw err + } +} diff --git a/src/components/storage/index.ts b/src/components/storage/index.ts index 6ae11bd87..b35b7bd56 100644 --- a/src/components/storage/index.ts +++ b/src/components/storage/index.ts @@ -1,543 +1,10 @@ -import { - ArweaveFileObject, - FileInfoRequest, - FileInfoResponse, - FileObjectType, - IpfsFileObject, - StorageReadable, - UrlFileObject, - EncryptMethod, - S3FileObject, - S3Object -} from '../../@types/fileObject.js' -import { OceanNodeConfig } from '../../@types/OceanNode.js' -import { fetchFileMetadata } from '../../utils/asset.js' -import axios from 'axios' -import urlJoin from 'url-join' -import AWS from 'aws-sdk' -import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3' -import { CORE_LOGGER } from '../../utils/logging/common.js' -import { Readable } from 'stream' -import { encrypt as encryptData, decrypt as decryptData } from '../../utils/crypt.js' -export abstract class Storage { - private file: UrlFileObject | IpfsFileObject | ArweaveFileObject | S3FileObject - config: OceanNodeConfig - public constructor( - file: UrlFileObject | IpfsFileObject | ArweaveFileObject | S3FileObject, - config: OceanNodeConfig - ) { - this.file = file - this.config = config - } +import { getStorageClass } from './getStorageClass.js' +import { Storage } from './Storage.js' +import { ArweaveStorage } from './ArweaveStorage.js' +import { IpfsStorage } from './IpfsStorage.js' +import { S3Storage } from './S3Storage.js' +import { UrlStorage } from './UrlStorage.js' - abstract validate(): [boolean, string] - abstract getDownloadUrl(): string +Storage.getStorageClass = getStorageClass - abstract fetchSpecificFileMetadata( - fileObject: any, - forceChecksum: boolean - ): Promise - - abstract isFilePath(): boolean - - getFile(): any { - return this.file - } - - // similar to all subclasses - async getReadableStream(): Promise { - const input = this.getDownloadUrl() - CORE_LOGGER.info(`Fetching the file from ${input}`) - const response = await axios({ - method: 'get', - url: input, - responseType: 'stream', - timeout: 30000 - }) - CORE_LOGGER.info(`Successfully fetched the file from ${input}`) - - return { - httpStatus: response.status, - stream: response.data, - headers: response.headers as any - } - } - - static getStorageClass( - file: any, - config: OceanNodeConfig - ): UrlStorage | IpfsStorage | ArweaveStorage { - if (!file) { - throw new Error('Empty file object') - } - try { - const { type } = file - switch ( - type?.toLowerCase() // case insensitive - ) { - case FileObjectType.URL: - return new UrlStorage(file, config) - case FileObjectType.IPFS: - return new IpfsStorage(file, config) - case FileObjectType.ARWEAVE: - return new ArweaveStorage(file, config) - default: - throw new Error(`Invalid storage type: ${type}`) - } - } catch (err) { - console.error('Error in getStorageClass: ', err) - throw err - } - } - - getStorageType(file: any): FileObjectType { - const { type } = file - return type - } - - async getFileInfo( - fileInfoRequest: FileInfoRequest, - forceChecksum: boolean = false - ): Promise { - if (!fileInfoRequest.type) { - throw new Error('Storage type is not provided') - } - - const response: FileInfoResponse[] = [] - - try { - const file = this.getFile() - - if (!file) { - throw new Error('Empty file object') - } else { - const fileInfo = await this.fetchSpecificFileMetadata(file, forceChecksum) - response.push(fileInfo) - } - } catch (error) { - CORE_LOGGER.error(error) - } - return response - } - - async encrypt(encryptionType: EncryptMethod = EncryptMethod.AES) { - const readableStream = await this.getReadableStream() - - // Convert the readable stream to a buffer - const chunks: Buffer[] = [] - for await (const chunk of readableStream.stream) { - chunks.push(chunk) - } - const buffer = Buffer.concat(chunks) - - // Encrypt the buffer using the encrypt function - const encryptedBuffer = await encryptData(new Uint8Array(buffer), encryptionType) - - // Convert the encrypted buffer back into a stream - const encryptedStream = Readable.from(encryptedBuffer) - - return { - ...readableStream, - stream: encryptedStream - } - } - - async decrypt() { - const { keys } = this.config - const nodeId = keys.peerId.toString() - - if (!this.canDecrypt(nodeId)) { - throw new Error('Node is not authorized to decrypt this file') - } - - const { encryptMethod } = this.file - const readableStream = await this.getReadableStream() - - // Convert the readable stream to a buffer - const chunks: Buffer[] = [] - for await (const chunk of readableStream.stream) { - chunks.push(chunk) - } - const buffer = Buffer.concat(chunks) - - // Decrypt the buffer using your existing function - const decryptedBuffer = await decryptData(new Uint8Array(buffer), encryptMethod) - - // Convert the decrypted buffer back into a stream - const decryptedStream = Readable.from(decryptedBuffer) - - return { - ...readableStream, - stream: decryptedStream - } - } - - isEncrypted(): boolean { - if ( - this.file.encryptedBy && - (this.file.encryptMethod === EncryptMethod.AES || - this.file.encryptMethod === EncryptMethod.ECIES) - ) { - return true - } else { - return false - } - } - - canDecrypt(nodeId: string): boolean { - if ( - this.file.encryptedBy === nodeId && - (this.file.encryptMethod === EncryptMethod.AES || - this.file.encryptMethod === EncryptMethod.ECIES) - ) { - return true - } else { - return false - } - } -} - -export class UrlStorage extends Storage { - public constructor(file: UrlFileObject, config: OceanNodeConfig) { - super(file, config) - const [isValid, message] = this.validate() - if (isValid === false) { - throw new Error(`Error validating the URL file: ${message}`) - } - } - - async getReadableStream(): Promise { - const input = this.getDownloadUrl() - const file = this.getFile() - const { headers } = file - const response = await axios({ - method: 'get', - url: input, - headers, - responseType: 'stream', - timeout: 30000 - }) - - return { - httpStatus: response.status, - stream: response.data, - headers: response.headers as any - } - } - - validate(): [boolean, string] { - const file: UrlFileObject = this.getFile() as UrlFileObject - if (!file.url || !file.method) { - return [false, 'URL or method are missing'] - } - if (!['get', 'post'].includes(file.method?.toLowerCase())) { - return [false, 'Invalid method for URL'] - } - if (this.config && this.config.unsafeURLs) { - for (const regex of this.config.unsafeURLs) { - try { - // eslint-disable-next-line security/detect-non-literal-regexp - const pattern = new RegExp(regex) - if (pattern.test(file.url)) { - return [false, 'URL is marked as unsafe'] - } - } catch (e) {} - } - } - if (this.isFilePath() === true) { - return [false, 'URL looks like a file path'] - } - - return [true, ''] - } - - isFilePath(): boolean { - const regex: RegExp = /^(.+)\/([^/]*)$/ // The URL should not represent a path - const { url } = this.getFile() - if (url.startsWith('http://') || url.startsWith('https://')) { - return false - } - return regex.test(url) - } - - getDownloadUrl(): string { - if (this.validate()[0] === true) { - return this.getFile().url - } - return null - } - - async fetchSpecificFileMetadata( - fileObject: UrlFileObject, - forceChecksum: boolean - ): Promise { - const { url, method, headers } = fileObject - const { contentLength, contentType, contentChecksum } = await fetchFileMetadata( - url, - method || 'get', - forceChecksum, - headers - ) - return { - valid: true, - contentLength, - contentType, - checksum: contentChecksum, - name: new URL(url).pathname.split('/').pop() || '', - type: 'url', - encryptedBy: fileObject.encryptedBy, - encryptMethod: fileObject.encryptMethod - } - } -} - -export class ArweaveStorage extends Storage { - public constructor(file: ArweaveFileObject, config: OceanNodeConfig) { - super(file, config) - - const [isValid, message] = this.validate() - if (isValid === false) { - throw new Error(`Error validating the Arweave file: ${message}`) - } - } - - validate(): [boolean, string] { - if (!this.config.arweaveGateway) { - return [false, 'Arweave gateway is not configured!'] - } - const file: ArweaveFileObject = this.getFile() as ArweaveFileObject - if (!file.transactionId) { - return [false, 'Missing transaction ID'] - } - if ( - file.transactionId.startsWith('http://') || - file.transactionId.startsWith('https://') - ) { - return [ - false, - 'Transaction ID looks like an URL. Please specify URL storage instead.' - ] - } - if (this.isFilePath() === true) { - return [false, 'Transaction ID looks like a file path'] - } - return [true, ''] - } - - isFilePath(): boolean { - const regex: RegExp = /^(.+)\/([^/]*)$/ // The transaction ID should not represent a path - const { transactionId } = this.getFile() - - return regex.test(transactionId) - } - - getDownloadUrl(): string { - return urlJoin(this.config.arweaveGateway, this.getFile().transactionId) - } - - async fetchSpecificFileMetadata( - fileObject: ArweaveFileObject, - forceChecksum: boolean - ): Promise { - const url = urlJoin(this.config.arweaveGateway, fileObject.transactionId) - const { contentLength, contentType, contentChecksum } = await fetchFileMetadata( - url, - 'get', - forceChecksum - ) - return { - valid: true, - contentLength, - contentType, - checksum: contentChecksum, - name: '', // Never send the file name for Arweave as it may leak the transaction ID - type: 'arweave', - encryptedBy: fileObject.encryptedBy, - encryptMethod: fileObject.encryptMethod - } - } -} - -export class IpfsStorage extends Storage { - public constructor(file: IpfsFileObject, config: OceanNodeConfig) { - super(file, config) - - const [isValid, message] = this.validate() - if (isValid === false) { - throw new Error(`Error validating the IPFS file: ${message}`) - } - } - - validate(): [boolean, string] { - if (!this.config.ipfsGateway) { - return [false, 'IPFS gateway is not configured!'] - } - const file: IpfsFileObject = this.getFile() as IpfsFileObject - if (!file.hash) { - return [false, 'Missing CID'] - } - if (file.hash.startsWith('http://') || file.hash.startsWith('https://')) { - return [false, 'CID looks like an URL. Please specify URL storage instead.'] - } - if (this.isFilePath() === true) { - return [false, 'CID looks like a file path'] - } - return [true, ''] - } - - isFilePath(): boolean { - const regex: RegExp = /^(.+)\/([^/]*)$/ // The CID should not represent a path - const { hash } = this.getFile() - - return regex.test(hash) - } - - getDownloadUrl(): string { - return urlJoin(this.config.ipfsGateway, urlJoin('/ipfs', this.getFile().hash)) - } - - async fetchSpecificFileMetadata( - fileObject: IpfsFileObject, - forceChecksum: boolean - ): Promise { - const url = urlJoin(this.config.ipfsGateway, urlJoin('/ipfs', fileObject.hash)) - const { contentLength, contentType, contentChecksum } = await fetchFileMetadata( - url, - 'get', - forceChecksum - ) - return { - valid: true, - contentLength, - contentType, - checksum: contentChecksum, - name: '', - type: 'ipfs', - encryptedBy: fileObject.encryptedBy, - encryptMethod: fileObject.encryptMethod - } - } -} - -export class S3Storage extends Storage { - public constructor(file: S3FileObject, config: OceanNodeConfig) { - super(file, config) - const [isValid, message] = this.validate() - if (isValid === false) { - throw new Error(`Error validationg the S3 file: ${message}`) - } - } - - validate(): [boolean, string] { - const file: S3FileObject = this.getFile() as S3FileObject - if (!file.s3Access) { - return [false, 'Missing s3Access'] - } - return [true, ''] - } - - parseDecryptedStream(decryptedStream: Readable): Promise { - return new Promise((resolve, reject) => { - let data = '' - decryptedStream.on('data', (chunk) => { - data += chunk - }) - decryptedStream.on('end', () => { - try { - const parsedData = JSON.parse(data) - resolve(parsedData) - } catch (error) { - reject(error) - } - }) - decryptedStream.on('error', (error) => { - reject(error) - }) - }) - } - - isFilePath(): boolean { - const { endpoint } = this.getFile().s3Access - return endpoint.includes('.') - } - - getDownloadUrl(): string { - const { s3Access } = this.getFile() - return JSON.stringify(s3Access) - } - - async fetchDataContent(): Promise { - const s3Obj = await this.getFile().s3Access - const spacesEndpoint = new AWS.Endpoint(s3Obj.endpoint) - const s3 = new AWS.S3({ - endpoint: spacesEndpoint, - accessKeyId: s3Obj.accessKeyId, - secretAccessKey: s3Obj.secretAccessKey, - region: s3Obj.region - }) - - const params = { - Bucket: s3Obj.bucket, - Key: s3Obj.objectKey - } - try { - const data = await s3.getObject(params).promise() - console.log('Successfully fetched data from S3') - return data - } catch (err) { - console.error('Error fetching object from S3:', err) - } - } - - async fetchDataStream(): Promise { - const s3Obj = await this.getFile().s3Access - const spacesEndpoint = new AWS.Endpoint(s3Obj.endpoint) - const s3Client = new S3Client({ - endpoint: { - hostname: spacesEndpoint.hostname, - protocol: spacesEndpoint.protocol, - path: '/' - }, - region: s3Obj.region, - credentials: { - accessKeyId: s3Obj.accessKeyId, - secretAccessKey: s3Obj.secretAccessKey - } - }) - - const params = { - Bucket: s3Obj.bucket, - Key: s3Obj.objectKey - } - try { - const response = await s3Client.send(new GetObjectCommand(params)) - - const dataStream = response.Body - console.log('Successfully retrieved object from S3') - return dataStream - } catch (err) { - console.error('Error fetching object from S3:', err) - } - } - - async fetchSpecificFileMetadata(): Promise { - const data = await this.fetchDataContent() - const s3Obj = await this.getFile().s3Access - return { - valid: true, - contentLength: data.ContentLength, - contentType: data.ContentType, - name: s3Obj.objectKey, - type: 's3', - encryptedBy: this.getFile().encryptedBy, - encryptMethod: this.getFile().encryptMethod - } - } - - async encryptContent( - encryptionType: EncryptMethod.AES | EncryptMethod.ECIES - ): Promise { - const data = await this.fetchDataContent() - return await encryptData(data, encryptionType) - } -} +export { Storage, UrlStorage, ArweaveStorage, IpfsStorage, S3Storage } diff --git a/src/index.ts b/src/index.ts index 67b9093f9..679821f04 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,17 @@ import { hasValidDBConfiguration } from './utils/database.js' const app: Express = express() +process.on('uncaughtException', (err) => { + OCEAN_NODE_LOGGER.error(`Uncaught exception: ${err.message}`) + process.exit(1) +}) +process.on('unhandledRejection', (err) => { + OCEAN_NODE_LOGGER.error( + `Unhandled rejection: ${err instanceof Error ? err.message : String(err)}` + ) + process.exit(1) +}) + // const port = getRandomInt(6000,6500) express.static.mime.define({ 'image/svg+xml': ['svg'] }) diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index 17eb1a989..e1c8a6561 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -154,16 +154,13 @@ describe('Compute', () => { ) config = await getConfiguration(true) dbconn = await Database.init(config.dbConfig) - oceanNode = await OceanNode.getInstance( - config, - dbconn, - null, - null, - null, - null, - null, - true - ) + + const staleJobs = await dbconn.c2d.getRunningJobs() + for (const job of staleJobs) { + await dbconn.c2d.deleteJob(job.jobId) + } + + oceanNode = OceanNode.getInstance(config, dbconn, null, null, null, null, null, true) indexer = new OceanIndexer( dbconn, config.indexingNetworks, @@ -641,62 +638,43 @@ describe('Compute', () => { assert(!response.stream, 'We should not have a stream') }) - it('should start a compute job with maxed resources', async () => { - // first check escrow auth - - let balance = await paymentTokenContract.balanceOf(await consumerAccount.getAddress()) - let funds = await oceanNode.escrow.getUserAvailableFunds( + it('should fail to start a compute job without escrow funds', async () => { + // ensure clean escrow state: no funds, no auths, no locks + const funds = await oceanNode.escrow.getUserAvailableFunds( DEVELOPMENT_CHAIN_ID, await consumerAccount.getAddress(), paymentToken ) - // make sure we have 0 funds if (BigInt(funds.toString()) > BigInt(0)) { await escrowContract .connect(consumerAccount) .withdraw([initializeResponse.payment.token], [funds]) } - let auth = await oceanNode.escrow.getAuthorizations( + const auth = await oceanNode.escrow.getAuthorizations( DEVELOPMENT_CHAIN_ID, paymentToken, await consumerAccount.getAddress(), firstEnv.consumerAddress ) if (auth.length > 0) { - // remove any auths await escrowContract .connect(consumerAccount) .authorize(initializeResponse.payment.token, firstEnv.consumerAddress, 0, 0, 0) } - let locks = await oceanNode.escrow.getLocks( + const locks = await oceanNode.escrow.getLocks( DEVELOPMENT_CHAIN_ID, paymentToken, await consumerAccount.getAddress(), firstEnv.consumerAddress ) - - if (locks.length > 0) { - // cancel all locks - for (const lock of locks) { - try { - await escrowContract - .connect(consumerAccount) - .cancelExpiredLock( - lock.jobId, - lock.token, - lock.payer, - firstEnv.consumerAddress - ) - } catch (e) {} - } - locks = await oceanNode.escrow.getLocks( - DEVELOPMENT_CHAIN_ID, - paymentToken, - await consumerAccount.getAddress(), - firstEnv.consumerAddress - ) + for (const lock of locks) { + try { + await escrowContract + .connect(consumerAccount) + .cancelExpiredLock(lock.jobId, lock.token, lock.payer, firstEnv.consumerAddress) + } catch (e) {} } - const locksBefore = locks.length + const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( await consumerAccount.getAddress(), @@ -738,15 +716,17 @@ describe('Compute', () => { additionalViewers: [await additionalViewerAccount.getAddress()], maxJobDuration: computeJobDuration, resources: re - // additionalDatasets?: ComputeAsset[] - // output?: ComputeOutput } - // it should fail, because we don't have funds & auths in escrow - let response = await new PaidComputeStartHandler(oceanNode).handle(startComputeTask) + const response = await new PaidComputeStartHandler(oceanNode).handle(startComputeTask) assert(response.status.httpStatus === 400, 'Failed to get 400 response') assert(!response.stream, 'We should not have a stream') - // let's put funds in escrow & create an auth - balance = await paymentTokenContract.balanceOf(await consumerAccount.getAddress()) + }) + + it('should start a compute job with maxed resources', async () => { + // deposit funds and create auth in escrow + const balance = await paymentTokenContract.balanceOf( + await consumerAccount.getAddress() + ) await paymentTokenContract .connect(consumerAccount) .approve(initializeResponse.payment.escrowAddress, balance) @@ -762,20 +742,13 @@ describe('Compute', () => { initializeResponse.payment.minLockSeconds, 10 ) - auth = await oceanNode.escrow.getAuthorizations( + + const auth = await oceanNode.escrow.getAuthorizations( DEVELOPMENT_CHAIN_ID, paymentToken, await consumerAccount.getAddress(), firstEnv.consumerAddress ) - const authBefore = auth[0] - funds = await oceanNode.escrow.getUserAvailableFunds( - DEVELOPMENT_CHAIN_ID, - await consumerAccount.getAddress(), - paymentToken - ) - const fundsBefore = funds - assert(BigInt(funds.toString()) > BigInt(0), 'Should have funds in escrow') assert(auth.length > 0, 'Should have authorization') assert( BigInt(auth[0].maxLockedAmount.toString()) > BigInt(0), @@ -785,19 +758,68 @@ describe('Compute', () => { BigInt(auth[0].maxLockCounts.toString()) > BigInt(0), ' Should have maxLockCounts in auth' ) - const nonce2 = Date.now().toString() - const messageHashBytes2 = createHashForSignature( + const authBefore = auth[0] + + const fundsBefore = await oceanNode.escrow.getUserAvailableFunds( + DEVELOPMENT_CHAIN_ID, + await consumerAccount.getAddress(), + paymentToken + ) + assert(BigInt(fundsBefore.toString()) > BigInt(0), 'Should have funds in escrow') + + const locksBefore = ( + await oceanNode.escrow.getLocks( + DEVELOPMENT_CHAIN_ID, + paymentToken, + await consumerAccount.getAddress(), + firstEnv.consumerAddress + ) + ).length + + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( await consumerAccount.getAddress(), - nonce2, + nonce, PROTOCOL_COMMANDS.COMPUTE_START ) - const signature2 = await safeSign(consumerAccount, messageHashBytes2) - response = await new PaidComputeStartHandler(oceanNode).handle({ - ...startComputeTask, - nonce: nonce2, - signature: signature2 - }) - console.log(response) + const signature = await safeSign(consumerAccount, messageHashBytes) + const re = [] + for (const res of firstEnv.resources) { + re.push({ id: res.id, amount: res.total }) + } + const startComputeTask: PaidComputeStartCommand = { + command: PROTOCOL_COMMANDS.COMPUTE_START, + consumerAddress: await consumerAccount.getAddress(), + signature, + nonce, + environment: firstEnv.id, + datasets: [ + { + documentId: publishedComputeDataset.ddo.id, + serviceId: publishedComputeDataset.ddo.services[0].id, + transferTxId: datasetOrderTxId + } + ], + algorithm: { + documentId: publishedAlgoDataset.ddo.id, + serviceId: publishedAlgoDataset.ddo.services[0].id, + transferTxId: algoOrderTxId, + meta: publishedAlgoDataset.ddo.metadata.algorithm + }, + output: {}, + payment: { + chainId: DEVELOPMENT_CHAIN_ID, + token: paymentToken + }, + metadata: { + key: 'value' + }, + additionalViewers: [await additionalViewerAccount.getAddress()], + maxJobDuration: computeJobDuration, + resources: re + } + const response = await new PaidComputeStartHandler(oceanNode).handle(startComputeTask) + console.log({ response }) assert(response, 'Failed to get response') assert(response.status.httpStatus === 200, 'Failed to get 200 response') assert(response.stream, 'Failed to get stream') @@ -807,29 +829,35 @@ describe('Compute', () => { // eslint-disable-next-line prefer-destructuring jobId = jobs[0].jobId console.log('**** Started compute job with id: ', jobId) - // check escrow - funds = await oceanNode.escrow.getUserAvailableFunds( + + // check escrow state changed after job start + const fundsAfter = await oceanNode.escrow.getUserAvailableFunds( DEVELOPMENT_CHAIN_ID, await consumerAccount.getAddress(), paymentToken ) - assert(fundsBefore > funds, 'We should have less funds') - locks = await oceanNode.escrow.getLocks( + assert(fundsBefore > fundsAfter, 'We should have less funds') + + const locksAfter = await oceanNode.escrow.getLocks( DEVELOPMENT_CHAIN_ID, paymentToken, await consumerAccount.getAddress(), firstEnv.consumerAddress ) - assert(locks.length > locksBefore, 'We should have locks') - auth = await oceanNode.escrow.getAuthorizations( + assert(locksAfter.length > locksBefore, 'We should have locks') + + const authAfter = await oceanNode.escrow.getAuthorizations( DEVELOPMENT_CHAIN_ID, paymentToken, await consumerAccount.getAddress(), firstEnv.consumerAddress ) - assert(auth[0].currentLocks > authBefore.currentLocks, 'We should have running jobs') assert( - auth[0].currentLockedAmount > authBefore.currentLockedAmount, + authAfter[0].currentLocks > authBefore.currentLocks, + 'We should have running jobs' + ) + assert( + authAfter[0].currentLockedAmount > authBefore.currentLockedAmount, 'We should have higher currentLockedAmount' ) }) @@ -2124,7 +2152,7 @@ describe('Compute Access Restrictions', () => { ) config = await getConfiguration(true) dbconn = await Database.init(config.dbConfig) - oceanNode = await OceanNode.getInstance( + oceanNode = OceanNode.getInstance( config, dbconn, null, @@ -2311,7 +2339,7 @@ describe('Compute Access Restrictions', () => { ) config = await getConfiguration(true) dbconn = await Database.init(config.dbConfig) - oceanNode = await OceanNode.getInstance( + oceanNode = OceanNode.getInstance( config, dbconn, null, @@ -2443,7 +2471,7 @@ describe('Compute Access Restrictions', () => { ) config = await getConfiguration(true) dbconn = await Database.init(config.dbConfig) - oceanNode = await OceanNode.getInstance( + oceanNode = OceanNode.getInstance( config, dbconn, null, @@ -2573,7 +2601,7 @@ describe('Compute Access Restrictions', () => { const now = Math.floor(Date.now() / 1000) const expiry = 3500 - const providerAddress = await (await oceanNode.getKeyManager()).getEthAddress() + const providerAddress = oceanNode.getKeyManager().getEthAddress() // Clean up existing locks and authorizations first const locks = await oceanNode.escrow.getLocks( diff --git a/src/test/integration/getJobs.test.ts b/src/test/integration/getJobs.test.ts index 0f405a66b..58aba7857 100644 --- a/src/test/integration/getJobs.test.ts +++ b/src/test/integration/getJobs.test.ts @@ -131,7 +131,7 @@ describe('GetJobsHandler integration', () => { expect(filtered.every((j) => Number(j.dateFinished) >= Number(fromTs))).to.equal(true) }) - it('should exclude jobs owned by specified consumer addresses', async function () { + it('should include jobs owned by specified consumer addresses', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) const resp = await handler.handle({ @@ -144,7 +144,7 @@ describe('GetJobsHandler integration', () => { const jobs = (await streamToObject(resp.stream as Readable)) as any[] const owners = jobs.filter((j) => j.environment === uniqueEnv).map((j) => j.owner) - expect(owners.includes(ownerA)).to.equal(false) - expect(owners.includes(ownerB)).to.equal(true) + expect(owners.includes(ownerA)).to.equal(true) + expect(owners.includes(ownerB)).to.equal(false) }) }) diff --git a/src/test/integration/indexer.test.ts b/src/test/integration/indexer.test.ts index 059895cd1..2ef8df0aa 100644 --- a/src/test/integration/indexer.test.ts +++ b/src/test/integration/indexer.test.ts @@ -100,7 +100,21 @@ describe('Indexer stores a new metadata events and orders.', () => { const config = await getConfiguration(true) database = await Database.init(config.dbConfig) - oceanNode = OceanNode.getInstance(config, database) + + const oldIndexer = OceanNode.getInstance(config, database).getIndexer() + if (oldIndexer) { + await oldIndexer.stopAllChainIndexers() + } + oceanNode = OceanNode.getInstance( + config, + database, + null, + null, + null, + null, + null, + true + ) indexer = new OceanIndexer( database, mockSupportedNetworks, @@ -342,11 +356,12 @@ describe('Indexer stores a new metadata events and orders.', () => { }) it('should get the updated state', async function () { + this.timeout(DEFAULT_TEST_TIMEOUT * 3) const result = await nftContract.getMetaData() const { ddo, wasTimeout } = await waitToIndex( assetDID, EVENTS.METADATA_UPDATED, - DEFAULT_TEST_TIMEOUT, + DEFAULT_TEST_TIMEOUT * 3, true ) const retrievedDDO: any = ddo @@ -456,16 +471,17 @@ describe('Indexer stores a new metadata events and orders.', () => { }) it('should get number of orders', async function () { - this.timeout(DEFAULT_TEST_TIMEOUT * 2) + this.timeout(DEFAULT_TEST_TIMEOUT * 4) const { ddo, wasTimeout } = await waitToIndex( assetDID, EVENTS.ORDER_STARTED, - DEFAULT_TEST_TIMEOUT * 2, + DEFAULT_TEST_TIMEOUT * 4, true ) if (ddo) { const retrievedDDO = ddo console.log('indexer retrieved ddo: ', JSON.stringify(retrievedDDO)) + console.log('stats: ', JSON.stringify(retrievedDDO.indexedMetadata.stats)) for (const stat of retrievedDDO.indexedMetadata.stats) { if (stat.datatokenAddress === datatokenAddress) { expect(stat.orders).to.equal(1) @@ -597,13 +613,14 @@ describe('Indexer stores a new metadata events and orders.', () => { }) it('Deprecated asset should have a short version of ddo', async function () { + this.timeout(DEFAULT_TEST_TIMEOUT * 3) const result = await nftContract.getMetaData() expect(parseInt(result[2].toString())).to.equal(2) const { ddo, wasTimeout } = await waitToIndex( assetDID, EVENTS.METADATA_STATE, - DEFAULT_TEST_TIMEOUT, + DEFAULT_TEST_TIMEOUT * 3, true ) const resolvedDDO: any = ddo diff --git a/src/test/integration/logs.test.ts b/src/test/integration/logs.test.ts index 4d528ef73..102c2b0b2 100644 --- a/src/test/integration/logs.test.ts +++ b/src/test/integration/logs.test.ts @@ -88,7 +88,7 @@ describe('LogDatabase CRUD', () => { logs = logs.filter((log) => log.message === newLogEntry.message) expect(logs.length).to.equal(1) - expect(Number(logs[0].id)).to.greaterThan(Number(logId)) + expect(logs[0].id).to.not.equal(logId) expect(logs[0].level).to.equal(newLogEntry.level) expect(logs[0].message).to.equal(newLogEntry.message) expect(logs[0].moduleName).to.equal('HTTP') @@ -119,7 +119,7 @@ describe('LogDatabase CRUD', () => { if (logs.length > 0) { expect(logs.length).to.equal(1) - expect(Number(logs[0].id)).to.greaterThan(Number(logId)) + expect(logs[0].id).to.not.equal(logId) expect(logs[0].level).to.equal(newLogEntry.level) expect(logs[0].message).to.equal(newLogEntry.message) expect(logs[0].moduleName).to.equal('HTTP') @@ -154,7 +154,7 @@ describe('LogDatabase CRUD', () => { if (logs.length > 0) { expect(logs.length).to.equal(1) - expect(Number(logs[0].id)).to.greaterThan(Number(logId)) + expect(logs[0].id).to.not.equal(logId) expect(logs[0].level).to.equal(newLogEntry.level) assert(logs[0].message) expect(logs[0].moduleName).to.equal('HTTP') diff --git a/src/test/integration/s3Storage.test.ts b/src/test/integration/s3Storage.test.ts new file mode 100644 index 000000000..93ea4b6de --- /dev/null +++ b/src/test/integration/s3Storage.test.ts @@ -0,0 +1,210 @@ +/** + * S3 Storage integration tests against Ceph RGW (or any S3-compatible endpoint). + * + * Prerequisites: + * - Ceph RGW (or compatible) running, e.g. at http://172.15.0.7:7480 + * - Environment variables set: + * - S3_TEST_ENDPOINT (optional, default: http://172.15.0.7:7480) + * - S3_TEST_ACCESS_KEY_ID + * - S3_TEST_SECRET_ACCESS_KEY + * - S3_TEST_BUCKET + * + * The test creates the bucket if missing, puts a temporary object, then removes the object in after(). + * If credentials are missing or setup fails (endpoint unreachable, auth failure), the suite is skipped. + */ + +import { expect } from 'chai' +import { Readable } from 'stream' +import { Storage, S3Storage } from '../../components/storage/index.js' +import { getConfiguration } from '../../utils/index.js' +import { + CreateBucketCommand, + DeleteObjectCommand, + PutObjectCommand, + S3Client +} from '@aws-sdk/client-s3' +import { FileInfoRequest, FileObjectType } from '../../@types/fileObject.js' +import { DEFAULT_TEST_TIMEOUT } from '../utils/utils.js' + +const S3_TEST_ENDPOINT = 'http://172.15.0.7:7480' +const S3_TEST_ACCESS_KEY_ID = 'ocean123' +const S3_TEST_SECRET_ACCESS_KEY = 'ocean123secret' +const S3_TEST_BUCKET = 'testbucket' +const TEST_OBJECT_KEY = 'integration-test/hello.txt' +const TEST_BODY = 'Hello S3 from integration test' + +function canRunS3Tests(): boolean { + return Boolean(S3_TEST_ACCESS_KEY_ID && S3_TEST_SECRET_ACCESS_KEY && S3_TEST_BUCKET) +} + +function createTestS3Client(): S3Client { + return new S3Client({ + endpoint: S3_TEST_ENDPOINT, + region: 'us-east-1', + forcePathStyle: true, + credentials: { + accessKeyId: S3_TEST_ACCESS_KEY_ID!, + secretAccessKey: S3_TEST_SECRET_ACCESS_KEY! + } + }) +} + +describe('S3 Storage integration (Ceph RGW)', function () { + this.timeout(DEFAULT_TEST_TIMEOUT) + + let config: Awaited> + let s3Client: S3Client + let objectCreated = false + + before(async function () { + if (!canRunS3Tests()) { + this.skip() + return + } + config = await getConfiguration() + s3Client = createTestS3Client() + try { + await s3Client.send(new CreateBucketCommand({ Bucket: S3_TEST_BUCKET! })) + } catch (err: any) { + const alreadyExists = + err.name === 'BucketAlreadyExists' || + err.Code === 'BucketAlreadyExists' || + err.Code === 'BucketAlreadyOwnedByYou' || + err.$metadata?.httpStatusCode === 409 + if (!alreadyExists) { + this.skip() + return + } + } + try { + await s3Client.send( + new PutObjectCommand({ + Bucket: S3_TEST_BUCKET, + Key: TEST_OBJECT_KEY, + Body: TEST_BODY, + ContentType: 'text/plain' + }) + ) + objectCreated = true + } catch (err: any) { + this.skip() + } + }) + + after(async function () { + if (!objectCreated || !s3Client) return + try { + await s3Client.send( + new DeleteObjectCommand({ + Bucket: S3_TEST_BUCKET, + Key: TEST_OBJECT_KEY + }) + ) + } catch { + // ignore cleanup errors + } + }) + + it('returns S3Storage from getStorageClass for type s3', function () { + if (!canRunS3Tests()) this.skip() + const file = { + type: 's3', + s3Access: { + endpoint: S3_TEST_ENDPOINT, + bucket: S3_TEST_BUCKET!, + objectKey: TEST_OBJECT_KEY, + accessKeyId: S3_TEST_ACCESS_KEY_ID!, + secretAccessKey: S3_TEST_SECRET_ACCESS_KEY!, + forcePathStyle: true + } + } + const storage = Storage.getStorageClass(file, config) + expect(storage).to.be.instanceOf(S3Storage) + }) + + it('validates S3 file object', function () { + if (!canRunS3Tests()) this.skip() + const file = { + type: 's3', + s3Access: { + endpoint: S3_TEST_ENDPOINT, + bucket: S3_TEST_BUCKET!, + objectKey: TEST_OBJECT_KEY, + accessKeyId: S3_TEST_ACCESS_KEY_ID!, + secretAccessKey: S3_TEST_SECRET_ACCESS_KEY!, + forcePathStyle: true + } + } + const storage = Storage.getStorageClass(file, config) as S3Storage + expect(storage.validate()).to.eql([true, '']) + }) + + it('gets readable stream and reads body', async function () { + if (!canRunS3Tests()) this.skip() + const file = { + type: 's3', + s3Access: { + endpoint: S3_TEST_ENDPOINT, + bucket: S3_TEST_BUCKET!, + objectKey: TEST_OBJECT_KEY, + accessKeyId: S3_TEST_ACCESS_KEY_ID!, + secretAccessKey: S3_TEST_SECRET_ACCESS_KEY!, + forcePathStyle: true + } + } + const storage = Storage.getStorageClass(file, config) as S3Storage + const result = await storage.getReadableStream() + expect(result.httpStatus).to.equal(200) + expect(result.stream).to.be.instanceOf(Readable) + const chunks: Buffer[] = [] + for await (const chunk of result.stream as Readable) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)) + } + const body = Buffer.concat(chunks).toString('utf8') + expect(body).to.equal(TEST_BODY) + }) + + it('fetches file metadata via fetchSpecificFileMetadata', async function () { + if (!canRunS3Tests()) this.skip() + const file = { + type: 's3', + s3Access: { + endpoint: S3_TEST_ENDPOINT, + bucket: S3_TEST_BUCKET!, + objectKey: TEST_OBJECT_KEY, + accessKeyId: S3_TEST_ACCESS_KEY_ID!, + secretAccessKey: S3_TEST_SECRET_ACCESS_KEY!, + forcePathStyle: true + } + } + const storage = Storage.getStorageClass(file, config) as S3Storage + const meta = await storage.fetchSpecificFileMetadata(file, false) + expect(meta.valid).to.equal(true) + expect(meta.type).to.equal('s3') + expect(meta.contentLength).to.equal(String(TEST_BODY.length)) + expect(meta.contentType).to.equal('text/plain') + expect(meta.name).to.equal('hello.txt') + }) + + it('getFileInfo returns file info for S3', async function () { + if (!canRunS3Tests()) this.skip() + const file = { + type: 's3', + s3Access: { + endpoint: S3_TEST_ENDPOINT, + bucket: S3_TEST_BUCKET!, + objectKey: TEST_OBJECT_KEY, + accessKeyId: S3_TEST_ACCESS_KEY_ID!, + secretAccessKey: S3_TEST_SECRET_ACCESS_KEY!, + forcePathStyle: true + } + } + const storage = Storage.getStorageClass(file, config) as S3Storage + const fileInfoRequest: FileInfoRequest = { type: FileObjectType.S3 } + const fileInfo = await storage.getFileInfo(fileInfoRequest) + expect(fileInfo).to.have.lengthOf(1) + expect(fileInfo[0].valid).to.equal(true) + expect(fileInfo[0].contentLength).to.equal(String(TEST_BODY.length)) + expect(fileInfo[0].contentType).to.equal('text/plain') + }) +}) diff --git a/src/test/unit/compute.test.ts b/src/test/unit/compute.test.ts index c6d10f6e6..3f000f294 100644 --- a/src/test/unit/compute.test.ts +++ b/src/test/unit/compute.test.ts @@ -30,7 +30,6 @@ import { import { OceanNodeConfig } from '../../@types/OceanNode.js' import { ENVIRONMENT_VARIABLES } from '../../utils/constants.js' import { dockerImageManifest } from '../data/assets.js' -// import { omitDBComputeFieldsFromComputeJob } from '../../components/c2d/index.js' import { checkManifestPlatform } from '../../components/c2d/compute_engine_docker.js' describe('Compute Jobs Database', () => { diff --git a/src/test/unit/download.test.ts b/src/test/unit/download.test.ts index 0c119fb45..f31adfa00 100644 --- a/src/test/unit/download.test.ts +++ b/src/test/unit/download.test.ts @@ -21,6 +21,7 @@ import { DEVELOPMENT_CHAIN_ID, KNOWN_CONFIDENTIAL_EVMS } from '../../utils/addre import { DDO } from '@oceanprotocol/ddo-js' import { Wallet } from 'ethers' import { createHashForSignature, safeSign } from '../utils/signature.js' +import { KeyManager } from '../../components/KeyManager/index.js' let envOverrides: OverrideEnvConfig[] let config: OceanNodeConfig @@ -37,8 +38,18 @@ describe('Should validate files structure for download', () => { envOverrides = await setupEnvironment(TEST_ENV_CONFIG_FILE, envOverrides) config = await getConfiguration(true) + const keyManager = new KeyManager(config) db = await Database.init(config.dbConfig) - oceanNode = OceanNode.getInstance(config, db) + oceanNode = OceanNode.getInstance( + config, + db, + null, + null, + null, + keyManager, + null, + true + ) consumerAccount = new Wallet(process.env.PRIVATE_KEY) }) diff --git a/src/test/unit/s3.storage.test.ts b/src/test/unit/s3.storage.test.ts deleted file mode 100644 index ea423bded..000000000 --- a/src/test/unit/s3.storage.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { expect } from 'chai' -import { EncryptMethod, S3FileObject, S3Object } from '../../@types/fileObject.js' -import { Readable, Transform } from 'stream' -import { S3Storage } from '../../components/storage/index.js' -import { getConfiguration } from '../../utils/index.js' - -describe('S3 Storage tests', () => { - let s3Storage: S3Storage - let s3Object: S3Object - - console.log('process.env.ACCESS_KEY_ID_S3', process.env.ACCESS_KEY_ID_S3) - - beforeEach(async () => { - s3Object = { - endpoint: 'fra1.digitaloceanspaces.com', - region: 'fra1', - objectKey: 'test.json', - bucket: 'mybuckettestadri', - accessKeyId: process.env.ACCESS_KEY_ID_S3, - secretAccessKey: process.env.SECRET_ACCESS_KEY_S3 - } - const s3FileObject: S3FileObject = { - type: 's3', - s3Access: s3Object, - encryptedBy: '16Uiu2HAm7YHuXeBpoFoKHyAieKDAsdg3RNmCUEVgNxffByRS7Hdt', - encryptMethod: EncryptMethod.ECIES - } - s3Storage = new S3Storage(s3FileObject, await getConfiguration()) - }) - - it('should create an instance of S3Storage', () => { - expect(s3Storage).to.be.an.instanceOf(S3Storage) - }) - - it('should validate S3 file object successfully', () => { - const [isValid, message] = s3Storage.validate() - // eslint-disable-next-line no-unused-expressions - expect(isValid).to.be.true - // eslint-disable-next-line no-unused-expressions - expect(message).to.be.empty - }) - - it('should parse decrypted stream correctly', async () => { - const decryptedStream = Readable.from('{"key": "value"}') - const parsedData = await s3Storage.parseDecryptedStream(decryptedStream) - expect(parsedData).to.deep.equal({ key: 'value' }) - }) - - it('should fetch data from s3', () => { - const result = s3Storage.getDownloadUrl() - expect(result).to.be.equal(JSON.stringify(s3Object)) - }) - - it('should fetch data from s3', async () => { - const data = await s3Storage.fetchDataStream() - - const jsonTransformStream = new Transform({ - transform(chunk, encoding, callback) { - const json = JSON.parse(chunk.toString('utf-8')) - callback(null, JSON.stringify(json)) - } - }) - - // Pipe the data stream through the JSON transform stream - const dataJson = data.pipe(jsonTransformStream) - let transformedJson = '' - - const streamFinishedPromise = new Promise((resolve, reject) => { - dataJson.on('data', (chunk: any) => { - transformedJson += chunk - }) - dataJson.on('finish', () => { - resolve() - }) - dataJson.on('error', (err: any) => { - console.error('Error reading stream:', err) - reject(err) - }) - }) - - await streamFinishedPromise - - const jsonData = JSON.parse(transformedJson) - - expect(jsonData.test).to.equal(1) - }) - - it('should fetch fetchSpecificFileMetadata from s3', async () => { - const data = await s3Storage.fetchSpecificFileMetadata() - expect(data.encryptMethod).to.be.equals(EncryptMethod.ECIES) - expect(data.name).to.be.equals(s3Object.objectKey) - expect(data.contentType).to.be.equals('application/json') - }) -}) diff --git a/src/test/unit/storage.test.ts b/src/test/unit/storage.test.ts index 43ad6cbab..394f7d737 100644 --- a/src/test/unit/storage.test.ts +++ b/src/test/unit/storage.test.ts @@ -36,12 +36,12 @@ describe('URL Storage tests', () => { encryptedBy: nodeId, encryptMethod: EncryptMethod.AES } - let storage: Storage + let storage: UrlStorage let error: Error let config: any before(async () => { config = await getConfiguration() - storage = Storage.getStorageClass(file, config) + storage = Storage.getStorageClass(file, config) as UrlStorage }) it('Storage instance', () => { @@ -145,7 +145,7 @@ describe('URL Storage tests', () => { Authorization: 'Bearer auth_token_X' } } - storage = Storage.getStorageClass(file, config) + storage = Storage.getStorageClass(file, config) as UrlStorage expect(storage.getDownloadUrl()).to.eql('http://someUrl.com/file.json') }) @@ -208,7 +208,7 @@ describe('Unsafe URL tests', () => { url: 'https://oceanprotocol.com', method: 'get' } - const storage = Storage.getStorageClass(file, config) + const storage = Storage.getStorageClass(file, config) as UrlStorage expect(storage.getDownloadUrl()).to.eql('https://oceanprotocol.com') }) after(() => { diff --git a/src/utils/config/builder.ts b/src/utils/config/builder.ts index 5a97b18ac..23a0f5218 100644 --- a/src/utils/config/builder.ts +++ b/src/utils/config/builder.ts @@ -143,7 +143,18 @@ export function buildC2DClusters( if (dockerComputeEnvironments) { for (const dockerC2d of dockerComputeEnvironments) { if (dockerC2d.socketPath || dockerC2d.host) { - const hash = create256Hash(String(count) + JSON.stringify(dockerC2d)) + const hash = create256Hash( + String(count) + + JSON.stringify({ + socketPath: dockerC2d.socketPath, + protocol: dockerC2d.protocol, + host: dockerC2d.host, + port: dockerC2d.port, + caPath: dockerC2d.caPath, + certPath: dockerC2d.certPath, + keyPath: dockerC2d.keyPath + }) + ) clusters.push({ connection: dockerC2d, hash,