diff --git a/CONTRIBUTING/README.md b/CONTRIBUTING/README.md index ff7994879..741de5104 100644 --- a/CONTRIBUTING/README.md +++ b/CONTRIBUTING/README.md @@ -101,6 +101,7 @@ $ git add $ git commit -m "Meaningful message for the update" # pull new changes $ git checkout master +$ git fetch upstream $ git merge upstream/master $ git checkout $ git rebase master diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index ec0defeb9..a9c1b31d6 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -62,6 +62,7 @@ $ git add $ git commit -m "Meaningful message for the update" # pull new changes $ git checkout master +$ git fetch upstream $ git merge upstream/master $ git checkout $ git rebase master diff --git a/docs/hexists_command_docs.md b/docs/hexists_command_docs.md new file mode 100644 index 000000000..5e0641394 --- /dev/null +++ b/docs/hexists_command_docs.md @@ -0,0 +1,86 @@ +--- +title: HEXISTS +description: The `HEXISTS` command in DiceDB checks if a specified field exists within a hash stored at a given key. This command is used to verify the presence of a field within hash data structures, making it essential for conditional logic. +--- + +The `HEXISTS` command in DiceDB checks if a specified field exists within a hash stored at a given key. This command is used to verify the presence of a field within hash data structures, making it essential for conditional logic. + +## Syntax + +```bash +HEXISTS key field +``` + +## Parameters + +| Parameter | Description | Type | Required | +|-----------|------------------------------------|--------|----------| +| `key` | The name of the key holding a hash | String | Yes | +| `field` | The field to check within the hash | String | Yes | + +## Return values + +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| If the field exists within the hash | `1` | +| If the field does not exist within the hash | `0` | + +## Behaviour + +- The `HEXISTS` command checks if the specified `field` exists within the hash stored at `key`. +- If the specified `field` is present in the hash, `HEXISTS` returns `1`. +- If the specified `field` is not present or if `key` does not contain a hash, it returns `0`. +- If `key` does not exist, `HEXISTS` returns `0`. + +## Errors + +1. `Non-hash type or wrong data type`: + + - Error Message: `(error) WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs if `key` holds a non-hash data structure, such as a string or list. + +2. `Invalid syntax or missing parameter`: + + - Error Message: `(error) ERR syntax error` + - Occurs if the syntax is incorrect or required parameters (`key` and `field`) are missing. + +## Example Usage + +### Basic Usage + +Checking if field `name` exists in the hash stored at key `user:1001` + +```bash +127.0.0.1:7379> HEXISTS user:1001 name +1 +``` + +If the field `name` is not present + +```bash +127.0.0.1:7379> HEXISTS user:1001 age +0 +``` + +### Checking non-existent key + +If the hash `user:1002` does not exist: + +```bash +127.0.0.1:7379> HEXISTS user:1002 name +0 +``` + +## Best Practices + +- `Check for Field Existence`: Use `HEXISTS` to check for a field’s existence in conditional logic, especially if subsequent commands depend on the field's presence. + +## Alternatives + +- `HGET`: The `HGET` command retrieves the value of a specified field within a hash. However, unlike `HEXISTS`, it returns `nil` if the field does not exist, rather than a boolean response. + +## Notes + +- If `key` is not of type hash, consider using commands specifically designed for other data types. + +By utilizing the `HEXISTS` command, you can conditionally manage hash data in DiceDB, verifying field presence before performing operations based on field existence. \ No newline at end of file diff --git a/docs/hkeys_command_docs.md b/docs/hkeys_command_docs.md new file mode 100644 index 000000000..6c993206a --- /dev/null +++ b/docs/hkeys_command_docs.md @@ -0,0 +1,88 @@ +--- +title: HKEYS +description: The `HKEYS` command in DiceDB retrieves all fields in a hash stored at a given key. This command is essential for working with hash data structures, enabling retrieval of all field names for dynamic inspection or iteration. +--- + +The `HKEYS` command in DiceDB retrieves all fields in a hash stored at a given key. This command is essential for working with hash data structures, enabling retrieval of all field names for dynamic inspection or iteration. + +## Syntax + +```bash +HKEYS key +``` + +## Parameters + +| Parameter | Description | Type | Required | +|-----------|------------------------------------|--------|----------| +| `key` | The name of the key holding a hash | String | Yes | + +## Return values + +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| If the key exists and holds a hash | Array of field names | +| If the key does not exist or is empty | Empty array `[]` | + +## Behaviour + +- The `HKEYS` command retrieves all field names within the hash stored at the specified `key`. +- If the hash is empty or the `key` does not exist, it returns an empty array `[]`. +- If `key` exists but does not hold a hash, an error is returned. + +## Errors + +1. `Non-hash type or wrong data type`: + + - Error Message: `(error) WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs if `key` holds a non-hash data structure, such as a string or list. + +2. `Missing required parameter`: + + - Error Message: `(error) ERR wrong number of arguments for 'HKEYS' command` + - Occurs if the `key` parameter is missing from the command. + +## Example Usage + +### Basic Usage + +Retrieving all field names in the hash stored at key `user:1001` + +```bash +127.0.0.1:7379> HKEYS user:1001 +1) "name" +2) "age" +3) "email" +``` + +### Empty hash + +If the hash stored at `user:1002` exists but has no fields: + +```bash +127.0.0.1:7379> HKEYS user:1002 +(nil) +``` + +### Non-existent key + +If the hash `user:1003` does not exist: + +```bash +127.0.0.1:7379> HKEYS user:1003 +(nil) +``` + +## Best Practices + +- `Use Before Iterating`: Use `HKEYS` to retrieve field names in dynamic applications where the field names may not be predetermined. + +## Alternatives + +- `HGETALL`: The `HGETALL` command retrieves all field-value pairs in a hash as an array, rather than only the field names. + +## Notes + +- Ensure that `key` is of type hash before using `HKEYS`, as other data types will produce errors. + +Using the `HKEYS` command, you can efficiently access all field names in hash structures, making it a valuable tool for dynamic data inspection in DiceDB. \ No newline at end of file diff --git a/docs/hvals_command_docs.md b/docs/hvals_command_docs.md new file mode 100644 index 000000000..8ab5dc3ae --- /dev/null +++ b/docs/hvals_command_docs.md @@ -0,0 +1,88 @@ +--- +title: HVALS +description: The `HVALS` command in DiceDB retrieves all values in a hash stored at a given key. This command allows you to access only the values within a hash, which is helpful for data inspection or retrieval without needing the field names. +--- + +The `HVALS` command in DiceDB retrieves all values in a hash stored at a given key. This command allows you to access only the values within a hash, which is helpful for data inspection or retrieval without needing the field names. + +## Syntax + +```bash +HVALS key +``` + +## Parameters + +| Parameter | Description | Type | Required | +|-----------|------------------------------------|--------|----------| +| `key` | The name of the key holding a hash | String | Yes | + +## Return values + +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| If the key exists and holds a hash | Array of values within the hash | +| If the key does not exist or is empty | Empty array `[]` | + +## Behaviour + +- The `HVALS` command retrieves all values stored in the hash at the specified `key`, without returning the associated field names. +- If the hash is empty or `key` does not exist, it returns an empty array `[]`. +- If `key` exists but does not contain a hash, an error is returned. + +## Errors + +1. `Non-hash type or wrong data type`: + + - Error Message: `(error) WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs if `key` holds a non-hash data structure, such as a string or list. + +2. `Missing required parameter`: + + - Error Message: `(error) ERR wrong number of arguments for 'HVALS' command` + - Occurs if the `key` parameter is missing from the command. + +## Example Usage + +### Basic Usage + +Retrieving all values in the hash stored at key `user:1001` + +```bash +127.0.0.1:7379> HVALS user:1001 +1) "John Doe" +2) "30" +3) "john@example.com" +``` + +### Empty hash + +If the hash stored at `user:1002` exists but has no fields: + +```bash +127.0.0.1:7379> HVALS user:1002 +(nil) +``` + +### Non-existent key + +If the hash `user:1003` does not exist: + +```bash +127.0.0.1:7379> HVALS user:1003 +(nil) +``` + +## Best Practices + +- `Use for Values Only`: Use `HVALS` when only the values within a hash are needed without requiring field names, simplifying value extraction. + +## Alternatives + +- `HGETALL`: The `HGETALL` command retrieves all field-value pairs in a hash, providing both names and values. + +## Notes + +- Ensure `key` is a hash type to avoid errors when using `HVALS`. + +Using the `HVALS` command enables efficient access to all values within a hash structure in DiceDB, simplifying data retrieval when field names are unnecessary. \ No newline at end of file diff --git a/docs/package-lock.json b/docs/package-lock.json index 462d64f53..9e4fa28bd 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1,91 +1,52 @@ { "name": "DiceDB", "version": "0.0.1", - "lockfileVersion": 3, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "DiceDB", - "version": "0.0.1", - "dependencies": { - "@astrojs/sitemap": "^3.1.6", - "@astrojs/starlight": "^0.26.3", - "astro-analytics": "^2.7.0", - "bulma": "^0.9.4", - "lucide-astro": "^0.438.0", - "sass": "^1.62.1", - "sharp": "^0.32.5" - }, - "devDependencies": { - "eslint": "^9.11.0", - "eslint-plugin-astro": "^1.2.4", - "prettier": "^3.3.3", - "prettier-plugin-astro": "^0.14.1", - "sass-embedded": "^1.78.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@astrojs/compiler": { + "dependencies": { + "@astrojs/compiler": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.10.3.tgz", - "integrity": "sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw==" + "integrity": "sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw==", + "dev": true }, - "node_modules/@astrojs/internal-helpers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.4.1.tgz", - "integrity": "sha512-bMf9jFihO8YP940uD70SI/RDzIhUHJAolWVcO1v5PUivxGKvfLZTLTVVxEYzGYyPsA3ivdLNqMnL5VgmQySa+g==", - "peer": true - }, - "node_modules/@astrojs/markdown-remark": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-5.2.0.tgz", - "integrity": "sha512-vWGM24KZXz11jR3JO+oqYU3T2qpuOi4uGivJ9SQLCAI01+vEkHC60YJMRvHPc+hwd60F7euNs1PeOEixIIiNQw==", - "dependencies": { + "@astrojs/markdown-remark": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-5.3.0.tgz", + "integrity": "sha512-r0Ikqr0e6ozPb5bvhup1qdWnSPUvQu6tub4ZLYaKyG50BXZ0ej6FhGz3GpChKpH7kglRFPObJd/bDyf2VM9pkg==", + "requires": { "@astrojs/prism": "3.1.0", "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.1", + "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.0", + "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.0", + "remark-rehype": "^11.1.1", "remark-smartypants": "^3.0.2", - "shiki": "^1.10.3", + "shiki": "^1.22.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", - "vfile": "^6.0.2" + "vfile": "^6.0.3" } }, - "node_modules/@astrojs/mdx": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-3.1.7.tgz", - "integrity": "sha512-8lGdCt+S0TrZgQpbcP3fQJc4cTeacAirtz9TpAMtHCWrQGW8slKt3WG4/0N+bhZgYRC4h5AT5drzFz+y3wvmsg==", - "license": "MIT", - "dependencies": { - "@astrojs/markdown-remark": "5.2.0", + "@astrojs/mdx": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-3.1.8.tgz", + "integrity": "sha512-4o/+pvgoLFG0eG96cFs4t3NzZAIAOYu57fKAprWHXJrnq/qdBV0av6BYDjoESxvxNILUYoj8sdZVWtlPWVDLog==", + "requires": { + "@astrojs/markdown-remark": "5.3.0", "@mdx-js/mdx": "^3.0.1", "acorn": "^8.12.1", "es-module-lexer": "^1.5.4", "estree-util-visit": "^2.0.0", "gray-matter": "^4.0.3", - "hast-util-to-html": "^9.0.2", + "hast-util-to-html": "^9.0.3", "kleur": "^4.1.5", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.0", @@ -93,41 +54,31 @@ "source-map": "^0.7.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" - }, - "engines": { - "node": "^18.17.1 || ^20.3.0 || >=21.0.0" - }, - "peerDependencies": { - "astro": "^4.8.0" } }, - "node_modules/@astrojs/prism": { + "@astrojs/prism": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.1.0.tgz", "integrity": "sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw==", - "dependencies": { + "requires": { "prismjs": "^1.29.0" - }, - "engines": { - "node": "^18.17.1 || ^20.3.0 || >=21.0.0" } }, - "node_modules/@astrojs/sitemap": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.1.6.tgz", - "integrity": "sha512-1Qp2NvAzVImqA6y+LubKi1DVhve/hXXgFvB0szxiipzh7BvtuKe4oJJ9dXSqaubaTkt4nMa6dv6RCCAYeB6xaQ==", - "dependencies": { - "sitemap": "^7.1.2", + "@astrojs/sitemap": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.2.1.tgz", + "integrity": "sha512-uxMfO8f7pALq0ADL6Lk68UV6dNYjJ2xGUzyjjVj60JLBs5a6smtlkBYv3tQ0DzoqwS7c9n4FUx5lgv0yPo/fgA==", + "requires": { + "sitemap": "^8.0.0", "stream-replace-string": "^2.0.0", "zod": "^3.23.8" } }, - "node_modules/@astrojs/starlight": { + "@astrojs/starlight": { "version": "0.26.4", "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.26.4.tgz", "integrity": "sha512-ks+GAYkYGZxuCjAJR88HFafY4/K73PtkbYniGaptmdB0yDJY/HwJ/s1vIuig3j63oq9otQfuZFByxWsb4x1urg==", - "license": "MIT", - "dependencies": { + "requires": { "@astrojs/mdx": "^3.1.3", "@astrojs/sitemap": "^3.1.6", "@pagefind/default-ui": "^1.0.3", @@ -149,793 +100,65 @@ "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "vfile": "^6.0.2" - }, - "peerDependencies": { - "astro": "^4.8.6" - } - }, - "node_modules/@astrojs/telemetry": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.1.0.tgz", - "integrity": "sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA==", - "peer": true, - "dependencies": { - "ci-info": "^4.0.0", - "debug": "^4.3.4", - "dlv": "^1.1.3", - "dset": "^3.1.3", - "is-docker": "^3.0.0", - "is-wsl": "^3.0.0", - "which-pm-runs": "^1.1.0" - }, - "engines": { - "node": "^18.17.1 || ^20.3.0 || >=21.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", - "peer": true, - "dependencies": { - "@babel/highlight": "^7.25.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", - "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", - "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", - "peer": true, - "dependencies": { - "@babel/types": "^7.25.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", - "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", - "peer": true, - "dependencies": { - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", - "peer": true, - "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", - "peer": true, - "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", - "peer": true, - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", - "peer": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", - "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", - "peer": true, - "dependencies": { - "@babel/types": "^7.25.7" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", - "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.7.tgz", - "integrity": "sha512-vILAg5nwGlR9EXE8JIOX4NHXd49lrYbN8hnjffDtoULwpL9hUx/N55nqh2qd0q6FyNDfjl9V79ecKGvFbcSA0Q==", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-jsx": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", - "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", - "peer": true, - "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bufbuild/protobuf": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz", - "integrity": "sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==", - "devOptional": true + "@bufbuild/protobuf": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.1.tgz", + "integrity": "sha512-gdWzq7eX017a1kZCU/bP/sbk4e0GZ6idjsXOcMrQwODCb/rx985fHJJ8+hCu79KpuG7PfZh7bo3BBjPH37JuZw==", + "dev": true }, - "node_modules/@ctrl/tinycolor": { + "@ctrl/tinycolor": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz", - "integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", - "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } + "integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==" }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "aix" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "requires": { + "eslint-visitor-keys": "^3.4.3" }, - "funding": { - "url": "https://opencollective.com/eslint" + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } + "@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true }, - "node_modules/@eslint/config-array": { + "@eslint/config-array": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", "dev": true, - "dependencies": { + "requires": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/eslintrc": { + "@eslint/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true + }, + "@eslint/eslintrc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, - "dependencies": { + "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", @@ -946,72 +169,56 @@ "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } } }, - "node_modules/@eslint/js": { - "version": "9.11.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.0.tgz", - "integrity": "sha512-LPkkenkDqyzTFauZLLAPhIb48fj6drrfMvRGSL9tS3AcZBSVTllemLSNyCvHNNL2t797S/6DJNSIwRwXgMO/eQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } + "@eslint/js": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", + "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", + "dev": true }, - "node_modules/@eslint/object-schema": { + "@eslint/object-schema": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } + "dev": true }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", - "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "@eslint/plugin-kit": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz", + "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==", "dev": true, - "dependencies": { + "requires": { "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@expressive-code/core": { + "@expressive-code/core": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.35.6.tgz", "integrity": "sha512-xGqCkmfkgT7lr/rvmfnYdDSeTdCSp1otAHgoFS6wNEeO7wGDPpxdosVqYiIcQ8CfWUABh/pGqWG90q+MV3824A==", - "dependencies": { + "requires": { "@ctrl/tinycolor": "^4.0.4", "hast-util-select": "^6.0.2", "hast-util-to-html": "^9.0.1", @@ -1023,573 +230,85 @@ "unist-util-visit-parents": "^6.0.1" } }, - "node_modules/@expressive-code/plugin-frames": { + "@expressive-code/plugin-frames": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.35.6.tgz", "integrity": "sha512-CqjSWjDJ3wabMJZfL9ZAzH5UAGKg7KWsf1TBzr4xvUbZvWoBtLA/TboBML0U1Ls8h/4TRCIvR4VEb8dv5+QG3w==", - "dependencies": { + "requires": { "@expressive-code/core": "^0.35.6" } }, - "node_modules/@expressive-code/plugin-shiki": { + "@expressive-code/plugin-shiki": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.35.6.tgz", "integrity": "sha512-xm+hzi9BsmhkDUGuyAWIydOAWer7Cs9cj8FM0t4HXaQ+qCubprT6wJZSKUxuvFJIUsIOqk1xXFaJzGJGnWtKMg==", - "dependencies": { + "requires": { "@expressive-code/core": "^0.35.6", "shiki": "^1.1.7" } }, - "node_modules/@expressive-code/plugin-text-markers": { + "@expressive-code/plugin-text-markers": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.35.6.tgz", "integrity": "sha512-/k9eWVZSCs+uEKHR++22Uu6eIbHWEciVHbIuD8frT8DlqTtHYaaiwHPncO6KFWnGDz5i/gL7oyl6XmOi/E6GVg==", - "dependencies": { + "requires": { "@expressive-code/core": "^0.35.6" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } + "@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true }, - "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", - "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.2" + "requires": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz", - "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.2" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", - "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "macos": ">=11", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", - "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "macos": ">=10.13", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", - "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", - "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", - "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", - "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", - "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", - "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz", - "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "glibc": ">=2.28", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.2" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz", - "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.2" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz", - "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "glibc": ">=2.31", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.2" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz", - "integrity": "sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.2" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz", - "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "musl": ">=1.2.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz", - "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "musl": ">=1.2.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.2" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz", - "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==", - "cpu": [ - "wasm32" - ], - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/runtime": "^1.1.1" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz", - "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz", - "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "peer": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "peer": true, - "engines": { - "node": ">=6.0.0" - } + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "peer": true, - "engines": { - "node": ">=6.0.0" - } + "@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true }, - "node_modules/@jridgewell/sourcemap-codec": { + "@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true }, - "node_modules/@mdx-js/mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", - "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==", - "dependencies": { + "@mdx-js/mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", + "integrity": "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==", + "requires": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", - "estree-util-to-js": "^2.0.0", + "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", - "hast-util-to-estree": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", - "periscopic": "^3.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", @@ -1599,942 +318,449 @@ "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/@nodelib/fs.scandir": { + "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { + "dev": true, + "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" } }, - "node_modules/@nodelib/fs.stat": { + "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } + "dev": true }, - "node_modules/@nodelib/fs.walk": { + "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { + "dev": true, + "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" } }, - "node_modules/@oslojs/encoding": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", - "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@pagefind/darwin-arm64": { + "@pagefind/darwin-arm64": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.1.1.tgz", "integrity": "sha512-tZ9tysUmQpFs2EqWG2+E1gc+opDAhSyZSsgKmFzhnWfkK02YHZhvL5XJXEZDqYy3s1FAKhwjTg8XDxneuBlDZQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ] + "optional": true }, - "node_modules/@pagefind/darwin-x64": { + "@pagefind/darwin-x64": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.1.1.tgz", "integrity": "sha512-ChohLQ39dLwaxQv0jIQB/SavP3TM5K5ENfDTqIdzLkmfs3+JlzSDyQKcJFjTHYcCzQOZVeieeGq8PdqvLJxJxQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ] + "optional": true }, - "node_modules/@pagefind/default-ui": { + "@pagefind/default-ui": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.1.1.tgz", "integrity": "sha512-ZM0zDatWDnac/VGHhQCiM7UgA4ca8jpjA+VfuTJyHJBaxGqZMQnm4WoTz9E0KFcue1Bh9kxpu7uWFZfwpZZk0A==" }, - "node_modules/@pagefind/linux-arm64": { + "@pagefind/linux-arm64": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.1.1.tgz", "integrity": "sha512-H5P6wDoCoAbdsWp0Zx0DxnLUrwTGWGLu/VI1rcN2CyFdY2EGSvPQsbGBMrseKRNuIrJDFtxHHHyjZ7UbzaM9EA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] + "optional": true }, - "node_modules/@pagefind/linux-x64": { + "@pagefind/linux-x64": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.1.1.tgz", "integrity": "sha512-yJs7tTYbL2MI3HT+ngs9E1BfUbY9M4/YzA0yEM5xBo4Xl8Yu8Qg2xZTOQ1/F6gwvMrjCUFo8EoACs6LRDhtMrQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] + "optional": true }, - "node_modules/@pagefind/windows-x64": { + "@pagefind/windows-x64": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.1.1.tgz", "integrity": "sha512-b7/qPqgIl+lMzkQ8fJt51SfguB396xbIIR+VZ3YrL2tLuyifDJ1wL5mEm+ddmHxJ2Fki340paPcDan9en5OmAw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ] + "optional": true + }, + "@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "requires": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1", + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + } + }, + "@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "optional": true + }, + "@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "optional": true + }, + "@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "optional": true }, - "node_modules/@pkgr/core": { + "@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "optional": true + }, + "@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "optional": true + }, + "@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "optional": true + }, + "@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "optional": true + }, + "@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "optional": true + }, + "@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "optional": true + }, + "@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "optional": true + }, + "@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "optional": true + }, + "@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "optional": true + }, + "@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", - "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } + "dev": true }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT", - "peer": true - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/@shikijs/core": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.0.tgz", - "integrity": "sha512-S8sMe4q71TJAW+qG93s5VaiihujRK6rqDFqBnxqvga/3LvqHEnxqBIOPkt//IdXVtHkQWKu4nOQNk0uBGicU7Q==", - "dependencies": { - "@shikijs/engine-javascript": "1.22.0", - "@shikijs/engine-oniguruma": "1.22.0", - "@shikijs/types": "1.22.0", + "@shikijs/core": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.2.tgz", + "integrity": "sha512-bvIQcd8BEeR1yFvOYv6HDiyta2FFVePbzeowf5pPS1avczrPK+cjmaxxh0nx5QzbON7+Sv0sQfQVciO7bN72sg==", + "requires": { + "@shikijs/engine-javascript": "1.22.2", + "@shikijs/engine-oniguruma": "1.22.2", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.3" } }, - "node_modules/@shikijs/engine-javascript": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.0.tgz", - "integrity": "sha512-AeEtF4Gcck2dwBqCFUKYfsCq0s+eEbCEbkUuFou53NZ0sTGnJnJ/05KHQFZxpii5HMXbocV9URYVowOP2wH5kw==", - "dependencies": { - "@shikijs/types": "1.22.0", + "@shikijs/engine-javascript": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.2.tgz", + "integrity": "sha512-iOvql09ql6m+3d1vtvP8fLCVCK7BQD1pJFmHIECsujB0V32BJ0Ab6hxk1ewVSMFA58FI0pR2Had9BKZdyQrxTw==", + "requires": { + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "oniguruma-to-js": "0.4.3" } }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.0.tgz", - "integrity": "sha512-5iBVjhu/DYs1HB0BKsRRFipRrD7rqjxlWTj4F2Pf+nQSPqc3kcyqFFeZXnBMzDf0HdqaFVvhDRAGiYNvyLP+Mw==", - "dependencies": { - "@shikijs/types": "1.22.0", + "@shikijs/engine-oniguruma": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.2.tgz", + "integrity": "sha512-GIZPAGzQOy56mGvWMoZRPggn0dTlBf1gutV5TdceLCZlFNqWmuc7u+CzD0Gd9vQUTgLbrt0KLzz6FNprqYAxlA==", + "requires": { + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0" } }, - "node_modules/@shikijs/types": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.0.tgz", - "integrity": "sha512-Fw/Nr7FGFhlQqHfxzZY8Cwtwk5E9nKDUgeLjZgt3UuhcM3yJR9xj3ZGNravZZok8XmEZMiYkSMTPlPkULB8nww==", - "dependencies": { + "@shikijs/types": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.2.tgz", + "integrity": "sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==", + "requires": { "@shikijs/vscode-textmate": "^9.3.0", "@types/hast": "^3.0.4" } }, - "node_modules/@shikijs/vscode-textmate": { + "@shikijs/vscode-textmate": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz", "integrity": "sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==" }, - "node_modules/@types/acorn": { + "@types/acorn": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", - "dependencies": { + "requires": { "@types/estree": "*" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "peer": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "peer": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "peer": true - }, - "node_modules/@types/debug": { + "@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dependencies": { + "requires": { "@types/ms": "*" } }, - "node_modules/@types/estree": { + "@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "license": "MIT" + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, - "node_modules/@types/estree-jsx": { + "@types/estree-jsx": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "dependencies": { + "requires": { "@types/estree": "*" } }, - "node_modules/@types/hast": { + "@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { + "requires": { "@types/unist": "*" } }, - "node_modules/@types/mdast": { + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dependencies": { + "requires": { "@types/unist": "*" } }, - "node_modules/@types/mdx": { + "@types/mdx": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==" }, - "node_modules/@types/ms": { + "@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, - "node_modules/@types/nlcst": { + "@types/nlcst": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", - "dependencies": { + "requires": { "@types/unist": "*" } }, - "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", - "dependencies": { - "undici-types": "~5.26.4" - } + "@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" }, - "node_modules/@types/sax": { + "@types/sax": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "dependencies": { + "requires": { "@types/node": "*" } }, - "node_modules/@types/unist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", - "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.7.0.tgz", - "integrity": "sha512-87rC0k3ZlDOuz82zzXRtQ7Akv3GKhHs0ti4YcbAJtaomllXoSO8hi7Ix3ccEvCd824dy9aIX+j3d2UMAfCtVpg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.7.0", - "@typescript-eslint/visitor-keys": "8.7.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.7.0.tgz", - "integrity": "sha512-LLt4BLHFwSfASHSF2K29SZ+ZCsbQOM+LuarPjRUuHm+Qd09hSe3GCeaQbcCr+Mik+0QFRmep/FyZBO6fJ64U3w==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.7.0.tgz", - "integrity": "sha512-MC8nmcGHsmfAKxwnluTQpNqceniT8SteVwd2voYlmiSWGOtjvGXdPl17dYu2797GVscK30Z04WRM28CrKS9WOg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.7.0", - "@typescript-eslint/visitor-keys": "8.7.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } + "@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "@typescript-eslint/scope-manager": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz", + "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "requires": { + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "@typescript-eslint/types": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz", + "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==", + "dev": true }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.7.0.tgz", - "integrity": "sha512-b1tx0orFCCh/THWPQa2ZwWzvOeyzzp36vkJYOpVg0u8UVOIsfVrnuC9FqAw9gRKn+rG2VmWQ/zDJZzkxUnj/XQ==", + "@typescript-eslint/typescript-estree": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz", + "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.7.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "requires": { + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "@typescript-eslint/visitor-keys": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz", + "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "requires": { + "@typescript-eslint/types": "8.11.0", + "eslint-visitor-keys": "^3.4.3" }, - "funding": { - "url": "https://opencollective.com/eslint" + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } } }, - "node_modules/@ungap/structured-clone": { + "@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } + "acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==" }, - "node_modules/acorn-jsx": { + "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" }, - "node_modules/ajv": { + "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "dependencies": { + "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "peer": true, - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "peer": true - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "peer": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "peer": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" } }, - "node_modules/arg": { + "arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">= 0.4" + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" } }, - "node_modules/array-iterate": { + "array-iterate": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==" }, - "node_modules/array-union": { + "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } + "dev": true }, - "node_modules/astring": { + "astring": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/astro": { - "version": "4.15.12", - "resolved": "https://registry.npmjs.org/astro/-/astro-4.15.12.tgz", - "integrity": "sha512-PojmALAzwafLUD//hqKD4/+1Fj03Aa2VQY/rztCg6DUMgHLpo3TFV3ob1++kay91z/MdNWR+IGITcXhxAXhiTg==", - "peer": true, - "dependencies": { - "@astrojs/compiler": "^2.10.3", - "@astrojs/internal-helpers": "0.4.1", - "@astrojs/markdown-remark": "5.2.0", - "@astrojs/telemetry": "3.1.0", - "@babel/core": "^7.25.7", - "@babel/plugin-transform-react-jsx": "^7.25.7", - "@babel/types": "^7.25.7", - "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.1.2", - "@types/babel__core": "^7.20.5", - "@types/cookie": "^0.6.0", - "acorn": "^8.12.1", - "aria-query": "^5.3.2", - "axobject-query": "^4.1.0", - "boxen": "8.0.1", - "ci-info": "^4.0.0", - "clsx": "^2.1.1", - "common-ancestor-path": "^1.0.1", - "cookie": "^0.7.2", - "cssesc": "^3.0.0", - "debug": "^4.3.7", - "deterministic-object-hash": "^2.0.2", - "devalue": "^5.1.1", - "diff": "^5.2.0", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "es-module-lexer": "^1.5.4", - "esbuild": "^0.21.5", - "estree-walker": "^3.0.3", - "fast-glob": "^3.3.2", - "fastq": "^1.17.1", - "flattie": "^1.1.1", - "github-slugger": "^2.0.0", - "gray-matter": "^4.0.3", - "html-escaper": "^3.0.3", - "http-cache-semantics": "^4.1.1", - "js-yaml": "^4.1.0", - "kleur": "^4.1.5", - "magic-string": "^0.30.11", - "magicast": "^0.3.5", - "micromatch": "^4.0.8", - "mrmime": "^2.0.0", - "neotraverse": "^0.6.18", - "ora": "^8.1.0", - "p-limit": "^6.1.0", - "p-queue": "^8.0.1", - "preferred-pm": "^4.0.0", - "prompts": "^2.4.2", - "rehype": "^13.0.2", - "semver": "^7.6.3", - "shiki": "^1.22.0", - "string-width": "^7.2.0", - "tinyexec": "^0.3.0", - "tsconfck": "^3.1.3", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.3", - "vite": "^5.4.8", - "vitefu": "^1.0.2", - "which-pm": "^3.0.0", - "xxhash-wasm": "^1.0.2", - "yargs-parser": "^21.1.1", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.23.3", - "zod-to-ts": "^1.2.0" - }, - "bin": { - "astro": "astro.js" - }, - "engines": { - "node": "^18.17.1 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0" - }, - "optionalDependencies": { - "sharp": "^0.33.3" - } + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==" }, - "node_modules/astro-analytics": { + "astro-analytics": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/astro-analytics/-/astro-analytics-2.7.0.tgz", "integrity": "sha512-/qctBn7LF9DS3ff2YYFrYtCkLLw2fboPDJUgg3HErygGbEBvebW/eGRftZH4ykDJhlCvwTsSEKz/vff7WllyAw==" }, - "node_modules/astro-eslint-parser": { + "astro-eslint-parser": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/astro-eslint-parser/-/astro-eslint-parser-1.0.3.tgz", "integrity": "sha512-AGsGgcg7Jg9UpyCDgvl/EkdYpe1oMkFdmC2Zl+KWneoieLCtQIFjmcY8yt41gcNx4mby0w8BBJQcBmPuf8UAoQ==", "dev": true, - "dependencies": { + "requires": { "@astrojs/compiler": "^2.0.0", "@typescript-eslint/scope-manager": "^7.0.0 || ^8.0.0", "@typescript-eslint/types": "^7.0.0 || ^8.0.0", @@ -2548,948 +774,421 @@ "globby": "^11.1.0", "is-glob": "^4.0.3", "semver": "^7.3.8" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" } }, - "node_modules/astro-expressive-code": { + "astro-expressive-code": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.35.6.tgz", "integrity": "sha512-1U4KrvFuodaCV3z4I1bIR16SdhQlPkolGsYTtiANxPZUVv/KitGSCTjzksrkPonn1XuwVqvnwmUUVzTLWngnBA==", - "dependencies": { + "requires": { "rehype-expressive-code": "^0.35.6" - }, - "peerDependencies": { - "astro": "^4.0.0-beta || ^3.3.0" } }, - "node_modules/astro/node_modules/sharp": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", - "integrity": "sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==", - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.0" - }, - "engines": { - "libvips": ">=8.15.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.4", - "@img/sharp-darwin-x64": "0.33.4", - "@img/sharp-libvips-darwin-arm64": "1.0.2", - "@img/sharp-libvips-darwin-x64": "1.0.2", - "@img/sharp-libvips-linux-arm": "1.0.2", - "@img/sharp-libvips-linux-arm64": "1.0.2", - "@img/sharp-libvips-linux-s390x": "1.0.2", - "@img/sharp-libvips-linux-x64": "1.0.2", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", - "@img/sharp-libvips-linuxmusl-x64": "1.0.2", - "@img/sharp-linux-arm": "0.33.4", - "@img/sharp-linux-arm64": "0.33.4", - "@img/sharp-linux-s390x": "0.33.4", - "@img/sharp-linux-x64": "0.33.4", - "@img/sharp-linuxmusl-arm64": "0.33.4", - "@img/sharp-linuxmusl-x64": "0.33.4", - "@img/sharp-wasm32": "0.33.4", - "@img/sharp-win32-ia32": "0.33.4", - "@img/sharp-win32-x64": "0.33.4" - } - }, - "node_modules/astrojs-compiler-sync": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astrojs-compiler-sync/-/astrojs-compiler-sync-1.0.0.tgz", - "integrity": "sha512-IM6FxpMoBxkGGdKppkFHNQIC9Wge7jspG2MIJff8DOhG41USNJLxJfxRm7wnkTKWlYK5Y1YFFNYr2vUUKkI8sw==", + "astrojs-compiler-sync": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/astrojs-compiler-sync/-/astrojs-compiler-sync-1.0.1.tgz", + "integrity": "sha512-EdJILVkc/Iiw9sLMyb2uppp/vG7YL9TgkwaEumNDflI8s0AhR5XuCFkdbA/AcCGvcBfsRH9ngy/iIP8Uybl82g==", "dev": true, - "dependencies": { + "requires": { "synckit": "^0.9.0" - }, - "engines": { - "node": "^18.18.0 || >=20.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "@astrojs/compiler": ">=0.27.0" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "peer": true, - "engines": { - "node": ">= 0.4" } }, - "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" + "b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" }, - "node_modules/bail": { + "bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" }, - "node_modules/balanced-match": { + "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "bare-events": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", "optional": true }, - "node_modules/bare-fs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.3.tgz", - "integrity": "sha512-7RYKL+vZVCyAsMLi5SPu7QGauGGT8avnP/HO571ndEuV4MYdGXvLhtW67FuLPeEI8EiIY7zbbRR9x7x7HU0kgw==", + "bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", "optional": true, - "dependencies": { + "requires": { "bare-events": "^2.0.0", "bare-path": "^2.0.0", "bare-stream": "^2.0.0" } }, - "node_modules/bare-os": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.2.tgz", - "integrity": "sha512-HZoJwzC+rZ9lqEemTMiO0luOePoGYNBgsLLgegKR/cljiJvcDNhDZQkzC+NC5Oh0aHbdBNSOHpghwMuB5tqhjg==", + "bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "optional": true }, - "node_modules/bare-path": { + "bare-path": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", "optional": true, - "dependencies": { + "requires": { "bare-os": "^2.1.0" } }, - "node_modules/bare-stream": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.2.1.tgz", - "integrity": "sha512-YTB47kHwBW9zSG8LD77MIBAAQXjU2WjAkMHeeb7hUplVs6+IoM5I7uEVQNPMB7lj9r8I76UMdoMkGnCodHOLqg==", + "bare-stream": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.2.tgz", + "integrity": "sha512-EFZHSIBkDgSHIwj2l2QZfP4U5OcD4xFAOwhSb/vlr9PIqyGJGvB/nfClJbcnh3EY4jtPE4zsb5ztae96bVF79A==", "optional": true, - "dependencies": { - "b4a": "^1.6.6", - "streamx": "^2.18.0" + "requires": { + "streamx": "^2.20.0" } }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "peer": true - }, - "node_modules/base64-js": { + "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, - "node_modules/bcp-47": { + "bcp-47": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz", "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==", - "dependencies": { + "requires": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/bcp-47-match": { + "bcp-47-match": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", - "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==" }, - "node_modules/bl": { + "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { + "requires": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, - "node_modules/boolbase": { + "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, - "node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/brace-expansion": { + "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "dependencies": { + "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "node_modules/braces": { + "braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { + "requires": { "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { + "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { + "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, - "node_modules/buffer-builder": { + "buffer-builder": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", - "devOptional": true + "dev": true }, - "node_modules/bulma": { + "bulma": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.4.tgz", "integrity": "sha512-86FlT5+1GrsgKbPLRRY7cGDg8fsJiP/jzTqXXVqiUZZ2aZT8uemEOHlU1CDU+TxklPEZ11HZNNWclRBBecP4CQ==" }, - "node_modules/callsites": { + "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001667", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", - "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true + "dev": true }, - "node_modules/ccount": { + "ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==" }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "peer": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "node_modules/character-entities": { + "character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" }, - "node_modules/character-entities-html4": { + "character-entities-html4": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==" }, - "node_modules/character-entities-legacy": { + "character-entities-legacy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==" }, - "node_modules/character-reference-invalid": { + "character-reference-invalid": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==" }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "requires": { + "readdirp": "^4.0.1" } }, - "node_modules/chownr": { + "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, - "node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "peer": true, - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "peer": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { + "collapse-white-space": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==" }, - "node_modules/color": { + "color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { + "requires": { "color-convert": "^2.0.1", "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "peer": true, - "dependencies": { - "color-name": "1.1.3" + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/color-string": { + "color-string": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { + "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, - "node_modules/color/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "colorjs.io": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", + "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", + "dev": true }, - "node_modules/comma-separated-tokens": { + "comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "peer": true + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" }, - "node_modules/concat-map": { + "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cross-spawn": { + "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "dependencies": { + "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" } }, - "node_modules/css-selector-parser": { + "css-selector-parser": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.0.5.tgz", - "integrity": "sha512-3itoDFbKUNx1eKmVpYMFyqKX04Ww9osZ+dLgrk6GEv6KMVeXUhUnp4I5X+evw+u3ZxVU6RFXSSRxlTeMh8bA+g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ] + "integrity": "sha512-3itoDFbKUNx1eKmVpYMFyqKX04Ww9osZ+dLgrk6GEv6KMVeXUhUnp4I5X+evw+u3ZxVU6RFXSSRxlTeMh8bA+g==" }, - "node_modules/cssesc": { + "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, - "node_modules/debug": { + "debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { + "requires": { "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } } }, - "node_modules/decode-named-character-reference": { + "decode-named-character-reference": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "dependencies": { + "requires": { "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/decompress-response": { + "decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { + "requires": { "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-extend": { + "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, - "node_modules/deep-is": { + "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/dequal": { + "dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/deterministic-object-hash": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", - "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", - "peer": true, - "dependencies": { - "base-64": "^1.0.0" - }, - "engines": { - "node": ">=18" - } + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" }, - "node_modules/devalue": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", - "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", - "license": "MIT", - "peer": true + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==" }, - "node_modules/devlop": { + "devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dependencies": { + "requires": { "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "peer": true, - "engines": { - "node": ">=0.3.1" } }, - "node_modules/dir-glob": { + "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "dependencies": { + "requires": { "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" } }, - "node_modules/direction": { + "direction": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", - "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", - "bin": { - "direction": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "peer": true - }, - "node_modules/dset": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", - "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.33", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz", - "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==", - "peer": true - }, - "node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "peer": true - }, - "node_modules/end-of-stream": { + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==" + }, + "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { + "requires": { "once": "^1.4.0" } }, - "node_modules/entities": { + "entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" }, - "node_modules/es-module-lexer": { + "es-module-lexer": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==" }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "hasInstallScript": true, - "peer": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "peer": true, - "engines": { - "node": ">=0.8.0" + "esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "requires": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + } + }, + "esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "requires": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" } }, - "node_modules/eslint": { - "version": "9.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.11.0.tgz", - "integrity": "sha512-yVS6XODx+tMFMDFcG4+Hlh+qG7RM6cCJXtQhCKLSsr3XkLvWggHjCqjfh0XsPPnt1c56oaT6PMgW9XWQQjdHXA==", + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" + }, + "eslint": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", + "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", "dev": true, - "dependencies": { + "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.11.0", + "@eslint/js": "9.13.0", "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.2", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3499,54 +1198,37 @@ "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true } } }, - "node_modules/eslint-compat-utils": { + "eslint-compat-utils": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, - "dependencies": { + "requires": { "semver": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" } }, - "node_modules/eslint-plugin-astro": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-astro/-/eslint-plugin-astro-1.2.4.tgz", - "integrity": "sha512-45uXKW6lxmYEa8Gkh5lCfwAnOyQD90AaMS2Bu9ans88f+pFkliqjGeexiKv73oiTcY3I0vlzTUk5GlqvYlkjyA==", + "eslint-plugin-astro": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-astro/-/eslint-plugin-astro-1.3.0.tgz", + "integrity": "sha512-T4bAYOdF0V8zqFF/EeQat5xcYQV5nDmLeZgD1eHbcogY94HBOncwZxsOgcPNGpdCLXkpBDNbF2OMtrj26f5RFA==", "dev": true, - "dependencies": { + "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@jridgewell/sourcemap-codec": "^1.4.14", "@typescript-eslint/types": "^7.7.1 || ^8", @@ -3556,732 +1238,367 @@ "postcss": "^8.4.14", "postcss-selector-parser": "^6.0.10" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": ">=8.57.0" - } - }, - "node_modules/eslint-plugin-astro/node_modules/globals": { - "version": "15.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", - "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "globals": { + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", + "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", + "dev": true + } } }, - "node_modules/eslint-scope": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", - "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "eslint-scope": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", "dev": true, - "dependencies": { + "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", "dev": true }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "espree": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", "dev": true, - "dependencies": { + "requires": { "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "eslint-visitor-keys": "^4.1.0" } }, - "node_modules/esprima": { + "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, - "node_modules/esquery": { + "esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "dependencies": { + "requires": { "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" } }, - "node_modules/esrecurse": { + "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "dependencies": { + "requires": { "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" } }, - "node_modules/estraverse": { + "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } + "dev": true }, - "node_modules/estree-util-attach-comments": { + "estree-util-attach-comments": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "dependencies": { + "requires": { "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/estree-util-build-jsx": { + "estree-util-build-jsx": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "dependencies": { + "requires": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/estree-util-is-identifier-name": { + "estree-util-is-identifier-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==" + }, + "estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "requires": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" } }, - "node_modules/estree-util-to-js": { + "estree-util-to-js": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "dependencies": { + "requires": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/estree-util-visit": { + "estree-util-visit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "dependencies": { + "requires": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/estree-walker": { + "estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { + "requires": { "@types/estree": "^1.0.0" } }, - "node_modules/esutils": { + "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "peer": true + "dev": true }, - "node_modules/expand-template": { + "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" }, - "node_modules/expressive-code": { + "expressive-code": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.35.6.tgz", "integrity": "sha512-+mx+TPTbMqgo0mL92Xh9QgjW0kSQIsEivMgEcOnaqKqL7qCw8Vkqc5Rg/di7ZYw4aMUSr74VTc+w8GQWu05j1g==", - "dependencies": { + "requires": { "@expressive-code/core": "^0.35.6", "@expressive-code/plugin-frames": "^0.35.6", "@expressive-code/plugin-shiki": "^0.35.6", "@expressive-code/plugin-text-markers": "^0.35.6" } }, - "node_modules/extend": { + "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "node_modules/extend-shallow": { + "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dependencies": { + "requires": { "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" } }, - "node_modules/fast-deep-equal": { + "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/fast-fifo": { + "fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" }, - "node_modules/fast-glob": { + "fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dependencies": { + "dev": true, + "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" }, - "engines": { - "node": ">=8.6.0" + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, - "node_modules/fast-json-stable-stringify": { + "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "node_modules/fast-levenshtein": { + "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fastq": { + "fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { + "dev": true, + "requires": { "reusify": "^1.0.4" } }, - "node_modules/file-entry-cache": { + "file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "dependencies": { + "requires": { "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" } }, - "node_modules/fill-range": { + "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { + "requires": { "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "peer": true, - "dependencies": { - "locate-path": "^5.0.0", + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up-simple": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", - "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-yarn-workspace-root2": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz", - "integrity": "sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==", - "peer": true, - "dependencies": { - "micromatch": "^4.0.2", - "pkg-dir": "^4.2.0" } }, - "node_modules/flat-cache": { + "flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "dependencies": { + "requires": { "flatted": "^3.2.9", "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" } }, - "node_modules/flatted": { + "flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, - "node_modules/flattie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", - "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-constants": { + "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-from-package": { + "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, - "node_modules/github-slugger": { + "github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "peer": true, - "engines": { - "node": ">=4" - } + "globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true }, - "node_modules/globby": { + "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "dependencies": { + "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "peer": true - }, - "node_modules/gray-matter": { + "gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "dependencies": { + "requires": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "peer": true, - "engines": { - "node": ">=4" - } + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, - "node_modules/hast-util-embedded": { + "hast-util-embedded": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "hast-util-is-element": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-html": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.1.tgz", - "integrity": "sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==", - "dependencies": { + "hast-util-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", + "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", + "requires": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "html-whitespace-sensitive-tag-names": "^3.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "requires": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-parse5": { + "hast-util-from-parse5": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", @@ -4291,96 +1608,82 @@ "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/hastscript": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", - "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "hastscript": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", + "requires": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + } + } } }, - "node_modules/hast-util-has-property": { + "hast-util-has-property": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-is-body-ok-link": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.0.tgz", - "integrity": "sha512-VFHY5bo2nY8HiV6nir2ynmEB1XkxzuUffhEGeVx7orbu/B1KaGyeGgMZldvMVx5xWrDlLLG/kQ6YkJAMkBEx0w==", - "dependencies": { + "hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "requires": { "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-is-element": { + "hast-util-is-element": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-parse-selector": { + "hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "requires": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + } + }, + "hast-util-parse-selector": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-phrasing": { + "hast-util-phrasing": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-has-property": "^3.0.0", "hast-util-is-body-ok-link": "^3.0.0", "hast-util-is-element": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-raw": { + "hast-util-raw": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.4.tgz", "integrity": "sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", @@ -4394,17 +1697,13 @@ "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-select": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.2.tgz", - "integrity": "sha512-hT/SD/d/Meu+iobvgkffo1QecV8WeKWxwsNMzcTJsKw1cKTQKSR/7ArJeURLNJF9HDjp9nVoORyNNJxrvBye8Q==", - "dependencies": { + "hast-util-select": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.3.tgz", + "integrity": "sha512-OVRQlQ1XuuLP8aFVLYmC2atrfWHS5UD3shonxpnyrjcCkwtvmt/+N6kYJdcY4mkMJhxp4kj2EFIxQ9kvkkt/eQ==", + "requires": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "bcp-47-match": "^2.0.0", @@ -4415,23 +1714,18 @@ "hast-util-has-property": "^3.0.0", "hast-util-to-string": "^3.0.0", "hast-util-whitespace": "^3.0.0", - "not": "^0.1.0", "nth-check": "^2.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-estree": { + "hast-util-to-estree": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", - "dependencies": { + "requires": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", @@ -4449,17 +1743,27 @@ "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "dependencies": { + "inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "requires": { + "inline-style-parser": "0.1.1" + } + } } }, - "node_modules/hast-util-to-html": { + "hast-util-to-html": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", - "license": "MIT", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", @@ -4471,17 +1775,13 @@ "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", - "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", - "dependencies": { + "hast-util-to-jsx-runtime": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", + "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "requires": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", @@ -4497,30 +1797,13 @@ "style-to-object": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz", - "integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==" - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.7.tgz", - "integrity": "sha512-uSjr59G5u6fbxUfKbb8GcqMGT3Xs9v5IbPkjb0S16GyOeBLAzSRK0CixBv5YrYvzO6TDLzIS6QCn78tkqWngPw==", - "dependencies": { - "inline-style-parser": "0.2.3" } }, - "node_modules/hast-util-to-parse5": { + "hast-util-to-parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", @@ -4528,663 +1811,278 @@ "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.0.tgz", - "integrity": "sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==", - "dependencies": { + "hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "requires": { "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-text": { + "hast-util-to-text": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-whitespace": { + "hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/hastscript": { + "hastscript": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.0.tgz", "integrity": "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/html-escaper": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", - "peer": true - }, - "node_modules/html-void-elements": { + "html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/html-whitespace-sensitive-tag-names": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.0.tgz", - "integrity": "sha512-KlClZ3/Qy5UgvpvVvDomGhnQhNWH5INE8GwvSIQ9CWt1K0zbbXrl7eN5bWaafOZgtmO3jMPwUqmrmEwinhPq1w==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==" }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "peer": true + "html-whitespace-sensitive-tag-names": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", + "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==" }, - "node_modules/ieee754": { + "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, - "node_modules/ignore": { + "ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } + "dev": true }, - "node_modules/immutable": { + "immutable": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==" }, - "node_modules/import-fresh": { + "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, - "dependencies": { + "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-meta-resolve": { + "import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==" }, - "node_modules/imurmurhash": { + "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } + "dev": true }, - "node_modules/inherits": { + "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ini": { + "ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + "inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" }, - "node_modules/is-alphabetical": { + "is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==" }, - "node_modules/is-alphanumerical": { + "is-alphanumerical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "dependencies": { + "requires": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-arrayish": { + "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-decimal": { + "is-decimal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "peer": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==" }, - "node_modules/is-extendable": { + "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" - } + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" }, - "node_modules/is-extglob": { + "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, - "node_modules/is-glob": { + "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { + "requires": { "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" } }, - "node_modules/is-hexadecimal": { + "is-hexadecimal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "peer": true, - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==" }, - "node_modules/is-number": { + "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, - "node_modules/is-plain-obj": { + "is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/is-unicode-supported": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", - "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "peer": true, - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" }, - "node_modules/isexe": { + "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "peer": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "peer": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, - "node_modules/json-buffer": { + "json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/json-schema-traverse": { + "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "node_modules/json-stable-stringify-without-jsonify": { + "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "peer": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { + "keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "dependencies": { + "requires": { "json-buffer": "3.0.1" } }, - "node_modules/kind-of": { + "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, - "node_modules/kleur": { + "kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "engines": { - "node": ">=6" - } + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" }, - "node_modules/levn": { + "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "dependencies": { + "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/load-yaml-file": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz", - "integrity": "sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==", - "peer": true, - "dependencies": { - "graceful-fs": "^4.1.5", - "js-yaml": "^3.13.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/load-yaml-file/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "peer": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/load-yaml-file/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "peer": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" } }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "peer": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" } }, - "node_modules/lodash.merge": { + "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "peer": true, - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/longest-streak": { + "longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "peer": true, - "dependencies": { - "yallist": "^3.0.2" - } + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==" }, - "node_modules/lucide-astro": { + "lucide-astro": { "version": "0.438.0", "resolved": "https://registry.npmjs.org/lucide-astro/-/lucide-astro-0.438.0.tgz", - "integrity": "sha512-FMoOuw11xvbPOSvJDNIhmA+kXiwXIL2DCMeO1QPZj9q2siU6q/Es+eDSSU9qu1ZzF9d7rAnr4Mh+79r2vQuSuQ==", - "peerDependencies": { - "astro": ">=2.7.1" - } - }, - "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } + "integrity": "sha512-FMoOuw11xvbPOSvJDNIhmA+kXiwXIL2DCMeO1QPZj9q2siU6q/Es+eDSSU9qu1ZzF9d7rAnr4Mh+79r2vQuSuQ==" }, - "node_modules/markdown-extensions": { + "markdown-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==" }, - "node_modules/markdown-table": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", - "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==" }, - "node_modules/mdast-util-definitions": { + "mdast-util-definitions": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-directive": { + "mdast-util-directive": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", @@ -5193,43 +2091,24 @@ "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-find-and-replace": { + "mdast-util-find-and-replace": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", - "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", - "dependencies": { + "mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "requires": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", @@ -5242,17 +2121,13 @@ "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm": { + "mdast-util-gfm": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", - "dependencies": { + "requires": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", @@ -5260,127 +2135,95 @@ "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", - "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", - "dependencies": { + "mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "requires": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-footnote": { + "mdast-util-gfm-footnote": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-strikethrough": { + "mdast-util-gfm-strikethrough": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-table": { + "mdast-util-gfm-table": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-task-list-item": { + "mdast-util-gfm-task-list-item": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdx": { + "mdast-util-mdx": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "dependencies": { + "requires": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", - "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", - "dependencies": { + "mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "requires": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdx-jsx": { + "mdast-util-mdx-jsx": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", - "dependencies": { + "requires": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", @@ -5393,47 +2236,35 @@ "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdxjs-esm": { + "mdast-util-mdxjs-esm": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "dependencies": { + "requires": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-phrasing": { + "mdast-util-phrasing": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-to-hast": { + "mdast-util-to-hast": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", @@ -5443,17 +2274,13 @@ "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-to-markdown": { + "mdast-util-to-markdown": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", @@ -5462,47 +2289,27 @@ "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-to-string": { + "mdast-util-to-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/merge2": { + "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } + "dev": true }, - "node_modules/micromark": { + "micromark": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", @@ -5522,21 +2329,11 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-core-commonmark": { + "micromark-core-commonmark": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", @@ -5555,11 +2352,11 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-extension-directive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.1.tgz", - "integrity": "sha512-VGV2uxUzhEZmaP7NSFo2vtq7M2nUD+WfmYQD+d8i/1nHbzE+rMy9uzTvUybBbNiVbrhOZibg3gbyoARGqgDWyg==", - "dependencies": { + "micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "requires": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", @@ -5567,17 +2364,13 @@ "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm": { + "micromark-extension-gfm": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "dependencies": { + "requires": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", @@ -5586,32 +2379,24 @@ "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-autolink-literal": { + "micromark-extension-gfm-autolink-literal": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "dependencies": { + "requires": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-footnote": { + "micromark-extension-gfm-footnote": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "dependencies": { + "requires": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", @@ -5620,88 +2405,58 @@ "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-strikethrough": { + "micromark-extension-gfm-strikethrough": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "dependencies": { + "requires": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-table": { + "micromark-extension-gfm-table": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", - "dependencies": { + "requires": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-tagfilter": { + "micromark-extension-gfm-tagfilter": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "dependencies": { + "requires": { "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-task-list-item": { + "micromark-extension-gfm-task-list-item": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "dependencies": { + "requires": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-mdx-expression": { + "micromark-extension-mdx-expression": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", @@ -5712,11 +2467,11 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-extension-mdx-jsx": { + "micromark-extension-mdx-jsx": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.1.tgz", "integrity": "sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg==", - "dependencies": { + "requires": { "@types/acorn": "^4.0.0", "@types/estree": "^1.0.0", "devlop": "^1.0.0", @@ -5728,29 +2483,21 @@ "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-mdx-md": { + "micromark-extension-mdx-md": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "dependencies": { + "requires": { "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-mdxjs": { + "micromark-extension-mdxjs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "dependencies": { + "requires": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", @@ -5759,17 +2506,13 @@ "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-mdxjs-esm": { + "micromark-extension-mdxjs-esm": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "dependencies": { + "requires": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", @@ -5779,68 +2522,34 @@ "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-factory-destination": { + "micromark-factory-destination": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-factory-label": { + "micromark-factory-label": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-factory-mdx-expression": { + "micromark-factory-mdx-expression": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.2.tgz", "integrity": "sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", @@ -5852,212 +2561,102 @@ "vfile-message": "^4.0.0" } }, - "node_modules/micromark-factory-space": { + "micromark-factory-space": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-factory-title": { + "micromark-factory-title": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-factory-whitespace": { + "micromark-factory-whitespace": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-character": { + "micromark-util-character": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-chunked": { + "micromark-util-chunked": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-classify-character": { + "micromark-util-classify-character": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-combine-extensions": { + "micromark-util-combine-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-decode-numeric-character-reference": { + "micromark-util-decode-numeric-character-reference": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-decode-string": { + "micromark-util-decode-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-encode": { + "micromark-util-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==" }, - "node_modules/micromark-util-events-to-acorn": { + "micromark-util-events-to-acorn": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "@types/acorn": "^4.0.0", "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", @@ -6068,470 +2667,194 @@ "vfile-message": "^4.0.0" } }, - "node_modules/micromark-util-html-tag-name": { + "micromark-util-html-tag-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", - "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==" }, - "node_modules/micromark-util-normalize-identifier": { + "micromark-util-normalize-identifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-resolve-all": { + "micromark-util-resolve-all": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-sanitize-uri": { + "micromark-util-sanitize-uri": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-subtokenize": { + "micromark-util-subtokenize": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { + "requires": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-symbol": { + "micromark-util-symbol": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" }, - "node_modules/micromark-util-types": { + "micromark-util-types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==" }, - "node_modules/micromatch": { + "micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dependencies": { + "requires": { "braces": "^3.0.3", "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-response": { + "mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" }, - "node_modules/minimatch": { + "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "dependencies": { + "requires": { "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" } }, - "node_modules/minimist": { + "minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, - "node_modules/mkdirp-classic": { + "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, - "node_modules/mrmime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", - "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { + "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/nanoid": { + "nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" }, - "node_modules/napi-build-utils": { + "napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" }, - "node_modules/natural-compare": { + "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/neotraverse": { - "version": "0.6.18", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", - "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/nlcst-to-string": { + "nlcst-to-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", - "dependencies": { + "requires": { "@types/nlcst": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/node-abi": { - "version": "3.67.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.67.0.tgz", - "integrity": "sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==", - "dependencies": { + "node-abi": { + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", + "requires": { "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" - }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "peer": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" } }, - "node_modules/not": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz", - "integrity": "sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==" + "node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" }, - "node_modules/nth-check": { + "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { + "requires": { "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/once": { + "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { + "requires": { "wrappy": "1" } }, - "node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "peer": true, - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/oniguruma-to-js": { + "oniguruma-to-js": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz", "integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==", - "dependencies": { + "requires": { "regex": "^4.3.2" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" } }, - "node_modules/optionator": { + "optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "dependencies": { + "requires": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.1.0.tgz", - "integrity": "sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ==", - "peer": true, - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/p-limit": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.1.0.tgz", - "integrity": "sha512-H0jc0q1vOzlEk0TqAKXKZxdl7kX3OFUzCnNVUnq5Pc3DGo0kpeaMuPqxQn235HibwBEb0/pm9dgKTjXy66fBkg==", - "peer": true, - "dependencies": { - "yocto-queue": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "peer": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "peer": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.0.1.tgz", - "integrity": "sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==", - "peer": true, - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-timeout": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", - "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", - "peer": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "peer": true, - "engines": { - "node": ">=6" + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" } }, - "node_modules/pagefind": { + "pagefind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.1.1.tgz", "integrity": "sha512-U2YR0dQN5B2fbIXrLtt/UXNS0yWSSYfePaad1KcBPTi0p+zRtsVjwmoPaMQgTks5DnHNbmDxyJUL5TGaLljK3A==", - "bin": { - "pagefind": "lib/runner/bin.cjs" - }, - "optionalDependencies": { + "requires": { "@pagefind/darwin-arm64": "1.1.1", "@pagefind/darwin-x64": "1.1.1", "@pagefind/linux-arm64": "1.1.1", @@ -6539,23 +2862,20 @@ "@pagefind/windows-x64": "1.1.1" } }, - "node_modules/parent-module": { + "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "dependencies": { + "requires": { "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" } }, - "node_modules/parse-entities": { + "parse-entities": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", - "dependencies": { + "requires": { "@types/unist": "^2.0.0", "character-entities": "^2.0.0", "character-entities-legacy": "^3.0.0", @@ -6565,185 +2885,95 @@ "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + } } }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" - }, - "node_modules/parse-latin": { + "parse-latin": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", - "dependencies": { + "requires": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "requires": { + "entities": "^4.5.0" } }, - "node_modules/path-exists": { + "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } + "dev": true }, - "node_modules/path-key": { + "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } + "dev": true }, - "node_modules/path-type": { + "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } + "dev": true }, - "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, - "node_modules/picomatch": { + "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "peer": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss": { - "version": "8.4.45", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", - "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { + "postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "requires": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" } }, - "node_modules/postcss-nested": { + "postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { + "requires": { "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" } }, - "node_modules/postcss-selector-parser": { + "postcss-selector-parser": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dependencies": { + "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" } }, - "node_modules/prebuild-install": { + "prebuild-install": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "dependencies": { + "requires": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", @@ -6757,1048 +2987,616 @@ "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prebuild-install/node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/prebuild-install/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/preferred-pm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-4.0.0.tgz", - "integrity": "sha512-gYBeFTZLu055D8Vv3cSPox/0iTPtkzxpLroSYYA7WXgRi31WCJ51Uyl8ZiPeUUjyvs2MBzK+S8v9JVUgHU/Sqw==", - "peer": true, "dependencies": { - "find-up-simple": "^1.0.0", - "find-yarn-workspace-root2": "1.2.16", - "which-pm": "^3.0.0" - }, - "engines": { - "node": ">=18.12" + "detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==" + }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + } } }, - "node_modules/prelude-ls": { + "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } + "dev": true }, - "node_modules/prettier": { + "prettier": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } + "dev": true }, - "node_modules/prettier-plugin-astro": { + "prettier-plugin-astro": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/prettier-plugin-astro/-/prettier-plugin-astro-0.14.1.tgz", "integrity": "sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw==", "dev": true, - "dependencies": { + "requires": { "@astrojs/compiler": "^2.9.1", "prettier": "^3.0.0", "sass-formatter": "^0.7.6" - }, - "engines": { - "node": "^14.15.0 || >=16.0.0" } }, - "node_modules/prismjs": { + "prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "engines": { - "node": ">=6" - } + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "peer": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prompts/node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/property-information": { + "property-information": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==" }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { + "pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "node_modules/punycode": { + "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } + "dev": true }, - "node_modules/queue-microtask": { + "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "dev": true }, - "node_modules/queue-tick": { + "queue-tick": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" }, - "node_modules/rc": { + "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { + "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" } }, - "node_modules/readable-stream": { + "readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { + "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" + "readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==" + }, + "recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "requires": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + } + }, + "recma-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.0.tgz", + "integrity": "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==", + "requires": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + } + }, + "recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "requires": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + } + }, + "recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "requires": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" } }, - "node_modules/regex": { + "regex": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.3.tgz", "integrity": "sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==" }, - "node_modules/rehype": { + "rehype": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", - "license": "MIT", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-expressive-code": { + "rehype-expressive-code": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.35.6.tgz", "integrity": "sha512-pPdE+pRcRw01kxMOwHQjuRxgwlblZt5+wAc3w2aPGgmcnn57wYjn07iKO7zaznDxYVxMYVvYlnL+R3vWFQS4Gw==", - "dependencies": { + "requires": { "expressive-code": "^0.35.6" } }, - "node_modules/rehype-format": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.0.tgz", - "integrity": "sha512-kM4II8krCHmUhxrlvzFSptvaWh280Fr7UGNJU5DCMuvmAwGCNmGfi9CvFAQK6JDjsNoRMWQStglK3zKJH685Wg==", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "hast-util-phrasing": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "html-whitespace-sensitive-tag-names": "^3.0.0", - "rehype-minify-whitespace": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-minify-whitespace": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-6.0.0.tgz", - "integrity": "sha512-i9It4YHR0Sf3GsnlR5jFUKXRr9oayvEk9GKQUkwZv6hs70OH9q3OCZrq9PpLvIGKt3W+JxBOxCidNVpH/6rWdA==", - "dependencies": { + "rehype-format": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", + "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", + "requires": { "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "hast-util-format": "^1.0.0" } }, - "node_modules/rehype-parse": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.0.tgz", - "integrity": "sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==", - "dependencies": { + "rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "requires": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-raw": { + "rehype-raw": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "dependencies": { + "requires": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-stringify": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.0.tgz", - "integrity": "sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==", - "dependencies": { + "rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "requires": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + } + }, + "rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "requires": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/remark-directive": { + "remark-directive": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.0.tgz", "integrity": "sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "mdast-util-directive": "^3.0.0", "micromark-extension-directive": "^3.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/remark-gfm": { + "remark-gfm": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/remark-mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", - "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", - "dependencies": { + "remark-mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.0.tgz", + "integrity": "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==", + "requires": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/remark-parse": { + "remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/remark-rehype": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", - "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", - "dependencies": { + "remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "requires": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/remark-smartypants": { + "remark-smartypants": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", - "dependencies": { + "requires": { "retext": "^9.0.0", "retext-smartypants": "^6.0.0", "unified": "^11.0.4", "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" } }, - "node_modules/remark-stringify": { + "remark-stringify": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "dependencies": { + "requires": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/resolve-from": { + "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "peer": true, - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "dev": true }, - "node_modules/retext": { + "retext": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", - "dependencies": { + "requires": { "@types/nlcst": "^2.0.0", "retext-latin": "^4.0.0", "retext-stringify": "^4.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/retext-latin": { + "retext-latin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", - "dependencies": { + "requires": { "@types/nlcst": "^2.0.0", "parse-latin": "^7.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/retext-smartypants": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.1.0.tgz", - "integrity": "sha512-LDPXg95346bqFZnDMHo0S7Rq5p64+B+N8Vz733+wPMDtwb9rCOs9LIdIEhrUOU+TAywX9St+ocQWJt8wrzivcQ==", - "dependencies": { + "retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "requires": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/retext-stringify": { + "retext-stringify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", - "dependencies": { + "requires": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/reusify": { + "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { + "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { + "dev": true, + "requires": { "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { + "rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "devOptional": true, - "dependencies": { + "dev": true, + "requires": { "tslib": "^2.1.0" } }, - "node_modules/s.color": { + "s.color": { "version": "0.0.15", "resolved": "https://registry.npmjs.org/s.color/-/s.color-0.0.15.tgz", "integrity": "sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==", "dev": true }, - "node_modules/safe-buffer": { + "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "sass": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.4.tgz", + "integrity": "sha512-rhMQ2tSF5CsuuspvC94nPM9rToiAFw2h3JTrLlgmNw1MH79v8Cr3DH6KF6o6r+8oofY3iYVPUf66KzC8yuVN1w==", + "requires": { + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" } }, - "node_modules/sass-embedded": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.78.0.tgz", - "integrity": "sha512-NR2kvhWVFABmBm0AqgFw9OweQycs0Qs+/teJ9Su+BUY7up+f8S5F/Zi+7QtAqJlewsQyUNfzm1vRuM+20lBwRQ==", - "devOptional": true, - "dependencies": { - "@bufbuild/protobuf": "^1.0.0", + "sass-embedded": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.80.4.tgz", + "integrity": "sha512-lPzKX5g79ZxohlPxh0pXTPFseWj9RfgYI0cPm14CH5ok77Ujuheq/DCp7RStvNDWS8RCQ8Ii6gJC/5WTkGyrhA==", + "dev": true, + "requires": { + "@bufbuild/protobuf": "^2.0.0", "buffer-builder": "^0.2.0", + "colorjs.io": "^0.5.0", "immutable": "^4.0.0", "rxjs": "^7.4.0", + "sass-embedded-android-arm": "1.80.4", + "sass-embedded-android-arm64": "1.80.4", + "sass-embedded-android-ia32": "1.80.4", + "sass-embedded-android-riscv64": "1.80.4", + "sass-embedded-android-x64": "1.80.4", + "sass-embedded-darwin-arm64": "1.80.4", + "sass-embedded-darwin-x64": "1.80.4", + "sass-embedded-linux-arm": "1.80.4", + "sass-embedded-linux-arm64": "1.80.4", + "sass-embedded-linux-ia32": "1.80.4", + "sass-embedded-linux-musl-arm": "1.80.4", + "sass-embedded-linux-musl-arm64": "1.80.4", + "sass-embedded-linux-musl-ia32": "1.80.4", + "sass-embedded-linux-musl-riscv64": "1.80.4", + "sass-embedded-linux-musl-x64": "1.80.4", + "sass-embedded-linux-riscv64": "1.80.4", + "sass-embedded-linux-x64": "1.80.4", + "sass-embedded-win32-arm64": "1.80.4", + "sass-embedded-win32-ia32": "1.80.4", + "sass-embedded-win32-x64": "1.80.4", "supports-color": "^8.1.1", "varint": "^6.0.0" }, - "bin": { - "sass": "dist/bin/sass.js" - }, - "engines": { - "node": ">=16.0.0" - }, - "optionalDependencies": { - "sass-embedded-android-arm": "1.78.0", - "sass-embedded-android-arm64": "1.78.0", - "sass-embedded-android-ia32": "1.78.0", - "sass-embedded-android-riscv64": "1.78.0", - "sass-embedded-android-x64": "1.78.0", - "sass-embedded-darwin-arm64": "1.78.0", - "sass-embedded-darwin-x64": "1.78.0", - "sass-embedded-linux-arm": "1.78.0", - "sass-embedded-linux-arm64": "1.78.0", - "sass-embedded-linux-ia32": "1.78.0", - "sass-embedded-linux-musl-arm": "1.78.0", - "sass-embedded-linux-musl-arm64": "1.78.0", - "sass-embedded-linux-musl-ia32": "1.78.0", - "sass-embedded-linux-musl-riscv64": "1.78.0", - "sass-embedded-linux-musl-x64": "1.78.0", - "sass-embedded-linux-riscv64": "1.78.0", - "sass-embedded-linux-x64": "1.78.0", - "sass-embedded-win32-arm64": "1.78.0", - "sass-embedded-win32-ia32": "1.78.0", - "sass-embedded-win32-x64": "1.78.0" - } - }, - "node_modules/sass-embedded-android-arm": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.78.0.tgz", - "integrity": "sha512-YM6nrmKsj+ImaSTd96F+jzbWSbhPkRN4kedbLgIJ5FsILNa9NAqhmrCQz9pdcjuAhyfxWImdUACsT23CPGENZQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-android-arm64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.78.0.tgz", - "integrity": "sha512-2sAr11EgwPudAuyk4Ite+fWGYJspiFSiZDU2D8/vjjI7BaB9FG6ksYqww3svoMMnjPUWBCjKPDELpZTxViLJbw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-android-ia32": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.78.0.tgz", - "integrity": "sha512-TyJOo4TgnHpOfC/PfqCBqd+jGRanWoRd4Br/0KAfIvaIFjTGIPdk26vUyDVugV1J8QUEY4INGE8EXAuDeRldUQ==", - "cpu": [ - "ia32" - ], + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "sass-embedded-android-arm": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.80.4.tgz", + "integrity": "sha512-iAZ7AiKTLGxQGTkZ37c2/7YC4lkbP1o3eP/K74YaF8O+qhKTLyLOwV7OcmzIywac7dqLcNuGqhFCmFqTYpewZw==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-android-riscv64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.78.0.tgz", - "integrity": "sha512-wwajpsVRuhb7ixrkA3Yu60V2LtROYn45PIYeda30/MrMJi9k3xEqHLhodTexFm6wZoKclGSDZ6L9U5q0XyRKiQ==", - "cpu": [ - "riscv64" - ], + "optional": true + }, + "sass-embedded-android-arm64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.80.4.tgz", + "integrity": "sha512-htAuBmRvvN2d4smrqxZ6WBw4+OOURaoHzq5oZKqS/E35zYl5FHmrJzp4S5e26a0tEBcjca014tfb/uu9cQgnqA==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-android-x64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.78.0.tgz", - "integrity": "sha512-k5l66PO0LgSHMDbDzAQ/vqrXMlJ3r42ZHJA8MJvUbA6sQxTzDS381V7L+EhOATwyI225j2FhEeTHW6rr4WBQzA==", - "cpu": [ - "x64" - ], + "optional": true + }, + "sass-embedded-android-ia32": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.80.4.tgz", + "integrity": "sha512-IIee89Jco8/ad2s/oRJTFqpLhBMzg0UXteJyZ5waZPZmkeSR/t9l67Ef1lLQVh9t9/fJ1ViTTiGYm/g/zu6UGw==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-darwin-arm64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.78.0.tgz", - "integrity": "sha512-3JaxceFSR6N+a22hPYYkj1p45eBaWTt/M8MPTbfzU3TGZrU9bmRX7WlUVtXTo1yYI2iMf22nCv0PQ5ExFF3FMQ==", - "cpu": [ - "arm64" - ], + "optional": true + }, + "sass-embedded-android-riscv64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.80.4.tgz", + "integrity": "sha512-iJM2kqmWrOeE1aUyTp3uMAG86hyAqbpbOEV7tv828fUsMRDM4uHsHtmyp2n8P2Y0Y2FnLzJpvIm3SwDXGDzT1Q==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-darwin-x64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.78.0.tgz", - "integrity": "sha512-UMTijqE3fJ8vEaaD7GPG7G3GsHuPKOdpS8vuA2v2uwO3BPFp/rEKah66atvGqvGO+0JYApkSv0YTnnexSrkHIQ==", - "cpu": [ - "x64" - ], + "optional": true + }, + "sass-embedded-android-x64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.80.4.tgz", + "integrity": "sha512-vd8VrLvUoHeTcsDoIJesXLbQYZH26a8lAzXy6u4+vEuAwikF4WiXBDFrpqiv38QeD3faLeoPtksRsFbAdQqJAA==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-arm": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.78.0.tgz", - "integrity": "sha512-JafT+Co0RK8oO3g9TfVRuG7tkYeh35yDGTgqCFxLrktnkiw5pmIagCfpjxk5GBcSfJMOzhCgclTCDJWAuHGuMQ==", - "cpu": [ - "arm" - ], + "optional": true + }, + "sass-embedded-darwin-arm64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.80.4.tgz", + "integrity": "sha512-SJz7EM1i4NXa7CT/njIWMNYJ6CvbHljDIzUAZEe3V3u1KWl/eNO3pbWAnnDN62tBppwgWx/UdDUbAKowsT6Z8w==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-arm64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.78.0.tgz", - "integrity": "sha512-juMIMpp3DIAiQ842y+boqh0u2SjN4m3mDKrDfMuBznj8DSQoy9J/3e4hLh3g+p0/j83WuROu5nNoYxm2Xz8rww==", - "cpu": [ - "arm64" - ], + "optional": true + }, + "sass-embedded-darwin-x64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.80.4.tgz", + "integrity": "sha512-J/QlBVO66DLtgALgCmM8rZ5zG0dBCIYW1eXIAnnDwC7vGkbAXMtO60M0O/2WNrAfmFfJz1hvKDLjlsxB2XGBLg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-ia32": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.78.0.tgz", - "integrity": "sha512-Gy8GW5g6WX9t8CT2Dto5AL6ikB+pG7aAXWXvfu3RFHktixSwSbyy6CeGqSk1t0xyJCFkQQA/V8HU9bNdeHiBxg==", - "cpu": [ - "ia32" - ], + "optional": true + }, + "sass-embedded-linux-arm": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.80.4.tgz", + "integrity": "sha512-vuaWhc4ebnaY1AgIWNvFv1snxmkWfvlCU7vnQf4qkn3R2Yyd2J+sjkO8o0NgMX8n5XRUSkAaYUJFCH+Nim6KgQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-arm": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.78.0.tgz", - "integrity": "sha512-DUVXtcsfsiOJ2Zwp4Y3T6KZWX8h0gWpzmFUrx+gSIbg67vV8Ww2DWMjWRwqLe7HOLTYBegMBYpMgMgZiPtXhIA==", - "cpu": [ - "arm" - ], + "optional": true + }, + "sass-embedded-linux-arm64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.80.4.tgz", + "integrity": "sha512-hI6zQyrR6qJbvyEHfj8UGXNB8VyUa72jel46406AuxUnViA0RyZDSqXUF8vwVw/Hjv1LkA5ihK9dBmWNbLz1zQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-arm64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.78.0.tgz", - "integrity": "sha512-Lu/TlRHbe9aJY7B7PwWCJz7pTT5Rc50VkApWEmPiU/nu0mGbSpg0Xwar6pNeG8+98ubgKKdRb01N3bvclf5a4A==", - "cpu": [ - "arm64" - ], + "optional": true + }, + "sass-embedded-linux-ia32": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.80.4.tgz", + "integrity": "sha512-wcPExI8UbYrrJvGvo4v2Q+RktbCp44i3qZQ18hglPcVZOC1IzT9NPqZn0XmrqD4hmNbgsYR+picODkvqGw7iDA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-ia32": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.78.0.tgz", - "integrity": "sha512-1E5ywUnq6MRPAecr2r/vDOBr93wXyculEmfyF5JRG8mUufMaxGIhfx64OQE6Drjs+EDURcYZ+Qcg6/ubJWqhcw==", - "cpu": [ - "ia32" - ], + "optional": true + }, + "sass-embedded-linux-musl-arm": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.80.4.tgz", + "integrity": "sha512-HWo0G/9tuhj/uSEwte9KiDK2Xezrfh7nhdEH69ZIfOAqP5byTXL7o08TYagbvMAoljR43Vfna6MelV7NUX4WCw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-riscv64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.78.0.tgz", - "integrity": "sha512-YvQEvX7ctn5BwC79+HBagDYIciEkwcl2NLgoydmEsBO/0+ncMKSGnjsn/iRzErbq1KJNyjGEni8eSHlrtQI1vQ==", - "cpu": [ - "riscv64" - ], + "optional": true + }, + "sass-embedded-linux-musl-arm64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.80.4.tgz", + "integrity": "sha512-y8slzQ8Jjkl+53mUDkp3zxcDrTXVVxzpa+6nKh5Ue8l1YU2KdVZG1v2PoDXxE6o99B5I2TVBG8i02IsdYoL8jQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-x64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.78.0.tgz", - "integrity": "sha512-azdUcZZvZmtUBslIKr2/l4aQrTX7BvO96TD0GLdWz9vuXZrokYm09AJZEnb5j6Pk5I4Xr0yM6BG1Vgcbzqi5Zg==", - "cpu": [ - "x64" - ], + "optional": true + }, + "sass-embedded-linux-musl-ia32": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.80.4.tgz", + "integrity": "sha512-A2WSwnomho491iCeHh3c0YRympfAoJOKr+IyxalTcRH/pjENOWZWZUt00WE2q0tTpEd2V+goWvgS5pmUGewgmg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-riscv64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.78.0.tgz", - "integrity": "sha512-g8M6vqHMjZUoH9C1WJsgwu+qmwdJAAMDaJTM1emeAScUZMTaQGzm+Q6C5oSGnAGR3XLT/drgbHhbmruXDgkdeQ==", - "cpu": [ - "riscv64" - ], + "optional": true + }, + "sass-embedded-linux-musl-riscv64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.80.4.tgz", + "integrity": "sha512-tYQsAHZLr2mnlJQBJ8Z/n/ySIFJ9JWpsUsoLe9fYgGDaBUfItdzUnj15CChRWld8vFe/I84hb7fbCtYXrI60Jg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-x64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.78.0.tgz", - "integrity": "sha512-m997ThzpMwql4u6LzZCoHPIQkgK6bbLPLc7ydemo2Wusqzh6j8XAGxVT5oANp6s2Dmj+yh49pKDozal+tzEX9w==", - "cpu": [ - "x64" - ], + "optional": true + }, + "sass-embedded-linux-musl-x64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.80.4.tgz", + "integrity": "sha512-NZnr+SYbWlmXx0IaSQ8oF0jYkOULp9qKWMmmZQ1mxuGQ3z7tJqFhpH3M+hYkrFNeOq+GaH+nhHGOD4ZNBxeRkg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-win32-arm64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.78.0.tgz", - "integrity": "sha512-qTLIIC5URYRmeuYYllfoL0K1cHSUd+f3sFHAA6fjtdgf288usd6ToCbWpuFb0BtVceEfGQX8lEp+teOG7n7Quw==", - "cpu": [ - "arm64" - ], + "optional": true + }, + "sass-embedded-linux-riscv64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.80.4.tgz", + "integrity": "sha512-h/BmU7QONa7ScvQztFp4Th4aSo3X+Olu3I+RYsaH9s7P683WT3f2w5zr+wwP1V4roM5eyKDCRJBuefT3Fkkkgw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-win32-ia32": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.78.0.tgz", - "integrity": "sha512-BrOWh18T6Y9xgCokGXElEnd8j03fO4W83bwJ9wHRRkrQWaeHtHs3XWW0fX1j2brngWUTjU+jcYUijWF1Z60krw==", - "cpu": [ - "ia32" - ], + "optional": true + }, + "sass-embedded-linux-x64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.80.4.tgz", + "integrity": "sha512-aZbZFs/X9bEmzDiBEiV4IAsKEA0zrCM+s/u2OzvrX4GRvZFJ+/XRTTvf+RTm7mgvTFgfPwCkNGVECQZ1eHh+6A==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-win32-x64": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.78.0.tgz", - "integrity": "sha512-C14iFDJd7oGhmQehRiEL7GtzMmLwubcDqsBarQ+u9LbHoDlUQfIPd7y8mVtNgtxJCdrAO/jc5qR4C+85yE3xPQ==", - "cpu": [ - "x64" - ], + "optional": true + }, + "sass-embedded-win32-arm64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.80.4.tgz", + "integrity": "sha512-8JiatFi2VVFqCdJzKNDteaPC4KPmh8/giaVh7TyMcDhKjnvRLeu3v5V1egTMiwwpnQHuwzU3uqBlm/llVNR2Pw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.0.0" - } + "optional": true }, - "node_modules/sass-embedded/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "devOptional": true, - "engines": { - "node": ">=8" - } + "sass-embedded-win32-ia32": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.80.4.tgz", + "integrity": "sha512-SodmTD6mjxEgoq44jWMibmBQvWkCfENK/70zp4qsztcBSOggg3nYUzwG0YpraClAMXpB1xOvzrArWu9/9fguAg==", + "dev": true, + "optional": true }, - "node_modules/sass-embedded/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "devOptional": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } + "sass-embedded-win32-x64": { + "version": "1.80.4", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.80.4.tgz", + "integrity": "sha512-7+oRRwCCcnOmw152qDiC7x7SphYBo1eLB4KdyThO+7+rYRO8AftXO+kqBPTVSkM8kGp4wxCMF9auPpYBZbjsow==", + "dev": true, + "optional": true }, - "node_modules/sass-formatter": { + "sass-formatter": { "version": "0.7.9", "resolved": "https://registry.npmjs.org/sass-formatter/-/sass-formatter-0.7.9.tgz", "integrity": "sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==", "dev": true, - "dependencies": { + "requires": { "suf-log": "^2.5.3" } }, - "node_modules/sax": { + "sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, - "node_modules/section-matter": { + "section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "dependencies": { + "requires": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" } }, - "node_modules/semver": { + "semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" }, - "node_modules/sharp": { + "sharp": { "version": "0.32.6", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", - "hasInstallScript": true, - "dependencies": { + "requires": { "color": "^4.2.3", "detect-libc": "^2.0.2", "node-addon-api": "^6.1.0", @@ -7808,509 +3606,284 @@ "tar-fs": "^3.0.4", "tunnel-agent": "^0.6.0" }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "dependencies": { + "detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==" + }, + "node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" + } } }, - "node_modules/shebang-command": { + "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "dependencies": { + "requires": { "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" } }, - "node_modules/shebang-regex": { + "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } + "dev": true }, - "node_modules/shiki": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.0.tgz", - "integrity": "sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==", - "dependencies": { - "@shikijs/core": "1.22.0", - "@shikijs/engine-javascript": "1.22.0", - "@shikijs/engine-oniguruma": "1.22.0", - "@shikijs/types": "1.22.0", + "shiki": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.2.tgz", + "integrity": "sha512-3IZau0NdGKXhH2bBlUk4w1IHNxPh6A5B2sUpyY+8utLu2j/h1QpFkAaUA1bAMxOWWGtTWcAh531vnS4NJKS/lA==", + "requires": { + "@shikijs/core": "1.22.2", + "@shikijs/engine-javascript": "1.22.2", + "@shikijs/engine-oniguruma": "1.22.2", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "@types/hast": "^3.0.4" } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "peer": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-concat": { + "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" }, - "node_modules/simple-get": { + "simple-get": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { + "requires": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, - "node_modules/simple-swizzle": { + "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { + "requires": { "is-arrayish": "^0.3.1" } }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "peer": true - }, - "node_modules/sitemap": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", - "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", - "dependencies": { + "sitemap": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.0.tgz", + "integrity": "sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==", + "requires": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.2.4" - }, - "bin": { - "sitemap": "dist/cli.js" - }, - "engines": { - "node": ">=12.0.0", - "npm": ">=5.6.0" } }, - "node_modules/sitemap/node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" - }, - "node_modules/slash": { + "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } + "dev": true }, - "node_modules/source-map": { + "source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "engines": { - "node": ">=0.10.0" - } + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" }, - "node_modules/space-separated-tokens": { + "space-separated-tokens": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" }, - "node_modules/sprintf-js": { + "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stream-replace-string": { + "stream-replace-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==" }, - "node_modules/streamx": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.0.tgz", - "integrity": "sha512-ZGd1LhDeGFucr1CUCTBOS58ZhEendd0ttpGT3usTvosS4ntIwKN9LJFp+OeCSprsCPL14BXVRZlHGRY1V9PVzQ==", - "dependencies": { + "streamx": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "requires": { + "bare-events": "^2.2.0", "fast-fifo": "^1.3.2", "queue-tick": "^1.0.1", "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" } }, - "node_modules/string_decoder": { + "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { + "requires": { "safe-buffer": "~5.2.0" } }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "peer": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stringify-entities": { + "stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dependencies": { + "requires": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "peer": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom-string": { + "strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "engines": { - "node": ">=0.10.0" - } + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" }, - "node_modules/strip-json-comments": { + "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" }, - "node_modules/style-to-object": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", - "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", - "dependencies": { - "inline-style-parser": "0.1.1" + "style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "requires": { + "inline-style-parser": "0.2.4" } }, - "node_modules/suf-log": { + "suf-log": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/suf-log/-/suf-log-2.5.3.tgz", "integrity": "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==", "dev": true, - "dependencies": { + "requires": { "s.color": "0.0.15" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "peer": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } }, - "node_modules/synckit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", - "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, - "dependencies": { + "requires": { "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" } }, - "node_modules/tar-fs": { + "tar-fs": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", - "dependencies": { + "requires": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0", "pump": "^3.0.0", "tar-stream": "^3.1.5" }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "requires": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + } } }, - "node_modules/text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", - "dependencies": { - "b4a": "^1.6.4" + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" } }, - "node_modules/text-table": { + "text-decoder": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", + "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==" + }, + "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/tinyexec": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", - "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", - "peer": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { + "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { + "requires": { "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" } }, - "node_modules/trim-lines": { + "trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" }, - "node_modules/trough": { + "trough": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==" }, - "node_modules/ts-api-utils": { + "ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/tsconfck": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.3.tgz", - "integrity": "sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ==", - "peer": true, - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } + "dev": true }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "devOptional": true + "tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "dev": true }, - "node_modules/tunnel-agent": { + "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { + "requires": { "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" } }, - "node_modules/type-check": { + "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "dependencies": { + "requires": { "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", - "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", - "license": "(MIT OR CC0-1.0)", - "peer": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" } }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unified": { + "unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", @@ -8318,476 +3891,181 @@ "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-find-after": { + "unist-util-find-after": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-is": { + "unist-util-is": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-modify-children": { + "unist-util-modify-children": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-position": { + "unist-util-position": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-position-from-estree": { + "unist-util-position-from-estree": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-remove-position": { + "unist-util-remove-position": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-stringify-position": { + "unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-visit": { + "unist-util-visit": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-visit-children": { + "unist-util-visit-children": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-visit-parents": { + "unist-util-visit-parents": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { + "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "dependencies": { + "requires": { "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { + "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/varint": { + "varint": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", - "devOptional": true + "dev": true }, - "node_modules/vfile": { + "vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/vfile-location": { + "vfile-location": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/vfile-message": { + "vfile-message": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dependencies": { + "requires": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vitefu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.2.tgz", - "integrity": "sha512-0/iAvbXyM3RiPPJ4lyD4w6Mjgtf4ejTK6TPvTNG3H32PLwuT0N/ZjJLiXug7ETE/LWtTeHw9WRv7uX/tIKYyKg==", - "peer": true, - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } } }, - "node_modules/web-namespaces": { + "web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==" }, - "node_modules/which": { + "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "dependencies": { + "requires": { "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-pm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-3.0.0.tgz", - "integrity": "sha512-ysVYmw6+ZBhx3+ZkcPwRuJi38ZOTLJJ33PSHaitLxSKUMsh0LkKd0nC69zZCwt5D+AYUcMK2hhw4yWny20vSGg==", - "peer": true, - "dependencies": { - "load-yaml-file": "^0.2.0" - }, - "engines": { - "node": ">=18.12" - } - }, - "node_modules/which-pm-runs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", - "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "peer": true, - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/word-wrap": { + "word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "dev": true }, - "node_modules/wrappy": { + "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "node_modules/xxhash-wasm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz", - "integrity": "sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==", - "peer": true - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "peer": true - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", - "peer": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true }, - "node_modules/zod": { + "zod": { "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.23.3", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.3.tgz", - "integrity": "sha512-TYWChTxKQbRJp5ST22o/Irt9KC5nj7CdBKYB/AosCRdj/wxEMvv4NNaj9XVUHDOIp53ZxArGhnw5HMZziPFjog==", - "license": "ISC", - "peer": true, - "peerDependencies": { - "zod": "^3.23.3" - } + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==" }, - "node_modules/zod-to-ts": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", - "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", - "peer": true, - "peerDependencies": { - "typescript": "^4.9.4 || ^5.0.2", - "zod": "^3" - } - }, - "node_modules/zwitch": { + "zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" } } } diff --git a/docs/src/content/docs/commands/HLEN.md b/docs/src/content/docs/commands/HLEN.md index 65d271201..c8ef24a03 100644 --- a/docs/src/content/docs/commands/HLEN.md +++ b/docs/src/content/docs/commands/HLEN.md @@ -50,30 +50,30 @@ HLEN key ### Basic Usage Creating hash `myhash` with two fields `field1` and `field2`. Getting hash length of `myhash`. -```DiceDB -> HSET myhash field1 "value1" field2 "value2" +```bash +127.0.0.1:7379> HSET myhash field1 "value1" field2 "value2" (integer) 2 -> HLEN myhash +127.0.0.1:7379> HLEN myhash (integer) 2 ``` ### Invalid Usage on non-existent key Getting hash length from a non-existent hash key `nonExistentHash`. -```DiceDB -> HLEN nonExistentHash +```bash +127.0.0.1:7379> HLEN nonExistentHash (integer) 0 ``` ### Invalid Usage on non-hash key Getting hash length from a key `mystring` associated with a non-hash type. -```DiceDB -> SET mystring "This is a string" +```bash +127.0.0.1:7379> SET mystring "This is a string" OK -> HLEN mystring +127.0.0.1:7379> HLEN mystring (error) WRONGTYPE Operation against a key holding the wrong kind of value ``` diff --git a/docs/src/content/docs/commands/HSCAN.md b/docs/src/content/docs/commands/HSCAN.md index 5c46c6e0d..9177eea7a 100644 --- a/docs/src/content/docs/commands/HSCAN.md +++ b/docs/src/content/docs/commands/HSCAN.md @@ -51,31 +51,31 @@ The `HSCAN` command returns an array containing the next cursor and the matching Creating a hash `myhash` with two fields `field1` and `field2`. Getting `HSCAN` on `myhash` with valid cursors. ```bash -> HSET myhash field1 "value1" field2 "value2" +127.0.0.1:7379> HSET myhash field1 "value1" field2 "value2" 1) (integer) 2 -> HSCAN myhash 0 +127.0.0.1:7379> HSCAN myhash 0 1) "2" 2) 1) "field1" 2) "value1" 3) "field2" 4) "value2" -> HSCAN myhash 0 MATCH field* COUNT 1 +127.0.0.1:7379> HSCAN myhash 0 MATCH field* COUNT 1 1) "1" 2) 1) "field1" 2) "value1" -> HSCAN myhash 1 MATCH field* COUNT 1 +127.0.0.1:7379> HSCAN myhash 1 MATCH field* COUNT 1 1) "0" 2) 1) "field2" 2) "value2" ``` ### Invalid Usage on non-existent key -Getting `HSCAN` on nonExistentHash. +Getting `HSCAN` on `nonExistentHash`. ```bash -> HSCAN nonExistentHash 0 +127.0.0.1:7379> HSCAN nonExistentHash 0 1) "0" 2) (empty array) ``` diff --git a/docs/src/content/docs/commands/HSTRLEN.md b/docs/src/content/docs/commands/HSTRLEN.md index 35c6e56ad..a09279939 100644 --- a/docs/src/content/docs/commands/HSTRLEN.md +++ b/docs/src/content/docs/commands/HSTRLEN.md @@ -50,30 +50,30 @@ HSTRLEN key field ### Basic Usage Creating hash `myhash` with two fields `field1` and `field2`. Getting string length of value in `field1`. -```DiceDB -> HSET myhash field1 "helloworld" field2 "value2" +```bash +127.0.0.1:7379> HSET myhash field1 "helloworld" field2 "value2" (integer) 1 -> HSTRLEN myhash field1 +127.0.0.1:7379> HSTRLEN myhash field1 (integer) 10 ``` ### Invalid Usage on non-existent key Getting string length from a non-existent key `nonExistentHash`. -```DiceDB -> HSTRLEN nonExistentHash field1 +```bash +127.0.0.1:7379> HSTRLEN nonExistentHash field1 (integer) 0 ``` ### Invalid Usage on non-hash key Getting string length from a key `mystring` associated with a non-hash type. -```DiceDB -> SET mystring "This is a string" +```bash +127.0.0.1:7379> SET mystring "This is a string" OK -> HSTRLEN mystring field1 +127.0.0.1:7379> HSTRLEN mystring field1 (error) WRONGTYPE Operation against a key holding the wrong kind of value ``` diff --git a/docs/src/content/docs/commands/ZCARD.md b/docs/src/content/docs/commands/ZCARD.md new file mode 100644 index 000000000..5f2f086db --- /dev/null +++ b/docs/src/content/docs/commands/ZCARD.md @@ -0,0 +1,90 @@ +--- +title: ZCARD +description: Documentation for the DiceDB command ZCARD +--- + +The `ZCARD` command in DiceDB is used to obtain the cardinality (number of elements) of the sorted set stored at the specified key. This command is useful for understanding the cardinality of the sorted set and for performing operations that depend on the number of elements in the sorted set. + +## Syntax + +``` +ZCARD key +``` + +## Parameters + +| Parameter | Description | Type | Required | +|-----------------|------------------------------------------------------------------------------------------|---------|----------| +| `key` | The key associated with the sorted set for whose cardinality is to be retrieved | String | Yes | + + +## Return Value + +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| If specified key exists | cardinality of the sorted set at key | +| If key doesn't exist | `0` | + +## Behaviour + +- DiceDB checks if the specified key exists. +- If the key exists and is associated with a sorted set, DiceDB counts the number of elements in the sorted set and returns this count. +- If the key does not exist, DiceDB returns `0`. +- If the key exists but is not associated with a sorted set, an error is returned. + +## Errors + +1. `Wrong type of key`: + + - Error Message: `(error) WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs when attempting to use the command on a key that contains a non sorted set value. + +2. `Wrong number of arguments`: + + - Error Message: `(error) -ERR wrong number of arguments for 'ZCARD' command` + - Occurs if key isn't specified in the command. + + +## Example Usage + +### Basic Usage +Creating sorted set `myzset` with two fields `one`, `two` with scores 1, 2 respectively. Getting cardinality of `myzset`. Adding new element into `myzset` and getting updated cardinality. + +```bash +127.0.0.1:7379> ZADD myzset 1 "one" 2 "two" +(integer) 2 + +127.0.0.1:7379> ZCARD myzset +(integer) 2 + +127.0.0.1:7379> ZADD myzset 3 "three" +(integer) 1 + +127.0.0.1:7379> ZCARD myzset +(integer) 3 +``` + +### Invalid Usage on non-existent sorted set +Getting cardinality of a non-existent sorted set `nonExistentZSet`. + +```bash +127.0.0.1:7379> ZCARD nonExistentZSet +(integer) 0 +``` + +### Invalid Usage on a non sorted set key +Getting cardinality of a key `mystring` associated with a non sorted set type. + +```bash +127.0.0.1:7379> SET mystring "This is a string" +OK + +127.0.0.1:7379> ZCARD mystring +(error) WRONGTYPE Operation against a key holding the wrong kind of value +``` + +## Notes + +- The `ZCARD` command is a constant-time operation, meaning its execution time is O(1) regardless of the number of elements in the sorted set. +- This command is useful for quickly determining the size of the sorted set without needing to retrieve all the fields and values. + diff --git a/docs/src/content/docs/commands/ZREM.md b/docs/src/content/docs/commands/ZREM.md new file mode 100644 index 000000000..44acc5d66 --- /dev/null +++ b/docs/src/content/docs/commands/ZREM.md @@ -0,0 +1,86 @@ +--- +title: ZREM +description: Documentation for the DiceDB command ZREM +--- + +The `ZREM` command in DiceDB is used to remove the specified members from the sorted set stored at key and return the number of members removed from the sorted set. This command ignores the non-existing members. + +## Syntax + +``` +ZREM key member [member ...] +``` + +## Parameters + +| Parameter | Description | Type | Required | Multiple | +|-----------|----------------------------------------------------|--------|----------|----------| +| `key` | The key associated with the sorted set whose members are to be removed | String | Yes | No | +| `member` | One or more members to remove from the sorted set | String | Yes | Yes | + + +## Return Value + +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| If specified key and members exists | Count of members removed from the sorted set at key | +| If key doesn't exist | `0` | +| If member doesn't exist | `0` | + +## Behaviour + +- DiceDB checks if the specified key exists. +- If the key exists and is associated with a sorted set, DiceDB removes the members from the sorted set and returns number of members removed. +- If the key does not exist, DiceDB returns `0`. +- If the key exists but is not associated with a sorted set, an error is returned. + +## Errors + +1. `Wrong type of key`: + + - Error Message: `(error) WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs when attempting to use the command on a key that contains a non sorted set value. + +2. `Wrong number of arguments`: + + - Error Message: `(error) -ERR wrong number of arguments for 'ZREM' command` + - Occurs if key or member isn't specified in the command. + + +## Example Usage + +### Basic Usage +Creating sorted set `myzset` with fields `one`, `two`, `three`, `four`, `five` with scores 1, 2, 3, 4, 5 respectively. Removing elements from `myzset`. + +```bash +127.0.0.1:7379> ZADD myzset 1 "one" 2 "two" 3 "three" 4 "four" 5 "five" +(integer) 5 +127.0.0.1:7379> ZREM myzset one +(integer) 1 +127.0.0.1:7379> ZREM myzset two six +(integer) 1 +127.0.0.1:7379> ZREM myzset three four +(integer) 2 +``` + +### Invalid Usage on non-existent sorted set +Removing element from a non-existent sorted set `nonExistentZSet`. + +```bash +127.0.0.1:7379> ZREM nonExistentZSet one +(integer) 0 +``` + +### Invalid Usage on a non sorted set key +Getting cardinality of a key `mystring` associated with a non sorted set type. + +```bash +127.0.0.1:7379> SET mystring "This is a string" +OK +127.0.0.1:7379> ZREM mystring +(error) WRONGTYPE Operation against a key holding the wrong kind of value +``` + +## Notes + +- The `ZREM` command is a O(M*log(N)) time-complexity operation, with N being the number of elements in the sorted set and M the number of elements to be removed. \ No newline at end of file diff --git a/go.mod b/go.mod index dd1f08f29..145a914ac 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 require gotest.tools/v3 v3.5.1 require ( - github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/bytedance/sonic/loader v0.2.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -28,9 +28,9 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.10.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/arch v0.11.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -48,7 +48,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/mmcloughlin/geohash v0.10.0 - github.com/ohler55/ojg v1.24.0 + github.com/ohler55/ojg v1.25.0 github.com/pelletier/go-toml/v2 v2.2.3 github.com/rs/xid v1.6.0 github.com/rs/zerolog v1.33.0 @@ -56,6 +56,6 @@ require ( github.com/stretchr/testify v1.9.0 github.com/twmb/murmur3 v1.1.8 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 - golang.org/x/crypto v0.27.0 - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 + golang.org/x/crypto v0.28.0 + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c ) diff --git a/go.sum b/go.sum index 68fe43ac1..662ea7137 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0 github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= -github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E= +github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= @@ -69,8 +69,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mmcloughlin/geohash v0.10.0 h1:9w1HchfDfdeLc+jFEf/04D27KP7E2QmpDu52wPbJWRE= github.com/mmcloughlin/geohash v0.10.0/go.mod h1:oNZxQo5yWJh0eMQEP/8hwQuVx9Z9tjwFUqcTB1SmG0c= -github.com/ohler55/ojg v1.24.0 h1:y2AVez6fPTszK/jPhaAYMCAzAoSleConMqSDD5wJKJg= -github.com/ohler55/ojg v1.24.0/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o= +github.com/ohler55/ojg v1.25.0 h1:sDwc4u4zex65Uz5Nm7O1QwDKTT+YRcpeZQTy1pffRkw= +github.com/ohler55/ojg v1.25.0/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -117,20 +117,20 @@ github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryB github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= -golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= +golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/integration_tests/commands/async/bit_operation_test.go b/integration_tests/commands/async/bit_operation_test.go index 512109b92..959745f58 100644 --- a/integration_tests/commands/async/bit_operation_test.go +++ b/integration_tests/commands/async/bit_operation_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestBitOp(t *testing.T) { diff --git a/integration_tests/commands/async/bit_ops_string_int_test.go b/integration_tests/commands/async/bit_ops_string_int_test.go index 94c1c0e75..13379fdfc 100644 --- a/integration_tests/commands/async/bit_ops_string_int_test.go +++ b/integration_tests/commands/async/bit_ops_string_int_test.go @@ -5,7 +5,7 @@ import ( "math/rand" "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestBitOpsString(t *testing.T) { @@ -180,7 +180,7 @@ func TestBitOpsString(t *testing.T) { case "equal": assert.Equal(t, res, tc.expected[i]) case "less": - assert.Assert(t, res.(int64) <= tc.expected[i].(int64), "CMD: %s Expected %d to be less than or equal to %d", tc.cmds[i], res, tc.expected[i]) + assert.True(t, res.(int64) <= tc.expected[i].(int64), "CMD: %s Expected %d to be less than or equal to %d", tc.cmds[i], res, tc.expected[i]) } } }) diff --git a/integration_tests/commands/async/bitfield_test.go b/integration_tests/commands/async/bitfield_test.go index c587cdbd5..4664e8ebe 100644 --- a/integration_tests/commands/async/bitfield_test.go +++ b/integration_tests/commands/async/bitfield_test.go @@ -4,14 +4,14 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestBitfield(t *testing.T) { conn := getLocalConnection() defer conn.Close() - FireCommand(conn, "FLUSHDB") + FireCommand(conn, "FLUSHDB") defer FireCommand(conn, "FLUSHDB") // clean up after all test cases syntaxErrMsg := "ERR syntax error" bitFieldTypeErrMsg := "ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is." @@ -245,7 +245,7 @@ func TestBitfield(t *testing.T) { } result := FireCommand(conn, tc.Commands[i]) expected := tc.Expected[i] - testifyAssert.Equal(t, expected, result) + assert.Equal(t, expected, result) } for _, cmd := range tc.CleanUp { @@ -262,9 +262,9 @@ func TestBitfieldRO(t *testing.T) { FireCommand(conn, "FLUSHDB") defer FireCommand(conn, "FLUSHDB") - syntaxErrMsg := "ERR syntax error" - bitFieldTypeErrMsg := "ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is." - unsupportedCmdErrMsg := "ERR BITFIELD_RO only supports the GET subcommand" + syntaxErrMsg := "ERR syntax error" + bitFieldTypeErrMsg := "ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is." + unsupportedCmdErrMsg := "ERR BITFIELD_RO only supports the GET subcommand" testCases := []struct { Name string @@ -311,7 +311,7 @@ func TestBitfieldRO(t *testing.T) { unsupportedCmdErrMsg, unsupportedCmdErrMsg, }, - Delay: []time.Duration{0, 0, }, + Delay: []time.Duration{0, 0}, CleanUp: []string{"Del bits"}, }, { @@ -328,7 +328,7 @@ func TestBitfieldRO(t *testing.T) { syntaxErrMsg, syntaxErrMsg, }, - Delay: []time.Duration{0, 0, 0, 0, }, + Delay: []time.Duration{0, 0, 0, 0}, CleanUp: []string{"Del bits"}, }, { @@ -345,7 +345,7 @@ func TestBitfieldRO(t *testing.T) { bitFieldTypeErrMsg, bitFieldTypeErrMsg, }, - Delay: []time.Duration{0, 0, 0, 0, }, + Delay: []time.Duration{0, 0, 0, 0}, CleanUp: []string{"Del bits"}, }, { @@ -366,7 +366,7 @@ func TestBitfieldRO(t *testing.T) { } result := FireCommand(conn, tc.Commands[i]) expected := tc.Expected[i] - testifyAssert.Equal(t, expected, result) + assert.Equal(t, expected, result) } for _, cmd := range tc.CleanUp { diff --git a/integration_tests/commands/async/check_type_test.go b/integration_tests/commands/async/check_type_test.go index 6d58f219b..eca785760 100644 --- a/integration_tests/commands/async/check_type_test.go +++ b/integration_tests/commands/async/check_type_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) // this file may contain test cases for checking error messages across all commands @@ -14,8 +14,8 @@ func TestErrorsForSetData(t *testing.T) { setErrorMsg := "WRONGTYPE Operation against a key holding the wrong kind of value" testCases := []struct { - name string - cmd []string + name string + cmd []string expected []interface{} assertType []string delay []time.Duration diff --git a/integration_tests/commands/async/command_count_test.go b/integration_tests/commands/async/command_count_test.go index 639f50bd6..9f7e33673 100644 --- a/integration_tests/commands/async/command_count_test.go +++ b/integration_tests/commands/async/command_count_test.go @@ -5,7 +5,7 @@ import ( "net" "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandCount(t *testing.T) { @@ -14,7 +14,7 @@ func TestCommandCount(t *testing.T) { t.Run("Command count should be positive", func(t *testing.T) { commandCount := getCommandCount(conn) - assert.Assert(t, commandCount > 0, + assert.True(t, commandCount > 0, fmt.Sprintf("Unexpected number of CLI commands found. expected greater than 0, %d found", commandCount)) }) } diff --git a/integration_tests/commands/async/command_default_test.go b/integration_tests/commands/async/command_default_test.go index 757404ef6..f5ca6e542 100644 --- a/integration_tests/commands/async/command_default_test.go +++ b/integration_tests/commands/async/command_default_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/dicedb/dice/internal/eval" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandDefault(t *testing.T) { @@ -15,12 +15,12 @@ func TestCommandDefault(t *testing.T) { commands := getCommandDefault(conn) t.Run("Command should not be empty", func(t *testing.T) { - assert.Assert(t, len(commands) > 0, + assert.True(t, len(commands) > 0, fmt.Sprintf("Unexpected number of CLI commands found. expected greater than 0, %d found", len(commands))) }) t.Run("Command count matches", func(t *testing.T) { - assert.Assert(t, len(commands) == len(eval.DiceCmds), + assert.True(t, len(commands) == len(eval.DiceCmds), fmt.Sprintf("Unexpected number of CLI commands found. expected %d, %d found", len(eval.DiceCmds), len(commands))) }) } diff --git a/integration_tests/commands/async/command_getkeys_test.go b/integration_tests/commands/async/command_getkeys_test.go index 0c2628291..0a7d94f65 100644 --- a/integration_tests/commands/async/command_getkeys_test.go +++ b/integration_tests/commands/async/command_getkeys_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var getKeysTestCases = []struct { @@ -31,7 +31,7 @@ func TestCommandGetKeys(t *testing.T) { for _, tc := range getKeysTestCases { t.Run(tc.name, func(t *testing.T) { result := FireCommand(conn, "COMMAND GETKEYS "+tc.inCmd) - assert.DeepEqual(t, tc.expected, result) + assert.Equal(t, tc.expected, result) }) } } diff --git a/integration_tests/commands/async/command_info_test.go b/integration_tests/commands/async/command_info_test.go index 7e3e8ce64..4b58576f4 100644 --- a/integration_tests/commands/async/command_info_test.go +++ b/integration_tests/commands/async/command_info_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var getInfoTestCases = []struct { @@ -32,7 +32,7 @@ func TestCommandInfo(t *testing.T) { for _, tc := range getInfoTestCases { t.Run(tc.name, func(t *testing.T) { result := FireCommand(conn, "COMMAND INFO "+tc.inCmd) - assert.DeepEqual(t, tc.expected, result) + assert.Equal(t, tc.expected, result) }) } } diff --git a/integration_tests/commands/async/command_list_test.go b/integration_tests/commands/async/command_list_test.go index ec9182cf7..56722db3c 100644 --- a/integration_tests/commands/async/command_list_test.go +++ b/integration_tests/commands/async/command_list_test.go @@ -5,7 +5,7 @@ import ( "net" "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandList(t *testing.T) { @@ -14,7 +14,7 @@ func TestCommandList(t *testing.T) { t.Run("Command list should not be empty", func(t *testing.T) { commandList := getCommandList(conn) - assert.Assert(t, len(commandList) > 0, + assert.True(t, len(commandList) > 0, fmt.Sprintf("Unexpected number of CLI commands found. expected greater than 0, %d found", len(commandList))) }) } diff --git a/integration_tests/commands/async/command_rename_test.go b/integration_tests/commands/async/command_rename_test.go index 6d3d0d499..4d5be2889 100644 --- a/integration_tests/commands/async/command_rename_test.go +++ b/integration_tests/commands/async/command_rename_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var renameKeysTestCases = []struct { diff --git a/integration_tests/commands/async/copy_test.go b/integration_tests/commands/async/copy_test.go index 4008cea1a..3fad1db47 100644 --- a/integration_tests/commands/async/copy_test.go +++ b/integration_tests/commands/async/copy_test.go @@ -4,8 +4,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - testifyAssert "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCopy(t *testing.T) { @@ -84,7 +83,7 @@ func TestCopy(t *testing.T) { // else compare the values as is. // This is to handle cases where the expected value is a json string with a different key order. if resOk && expOk && testutils.IsJSONResponse(resStr) && testutils.IsJSONResponse(expStr) { - testifyAssert.JSONEq(t, expStr, resStr) + assert.JSONEq(t, expStr, resStr) } else { assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s", cmd) } diff --git a/integration_tests/commands/async/dbsize_test.go b/integration_tests/commands/async/dbsize_test.go index 901f5497a..7278127d8 100644 --- a/integration_tests/commands/async/dbsize_test.go +++ b/integration_tests/commands/async/dbsize_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestDBSIZE(t *testing.T) { diff --git a/integration_tests/commands/async/del_test.go b/integration_tests/commands/async/del_test.go index 2e31fa76d..58305866f 100644 --- a/integration_tests/commands/async/del_test.go +++ b/integration_tests/commands/async/del_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestDel(t *testing.T) { @@ -50,4 +50,4 @@ func TestDel(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/integration_tests/commands/async/deque_test.go b/integration_tests/commands/async/deque_test.go index 9119f682b..2e48bc04e 100644 --- a/integration_tests/commands/async/deque_test.go +++ b/integration_tests/commands/async/deque_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var deqRandGenerator *rand.Rand diff --git a/integration_tests/commands/async/discard_test.go b/integration_tests/commands/async/discard_test.go index 009aab70e..b26ef5c0a 100644 --- a/integration_tests/commands/async/discard_test.go +++ b/integration_tests/commands/async/discard_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestDiscard(t *testing.T) { diff --git a/integration_tests/commands/async/dump_test.go b/integration_tests/commands/async/dump_test.go index 44cb7aee9..7ec050b91 100644 --- a/integration_tests/commands/async/dump_test.go +++ b/integration_tests/commands/async/dump_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestDumpRestore(t *testing.T) { @@ -38,8 +38,8 @@ func TestDumpRestore(t *testing.T) { return false } return len(decoded) > 11 && - decoded[0] == 0x09 && - decoded[1] == 0x00 && + decoded[0] == 0x09 && + decoded[1] == 0x00 && string(decoded[6:11]) == "hello" && decoded[11] == 0xFF }, @@ -96,13 +96,13 @@ func TestDumpRestore(t *testing.T) { switch exp := expected.(type) { case string: - assert.DeepEqual(t, exp, result) + assert.Equal(t, exp, result) case []interface{}: - assert.Assert(t, testutils.UnorderedEqual(exp, result)) + assert.True(t, testutils.UnorderedEqual(exp, result)) case func(interface{}) bool: - assert.Assert(t, exp(result), cmd) + assert.True(t, exp(result), cmd) default: - assert.DeepEqual(t, expected, result) + assert.Equal(t, expected, result) } } }) diff --git a/integration_tests/commands/async/echo_test.go b/integration_tests/commands/async/echo_test.go index 1c46dadbd..06d14c022 100644 --- a/integration_tests/commands/async/echo_test.go +++ b/integration_tests/commands/async/echo_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestEcho(t *testing.T) { diff --git a/integration_tests/commands/async/exists_test.go b/integration_tests/commands/async/exists_test.go index 73d7cea8c..05d4da1f1 100644 --- a/integration_tests/commands/async/exists_test.go +++ b/integration_tests/commands/async/exists_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExists(t *testing.T) { diff --git a/integration_tests/commands/async/expire_test.go b/integration_tests/commands/async/expire_test.go index 519057662..129ef39ad 100644 --- a/integration_tests/commands/async/expire_test.go +++ b/integration_tests/commands/async/expire_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExpire(t *testing.T) { @@ -232,10 +232,10 @@ func TestExpire(t *testing.T) { } if expected == "(nil)" { - assert.Assert(t, results[i] == "(nil)" || results[i] == "", + assert.True(t, results[i] == "(nil)" || results[i] == "", "Expected nil or empty result, got %v", results[i]) } else { - assert.DeepEqual(t, expected, results[i]) + assert.Equal(t, expected, results[i]) } } }) diff --git a/integration_tests/commands/async/expireat_test.go b/integration_tests/commands/async/expireat_test.go index b6c9210d3..4504e50fa 100644 --- a/integration_tests/commands/async/expireat_test.go +++ b/integration_tests/commands/async/expireat_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExpireat(t *testing.T) { @@ -231,10 +231,10 @@ func TestExpireat(t *testing.T) { } if expected == "(nil)" { - assert.Assert(t, results[i] == "(nil)" || results[i] == "", + assert.True(t, results[i] == "(nil)" || results[i] == "", "Expected nil or empty result, got %v", results[i]) } else { - assert.DeepEqual(t, expected, results[i]) + assert.Equal(t, expected, results[i]) } } }) diff --git a/integration_tests/commands/async/expiretime_test.go b/integration_tests/commands/async/expiretime_test.go index 017216583..9ee7fb8ed 100644 --- a/integration_tests/commands/async/expiretime_test.go +++ b/integration_tests/commands/async/expiretime_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExpiretime(t *testing.T) { @@ -86,10 +86,10 @@ func TestExpiretime(t *testing.T) { } if expected == "(nil)" { - assert.Assert(t, results[i] == "(nil)" || results[i] == "", + assert.True(t, results[i] == "(nil)" || results[i] == "", "Expected nil or empty result, got %v", results[i]) } else { - assert.DeepEqual(t, expected, results[i]) + assert.Equal(t, expected, results[i]) } } }) diff --git a/integration_tests/commands/async/flushdb_test.go b/integration_tests/commands/async/flushdb_test.go index 45b34dc8a..b11d9e804 100644 --- a/integration_tests/commands/async/flushdb_test.go +++ b/integration_tests/commands/async/flushdb_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestFLUSHDB(t *testing.T) { diff --git a/integration_tests/commands/async/get_test.go b/integration_tests/commands/async/get_test.go index f427a8f1d..697d4a03f 100644 --- a/integration_tests/commands/async/get_test.go +++ b/integration_tests/commands/async/get_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGet(t *testing.T) { diff --git a/integration_tests/commands/async/getdel_test.go b/integration_tests/commands/async/getdel_test.go index 262bffc6b..476631bed 100644 --- a/integration_tests/commands/async/getdel_test.go +++ b/integration_tests/commands/async/getdel_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGetDel(t *testing.T) { diff --git a/integration_tests/commands/async/getex_test.go b/integration_tests/commands/async/getex_test.go index 8266cc671..8d552fac6 100644 --- a/integration_tests/commands/async/getex_test.go +++ b/integration_tests/commands/async/getex_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGetEx(t *testing.T) { @@ -156,9 +156,9 @@ func TestGetEx(t *testing.T) { } result := FireCommand(conn, cmd) if tc.assertType[i] == "equal" { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } else if tc.assertType[i] == "assert" { - assert.Assert(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) + assert.True(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) } } }) diff --git a/integration_tests/commands/async/getset_test.go b/integration_tests/commands/async/getset_test.go index 0f4323232..ce43696e2 100644 --- a/integration_tests/commands/async/getset_test.go +++ b/integration_tests/commands/async/getset_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGetSet(t *testing.T) { diff --git a/integration_tests/commands/async/hdel_test.go b/integration_tests/commands/async/hdel_test.go index 3f9fe5d0b..eb6bff455 100644 --- a/integration_tests/commands/async/hdel_test.go +++ b/integration_tests/commands/async/hdel_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHDEL(t *testing.T) { @@ -36,7 +36,7 @@ func TestHDEL(t *testing.T) { for _, tc := range testCases { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } } diff --git a/integration_tests/commands/async/hello_test.go b/integration_tests/commands/async/hello_test.go index 7b8620128..263865f35 100644 --- a/integration_tests/commands/async/hello_test.go +++ b/integration_tests/commands/async/hello_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHello(t *testing.T) { @@ -20,6 +20,6 @@ func TestHello(t *testing.T) { t.Run("HELLO command response", func(t *testing.T) { actual := FireCommand(conn, "HELLO") - assert.DeepEqual(t, expected, actual) + assert.Equal(t, expected, actual) }) } diff --git a/integration_tests/commands/async/hexists_test.go b/integration_tests/commands/async/hexists_test.go deleted file mode 100644 index 5ab181317..000000000 --- a/integration_tests/commands/async/hexists_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package async - -import ( - "testing" - - testifyAssert "github.com/stretchr/testify/assert" -) - -func TestHEXISTS(t *testing.T) { - conn := getLocalConnection() - FireCommand(conn, "DEL key_hExists1 key_hExists2 key_hExists3 key") - - defer conn.Close() - defer FireCommand(conn, "DEL key_hExists1 key_hExists2 key_hExists3 key") - - testCases := []TestCase{ - { - commands: []string{"HEXISTS", "HEXISTS KEY", "HEXISTS KEY FIELD ANOTHER_FIELD"}, - expected: []interface{}{"ERR wrong number of arguments for 'hexists' command", - "ERR wrong number of arguments for 'hexists' command", - "ERR wrong number of arguments for 'hexists' command"}, - }, - { - commands: []string{"HSET key_hExists1 field value", "HEXISTS wrong_key_hExists field"}, - expected: []interface{}{int64(1), int64(0)}, - }, - { - commands: []string{"HSET key_hExists2 field value", "HEXISTS key_hExists2 wrong_field"}, - expected: []interface{}{int64(1), int64(0)}, - }, - { - commands: []string{"HSET key_hExists3 field HelloWorld", "HEXISTS key_hExists3 field"}, - expected: []interface{}{int64(1), int64(1)}, - }, - { - commands: []string{"SET key value", "HEXISTS key field"}, - expected: []interface{}{"OK", "WRONGTYPE Operation against a key holding the wrong kind of value"}, - }, - } - - for _, tc := range testCases { - for i, cmd := range tc.commands { - result := FireCommand(conn, cmd) - testifyAssert.Equal(t, tc.expected[i], result) - } - } -} diff --git a/integration_tests/commands/async/hget_test.go b/integration_tests/commands/async/hget_test.go index d604ceff1..3b0618c91 100644 --- a/integration_tests/commands/async/hget_test.go +++ b/integration_tests/commands/async/hget_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHGET(t *testing.T) { @@ -43,7 +43,7 @@ func TestHGET(t *testing.T) { for _, tc := range testCases { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } } diff --git a/integration_tests/commands/async/hgetall_test.go b/integration_tests/commands/async/hgetall_test.go index 24b2e7b2f..72448f64b 100644 --- a/integration_tests/commands/async/hgetall_test.go +++ b/integration_tests/commands/async/hgetall_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHGETALL(t *testing.T) { @@ -52,7 +52,7 @@ func TestHGETALL(t *testing.T) { } } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/async/hkeys_test.go b/integration_tests/commands/async/hkeys_test.go deleted file mode 100644 index c0cd2954f..000000000 --- a/integration_tests/commands/async/hkeys_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package async - -import ( - "testing" - - testifyAssert "github.com/stretchr/testify/assert" -) - -func TestHKEYS(t *testing.T) { - conn := getLocalConnection() - defer conn.Close() - defer FireCommand(conn, "DEL key_hKeys1 key_hKeys2 key_hKeys3 key") - - testCases := []TestCase{ - { - name: "HKEYS with key containing hash with multiple fields", - commands: []string{"HSET key_hkeys field1 value1", "HSET key_hkeys field2 value2", "HKEYS key_hkeys"}, - expected: []interface{}{int64(1), int64(1), []interface{}{"field1", "field2"}}, - }, - { - name: "HKEYS with non-existent key", - commands: []string{"HKEYS key_hkeys01"}, - expected: []interface{}{[]interface{}{}}, - }, - { - name: "HKEYS with key containing a non-hash value", - commands: []string{"SET key_hkeys02 field1", "HKEYS key_hkeys02"}, - expected: []interface{}{"OK", "WRONGTYPE Operation against a key holding the wrong kind of value"}, - }, - { - name: "HKEYS with wrong number of arguments", - commands: []string{"HKEYS key_hkeys03 x", "HKEYS"}, - expected: []interface{}{"ERR wrong number of arguments for 'hkeys' command", - "ERR wrong number of arguments for 'hkeys' command"}, - }, - } - - for _, tc := range testCases { - for i, cmd := range tc.commands { - result := FireCommand(conn, cmd) - if slice, ok := tc.expected[i].([]interface{}); ok { - testifyAssert.ElementsMatch(t, slice, result) - } else { - testifyAssert.Equal(t, tc.expected[i], result) - } - } - } -} \ No newline at end of file diff --git a/integration_tests/commands/async/hmget_test.go b/integration_tests/commands/async/hmget_test.go index 72cb9804d..cad7e7894 100644 --- a/integration_tests/commands/async/hmget_test.go +++ b/integration_tests/commands/async/hmget_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHMGET(t *testing.T) { @@ -50,7 +50,7 @@ func TestHMGET(t *testing.T) { for i, cmd := range tc.commands { // Fire the command and get the result result := FireCommand(conn, cmd) - assert.DeepEqual(t, result, tc.expected[i]) + assert.Equal(t, result, tc.expected[i]) } }) } diff --git a/integration_tests/commands/async/hmset_test.go b/integration_tests/commands/async/hmset_test.go index 8cbd20e60..ce2508436 100644 --- a/integration_tests/commands/async/hmset_test.go +++ b/integration_tests/commands/async/hmset_test.go @@ -1,7 +1,7 @@ package async import ( - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" "testing" ) @@ -39,7 +39,7 @@ func TestHMSET(t *testing.T) { for _, tc := range testCases { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } } diff --git a/integration_tests/commands/async/hset_test.go b/integration_tests/commands/async/hset_test.go index 4e41c1898..81240c6d0 100644 --- a/integration_tests/commands/async/hset_test.go +++ b/integration_tests/commands/async/hset_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var ZERO int64 = 0 @@ -40,7 +40,7 @@ func TestHSET(t *testing.T) { for _, tc := range testCases { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } } diff --git a/integration_tests/commands/async/hsetnx_test.go b/integration_tests/commands/async/hsetnx_test.go index 9d77bebc4..dfb4b6e9d 100644 --- a/integration_tests/commands/async/hsetnx_test.go +++ b/integration_tests/commands/async/hsetnx_test.go @@ -1,38 +1,38 @@ -package async - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -func TestHSETNX(t *testing.T) { - conn := getLocalConnection() - defer conn.Close() - - testCases := []TestCase{ - { - commands: []string{"HSETNX key_nx_t1 field value", "HSET key_nx_t1 field value_new"}, - expected: []interface{}{ONE, ZERO}, - }, - { - commands: []string{"HSETNX key_nx_t2 field1 value1"}, - expected: []interface{}{ONE}, - }, - { - commands: []string{"HSETNX key_nx_t3 field value", "HSETNX key_nx_t3 field new_value", "HSETNX key_nx_t3"}, - expected: []interface{}{ONE, ZERO, "ERR wrong number of arguments for 'hsetnx' command"}, - }, - { - commands: []string{"SET key_nx_t4 v", "HSETNX key_nx_t4 f v"}, - expected: []interface{}{"OK", "WRONGTYPE Operation against a key holding the wrong kind of value"}, - }, - } - - for _, tc := range testCases { - for i, cmd := range tc.commands { - result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) - } - } -} +package async + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHSETNX(t *testing.T) { + conn := getLocalConnection() + defer conn.Close() + + testCases := []TestCase{ + { + commands: []string{"HSETNX key_nx_t1 field value", "HSET key_nx_t1 field value_new"}, + expected: []interface{}{ONE, ZERO}, + }, + { + commands: []string{"HSETNX key_nx_t2 field1 value1"}, + expected: []interface{}{ONE}, + }, + { + commands: []string{"HSETNX key_nx_t3 field value", "HSETNX key_nx_t3 field new_value", "HSETNX key_nx_t3"}, + expected: []interface{}{ONE, ZERO, "ERR wrong number of arguments for 'hsetnx' command"}, + }, + { + commands: []string{"SET key_nx_t4 v", "HSETNX key_nx_t4 f v"}, + expected: []interface{}{"OK", "WRONGTYPE Operation against a key holding the wrong kind of value"}, + }, + } + + for _, tc := range testCases { + for i, cmd := range tc.commands { + result := FireCommand(conn, cmd) + assert.Equal(t, tc.expected[i], result) + } + } +} diff --git a/integration_tests/commands/async/hvals_test.go b/integration_tests/commands/async/hvals_test.go deleted file mode 100644 index 36a2835e4..000000000 --- a/integration_tests/commands/async/hvals_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package async - -import ( - testifyAssert "github.com/stretchr/testify/assert" - "testing" - - "gotest.tools/v3/assert" -) - -func TestHvals(t *testing.T) { - conn := getLocalConnection() - defer conn.Close() - defer FireCommand(conn, "DEL hvalsKey hvalsKey01 hvalsKey02") - - testCases := []TestCase{ - { - name: "HVALS with multiple fields", - commands: []string{"HSET hvalsKey field value", "HSET hvalsKey field2 value_new", "HVALS hvalsKey"}, - expected: []interface{}{ONE, ONE, []string{"value", "value_new"}}, - }, - { - name: "HVALS with non-existing key", - commands: []string{"HVALS hvalsKey01"}, - expected: []interface{}{[]string{}}, - }, - { - name: "HVALS on wrong key type", - commands: []string{"SET hvalsKey02 field", "HVALS hvalsKey02"}, - expected: []interface{}{"OK", "WRONGTYPE Operation against a key holding the wrong kind of value"}, - }, - { - name: "HVALS with wrong number of arguments", - commands: []string{"HVALS hvalsKey03 x", "HVALS"}, - expected: []interface{}{"ERR wrong number of arguments for 'hvals' command", "ERR wrong number of arguments for 'hvals' command"}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - for i, cmd := range tc.commands { - result := FireCommand(conn, cmd) - - // Type check for expected value and result - expectedList, isExpectedList := tc.expected[i].([]string) - resultList, isResultList := result.([]interface{}) - - // If both are lists, compare them unordered - if isExpectedList && isResultList && len(resultList) == len(expectedList) { - testifyAssert.ElementsMatch(t, expectedList, convertToStringSlice(resultList)) - } else { - // Otherwise, do a deep comparison - assert.DeepEqual(t, tc.expected[i], result) - } - } - }) - } -} - -// Helper function to convert []interface{} to []string for easier comparison -func convertToStringSlice(input []interface{}) []string { - output := make([]string, len(input)) - for i, v := range input { - output[i] = v.(string) - } - return output -} diff --git a/integration_tests/commands/async/json_arrpop_test.go b/integration_tests/commands/async/json_arrpop_test.go index 50b75f24a..cbfc7bf6b 100644 --- a/integration_tests/commands/async/json_arrpop_test.go +++ b/integration_tests/commands/async/json_arrpop_test.go @@ -4,8 +4,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - testifyAssert "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestJSONARRPOP(t *testing.T) { @@ -19,9 +18,9 @@ func TestJSONARRPOP(t *testing.T) { testCases := []struct { name string commands []string - expected []interface{} - assertType []string - jsonResp []bool + expected []interface{} + assertType []string + jsonResp []bool nestedArray bool path string }{ @@ -49,14 +48,14 @@ func TestJSONARRPOP(t *testing.T) { jsonResult, isString := result.(string) if isString && testutils.IsJSONResponse(jsonResult) { - testifyAssert.JSONEq(t, out.(string), jsonResult) + assert.JSONEq(t, out.(string), jsonResult) continue } if tcase.assertType[i] == "equal" { assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } } }) diff --git a/integration_tests/commands/async/json_test.go b/integration_tests/commands/async/json_test.go index dc6957b35..9273daae3 100644 --- a/integration_tests/commands/async/json_test.go +++ b/integration_tests/commands/async/json_test.go @@ -8,8 +8,7 @@ import ( "github.com/bytedance/sonic" "github.com/dicedb/dice/testutils" - testifyAssert "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestJSONOperations(t *testing.T) { @@ -189,7 +188,7 @@ func TestJSONOperations(t *testing.T) { if tc.getCmd != "" { result := FireCommand(conn, tc.getCmd) if testutils.IsJSONResponse(result.(string)) { - testifyAssert.JSONEq(t, tc.expected, result.(string)) + assert.JSONEq(t, tc.expected, result.(string)) } else { assert.Equal(t, tc.expected, result) } @@ -239,7 +238,7 @@ func TestJSONSetWithInvalidJSON(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := FireCommand(conn, tc.command) - assert.Check(t, strings.HasPrefix(result.(string), tc.expected), fmt.Sprintf("Expected: %s, Got: %s", tc.expected, result)) + assert.True(t, strings.HasPrefix(result.(string), tc.expected), fmt.Sprintf("Expected: %s, Got: %s", tc.expected, result)) }) } } @@ -340,7 +339,7 @@ func TestJSONSetWithNXAndXX(t *testing.T) { result := FireCommand(conn, cmd) jsonResult, isString := result.(string) if isString && testutils.IsJSONResponse(jsonResult) { - testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult) + assert.JSONEq(t, tc.expected[i].(string), jsonResult) } else { assert.Equal(t, tc.expected[i], result) } @@ -525,7 +524,7 @@ func TestJSONDelOperations(t *testing.T) { result := FireCommand(conn, cmd) stringResult, ok := result.(string) if ok && testutils.IsJSONResponse(stringResult) { - testifyAssert.JSONEq(t, tc.expected[i].(string), stringResult) + assert.JSONEq(t, tc.expected[i].(string), stringResult) } else { assert.Equal(t, tc.expected[i], result) } @@ -620,7 +619,7 @@ func TestJSONForgetOperations(t *testing.T) { result := FireCommand(conn, cmd) stringResult, ok := result.(string) if ok && testutils.IsJSONResponse(stringResult) { - testifyAssert.JSONEq(t, tc.expected[i].(string), stringResult) + assert.JSONEq(t, tc.expected[i].(string), stringResult) } else { assert.Equal(t, tc.expected[i], result) } @@ -735,7 +734,7 @@ func TestJsonStrlen(t *testing.T) { if ok { assert.Equal(t, tc.expected[i], stringResult) } else { - assert.Assert(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{}))) } } }) @@ -795,7 +794,7 @@ func TestJSONMGET(t *testing.T) { assert.Equal(t, len(tc.expected), len(results)) for i := range results { if testutils.IsJSONResponse(tc.expected[i].(string)) { - testifyAssert.JSONEq(t, tc.expected[i].(string), results[i].(string)) + assert.JSONEq(t, tc.expected[i].(string), results[i].(string)) } else { assert.Equal(t, tc.expected[i], results[i]) } @@ -813,7 +812,7 @@ func testJSONMGETRecursive(conn net.Conn) func(*testing.T) { return func(t *testing.T) { result := FireCommand(conn, "JSON.MGET doc1 doc2 $..a") results, ok := result.([]interface{}) - assert.Assert(t, ok, "Expected result to be a slice of interface{}") + assert.True(t, ok, "Expected result to be a slice of interface{}") assert.Equal(t, 2, len(results), "Expected 2 results") expectedSets := [][]int{ @@ -824,13 +823,13 @@ func testJSONMGETRecursive(conn net.Conn) func(*testing.T) { for i, res := range results { var actualSet []int err := sonic.UnmarshalString(res.(string), &actualSet) - assert.NilError(t, err, "Failed to unmarshal JSON") + assert.Nil(t, err, "Failed to unmarshal JSON") - assert.Assert(t, len(actualSet) == len(expectedSets[i]), + assert.True(t, len(actualSet) == len(expectedSets[i]), "Mismatch in number of elements for set %d", i) for _, expected := range expectedSets[i] { - assert.Assert(t, sliceContainsItem(actualSet, expected), + assert.True(t, sliceContainsItem(actualSet, expected), "Set %d does not contain expected value %d", i, expected) } } @@ -897,7 +896,7 @@ func TestJsonARRAPPEND(t *testing.T) { if tcase.assertType[i] == "equal" { assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } } }) @@ -975,7 +974,7 @@ func TestJsonNummultby(t *testing.T) { if tcase.assertType[i] == "equal" { assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(deStringify(out.(string)), deStringify(result.(string)))) + assert.True(t, arraysArePermutations(deStringify(out.(string)), deStringify(result.(string)))) } } }) @@ -1095,7 +1094,8 @@ func TestJsonObjLen(t *testing.T) { cmd := tcase.commands[i] out := tcase.expected[i] result := FireCommand(conn, cmd) - assert.DeepEqual(t, out, result) + + assert.Equal(t, out, result); } }) } @@ -1187,11 +1187,11 @@ func TestJSONNumIncrBy(t *testing.T) { case "equal": assert.Equal(t, out, result) case "perm_equal": - assert.Assert(t, arraysArePermutations(convertToArray(out.(string)), convertToArray(result.(string)))) + assert.True(t, arraysArePermutations(convertToArray(out.(string)), convertToArray(result.(string)))) case "range": - assert.Assert(t, result.(int64) <= tc.expected[i].(int64) && result.(int64) > 0, "Expected %v to be within 0 to %v", result, tc.expected[i]) + assert.True(t, result.(int64) <= tc.expected[i].(int64) && result.(int64) > 0, "Expected %v to be within 0 to %v", result, tc.expected[i]) case "json_equal": - testifyAssert.JSONEq(t, out.(string), result.(string)) + assert.JSONEq(t, out.(string), result.(string)) } } for i := 0; i < len(tc.cleanUp); i++ { @@ -1256,7 +1256,7 @@ func TestJsonSTRAPPEND(t *testing.T) { assert.Equal(t, "OK", result) result = FireCommand(conn, tc.getCmd) - assert.DeepEqual(t, tc.expected, result) + assert.ElementsMatch(t, tc.expected, result) }) } diff --git a/integration_tests/commands/async/jsonresp_test.go b/integration_tests/commands/async/jsonresp_test.go index 2a1ee58d4..82d72c216 100644 --- a/integration_tests/commands/async/jsonresp_test.go +++ b/integration_tests/commands/async/jsonresp_test.go @@ -3,8 +3,7 @@ package async import ( "testing" - testifyAssert "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestJSONRESP(t *testing.T) { @@ -52,9 +51,9 @@ func TestJSONRESP(t *testing.T) { result := FireCommand(conn, cmd) if tcase.assert_type[i] == "equal" { - testifyAssert.Equal(t, out, result) + assert.Equal(t, out, result) } else if tcase.assert_type[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } } }) diff --git a/integration_tests/commands/async/keys_test.go b/integration_tests/commands/async/keys_test.go index 654a4942b..9cd90f504 100644 --- a/integration_tests/commands/async/keys_test.go +++ b/integration_tests/commands/async/keys_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestKeys(t *testing.T) { @@ -54,9 +54,9 @@ func TestKeys(t *testing.T) { // because the order of keys is not guaranteed, we need to check if the result is an array if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } diff --git a/integration_tests/commands/async/mget_test.go b/integration_tests/commands/async/mget_test.go index 56a9ba2ee..715c372ee 100644 --- a/integration_tests/commands/async/mget_test.go +++ b/integration_tests/commands/async/mget_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestMGET(t *testing.T) { @@ -13,7 +13,7 @@ func TestMGET(t *testing.T) { defer FireCommand(conn, "DEL k1") defer FireCommand(conn, "DEL k2") - + testCases := []struct { name string commands []string @@ -49,9 +49,9 @@ func TestMGET(t *testing.T) { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/async/mset_test.go b/integration_tests/commands/async/mset_test.go index 1b1413b53..d0d8ba647 100644 --- a/integration_tests/commands/async/mset_test.go +++ b/integration_tests/commands/async/mset_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestMset(t *testing.T) { @@ -40,7 +40,7 @@ func TestMset(t *testing.T) { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } @@ -90,7 +90,7 @@ func TestMSETInconsistency(t *testing.T) { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/async/object_test.go b/integration_tests/commands/async/object_test.go index dd44dd032..93329411d 100644 --- a/integration_tests/commands/async/object_test.go +++ b/integration_tests/commands/async/object_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestObjectCommand(t *testing.T) { @@ -126,9 +126,9 @@ func TestObjectCommand(t *testing.T) { fmt.Println(cmd, result, tc.expected[i]) if tc.assertType[i] == "equal" { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } else { - assert.Assert(t, result.(int64) >= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) + assert.True(t, result.(int64) >= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) } } for _, cmd := range tc.cleanup { // run cleanup diff --git a/integration_tests/commands/async/qunwatch_test.go b/integration_tests/commands/async/qunwatch_test.go index b425b7f4f..536875d56 100644 --- a/integration_tests/commands/async/qunwatch_test.go +++ b/integration_tests/commands/async/qunwatch_test.go @@ -8,7 +8,7 @@ import ( "github.com/dicedb/dice/internal/clientio" "github.com/dicedb/dice/internal/sql" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) // need to test the following here: @@ -40,7 +40,7 @@ func TestQWatchUnwatch(t *testing.T) { // Check if the response is OK resp, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, 3, len(resp.([]interface{}))) } @@ -51,7 +51,7 @@ func TestQWatchUnwatch(t *testing.T) { for _, sub := range subscribers[0:2] { rp := fireCommandAndGetRESPParser(sub, "Q.UNWATCH \""+qWatchQuery+"\"") resp, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, "OK", resp) } @@ -60,21 +60,21 @@ func TestQWatchUnwatch(t *testing.T) { // continue from the qwatch scenarios that ran previously FireCommand(publisher, "SET match:100:user:1 62") resp, err := respParsers[2].DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) expectedUpdate := []interface{}{[]interface{}{"match:100:user:5", int64(70)}, []interface{}{"match:100:user:1", int64(62)}, []interface{}{"match:100:user:0", int64(60)}} - assert.DeepEqual(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp) + assert.Equal(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp) FireCommand(publisher, "SET match:100:user:5 75") resp, err = respParsers[2].DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) expectedUpdate = []interface{}{[]interface{}{"match:100:user:5", int64(75)}, []interface{}{"match:100:user:1", int64(62)}, []interface{}{"match:100:user:0", int64(60)}} - assert.DeepEqual(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp) + assert.Equal(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp) FireCommand(publisher, "SET match:100:user:0 80") resp, err = respParsers[2].DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) expectedUpdate = []interface{}{[]interface{}{"match:100:user:0", int64(80)}, []interface{}{"match:100:user:5", int64(75)}, []interface{}{"match:100:user:1", int64(62)}} - assert.DeepEqual(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp) + assert.Equal(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp) // Cleanup store for next tests for _, tc := range qWatchTestCases { diff --git a/integration_tests/commands/async/qwatch_test.go b/integration_tests/commands/async/qwatch_test.go index 119eb9019..87e08cfd3 100644 --- a/integration_tests/commands/async/qwatch_test.go +++ b/integration_tests/commands/async/qwatch_test.go @@ -11,7 +11,7 @@ import ( "github.com/dicedb/dice/internal/clientio" "github.com/dicedb/dice/internal/sql" dicedb "github.com/dicedb/dicedb-go" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) type qWatchTestCase struct { @@ -142,11 +142,11 @@ func subscribeToQWATCH(t *testing.T, subscribers []net.Conn, query string) []*cl respParsers := make([]*clientio.RESPParser, len(subscribers)) for i, subscriber := range subscribers { rp := fireCommandAndGetRESPParser(subscriber, fmt.Sprintf("Q.WATCH \"%s\"", query)) - assert.Assert(t, rp != nil) + assert.True(t, rp != nil) respParsers[i] = rp v, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) castedValue, ok := v.([]interface{}) if !ok { t.Errorf("Type assertion to []interface{} failed for value: %v", v) @@ -164,9 +164,9 @@ func subscribeToQWATCHWithSDK(t *testing.T, subscribers []qWatchSDKSubscriber) [ for i, subscriber := range subscribers { qwatch := subscriber.client.QWatch(ctx) subscribers[i].qwatch = qwatch - assert.Assert(t, qwatch != nil) + assert.True(t, qwatch != nil) err := qwatch.WatchQuery(ctx, qWatchQuery) - assert.NilError(t, err) + assert.Nil(t, err) channels[i] = qwatch.Channel() <-channels[i] // Get the first message } @@ -188,7 +188,7 @@ func publishUpdate(t *testing.T, publisher interface{}, tc qWatchTestCase) { FireCommand(p, fmt.Sprintf("SET %s %d", key, tc.score)) case *dicedb.Client: err := p.Set(context.Background(), key, tc.score, 0).Err() - assert.NilError(t, err) + assert.Nil(t, err) } } @@ -206,13 +206,13 @@ func verifyUpdates(t *testing.T, receivers interface{}, expectedUpdates [][]inte func verifyRESPUpdates(t *testing.T, respParsers []*clientio.RESPParser, expectedUpdate []interface{}, query string) { for _, rp := range respParsers { v, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) update, ok := v.([]interface{}) if !ok { t.Errorf("Type assertion to []interface{} failed for value: %v", v) return } - assert.DeepEqual(t, []interface{}{sql.Qwatch, query, expectedUpdate}, update) + assert.Equal(t, []interface{}{sql.Qwatch, query, expectedUpdate}, update) } } @@ -221,7 +221,7 @@ func verifySDKUpdates(t *testing.T, channels []<-chan *dicedb.QMessage, expected v := <-ch assert.Equal(t, len(v.Updates), len(expectedUpdate), v.Updates) for i, update := range v.Updates { - assert.DeepEqual(t, expectedUpdate[i], []interface{}{update.Key, update.Value}) + assert.Equal(t, expectedUpdate[i], []interface{}{update.Key, update.Value}) } } } @@ -339,11 +339,11 @@ func subscribeToJSONQueries(t *testing.T, subscribers []net.Conn, tests []JSONTe respParsers := make([]*clientio.RESPParser, len(subscribers)) for i, testCase := range tests { rp := fireCommandAndGetRESPParser(subscribers[i], fmt.Sprintf("Q.WATCH \"%s\"", testCase.qwatchQuery)) - assert.Assert(t, rp != nil) + assert.True(t, rp != nil) respParsers[i] = rp v, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, 3, len(v.([]interface{})), fmt.Sprintf("Expected 3 elements, got %v", v)) } return respParsers @@ -359,7 +359,7 @@ func runJSONScenarios(t *testing.T, publisher net.Conn, respParsers []*clientio. func verifyJSONUpdates(t *testing.T, rp *clientio.RESPParser, tc JSONTestCase) { for _, expectedUpdate := range tc.expectedUpdates { v, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) response, ok := v.([]interface{}) if !ok { t.Errorf("Type assertion to []interface{} failed for value: %v", v) @@ -377,9 +377,9 @@ func verifyJSONUpdates(t *testing.T, rp *clientio.RESPParser, tc JSONTestCase) { assert.Equal(t, expectedUpdate[0].([]interface{})[0], update[0].([]interface{})[0], "Key mismatch") var expectedJSON, actualJSON interface{} - assert.NilError(t, sonic.UnmarshalString(tc.value, &expectedJSON)) - assert.NilError(t, sonic.UnmarshalString(update[0].([]interface{})[1].(string), &actualJSON)) - assert.DeepEqual(t, expectedJSON, actualJSON) + assert.Nil(t, sonic.UnmarshalString(tc.value, &expectedJSON)) + assert.Nil(t, sonic.UnmarshalString(update[0].([]interface{})[1].(string), &actualJSON)) + assert.Equal(t, expectedJSON, actualJSON) } } @@ -418,10 +418,10 @@ func setupJSONOrderByTest(t *testing.T) (net.Conn, net.Conn, func(), string) { func subscribeToJSONOrderByQuery(t *testing.T, subscriber net.Conn, watchquery string) *clientio.RESPParser { rp := fireCommandAndGetRESPParser(subscriber, fmt.Sprintf("Q.WATCH \"%s\"", watchquery)) - assert.Assert(t, rp != nil) + assert.True(t, rp != nil) v, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, 3, len(v.([]interface{})), fmt.Sprintf("Expected 3 elements, got %v", v)) return rp @@ -483,11 +483,11 @@ func verifyJSONOrderByUpdates(t *testing.T, rp *clientio.RESPParser, tc struct { // Decode the response v, err := rp.DecodeOne() - assert.NilError(t, err, "Failed to decode response") + assert.Nil(t, err, "Failed to decode response") // Cast the response to []interface{} response, ok := v.([]interface{}) - assert.Assert(t, ok, "Response is not of type []interface{}: %v", v) + assert.True(t, ok, "Response is not of type []interface{}: %v", v) // Verify response structure assert.Equal(t, 3, len(response), "Expected response to have 3 elements") @@ -495,7 +495,7 @@ func verifyJSONOrderByUpdates(t *testing.T, rp *clientio.RESPParser, tc struct { // Extract updates from the response updates, ok := response[2].([]interface{}) - assert.Assert(t, ok, "Updates are not of type []interface{}: %v", response[2]) + assert.True(t, ok, "Updates are not of type []interface{}: %v", response[2]) // Verify number of updates assert.Equal(t, len(expectedUpdates), len(updates), @@ -504,7 +504,7 @@ func verifyJSONOrderByUpdates(t *testing.T, rp *clientio.RESPParser, tc struct { // Verify each update for i, expectedRow := range expectedUpdates { actualRow, ok := updates[i].([]interface{}) - assert.Assert(t, ok, "Update row is not of type []interface{}: %v", updates[i]) + assert.True(t, ok, "Update row is not of type []interface{}: %v", updates[i]) // Verify key assert.Equal(t, expectedRow.([]interface{})[0], actualRow[0], @@ -513,9 +513,9 @@ func verifyJSONOrderByUpdates(t *testing.T, rp *clientio.RESPParser, tc struct { // Verify JSON value var actualJSON interface{} err := sonic.UnmarshalString(actualRow[1].(string), &actualJSON) - assert.NilError(t, err, "Failed to unmarshal JSON at index %d", i) + assert.Nil(t, err, "Failed to unmarshal JSON at index %d", i) - assert.DeepEqual(t, expectedRow.([]interface{})[1], actualJSON) + assert.Equal(t, expectedRow.([]interface{})[1], actualJSON) } } diff --git a/integration_tests/commands/async/select_test.go b/integration_tests/commands/async/select_test.go index ff7beb4ed..e016339f9 100644 --- a/integration_tests/commands/async/select_test.go +++ b/integration_tests/commands/async/select_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestSelect(t *testing.T) { @@ -12,11 +12,11 @@ func TestSelect(t *testing.T) { t.Run("SELECT command response", func(t *testing.T) { actual := FireCommand(conn, "SELECT 1") - assert.DeepEqual(t, "OK", actual) + assert.Equal(t, "OK", actual) }) t.Run("SELECT command error response", func(t *testing.T) { actual := FireCommand(conn, "SELECT") - assert.DeepEqual(t, "ERR wrong number of arguments for 'select' command", actual) + assert.Equal(t, "ERR wrong number of arguments for 'select' command", actual) }) } diff --git a/integration_tests/commands/async/set_data_cmd_test.go b/integration_tests/commands/async/set_data_cmd_test.go index d809f5973..ffac6fe07 100644 --- a/integration_tests/commands/async/set_data_cmd_test.go +++ b/integration_tests/commands/async/set_data_cmd_test.go @@ -5,12 +5,12 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func CustomDeepEqual(t *testing.T, a, b interface{}) { if a == nil || b == nil { - assert.DeepEqual(t, a, b) + assert.Equal(t, a, b) } switch a.(type) { @@ -23,7 +23,7 @@ func CustomDeepEqual(t *testing.T, a, b interface{}) { }) } - assert.DeepEqual(t, a, b) + assert.Equal(t, a, b) } func TestSetDataCommand(t *testing.T) { conn := getLocalConnection() @@ -231,7 +231,7 @@ func TestSetDataCommand(t *testing.T) { if tc.assertType[i] == "equal" { CustomDeepEqual(t, result, tc.expected[i]) } else if tc.assertType[i] == "assert" { - assert.Assert(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) + assert.True(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) } } }) diff --git a/integration_tests/commands/async/set_test.go b/integration_tests/commands/async/set_test.go index 18476a3de..fe9710e42 100644 --- a/integration_tests/commands/async/set_test.go +++ b/integration_tests/commands/async/set_test.go @@ -169,12 +169,13 @@ func TestSetWithExat(t *testing.T) { func TestWithKeepTTLFlag(t *testing.T) { conn := getLocalConnection() + expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10) defer conn.Close() for _, tcase := range []TestCase{ { - commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk"}, - expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv"}, + commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk", "SET K V EX 2 KEEPTTL", "SET K1 vv PX 2000 KEEPTTL", "SET K2 vv EXAT " + expiryTime + " KEEPTTL"}, + expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv", "ERR syntax error", "ERR syntax error", "ERR syntax error"}, }, } { for i := 0; i < len(tcase.commands); i++ { diff --git a/integration_tests/commands/async/toggle_test.go b/integration_tests/commands/async/toggle_test.go index 3e32663f6..c7846f211 100644 --- a/integration_tests/commands/async/toggle_test.go +++ b/integration_tests/commands/async/toggle_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func compareJSON(t *testing.T, expected, actual string) { @@ -16,10 +16,10 @@ func compareJSON(t *testing.T, expected, actual string) { err1 := json.Unmarshal([]byte(expected), &expectedMap) err2 := json.Unmarshal([]byte(actual), &actualMap) - assert.NilError(t, err1) - assert.NilError(t, err2) + assert.Nil(t, err1) + assert.Nil(t, err2) - assert.DeepEqual(t, expectedMap, actualMap) + assert.Equal(t, expectedMap, actualMap) } func TestJSONToggle(t *testing.T) { @@ -84,9 +84,9 @@ func TestJSONToggle(t *testing.T) { assert.Equal(t, expected, result) } case []interface{}: - assert.Assert(t, testutils.UnorderedEqual(expected, result)) + assert.True(t, testutils.UnorderedEqual(expected, result)) default: - assert.DeepEqual(t, expected, result) + assert.Equal(t, expected, result) } } }) diff --git a/integration_tests/commands/async/touch_test.go b/integration_tests/commands/async/touch_test.go index bf51f5088..5efe3f259 100644 --- a/integration_tests/commands/async/touch_test.go +++ b/integration_tests/commands/async/touch_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestTouch(t *testing.T) { @@ -52,9 +52,9 @@ func TestTouch(t *testing.T) { } result := FireCommand(conn, cmd) if tc.assertType[i] == "equal" { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } else { - assert.Assert(t, result.(int64) >= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) + assert.True(t, result.(int64) >= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) } } }) diff --git a/integration_tests/commands/async/ttl_pttl_test.go b/integration_tests/commands/async/ttl_pttl_test.go index b3f74f0bd..ad5db6e5c 100644 --- a/integration_tests/commands/async/ttl_pttl_test.go +++ b/integration_tests/commands/async/ttl_pttl_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestTTLPTTL(t *testing.T) { @@ -12,8 +12,8 @@ func TestTTLPTTL(t *testing.T) { defer conn.Close() testCases := []struct { - name string - commands []string + name string + commands []string expected []interface{} assertType []string delay []time.Duration @@ -72,9 +72,9 @@ func TestTTLPTTL(t *testing.T) { } result := FireCommand(conn, cmd) if tc.assertType[i] == "equal" { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } else if tc.assertType[i] == "assert" { - assert.Assert(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) + assert.True(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) } } }) diff --git a/integration_tests/commands/async/type_test.go b/integration_tests/commands/async/type_test.go index 5f8bcbb00..4ba168cc9 100644 --- a/integration_tests/commands/async/type_test.go +++ b/integration_tests/commands/async/type_test.go @@ -3,7 +3,7 @@ package async import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestType(t *testing.T) { diff --git a/integration_tests/commands/http/append_test.go b/integration_tests/commands/http/append_test.go index fc2473055..8a1236be6 100644 --- a/integration_tests/commands/http/append_test.go +++ b/integration_tests/commands/http/append_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestAPPEND(t *testing.T) { @@ -96,7 +96,7 @@ func TestAPPEND(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - testifyAssert.Equal(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } exec.FireCommand(tc.cleanup[0]) }) diff --git a/integration_tests/commands/http/bit_test.go b/integration_tests/commands/http/bit_test.go new file mode 100644 index 000000000..783ec4249 --- /dev/null +++ b/integration_tests/commands/http/bit_test.go @@ -0,0 +1,104 @@ +package http + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestBitPos(t *testing.T) { + exec := NewHTTPCommandExecutor() + + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + defer exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) // clean up after all test cases + testcases := []struct { + name string + val interface{} + inCmd HTTPCommand + out interface{} + setCmdSETBIT bool + }{ + { + name: "FindsFirstZeroBit", + val: []byte("\xff\xf0\x00"), + inCmd: HTTPCommand{Command: "BITPOS", Body: map[string]interface{}{"key": "testkey", "value": 0}}, + out: float64(12), + }, + { + name: "FindsFirstOneBit", + val: []byte("\x00\x0f\xff"), + inCmd: HTTPCommand{Command: "BITPOS", Body: map[string]interface{}{"key": "testkey", "value": 1}}, + out: float64(12), + }, + { + name: "NoZeroBitFound", + val: []byte("\xff\xff\xff"), + inCmd: HTTPCommand{Command: "BITPOS", Body: map[string]interface{}{"key": "testkey", "value": 0}}, + out: float64(24), + }, + { + name: "NoZeroBitFoundWithRangeStartPos", + val: []byte("\xff\xff\xff"), + inCmd: HTTPCommand{Command: "BITPOS", Body: map[string]interface{}{"key": "testkey", "values": []interface{}{0, 2}}}, + out: float64(24), + }, + { + name: "NoZeroBitFoundWithOOBRangeStartPos", + val: []byte("\xff\xff\xff"), + inCmd: HTTPCommand{Command: "BITPOS", Body: map[string]interface{}{"key": "testkey", "values": []interface{}{0, 4}}}, + out: float64(-1), + }, + { + name: "NoZeroBitFoundWithRange", + val: []byte("\xff\xff\xff"), + inCmd: HTTPCommand{Command: "BITPOS", Body: map[string]interface{}{"key": "testkey", "values": []interface{}{0, 2, 2}}}, + out: float64(-1), + }, + { + name: "NoZeroBitFoundWithRangeAndRangeType", + val: []byte("\xff\xff\xff"), + inCmd: HTTPCommand{Command: "BITPOS", Body: map[string]interface{}{"key": "testkey", "values": []interface{}{0, 2, 2, "BIT"}}}, + out: float64(-1), + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + var setCmd HTTPCommand + if tc.setCmdSETBIT { + setCmd = HTTPCommand{ + Command: "SETBIT", + Body: map[string]interface{}{"key": "testkeysb", "value": fmt.Sprintf("%s", tc.val.(string))}, + } + } else { + switch v := tc.val.(type) { + case []byte: + setCmd = HTTPCommand{ + Command: "SET", + Body: map[string]interface{}{"key": "testkey", "value": v, "isByteEncodedVal": true}, + } + case string: + setCmd = HTTPCommand{ + Command: "SET", + Body: map[string]interface{}{"key": "testkey", "value": fmt.Sprintf("%s", v)}, + } + case int: + setCmd = HTTPCommand{ + Command: "SET", + Body: map[string]interface{}{"key": "testkey", "value": fmt.Sprintf("%d", v)}, + } + default: + // For test cases where we don't set a value (e.g., error cases) + setCmd = HTTPCommand{Command: ""} + } + } + + if setCmd.Command != "" { + _, _ = exec.FireCommand(setCmd) + } + + result, _ := exec.FireCommand(tc.inCmd) + assert.Equal(t, tc.out, result, "Mismatch for cmd %s\n", tc.inCmd) + }) + } +} diff --git a/integration_tests/commands/http/bloom_test.go b/integration_tests/commands/http/bloom_test.go index ef2fff8e0..f0cae9a12 100644 --- a/integration_tests/commands/http/bloom_test.go +++ b/integration_tests/commands/http/bloom_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - assert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestBloomFilter(t *testing.T) { @@ -27,7 +27,7 @@ func TestBloomFilter(t *testing.T) { Body: map[string]interface{}{"key": "bf", "value": "item1"}, }, }, - expected: []interface{}{"OK", "1", "1"}, + expected: []interface{}{"OK", float64(1), float64(1)}, }, { name: "BF.EXISTS returns false for non-existing item", @@ -41,7 +41,7 @@ func TestBloomFilter(t *testing.T) { Body: map[string]interface{}{"key": "bf", "value": "item2"}, }, }, - expected: []interface{}{"OK", "0"}, + expected: []interface{}{"OK", float64(0)}, }, { name: "BF.INFO provides correct information", @@ -61,7 +61,7 @@ func TestBloomFilter(t *testing.T) { }, expected: []interface{}{ "OK", - "1", + float64(1), []interface{}{ "Capacity", float64(1000), "Size", float64(10104), @@ -179,7 +179,7 @@ func TestBFEdgeCasesAndErrors(t *testing.T) { Body: map[string]interface{}{"key": "bf", "value": "item1"}, }, }, - expected: []interface{}{"1"}, + expected: []interface{}{float64(1)}, }, { name: "BF.EXISTS on an unreserved filter", @@ -189,7 +189,7 @@ func TestBFEdgeCasesAndErrors(t *testing.T) { Body: map[string]interface{}{"key": "bf", "value": "item1"}, }, }, - expected: []interface{}{"0"}, + expected: []interface{}{float64(0)}, }, { name: "BF.INFO on a non-existent filter", @@ -249,7 +249,7 @@ func TestBFEdgeCasesAndErrors(t *testing.T) { Body: map[string]interface{}{"key": "bf", "value": "item3"}, }, }, - expected: []interface{}{"OK", "1", "1", "1", "1", "0"}, + expected: []interface{}{"OK", float64(1), float64(1), float64(1), float64(1), float64(0)}, }, { name: "BF.EXISTS after BF.ADD returns false on non-existing item", @@ -267,7 +267,7 @@ func TestBFEdgeCasesAndErrors(t *testing.T) { Body: map[string]interface{}{"key": "bf", "value": "item2"}, }, }, - expected: []interface{}{"OK", "1", "0"}, + expected: []interface{}{"OK", float64(1), float64(0)}, }, { name: "BF.RESERVE with duplicate filter name", @@ -305,8 +305,8 @@ func TestBFEdgeCasesAndErrors(t *testing.T) { }, expected: []interface{}{ "OK", - "1", - "1", + float64(1), + float64(1), []interface{}{ "Capacity", float64(1000), "Size", float64(10104), diff --git a/integration_tests/commands/http/check_type_test.go b/integration_tests/commands/http/check_type_test.go index 7353b36cd..81127f284 100644 --- a/integration_tests/commands/http/check_type_test.go +++ b/integration_tests/commands/http/check_type_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) // This file may contain test cases for checking error messages across all commands diff --git a/integration_tests/commands/http/command_count_test.go b/integration_tests/commands/http/command_count_test.go index 265d3a7be..dd9e55338 100644 --- a/integration_tests/commands/http/command_count_test.go +++ b/integration_tests/commands/http/command_count_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandCount(t *testing.T) { @@ -39,9 +39,9 @@ func TestCommandCount(t *testing.T) { result, _ := exec.FireCommand(cmd) switch tc.assertType[c] { case "equal": - assert.DeepEqual(t, tc.expected[c], result) + assert.Equal(t, tc.expected[c], result) case "greater": - assert.Assert(t, result.(float64) >= tc.expected[c].(float64)) + assert.True(t, result.(float64) >= tc.expected[c].(float64)) } } diff --git a/integration_tests/commands/http/command_default_test.go b/integration_tests/commands/http/command_default_test.go index e3d2c8dfe..c4a67a8c7 100644 --- a/integration_tests/commands/http/command_default_test.go +++ b/integration_tests/commands/http/command_default_test.go @@ -5,19 +5,19 @@ import ( "testing" "github.com/dicedb/dice/internal/eval" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandDefault(t *testing.T) { exec := NewHTTPCommandExecutor() commands := getCommandDefault(exec) t.Run("Command should not be empty", func(t *testing.T) { - assert.Assert(t, len(commands) > 0, + assert.True(t, len(commands) > 0, fmt.Sprintf("Unexpected number of CLI commands found. expected greater than 0, %d found", len(commands))) }) t.Run("Command count matches", func(t *testing.T) { - assert.Assert(t, len(commands) == len(eval.DiceCmds), + assert.True(t, len(commands) == len(eval.DiceCmds), fmt.Sprintf("Unexpected number of CLI commands found. expected %d, %d found", len(eval.DiceCmds), len(commands))) }) } diff --git a/integration_tests/commands/http/command_getkeys_test.go b/integration_tests/commands/http/command_getkeys_test.go index f2cca2161..906117296 100644 --- a/integration_tests/commands/http/command_getkeys_test.go +++ b/integration_tests/commands/http/command_getkeys_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandGetKeys(t *testing.T) { @@ -93,7 +93,7 @@ func TestCommandGetKeys(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/http/command_help_test.go b/integration_tests/commands/http/command_help_test.go index 79ac368e3..d39af3044 100644 --- a/integration_tests/commands/http/command_help_test.go +++ b/integration_tests/commands/http/command_help_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandHelp(t *testing.T) { @@ -23,7 +23,7 @@ func TestCommandHelp(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for _, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } diff --git a/integration_tests/commands/http/command_info_test.go b/integration_tests/commands/http/command_info_test.go index a0f3726a8..034258007 100644 --- a/integration_tests/commands/http/command_info_test.go +++ b/integration_tests/commands/http/command_info_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandInfo(t *testing.T) { @@ -64,7 +64,7 @@ func TestCommandInfo(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/http/command_list_test.go b/integration_tests/commands/http/command_list_test.go index 8740b3332..87ed4508f 100644 --- a/integration_tests/commands/http/command_list_test.go +++ b/integration_tests/commands/http/command_list_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandList(t *testing.T) { @@ -23,10 +23,8 @@ func TestCommandList(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for _, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.DeepEqual(t, tc.expected[i], result) - + assert.Equal(t, tc.expected[i], result) } - }) } } diff --git a/integration_tests/commands/http/command_rename_test.go b/integration_tests/commands/http/command_rename_test.go index 0bc9f3ddb..caef9ed8c 100644 --- a/integration_tests/commands/http/command_rename_test.go +++ b/integration_tests/commands/http/command_rename_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCommandRename(t *testing.T) { @@ -62,7 +62,7 @@ func TestCommandRename(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/http/copy_test.go b/integration_tests/commands/http/copy_test.go index 7e6d90850..51fdaa41a 100644 --- a/integration_tests/commands/http/copy_test.go +++ b/integration_tests/commands/http/copy_test.go @@ -5,7 +5,7 @@ import ( "github.com/dicedb/dice/testutils" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestCopy(t *testing.T) { @@ -110,20 +110,20 @@ func TestCopy(t *testing.T) { result, _ := exec.FireCommand(cmd) if result == nil { - testifyAssert.Equal(t, tc.expected[i], result, "Expected result to be nil for command %v", cmd) + assert.Equal(t, tc.expected[i], result, "Expected result to be nil for command %v", cmd) continue } if floatResult, ok := result.(float64); ok { - testifyAssert.Equal(t, tc.expected[i], floatResult, "Mismatch for command %v", cmd) + assert.Equal(t, tc.expected[i], floatResult, "Mismatch for command %v", cmd) continue } if resultStr, ok := result.(string); ok { if testutils.IsJSONResponse(resultStr) { - testifyAssert.JSONEq(t, tc.expected[i].(string), resultStr, "Mismatch in JSON response for command %v", cmd) + assert.JSONEq(t, tc.expected[i].(string), resultStr, "Mismatch in JSON response for command %v", cmd) } else { - testifyAssert.Equal(t, tc.expected[i], resultStr, "Mismatch for command %v", cmd) + assert.Equal(t, tc.expected[i], resultStr, "Mismatch for command %v", cmd) } } else { t.Fatalf("command %v returned unexpected type: %T", cmd, result) diff --git a/integration_tests/commands/http/countminsketch_test.go b/integration_tests/commands/http/countminsketch_test.go index 5f6ff09d4..bc051a147 100644 --- a/integration_tests/commands/http/countminsketch_test.go +++ b/integration_tests/commands/http/countminsketch_test.go @@ -4,7 +4,7 @@ import ( "log" "testing" - assert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestCMSInitByDim(t *testing.T) { diff --git a/integration_tests/commands/http/dbsize_test.go b/integration_tests/commands/http/dbsize_test.go new file mode 100644 index 000000000..a76929d66 --- /dev/null +++ b/integration_tests/commands/http/dbsize_test.go @@ -0,0 +1,73 @@ +package http + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDBSize(t *testing.T) { + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + testCases := []TestCase{ + { + name: "DBSIZE with 3 keys", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "k1", "value": "v1"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k2", "value": "v2"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k3", "value": "v3"}}, + {Command: "DBSIZE", Body: nil}, + }, + expected: []interface{}{"OK", "OK", "OK", float64(3)}, + }, + { + name: "DBSIZE with repetitive keys", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "k1", "value": "v1"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k2", "value": "v2"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k3", "value": "v3"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k1", "value": "v3"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k2", "value": "v22"}}, + {Command: "DBSIZE", Body: nil}, + }, + expected: []interface{}{"OK", "OK", "OK", "OK", "OK", float64(3)}, + }, + { + name: "DBSIZE with expired keys", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "k1", "value": "v1"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k2", "value": "v2"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k3", "value": "v3"}}, + {Command: "DBSIZE", Body: nil}, + {Command: "EXPIRE", Body: map[string]interface{}{"key": "k3", "seconds": 1}}, + {Command: "SLEEP", Body: map[string]interface{}{"key": 2}}, + {Command: "DBSIZE", Body: nil}, + }, + expected: []interface{}{"OK", "OK", "OK", float64(3), float64(1), "OK", float64(2)}, + }, + { + name: "DBSIZE after deleting a key", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "k1", "value": "v1"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k2", "value": "v2"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k3", "value": "v3"}}, + {Command: "DBSIZE", Body: nil}, + {Command: "DEL", Body: map[string]interface{}{"key": "k1"}}, + {Command: "DBSIZE", Body: nil}, + }, + expected: []interface{}{"OK", "OK", "OK", float64(3), float64(1), float64(2)}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k1", "k2", "k3"}}}) + + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } +} diff --git a/integration_tests/commands/http/decr_test.go b/integration_tests/commands/http/decr_test.go index 4b5892bd4..a41fc53d3 100644 --- a/integration_tests/commands/http/decr_test.go +++ b/integration_tests/commands/http/decr_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestDECR(t *testing.T) { exec := NewHTTPCommandExecutor() - + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"key1", "key2", "key3"}}}) testCases := []struct { @@ -60,7 +60,7 @@ func TestDECR(t *testing.T) { func TestDECRBY(t *testing.T) { exec := NewHTTPCommandExecutor() - + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"key1", "key2", "key3"}}}) testCases := []struct { diff --git a/integration_tests/commands/http/del_test.go b/integration_tests/commands/http/del_test.go new file mode 100644 index 000000000..c7f8d693c --- /dev/null +++ b/integration_tests/commands/http/del_test.go @@ -0,0 +1,60 @@ +package http + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestDel(t *testing.T) { + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + testCases := []TestCase{ + { + name: "DEL with set key", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "k1", "value": "v1"}}, + {Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k1"}}}, + {Command: "GET", Body: map[string]interface{}{"key": "k1"}}, + }, + expected: []interface{}{"OK", float64(1), nil}, + }, + { + name: "DEL with multiple keys", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "k1", "value": "v1"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k2", "value": "v2"}}, + {Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k1", "k2"}}}, + {Command: "GET", Body: map[string]interface{}{"key": "k1"}}, + {Command: "GET", Body: map[string]interface{}{"key": "k2"}}, + }, + expected: []interface{}{"OK", "OK", float64(2), nil, nil}, + }, + { + name: "DEL with key not set", + commands: []HTTPCommand{ + {Command: "GET", Body: map[string]interface{}{"key": "k3"}}, + {Command: "DEL", Body: map[string]interface{}{"key": "k3"}}, + }, + expected: []interface{}{nil, float64(0)}, + }, + { + name: "DEL with no keys or arguments", + commands: []HTTPCommand{ + {Command: "DEL", Body: map[string]interface{}{}}, + }, + expected: []interface{}{"Invalid HTTP request format"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k1", "k2", "k3"}}}) + + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } +} diff --git a/integration_tests/commands/http/deque_test.go b/integration_tests/commands/http/deque_test.go new file mode 100644 index 000000000..234d551f0 --- /dev/null +++ b/integration_tests/commands/http/deque_test.go @@ -0,0 +1,558 @@ +package http + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var deqRandGenerator *rand.Rand +var deqRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_!@#$%^&*()-=+[]\\;':,.<>/?~.|") + +var ( + deqNormalValues []string + deqEdgeValues []string +) + +func deqRandStr(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = deqRunes[deqRandGenerator.Intn(len(deqRunes))] + } + return string(b) +} + +func deqTestInit() { + randSeed := time.Now().UnixNano() + deqRandGenerator = rand.New(rand.NewSource(randSeed)) + fmt.Printf("rand seed: %v", randSeed) + deqNormalValues = []string{ + deqRandStr(10), // 6 bit string + deqRandStr(256), // 12 bit string + deqRandStr((1 << 13) - 1000), // 32 bit string + "28", // 7 bit uint + "2024", // + 13 bit int + "-2024", // - 13 bit int + "15384", // + 16 bit int + "-15384", // - 16 bit int + "4193301", // + 24 bit int + "-4193301", // - 24 bit int + "1073731765", // + 32 bit int + "-1073731765", // - 32 bit int + "4611686018427287903", // + 64 bit int + "-4611686018427287903", // - 64 bit int + } + deqEdgeValues = []string{ + deqRandStr(1), // min 6 bit string + deqRandStr((1 << 6) - 1), // max 6 bit string + deqRandStr(1 << 6), // min 12 bit string + deqRandStr((1 << 12) - 1), // max 12 bit string + deqRandStr(1 << 12), // min 32 bit string + // randStr((1 << 32) - 1), // max 32 bit string, maybe too huge to test. + + "0", // min 7 bit uint + "127", // max 7 bit uint + "-4096", // min 13 bit int + "4095", // max 13 bit int + "-32768", // min 16 bit int + "32767", // max 16 bit int + "-8388608", // min 24 bit int + "8388607", // max 24 bit int + "-2147483648", // min 32 bit int + "2147483647", // max 32 bit int + "-9223372036854775808", // min 64 bit int + "9223372036854775807", // max 64 bit int + } +} + +func TestLPush(t *testing.T) { + deqTestInit() + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + testCases := []TestCase{ + { + name: "LPUSH", + commands: []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v"}}}, + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v1", 1, "v2", 2}}}, + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{3, 3, 3, "v3", "v3", "v3"}}}, + }, + expected: []interface{}{float64(1), float64(5), float64(11)}, + }, + { + name: "LPUSH normal values", + commands: []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": deqNormalValues}}, + }, + expected: []interface{}{float64(25)}, + }, + { + name: "LPUSH edge values", + commands: []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": deqEdgeValues}}, + }, + expected: []interface{}{float64(42)}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k"}}}) +} + +func TestRPush(t *testing.T) { + deqTestInit() + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + testCases := []TestCase{ + { + name: "RPUSH", + commands: []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v"}}}, + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v1", 1, "v2", 2}}}, + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{3, 3, 3, "v3", "v3", "v3"}}}, + }, + expected: []interface{}{float64(1), float64(5), float64(11)}, + }, + { + name: "RPUSH normal values", + commands: []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": deqNormalValues}}, + }, + expected: []interface{}{float64(25)}, + }, + { + name: "RPUSH edge values", + commands: []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": deqEdgeValues}}, + }, + expected: []interface{}{float64(42)}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k"}}}) +} + +func TestLPushLPop(t *testing.T) { + deqTestInit() + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + getPops := func(values []string) []HTTPCommand { + pops := make([]HTTPCommand, len(values)+1) + for i := 0; i < len(values)+1; i++ { + pops[i] = HTTPCommand{ + Command: "LPOP", + Body: map[string]interface{}{"key": "k"}, + } + } + return pops + } + getPopExpects := func(values []string) []interface{} { + expects := make([]interface{}, len(values)) + for i := 0; i < len(values); i++ { + expects[i] = values[len(values)-1-i] + } + return expects + } + + testCases := []TestCase{ + { + name: "LPUSH LPOP", + commands: []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v1", 1}}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{float64(2), "1", "v1", nil}, + }, + { + name: "LPUSH LPOP normal values", + commands: append( + []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": deqNormalValues}}, + }, + getPops(deqNormalValues)..., + ), + expected: append( + append( + []interface{}{float64(14)}, + getPopExpects(deqNormalValues)...), + nil, + ), + }, + { + name: "LPUSH LPOP edge values", + commands: append( + []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": deqEdgeValues}}, + }, + getPops(deqEdgeValues)..., + ), + expected: append( + append( + []interface{}{float64(17)}, + getPopExpects(deqEdgeValues)...), + nil, + ), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k"}}}) +} + +func TestLPushRPop(t *testing.T) { + deqTestInit() + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + getPops := func(values []string) []HTTPCommand { + pops := make([]HTTPCommand, len(values)+1) + for i := 0; i < len(values)+1; i++ { + pops[i] = HTTPCommand{ + Command: "RPOP", + Body: map[string]interface{}{"key": "k"}, + } + } + return pops + } + getPopExpects := func(values []string) []interface{} { + expects := make([]interface{}, len(values)) + for i := 0; i < len(values); i++ { + expects[i] = values[i] + } + return expects + } + + testCases := []TestCase{ + { + name: "LPUSH RPOP", + commands: []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v1", 1}}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{float64(2), "v1", "1", nil}, + }, + { + name: "LPUSH RPOP normal values", + commands: append( + []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": deqNormalValues}}, + }, + getPops(deqNormalValues)..., + ), + expected: append( + append( + []interface{}{float64(14)}, + getPopExpects(deqNormalValues)..., + ), + nil, + ), + }, + { + name: "LPUSH RPOP edge values", + commands: append( + []HTTPCommand{ + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": deqEdgeValues}}, + }, + getPops(deqEdgeValues)..., + ), + expected: append( + append( + []interface{}{float64(17)}, + getPopExpects(deqEdgeValues)...), + nil, + ), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k"}}}) +} + +func TestRPushLPop(t *testing.T) { + deqTestInit() + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + getPops := func(values []string) []HTTPCommand { + pops := make([]HTTPCommand, len(values)+1) + for i := 0; i < len(values)+1; i++ { + pops[i] = HTTPCommand{ + Command: "LPOP", + Body: map[string]interface{}{"key": "k"}, + } + } + return pops + } + getPopExpects := func(values []string) []interface{} { + expects := make([]interface{}, len(values)) + for i := 0; i < len(values); i++ { + expects[i] = values[i] + } + return expects + } + + testCases := []TestCase{ + { + name: "RPUSH LPOP", + commands: []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v1", 1}}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{float64(2), "v1", "1", nil}, + }, + { + name: "RPUSH LPOP normal values", + commands: append( + []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": deqNormalValues}}, + }, + getPops(deqNormalValues)..., + ), + expected: append( + append( + []interface{}{float64(14)}, + getPopExpects(deqNormalValues)..., + ), + nil, + ), + }, + { + name: "RPUSH LPOP edge values", + commands: append( + []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": deqEdgeValues}}, + }, + getPops(deqEdgeValues)..., + ), + expected: append( + append( + []interface{}{float64(17)}, + getPopExpects(deqEdgeValues)...), + nil, + ), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k"}}}) +} + +func TestRPushRPop(t *testing.T) { + deqTestInit() + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + getPops := func(values []string) []HTTPCommand { + pops := make([]HTTPCommand, len(values)+1) + for i := 0; i < len(values)+1; i++ { + pops[i] = HTTPCommand{ + Command: "RPOP", + Body: map[string]interface{}{"key": "k"}, + } + } + return pops + } + getPopExpects := func(values []string) []interface{} { + expects := make([]interface{}, len(values)) + for i := 0; i < len(values); i++ { + expects[i] = values[len(values)-1-i] + } + return expects + } + + testCases := []TestCase{ + { + name: "RPUSH RPOP", + commands: []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v1", 1}}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{float64(2), "1", "v1", nil}, + }, + { + name: "RPUSH RPOP normal values", + commands: append( + []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": deqNormalValues}}, + }, + getPops(deqNormalValues)..., + ), + expected: append( + append( + []interface{}{float64(14)}, + getPopExpects(deqNormalValues)..., + ), + nil, + ), + }, + { + name: "RPUSH RPOP edge values", + commands: append( + []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": deqEdgeValues}}, + }, + getPops(deqEdgeValues)..., + ), + expected: append( + append( + []interface{}{float64(17)}, + getPopExpects(deqEdgeValues)...), + nil, + ), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k"}}}) +} + +func TestLRPushLRPop(t *testing.T) { + deqTestInit() + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + testCases := []TestCase{ + { + name: "L/RPush L/RPop", + commands: []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v1000", 1000}}}, + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v2000", 2000}}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v6"}}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{ + float64(2), float64(4), + "1000", "v1000", "2000", + float64(2), + "v2000", "v6", nil, nil, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k"}}}) +} + +func TestLLEN(t *testing.T) { + deqTestInit() + exec := NewHTTPCommandExecutor() + exec.FireCommand(HTTPCommand{Command: "FLUSHDB"}) + + testCases := []TestCase{ + { + name: "LLEN", + commands: []HTTPCommand{ + {Command: "RPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v1000", 1000}}}, + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v2000", 2000}}}, + {Command: "LLEN", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LLEN", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LLEN", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPUSH", Body: map[string]interface{}{"key": "k", "values": []interface{}{"v6"}}}, + {Command: "LLEN", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LLEN", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "RPOP", Body: map[string]interface{}{"key": "k"}}, + {Command: "LLEN", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []any{ + float64(2), float64(4), float64(4), + "1000", float64(3), "v1000", "2000", float64(1), + float64(2), float64(2), + "v2000", float64(1), "v6", nil, nil, float64(0), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": [...]string{"k"}}}) +} diff --git a/integration_tests/commands/http/echo_test.go b/integration_tests/commands/http/echo_test.go index 17af745b6..d2f8db08c 100644 --- a/integration_tests/commands/http/echo_test.go +++ b/integration_tests/commands/http/echo_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestEchoHttp(t *testing.T) { @@ -36,7 +36,7 @@ func TestEchoHttp(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/http/exists_test.go b/integration_tests/commands/http/exists_test.go index 52b1ab893..a7da3bd66 100644 --- a/integration_tests/commands/http/exists_test.go +++ b/integration_tests/commands/http/exists_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExistsHttp(t *testing.T) { @@ -86,7 +86,7 @@ func TestExistsHttp(t *testing.T) { if err != nil { log.Printf("Error executing command: %v", err) } - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/http/expire_test.go b/integration_tests/commands/http/expire_test.go index 4ba1d04e3..f785e89f8 100644 --- a/integration_tests/commands/http/expire_test.go +++ b/integration_tests/commands/http/expire_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExpireHttp(t *testing.T) { @@ -224,10 +224,10 @@ func TestExpireHttp(t *testing.T) { t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results)) } if expected == nil { - assert.Assert(t, results[i] == nil || results[i] == "", + assert.True(t, results[i] == nil || results[i] == "", "Expected nil or empty result, got %v", results[i]) } else { - assert.DeepEqual(t, expected, results[i]) + assert.Equal(t, expected, results[i]) } } }) diff --git a/integration_tests/commands/http/expireat_test.go b/integration_tests/commands/http/expireat_test.go index f33f8d6f2..371c233d4 100644 --- a/integration_tests/commands/http/expireat_test.go +++ b/integration_tests/commands/http/expireat_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExpireAtHttp(t *testing.T) { @@ -230,10 +230,10 @@ func TestExpireAtHttp(t *testing.T) { } if expected == nil { - assert.Assert(t, results[i] == nil || results[i] == "", + assert.True(t, results[i] == nil || results[i] == "", "Expected nil or empty result, got %v", results[i]) } else { - assert.DeepEqual(t, expected, results[i]) + assert.Equal(t, expected, results[i]) } } }) diff --git a/integration_tests/commands/http/expiretime_test.go b/integration_tests/commands/http/expiretime_test.go index df5cdad79..f3d4f0491 100644 --- a/integration_tests/commands/http/expiretime_test.go +++ b/integration_tests/commands/http/expiretime_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExpireTimeHttp(t *testing.T) { @@ -96,10 +96,10 @@ func TestExpireTimeHttp(t *testing.T) { } if expected == nil { - assert.Assert(t, results[i] == nil || results[i] == "", + assert.True(t, results[i] == nil || results[i] == "", "Expected nil or empty result, got %v", results[i]) } else { - assert.DeepEqual(t, expected, results[i]) + assert.Equal(t, expected, results[i]) } } }) diff --git a/integration_tests/commands/http/get_test.go b/integration_tests/commands/http/get_test.go index 962920bd4..392ea42c6 100644 --- a/integration_tests/commands/http/get_test.go +++ b/integration_tests/commands/http/get_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGet(t *testing.T) { diff --git a/integration_tests/commands/http/getdel_test.go b/integration_tests/commands/http/getdel_test.go index 28a9454a2..9233a0b90 100644 --- a/integration_tests/commands/http/getdel_test.go +++ b/integration_tests/commands/http/getdel_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGetDel(t *testing.T) { diff --git a/integration_tests/commands/http/getex_test.go b/integration_tests/commands/http/getex_test.go index 81f02c3d9..12fb9d03b 100644 --- a/integration_tests/commands/http/getex_test.go +++ b/integration_tests/commands/http/getex_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGetEx(t *testing.T) { @@ -259,11 +259,11 @@ func TestGetEx(t *testing.T) { time.Sleep(tc.delay[i]) } result, err := exec.FireCommand(cmd) - assert.NilError(t, err) + assert.Nil(t, err) if tc.assertType[i] == "equal" { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } else if tc.assertType[i] == "assert" { - assert.Assert(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) + assert.True(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) } } }) diff --git a/integration_tests/commands/http/getrange_test.go b/integration_tests/commands/http/getrange_test.go index 82f1c32a1..5de0e3444 100644 --- a/integration_tests/commands/http/getrange_test.go +++ b/integration_tests/commands/http/getrange_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestGETRANGE(t *testing.T) { @@ -75,7 +75,7 @@ func TestGETRANGE(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - testifyAssert.Equal(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } exec.FireCommand(tc.cleanup[0]) }) diff --git a/integration_tests/commands/http/getset_test.go b/integration_tests/commands/http/getset_test.go index b8bd21582..0072eee92 100644 --- a/integration_tests/commands/http/getset_test.go +++ b/integration_tests/commands/http/getset_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGetSet(t *testing.T) { diff --git a/integration_tests/commands/http/hexists_test.go b/integration_tests/commands/http/hexists_test.go new file mode 100644 index 000000000..2013810a1 --- /dev/null +++ b/integration_tests/commands/http/hexists_test.go @@ -0,0 +1,91 @@ +package http + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestHExists(t *testing.T) { + cmdExec := NewHTTPCommandExecutor() + + testCases := []struct { + name string + commands []HTTPCommand + expected []interface{} + delays []time.Duration + }{ + { + name: "HTTP Check if field exists when k f and v are set", + commands: []HTTPCommand{ + {Command: "HSET", Body: map[string]interface{}{"key": "k", "field": "f", "value": "v"}}, + {Command: "HEXISTS", Body: map[string]interface{}{"key": "k", "field": "f"}}, + }, + expected: []interface{}{float64(1), float64(1)}, + delays: []time.Duration{0, 0}, + }, + { + name: "HTTP Check if field exists when k exists but not f and v", + commands: []HTTPCommand{ + {Command: "HSET", Body: map[string]interface{}{"key": "k", "field": "f1", "value": "v"}}, + {Command: "HEXISTS", Body: map[string]interface{}{"key": "k", "field": "f"}}, + }, + expected: []interface{}{float64(1), float64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "HTTP Check if field exists when no k,f and v exist", + commands: []HTTPCommand{ + {Command: "HEXISTS", Body: map[string]interface{}{"key": "k", "field": "f"}}, + }, + expected: []interface{}{float64(0)}, + delays: []time.Duration{0}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f"}, + }) + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f1"}, + }) + cmdExec.FireCommand(HTTPCommand{ + Command: "DEL", + Body: map[string]interface{}{"key": "k"}, + }) + + for i, cmd := range tc.commands { + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + + result, err := cmdExec.FireCommand(cmd) + if err != nil { + // Check if the error message matches the expected result + assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd) + } else { + assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result) + } + } + }) + } + + // Deleting the used keys + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f"}, + }) + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f1"}, + }) + cmdExec.FireCommand(HTTPCommand{ + Command: "DEL", + Body: map[string]interface{}{"key": "k"}, + }) +} diff --git a/integration_tests/commands/http/hincrby_test.go b/integration_tests/commands/http/hincrby_test.go index 75c153c72..c01590762 100644 --- a/integration_tests/commands/http/hincrby_test.go +++ b/integration_tests/commands/http/hincrby_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHINCRBY(t *testing.T) { diff --git a/integration_tests/commands/http/hincrbyfloat_test.go b/integration_tests/commands/http/hincrbyfloat_test.go index 5cfb655e9..94b42e8e9 100644 --- a/integration_tests/commands/http/hincrbyfloat_test.go +++ b/integration_tests/commands/http/hincrbyfloat_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHINCRBYFLOAT(t *testing.T) { diff --git a/integration_tests/commands/http/hkeys_test.go b/integration_tests/commands/http/hkeys_test.go new file mode 100644 index 000000000..b93373669 --- /dev/null +++ b/integration_tests/commands/http/hkeys_test.go @@ -0,0 +1,62 @@ +package http + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHKeys(t *testing.T) { + cmdExec := NewHTTPCommandExecutor() + + testCases := []TestCase{ + { + name: "HTTP One or more keys exist", + commands: []HTTPCommand{ + {Command: "HSET", Body: map[string]interface{}{"key": "k", "field": "f", "value": "v"}}, + {Command: "HSET", Body: map[string]interface{}{"key": "k", "field": "f1", "value": "v1"}}, + {Command: "HKEYS", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{float64(1), float64(1), []interface{}{"f", "f1"}}, + }, + { + name: "HTTP No keys exist", + commands: []HTTPCommand{ + {Command: "HKEYS", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{nil}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f"}, + }) + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f1"}, + }) + + for i, cmd := range tc.commands { + result, _ := cmdExec.FireCommand(cmd) + switch e := tc.expected[i].(type) { + case []interface{}: + assert.ElementsMatch(t, e, tc.expected[i]) + default: + assert.Equal(t, tc.expected[i], result) + } + } + }) + } + + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f"}, + }) + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f1"}, + }) +} diff --git a/integration_tests/commands/http/hlen_test.go b/integration_tests/commands/http/hlen_test.go index 6abd1b1e4..91daef6f4 100644 --- a/integration_tests/commands/http/hlen_test.go +++ b/integration_tests/commands/http/hlen_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestHLen(t *testing.T) { @@ -34,7 +34,7 @@ func TestHLen(t *testing.T) { {Command: "HSET", Body: map[string]interface{}{"key": "key_hLen1", "field": "field", "value": "value"}}, {Command: "HLEN", Body: map[string]interface{}{"key": "wrong_key_hLen1"}}, }, - expected: []interface{}{float64(1), "0"}, + expected: []interface{}{float64(1), float64(0)}, delays: []time.Duration{0, 0}, }, { @@ -79,9 +79,9 @@ func TestHLen(t *testing.T) { if err != nil { // Check if the error message matches the expected result log.Println(tc.expected[i]) - testifyAssert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd) + assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd) } else { - testifyAssert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/http/hrandfield_test.go b/integration_tests/commands/http/hrandfield_test.go index b47d6447f..2067e9fe8 100644 --- a/integration_tests/commands/http/hrandfield_test.go +++ b/integration_tests/commands/http/hrandfield_test.go @@ -5,7 +5,7 @@ import ( "time" "github.com/google/go-cmp/cmp/cmpopts" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHRANDFIELD(t *testing.T) { @@ -86,7 +86,7 @@ func TestHRANDFIELD(t *testing.T) { if str, ok := result.(string); ok { assert.Equal(t, str, expected, "Unexpected result for command: %s", cmd) } else { - assert.DeepEqual(t, result, expected, cmpopts.EquateEmpty()) + assert.Equal(t, result, expected, cmpopts.EquateEmpty()) } } } @@ -126,7 +126,7 @@ func assertRandomFieldResult(t *testing.T, result interface{}, expected []string } // assert that all results are in the expected set or that there is a single valid result - assert.Assert(t, count == len(resultsList) || count == 1, + assert.True(t, count == len(resultsList) || count == 1, "Expected all results to be in the expected set or a single valid result. Got %d out of %d", count, len(resultsList)) } diff --git a/integration_tests/commands/http/hscan_test.go b/integration_tests/commands/http/hscan_test.go index 2af2f2398..d3c048210 100644 --- a/integration_tests/commands/http/hscan_test.go +++ b/integration_tests/commands/http/hscan_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestHScan(t *testing.T) { @@ -139,9 +139,9 @@ func TestHScan(t *testing.T) { if err != nil { // Check if the error message matches the expected result log.Println(tc.expected[i]) - testifyAssert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd) + assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd) } else { - testifyAssert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/http/hsetnx_test.go b/integration_tests/commands/http/hsetnx_test.go index f4925f26e..133ab883c 100644 --- a/integration_tests/commands/http/hsetnx_test.go +++ b/integration_tests/commands/http/hsetnx_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHSetNX(t *testing.T) { diff --git a/integration_tests/commands/http/hstrlen_test.go b/integration_tests/commands/http/hstrlen_test.go index bd6650119..7e4685463 100644 --- a/integration_tests/commands/http/hstrlen_test.go +++ b/integration_tests/commands/http/hstrlen_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHStrLen(t *testing.T) { @@ -34,7 +34,7 @@ func TestHStrLen(t *testing.T) { {Command: "HSET", Body: map[string]interface{}{"key": "key_hStrLen1", "field": "field", "value": "value"}}, {Command: "HSTRLEN", Body: map[string]interface{}{"key": "wrong_key_hStrLen", "field": "field"}}, }, - expected: []interface{}{float64(1), "0"}, + expected: []interface{}{float64(1), float64(0)}, delays: []time.Duration{0, 0}, }, { @@ -43,7 +43,7 @@ func TestHStrLen(t *testing.T) { {Command: "HSET", Body: map[string]interface{}{"key": "key_hStrLen2", "field": "field", "value": "value"}}, {Command: "HSTRLEN", Body: map[string]interface{}{"key": "key_hStrLen2", "field": "wrong_field"}}, }, - expected: []interface{}{float64(1), "0"}, + expected: []interface{}{float64(1), float64(0)}, delays: []time.Duration{0, 0}, }, { diff --git a/integration_tests/commands/http/hvals_test.go b/integration_tests/commands/http/hvals_test.go new file mode 100644 index 000000000..01195fcd1 --- /dev/null +++ b/integration_tests/commands/http/hvals_test.go @@ -0,0 +1,64 @@ +package http + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHVals(t *testing.T) { + cmdExec := NewHTTPCommandExecutor() + + testCases := []TestCase{ + { + name: "HTTP One or more keys exist", + commands: []HTTPCommand{ + {Command: "HSET", Body: map[string]interface{}{"key": "k", "field": "f", "value": "v"}}, + {Command: "HSET", Body: map[string]interface{}{"key": "k", "field": "f1", "value": "v1"}}, + {Command: "HVALS", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{float64(1), float64(1), []interface{}{"v", "v1"}}, + }, + { + name: "HTTP No keys exist", + commands: []HTTPCommand{ + {Command: "HVALS", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{[]interface{}{}}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f"}, + }) + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f1"}, + }) + + for i, cmd := range tc.commands { + result, _ := cmdExec.FireCommand(cmd) + fmt.Printf("%v | %v\n", result, tc.expected[i]) + switch e := tc.expected[i].(type) { + case []interface{}: + assert.ElementsMatch(t, e, tc.expected[i]) + default: + assert.Equal(t, tc.expected[i], result) + } + } + }) + } + + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f"}, + }) + cmdExec.FireCommand(HTTPCommand{ + Command: "HDEL", + Body: map[string]interface{}{"key": "k", "field": "f1"}, + }) +} diff --git a/integration_tests/commands/http/hyperloglog_test.go b/integration_tests/commands/http/hyperloglog_test.go index c2b9c4e40..70ac1e807 100644 --- a/integration_tests/commands/http/hyperloglog_test.go +++ b/integration_tests/commands/http/hyperloglog_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHyperLogLogCommands(t *testing.T) { diff --git a/integration_tests/commands/http/incr_by_float_test.go b/integration_tests/commands/http/incr_by_float_test.go index 0ad0054a6..d38c2fe0a 100644 --- a/integration_tests/commands/http/incr_by_float_test.go +++ b/integration_tests/commands/http/incr_by_float_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestINCRBYFLOAT(t *testing.T) { diff --git a/integration_tests/commands/http/incr_test.go b/integration_tests/commands/http/incr_test.go index 23da18c21..fa2aeaf3a 100644 --- a/integration_tests/commands/http/incr_test.go +++ b/integration_tests/commands/http/incr_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestINCR(t *testing.T) { diff --git a/integration_tests/commands/http/json_arrpop_test.go b/integration_tests/commands/http/json_arrpop_test.go index 4ea6cfe2e..4ee78384e 100644 --- a/integration_tests/commands/http/json_arrpop_test.go +++ b/integration_tests/commands/http/json_arrpop_test.go @@ -4,8 +4,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - testifyAssert "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestJSONARRPOP(t *testing.T) { @@ -169,14 +168,14 @@ func TestJSONARRPOP(t *testing.T) { jsonResult, isString := result.(string) if isString && testutils.IsJSONResponse(jsonResult) { - testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult) + assert.JSONEq(t, tc.expected[i].(string), jsonResult) continue } if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/http/json_test.go b/integration_tests/commands/http/json_test.go index 983a66ac2..9a8e8f4ae 100644 --- a/integration_tests/commands/http/json_test.go +++ b/integration_tests/commands/http/json_test.go @@ -9,9 +9,7 @@ import ( "github.com/bytedance/sonic" "github.com/dicedb/dice/testutils" "github.com/google/go-cmp/cmp/cmpopts" - testifyAssert "github.com/stretchr/testify/assert" - - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestJSONOperations(t *testing.T) { @@ -194,7 +192,7 @@ func TestJSONOperations(t *testing.T) { result, _ := exec.FireCommand(cmd) if jsonResult, ok := result.(string); ok && testutils.IsJSONResponse(jsonResult) { - testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult) + assert.JSONEq(t, tc.expected[i].(string), jsonResult) } else { assert.Equal(t, tc.expected[i], result) } @@ -212,9 +210,9 @@ func TestJSONOperations(t *testing.T) { if jsonResult, ok := result.(string); ok && testutils.IsJSONResponse(jsonResult) { var jsonPayload []interface{} json.Unmarshal([]byte(jsonResult), &jsonPayload) - assert.Assert(t, testutils.UnorderedEqual(tc.expected[i], jsonPayload)) + assert.True(t, testutils.UnorderedEqual(tc.expected[i], jsonPayload)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) @@ -253,7 +251,7 @@ func TestJSONSetWithInvalidCases(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.Check(t, strings.HasPrefix(result.(string), tc.expected[i].(string)), fmt.Sprintf("Expected: %s, Got: %s", tc.expected[i], result)) + assert.True(t, strings.HasPrefix(result.(string), tc.expected[i].(string)), fmt.Sprintf("Expected: %s, Got: %s", tc.expected[i], result)) } }) } @@ -321,7 +319,7 @@ func TestJSONSetWithNXAndXX(t *testing.T) { result, _ := exec.FireCommand(cmd) jsonResult, isString := result.(string) if isString && testutils.IsJSONResponse(jsonResult) { - testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult) + assert.JSONEq(t, tc.expected[i].(string), jsonResult) } else { assert.Equal(t, tc.expected[i], result) } @@ -511,7 +509,7 @@ func TestJSONDelOperations(t *testing.T) { result, _ := exec.FireCommand(cmd) jsonResult, isString := result.(string) if isString && testutils.IsJSONResponse(jsonResult) { - testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult) + assert.JSONEq(t, tc.expected[i].(string), jsonResult) } else { assert.Equal(t, tc.expected[i], result) } @@ -616,7 +614,7 @@ func TestJSONForgetOperations(t *testing.T) { result, _ := exec.FireCommand(cmd) jsonResult, isString := result.(string) if isString && testutils.IsJSONResponse(jsonResult) { - testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult) + assert.JSONEq(t, tc.expected[i].(string), jsonResult) } else { assert.Equal(t, tc.expected[i], result) } @@ -697,7 +695,7 @@ func TestJsonStrlen(t *testing.T) { if stringResult, ok := result.(string); ok { assert.Equal(t, tc.expected[i], stringResult) } else { - assert.Assert(t, testutils.UnorderedEqual(tc.expected[i], result.([]interface{}))) + assert.True(t, testutils.UnorderedEqual(tc.expected[i], result.([]interface{}))) } } }) @@ -789,7 +787,7 @@ func TestJSONMGET(t *testing.T) { resultStr, resultIsString := resultVal.(string) if isString && resultIsString && testutils.IsJSONResponse(expectedStr) { - testifyAssert.JSONEq(t, expectedStr, resultStr) + assert.JSONEq(t, expectedStr, resultStr) } else { assert.Equal(t, expectedVal, resultVal) } @@ -805,7 +803,7 @@ func TestJSONMGET(t *testing.T) { t.Run("MGET with recursive path", func(t *testing.T) { result, _ := exec.FireCommand(HTTPCommand{Command: "JSON.MGET", Body: map[string]interface{}{"keys": []interface{}{"doc1", "doc2"}, "path": "$..a"}}) results, ok := result.([]interface{}) - assert.Assert(t, ok, "Expected result to be a slice of interface{}") + assert.True(t, ok, "Expected result to be a slice of interface{}") expectedResults := [][]int{{1, 3}, {4, 6}} assert.Equal(t, len(expectedResults), len(results), "Expected 2 results") @@ -881,11 +879,11 @@ func TestJsonARRAPPEND(t *testing.T) { // because the order of keys is not guaranteed, we need to check if the result is an array if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else if testutils.IsJSONResponse(tc.expected[i].(string)) { - testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string)) + assert.JSONEq(t, tc.expected[i].(string), result.(string)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) @@ -968,11 +966,11 @@ func TestJsonNummultby(t *testing.T) { if slice, ok := tc.expected[i].([]interface{}); ok { var resultPayload []interface{} sonic.UnmarshalString(result.(string), &resultPayload) - assert.Assert(t, testutils.UnorderedEqual(slice, resultPayload)) + assert.True(t, testutils.UnorderedEqual(slice, resultPayload)) } else if testutils.IsJSONResponse(tc.expected[i].(string)) { - testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string)) + assert.JSONEq(t, tc.expected[i].(string), result.(string)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) @@ -1126,9 +1124,9 @@ func TestJsonObjLen(t *testing.T) { result, _ := exec.FireCommand(cmd) if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) @@ -1214,11 +1212,11 @@ func TestJSONNumIncrBy(t *testing.T) { if slice, ok := tc.expected[i].([]interface{}); ok { var resultPayload []interface{} sonic.UnmarshalString(result.(string), &resultPayload) - assert.Assert(t, testutils.UnorderedEqual(slice, resultPayload)) + assert.True(t, testutils.UnorderedEqual(slice, resultPayload)) } else if testutils.IsJSONResponse(tc.expected[i].(string)) { - testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string)) + assert.JSONEq(t, tc.expected[i].(string), result.(string)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) @@ -1301,11 +1299,11 @@ func TestJsonARRINSERT(t *testing.T) { // because the order of keys is not guaranteed, we need to check if the result is an array if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else if testutils.IsJSONResponse(tc.expected[i].(string)) { - testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string)) + assert.JSONEq(t, tc.expected[i].(string), result.(string)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) @@ -1428,11 +1426,17 @@ func TestJsonObjKeys(t *testing.T) { result, _ := exec.FireCommand(cmd) if slice, ok := tc.expected[i].([]interface{}); ok { - assert.DeepEqual(t, slice, tc.expected[i], cmpopts.SortSlices(func(a, b interface{}) bool { + assert.Equal(t, slice, tc.expected[i], cmpopts.SortSlices(func(a, b interface{}) bool { return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) })) } else { - assert.DeepEqual(t, tc.expected[i], result) + if _, ok := result.([]interface{}); ok { + assert.ElementsMatch(t, tc.expected[i].([]interface{}), result.([]interface{})) + } else { + // handle the case where result is not a []interface{} + assert.Equal(t, tc.expected[i], result) + } + } } }) @@ -1531,11 +1535,11 @@ func TestJsonARRTRIM(t *testing.T) { result, _ := exec.FireCommand(cmd) if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else if testutils.IsJSONResponse(tc.expected[i].(string)) { - testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string)) + assert.JSONEq(t, tc.expected[i].(string), result.(string)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/http/keys_test.go b/integration_tests/commands/http/keys_test.go index 603ad8397..75c9b05b1 100644 --- a/integration_tests/commands/http/keys_test.go +++ b/integration_tests/commands/http/keys_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestKeys(t *testing.T) { @@ -74,9 +74,9 @@ func TestKeys(t *testing.T) { // because the order of keys is not guaranteed, we need to check if the result is an array if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/http/mget_test.go b/integration_tests/commands/http/mget_test.go index 21aa58100..93c97e0a7 100644 --- a/integration_tests/commands/http/mget_test.go +++ b/integration_tests/commands/http/mget_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestMGET(t *testing.T) { @@ -50,9 +50,9 @@ func TestMGET(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) if slice, ok := tc.expected[i].([]interface{}); ok { - assert.Assert(t, testutils.UnorderedEqual(slice, result)) + assert.True(t, testutils.UnorderedEqual(slice, result)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/http/mset_test.go b/integration_tests/commands/http/mset_test.go index dec941a2f..8b8182e1f 100644 --- a/integration_tests/commands/http/mset_test.go +++ b/integration_tests/commands/http/mset_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestMSET(t *testing.T) { @@ -46,7 +46,7 @@ func TestMSET(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/http/object_test.go b/integration_tests/commands/http/object_test.go index 749f9cbf9..e16cdab27 100644 --- a/integration_tests/commands/http/object_test.go +++ b/integration_tests/commands/http/object_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestOBJECT(t *testing.T) { @@ -45,9 +45,9 @@ func TestOBJECT(t *testing.T) { } result, _ := exec.FireCommand(cmd) if tc.assert_type[i] == "equal" { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } else if tc.assert_type[i] == "assert" { - assert.Assert(t, result.(float64) >= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) + assert.True(t, result.(float64) >= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) } } }) diff --git a/integration_tests/commands/http/set_data_cmd_test.go b/integration_tests/commands/http/set_data_cmd_test.go index d354aa0db..e850a7569 100644 --- a/integration_tests/commands/http/set_data_cmd_test.go +++ b/integration_tests/commands/http/set_data_cmd_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestSetDataCmd(t *testing.T) { @@ -299,9 +299,9 @@ func TestSetDataCmd(t *testing.T) { result, _ := exec.FireCommand(cmd) switch tc.assert_type[i] { case "array": - testifyAssert.ElementsMatch(t, tc.expected[i], result) + assert.ElementsMatch(t, tc.expected[i], result) default: - testifyAssert.Equal(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/http/set_test.go b/integration_tests/commands/http/set_test.go index 259bf2ac5..f4b3f3781 100644 --- a/integration_tests/commands/http/set_test.go +++ b/integration_tests/commands/http/set_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) type TestCase struct { @@ -55,7 +55,7 @@ func TestSet(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } @@ -203,6 +203,94 @@ func TestSetWithOptions(t *testing.T) { } } +func TestWithKeepTTLFlag(t *testing.T) { + exec := NewHTTPCommandExecutor() + expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10) + + testCases := []TestCase { + { + name: "SET WITH KEEP TTL", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "ex": 3}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v2", "keepttl": true}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{"OK", "v", "OK", "v2"}, + }, + { + name: "SET WITH KEEP TTL on non-existing key", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "keepttl": true}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{"OK", "v"}, + }, + { + name: "SET WITH KEEPTTL with PX", + commands: []HTTPCommand { + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "px": 2000, "keepttl": true}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{"ERR syntax error", nil}, + }, + { + name: "SET WITH KEEPTTL with EX", + commands: []HTTPCommand { + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "ex": 3, "keepttl": true}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{"ERR syntax error", nil}, + }, + { + name: "SET WITH KEEPTTL with NX", + commands: []HTTPCommand { + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "nx": true, "keepttl": true}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{"OK", "v"}, + }, + { + name: "SET WITH KEEPTTL with XX", + commands: []HTTPCommand { + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "xx": true, "keepttl": true}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{nil, nil}, + }, + { + name: "SET WITH KEEPTTL with PXAT", + commands: []HTTPCommand { + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "pxat": expiryTime, "keepttl": true}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{"ERR syntax error", nil}, + }, + { + + name: "SET WITH KEEPTTL with EXAT", + commands: []HTTPCommand { + {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "exat": expiryTime, "keepttl": true}}, + {Command: "GET", Body: map[string]interface{}{"key": "k"}}, + }, + expected: []interface{}{"ERR syntax error", nil}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"key": "k"}}) + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"key": "k1"}}) + exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"key": "k2"}}) + for i, cmd := range tc.commands { + result, _ := exec.FireCommand(cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + +} + func TestSetWithExat(t *testing.T) { exec := NewHTTPCommandExecutor() Etime := strconv.FormatInt(time.Now().Unix()+5, 10) @@ -242,9 +330,9 @@ func TestSetWithExat(t *testing.T) { for i, cmd := range tc.commands { result, _ := exec.FireCommand(cmd) if cmd.Command == "TTL" { - assert.Assert(t, result.(float64) <= tc.expected[i].(float64)) + assert.True(t, result.(float64) <= tc.expected[i].(float64)) } else { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } } }) diff --git a/integration_tests/commands/http/toggle_test.go b/integration_tests/commands/http/toggle_test.go index 7337e197d..4fd6529ec 100644 --- a/integration_tests/commands/http/toggle_test.go +++ b/integration_tests/commands/http/toggle_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/dicedb/dice/testutils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func compareJSON(t *testing.T, expected, actual string) { @@ -15,10 +15,10 @@ func compareJSON(t *testing.T, expected, actual string) { err1 := json.Unmarshal([]byte(expected), &expectedMap) err2 := json.Unmarshal([]byte(actual), &actualMap) - assert.NilError(t, err1) - assert.NilError(t, err2) + assert.Nil(t, err1) + assert.Nil(t, err2) - assert.DeepEqual(t, expectedMap, actualMap) + assert.Equal(t, expectedMap, actualMap) } func TestJSONTOGGLE(t *testing.T) { exec := NewHTTPCommandExecutor() @@ -93,9 +93,9 @@ func TestJSONTOGGLE(t *testing.T) { assert.Equal(t, expected, result) } case []interface{}: - assert.Assert(t, testutils.UnorderedEqual(expected, result)) + assert.True(t, testutils.UnorderedEqual(expected, result)) default: - assert.DeepEqual(t, expected, result) + assert.Equal(t, expected, result) } } }) diff --git a/integration_tests/commands/http/touch_test.go b/integration_tests/commands/http/touch_test.go index c389f80e6..88400e51c 100644 --- a/integration_tests/commands/http/touch_test.go +++ b/integration_tests/commands/http/touch_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestTouch(t *testing.T) { diff --git a/integration_tests/commands/http/ttl_pttl_test.go b/integration_tests/commands/http/ttl_pttl_test.go index 1fc710ce5..0d3f8ce37 100644 --- a/integration_tests/commands/http/ttl_pttl_test.go +++ b/integration_tests/commands/http/ttl_pttl_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestTTLPTTL(t *testing.T) { @@ -111,9 +111,9 @@ func TestTTLPTTL(t *testing.T) { } result, _ := exec.FireCommand(cmd) if tc.assert_type[i] == "equal" { - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } else if tc.assert_type[i] == "assert" { - assert.Assert(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) + assert.True(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i]) } } }) diff --git a/integration_tests/commands/http/type_test.go b/integration_tests/commands/http/type_test.go index 136dd0ccc..550d451e2 100644 --- a/integration_tests/commands/http/type_test.go +++ b/integration_tests/commands/http/type_test.go @@ -3,7 +3,7 @@ package http import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestType(t *testing.T) { diff --git a/integration_tests/commands/http/zcard_test.go b/integration_tests/commands/http/zcard_test.go new file mode 100644 index 000000000..29c869d97 --- /dev/null +++ b/integration_tests/commands/http/zcard_test.go @@ -0,0 +1,92 @@ +package http + +import ( + "log" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestZCARD(t *testing.T) { + exec := NewHTTPCommandExecutor() + + testCases := []struct { + name string + commands []HTTPCommand + expected []interface{} + delays []time.Duration + }{ + { + name: "ZCARD with wrong number of arguments", + commands: []HTTPCommand{ + {Command: "ZCARD", Body: nil}, + {Command: "ZCARD", Body: map[string]interface{}{"key": "myzset", "field": "field"}}, + }, + expected: []interface{}{ + "ERR wrong number of arguments for 'zcard' command", + "ERR wrong number of arguments for 'zcard' command"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with wrong type of key", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "string_key", "value": "string_value"}}, + {Command: "ZCARD", Body: map[string]interface{}{"key": "string_key"}}, + }, + expected: []interface{}{"OK", "WRONGTYPE Operation against a key holding the wrong kind of value"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with non-existent key", + commands: []HTTPCommand{ + {Command: "ZADD", Body: map[string]interface{}{"key": "myzset", "values": []string{"1", "one"}}}, + {Command: "ZCARD", Body: map[string]interface{}{"key": "wrong_myzset"}}, + }, + expected: []interface{}{float64(1), float64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with sorted set holding single element", + commands: []HTTPCommand{ + {Command: "ZADD", Body: map[string]interface{}{"key": "myzset", "values": []string{"1", "one"}}}, + {Command: "ZCARD", Body: map[string]interface{}{"key": "myzset"}}, + }, + expected: []interface{}{float64(1), float64(1)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with sorted set holding multiple elements", + commands: []HTTPCommand{ + {Command: "ZADD", Body: map[string]interface{}{"key": "myzset", "values": []string{"1", "one", "2", "two"}}}, + {Command: "ZCARD", Body: map[string]interface{}{"key": "myzset"}}, + {Command: "ZADD", Body: map[string]interface{}{"key": "myzset", "values": []string{"3", "three"}}}, + {Command: "ZCARD", Body: map[string]interface{}{"key": "myzset"}}, + {Command: "ZREM", Body: map[string]interface{}{"key": "myzset", "field": "two"}}, + {Command: "ZCARD", Body: map[string]interface{}{"key": "myzset"}}, + }, + expected: []interface{}{float64(2), float64(2), float64(1), float64(3), float64(1), float64(2)}, + delays: []time.Duration{0, 0, 0, 0, 0, 0}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + defer exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": []string{"string_key", "myzset"}}}) + + for i, cmd := range tc.commands { + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + result, err := exec.FireCommand(cmd) + if err != nil { + // Check if the error message matches the expected result + log.Println(tc.expected[i]) + assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd) + } else { + assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result) + } + } + }) + } +} diff --git a/integration_tests/commands/http/zrem_test.go b/integration_tests/commands/http/zrem_test.go new file mode 100644 index 000000000..733c5c793 --- /dev/null +++ b/integration_tests/commands/http/zrem_test.go @@ -0,0 +1,98 @@ +package http + +import ( + "log" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestZREM(t *testing.T) { + exec := NewHTTPCommandExecutor() + + testCases := []struct { + name string + commands []HTTPCommand + expected []interface{} + delays []time.Duration + }{ + { + name: "ZREM with wrong number of arguments", + commands: []HTTPCommand{ + {Command: "ZREM", Body: nil}, + {Command: "ZREM", Body: map[string]interface{}{"key": "myzset"}}, + }, + expected: []interface{}{ + "ERR wrong number of arguments for 'zrem' command", + "ERR wrong number of arguments for 'zrem' command"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with wrong type of key", + commands: []HTTPCommand{ + {Command: "SET", Body: map[string]interface{}{"key": "string_key", "value": "string_value"}}, + {Command: "ZREM", Body: map[string]interface{}{"key": "string_key", "field": "string_value"}}, + }, + expected: []interface{}{"OK", "WRONGTYPE Operation against a key holding the wrong kind of value"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with non-existent key", + commands: []HTTPCommand{ + {Command: "ZADD", Body: map[string]interface{}{"key": "myzset", "values": []string{"1", "one"}}}, + {Command: "ZREM", Body: map[string]interface{}{"key": "wrong_myzset", "field": "one"}}, + }, + expected: []interface{}{float64(1), float64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with non-existent element", + commands: []HTTPCommand{ + {Command: "ZADD", Body: map[string]interface{}{"key": "myzset", "values": []string{"1", "one"}}}, + {Command: "ZREM", Body: map[string]interface{}{"key": "wrong_myzset", "field": "two"}}, + }, + expected: []interface{}{float64(1), float64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with sorted set holding single element", + commands: []HTTPCommand{ + {Command: "ZADD", Body: map[string]interface{}{"key": "myzset", "values": []string{"1", "one"}}}, + {Command: "ZREM", Body: map[string]interface{}{"key": "myzset", "values": []string{"one"}}}, + }, + expected: []interface{}{float64(1), float64(1)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with sorted set holding multiple elements", + commands: []HTTPCommand{ + {Command: "ZADD", Body: map[string]interface{}{"key": "myzset", "values": []string{"1", "one", "2", "two", "3", "three", "4", "four"}}}, + {Command: "ZREM", Body: map[string]interface{}{"key": "myzset", "values": []string{"four", "five"}}}, + {Command: "ZREM", Body: map[string]interface{}{"key": "myzset", "values": []string{"one", "two"}}}, + }, + expected: []interface{}{float64(4), float64(1), float64(2)}, + delays: []time.Duration{0, 0, 0}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + defer exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": []string{"string_key", "myzset"}}}) + + for i, cmd := range tc.commands { + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + result, err := exec.FireCommand(cmd) + if err != nil { + // Check if the error message matches the expected result + log.Println(tc.expected[i]) + assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd) + } else { + assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result) + } + } + }) + } +} diff --git a/integration_tests/commands/resp/append_test.go b/integration_tests/commands/resp/append_test.go index 4f252a84a..fc70851cf 100644 --- a/integration_tests/commands/resp/append_test.go +++ b/integration_tests/commands/resp/append_test.go @@ -3,7 +3,7 @@ package resp import ( "testing" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestAPPEND(t *testing.T) { @@ -65,7 +65,7 @@ func TestAPPEND(t *testing.T) { for i := 0; i < len(tc.commands); i++ { result := FireCommand(conn, tc.commands[i]) expected := tc.expected[i] - testifyAssert.Equal(t, expected, result) + assert.Equal(t, expected, result) } for _, cmd := range tc.cleanup { diff --git a/integration_tests/commands/resp/bloom_test.go b/integration_tests/commands/resp/bloom_test.go index c5c37f116..bda85c284 100644 --- a/integration_tests/commands/resp/bloom_test.go +++ b/integration_tests/commands/resp/bloom_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - assert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestBFReserveAddInfoExists(t *testing.T) { diff --git a/integration_tests/commands/resp/command_docs_test.go b/integration_tests/commands/resp/command_docs_test.go index 55ff02196..f566a3c26 100644 --- a/integration_tests/commands/resp/command_docs_test.go +++ b/integration_tests/commands/resp/command_docs_test.go @@ -3,7 +3,7 @@ package resp import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var getDocsTestCases = []struct { @@ -86,7 +86,7 @@ func TestCommandDocs(t *testing.T) { for _, tc := range getDocsTestCases { t.Run(tc.name, func(t *testing.T) { result := FireCommand(conn, "COMMAND DOCS "+tc.inCmd) - assert.DeepEqual(t, tc.expected, result) + assert.Equal(t, tc.expected, result) }) } } diff --git a/integration_tests/commands/resp/command_getkeys_test.go b/integration_tests/commands/resp/command_getkeys_test.go index d1fd162a1..607bba994 100644 --- a/integration_tests/commands/resp/command_getkeys_test.go +++ b/integration_tests/commands/resp/command_getkeys_test.go @@ -3,7 +3,7 @@ package resp import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var getKeysTestCases = []struct { @@ -31,7 +31,7 @@ func TestCommandGetKeys(t *testing.T) { for _, tc := range getKeysTestCases { t.Run(tc.name, func(t *testing.T) { result := FireCommand(conn, "COMMAND GETKEYS "+tc.inCmd) - assert.DeepEqual(t, tc.expected, result) + assert.Equal(t, tc.expected, result) }) } } diff --git a/integration_tests/commands/resp/command_info_test.go b/integration_tests/commands/resp/command_info_test.go index 545d81984..e9d71ff5a 100644 --- a/integration_tests/commands/resp/command_info_test.go +++ b/integration_tests/commands/resp/command_info_test.go @@ -3,7 +3,7 @@ package resp import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var getInfoTestCases = []struct { @@ -32,7 +32,7 @@ func TestCommandInfo(t *testing.T) { for _, tc := range getInfoTestCases { t.Run(tc.name, func(t *testing.T) { result := FireCommand(conn, "COMMAND INFO "+tc.inCmd) - assert.DeepEqual(t, tc.expected, result) + assert.Equal(t, tc.expected, result) }) } } diff --git a/integration_tests/commands/resp/countminsketch_test.go b/integration_tests/commands/resp/countminsketch_test.go index 574905157..e1c66575c 100644 --- a/integration_tests/commands/resp/countminsketch_test.go +++ b/integration_tests/commands/resp/countminsketch_test.go @@ -3,7 +3,7 @@ package resp import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestCMSInitByDim(t *testing.T) { @@ -154,7 +154,7 @@ func TestCMSInfo(t *testing.T) { FireCommand(conn, "DEL cms_key2") for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } @@ -219,7 +219,7 @@ func TestCMSIncrBy(t *testing.T) { FireCommand(conn, "DEL cms_key3") for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } @@ -261,7 +261,7 @@ func TestCMSQuery(t *testing.T) { FireCommand(conn, "DEL cms_key4") for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } @@ -408,7 +408,7 @@ func TestCMSMerge(t *testing.T) { FireCommand(conn, "DEL cms_key5 test test1") for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/resp/get_test.go b/integration_tests/commands/resp/get_test.go index bb826a499..a7dc5f118 100644 --- a/integration_tests/commands/resp/get_test.go +++ b/integration_tests/commands/resp/get_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGet(t *testing.T) { diff --git a/integration_tests/commands/resp/getrange_test.go b/integration_tests/commands/resp/getrange_test.go index db5700769..14f068b3e 100644 --- a/integration_tests/commands/resp/getrange_test.go +++ b/integration_tests/commands/resp/getrange_test.go @@ -3,7 +3,7 @@ package resp import ( "testing" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestGETRANGE(t *testing.T) { @@ -62,7 +62,7 @@ func TestGETRANGE(t *testing.T) { for i := 0; i < len(tc.commands); i++ { result := FireCommand(conn, tc.commands[i]) expected := tc.expected[i] - testifyAssert.Equal(t, expected, result) + assert.Equal(t, expected, result) } for _, cmd := range tc.cleanup { diff --git a/integration_tests/commands/resp/getset_test.go b/integration_tests/commands/resp/getset_test.go index 09a1eff69..8846bae9e 100644 --- a/integration_tests/commands/resp/getset_test.go +++ b/integration_tests/commands/resp/getset_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestGetSet(t *testing.T) { diff --git a/integration_tests/commands/resp/getwatch_test.go b/integration_tests/commands/resp/getwatch_test.go index c474ace47..537644b8f 100644 --- a/integration_tests/commands/resp/getwatch_test.go +++ b/integration_tests/commands/resp/getwatch_test.go @@ -9,7 +9,7 @@ import ( "github.com/dicedb/dice/internal/clientio" "github.com/dicedb/dicedb-go" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) type WatchSubscriber struct { @@ -57,11 +57,11 @@ func TestGETWATCH(t *testing.T) { respParsers := make([]*clientio.RESPParser, len(subscribers)) for i, subscriber := range subscribers { rp := fireCommandAndGetRESPParser(subscriber, fmt.Sprintf("GET.WATCH %s", getWatchKey)) - assert.Assert(t, rp != nil) + assert.True(t, rp != nil) respParsers[i] = rp v, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) castedValue, ok := v.([]interface{}) if !ok { t.Errorf("Type assertion to []interface{} failed for value: %v", v) @@ -76,7 +76,7 @@ func TestGETWATCH(t *testing.T) { for _, rp := range respParsers { v, err := rp.DecodeOne() - assert.NilError(t, err) + assert.Nil(t, err) castedValue, ok := v.([]interface{}) if !ok { t.Errorf("Type assertion to []interface{} failed for value: %v", v) @@ -99,9 +99,9 @@ func TestGETWATCHWithSDK(t *testing.T) { for i, subscriber := range subscribers { watch := subscriber.client.WatchConn(context.Background()) subscribers[i].watch = watch - assert.Assert(t, watch != nil) + assert.True(t, watch != nil) firstMsg, err := watch.Watch(context.Background(), "GET", getWatchKey) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, firstMsg.Command, "GET") assert.Equal(t, firstMsg.Fingerprint, "1768826704") channels[i] = watch.Channel() @@ -109,7 +109,7 @@ func TestGETWATCHWithSDK(t *testing.T) { for _, tc := range getWatchTestCases { err := publisher.Set(context.Background(), tc.key, tc.val, 0).Err() - assert.NilError(t, err) + assert.Nil(t, err) for _, channel := range channels { v := <-channel @@ -130,9 +130,9 @@ func TestGETWATCHWithSDK2(t *testing.T) { for i, subscriber := range subscribers { watch := subscriber.client.WatchConn(context.Background()) subscribers[i].watch = watch - assert.Assert(t, watch != nil) + assert.True(t, watch != nil) firstMsg, err := watch.GetWatch(context.Background(), getWatchKey) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, firstMsg.Command, "GET") assert.Equal(t, firstMsg.Fingerprint, "1768826704") channels[i] = watch.Channel() @@ -140,7 +140,7 @@ func TestGETWATCHWithSDK2(t *testing.T) { for _, tc := range getWatchTestCases { err := publisher.Set(context.Background(), tc.key, tc.val, 0).Err() - assert.NilError(t, err) + assert.Nil(t, err) for _, channel := range channels { v := <-channel diff --git a/integration_tests/commands/resp/hexists_test.go b/integration_tests/commands/resp/hexists_test.go new file mode 100644 index 000000000..65ab8914e --- /dev/null +++ b/integration_tests/commands/resp/hexists_test.go @@ -0,0 +1,74 @@ +package resp + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHExists(t *testing.T) { + conn := getLocalConnection() + defer conn.Close() + + testCases := []TestCase{ + { + name: "RESP wrong number of arguments for HEXISTS", + commands: []string{"HEXISTS", "HEXISTS KEY", "HEXISTS KEY FIELD ANOTHER_FIELD"}, + expected: []interface{}{"ERR wrong number of arguments for 'hexists' command", + "ERR wrong number of arguments for 'hexists' command", + "ERR wrong number of arguments for 'hexists' command"}, + }, + { + name: "RESP HEXISTS non existent key", + commands: []string{"HSET key_hExists1 field value", "HEXISTS wrong_key_hExists field"}, + expected: []interface{}{int64(1), int64(0)}, + }, + { + name: "RESP HEXISTS non existent field", + commands: []string{"HSET key_hExists2 field value", "HEXISTS key_hExists2 wrong_field"}, + expected: []interface{}{int64(1), int64(0)}, + }, + { + name: "RESP HEXISTS existent key and field", + commands: []string{"HSET key_hExists3 field HelloWorld", "HEXISTS key_hExists3 field"}, + expected: []interface{}{int64(1), int64(1)}, + }, + { + name: "RESP Check if field exists when k f and v are set", + commands: []string{"HSET key field value", "HEXISTS key field"}, + expected: []interface{}{int64(1), int64(1)}, + }, + { + name: "RESP Check if field exists when k exists but not f and v", + commands: []string{"HSET key field1 value", "HEXISTS key field"}, + expected: []interface{}{int64(1), int64(0)}, + }, + { + name: "RESP Check if field exists when no k,f and v exist", + commands: []string{"HEXISTS key field"}, + expected: []interface{}{int64(0)}, + }, + { + name: "RESP HEXISTS operation against a key holding the wrong kind of value", + commands: []string{"SET key value", "HEXISTS key field"}, + expected: []interface{}{"OK", "ERR -WRONGTYPE Operation against a key holding the wrong kind of value"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + FireCommand(conn, "HDEL key field") + FireCommand(conn, "DEL key value") + FireCommand(conn, "HDEL key field1") + + for i, cmd := range tc.commands { + result := FireCommand(conn, cmd) + assert.Equal(t, tc.expected[i], result) + } + }) + } + + FireCommand(conn, "HDEL key field") + FireCommand(conn, "DEL key value") + FireCommand(conn, "HDEL key field1") +} diff --git a/integration_tests/commands/resp/hincrby_test.go b/integration_tests/commands/resp/hincrby_test.go index 4d02ec07e..01f694a3b 100644 --- a/integration_tests/commands/resp/hincrby_test.go +++ b/integration_tests/commands/resp/hincrby_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHINCRBY(t *testing.T) { diff --git a/integration_tests/commands/resp/hincrbyfloat_test.go b/integration_tests/commands/resp/hincrbyfloat_test.go index 7696106b5..df16faeb7 100644 --- a/integration_tests/commands/resp/hincrbyfloat_test.go +++ b/integration_tests/commands/resp/hincrbyfloat_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHINCRBYFLOAT(t *testing.T) { diff --git a/integration_tests/commands/resp/hkeys_test.go b/integration_tests/commands/resp/hkeys_test.go new file mode 100644 index 000000000..92597730b --- /dev/null +++ b/integration_tests/commands/resp/hkeys_test.go @@ -0,0 +1,68 @@ +package resp + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHKeys(t *testing.T) { + conn := getLocalConnection() + defer conn.Close() + + testCases := []TestCase{ + { + name: "RESP HKEYS with key containing hash with multiple fields", + commands: []string{"HSET key_hkeys field1 value1", "HSET key_hkeys field2 value2", "HKEYS key_hkeys"}, + expected: []interface{}{int64(1), int64(1), []interface{}{"field1", "field2"}}, + }, + { + name: "RESP HKEYS with non-existent key", + commands: []string{"HKEYS key_hkeys01"}, + expected: []interface{}{[]interface{}{}}, + }, + { + name: "RESP HKEYS with key containing a non-hash value", + commands: []string{"SET key_hkeys02 field1", "HKEYS key_hkeys02"}, + expected: []interface{}{"OK", "ERR -WRONGTYPE Operation against a key holding the wrong kind of value"}, + }, + { + name: "RESP HKEYS with wrong number of arguments", + commands: []string{"HKEYS key_hkeys03 x", "HKEYS"}, + expected: []interface{}{"ERR wrong number of arguments for 'hkeys' command", + "ERR wrong number of arguments for 'hkeys' command"}, + }, + { + name: "RESP One or more keys exist", + commands: []string{"HSET key field value", "HSET key field1 value1", "HKEYS key"}, + expected: []interface{}{int64(1), int64(1), []interface{}{"field", "field1"}}, + }, + { + name: "RESP No keys exist", + commands: []string{"HKEYS key"}, + expected: []interface{}{[]interface{}{}}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + FireCommand(conn, "HDEL key field") + FireCommand(conn, "HDEL key field1") + FireCommand(conn, "DEL key") + + for i, cmd := range tc.commands { + result := FireCommand(conn, cmd) + switch e := tc.expected[i].(type) { + case []interface{}: + assert.ElementsMatch(t, e, tc.expected[i]) + default: + assert.Equal(t, tc.expected[i], result) + } + } + }) + } + + FireCommand(conn, "HDEL key field") + FireCommand(conn, "HDEL key field1") + FireCommand(conn, "DEL key") +} diff --git a/integration_tests/commands/resp/hlen_test.go b/integration_tests/commands/resp/hlen_test.go index e0a4b6e51..eae9b1be8 100644 --- a/integration_tests/commands/resp/hlen_test.go +++ b/integration_tests/commands/resp/hlen_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestHLEN(t *testing.T) { @@ -83,7 +83,7 @@ func TestHLEN(t *testing.T) { time.Sleep(tc.delays[i]) } result := FireCommand(conn, cmd) - testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) } }) } diff --git a/integration_tests/commands/resp/hrandfield_test.go b/integration_tests/commands/resp/hrandfield_test.go index 5ba4b847c..45f879a7d 100644 --- a/integration_tests/commands/resp/hrandfield_test.go +++ b/integration_tests/commands/resp/hrandfield_test.go @@ -5,7 +5,7 @@ import ( "time" "github.com/google/go-cmp/cmp/cmpopts" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHRANDFIELD(t *testing.T) { @@ -71,7 +71,7 @@ func TestHRANDFIELD(t *testing.T) { if str, ok := result.(string); ok { assert.Equal(t, str, expected, "Unexpected result for command: %s", cmd) } else { - assert.DeepEqual(t, result, expected, cmpopts.EquateEmpty()) + assert.Equal(t, result, expected, cmpopts.EquateEmpty()) } } } @@ -111,7 +111,7 @@ func assertRandomFieldResult(t *testing.T, result interface{}, expected []string } // assert that all results are in the expected set or that there is a single valid result - assert.Assert(t, count == len(resultsList) || count == 1, + assert.True(t, count == len(resultsList) || count == 1, "Expected all results to be in the expected set or a single valid result. Got %d out of %d", count, len(resultsList)) } diff --git a/integration_tests/commands/resp/hscan_test.go b/integration_tests/commands/resp/hscan_test.go index aa540174a..d019cb852 100644 --- a/integration_tests/commands/resp/hscan_test.go +++ b/integration_tests/commands/resp/hscan_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestHSCAN(t *testing.T) { @@ -128,7 +128,7 @@ func TestHSCAN(t *testing.T) { time.Sleep(tc.delays[i]) } result := FireCommand(conn, cmd) - testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) } }) } diff --git a/integration_tests/commands/resp/hstrlen_test.go b/integration_tests/commands/resp/hstrlen_test.go index aff2d49cf..28c51bf13 100644 --- a/integration_tests/commands/resp/hstrlen_test.go +++ b/integration_tests/commands/resp/hstrlen_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestHSTRLEN(t *testing.T) { @@ -66,7 +66,7 @@ func TestHSTRLEN(t *testing.T) { time.Sleep(tc.delays[i]) } result := FireCommand(conn, cmd) - testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) } }) } diff --git a/integration_tests/commands/resp/hvals_test.go b/integration_tests/commands/resp/hvals_test.go new file mode 100644 index 000000000..ca0a10dfe --- /dev/null +++ b/integration_tests/commands/resp/hvals_test.go @@ -0,0 +1,65 @@ +package resp + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHVals(t *testing.T) { + conn := getLocalConnection() + defer conn.Close() + + testCases := []TestCase{ + { + name: "RESP HVALS with multiple fields", + commands: []string{"HSET hvalsKey field value", "HSET hvalsKey field2 value1", "HVALS hvalsKey"}, + expected: []interface{}{int64(1), int64(1), []interface{}{"value", "value1"}}, + }, + { + name: "RESP HVALS with non-existing key", + commands: []string{"HVALS hvalsKey01"}, + expected: []interface{}{[]any{}}, + }, + { + name: "HVALS on wrong key type", + commands: []string{"SET hvalsKey02 field", "HVALS hvalsKey02"}, + expected: []interface{}{"OK", "ERR -WRONGTYPE Operation against a key holding the wrong kind of value"}, + }, + { + name: "HVALS with wrong number of arguments", + commands: []string{"HVALS hvalsKey03 x", "HVALS"}, + expected: []interface{}{"ERR wrong number of arguments for 'hvals' command", "ERR wrong number of arguments for 'hvals' command"}, + }, + { + name: "RESP One or more vals exist", + commands: []string{"HSET key field value", "HSET key field1 value1", "HVALS key"}, + expected: []interface{}{int64(1), int64(1), []interface{}{"value", "value1"}}, + }, + { + name: "RESP No values exist", + commands: []string{"HVALS key"}, + expected: []interface{}{[]interface{}{}}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + FireCommand(conn, "HDEL key field") + FireCommand(conn, "HDEL key field1") + + for i, cmd := range tc.commands { + result := FireCommand(conn, cmd) + switch e := tc.expected[i].(type) { + case []interface{}: + assert.ElementsMatch(t, e, tc.expected[i]) + default: + assert.Equal(t, tc.expected[i], result) + } + } + }) + } + + FireCommand(conn, "HDEL key field") + FireCommand(conn, "HDEL key field1") +} diff --git a/integration_tests/commands/resp/hyperloglog_test.go b/integration_tests/commands/resp/hyperloglog_test.go index 75382c5d5..d6e09a3c7 100644 --- a/integration_tests/commands/resp/hyperloglog_test.go +++ b/integration_tests/commands/resp/hyperloglog_test.go @@ -5,7 +5,7 @@ package resp import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestHyperLogLogCommands(t *testing.T) { @@ -90,7 +90,7 @@ func TestHyperLogLogCommands(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } diff --git a/integration_tests/commands/resp/incr_by_float_test.go b/integration_tests/commands/resp/incr_by_float_test.go index 95dd4443f..e47bdf717 100644 --- a/integration_tests/commands/resp/incr_by_float_test.go +++ b/integration_tests/commands/resp/incr_by_float_test.go @@ -3,7 +3,7 @@ package resp import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestINCRBYFLOAT(t *testing.T) { diff --git a/integration_tests/commands/resp/incr_test.go b/integration_tests/commands/resp/incr_test.go index 298b42059..24077f925 100644 --- a/integration_tests/commands/resp/incr_test.go +++ b/integration_tests/commands/resp/incr_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/dicedb/dice/internal/server/utils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestINCR(t *testing.T) { diff --git a/integration_tests/commands/resp/json_test.go b/integration_tests/commands/resp/json_test.go index 7915ed6d1..765c9babf 100644 --- a/integration_tests/commands/resp/json_test.go +++ b/integration_tests/commands/resp/json_test.go @@ -2,12 +2,11 @@ package resp import ( "fmt" + "sort" "testing" "github.com/dicedb/dice/testutils" - "github.com/google/go-cmp/cmp/cmpopts" - testifyAssert "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestJsonStrlen(t *testing.T) { @@ -86,7 +85,7 @@ func TestJsonStrlen(t *testing.T) { if ok { assert.Equal(t, tc.expected[i], stringResult) } else { - assert.Assert(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{}))) } } }) @@ -289,7 +288,7 @@ func TestJsonObjLen(t *testing.T) { cmd := tcase.commands[i] out := tcase.expected[i] result := FireCommand(conn, cmd) - assert.DeepEqual(t, out, result) + assert.Equal(t, out, result) } }) } @@ -366,14 +365,14 @@ func TestJSONARRPOP(t *testing.T) { jsonResult, isString := result.(string) if isString && testutils.IsJSONResponse(jsonResult) { - testifyAssert.JSONEq(t, out.(string), jsonResult) + assert.JSONEq(t, out.(string), jsonResult) continue } if tcase.assertType[i] == "equal" { assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } } }) @@ -430,7 +429,7 @@ func TestJsonARRAPPEND(t *testing.T) { if tcase.assertType[i] == "equal" { assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } } }) @@ -502,9 +501,9 @@ func TestJsonARRINSERT(t *testing.T) { if tcase.assertType[i] == "equal" { assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } else if tcase.assertType[i] == "jsoneq" { - testifyAssert.JSONEq(t, out.(string), result.(string)) + assert.JSONEq(t, out.(string), result.(string)) } } }) @@ -616,16 +615,32 @@ func TestJsonObjKeys(t *testing.T) { _, isString := out.(string) if isString { outInterface := []interface{}{out} - assert.DeepEqual(t, outInterface, expected) + assert.Equal(t, outInterface, expected) } else { - assert.DeepEqual(t, out.([]interface{}), expected, - cmpopts.SortSlices(func(a, b interface{}) bool { - return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) - })) + assert.ElementsMatch(t, + sortNestedSlices(expected), + sortNestedSlices(out.([]interface{})), + "Mismatch in JSON object keys") } }) } +} +func sortNestedSlices(data []interface{}) []interface{} { + result := make([]interface{}, len(data)) + for i, item := range data { + if slice, ok := item.([]interface{}); ok { + sorted := make([]interface{}, len(slice)) + copy(sorted, slice) + sort.Slice(sorted, func(i, j int) bool { + return fmt.Sprintf("%v", sorted[i]) < fmt.Sprintf("%v", sorted[j]) + }) + result[i] = sorted + } else { + result[i] = item + } + } + return result } func TestJsonARRTRIM(t *testing.T) { @@ -702,9 +717,9 @@ func TestJsonARRTRIM(t *testing.T) { if tcase.assertType[i] == "equal" { assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } else if tcase.assertType[i] == "jsoneq" { - testifyAssert.JSONEq(t, out.(string), result.(string)) + assert.JSONEq(t, out.(string), result.(string)) } } }) diff --git a/integration_tests/commands/resp/set_test.go b/integration_tests/commands/resp/set_test.go index e6fa927c3..e5ddb036c 100644 --- a/integration_tests/commands/resp/set_test.go +++ b/integration_tests/commands/resp/set_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) type TestCase struct { @@ -43,7 +43,7 @@ func TestSet(t *testing.T) { for i, cmd := range tc.commands { result := FireCommand(conn, cmd) - assert.DeepEqual(t, tc.expected[i], result) + assert.Equal(t, tc.expected[i], result) } }) } @@ -148,9 +148,9 @@ func TestSetWithExat(t *testing.T) { FireCommand(conn, "DEL k") assert.Equal(t, "OK", FireCommand(conn, "SET k v EXAT "+Etime), "Value mismatch for cmd SET k v EXAT "+Etime) assert.Equal(t, "v", FireCommand(conn, "GET k"), "Value mismatch for cmd GET k") - assert.Assert(t, FireCommand(conn, "TTL k").(int64) <= 5, "Value mismatch for cmd TTL k") + assert.True(t, FireCommand(conn, "TTL k").(int64) <= 5, "Value mismatch for cmd TTL k") time.Sleep(3 * time.Second) - assert.Assert(t, FireCommand(conn, "TTL k").(int64) <= 3, "Value mismatch for cmd TTL k") + assert.True(t, FireCommand(conn, "TTL k").(int64) <= 3, "Value mismatch for cmd TTL k") time.Sleep(3 * time.Second) assert.Equal(t, "(nil)", FireCommand(conn, "GET k"), "Value mismatch for cmd GET k") assert.Equal(t, int64(-2), FireCommand(conn, "TTL k"), "Value mismatch for cmd TTL k") @@ -176,12 +176,13 @@ func TestSetWithExat(t *testing.T) { func TestWithKeepTTLFlag(t *testing.T) { conn := getLocalConnection() + expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10) defer conn.Close() for _, tcase := range []TestCase{ { - commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk"}, - expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv"}, + commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk", "SET K V EX 2 KEEPTTL", "SET K1 vv PX 2000 KEEPTTL", "SET K2 vv EXAT " + expiryTime + " KEEPTTL"}, + expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv", "ERR syntax error", "ERR syntax error", "ERR syntax error"}, }, } { for i := 0; i < len(tcase.commands); i++ { diff --git a/integration_tests/commands/resp/setup.go b/integration_tests/commands/resp/setup.go index 64abaf355..7026134eb 100644 --- a/integration_tests/commands/resp/setup.go +++ b/integration_tests/commands/resp/setup.go @@ -12,12 +12,12 @@ import ( "time" "github.com/dicedb/dice/internal/server/resp" + "github.com/dicedb/dice/internal/watchmanager" "github.com/dicedb/dice/internal/worker" "github.com/dicedb/dice/config" "github.com/dicedb/dice/internal/clientio" derrors "github.com/dicedb/dice/internal/errors" - "github.com/dicedb/dice/internal/logger" "github.com/dicedb/dice/internal/shard" dstore "github.com/dicedb/dice/internal/store" "github.com/dicedb/dice/testutils" @@ -28,6 +28,8 @@ type TestServerOptions struct { Port int } +// getLocalConnection returns a local TCP connection to the database +// //nolint:unused func getLocalConnection() net.Conn { conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", config.DiceConfig.AsyncServer.Port)) @@ -111,7 +113,6 @@ func fireCommandAndGetRESPParser(conn net.Conn, cmd string) *clientio.RESPParser } func RunTestServer(wg *sync.WaitGroup, opt TestServerOptions) { - slog.SetDefault(logger.New()) config.DiceConfig.Network.IOBufferLength = 16 config.DiceConfig.Persistence.WriteAOFOnCleanup = false if opt.Port != 0 { @@ -122,11 +123,12 @@ func RunTestServer(wg *sync.WaitGroup, opt TestServerOptions) { queryWatchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Memory.KeysLimit) cmdWatchChan := make(chan dstore.CmdWatchEvent, config.DiceConfig.Memory.KeysLimit) + cmdWatchSubscriptionChan := make(chan watchmanager.WatchSubscription) gec := make(chan error) shardManager := shard.NewShardManager(1, queryWatchChan, cmdWatchChan, gec) workerManager := worker.NewWorkerManager(20000, shardManager) // Initialize the RESP Server - testServer := resp.NewServer(shardManager, workerManager, cmdWatchChan, gec) + testServer := resp.NewServer(shardManager, workerManager, cmdWatchSubscriptionChan, cmdWatchChan, gec) ctx, cancel := context.WithCancel(context.Background()) fmt.Println("Starting the test server on port", config.DiceConfig.AsyncServer.Port) diff --git a/integration_tests/commands/resp/zcard_test.go b/integration_tests/commands/resp/zcard_test.go new file mode 100644 index 000000000..b799ae170 --- /dev/null +++ b/integration_tests/commands/resp/zcard_test.go @@ -0,0 +1,66 @@ +package resp + +import ( + "testing" + "time" + + testifyAssert "github.com/stretchr/testify/assert" +) + +func TestZCARD(t *testing.T) { + conn := getLocalConnection() + defer conn.Close() + + testCases := []struct { + name string + cmds []string + expect []interface{} + delays []time.Duration + }{ + { + name: "ZCARD with wrong number of arguments", + cmds: []string{"ZCARD", "ZCARD myzset field"}, + expect: []interface{}{"ERR wrong number of arguments for 'zcard' command", + "ERR wrong number of arguments for 'zcard' command"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with wrong type of key", + cmds: []string{"SET string_key string_value", "ZCARD string_key"}, + expect: []interface{}{"OK", + "WRONGTYPE Operation against a key holding the wrong kind of value"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with non-existent key", + cmds: []string{"ZADD myzset 1 one", "ZCARD wrong_myzset"}, + expect: []interface{}{int64(1), int64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with sorted set holding single element", + cmds: []string{"ZADD myzset 1 one", "ZCARD myzset"}, + expect: []interface{}{int64(1), int64(1)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with sorted set holding multiple elements", + cmds: []string{"ZADD myzset 1 one 2 two", "ZCARD myzset", "ZADD myzset 3 three", "ZCARD myzset", "ZREM myzset two", "ZCARD myzset"}, + expect: []interface{}{int64(2), int64(2), int64(1), int64(3), int64(1), int64(2)}, + delays: []time.Duration{0, 0, 0, 0, 0, 0}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + FireCommand(conn, "DEL myzset string_key") + for i, cmd := range tc.cmds { + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + result := FireCommand(conn, cmd) + testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + } + }) + } +} diff --git a/integration_tests/commands/resp/zrem_test.go b/integration_tests/commands/resp/zrem_test.go new file mode 100644 index 000000000..7ff8956bb --- /dev/null +++ b/integration_tests/commands/resp/zrem_test.go @@ -0,0 +1,72 @@ +package resp + +import ( + "testing" + "time" + + testifyAssert "github.com/stretchr/testify/assert" +) + +func TestZREM(t *testing.T) { + conn := getLocalConnection() + defer conn.Close() + + testCases := []struct { + name string + cmds []string + expect []interface{} + delays []time.Duration + }{ + { + name: "ZREM with wrong number of arguments", + cmds: []string{"ZREM", "ZREM myzset"}, + expect: []interface{}{"ERR wrong number of arguments for 'zrem' command", + "ERR wrong number of arguments for 'zrem' command"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with wrong type of key", + cmds: []string{"SET string_key string_value", "ZREM string_key string_value"}, + expect: []interface{}{"OK", + "WRONGTYPE Operation against a key holding the wrong kind of value"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with non-existent key", + cmds: []string{"ZADD myzset 1 one", "ZREM wrong_myzset one"}, + expect: []interface{}{int64(1), int64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with non-existent element", + cmds: []string{"ZADD myzset 1 one", "ZREM myzset two"}, + expect: []interface{}{int64(1), int64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with sorted set holding single element", + cmds: []string{"ZADD myzset 1 one", "ZREM myzset one"}, + expect: []interface{}{int64(1), int64(1)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with sorted set holding multiple elements", + cmds: []string{"ZADD myzset 1 one 2 two 3 three 4 four", "ZREM myzset four five", "ZREM myzset one two"}, + expect: []interface{}{int64(4), int64(1), int64(2)}, + delays: []time.Duration{0, 0, 0}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + FireCommand(conn, "DEL myzset string_key") + for i, cmd := range tc.cmds { + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + result := FireCommand(conn, cmd) + testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + } + }) + } +} diff --git a/integration_tests/commands/websocket/bloom_test.go b/integration_tests/commands/websocket/bloom_test.go index 1ab59033f..44c28cb08 100644 --- a/integration_tests/commands/websocket/bloom_test.go +++ b/integration_tests/commands/websocket/bloom_test.go @@ -19,19 +19,19 @@ func TestBFReserveAddInfoExists(t *testing.T) { { name: "BF.RESERVE and BF.ADD", cmds: []string{"BF.RESERVE bf 0.01 1000", "BF.ADD bf item1", "BF.EXISTS bf item1"}, - expect: []interface{}{"OK", "1", "1"}, + expect: []interface{}{"OK", float64(1), float64(1)}, cleanUp: []string{"DEL bf"}, }, { name: "BF.EXISTS returns false for non-existing item", cmds: []string{"BF.RESERVE bf 0.01 1000", "BF.EXISTS bf item2"}, - expect: []interface{}{"OK", "0"}, + expect: []interface{}{"OK", float64(0)}, cleanUp: []string{"DEL bf"}, }, { name: "BF.INFO provides correct information", cmds: []string{"BF.RESERVE bf 0.01 1000", "BF.ADD bf item1", "BF.INFO bf"}, - expect: []interface{}{"OK", "1", []interface{}{"Capacity", float64(1000), "Size", float64(10104), "Number of filters", float64(7), "Number of items inserted", float64(1), "Expansion rate", float64(2)}}, + expect: []interface{}{"OK", float64(1), []interface{}{"Capacity", float64(1000), "Size", float64(10104), "Number of filters", float64(7), "Number of items inserted", float64(1), "Expansion rate", float64(2)}}, cleanUp: []string{"DEL bf"}, }, { @@ -112,14 +112,14 @@ func TestBFEdgeCasesAndErrors(t *testing.T) { { name: "BF.ADD to a Bloom filter without reserving", cmds: []string{"BF.ADD bf item1"}, - expect: []interface{}{"1"}, + expect: []interface{}{float64(1)}, delays: []time.Duration{0}, cleanUp: []string{"del bf"}, }, { name: "BF.EXISTS on an unreserved filter", cmds: []string{"BF.EXISTS bf item1"}, - expect: []interface{}{"0"}, + expect: []interface{}{float64(0)}, delays: []time.Duration{0}, cleanUp: []string{"del bf"}, }, @@ -147,14 +147,14 @@ func TestBFEdgeCasesAndErrors(t *testing.T) { { name: "BF.ADD multiple items and check existence", cmds: []string{"BF.RESERVE bf 0.01 1000", "BF.ADD bf item1", "BF.ADD bf item2", "BF.EXISTS bf item1", "BF.EXISTS bf item2", "BF.EXISTS bf item3"}, - expect: []interface{}{"OK", "1", "1", "1", "1", "0"}, + expect: []interface{}{"OK", float64(1), float64(1), float64(1), float64(1), float64(0)}, delays: []time.Duration{0, 0, 0, 0, 0, 0}, cleanUp: []string{"del bf"}, }, { name: "BF.EXISTS after BF.ADD returns false on non-existing item", cmds: []string{"BF.RESERVE bf 0.01 1000", "BF.ADD bf item1", "BF.EXISTS bf nonExistentItem"}, - expect: []interface{}{"OK", "1", "0"}, + expect: []interface{}{"OK", float64(1), float64(0)}, delays: []time.Duration{0, 0, 0}, cleanUp: []string{"del bf"}, }, @@ -168,7 +168,7 @@ func TestBFEdgeCasesAndErrors(t *testing.T) { { name: "BF.INFO after multiple additions", cmds: []string{"BF.RESERVE bf 0.01 1000", "BF.ADD bf item1", "BF.ADD bf item2", "BF.ADD bf item3", "BF.INFO bf"}, - expect: []interface{}{"OK", "1", "1", "1", []interface{}{"Capacity", float64(1000), "Size", float64(10104), "Number of filters", float64(7), "Number of items inserted", float64(3), "Expansion rate", float64(2)}}, + expect: []interface{}{"OK", float64(1), float64(1), float64(1), []interface{}{"Capacity", float64(1000), "Size", float64(10104), "Number of filters", float64(7), "Number of items inserted", float64(3), "Expansion rate", float64(2)}}, delays: []time.Duration{0, 0, 0, 0, 0}, cleanUp: []string{"del bf"}, }, diff --git a/integration_tests/commands/websocket/get_test.go b/integration_tests/commands/websocket/get_test.go index 98d1d854a..b7b9b5688 100644 --- a/integration_tests/commands/websocket/get_test.go +++ b/integration_tests/commands/websocket/get_test.go @@ -20,7 +20,7 @@ func TestGet(t *testing.T) { { name: "Get with expiration", cmds: []string{"SET k v EX 1", "GET k", "GET k"}, - expect: []interface{}{"OK", "v", "(nil)"}, + expect: []interface{}{"OK", "v", nil}, delays: []time.Duration{0, 0, 2 * time.Second}, }, } diff --git a/integration_tests/commands/websocket/helper.go b/integration_tests/commands/websocket/helper.go index ca14c5b43..1face2836 100644 --- a/integration_tests/commands/websocket/helper.go +++ b/integration_tests/commands/websocket/helper.go @@ -1,6 +1,7 @@ package websocket import ( + "fmt" "testing" "github.com/gorilla/websocket" @@ -15,3 +16,12 @@ func DeleteKey(t *testing.T, conn *websocket.Conn, exec *WebsocketCommandExecuto assert.True(t, ok, "error converting response to float64") assert.True(t, respFloat == 1 || respFloat == 0, "unexpected response in %v: %v", cmd, resp) } + +func DeleteHKey(t *testing.T, conn *websocket.Conn, exec *WebsocketCommandExecutor, key, field string) { + cmd := fmt.Sprintf("HDEL %s %s", key, field) + resp, err := exec.FireCommandAndReadResponse(conn, cmd) + assert.Nil(t, err) + respFloat, ok := resp.(float64) + assert.True(t, ok, "error converting response to float64") + assert.True(t, respFloat == 1 || respFloat == 0, "unexpected response in %v: %v", cmd, resp) +} diff --git a/integration_tests/commands/websocket/hexists_test.go b/integration_tests/commands/websocket/hexists_test.go new file mode 100644 index 000000000..19c28b1bb --- /dev/null +++ b/integration_tests/commands/websocket/hexists_test.go @@ -0,0 +1,79 @@ +package websocket + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestHExists(t *testing.T) { + exec := NewWebsocketCommandExecutor() + + testCases := []struct { + name string + commands []string + expected []interface{} + delays []time.Duration + }{ + // { + // name: "WS Check if field exists when k f and v are set", + // commands: []string{"HSET key field value", "HEXISTS key field"}, + // expected: []interface{}{float64(1), "1"}, + // delays: []time.Duration{0, 0}, + // }, + // { + // name: "WS Check if field exists when k exists but not f and v", + // commands: []string{"HSET key field1 value", "HEXISTS key field"}, + // expected: []interface{}{float64(1), "0"}, + // delays: []time.Duration{0, 0}, + // }, + // { + // name: "WS Check if field exists when no k,f and v exist", + // commands: []string{"HEXISTS key field"}, + // expected: []interface{}{"0"}, + // delays: []time.Duration{0}, + // }, + { + name: "WS Check if field exists when k f and v are set", + commands: []string{"HSET key field value", "HEXISTS key field"}, + // Adjusted expected values to match the actual float64 type + expected: []interface{}{float64(1), float64(1)}, + delays: []time.Duration{0, 0}, + }, + { + name: "WS Check if field exists when k exists but not f and v", + commands: []string{"HSET key field1 value", "HEXISTS key field"}, + expected: []interface{}{float64(1), float64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "WS Check if field exists when no k,f and v exist", + commands: []string{"HEXISTS key field"}, + expected: []interface{}{float64(0)}, + delays: []time.Duration{0}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + conn := exec.ConnectToServer() + + t.Log("Clearing keys before test execution") + exec.FireCommandAndReadResponse(conn, "HDEL key field") + exec.FireCommandAndReadResponse(conn, "HDEL key field1") + + for i, cmd := range tc.commands { + t.Logf("Executing command: %s", cmd) + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + + result, err := exec.FireCommandAndReadResponse(conn, cmd) + t.Logf("Received result: %v for command: %s", result, cmd) + assert.Nil(t, err, "Error encountered while executing command %s: %v", cmd, err) + assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s", cmd) + } + }) + } +} diff --git a/integration_tests/commands/websocket/hkeys_test.go b/integration_tests/commands/websocket/hkeys_test.go new file mode 100644 index 000000000..16a86291c --- /dev/null +++ b/integration_tests/commands/websocket/hkeys_test.go @@ -0,0 +1,53 @@ +package websocket + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestHKeys(t *testing.T) { + exec := NewWebsocketCommandExecutor() + + testCases := []struct { + name string + commands []string + expected []interface{} + delays []time.Duration + }{ + { + name: "WS No keys exist", + commands: []string{"HKEYS key"}, + expected: []interface{}{"*0"}, + delays: []time.Duration{0}, + }, + { + name: "WS One or more keys exist", + commands: []string{"HSET key field value", "HKEYS key"}, + expected: []interface{}{float64(1), []interface{}{"field"}}, + delays: []time.Duration{0, 0, 3 * time.Second}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + conn := exec.ConnectToServer() + exec.FireCommandAndReadResponse(conn, "HDEL key field") + exec.FireCommandAndReadResponse(conn, "HDEL key field1") + exec.FireCommandAndReadResponse(conn, "DEL key") + + for i, cmd := range tc.commands { + t.Logf("Executing command: %s", cmd) + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + + result, err := exec.FireCommandAndReadResponse(conn, cmd) + t.Logf("Received result: %v for command: %s", result, cmd) + assert.Nil(t, err, "Error encountered while executing command %s: %v", cmd, err) + assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s", cmd) + } + }) + } +} diff --git a/integration_tests/commands/websocket/hlen_test.go b/integration_tests/commands/websocket/hlen_test.go index caafb3f89..093a89dd6 100644 --- a/integration_tests/commands/websocket/hlen_test.go +++ b/integration_tests/commands/websocket/hlen_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestHLEN(t *testing.T) { @@ -31,7 +31,7 @@ func TestHLEN(t *testing.T) { { name: "HLEN with non-existent key", cmds: []string{"HLEN non_existent_key"}, - expect: []interface{}{"0"}, + expect: []interface{}{float64(0)}, delays: []time.Duration{0}, }, { @@ -68,8 +68,8 @@ func TestHLEN(t *testing.T) { time.Sleep(tc.delays[i]) } result, err := exec.FireCommandAndReadResponse(conn, cmd) - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + assert.Nil(t, err) + assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) } }) } diff --git a/integration_tests/commands/websocket/hrandfield_test.go b/integration_tests/commands/websocket/hrandfield_test.go index dd061933e..f073f0a6f 100644 --- a/integration_tests/commands/websocket/hrandfield_test.go +++ b/integration_tests/commands/websocket/hrandfield_test.go @@ -41,7 +41,7 @@ func TestHRANDFIELD(t *testing.T) { { name: "HRANDFIELD on non-existent key", cmds: []string{"HRANDFIELD key_hrandfield_nonexistent"}, - expect: []interface{}{"(nil)"}, + expect: []interface{}{nil}, delays: []time.Duration{0}, }, { diff --git a/integration_tests/commands/websocket/hscan_test.go b/integration_tests/commands/websocket/hscan_test.go index 2f65ae805..d9f1f8ca2 100644 --- a/integration_tests/commands/websocket/hscan_test.go +++ b/integration_tests/commands/websocket/hscan_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestHSCAN(t *testing.T) { @@ -110,8 +110,8 @@ func TestHSCAN(t *testing.T) { time.Sleep(tc.delays[i]) } result, err := exec.FireCommandAndReadResponse(conn, cmd) - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + assert.Nil(t, err) + assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) } }) } diff --git a/integration_tests/commands/websocket/hstrlen_test.go b/integration_tests/commands/websocket/hstrlen_test.go index 591e507af..3215a0b9e 100644 --- a/integration_tests/commands/websocket/hstrlen_test.go +++ b/integration_tests/commands/websocket/hstrlen_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestHSTRLEN(t *testing.T) { @@ -36,13 +36,13 @@ func TestHSTRLEN(t *testing.T) { { name: "HSTRLEN with non-existent key", cmds: []string{"HSTRLEN non_existent_key field"}, - expect: []interface{}{"0"}, + expect: []interface{}{float64(0)}, delays: []time.Duration{0}, }, { name: "HSTRLEN with non-existent field", cmds: []string{"HSET key_hStrLen2 field value", "HSTRLEN key_hStrLen2 wrong_field"}, - expect: []interface{}{float64(1), "0"}, + expect: []interface{}{float64(1), float64(0)}, delays: []time.Duration{0, 0}, }, @@ -62,8 +62,8 @@ func TestHSTRLEN(t *testing.T) { time.Sleep(tc.delays[i]) } result, err := exec.FireCommandAndReadResponse(conn, cmd) - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + assert.Nil(t, err) + assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) } }) } diff --git a/integration_tests/commands/websocket/hvals_test.go b/integration_tests/commands/websocket/hvals_test.go new file mode 100644 index 000000000..e939a6495 --- /dev/null +++ b/integration_tests/commands/websocket/hvals_test.go @@ -0,0 +1,53 @@ +package websocket + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestHVals(t *testing.T) { + exec := NewWebsocketCommandExecutor() + + testCases := []struct { + name string + commands []string + expected []interface{} + delays []time.Duration + }{ + { + name: "WS No values exist", + commands: []string{"HVALS key"}, + expected: []interface{}{"*0"}, + delays: []time.Duration{3 * time.Second}, + }, + { + name: "WS One or more vals exist", + commands: []string{"HSET key field value", "HVALS key"}, + expected: []interface{}{float64(1), []interface{}{"value"}}, + delays: []time.Duration{0, 0, 3 * time.Second}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + conn := exec.ConnectToServer() + exec.FireCommandAndReadResponse(conn, "HDEL key field") + exec.FireCommandAndReadResponse(conn, "HDEL key field1") + exec.FireCommandAndReadResponse(conn, "DEL key") + + for i, cmd := range tc.commands { + t.Logf("Executing command: %s", cmd) + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + + result, err := exec.FireCommandAndReadResponse(conn, cmd) + t.Logf("Received result: %v for command: %s", result, cmd) + assert.Nil(t, err, "Error encountered while executing command %s: %v", cmd, err) + assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s", cmd) + } + }) + } +} diff --git a/integration_tests/commands/websocket/json_test.go b/integration_tests/commands/websocket/json_test.go index 3d9a33bf5..b5399d93b 100644 --- a/integration_tests/commands/websocket/json_test.go +++ b/integration_tests/commands/websocket/json_test.go @@ -1,12 +1,12 @@ package websocket import ( - "fmt" + "sort" "testing" - "github.com/google/go-cmp/cmp/cmpopts" - testifyAssert "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" + "github.com/dicedb/dice/testutils" + + "github.com/stretchr/testify/assert" ) func TestJSONClearOperations(t *testing.T) { @@ -18,8 +18,8 @@ func TestJSONClearOperations(t *testing.T) { defer func() { resp, err := exec.FireCommandAndReadResponse(conn, "DEL user") - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, float64(1), resp) + assert.Nil(t, err) + assert.Equal(t, float64(1), resp) }() testCases := []struct { @@ -92,8 +92,8 @@ func TestJSONClearOperations(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for i, cmd := range tc.commands { result, err := exec.FireCommandAndReadResponse(conn, cmd) - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, tc.expected[i], result) + assert.Nil(t, err) + assert.Equal(t, tc.expected[i], result) } }) } @@ -109,8 +109,8 @@ func TestJsonStrlen(t *testing.T) { defer func() { resp, err := exec.FireCommandAndReadResponse(conn, "DEL doc") - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, float64(1), resp) + assert.Nil(t, err) + assert.Equal(t, float64(1), resp) }() testCases := []struct { @@ -180,12 +180,12 @@ func TestJsonStrlen(t *testing.T) { t.Run(tc.name, func(t *testing.T) { for i, cmd := range tc.commands { result, err := exec.FireCommandAndReadResponse(conn, cmd) - testifyAssert.Nil(t, err, "error: %v", err) + assert.Nil(t, err, "error: %v", err) stringResult, ok := result.(string) if ok { - testifyAssert.Equal(t, tc.expected[i], stringResult) + assert.Equal(t, tc.expected[i], stringResult) } else { - testifyAssert.True(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{}))) } } }) @@ -206,8 +206,8 @@ func TestJsonObjLen(t *testing.T) { defer func() { resp, err := exec.FireCommandAndReadResponse(conn, "DEL obj") - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, float64(1), resp) + assert.Nil(t, err) + assert.Equal(t, float64(1), resp) }() testCases := []struct { @@ -309,8 +309,8 @@ func TestJsonObjLen(t *testing.T) { cmd := tcase.commands[i] out := tcase.expected[i] result, err := exec.FireCommandAndReadResponse(conn, cmd) - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, out, result) + assert.Nil(t, err) + assert.Equal(t, out, result) } }) } @@ -327,9 +327,9 @@ func TestJsonARRTRIM(t *testing.T) { defer func() { resp1, err := exec.FireCommandAndReadResponse(conn, "DEL a") resp2, err := exec.FireCommandAndReadResponse(conn, "DEL b") - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, float64(1), resp1) - testifyAssert.Equal(t, float64(1), resp2) + assert.Nil(t, err) + assert.Equal(t, float64(1), resp1) + assert.Equal(t, float64(1), resp2) }() testCases := []struct { @@ -394,13 +394,13 @@ func TestJsonARRTRIM(t *testing.T) { cmd := tcase.commands[i] out := tcase.expected[i] result, err := exec.FireCommandAndReadResponse(conn, cmd) - testifyAssert.Nil(t, err) + assert.Nil(t, err) if tcase.assertType[i] == "equal" { - testifyAssert.Equal(t, out, result) + assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } else if tcase.assertType[i] == "jsoneq" { - testifyAssert.JSONEq(t, out.(string), result.(string)) + assert.JSONEq(t, out.(string), result.(string)) } } }) @@ -418,9 +418,9 @@ func TestJsonARRINSERT(t *testing.T) { defer func() { resp1, err := exec.FireCommandAndReadResponse(conn, "DEL a") resp2, err := exec.FireCommandAndReadResponse(conn, "DEL b") - testifyAssert.Nil(t, err) - testifyAssert.Equal(t, float64(1), resp1) - testifyAssert.Equal(t, float64(1), resp2) + assert.Nil(t, err) + assert.Equal(t, float64(1), resp1) + assert.Equal(t, float64(1), resp2) }() testCases := []struct { @@ -473,13 +473,13 @@ func TestJsonARRINSERT(t *testing.T) { cmd := tcase.commands[i] out := tcase.expected[i] result, err := exec.FireCommandAndReadResponse(conn, cmd) - testifyAssert.Nil(t, err) + assert.Nil(t, err) if tcase.assertType[i] == "equal" { - testifyAssert.Equal(t, out, result) + assert.Equal(t, out, result) } else if tcase.assertType[i] == "deep_equal" { - assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) + assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{}))) } else if tcase.assertType[i] == "jsoneq" { - testifyAssert.JSONEq(t, out.(string), result.(string)) + assert.JSONEq(t, out.(string), result.(string)) } } }) @@ -583,20 +583,30 @@ func TestJsonObjKeyslmao(t *testing.T) { for _, tcase := range testCases { t.Run(tcase.name, func(t *testing.T) { _, err := exec.FireCommandAndReadResponse(conn, tcase.setCommand) - testifyAssert.Nil(t, err) + assert.Nil(t, err) expected := tcase.expected - out, err := exec.FireCommandAndReadResponse(conn, tcase.testCommand) + out, _ := exec.FireCommandAndReadResponse(conn, tcase.testCommand) + + sortNested := func(data []interface{}) { + for _, elem := range data { + if innerSlice, ok := elem.([]interface{}); ok { + sort.Slice(innerSlice, func(i, j int) bool { + return innerSlice[i].(string) < innerSlice[j].(string) + }) + } + } + } - _, isString := out.(string) - if isString { - outInterface := []interface{}{out} - assert.DeepEqual(t, outInterface, expected) - } else { - assert.DeepEqual(t, out.([]interface{}), expected, - cmpopts.SortSlices(func(a, b interface{}) bool { - return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) - })) + if expected != nil { + sortNested(expected) } + if outSlice, ok := out.([]interface{}); ok { + sortNested(outSlice) + assert.ElementsMatch(t, expected, outSlice) + } else { + outInterface := []interface{}{out} + assert.ElementsMatch(t, expected, outInterface) + } }) } } @@ -630,3 +640,128 @@ func arraysArePermutations[T comparable](a, b []T) bool { return true } + +func TestJSONARRPOP(t *testing.T) { + exec := NewWebsocketCommandExecutor() + conn := exec.ConnectToServer() + defer conn.Close() + + DeleteKey(t, conn, exec, "key") + + arrayAtRoot := `[0,1,2,3]` + nestedArray := `{"a":2,"b":[0,1,2,3]}` + + testCases := []struct { + name string + commands []string + expected []interface{} + assertType []string + jsonResp []bool + nestedArray bool + path string + }{ + { + name: "update array at root path", + commands: []string{"json.set key $ " + arrayAtRoot, "json.arrpop key $ 2", "json.get key"}, + expected: []interface{}{"OK", float64(2), "[0,1,3]"}, + assertType: []string{"equal", "equal", "deep_equal"}, + }, + { + name: "update nested array", + commands: []string{"json.set key $ " + nestedArray, "json.arrpop key $.b 2", "json.get key"}, + expected: []interface{}{"OK", []interface{}{float64(2)}, `{"a":2,"b":[0,1,3]}`}, + assertType: []string{"equal", "deep_equal", "na"}, + }, + } + + for _, tcase := range testCases { + t.Run(tcase.name, func(t *testing.T) { + for i := 0; i < len(tcase.commands); i++ { + cmd := tcase.commands[i] + out := tcase.expected[i] + result, err := exec.FireCommandAndReadResponse(conn, cmd) + assert.Nil(t, err) + jsonResult, isString := result.(string) + + if isString && testutils.IsJSONResponse(jsonResult) { + assert.JSONEq(t, out.(string), jsonResult) + continue + } + + if tcase.assertType[i] == "equal" { + assert.Equal(t, out, result) + } else if tcase.assertType[i] == "deep_equal" { + assert.True(t, arraysArePermutations(tcase.expected[i].([]interface{}), result.([]interface{}))) + } + } + }) + } +} + +func TestJsonARRAPPEND(t *testing.T) { + exec := NewWebsocketCommandExecutor() + conn := exec.ConnectToServer() + defer conn.Close() + + DeleteKey(t, conn, exec, "key") + a := `[1,2]` + b := `{"name":"jerry","partner":{"name":"tom","score":[10]},"partner2":{"score":[10,20]}}` + c := `{"name":["jerry"],"partner":{"name":"tom","score":[10]},"partner2":{"name":12,"score":"rust"}}` + + testCases := []struct { + name string + commands []string + expected []interface{} + assertType []string + }{ + + { + name: "JSON.ARRAPPEND with root path", + commands: []string{"json.set a $ " + a, `json.arrappend a $ 3`}, + expected: []interface{}{"OK", []interface{}{float64(3)}}, + assertType: []string{"equal", "deep_equal"}, + }, + { + name: "JSON.ARRAPPEND nested", + commands: []string{"JSON.SET doc $ " + b, `JSON.ARRAPPEND doc $..score 10`}, + expected: []interface{}{"OK", []interface{}{float64(2), float64(3)}}, + assertType: []string{"equal", "deep_equal"}, + }, + { + name: "JSON.ARRAPPEND nested with nil", + commands: []string{"JSON.SET doc $ " + c, `JSON.ARRAPPEND doc $..score 10`}, + expected: []interface{}{"OK", []interface{}{float64(2), nil}}, + assertType: []string{"equal", "deep_equal"}, + }, + { + name: "JSON.ARRAPPEND with different datatypes", + commands: []string{"JSON.SET doc $ " + c, "JSON.ARRAPPEND doc $.name 1"}, + expected: []interface{}{"OK", []interface{}{float64(2)}}, + assertType: []string{"equal", "deep_equal"}, + }, + } + for _, tcase := range testCases { + t.Run(tcase.name, func(t *testing.T) { + DeleteKey(t, conn, exec, "a") + DeleteKey(t, conn, exec, "doc") + for i := 0; i < len(tcase.commands); i++ { + cmd := tcase.commands[i] + out := tcase.expected[i] + result, err := exec.FireCommandAndReadResponse(conn, cmd) + assert.Nil(t, err) + jsonResult, isString := result.(string) + + if isString && testutils.IsJSONResponse(jsonResult) { + assert.JSONEq(t, out.(string), jsonResult) + continue + } + + if tcase.assertType[i] == "equal" { + assert.Equal(t, out, result) + } else if tcase.assertType[i] == "deep_equal" { + assert.True(t, arraysArePermutations(tcase.expected[i].([]interface{}), result.([]interface{}))) + } + } + }) + } +} diff --git a/integration_tests/commands/websocket/set_test.go b/integration_tests/commands/websocket/set_test.go index 9f31d4dde..94480ebde 100644 --- a/integration_tests/commands/websocket/set_test.go +++ b/integration_tests/commands/websocket/set_test.go @@ -59,12 +59,12 @@ func TestSetWithOptions(t *testing.T) { { name: "Set with EX option", commands: []string{"SET k v EX 2", "GET k", "SLEEP 3", "GET k"}, - expected: []interface{}{"OK", "v", "OK", "(nil)"}, + expected: []interface{}{"OK", "v", "OK", nil}, }, { name: "Set with PX option", commands: []string{"SET k v PX 2000", "GET k", "SLEEP 3", "GET k"}, - expected: []interface{}{"OK", "v", "OK", "(nil)"}, + expected: []interface{}{"OK", "v", "OK", nil}, }, { name: "Set with EX and PX option", @@ -74,7 +74,7 @@ func TestSetWithOptions(t *testing.T) { { name: "XX on non-existing key", commands: []string{"DEL k", "SET k v XX", "GET k"}, - expected: []interface{}{float64(0), "(nil)", "(nil)"}, + expected: []interface{}{float64(0), nil, nil}, }, { name: "NX on non-existing key", @@ -84,7 +84,7 @@ func TestSetWithOptions(t *testing.T) { { name: "NX on existing key", commands: []string{"DEL k", "SET k v NX", "GET k", "SET k v NX"}, - expected: []interface{}{float64(0), "OK", "v", "(nil)"}, + expected: []interface{}{float64(0), "OK", "v", nil}, }, { name: "PXAT option", @@ -99,7 +99,7 @@ func TestSetWithOptions(t *testing.T) { { name: "PXAT option with invalid unix time ms", commands: []string{"SET k2 v2 PXAT 123123", "GET k2"}, - expected: []interface{}{"OK", "(nil)"}, + expected: []interface{}{"OK", nil}, }, { name: "XX on existing key", @@ -114,12 +114,12 @@ func TestSetWithOptions(t *testing.T) { { name: "EX option", commands: []string{"SET k v EX 1", "GET k", "SLEEP 2", "GET k"}, - expected: []interface{}{"OK", "v", "OK", "(nil)"}, + expected: []interface{}{"OK", "v", "OK", nil}, }, { name: "XX option", commands: []string{"SET k v XX EX 1", "GET k", "SLEEP 2", "GET k", "SET k v XX EX 1", "GET k"}, - expected: []interface{}{"(nil)", "(nil)", "OK", "(nil)", "(nil)", "(nil)"}, + expected: []interface{}{nil, nil, "OK", nil, nil, nil}, }, } @@ -175,7 +175,7 @@ func TestSetWithExat(t *testing.T) { time.Sleep(3 * time.Second) resp, err = exec.FireCommandAndReadResponse(conn, "GET k") assert.Nil(t, err) - assert.Equal(t, "(nil)", resp, "Value mismatch for cmd GET k") + assert.Equal(t, nil, resp, "Value mismatch for cmd GET k") resp, err = exec.FireCommandAndReadResponse(conn, "TTL k") assert.Nil(t, err) @@ -196,7 +196,7 @@ func TestSetWithExat(t *testing.T) { resp, err = exec.FireCommandAndReadResponse(conn, "GET k") assert.Nil(t, err) - assert.Equal(t, "(nil)", resp, "Value mismatch for cmd GET k") + assert.Equal(t, nil, resp, "Value mismatch for cmd GET k") resp, err = exec.FireCommandAndReadResponse(conn, "TTL k") assert.Nil(t, err) @@ -217,18 +217,19 @@ func TestSetWithExat(t *testing.T) { resp, err = exec.FireCommandAndReadResponse(conn, "GET k") assert.Nil(t, err) - assert.Equal(t, "(nil)", resp, "Value mismatch for cmd GET k") + assert.Equal(t, nil, resp, "Value mismatch for cmd GET k") }) } func TestWithKeepTTLFlag(t *testing.T) { exec := NewWebsocketCommandExecutor() + expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10) conn := exec.ConnectToServer() for _, tcase := range []TestCase{ { - commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk"}, - expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv"}, + commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk", "SET K V EX 2 KEEPTTL", "SET K1 vv PX 2000 KEEPTTL", "SET K2 vv EXAT " + expiryTime + " KEEPTTL"}, + expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv", "ERR syntax error", "ERR syntax error", "ERR syntax error"}, }, } { for i := 0; i < len(tcase.commands); i++ { @@ -243,8 +244,7 @@ func TestWithKeepTTLFlag(t *testing.T) { time.Sleep(2 * time.Second) cmd := "GET k" - out := "(nil)" resp, err := exec.FireCommandAndReadResponse(conn, cmd) assert.Nil(t, err) - assert.Equal(t, out, resp, "Value mismatch for cmd %s\n.", cmd) + assert.Equal(t, nil, resp, "Value mismatch for cmd %s\n.", cmd) } diff --git a/integration_tests/commands/websocket/writeretry_test.go b/integration_tests/commands/websocket/writeretry_test.go index b0b270fc8..00aba1e1e 100644 --- a/integration_tests/commands/websocket/writeretry_test.go +++ b/integration_tests/commands/websocket/writeretry_test.go @@ -94,11 +94,20 @@ func startWebSocketServer() { // Helper to create a WebSocket connection for testing func createWebSocketConn(t *testing.T) *websocket.Conn { once.Do(startWebSocketServer) + var conn *websocket.Conn + var err error u := url.URL{Scheme: "ws", Host: serverAddr, Path: "/ws"} - conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil) - if err != nil { - t.Fatalf("Failed to connect to WebSocket server: %v", err) + + // Retry up to 5 times with a short delay + for i := 0; i < 5; i++ { + conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil) + if err == nil { + return conn + } + time.Sleep(200 * time.Millisecond) // Adjust delay as necessary } - return conn + + t.Fatalf("Failed to connect to WebSocket server: %v", err) + return nil } diff --git a/integration_tests/commands/websocket/zcard_test.go b/integration_tests/commands/websocket/zcard_test.go new file mode 100644 index 000000000..34561134b --- /dev/null +++ b/integration_tests/commands/websocket/zcard_test.go @@ -0,0 +1,71 @@ +package websocket + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestZCARD(t *testing.T) { + exec := NewWebsocketCommandExecutor() + conn := exec.ConnectToServer() + DeleteKey(t, conn, exec, "string_key") + DeleteKey(t, conn, exec, "myzset1") + DeleteKey(t, conn, exec, "myzset2") + DeleteKey(t, conn, exec, "myzset3") + + testCases := []struct { + name string + cmds []string + expect []interface{} + delays []time.Duration + }{ + { + name: "ZCARD with wrong number of arguments", + cmds: []string{"ZCARD", "ZCARD myzset field"}, + expect: []interface{}{"ERR wrong number of arguments for 'zcard' command", + "ERR wrong number of arguments for 'zcard' command"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with wrong type of key", + cmds: []string{"SET string_key string_value", "ZCARD string_key"}, + expect: []interface{}{"OK", + "WRONGTYPE Operation against a key holding the wrong kind of value"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with non-existent key", + cmds: []string{"ZADD myzset1 1 one", "ZCARD wrong_myzset1"}, + expect: []interface{}{float64(1), float64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with sorted set holding single element", + cmds: []string{"ZADD myzset2 1 one", "ZCARD myzset2"}, + expect: []interface{}{float64(1), float64(1)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZCARD with sorted set holding multiple elements", + cmds: []string{"ZADD myzset3 1 one 2 two", "ZCARD myzset3", "ZADD myzset3 3 three", "ZCARD myzset3", "ZREM myzset3 two", "ZCARD myzset3"}, + expect: []interface{}{float64(2), float64(2), float64(1), float64(3), float64(1), float64(2)}, + delays: []time.Duration{0, 0, 0, 0, 0, 0}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + for i, cmd := range tc.cmds { + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + result, err := exec.FireCommandAndReadResponse(conn, cmd) + assert.Nil(t, err) + assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + } + }) + } +} diff --git a/integration_tests/commands/websocket/zrank_test.go b/integration_tests/commands/websocket/zrank_test.go index 163be094a..c48694e35 100644 --- a/integration_tests/commands/websocket/zrank_test.go +++ b/integration_tests/commands/websocket/zrank_test.go @@ -31,7 +31,7 @@ func TestZRANK(t *testing.T) { { name: "ZRANK of non-existing member", commands: []string{"ZRANK myset member6"}, - expected: []interface{}{"(nil)"}, + expected: []interface{}{nil}, }, { name: "ZRANK with WITHSCORE option for existing member", @@ -41,12 +41,12 @@ func TestZRANK(t *testing.T) { { name: "ZRANK with WITHSCORE option for non-existing member", commands: []string{"ZRANK myset member6 WITHSCORE"}, - expected: []interface{}{"(nil)"}, + expected: []interface{}{nil}, }, { name: "ZRANK on non-existing myset", commands: []string{"ZRANK nonexisting member1"}, - expected: []interface{}{"(nil)"}, + expected: []interface{}{nil}, }, { name: "ZRANK with wrong number of arguments", diff --git a/integration_tests/commands/websocket/zrem_test.go b/integration_tests/commands/websocket/zrem_test.go new file mode 100644 index 000000000..ef8f9e3ae --- /dev/null +++ b/integration_tests/commands/websocket/zrem_test.go @@ -0,0 +1,78 @@ +package websocket + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestZREM(t *testing.T) { + exec := NewWebsocketCommandExecutor() + conn := exec.ConnectToServer() + DeleteKey(t, conn, exec, "string_key") + DeleteKey(t, conn, exec, "myzset1") + DeleteKey(t, conn, exec, "myzset2") + DeleteKey(t, conn, exec, "myzset3") + DeleteKey(t, conn, exec, "myzset4") + + testCases := []struct { + name string + cmds []string + expect []interface{} + delays []time.Duration + }{ + { + name: "ZREM with wrong number of arguments", + cmds: []string{"ZREM", "ZREM myzset"}, + expect: []interface{}{"ERR wrong number of arguments for 'zrem' command", + "ERR wrong number of arguments for 'zrem' command"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with wrong type of key", + cmds: []string{"SET string_key string_value", "ZREM string_key string_value"}, + expect: []interface{}{"OK", + "WRONGTYPE Operation against a key holding the wrong kind of value"}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with non-existent key", + cmds: []string{"ZADD myzset1 1 one", "ZREM wrong_myzset1 one"}, + expect: []interface{}{float64(1), float64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with non-existent element", + cmds: []string{"ZADD myzset2 1 one", "ZREM myzset2 two"}, + expect: []interface{}{float64(1), float64(0)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with sorted set holding single element", + cmds: []string{"ZADD myzset3 1 one", "ZREM myzset3 one"}, + expect: []interface{}{float64(1), float64(1)}, + delays: []time.Duration{0, 0}, + }, + { + name: "ZREM with sorted set holding multiple elements", + cmds: []string{"ZADD myzset4 1 one 2 two 3 three 4 four", "ZREM myzset4 four five", "ZREM myzset4 one two"}, + expect: []interface{}{float64(4), float64(1), float64(2)}, + delays: []time.Duration{0, 0, 0}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + for i, cmd := range tc.cmds { + if tc.delays[i] > 0 { + time.Sleep(tc.delays[i]) + } + result, err := exec.FireCommandAndReadResponse(conn, cmd) + assert.Nil(t, err) + assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd) + } + }) + } +} diff --git a/integration_tests/server/max_conn_test.go b/integration_tests/server/max_conn_test.go index d8d667ccc..3c18a0a3f 100644 --- a/integration_tests/server/max_conn_test.go +++ b/integration_tests/server/max_conn_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" commands "github.com/dicedb/dice/integration_tests/commands/async" ) diff --git a/internal/clientio/io_test.go b/internal/clientio/io_test.go index 3d4db2e13..f99e641ba 100644 --- a/internal/clientio/io_test.go +++ b/internal/clientio/io_test.go @@ -9,7 +9,7 @@ import ( "github.com/dicedb/dice/config" "github.com/dicedb/dice/internal/server/utils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) // MockReadWriter to simulate different io behaviors diff --git a/internal/clientio/resp_test.go b/internal/clientio/resp_test.go index 5bc897398..97cbacd3e 100644 --- a/internal/clientio/resp_test.go +++ b/internal/clientio/resp_test.go @@ -8,7 +8,7 @@ import ( "github.com/dicedb/dice/internal/clientio" "github.com/dicedb/dice/internal/server/utils" - testifyAssert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestSimpleStringDecode(t *testing.T) { @@ -112,7 +112,7 @@ func TestSimpleStrings(t *testing.T) { var b []byte var buf = bytes.NewBuffer(b) for i := 0; i < 1024; i++ { - buf.WriteByte('a' + byte(i % 26)) + buf.WriteByte('a' + byte(i%26)) e := clientio.Encode(buf.String(), true) p := clientio.NewRESPParser(bytes.NewBuffer(e)) nv, err := p.DecodeOne() @@ -130,7 +130,7 @@ func TestBulkStrings(t *testing.T) { var b []byte var buf = bytes.NewBuffer(b) for i := 0; i < 1024; i++ { - buf.WriteByte('a' + byte(i % 26)) + buf.WriteByte('a' + byte(i%26)) e := clientio.Encode(buf.String(), false) p := clientio.NewRESPParser(bytes.NewBuffer(e)) nv, err := p.DecodeOne() @@ -163,7 +163,7 @@ func TestArrayInt(t *testing.T) { var b []byte var buf = bytes.NewBuffer(b) for i := 0; i < 1024; i++ { - buf.WriteByte('a' + byte(i % 26)) + buf.WriteByte('a' + byte(i%26)) e := clientio.Encode(buf.String(), true) p := clientio.NewRESPParser(bytes.NewBuffer(e)) nv, err := p.DecodeOne() @@ -194,7 +194,7 @@ func TestBoolean(t *testing.T) { for _, v := range tests { ev := clientio.Encode(v.input, false) - testifyAssert.Equal(t, ev, v.output) + assert.Equal(t, ev, v.output) } } @@ -215,6 +215,6 @@ func TestInteger(t *testing.T) { for _, v := range tests { ev := clientio.Encode(v.input, false) - testifyAssert.Equal(t, ev, v.output) + assert.Equal(t, ev, v.output) } } diff --git a/internal/dencoding/dencoding_benchmark_test.go b/internal/dencoding/dencoding_benchmark_test.go index 686a1da4d..66787c403 100644 --- a/internal/dencoding/dencoding_benchmark_test.go +++ b/internal/dencoding/dencoding_benchmark_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/dicedb/dice/internal/dencoding" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func BenchmarkEncodeDecodeInt(b *testing.B) { diff --git a/internal/dencoding/int_test.go b/internal/dencoding/int_test.go index a9bc93bf2..9d2663044 100644 --- a/internal/dencoding/int_test.go +++ b/internal/dencoding/int_test.go @@ -7,7 +7,7 @@ import ( "github.com/dicedb/dice/internal/dencoding" "github.com/dicedb/dice/testutils" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestDencodingUInt(t *testing.T) { @@ -51,7 +51,7 @@ func TestDencodingUInt(t *testing.T) { for value, expected := range testCases { t.Run(fmt.Sprintf("value_%d", value), func(t *testing.T) { encoded := dencoding.EncodeUInt(uint64(value)) - assert.Assert(t, testutils.EqualByteSlice(encoded, expected), "Unexpected encoding") + assert.True(t, testutils.EqualByteSlice(encoded, expected), "Unexpected encoding") }) } }) @@ -102,7 +102,7 @@ func TestDencodingInt(t *testing.T) { for value, expected := range testCases { t.Run(fmt.Sprintf("value_%d", value), func(t *testing.T) { encoded := dencoding.EncodeInt(int64(value)) - assert.Assert(t, testutils.EqualByteSlice(encoded, expected), "Unexpected encoding") + assert.True(t, testutils.EqualByteSlice(encoded, expected), "Unexpected encoding") }) } }) diff --git a/internal/eval/bloom_test.go b/internal/eval/bloom_test.go index 32452ec7d..8e027eaa4 100644 --- a/internal/eval/bloom_test.go +++ b/internal/eval/bloom_test.go @@ -9,7 +9,7 @@ import ( "github.com/dicedb/dice/internal/clientio" dstore "github.com/dicedb/dice/internal/store" - assert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func TestBloomFilter(t *testing.T) { diff --git a/internal/eval/bytearray_test.go b/internal/eval/bytearray_test.go index 86b945a90..efaa10f06 100644 --- a/internal/eval/bytearray_test.go +++ b/internal/eval/bytearray_test.go @@ -3,7 +3,7 @@ package eval import ( "testing" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestMixedOperations(t *testing.T) { @@ -143,9 +143,9 @@ func TestDeepCopy(t *testing.T) { // Modify the deepCopy's data to ensure it's independent of the original deepCopy.data[0] = 9 - assert.Assert(t, deepCopy.data[0] != original.data[0], "ByteArray DeepCopy did not create an independent deepCopy, original and deepCopy data are linked") + assert.True(t, deepCopy.data[0] != original.data[0], "ByteArray DeepCopy did not create an independent deepCopy, original and deepCopy data are linked") // Modify the original's data to ensure it doesn't affect the deepCopy original.data[1] = 8 - assert.Assert(t, deepCopy.data[1] != original.data[1], "ByteArray DeepCopy did not create an independent deepCopy, original and deepCopy data are linked") + assert.True(t, deepCopy.data[1] != original.data[1], "ByteArray DeepCopy did not create an independent deepCopy, original and deepCopy data are linked") } diff --git a/internal/eval/bytelist_test.go b/internal/eval/bytelist_test.go index 822974eb9..2d22d2ebd 100644 --- a/internal/eval/bytelist_test.go +++ b/internal/eval/bytelist_test.go @@ -2,7 +2,7 @@ package eval import ( "bytes" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" "testing" ) @@ -95,9 +95,9 @@ func TestByteListDeepCopy(t *testing.T) { // Verify that changes to the deepCopy do not affect the original deepCopy.head.buf[0] = 9 - assert.Assert(t, original.head.buf[0] != deepCopy.head.buf[0], "Original and deepCopy head buffer should not be linked") + assert.True(t, original.head.buf[0] != deepCopy.head.buf[0], "Original and deepCopy head buffer should not be linked") // Verify that changes to the original do not affect the deepCopy original.head.buf[1] = 8 - assert.Assert(t, original.head.buf[1] != deepCopy.head.buf[1], "Original and deepCopy head buffer should not be linked") + assert.True(t, original.head.buf[1] != deepCopy.head.buf[1], "Original and deepCopy head buffer should not be linked") } diff --git a/internal/eval/commands.go b/internal/eval/commands.go index e386081e1..2765d7313 100644 --- a/internal/eval/commands.go +++ b/internal/eval/commands.go @@ -260,9 +260,9 @@ var ( Retrieves the keys of a JSON object stored at path specified. Null reply: If the key doesn't exist or has expired. Error reply: If the number of arguments is incorrect or the stored value is not a JSON type.`, - NewEval: evalJSONOBJKEYS, + NewEval: evalJSONOBJKEYS, IsMigrated: true, - Arity: 2, + Arity: 2, } jsonarrpopCmdMeta = DiceCmdMeta{ Name: "JSON.ARRPOP", @@ -294,10 +294,10 @@ var ( Returns nil if the matching JSON value is not an array. Returns error response if the key doesn't exist or key is expired or the matching value is not an array. Error reply: If the number of arguments is incorrect.`, - NewEval: evalJSONARRINSERT, + NewEval: evalJSONARRINSERT, IsMigrated: true, - Arity: -5, - KeySpecs: KeySpecs{BeginIndex: 1}, + Arity: -5, + KeySpecs: KeySpecs{BeginIndex: 1}, } jsonrespCmdMeta = DiceCmdMeta{ Name: "JSON.RESP", @@ -314,9 +314,9 @@ var ( Returns an array of integer replies for each path. Returns error response if the key doesn't exist or key is expired. Error reply: If the number of arguments is incorrect.`, - NewEval: evalJSONARRTRIM, + NewEval: evalJSONARRTRIM, IsMigrated: true, - Arity: -5, + Arity: -5, } ttlCmdMeta = DiceCmdMeta{ Name: "TTL", @@ -696,11 +696,12 @@ var ( KeySpecs: KeySpecs{BeginIndex: 1}, } hkeysCmdMeta = DiceCmdMeta{ - Name: "HKEYS", - Info: `HKEYS command is used to retrieve all the keys(or field names) within a hash. Complexity is O(n) where n is the size of the hash.`, - Eval: evalHKEYS, - Arity: 1, - KeySpecs: KeySpecs{BeginIndex: 1}, + Name: "HKEYS", + Info: `HKEYS command is used to retrieve all the keys(or field names) within a hash. Complexity is O(n) where n is the size of the hash.`, + NewEval: evalHKEYS, + Arity: 1, + KeySpecs: KeySpecs{BeginIndex: 1}, + IsMigrated: true, } hsetnxCmdMeta = DiceCmdMeta{ Name: "HSETNX", @@ -734,11 +735,12 @@ var ( KeySpecs: KeySpecs{BeginIndex: 1}, } hValsCmdMeta = DiceCmdMeta{ - Name: "HVALS", - Info: `Returns all values of the hash stored at key. The length of the reply is same as the size of the hash.`, - Eval: evalHVALS, - Arity: -2, - KeySpecs: KeySpecs{BeginIndex: 1}, + Name: "HVALS", + Info: `Returns all values of the hash stored at key. The length of the reply is same as the size of the hash.`, + NewEval: evalHVALS, + Arity: -2, + KeySpecs: KeySpecs{BeginIndex: 1}, + IsMigrated: true, } hincrbyCmdMeta = DiceCmdMeta{ Name: "HINCRBY", @@ -783,11 +785,12 @@ var ( KeySpecs: KeySpecs{BeginIndex: 1}, } hexistsCmdMeta = DiceCmdMeta{ - Name: "HEXISTS", - Info: `Returns if field is an existing field in the hash stored at key.`, - Eval: evalHEXISTS, - Arity: -3, - KeySpecs: KeySpecs{BeginIndex: 1}, + Name: "HEXISTS", + Info: `Returns if field is an existing field in the hash stored at key.`, + NewEval: evalHEXISTS, + Arity: -3, + KeySpecs: KeySpecs{BeginIndex: 1}, + IsMigrated: true, } objectCmdMeta = DiceCmdMeta{ @@ -1135,9 +1138,9 @@ var ( zpopminCmdMeta = DiceCmdMeta{ Name: "ZPOPMIN", Info: `ZPOPMIN key [count] - Removes and returns the member with the lowest score from the sorted set at the specified key. - If multiple members have the same score, the one that comes first alphabetically is returned. - You can also specify a count to remove and return multiple members at once. + Removes and returns the member with the lowest score from the sorted set at the specified key. + If multiple members have the same score, the one that comes first alphabetically is returned. + You can also specify a count to remove and return multiple members at once. If the set is empty, it returns an empty result.`, Arity: -1, KeySpecs: KeySpecs{BeginIndex: 1}, @@ -1155,6 +1158,25 @@ var ( IsMigrated: true, NewEval: evalZRANK, } + zcardCmdMeta = DiceCmdMeta{ + Name: "ZCARD", + Info: `ZCARD key + Returns the sorted set cardinality (number of elements) of the sorted set stored at key.`, + Arity: 2, + KeySpecs: KeySpecs{BeginIndex: 1}, + IsMigrated: true, + NewEval: evalZCARD, + } + zremCmdMeta = DiceCmdMeta{ + Name: "ZREM", + Info: `ZREM key member [member ...] + Removes the specified members from the sorted set stored at key. Non existing members are ignored. + An error is returned when key exists and does not hold a sorted set.`, + Arity: -3, + KeySpecs: KeySpecs{BeginIndex: 1}, + IsMigrated: true, + NewEval: evalZREM, + } bitfieldCmdMeta = DiceCmdMeta{ Name: "BITFIELD", Info: `The command treats a string as an array of bits as well as bytearray data structure, @@ -1181,7 +1203,7 @@ var ( } bitfieldroCmdMeta = DiceCmdMeta{ Name: "BITFIELD_RO", - Info: `It is read-only variant of the BITFIELD command. + Info: `It is read-only variant of the BITFIELD command. It is like the original BITFIELD but only accepts GET subcommand.`, Arity: -1, KeySpecs: KeySpecs{BeginIndex: 1}, @@ -1218,7 +1240,7 @@ var ( Name: "JSON.STRAPPEND", Info: `JSON.STRAPPEND key [path] value Append the JSON string values to the string at path - Returns an array of integer replies for each path, the string's new length, or nil, if the matching JSON value is not a string. + Returns an array of integer replies for each path, the string's new length, or nil, if the matching JSON value is not a string. Error reply: If the value at path is not a string or if the key doesn't exist.`, Eval: evalJSONSTRAPPEND, Arity: 3, @@ -1406,6 +1428,8 @@ func init() { DiceCmds["ZPOPMAX"] = zpopmaxCmdMeta DiceCmds["ZPOPMIN"] = zpopminCmdMeta DiceCmds["ZRANK"] = zrankCmdMeta + DiceCmds["ZCARD"] = zcardCmdMeta + DiceCmds["ZREM"] = zremCmdMeta DiceCmds["JSON.STRAPPEND"] = jsonstrappendCmdMeta DiceCmds["CMS.INITBYDIM"] = cmsInitByDimCmdMeta DiceCmds["CMS.INITBYPROB"] = cmsInitByProbCmdMeta diff --git a/internal/eval/deque_test.go b/internal/eval/deque_test.go index da2422908..b21ee3243 100644 --- a/internal/eval/deque_test.go +++ b/internal/eval/deque_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) var deqRandGenerator *rand.Rand diff --git a/internal/eval/eval.go b/internal/eval/eval.go index 9b95bd853..f0370fbbb 100644 --- a/internal/eval/eval.go +++ b/internal/eval/eval.go @@ -53,7 +53,16 @@ type EvalResponse struct { Error error // Error holds any error that occurred during the operation. If no error, it will be nil. } -//go:inline +// Following functions should be used to create a new EvalResponse with the given result and error. +// These ensure that result and error are mutually exclusive. +// If result is nil, then error should be non-nil and vice versa. + +// makeEvalResult creates a new EvalResponse with the given result and nil error. +// This is a helper function to create a new EvalResponse with the given result and nil error. +/** + * @param {interface{}} result - The result of the store operation. + * @returns {EvalResponse} A new EvalResponse with the given result and nil error. + */ func makeEvalResult(result interface{}) *EvalResponse { return &EvalResponse{ Result: result, @@ -61,7 +70,12 @@ func makeEvalResult(result interface{}) *EvalResponse { } } -//go:inline +// makeEvalError creates a new EvalResponse with the given error and nil result. +// This is a helper function to create a new EvalResponse with the given error and nil result. +/** + * @param {error} err - The error that occurred during the store operation. + * @returns {EvalResponse} A new EvalResponse with the given error and nil result. + */ func makeEvalError(err error) *EvalResponse { return &EvalResponse{ Result: nil, @@ -2393,39 +2407,6 @@ func insertInHashMap(args []string, store *dstore.Store) (numKeys int64, err2 [] return numKeys, nil } -// evalHKEYS is used toretrieve all the keys(or field names) within a hash. -// -// This command returns empty array, if the specified key doesn't exist. -// -// Complexity is O(n) where n is the size of the hash. -// -// Usage: HKEYS key -func evalHKEYS(args []string, store *dstore.Store) []byte { - if len(args) != 1 { - return diceerrors.NewErrArity("HKEYS") - } - - key := args[0] - obj := store.Get(key) - - var hashMap HashMap - var result []string - - if obj != nil { - if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil { - return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr) - } - hashMap = obj.Value.(HashMap) - } else { - return clientio.Encode([]interface{}{}, false) - } - - for hmKey := range hashMap { - result = append(result, hmKey) - } - - return clientio.Encode(result, false) -} func evalHSETNX(args []string, store *dstore.Store) []byte { if len(args) != 3 { return diceerrors.NewErrArity("HSETNX") @@ -2554,68 +2535,6 @@ func evalHDEL(args []string, store *dstore.Store) []byte { return clientio.Encode(count, false) } -// evalHKEYS returns all the values in the hash stored at key. -func evalHVALS(args []string, store *dstore.Store) []byte { - if len(args) != 1 { - return diceerrors.NewErrArity("HVALS") - } - - key := args[0] - obj := store.Get(key) - - if obj == nil { - return clientio.Encode([]string{}, false) // Return an empty array for non-existent keys - } - - if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil { - return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr) - } - - hashMap := obj.Value.(HashMap) - results := make([]string, 0, len(hashMap)) - - for _, value := range hashMap { - results = append(results, value) - } - - return clientio.Encode(results, false) -} - -// evalHEXISTS returns if field is an existing field in the hash stored at key. -// -// This command returns 0, if the specified field doesn't exist in the key and 1 if it exists. -// -// If key doesn't exist, it returns 0. -// -// Usage: HEXISTS key field -func evalHEXISTS(args []string, store *dstore.Store) []byte { - if len(args) != 2 { - return diceerrors.NewErrArity("HEXISTS") - } - - key := args[0] - hmKey := args[1] - obj := store.Get(key) - - var hashMap HashMap - - if obj == nil { - return clientio.Encode(0, false) - } - if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil { - return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr) - } - - hashMap = obj.Value.(HashMap) - - _, ok := hashMap.Get(hmKey) - if ok { - return clientio.Encode(1, false) - } - // Return 0, if specified field doesn't exist in the HashMap. - return clientio.Encode(0, false) -} - func evalObjectIdleTime(key string, store *dstore.Store) []byte { obj := store.GetNoTouch(key) if obj == nil { diff --git a/internal/eval/eval_test.go b/internal/eval/eval_test.go index c85d3ee1d..cf28589ca 100644 --- a/internal/eval/eval_test.go +++ b/internal/eval/eval_test.go @@ -23,8 +23,7 @@ import ( diceerrors "github.com/dicedb/dice/internal/errors" "github.com/dicedb/dice/internal/object" dstore "github.com/dicedb/dice/internal/store" - testifyAssert "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) type evalTestCase struct { @@ -112,6 +111,8 @@ func TestEval(t *testing.T) { testEvalZPOPMAX(t, store) testEvalZPOPMIN(t, store) testEvalZRANK(t, store) + testEvalZCARD(t, store) + testEvalZREM(t, store) testEvalHVALS(t, store) testEvalBitField(t, store) testEvalHINCRBYFLOAT(t, store) @@ -305,6 +306,51 @@ func testEvalSET(t *testing.T, store *dstore.Store) { input: []string{"KEY", "VAL", Pxat, strconv.FormatInt(time.Now().Add(2*time.Minute).UnixMilli(), 10)}, migratedOutput: EvalResponse{Result: clientio.OK, Error: nil}, }, + { + name: "key val pair and invalid EX and PX", + input: []string{"KEY", "VAL", Ex, "2", Px, "2000"}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")}, + }, + { + name: "key val pair and invalid EX and PXAT", + input: []string{"KEY", "VAL", Ex, "2", Pxat, "2"}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")}, + }, + { + name: "key val pair and invalid PX and PXAT", + input: []string{"KEY", "VAL", Px, "2000", Pxat, "2"}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")}, + }, + { + name: "key val pair and KeepTTL", + input: []string{"KEY", "VAL", KeepTTL}, + migratedOutput: EvalResponse{Result: clientio.OK, Error: nil}, + }, + { + name: "key val pair and invalid KeepTTL", + input: []string{"KEY", "VAL", KeepTTL, "2"}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")}, + }, + { + name: "key val pair and KeepTTL, EX", + input: []string{"KEY", "VAL", Ex, "2", KeepTTL}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")}, + }, + { + name: "key val pair and KeepTTL, PX", + input: []string{"KEY", "VAL", Px, "2000", KeepTTL}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")}, + }, + { + name: "key val pair and KeepTTL, PXAT", + input: []string{"KEY", "VAL", Pxat, strconv.FormatInt(time.Now().Add(2*time.Minute).UnixMilli(), 10), KeepTTL}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")}, + }, + { + name: "key val pair and KeepTTL, invalid PXAT", + input: []string{"KEY", "VAL", Pxat, "invalid_expiry_val", KeepTTL}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR value is not an integer or out of range")}, + }, } for _, tt := range tests { @@ -314,16 +360,16 @@ func testEvalSET(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -452,16 +498,16 @@ func testEvalGET(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -526,16 +572,16 @@ func testEvalGETSET(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -920,16 +966,16 @@ func testEvalJSONARRTRIM(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { - testifyAssert.Equal(t, tt.migratedOutput.Result, response.Result) + assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -1088,16 +1134,16 @@ func testEvalJSONARRINSERT(t *testing.T, store *dstore.Store) { if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { - testifyAssert.Equal(t, tt.migratedOutput.Result, response.Result) + assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -1202,16 +1248,16 @@ func testEvalJSONARRLEN(t *testing.T, store *dstore.Store) { if tt.migratedOutput.Result != nil { if slice, ok := tt.migratedOutput.Result.([]interface{}); ok { - assert.DeepEqual(t, slice, response.Result) + assert.Equal(t, slice, response.Result) } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -1386,16 +1432,16 @@ func testEvalJSONOBJLEN(t *testing.T, store *dstore.Store) { response := evalJSONOBJLEN(tt.input, store) if tt.migratedOutput.Result != nil { if slice, ok := tt.migratedOutput.Result.([]interface{}); ok { - assert.DeepEqual(t, slice, response.Result) + assert.Equal(t, slice, response.Result) } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -1702,16 +1748,16 @@ func testEvalJSONCLEAR(t *testing.T, store *dstore.Store) { response := evalJSONCLEAR(tt.input, store) if tt.migratedOutput.Result != nil { if slice, ok := tt.migratedOutput.Result.([]interface{}); ok { - assert.DeepEqual(t, slice, response.Result) + assert.Equal(t, slice, response.Result) } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -1915,8 +1961,8 @@ func testEvalJSONSET(t *testing.T, store *dstore.Store) { input: []string{"doc", "$", "{\"a\":}"}, output: nil, validator: func(output []byte) { - assert.Assert(t, output != nil) - assert.Assert(t, strings.Contains(string(output), "-ERR invalid JSON:")) + assert.True(t, output != nil) + assert.True(t, strings.Contains(string(output), "-ERR invalid JSON:")) }, }, "valid json path": { @@ -2175,9 +2221,9 @@ func testEvalJSONARRAPPEND(t *testing.T, store *dstore.Store) { } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -2321,9 +2367,9 @@ func testEvalTTL(t *testing.T, store *dstore.Store) { }, input: []string{"EXISTING_KEY"}, validator: func(output []byte) { - assert.Assert(t, output != nil) - assert.Assert(t, !bytes.Equal(output, clientio.RespMinusOne)) - assert.Assert(t, !bytes.Equal(output, clientio.RespMinusTwo)) + assert.True(t, output != nil) + assert.True(t, !bytes.Equal(output, clientio.RespMinusOne)) + assert.True(t, !bytes.Equal(output, clientio.RespMinusTwo)) }, }, "key exists but expired": { @@ -2839,23 +2885,26 @@ func testEvalHMGET(t *testing.T, store *dstore.Store) { } func testEvalHVALS(t *testing.T, store *dstore.Store) { - tests := map[string]evalTestCase{ - "wrong number of args passed": { - setup: func() {}, - input: nil, - output: []byte("-ERR wrong number of arguments for 'hvals' command\r\n"), + tests := []evalTestCase{ + { + name: "HVALS wrong number of args passed", + setup: nil, + input: nil, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR wrong number of arguments for 'hvals' command")}, }, - "key doesn't exists": { - setup: func() {}, - input: []string{"NONEXISTENTHVALSKEY"}, - output: clientio.Encode([]string{}, false), + { + name: "HVALS key doesn't exists", + setup: nil, + input: []string{"NONEXISTENTHVALSKEY"}, + migratedOutput: EvalResponse{Result: clientio.EmptyArray, Error: nil}, }, - "key exists": { + { + name: "HVALS key exists", setup: func() { - key := "KEY_MOCK" - field := "mock_field_name" + key := "MOCK_KEY" + field := "mock_field" newMap := make(HashMap) - newMap[field] = "mock_field_value" + newMap[field] = "mock_value" obj := &object.Obj{ TypeEncoding: object.ObjTypeHashMap | object.ObjEncodingHashMap, @@ -2865,12 +2914,44 @@ func testEvalHVALS(t *testing.T, store *dstore.Store) { store.Put(key, obj) }, - input: []string{"KEY_MOCK"}, - output: clientio.Encode([]string{"mock_field_value"}, false), + input: []string{"MOCK_KEY"}, + migratedOutput: EvalResponse{Result: []string{"mock_value"}, Error: nil}, }, } - runEvalTests(t, tests, evalHVALS, store) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if tt.setup != nil { + tt.setup() + } + + response := evalHVALS(tt.input, store) + + fmt.Printf("Eval Response: %v\n", response) + + // Handle comparison for byte slices + if responseBytes, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { + if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { + assert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal") + } + } else { + fmt.Printf("G1: %v | %v\n", response.Result, tt.migratedOutput.Result) + switch e := tt.migratedOutput.Result.(type) { + case []interface{}, []string: + assert.ElementsMatch(t, e, response.Result) + default: + assert.Equal(t, tt.migratedOutput.Result, response.Result) + } + } + + if tt.migratedOutput.Error != nil { + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + } else { + assert.NoError(t, response.Error) + } + }) + } } func testEvalHSTRLEN(t *testing.T, store *dstore.Store) { @@ -2932,23 +3013,27 @@ func testEvalHSTRLEN(t *testing.T, store *dstore.Store) { } func testEvalHEXISTS(t *testing.T, store *dstore.Store) { - tests := map[string]evalTestCase{ - "wrong number of args passed": { - setup: func() {}, - input: nil, - output: []byte("-ERR wrong number of arguments for 'hexists' command\r\n"), + tests := []evalTestCase{ + { + name: "HEXISTS wrong number of args passed", + setup: nil, + input: nil, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR wrong number of arguments for 'hexists' command")}, }, - "only key passed": { - setup: func() {}, - input: []string{"KEY"}, - output: []byte("-ERR wrong number of arguments for 'hexists' command\r\n"), + { + name: "HEXISTS only key passed", + setup: nil, + input: []string{"KEY"}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR wrong number of arguments for 'hexists' command")}, }, - "key doesn't exist": { - setup: func() {}, - input: []string{"KEY", "field_name"}, - output: clientio.Encode(0, false), + { + name: "HEXISTS key doesn't exist", + setup: nil, + input: []string{"KEY", "field_name"}, + migratedOutput: EvalResponse{Result: clientio.IntegerZero, Error: nil}, }, - "key exists but field_name doesn't exists": { + { + name: "HEXISTS key exists but field_name doesn't exists", setup: func() { key := "KEY_MOCK" field := "mock_field_name" @@ -2963,10 +3048,11 @@ func testEvalHEXISTS(t *testing.T, store *dstore.Store) { store.Put(key, obj) }, - input: []string{"KEY_MOCK", "non_existent_key"}, - output: clientio.Encode(0, false), + input: []string{"KEY_MOCK", "non_existent_key"}, + migratedOutput: EvalResponse{Result: clientio.IntegerZero, Error: nil}, }, - "both key and field_name exists": { + { + name: "HEXISTS both key and field_name exists", setup: func() { key := "KEY_MOCK" field := "mock_field_name" @@ -2981,12 +3067,39 @@ func testEvalHEXISTS(t *testing.T, store *dstore.Store) { store.Put(key, obj) }, - input: []string{"KEY_MOCK", "mock_field_name"}, - output: clientio.Encode(1, false), + input: []string{"KEY_MOCK", "mock_field_name"}, + migratedOutput: EvalResponse{Result: clientio.IntegerOne, Error: nil}, }, } - runEvalTests(t, tests, evalHEXISTS, store) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if tt.setup != nil { + tt.setup() + } + + response := evalHEXISTS(tt.input, store) + + // Handle comparison for byte slices + if responseBytes, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { + // If has result + if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { + // fmt.Printf("%v | %v\n", responseBytes, expectedBytes) + assert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal") + } + } else { + // If has error + assert.Equal(t, tt.migratedOutput.Result, response.Result) + } + + if tt.migratedOutput.Error != nil { + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + } else { + assert.NoError(t, response.Error) + } + }) + } } func testEvalHDEL(t *testing.T, store *dstore.Store) { @@ -3271,16 +3384,16 @@ func testEvalJSONSTRLEN(t *testing.T, store *dstore.Store) { response := evalJSONSTRLEN(tt.input, store) if tt.migratedOutput.Result != nil { if slice, ok := tt.migratedOutput.Result.([]interface{}); ok { - assert.DeepEqual(t, slice, response.Result) + assert.Equal(t, slice, response.Result) } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -3368,7 +3481,7 @@ func testEvalJSONNUMINCRBY(t *testing.T, store *dstore.Store) { endIndex := strings.Index(outPutString, "]") arrayString := outPutString[startIndex+1 : endIndex] arr := strings.Split(arrayString, ",") - testifyAssert.ElementsMatch(t, arr, []string{"25", "20", "7", "15", "null", "null"}) + assert.ElementsMatch(t, arr, []string{"25", "20", "7", "15", "null", "null"}) }, }, @@ -3413,7 +3526,7 @@ func testEvalJSONNUMINCRBY(t *testing.T, store *dstore.Store) { endIndex := strings.Index(outPutString, "]") arrayString := outPutString[startIndex+1 : endIndex] arr := strings.Split(arrayString, ",") - testifyAssert.ElementsMatch(t, arr, []string{"3", "4", "7", "null", "null"}) + assert.ElementsMatch(t, arr, []string{"3", "4", "7", "null", "null"}) }, }, @@ -3475,7 +3588,7 @@ func runMigratedEvalTests(t *testing.T, tests map[string]evalTestCase, evalFunc } if tc.migratedOutput.Error != nil { - testifyAssert.EqualError(t, output.Error, tc.migratedOutput.Error.Error()) + assert.EqualError(t, output.Error, tc.migratedOutput.Error.Error()) return } @@ -3483,17 +3596,17 @@ func runMigratedEvalTests(t *testing.T, tests map[string]evalTestCase, evalFunc // TODO: Make this generic so that all kind of slices can be handled if b, ok := output.Result.([]byte); ok && tc.migratedOutput.Result != nil { if expectedBytes, ok := tc.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else if a, ok := output.Result.([]string); ok && tc.migratedOutput.Result != nil { if expectedStringSlice, ok := tc.migratedOutput.Result.([]string); ok { - testifyAssert.ElementsMatch(t, a, expectedStringSlice) + assert.ElementsMatch(t, a, expectedStringSlice) } } else { - testifyAssert.Equal(t, tc.migratedOutput.Result, output.Result) + assert.Equal(t, tc.migratedOutput.Result, output.Result) } - testifyAssert.NoError(t, output.Error) + assert.NoError(t, output.Error) }) } } @@ -3582,8 +3695,8 @@ func testEvalHSET(t *testing.T, store *dstore.Store) { // Check if the map is saved correctly in the store res, err := getValueFromHashMap(key, field, store) - assert.Assert(t, err == nil) - assert.DeepEqual(t, res, clientio.Encode(mockValue, false)) + assert.Nil(t, err) + assert.Equal(t, res, clientio.Encode(mockValue, false)) }, input: []string{ "KEY_MOCK", @@ -3668,8 +3781,8 @@ func testEvalHMSET(t *testing.T, store *dstore.Store) { // Check if the map is saved correctly in the store res, err := getValueFromHashMap(key, field, store) - assert.Assert(t, err == nil) - assert.DeepEqual(t, res, clientio.Encode(mockValue, false)) + assert.True(t, err == nil) + assert.Equal(t, res, clientio.Encode(mockValue, false)) }, input: []string{ "KEY_MOCK", @@ -3686,25 +3799,29 @@ func testEvalHMSET(t *testing.T, store *dstore.Store) { } func testEvalHKEYS(t *testing.T, store *dstore.Store) { - tests := map[string]evalTestCase{ - "wrong number of args passed": { - setup: func() {}, - input: nil, - output: []byte("-ERR wrong number of arguments for 'hkeys' command\r\n"), + tests := []evalTestCase{ + { + name: "HKEYS wrong number of args passed", + setup: nil, + input: nil, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR wrong number of arguments for 'hkeys' command")}, }, - "key doesn't exist": { - setup: func() {}, - input: []string{"KEY"}, - output: clientio.Encode([]string{}, false), + { + name: "HKEYS key doesn't exist", + setup: nil, + input: []string{"KEY"}, + migratedOutput: EvalResponse{Result: clientio.EmptyArray, Error: nil}, }, - "key exists but not a hash": { + { + name: "HKEYS key exists but not a hash", setup: func() { evalSET([]string{"string_key", "string_value"}, store) }, - input: []string{"string_key"}, - output: []byte("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"), + input: []string{"string_key"}, + migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR -WRONGTYPE Operation against a key holding the wrong kind of value")}, }, - "key exists and is a hash": { + { + name: "HKEYS key exists and is a hash", setup: func() { key := "KEY_MOCK" field1 := "mock_field_name" @@ -3719,12 +3836,46 @@ func testEvalHKEYS(t *testing.T, store *dstore.Store) { store.Put(key, obj) }, - input: []string{"KEY_MOCK"}, - output: clientio.Encode([]string{"mock_field_name"}, false), + input: []string{"KEY_MOCK"}, + migratedOutput: EvalResponse{Result: []string{"mock_field_name"}, Error: nil}, }, } - runEvalTests(t, tests, evalHKEYS, store) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if tt.setup != nil { + tt.setup() + } + + response := evalHKEYS(tt.input, store) + + // fmt.Printf("EvalReponse: %v\n", response) + + // Handle comparison for byte slices + if responseBytes, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { + if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { + // fmt.Printf("G: %v | %v\n", responseBytes, expectedBytes) + assert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal") + } + } else { + // fmt.Printf("G1: %v | %v\n", response.Result, tt.migratedOutput.Result) + switch e := tt.migratedOutput.Result.(type) { + case []interface{}, []string: + assert.ElementsMatch(t, e, response.Result) + default: + assert.Equal(t, tt.migratedOutput.Result, response.Result) + } + } + + if tt.migratedOutput.Error != nil { + // fmt.Printf("E: %v | %v\n", response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + } else { + assert.NoError(t, response.Error) + } + }) + } } func BenchmarkEvalHKEYS(b *testing.B) { @@ -4312,7 +4463,7 @@ func testEvalJSONARRPOP(t *testing.T, store *dstore.Store) { obj := store.Get(key) expr, err := jp.ParseString(path) - assert.NilError(t, err, "error parsing path") + assert.Nil(t, err, "error parsing path") results := expr.Get(obj.Value) assert.Equal(t, len(results), 1) @@ -4339,16 +4490,16 @@ func testEvalJSONARRPOP(t *testing.T, store *dstore.Store) { if tt.migratedOutput.Result != nil { if slice, ok := tt.migratedOutput.Result.([]interface{}); ok { - assert.DeepEqual(t, slice, response.Result) + assert.Equal(t, slice, response.Result) } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -4726,16 +4877,16 @@ func testEvalJSONOBJKEYS(t *testing.T, store *dstore.Store) { if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { - testifyAssert.Equal(t, tt.migratedOutput.Result, response.Result) + assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -5260,8 +5411,8 @@ func testEvalSETEX(t *testing.T, store *dstore.Store) { // Check if the TTL is set correctly (should be 5 seconds or less) ttlValue := evalTTL([]string{"TEST_KEY"}, store) ttl, err := strconv.Atoi(strings.TrimPrefix(strings.TrimSpace(string(ttlValue)), ":")) - assert.NilError(t, err, "Failed to parse TTL") - assert.Assert(t, ttl > 0 && ttl <= 5) + assert.Nil(t, err, "Failed to parse TTL") + assert.True(t, ttl > 0 && ttl <= 5) // Wait for the key to expire mockTime.SetTime(mockTime.CurrTime.Add(6 * time.Second)) @@ -5286,8 +5437,8 @@ func testEvalSETEX(t *testing.T, store *dstore.Store) { // Check if the TTL is set correctly ttlValue := evalTTL([]string{"EXISTING_KEY"}, store) ttl, err := strconv.Atoi(strings.TrimPrefix(strings.TrimSpace(string(ttlValue)), ":")) - assert.NilError(t, err, "Failed to parse TTL") - assert.Assert(t, ttl > 0 && ttl <= 10) + assert.Nil(t, err, "Failed to parse TTL") + assert.True(t, ttl > 0 && ttl <= 10) }, }, } @@ -5306,16 +5457,16 @@ func testEvalSETEX(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } } }) @@ -5486,16 +5637,16 @@ func testEvalINCRBYFLOAT(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -5536,7 +5687,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) { output: clientio.Encode(0, true), validator: func(output []byte) { expectedResult := []byte{} - assert.DeepEqual(t, expectedResult, store.Get("dest{t}").Value.(*ByteArray).data) + assert.Equal(t, expectedResult, store.Get("dest{t}").Value.(*ByteArray).data) }, }, "BITOP NOT (known string)": { @@ -5547,7 +5698,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) { output: clientio.Encode(4, true), validator: func(output []byte) { expectedResult := []byte{0x55, 0xff, 0x00, 0xaa} - assert.DeepEqual(t, expectedResult, store.Get("dest{t}").Value.(*ByteArray).data) + assert.Equal(t, expectedResult, store.Get("dest{t}").Value.(*ByteArray).data) }, }, "BITOP where dest and target are the same key": { @@ -5558,7 +5709,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) { output: clientio.Encode(4, true), validator: func(output []byte) { expectedResult := []byte{0x55, 0xff, 0x00, 0xaa} - assert.DeepEqual(t, expectedResult, store.Get("s").Value.(*ByteArray).data) + assert.Equal(t, expectedResult, store.Get("s").Value.(*ByteArray).data) }, }, "BITOP AND|OR|XOR don't change the string with single input key": { @@ -5569,7 +5720,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) { output: clientio.Encode(3, true), validator: func(output []byte) { expectedResult := []byte{0x01, 0x02, 0xff} - assert.DeepEqual(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data) + assert.Equal(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data) }, }, "BITOP missing key is considered a stream of zero": { @@ -5580,7 +5731,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) { output: clientio.Encode(3, true), validator: func(output []byte) { expectedResult := []byte{0x00, 0x00, 0x00} - assert.DeepEqual(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data) + assert.Equal(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data) }, }, "BITOP shorter keys are zero-padded to the key with max length": { @@ -5592,7 +5743,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) { output: clientio.Encode(4, true), validator: func(output []byte) { expectedResult := []byte{0x01, 0x02, 0xff, 0x00} - assert.DeepEqual(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data) + assert.Equal(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data) }, }, "BITOP with non string source key": { @@ -5679,13 +5830,13 @@ func testEvalHRANDFIELD(t *testing.T, store *dstore.Store) { }, input: []string{"KEY_MOCK"}, newValidator: func(output interface{}) { - assert.Assert(t, output != nil) + assert.True(t, output != nil) stringSlice, ok := output.([]string) if !ok { - testifyAssert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output))) + assert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output))) } resultString := strings.Join(stringSlice, " ") - assert.Assert(t, + assert.True(t, resultString == "field1" || resultString == "field2", "Unexpected field returned: %s", resultString) }, @@ -5708,10 +5859,10 @@ func testEvalHRANDFIELD(t *testing.T, store *dstore.Store) { }, input: []string{"KEY_MOCK", "2"}, newValidator: func(output interface{}) { - assert.Assert(t, output != nil) + assert.True(t, output != nil) stringSlice, ok := output.([]string) if !ok { - testifyAssert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output))) + assert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output))) } decodedResult := strings.Join(stringSlice, " ") fields := []string{"field1", "field2", "field3"} @@ -5723,7 +5874,7 @@ func testEvalHRANDFIELD(t *testing.T, store *dstore.Store) { } } - assert.Assert(t, count == 2) + assert.True(t, count == 2) }, }, "key exists with count and WITHVALUES argument": { @@ -5744,10 +5895,10 @@ func testEvalHRANDFIELD(t *testing.T, store *dstore.Store) { }, input: []string{"KEY_MOCK", "2", WithValues}, newValidator: func(output interface{}) { - assert.Assert(t, output != nil) + assert.True(t, output != nil) stringSlice, ok := output.([]string) if !ok { - testifyAssert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output))) + assert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output))) } decodedResult := strings.Join(stringSlice, " ") fieldsAndValues := []string{"field1", "value1", "field2", "value2", "field3", "value3"} @@ -6516,6 +6667,74 @@ func testEvalZRANK(t *testing.T, store *dstore.Store) { runMigratedEvalTests(t, tests, evalZRANK, store) } +func testEvalZREM(t *testing.T, store *dstore.Store) { + tests := map[string]evalTestCase{ + "ZREM with wrong number of arguments": { + input: []string{"myzset"}, + migratedOutput: EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("ZREM"), + }, + }, + "ZREM with missing key": { + input: []string{}, + migratedOutput: EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("ZREM"), + }, + }, + "ZREM with wrong type key": { + setup: func() { + store.Put("string_key", store.NewObj("string_value", -1, object.ObjTypeString, object.ObjEncodingRaw)) + }, + input: []string{"string_key", "field"}, + migratedOutput: EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongTypeOperation, + }, + }, + "ZREM with non-existent key": { + input: []string{"non_existent_key", "field"}, + migratedOutput: EvalResponse{ + Result: clientio.IntegerZero, + Error: nil, + }, + }, + "ZREM with non-existent element": { + setup: func() { + evalZADD([]string{"myzset", "1", "one"}, store) + }, + input: []string{"myzset", "two"}, + migratedOutput: EvalResponse{ + Result: int64(0), + Error: nil, + }, + }, + "ZREM with sorted set holding single element": { + setup: func() { + evalZADD([]string{"myzset", "1", "one"}, store) + }, + input: []string{"myzset", "one"}, + migratedOutput: EvalResponse{ + Result: int64(1), + Error: nil, + }, + }, + "ZREM with sorted set holding multiple elements": { + setup: func() { + evalZADD([]string{"myzset", "1", "one", "2", "two", "3", "three"}, store) + }, + input: []string{"myzset", "one", "two"}, + migratedOutput: EvalResponse{ + Result: int64(2), + Error: nil, + }, + }, + } + + runMigratedEvalTests(t, tests, evalZREM, store) +} + func BenchmarkEvalZRANK(b *testing.B) { store := dstore.NewStore(nil, nil) @@ -6541,6 +6760,64 @@ func BenchmarkEvalZRANK(b *testing.B) { } } +func testEvalZCARD(t *testing.T, store *dstore.Store) { + tests := map[string]evalTestCase{ + "ZCARD with wrong number of arguments": { + input: []string{"myzset", "field"}, + migratedOutput: EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("ZCARD"), + }, + }, + "ZCARD with missing key": { + input: []string{}, + migratedOutput: EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("ZCARD"), + }, + }, + "ZCARD with wrong type key": { + setup: func() { + store.Put("string_key", store.NewObj("string_value", -1, object.ObjTypeString, object.ObjEncodingRaw)) + }, + input: []string{"string_key"}, + migratedOutput: EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongTypeOperation, + }, + }, + "ZCARD with non-existent key": { + input: []string{"non_existent_key"}, + migratedOutput: EvalResponse{ + Result: clientio.IntegerZero, + Error: nil, + }, + }, + "ZCARD with sorted set holding single element": { + setup: func() { + evalZADD([]string{"myzset", "1", "one"}, store) + }, + input: []string{"myzset"}, + migratedOutput: EvalResponse{ + Result: int64(1), + Error: nil, + }, + }, + "ZCARD with sorted set holding multiple elements": { + setup: func() { + evalZADD([]string{"myzset", "1", "one", "2", "two", "3", "three"}, store) + }, + input: []string{"myzset"}, + migratedOutput: EvalResponse{ + Result: int64(3), + Error: nil, + }, + }, + } + + runMigratedEvalTests(t, tests, evalZCARD, store) +} + func testEvalBitField(t *testing.T, store *dstore.Store) { testCases := map[string]evalTestCase{ "BITFIELD signed SET": { @@ -7350,16 +7627,16 @@ func testEvalINCR(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -7437,16 +7714,16 @@ func testEvalINCRBY(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -7524,16 +7801,16 @@ func testEvalDECR(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } @@ -7611,16 +7888,16 @@ func testEvalDECRBY(t *testing.T, store *dstore.Store) { // Handle comparison for byte slices if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil { if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok { - testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") + assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal") } } else { assert.Equal(t, tt.migratedOutput.Result, response.Result) } if tt.migratedOutput.Error != nil { - testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) + assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error()) } else { - testifyAssert.NoError(t, response.Error) + assert.NoError(t, response.Error) } }) } diff --git a/internal/eval/sortedset/sorted_set.go b/internal/eval/sortedset/sorted_set.go index 36caee5de..5cb390514 100644 --- a/internal/eval/sortedset/sorted_set.go +++ b/internal/eval/sortedset/sorted_set.go @@ -203,6 +203,11 @@ func (ss *Set) Get(member string) (float64, bool) { return score, exists } +func (ss *Set) Len() int { + cardinality := len(ss.memberMap) + return cardinality + } + // This func is used to remove the maximum element from the sortedset. // It takes count as an argument which tells the number of elements to be removed from the sortedset. func (ss *Set) PopMax(count int) []string { diff --git a/internal/eval/store_eval.go b/internal/eval/store_eval.go index 5c624b95c..892ed0d18 100644 --- a/internal/eval/store_eval.go +++ b/internal/eval/store_eval.go @@ -36,151 +36,169 @@ import ( // Returns encoded OK RESP once new entry is added // If the key already exists then the value will be overwritten and expiry will be discarded func evalSET(args []string, store *dstore.Store) *EvalResponse { - if len(args) <= 1 { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrWrongArgumentCount("SET"), - } - } - - var key, value string - var exDurationMs int64 = -1 - var state exDurationState = Uninitialized - var keepttl bool = false - + if len(args) <= 1 { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("SET"), + } + } + + var key, value string + var exDurationMs int64 = -1 + var state exDurationState = Uninitialized + var keepttl bool = false + key, value = args[0], args[1] - oType, oEnc := deduceTypeEncoding(value) - - for i := 2; i < len(args); i++ { - arg := strings.ToUpper(args[i]) - switch arg { - case Ex, Px: - if state != Uninitialized { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrSyntax, - } - } - i++ - if i == len(args) { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrSyntax, - } - } - - exDuration, err := strconv.ParseInt(args[i], 10, 64) - if err != nil { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrIntegerOutOfRange, - } - } - - if exDuration <= 0 || exDuration >= maxExDuration { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrInvalidExpireTime("SET"), - } - } - - // converting seconds to milliseconds - if arg == Ex { - exDuration *= 1000 - } - exDurationMs = exDuration - state = Initialized - - case Pxat, Exat: - if state != Uninitialized { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrSyntax, - } - } - i++ - if i == len(args) { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrSyntax, - } - } - exDuration, err := strconv.ParseInt(args[i], 10, 64) - if err != nil { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrIntegerOutOfRange, - } - } - - if exDuration < 0 { - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrInvalidExpireTime("SET"), - } - } - - if arg == Exat { - exDuration *= 1000 - } - exDurationMs = exDuration - utils.GetCurrentTime().UnixMilli() - // If the expiry time is in the past, set exDurationMs to 0 - // This will be used to signal immediate expiration - if exDurationMs < 0 { - exDurationMs = 0 - } - state = Initialized - - case XX: - // Get the key from the hash table - obj := store.Get(key) - - // if key does not exist, return RESP encoded nil - if obj == nil { - return &EvalResponse{ - Result: clientio.NIL, - Error: nil, - } - } - case NX: - obj := store.Get(key) - if obj != nil { - return &EvalResponse{ - Result: clientio.NIL, - Error: nil, - } - } - case KeepTTL: - keepttl = true - default: - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrSyntax, - } - } - } - - // Cast the value properly based on the encoding type - var storedValue interface{} - switch oEnc { - case object.ObjEncodingInt: - storedValue, _ = strconv.ParseInt(value, 10, 64) - case object.ObjEncodingEmbStr, object.ObjEncodingRaw: - storedValue = value - default: - return &EvalResponse{ - Result: nil, - Error: diceerrors.ErrUnsupportedEncoding(int(oEnc)), - } - } - - // putting the k and value in a Hash Table - store.Put(key, store.NewObj(storedValue, exDurationMs, oType, oEnc), dstore.WithKeepTTL(keepttl)) - - return &EvalResponse{ - Result: clientio.OK, - Error: nil, - } + oType, oEnc := deduceTypeEncoding(value) + + for i := 2; i < len(args); i++ { + arg := strings.ToUpper(args[i]) + switch arg { + case Ex, Px: + if state != Uninitialized { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrSyntax, + } + } + if keepttl { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrSyntax, + } + } + i++ + if i == len(args) { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrSyntax, + } + } + + exDuration, err := strconv.ParseInt(args[i], 10, 64) + if err != nil { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrIntegerOutOfRange, + } + } + + if exDuration <= 0 || exDuration >= maxExDuration { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrInvalidExpireTime("SET"), + } + } + + // converting seconds to milliseconds + if arg == Ex { + exDuration *= 1000 + } + exDurationMs = exDuration + state = Initialized + + case Pxat, Exat: + if state != Uninitialized { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrSyntax, + } + } + if keepttl { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrSyntax, + } + } + i++ + if i == len(args) { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrSyntax, + } + } + exDuration, err := strconv.ParseInt(args[i], 10, 64) + if err != nil { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrIntegerOutOfRange, + } + } + + if exDuration < 0 { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrInvalidExpireTime("SET"), + } + } + + if arg == Exat { + exDuration *= 1000 + } + exDurationMs = exDuration - utils.GetCurrentTime().UnixMilli() + // If the expiry time is in the past, set exDurationMs to 0 + // This will be used to signal immediate expiration + if exDurationMs < 0 { + exDurationMs = 0 + } + state = Initialized + + case XX: + // Get the key from the hash table + obj := store.Get(key) + + // if key does not exist, return RESP encoded nil + if obj == nil { + return &EvalResponse{ + Result: clientio.NIL, + Error: nil, + } + } + case NX: + obj := store.Get(key) + if obj != nil { + return &EvalResponse{ + Result: clientio.NIL, + Error: nil, + } + } + case KeepTTL: + if state != Uninitialized { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrSyntax, + } + } + keepttl = true + default: + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrSyntax, + } + } + } + + // Cast the value properly based on the encoding type + var storedValue interface{} + switch oEnc { + case object.ObjEncodingInt: + storedValue, _ = strconv.ParseInt(value, 10, 64) + case object.ObjEncodingEmbStr, object.ObjEncodingRaw: + storedValue = value + default: + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrUnsupportedEncoding(int(oEnc)), + } + } + + // putting the k and value in a Hash Table + store.Put(key, store.NewObj(storedValue, exDurationMs, oType, oEnc), dstore.WithKeepTTL(keepttl)) + + return &EvalResponse{ + Result: clientio.OK, + Error: nil, + } } // evalGET returns the value for the queried key in args @@ -326,6 +344,145 @@ func evalSETEX(args []string, store *dstore.Store) *EvalResponse { return evalSET(newArgs, store) } +// evalHEXISTS returns if field is an existing field in the hash stored at key. +// +// This command returns 0, if the specified field doesn't exist in the key and 1 if it exists. +// +// If key doesn't exist, it returns 0. +// +// Usage: HEXISTS key field +func evalHEXISTS(args []string, store *dstore.Store) *EvalResponse { + if len(args) != 2 { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("HEXISTS"), + } + } + + key := args[0] + hmKey := args[1] + obj := store.Get(key) + + var hashMap HashMap + + if obj == nil { + return &EvalResponse{ + Result: clientio.IntegerZero, + Error: nil, + } + } + if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil { + return &EvalResponse{ + Error: diceerrors.ErrGeneral(diceerrors.WrongTypeErr), + Result: nil, + } + } + + hashMap = obj.Value.(HashMap) + + _, ok := hashMap.Get(hmKey) + if ok { + return &EvalResponse{ + Result: clientio.IntegerOne, + Error: nil, + } + } + // Return 0, if specified field doesn't exist in the HashMap. + return &EvalResponse{ + Result: clientio.IntegerZero, + Error: nil, + } +} + +// evalHKEYS is used to retrieve all the keys(or field names) within a hash. +// +// This command returns empty array, if the specified key doesn't exist. +// +// Complexity is O(n) where n is the size of the hash. +// +// Usage: HKEYS key +func evalHKEYS(args []string, store *dstore.Store) *EvalResponse { + if len(args) != 1 { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("HKEYS"), + } + } + + key := args[0] + obj := store.Get(key) + + var hashMap HashMap + var result []string + + if obj != nil { + if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil { + return &EvalResponse{ + Error: diceerrors.ErrGeneral(diceerrors.WrongTypeErr), + Result: nil, + } + } + hashMap = obj.Value.(HashMap) + } else { + return &EvalResponse{ + Result: clientio.EmptyArray, + Error: nil, + } + } + + for hmKey := range hashMap { + result = append(result, hmKey) + } + + return &EvalResponse{ + Result: result, + Error: nil, + } +} + +// evalHKEYS is used to retrieve all the values within a hash. +// +// This command returns empty array, if the specified key doesn't exist. +// +// Complexity is O(n) where n is the size of the hash. +// +// Usage: HVALS key +func evalHVALS(args []string, store *dstore.Store) *EvalResponse { + if len(args) != 1 { + return &EvalResponse{Error: diceerrors.ErrWrongArgumentCount("HVALS"), Result: nil} + } + + key := args[0] + obj := store.Get(key) + + if obj == nil { + // Return an empty array for non-existent keys + return &EvalResponse{ + Result: clientio.EmptyArray, + Error: nil, + } + } + + if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil { + return &EvalResponse{ + Error: diceerrors.ErrGeneral(diceerrors.WrongTypeErr), + Result: nil, + } + } + + hashMap := obj.Value.(HashMap) + results := make([]string, 0, len(hashMap)) + + for _, value := range hashMap { + results = append(results, value) + } + + return &EvalResponse{ + Result: results, + Error: nil, + } +} + // Key, start and end are mandatory args. // Returns a substring from the key(if it's a string) from start -> end. // Returns ""(empty string) if key is not present and if start > end. @@ -601,6 +758,48 @@ func evalZRANGE(args []string, store *dstore.Store) *EvalResponse { } } +// evalZREM removes the specified members from the sorted set stored at key. +// Non-existing members are ignored. +// Returns the number of members removed. +func evalZREM(args []string, store *dstore.Store) *EvalResponse { + if len(args) < 2 { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("ZREM"), + } + } + + key := args[0] + obj := store.Get(key) + + if obj == nil { + return &EvalResponse{ + Result: clientio.IntegerZero, + Error: nil, + } + } + + sortedSet, err := sortedset.FromObject(obj) + if err != nil { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongTypeOperation, + } + } + + countRem := 0 + for i := 1; i < len(args); i++ { + if sortedSet.Remove(args[i]) { + countRem += 1 + } + } + + return &EvalResponse{ + Result: int64(countRem), + Error: nil, + } +} + // evalAPPEND takes two arguments: the key and the value to append to the key's current value. // If the key does not exist, it creates a new key with the given value (so APPEND will be similar to SET in this special case) // If key already exists and is a string (or integers stored as strings), this command appends the value at the end of the string @@ -744,6 +943,39 @@ func evalZRANK(args []string, store *dstore.Store) *EvalResponse { } } +// evalZCARD returns the cardinality (number of elements) of the sorted set stored at key. +// Returns 0 if the key does not exist. +func evalZCARD(args []string, store *dstore.Store) *EvalResponse { + if len(args) < 1 || len(args) > 1 { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongArgumentCount("ZCARD"), + } + } + + key := args[0] + obj := store.Get(key) + + if obj == nil { + return &EvalResponse{ + Result: clientio.IntegerZero, + Error: nil, + } + } + + sortedSet, err := sortedset.FromObject(obj) + if err != nil { + return &EvalResponse{ + Result: nil, + Error: diceerrors.ErrWrongTypeOperation, + } + } + return &EvalResponse{ + Result: int64(sortedSet.Len()), + Error: nil, + } +} + // evalJSONCLEAR Clear container values (arrays/objects) and set numeric values to 0, // Already cleared values are ignored for empty containers and zero numbers // args must contain at least the key; (path unused in this implementation) @@ -2740,4 +2972,4 @@ func evalJSONOBJKEYS(args []string, store *dstore.Store) *EvalResponse { Result: keysList, Error: nil, } -} \ No newline at end of file +} diff --git a/internal/server/cmd_meta.go b/internal/server/cmd_meta.go index ecaa58d34..c50051597 100644 --- a/internal/server/cmd_meta.go +++ b/internal/server/cmd_meta.go @@ -78,6 +78,18 @@ var ( Cmd: "GETRANGE", CmdType: SingleShard, } + hexistsCmdMeta = CmdsMeta{ + Cmd: "HEXISTS", + CmdType: SingleShard, + } + hkeysCmdMeta = CmdsMeta{ + Cmd: "HKEYS", + CmdType: SingleShard, + } + hvalsCmdMeta = CmdsMeta{ + Cmd: "HVALS", + CmdType: SingleShard, + } zaddCmdMeta = CmdsMeta{ Cmd: "ZADD", CmdType: SingleShard, @@ -102,6 +114,14 @@ var ( Cmd: "ZRANK", CmdType: SingleShard, } + zcardCmdMeta = CmdsMeta{ + Cmd: "ZCARD", + CmdType: SingleShard, + } + zremCmdMeta = CmdsMeta{ + Cmd: "ZREM", + CmdType: SingleShard, + } pfaddCmdMeta = CmdsMeta{ Cmd: "PFADD", CmdType: SingleShard, @@ -264,6 +284,9 @@ func init() { WorkerCmdsMeta["JSON.CLEAR"] = jsonclearCmdMeta WorkerCmdsMeta["JSON.STRLEN"] = jsonstrlenCmdMeta WorkerCmdsMeta["JSON.OBJLEN"] = jsonobjlenCmdMeta + WorkerCmdsMeta["HEXISTS"] = hexistsCmdMeta + WorkerCmdsMeta["HKEYS"] = hkeysCmdMeta + WorkerCmdsMeta["HVALS"] = hvalsCmdMeta WorkerCmdsMeta["JSON.ARRINSERT"] = jsonarrinsertCmdMeta WorkerCmdsMeta["JSON.ARRTRIM"] = jsonarrtrimCmdMeta WorkerCmdsMeta["JSON.OBJKEYS"] = jsonobjkeystCmdMeta @@ -271,6 +294,8 @@ func init() { WorkerCmdsMeta["ZCOUNT"] = zcountCmdMeta WorkerCmdsMeta["ZRANGE"] = zrangeCmdMeta WorkerCmdsMeta["ZRANK"] = zrankCmdMeta + WorkerCmdsMeta["ZCARD"] = zcardCmdMeta + WorkerCmdsMeta["ZREM"] = zremCmdMeta WorkerCmdsMeta["PFADD"] = pfaddCmdMeta WorkerCmdsMeta["ZPOPMIN"] = zpopminCmdMeta WorkerCmdsMeta["PFCOUNT"] = pfcountCmdMeta diff --git a/internal/server/httpServer.go b/internal/server/httpServer.go index e86395418..c748be162 100644 --- a/internal/server/httpServer.go +++ b/internal/server/httpServer.go @@ -12,10 +12,10 @@ import ( "sync" "time" - "github.com/dicedb/dice/internal/server/abstractserver" - "github.com/dicedb/dice/internal/eval" + "github.com/dicedb/dice/internal/server/abstractserver" + "github.com/dicedb/dice/config" "github.com/dicedb/dice/internal/clientio" "github.com/dicedb/dice/internal/cmd" @@ -92,7 +92,7 @@ func NewHTTPServer(shardManager *shard.ShardManager) *HTTPServer { func (s *HTTPServer) Run(ctx context.Context) error { var wg sync.WaitGroup - var err error + var shutdownErr, listenErr error httpCtx, cancelHTTP := context.WithCancel(ctx) defer cancelHTTP() @@ -105,14 +105,14 @@ func (s *HTTPServer) Run(ctx context.Context) error { select { case <-ctx.Done(): case <-s.shutdownChan: - err = derrors.ErrAborted + shutdownErr = derrors.ErrAborted slog.Debug("Shutting down HTTP Server") } - shutdownErr := s.httpServer.Shutdown(httpCtx) - if shutdownErr != nil { + err := s.httpServer.Shutdown(httpCtx) + if err != nil { slog.Error("HTTP Server Shutdown Failed", slog.Any("error", err)) - err = shutdownErr + shutdownErr = err return } }() @@ -120,12 +120,17 @@ func (s *HTTPServer) Run(ctx context.Context) error { wg.Add(1) go func() { defer wg.Done() - slog.Info("also listenting HTTP on", slog.String("port", s.httpServer.Addr[1:])) - err = s.httpServer.ListenAndServe() + slog.Info("also listening HTTP on", slog.String("port", s.httpServer.Addr[1:])) + listenErr = s.httpServer.ListenAndServe() }() wg.Wait() - return err + // Return the appropriate error + if shutdownErr != nil { + return shutdownErr + } + + return listenErr } func (s *HTTPServer) DiceHTTPHandler(writer http.ResponseWriter, request *http.Request) { @@ -342,7 +347,7 @@ func (s *HTTPServer) writeResponse(writer http.ResponseWriter, result *ops.Store _, ok := WorkerCmdsMeta[diceDBCmd.Cmd] // TODO: Remove this conditional check and if (true) condition when all commands are migrated if !ok { - responseValue, err = decodeEvalResponse(result.EvalResponse) + responseValue, err = DecodeEvalResponse(result.EvalResponse) if err != nil { slog.Error("Error decoding response", "error", err) httpResponse = utils.HTTPResponse{Status: utils.HTTPStatusError, Data: "Internal Server Error"} @@ -359,7 +364,7 @@ func (s *HTTPServer) writeResponse(writer http.ResponseWriter, result *ops.Store } // Create the HTTP response - httpResponse = createHTTPResponse(responseValue) + httpResponse = utils.HTTPResponse{Data: ResponseParser(responseValue)} if isDiceErr { httpResponse.Status = utils.HTTPStatusError } else { @@ -370,24 +375,6 @@ func (s *HTTPServer) writeResponse(writer http.ResponseWriter, result *ops.Store writeJSONResponse(writer, httpResponse, http.StatusOK) } -// Helper function to decode EvalResponse based on the error or result -func decodeEvalResponse(evalResp *eval.EvalResponse) (interface{}, error) { - var rp *clientio.RESPParser - - if evalResp.Error != nil { - rp = clientio.NewRESPParser(bytes.NewBuffer([]byte(evalResp.Error.Error()))) - } else { - rp = clientio.NewRESPParser(bytes.NewBuffer(evalResp.Result.([]byte))) - } - - res, err := rp.DecodeOne() - if err != nil { - return nil, err - } - - return replaceNilInInterface(res), nil -} - // Helper function to write the JSON response func writeJSONResponse(writer http.ResponseWriter, response utils.HTTPResponse, statusCode int) { writer.Header().Set("Content-Type", "application/json") @@ -406,18 +393,9 @@ func writeJSONResponse(writer http.ResponseWriter, response utils.HTTPResponse, } } -func createHTTPResponse(responseValue interface{}) utils.HTTPResponse { - respArr := []string{ - "(nil)", // Represents a RESP Nil Bulk String, which indicates a null value. - "OK", // Represents a RESP Simple String with value "OK". - "QUEUED", // Represents a Simple String indicating that a command has been queued. - "0", // Represents a RESP Integer with value 0. - "1", // Represents a RESP Integer with value 1. - "-1", // Represents a RESP Integer with value -1. - "-2", // Represents a RESP Integer with value -2. - "*0", // Represents an empty RESP Array. - } - +// ResponseParser parses the response value for both migrated and non-migrated cmds and +// returns response to be rendered for HTTP/WS response +func ResponseParser(responseValue interface{}) interface{} { switch v := responseValue.(type) { case []interface{}: // Parses []interface{} as part of EvalResponse e.g. JSON.ARRPOP @@ -426,35 +404,35 @@ func createHTTPResponse(responseValue interface{}) utils.HTTPResponse { r := make([]interface{}, 0, len(v)) for _, resp := range v { if val, ok := resp.(clientio.RespType); ok { - if stringNil == respArr[val] { + if stringNil == RespTypeToValue(val) { r = append(r, nil) } else { - r = append(r, respArr[val]) + r = append(r, RespTypeToValue(val)) } } else { r = append(r, resp) } } - return utils.HTTPResponse{Data: r} + return r case []byte: - return utils.HTTPResponse{Data: string(v)} + return string(v) case clientio.RespType: - responseValue = respArr[v] + responseValue = RespTypeToValue(v) if responseValue == stringNil { responseValue = nil // in order to convert it in json null } - return utils.HTTPResponse{Data: responseValue} + return responseValue case interface{}: if val, ok := v.(clientio.RespType); ok { - return utils.HTTPResponse{Data: respArr[val]} + return RespTypeToValue(val) } } - return utils.HTTPResponse{Data: responseValue} + return responseValue } func generateUniqueInt32(r *http.Request) uint32 { @@ -468,6 +446,24 @@ func generateUniqueInt32(r *http.Request) uint32 { return crc32.ChecksumIEEE([]byte(sb.String())) } +// DecodeEvalResponse Helper function to decode EvalResponse based on the error or result +func DecodeEvalResponse(evalResp *eval.EvalResponse) (interface{}, error) { + var rp *clientio.RESPParser + + if evalResp.Error != nil { + rp = clientio.NewRESPParser(bytes.NewBuffer([]byte(evalResp.Error.Error()))) + } else { + rp = clientio.NewRESPParser(bytes.NewBuffer(evalResp.Result.([]byte))) + } + + res, err := rp.DecodeOne() + if err != nil { + return nil, err + } + + return replaceNilInInterface(res), nil +} + func replaceNilInInterface(data interface{}) interface{} { switch v := data.(type) { case string: @@ -492,3 +488,30 @@ func replaceNilInInterface(data interface{}) interface{} { return data } } + +func RespTypeToValue(respType clientio.RespType) interface{} { + var respArrString = map[clientio.RespType]string{ + clientio.NIL: "(nil)", // Represents a RESP Nil Bulk String, which indicates a null value. + clientio.OK: "OK", // Represents a RESP Simple String with value "OK". + clientio.CommandQueued: "QUEUED", // Represents a Simple String indicating that a command has been queued. + clientio.EmptyArray: "*0", // Represents an empty RESP Array. + } + + var respArrInt = map[clientio.RespType]float64{ + clientio.IntegerZero: 0, // Represents a RESP Integer with value 0. + clientio.IntegerOne: 1, // Represents a RESP Integer with value 1. + clientio.IntegerNegativeOne: -1, // Represents a RESP Integer with value -1. + clientio.IntegerNegativeTwo: -2, // Represents a RESP Integer with value -2. + } + + if val, exists := respArrString[respType]; exists { + return val + } + // Check if respType exists in respArrInt map + if val, exists := respArrInt[respType]; exists { + return val + } + + // Default to nil if respType is not recognized + return nil +} diff --git a/internal/server/resp/server.go b/internal/server/resp/server.go index 7818e0152..1fb8b86c0 100644 --- a/internal/server/resp/server.go +++ b/internal/server/resp/server.go @@ -39,27 +39,30 @@ const ( type Server struct { abstractserver.AbstractServer - Host string - Port int - serverFD int - connBacklogSize int - workerManager *worker.WorkerManager - shardManager *shard.ShardManager - watchManager *watchmanager.Manager - cmdWatchChan chan dstore.CmdWatchEvent - globalErrorChan chan error + Host string + Port int + serverFD int + connBacklogSize int + workerManager *worker.WorkerManager + shardManager *shard.ShardManager + watchManager *watchmanager.Manager + cmdWatchSubscriptionChan chan watchmanager.WatchSubscription + cmdWatchChan chan dstore.CmdWatchEvent + globalErrorChan chan error } -func NewServer(shardManager *shard.ShardManager, workerManager *worker.WorkerManager, cmdWatchChan chan dstore.CmdWatchEvent, globalErrChan chan error) *Server { +func NewServer(shardManager *shard.ShardManager, workerManager *worker.WorkerManager, cmdWatchSubscriptionChan chan watchmanager.WatchSubscription, + cmdWatchChan chan dstore.CmdWatchEvent, globalErrChan chan error) *Server { return &Server{ - Host: config.DiceConfig.AsyncServer.Addr, - Port: config.DiceConfig.AsyncServer.Port, - connBacklogSize: DefaultConnBacklogSize, - workerManager: workerManager, - shardManager: shardManager, - watchManager: watchmanager.NewManager(), - cmdWatchChan: cmdWatchChan, - globalErrorChan: globalErrChan, + Host: config.DiceConfig.AsyncServer.Addr, + Port: config.DiceConfig.AsyncServer.Port, + connBacklogSize: DefaultConnBacklogSize, + workerManager: workerManager, + shardManager: shardManager, + watchManager: watchmanager.NewManager(cmdWatchSubscriptionChan), + cmdWatchChan: cmdWatchChan, + cmdWatchSubscriptionChan: cmdWatchSubscriptionChan, + globalErrorChan: globalErrChan, } } @@ -195,7 +198,7 @@ func (s *Server) AcceptConnectionRequests(ctx context.Context, wg *sync.WaitGrou preprocessingChan := make(chan *ops.StoreResponse) // preprocessingChan is specifically for handling responses from shards for commands that require preprocessing wID := GenerateUniqueWorkerID() - w := worker.NewWorker(wID, responseChan, preprocessingChan, ioHandler, parser, s.shardManager, s.globalErrorChan) + w := worker.NewWorker(wID, responseChan, preprocessingChan, s.cmdWatchSubscriptionChan, ioHandler, parser, s.shardManager, s.globalErrorChan) // Register the worker with the worker manager err = s.workerManager.RegisterWorker(w) diff --git a/internal/server/server.go b/internal/server/server.go index db67af221..cf9b98ca1 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -258,6 +258,7 @@ func (s *AsyncServer) handleClientEvent(event iomultiplexer.Event) error { return err } + // function used within package, limit the scope s.EvalAndRespond(commands, client) if hasAbort { return diceerrors.ErrAborted diff --git a/internal/server/utils/jsontype_test.go b/internal/server/utils/jsontype_test.go index b2ecf2502..768568fe7 100644 --- a/internal/server/utils/jsontype_test.go +++ b/internal/server/utils/jsontype_test.go @@ -1,7 +1,7 @@ package utils import ( - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" "testing" ) diff --git a/internal/server/utils/redisCmdAdapter.go b/internal/server/utils/redisCmdAdapter.go index f1421a735..dd9d78196 100644 --- a/internal/server/utils/redisCmdAdapter.go +++ b/internal/server/utils/redisCmdAdapter.go @@ -2,6 +2,7 @@ package utils import ( bytes_ext "bytes" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -16,28 +17,29 @@ import ( ) const ( - Key = "key" - Keys = "keys" - KeyPrefix = "key_prefix" - Field = "field" - Path = "path" - Value = "value" - Values = "values" - User = "user" - Password = "password" - Seconds = "seconds" - KeyValues = "key_values" - True = "true" - QwatchQuery = "query" - Offset = "offset" - Member = "member" - Members = "members" - Index = "index" - JSON = "json" + Key = "key" + Keys = "keys" + KeyPrefix = "key_prefix" + Field = "field" + Path = "path" + Value = "value" + Values = "values" + User = "user" + Password = "password" + Seconds = "seconds" + KeyValues = "key_values" + True = "true" + QwatchQuery = "query" + Offset = "offset" + Member = "member" + Members = "members" + Index = "index" + JSON = "json" + QWatch = "Q.WATCH" + ABORT = "ABORT" + IsByteEncodedVal = "isByteEncodedVal" ) -const QWatch string = "Q.WATCH" - func ParseHTTPRequest(r *http.Request) (*cmd.DiceDBCmd, error) { commandParts := strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/") if len(commandParts) == 0 { @@ -79,7 +81,7 @@ func ParseHTTPRequest(r *http.Request) (*cmd.DiceDBCmd, error) { return nil, err } - if len(jsonBody) == 0 { + if len(jsonBody) == 0 && command != ABORT { return nil, fmt.Errorf("empty JSON object") } @@ -198,7 +200,13 @@ func processPriorityKeys(jsonBody map[string]interface{}, args *[]string) { *args = append(*args, k, fmt.Sprintf("%v", v)) } case Value: - *args = append(*args, formatValue(val)) + if _, ok := jsonBody[IsByteEncodedVal]; ok { + *args = append(*args, formatValue(true, val)) + } else { + *args = append(*args, formatValue(false, val)) + } + // Delete the byte encoded val key as it's not required once value is decoded + delete(jsonBody, IsByteEncodedVal) case Values: for _, v := range val.([]interface{}) { *args = append(*args, fmt.Sprintf("%v", v)) @@ -218,9 +226,16 @@ func getPriorityKeys() []string { } } -func formatValue(val interface{}) string { +func formatValue(isByteEncodedVal bool, val interface{}) string { switch v := val.(type) { case string: + if isByteEncodedVal && isBase64Encoded(v) { + decoded, err := base64.StdEncoding.DecodeString(v) + if err == nil { + // Replace the base64 string with the decoded `[]byte` + return string(decoded) + } + } return v default: jsonBytes, _ := json.Marshal(v) @@ -228,6 +243,18 @@ func formatValue(val interface{}) string { } } +func isBase64Encoded(s string) bool { + if len(s)%4 == 0 && s != "" { + for _, r := range s { + if !(r >= 'A' && r <= 'Z') && !(r >= 'a' && r <= 'z') && !(r >= '0' && r <= '9') && r != '+' && r != '/' && r != '=' { + return false + } + } + return true + } + return false +} + func unmarshalRequestBody(data []byte, v *map[string]interface{}) error { var rawMap map[string]interface{} diff --git a/internal/server/websocketServer.go b/internal/server/websocketServer.go index 76c17121f..7d30f60dc 100644 --- a/internal/server/websocketServer.go +++ b/internal/server/websocketServer.go @@ -179,7 +179,7 @@ func (s *WebsocketServer) WebsocketHandler(w http.ResponseWriter, r *http.Reques sp.Client = comm.NewHTTPQwatchClient(s.qwatchResponseChan, clientIdentifierID) // start a goroutine for subsequent updates - go s.processQwatchUpdates(clientIdentifierID, conn, diceDBCmd) + go s.processQwatchUpdates(clientIdentifierID, conn) } s.shardManager.GetShard(0).ReqChan <- sp @@ -190,12 +190,12 @@ func (s *WebsocketServer) WebsocketHandler(w http.ResponseWriter, r *http.Reques } } -func (s *WebsocketServer) processQwatchUpdates(clientIdentifierID uint32, conn *websocket.Conn, dicDBCmd *cmd.DiceDBCmd) { +func (s *WebsocketServer) processQwatchUpdates(clientIdentifierID uint32, conn *websocket.Conn) { for { select { case resp := <-s.qwatchResponseChan: if resp.ClientIdentifierID == clientIdentifierID { - if err := s.processResponse(conn, dicDBCmd, resp); err != nil { + if err := s.processQwatchResponse(conn, resp); err != nil { slog.Debug("Error writing response to client. Shutting down goroutine for q.watch updates", slog.Any("clientIdentifierID", clientIdentifierID), slog.Any("error", err)) return } @@ -206,7 +206,7 @@ func (s *WebsocketServer) processQwatchUpdates(clientIdentifierID uint32, conn * } } -func (s *WebsocketServer) processResponse(conn *websocket.Conn, diceDBCmd *cmd.DiceDBCmd, response interface{}) error { +func (s *WebsocketServer) processQwatchResponse(conn *websocket.Conn, response interface{}) error { var result interface{} var err error maxRetries := config.DiceConfig.WebSocket.MaxWriteResponseRetries @@ -216,9 +216,6 @@ func (s *WebsocketServer) processResponse(conn *websocket.Conn, diceDBCmd *cmd.D case comm.QwatchResponse: result = resp.Result err = resp.Error - case *ops.StoreResponse: - result = resp.EvalResponse.Result - err = resp.EvalResponse.Error default: slog.Debug("Unsupported response type") if err := WriteResponseWithRetries(conn, []byte("error: 500 Internal Server Error"), maxRetries); err != nil { @@ -228,29 +225,55 @@ func (s *WebsocketServer) processResponse(conn *websocket.Conn, diceDBCmd *cmd.D return nil } - _, ok := WorkerCmdsMeta[diceDBCmd.Cmd] - respArr := []string{ - "(nil)", // Represents a RESP Nil Bulk String, which indicates a null value. - "OK", // Represents a RESP Simple String with value "OK". - "QUEUED", // Represents a Simple String indicating that a command has been queued. - "0", // Represents a RESP Integer with value 0. - "1", // Represents a RESP Integer with value 1. - "-1", // Represents a RESP Integer with value -1. - "-2", // Represents a RESP Integer with value -2. - "*0", // Represents an empty RESP Array. + var responseValue interface{} + var rp *clientio.RESPParser + if err != nil { + rp = clientio.NewRESPParser(bytes.NewBuffer([]byte(err.Error()))) + } else { + rp = clientio.NewRESPParser(bytes.NewBuffer(result.([]byte))) + } + + responseValue, err = rp.DecodeOne() + if err != nil { + slog.Debug("Error decoding response", "error", err) + if err := WriteResponseWithRetries(conn, []byte("error: 500 Internal Server Error"), maxRetries); err != nil { + slog.Debug(fmt.Sprintf("Error writing message: %v", err)) + return fmt.Errorf("error writing response: %v", err) + } + return nil + } + + respBytes, err := json.Marshal(responseValue) + if err != nil { + slog.Debug("Error marshaling json", "error", err) + if err := WriteResponseWithRetries(conn, []byte("error: marshaling json"), maxRetries); err != nil { + slog.Debug(fmt.Sprintf("Error writing message: %v", err)) + return fmt.Errorf("error writing response: %v", err) + } + return nil + } + + // success + // Write response with retries for transient errors + if err := WriteResponseWithRetries(conn, respBytes, config.DiceConfig.WebSocket.MaxWriteResponseRetries); err != nil { + slog.Debug(fmt.Sprintf("Error writing message: %v", err)) + return fmt.Errorf("error writing response: %v", err) } + return nil +} + +func (s *WebsocketServer) processResponse(conn *websocket.Conn, diceDBCmd *cmd.DiceDBCmd, response *ops.StoreResponse) error { + var err error + maxRetries := config.DiceConfig.WebSocket.MaxWriteResponseRetries + var responseValue interface{} + // Check if the command is migrated, if it is we use EvalResponse values + // else we use RESPParser to decode the response + _, ok := WorkerCmdsMeta[diceDBCmd.Cmd] // TODO: Remove this conditional check and if (true) condition when all commands are migrated if !ok { - var rp *clientio.RESPParser - if err != nil { - rp = clientio.NewRESPParser(bytes.NewBuffer([]byte(err.Error()))) - } else { - rp = clientio.NewRESPParser(bytes.NewBuffer(result.([]byte))) - } - - responseValue, err = rp.DecodeOne() + responseValue, err = DecodeEvalResponse(response.EvalResponse) if err != nil { slog.Debug("Error decoding response", "error", err) if err := WriteResponseWithRetries(conn, []byte("error: 500 Internal Server Error"), maxRetries); err != nil { @@ -260,22 +283,16 @@ func (s *WebsocketServer) processResponse(conn *websocket.Conn, diceDBCmd *cmd.D return nil } } else { - if err != nil { - responseValue = err.Error() + if response.EvalResponse.Error != nil { + responseValue = response.EvalResponse.Error.Error() } else { - responseValue = result + responseValue = response.EvalResponse.Result } } - if val, ok := responseValue.(clientio.RespType); ok { - responseValue = respArr[val] - } - - if bt, ok := responseValue.([]byte); ok { - responseValue = string(bt) - } - - respBytes, err := json.Marshal(responseValue) + // Create websocket response + wsResponse := ResponseParser(responseValue) + respBytes, err := json.Marshal(wsResponse) if err != nil { slog.Debug("Error marshaling json", "error", err) if err := WriteResponseWithRetries(conn, []byte("error: marshaling json"), maxRetries); err != nil { diff --git a/internal/sql/dsql_test.go b/internal/sql/dsql_test.go index c44f15196..0976679c7 100644 --- a/internal/sql/dsql_test.go +++ b/internal/sql/dsql_test.go @@ -5,7 +5,7 @@ import ( "github.com/dicedb/dice/internal/server/utils" "github.com/xwb1989/sqlparser" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestParseQuery(t *testing.T) { @@ -225,16 +225,16 @@ func TestParseQuery(t *testing.T) { if tt.wantErr { assert.Error(t, err, tt.error) } else { - assert.NilError(t, err) - assert.DeepEqual(t, tt.want.Selection, got.Selection) - assert.DeepEqual(t, tt.want.OrderBy, got.OrderBy) + assert.Nil(t, err) + assert.Equal(t, tt.want.Selection, got.Selection) + assert.Equal(t, tt.want.OrderBy, got.OrderBy) assert.Equal(t, tt.want.Limit, got.Limit) //if tt.want.Where == nil { // assert.Assert(t, got.Where == nil) //} else { - assert.Assert(t, got.Where != nil) - assert.DeepEqual(t, tt.want.Where, got.Where) + assert.True(t, got.Where != nil) + assert.Equal(t, tt.want.Where, got.Where) //} } }) @@ -279,17 +279,17 @@ func TestParseSelectExpressions(t *testing.T) { t.Run(tt.name, func(t *testing.T) { stmt, err := sqlparser.Parse(replaceCustomSyntax(tt.sql)) - assert.NilError(t, err) + assert.Nil(t, err) selectStmt, ok := stmt.(*sqlparser.Select) - assert.Assert(t, ok) + assert.True(t, ok) got, err := parseSelectExpressions(selectStmt) if tt.wantErr { - assert.Assert(t, err != nil) + assert.True(t, err != nil) } else { - assert.NilError(t, err) - assert.DeepEqual(t, tt.want, got) + assert.Nil(t, err) + assert.Equal(t, tt.want, got) } }) } @@ -367,17 +367,17 @@ func TestParseOrderBy(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { stmt, err := sqlparser.Parse(replaceCustomSyntax(tt.sql)) - assert.NilError(t, err) + assert.Nil(t, err) selectStmt, ok := stmt.(*sqlparser.Select) - assert.Assert(t, ok) + assert.True(t, ok) got, err := parseOrderBy(selectStmt) if tt.wantErr { - assert.Assert(t, err != nil) + assert.True(t, err != nil) } else { - assert.NilError(t, err) - assert.DeepEqual(t, tt.want, got) + assert.Nil(t, err) + assert.Equal(t, tt.want, got) } }) } @@ -410,16 +410,16 @@ func TestParseLimit(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { stmt, err := sqlparser.Parse(replaceCustomSyntax(tt.sql)) - assert.NilError(t, err) + assert.Nil(t, err) selectStmt, ok := stmt.(*sqlparser.Select) - assert.Assert(t, ok) + assert.True(t, ok) got, err := parseLimit(selectStmt) if tt.wantErr { - assert.Assert(t, err != nil) + assert.True(t, err != nil) } else { - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, tt.want, got) } }) diff --git a/internal/sql/executor_test.go b/internal/sql/executor_test.go index 7feb9f4d3..918c61f95 100644 --- a/internal/sql/executor_test.go +++ b/internal/sql/executor_test.go @@ -1,17 +1,17 @@ package sql_test import ( - "github.com/dicedb/dice/internal/object" - "github.com/dicedb/dice/internal/sql" "sort" "testing" + "github.com/dicedb/dice/internal/object" + "github.com/dicedb/dice/internal/sql" + "github.com/bytedance/sonic" "github.com/dicedb/dice/internal/server/utils" dstore "github.com/dicedb/dice/internal/store" + "github.com/stretchr/testify/assert" "github.com/xwb1989/sqlparser" - "gotest.tools/v3/assert" - "gotest.tools/v3/assert/cmp" ) type keyValue struct { @@ -47,11 +47,11 @@ func TestExecuteQueryOrderBykey(t *testing.T) { queryString := "SELECT $key, $value WHERE $key like 'k*' ORDER BY $key ASC" query, err := sql.ParseQuery(queryString) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), len(simpleKVDataset)) sortedDataset := make([]keyValue, len(simpleKVDataset)) @@ -64,7 +64,7 @@ func TestExecuteQueryOrderBykey(t *testing.T) { for i, data := range sortedDataset { assert.Equal(t, result[i].Key, data.key) - assert.DeepEqual(t, result[i].Value.Value, data.value) + assert.Equal(t, result[i].Value.Value, data.value) } } @@ -74,11 +74,11 @@ func TestExecuteQueryBasicOrderByValue(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like 'k*' ORDER BY $value ASC" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), len(simpleKVDataset)) sortedDataset := make([]keyValue, len(simpleKVDataset)) @@ -91,7 +91,7 @@ func TestExecuteQueryBasicOrderByValue(t *testing.T) { for i, data := range sortedDataset { assert.Equal(t, result[i].Key, data.key) - assert.DeepEqual(t, result[i].Value.Value, data.value) + assert.Equal(t, result[i].Value.Value, data.value) } } @@ -101,12 +101,12 @@ func TestExecuteQueryLimit(t *testing.T) { queryStr := "SELECT $value WHERE $key like 'k*' ORDER BY $key ASC LIMIT 3" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) - assert.Assert(t, cmp.Len(result, 3)) // Checks if limit is respected + assert.Nil(t, err) + assert.Equal(t, len(result), 3) // Checks if limit is respected sortedDataset := make([]keyValue, len(simpleKVDataset)) copy(sortedDataset, simpleKVDataset) @@ -118,7 +118,7 @@ func TestExecuteQueryLimit(t *testing.T) { for i, data := range sortedDataset[:3] { assert.Equal(t, result[i].Key, utils.EmptyStr) - assert.DeepEqual(t, result[i].Value.Value, data.value) + assert.Equal(t, result[i].Value.Value, data.value) } } @@ -128,12 +128,12 @@ func TestExecuteQueryNoMatch(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like 'x*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) - assert.Assert(t, cmp.Len(result, 0)) // No keys match "x*" + assert.Nil(t, err) + assert.Equal(t, len(result), 0) // No keys match "x*" } func TestExecuteQueryWithWhere(t *testing.T) { @@ -142,50 +142,50 @@ func TestExecuteQueryWithWhere(t *testing.T) { t.Run("BasicWhereClause", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $value = 'v3' AND $key like 'k*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 1, "Expected 1 result for WHERE clause") assert.Equal(t, result[0].Key, "k3") - assert.DeepEqual(t, result[0].Value.Value, "v3") + assert.Equal(t, result[0].Value.Value, "v3") }) t.Run("EmptyResult", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $value = 'nonexistent' AND $key like 'k*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 0, "Expected empty result for non-matching WHERE clause") }) t.Run("ComplexWhereClause", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $value > 'v2' AND $value < 'v5' AND $key like 'k*' ORDER BY $key ASC" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 2, "Expected 2 results for complex WHERE clause") - assert.DeepEqual(t, []string{result[0].Key, result[1].Key}, []string{"k2", "k3"}) + assert.Equal(t, []string{result[0].Key, result[1].Key}, []string{"k2", "k3"}) }) t.Run("ComparingKeyWithValue", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key = $value" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 1, "Expected 1 result for comparison between key and value") assert.Equal(t, result[0].Key, "k") - assert.DeepEqual(t, result[0].Value.Value, "k") + assert.Equal(t, result[0].Value.Value, "k") }) } @@ -196,7 +196,7 @@ func TestExecuteQueryWithIncompatibleTypes(t *testing.T) { t.Run("ComparingStrWithInt", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $value = 42 AND $key like 'k*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) _, err = sql.ExecuteQuery(&query, store.GetStore()) @@ -223,7 +223,7 @@ func TestExecuteQueryWithEdgeCases(t *testing.T) { result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 0, "Expected 0 results due to case sensitivity") }) @@ -233,15 +233,15 @@ func TestExecuteQueryWithEdgeCases(t *testing.T) { result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 2, "Expected 2 results for WHERE clause on key") - assert.DeepEqual(t, []string{result[0].Key, result[1].Key}, []string{"k4", "k5"}) + assert.Equal(t, []string{result[0].Key, result[1].Key}, []string{"k4", "k5"}) }) t.Run("UnsupportedOperator", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $value regexp '%3' AND $key like 'k*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) _, err = sql.ExecuteQuery(&query, store.GetStore()) @@ -251,11 +251,11 @@ func TestExecuteQueryWithEdgeCases(t *testing.T) { t.Run("EmptyKeyRegex", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like ''" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 0, "Expected no keys to be returned for empty regex") }) } @@ -291,97 +291,97 @@ func TestExecuteQueryWithJsonExpressionInWhere(t *testing.T) { t.Run("BasicWhereClauseWithJSON", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE '$value.name' = 'Tom' AND $key like 'json*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 1, "Expected 1 results for WHERE clause") assert.Equal(t, result[0].Key, "json1") var expected, actual interface{} - assert.NilError(t, sonic.UnmarshalString(`{"name":"Tom"}`, &expected)) - assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) - assert.DeepEqual(t, actual, expected) + assert.Nil(t, sonic.UnmarshalString(`{"name":"Tom"}`, &expected)) + assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) + assert.Equal(t, actual, expected) }) t.Run("EmptyResult", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE '$value.name' = 'Bill' AND $key like 'json*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 0, "Expected empty result for non-matching WHERE clause") }) t.Run("WhereClauseWithFloats", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE '$value.score' > 13.15 AND $key like 'json*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 1, "Expected 1 result for WHERE clause with floating point values") assert.Equal(t, result[0].Key, "json2") var expected, actual interface{} - assert.NilError(t, sonic.UnmarshalString(`{"name":"Bob","score":18.1}`, &expected)) - assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) - assert.DeepEqual(t, actual, expected) + assert.Nil(t, sonic.UnmarshalString(`{"name":"Bob","score":18.1}`, &expected)) + assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) + assert.Equal(t, actual, expected) }) t.Run("WhereClauseWithInteger", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE '$value.scoreInt' > 13 AND $key like 'json*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 1, "Expected 1 result for WHERE clause with integer values") assert.Equal(t, result[0].Key, "json3") var expected, actual interface{} - assert.NilError(t, sonic.UnmarshalString(`{"scoreInt":20}`, &expected)) - assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) - assert.DeepEqual(t, actual, expected) + assert.Nil(t, sonic.UnmarshalString(`{"scoreInt":20}`, &expected)) + assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) + assert.Equal(t, actual, expected) }) t.Run("NestedWhereClause", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE '$value.field1.field2.field3.score' < 13 AND $key like 'json*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 1, "Expected 1 result for WHERE clause with nested json") assert.Equal(t, result[0].Key, "json4") var expected, actual interface{} - assert.NilError(t, sonic.UnmarshalString(`{"field1":{"field2":{"field3":{"score":2}}}}`, &expected)) - assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) - assert.DeepEqual(t, actual, expected) + assert.Nil(t, sonic.UnmarshalString(`{"field1":{"field2":{"field3":{"score":2}}}}`, &expected)) + assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) + assert.Equal(t, actual, expected) }) t.Run("ComplexWhereClause", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE '$value.field1.field2.field3.score' > '$value.field1.score2' AND $key like 'json*'" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), 1, "Expected 1 result for Complex WHERE clause expression") assert.Equal(t, result[0].Key, "json5") var expected, actual interface{} - assert.NilError(t, sonic.UnmarshalString(`{"field1":{"field2":{"field3":{"score":18}},"score2":5}}`, &expected)) - assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) - assert.DeepEqual(t, actual, expected) + assert.Nil(t, sonic.UnmarshalString(`{"field1":{"field2":{"field3":{"score":18}},"score2":5}}`, &expected)) + assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual)) + assert.Equal(t, actual, expected) }) } @@ -400,11 +400,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) { t.Run("OrderBySimpleJSONField", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY $value.name ASC" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, 5, len(result), "Expected 5 results") assert.Equal(t, "json3", result[0].Key) // Alice @@ -426,11 +426,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) { t.Run("OrderByNumericJSONField", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY $value.age DESC" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, 5, len(result)) assert.Equal(t, "json5", result[0].Key) // Charlie, age 50 @@ -452,11 +452,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) { t.Run("OrderByNestedJSONField", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY '$value.nested.field.value' ASC" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, 5, len(result)) assert.Equal(t, "json3", result[0].Key) // Alice, nested.field.value: 15 validateJSONStringRepresentationsAreEqual(t, jsonOrderDataset[0].value, result[0].Value.Value.(string)) @@ -477,11 +477,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) { t.Run("OrderByMixedTypes", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY $value.score DESC" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) // No ordering guarantees for mixed types. assert.Equal(t, 5, len(result)) }) @@ -489,11 +489,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) { t.Run("OrderByWithWhereClause", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like 'json*' AND '$value.age' > 30 ORDER BY $value.name DESC" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, 3, len(result), "Expected 3 results (age > 30, ordered by name)") assert.Equal(t, "json4", result[0].Key) // Eve, age 32 validateJSONStringRepresentationsAreEqual(t, jsonOrderDataset[4].value, result[0].Value.Value.(string)) @@ -508,11 +508,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) { t.Run("OrderByNonExistentField", func(t *testing.T) { queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY $value.nonexistent ASC" query, err := sql.ParseQuery(queryStr) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) // No ordering guarantees for non-existent field references. assert.Equal(t, 5, len(result), "Expected 5 results") }) @@ -522,9 +522,9 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) { func validateJSONStringRepresentationsAreEqual(t *testing.T, expectedJSONString, actualJSONString string) { t.Helper() var expectedValue, actualValue interface{} - assert.NilError(t, sonic.UnmarshalString(expectedJSONString, &expectedValue)) - assert.NilError(t, sonic.UnmarshalString(actualJSONString, &actualValue)) - assert.DeepEqual(t, actualValue, expectedValue) + assert.Nil(t, sonic.UnmarshalString(expectedJSONString, &expectedValue)) + assert.Nil(t, sonic.UnmarshalString(actualJSONString, &actualValue)) + assert.Equal(t, actualValue, expectedValue) } // Dataset will be used for LIKE comparisons @@ -625,10 +625,10 @@ func TestExecuteQueryWithLikeStringComparisons(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { query, err := sql.ParseQuery(tc.query) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), tc.expectLen, "Expected %d results, got %d", tc.expectLen, len(result)) resultKeys := make([]string, len(result)) @@ -636,7 +636,7 @@ func TestExecuteQueryWithLikeStringComparisons(t *testing.T) { resultKeys[i] = r.Key } - assert.DeepEqual(t, resultKeys, tc.expectKeys) + assert.Equal(t, resultKeys, tc.expectKeys) }) } } @@ -704,10 +704,10 @@ func TestExecuteQueryWithStringNotLikeComparisons(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { query, err := sql.ParseQuery(tc.query) - assert.NilError(t, err) + assert.Nil(t, err) result, err := sql.ExecuteQuery(&query, store.GetStore()) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, len(result), tc.expectLen, "Expected %d results, got %d", tc.expectLen, len(result)) resultKeys := make([]string, len(result)) @@ -715,7 +715,7 @@ func TestExecuteQueryWithStringNotLikeComparisons(t *testing.T) { resultKeys[i] = r.Key } - assert.DeepEqual(t, resultKeys, tc.expectKeys) + assert.Equal(t, resultKeys, tc.expectKeys) }) } } diff --git a/internal/sql/fingerprint_test.go b/internal/sql/fingerprint_test.go index 38fecf40c..baedca8a9 100644 --- a/internal/sql/fingerprint_test.go +++ b/internal/sql/fingerprint_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/xwb1989/sqlparser" - "gotest.tools/v3/assert" + "github.com/stretchr/testify/assert" ) func TestExpressionString(t *testing.T) { @@ -112,7 +112,7 @@ func TestCombineOr(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.DeepEqual(t, tt.expected, combineOr(tt.a, tt.b)) + assert.Equal(t, tt.expected, combineOr(tt.a, tt.b)) }) } } @@ -207,7 +207,7 @@ func TestCombineAnd(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.DeepEqual(t, tt.expected, combineAnd(tt.a, tt.b)) + assert.Equal(t, tt.expected, combineAnd(tt.a, tt.b)) }) } } @@ -350,8 +350,8 @@ func TestGenerateFingerprintAndParseAstExpression(t *testing.T) { if err != nil { t.Fail() } - assert.DeepEqual(t, tt.expression, parseAstExpression(where).String()) - assert.DeepEqual(t, tt.fingerprint, generateFingerprint(where)) + assert.Equal(t, tt.expression, parseAstExpression(where).String()) + assert.Equal(t, tt.fingerprint, generateFingerprint(where)) } }) } diff --git a/internal/watchmanager/watch_manager.go b/internal/watchmanager/watch_manager.go index 571dc1f33..5dae688cd 100644 --- a/internal/watchmanager/watch_manager.go +++ b/internal/watchmanager/watch_manager.go @@ -18,15 +18,15 @@ type ( } Manager struct { - querySubscriptionMap map[string]map[uint32]struct{} // querySubscriptionMap is a map of Key -> [fingerprint1, fingerprint2, ...] - tcpSubscriptionMap map[uint32]map[chan *cmd.DiceDBCmd]struct{} // tcpSubscriptionMap is a map of fingerprint -> [client1Chan, client2Chan, ...] - fingerprintCmdMap map[uint32]*cmd.DiceDBCmd // fingerprintCmdMap is a map of fingerprint -> DiceDBCmd + querySubscriptionMap map[string]map[uint32]struct{} // querySubscriptionMap is a map of Key -> [fingerprint1, fingerprint2, ...] + tcpSubscriptionMap map[uint32]map[chan *cmd.DiceDBCmd]struct{} // tcpSubscriptionMap is a map of fingerprint -> [client1Chan, client2Chan, ...] + fingerprintCmdMap map[uint32]*cmd.DiceDBCmd // fingerprintCmdMap is a map of fingerprint -> DiceDBCmd + cmdWatchSubscriptionChan chan WatchSubscription // cmdWatchSubscriptionChan is the channel to send/receive watch subscription requests. } ) var ( - CmdWatchSubscriptionChan chan WatchSubscription - affectedCmdMap = map[string]map[string]struct{}{ + affectedCmdMap = map[string]map[string]struct{}{ dstore.Set: {dstore.Get: struct{}{}}, dstore.Del: {dstore.Get: struct{}{}}, dstore.Rename: {dstore.Get: struct{}{}}, @@ -34,12 +34,12 @@ var ( } ) -func NewManager() *Manager { - CmdWatchSubscriptionChan = make(chan WatchSubscription) +func NewManager(cmdWatchSubscriptionChan chan WatchSubscription) *Manager { return &Manager{ - querySubscriptionMap: make(map[string]map[uint32]struct{}), - tcpSubscriptionMap: make(map[uint32]map[chan *cmd.DiceDBCmd]struct{}), - fingerprintCmdMap: make(map[uint32]*cmd.DiceDBCmd), + querySubscriptionMap: make(map[string]map[uint32]struct{}), + tcpSubscriptionMap: make(map[uint32]map[chan *cmd.DiceDBCmd]struct{}), + fingerprintCmdMap: make(map[uint32]*cmd.DiceDBCmd), + cmdWatchSubscriptionChan: cmdWatchSubscriptionChan, } } @@ -63,7 +63,7 @@ func (m *Manager) listenForEvents(ctx context.Context, cmdWatchChan chan dstore. select { case <-ctx.Done(): return - case sub := <-CmdWatchSubscriptionChan: + case sub := <-m.cmdWatchSubscriptionChan: if sub.Subscribe { m.handleSubscription(sub) } else { diff --git a/internal/worker/cmd_meta.go b/internal/worker/cmd_meta.go index 55f666bc8..b92d38ce0 100644 --- a/internal/worker/cmd_meta.go +++ b/internal/worker/cmd_meta.go @@ -67,6 +67,9 @@ const ( const ( CmdGetWatch = "GET.WATCH" CmdZRangeWatch = "ZRANGE.WATCH" + CmdHExists = "HEXISTS" + CmdHKeys = "HKEYS" + CmdHVals = "HVALS" CmdZPopMin = "ZPOPMIN" CmdJSONClear = "JSON.CLEAR" CmdJSONStrlen = "JSON.STRLEN" @@ -75,6 +78,8 @@ const ( CmdZRange = "ZRANGE" CmdZRank = "ZRANK" CmdZCount = "ZCOUNT" + CmdZRem = "ZREM" + CmdZCard = "ZCARD" CmdPFAdd = "PFADD" CmdPFCount = "PFCOUNT" CmdPFMerge = "PFMERGE" @@ -149,6 +154,15 @@ var CommandsMeta = map[string]CmdMeta{ CmdGetSet: { CmdType: SingleShard, }, + CmdHExists: { + CmdType: SingleShard, + }, + CmdHKeys: { + CmdType: SingleShard, + }, + CmdHVals: { + CmdType: SingleShard, + }, CmdJSONArrAppend: { CmdType: SingleShard, }, @@ -274,6 +288,12 @@ var CommandsMeta = map[string]CmdMeta{ CmdZRange: { CmdType: SingleShard, }, + CmdZCard: { + CmdType: SingleShard, + }, + CmdZRem: { + CmdType: SingleShard, + }, CmdAppend: { CmdType: SingleShard, }, diff --git a/internal/worker/worker.go b/internal/worker/worker.go index 107757ea2..67f127001 100644 --- a/internal/worker/worker.go +++ b/internal/worker/worker.go @@ -38,30 +38,33 @@ type Worker interface { } type BaseWorker struct { - id string - ioHandler iohandler.IOHandler - parser requestparser.Parser - shardManager *shard.ShardManager - adhocReqChan chan *cmd.DiceDBCmd - Session *auth.Session - globalErrorChan chan error - responseChan chan *ops.StoreResponse - preprocessingChan chan *ops.StoreResponse + id string + ioHandler iohandler.IOHandler + parser requestparser.Parser + shardManager *shard.ShardManager + adhocReqChan chan *cmd.DiceDBCmd + Session *auth.Session + globalErrorChan chan error + responseChan chan *ops.StoreResponse + preprocessingChan chan *ops.StoreResponse + cmdWatchSubscriptionChan chan watchmanager.WatchSubscription } func NewWorker(wid string, responseChan, preprocessingChan chan *ops.StoreResponse, + cmdWatchSubscriptionChan chan watchmanager.WatchSubscription, ioHandler iohandler.IOHandler, parser requestparser.Parser, shardManager *shard.ShardManager, gec chan error) *BaseWorker { return &BaseWorker{ - id: wid, - ioHandler: ioHandler, - parser: parser, - shardManager: shardManager, - globalErrorChan: gec, - responseChan: responseChan, - preprocessingChan: preprocessingChan, - Session: auth.NewSession(), - adhocReqChan: make(chan *cmd.DiceDBCmd, config.DiceConfig.Performance.AdhocReqChanBufSize), + id: wid, + ioHandler: ioHandler, + parser: parser, + shardManager: shardManager, + globalErrorChan: gec, + responseChan: responseChan, + preprocessingChan: preprocessingChan, + cmdWatchSubscriptionChan: cmdWatchSubscriptionChan, + Session: auth.NewSession(), + adhocReqChan: make(chan *cmd.DiceDBCmd, config.DiceConfig.Performance.AdhocReqChanBufSize), } } @@ -260,7 +263,7 @@ func (w *BaseWorker) executeCommand(ctx context.Context, diceDBCmd *cmd.DiceDBCm if meta.CmdType == Watch { // Proceed to subscribe after successful execution - watchmanager.CmdWatchSubscriptionChan <- watchmanager.WatchSubscription{ + w.cmdWatchSubscriptionChan <- watchmanager.WatchSubscription{ Subscribe: true, WatchCmd: cmdList[len(cmdList)-1], AdhocReqChan: w.adhocReqChan, diff --git a/main.go b/main.go index 11c99dd5a..e11a66479 100644 --- a/main.go +++ b/main.go @@ -12,11 +12,13 @@ import ( "runtime" "runtime/pprof" "runtime/trace" + "strings" "sync" "syscall" "github.com/dicedb/dice/internal/logger" "github.com/dicedb/dice/internal/server/abstractserver" + "github.com/dicedb/dice/internal/watchmanager" "github.com/dicedb/dice/config" diceerrors "github.com/dicedb/dice/internal/errors" @@ -28,6 +30,13 @@ import ( "github.com/dicedb/dice/internal/worker" ) +type configEntry struct { + Key string + Value interface{} +} + +var configTable = []configEntry{} + func init() { flag.StringVar(&config.Host, "host", "0.0.0.0", "host for the DiceDB server") @@ -65,24 +74,95 @@ func init() { slog.SetDefault(logger.New()) } -func main() { +func printSplash() { fmt.Print(` -██████╗ ██╗ ██████╗███████╗██████╗ ██████╗ -██╔══██╗██║██╔════╝██╔════╝██╔══██╗██╔══██╗ -██║ ██║██║██║ █████╗ ██║ ██║██████╔╝ -██║ ██║██║██║ ██╔══╝ ██║ ██║██╔══██╗ -██████╔╝██║╚██████╗███████╗██████╔╝██████╔╝ -╚═════╝ ╚═╝ ╚═════╝╚══════╝╚═════╝ ╚═════╝ - -`) - slog.Info("starting DiceDB", slog.String("version", config.DiceDBVersion)) - slog.Info("running with", slog.Int("port", config.Port)) - slog.Info("running with", slog.Bool("enable-watch", config.EnableWatch)) - - if config.EnableProfiling { - slog.Info("running with", slog.Bool("enable-profiling", config.EnableProfiling)) + ██████╗ ██╗ ██████╗███████╗██████╗ ██████╗ + ██╔══██╗██║██╔════╝██╔════╝██╔══██╗██╔══██╗ + ██║ ██║██║██║ █████╗ ██║ ██║██████╔╝ + ██║ ██║██║██║ ██╔══╝ ██║ ██║██╔══██╗ + ██████╔╝██║╚██████╗███████╗██████╔╝██████╔╝ + ╚═════╝ ╚═╝ ╚═════╝╚══════╝╚═════╝ ╚═════╝ + + `) +} + +// configuration function used to add configuration values to the print table at the startup. +// add entry to this function to add a new row in the startup configuration table. +func configuration() { + // Add the version of the DiceDB to the configuration table + addEntry("Version", config.DiceDBVersion) + + // Add the port number on which DiceDB is running to the configuration table + addEntry("Port", config.Port) + + // Add whether multi-threading is enabled to the configuration table + addEntry("Multi Threading Enabled", config.EnableMultiThreading) + + // Add the number of CPU cores available on the machine to the configuration table + addEntry("Cores", runtime.NumCPU()) + + // Conditionally add the number of shards to be used for DiceDB to the configuration table + if config.EnableMultiThreading { + if config.NumShards > 0 { + configTable = append(configTable, configEntry{"Shards", config.NumShards}) + } else { + configTable = append(configTable, configEntry{"Shards", runtime.NumCPU()}) + } + } else { + configTable = append(configTable, configEntry{"Shards", 1}) } + // Add whether the watch feature is enabled to the configuration table + addEntry("Watch Enabled", config.EnableWatch) + + // Add whether the watch feature is enabled to the configuration table + addEntry("HTTP Enabled", config.EnableHTTP) + + // Add whether the watch feature is enabled to the configuration table + addEntry("Websocket Enabled", config.EnableWebsocket) +} + +func addEntry(k string, v interface{}) { + configTable = append(configTable, configEntry{k, v}) +} + +// printConfigTable prints key-value pairs in a vertical table format. +func printConfigTable() { + configuration() + + // Find the longest key to align the values properly + maxKeyLength := 0 + maxValueLength := 20 // Default value length for alignment + for _, entry := range configTable { + if len(entry.Key) > maxKeyLength { + maxKeyLength = len(entry.Key) + } + if len(fmt.Sprintf("%v", entry.Value)) > maxValueLength { + maxValueLength = len(fmt.Sprintf("%v", entry.Value)) + } + } + + // Create the table header and separator line + fmt.Println() + totalWidth := maxKeyLength + maxValueLength + 7 // 7 is for spacing and pipes + fmt.Println(strings.Repeat("-", totalWidth)) + fmt.Printf("| %-*s | %-*s |\n", maxKeyLength, "Configuration", maxValueLength, "Value") + fmt.Println(strings.Repeat("-", totalWidth)) + + // Print each configuration key-value pair without row lines + for _, entry := range configTable { + fmt.Printf("| %-*s | %-20v |\n", maxKeyLength, entry.Key, entry.Value) + } + + // Final bottom line + fmt.Println(strings.Repeat("-", totalWidth)) + fmt.Println() +} + +func main() { + printSplash() + printConfigTable() + go observability.Ping() ctx, cancel := context.WithCancel(context.Background()) @@ -92,9 +172,10 @@ func main() { signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT) var ( - queryWatchChan chan dstore.QueryWatchEvent - cmdWatchChan chan dstore.CmdWatchEvent - serverErrCh = make(chan error, 2) + queryWatchChan chan dstore.QueryWatchEvent + cmdWatchChan chan dstore.CmdWatchEvent + cmdWatchSubscriptionChan chan watchmanager.WatchSubscription + serverErrCh = make(chan error, 2) ) if config.EnableWatch { @@ -114,10 +195,8 @@ func main() { if config.NumShards > 0 { numShards = config.NumShards } - slog.Info("running with", slog.String("mode", "multi-threaded"), slog.Int("num-shards", numShards)) } else { numShards = 1 - slog.Info("running with", slog.String("mode", "single-threaded")) } // The runtime.GOMAXPROCS(numShards) call limits the number of operating system @@ -150,7 +229,7 @@ func main() { } workerManager := worker.NewWorkerManager(config.DiceConfig.Performance.MaxClients, shardManager) - respServer := resp.NewServer(shardManager, workerManager, cmdWatchChan, serverErrCh) + respServer := resp.NewServer(shardManager, workerManager, cmdWatchSubscriptionChan, cmdWatchChan, serverErrCh) serverWg.Add(1) go runServer(ctx, &serverWg, respServer, serverErrCh) } else {