diff --git a/.eslintignore b/.eslintignore index db331168..56232c2d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ node_modules dist types +docs \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 99% rename from .eslintrc.js rename to .eslintrc.cjs index d07c2d21..a56a62c6 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -110,7 +110,7 @@ module.exports = { 'no-native-reassign': 'error', 'no-negated-condition': 'off', 'no-negated-in-lhs': 'error', - 'no-nested-ternary': 'error', + 'no-nested-ternary': 'off', 'no-new': 'error', 'no-new-func': 'error', 'no-new-object': 'error', diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index e749da5c..3b38b421 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -6,23 +6,32 @@ on: - '**' pull_request: - env: - WEAVIATE_VERSION: 1.25.1 + WEAVIATE_124: 1.24.19 + WEAVIATE_125: 1.25.5 jobs: checks: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: [ + "18.x", + "20.x", + "22.x" + ] steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '18.x' + node-version: ${{ matrix.node }} - name: "Run checks" run: | - npm install + npm ci npm run lint npm run format:check + npm run docs tests: needs: checks @@ -31,9 +40,12 @@ jobs: fail-fast: false matrix: versions: [ - { node: "18.x", weaviate: $WEAVIATE_VERSION}, - { node: "20.x", weaviate: $WEAVIATE_VERSION}, - { node: "22.x", weaviate: $WEAVIATE_VERSION} + { node: "18.x", weaviate: $WEAVIATE_124}, + { node: "20.x", weaviate: $WEAVIATE_124}, + { node: "22.x", weaviate: $WEAVIATE_124}, + { node: "18.x", weaviate: $WEAVIATE_125}, + { node: "20.x", weaviate: $WEAVIATE_125}, + { node: "22.x", weaviate: $WEAVIATE_125} ] steps: - uses: actions/checkout@v3 @@ -42,7 +54,7 @@ jobs: node-version: ${{ matrix.versions.node }} - name: "Install dependencies" run: | - npm install + npm ci ci/run_dependencies.sh ${{ matrix.versions.weaviate }} - name: "Run tests with authentication tests" if: ${{ !github.event.pull_request.head.repo.fork }} @@ -66,6 +78,10 @@ jobs: needs: tests if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') runs-on: ubuntu-latest + permissions: + contents: write # to upload the release + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source steps: - uses: actions/checkout@v3 # Setup .npmrc file to publish to npm @@ -73,12 +89,20 @@ jobs: with: node-version: '18.x' registry-url: 'https://registry.npmjs.org' - - run: npm ci && npm run build + - run: npm ci + - run: npm run build - run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }} + - run: npm run docs + - name: "Upload docs as pages artifact" + uses: actions/upload-pages-artifact@v3 + with: + path: ./docs + - name: "Deploy the uploaded pages artifact" + uses: actions/deploy-pages@v4 - name: "Create a GitHub release" uses: softprops/action-gh-release@v1 with: generate_release_notes: true - draft: true + draft: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 075bcdd9..a82a7b68 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ weaviate-data/ *.tgz .npmrc .eslintcache -test.sh \ No newline at end of file +test.sh +scratch/ +docs/ \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..4641164b --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +src/proto/**/*.ts +src/dist/**/* +src/docs/**/*.js \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index f5e77c5f..9a3e98d0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,5 +4,6 @@ "trailingComma": "es5", "bracketSpacing": true, "singleQuote": true, - "printWidth": 110 + "printWidth": 110, + "tabWidth": 2 } diff --git a/README.md b/README.md index 9e03a7a4..11228b40 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ -# Weaviate TypeScript client Weaviate logo +# Weaviate JS/TS client Weaviate logo -Official TypeScript client for easy interaction with a Weaviate instance. +Official JS/TS client for easy interaction with a Weaviate instance. ## Documentation -- [Documentation](https://weaviate.io/developers/weaviate/client-libraries/typescript). +- [General Documentation](https://weaviate.io/developers/weaviate/client-libraries/typescript). +- [Client-specific Documentation](https://weaviate.github.io/typescript-client/) ## Support diff --git a/ci/compose.sh b/ci/compose.sh index d8e91883..a841e738 100644 --- a/ci/compose.sh +++ b/ci/compose.sh @@ -8,7 +8,7 @@ function ls_compose { function exec_all { for file in $(ls_compose); do - docker-compose -f $(echo "ci/${file} ${1}") + docker compose -f $(echo "ci/${file} ${1}") done } @@ -21,5 +21,5 @@ function compose_down_all { } function all_weaviate_ports { - echo "8080 8081 8082 8083 8085 8086 8087 8088" + echo "8078 8079 8080 8081 8082 8083 8085 8086 8087 8088 8089 8090" } diff --git a/ci/docker-compose-async.yml b/ci/docker-compose-async.yml new file mode 100644 index 00000000..d1bb2734 --- /dev/null +++ b/ci/docker-compose-async.yml @@ -0,0 +1,23 @@ +--- +version: '3.4' +services: + weaviate_async: + command: + - --host + - 0.0.0.0 + - --port + - '8090' + - --scheme + - http + image: semitechnologies/weaviate:${WEAVIATE_VERSION} + ports: + - "8078:8090" + - "50049:50051" + restart: on-failure:0 + environment: + QUERY_DEFAULTS_LIMIT: 25 + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' + PERSISTENCE_DATA_PATH: '/var/lib/weaviate' + CLUSTER_HOSTNAME: 'node1' + ASYNC_INDEXING: 'true' + DISABLE_TELEMETRY: 'true' diff --git a/ci/docker-compose-backup.yml b/ci/docker-compose-backup.yml new file mode 100644 index 00000000..9d1e0d64 --- /dev/null +++ b/ci/docker-compose-backup.yml @@ -0,0 +1,19 @@ +--- +version: '3.4' +services: + weaviate-backup: + image: semitechnologies/weaviate:${WEAVIATE_VERSION} + restart: on-failure:0 + ports: + - 8090:8080 + - 50061:50051 + environment: + QUERY_DEFAULTS_LIMIT: 20 + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' + PERSISTENCE_DATA_PATH: "./weaviate-data" + BACKUP_FILESYSTEM_PATH: "/tmp/backups" + ENABLE_MODULES: backup-filesystem + CLUSTER_GOSSIP_BIND_PORT: "7100" + CLUSTER_DATA_BIND_PORT: "7101" + DISABLE_TELEMETRY: 'true' +... diff --git a/ci/docker-compose-cluster.yml b/ci/docker-compose-cluster.yml index 2d22c48d..aa44675a 100644 --- a/ci/docker-compose-cluster.yml +++ b/ci/docker-compose-cluster.yml @@ -86,4 +86,4 @@ services: CONTEXTIONARY_URL: contextionary:9999 DEFAULT_VECTORIZER_MODULE: text2vec-contextionary ENABLE_MODULES: text2vec-contextionary -... \ No newline at end of file +... diff --git a/ci/docker-compose-openai.yml b/ci/docker-compose-openai.yml index 7ab46221..788ed413 100644 --- a/ci/docker-compose-openai.yml +++ b/ci/docker-compose-openai.yml @@ -12,6 +12,7 @@ services: image: semitechnologies/weaviate:${WEAVIATE_VERSION} ports: - 8086:8086 + - 50057:50051 restart: on-failure:0 environment: QUERY_DEFAULTS_LIMIT: 25 diff --git a/ci/docker-compose-proxy.yml b/ci/docker-compose-proxy.yml new file mode 100644 index 00000000..569eab3b --- /dev/null +++ b/ci/docker-compose-proxy.yml @@ -0,0 +1,53 @@ +--- +version: '3.4' +services: + weaviate-proxy: + command: + - --host + - 0.0.0.0 + - --port + - '8020' + - --scheme + - http + - --write-timeout=600s + image: semitechnologies/weaviate:${WEAVIATE_VERSION} + restart: on-failure:0 + environment: + CONTEXTIONARY_URL: contextionary:9999 + QUERY_DEFAULTS_LIMIT: 25 + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' + PERSISTENCE_DATA_PATH: '/var/lib/weaviate' + DEFAULT_VECTORIZER_MODULE: 'text2vec-contextionary' + ENABLE_MODULES: text2vec-contextionary + BACKUP_FILESYSTEM_PATH: "/tmp/backups" + CLUSTER_GOSSIP_BIND_PORT: "7100" + CLUSTER_DATA_BIND_PORT: "7101" + CLUSTER_HOSTNAME: "node1" + AUTOSCHEMA_ENABLED: 'false' + DISABLE_TELEMETRY: 'true' + GRPC_PORT: 8021 + contextionary: + environment: + OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75 + EXTENSIONS_STORAGE_MODE: weaviate + EXTENSIONS_STORAGE_ORIGIN: http://weaviate-proxy:8020 + NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5 + ENABLE_COMPOUND_SPLITTING: 'false' + image: semitechnologies/contextionary:en0.16.0-v1.2.0 + ports: + - 9999:9999 + proxy-http: + image: envoyproxy/envoy:v1.29-latest + command: envoy --config-path /etc/envoy/http.yaml + ports: + - 10000:10000 + volumes: + - ./proxy:/etc/envoy + proxy-grpc: + image: envoyproxy/envoy:v1.29-latest + command: envoy --config-path /etc/envoy/grpc.yaml + ports: + - 10001:10000 + volumes: + - ./proxy:/etc/envoy +... diff --git a/ci/docker-compose-rerank.yml b/ci/docker-compose-rerank.yml new file mode 100644 index 00000000..302bee1d --- /dev/null +++ b/ci/docker-compose-rerank.yml @@ -0,0 +1,30 @@ +--- +version: '3.4' +services: + weaviate-reranker: + command: + - --host + - 0.0.0.0 + - --port + - '8080' + - --scheme + - http + image: semitechnologies/weaviate:${WEAVIATE_VERSION} + ports: + - 8079:8080 + - 50050:50051 + restart: on-failure:0 + environment: + RERANKER_INFERENCE_API: http://reranker-transformers:8080 + QUERY_DEFAULTS_LIMIT: 25 + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' + PERSISTENCE_DATA_PATH: "./data" + DEFAULT_VECTORIZER_MODULE: 'text2vec-openai' + ENABLE_MODULES: 'text2vec-openai,reranker-transformers,generative-openai' + CLUSTER_HOSTNAME: 'node1' + DISABLE_TELEMETRY: 'true' + reranker-transformers: + image: semitechnologies/reranker-transformers:cross-encoder-ms-marco-MiniLM-L-6-v2 + environment: + ENABLE_CUDA: '0' +... \ No newline at end of file diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml index f0b490e8..74746f97 100644 --- a/ci/docker-compose.yml +++ b/ci/docker-compose.yml @@ -2,29 +2,43 @@ version: '3.4' services: weaviate: + command: + - --host + - 0.0.0.0 + - --port + - '8080' + - --scheme + - http + - --write-timeout=600s image: semitechnologies/weaviate:${WEAVIATE_VERSION} - restart: on-failure:0 ports: - "8080:8080" + - "50051:50051" + restart: on-failure:0 environment: CONTEXTIONARY_URL: contextionary:9999 - QUERY_DEFAULTS_LIMIT: 20 + QUERY_DEFAULTS_LIMIT: 25 AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' - PERSISTENCE_DATA_PATH: "./weaviate-data" - DEFAULT_VECTORIZER_MODULE: text2vec-contextionary - ENABLE_MODULES: text2vec-contextionary,backup-filesystem + PERSISTENCE_DATA_PATH: '/var/lib/weaviate' + DEFAULT_VECTORIZER_MODULE: 'text2vec-contextionary' + ENABLE_MODULES: text2vec-contextionary,backup-filesystem,img2vec-neural BACKUP_FILESYSTEM_PATH: "/tmp/backups" CLUSTER_GOSSIP_BIND_PORT: "7100" CLUSTER_DATA_BIND_PORT: "7101" + CLUSTER_HOSTNAME: "node1" + AUTOSCHEMA_ENABLED: 'false' + IMAGE_INFERENCE_API: "http://i2v-neural:8080" DISABLE_TELEMETRY: 'true' contextionary: - image: semitechnologies/contextionary:en0.16.0-v1.2.1 - ports: - - "9999:9999" environment: OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75 EXTENSIONS_STORAGE_MODE: weaviate EXTENSIONS_STORAGE_ORIGIN: http://weaviate:8080 NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5 ENABLE_COMPOUND_SPLITTING: 'false' + image: semitechnologies/contextionary:en0.16.0-v1.2.0 + ports: + - 9999:9999 + i2v-neural: + image: semitechnologies/img2vec-pytorch:resnet50 ... diff --git a/ci/proxy/grpc.yaml b/ci/proxy/grpc.yaml new file mode 100644 index 00000000..1b90a1d0 --- /dev/null +++ b/ci/proxy/grpc.yaml @@ -0,0 +1,66 @@ +# This proxy configuration is one that allows the use of a gRPC proxy between Weaviate and the client. +# It is used in the CI/CD pipeline to test the gRPC proxy functionality of the client. +# It follows the method as described here: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/upgrades#connect-support +# where special attention should be paid to the fact that we are using a terminating HTTP/2 connection, +# as in this example: https://github.com/envoyproxy/envoy/blob/8e93d16d433d3364c2b000dc9067ffc400e8f0d6/configs/terminate_http2_connect.yaml, +# because Weaviate itself is not capable of handling CONNECT requests. So Envoy instead upgrades these to POSTs and sends them on +admin: + address: + socket_address: + protocol: TCP + address: 127.0.0.1 + port_value: 9902 +static_resources: + listeners: + - name: proxy + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 10000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + connect_matcher: {} + route: + cluster: weaviate-grpc + upgrade_configs: + - upgrade_type: CONNECT + connect_config: + {} + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + http2_protocol_options: + allow_connect: true + upgrade_configs: + - upgrade_type: CONNECT + clusters: + - name: weaviate-grpc + type: STRICT_DNS + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: {} + load_assignment: + cluster_name: weaviate-grpc + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: weaviate-proxy + port_value: 8021 \ No newline at end of file diff --git a/ci/proxy/http.yaml b/ci/proxy/http.yaml new file mode 100644 index 00000000..5ddab439 --- /dev/null +++ b/ci/proxy/http.yaml @@ -0,0 +1,58 @@ +# This proxy configuration is one that allows the use of a gRPC proxy between Weaviate and the client. +# It is used in the CI/CD pipeline to test the gRPC proxy functionality of the client. +# It follows the method as described here: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/upgrades#connect-support +# where special attention should be paid to the fact that we are using a terminating HTTP/2 connection, +# as in this example: https://github.com/envoyproxy/envoy/blob/8e93d16d433d3364c2b000dc9067ffc400e8f0d6/configs/terminate_http2_connect.yaml, +# because Weaviate itself is not capable of handling CONNECT requests. So Envoy instead upgrades these to POSTs and sends them on +admin: + address: + socket_address: + protocol: TCP + address: 127.0.0.1 + port_value: 9902 +static_resources: + listeners: + - name: proxy + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 10000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + codec_type: HTTP1 + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/http" + route: + prefix_rewrite: "/" + cluster: weaviate-http + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + clusters: + - name: weaviate-http + type: STRICT_DNS + connect_timeout: 5s + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: weaviate-http + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: weaviate-proxy + port_value: 8020 \ No newline at end of file diff --git a/examples/typescript/index.ts b/examples/typescript/index.ts index 146a2498..f2b39162 100644 --- a/examples/typescript/index.ts +++ b/examples/typescript/index.ts @@ -1,8 +1,8 @@ import weaviate, { - AuthClientCredentials, - AuthUserPasswordCredentials, ApiKey, AuthAccessTokenCredentials, + AuthClientCredentials, + AuthUserPasswordCredentials, generateUuid5, } from 'weaviate-ts-client'; diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index e12025f9..00000000 --- a/jest.config.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - clearMocks: false, - collectCoverage: false, - coverageDirectory: 'coverage', - coverageProvider: 'v8', - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ['**/*.test.ts'], - transform: { - '^.+\\.tsx?$': [ - 'ts-jest', - { - tsconfig: 'tsconfig-test.json', - }, - ], - }, -}; diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 00000000..91d9e094 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,28 @@ +import { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + clearMocks: false, + collectCoverage: false, + coverageDirectory: 'coverage', + coveragePathIgnorePatterns: ['/node_modules/', '/dist/', '/src/proto'], + coverageProvider: 'v8', + preset: 'ts-jest/presets/default-esm', // or other ESM presets + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + transform: { + // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest` + // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest` + '^.+\\.tsx?$': [ + 'ts-jest', + { + useESM: true, + }, + ], + }, + testEnvironment: 'node', + testMatch: ['**/*.test.ts'], + testTimeout: 100000, +}; + +export default config; diff --git a/package-lock.json b/package-lock.json index 102cd44d..d8eb5d5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,45 +1,61 @@ { - "name": "weaviate-ts-client", - "version": "2.2.0", - "lockfileVersion": 2, + "name": "weaviate-client", + "version": "3.0.8", + "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "weaviate-ts-client", - "version": "2.2.0", + "name": "weaviate-client", + "version": "3.0.8", "license": "SEE LICENSE IN LICENSE", "dependencies": { - "graphql-request": "^5.2.0", + "graphql": "^16.8.1", + "graphql-request": "^6.1.0", + "long": "^5.2.3", + "nice-grpc": "^2.1.7", + "nice-grpc-client-middleware-deadline": "^2.0.11", "uuid": "^9.0.1" }, "devDependencies": { "@babel/core": "^7.20.12", "@babel/preset-typescript": "^7.18.6", "@babel/runtime": "^7.20.7", - "@curveball/bodyparser": "^0.5.0", - "@curveball/core": "^0.20.0", + "@curveball/bodyparser": "0.5.0", + "@curveball/core": "0.20.0", + "@curveball/kernel": "0.20.1", "@rollup/plugin-babel": "^5.3.1", "@types/isomorphic-fetch": "^0.0.36", "@types/jest": "^29.4.0", "@types/node": "^18.14.0", "@types/uuid": "^9.0.1", - "@typescript-eslint/eslint-plugin": "^5.54.1", - "@typescript-eslint/parser": "^5.54.1", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "babel-jest": "^29.4.3", "eslint": "^8.35.0", "eslint-config-prettier": "^8.7.0", "eslint-plugin-prettier": "^4.2.1", + "graphql-request": "^6.1.0", + "grpc-tools": "^1.12.4", "husky": "^8.0.3", "jest": "^29.4.3", "lint-staged": "^13.2.0", + "long": "^5.2.3", + "nice-grpc": "^2.1.7", "openapi-typescript": "^5.4.1", "prettier": "^2.8.4", + "prettier-plugin-organize-imports": "^3.2.4", + "protobufjs": "^7.2.6", "ts-jest": "^29.0.5", - "tsup": "^6.7.0", - "typescript": "^4.9.5" + "ts-node": "^10.9.2", + "ts-proto": "^1.163.0", + "tsup": "^8.0.2", + "typedoc": "^0.25.12", + "typedoc-plugin-extras": "^3.0.0", + "typescript": "5.1.3", + "uuid": "^9.0.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@ampproject/remapping": { @@ -779,8 +795,6 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -793,8 +807,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -883,13 +895,78 @@ } } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.10", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -899,9 +976,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.10.tgz", - "integrity": "sha512-J4MJzGchuCRG5n+B4EHpAMoJmBeAE1L3wGYDIN5oWNqX0tEr7VKOzw0ymSwpoeSpdCa030lagGUfnfhS7OvzrQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "cpu": [ "x64" ], @@ -915,9 +992,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.10.tgz", - "integrity": "sha512-ZkX40Z7qCbugeK4U5/gbzna/UQkM9d9LNV+Fro8r7HA7sRof5Rwxc46SsqeMvB5ZaR0b1/ITQ/8Y1NmV2F0fXQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", "cpu": [ "arm64" ], @@ -931,9 +1008,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.10.tgz", - "integrity": "sha512-0m0YX1IWSLG9hWh7tZa3kdAugFbZFFx9XrvfpaCMMvrswSTvUZypp0NFKriUurHpBA3xsHVE9Qb/0u2Bbi/otg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "cpu": [ "x64" ], @@ -947,9 +1024,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.10.tgz", - "integrity": "sha512-whRdrrl0X+9D6o5f0sTZtDM9s86Xt4wk1bf7ltx6iQqrIIOH+sre1yjpcCdrVXntQPCNw/G+XqsD4HuxeS+2QA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", "cpu": [ "arm" ], @@ -963,9 +1040,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.10.tgz", - "integrity": "sha512-g1EZJR1/c+MmCgVwpdZdKi4QAJ8DCLP5uTgLWSAVd9wlqk9GMscaNMEViG3aE1wS+cNMzXXgdWiW/VX4J+5nTA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "cpu": [ "arm64" ], @@ -979,9 +1056,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.10.tgz", - "integrity": "sha512-1vKYCjfv/bEwxngHERp7huYfJ4jJzldfxyfaF7hc3216xiDA62xbXJfRlradiMhGZbdNLj2WA1YwYFzs9IWNPw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "cpu": [ "ia32" ], @@ -995,9 +1072,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.10.tgz", - "integrity": "sha512-mvwAr75q3Fgc/qz3K6sya3gBmJIYZCgcJ0s7XshpoqIAIBszzfXsqhpRrRdVFAyV1G9VUjj7VopL2HnAS8aHFA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "cpu": [ "loong64" ], @@ -1011,9 +1088,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.10.tgz", - "integrity": "sha512-XilKPgM2u1zR1YuvCsFQWl9Fc35BqSqktooumOY2zj7CSn5czJn279j9TE1JEqSqz88izJo7yE4x3LSf7oxHzg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "cpu": [ "mips64el" ], @@ -1027,9 +1104,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.10.tgz", - "integrity": "sha512-kM4Rmh9l670SwjlGkIe7pYWezk8uxKHX4Lnn5jBZYBNlWpKMBCVfpAgAJqp5doLobhzF3l64VZVrmGeZ8+uKmQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "cpu": [ "ppc64" ], @@ -1043,9 +1120,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.10.tgz", - "integrity": "sha512-r1m9ZMNJBtOvYYGQVXKy+WvWd0BPvSxMsVq8Hp4GzdMBQvfZRvRr5TtX/1RdN6Va8JMVQGpxqde3O+e8+khNJQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "cpu": [ "riscv64" ], @@ -1059,9 +1136,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.10.tgz", - "integrity": "sha512-LsY7QvOLPw9WRJ+fU5pNB3qrSfA00u32ND5JVDrn/xG5hIQo3kvTxSlWFRP0NJ0+n6HmhPGG0Q4jtQsb6PFoyg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "cpu": [ "s390x" ], @@ -1075,9 +1152,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.10.tgz", - "integrity": "sha512-zJUfJLebCYzBdIz/Z9vqwFjIA7iSlLCFvVi7glMgnu2MK7XYigwsonXshy9wP9S7szF+nmwrelNaP3WGanstEg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ "x64" ], @@ -1091,9 +1168,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.10.tgz", - "integrity": "sha512-lOMkailn4Ok9Vbp/q7uJfgicpDTbZFlXlnKT2DqC8uBijmm5oGtXAJy2ZZVo5hX7IOVXikV9LpCMj2U8cTguWA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "cpu": [ "x64" ], @@ -1107,9 +1184,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.10.tgz", - "integrity": "sha512-/VE0Kx6y7eekqZ+ZLU4AjMlB80ov9tEz4H067Y0STwnGOYL8CsNg4J+cCmBznk1tMpxMoUOf0AbWlb1d2Pkbig==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "cpu": [ "x64" ], @@ -1123,9 +1200,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.10.tgz", - "integrity": "sha512-ERNO0838OUm8HfUjjsEs71cLjLMu/xt6bhOlxcJ0/1MG3hNqCmbWaS+w/8nFLa0DDjbwZQuGKVtCUJliLmbVgg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "cpu": [ "x64" ], @@ -1139,9 +1216,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.10.tgz", - "integrity": "sha512-fXv+L+Bw2AeK+XJHwDAQ9m3NRlNemG6Z6ijLwJAAVdu4cyoFbBWbEtyZzDeL+rpG2lWI51cXeMt70HA8g2MqIg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "cpu": [ "arm64" ], @@ -1155,9 +1232,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.10.tgz", - "integrity": "sha512-3s+HADrOdCdGOi5lnh5DMQEzgbsFsd4w57L/eLKKjMnN0CN4AIEP0DCP3F3N14xnxh3ruNc32A0Na9zYe1Z/AQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "cpu": [ "ia32" ], @@ -1171,9 +1248,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.10.tgz", - "integrity": "sha512-oP+zFUjYNaMNmjTwlFtWep85hvwUu19cZklB3QsBOcZSs6y7hmH4LNCJ7075bsqzYaNvZFXJlAVaQ2ApITDXtw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ "x64" ], @@ -1186,6 +1263,30 @@ "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/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", @@ -1273,10 +1374,43 @@ } }, "node_modules/@graphql-typed-document-node/core": { - "version": "3.1.1", - "license": "MIT", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "dev": true, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.14", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.14.tgz", + "integrity": "sha512-nOpuzZ2G3IuMFN+UPPpKrC6NsLmWsTqSsm66IRfnBt1D4pwTqE27lmbpcPM+l2Ua4gE7PfjRHI6uedAy7hoXUw==", + "dev": true, + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dev": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/@humanwhocodes/config-array": { @@ -1646,6 +1780,38 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1681,6 +1847,70 @@ "node": ">= 8" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "dev": true, @@ -1719,93 +1949,293 @@ "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.25.23", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", + "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@sinonjs/commons": { - "version": "2.0.0", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", + "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", + "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^2.0.0" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", + "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "cpu": [ + "x64" + ], "dev": true, "optional": true, - "peer": true + "os": [ + "darwin" + ] }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", + "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "cpu": [ + "arm" + ], "dev": true, "optional": true, - "peer": true + "os": [ + "linux" + ] }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", + "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "cpu": [ + "arm" + ], "dev": true, "optional": true, - "peer": true + "os": [ + "linux" + ] }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", + "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true + "os": [ + "linux" + ] }, - "node_modules/@types/babel__core": { - "version": "7.20.0", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", + "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/babel__generator": { - "version": "7.6.1", + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", + "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/babel__template": { - "version": "7.0.2", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", + "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/babel__traverse": { - "version": "7.0.14", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", + "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "MIT", - "dependencies": { + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", + "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", + "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", + "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", + "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", + "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.23", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "2.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.0", + "dev": true, + "license": "MIT", + "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.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.0.14", + "dev": true, + "license": "MIT", + "dependencies": { "@babel/types": "^7.3.0" } }, @@ -1863,9 +2293,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "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 }, "node_modules/@types/node": { @@ -1879,9 +2309,9 @@ "license": "MIT" }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1917,32 +2347,33 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz", - "integrity": "sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/type-utils": "5.54.1", - "@typescript-eslint/utils": "5.54.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1984,25 +2415,26 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz", - "integrity": "sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/typescript-estree": "5.54.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2011,16 +2443,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz", - "integrity": "sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/visitor-keys": "5.54.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -2028,25 +2460,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz", - "integrity": "sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.54.1", - "@typescript-eslint/utils": "5.54.1", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2055,12 +2487,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz", - "integrity": "sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -2068,21 +2500,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz", - "integrity": "sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/visitor-keys": "5.54.1", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -2094,26 +2527,35 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "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==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2121,58 +2563,36 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/utils": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz", - "integrity": "sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/typescript-estree": "5.54.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2180,29 +2600,35 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz", - "integrity": "sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/abort-controller-x": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/abort-controller-x/-/abort-controller-x-0.4.3.tgz", + "integrity": "sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==", + "dev": true + }, "node_modules/accepts": { "version": "1.3.8", "dev": true, @@ -2241,12 +2667,22 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -2298,6 +2734,12 @@ "node": ">=8" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "node_modules/ansi-styles": { "version": "4.2.1", "dev": true, @@ -2331,13 +2773,30 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/argparse": { "version": "1.0.10", @@ -2365,10 +2824,6 @@ "node": ">=8" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" - }, "node_modules/babel-jest": { "version": "29.4.3", "dev": true, @@ -2603,6 +3058,18 @@ ], "license": "CC-BY-4.0" }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/chalk": { "version": "4.1.0", "dev": true, @@ -2665,6 +3132,15 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ci-info": { "version": "3.8.0", "dev": true, @@ -2814,22 +3290,21 @@ "dev": true, "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colorette": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", @@ -2844,6 +3319,12 @@ "dev": true, "license": "MIT" }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, "node_modules/convert-source-map": { "version": "1.7.0", "dev": true, @@ -2856,12 +3337,11 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/cross-fetch": { "version": "3.1.5", + "dev": true, "license": "MIT", "dependencies": { "node-fetch": "2.6.7" @@ -2917,12 +3397,11 @@ "node": ">=0.10.0" } }, - "node_modules/delayed-stream": { + "node_modules/delegates": { "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true }, "node_modules/depd": { "version": "2.0.0", @@ -2932,6 +3411,15 @@ "node": ">= 0.8" } }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "dev": true, @@ -2945,8 +3433,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=0.3.1" } @@ -2983,6 +3469,27 @@ "node": ">=6.0.0" } }, + "node_modules/dprint-node": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz", + "integrity": "sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3" + } + }, + "node_modules/dprint-node/node_modules/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==", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3012,6 +3519,7 @@ }, "node_modules/encoding": { "version": "0.1.13", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -3021,6 +3529,7 @@ }, "node_modules/encoding/node_modules/iconv-lite": { "version": "0.6.3", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -3040,10 +3549,11 @@ } }, "node_modules/esbuild": { - "version": "0.17.10", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -3051,28 +3561,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.10", - "@esbuild/android-arm64": "0.17.10", - "@esbuild/android-x64": "0.17.10", - "@esbuild/darwin-arm64": "0.17.10", - "@esbuild/darwin-x64": "0.17.10", - "@esbuild/freebsd-arm64": "0.17.10", - "@esbuild/freebsd-x64": "0.17.10", - "@esbuild/linux-arm": "0.17.10", - "@esbuild/linux-arm64": "0.17.10", - "@esbuild/linux-ia32": "0.17.10", - "@esbuild/linux-loong64": "0.17.10", - "@esbuild/linux-mips64el": "0.17.10", - "@esbuild/linux-ppc64": "0.17.10", - "@esbuild/linux-riscv64": "0.17.10", - "@esbuild/linux-s390x": "0.17.10", - "@esbuild/linux-x64": "0.17.10", - "@esbuild/netbsd-x64": "0.17.10", - "@esbuild/openbsd-x64": "0.17.10", - "@esbuild/sunos-x64": "0.17.10", - "@esbuild/win32-arm64": "0.17.10", - "@esbuild/win32-ia32": "0.17.10", - "@esbuild/win32-x64": "0.17.10" + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" } }, "node_modules/escalade": { @@ -3182,19 +3693,6 @@ } } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", @@ -3223,12 +3721,15 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "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, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/argparse": { @@ -3427,15 +3928,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/estree-walker": { "version": "1.0.1", "dev": true, @@ -3494,16 +3986,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/extract-files": { - "version": "9.0.0", - "license": "MIT", - "engines": { - "node": "^10.17.0 || ^12.0.0 || >= 13.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/jaydenseric" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3626,18 +4108,36 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, - "node_modules/form-data": { - "version": "3.0.1", - "license": "MIT", + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "minipass": "^3.0.0" }, "engines": { - "node": ">= 6" + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "dev": true, @@ -3660,6 +4160,26 @@ "dev": true, "license": "MIT" }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "dev": true, @@ -3777,29 +4297,47 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/graphql": { "version": "16.8.1", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, "node_modules/graphql-request": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-5.2.0.tgz", - "integrity": "sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", + "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", + "dev": true, "dependencies": { - "@graphql-typed-document-node/core": "^3.1.1", - "cross-fetch": "^3.1.5", - "extract-files": "^9.0.0", - "form-data": "^3.0.0" + "@graphql-typed-document-node/core": "^3.2.0", + "cross-fetch": "^3.1.5" }, "peerDependencies": { "graphql": "14 - 16" } }, + "node_modules/grpc-tools": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.12.4.tgz", + "integrity": "sha512-5+mLAJJma3BjnW/KQp6JBjUMgvu7Mu3dBvBPd1dcbNIb+qiR0817zDpgPjS7gRb+l/8EVNIa3cB02xI9JLToKg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.5" + }, + "bin": { + "grpc_tools_node_protoc": "bin/protoc.js", + "grpc_tools_node_protoc_plugin": "bin/protoc_plugin.js" + } + }, "node_modules/has": { "version": "1.0.3", "dev": true, @@ -3819,6 +4357,12 @@ "node": ">=8" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, "node_modules/html-escaper": { "version": "2.0.2", "dev": true, @@ -3839,6 +4383,19 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "dev": true, @@ -4752,6 +5309,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, "node_modules/kleur": { "version": "3.0.3", "dev": true, @@ -5024,6 +5587,12 @@ "node": ">=8" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, "node_modules/lodash.memoize": { "version": "4.1.2", "dev": true, @@ -5090,6 +5659,12 @@ "node": ">=8" } }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "dev": true + }, "node_modules/lru-cache": { "version": "5.1.1", "dev": true, @@ -5098,6 +5673,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "node_modules/make-dir": { "version": "3.1.0", "dev": true, @@ -5125,6 +5706,18 @@ "tmpl": "1.0.5" } }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "dev": true, @@ -5165,6 +5758,7 @@ }, "node_modules/mime-db": { "version": "1.52.0", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -5172,6 +5766,7 @@ }, "node_modules/mime-types": { "version": "2.1.35", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -5199,6 +5794,58 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "dev": true, @@ -5220,12 +5867,6 @@ "dev": true, "license": "MIT" }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/negotiator": { "version": "0.6.3", "dev": true, @@ -5234,8 +5875,36 @@ "node": ">= 0.6" } }, + "node_modules/nice-grpc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/nice-grpc/-/nice-grpc-2.1.8.tgz", + "integrity": "sha512-pTugD3cZ1Vb0Q2OjZZh80wpLY6L7jSADnzY7Dq6mL9EGUJJF5mfQjcHF4gqpQtyTq2YsZgPIArfNcq0k3ApgQg==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "~1.9.14", + "abort-controller-x": "^0.4.0", + "nice-grpc-common": "^2.0.2" + } + }, + "node_modules/nice-grpc-client-middleware-deadline": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/nice-grpc-client-middleware-deadline/-/nice-grpc-client-middleware-deadline-2.0.11.tgz", + "integrity": "sha512-mwO70/ye7EhbW13HXx3Hjq05QBKR52qyxYzOwBVQvZS/THyT0h5fGbtmQBtlHxAA+lyVJdt51PXu6ic5al/eeg==", + "dependencies": { + "nice-grpc-common": "^2.0.2" + } + }, + "node_modules/nice-grpc-common": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/nice-grpc-common/-/nice-grpc-common-2.0.2.tgz", + "integrity": "sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==", + "dependencies": { + "ts-error": "^1.0.6" + } + }, "node_modules/node-fetch": { "version": "2.6.7", + "dev": true, "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -5254,14 +5923,17 @@ }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", + "dev": true, "license": "MIT" }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -5278,6 +5950,21 @@ "dev": true, "license": "MIT" }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "dev": true, @@ -5297,6 +5984,18 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5569,20 +6268,26 @@ } }, "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">= 14" }, "peerDependencies": { "postcss": ">=8.0.9", @@ -5597,13 +6302,16 @@ } } }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", "dev": true, "engines": { - "node": ">= 6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/prelude-ls": { @@ -5643,6 +6351,26 @@ "node": ">=6.0.0" } }, + "node_modules/prettier-plugin-organize-imports": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz", + "integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==", + "dev": true, + "peerDependencies": { + "@volar/vue-language-plugin-pug": "^1.0.4", + "@volar/vue-typescript": "^1.0.4", + "prettier": ">=2.0", + "typescript": ">=2.9" + }, + "peerDependenciesMeta": { + "@volar/vue-language-plugin-pug": { + "optional": true + }, + "@volar/vue-typescript": { + "optional": true + } + } + }, "node_modules/pretty-format": { "version": "29.4.3", "dev": true, @@ -5679,6 +6407,30 @@ "node": ">= 6" } }, + "node_modules/protobufjs": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.0.tgz", + "integrity": "sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -5727,6 +6479,20 @@ "dev": true, "license": "MIT" }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "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", @@ -5911,7 +6677,7 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/semver": { @@ -5923,6 +6689,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, "node_modules/setprototypeof": { "version": "1.2.0", "dev": true, @@ -5947,6 +6719,18 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "dev": true, @@ -6054,17 +6838,46 @@ "node": ">= 0.8" } }, - "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "node_modules/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==", "dev": true, - "engines": { - "node": ">=0.6.19" + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "node_modules/string-length": { - "version": "4.0.2", + "node_modules/string_decoder/node_modules/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==", + "dev": true, + "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/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-length": { + "version": "4.0.2", "dev": true, "license": "MIT", "dependencies": { @@ -6213,6 +7026,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/test-exclude": { "version": "6.0.0", "dev": true, @@ -6319,6 +7155,23 @@ "tree-kill": "cli.js" } }, + "node_modules/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/ts-error": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/ts-error/-/ts-error-1.0.6.tgz", + "integrity": "sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==" + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -6399,12 +7252,10 @@ "license": "ISC" }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -6443,29 +7294,57 @@ } } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "node_modules/ts-poet": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.9.0.tgz", + "integrity": "sha512-roe6W6MeZmCjRmppyfOURklO5tQFQ6Sg7swURKkwYJvV7dbGCrK28um5+51iW3twdPRKtwarqFAVMU6G1mvnuQ==", + "dev": true, + "dependencies": { + "dprint-node": "^1.0.8" + } + }, + "node_modules/ts-proto": { + "version": "1.176.0", + "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.176.0.tgz", + "integrity": "sha512-OWVrVUpNfZVfvKJZlSjRmCPkcNcox6IPRw3RIeyK2Z4d0Uoy1/gkumMwCVOAMznZcv80D1r9GyYHgYVdp6Cj+g==", + "dev": true, + "dependencies": { + "case-anything": "^2.1.13", + "protobufjs": "^7.2.4", + "ts-poet": "^6.7.0", + "ts-proto-descriptors": "1.16.0" + }, + "bin": { + "protoc-gen-ts_proto": "protoc-gen-ts_proto" + } + }, + "node_modules/ts-proto-descriptors": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.16.0.tgz", + "integrity": "sha512-3yKuzMLpltdpcyQji1PJZRfoo4OJjNieKTYkQY8pF7xGKsYz/RHe3aEe4KiRxcinoBmnEhmuI+yJTxLb922ULA==", + "dev": true, + "dependencies": { + "long": "^5.2.3", + "protobufjs": "^7.2.4" + } }, "node_modules/tsup": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-6.7.0.tgz", - "integrity": "sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.0.2.tgz", + "integrity": "sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==", "dev": true, "dependencies": { "bundle-require": "^4.0.0", "cac": "^6.7.12", "chokidar": "^3.5.1", "debug": "^4.3.1", - "esbuild": "^0.17.6", + "esbuild": "^0.19.2", "execa": "^5.0.0", "globby": "^11.0.3", "joycon": "^3.0.1", - "postcss-load-config": "^3.0.1", + "postcss-load-config": "^4.0.1", "resolve-from": "^5.0.0", - "rollup": "^3.2.5", + "rollup": "^4.0.2", "source-map": "0.8.0-beta.0", "sucrase": "^3.20.3", "tree-kill": "^1.2.2" @@ -6475,14 +7354,18 @@ "tsup-node": "dist/cli-node.js" }, "engines": { - "node": ">=14.18" + "node": ">=18" }, "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", - "typescript": ">=4.1.0" + "typescript": ">=4.5.0" }, "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, "@swc/core": { "optional": true }, @@ -6494,19 +7377,44 @@ } } }, + "node_modules/tsup/node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, "node_modules/tsup/node_modules/rollup": { - "version": "3.21.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.5.tgz", - "integrity": "sha512-a4NTKS4u9PusbUJcfF4IMxuqjFzjm6ifj76P54a7cKnvVzJaG12BLVR+hgU2YDGHzyMMQNxLAZWuALsn8q2oQg==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", + "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.17.2", + "@rollup/rollup-android-arm64": "4.17.2", + "@rollup/rollup-darwin-arm64": "4.17.2", + "@rollup/rollup-darwin-x64": "4.17.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", + "@rollup/rollup-linux-arm-musleabihf": "4.17.2", + "@rollup/rollup-linux-arm64-gnu": "4.17.2", + "@rollup/rollup-linux-arm64-musl": "4.17.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", + "@rollup/rollup-linux-riscv64-gnu": "4.17.2", + "@rollup/rollup-linux-s390x-gnu": "4.17.2", + "@rollup/rollup-linux-x64-gnu": "4.17.2", + "@rollup/rollup-linux-x64-musl": "4.17.2", + "@rollup/rollup-win32-arm64-msvc": "4.17.2", + "@rollup/rollup-win32-ia32-msvc": "4.17.2", + "@rollup/rollup-win32-x64-msvc": "4.17.2", "fsevents": "~2.3.2" } }, @@ -6522,21 +7430,6 @@ "node": ">= 8" } }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6568,16 +7461,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typedoc": { + "version": "0.25.13", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz", + "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x" + } + }, + "node_modules/typedoc-plugin-extras": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-extras/-/typedoc-plugin-extras-3.0.0.tgz", + "integrity": "sha512-eiAe3qtm2WbV5owdncpt0zHZPqsNZH2mzNGILPd4zqrvEZie3Et9es4cpGZ+8lHO/SI0pVKwsAj7IuMxPNOdYg==", + "dev": true, + "peerDependencies": { + "typedoc": "0.25.x" + } + }, + "node_modules/typedoc/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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { - "version": "4.9.5", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/undici": { @@ -6634,10 +7582,17 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -6650,9 +7605,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "9.1.0", @@ -6667,6 +7620,18 @@ "node": ">=10.12.0" } }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "node_modules/walker": { "version": "1.0.8", "dev": true, @@ -6706,6 +7671,15 @@ "node": ">= 8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -6762,18 +7736,22 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", "dev": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } }, "node_modules/yargs": { - "version": "17.7.1", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -6800,8 +7778,6 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=6" } @@ -6817,4485 +7793,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "dependencies": { - "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==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "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==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "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==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/compat-data": { - "version": "7.20.10", - "dev": true - }, - "@babel/core": { - "version": "7.21.0", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - } - }, - "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.20.7", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.21.0", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.21.0", - "dev": true, - "requires": { - "@babel/types": "^7.21.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.21.0", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.20.2", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.20.7", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.20.7", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" - } - }, - "@babel/helper-simple-access": { - "version": "7.20.2", - "dev": true, - "requires": { - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "dev": true, - "requires": { - "@babel/types": "^7.20.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.21.0", - "dev": true - }, - "@babel/helpers": { - "version": "7.21.0", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - } - }, - "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "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==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "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==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "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==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.21.0", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" - } - }, - "@babel/preset-typescript": { - "version": "7.21.0", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-transform-typescript": "^7.21.0" - } - }, - "@babel/runtime": { - "version": "7.20.7", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@curveball/bodyparser": { - "version": "0.5.0", - "dev": true, - "requires": {} - }, - "@curveball/core": { - "version": "0.20.0", - "dev": true, - "requires": { - "@curveball/http-errors": "^0.4.0", - "@curveball/kernel": "^0.20.0", - "@types/ws": "^8.5.3", - "raw-body": "^2.4.1", - "ws": "^8.5.0" - }, - "dependencies": { - "ws": { - "version": "8.12.0", - "dev": true, - "requires": {} - } - } - }, - "@curveball/http-errors": { - "version": "0.4.1", - "dev": true - }, - "@curveball/kernel": { - "version": "0.20.1", - "dev": true, - "requires": { - "@curveball/http-errors": "^0.4.0", - "@types/ws": "^8.5.3", - "accepts": "^1.3.7", - "raw-body": "^2.4.1", - "ws": "^8.5.0" - }, - "dependencies": { - "ws": { - "version": "8.12.0", - "dev": true, - "requires": {} - } - } - }, - "@esbuild/darwin-arm64": { - "version": "0.17.10", - "dev": true, - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.10.tgz", - "integrity": "sha512-J4MJzGchuCRG5n+B4EHpAMoJmBeAE1L3wGYDIN5oWNqX0tEr7VKOzw0ymSwpoeSpdCa030lagGUfnfhS7OvzrQ==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.10.tgz", - "integrity": "sha512-ZkX40Z7qCbugeK4U5/gbzna/UQkM9d9LNV+Fro8r7HA7sRof5Rwxc46SsqeMvB5ZaR0b1/ITQ/8Y1NmV2F0fXQ==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.10.tgz", - "integrity": "sha512-0m0YX1IWSLG9hWh7tZa3kdAugFbZFFx9XrvfpaCMMvrswSTvUZypp0NFKriUurHpBA3xsHVE9Qb/0u2Bbi/otg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.10.tgz", - "integrity": "sha512-whRdrrl0X+9D6o5f0sTZtDM9s86Xt4wk1bf7ltx6iQqrIIOH+sre1yjpcCdrVXntQPCNw/G+XqsD4HuxeS+2QA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.10.tgz", - "integrity": "sha512-g1EZJR1/c+MmCgVwpdZdKi4QAJ8DCLP5uTgLWSAVd9wlqk9GMscaNMEViG3aE1wS+cNMzXXgdWiW/VX4J+5nTA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.10.tgz", - "integrity": "sha512-1vKYCjfv/bEwxngHERp7huYfJ4jJzldfxyfaF7hc3216xiDA62xbXJfRlradiMhGZbdNLj2WA1YwYFzs9IWNPw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.10.tgz", - "integrity": "sha512-mvwAr75q3Fgc/qz3K6sya3gBmJIYZCgcJ0s7XshpoqIAIBszzfXsqhpRrRdVFAyV1G9VUjj7VopL2HnAS8aHFA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.10.tgz", - "integrity": "sha512-XilKPgM2u1zR1YuvCsFQWl9Fc35BqSqktooumOY2zj7CSn5czJn279j9TE1JEqSqz88izJo7yE4x3LSf7oxHzg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.10.tgz", - "integrity": "sha512-kM4Rmh9l670SwjlGkIe7pYWezk8uxKHX4Lnn5jBZYBNlWpKMBCVfpAgAJqp5doLobhzF3l64VZVrmGeZ8+uKmQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.10.tgz", - "integrity": "sha512-r1m9ZMNJBtOvYYGQVXKy+WvWd0BPvSxMsVq8Hp4GzdMBQvfZRvRr5TtX/1RdN6Va8JMVQGpxqde3O+e8+khNJQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.10.tgz", - "integrity": "sha512-LsY7QvOLPw9WRJ+fU5pNB3qrSfA00u32ND5JVDrn/xG5hIQo3kvTxSlWFRP0NJ0+n6HmhPGG0Q4jtQsb6PFoyg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.10.tgz", - "integrity": "sha512-zJUfJLebCYzBdIz/Z9vqwFjIA7iSlLCFvVi7glMgnu2MK7XYigwsonXshy9wP9S7szF+nmwrelNaP3WGanstEg==", - "dev": true, - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.10.tgz", - "integrity": "sha512-lOMkailn4Ok9Vbp/q7uJfgicpDTbZFlXlnKT2DqC8uBijmm5oGtXAJy2ZZVo5hX7IOVXikV9LpCMj2U8cTguWA==", - "dev": true, - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.10.tgz", - "integrity": "sha512-/VE0Kx6y7eekqZ+ZLU4AjMlB80ov9tEz4H067Y0STwnGOYL8CsNg4J+cCmBznk1tMpxMoUOf0AbWlb1d2Pkbig==", - "dev": true, - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.10.tgz", - "integrity": "sha512-ERNO0838OUm8HfUjjsEs71cLjLMu/xt6bhOlxcJ0/1MG3hNqCmbWaS+w/8nFLa0DDjbwZQuGKVtCUJliLmbVgg==", - "dev": true, - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.10.tgz", - "integrity": "sha512-fXv+L+Bw2AeK+XJHwDAQ9m3NRlNemG6Z6ijLwJAAVdu4cyoFbBWbEtyZzDeL+rpG2lWI51cXeMt70HA8g2MqIg==", - "dev": true, - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.10.tgz", - "integrity": "sha512-3s+HADrOdCdGOi5lnh5DMQEzgbsFsd4w57L/eLKKjMnN0CN4AIEP0DCP3F3N14xnxh3ruNc32A0Na9zYe1Z/AQ==", - "dev": true, - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.10.tgz", - "integrity": "sha512-oP+zFUjYNaMNmjTwlFtWep85hvwUu19cZklB3QsBOcZSs6y7hmH4LNCJ7075bsqzYaNvZFXJlAVaQ2ApITDXtw==", - "dev": true, - "optional": true - }, - "@eslint/eslintrc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", - "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "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 - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "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" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@eslint/js": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", - "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", - "dev": true - }, - "@fastify/busboy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", - "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", - "dev": true - }, - "@graphql-typed-document-node/core": { - "version": "3.1.1", - "requires": {} - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@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 - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "dev": true - }, - "@jest/console": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/types": "^29.4.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.4.3", - "jest-util": "^29.4.3", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/console": "^29.4.3", - "@jest/reporters": "^29.4.3", - "@jest/test-result": "^29.4.3", - "@jest/transform": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.4.3", - "jest-config": "^29.4.3", - "jest-haste-map": "^29.4.3", - "jest-message-util": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.4.3", - "jest-resolve-dependencies": "^29.4.3", - "jest-runner": "^29.4.3", - "jest-runtime": "^29.4.3", - "jest-snapshot": "^29.4.3", - "jest-util": "^29.4.3", - "jest-validate": "^29.4.3", - "jest-watcher": "^29.4.3", - "micromatch": "^4.0.4", - "pretty-format": "^29.4.3", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/node": "*", - "jest-mock": "^29.4.3" - } - }, - "@jest/expect": { - "version": "29.4.3", - "dev": true, - "requires": { - "expect": "^29.4.3", - "jest-snapshot": "^29.4.3" - } - }, - "@jest/expect-utils": { - "version": "29.4.3", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3" - } - }, - "@jest/fake-timers": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/types": "^29.4.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.4.3", - "jest-mock": "^29.4.3", - "jest-util": "^29.4.3" - } - }, - "@jest/globals": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/environment": "^29.4.3", - "@jest/expect": "^29.4.3", - "@jest/types": "^29.4.3", - "jest-mock": "^29.4.3" - } - }, - "@jest/reporters": { - "version": "29.4.3", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.4.3", - "@jest/test-result": "^29.4.3", - "@jest/transform": "^29.4.3", - "@jest/types": "^29.4.3", - "@jridgewell/trace-mapping": "^0.3.15", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.4.3", - "jest-util": "^29.4.3", - "jest-worker": "^29.4.3", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - } - }, - "@jest/schemas": { - "version": "29.4.3", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.25.16" - } - }, - "@jest/source-map": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/console": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/test-result": "^29.4.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.3", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.4.3", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.4.3", - "@jridgewell/trace-mapping": "^0.3.15", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.4.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "dev": true - } - } - }, - "@jest/types": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@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==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@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==", - "dev": true - }, - "@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==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@rollup/plugin-babel": { - "version": "5.3.1", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - } - }, - "@sinclair/typebox": { - "version": "0.25.23", - "dev": true - }, - "@sinonjs/commons": { - "version": "2.0.0", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.0.2", - "dev": true, - "requires": { - "@sinonjs/commons": "^2.0.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true, - "optional": true, - "peer": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "optional": true, - "peer": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "optional": true, - "peer": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true, - "optional": true, - "peer": true - }, - "@types/babel__core": { - "version": "7.20.0", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.1", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.0.2", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.0.14", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/color-name": { - "version": "1.1.1", - "dev": true - }, - "@types/estree": { - "version": "0.0.39", - "dev": true - }, - "@types/graceful-fs": { - "version": "4.1.6", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/isomorphic-fetch": { - "version": "0.0.36", - "dev": true - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.4.0", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/node": { - "version": "18.14.0", - "dev": true - }, - "@types/prettier": { - "version": "2.7.2", - "dev": true - }, - "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.1", - "dev": true - }, - "@types/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", - "dev": true - }, - "@types/ws": { - "version": "8.5.4", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "17.0.22", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz", - "integrity": "sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/type-utils": "5.54.1", - "@typescript-eslint/utils": "5.54.1", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@typescript-eslint/parser": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz", - "integrity": "sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/typescript-estree": "5.54.1", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz", - "integrity": "sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/visitor-keys": "5.54.1" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz", - "integrity": "sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.54.1", - "@typescript-eslint/utils": "5.54.1", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz", - "integrity": "sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz", - "integrity": "sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/visitor-keys": "5.54.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@typescript-eslint/utils": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz", - "integrity": "sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/typescript-estree": "5.54.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz", - "integrity": "sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.54.1", - "eslint-visitor-keys": "^3.3.0" - } - }, - "accepts": { - "version": "1.3.8", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "optional": true, - "peer": true - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "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" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "anymatch": { - "version": "3.1.3", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "optional": true, - "peer": true - }, - "argparse": { - "version": "1.0.10", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "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 - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0" - }, - "babel-jest": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/transform": "^29.4.3", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.4.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "29.4.3", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.4.3", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.4.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.4", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - } - }, - "bs-logger": { - "version": "0.2.6", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "dev": true - }, - "bundle-require": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-4.0.1.tgz", - "integrity": "sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==", - "dev": true, - "requires": { - "load-tsconfig": "^0.2.3" - } - }, - "bytes": { - "version": "3.1.2", - "dev": true - }, - "cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001441", - "dev": true - }, - "chalk": { - "version": "4.1.0", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.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" - }, - "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" - } - } - } - }, - "ci-info": { - "version": "3.8.0", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, - "requires": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "cliui": { - "version": "8.0.1", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "dev": true - }, - "colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", - "integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "optional": true, - "peer": true - }, - "cross-fetch": { - "version": "3.1.5", - "requires": { - "node-fetch": "2.6.7" - } - }, - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "dedent": { - "version": "0.7.0", - "dev": true - }, - "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 - }, - "deepmerge": { - "version": "4.3.0", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0" - }, - "depd": { - "version": "2.0.0", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "optional": true, - "peer": true - }, - "diff-sequences": { - "version": "29.4.3", - "dev": true - }, - "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, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.284", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "dev": true - }, - "encoding": { - "version": "0.1.13", - "optional": true, - "peer": true, - "requires": { - "iconv-lite": "^0.6.2" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "optional": true, - "peer": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "error-ex": { - "version": "1.3.2", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "esbuild": { - "version": "0.17.10", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.17.10", - "@esbuild/android-arm64": "0.17.10", - "@esbuild/android-x64": "0.17.10", - "@esbuild/darwin-arm64": "0.17.10", - "@esbuild/darwin-x64": "0.17.10", - "@esbuild/freebsd-arm64": "0.17.10", - "@esbuild/freebsd-x64": "0.17.10", - "@esbuild/linux-arm": "0.17.10", - "@esbuild/linux-arm64": "0.17.10", - "@esbuild/linux-ia32": "0.17.10", - "@esbuild/linux-loong64": "0.17.10", - "@esbuild/linux-mips64el": "0.17.10", - "@esbuild/linux-ppc64": "0.17.10", - "@esbuild/linux-riscv64": "0.17.10", - "@esbuild/linux-s390x": "0.17.10", - "@esbuild/linux-x64": "0.17.10", - "@esbuild/netbsd-x64": "0.17.10", - "@esbuild/openbsd-x64": "0.17.10", - "@esbuild/sunos-x64": "0.17.10", - "@esbuild/win32-arm64": "0.17.10", - "@esbuild/win32-ia32": "0.17.10", - "@esbuild/win32-x64": "0.17.10" - } - }, - "escalade": { - "version": "3.1.1", - "dev": true - }, - "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==", - "dev": true - }, - "eslint": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", - "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^2.0.0", - "@eslint/js": "8.35.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "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 - }, - "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 - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "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" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "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" - } - }, - "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" - } - }, - "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" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", - "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "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, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "estree-walker": { - "version": "1.0.1", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "dev": true - }, - "expect": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.4.3", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.4.3", - "jest-message-util": "^29.4.3", - "jest-util": "^29.4.3" - } - }, - "extract-files": { - "version": "9.0.0" - }, - "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 - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "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" - }, - "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" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true - }, - "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 - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "form-data": { - "version": "3.0.1", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "dev": true - }, - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "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" - } - }, - "globals": { - "version": "11.12.0", - "dev": true - }, - "globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", - "dev": true - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "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" - } - }, - "globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.10", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "graphql": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "peer": true - }, - "graphql-request": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-5.2.0.tgz", - "integrity": "sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ==", - "requires": { - "@graphql-typed-document-node/core": "^3.1.1", - "cross-fetch": "^3.1.5", - "extract-files": "^9.0.0", - "form-data": "^3.0.0" - } - }, - "has": { - "version": "1.0.3", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "human-signals": { - "version": "2.1.0", - "dev": true - }, - "husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "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, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "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 - } - } - }, - "import-local": { - "version": "3.1.0", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.11.0", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "dev": true - }, - "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 - }, - "is-stream": { - "version": "2.0.1", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/core": "^29.4.3", - "@jest/types": "^29.4.3", - "import-local": "^3.0.2", - "jest-cli": "^29.4.3" - } - }, - "jest-changed-files": { - "version": "29.4.3", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/environment": "^29.4.3", - "@jest/expect": "^29.4.3", - "@jest/test-result": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.4.3", - "jest-matcher-utils": "^29.4.3", - "jest-message-util": "^29.4.3", - "jest-runtime": "^29.4.3", - "jest-snapshot": "^29.4.3", - "jest-util": "^29.4.3", - "p-limit": "^3.1.0", - "pretty-format": "^29.4.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/core": "^29.4.3", - "@jest/test-result": "^29.4.3", - "@jest/types": "^29.4.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.4.3", - "jest-util": "^29.4.3", - "jest-validate": "^29.4.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "29.4.3", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.4.3", - "@jest/types": "^29.4.3", - "babel-jest": "^29.4.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.4.3", - "jest-environment-node": "^29.4.3", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.4.3", - "jest-runner": "^29.4.3", - "jest-util": "^29.4.3", - "jest-validate": "^29.4.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.4.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "29.4.3", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.4.3" - } - }, - "jest-docblock": { - "version": "29.4.3", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/types": "^29.4.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.4.3", - "pretty-format": "^29.4.3" - } - }, - "jest-environment-node": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/environment": "^29.4.3", - "@jest/fake-timers": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/node": "*", - "jest-mock": "^29.4.3", - "jest-util": "^29.4.3" - } - }, - "jest-get-type": { - "version": "29.4.3", - "dev": true - }, - "jest-haste-map": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/types": "^29.4.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.4.3", - "jest-worker": "^29.4.3", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.4.3", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.4.3" - } - }, - "jest-matcher-utils": { - "version": "29.4.3", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.4.3" - } - }, - "jest-message-util": { - "version": "29.4.3", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.4.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.4.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/types": "^29.4.3", - "@types/node": "*", - "jest-util": "^29.4.3" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.4.3", - "dev": true - }, - "jest-resolve": { - "version": "29.4.3", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.4.3", - "jest-validate": "^29.4.3", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.4.3", - "dev": true, - "requires": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.4.3" - } - }, - "jest-runner": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/console": "^29.4.3", - "@jest/environment": "^29.4.3", - "@jest/test-result": "^29.4.3", - "@jest/transform": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.4.3", - "jest-haste-map": "^29.4.3", - "jest-leak-detector": "^29.4.3", - "jest-message-util": "^29.4.3", - "jest-resolve": "^29.4.3", - "jest-runtime": "^29.4.3", - "jest-util": "^29.4.3", - "jest-watcher": "^29.4.3", - "jest-worker": "^29.4.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - } - }, - "jest-runtime": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/environment": "^29.4.3", - "@jest/fake-timers": "^29.4.3", - "@jest/globals": "^29.4.3", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.4.3", - "@jest/transform": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.3", - "jest-message-util": "^29.4.3", - "jest-mock": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.4.3", - "jest-snapshot": "^29.4.3", - "jest-util": "^29.4.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "29.4.3", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.4.3", - "@jest/transform": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.4.3", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.4.3", - "jest-get-type": "^29.4.3", - "jest-haste-map": "^29.4.3", - "jest-matcher-utils": "^29.4.3", - "jest-message-util": "^29.4.3", - "jest-util": "^29.4.3", - "natural-compare": "^1.4.0", - "pretty-format": "^29.4.3", - "semver": "^7.3.5" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "jest-util": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/types": "^29.4.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/types": "^29.4.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "leven": "^3.1.0", - "pretty-format": "^29.4.3" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/test-result": "^29.4.3", - "@jest/types": "^29.4.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.4.3", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.4.3", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.4.3", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true - }, - "js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true - }, - "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 - }, - "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 - }, - "json5": { - "version": "2.2.3", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "dev": true - }, - "leven": { - "version": "3.1.0", - "dev": true - }, - "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, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "dev": true - }, - "lint-staged": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.0.tgz", - "integrity": "sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw==", - "dev": true, - "requires": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", - "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.1" - }, - "dependencies": { - "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true - }, - "execa": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.0.0.tgz", - "integrity": "sha512-tQbH0pH/8LHTnwTrsKWideqi6rFB/QNUawEwrn+WHyz7PX1Tuz2u7wfTvbaNBdP5JD5LVWxNo8/A8CHNZ3bV6g==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.0.tgz", - "integrity": "sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ==", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - } - } - }, - "listr2": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.7.tgz", - "integrity": "sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==", - "dev": true, - "requires": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } - } - }, - "load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash.memoize": { - "version": "4.1.2", - "dev": true - }, - "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 - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "lru-cache": { - "version": "5.1.1", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "3.1.0", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "merge-stream": { - "version": "2.0.0", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "dev": true - }, - "mime-db": { - "version": "1.52.0" - }, - "mime-types": { - "version": "2.1.35", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "dev": true - }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "natural-compare": { - "version": "1.4.0", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "negotiator": { - "version": "0.6.3", - "dev": true - }, - "node-fetch": { - "version": "2.6.7", - "requires": { - "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3" - }, - "webidl-conversions": { - "version": "3.0.1" - }, - "whatwg-url": { - "version": "5.0.0", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } - }, - "node-int64": { - "version": "0.4.0", - "dev": true - }, - "node-releases": { - "version": "2.0.8", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "once": { - "version": "1.4.0", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "openapi-typescript": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-5.4.1.tgz", - "integrity": "sha512-AGB2QiZPz4rE7zIwV3dRHtoUC/CWHhUjuzGXvtmMQN2AFV8xCTLKcZUHLcdPQmt/83i22nRE7+TxXOXkK+gf4Q==", - "dev": true, - "requires": { - "js-yaml": "^4.1.0", - "mime": "^3.0.0", - "prettier": "^2.6.2", - "tiny-glob": "^0.2.9", - "undici": "^5.4.0", - "yargs-parser": "^21.0.1" - }, - "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" - } - } - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "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.3" - } - }, - "p-limit": { - "version": "3.1.0", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - }, - "dependencies": { - "p-limit": { - "version": "2.3.0", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - } - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "dev": true - }, - "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, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "dev": true - }, - "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 - }, - "picocolors": { - "version": "1.0.0", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "dev": true - }, - "pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dev": true, - "requires": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "dependencies": { - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - } - } - }, - "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 - }, - "prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-format": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "raw-body": { - "version": "2.5.1", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "dev": true - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "dev": true - }, - "resolve": { - "version": "1.22.1", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "dev": true - }, - "resolve.exports": { - "version": "2.0.0", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "2.79.1", - "dev": true, - "peer": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "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==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "devOptional": true - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "dev": true - }, - "slash": { - "version": "3.0.0", - "dev": true - }, - "slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "requires": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true - } - } - }, - "source-map": { - "version": "0.6.1", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "dev": true - } - } - }, - "statuses": { - "version": "2.0.1", - "dev": true - }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-length": { - "version": "4.0.2", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "dev": true - }, - "sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true - }, - "test-exclude": { - "version": "6.0.0", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "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 - }, - "thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "requires": { - "thenify": ">= 3.1.0 < 4" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "dev": true, - "requires": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "tmpl": { - "version": "1.0.5", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "dev": true - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, - "ts-jest": { - "version": "29.0.5", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsup": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-6.7.0.tgz", - "integrity": "sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==", - "dev": true, - "requires": { - "bundle-require": "^4.0.0", - "cac": "^6.7.12", - "chokidar": "^3.5.1", - "debug": "^4.3.1", - "esbuild": "^0.17.6", - "execa": "^5.0.0", - "globby": "^11.0.3", - "joycon": "^3.0.1", - "postcss-load-config": "^3.0.1", - "resolve-from": "^5.0.0", - "rollup": "^3.2.5", - "source-map": "0.8.0-beta.0", - "sucrase": "^3.20.3", - "tree-kill": "^1.2.2" - }, - "dependencies": { - "rollup": { - "version": "3.21.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.5.tgz", - "integrity": "sha512-a4NTKS4u9PusbUJcfF4IMxuqjFzjm6ifj76P54a7cKnvVzJaG12BLVR+hgU2YDGHzyMMQNxLAZWuALsn8q2oQg==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "requires": { - "whatwg-url": "^7.0.0" - } - } - } - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "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, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "dev": true - }, - "type-fest": { - "version": "0.21.3", - "dev": true - }, - "typescript": { - "version": "4.9.5", - "dev": true - }, - "undici": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.0.tgz", - "integrity": "sha512-l3ydWhlhOJzMVOYkymLykcRRXqbUaQriERtR70B9LzNkZ4bX52Fc8wbTDneMiwo8T+AemZXvXaTx+9o5ROxrXg==", - "dev": true, - "requires": { - "@fastify/busboy": "^2.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.10", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "optional": true, - "peer": true - }, - "v8-to-istanbul": { - "version": "9.1.0", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - } - }, - "walker": { - "version": "1.0.8", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "which": { - "version": "2.0.2", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "dev": true - }, - "write-file-atomic": { - "version": "4.0.2", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "y18n": { - "version": "5.0.8", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "dev": true - }, - "yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true - }, - "yargs": { - "version": "17.7.1", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "optional": true, - "peer": true - }, - "yocto-queue": { - "version": "0.1.0", - "dev": true - } } } diff --git a/package.json b/package.json index 1628cee8..0114c3b9 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,41 @@ { - "name": "weaviate-ts-client", - "version": "2.2.0", - "description": "TypeScript client for Weaviate", - "main": "./dist/index.js", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", + "name": "weaviate-client", + "version": "3.0.8", + "description": "JS/TS client for Weaviate", + "main": "dist/node/cjs/index.js", + "type": "module", "exports": { ".": { - "require": "./dist/index.js", - "import": "./dist/index.mjs", - "types": "./dist/index.d.ts" + "types": { + "require": "./dist/node/cjs/index.d.ts", + "default": "./dist/node/esm/index.d.ts" + }, + "default": { + "require": "./dist/node/cjs/index.js", + "default": "./dist/node/esm/index.js" + } } }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, - "files": [ - "/dist/index.js", - "/dist/index.mjs", - "/dist/index.d.ts" - ], "scripts": { - "test": "tsc -noEmit -p tsconfig-test.json && jest --useStderr --runInBand --detectOpenHandles", - "build": "npm run lint && tsup src/index.ts --format cjs,esm --dts --clean --platform neutral --minify", + "test": "jest --no-cache --useStderr --runInBand --detectOpenHandles", + "test:coverage": "npm run test -- --coverage", + "build": "npm run build:node", + "build:web": "tsup", + "build:cjs": "tsc --module commonjs --outDir dist/node/cjs && touch dist/node/cjs/package.json && echo '{\"type\": \"commonjs\"}' > dist/node/cjs/package.json", + "build:esm": "tsc --module esnext --outDir dist/node/esm && touch dist/node/esm/package.json && echo '{\"type\": \"module\"}' > dist/node/esm/package.json", + "build:node": "npm run lint && npm run build:cjs && npm run build:esm && prettier --write --no-error-on-unmatched-pattern '**/dist/**/*.{ts,js}'", "prepack": "npm run build", "lint": "eslint --ext .ts,.js .", "lint:fix": "npm run lint -- --fix", "format": "prettier --write --no-error-on-unmatched-pattern '**/*.{ts,js}' '!dist/**'", "format:check": "prettier --check --no-error-on-unmatched-pattern '**/*.{ts,js}' '!dist/**'", - "prepare": "husky install", - "schema": "./tools/refresh_schema.sh" + "format:dist": "prettier --write --no-error-on-unmatched-pattern '**/dist/**/*.{ts,js}'", + "schema": "./tools/refresh_schema.sh", + "protos": "./tools/refresh_protos.sh", + "docs": "typedoc --plugin typedoc-plugin-extras --favicon public/favicon.ico --out docs/ src/" }, "repository": { "type": "git", @@ -45,34 +51,50 @@ }, "homepage": "https://github.com/weaviate/typescript-client#readme", "dependencies": { - "graphql-request": "^5.2.0", + "graphql": "^16.8.1", + "graphql-request": "^6.1.0", + "long": "^5.2.3", + "nice-grpc": "^2.1.7", + "nice-grpc-client-middleware-deadline": "^2.0.11", "uuid": "^9.0.1" }, "devDependencies": { "@babel/core": "^7.20.12", "@babel/preset-typescript": "^7.18.6", "@babel/runtime": "^7.20.7", - "@curveball/bodyparser": "^0.5.0", - "@curveball/core": "^0.20.0", + "@curveball/bodyparser": "0.5.0", + "@curveball/core": "0.20.0", + "@curveball/kernel": "0.20.1", "@rollup/plugin-babel": "^5.3.1", "@types/isomorphic-fetch": "^0.0.36", "@types/jest": "^29.4.0", "@types/node": "^18.14.0", "@types/uuid": "^9.0.1", - "@typescript-eslint/eslint-plugin": "^5.54.1", - "@typescript-eslint/parser": "^5.54.1", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "babel-jest": "^29.4.3", "eslint": "^8.35.0", "eslint-config-prettier": "^8.7.0", "eslint-plugin-prettier": "^4.2.1", + "graphql-request": "^6.1.0", + "grpc-tools": "^1.12.4", "husky": "^8.0.3", "jest": "^29.4.3", "lint-staged": "^13.2.0", + "long": "^5.2.3", + "nice-grpc": "^2.1.7", "openapi-typescript": "^5.4.1", "prettier": "^2.8.4", + "prettier-plugin-organize-imports": "^3.2.4", + "protobufjs": "^7.2.6", "ts-jest": "^29.0.5", - "tsup": "^6.7.0", - "typescript": "^4.9.5" + "ts-node": "^10.9.2", + "ts-proto": "^1.163.0", + "tsup": "^8.0.2", + "typedoc": "^0.25.12", + "typedoc-plugin-extras": "^3.0.0", + "typescript": "5.1.3", + "uuid": "^9.0.1" }, "lint-staged": { "*.{ts,js}": [ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..307c39e0 Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/backup/backupCreateStatusGetter.ts b/src/backup/backupCreateStatusGetter.ts index 0379f21f..3a0c680c 100644 --- a/src/backup/backupCreateStatusGetter.ts +++ b/src/backup/backupCreateStatusGetter.ts @@ -1,8 +1,8 @@ -import Connection from '../connection'; -import { validateBackupId, validateBackend } from './validation'; -import { CommandBase } from '../validation/commandBase'; -import { BackupCreateStatusResponse } from '../openapi/types'; -import { Backend } from '.'; +import Connection from '../connection/index.js'; +import { BackupCreateStatusResponse } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { Backend } from './index.js'; +import { validateBackend, validateBackupId } from './validation.js'; export default class BackupCreateStatusGetter extends CommandBase { private backend?: Backend; diff --git a/src/backup/backupCreator.ts b/src/backup/backupCreator.ts index d4e07498..1c1b7908 100644 --- a/src/backup/backupCreator.ts +++ b/src/backup/backupCreator.ts @@ -1,19 +1,19 @@ +import Connection from '../connection/index.js'; +import { + BackupConfig, + BackupCreateRequest, + BackupCreateResponse, + BackupCreateStatusResponse, +} from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import BackupCreateStatusGetter from './backupCreateStatusGetter.js'; +import { Backend } from './index.js'; import { validateBackend, validateBackupId, validateExcludeClassNames, validateIncludeClassNames, -} from './validation'; -import BackupCreateStatusGetter from './backupCreateStatusGetter'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { - BackupCreateRequest, - BackupCreateResponse, - BackupCreateStatusResponse, - BackupConfig, -} from '../openapi/types'; -import { Backend } from '.'; +} from './validation.js'; const WAIT_INTERVAL = 1000; @@ -98,7 +98,7 @@ export default class BackupCreator extends CommandBase { }; _create = (payload: BackupCreateRequest): Promise => { - return this.client.post(this._path(), payload) as Promise; + return this.client.postReturn(this._path(), payload) as Promise; }; _createAndWaitForCompletion = (payload: BackupCreateRequest): Promise => { diff --git a/src/backup/backupGetter.ts b/src/backup/backupGetter.ts index 0da885f2..a9d0905d 100644 --- a/src/backup/backupGetter.ts +++ b/src/backup/backupGetter.ts @@ -1,8 +1,8 @@ -import { validateBackend } from './validation'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { BackupCreateResponse } from '../openapi/types'; -import { Backend } from '.'; +import Connection from '../connection/index.js'; +import { BackupCreateResponse } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { Backend } from './index.js'; +import { validateBackend } from './validation.js'; export default class BackupGetter extends CommandBase { private backend?: Backend; diff --git a/src/backup/backupRestoreStatusGetter.ts b/src/backup/backupRestoreStatusGetter.ts index caa147c5..fa59afda 100644 --- a/src/backup/backupRestoreStatusGetter.ts +++ b/src/backup/backupRestoreStatusGetter.ts @@ -1,8 +1,8 @@ -import { validateBackupId, validateBackend } from './validation'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { BackupRestoreStatusResponse } from '../openapi/types'; -import { Backend } from '.'; +import Connection from '../connection/index.js'; +import { BackupRestoreStatusResponse } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { Backend } from './index.js'; +import { validateBackend, validateBackupId } from './validation.js'; export default class BackupRestoreStatusGetter extends CommandBase { private backend?: Backend; diff --git a/src/backup/backupRestorer.ts b/src/backup/backupRestorer.ts index c5a39b3a..a4acb226 100644 --- a/src/backup/backupRestorer.ts +++ b/src/backup/backupRestorer.ts @@ -1,19 +1,19 @@ -import { - validateBackend, - validateBackupId, - validateExcludeClassNames, - validateIncludeClassNames, -} from './validation'; -import Connection from '../connection'; -import BackupRestoreStatusGetter from './backupRestoreStatusGetter'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; import { BackupRestoreRequest, BackupRestoreResponse, BackupRestoreStatusResponse, RestoreConfig, -} from '../openapi/types'; -import { Backend } from '.'; +} from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import BackupRestoreStatusGetter from './backupRestoreStatusGetter.js'; +import { Backend } from './index.js'; +import { + validateBackend, + validateBackupId, + validateExcludeClassNames, + validateIncludeClassNames, +} from './validation.js'; const WAIT_INTERVAL = 1000; @@ -97,7 +97,7 @@ export default class BackupRestorer extends CommandBase { }; _restore = (payload: BackupRestoreRequest): Promise => { - return this.client.post(this._path(), payload); + return this.client.postReturn(this._path(), payload); }; _restoreAndWaitForCompletion = (payload: BackupRestoreRequest): Promise => { diff --git a/src/backup/index.ts b/src/backup/index.ts index 860cae1a..9b395cd6 100644 --- a/src/backup/index.ts +++ b/src/backup/index.ts @@ -1,11 +1,12 @@ -import BackupCreator from './backupCreator'; -import BackupCreateStatusGetter from './backupCreateStatusGetter'; -import BackupRestorer from './backupRestorer'; -import BackupRestoreStatusGetter from './backupRestoreStatusGetter'; -import Connection from '../connection'; +import Connection from '../connection/index.js'; +import BackupCreateStatusGetter from './backupCreateStatusGetter.js'; +import BackupCreator from './backupCreator.js'; +import BackupRestoreStatusGetter from './backupRestoreStatusGetter.js'; +import BackupRestorer from './backupRestorer.js'; export type Backend = 'filesystem' | 's3' | 'gcs' | 'azure'; export type BackupStatus = 'STARTED' | 'TRANSFERRING' | 'TRANSFERRED' | 'SUCCESS' | 'FAILED'; +export type BackupCompressionLevel = 'DefaultCompression' | 'BestSpeed' | 'BestCompression'; export interface Backup { creator: () => BackupCreator; @@ -24,7 +25,7 @@ const backup = (client: Connection): Backup => { }; export default backup; -export { default as BackupCreator } from './backupCreator'; -export { default as BackupCreateStatusGetter } from './backupCreateStatusGetter'; -export { default as BackupRestorer } from './backupRestorer'; -export { default as BackupRestoreStatusGetter } from './backupRestoreStatusGetter'; +export { default as BackupCreateStatusGetter } from './backupCreateStatusGetter.js'; +export { default as BackupCreator } from './backupCreator.js'; +export { default as BackupRestoreStatusGetter } from './backupRestoreStatusGetter.js'; +export { default as BackupRestorer } from './backupRestorer.js'; diff --git a/src/backup/journey.test.ts b/src/backup/journey.test.ts index d33caab2..fc04ff09 100644 --- a/src/backup/journey.test.ts +++ b/src/backup/journey.test.ts @@ -1,11 +1,11 @@ -import { Backend } from '.'; -import weaviate, { WeaviateClient } from '..'; import { BackupCreateResponse, BackupCreateStatusResponse, BackupRestoreResponse, BackupRestoreStatusResponse, -} from '../openapi/types'; +} from '../openapi/types.js'; +import weaviate, { WeaviateClient } from '../v2/index.js'; +import { Backend } from './index.js'; const { createTestFoodSchemaAndData, @@ -279,7 +279,6 @@ describe('create and restore 1 of 2 classes', () => { .do() .then((createResponse: BackupCreateResponse) => { expect(createResponse.id).toBe(BACKUP_ID); - expect(createResponse.classes).toHaveLength(2); expect(createResponse.classes).toContain(PIZZA_CLASS_NAME); expect(createResponse.classes).toContain(SOUP_CLASS_NAME); expect(createResponse.path).toBe(`${DOCKER_COMPOSE_BACKUPS_DIR}/${BACKUP_ID}`); diff --git a/src/backup/validation.ts b/src/backup/validation.ts index aae0210b..778ecb87 100644 --- a/src/backup/validation.ts +++ b/src/backup/validation.ts @@ -1,4 +1,4 @@ -import { isValidStringProperty } from '../validation/string'; +import { isValidStringProperty } from '../validation/string.js'; export function validateIncludeClassNames(classNames?: string[]) { if (Array.isArray(classNames)) { diff --git a/src/batch/index.ts b/src/batch/index.ts index 30bfb0e4..fa2b1486 100644 --- a/src/batch/index.ts +++ b/src/batch/index.ts @@ -1,10 +1,10 @@ -import ObjectsBatcher from './objectsBatcher'; -import ObjectsBatchDeleter from './objectsBatchDeleter'; -import ReferencesBatcher from './referencesBatcher'; -import ReferencePayloadBuilder from './referencePayloadBuilder'; -import { BeaconPath } from '../utils/beaconPath'; -import { DbVersionSupport } from '../utils/dbVersion'; -import Connection from '../connection'; +import Connection from '../connection/index.js'; +import { BeaconPath } from '../utils/beaconPath.js'; +import { DbVersionSupport } from '../utils/dbVersion.js'; +import ObjectsBatchDeleter from './objectsBatchDeleter.js'; +import ObjectsBatcher from './objectsBatcher.js'; +import ReferencePayloadBuilder from './referencePayloadBuilder.js'; +import ReferencesBatcher from './referencesBatcher.js'; export type DeleteOutput = 'verbose' | 'minimal'; export type DeleteResultStatus = 'SUCCESS' | 'FAILED' | 'DRYRUN'; @@ -28,6 +28,6 @@ const batch = (client: Connection, dbVersionSupport: DbVersionSupport): Batch => }; export default batch; -export { default as ObjectsBatcher } from './objectsBatcher'; -export { default as ObjectsBatchDeleter } from './objectsBatchDeleter'; -export { default as ReferencesBatcher } from './referencesBatcher'; +export { default as ObjectsBatchDeleter } from './objectsBatchDeleter.js'; +export { default as ObjectsBatcher } from './objectsBatcher.js'; +export { default as ReferencesBatcher } from './referencesBatcher.js'; diff --git a/src/batch/journey.test.ts b/src/batch/journey.test.ts index e39f28f0..d75e785e 100644 --- a/src/batch/journey.test.ts +++ b/src/batch/journey.test.ts @@ -1,13 +1,13 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import weaviate, { WeaviateClient } from '..'; import { + BatchDeleteResponse, BatchReference, BatchReferenceResponse, - WeaviateObject, - BatchDeleteResponse, Tenant, WeaviateClass, -} from '../openapi/types'; + WeaviateObject, +} from '../openapi/types.js'; +import weaviate, { WeaviateClient } from '../v2/index.js'; const thingClassName = 'BatchJourneyTestThing'; const otherThingClassName = 'BatchJourneyTestOtherThing'; diff --git a/src/batch/objectsBatchDeleter.ts b/src/batch/objectsBatchDeleter.ts index 0c68d5d8..5998af12 100644 --- a/src/batch/objectsBatchDeleter.ts +++ b/src/batch/objectsBatchDeleter.ts @@ -1,10 +1,10 @@ -import { isValidStringProperty } from '../validation/string'; -import { buildObjectsPath } from './path'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { BatchDelete, BatchDeleteResponse, WhereFilter } from '../openapi/types'; -import { ConsistencyLevel } from '../data/replication'; -import { DeleteOutput } from '.'; +import Connection from '../connection/index.js'; +import { ConsistencyLevel } from '../data/replication.js'; +import { BatchDelete, BatchDeleteResponse, WhereFilter } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; +import { DeleteOutput } from './index.js'; +import { buildObjectsPath } from './path.js'; export default class ObjectsBatchDeleter extends CommandBase { private className?: string; diff --git a/src/batch/objectsBatcher.ts b/src/batch/objectsBatcher.ts index c6d42a48..df9d02d2 100644 --- a/src/batch/objectsBatcher.ts +++ b/src/batch/objectsBatcher.ts @@ -1,8 +1,8 @@ -import { buildObjectsPath } from './path'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { BatchRequest, WeaviateObject, WeaviateObjectsGet } from '../openapi/types'; -import { ConsistencyLevel } from '../data/replication'; +import Connection from '../connection/index.js'; +import { ConsistencyLevel } from '../data/replication.js'; +import { BatchRequest, WeaviateObject, WeaviateObjectsGet } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { buildObjectsPath } from './path.js'; export default class ObjectsBatcher extends CommandBase { private consistencyLevel?: ConsistencyLevel; @@ -62,6 +62,6 @@ export default class ObjectsBatcher extends CommandBase { params.set('consistency_level', this.consistencyLevel); } const path = buildObjectsPath(params); - return this.client.post(path, this.payload()); + return this.client.postReturn(path, this.payload()); }; } diff --git a/src/batch/path.test.ts b/src/batch/path.test.ts index d097b49e..aabb6e53 100644 --- a/src/batch/path.test.ts +++ b/src/batch/path.test.ts @@ -1,4 +1,4 @@ -import { buildObjectsPath, buildRefsPath } from './path'; +import { buildObjectsPath, buildRefsPath } from './path.js'; describe('paths', () => { it('builds batch objects without params', () => { diff --git a/src/batch/referencePayloadBuilder.ts b/src/batch/referencePayloadBuilder.ts index e7d2af1b..ddf76e27 100644 --- a/src/batch/referencePayloadBuilder.ts +++ b/src/batch/referencePayloadBuilder.ts @@ -1,7 +1,7 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { BatchReference } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { BatchReference } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class ReferencesBatcher extends CommandBase { private fromClassName?: string; diff --git a/src/batch/referencesBatcher.ts b/src/batch/referencesBatcher.ts index 948d229b..13e89bc2 100644 --- a/src/batch/referencesBatcher.ts +++ b/src/batch/referencesBatcher.ts @@ -1,9 +1,9 @@ -import { buildRefsPath } from './path'; -import { BeaconPath } from '../utils/beaconPath'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { BatchReference, BatchReferenceResponse } from '../openapi/types'; -import { ConsistencyLevel } from '../data/replication'; +import Connection from '../connection/index.js'; +import { ConsistencyLevel } from '../data/replication.js'; +import { BatchReference, BatchReferenceResponse } from '../openapi/types.js'; +import { BeaconPath } from '../utils/beaconPath.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { buildRefsPath } from './path.js'; export default class ReferencesBatcher extends CommandBase { private beaconPath: BeaconPath; @@ -65,7 +65,7 @@ export default class ReferencesBatcher extends CommandBase { const path = buildRefsPath(params); const payloadPromise = Promise.all(this.references.map((ref) => this.rebuildReferencePromise(ref))); - return payloadPromise.then((payload) => this.client.post(path, payload)); + return payloadPromise.then((payload) => this.client.postReturn(path, payload)); }; rebuildReferencePromise = (reference: BatchReference): Promise => { diff --git a/src/c11y/conceptsGetter.ts b/src/c11y/conceptsGetter.ts index 64275fbd..e8bd7840 100644 --- a/src/c11y/conceptsGetter.ts +++ b/src/c11y/conceptsGetter.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { C11yWordsResponse } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { C11yWordsResponse } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class ConceptsGetter extends CommandBase { private concept?: string; diff --git a/src/c11y/extensionCreator.ts b/src/c11y/extensionCreator.ts index b5a1bd6c..eee75ea3 100644 --- a/src/c11y/extensionCreator.ts +++ b/src/c11y/extensionCreator.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { C11yExtension } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { C11yExtension } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class ExtensionCreator extends CommandBase { private concept?: string; @@ -51,6 +51,6 @@ export default class ExtensionCreator extends CommandBase { } const path = `/modules/text2vec-contextionary/extensions`; - return this.client.post(path, this.payload()); + return this.client.postReturn(path, this.payload()); }; } diff --git a/src/c11y/index.ts b/src/c11y/index.ts index c6f158b8..e9ee7281 100644 --- a/src/c11y/index.ts +++ b/src/c11y/index.ts @@ -1,6 +1,6 @@ -import ExtensionCreator from './extensionCreator'; -import ConceptsGetter from './conceptsGetter'; -import Connection from '../connection'; +import Connection from '../connection/index.js'; +import ConceptsGetter from './conceptsGetter.js'; +import ExtensionCreator from './extensionCreator.js'; export interface C11y { conceptsGetter: () => ConceptsGetter; @@ -15,5 +15,5 @@ const c11y = (client: Connection): C11y => { }; export default c11y; -export { default as ExtensionCreator } from './extensionCreator'; -export { default as ConceptsGetter } from './conceptsGetter'; +export { default as ConceptsGetter } from './conceptsGetter.js'; +export { default as ExtensionCreator } from './extensionCreator.js'; diff --git a/src/c11y/journey.test.ts b/src/c11y/journey.test.ts index 1525d5bb..5ffcfdb3 100644 --- a/src/c11y/journey.test.ts +++ b/src/c11y/journey.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import weaviate from '..'; -import { C11yWordsResponse, C11yExtension } from '../openapi/types'; +import { C11yExtension, C11yWordsResponse } from '../openapi/types.js'; +import weaviate from '../v2/index.js'; describe('c11y endpoints', () => { const client = weaviate.client({ diff --git a/src/classifications/contextual.journey.test.ts b/src/classifications/contextual.journey.test.ts index ceb1f4a7..5a2f5455 100644 --- a/src/classifications/contextual.journey.test.ts +++ b/src/classifications/contextual.journey.test.ts @@ -1,5 +1,5 @@ -import weaviate from '..'; -import { Classification } from '../openapi/types'; +import { Classification } from '../openapi/types.js'; +import weaviate from '../v2/index.js'; const targetDessertId = '9f399d3e-45a4-44f4-b0fd-fa291abfb211'; const targetSavoryId = 'b7a64fbd-7c22-44ac-afbb-8d1432b8061b'; diff --git a/src/classifications/getter.ts b/src/classifications/getter.ts index c044f314..ba06739c 100644 --- a/src/classifications/getter.ts +++ b/src/classifications/getter.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Classification } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { Classification } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class ClassificationsGetter extends CommandBase { private id?: string; diff --git a/src/classifications/index.ts b/src/classifications/index.ts index c6a6a1aa..21708781 100644 --- a/src/classifications/index.ts +++ b/src/classifications/index.ts @@ -1,6 +1,6 @@ -import ClassificationsScheduler from './scheduler'; -import ClassificationsGetter from './getter'; -import Connection from '../connection'; +import Connection from '../connection/index.js'; +import ClassificationsGetter from './getter.js'; +import ClassificationsScheduler from './scheduler.js'; export interface Classifications { scheduler: () => ClassificationsScheduler; @@ -15,5 +15,5 @@ const data = (client: Connection): Classifications => { }; export default data; -export { default as ClassificationsGetter } from './getter'; -export { default as ClassificationsScheduler } from './scheduler'; +export { default as ClassificationsGetter } from './getter.js'; +export { default as ClassificationsScheduler } from './scheduler.js'; diff --git a/src/classifications/knn.journey.test.ts b/src/classifications/knn.journey.test.ts index 0c05b6fb..2d8ee246 100644 --- a/src/classifications/knn.journey.test.ts +++ b/src/classifications/knn.journey.test.ts @@ -1,6 +1,5 @@ -import weaviate, { WeaviateClient } from '..'; -import Connection from '../connection'; -import { Classification } from '../openapi/types'; +import { Classification } from '../openapi/types.js'; +import weaviate, { WeaviateClient } from '../v2/index.js'; const targetDessertId = 'cd54852a-209d-423b-bf1c-884468215237'; const targetSavoryId = 'e5da0127-327e-4184-85b8-7b9d1af4a850'; diff --git a/src/classifications/scheduler.ts b/src/classifications/scheduler.ts index 27298bef..a46045e3 100644 --- a/src/classifications/scheduler.ts +++ b/src/classifications/scheduler.ts @@ -1,7 +1,7 @@ -import ClassificationsGetter from './getter'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Classification } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { Classification } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import ClassificationsGetter from './getter.js'; export default class ClassificationsScheduler extends CommandBase { private basedOnProperties?: string[]; @@ -128,7 +128,7 @@ export default class ClassificationsScheduler extends CommandBase { this.validate(); const path = `/classifications`; - return this.client.post(path, this.payload()).then((res: any) => { + return this.client.postReturn(path, this.payload()).then((res: any) => { if (!this.waitForCompletion) { return Promise.resolve(res); } diff --git a/src/cluster/index.ts b/src/cluster/index.ts index 0f687155..a26567a2 100644 --- a/src/cluster/index.ts +++ b/src/cluster/index.ts @@ -1,5 +1,5 @@ -import NodesStatusGetter from './nodesStatusGetter'; -import Connection from '../connection'; +import Connection from '../connection/index.js'; +import NodesStatusGetter from './nodesStatusGetter.js'; export type NodeStatus = 'HEALTHY' | 'UNHEALTHY' | 'UNAVAILABLE'; @@ -14,4 +14,4 @@ const cluster = (client: Connection): Cluster => { }; export default cluster; -export { default as NodesStatusGetter } from './nodesStatusGetter'; +export { default as NodesStatusGetter } from './nodesStatusGetter.js'; diff --git a/src/cluster/journey.test.ts b/src/cluster/journey.test.ts deleted file mode 100644 index e47a5faa..00000000 --- a/src/cluster/journey.test.ts +++ /dev/null @@ -1,194 +0,0 @@ -import weaviate, { NodesStatusResponse } from '..'; - -import { - createTestFoodSchemaAndData, - cleanupTestFood, - PIZZA_CLASS_NAME, - SOUP_CLASS_NAME, -} from '../utils/testData'; - -describe('cluster nodes endpoint', () => { - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8080', - }); - - it('get nodes status of empty db', () => { - return client.cluster - .nodesStatusGetter() - .withOutput('verbose') - .do() - .then((nodesStatusResponse: NodesStatusResponse) => { - expect(nodesStatusResponse.nodes).toBeDefined(); - expect(nodesStatusResponse.nodes).toHaveLength(1); - if (nodesStatusResponse.nodes) { - const node = nodesStatusResponse.nodes[0] ?? []; - expect(node.name).toMatch(/.+/); - expect(node.version).toBeDefined(); - expect(node.gitHash).toBeDefined(); - expect(node.status).toEqual('HEALTHY'); - expect(node.stats).toBeDefined(); - expect(node.stats?.objectCount).toEqual(0); - expect(node.stats?.shardCount).toEqual(0); - expect(node.shards).toBeNull(); - } else { - throw new Error('nodesStatusResponse.nodes should be defined'); - } - }) - .catch((e: any) => { - throw new Error('should not fail on getting nodes: ' + e); - }); - }); - - it('sets up db', () => createTestFoodSchemaAndData(client)); - - it('get verbose nodes status of food db', () => { - return client.cluster - .nodesStatusGetter() - .withOutput('verbose') - .do() - .then((nodesStatusResponse: NodesStatusResponse) => { - expect(nodesStatusResponse.nodes).toHaveLength(1); - if (nodesStatusResponse.nodes) { - const node = nodesStatusResponse.nodes[0]; - expect(node.name).toMatch(/.+/); - expect(node.version).toBeDefined(); - expect(node.gitHash).toBeDefined(); - expect(node.status).toEqual('HEALTHY'); - expect(node.stats?.objectCount).toEqual(0); - expect(node.stats?.shardCount).toEqual(2); - expect(node.shards).toBeDefined(); - expect(node.shards).toHaveLength(2); - if (node.shards) { - expect([node.shards[0].class, node.shards[1].class]).toEqual( - expect.arrayContaining([PIZZA_CLASS_NAME, SOUP_CLASS_NAME]) - ); - for (let i = 0; i < node.shards.length; i++) { - const shard = node.shards[i]; - expect(shard.name).toMatch(/.+/); - switch (shard.class) { - case PIZZA_CLASS_NAME: - expect(shard.objectCount).toEqual(0); - break; - case SOUP_CLASS_NAME: - expect(shard.objectCount).toEqual(0); - break; - } - } - } else { - throw new Error('node.shards should be defined'); - } - } else { - throw new Error('nodesStatusResponse.nodes should be defined'); - } - }) - .catch((e: any) => { - throw new Error('should not fail on getting nodes: ' + e); - }); - }); - - it('get default nodes status of food db', () => { - return client.cluster - .nodesStatusGetter() - .do() - .then((nodesStatusResponse: NodesStatusResponse) => { - expect(nodesStatusResponse.nodes).toHaveLength(1); - if (nodesStatusResponse.nodes) { - const node = nodesStatusResponse.nodes[0]; - expect(node.name).toMatch(/.+/); - expect(node.version).toBeDefined(); - expect(node.gitHash).toBeDefined(); - expect(node.status).toEqual('HEALTHY'); - expect(node.stats?.objectCount).toEqual(0); - expect(node.stats?.shardCount).toEqual(2); - expect(node.shards).toBeDefined(); - expect(node.shards).toHaveLength(2); - if (node.shards) { - expect([node.shards[0].class, node.shards[1].class]).toEqual( - expect.arrayContaining([PIZZA_CLASS_NAME, SOUP_CLASS_NAME]) - ); - for (let i = 0; i < node.shards.length; i++) { - const shard = node.shards[i]; - expect(shard.name).toMatch(/.+/); - switch (shard.class) { - case PIZZA_CLASS_NAME: - expect(shard.objectCount).toEqual(0); - break; - case SOUP_CLASS_NAME: - expect(shard.objectCount).toEqual(0); - break; - } - } - } else { - throw new Error('node.shards should be defined'); - } - } else { - throw new Error('nodesStatusResponse.nodes should be defined'); - } - }) - .catch((e: any) => { - throw new Error('should not fail on getting nodes: ' + e); - }); - }); - - it('get minimal nodes status of food db', () => { - return client.cluster - .nodesStatusGetter() - .withOutput('minimal') - .do() - .then((nodesStatusResponse: NodesStatusResponse) => { - expect(nodesStatusResponse.nodes).toHaveLength(1); - if (nodesStatusResponse.nodes) { - const node = nodesStatusResponse.nodes[0]; - expect(node.name).toMatch(/.+/); - expect(node.version).toBeDefined(); - expect(node.gitHash).toBeDefined(); - expect(node.status).toEqual('HEALTHY'); - expect(node.stats).toBeUndefined(); - expect(node.shards).toBeNull(); - } else { - throw new Error('nodesStatusResponse.nodes should be defined'); - } - }) - .catch((e: any) => { - throw new Error('should not fail on getting nodes: ' + e); - }); - }); - - it('get nodes status of only Pizza class in food db', () => { - return client.cluster - .nodesStatusGetter() - .withClassName(PIZZA_CLASS_NAME) - .withOutput('verbose') - .do() - .then((nodesStatusResponse: NodesStatusResponse) => { - expect(nodesStatusResponse.nodes).toBeDefined(); - expect(nodesStatusResponse.nodes).toHaveLength(1); - if (nodesStatusResponse.nodes) { - const node = nodesStatusResponse.nodes[0]; - expect(node.name).toMatch(/.+/); - expect(node.version).toBeDefined(); - expect(node.gitHash).toBeDefined(); - expect(node.status).toEqual('HEALTHY'); - expect(node.stats?.objectCount).toEqual(0); - expect(node.stats?.shardCount).toEqual(1); - expect(node.shards).toBeDefined(); - expect(node.shards).toHaveLength(1); - if (node.shards) { - expect(node.shards[0].class).toEqual(PIZZA_CLASS_NAME); - expect(node.shards[0].objectCount).toEqual(0); - expect(node.shards[0].name).toBeDefined(); - } else { - throw new Error('node.shards should be defined'); - } - } else { - throw new Error('nodesStatusResponse.nodes should be defined'); - } - }) - .catch((e: any) => { - throw new Error('should not fail on getting nodes: ' + e); - }); - }); - - it('cleans up db', () => cleanupTestFood(client)); -}); diff --git a/src/cluster/nodesStatusGetter.ts b/src/cluster/nodesStatusGetter.ts index 3f0b8d37..b29ed7a2 100644 --- a/src/cluster/nodesStatusGetter.ts +++ b/src/cluster/nodesStatusGetter.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { NodesStatusResponse } from '../openapi/types'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { NodesStatusResponse } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class NodesStatusGetter extends CommandBase { private className?: string; diff --git a/src/collections/aggregate/index.ts b/src/collections/aggregate/index.ts new file mode 100644 index 00000000..63a41496 --- /dev/null +++ b/src/collections/aggregate/index.ts @@ -0,0 +1,746 @@ +import Connection from '../../connection/index.js'; + +import { ConsistencyLevel } from '../../data/index.js'; +import { DbVersionSupport } from '../../utils/dbVersion.js'; + +import { FilterValue } from '../filters/index.js'; + +import { WeaviateQueryError } from '../../errors.js'; +import { Aggregator } from '../../graphql/index.js'; +import { toBase64FromMedia } from '../../index.js'; +import { Serialize } from '../serialize/index.js'; + +export type AggregateBaseOptions = { + filters?: FilterValue; + returnMetrics?: M; +}; + +export type AggregateGroupByOptions = AggregateOptions & { + groupBy: (keyof T & string) | GroupByAggregate; +}; + +export type GroupByAggregate = { + property: keyof T & string; + limit?: number; +}; + +export type AggregateOptions = AggregateBaseOptions; + +export type AggregateBaseOverAllOptions = AggregateBaseOptions; + +export type AggregateNearOptions = AggregateBaseOptions & { + certainty?: number; + distance?: number; + objectLimit?: number; + targetVector?: string; +}; + +export type AggregateGroupByNearOptions = AggregateNearOptions & { + groupBy: (keyof T & string) | GroupByAggregate; +}; + +export type AggregateBoolean = { + count?: number; + percentageFalse?: number; + percentageTrue?: number; + totalFalse?: number; + totalTrue?: number; +}; + +export type AggregateDate = { + count?: number; + maximum?: number; + median?: number; + minimum?: number; + mode?: number; +}; + +export type AggregateNumber = { + count?: number; + maximum?: number; + mean?: number; + median?: number; + minimum?: number; + mode?: number; + sum?: number; +}; + +export type AggregateReference = { + pointingTo?: string; +}; + +export type AggregateText = { + count?: number; + topOccurrences?: { + occurs?: number; + value?: number; + }[]; +}; + +export type MetricsInput = + | MetricsBoolean + | MetricsInteger + | MetricsNumber + | MetricsText + | MetricsDate; +// | MetricsReference; + +export type PropertiesMetrics = T extends undefined + ? MetricsInput | MetricsInput[] + : MetricsInput | MetricsInput[]; + +export type MetricsBase = { + kind: K; + propertyName: N; +}; + +export type Option = { [key in keyof A]: boolean }; + +export type BooleanKeys = 'count' | 'percentageFalse' | 'percentageTrue' | 'totalFalse' | 'totalTrue'; +export type DateKeys = 'count' | 'maximum' | 'median' | 'minimum' | 'mode'; +export type NumberKeys = 'count' | 'maximum' | 'mean' | 'median' | 'minimum' | 'mode' | 'sum'; + +export type MetricsBoolean = MetricsBase & + Partial<{ [key in BooleanKeys]: boolean }>; +export type MetricsDate = MetricsBase & Partial<{ [key in DateKeys]: boolean }>; +export type MetricsInteger = MetricsBase & + Partial<{ [key in NumberKeys]: boolean }>; +export type MetricsNumber = MetricsBase & + Partial<{ [key in NumberKeys]: boolean }>; +// type MetricsReference = { +// kind: 'reference'; +// propertyName: RefKeys; +// pointingTo?: boolean; +// type?: boolean; +// }; +export type MetricsText = MetricsBase & { + count?: boolean; + topOccurrences?: { + occurs?: boolean; + value?: boolean; + }; + minOccurrences?: number; +}; + +export type AggregateMetrics = { + [K in keyof M]: M[K] extends true ? number : never; +}; + +export type MetricsProperty = T extends undefined ? string : keyof T & string; + +export const metrics = () => { + return { + aggregate:

>(property: P) => new MetricsManager(property), + }; +}; + +export interface Metrics { + /** + * Define the metrics to be returned based on a property when aggregating over a collection. + + Use this `aggregate` method to define the name to the property to be aggregated on. + Then use the `text`, `integer`, `number`, `boolean`, `date_`, or `reference` methods to define the metrics to be returned. + + See [the docs](https://weaviate.io/developers/weaviate/search/aggregate) for more details! + */ + aggregate:

>(property: P) => MetricsManager; +} + +export class MetricsManager> { + private propertyName: P; + + constructor(property: P) { + this.propertyName = property; + } + + private map(metrics: (keyof A)[]): Option { + const out: any = {}; + metrics.forEach((metric) => { + out[metric] = true; + }); + return out as Option; + } + + /** + * Define the metrics to be returned for a BOOL or BOOL_ARRAY property when aggregating over a collection. + * + * If none of the arguments are provided then all metrics will be returned. + * + * @param {('count' | 'percentageFalse' | 'percentageTrue' | 'totalFalse' | 'totalTrue')[]} metrics The metrics to return. + * @returns {MetricsBoolean

} The metrics for the property. + */ + public boolean( + metrics?: ('count' | 'percentageFalse' | 'percentageTrue' | 'totalFalse' | 'totalTrue')[] + ): MetricsBoolean

{ + if (metrics === undefined || metrics.length === 0) { + metrics = ['count', 'percentageFalse', 'percentageTrue', 'totalFalse', 'totalTrue']; + } + return { + ...this.map(metrics), + kind: 'boolean', + propertyName: this.propertyName, + }; + } + + /** + * Define the metrics to be returned for a DATE or DATE_ARRAY property when aggregating over a collection. + * + * If none of the arguments are provided then all metrics will be returned. + * + * @param {('count' | 'maximum' | 'median' | 'minimum' | 'mode')[]} metrics The metrics to return. + * @returns {MetricsDate

} The metrics for the property. + */ + public date(metrics?: ('count' | 'maximum' | 'median' | 'minimum' | 'mode')[]): MetricsDate

{ + if (metrics === undefined || metrics.length === 0) { + metrics = ['count', 'maximum', 'median', 'minimum', 'mode']; + } + return { + ...this.map(metrics), + kind: 'date', + propertyName: this.propertyName, + }; + } + + /** + * Define the metrics to be returned for an INT or INT_ARRAY property when aggregating over a collection. + * + * If none of the arguments are provided then all metrics will be returned. + * + * @param {('count' | 'maximum' | 'mean' | 'median' | 'minimum' | 'mode' | 'sum')[]} metrics The metrics to return. + * @returns {MetricsInteger

} The metrics for the property. + */ + public integer( + metrics?: ('count' | 'maximum' | 'mean' | 'median' | 'minimum' | 'mode' | 'sum')[] + ): MetricsInteger

{ + if (metrics === undefined || metrics.length === 0) { + metrics = ['count', 'maximum', 'mean', 'median', 'minimum', 'mode', 'sum']; + } + return { + ...this.map(metrics), + kind: 'integer', + propertyName: this.propertyName, + }; + } + + /** + * Define the metrics to be returned for a NUMBER or NUMBER_ARRAY property when aggregating over a collection. + * + * If none of the arguments are provided then all metrics will be returned. + * + * @param {('count' | 'maximum' | 'mean' | 'median' | 'minimum' | 'mode' | 'sum')[]} metrics The metrics to return. + * @returns {MetricsNumber

} The metrics for the property. + */ + public number( + metrics?: ('count' | 'maximum' | 'mean' | 'median' | 'minimum' | 'mode' | 'sum')[] + ): MetricsNumber

{ + if (metrics === undefined || metrics.length === 0) { + metrics = ['count', 'maximum', 'mean', 'median', 'minimum', 'mode', 'sum']; + } + return { + ...this.map(metrics), + kind: 'number', + propertyName: this.propertyName, + }; + } + + // public reference(metrics: 'pointingTo'[]): MetricsReference { + // return { + // ...this.map(metrics), + // kind: 'reference', + // propertyName: this.propertyName, + // }; + // } + + /** + * Define the metrics to be returned for a TEXT or TEXT_ARRAY property when aggregating over a collection. + * + * If none of the arguments are provided then all metrics will be returned. + * + * @param {('count' | 'topOccurrencesOccurs' | 'topOccurrencesValue')[]} metrics The metrics to return. + * @param {number} [minOccurrences] The how many top occurrences to return. + * @returns {MetricsText

} The metrics for the property. + */ + public text( + metrics?: ('count' | 'topOccurrencesOccurs' | 'topOccurrencesValue')[], + minOccurrences?: number + ): MetricsText

{ + if (metrics === undefined || metrics.length === 0) { + metrics = ['count', 'topOccurrencesOccurs', 'topOccurrencesValue']; + } + return { + count: metrics.includes('count'), + topOccurrences: + metrics.includes('topOccurrencesOccurs') || metrics.includes('topOccurrencesValue') + ? { + occurs: metrics.includes('topOccurrencesOccurs'), + value: metrics.includes('topOccurrencesValue'), + } + : undefined, + minOccurrences, + kind: 'text', + propertyName: this.propertyName, + }; + } +} + +type KindToAggregateType = K extends 'text' + ? AggregateText + : K extends 'date' + ? AggregateDate + : K extends 'integer' + ? AggregateNumber + : K extends 'number' + ? AggregateNumber + : K extends 'boolean' + ? AggregateBoolean + : K extends 'reference' + ? AggregateReference + : never; + +export type AggregateType = AggregateBoolean | AggregateDate | AggregateNumber | AggregateText; + +export type AggregateResult | undefined = undefined> = { + properties: T extends undefined + ? Record + : M extends MetricsInput[] + ? { + [K in M[number] as K['propertyName']]: KindToAggregateType; + } + : M extends MetricsInput + ? { + [K in M as K['propertyName']]: KindToAggregateType; + } + : undefined; + totalCount: number; +}; + +export type AggregateGroupByResult< + T, + M extends PropertiesMetrics | undefined = undefined +> = AggregateResult & { + groupedBy: { + prop: string; + value: string; + }; +}; + +class AggregateManager implements Aggregate { + connection: Connection; + groupBy: AggregateGroupBy; + name: string; + dbVersionSupport: DbVersionSupport; + consistencyLevel?: ConsistencyLevel; + tenant?: string; + + private constructor( + connection: Connection, + name: string, + dbVersionSupport: DbVersionSupport, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ) { + this.connection = connection; + this.name = name; + this.dbVersionSupport = dbVersionSupport; + this.consistencyLevel = consistencyLevel; + this.tenant = tenant; + + this.groupBy = { + nearImage: async | undefined = undefined>( + image: string | Buffer, + opts?: AggregateGroupByNearOptions + ): Promise[]> => { + const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearImage({ + image: await toBase64FromMedia(image), + certainty: opts?.certainty, + distance: opts?.distance, + targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, + }); + if (opts?.objectLimit) { + builder.withObjectLimit(opts?.objectLimit); + } + return this.doGroupBy(builder); + }, + nearObject: | undefined = undefined>( + id: string, + opts?: AggregateGroupByNearOptions + ): Promise[]> => { + const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearObject({ + id: id, + certainty: opts?.certainty, + distance: opts?.distance, + targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, + }); + if (opts?.objectLimit) { + builder.withObjectLimit(opts.objectLimit); + } + return this.doGroupBy(builder); + }, + nearText: | undefined = undefined>( + query: string | string[], + opts?: AggregateGroupByNearOptions + ): Promise[]> => { + const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearText({ + concepts: Array.isArray(query) ? query : [query], + certainty: opts?.certainty, + distance: opts?.distance, + targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, + }); + if (opts?.objectLimit) { + builder.withObjectLimit(opts.objectLimit); + } + return this.doGroupBy(builder); + }, + nearVector: | undefined = undefined>( + vector: number[], + opts?: AggregateGroupByNearOptions + ): Promise[]> => { + const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearVector({ + vector: vector, + certainty: opts?.certainty, + distance: opts?.distance, + targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, + }); + if (opts?.objectLimit) { + builder.withObjectLimit(opts.objectLimit); + } + return this.doGroupBy(builder); + }, + overAll: | undefined = undefined>( + opts: AggregateGroupByOptions + ): Promise[]> => { + const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy); + return this.doGroupBy(builder); + }, + }; + } + + query() { + return new Aggregator(this.connection); + } + + base( + metrics?: PropertiesMetrics, + filters?: FilterValue, + groupBy?: (keyof T & string) | GroupByAggregate + ) { + let fields = 'meta { count }'; + let builder = this.query().withClassName(this.name); + if (metrics) { + if (Array.isArray(metrics)) { + fields += metrics.map((m) => this.metrics(m)).join(' '); + } else { + fields += this.metrics(metrics); + } + } + if (groupBy) { + builder = builder.withGroupBy(typeof groupBy === 'string' ? [groupBy] : [groupBy.property]); + fields += 'groupedBy { path value }'; + if (typeof groupBy !== 'string' && groupBy?.limit) { + builder = builder.withLimit(groupBy.limit); + } + } + if (fields !== '') { + builder = builder.withFields(fields); + } + if (filters) { + builder = builder.withWhere(Serialize.filtersREST(filters)); + } + if (this.tenant) { + builder = builder.withTenant(this.tenant); + } + return builder; + } + + metrics(metrics: MetricsInput<(keyof T & string) | string>) { + let body = ''; + const { kind, propertyName, ...rest } = metrics; + switch (kind) { + case 'text': { + const { minOccurrences, ...restText } = rest as MetricsText; + body = Object.entries(restText) + .map(([key, value]) => { + if (value) { + return value instanceof Object + ? `topOccurrences${minOccurrences ? `(limit: ${minOccurrences})` : ''} { ${ + value.occurs ? 'occurs' : '' + } ${value.value ? 'value' : ''} }` + : key; + } + }) + .join(' '); + break; + } + default: + body = Object.entries(rest) + .map(([key, value]) => (value ? key : '')) + .join(' '); + } + return `${propertyName} { ${body} }`; + } + + static use( + connection: Connection, + name: string, + dbVersionSupport: DbVersionSupport, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ): AggregateManager { + return new AggregateManager(connection, name, dbVersionSupport, consistencyLevel, tenant); + } + + async nearImage>( + image: string | Buffer, + opts?: AggregateNearOptions + ): Promise> { + const builder = this.base(opts?.returnMetrics, opts?.filters).withNearImage({ + image: await toBase64FromMedia(image), + certainty: opts?.certainty, + distance: opts?.distance, + targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, + }); + if (opts?.objectLimit) { + builder.withObjectLimit(opts?.objectLimit); + } + return this.do(builder); + } + + nearObject>( + id: string, + opts?: AggregateNearOptions + ): Promise> { + const builder = this.base(opts?.returnMetrics, opts?.filters).withNearObject({ + id: id, + certainty: opts?.certainty, + distance: opts?.distance, + targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, + }); + if (opts?.objectLimit) { + builder.withObjectLimit(opts.objectLimit); + } + return this.do(builder); + } + + nearText>( + query: string | string[], + opts?: AggregateNearOptions + ): Promise> { + const builder = this.base(opts?.returnMetrics, opts?.filters).withNearText({ + concepts: Array.isArray(query) ? query : [query], + certainty: opts?.certainty, + distance: opts?.distance, + targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, + }); + if (opts?.objectLimit) { + builder.withObjectLimit(opts.objectLimit); + } + return this.do(builder); + } + + nearVector>( + vector: number[], + opts?: AggregateNearOptions + ): Promise> { + const builder = this.base(opts?.returnMetrics, opts?.filters).withNearVector({ + vector: vector, + certainty: opts?.certainty, + distance: opts?.distance, + targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, + }); + if (opts?.objectLimit) { + builder.withObjectLimit(opts.objectLimit); + } + return this.do(builder); + } + + overAll>(opts?: AggregateOptions): Promise> { + const builder = this.base(opts?.returnMetrics, opts?.filters); + return this.do(builder); + } + + do = | undefined = undefined>( + query: Aggregator + ): Promise> => { + return query + .do() + .then(({ data }: any) => { + const { meta, ...rest } = data.Aggregate[this.name][0]; + return { + properties: rest, + totalCount: meta?.count, + }; + }) + .catch((err: Error) => { + throw new WeaviateQueryError(err.message, 'GraphQL'); + }); + }; + + doGroupBy = | undefined = undefined>( + query: Aggregator + ): Promise[]> => { + return query + .do() + .then(({ data }: any) => + data.Aggregate[this.name].map((item: any) => { + const { groupedBy, meta, ...rest } = item; + return { + groupedBy: { + prop: groupedBy.path[0], + value: groupedBy.value, + }, + properties: rest.length > 0 ? rest : undefined, + totalCount: meta?.count, + }; + }) + ) + .catch((err: Error) => { + throw new WeaviateQueryError(err.message, 'GraphQL'); + }); + }; +} + +export interface Aggregate { + /** This namespace contains methods perform a group by search while aggregating metrics. */ + groupBy: AggregateGroupBy; + /** + * Aggregate metrics over the objects returned by a near image vector search on this collection. + * + * At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search. + * + * This method requires a vectorizer capable of handling base64-encoded images, e.g. `img2vec-neural`, `multi2vec-clip`, and `multi2vec-bind`. + * + * @param {string | Buffer} image The image to search on. This can be a base64 string, a file path string, or a buffer. + * @param {AggregateNearOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects returned by the vector search. + */ + nearImage>( + image: string | Buffer, + opts?: AggregateNearOptions + ): Promise>; + /** + * Aggregate metrics over the objects returned by a near object search on this collection. + * + * At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search. + * + * This method requires that the objects in the collection have associated vectors. + * + * @param {string} id The ID of the object to search for. + * @param {AggregateNearOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects returned by the vector search. + */ + nearObject>( + id: string, + opts?: AggregateNearOptions + ): Promise>; + /** + * Aggregate metrics over the objects returned by a near vector search on this collection. + * + * At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search. + * + * This method requires that the objects in the collection have associated vectors. + * + * @param {number[]} query The text query to search for. + * @param {AggregateNearOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects returned by the vector search. + */ + nearText>( + query: string | string[], + opts?: AggregateNearOptions + ): Promise>; + /** + * Aggregate metrics over the objects returned by a near vector search on this collection. + * + * At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search. + * + * This method requires that the objects in the collection have associated vectors. + * + * @param {number[]} vector The vector to search for. + * @param {AggregateNearOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects returned by the vector search. + */ + nearVector>( + vector: number[], + opts?: AggregateNearOptions + ): Promise>; + /** + * Aggregate metrics over all the objects in this collection without any vector search. + * + * @param {AggregateOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects in the collection. + */ + overAll>(opts?: AggregateOptions): Promise>; +} + +export interface AggregateGroupBy { + /** + * Aggregate metrics over the objects returned by a near image vector search on this collection. + * + * At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search. + * + * This method requires a vectorizer capable of handling base64-encoded images, e.g. `img2vec-neural`, `multi2vec-clip`, and `multi2vec-bind`. + * + * @param {string | Buffer} image The image to search on. This can be a base64 string, a file path string, or a buffer. + * @param {AggregateGroupByNearOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects returned by the vector search. + */ + nearImage>( + image: string | Buffer, + opts?: AggregateGroupByNearOptions + ): Promise[]>; + /** + * Aggregate metrics over the objects returned by a near object search on this collection. + * + * At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search. + * + * This method requires that the objects in the collection have associated vectors. + * + * @param {string} id The ID of the object to search for. + * @param {AggregateGroupByNearOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects returned by the vector search. + */ + nearObject>( + id: string, + opts?: AggregateGroupByNearOptions + ): Promise[]>; + /** + * Aggregate metrics over the objects returned by a near text vector search on this collection. + * + * At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search. + * + * This method requires a vectorizer capable of handling text, e.g. `text2vec-contextionary`, `text2vec-openai`, etc. + * + * @param {string | string[]} query The text to search for. + * @param {AggregateGroupByNearOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects returned by the vector search. + */ + nearText>( + query: string | string[], + opts: AggregateGroupByNearOptions + ): Promise[]>; + /** + * Aggregate metrics over the objects returned by a near vector search on this collection. + * + * At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search. + * + * This method requires that the objects in the collection have associated vectors. + * + * @param {number[]} vector The vector to search for. + * @param {AggregateGroupByNearOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects returned by the vector search. + */ + nearVector>( + vector: number[], + opts?: AggregateGroupByNearOptions + ): Promise[]>; + /** + * Aggregate metrics over all the objects in this collection without any vector search. + * + * @param {AggregateGroupByOptions} [opts] The options for the request. + * @returns {Promise[]>} The aggregated metrics for the objects in the collection. + */ + overAll>( + opts?: AggregateGroupByOptions + ): Promise[]>; +} + +export default AggregateManager.use; diff --git a/src/collections/aggregate/integration.test.ts b/src/collections/aggregate/integration.test.ts new file mode 100644 index 00000000..06f1f506 --- /dev/null +++ b/src/collections/aggregate/integration.test.ts @@ -0,0 +1,390 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import { WeaviateQueryError, WeaviateUnsupportedFeatureError } from '../../errors.js'; +import weaviate, { AggregateText, WeaviateClient } from '../../index.js'; +import { Collection } from '../collection/index.js'; +import { CrossReference } from '../references/index.js'; +import { DataObject } from '../types/index.js'; + +describe('Testing of the collection.aggregate methods', () => { + type TestCollectionAggregate = { + text: string; + texts: string[]; + int: number; + ints: number[]; + number: number; + numbers: number[]; + date: string; + dates: string[]; + boolean: boolean; + booleans: boolean[]; + ref?: CrossReference; + }; + + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionAggregate'; + + const date0 = '2023-01-01T00:00:00Z'; + const date1 = '2023-01-01T00:00:00Z'; + const date2 = '2023-01-02T00:00:00Z'; + const dateMid = '2023-01-01T12:00:00Z'; + + afterAll(async () => { + return (await client).collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + return client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'text', + dataType: 'text', + }, + { + name: 'texts', + dataType: 'text[]', + }, + { + name: 'int', + dataType: 'int', + }, + { + name: 'ints', + dataType: 'int[]', + }, + { + name: 'number', + dataType: 'number', + }, + { + name: 'numbers', + dataType: 'number[]', + }, + { + name: 'date', + dataType: 'date', + }, + { + name: 'dates', + dataType: 'date[]', + }, + { + name: 'boolean', + dataType: 'boolean', + }, + { + name: 'booleans', + dataType: 'boolean[]', + }, + // { + // name: 'ref', + // dataType: [collectionName], + // }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecContextionary({ + vectorizeCollectionName: false, + }), + }) + .then(async () => { + const data: DataObject[] = []; + for (let i = 0; i < 100; i++) { + data.push({ + properties: { + text: 'test', + texts: ['tests', 'tests'], + int: 1, + ints: [1, 2], + number: 1.0, + numbers: [1.0, 2.0], + date: date0, + dates: [date1, date2], + boolean: true, + booleans: [true, false], + }, + }); + } + const res = (await collection).data.insertMany(data); + return res; + }); + // .then(async (res) => { + // const uuid1 = res.uuids[0]; + // await collection.data.referenceAddMany({ + // refs: Object.values(res.uuids).map((uuid) => { + // return { + // fromProperty: 'ref', + // fromUuid: uuid1, + // reference: Reference.to({ uuids: [uuid] }) + // } + // }) + // }) + // }) + }); + + it('should aggregate data without a search and no property metrics', async () => { + const result = await collection.aggregate.overAll(); + expect(result.totalCount).toEqual(100); + }); + + it('should aggregate grouped by data without a search and no property metrics', async () => { + const result = await collection.aggregate.groupBy.overAll({ groupBy: 'text' }); + expect(result.length).toEqual(1); + expect(result[0].totalCount).toEqual(100); + expect(result[0].groupedBy.prop).toEqual('text'); + expect(result[0].groupedBy.value).toEqual('test'); + expect(result[0].properties).toBeUndefined(); + }); + + it('should aggregate grouped by data with a near text search and no property metrics', async () => { + const result = await collection.aggregate.groupBy.nearText('test', { + groupBy: 'text', + certainty: 0.1, + }); + expect(result.length).toEqual(1); + expect(result[0].totalCount).toEqual(100); + expect(result[0].groupedBy.prop).toEqual('text'); + expect(result[0].groupedBy.value).toEqual('test'); + expect(result[0].properties).toBeUndefined(); + }); + + it('should aggregate data without a search and one generic property metric', async () => { + const result = await collection.aggregate.overAll({ + returnMetrics: collection.metrics + .aggregate('text') + .text(['count', 'topOccurrencesOccurs', 'topOccurrencesValue']), + }); + expect(result.totalCount).toEqual(100); + expect(result.properties.text.count).toEqual(100); + expect(result.properties.text.topOccurrences![0].occurs).toEqual(100); + expect(result.properties.text.topOccurrences![0].value).toEqual('test'); + }); + + it('should aggregate data without a search and one non-generic property metric', async () => { + const result = await (await client).collections.get(collectionName).aggregate.overAll({ + returnMetrics: collection.metrics + .aggregate('text') + .text(['count', 'topOccurrencesOccurs', 'topOccurrencesValue']), + }); + expect(result.totalCount).toEqual(100); + expect(result.properties.text.count).toEqual(100); + expect((result.properties.text as AggregateText).topOccurrences![0].occurs).toEqual(100); + expect((result.properties.text as AggregateText).topOccurrences![0].value).toEqual('test'); + }); + + it('should aggregate data without a search and all property metrics', async () => { + const result = await collection.aggregate.overAll({ + returnMetrics: [ + collection.metrics.aggregate('text').text(['count', 'topOccurrencesOccurs', 'topOccurrencesValue']), + collection.metrics.aggregate('texts').text(['count', 'topOccurrencesOccurs', 'topOccurrencesValue']), + collection.metrics + .aggregate('int') + .integer(['count', 'maximum', 'mean', 'median', 'minimum', 'mode', 'sum']), + collection.metrics + .aggregate('ints') + .integer(['count', 'maximum', 'mean', 'median', 'minimum', 'mode', 'sum']), + collection.metrics + .aggregate('number') + .number(['count', 'maximum', 'mean', 'median', 'minimum', 'mode', 'sum']), + collection.metrics + .aggregate('numbers') + .number(['count', 'maximum', 'mean', 'median', 'minimum', 'mode', 'sum']), + collection.metrics.aggregate('date').date(['count', 'maximum', 'median', 'minimum', 'mode']), + collection.metrics.aggregate('dates').date(['count', 'maximum', 'median', 'minimum']), // 'mode' flakes between date1 and date2 + collection.metrics + .aggregate('boolean') + .boolean(['count', 'percentageFalse', 'percentageTrue', 'totalFalse', 'totalTrue']), + collection.metrics + .aggregate('booleans') + .boolean(['count', 'percentageFalse', 'percentageTrue', 'totalFalse', 'totalTrue']), + // Metrics.aggregate('ref').reference(['pointingTo']) + ], + }); + expect(result).toEqual({ + totalCount: 100, + properties: { + text: { + count: 100, + topOccurrences: [{ occurs: 100, value: 'test' }], + }, + texts: { + count: 200, + topOccurrences: [{ occurs: 200, value: 'tests' }], + }, + int: { + count: 100, + maximum: 1, + mean: 1, + median: 1, + minimum: 1, + mode: 1, + sum: 100, + }, + ints: { + count: 200, + maximum: 2, + mean: 1.5, + median: 1.5, + minimum: 1, + mode: 1, + sum: 300, + }, + number: { + count: 100, + maximum: 1, + mean: 1, + median: 1, + minimum: 1, + mode: 1, + sum: 100, + }, + numbers: { + count: 200, + maximum: 2, + mean: 1.5, + median: 1.5, + minimum: 1, + mode: 1, + sum: 300, + }, + date: { + count: 100, + maximum: date0, + median: date0, + minimum: date0, + mode: date0, + }, + dates: { + count: 200, + maximum: date2, + median: dateMid, + minimum: date1, + // mode: date1, // randomly switches between date1 and date2 + }, + boolean: { + count: 100, + percentageFalse: 0, + percentageTrue: 1, + totalFalse: 0, + totalTrue: 100, + }, + booleans: { + count: 200, + percentageFalse: 0.5, + percentageTrue: 0.5, + totalFalse: 100, + totalTrue: 100, + }, + // ref: { + // pointingTo: collectionName + // } + }, + }); + }); +}); + +describe('Testing of the collection.aggregate methods with named vectors', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionAggregateVectors'; + type TestCollectionAggregateVectors = { + text: string; + }; + + afterAll(async () => { + return (await client).collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + const query = () => + client.collections.create({ + name: collectionName, + properties: [ + { + name: 'text', + dataType: 'text', + }, + ], + vectorizers: [ + weaviate.configure.vectorizer.text2VecContextionary({ + name: 'text', + sourceProperties: ['text'], + vectorIndexConfig: weaviate.configure.vectorIndex.hnsw(), + }), + ], + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + return query(); + }); + + it('should aggregate data with a near text search over a named vector', async () => { + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + return; + } + const result = await collection.aggregate.nearText('test', { certainty: 0.9, targetVector: 'text' }); + expect(result.totalCount).toEqual(0); + }); +}); + +describe('Testing of collection.aggregate.overAll with a multi-tenancy collection', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionAggregate'; + + afterAll(async () => { + return (await client).collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + return client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'text', + dataType: 'text', + }, + ], + multiTenancy: { enabled: true }, + }) + .then(async (created) => { + const tenants = await created.tenants.create({ name: 'test' }); + collection = created.withTenant(tenants[0].name); + const data: Array = []; + for (let i = 0; i < 100; i++) { + data.push({ + properties: { + text: 'test', + }, + }); + } + await collection.data.insertMany(data); + }); + }); + + it('should aggregate data without a search and no property metrics over the tenant', () => + collection.aggregate.overAll().then((result) => expect(result.totalCount).toEqual(100))); + + it('should throw an error for a non-existant tenant', () => + expect(collection.withTenant('non-existing-tenant').aggregate.overAll()).rejects.toThrow( + WeaviateQueryError + )); +}); diff --git a/src/collections/backup/client.ts b/src/collections/backup/client.ts new file mode 100644 index 00000000..83262618 --- /dev/null +++ b/src/collections/backup/client.ts @@ -0,0 +1,195 @@ +import { + Backend, + BackupCompressionLevel, + BackupCreateStatusGetter, + BackupCreator, + BackupRestoreStatusGetter, + BackupRestorer, +} from '../../backup/index.js'; +import Connection from '../../connection/index.js'; +import { WeaviateBackupFailed } from '../../errors.js'; +import { + BackupCreateResponse, + BackupCreateStatusResponse, + BackupRestoreResponse, + BackupRestoreStatusResponse, +} from '../../openapi/types.js'; + +/** Configuration options available when creating a backup */ +export type BackupConfigCreate = { + /** The size of the chunks to use for the backup. */ + chunkSize?: number; + /** The standard of compression to use for the backup. */ + compressionLevel?: BackupCompressionLevel; + /** The percentage of CPU to use for the backup creation job. */ + cpuPercentage?: number; +}; + +/** Configuration options available when restoring a backup */ +export type BackupConfigRestore = { + /** The percentage of CPU to use for the backuop restoration job. */ + cpuPercentage?: number; +}; + +/** The arguments required to create and restore backups. */ +export type BackupArgs = { + /** The ID of the backup. */ + backupId: string; + /** The backend to use for the backup. */ + backend: Backend; + /** The collections to include in the backup. */ + includeCollections?: string[]; + /** The collections to exclude from the backup. */ + excludeCollections?: string[]; + /** Whether to wait for the backup to complete. */ + waitForCompletion?: boolean; + /** The configuration options for the backup. */ + config?: C; +}; + +/** The arguments required to get the status of a backup. */ +export type BackupStatusArgs = { + /** The ID of the backup. */ + backupId: string; + /** The backend to use for the backup. */ + backend: Backend; +}; + +export const backup = (connection: Connection) => { + const getCreateStatus = (args: BackupStatusArgs): Promise => { + return new BackupCreateStatusGetter(connection) + .withBackupId(args.backupId) + .withBackend(args.backend) + .do(); + }; + const getRestoreStatus = (args: BackupStatusArgs): Promise => { + return new BackupRestoreStatusGetter(connection) + .withBackupId(args.backupId) + .withBackend(args.backend) + .do(); + }; + return { + create: async (args: BackupArgs): Promise => { + let builder = new BackupCreator(connection, new BackupCreateStatusGetter(connection)) + .withBackupId(args.backupId) + .withBackend(args.backend); + if (args.includeCollections) { + builder = builder.withIncludeClassNames(...args.includeCollections); + } + if (args.excludeCollections) { + builder = builder.withExcludeClassNames(...args.excludeCollections); + } + if (args.config) { + builder = builder.withConfig({ + ChunkSize: args.config.chunkSize, + CompressionLevel: args.config.compressionLevel, + CPUPercentage: args.config.cpuPercentage, + }); + } + let res: BackupCreateResponse; + try { + res = await builder.do(); + } catch (err) { + throw new Error(`Backup creation failed: ${err}`); + } + if (res.status === 'FAILED') { + throw new Error(`Backup creation failed: ${res.error}`); + } + let status: BackupCreateStatusResponse | undefined; + if (args.waitForCompletion) { + let wait = true; + while (wait) { + const res = await getCreateStatus(args); // eslint-disable-line no-await-in-loop + if (res.status === 'SUCCESS') { + wait = false; + status = res; + } + if (res.status === 'FAILED') { + throw new WeaviateBackupFailed(res.error ? res.error : '', 'creation'); + } + await new Promise((resolve) => setTimeout(resolve, 1000)); // eslint-disable-line no-await-in-loop + } + } + return status ? { ...status, classes: res.classes } : res; + }, + getCreateStatus: getCreateStatus, + getRestoreStatus: getRestoreStatus, + restore: async (args: BackupArgs): Promise => { + let builder = new BackupRestorer(connection, new BackupRestoreStatusGetter(connection)) + .withBackupId(args.backupId) + .withBackend(args.backend); + if (args.includeCollections) { + builder = builder.withIncludeClassNames(...args.includeCollections); + } + if (args.excludeCollections) { + builder = builder.withExcludeClassNames(...args.excludeCollections); + } + if (args.config) { + builder = builder.withConfig({ + CPUPercentage: args.config.cpuPercentage, + }); + } + let res: BackupRestoreResponse; + try { + res = await builder.do(); + } catch (err) { + throw new Error(`Backup restoration failed: ${err}`); + } + if (res.status === 'FAILED') { + throw new Error(`Backup restoration failed: ${res.error}`); + } + let status: BackupRestoreStatusResponse | undefined; + if (args.waitForCompletion) { + let wait = true; + while (wait) { + const res = await getRestoreStatus(args); // eslint-disable-line no-await-in-loop + if (res.status === 'SUCCESS') { + wait = false; + status = res; + } + if (res.status === 'FAILED') { + throw new WeaviateBackupFailed(res.error ? res.error : '', 'restoration'); + } + await new Promise((resolve) => setTimeout(resolve, 1000)); // eslint-disable-line no-await-in-loop + } + } + return status + ? { + ...status, + classes: res.classes, + } + : res; + }, + }; +}; + +export interface Backup { + /** + * Create a backup of the database. + * + * @param {BackupArgs} args The arguments for the request. + * @returns {Promise} The response from Weaviate. + */ + create(args: BackupArgs): Promise; + /** + * Get the status of a backup creation. + * + * @param {BackupStatusArgs} args The arguments for the request. + * @returns {Promise} The status of the backup creation. + */ + getCreateStatus(args: BackupStatusArgs): Promise; + /** + * Get the status of a backup restore. + * + * @param {BackupStatusArgs} args The arguments for the request. + * @returns {Promise} The status of the backup restore. + */ + getRestoreStatus(args: BackupStatusArgs): Promise; + /** + * Restore a backup of the database. + * + * @param {BackupArgs} args The arguments for the request. + * @returns {Promise} The response from Weaviate. + */ + restore(args: BackupArgs): Promise; +} diff --git a/src/collections/backup/collection.ts b/src/collections/backup/collection.ts new file mode 100644 index 00000000..956fbb33 --- /dev/null +++ b/src/collections/backup/collection.ts @@ -0,0 +1,68 @@ +import { Backend } from '../../backup/index.js'; +import Connection from '../../connection/index.js'; +import { + BackupCreateResponse, + BackupCreateStatusResponse, + BackupRestoreResponse, + BackupRestoreStatusResponse, +} from '../../openapi/types.js'; +import { BackupStatusArgs, backup } from './client.js'; + +/** The arguments required to create and restore backups. */ +export type BackupCollectionArgs = { + /** The ID of the backup. */ + backupId: string; + /** The backend to use for the backup. */ + backend: Backend; + /** The collections to include in the backup. */ + waitForCompletion?: boolean; +}; + +export const backupCollection = (connection: Connection, name: string) => { + const handler = backup(connection); + return { + create: (args: BackupCollectionArgs) => + handler.create({ + ...args, + includeCollections: [name], + }), + getCreateStatus: handler.getCreateStatus, + getRestoreStatus: handler.getRestoreStatus, + restore: (args: BackupCollectionArgs) => + handler.restore({ + ...args, + includeCollections: [name], + }), + }; +}; + +export interface BackupCollection { + /** + * Create a backup of this collection. + * + * @param {BackupArgs} args The arguments for the request. + * @returns {Promise} The response from Weaviate. + */ + create(args: BackupCollectionArgs): Promise; + /** + * Get the status of a backup. + * + * @param {BackupStatusArgs} args The arguments for the request. + * @returns {Promise} The status of the backup. + */ + getCreateStatus(args: BackupStatusArgs): Promise; + /** + * Get the status of a restore. + * + * @param {BackupStatusArgs} args The arguments for the request. + * @returns {Promise} The status of the restore. + */ + getRestoreStatus(args: BackupStatusArgs): Promise; + /** + * Restore a backup of this collection. + * + * @param {BackupArgs} args The arguments for the request. + * @returns {Promise} The response from Weaviate. + */ + restore(args: BackupCollectionArgs): Promise; +} diff --git a/src/collections/backup/index.ts b/src/collections/backup/index.ts new file mode 100644 index 00000000..5b5c3e11 --- /dev/null +++ b/src/collections/backup/index.ts @@ -0,0 +1,8 @@ +export type { + Backup, + BackupArgs, + BackupConfigCreate, + BackupConfigRestore, + BackupStatusArgs, +} from './client.js'; +export type { BackupCollection, BackupCollectionArgs } from './collection.js'; diff --git a/src/collections/backup/integration.test.ts b/src/collections/backup/integration.test.ts new file mode 100644 index 00000000..cf8888a6 --- /dev/null +++ b/src/collections/backup/integration.test.ts @@ -0,0 +1,105 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +/* eslint-disable no-await-in-loop */ +import { Backend } from '../../backup/index.js'; +import weaviate, { Collection, WeaviateClient } from '../../index.js'; + +// These must run sequentially because Weaviate is not capable of running multiple backups at the same time +describe('Integration testing of backups', () => { + const clientPromise = weaviate.connectToLocal({ + port: 8090, + grpcPort: 50061, + }); + + const getCollection = (client: WeaviateClient) => client.collections.get('TestBackupCollection'); + + beforeAll(() => + clientPromise.then((client) => + Promise.all([ + client.collections.create({ name: 'TestBackupClient' }).then((col) => col.data.insert()), + client.collections.create({ name: 'TestBackupCollection' }).then((col) => col.data.insert()), + ]) + ) + ); + + afterAll(() => clientPromise.then((client) => client.collections.deleteAll())); + + const testClientWaitForCompletion = async (client: WeaviateClient) => { + const res = await client.backup.create({ + backupId: `test-backup-${randomBackupId()}`, + backend: 'filesystem', + waitForCompletion: true, + }); + expect(res.status).toBe('SUCCESS'); + return client; + }; + + const testClientNoWaitForCompletion = async (client: WeaviateClient) => { + const res = await client.backup.create({ + backupId: `test-backup-${randomBackupId()}`, + backend: 'filesystem', + }); + expect(res.status).toBe('STARTED'); + const status = await client.backup.getCreateStatus({ + backupId: res.id as string, + backend: res.backend as 'filesystem', + }); + expect(status).not.toBe('SUCCESS'); // can be 'STARTED' or 'TRANSFERRING' depending on the speed of the test machine + + // wait to complete so that other tests can run without colliding with Weaviate's lack of simultaneous backups + let wait = true; + while (wait) { + const { status, error } = await client.backup.getCreateStatus({ + backupId: res.id as string, + backend: res.backend as Backend, + }); + if (status === 'SUCCESS') { + wait = false; + } + if (status === 'FAILED') { + throw new Error(`Backup creation failed: ${error}`); + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + return client; + }; + + const testCollectionWaitForCompletion = async (collection: Collection) => { + const res = await collection.backup.create({ + backupId: `test-backup-${randomBackupId()}`, + backend: 'filesystem', + waitForCompletion: true, + }); + expect(res.status).toBe('SUCCESS'); + expect(res.classes).toEqual(['TestBackupCollection']); + return collection; + }; + + const testCollectionNoWaitForCompletion = async (collection: Collection) => { + const res = await collection.backup.create({ + backupId: `test-backup-${randomBackupId()}`, + backend: 'filesystem', + }); + expect(res.status).toBe('STARTED'); + expect(res.classes).toEqual(['TestBackupCollection']); + const status = await collection.backup.getCreateStatus({ + backupId: res.id as string, + backend: res.backend as 'filesystem', + }); + expect(status).not.toBe('SUCCESS'); // can be 'STARTED' or 'TRANSFERRING' depending on the speed of the test machine + return collection; + }; + + it('run', () => + clientPromise + .then(testClientWaitForCompletion) + .then(testClientNoWaitForCompletion) + .then(getCollection) + .then(testCollectionWaitForCompletion) + .then(testCollectionNoWaitForCompletion)); +}); + +function randomBackupId() { + return 'backup-id-' + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); +} diff --git a/src/collections/cluster/index.ts b/src/collections/cluster/index.ts new file mode 100644 index 00000000..1f16a844 --- /dev/null +++ b/src/collections/cluster/index.ts @@ -0,0 +1,46 @@ +import { NodesStatusGetter } from '../../cluster/index.js'; +import Connection from '../../connection/index.js'; +import { BatchStats, NodeShardStatus, NodeStats } from '../../openapi/types.js'; + +export type Output = 'minimal' | 'verbose' | undefined; + +export type NodesOptions = { + /** The name of the collection to get the status of. */ + collection?: string; + /** Set the desired output verbosity level. Can be `minimal | verbose | undefined` with `undefined` defaulting to `minimal`. */ + output: O; +}; + +export type Node = { + name: string; + status: 'HEALTHY' | 'UNHEALTHY' | 'UNAVAILABLE'; + version: string; + gitHash: string; + stats: O extends 'minimal' | undefined ? undefined : Required; + batchStats: Required; + shards: O extends 'minimal' | undefined ? null : Required[]; +}; + +const cluster = (connection: Connection) => { + return { + nodes: (opts?: NodesOptions): Promise[]> => { + let builder = new NodesStatusGetter(connection).withOutput(opts?.output ? opts.output : 'minimal'); + if (opts?.collection) { + builder = builder.withClassName(opts.collection); + } + return builder.do().then((res) => res.nodes) as Promise[]>; + }, + }; +}; + +export default cluster; + +export interface Cluster { + /** + * Get the status of all nodes in the cluster. + * + * @param {NodesOptions} [opts] The options for the request. + * @returns {Promise[]>} The status of all nodes in the cluster. + */ + nodes: (opts?: NodesOptions) => Promise[]>; +} diff --git a/src/collections/cluster/integration.test.ts b/src/collections/cluster/integration.test.ts new file mode 100644 index 00000000..1ec26095 --- /dev/null +++ b/src/collections/cluster/integration.test.ts @@ -0,0 +1,71 @@ +import weaviate, { WeaviateClient } from '../../index.js'; + +describe('Testing of the client.cluster methods', () => { + let client: WeaviateClient; + + const one = 'TestClusterCollectionOne'; + const two = 'TestClusterCollectionTwo'; + + afterAll(async () => { + await (await client).collections.delete(one); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + return Promise.all([client.collections.create({ name: one }), client.collections.create({ name: two })]); + }); + + it('should return the default node statuses', async () => { + const nodes = await client.cluster.nodes(); + expect(nodes).toBeDefined(); + expect(nodes.length).toBeGreaterThan(0); + expect(nodes[0].gitHash).toBeDefined(); + expect(nodes[0].version).toBeDefined(); + expect(nodes[0].status).toEqual('HEALTHY'); + expect(nodes[0].stats).toBeUndefined(); + expect(nodes[0].shards).toBeNull(); + expect(nodes[0].batchStats.queueLength).toBeGreaterThanOrEqual(0); + expect(nodes[0].batchStats.ratePerSecond).toBeGreaterThanOrEqual(0); + }); + + it('should return the minimal node statuses', async () => { + const nodes = await client.cluster.nodes({ output: 'minimal' }); + expect(nodes).toBeDefined(); + expect(nodes.length).toBeGreaterThan(0); + expect(nodes[0].gitHash).toBeDefined(); + expect(nodes[0].version).toBeDefined(); + expect(nodes[0].status).toEqual('HEALTHY'); + expect(nodes[0].stats).toBeUndefined(); + expect(nodes[0].shards).toBeNull(); + expect(nodes[0].batchStats.queueLength).toBeGreaterThanOrEqual(0); + expect(nodes[0].batchStats.ratePerSecond).toBeGreaterThanOrEqual(0); + }); + + it('should return the verbose node statuses', async () => { + const nodes = await client.cluster.nodes({ output: 'verbose' }); + expect(nodes).toBeDefined(); + expect(nodes.length).toBeGreaterThan(0); + expect(nodes[0].gitHash).toBeDefined(); + expect(nodes[0].version).toBeDefined(); + expect(nodes[0].status).toEqual('HEALTHY'); + expect(nodes[0].stats.shardCount).toBeDefined(); + expect(nodes[0].stats.objectCount).toBeDefined(); + expect(nodes[0].shards.length).toBeGreaterThanOrEqual(0); + expect(nodes[0].batchStats.queueLength).toBeGreaterThanOrEqual(0); + expect(nodes[0].batchStats.ratePerSecond).toBeGreaterThanOrEqual(0); + }); + + it('should return the node statuses for a specific collection', async () => { + const nodes = await client.cluster.nodes({ collection: one, output: 'verbose' }); + expect(nodes).toBeDefined(); + expect(nodes.length).toBeGreaterThan(0); + expect(nodes[0].gitHash).toBeDefined(); + expect(nodes[0].version).toBeDefined(); + expect(nodes[0].status).toEqual('HEALTHY'); + expect(nodes[0].stats.shardCount).toBeDefined(); + expect(nodes[0].stats.objectCount).toBeDefined(); + expect(nodes[0].shards.length).toBeGreaterThanOrEqual(0); + expect(nodes[0].batchStats.queueLength).toBeGreaterThanOrEqual(0); + expect(nodes[0].batchStats.ratePerSecond).toBeGreaterThanOrEqual(0); + }); +}); diff --git a/src/collections/collection/index.ts b/src/collections/collection/index.ts new file mode 100644 index 00000000..6433ddef --- /dev/null +++ b/src/collections/collection/index.ts @@ -0,0 +1,156 @@ +import Connection from '../../connection/grpc.js'; +import { ConsistencyLevel } from '../../data/index.js'; +import { WeaviateInvalidInputError } from '../../errors.js'; +import ClassExists from '../../schema/classExists.js'; +import { DbVersionSupport } from '../../utils/dbVersion.js'; + +import aggregate, { Aggregate, Metrics, metrics } from '../aggregate/index.js'; +import { BackupCollection, backupCollection } from '../backup/collection.js'; +import config, { Config } from '../config/index.js'; +import data, { Data } from '../data/index.js'; +import filter, { Filter } from '../filters/index.js'; +import generate, { Generate } from '../generate/index.js'; +import { Iterator } from '../iterator/index.js'; +import query, { Query } from '../query/index.js'; +import sort, { Sort } from '../sort/index.js'; +import tenants, { Tenant, Tenants } from '../tenants/index.js'; +import { QueryMetadata, QueryProperty, QueryReference } from '../types/index.js'; + +export interface Collection { + /** This namespace includes all the querying methods available to you when using Weaviate's standard aggregation capabilities. */ + aggregate: Aggregate; + /** This namespace includes all the backup methods available to you when backing up a collection in Weaviate. */ + backup: BackupCollection; + /** This namespace includes all the CRUD methods available to you when modifying the configuration of the collection in Weaviate. */ + config: Config; + /** This namespace includes all the CUD methods available to you when modifying the data of the collection in Weaviate. */ + data: Data; + /** This namespace includes the methods by which you can create the `FilterValue` values for use when filtering queries over your collection. */ + filter: Filter; + /** This namespace includes all the querying methods available to you when using Weaviate's generative capabilities. */ + generate: Generate; + /** This namespace includes the methods by which you can create the `MetricsX` values for use when aggregating over your collection. */ + metrics: Metrics; + /** The name of the collection. */ + name: N; + /** This namespace includes all the querying methods available to you when using Weaviate's standard query capabilities. */ + query: Query; + /** This namespaces includes the methods by which you can create the `Sorting` values for use when sorting queries over your collection. */ + sort: Sort; + /** This namespace includes all the CRUD methods available to you when modifying the tenants of a multi-tenancy-enabled collection in Weaviate. */ + tenants: Tenants; + /** + * Use this method to check if the collection exists in Weaviate. + * + * @returns {Promise} A promise that resolves to `true` if the collection exists, and `false` otherwise. + */ + exists: () => Promise; + /** + * Use this method to return an iterator over the objects in the collection. + * + * This iterator keeps a record of the last object that it returned to be used in each subsequent call to Weaviate. + * Once the collection is exhausted, the iterator exits. + * + * @param {IteratorOptions} opts The options to use when fetching objects from Weaviate. + * @returns {Iterator} An iterator over the objects in the collection as an async generator. + * + * @description If `return_properties` is not provided, all the properties of each object will be + * requested from Weaviate except for its vector as this is an expensive operation. Specify `include_vector` + * to request the vector back as well. In addition, if `return_references=None` then none of the references + * are returned. Use `wvc.QueryReference` to specify which references to return. + */ + iterator: (opts?: IteratorOptions) => Iterator; + /** + * Use this method to return a collection object specific to a single consistency level. + * + * If replication is not configured for this collection then Weaviate will throw an error. + * + * This method does not send a request to Weaviate. It only returns a new collection object that is specific to the consistency level you specify. + * + * @param {ConsistencyLevel} consistencyLevel The consistency level to use. + * @returns {Collection} A new collection object specific to the consistency level you specified. + */ + withConsistency: (consistencyLevel: ConsistencyLevel) => Collection; + /** + * Use this method to return a collection object specific to a single tenant. + * + * If multi-tenancy is not configured for this collection then Weaviate will throw an error. + * + * This method does not send a request to Weaviate. It only returns a new collection object that is specific to the tenant you specify. + * + * @param {string | Tenant} tenant The tenant name or tenant object to use. + * @returns {Collection} A new collection object specific to the tenant you specified. + */ + withTenant: (tenant: string | Tenant) => Collection; +} + +export type IteratorOptions = { + includeVector?: boolean | string[]; + returnMetadata?: QueryMetadata; + returnProperties?: QueryProperty[]; + returnReferences?: QueryReference[]; +}; + +const isString = (value: any): value is string => typeof value === 'string'; + +const capitalizeCollectionName = (name: N): N => + (name.charAt(0).toUpperCase() + name.slice(1)) as N; + +const collection = ( + connection: Connection, + name: N, + dbVersionSupport: DbVersionSupport, + consistencyLevel?: ConsistencyLevel, + tenant?: Tenant +): Collection => { + if (!isString(name)) { + throw new WeaviateInvalidInputError(`The collection name must be a string, got: ${typeof name}`); + } + const capitalizedName = capitalizeCollectionName(name); + const queryCollection = query( + connection, + capitalizedName, + dbVersionSupport, + consistencyLevel, + tenant?.name + ); + return { + aggregate: aggregate(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant?.name), + backup: backupCollection(connection, capitalizedName), + config: config(connection, capitalizedName, dbVersionSupport, tenant?.name), + data: data(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant?.name), + filter: filter(), + generate: generate(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant?.name), + metrics: metrics(), + name: name, + query: queryCollection, + sort: sort(), + tenants: tenants(connection, capitalizedName, dbVersionSupport), + exists: () => new ClassExists(connection).withClassName(capitalizedName).do(), + iterator: (opts?: IteratorOptions) => + new Iterator((limit: number, after?: string) => + queryCollection + .fetchObjects({ + limit, + after, + includeVector: opts?.includeVector, + returnMetadata: opts?.returnMetadata, + returnProperties: opts?.returnProperties, + returnReferences: opts?.returnReferences, + }) + .then((res) => res.objects) + ), + withConsistency: (consistencyLevel: ConsistencyLevel) => + collection(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant), + withTenant: (tenant: string | Tenant) => + collection( + connection, + capitalizedName, + dbVersionSupport, + consistencyLevel, + typeof tenant === 'string' ? { name: tenant } : tenant + ), + }; +}; + +export default collection; diff --git a/src/collections/config/classes.ts b/src/collections/config/classes.ts new file mode 100644 index 00000000..2269780f --- /dev/null +++ b/src/collections/config/classes.ts @@ -0,0 +1,137 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { + WeaviateClass, + WeaviateInvertedIndexConfig, + WeaviateReplicationConfig, + WeaviateVectorIndexConfig, + WeaviateVectorsConfig, +} from '../../openapi/types.js'; +import { QuantizerGuards } from '../configure/parsing.js'; +import { + InvertedIndexConfigUpdate, + ReplicationConfigUpdate, + VectorConfigUpdate, + VectorIndexConfigFlatUpdate, + VectorIndexConfigHNSWUpdate, +} from '../configure/types/index.js'; +import { CollectionConfigUpdate, VectorIndexType } from './types/index.js'; + +export class MergeWithExisting { + static schema( + current: WeaviateClass, + supportsNamedVectors: boolean, + update?: CollectionConfigUpdate + ): WeaviateClass { + if (update === undefined) return current; + if (update.description !== undefined) current.description = update.description; + if (update.invertedIndex !== undefined) + current.invertedIndexConfig = MergeWithExisting.invertedIndex( + current.invertedIndexConfig, + update.invertedIndex + ); + if (update.replication !== undefined) + current.replicationConfig = MergeWithExisting.replication( + current.replicationConfig!, + update.replication + ); + if (update.vectorizers !== undefined) { + if (Array.isArray(update.vectorizers)) { + current.vectorConfig = MergeWithExisting.vectors(current.vectorConfig, update.vectorizers); + } else if (supportsNamedVectors) { + const updateVectorizers = { + ...update.vectorizers, + name: 'default', + }; + current.vectorConfig = MergeWithExisting.vectors(current.vectorConfig, [updateVectorizers]); + } else { + current.vectorIndexConfig = + update.vectorizers?.vectorIndex.name === 'hnsw' + ? MergeWithExisting.hnsw(current.vectorIndexConfig, update.vectorizers.vectorIndex.config) + : MergeWithExisting.flat(current.vectorIndexConfig, update.vectorizers.vectorIndex.config); + } + } + return current; + } + + static invertedIndex( + current: WeaviateInvertedIndexConfig, + update?: InvertedIndexConfigUpdate + ): WeaviateInvertedIndexConfig { + if (current === undefined) throw Error('Inverted index config is missing from the class schema.'); + if (update === undefined) return current; + const { bm25, stopwords, ...rest } = update; + const merged = { ...current, ...rest }; + if (bm25 !== undefined) merged.bm25 = { ...current.bm25!, ...bm25 }; + if (stopwords !== undefined) merged.stopwords = { ...current.stopwords!, ...stopwords }; + return merged; + } + + static replication( + current: WeaviateReplicationConfig, + update?: ReplicationConfigUpdate + ): WeaviateReplicationConfig { + if (current === undefined) throw Error('Replication config is missing from the class schema.'); + if (update === undefined) return current; + return { ...current, ...update }; + } + + static vectors( + current: WeaviateVectorsConfig, + update?: VectorConfigUpdate[] + ): WeaviateVectorsConfig { + if (current === undefined) throw Error('Vector index config is missing from the class schema.'); + if (update === undefined) return current; + update.forEach((v) => { + const existing = current[v.name]; + if (existing !== undefined) { + current[v.name].vectorIndexConfig = + v.vectorIndex.name === 'hnsw' + ? MergeWithExisting.hnsw(existing.vectorIndexConfig, v.vectorIndex.config) + : MergeWithExisting.flat(existing.vectorIndexConfig, v.vectorIndex.config); + } + }); + return current; + } + + static flat( + current: WeaviateVectorIndexConfig, + update?: VectorIndexConfigFlatUpdate + ): WeaviateVectorIndexConfig { + if (update === undefined) return current; + if ( + (QuantizerGuards.isPQUpdate(update.quantizer) && (current?.bq as any).enabled) || + (QuantizerGuards.isBQUpdate(update.quantizer) && (current?.pq as any).enabled) + ) + throw Error(`Cannot update the quantizer type of an enabled vector index.`); + const { quantizer, ...rest } = update; + const merged: WeaviateVectorIndexConfig = { ...current, ...rest }; + if (QuantizerGuards.isBQUpdate(quantizer)) { + const { type, ...quant } = quantizer; + merged.bq = { ...current!.bq!, ...quant, enabled: true }; + } + return merged; + } + + static hnsw( + current: WeaviateVectorIndexConfig, + update?: VectorIndexConfigHNSWUpdate + ): WeaviateVectorIndexConfig { + if (update === undefined) return current; + if ( + (QuantizerGuards.isBQUpdate(update.quantizer) && ((current?.bq as any) || {}).enabled) || + (QuantizerGuards.isPQUpdate(update.quantizer) && ((current?.pq as any) || {}).enabled) + ) + throw Error(`Cannot update the quantizer type of an enabled vector index.`); + const { quantizer, ...rest } = update; + const merged: WeaviateVectorIndexConfig = { ...current, ...rest }; + if (QuantizerGuards.isBQUpdate(quantizer)) { + const { type, ...quant } = quantizer; + merged.bq = { ...current!.bq!, ...quant, enabled: true }; + } + if (QuantizerGuards.isPQUpdate(quantizer)) { + const { type, ...quant } = quantizer; + merged.pq = { ...current!.pq!, ...quant, enabled: true }; + } + return merged; + } +} diff --git a/src/collections/config/index.ts b/src/collections/config/index.ts new file mode 100644 index 00000000..e7065224 --- /dev/null +++ b/src/collections/config/index.ts @@ -0,0 +1,177 @@ +import Connection from '../../connection/index.js'; +import { WeaviateDeserializationError } from '../../errors.js'; +import { WeaviateShardStatus } from '../../openapi/types.js'; +import ClassUpdater from '../../schema/classUpdater.js'; +import { ClassGetter, PropertyCreator, ShardUpdater } from '../../schema/index.js'; +import ShardsGetter from '../../schema/shardsGetter.js'; +import { DbVersionSupport } from '../../utils/dbVersion.js'; +import { + PropertyConfigCreate, + ReferenceMultiTargetConfigCreate, + ReferenceSingleTargetConfigCreate, +} from '../configure/types/index.js'; +import { MergeWithExisting } from './classes.js'; +import { + BQConfig, + CollectionConfig, + CollectionConfigUpdate, + PQConfig, + VectorIndexConfig, + VectorIndexConfigDynamic, + VectorIndexConfigFlat, + VectorIndexConfigHNSW, +} from './types/index.js'; +import { classToCollection, resolveProperty, resolveReference } from './utils.js'; + +const config = ( + connection: Connection, + name: string, + dbVersionSupport: DbVersionSupport, + tenant?: string +): Config => { + const getRaw = new ClassGetter(connection).withClassName(name).do; + return { + addProperty: (property: PropertyConfigCreate) => + new PropertyCreator(connection) + .withClassName(name) + .withProperty(resolveProperty(property, [])) + .do() + .then(() => {}), + addReference: ( + reference: ReferenceSingleTargetConfigCreate | ReferenceMultiTargetConfigCreate + ) => + new PropertyCreator(connection) + .withClassName(name) + .withProperty(resolveReference(reference)) + .do() + .then(() => {}), + get: () => getRaw().then(classToCollection), + getShards: () => { + let builder = new ShardsGetter(connection).withClassName(name); + if (tenant) { + builder = builder.withTenant(tenant); + } + return builder.do().then((shards) => + shards.map((shard) => { + if (shard.name === undefined) + throw new WeaviateDeserializationError('Shard name was not returned by Weaviate'); + if (shard.status === undefined) + throw new WeaviateDeserializationError('Shard status was not returned by Weaviate'); + if (shard.vectorQueueSize === undefined) + throw new WeaviateDeserializationError('Shard vector queue size was not returned by Weaviate'); + return { name: shard.name, status: shard.status, vectorQueueSize: shard.vectorQueueSize }; + }) + ); + }, + updateShards: async function (status: 'READY' | 'READONLY', names?: string | string[]) { + let shardNames: string[]; + if (names === undefined) { + shardNames = await this.getShards().then((shards) => shards.map((s) => s.name)); + } else if (typeof names === 'string') { + shardNames = [names]; + } else { + shardNames = names; + } + return Promise.all( + shardNames.map((shardName) => + new ShardUpdater(connection).withClassName(name).withShardName(shardName).withStatus(status).do() + ) + ).then(() => this.getShards()); + }, + update: (config?: CollectionConfigUpdate) => { + return getRaw() + .then(async (current) => + MergeWithExisting.schema( + current, + await dbVersionSupport.supportsNamedVectors().then((s) => s.supports), + config + ) + ) + .then((merged) => new ClassUpdater(connection).withClass(merged).do()) + .then(() => {}); + }, + }; +}; + +export default config; + +export interface Config { + /** + * Add a property to the collection in Weaviate. + * + * @param {PropertyConfigCreate} property The property configuration. + * @returns {Promise} A promise that resolves when the property has been added. + */ + addProperty: (property: PropertyConfigCreate) => Promise; + /** + * Add a reference to the collection in Weaviate. + * + * @param {ReferenceSingleTargetConfigCreate | ReferenceMultiTargetConfigCreate} reference The reference configuration. + * @returns {Promise} A promise that resolves when the reference has been added. + */ + addReference: ( + reference: ReferenceSingleTargetConfigCreate | ReferenceMultiTargetConfigCreate + ) => Promise; + /** + * Get the configuration for this collection from Weaviate. + * + * @returns {Promise>} A promise that resolves with the collection configuration. + */ + get: () => Promise; + /** + * Get the statuses of the shards of this collection. + * + * If the collection is multi-tenancy and you did not call `.with_tenant` then you + * will receive the statuses of all the tenants within the collection. Otherwise, call + * `.with_tenant` on the collection first and you will receive only that single shard. + * + * @returns {Promise[]>} A promise that resolves with the shard statuses. + */ + getShards: () => Promise[]>; + /** + * Update the status of one or all shards of this collection. + * + * @param {'READY' | 'READONLY'} status The new status of the shard(s). + * @param {string | string[]} [names] The name(s) of the shard(s) to update. If not provided, all shards will be updated. + * @returns {Promise[]>} A promise that resolves with the updated shard statuses. + */ + updateShards: ( + status: 'READY' | 'READONLY', + names?: string | string[] + ) => Promise[]>; + /** + * Update the configuration for this collection in Weaviate. + * + * Use the `weaviate.classes.Reconfigure` class to generate the necessary configuration objects for this method. + * + * @param {CollectionConfigUpdate} [config] The configuration to update. Only a subset of the actual collection configuration can be updated. + * @returns {Promise} A promise that resolves when the collection has been updated. + */ + update: (config?: CollectionConfigUpdate) => Promise; +} + +export class VectorIndex { + static isHNSW(config?: VectorIndexConfig): config is VectorIndexConfigHNSW { + return config?.type === 'hnsw'; + } + static isFlat(config?: VectorIndexConfig): config is VectorIndexConfigFlat { + return config?.type === 'flat'; + } + static isDynamic(config?: VectorIndexConfig): config is VectorIndexConfigDynamic { + return config?.type === 'dynamic'; + } +} + +export class Quantizer { + static isPQ(config?: PQConfig | BQConfig): config is PQConfig { + return config?.type === 'pq'; + } + static isBQ(config?: PQConfig | BQConfig): config is BQConfig { + return config?.type === 'bq'; + } +} + +export const configGuards = { + quantizer: Quantizer, + vectorIndex: VectorIndex, +}; diff --git a/src/collections/config/integration.test.ts b/src/collections/config/integration.test.ts new file mode 100644 index 00000000..1df6a634 --- /dev/null +++ b/src/collections/config/integration.test.ts @@ -0,0 +1,592 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { WeaviateUnsupportedFeatureError } from '../../errors.js'; +import weaviate, { WeaviateClient } from '../../index.js'; +import { PropertyConfig, VectorIndexConfigDynamic, VectorIndexConfigHNSW } from './types/index.js'; + +const fail = (msg: string) => { + throw new Error(msg); +}; + +describe('Testing of the collection.config namespace', () => { + let client: WeaviateClient; + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + }); + + afterAll(() => client.collections.deleteAll()); + + it('should be able get the config of a collection without generics', async () => { + const collectionName = 'TestCollectionConfigGetWithGenerics'; + type TestCollectionConfigGet = { + testProp: string; + }; + await client.collections.create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.none(), + }); + const collection = client.collections.get(collectionName); + const config = await collection.config.get(); + + expect(config.name).toEqual(collectionName); + expect(config.properties).toEqual([ + { + name: 'testProp', + dataType: 'text', + description: undefined, + indexSearchable: true, + indexFilterable: true, + indexInverted: false, + vectorizerConfig: undefined, + nestedProperties: undefined, + tokenization: 'word', + }, + ]); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(config.vectorizers.default.indexConfig).toEqual({ + skip: false, + cleanupIntervalSeconds: 300, + maxConnections: 64, + efConstruction: 128, + ef: -1, + dynamicEfMin: 100, + dynamicEfMax: 500, + dynamicEfFactor: 8, + vectorCacheMaxObjects: 1000000000000, + flatSearchCutoff: 40000, + distance: 'cosine', + quantizer: undefined, + type: 'hnsw', + }); + expect(config.vectorizers.default.indexType).toEqual('hnsw'); + expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + }); + + it('should be able get the config of a collection with generics', async () => { + const collectionName = 'TestCollectionConfigGetWithoutGenerics'; + type TestCollectionConfigGet = { + testProp: string; + }; + await client.collections.create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.none(), + }); + const collection = client.collections.get(collectionName); + const config = await collection.config.get(); + + expect(config.name).toEqual(collectionName); + expect(config.properties).toEqual([ + { + name: 'testProp', + dataType: 'text', + description: undefined, + indexSearchable: true, + indexFilterable: true, + indexInverted: false, + vectorizerConfig: undefined, + nestedProperties: undefined, + tokenization: 'word', + }, + ]); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(config.vectorizers.default.indexConfig).toEqual({ + skip: false, + cleanupIntervalSeconds: 300, + maxConnections: 64, + efConstruction: 128, + ef: -1, + dynamicEfMin: 100, + dynamicEfMax: 500, + dynamicEfFactor: 8, + vectorCacheMaxObjects: 1000000000000, + flatSearchCutoff: 40000, + distance: 'cosine', + quantizer: undefined, + type: 'hnsw', + }); + expect(config.vectorizers.default.indexType).toEqual('hnsw'); + expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + }); + + it('should be able to get a collection with named vectors', async () => { + const collectionName = 'TestCollectionConfigGetVectors'; + const query = () => + client.collections.create({ + name: collectionName, + properties: [ + { + name: 'title', + dataType: 'text', + skipVectorization: true, + vectorizePropertyName: false, + }, + { + name: 'age', + dataType: 'int', + }, + ], + vectorizers: [ + weaviate.configure.vectorizer.text2VecContextionary({ + name: 'title', + sourceProperties: ['title'], + }), + weaviate.configure.vectorizer.text2VecContextionary({ + name: 'age', + sourceProperties: ['age'], + }), + ], + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const config = await query().then((col) => col.config.get()); + + expect(config.name).toEqual(collectionName); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(config.properties[0].vectorizerConfig?.['text2vec-contextionary'].vectorizePropertyName).toEqual( + false + ); + expect(config.properties[0].vectorizerConfig?.['text2vec-contextionary'].skip).toEqual(true); + expect(config.properties[1].vectorizerConfig?.['text2vec-contextionary'].vectorizePropertyName).toEqual( + true + ); + expect(config.properties[1].vectorizerConfig?.['text2vec-contextionary'].skip).toEqual(false); + expect(config.vectorizers.title.indexConfig).toBeDefined(); + expect(config.vectorizers.title.indexType).toEqual('hnsw'); + expect(config.vectorizers.title.properties).toEqual(['title']); + expect(config.vectorizers.title.vectorizer.name).toEqual('text2vec-contextionary'); + }); + + it('should be able to get the config of a collection with HNSW+PQ', async () => { + const collectionName = 'TestCollectionConfigGetHNSWPlusPQ'; + const collection = await client.collections.create({ + name: collectionName, + vectorizers: weaviate.configure.vectorizer.none({ + vectorIndexConfig: weaviate.configure.vectorIndex.hnsw({ + quantizer: weaviate.configure.vectorIndex.quantizer.pq(), + }), + }), + }); + const config = await collection.config.get(); + + const vectorIndexConfig = config.vectorizers.default.indexConfig as VectorIndexConfigHNSW; + expect(config.name).toEqual(collectionName); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(vectorIndexConfig).toBeDefined(); + expect(vectorIndexConfig.quantizer).toBeDefined(); + expect(config.vectorizers.default.indexType).toEqual('hnsw'); + expect(config.vectorizers.default.properties).toBeUndefined(); + expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + }); + + it('should be able to get the config of a collection with HNSW+BQ', async () => { + const collectionName = 'TestCollectionConfigGetHNSWPlusBQ'; + const query = () => + client.collections.create({ + name: collectionName, + vectorizers: weaviate.configure.vectorizer.none({ + vectorIndexConfig: weaviate.configure.vectorIndex.hnsw({ + quantizer: weaviate.configure.vectorIndex.quantizer.bq(), + }), + }), + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const config = await query().then((col) => col.config.get()); + + const vectorIndexConfig = config.vectorizers.default.indexConfig as VectorIndexConfigHNSW; + expect(config.name).toEqual(collectionName); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(vectorIndexConfig).toBeDefined(); + expect(vectorIndexConfig.quantizer).toBeDefined(); + expect(config.vectorizers.default.indexType).toEqual('hnsw'); + expect(config.vectorizers.default.properties).toBeUndefined(); + expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + }); + + it('should be able to get the config of a collection with flat+BQ', async () => { + const collectionName = 'TestCollectionConfigGetFlatPlusBQ'; + const collection = await client.collections.create({ + name: collectionName, + vectorizers: weaviate.configure.vectorizer.none({ + vectorIndexConfig: weaviate.configure.vectorIndex.flat({ + quantizer: weaviate.configure.vectorIndex.quantizer.bq(), + }), + }), + }); + const config = await collection.config.get(); + + const vectorIndexConfig = config.vectorizers.default.indexConfig as VectorIndexConfigHNSW; + expect(config.name).toEqual(collectionName); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(vectorIndexConfig).toBeDefined(); + expect(vectorIndexConfig.quantizer).toBeDefined(); + expect(config.vectorizers.default.indexType).toEqual('flat'); + expect(config.vectorizers.default.properties).toBeUndefined(); + expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + }); + + it('should be able to get the config of a single-vector collection with dynamic+BQ', async () => { + const asyncIndexing = await weaviate.connectToLocal({ port: 8078, grpcPort: 50049 }); // need async indexing for dynamic vectorizer + const collectionName = 'TestSVCollectionConfigGetDynamicPlusBQ'; + await asyncIndexing.collections.delete(collectionName); + const query = () => + asyncIndexing.collections.create({ + name: collectionName, + vectorizers: weaviate.configure.vectorizer.none({ + vectorIndexConfig: weaviate.configure.vectorIndex.dynamic({ + hnsw: weaviate.configure.vectorIndex.hnsw({ + quantizer: weaviate.configure.vectorIndex.quantizer.pq(), + }), + flat: weaviate.configure.vectorIndex.flat({ + quantizer: weaviate.configure.vectorIndex.quantizer.bq(), + }), + }), + }), + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const config = await query().then((collection) => collection.config.get()); + + const vectorIndexConfig = config.vectorizers.default.indexConfig as VectorIndexConfigDynamic; + expect(config.name).toEqual(collectionName); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(vectorIndexConfig).toBeDefined(); + expect((vectorIndexConfig as any).quantizer).toBeUndefined(); + expect(vectorIndexConfig.hnsw).toBeDefined(); + expect(vectorIndexConfig.hnsw.quantizer).toBeDefined(); + expect(vectorIndexConfig.flat).toBeDefined(); + expect(vectorIndexConfig.flat.quantizer).toBeDefined(); + expect(config.vectorizers.default.indexType).toEqual('dynamic'); + expect(config.vectorizers.default.properties).toBeUndefined(); + expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + }); + + it('should be able to get the config of a multi-vector collection with dynamic+BQ', async () => { + const asyncIndexing = await weaviate.connectToLocal({ port: 8078, grpcPort: 50049 }); // need async indexing for dynamic vectorizer + const collectionName = 'TestMVCollectionConfigGetDynamicPlusBQ'; + await asyncIndexing.collections.delete(collectionName); + const query = () => + asyncIndexing.collections.create({ + name: collectionName, + vectorizers: weaviate.configure.vectorizer.none({ + vectorIndexConfig: weaviate.configure.vectorIndex.dynamic({ + hnsw: weaviate.configure.vectorIndex.hnsw({ + quantizer: weaviate.configure.vectorIndex.quantizer.pq(), + }), + flat: weaviate.configure.vectorIndex.flat({ + quantizer: weaviate.configure.vectorIndex.quantizer.bq(), + }), + }), + }), + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const config = await query().then((collection) => collection.config.get()); + + const vectorIndexConfig = config.vectorizers.default.indexConfig as VectorIndexConfigDynamic; + expect(config.name).toEqual(collectionName); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(vectorIndexConfig).toBeDefined(); + expect((vectorIndexConfig as any).quantizer).toBeUndefined(); + expect(vectorIndexConfig.hnsw).toBeDefined(); + expect(vectorIndexConfig.hnsw.quantizer).toBeDefined(); + expect(vectorIndexConfig.flat).toBeDefined(); + expect(vectorIndexConfig.flat.quantizer).toBeDefined(); + expect(config.vectorizers.default.indexType).toEqual('dynamic'); + expect(config.vectorizers.default.properties).toBeUndefined(); + expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + }); + + it('should be able to add a property to a collection', async () => { + const collectionName = 'TestCollectionConfigAddProperty'; + const collection = await client.collections.create({ + name: collectionName, + vectorizers: weaviate.configure.vectorizer.none(), + }); + const config = await collection.config + .addProperty({ + name: 'testProp', + dataType: 'text', + }) + .then(() => collection.config.get()); + expect(config.properties).toEqual([ + { + name: 'testProp', + dataType: 'text', + description: undefined, + indexSearchable: true, + indexFilterable: true, + indexInverted: false, + vectorizerConfig: undefined, + nestedProperties: undefined, + tokenization: 'word', + }, + ]); + }); + + it('should be able to add a reference to a collection', async () => { + const collectionName = 'TestCollectionConfigAddReference' as const; + const collection = await client.collections.create({ + name: collectionName, + vectorizers: weaviate.configure.vectorizer.none(), + }); + const config = await collection.config + .addReference({ + name: 'testProp', + targetCollection: collection.name, + }) + .then(() => collection.config.get()); + expect(config.references).toEqual([ + { + name: 'testProp', + targetCollections: [collectionName], + description: undefined, + }, + ]); + }); + + it('should get the shards of a sharded collection', async () => { + const shards = await client.collections + .create({ + name: 'TestCollectionConfigGetShards', + sharding: { + desiredCount: 2, + }, + }) + .then((collection) => collection.config.getShards()); + + expect(shards.length).toEqual(2); + expect(shards[0].name).toBeDefined(); + expect(shards[0].status).toEqual('READY'); + expect(shards[0].vectorQueueSize).toEqual(0); + expect(shards[1].name).toBeDefined(); + expect(shards[1].status).toEqual('READY'); + expect(shards[1].vectorQueueSize).toEqual(0); + }); + + it('should update all the shard statuses of a sharded collection', async () => { + const shards = await client.collections + .create({ + name: 'TestCollectionConfigUpdateAllShardStatuses', + sharding: { + desiredCount: 2, + }, + }) + .then((collection) => collection.config.updateShards('READONLY')); + + expect(shards.length).toEqual(2); + expect(shards[0].name).toBeDefined(); + expect(shards[0].status).toEqual('READONLY'); + expect(shards[0].vectorQueueSize).toEqual(0); + expect(shards[1].name).toBeDefined(); + expect(shards[1].status).toEqual('READONLY'); + expect(shards[1].vectorQueueSize).toEqual(0); + }); + + it('should update all the shard statuses of a sharded collection', async () => { + const { shards, shard } = await client.collections + .create({ + name: 'TestCollectionConfigUpdateOneShardStatus', + sharding: { + desiredCount: 2, + }, + }) + .then(async (collection) => { + return { collection, shard: await collection.config.getShards().then((shards) => shards[0].name) }; + }) + .then(async ({ collection, shard }) => { + return { shard, shards: await collection.config.updateShards('READONLY', shard) }; + }); + + expect(shards.length).toEqual(2); + const updated = shards.find((s) => s.name === shard); + const notUpdated = shards.find((s) => s.name !== shard); + expect(updated?.status).toEqual('READONLY'); + expect(notUpdated?.status).toEqual('READY'); + }); + + it('should be able update the config of a collection', async () => { + const collectionName = 'TestCollectionConfigUpdate'; + const collection = await client.collections.create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.none(), + }); + const config = await collection.config + .update({ + vectorizers: weaviate.reconfigure.vectorizer.update({ + vectorIndexConfig: weaviate.reconfigure.vectorIndex.hnsw({ + quantizer: weaviate.reconfigure.vectorIndex.quantizer.pq(), + ef: 4, + }), + }), + }) + .then(() => collection.config.get()); + + expect(config.name).toEqual(collectionName); + expect(config.properties).toEqual([ + { + name: 'testProp', + dataType: 'text', + description: undefined, + indexSearchable: true, + indexFilterable: true, + indexInverted: false, + vectorizerConfig: undefined, + nestedProperties: undefined, + tokenization: 'word', + }, + ]); + expect(config.generative).toBeUndefined(); + expect(config.reranker).toBeUndefined(); + expect(config.vectorizers.default.indexConfig).toEqual({ + skip: false, + cleanupIntervalSeconds: 300, + maxConnections: 64, + efConstruction: 128, + ef: 4, + dynamicEfMin: 100, + dynamicEfMax: 500, + dynamicEfFactor: 8, + vectorCacheMaxObjects: 1000000000000, + flatSearchCutoff: 40000, + distance: 'cosine', + quantizer: { + bitCompression: false, + segments: 0, + centroids: 256, + trainingLimit: 100000, + encoder: { + type: 'kmeans', + distribution: 'log-normal', + }, + type: 'pq', + }, + type: 'hnsw', + }); + expect(config.vectorizers.default.indexType).toEqual('hnsw'); + expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + }); + + it('should be able to create and get a collection with multi-tenancy enabled', async () => { + const collectionName = 'TestCollectionConfigMultiTenancy'; + const collection = await client.collections.create({ + name: collectionName, + multiTenancy: weaviate.configure.multiTenancy({ + autoTenantActivation: true, + autoTenantCreation: true, + }), + }); + const config = await collection.config.get(); + + expect(config.name).toEqual(collectionName); + expect(config.multiTenancy.autoTenantActivation).toEqual( + await client.getWeaviateVersion().then((ver) => !ver.isLowerThan(1, 25, 2)) + ); + expect(config.multiTenancy.autoTenantCreation).toEqual( + await client.getWeaviateVersion().then((ver) => !ver.isLowerThan(1, 25, 0)) + ); + expect(config.multiTenancy.enabled).toEqual(true); + }); + + // it('should be able update the config of a collection with legacy vectors', async () => { + // const collectionName = 'TestCollectionConfigUpdateLegacyVectors'; + // const collection = await client.collections.create({ + // name: collectionName, + // properties: [ + // { + // name: 'testProp', + // dataType: 'text', + // }, + // ], + // vectorizer: { + // name: 'none', + // config: {}, + // }, + // }); + // const config = await collection.config + // .update({ + // vectorizers: weaviate.reconfigure.vectorIndex.hnsw({ + // quantizer: weaviate.reconfigure.vectorIndex.quantizer.pq(), + // ef: 4, + // }), + // }) + // .then(() => collection.config.get()); + + // expect(config.name).toEqual(collectionName); + // expect(config.properties).toEqual([ + // { + // name: 'testProp', + // dataType: 'text', + // description: undefined, + // indexSearchable: true, + // indexFilterable: true, + // indexInverted: false, + // vectorizerConfig: undefined, + // nestedProperties: undefined, + // tokenization: 'word', + // }, + // ]); + // expect(config.generative).toBeUndefined(); + // expect(config.reranker).toBeUndefined(); + // expect(config.vectorizers.default.indexConfig).toEqual({ + // skip: false, + // cleanupIntervalSeconds: 300, + // maxConnections: 64, + // efConstruction: 128, + // ef: 4, + // dynamicEfMin: 100, + // dynamicEfMax: 500, + // dynamicEfFactor: 8, + // vectorCacheMaxObjects: 1000000000000, + // flatSearchCutoff: 40000, + // distance: 'cosine', + // quantizer: { + // bitCompression: false, + // segments: 0, + // centroids: 256, + // trainingLimit: 100000, + // encoder: { + // type: 'kmeans', + // distribution: 'log-normal', + // }, + // type: 'pq', + // }, + // }); + // expect(config.vectorizers.default.indexType).toEqual('hnsw'); + // expect(config.vectorizers.default.vectorizer.name).toEqual('none'); + // }); +}); diff --git a/src/collections/config/types/generative.ts b/src/collections/config/types/generative.ts new file mode 100644 index 00000000..ed1e69b4 --- /dev/null +++ b/src/collections/config/types/generative.ts @@ -0,0 +1,95 @@ +export type GenerativeOpenAIConfigBase = { + baseURL?: string; + frequencyPenaltyProperty?: number; + maxTokensProperty?: number; + presencePenaltyProperty?: number; + temperatureProperty?: number; + topPProperty?: number; +}; + +export type GenerativeAWSConfig = { + region: string; + service: string; + model?: string; + endpoint?: string; +}; + +export type GenerativeAnyscaleConfig = { + model?: string; + temperature?: number; +}; + +export type GenerativeMistralConfig = { + maxTokens?: number; + model?: string; + temperature?: number; +}; + +export type GenerativeOctoAIConfig = { + baseURL?: string; + maxTokens?: number; + model?: string; + temperature?: number; +}; + +export type GenerativeOllamaConfig = { + apiEndpoint?: string; + model?: string; +}; + +export type GenerativeOpenAIConfig = GenerativeOpenAIConfigBase & { + model?: string; +}; + +export type GenerativeAzureOpenAIConfig = GenerativeOpenAIConfigBase & { + resourceName: string; + deploymentId: string; +}; + +export type GenerativeCohereConfig = { + kProperty?: number; + model?: string; + maxTokensProperty?: number; + returnLikelihoodsProperty?: string; + stopSequencesProperty?: string[]; + temperatureProperty?: number; +}; + +export type GenerativePaLMConfig = { + apiEndpoint?: string; + maxOutputTokens?: number; + modelId?: string; + projectId: string; + temperature?: number; + topK?: number; + topP?: number; +}; + +export type GenerativeConfig = + | GenerativeOpenAIConfig + | GenerativeCohereConfig + | GenerativePaLMConfig + | Record + | undefined; + +export type GenerativeConfigType = G extends 'generative-openai' + ? GenerativeOpenAIConfig + : G extends 'generative-cohere' + ? GenerativeCohereConfig + : G extends 'generative-palm' + ? GenerativePaLMConfig + : G extends 'none' + ? undefined + : Record | undefined; + +export type GenerativeSearch = + | 'generative-anyscale' + | 'generative-aws' + | 'generative-mistral' + | 'generative-octoai' + | 'generative-ollama' + | 'generative-openai' + | 'generative-cohere' + | 'generative-palm' + | 'none' + | string; diff --git a/src/collections/config/types/index.ts b/src/collections/config/types/index.ts new file mode 100644 index 00000000..0dacdd3d --- /dev/null +++ b/src/collections/config/types/index.ts @@ -0,0 +1,105 @@ +export * from './generative.js'; +export * from './reranker.js'; +export * from './vectorIndex.js'; +export * from './vectorizer.js'; + +import { + InvertedIndexConfigUpdate, + ReplicationConfigUpdate, + VectorConfigUpdate, +} from '../../configure/types/index.js'; +import { GenerativeConfig } from './generative.js'; +import { RerankerConfig } from './reranker.js'; +import { VectorIndexType } from './vectorIndex.js'; +import { VectorConfig } from './vectorizer.js'; + +export type ModuleConfig = { + name: N; + config: C; +}; + +export type InvertedIndexConfig = { + bm25: { + k1: number; + b: number; + }; + cleanupIntervalSeconds: number; + indexTimestamps: boolean; + indexPropertyLength: boolean; + indexNullState: boolean; + stopwords: { + preset: string; + additions: string[]; + removals: string[]; + }; +}; + +export type MultiTenancyConfig = { + autoTenantActivation: boolean; + autoTenantCreation: boolean; + enabled: boolean; +}; + +export type ReplicationConfig = { + factor: number; +}; + +export type PropertyVectorizerConfig = Record< + string, + { + skip: boolean; + vectorizePropertyName: boolean; + } +>; + +export type PropertyConfig = { + name: string; + dataType: string; + description?: string; + indexInverted: boolean; + indexFilterable: boolean; + indexSearchable: boolean; + nestedProperties?: PropertyConfig[]; + tokenization: string; + vectorizerConfig?: PropertyVectorizerConfig; +}; + +export type ReferenceConfig = { + name: string; + description?: string; + targetCollections: string[]; +}; + +export type ShardingConfig = { + virtualPerPhysical: number; + desiredCount: number; + actualCount: number; + desiredVirtualCount: number; + actualVirtualCount: number; + key: '_id'; + strategy: 'hash'; + function: 'murmur3'; +}; + +export type CollectionConfig = { + name: string; + description?: string; + generative?: GenerativeConfig; + invertedIndex: InvertedIndexConfig; + multiTenancy: MultiTenancyConfig; + properties: PropertyConfig[]; + references: ReferenceConfig[]; + replication: ReplicationConfig; + reranker?: RerankerConfig; + sharding: ShardingConfig; + vectorizers: VectorConfig; +}; + +export type CollectionConfigUpdate = { + description?: string; + invertedIndex?: InvertedIndexConfigUpdate; + replication?: ReplicationConfigUpdate; + vectorizers?: + | VectorConfigUpdate + | VectorConfigUpdate[]; +}; diff --git a/src/collections/config/types/reranker.ts b/src/collections/config/types/reranker.ts new file mode 100644 index 00000000..774fe685 --- /dev/null +++ b/src/collections/config/types/reranker.ts @@ -0,0 +1,28 @@ +export type RerankerTransformersConfig = {}; + +export type RerankerCohereConfig = { + model?: 'rerank-english-v2.0' | 'rerank-multilingual-v2.0' | string; +}; + +export type RerankerVoyageAIConfig = { + model?: 'rerank-lite-1' | string; +}; + +export type RerankerConfig = + | RerankerCohereConfig + | RerankerTransformersConfig + | RerankerVoyageAIConfig + | Record + | undefined; + +export type Reranker = 'reranker-cohere' | 'reranker-transformers' | 'reranker-voyageai' | 'none' | string; + +export type RerankerConfigType = R extends 'reranker-cohere' + ? RerankerCohereConfig + : R extends 'reranker-transformers' + ? RerankerTransformersConfig + : R extends 'reranker-voyageai' + ? RerankerVoyageAIConfig + : R extends 'none' + ? undefined + : Record | undefined; diff --git a/src/collections/config/types/vectorIndex.ts b/src/collections/config/types/vectorIndex.ts new file mode 100644 index 00000000..9622469e --- /dev/null +++ b/src/collections/config/types/vectorIndex.ts @@ -0,0 +1,69 @@ +export type VectorIndexConfigHNSW = { + cleanupIntervalSeconds: number; + distance: VectorDistance; + dynamicEfMin: number; + dynamicEfMax: number; + dynamicEfFactor: number; + efConstruction: number; + ef: number; + flatSearchCutoff: number; + maxConnections: number; + quantizer: PQConfig | BQConfig | undefined; + skip: boolean; + vectorCacheMaxObjects: number; + type: 'hnsw'; +}; + +export type VectorIndexConfigFlat = { + distance: VectorDistance; + vectorCacheMaxObjects: number; + quantizer: BQConfig | undefined; + type: 'flat'; +}; + +export type VectorIndexConfigDynamic = { + distance: VectorDistance; + threshold: number; + hnsw: VectorIndexConfigHNSW; + flat: VectorIndexConfigFlat; + type: 'dynamic'; +}; + +export type VectorIndexConfigType = I extends 'hnsw' + ? VectorIndexConfigHNSW + : I extends 'flat' + ? VectorIndexConfigFlat + : I extends 'dynamic' + ? VectorIndexConfigDynamic + : I extends string + ? Record + : never; + +export type BQConfig = { + cache: boolean; + rescoreLimit: number; + type: 'bq'; +}; + +export type PQConfig = { + bitCompression: boolean; + centroids: number; + encoder: PQEncoderConfig; + segments: number; + trainingLimit: number; + type: 'pq'; +}; + +export type PQEncoderConfig = { + type: PQEncoderType; + distribution: PQEncoderDistribution; +}; + +export type VectorDistance = 'cosine' | 'dot' | 'l2-squared' | 'hamming'; + +export type PQEncoderType = 'kmeans' | 'tile'; +export type PQEncoderDistribution = 'log-normal' | 'normal'; + +export type VectorIndexType = 'hnsw' | 'flat' | 'dynamic' | string; + +export type VectorIndexConfig = VectorIndexConfigHNSW | VectorIndexConfigFlat | VectorIndexConfigDynamic; diff --git a/src/collections/config/types/vectorizer.ts b/src/collections/config/types/vectorizer.ts new file mode 100644 index 00000000..8cbf0f4a --- /dev/null +++ b/src/collections/config/types/vectorizer.ts @@ -0,0 +1,397 @@ +import { ModuleConfig } from './index.js'; +import { VectorIndexConfig, VectorIndexType } from './vectorIndex.js'; + +export type VectorConfig = Record< + string, + { + properties?: string[]; + vectorizer: ModuleConfig | ModuleConfig; + indexConfig: VectorIndexConfig; + indexType: VectorIndexType; + } +>; + +export type Vectorizer = + | 'img2vec-neural' + | 'multi2vec-clip' + | 'multi2vec-bind' + | 'multi2vec-palm' + | 'ref2vec-centroid' + | 'text2vec-aws' + | 'text2vec-azure-openai' + | 'text2vec-cohere' + | 'text2vec-contextionary' + | 'text2vec-gpt4all' + | 'text2vec-huggingface' + | 'text2vec-jina' + | 'text2vec-ollama' + | 'text2vec-openai' + | 'text2vec-palm' + | 'text2vec-transformers' + | 'text2vec-voyageai' + | 'none'; + +/** The configuration for image vectorization using a neural network. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/img2vec-neural) for detailed usage. + */ +export type Img2VecNeuralConfig = { + /** The image fields used when vectorizing. This is a required field and must match the property fields of the collection that are defined as `DataType.BLOB`. */ + imageFields: string[]; +}; + +/** The field configuration for multi-media vectorization. */ +export type Multi2VecField = { + /** The name of the field to be used when performing multi-media vectorization. */ + name: string; + /** The weight of the field when performing multi-media vectorization. */ + weight?: number; +}; + +/** The configuration for multi-media vectorization using CLIP. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-clip) for detailed usage. + */ +export type Multi2VecClipConfig = { + /** The image fields used when vectorizing. */ + imageFields?: string[]; + /** The URL where inference requests are sent. */ + inferenceUrl?: string; + /** The text fields used when vectorizing. */ + textFields?: string[]; + /** Whether the collection name is vectorized. */ + vectorizeCollectionName?: boolean; + /** The weights of the fields used for vectorization. */ + weights?: { + /** The weights of the image fields. */ + imageFields?: number[]; + /** The weights of the text fields. */ + textFields?: number[]; + }; +}; + +/** The configuration for multi-media vectorization using Bind. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-bind) for detailed usage. + */ +export type Multi2VecBindConfig = { + /** The audio fields used when vectorizing. */ + audioFields?: string[]; + /** The depth fields used when vectorizing. */ + depthFields?: string[]; + /** The image fields used when vectorizing. */ + imageFields?: string[]; + /** The IMU fields used when vectorizing. */ + IMUFields?: string[]; + /** The text fields used when vectorizing. */ + textFields?: string[]; + /** The thermal fields used when vectorizing. */ + thermalFields?: string[]; + /** The video fields used when vectorizing. */ + videoFields?: string[]; + /** Whether the collection name is vectorized. */ + vectorizeCollectionName?: boolean; + /** The weights of the fields used for vectorization. */ + weights?: { + /** The weights of the audio fields. */ + audioFields?: number[]; + /** The weights of the depth fields. */ + depthFields?: number[]; + /** The weights of the image fields. */ + imageFields?: number[]; + /** The weights of the IMU fields. */ + IMUFields?: number[]; + /** The weights of the text fields. */ + textFields?: number[]; + /** The weights of the thermal fields. */ + thermalFields?: number[]; + /** The weights of the video fields. */ + videoFields?: number[]; + }; +}; + +/** The configuration for multi-media vectorization using Palm. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-palm) for detailed usage. + */ +export type Multi2VecPalmConfig = { + /** The project ID of the Palm model. */ + projectId: string; + /** The location where the model runs. */ + location: string; + /** The image fields used when vectorizing. */ + imageFields?: string[]; + /** The text fields used when vectorizing. */ + textFields?: string[]; + /** The video fields used when vectorizing. */ + videoFields?: string[]; + /** The model ID in use. */ + modelId?: string; + /** The number of dimensions in use. */ + dimensions?: number; + /** Whether the collection name is vectorized. */ + vectorizeCollectionName?: boolean; + /** The weights of the fields used for vectorization. */ + weights?: { + /** The weights of the image fields. */ + imageFields?: number[]; + /** The weights of the text fields. */ + textFields?: number[]; + /** The weights of the video fields. */ + videoFields?: number[]; + }; +}; + +/** The configuration for reference-based vectorization using centroids. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/ref2vec-centroid) for detailed usage. + */ +export type Ref2VecCentroidConfig = { + /** The properties used as reference points for vectorization. */ + referenceProperties: string[]; + /** The method used to calculate the centroid. */ + method: 'mean' | string; +}; + +/** The configuration for text vectorization using AWS. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-aws) for detailed usage. + */ +export type Text2VecAWSConfig = { + /** The model to use. REQUIRED for service `sagemaker`. */ + endpoint?: string; + /** The model to use. REQUIRED for service `bedrock`. */ + model?: 'amazon.titan-embed-text-v1' | 'cohere.embed-english-v3' | 'cohere.embed-multilingual-v3' | string; + /** The AWS region where the model runs. */ + region: string; + /** The AWS service to use. */ + service: 'sagemaker' | 'bedrock' | string; + /** Whether the collection name is vectorized. */ + vectorizeCollectionName?: boolean; +}; + +/** The configuration for text vectorization using Azure OpenAI. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-azure-openai) for detailed usage. + */ +export type Text2VecAzureOpenAIConfig = { + /** The base URL to use where API requests should go. */ + baseURL?: string; + /** The deployment ID to use */ + deploymentID: string; + /** The resource name to use. */ + resourceName: string; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** The configuration for text vectorization using Cohere. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-cohere) for detailed usage. + */ +export type Text2VecCohereConfig = { + /** The base URL to use where API requests should go. */ + baseURL?: string; + /** The model to use. */ + model?: string; + /** The truncation strategy to use. */ + truncate?: boolean; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** The configuration for text vectorization using Contextionary. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-contextionary) for detailed usage. + */ +export type Text2VecContextionaryConfig = { + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** The configuration for text vectorization using GPT4All. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-gpt4all) for detailed usage. + */ +export type Text2VecGPT4AllConfig = { + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** + * The configuration for text vectorization using Hugging Face. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-huggingface) for detailed usage. + */ +export type Text2VecHuggingFaceConfig = { + /** The endpoint URL to use. */ + endpointURL?: string; + /** The model to use. */ + model?: string; + /** The model to use for passage vectorization. */ + passageModel?: string; + /** The model to use for query vectorization. */ + queryModel?: string; + /** Whether to use the cache. */ + useCache?: boolean; + /** Whether to use the GPU. */ + useGPU?: boolean; + /** Whether to wait for the model. */ + waitForModel?: boolean; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** + * The configuration for text vectorization using Jina. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-jina) for detailed usage. + */ +export type Text2VecJinaConfig = { + /** The model to use. */ + model?: 'jina-embeddings-v2-base-en' | 'jina-embeddings-v2-small-en' | string; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** + * The configuration for text vectorization using Ollama. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-ollama) for detailed usage. + */ +export type Text2VecOllamaConfig = { + /** The base URL to use where API requests should go. */ + apiEndpoint?: string; + /** The model to use. */ + model?: string; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** + * The configuration for text vectorization using OpenAI. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-openai) for detailed usage. + */ +export type Text2VecOpenAIConfig = { + /** The base URL to use where API requests should go. */ + baseURL?: string; + /** The dimensions to use. */ + dimensions?: number; + /** The model to use. */ + model?: 'text-embedding-3-small' | 'text-embedding-3-large' | 'text-embedding-ada-002' | string; + /** The model version to use. */ + modelVersion?: string; + /** The type of model to use. */ + type?: 'text' | 'code' | string; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** + * The configuration for text vectorization using Palm. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-palm) for detailed usage. + */ +export type Text2VecPalmConfig = { + /** The API endpoint to use without a leading scheme such as `http://`. */ + apiEndpoint?: string; + /** The model ID to use. */ + modelId?: string; + /** The project ID to use. */ + projectId?: string; + /** The Weaviate property name for the `gecko-002` or `gecko-003` model to use as the title. */ + titleProperty?: string; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +export type Text2VecTransformersConfig = { + /** The inference url to use where API requests should go. You can use either this OR (`passage_inference_url` & `query_inference_url`). */ + inferenceUrl?: string; + /** The inference url to use where passage API requests should go. You can use either (this AND query_inference_url) OR `inference_url`. */ + passageInferenceUrl?: string; + /** The inference url to use where query API requests should go. You can use either (this AND `passage_inference_url`) OR `inference_url`. */ + queryInferenceUrl?: string; + /** The pooling strategy to use. */ + poolingStrategy?: 'masked_mean' | 'cls' | string; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** + * The configuration for text vectorization using Voyage AI. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-voyageai) for detailed usage. + */ +export type Text2VecVoyageAIConfig = { + /** The base URL to use where API requests should go. */ + baseURL?: string; + /** The model to use. */ + model?: string; + /** Whether to truncate the input texts to fit within the context length. */ + truncate?: boolean; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +export type NoVectorizerConfig = {}; + +export type VectorizerConfig = + | Img2VecNeuralConfig + | Multi2VecClipConfig + | Multi2VecBindConfig + | Multi2VecPalmConfig + | Ref2VecCentroidConfig + | Text2VecAWSConfig + | Text2VecAzureOpenAIConfig + | Text2VecContextionaryConfig + | Text2VecCohereConfig + | Text2VecGPT4AllConfig + | Text2VecHuggingFaceConfig + | Text2VecJinaConfig + | Text2VecOpenAIConfig + | Text2VecPalmConfig + | Text2VecTransformersConfig + | Text2VecVoyageAIConfig + | NoVectorizerConfig; + +export type VectorizerConfigType = V extends 'img2vec-neural' + ? Img2VecNeuralConfig | undefined + : V extends 'multi2vec-clip' + ? Multi2VecClipConfig | undefined + : V extends 'multi2vec-bind' + ? Multi2VecBindConfig | undefined + : V extends 'multi2vec-palm' + ? Multi2VecPalmConfig + : V extends 'ref2vec-centroid' + ? Ref2VecCentroidConfig + : V extends 'text2vec-aws' + ? Text2VecAWSConfig + : V extends 'text2vec-contextionary' + ? Text2VecContextionaryConfig | undefined + : V extends 'text2vec-cohere' + ? Text2VecCohereConfig | undefined + : V extends 'text2vec-gpt4all' + ? Text2VecGPT4AllConfig | undefined + : V extends 'text2vec-huggingface' + ? Text2VecHuggingFaceConfig | undefined + : V extends 'text2vec-jina' + ? Text2VecJinaConfig | undefined + : V extends 'text2vec-ollama' + ? Text2VecOllamaConfig | undefined + : V extends 'text2vec-openai' + ? Text2VecOpenAIConfig | undefined + : V extends 'text2vec-azure-openai' + ? Text2VecAzureOpenAIConfig + : V extends 'text2vec-palm' + ? Text2VecPalmConfig | undefined + : V extends 'text2vec-transformers' + ? Text2VecTransformersConfig | undefined + : V extends 'text2vec-voyageai' + ? Text2VecVoyageAIConfig | undefined + : V extends 'none' + ? {} + : V extends undefined + ? undefined + : never; diff --git a/src/collections/config/unit.test.ts b/src/collections/config/unit.test.ts new file mode 100644 index 00000000..d7cd4a47 --- /dev/null +++ b/src/collections/config/unit.test.ts @@ -0,0 +1,330 @@ +import { WeaviateInvertedIndexConfig, WeaviateVectorsConfig } from '../../openapi/types'; +import { MergeWithExisting } from './classes'; + +describe('Unit testing of the MergeWithExisting class', () => { + const invertedIndex: WeaviateInvertedIndexConfig = { + bm25: { + b: 0.8, + k1: 1.3, + }, + cleanupIntervalSeconds: 61, + indexPropertyLength: true, + indexTimestamps: true, + stopwords: { + preset: 'en', + }, + }; + + const hnswVectorConfig: WeaviateVectorsConfig = { + name: { + vectorIndexConfig: { + skip: false, + cleanupIntervalSeconds: 300, + maxConnections: 64, + efConstruction: 128, + ef: -1, + dynamicEfMin: 100, + dynamicEfMax: 500, + dynamicEfFactor: 8, + vectorCacheMaxObjects: 1000000000000, + flatSearchCutoff: 40000, + distance: 'cosine', + pq: { + enabled: false, + bitCompression: false, + segments: 0, + centroids: 256, + trainingLimit: 100000, + encoder: { + type: 'kmeans', + distribution: 'log-normal', + }, + }, + bq: { + enabled: false, + }, + }, + vectorIndexType: 'hnsw', + vectorizer: { + 'text2vec-contextionary': { + properties: ['name'], + vectorizeCollectionName: false, + }, + }, + }, + }; + + it('should merge a full invertedIndexUpdate with existing schema', () => { + const merged = MergeWithExisting.invertedIndex(Object.assign({}, invertedIndex), { + bm25: { + b: 0.9, + k1: 1.4, + }, + cleanupIntervalSeconds: 62, + stopwords: { + additions: ['foo', 'bar'], + preset: 'none', + removals: ['baz'], + }, + }); + expect(merged).toEqual({ + bm25: { + b: 0.9, + k1: 1.4, + }, + cleanupIntervalSeconds: 62, + indexPropertyLength: true, + indexTimestamps: true, + stopwords: { + preset: 'none', + additions: ['foo', 'bar'], + removals: ['baz'], + }, + }); + }); + + const flatVectorConfig: WeaviateVectorsConfig = { + name: { + vectorIndexConfig: { + distance: 'cosine', + vectorCacheMaxObjects: 1000000000000, + pq: { + enabled: false, + rescoreLimit: -1, + cache: false, + }, + bq: { + enabled: false, + rescoreLimit: -1, + cache: false, + }, + }, + vectorIndexType: 'flat', + vectorizer: { + 'text2vec-contextionary': { + properties: ['name'], + vectorizeCollectionName: false, + }, + }, + }, + }; + + it('should merge a partial invertedIndexUpdate with existing schema', () => { + const merged = MergeWithExisting.invertedIndex(Object.assign({}, invertedIndex), { + bm25: { + b: 0.9, + }, + stopwords: { + removals: ['baz'], + }, + }); + expect(merged).toEqual({ + bm25: { + b: 0.9, + k1: 1.3, + }, + cleanupIntervalSeconds: 61, + indexPropertyLength: true, + indexTimestamps: true, + stopwords: { + preset: 'en', + removals: ['baz'], + }, + }); + }); + + it('should merge a no quantizer HNSW vectorIndexConfig with existing schema', () => { + const merged = MergeWithExisting.vectors(Object.assign({}, hnswVectorConfig), [ + { + name: 'name', + vectorIndex: { + name: 'hnsw', + config: { + skip: true, + cleanupIntervalSeconds: 301, + maxConnections: 65, + efConstruction: 129, + ef: -2, + dynamicEfMin: 101, + dynamicEfMax: 501, + dynamicEfFactor: 9, + vectorCacheMaxObjects: 1000000000001, + flatSearchCutoff: 40001, + distance: 'euclidean', + }, + }, + }, + ]); + expect(merged).toEqual({ + name: { + vectorIndexConfig: { + skip: true, + cleanupIntervalSeconds: 301, + maxConnections: 65, + efConstruction: 129, + ef: -2, + dynamicEfMin: 101, + dynamicEfMax: 501, + dynamicEfFactor: 9, + vectorCacheMaxObjects: 1000000000001, + flatSearchCutoff: 40001, + distance: 'euclidean', + pq: { + enabled: false, + bitCompression: false, + segments: 0, + centroids: 256, + trainingLimit: 100000, + encoder: { + type: 'kmeans', + distribution: 'log-normal', + }, + }, + bq: { + enabled: false, + }, + }, + vectorIndexType: 'hnsw', + vectorizer: { + 'text2vec-contextionary': { + properties: ['name'], + vectorizeCollectionName: false, + }, + }, + }, + }); + }); + + it('should merge a PQ quantizer HNSW vectorIndexConfig with existing schema', () => { + const merged = MergeWithExisting.vectors(Object.assign({}, hnswVectorConfig), [ + { + name: 'name', + vectorIndex: { + name: 'hnsw', + config: { + quantizer: { + type: 'pq', + bitCompression: true, + segments: 1, + centroids: 512, + trainingLimit: 200000, + encoder: { + type: 'kmeans', + distribution: 'normal', + }, + }, + }, + }, + }, + ]); + expect(merged).toEqual({ + name: { + vectorIndexConfig: { + skip: true, + cleanupIntervalSeconds: 301, + maxConnections: 65, + efConstruction: 129, + ef: -2, + dynamicEfMin: 101, + dynamicEfMax: 501, + dynamicEfFactor: 9, + vectorCacheMaxObjects: 1000000000001, + flatSearchCutoff: 40001, + distance: 'euclidean', + pq: { + enabled: true, + bitCompression: true, + segments: 1, + centroids: 512, + trainingLimit: 200000, + encoder: { + type: 'kmeans', + distribution: 'normal', + }, + }, + bq: { + enabled: false, + }, + }, + vectorIndexType: 'hnsw', + vectorizer: { + 'text2vec-contextionary': { + properties: ['name'], + vectorizeCollectionName: false, + }, + }, + }, + }); + }); + + it('should merge a BQ quantizer HNSW vectorIndexConfig with existing schema', () => { + const merged = MergeWithExisting.vectors(Object.assign({}, hnswVectorConfig), [ + { + name: 'name', + vectorIndex: { + name: 'hnsw', + config: { + quantizer: { + type: 'bq', + rescoreLimit: 1000, + }, + }, + }, + }, + ]); + expect(merged).toEqual({ + name: { + vectorIndexConfig: { + ...hnswVectorConfig.name.vectorIndexConfig, + bq: { + enabled: true, + rescoreLimit: 1000, + }, + }, + vectorIndexType: 'hnsw', + vectorizer: { + 'text2vec-contextionary': { + properties: ['name'], + vectorizeCollectionName: false, + }, + }, + }, + }); + }); + + it('should merge a BQ quantizer Flat vectorIndexConfig with existing schema', () => { + const merged = MergeWithExisting.vectors(Object.assign({}, flatVectorConfig), [ + { + name: 'name', + vectorIndex: { + name: 'hnsw', + config: { + quantizer: { + type: 'bq', + rescoreLimit: 1000, + }, + }, + }, + }, + ]); + expect(merged).toEqual({ + name: { + vectorIndexConfig: { + ...flatVectorConfig.name.vectorIndexConfig, + bq: { + cache: false, + enabled: true, + rescoreLimit: 1000, + }, + }, + vectorIndexType: 'flat', + vectorizer: { + 'text2vec-contextionary': { + properties: ['name'], + vectorizeCollectionName: false, + }, + }, + }, + }); + }); +}); diff --git a/src/collections/config/utils.ts b/src/collections/config/utils.ts new file mode 100644 index 00000000..142c6e81 --- /dev/null +++ b/src/collections/config/utils.ts @@ -0,0 +1,500 @@ +import { WeaviateDeserializationError } from '../../errors.js'; +import { + WeaviateBM25Config, + WeaviateClass, + WeaviateInvertedIndexConfig, + WeaviateModuleConfig, + WeaviateMultiTenancyConfig, + WeaviateNestedProperty, + WeaviateProperty, + WeaviateReplicationConfig, + WeaviateShardingConfig, + WeaviateStopwordConfig, + WeaviateVectorIndexConfig, + WeaviateVectorsConfig, +} from '../../openapi/types.js'; +import { + PropertyConfigCreate, + ReferenceConfigCreate, + ReferenceMultiTargetConfigCreate, + ReferenceSingleTargetConfigCreate, +} from '../configure/types/index.js'; +import { + BQConfig, + CollectionConfig, + GenerativeConfig, + GenerativeSearch, + InvertedIndexConfig, + ModuleConfig, + MultiTenancyConfig, + PQConfig, + PQEncoderConfig, + PQEncoderDistribution, + PQEncoderType, + PropertyConfig, + PropertyVectorizerConfig, + ReferenceConfig, + ReplicationConfig, + Reranker, + RerankerConfig, + ShardingConfig, + VectorConfig, + VectorDistance, + VectorIndexConfigDynamic, + VectorIndexConfigFlat, + VectorIndexConfigHNSW, + VectorIndexConfigType, + VectorizerConfig, +} from './types/index.js'; + +export class ReferenceTypeGuards { + static isSingleTarget(ref: ReferenceConfigCreate): ref is ReferenceSingleTargetConfigCreate { + return (ref as ReferenceSingleTargetConfigCreate).targetCollection !== undefined; + } + static isMultiTarget(ref: ReferenceConfigCreate): ref is ReferenceMultiTargetConfigCreate { + return (ref as ReferenceMultiTargetConfigCreate).targetCollections !== undefined; + } +} + +export const resolveProperty = ( + prop: PropertyConfigCreate, + vectorizers?: string[] +): WeaviateProperty => { + const { dataType, nestedProperties, skipVectorization, vectorizePropertyName, ...rest } = prop; + const moduleConfig: any = {}; + vectorizers?.forEach((vectorizer) => { + moduleConfig[vectorizer] = { + skip: skipVectorization === undefined ? false : skipVectorization, + vectorizePropertyName: vectorizePropertyName === undefined ? true : vectorizePropertyName, + }; + }); + return { + ...rest, + dataType: [dataType], + nestedProperties: nestedProperties + ? nestedProperties.map((prop) => resolveNestedProperty(prop)) + : undefined, + moduleConfig: Object.keys(moduleConfig).length > 0 ? moduleConfig : undefined, + }; +}; + +const resolveNestedProperty = (prop: any): WeaviateNestedProperty => { + const { dataType, nestedProperties, ...rest } = prop; + return { + ...rest, + dataType: [dataType], + nestedProperties: nestedProperties ? nestedProperties.map(resolveNestedProperty) : undefined, + }; +}; + +export const resolveReference = ( + ref: ReferenceSingleTargetConfigCreate | ReferenceMultiTargetConfigCreate +): WeaviateProperty => { + if (ReferenceTypeGuards.isSingleTarget(ref)) { + const { targetCollection, ...rest } = ref; + return { + ...rest, + dataType: [targetCollection], + }; + } else { + const { targetCollections, ...rest } = ref; + return { + ...rest, + dataType: targetCollections, + }; + } +}; + +export const classToCollection = (cls: WeaviateClass): CollectionConfig => { + return { + name: ConfigMapping._name(cls.class), + description: cls.description, + generative: ConfigMapping.generative(cls.moduleConfig), + invertedIndex: ConfigMapping.invertedIndex(cls.invertedIndexConfig), + multiTenancy: ConfigMapping.multiTenancy(cls.multiTenancyConfig), + properties: ConfigMapping.properties(cls.properties), + references: ConfigMapping.references(cls.properties), + replication: ConfigMapping.replication(cls.replicationConfig), + reranker: ConfigMapping.reranker(cls.moduleConfig), + sharding: ConfigMapping.sharding(cls.shardingConfig), + vectorizers: ConfigMapping.vectorizer(cls), + }; +}; + +function populated(v: T | null | undefined): v is T { + return v !== undefined && v !== null; +} + +function exists(v: any): v is T { + return v !== undefined && v !== null; +} + +class ConfigMapping { + static _name(v?: string): string { + if (v === undefined) + throw new WeaviateDeserializationError('Collection name was not returned by Weaviate'); + return v; + } + static bm25(v?: WeaviateBM25Config): InvertedIndexConfig['bm25'] { + if (v === undefined) throw new WeaviateDeserializationError('BM25 was not returned by Weaviate'); + if (!populated(v.b)) throw new WeaviateDeserializationError('BM25 b was not returned by Weaviate'); + if (!populated(v.k1)) throw new WeaviateDeserializationError('BM25 k1 was not returned by Weaviate'); + return { + b: v.b, + k1: v.k1, + }; + } + static stopwords(v?: WeaviateStopwordConfig): InvertedIndexConfig['stopwords'] { + if (v === undefined) throw new WeaviateDeserializationError('Stopwords were not returned by Weaviate'); + return { + additions: v.additions ? v.additions : [], + preset: v.preset ? v.preset : 'none', + removals: v.removals ? v.removals : [], + }; + } + static generative( + v?: WeaviateModuleConfig + ): ModuleConfig | undefined { + if (!populated(v)) return undefined; + const generativeKey = Object.keys(v).find((k) => k.includes('generative')); + if (generativeKey === undefined) return undefined; + if (!generativeKey) + throw new WeaviateDeserializationError('Generative config was not returned by Weaviate'); + return { + name: generativeKey, + config: v[generativeKey] as GenerativeConfig, + }; + } + static reranker(v?: WeaviateModuleConfig): ModuleConfig | undefined { + if (!populated(v)) return undefined; + const rerankerKey = Object.keys(v).find((k) => k.includes('reranker')); + if (rerankerKey === undefined) return undefined; + return { + name: rerankerKey, + config: v[rerankerKey] as RerankerConfig, + }; + } + private static namedVectors(v: WeaviateVectorsConfig): VectorConfig { + if (!populated(v)) throw new WeaviateDeserializationError('Vector config was not returned by Weaviate'); + const out: VectorConfig = {}; + Object.keys(v).forEach((key) => { + const vectorizer = v[key].vectorizer; + if (!populated(vectorizer)) + throw new WeaviateDeserializationError( + `Vectorizer was not returned by Weaviate for ${key} named vector` + ); + const vectorizerNames = Object.keys(vectorizer); + if (vectorizerNames.length !== 1) + throw new WeaviateDeserializationError( + `Expected exactly one vectorizer for ${key} named vector, got ${vectorizerNames.length}` + ); + const vectorizerName = vectorizerNames[0]; + const { properties, ...restA } = vectorizer[vectorizerName] as any; + const { vectorizeClassName, ...restB } = restA; + out[key] = { + vectorizer: { + name: vectorizerName, + config: { + vectorizeCollectionName: vectorizeClassName, + ...restB, + }, + }, + properties: properties, + indexConfig: ConfigMapping.vectorIndex(v[key].vectorIndexConfig, v[key].vectorIndexType), + indexType: ConfigMapping.vectorIndexType(v[key].vectorIndexType), + }; + }); + return out; + } + static vectorizer(v?: WeaviateClass): VectorConfig { + if (!populated(v)) throw new WeaviateDeserializationError('Schema was not returned by Weaviate'); + if (populated(v.vectorConfig)) { + return ConfigMapping.namedVectors(v.vectorConfig); + } + if (!populated(v.vectorizer)) + throw new WeaviateDeserializationError('Vectorizer was not returned by Weaviate'); + return { + default: { + vectorizer: + v.vectorizer === 'none' + ? { + name: 'none', + config: undefined, + } + : { + name: v.vectorizer, + config: v.moduleConfig + ? ({ + ...(v.moduleConfig[v.vectorizer] as any), + vectorizeCollectionName: (v.moduleConfig[v.vectorizer] as any).vectorizeClassName, + } as VectorizerConfig) + : undefined, + }, + indexConfig: ConfigMapping.vectorIndex(v.vectorIndexConfig, v.vectorIndexType), + indexType: ConfigMapping.vectorIndexType(v.vectorIndexType), + }, + }; + } + static invertedIndex(v?: WeaviateInvertedIndexConfig): InvertedIndexConfig { + if (v === undefined) + throw new WeaviateDeserializationError('Inverted index was not returned by Weaviate'); + if (!populated(v.cleanupIntervalSeconds)) + throw new WeaviateDeserializationError('Inverted index cleanup interval was not returned by Weaviate'); + return { + bm25: ConfigMapping.bm25(v.bm25), + cleanupIntervalSeconds: v.cleanupIntervalSeconds, + stopwords: ConfigMapping.stopwords(v.stopwords), + indexNullState: v.indexNullState ? v.indexNullState : false, + indexPropertyLength: v.indexPropertyLength ? v.indexPropertyLength : false, + indexTimestamps: v.indexTimestamps ? v.indexTimestamps : false, + }; + } + static multiTenancy(v?: WeaviateMultiTenancyConfig): MultiTenancyConfig { + if (v === undefined) throw new WeaviateDeserializationError('Multi tenancy was not returned by Weaviate'); + return { + autoTenantActivation: v.autoTenantActivation ? v.autoTenantActivation : false, + autoTenantCreation: v.autoTenantCreation ? v.autoTenantCreation : false, + enabled: v.enabled ? v.enabled : false, + }; + } + static replication(v?: WeaviateReplicationConfig): ReplicationConfig { + if (v === undefined) throw new WeaviateDeserializationError('Replication was not returned by Weaviate'); + if (!populated(v.factor)) + throw new WeaviateDeserializationError('Replication factor was not returned by Weaviate'); + return { + factor: v.factor, + }; + } + static sharding(v?: WeaviateShardingConfig): ShardingConfig { + if (v === undefined) throw new WeaviateDeserializationError('Sharding was not returned by Weaviate'); + if (!exists(v.virtualPerPhysical)) + throw new WeaviateDeserializationError('Sharding enabled was not returned by Weaviate'); + if (!exists(v.desiredCount)) + throw new WeaviateDeserializationError('Sharding desired count was not returned by Weaviate'); + if (!exists(v.actualCount)) + throw new WeaviateDeserializationError('Sharding actual count was not returned by Weaviate'); + if (!exists(v.desiredVirtualCount)) + throw new WeaviateDeserializationError('Sharding desired virtual count was not returned by Weaviate'); + if (!exists(v.actualVirtualCount)) + throw new WeaviateDeserializationError('Sharding actual virtual count was not returned by Weaviate'); + if (!exists<'_id'>(v.key)) + throw new WeaviateDeserializationError('Sharding key was not returned by Weaviate'); + if (!exists<'hash'>(v.strategy)) + throw new WeaviateDeserializationError('Sharding strategy was not returned by Weaviate'); + if (!exists<'murmur3'>(v.function)) + throw new WeaviateDeserializationError('Sharding function was not returned by Weaviate'); + return { + virtualPerPhysical: v.virtualPerPhysical, + desiredCount: v.desiredCount, + actualCount: v.actualCount, + desiredVirtualCount: v.desiredVirtualCount, + actualVirtualCount: v.actualVirtualCount, + key: v.key, + strategy: v.strategy, + function: v.function, + }; + } + static pqEncoder(v?: Record): PQEncoderConfig { + if (v === undefined) throw new WeaviateDeserializationError('PQ encoder was not returned by Weaviate'); + if (!exists(v.type)) + throw new WeaviateDeserializationError('PQ encoder name was not returned by Weaviate'); + if (!exists(v.distribution)) + throw new WeaviateDeserializationError('PQ encoder distribution was not returned by Weaviate'); + return { + type: v.type, + distribution: v.distribution, + }; + } + static pq(v?: Record): PQConfig | undefined { + if (v === undefined) throw new WeaviateDeserializationError('PQ was not returned by Weaviate'); + if (!exists(v.enabled)) + throw new WeaviateDeserializationError('PQ enabled was not returned by Weaviate'); + if (v.enabled === false) return undefined; + if (!exists(v.bitCompression)) + throw new WeaviateDeserializationError('PQ bit compression was not returned by Weaviate'); + if (!exists(v.segments)) + throw new WeaviateDeserializationError('PQ segments was not returned by Weaviate'); + if (!exists(v.trainingLimit)) + throw new WeaviateDeserializationError('PQ training limit was not returned by Weaviate'); + if (!exists(v.centroids)) + throw new WeaviateDeserializationError('PQ centroids was not returned by Weaviate'); + if (!exists>(v.encoder)) + throw new WeaviateDeserializationError('PQ encoder was not returned by Weaviate'); + return { + bitCompression: v.bitCompression, + segments: v.segments, + centroids: v.centroids, + trainingLimit: v.trainingLimit, + encoder: ConfigMapping.pqEncoder(v.encoder), + type: 'pq', + }; + } + static vectorIndexHNSW(v: WeaviateVectorIndexConfig): VectorIndexConfigHNSW { + if (v === undefined) throw new WeaviateDeserializationError('Vector index was not returned by Weaviate'); + if (!exists(v.cleanupIntervalSeconds)) + throw new WeaviateDeserializationError('Vector index cleanup interval was not returned by Weaviate'); + if (!exists(v.distance)) + throw new WeaviateDeserializationError('Vector index distance was not returned by Weaviate'); + if (!exists(v.dynamicEfMin)) + throw new WeaviateDeserializationError('Vector index dynamic ef min was not returned by Weaviate'); + if (!exists(v.dynamicEfMax)) + throw new WeaviateDeserializationError('Vector index dynamic ef max was not returned by Weaviate'); + if (!exists(v.dynamicEfFactor)) + throw new WeaviateDeserializationError('Vector index dynamic ef factor was not returned by Weaviate'); + if (!exists(v.ef)) + throw new WeaviateDeserializationError('Vector index ef was not returned by Weaviate'); + if (!exists(v.efConstruction)) + throw new WeaviateDeserializationError('Vector index ef construction was not returned by Weaviate'); + if (!exists(v.flatSearchCutoff)) + throw new WeaviateDeserializationError('Vector index flat search cut off was not returned by Weaviate'); + if (!exists(v.maxConnections)) + throw new WeaviateDeserializationError('Vector index max connections was not returned by Weaviate'); + if (!exists(v.skip)) + throw new WeaviateDeserializationError('Vector index skip was not returned by Weaviate'); + if (!exists(v.vectorCacheMaxObjects)) + throw new WeaviateDeserializationError( + 'Vector index vector cache max objects was not returned by Weaviate' + ); + let quantizer: PQConfig | BQConfig | undefined; + if (exists>(v.pq) && v.pq.enabled === true) { + quantizer = ConfigMapping.pq(v.pq); + } else if (exists>(v.bq) && v.bq.enabled === true) { + quantizer = ConfigMapping.bq(v.bq); + } else { + quantizer = undefined; + } + return { + cleanupIntervalSeconds: v.cleanupIntervalSeconds, + distance: v.distance, + dynamicEfMin: v.dynamicEfMin, + dynamicEfMax: v.dynamicEfMax, + dynamicEfFactor: v.dynamicEfFactor, + ef: v.ef, + efConstruction: v.efConstruction, + flatSearchCutoff: v.flatSearchCutoff, + maxConnections: v.maxConnections, + quantizer: quantizer, + skip: v.skip, + vectorCacheMaxObjects: v.vectorCacheMaxObjects, + type: 'hnsw', + }; + } + static bq(v?: Record): BQConfig | undefined { + if (v === undefined) throw new WeaviateDeserializationError('BQ was not returned by Weaviate'); + if (!exists(v.enabled)) + throw new WeaviateDeserializationError('BQ enabled was not returned by Weaviate'); + if (v.enabled === false) return undefined; + const cache = v.cache === undefined ? false : (v.cache as boolean); + const rescoreLimit = v.rescoreLimit === undefined ? 1000 : (v.rescoreLimit as number); + return { + cache, + rescoreLimit, + type: 'bq', + }; + } + static vectorIndexFlat(v: WeaviateVectorIndexConfig): VectorIndexConfigFlat { + if (v === undefined) throw new WeaviateDeserializationError('Vector index was not returned by Weaviate'); + if (!exists(v.vectorCacheMaxObjects)) + throw new WeaviateDeserializationError( + 'Vector index vector cache max objects was not returned by Weaviate' + ); + if (!exists(v.distance)) + throw new WeaviateDeserializationError('Vector index distance was not returned by Weaviate'); + if (!exists>(v.bq)) + throw new WeaviateDeserializationError('Vector index bq was not returned by Weaviate'); + return { + vectorCacheMaxObjects: v.vectorCacheMaxObjects, + distance: v.distance, + quantizer: ConfigMapping.bq(v.bq), + type: 'flat', + }; + } + static vectorIndexDynamic(v: WeaviateVectorIndexConfig): VectorIndexConfigDynamic { + if (v === undefined) throw new WeaviateDeserializationError('Vector index was not returned by Weaviate'); + if (!exists(v.threshold)) + throw new WeaviateDeserializationError('Vector index threshold was not returned by Weaviate'); + if (!exists(v.distance)) + throw new WeaviateDeserializationError('Vector index distance was not returned by Weaviate'); + if (!exists(v.hnsw)) + throw new WeaviateDeserializationError('Vector index hnsw was not returned by Weaviate'); + if (!exists(v.flat)) + throw new WeaviateDeserializationError('Vector index flat was not returned by Weaviate'); + return { + distance: v.distance, + hnsw: ConfigMapping.vectorIndexHNSW(v.hnsw), + flat: ConfigMapping.vectorIndexFlat(v.flat), + threshold: v.threshold, + type: 'dynamic', + }; + } + static vectorIndex(v: WeaviateVectorIndexConfig, t?: string): VectorIndexConfigType { + if (t === 'hnsw') { + return ConfigMapping.vectorIndexHNSW(v) as VectorIndexConfigType; + } else if (t === 'flat') { + return ConfigMapping.vectorIndexFlat(v) as VectorIndexConfigType; + } else if (t === 'dynamic') { + return ConfigMapping.vectorIndexDynamic(v) as VectorIndexConfigType; + } else { + return v as VectorIndexConfigType; + } + } + static vectorIndexType(v?: string): I { + if (!populated(v)) + throw new WeaviateDeserializationError('Vector index type was not returned by Weaviate'); + return v as I; + } + static properties(v?: WeaviateProperty[]): PropertyConfig[] { + if (v === undefined) throw new WeaviateDeserializationError('Properties were not returned by Weaviate'); + if (v === null) return []; + return v + .filter((prop) => { + if (!populated(prop.dataType)) + throw new WeaviateDeserializationError('Property data type was not returned by Weaviate'); + return prop.dataType[0][0].toLowerCase() === prop.dataType[0][0]; // primitive property, e.g. text + }) + .map((prop) => { + if (!populated(prop.name)) + throw new WeaviateDeserializationError('Property name was not returned by Weaviate'); + if (!populated(prop.dataType)) + throw new WeaviateDeserializationError('Property data type was not returned by Weaviate'); + return { + name: prop.name, + dataType: prop.dataType[0], + description: prop.description, + indexFilterable: prop.indexFilterable ? prop.indexFilterable : false, + indexInverted: prop.indexInverted ? prop.indexInverted : false, + indexSearchable: prop.indexSearchable ? prop.indexSearchable : false, + vectorizerConfig: prop.moduleConfig + ? 'none' in prop.moduleConfig + ? undefined + : (prop.moduleConfig as PropertyVectorizerConfig) + : undefined, + nestedProperties: prop.nestedProperties + ? ConfigMapping.properties(prop.nestedProperties) + : undefined, + tokenization: prop.tokenization ? prop.tokenization : 'none', + }; + }); + } + static references(v?: WeaviateProperty[]): ReferenceConfig[] { + if (v === undefined) throw new WeaviateDeserializationError('Properties were not returned by Weaviate'); + if (v === null) return []; + return v + .filter((prop) => { + if (!populated(prop.dataType)) + throw new WeaviateDeserializationError('Reference data type was not returned by Weaviate'); + return prop.dataType[0][0].toLowerCase() !== prop.dataType[0][0]; // reference property, e.g. Myclass + }) + .map((prop) => { + if (!populated(prop.name)) + throw new WeaviateDeserializationError('Reference name was not returned by Weaviate'); + if (!populated(prop.dataType)) + throw new WeaviateDeserializationError('Reference data type was not returned by Weaviate'); + return { + name: prop.name, + description: prop.description, + targetCollections: prop.dataType, + }; + }); + } +} diff --git a/src/collections/configure/generative.ts b/src/collections/configure/generative.ts new file mode 100644 index 00000000..e51447a2 --- /dev/null +++ b/src/collections/configure/generative.ts @@ -0,0 +1,194 @@ +import { + GenerativeAWSConfig, + GenerativeAnyscaleConfig, + GenerativeAzureOpenAIConfig, + GenerativeCohereConfig, + GenerativeMistralConfig, + GenerativeOctoAIConfig, + GenerativeOllamaConfig, + GenerativeOpenAIConfig, + GenerativePaLMConfig, + ModuleConfig, +} from '../config/types/index.js'; +import { + GenerativeAWSConfigCreate, + GenerativeAnyscaleConfigCreate, + GenerativeAzureOpenAIConfigCreate, + GenerativeCohereConfigCreate, + GenerativeMistralConfigCreate, + GenerativeOctoAIConfigCreate, + GenerativeOllamaConfigCreate, + GenerativeOpenAIConfigCreate, + GenerativePaLMConfigCreate, +} from '../index.js'; + +export default { + /** + * Create a `ModuleConfig<'generative-anyscale', GenerativeAnyscaleConfig | undefined>` object for use when performing AI generation using the `generative-anyscale` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-anyscale) for detailed usage. + * + * @param {GenerativeAnyscaleConfigCreate} config The configuration for the `generative-aws` module. + * @returns {ModuleConfig<'generative-anyscale', GenerativeAnyscaleConfig | undefined>} The configuration object. + */ + anyscale( + config?: GenerativeAnyscaleConfigCreate + ): ModuleConfig<'generative-anyscale', GenerativeAnyscaleConfig | undefined> { + return { + name: 'generative-anyscale', + config, + }; + }, + /** + * Create a `ModuleConfig<'generative-aws', GenerativeAWSConfig>` object for use when performing AI generation using the `generative-aws` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-aws) for detailed usage. + * + * @param {GenerativeAWSConfigCreate} config The configuration for the `generative-aws` module. + * @returns {ModuleConfig<'generative-aws', GenerativeAWSConfig>} The configuration object. + */ + aws(config: GenerativeAWSConfigCreate): ModuleConfig<'generative-aws', GenerativeAWSConfig> { + return { + name: 'generative-aws', + config, + }; + }, + /** + * Create a `ModuleConfig<'generative-openai', GenerativeAzureOpenAIConfig>` object for use when performing AI generation using the `generative-openai` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-openai) for detailed usage. + * + * @param {GenerativeAzureOpenAIConfigCreate} config The configuration for the `generative-openai` module. + * @returns {ModuleConfig<'generative-openai', GenerativeAzureOpenAIConfig>} The configuration object. + */ + azureOpenAI: ( + config: GenerativeAzureOpenAIConfigCreate + ): ModuleConfig<'generative-openai', GenerativeAzureOpenAIConfig> => { + return { + name: 'generative-openai', + config: { + deploymentId: config.deploymentId, + resourceName: config.resourceName, + baseURL: config.baseURL, + frequencyPenaltyProperty: config.frequencyPenalty, + maxTokensProperty: config.maxTokens, + presencePenaltyProperty: config.presencePenalty, + temperatureProperty: config.temperature, + topPProperty: config.topP, + }, + }; + }, + /** + * Create a `ModuleConfig<'generative-cohere', GenerativeCohereConfig>` object for use when performing AI generation using the `generative-cohere` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-cohere) for detailed usage. + * + * @param {GenerativeCohereConfigCreate} [config] The configuration for the `generative-cohere` module. + * @returns {ModuleConfig<'generative-cohere', GenerativeCohereConfig>} The configuration object. + */ + cohere: ( + config?: GenerativeCohereConfigCreate + ): ModuleConfig<'generative-cohere', GenerativeCohereConfig | undefined> => { + return { + name: 'generative-cohere', + config: config + ? { + kProperty: config.k, + maxTokensProperty: config.maxTokens, + model: config.model, + returnLikelihoodsProperty: config.returnLikelihoods, + stopSequencesProperty: config.stopSequences, + temperatureProperty: config.temperature, + } + : undefined, + }; + }, + /** + * Create a `ModuleConfig<'generative-mistral', GenerativeMistralConfig | undefined>` object for use when performing AI generation using the `generative-mistral` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-mistral) for detailed usage. + * + * @param {GenerativeMistralConfigCreate} [config] The configuration for the `generative-mistral` module. + * @returns {ModuleConfig<'generative-mistral', GenerativeMistralConfig | undefined>} The configuration object. + */ + mistral( + config?: GenerativeMistralConfigCreate + ): ModuleConfig<'generative-mistral', GenerativeMistralConfig | undefined> { + return { + name: 'generative-mistral', + config, + }; + }, + /** + * Create a `ModuleConfig<'generative-octoai', GenerativeOpenAIConfig | undefined>` object for use when performing AI generation using the `generative-octoai` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-octoai) for detailed usage. + * + * @param {GenerativeOctoAIConfigCreate} [config] The configuration for the `generative-octoai` module. + * @returns {ModuleConfig<'generative-octoai', GenerativeOctoAIConfig | undefined>} The configuration object. + */ + octoai( + config?: GenerativeOctoAIConfigCreate + ): ModuleConfig<'generative-octoai', GenerativeOctoAIConfig | undefined> { + return { + name: 'generative-octoai', + config, + }; + }, + /** + * Create a `ModuleConfig<'generative-ollama', GenerativeOllamaConfig | undefined>` object for use when performing AI generation using the `generative-ollama` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-ollama) for detailed usage. + * + * @param {GenerativeOllamaConfigCreate} [config] The configuration for the `generative-openai` module. + * @returns {ModuleConfig<'generative-ollama', GenerativeOllamaConfig | undefined>} The configuration object. + */ + ollama( + config?: GenerativeOllamaConfigCreate + ): ModuleConfig<'generative-ollama', GenerativeOllamaConfig | undefined> { + return { + name: 'generative-ollama', + config, + }; + }, + /** + * Create a `ModuleConfig<'generative-openai', GenerativeOpenAIConfig | undefined>` object for use when performing AI generation using the `generative-openai` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-openai) for detailed usage. + * + * @param {GenerativeOpenAIConfigCreate} [config] The configuration for the `generative-openai` module. + * @returns {ModuleConfig<'generative-openai', GenerativeOpenAIConfig | undefined>} The configuration object. + */ + openAI: ( + config?: GenerativeOpenAIConfigCreate + ): ModuleConfig<'generative-openai', GenerativeOpenAIConfig | undefined> => { + return { + name: 'generative-openai', + config: config + ? { + baseURL: config.baseURL, + frequencyPenaltyProperty: config.frequencyPenalty, + maxTokensProperty: config.maxTokens, + model: config.model, + presencePenaltyProperty: config.presencePenalty, + temperatureProperty: config.temperature, + topPProperty: config.topP, + } + : undefined, + }; + }, + /** + * Create a `ModuleConfig<'generative-palm', GenerativePaLMConfig>` object for use when performing AI generation using the `generative-palm` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/reader-generator-modules/generative-palm) for detailed usage. + * + * @param {GenerativePaLMConfigCreate} config The configuration for the `generative-palm` module. + * @returns {ModuleConfig<'generative-palm', GenerativePaLMConfig>} The configuration object. + */ + palm: (config: GenerativePaLMConfigCreate): ModuleConfig<'generative-palm', GenerativePaLMConfig> => { + return { + name: 'generative-palm', + config, + }; + }, +}; diff --git a/src/collections/configure/index.ts b/src/collections/configure/index.ts new file mode 100644 index 00000000..2caa4d95 --- /dev/null +++ b/src/collections/configure/index.ts @@ -0,0 +1,234 @@ +import { + InvertedIndexConfigCreate, + InvertedIndexConfigUpdate, + MultiTenancyConfigCreate, + ReplicationConfigCreate, + ShardingConfigCreate, + VectorConfigUpdate, + VectorIndexType, + VectorizerUpdateOptions, +} from '../types/index.js'; + +import generative from './generative.js'; +import reranker from './reranker.js'; +import { configure as configureVectorIndex, reconfigure as reconfigureVectorIndex } from './vectorIndex.js'; +import { vectorizer } from './vectorizer.js'; + +import { parseWithDefault } from './parsing.js'; + +const dataType = { + INT: 'int' as const, + INT_ARRAY: 'int[]' as const, + NUMBER: 'number' as const, + NUMBER_ARRAY: 'number[]' as const, + TEXT: 'text' as const, + TEXT_ARRAY: 'text[]' as const, + UUID: 'uuid' as const, + UUID_ARRAY: 'uuid[]' as const, + BOOLEAN: 'boolean' as const, + BOOLEAN_ARRAY: 'boolean[]' as const, + DATE: 'date' as const, + DATE_ARRAY: 'date[]' as const, + OBJECT: 'object' as const, + OBJECT_ARRAY: 'object[]' as const, + BLOB: 'blob' as const, + GEO_COORDINATES: 'geoCoordinates' as const, + PHONE_NUMBER: 'phoneNumber' as const, +}; + +const tokenization = { + WORD: 'word' as const, + LOWERCASE: 'lowercase' as const, + WHITESPACE: 'whitespace' as const, + FIELD: 'field' as const, + TRIGRAM: 'trigram' as const, + GSE: 'gse' as const, +}; + +const vectorDistances = { + COSINE: 'cosine' as const, + DOT: 'dot' as const, + HAMMING: 'hamming' as const, + L2_SQUARED: 'l2-squared' as const, +}; + +const configure = { + generative, + reranker, + vectorizer, + vectorIndex: configureVectorIndex, + dataType, + tokenization, + vectorDistances, + /** + * Create an `InvertedIndexConfigCreate` object to be used when defining the configuration of the keyword searching algorithm of your collection. + * + * See [the docs](https://weaviate.io/developers/weaviate/configuration/indexes#configure-the-inverted-index) for details! + * + * @param {number} [options.bm25b] The BM25 b parameter. + * @param {number} [options.bm25k1] The BM25 k1 parameter. + * @param {number} [options.cleanupIntervalSeconds] The interval in seconds at which the inverted index is cleaned up. + * @param {boolean} [options.indexTimestamps] Whether to index timestamps. + * @param {boolean} [options.indexPropertyLength] Whether to index the length of properties. + * @param {boolean} [options.indexNullState] Whether to index the null state of properties. + * @param {'en' | 'none'} [options.stopwordsPreset] The stopwords preset to use. + * @param {string[]} [options.stopwordsAdditions] Additional stopwords to add. + * @param {string[]} [options.stopwordsRemovals] Stopwords to remove. + */ + invertedIndex: (options: { + bm25b?: number; + bm25k1?: number; + cleanupIntervalSeconds?: number; + indexTimestamps?: boolean; + indexPropertyLength?: boolean; + indexNullState?: boolean; + stopwordsPreset?: 'en' | 'none'; + stopwordsAdditions?: string[]; + stopwordsRemovals?: string[]; + }): InvertedIndexConfigCreate => { + return { + bm25: { + b: options.bm25b, + k1: options.bm25k1, + }, + cleanupIntervalSeconds: options.cleanupIntervalSeconds, + indexTimestamps: options.indexTimestamps, + indexPropertyLength: options.indexPropertyLength, + indexNullState: options.indexNullState, + stopwords: { + preset: options.stopwordsPreset, + additions: options.stopwordsAdditions, + removals: options.stopwordsRemovals, + }, + }; + }, + /** + * Create a `MultiTenancyConfigCreate` object to be used when defining the multi-tenancy configuration of your collection. + * + * @param {boolean} [options.autoTenantActivation] Whether auto-tenant activation is enabled. Default is false. + * @param {boolean} [options.autoTenantCreation] Whether auto-tenant creation is enabled. Default is false. + * @param {boolean} [options.enabled] Whether multi-tenancy is enabled. Default is true. + */ + multiTenancy: (options?: { + autoTenantActivation?: boolean; + autoTenantCreation?: boolean; + enabled?: boolean; + }): MultiTenancyConfigCreate => { + return options + ? { + autoTenantActivation: parseWithDefault(options.autoTenantActivation, false), + autoTenantCreation: parseWithDefault(options.autoTenantCreation, false), + enabled: parseWithDefault(options.enabled, true), + } + : { autoTenantActivation: false, autoTenantCreation: false, enabled: true }; + }, + /** + * Create a `ReplicationConfigCreate` object to be used when defining the replication configuration of your collection. + * + * NOTE: You can only use one of Sharding or Replication, not both. + * + * See [the docs](https://weaviate.io/developers/weaviate/concepts/replication-architecture#replication-vs-sharding) for more details. + * + * @param {number} options.factor The replication factor. Default is 1. + */ + replication: (options: { factor?: number }): ReplicationConfigCreate => { + return { factor: options.factor }; + }, + /** + * Create a `ShardingConfigCreate` object to be used when defining the sharding configuration of your collection. + * + * NOTE: You can only use one of Sharding or Replication, not both. + * + * See [the docs](https://weaviate.io/developers/weaviate/concepts/replication-architecture#replication-vs-sharding) for more details. + * + * @param {number} [options.virtualPerPhysical] The number of virtual shards per physical shard. + * @param {number} [options.desiredCount] The desired number of physical shards. + * @param {number} [options.desiredVirtualCount] The desired number of virtual shards. + */ + sharding: (options: { + virtualPerPhysical?: number; + desiredCount?: number; + desiredVirtualCount?: number; + }): ShardingConfigCreate => { + return { + virtualPerPhysical: options.virtualPerPhysical, + desiredCount: options.desiredCount, + desiredVirtualCount: options.desiredVirtualCount, + }; + }, +}; + +const reconfigure = { + vectorIndex: reconfigureVectorIndex, + /** + * Create an `InvertedIndexConfigUpdate` object to be used when updating the configuration of the keyword searching algorithm of your collection. + * + * See [the docs](https://weaviate.io/developers/weaviate/configuration/indexes#configure-the-inverted-index) for details! + * + * @param {number} [options.bm25b] The BM25 b parameter. + * @param {number} [options.bm25k1] The BM25 k1 parameter. + * @param {number} [options.cleanupIntervalSeconds] The interval in seconds at which the inverted index is cleaned up. + * @param {'en' | 'none'} [options.stopwordsPreset] The stopwords preset to use. + * @param {string[]} [options.stopwordsAdditions] Additional stopwords to add. + * @param {string[]} [options.stopwordsRemovals] Stopwords to remove. + */ + invertedIndex: (options: { + bm25b?: number; + bm25k1?: number; + cleanupIntervalSeconds?: number; + stopwordsPreset?: 'en' | 'none'; + stopwordsAdditions?: string[]; + stopwordsRemovals?: string[]; + }): InvertedIndexConfigUpdate => { + return { + bm25: { + b: options.bm25b, + k1: options.bm25k1, + }, + cleanupIntervalSeconds: options.cleanupIntervalSeconds, + stopwords: { + preset: options.stopwordsPreset, + additions: options.stopwordsAdditions, + removals: options.stopwordsRemovals, + }, + }; + }, + vectorizer: { + /** + * Create a `VectorConfigUpdate` object to be used when updating the named vector configuration of Weaviate. + * + * @param {string} name The name of the vector. + * @param {VectorizerOptions} options The options for the named vector. + */ + update: ( + options: VectorizerUpdateOptions + ): VectorConfigUpdate => { + return { + name: options?.name as N, + vectorIndex: options.vectorIndexConfig, + }; + }, + }, + /** + * Create a `ReplicationConfigUpdate` object to be used when defining the replication configuration of Weaviate. + * + * See [the docs](https://weaviate.io/developers/weaviate/concepts/replication-architecture#replication-vs-sharding) for more details. + * + * @param {number} [options.factor] The replication factor. + */ + replication: (options: { factor?: number }): ReplicationConfigCreate => { + return { factor: options.factor }; + }, +}; + +export { + configure, + dataType, + generative, + reconfigure, + reranker, + tokenization, + vectorDistances, + configureVectorIndex as vectorIndex, + vectorizer, +}; diff --git a/src/collections/configure/parsing.ts b/src/collections/configure/parsing.ts new file mode 100644 index 00000000..b858d3d6 --- /dev/null +++ b/src/collections/configure/parsing.ts @@ -0,0 +1,40 @@ +import { BQConfigCreate, BQConfigUpdate, PQConfigCreate, PQConfigUpdate } from './types/index.js'; + +type QuantizerConfig = PQConfigCreate | PQConfigUpdate | BQConfigCreate | BQConfigUpdate; + +export class QuantizerGuards { + static isPQCreate(config?: QuantizerConfig): config is PQConfigCreate { + return (config as PQConfigCreate)?.type === 'pq'; + } + static isPQUpdate(config?: QuantizerConfig): config is PQConfigUpdate { + return (config as PQConfigUpdate)?.type === 'pq'; + } + static isBQCreate(config?: QuantizerConfig): config is BQConfigCreate { + return (config as BQConfigCreate)?.type === 'bq'; + } + static isBQUpdate(config?: QuantizerConfig): config is BQConfigUpdate { + return (config as BQConfigUpdate)?.type === 'bq'; + } +} + +export function parseWithDefault(value: D | undefined, defaultValue: D): D { + return value !== undefined ? value : defaultValue; +} + +export const parseQuantizer = (config?: T): T | undefined => { + if (config === undefined) { + return undefined; + } + if (QuantizerGuards.isPQCreate(config)) { + return { + ...config, + type: 'pq', + } as T; + } else if (QuantizerGuards.isBQCreate(config)) { + return { + ...config, + type: 'bq', + } as T; + } + return config; +}; diff --git a/src/collections/configure/reranker.ts b/src/collections/configure/reranker.ts new file mode 100644 index 00000000..ad836f0a --- /dev/null +++ b/src/collections/configure/reranker.ts @@ -0,0 +1,49 @@ +import { ModuleConfig, RerankerCohereConfig, RerankerVoyageAIConfig } from '../config/types/index.js'; + +export default { + /** + * Create a `ModuleConfig<'reranker-cohere', RerankerCohereConfig>` object for use when reranking using the `reranker-cohere` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/reranker-cohere) for detailed usage. + * + * @param {RerankerCohereConfig} [config] The configuration for the `reranker-cohere` module. + * @returns {ModuleConfig<'reranker-cohere', RerankerCohereConfig>} The configuration object. + */ + cohere: ( + config?: RerankerCohereConfig + ): ModuleConfig<'reranker-cohere', RerankerCohereConfig | undefined> => { + return { + name: 'reranker-cohere', + config: config, + }; + }, + /** + * Create a `ModuleConfig<'reranker-transformers', Record>` object for use when reranking using the `reranker-transformers` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/reranker-transformers) for detailed usage. + * + * @returns {ModuleConfig<'reranker-transformers', Record>} The configuration object. + */ + transformers: (): ModuleConfig<'reranker-transformers', Record> => { + return { + name: 'reranker-transformers', + config: {}, + }; + }, + /** + * Create a `ModuleConfig<'reranker-voyageai', RerankerVoyageAIConfig>` object for use when reranking using the `reranker-voyageai` module. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/reranker-voyageai) for detailed usage. + * + * @param {RerankerVoyageAIConfig} [config] The configuration for the `reranker-voyage-ai` module. + * @returns {ModuleConfig<'reranker-voyage-ai', RerankerVoyageAIConfig | undefined>} The configuration object. + */ + voyageAI: ( + config?: RerankerVoyageAIConfig + ): ModuleConfig<'reranker-voyageai', RerankerVoyageAIConfig | undefined> => { + return { + name: 'reranker-voyageai', + config: config, + }; + }, +}; diff --git a/src/collections/configure/types/base.ts b/src/collections/configure/types/base.ts new file mode 100644 index 00000000..4b0e4b31 --- /dev/null +++ b/src/collections/configure/types/base.ts @@ -0,0 +1,139 @@ +import { WeaviateNestedProperty, WeaviateProperty } from '../../../openapi/types.js'; +import { InvertedIndexConfig, MultiTenancyConfig, ReplicationConfig } from '../../config/types/index.js'; +import { DataType } from '../../types/index.js'; +import { NonRefKeys, RefKeys } from '../../types/internal.js'; + +export type RecursivePartial = { + [P in keyof T]?: RecursivePartial; +}; + +export type InvertedIndexConfigCreate = RecursivePartial; + +export type InvertedIndexConfigUpdate = { + bm25?: { + b?: number; + k1?: number; + }; + cleanupIntervalSeconds?: number; + stopwords?: { + preset?: string; + additions?: string[]; + removals?: string[]; + }; +}; + +export type MultiTenancyConfigCreate = RecursivePartial; + +export type NestedPropertyCreate = T extends undefined + ? { + name: string; + dataType: DataType; + description?: string; + nestedProperties?: NestedPropertyConfigCreate[]; + indexInverted?: boolean; + indexFilterable?: boolean; + indexSearchable?: boolean; + tokenization?: WeaviateNestedProperty['tokenization']; + } + : { + [K in NonRefKeys]: RequiresNested> extends true + ? { + name: K; + dataType: DataType; + nestedProperties: NestedPropertyConfigCreate>[]; + } & NestedPropertyConfigCreateBase + : { + name: K; + dataType: DataType; + nestedProperties?: NestedPropertyConfigCreate>[]; + } & NestedPropertyConfigCreateBase; + }[NonRefKeys]; + +export type NestedPropertyConfigCreate = D extends 'object' | 'object[]' + ? T extends (infer U)[] + ? NestedPropertyCreate + : NestedPropertyCreate + : never; + +export type RequiresNested = T extends 'object' | 'object[]' ? true : false; + +export type PropertyConfigCreateBase = { + description?: string; + indexInverted?: boolean; + indexFilterable?: boolean; + indexSearchable?: boolean; + tokenization?: WeaviateProperty['tokenization']; + skipVectorization?: boolean; + vectorizePropertyName?: boolean; +}; + +export type NestedPropertyConfigCreateBase = { + description?: string; + indexInverted?: boolean; + indexFilterable?: boolean; + indexSearchable?: boolean; + tokenization?: WeaviateNestedProperty['tokenization']; +}; + +export type PropertyConfigCreate = T extends undefined + ? { + name: string; + dataType: DataType; + description?: string; + nestedProperties?: NestedPropertyConfigCreate[]; + indexInverted?: boolean; + indexFilterable?: boolean; + indexSearchable?: boolean; + tokenization?: WeaviateProperty['tokenization']; + skipVectorization?: boolean; + vectorizePropertyName?: boolean; + } + : { + [K in NonRefKeys]: RequiresNested> extends true + ? { + name: K; + dataType: DataType; + nestedProperties: NestedPropertyConfigCreate>[]; + } & PropertyConfigCreateBase + : { + name: K; + dataType: DataType; + nestedProperties?: NestedPropertyConfigCreate>[]; + } & PropertyConfigCreateBase; + }[NonRefKeys]; + +/** The base class for creating a reference configuration. */ +export type ReferenceConfigBaseCreate = { + /** The name of the reference. If no generic passed, the type is string. If a generic is passed, the type is a union of the keys labelled as CrossReference. */ + name: T extends undefined ? string : RefKeys; + /** The description of the reference. */ + description?: string; +}; + +/** Use this type when defining a single-target reference for your collection. */ +export type ReferenceSingleTargetConfigCreate = ReferenceConfigBaseCreate & { + /** The collection that this reference points to. */ + targetCollection: string; +}; + +/** Use this type when defining a multi-target reference for your collection. */ +export type ReferenceMultiTargetConfigCreate = ReferenceConfigBaseCreate & { + /** The collection(s) that this reference points to. */ + targetCollections: string[]; +}; + +export type ReferenceConfigCreate = + | ReferenceSingleTargetConfigCreate + | ReferenceMultiTargetConfigCreate; + +export type ReplicationConfigCreate = RecursivePartial; + +export type ReplicationConfigUpdate = { + factor?: number; +}; + +export type ShardingConfigCreate = { + virtualPerPhysical?: number; + desiredCount?: number; + desiredVirtualCount?: number; +}; diff --git a/src/collections/configure/types/generative.ts b/src/collections/configure/types/generative.ts new file mode 100644 index 00000000..52f23aaf --- /dev/null +++ b/src/collections/configure/types/generative.ts @@ -0,0 +1,64 @@ +import { + GenerativeAWSConfig, + GenerativeAnyscaleConfig, + GenerativeMistralConfig, + GenerativeOctoAIConfig, + GenerativeOllamaConfig, + GenerativePaLMConfig, +} from '../../index.js'; + +export type GenerativeOpenAIConfigBaseCreate = { + baseURL?: string; + frequencyPenalty?: number; + maxTokens?: number; + presencePenalty?: number; + temperature?: number; + topP?: number; +}; + +export type GenerativeOpenAIConfigCreate = GenerativeOpenAIConfigBaseCreate & { + model?: string; +}; + +export type GenerativeAzureOpenAIConfigCreate = GenerativeOpenAIConfigBaseCreate & { + resourceName: string; + deploymentId: string; +}; + +export type GenerativeCohereConfigCreate = { + k?: number; + maxTokens?: number; + model?: string; + returnLikelihoods?: string; + stopSequences?: string[]; + temperature?: number; +}; + +export type GenerativeAnyscaleConfigCreate = GenerativeAnyscaleConfig; + +export type GenerativeAWSConfigCreate = GenerativeAWSConfig; + +export type GenerativeMistralConfigCreate = GenerativeMistralConfig; + +export type GenerativeOctoAIConfigCreate = GenerativeOctoAIConfig; + +export type GenerativeOllamaConfigCreate = GenerativeOllamaConfig; + +export type GenerativePaLMConfigCreate = GenerativePaLMConfig; + +export type GenerativeConfigCreate = + | GenerativeOpenAIConfigCreate + | GenerativeCohereConfigCreate + | GenerativePaLMConfigCreate + | Record + | undefined; + +export type GenerativeConfigCreateType = G extends 'generative-openai' + ? GenerativeOpenAIConfigCreate + : G extends 'generative-cohere' + ? GenerativeCohereConfigCreate + : G extends 'generative-palm' + ? GenerativePaLMConfigCreate + : G extends 'none' + ? undefined + : Record | undefined; diff --git a/src/collections/configure/types/index.ts b/src/collections/configure/types/index.ts new file mode 100644 index 00000000..4a00d3cf --- /dev/null +++ b/src/collections/configure/types/index.ts @@ -0,0 +1,4 @@ +export * from './base.js'; +export * from './generative.js'; +export * from './vectorIndex.js'; +export * from './vectorizer.js'; diff --git a/src/collections/configure/types/vectorIndex.ts b/src/collections/configure/types/vectorIndex.ts new file mode 100644 index 00000000..a703b2d8 --- /dev/null +++ b/src/collections/configure/types/vectorIndex.ts @@ -0,0 +1,146 @@ +import { + BQConfig, + ModuleConfig, + PQConfig, + PQEncoderDistribution, + PQEncoderType, + VectorDistance, + VectorIndexConfigDynamic, + VectorIndexConfigFlat, + VectorIndexConfigHNSW, +} from '../../config/types/index.js'; +import { RecursivePartial } from './base.js'; + +export type QuantizerRecursivePartial = { + [P in keyof T]: P extends 'type' ? T[P] : RecursivePartial | undefined; +}; + +export type PQConfigCreate = QuantizerRecursivePartial; + +export type PQConfigUpdate = { + centroids?: number; + enabled?: boolean; + segments?: number; + trainingLimit?: number; + encoder?: { + type?: PQEncoderType; + distribution?: PQEncoderDistribution; + }; + type: 'pq'; +}; + +export type BQConfigCreate = QuantizerRecursivePartial; + +export type BQConfigUpdate = { + rescoreLimit?: number; + type: 'bq'; +}; + +export type VectorIndexConfigHNSWCreate = RecursivePartial; + +export type VectorIndexConfigDynamicCreate = RecursivePartial; + +export type VectorIndexConfigDymamicUpdate = RecursivePartial; + +export type VectorIndexConfigHNSWUpdate = { + dynamicEfMin?: number; + dynamicEfMax?: number; + dynamicEfFactor?: number; + ef?: number; + flatSearchCutoff?: number; + quantizer?: PQConfigUpdate | BQConfigUpdate; + vectorCacheMaxObjects?: number; +}; + +export type VectorIndexConfigCreateType = I extends 'hnsw' + ? VectorIndexConfigHNSWCreate | undefined + : I extends 'flat' + ? VectorIndexConfigFlatCreate | undefined + : I extends 'dynamic' + ? VectorIndexConfigDynamicCreate | undefined + : I extends string + ? Record + : never; + +export type VectorIndexConfigFlatCreate = RecursivePartial; + +export type VectorIndexConfigFlatUpdate = { + quantizer?: BQConfigUpdate; + vectorCacheMaxObjects?: number; +}; + +export type VectorIndexConfigCreate = + | VectorIndexConfigFlatCreate + | VectorIndexConfigHNSWCreate + | VectorIndexConfigDynamicCreate + | Record + | undefined; + +export type VectorIndexConfigUpdate = + | VectorIndexConfigFlatUpdate + | VectorIndexConfigHNSWUpdate + | VectorIndexConfigDymamicUpdate + | Record + | undefined; + +export type VectorIndexConfigUpdateType = I extends 'hnsw' + ? VectorIndexConfigHNSWUpdate + : I extends 'flat' + ? VectorIndexConfigFlatUpdate + : I extends 'dynamic' + ? VectorIndexConfigDymamicUpdate + : I extends string + ? Record + : never; + +export type LegacyVectorizerConfigUpdate = + | ModuleConfig<'flat', VectorIndexConfigFlatUpdate> + | ModuleConfig<'hnsw', VectorIndexConfigHNSWUpdate> + | ModuleConfig>; + +export type VectorIndexConfigHNSWCreateOptions = { + /** The interval in seconds at which to clean up the index. Default is 300. */ + cleanupIntervalSeconds?: number; + /** The distance metric to use. Default is 'cosine'. */ + distanceMetric?: VectorDistance; + /** The dynamic ef factor. Default is 8. */ + dynamicEfFactor?: number; + /** The dynamic ef max. Default is 500. */ + dynamicEfMax?: number; + /** The dynamic ef min. Default is 100. */ + dynamicEfMin?: number; + /** The ef parameter. Default is -1. */ + ef?: number; + /** The ef construction parameter. Default is 128. */ + efConstruction?: number; + /** The flat search cutoff. Default is 40000. */ + flatSearchCutoff?: number; + /** The maximum number of connections. Default is 64. */ + maxConnections?: number; + /** The quantizer configuration to use. Use `vectorIndex.quantizer.bq` or `vectorIndex.quantizer.pq` to make one. */ + quantizer?: PQConfigCreate | BQConfigCreate; + /** Whether to skip the index. Default is false. */ + skip?: boolean; + /** The maximum number of objects to cache in the vector cache. Default is 1000000000000. */ + vectorCacheMaxObjects?: number; +}; + +export type VectorIndexConfigFlatCreateOptions = { + /** The distance metric to use. Default is 'cosine'. */ + distanceMetric?: VectorDistance; + /** The maximum number of objects to cache in the vector cache. Default is 1000000000000. */ + vectorCacheMaxObjects?: number; + /** The quantizer configuration to use. Default is `bq`. */ + quantizer?: BQConfigCreate; +}; + +export type VectorIndexConfigDynamicCreateOptions = { + /** The distance metric to use. Default is 'cosine'. */ + distanceMetric?: VectorDistance; + /** The threshold at which to . Default is 0. */ + threshold?: number; + /** The HNSW configuration of the dynamic index. Use `configure.vectorIndex.hnsw` to make one or supply the type directly. */ + hnsw?: ModuleConfig<'hnsw', VectorIndexConfigHNSWCreate | undefined> | VectorIndexConfigHNSWCreateOptions; + /** The flat configuration of the dynamic index. Use `configure.vectorIndex.flat` to make one or supply the type directly. */ + flat?: ModuleConfig<'flat', VectorIndexConfigFlatCreate | undefined> | VectorIndexConfigFlatCreateOptions; +}; diff --git a/src/collections/configure/types/vectorizer.ts b/src/collections/configure/types/vectorizer.ts new file mode 100644 index 00000000..b314fbaf --- /dev/null +++ b/src/collections/configure/types/vectorizer.ts @@ -0,0 +1,195 @@ +import { + Img2VecNeuralConfig, + ModuleConfig, + Multi2VecField, + Ref2VecCentroidConfig, + Text2VecAWSConfig, + Text2VecAzureOpenAIConfig, + Text2VecCohereConfig, + Text2VecContextionaryConfig, + Text2VecGPT4AllConfig, + Text2VecHuggingFaceConfig, + Text2VecJinaConfig, + Text2VecOllamaConfig, + Text2VecOpenAIConfig, + Text2VecPalmConfig, + Text2VecTransformersConfig, + Text2VecVoyageAIConfig, + VectorIndexType, + Vectorizer, + VectorizerConfigType, +} from '../../config/types/index.js'; +import { PrimitiveKeys } from '../../types/internal.js'; +import { VectorIndexConfigCreateType, VectorIndexConfigUpdateType } from './vectorIndex.js'; + +export type VectorizerCreateOptions = { + sourceProperties?: P; + vectorIndexConfig?: ModuleConfig>; + vectorizerConfig?: ModuleConfig>; +}; + +export type VectorizerUpdateOptions = { + name?: N; + vectorIndexConfig: ModuleConfig>; +}; + +export type VectorConfigCreate< + P, + N extends string | undefined, + I extends VectorIndexType, + V extends Vectorizer +> = { + name: N; + properties?: P[]; + vectorizer: ModuleConfig>; + vectorIndex: ModuleConfig>; +}; + +export type VectorConfigUpdate = { + name: N; + vectorIndex: ModuleConfig>; +}; + +export type VectorizersConfigCreate = + | VectorConfigCreate, undefined, VectorIndexType, Vectorizer> + | VectorConfigCreate, string, VectorIndexType, Vectorizer>[]; + +export type ConfigureNonTextVectorizerOptions< + N extends string | undefined, + I extends VectorIndexType, + V extends Vectorizer +> = VectorizerConfigCreateType & { + name?: N; + vectorIndexConfig?: ModuleConfig>; +}; + +export type ConfigureTextVectorizerOptions< + T, + N extends string | undefined, + I extends VectorIndexType, + V extends Vectorizer +> = VectorizerConfigCreateType & { + name?: N; + sourceProperties?: PrimitiveKeys[]; + vectorIndexConfig?: ModuleConfig>; +}; + +export type Img2VecNeuralConfigCreate = Img2VecNeuralConfig; + +/** The configuration for the `multi2vec-clip` vectorizer. */ +export type Multi2VecClipConfigCreate = { + /** The image fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + imageFields?: string[] | Multi2VecField[]; + /** The inference url to use where API requests should go. */ + inferenceUrl?: string; + /** The text fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + textFields?: string[] | Multi2VecField[]; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** The configuration for the `multi2vec-bind` vectorizer. */ +export type Multi2VecBindConfigCreate = { + /** The audio fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + audioFields?: string[] | Multi2VecField[]; + /** The depth fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + depthFields?: string[] | Multi2VecField[]; + /** The image fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + imageFields?: string[] | Multi2VecField[]; + /** The IMU fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + IMUFields?: string[] | Multi2VecField[]; + /** The text fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + textFields?: string[] | Multi2VecField[]; + /** The thermal fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + thermalFields?: string[] | Multi2VecField[]; + /** The video fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + videoFields?: string[] | Multi2VecField[]; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +/** The configuration for the `multi2vec-palm` vectorizer. */ +export type Multi2VecPalmConfigCreate = { + /** The project id of the palm model. */ + projectId: string; + /** Where the model runs */ + location: string; + /** The image fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + imageFields?: string[] | Multi2VecField[]; + /** The text fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + textFields?: string[] | Multi2VecField[]; + /** The video fields to use in vectorization. Can be string of `Multi2VecField` type. If string, weight 0 will be assumed. */ + videoFields?: string[] | Multi2VecField[]; + /** The model ID to use. */ + modelId?: string; + /** The number of dimensions to use. */ + dimensions?: number; + /** Whether to vectorize the collection name. */ + vectorizeCollectionName?: boolean; +}; + +export type Ref2VecCentroidConfigCreate = Ref2VecCentroidConfig; + +export type Text2VecAWSConfigCreate = Text2VecAWSConfig; + +export type Text2VecAzureOpenAIConfigCreate = Text2VecAzureOpenAIConfig; + +export type Text2VecCohereConfigCreate = Text2VecCohereConfig; + +export type Text2VecContextionaryConfigCreate = Text2VecContextionaryConfig; + +export type Text2VecGPT4AllConfigCreate = Text2VecGPT4AllConfig; + +export type Text2VecHuggingFaceConfigCreate = Text2VecHuggingFaceConfig; + +export type Text2VecJinaConfigCreate = Text2VecJinaConfig; + +export type Text2VecOllamaConfigCreate = Text2VecOllamaConfig; + +export type Text2VecOpenAIConfigCreate = Text2VecOpenAIConfig; + +export type Text2VecPalmConfigCreate = Text2VecPalmConfig; + +export type Text2VecTransformersConfigCreate = Text2VecTransformersConfig; + +export type Text2VecVoyageAIConfigCreate = Text2VecVoyageAIConfig; + +export type VectorizerConfigCreateType = V extends 'img2vec-neural' + ? Img2VecNeuralConfigCreate | undefined + : V extends 'multi2vec-clip' + ? Multi2VecClipConfigCreate | undefined + : V extends 'multi2vec-bind' + ? Multi2VecBindConfigCreate | undefined + : V extends 'multi2vec-palm' + ? Multi2VecPalmConfigCreate + : V extends 'ref2vec-centroid' + ? Ref2VecCentroidConfigCreate + : V extends 'text2vec-aws' + ? Text2VecAWSConfigCreate + : V extends 'text2vec-contextionary' + ? Text2VecContextionaryConfigCreate | undefined + : V extends 'text2vec-cohere' + ? Text2VecCohereConfigCreate | undefined + : V extends 'text2vec-gpt4all' + ? Text2VecGPT4AllConfigCreate | undefined + : V extends 'text2vec-huggingface' + ? Text2VecHuggingFaceConfigCreate | undefined + : V extends 'text2vec-jina' + ? Text2VecJinaConfigCreate | undefined + : V extends 'text2vec-ollama' + ? Text2VecOllamaConfigCreate | undefined + : V extends 'text2vec-openai' + ? Text2VecOpenAIConfigCreate | undefined + : V extends 'text2vec-azure-openai' + ? Text2VecAzureOpenAIConfigCreate + : V extends 'text2vec-palm' + ? Text2VecPalmConfigCreate | undefined + : V extends 'text2vec-transformers' + ? Text2VecTransformersConfigCreate | undefined + : V extends 'text2vec-voyageai' + ? Text2VecVoyageAIConfigCreate | undefined + : V extends 'none' + ? {} + : V extends undefined + ? undefined + : never; diff --git a/src/collections/configure/unit.test.ts b/src/collections/configure/unit.test.ts new file mode 100644 index 00000000..35c0d319 --- /dev/null +++ b/src/collections/configure/unit.test.ts @@ -0,0 +1,1272 @@ +import { + GenerativeAWSConfig, + GenerativeAnyscaleConfig, + GenerativeAzureOpenAIConfig, + GenerativeCohereConfig, + GenerativeMistralConfig, + GenerativeOctoAIConfig, + GenerativeOllamaConfig, + GenerativeOpenAIConfig, + GenerativePaLMConfig, + ModuleConfig, + VectorConfigCreate, +} from '../types/index.js'; +import { configure } from './index.js'; +import { + InvertedIndexConfigCreate, + MultiTenancyConfigCreate, + ReplicationConfigCreate, + ShardingConfigCreate, + VectorIndexConfigFlatCreate, + VectorIndexConfigHNSWCreate, +} from './types/index.js'; + +describe('Unit testing of the configure factory class', () => { + it('should create the correct InvertedIndexConfig type with all values', () => { + const config = configure.invertedIndex({ + bm25b: 0.5, + bm25k1: 1.5, + cleanupIntervalSeconds: 120, + indexTimestamps: true, + indexPropertyLength: true, + indexNullState: true, + stopwordsPreset: 'none', + stopwordsAdditions: ['a', 'b'], + stopwordsRemovals: ['c', 'd'], + }); + expect(config).toEqual({ + bm25: { + b: 0.5, + k1: 1.5, + }, + cleanupIntervalSeconds: 120, + indexTimestamps: true, + indexPropertyLength: true, + indexNullState: true, + stopwords: { + additions: ['a', 'b'], + preset: 'none', + removals: ['c', 'd'], + }, + }); + }); + + it('should create the correct MultiTenancyConfig type with defaults', () => { + const config = configure.multiTenancy(); + expect(config).toEqual({ + autoTenantActivation: false, + autoTenantCreation: false, + enabled: true, + }); + }); + + it('should create the correct MultiTenancyConfig type with all values', () => { + const config = configure.multiTenancy({ + autoTenantActivation: true, + autoTenantCreation: true, + enabled: false, + }); + expect(config).toEqual({ + autoTenantActivation: true, + autoTenantCreation: true, + enabled: false, + }); + }); + + it('should create the correct ReplicationConfig type with all values', () => { + const config = configure.replication({ + factor: 2, + }); + expect(config).toEqual({ + factor: 2, + }); + }); + + it('should create the correct ShardingConfig type with all values', () => { + const config = configure.sharding({ + virtualPerPhysical: 256, + desiredCount: 2, + desiredVirtualCount: 256, + }); + expect(config).toEqual({ + virtualPerPhysical: 256, + desiredCount: 2, + desiredVirtualCount: 256, + }); + }); + + describe('using the vectorIndex namespace', () => { + it('should create the correct HNSW VectorIndexConfig type with defaults', () => { + const config = configure.vectorIndex.hnsw({ quantizer: configure.vectorIndex.quantizer.pq() }); + expect(config).toEqual>({ + name: 'hnsw', + config: { + quantizer: { + type: 'pq', + }, + }, + }); + }); + + it('should create the correct HNSW VectorIndexConfig type with all values', () => { + const config = configure.vectorIndex.hnsw({ + cleanupIntervalSeconds: 120, + distanceMetric: 'dot', + dynamicEfFactor: 16, + dynamicEfMax: 1000, + dynamicEfMin: 200, + ef: 100, + efConstruction: 256, + flatSearchCutoff: 80000, + maxConnections: 128, + quantizer: configure.vectorIndex.quantizer.pq({ + bitCompression: true, + centroids: 512, + encoder: { + distribution: 'normal', + type: 'tile', + }, + segments: 1, + trainingLimit: 200000, + }), + skip: true, + vectorCacheMaxObjects: 2000000000000, + }); + expect(config).toEqual>({ + name: 'hnsw', + config: { + cleanupIntervalSeconds: 120, + distance: 'dot', + dynamicEfFactor: 16, + dynamicEfMax: 1000, + dynamicEfMin: 200, + ef: 100, + efConstruction: 256, + flatSearchCutoff: 80000, + maxConnections: 128, + quantizer: { + bitCompression: true, + centroids: 512, + encoder: { + distribution: 'normal', + type: 'tile', + }, + segments: 1, + trainingLimit: 200000, + type: 'pq', + }, + skip: true, + vectorCacheMaxObjects: 2000000000000, + }, + }); + }); + + it('should create the correct flat VectorIndexConfig type with defaults', () => { + const config = configure.vectorIndex.flat({ quantizer: configure.vectorIndex.quantizer.bq() }); + expect(config).toEqual>({ + name: 'flat', + config: { + quantizer: { + type: 'bq', + }, + }, + }); + }); + }); + + it('should create the correct flat VectorIndexConfig type with all values', () => { + const config = configure.vectorIndex.flat({ + distanceMetric: 'cosine', + vectorCacheMaxObjects: 1000000000, + quantizer: configure.vectorIndex.quantizer.bq({ + cache: true, + rescoreLimit: 100, + }), + }); + expect(config).toEqual>({ + name: 'flat', + config: { + distance: 'cosine', + vectorCacheMaxObjects: 1000000000, + quantizer: { + cache: true, + rescoreLimit: 100, + type: 'bq', + }, + }, + }); + }); +}); + +describe('Unit testing of the vectorizer factory class', () => { + it('should create the correct Img2VecNeuralConfig type with all values', () => { + const config = configure.vectorizer.img2VecNeural({ + name: 'test', + imageFields: ['field1', 'field2'], + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'img2vec-neural', + config: { + imageFields: ['field1', 'field2'], + }, + }, + }); + }); + + it('should create the correct Multi2VecClipConfig type with defaults', () => { + const config = configure.vectorizer.multi2VecClip(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-clip', + config: undefined, + }, + }); + }); + + it('should create the correct Multi2VecClipConfig type with all values', () => { + const config = configure.vectorizer.multi2VecClip({ + name: 'test', + imageFields: ['field1', 'field2'], + textFields: ['field3', 'field4'], + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-clip', + config: { + imageFields: ['field1', 'field2'], + textFields: ['field3', 'field4'], + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Multi2VecClipConfig type with all values and weights', () => { + const config = configure.vectorizer.multi2VecClip({ + name: 'test', + imageFields: [ + { name: 'field1', weight: 0.1 }, + { name: 'field2', weight: 0.2 }, + ], + textFields: [ + { name: 'field3', weight: 0.3 }, + { name: 'field4', weight: 0.4 }, + ], + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-clip', + config: { + imageFields: ['field1', 'field2'], + textFields: ['field3', 'field4'], + vectorizeCollectionName: true, + weights: { + imageFields: [0.1, 0.2], + textFields: [0.3, 0.4], + }, + }, + }, + }); + }); + + it('should create the correct Multi2VecBindConfig type with defaults', () => { + const config = configure.vectorizer.multi2VecBind(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-bind', + config: undefined, + }, + }); + }); + + it('should create the correct Multi2VecBindConfig type with all values', () => { + const config = configure.vectorizer.multi2VecBind({ + name: 'test', + audioFields: ['field1', 'field2'], + depthFields: ['field3', 'field4'], + imageFields: ['field5', 'field6'], + IMUFields: ['field7', 'field8'], + textFields: ['field9', 'field10'], + thermalFields: ['field11', 'field12'], + videoFields: ['field13', 'field14'], + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-bind', + config: { + audioFields: ['field1', 'field2'], + depthFields: ['field3', 'field4'], + imageFields: ['field5', 'field6'], + IMUFields: ['field7', 'field8'], + textFields: ['field9', 'field10'], + thermalFields: ['field11', 'field12'], + videoFields: ['field13', 'field14'], + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Multi2VecBindConfig type with all values and weights', () => { + const config = configure.vectorizer.multi2VecBind({ + name: 'test', + audioFields: [ + { name: 'field1', weight: 0.1 }, + { name: 'field2', weight: 0.2 }, + ], + depthFields: [ + { name: 'field3', weight: 0.3 }, + { name: 'field4', weight: 0.4 }, + ], + imageFields: [ + { name: 'field5', weight: 0.5 }, + { name: 'field6', weight: 0.6 }, + ], + IMUFields: [ + { name: 'field7', weight: 0.7 }, + { name: 'field8', weight: 0.8 }, + ], + textFields: [ + { name: 'field9', weight: 0.9 }, + { name: 'field10', weight: 1.0 }, + ], + thermalFields: [ + { name: 'field11', weight: 1.1 }, + { name: 'field12', weight: 1.2 }, + ], + videoFields: [ + { name: 'field13', weight: 1.3 }, + { name: 'field14', weight: 1.4 }, + ], + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-bind', + config: { + audioFields: ['field1', 'field2'], + depthFields: ['field3', 'field4'], + imageFields: ['field5', 'field6'], + IMUFields: ['field7', 'field8'], + textFields: ['field9', 'field10'], + thermalFields: ['field11', 'field12'], + videoFields: ['field13', 'field14'], + vectorizeCollectionName: true, + weights: { + audioFields: [0.1, 0.2], + depthFields: [0.3, 0.4], + imageFields: [0.5, 0.6], + IMUFields: [0.7, 0.8], + textFields: [0.9, 1.0], + thermalFields: [1.1, 1.2], + videoFields: [1.3, 1.4], + }, + }, + }, + }); + }); + + it('should create the correct Multi2VecPalmConfig type with defaults', () => { + const config = configure.vectorizer.multi2VecPalm({ + projectId: 'project-id', + location: 'location', + }); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-palm', + config: { + projectId: 'project-id', + location: 'location', + }, + }, + }); + }); + + it('should create the correct Multi2VecPalmConfig type with all values', () => { + const config = configure.vectorizer.multi2VecPalm({ + name: 'test', + projectId: 'project-id', + imageFields: ['field1', 'field2'], + textFields: ['field3', 'field4'], + videoFields: ['field5', 'field6'], + location: 'location', + modelId: 'model-id', + dimensions: 256, + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-palm', + config: { + projectId: 'project-id', + imageFields: ['field1', 'field2'], + textFields: ['field3', 'field4'], + videoFields: ['field5', 'field6'], + location: 'location', + modelId: 'model-id', + dimensions: 256, + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Multi2VecPalmConfig type with all values and weights', () => { + const config = configure.vectorizer.multi2VecPalm({ + name: 'test', + projectId: 'project-id', + imageFields: [ + { name: 'field1', weight: 0.1 }, + { name: 'field2', weight: 0.2 }, + ], + textFields: [ + { name: 'field3', weight: 0.3 }, + { name: 'field4', weight: 0.4 }, + ], + videoFields: [ + { name: 'field5', weight: 0.5 }, + { name: 'field6', weight: 0.6 }, + ], + location: 'location', + modelId: 'model-id', + dimensions: 256, + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'multi2vec-palm', + config: { + projectId: 'project-id', + imageFields: ['field1', 'field2'], + textFields: ['field3', 'field4'], + videoFields: ['field5', 'field6'], + location: 'location', + modelId: 'model-id', + dimensions: 256, + vectorizeCollectionName: true, + weights: { + imageFields: [0.1, 0.2], + textFields: [0.3, 0.4], + videoFields: [0.5, 0.6], + }, + }, + }, + }); + }); + + it('should create the correct Text2VecAWSConfig type with defaults', () => { + const config = configure.vectorizer.text2VecAWS({ + region: 'region', + service: 'service', + }); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-aws', + config: { + region: 'region', + service: 'service', + }, + }, + }); + }); + + it('should create the correct Text2VecAWSConfig type with all values', () => { + const config = configure.vectorizer.text2VecAWS({ + name: 'test', + endpoint: 'endpoint', + model: 'model', + region: 'region', + service: 'service', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-aws', + config: { + endpoint: 'endpoint', + model: 'model', + region: 'region', + service: 'service', + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecAzureOpenAIConfig type with defaults', () => { + const config = configure.vectorizer.text2VecAzureOpenAI({ + deploymentID: 'deployment-id', + resourceName: 'resource-name', + }); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-azure-openai', + config: { + deploymentID: 'deployment-id', + resourceName: 'resource-name', + }, + }, + }); + }); + + it('should create the correct Text2VecAzureOpenAIConfig type with all values', () => { + const config = configure.vectorizer.text2VecAzureOpenAI({ + name: 'test', + baseURL: 'base-url', + deploymentID: 'deployment-id', + resourceName: 'resource-name', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-azure-openai', + config: { + baseURL: 'base-url', + deploymentID: 'deployment-id', + resourceName: 'resource-name', + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecCohereConfig type with defaults', () => { + const config = configure.vectorizer.text2VecCohere(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-cohere', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecCohereConfig type with all values', () => { + const config = configure.vectorizer.text2VecCohere({ + name: 'test', + baseURL: 'base-url', + model: 'model', + truncate: true, + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-cohere', + config: { + baseURL: 'base-url', + model: 'model', + truncate: true, + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecContextionaryConfig type with defaults', () => { + const config = configure.vectorizer.text2VecContextionary(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-contextionary', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecContextionaryConfig type with all values', () => { + const config = configure.vectorizer.text2VecContextionary({ + name: 'test', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-contextionary', + config: { + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecGPT4AllConfig type with defaults', () => { + const config = configure.vectorizer.text2VecGPT4All(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-gpt4all', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecGPT4AllConfig type with all values', () => { + const config = configure.vectorizer.text2VecGPT4All({ + name: 'test', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-gpt4all', + config: { + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecHuggingFaceConfig type with defaults', () => { + const config = configure.vectorizer.text2VecHuggingFace(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-huggingface', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecHuggingFaceConfig type with all values', () => { + const config = configure.vectorizer.text2VecHuggingFace({ + name: 'test', + endpointURL: 'endpoint-url', + model: 'model', + passageModel: 'passage-model', + queryModel: 'query-model', + useCache: true, + useGPU: true, + waitForModel: true, + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-huggingface', + config: { + endpointURL: 'endpoint-url', + model: 'model', + passageModel: 'passage-model', + queryModel: 'query-model', + useCache: true, + useGPU: true, + waitForModel: true, + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecJinaConfig type with defaults', () => { + const config = configure.vectorizer.text2VecJina(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-jina', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecJinaConfig type with all values', () => { + const config = configure.vectorizer.text2VecJina({ + name: 'test', + model: 'model', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-jina', + config: { + model: 'model', + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecOllamaConfig type with defaults', () => { + const config = configure.vectorizer.text2VecOllama(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-ollama', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecOllamaConfig type with all values', () => { + const config = configure.vectorizer.text2VecOllama({ + name: 'test', + apiEndpoint: 'api-endpoint', + model: 'model', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-ollama', + config: { + apiEndpoint: 'api-endpoint', + model: 'model', + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecOpenAIConfig type with defaults', () => { + const config = configure.vectorizer.text2VecOpenAI(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-openai', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecOpenAIConfig type with all values', () => { + const config = configure.vectorizer.text2VecOpenAI({ + name: 'test', + baseURL: 'base-url', + dimensions: 256, + model: 'model', + modelVersion: 'model-version', + type: 'type', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-openai', + config: { + baseURL: 'base-url', + dimensions: 256, + model: 'model', + modelVersion: 'model-version', + type: 'type', + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecPalmConfig type with defaults', () => { + const config = configure.vectorizer.text2VecPalm(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-palm', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecPalmConfig type with all values', () => { + const config = configure.vectorizer.text2VecPalm({ + name: 'test', + apiEndpoint: 'api-endpoint', + modelId: 'model-id', + projectId: 'project-id', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-palm', + config: { + apiEndpoint: 'api-endpoint', + modelId: 'model-id', + projectId: 'project-id', + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecTransformersConfig type with defaults', () => { + const config = configure.vectorizer.text2VecTransformers(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-transformers', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecTransformersConfig type with all values', () => { + const config = configure.vectorizer.text2VecTransformers({ + name: 'test', + poolingStrategy: 'pooling-strategy', + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-transformers', + config: { + poolingStrategy: 'pooling-strategy', + vectorizeCollectionName: true, + }, + }, + }); + }); + + it('should create the correct Text2VecVoyageAIConfig type with defaults', () => { + const config = configure.vectorizer.text2VecVoyageAI(); + expect(config).toEqual>({ + name: undefined, + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-voyageai', + config: undefined, + }, + }); + }); + + it('should create the correct Text2VecVoyageConfig type with all values', () => { + const config = configure.vectorizer.text2VecVoyageAI({ + name: 'test', + baseURL: 'base-url', + model: 'model', + truncate: true, + vectorizeCollectionName: true, + }); + expect(config).toEqual>({ + name: 'test', + vectorIndex: { + name: 'hnsw', + config: undefined, + }, + vectorizer: { + name: 'text2vec-voyageai', + config: { + baseURL: 'base-url', + model: 'model', + truncate: true, + vectorizeCollectionName: true, + }, + }, + }); + }); +}); + +describe('Unit testing of the generative factory class', () => { + it('should create the correct GenerativeAnyscaleConfig type with required & default values', () => { + const config = configure.generative.anyscale(); + expect(config).toEqual>({ + name: 'generative-anyscale', + config: undefined, + }); + }); + + it('should create the correct GenerativeAnyscaleConfig type with all values', () => { + const config = configure.generative.anyscale({ + model: 'model', + temperature: 0.5, + }); + expect(config).toEqual>({ + name: 'generative-anyscale', + config: { + model: 'model', + temperature: 0.5, + }, + }); + }); + + it('should create the correct GenerativeAWSConfig type with required & default values', () => { + const config = configure.generative.aws({ + region: 'region', + service: 'service', + }); + expect(config).toEqual>({ + name: 'generative-aws', + config: { + region: 'region', + service: 'service', + }, + }); + }); + + it('should create the correct GenerativeAWSConfig type with all values', () => { + const config = configure.generative.aws({ + endpoint: 'endpoint', + model: 'model', + region: 'region', + service: 'service', + }); + expect(config).toEqual>({ + name: 'generative-aws', + config: { + endpoint: 'endpoint', + model: 'model', + region: 'region', + service: 'service', + }, + }); + }); + + it('should create the correct GenerativeAzureOpenAIConfig type with required & default values', () => { + const config = configure.generative.azureOpenAI({ + resourceName: 'resource-name', + deploymentId: 'deployment-id', + }); + expect(config).toEqual>({ + name: 'generative-openai', + config: { + resourceName: 'resource-name', + deploymentId: 'deployment-id', + }, + }); + }); + + it('should create the correct GenerativeAzureOpenAIConfig type with all values', () => { + const config = configure.generative.azureOpenAI({ + resourceName: 'resource-name', + deploymentId: 'deployment-id', + baseURL: 'base-url', + frequencyPenalty: 0.5, + maxTokens: 100, + presencePenalty: 0.3, + temperature: 0.7, + topP: 0.8, + }); + expect(config).toEqual>({ + name: 'generative-openai', + config: { + resourceName: 'resource-name', + deploymentId: 'deployment-id', + baseURL: 'base-url', + frequencyPenaltyProperty: 0.5, + maxTokensProperty: 100, + presencePenaltyProperty: 0.3, + temperatureProperty: 0.7, + topPProperty: 0.8, + }, + }); + }); + + it('should create the correct GenerativeCohereConfig type with required & default values', () => { + const config = configure.generative.cohere(); + expect(config).toEqual>({ + name: 'generative-cohere', + config: undefined, + }); + }); + + it('should create the correct GenerativeCohereConfig type with all values', () => { + const config = configure.generative.cohere({ + k: 5, + maxTokens: 100, + model: 'model', + returnLikelihoods: 'return-likelihoods', + stopSequences: ['stop1', 'stop2'], + temperature: 0.5, + }); + expect(config).toEqual>({ + name: 'generative-cohere', + config: { + kProperty: 5, + maxTokensProperty: 100, + model: 'model', + returnLikelihoodsProperty: 'return-likelihoods', + stopSequencesProperty: ['stop1', 'stop2'], + temperatureProperty: 0.5, + }, + }); + }); + + it('should create the correct GenerativeMistralConfig type with required & default values', () => { + const config = configure.generative.mistral(); + expect(config).toEqual>({ + name: 'generative-mistral', + config: undefined, + }); + }); + + it('should create the correct GenerativeMistralConfig type with all values', () => { + const config = configure.generative.mistral({ + maxTokens: 100, + model: 'model', + temperature: 0.5, + }); + expect(config).toEqual>({ + name: 'generative-mistral', + config: { + maxTokens: 100, + model: 'model', + temperature: 0.5, + }, + }); + }); + + it('should create the correct GenerativeOctoAIConfig type with required & default values', () => { + const config = configure.generative.octoai(); + expect(config).toEqual>({ + name: 'generative-octoai', + config: undefined, + }); + }); + + it('should create the correct GenerativeOctoAIConfig type with all values', () => { + const config = configure.generative.octoai({ + baseURL: 'base-url', + maxTokens: 100, + model: 'model', + temperature: 0.5, + }); + expect(config).toEqual>({ + name: 'generative-octoai', + config: { + baseURL: 'base-url', + maxTokens: 100, + model: 'model', + temperature: 0.5, + }, + }); + }); + + it('should create the correct GenerativeOllamaConfig type with required & default values', () => { + const config = configure.generative.ollama(); + expect(config).toEqual>({ + name: 'generative-ollama', + config: undefined, + }); + }); + + it('should create the correct GenerativeOllamaConfig type with all values', () => { + const config = configure.generative.ollama({ + apiEndpoint: 'api-endpoint', + model: 'model', + }); + expect(config).toEqual>({ + name: 'generative-ollama', + config: { + apiEndpoint: 'api-endpoint', + model: 'model', + }, + }); + }); + + it('should create the correct GenerativeOpenAIConfig type with required & default values', () => { + const config = configure.generative.openAI(); + expect(config).toEqual>({ + name: 'generative-openai', + config: undefined, + }); + }); + + it('should create the correct GenerativeOpenAIConfig type with all values', () => { + const config = configure.generative.openAI({ + baseURL: 'base-url', + frequencyPenalty: 0.5, + maxTokens: 100, + model: 'model', + presencePenalty: 0.3, + temperature: 0.7, + topP: 0.8, + }); + expect(config).toEqual>({ + name: 'generative-openai', + config: { + baseURL: 'base-url', + frequencyPenaltyProperty: 0.5, + maxTokensProperty: 100, + model: 'model', + presencePenaltyProperty: 0.3, + temperatureProperty: 0.7, + topPProperty: 0.8, + }, + }); + }); + + it('should create the correct GenerativePaLMConfig type with required & default values', () => { + const config = configure.generative.palm({ + projectId: 'project-id', + }); + expect(config).toEqual>({ + name: 'generative-palm', + config: { + projectId: 'project-id', + }, + }); + }); + + it('should create the correct GenerativePaLMConfig type with all values', () => { + const config = configure.generative.palm({ + apiEndpoint: 'api-endpoint', + maxOutputTokens: 100, + modelId: 'model-id', + projectId: 'project-id', + temperature: 0.5, + topK: 5, + topP: 0.8, + }); + expect(config).toEqual>({ + name: 'generative-palm', + config: { + apiEndpoint: 'api-endpoint', + maxOutputTokens: 100, + modelId: 'model-id', + projectId: 'project-id', + temperature: 0.5, + topK: 5, + topP: 0.8, + }, + }); + }); +}); diff --git a/src/collections/configure/vectorIndex.ts b/src/collections/configure/vectorIndex.ts new file mode 100644 index 00000000..8ca7b07a --- /dev/null +++ b/src/collections/configure/vectorIndex.ts @@ -0,0 +1,248 @@ +import { ModuleConfig, PQEncoderDistribution, PQEncoderType, VectorDistance } from '../config/types/index.js'; +import { + BQConfigCreate, + BQConfigUpdate, + PQConfigCreate, + PQConfigUpdate, + VectorIndexConfigDynamicCreate, + VectorIndexConfigDynamicCreateOptions, + VectorIndexConfigFlatCreate, + VectorIndexConfigFlatCreateOptions, + VectorIndexConfigFlatUpdate, + VectorIndexConfigHNSWCreate, + VectorIndexConfigHNSWCreateOptions, + VectorIndexConfigHNSWUpdate, +} from './types/index.js'; + +import { parseQuantizer } from './parsing.js'; + +const isModuleConfig = (config: ModuleConfig | C): config is ModuleConfig => { + return config && typeof config === 'object' && 'name' in config && 'config' in config; +}; + +const configure = { + /** + * Create a `ModuleConfig<'flat', VectorIndexConfigFlatCreate | undefined>` object when defining the configuration of the FLAT vector index. + * + * Use this method when defining the `options.vectorIndexConfig` argument of the `configure.vectorizer` method. + * + * @param {VectorIndexConfigFlatCreateOptions} [opts] The options available for configuring the flat vector index. + * @returns {ModuleConfig<'flat', VectorIndexConfigFlatCreate | undefined>} The configuration object. + */ + flat: ( + opts?: VectorIndexConfigFlatCreateOptions + ): ModuleConfig<'flat', VectorIndexConfigFlatCreate | undefined> => { + const { distanceMetric: distance, vectorCacheMaxObjects, quantizer } = opts || {}; + return { + name: 'flat', + config: { + distance, + vectorCacheMaxObjects, + quantizer: parseQuantizer(quantizer), + }, + }; + }, + /** + * Create a `ModuleConfig<'hnsw', VectorIndexConfigHNSWCreate | undefined>` object when defining the configuration of the HNSW vector index. + * + * Use this method when defining the `options.vectorIndexConfig` argument of the `configure.vectorizer` method. + * + * @param {VectorIndexConfigHNSWCreateOptions} [opts] The options available for configuring the HNSW vector index. + * @returns {ModuleConfig<'hnsw', VectorIndexConfigHNSWCreate | undefined>} The configuration object. + */ + hnsw: ( + opts?: VectorIndexConfigHNSWCreateOptions + ): ModuleConfig<'hnsw', VectorIndexConfigHNSWCreate | undefined> => { + const { distanceMetric, ...rest } = opts || {}; + return { + name: 'hnsw', + config: rest + ? { + ...rest, + distance: distanceMetric, + quantizer: parseQuantizer(rest.quantizer), + } + : undefined, + }; + }, + /** + * Create a `ModuleConfig<'dynamic', VectorIndexConfigDynamicCreate | undefined>` object when defining the configuration of the dynamic vector index. + * + * Use this method when defining the `options.vectorIndexConfig` argument of the `configure.vectorizer` method. + * + * @param {VectorIndexConfigDynamicCreateOptions} [opts] The options available for configuring the dynamic vector index. + * @returns {ModuleConfig<'dynamic', VectorIndexConfigDynamicCreate | undefined>} The configuration object. + */ + dynamic: ( + opts?: VectorIndexConfigDynamicCreateOptions + ): ModuleConfig<'dynamic', VectorIndexConfigDynamicCreate | undefined> => { + return { + name: 'dynamic', + config: opts + ? { + distance: opts.distanceMetric, + threshold: opts.threshold, + hnsw: isModuleConfig(opts.hnsw) ? opts.hnsw.config : configure.hnsw(opts.hnsw).config, + flat: isModuleConfig(opts.flat) ? opts.flat.config : configure.flat(opts.flat).config, + } + : undefined, + }; + }, + /** + * Define the quantizer configuration to use when creating a vector index. + */ + quantizer: { + /** + * Create a `BQConfigCreate` object to be used when defining the quantizer configuration of a vector index. + * + * @param {boolean} [options.cache] Whether to cache the quantizer. Default is false. + * @param {number} [options.rescoreLimit] The rescore limit. Default is 1000. + * @returns {BQConfigCreate} The `BQConfigCreate` object. + */ + bq: (options?: { cache?: boolean; rescoreLimit?: number }): BQConfigCreate => { + return { + cache: options?.cache, + rescoreLimit: options?.rescoreLimit, + type: 'bq', + }; + }, + /** + * Create a `PQConfigCreate` object to be used when defining the quantizer configuration of a vector index. + * + * @param {boolean} [options.bitCompression] Whether to use bit compression. + * @param {number} [options.centroids] The number of centroids[. + * @param {PQEncoderDistribution} ]options.encoder.distribution The encoder distribution. + * @param {PQEncoderType} [options.encoder.type] The encoder type. + * @param {number} [options.segments] The number of segments. + * @param {number} [options.trainingLimit] The training limit. + * @returns {PQConfigCreate} The `PQConfigCreate` object. + */ + pq: (options?: { + bitCompression?: boolean; + centroids?: number; + encoder?: { + distribution?: PQEncoderDistribution; + type?: PQEncoderType; + }; + segments?: number; + trainingLimit?: number; + }): PQConfigCreate => { + return { + bitCompression: options?.bitCompression, + centroids: options?.centroids, + encoder: options?.encoder + ? { + distribution: options.encoder.distribution, + type: options.encoder.type, + } + : undefined, + segments: options?.segments, + trainingLimit: options?.trainingLimit, + type: 'pq', + }; + }, + }, +}; + +const reconfigure = { + /** + * Create a `ModuleConfig<'flat', VectorIndexConfigFlatUpdate>` object to update the configuration of the FLAT vector index. + * + * Use this method when defining the `options.vectorIndexConfig` argument of the `reconfigure.vectorizer` method. + * + * @param {VectorDistance} [options.distanceMetric] The distance metric to use. Default is 'cosine'. + * @param {number} [options.vectorCacheMaxObjects] The maximum number of objects to cache in the vector cache. Default is 1000000000000. + * @param {BQConfigCreate} [options.quantizer] The quantizer configuration to use. Default is `bq`. + * @returns {ModuleConfig<'flat', VectorIndexConfigFlatCreate>} The configuration object. + */ + flat: (options: { + vectorCacheMaxObjects?: number; + quantizer?: BQConfigUpdate; + }): ModuleConfig<'flat', VectorIndexConfigFlatUpdate> => { + return { + name: 'flat', + config: { + vectorCacheMaxObjects: options.vectorCacheMaxObjects, + quantizer: parseQuantizer(options.quantizer), + }, + }; + }, + /** + * Create a `ModuleConfig<'hnsw', VectorIndexConfigHNSWCreate>` object to update the configuration of the HNSW vector index. + * + * Use this method when defining the `options.vectorIndexConfig` argument of the `reconfigure.vectorizer` method. + * + * @param {number} [options.dynamicEfFactor] The dynamic ef factor. Default is 8. + * @param {number} [options.dynamicEfMax] The dynamic ef max. Default is 500. + * @param {number} [options.dynamicEfMin] The dynamic ef min. Default is 100. + * @param {number} [options.ef] The ef parameter. Default is -1. + * @param {number} [options.flatSearchCutoff] The flat search cutoff. Default is 40000. + * @param {PQConfigUpdate | BQConfigUpdate} [options.quantizer] The quantizer configuration to use. Use `vectorIndex.quantizer.bq` or `vectorIndex.quantizer.pq` to make one. + * @param {number} [options.vectorCacheMaxObjects] The maximum number of objects to cache in the vector cache. Default is 1000000000000. + * @returns {ModuleConfig<'hnsw', VectorIndexConfigHNSWUpdate>} The configuration object. + */ + hnsw: (options: { + dynamicEfFactor?: number; + dynamicEfMax?: number; + dynamicEfMin?: number; + ef?: number; + flatSearchCutoff?: number; + quantizer?: PQConfigUpdate | BQConfigUpdate; + vectorCacheMaxObjects?: number; + }): ModuleConfig<'hnsw', VectorIndexConfigHNSWUpdate> => { + return { + name: 'hnsw', + config: options, + }; + }, + /** + * Define the quantizer configuration to use when creating a vector index. + */ + quantizer: { + /** + * Create a `BQConfigUpdate` object to be used when defining the quantizer configuration of a vector index. + * + * @param {boolean} [options.cache] Whether to cache the quantizer. Default is false. + * @param {number} [options.rescoreLimit] The rescore limit. Default is 1000. + * @returns {BQConfigCreate} The configuration object. + */ + bq: (options?: { cache?: boolean; rescoreLimit?: number }): BQConfigUpdate => { + return { + ...options, + type: 'bq', + }; + }, + /** + * Create a `PQConfigCreate` object to be used when defining the quantizer configuration of a vector index. + * + * @param {number} [options.centroids] The number of centroids. Default is 256. + * @param {PQEncoderDistribution} [options.pqEncoderDistribution] The encoder distribution. Default is 'log-normal'. + * @param {PQEncoderType} [options.pqEncoderType] The encoder type. Default is 'kmeans'. + * @param {number} [options.segments] The number of segments. Default is 0. + * @param {number} [options.trainingLimit] The training limit. Default is 100000. + * @returns {PQConfigUpdate} The configuration object. + */ + pq: (options?: { + centroids?: number; + pqEncoderDistribution?: PQEncoderDistribution; + pqEncoderType?: PQEncoderType; + segments?: number; + trainingLimit?: number; + }): PQConfigUpdate => { + const { pqEncoderDistribution, pqEncoderType, ...rest } = options || {}; + return { + ...rest, + encoder: + pqEncoderDistribution || pqEncoderType + ? { + distribution: pqEncoderDistribution, + type: pqEncoderType, + } + : undefined, + type: 'pq', + }; + }, + }, +}; + +export { configure, reconfigure }; diff --git a/src/collections/configure/vectorizer.ts b/src/collections/configure/vectorizer.ts new file mode 100644 index 00000000..a7243b71 --- /dev/null +++ b/src/collections/configure/vectorizer.ts @@ -0,0 +1,470 @@ +import { + Multi2VecBindConfig, + Multi2VecClipConfig, + Multi2VecField, + Multi2VecPalmConfig, + VectorIndexType, + Vectorizer, + VectorizerConfigType, +} from '../config/types/index.js'; +import { VectorConfigCreate, VectorIndexConfigCreateType, VectorizerCreateOptions } from '../index.js'; +import { PrimitiveKeys } from '../types/internal.js'; +import { ConfigureNonTextVectorizerOptions, ConfigureTextVectorizerOptions } from './types/index.js'; + +const makeVectorizer = ( + name: N | undefined, + options?: VectorizerCreateOptions[], I, V> +) => { + return { + name: name as N, + properties: options?.sourceProperties, + vectorIndex: options?.vectorIndexConfig + ? options.vectorIndexConfig + : { name: 'hnsw' as I, config: undefined as VectorIndexConfigCreateType }, + vectorizer: options?.vectorizerConfig + ? options.vectorizerConfig + : { name: 'none' as V, config: undefined as VectorizerConfigType }, + }; +}; + +const mapMulti2VecField = (field: string | Multi2VecField): Multi2VecField => { + if (typeof field === 'string') { + return { name: field }; + } + return field; +}; + +const formatMulti2VecFields = ( + weights: Record, + key: string, + fields?: Multi2VecField[] +): Record => { + if (fields !== undefined && fields.length > 0) { + weights[key] = fields.filter((f) => f.weight !== undefined).map((f) => f.weight as number); + if (weights[key].length === 0) { + delete weights[key]; + } + } + return weights; +}; + +export const vectorizer = { + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'none'`. + * + * @param {ConfigureNonTextVectorizerOptions} [opts] The configuration options for the `none` vectorizer. + * @returns {VectorConfigCreate[], N, I, 'none'>} The configuration object. + */ + none: ( + opts?: ConfigureNonTextVectorizerOptions + ): VectorConfigCreate => { + const { name, vectorIndexConfig } = opts || {}; + return makeVectorizer(name, { vectorIndexConfig }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'img2vec-neural'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/img2vec-neural) for detailed usage. + * + * @param {ConfigureNonTextVectorizerOptions} [opts] The configuration options for the `img2vec-neural` vectorizer. + * @returns {VectorConfigCreate[], N, I, 'img2vec-neural'>} The configuration object. + */ + img2VecNeural: ( + opts: ConfigureNonTextVectorizerOptions + ): VectorConfigCreate => { + const { name, vectorIndexConfig, ...config } = opts; + return makeVectorizer(name, { + vectorIndexConfig, + vectorizerConfig: { + name: 'img2vec-neural', + config: config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'multi2vec-bind'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-bind) for detailed usage. + * + * @param {ConfigureNonTextVectorizerOptions} [opts] The configuration options for the `multi2vec-bind` vectorizer. + * @returns {VectorConfigCreate[], N, I, 'multi2vec-bind'>} The configuration object. + */ + multi2VecBind: ( + opts?: ConfigureNonTextVectorizerOptions + ): VectorConfigCreate => { + const { name, vectorIndexConfig, ...config } = opts || {}; + const audioFields = config.audioFields?.map(mapMulti2VecField); + const depthFields = config.depthFields?.map(mapMulti2VecField); + const imageFields = config.imageFields?.map(mapMulti2VecField); + const IMUFields = config.IMUFields?.map(mapMulti2VecField); + const textFields = config.textFields?.map(mapMulti2VecField); + const thermalFields = config.thermalFields?.map(mapMulti2VecField); + const videoFields = config.videoFields?.map(mapMulti2VecField); + let weights: Multi2VecClipConfig['weights'] = {}; + weights = formatMulti2VecFields(weights, 'audioFields', audioFields); + weights = formatMulti2VecFields(weights, 'depthFields', depthFields); + weights = formatMulti2VecFields(weights, 'imageFields', imageFields); + weights = formatMulti2VecFields(weights, 'IMUFields', IMUFields); + weights = formatMulti2VecFields(weights, 'textFields', textFields); + weights = formatMulti2VecFields(weights, 'thermalFields', thermalFields); + weights = formatMulti2VecFields(weights, 'videoFields', videoFields); + return makeVectorizer(name, { + vectorIndexConfig, + vectorizerConfig: { + name: 'multi2vec-bind', + config: + Object.keys(config).length === 0 + ? undefined + : { + ...config, + audioFields: audioFields?.map((f) => f.name), + depthFields: depthFields?.map((f) => f.name), + imageFields: imageFields?.map((f) => f.name), + IMUFields: IMUFields?.map((f) => f.name), + textFields: textFields?.map((f) => f.name), + thermalFields: thermalFields?.map((f) => f.name), + videoFields: videoFields?.map((f) => f.name), + weights: Object.keys(weights).length === 0 ? undefined : weights, + }, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'multi2vec-clip'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-clip) for detailed usage. + * + * @param {ConfigureNonTextVectorizerOptions} [opts] The configuration options for the `multi2vec-clip` vectorizer. + * @returns {VectorConfigCreate[], N, I, 'multi2vec-clip'>} The configuration object. + */ + multi2VecClip: ( + opts?: ConfigureNonTextVectorizerOptions + ): VectorConfigCreate => { + const { name, vectorIndexConfig, ...config } = opts || {}; + const imageFields = config.imageFields?.map(mapMulti2VecField); + const textFields = config.textFields?.map(mapMulti2VecField); + let weights: Multi2VecBindConfig['weights'] = {}; + weights = formatMulti2VecFields(weights, 'imageFields', imageFields); + weights = formatMulti2VecFields(weights, 'textFields', textFields); + return makeVectorizer(name, { + vectorIndexConfig, + vectorizerConfig: { + name: 'multi2vec-clip', + config: + Object.keys(config).length === 0 + ? undefined + : { + ...config, + imageFields: imageFields?.map((f) => f.name), + textFields: textFields?.map((f) => f.name), + weights: Object.keys(weights).length === 0 ? undefined : weights, + }, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'multi2vec-palm'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-palm) for detailed usage. + * + * @param {ConfigureNonTextVectorizerOptions} opts The configuration options for the `multi2vec-palm` vectorizer. + * @returns {VectorConfigCreate[], N, I, 'multi2vec-palm'>} The configuration object. + */ + multi2VecPalm: ( + opts: ConfigureNonTextVectorizerOptions + ): VectorConfigCreate => { + const { name, vectorIndexConfig, ...config } = opts; + const imageFields = config.imageFields?.map(mapMulti2VecField); + const textFields = config.textFields?.map(mapMulti2VecField); + const videoFields = config.videoFields?.map(mapMulti2VecField); + let weights: Multi2VecPalmConfig['weights'] = {}; + weights = formatMulti2VecFields(weights, 'imageFields', imageFields); + weights = formatMulti2VecFields(weights, 'textFields', textFields); + weights = formatMulti2VecFields(weights, 'videoFields', videoFields); + return makeVectorizer(name, { + vectorIndexConfig, + vectorizerConfig: { + name: 'multi2vec-palm', + config: { + ...config, + imageFields: imageFields?.map((f) => f.name), + textFields: textFields?.map((f) => f.name), + videoFields: videoFields?.map((f) => f.name), + weights: Object.keys(weights).length === 0 ? undefined : weights, + }, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'ref2vec-centroid'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/ref2vec-centroid) for detailed usage. + * + * @param {ConfigureNonTextVectorizerOptions} opts The configuration options for the `ref2vec-centroid` vectorizer. + * @returns {VectorConfigCreate} The configuration object. + */ + ref2VecCentroid: ( + opts: ConfigureNonTextVectorizerOptions + ): VectorConfigCreate => { + const { name, vectorIndexConfig, ...config } = opts; + return makeVectorizer(name, { + vectorIndexConfig, + vectorizerConfig: { + name: 'ref2vec-centroid', + config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-aws'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-aws) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} opts The configuration options for the `text2vec-aws` vectorizer. + * @returns { VectorConfigCreate, N, I, 'text2vec-aws'>} The configuration object. + */ + text2VecAWS: ( + opts: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-aws'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-aws', + config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-azure-openai'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-openai) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} opts The configuration options for the `text2vec-azure-openai` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-azure-openai'>} The configuration object. + */ + text2VecAzureOpenAI: ( + opts: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-azure-openai'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-azure-openai', + config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-cohere'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-cohere) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration options for the `text2vec-cohere` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-cohere'>} The configuration object. + */ + text2VecCohere: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-cohere'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-cohere', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-contextionary'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-contextionary) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration for the `text2vec-contextionary` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-contextionary'>} The configuration object. + */ + text2VecContextionary: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-contextionary'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-contextionary', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-gpt4all'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-gpt4all) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration for the `text2vec-contextionary` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-gpt4all'>} The configuration object. + */ + text2VecGPT4All: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-gpt4all'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-gpt4all', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-huggingface'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-huggingface) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration for the `text2vec-contextionary` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-huggingface'>} The configuration object. + */ + text2VecHuggingFace: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-huggingface'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-huggingface', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-jina'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-jina) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration for the `text2vec-jina` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-jina'>} The configuration object. + */ + text2VecJina: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-jina'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-jina', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-openai'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-openai) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration for the `text2vec-openai` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-openai'>} The configuration object. + */ + text2VecOpenAI: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-openai'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-openai', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-ollama'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-ollama) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration for the `text2vec-ollama` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-ollama'>} The configuration object. + */ + text2VecOllama: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-ollama'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-ollama', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-palm'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-palm) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} opts The configuration for the `text2vec-palm` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-palm'>} The configuration object. + */ + text2VecPalm: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-palm'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-palm', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-transformers'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-transformers) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration for the `text2vec-transformers` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-transformers'>} The configuration object. + */ + text2VecTransformers: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-transformers'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-transformers', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, + /** + * Create a `VectorConfigCreate` object with the vectorizer set to `'text2vec-voyageai'`. + * + * See the [documentation](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-voyageai) for detailed usage. + * + * @param {ConfigureTextVectorizerOptions} [opts] The configuration for the `text2vec-voyageai` vectorizer. + * @returns {VectorConfigCreate, N, I, 'text2vec-voyageai'>} The configuration object. + */ + text2VecVoyageAI: ( + opts?: ConfigureTextVectorizerOptions + ): VectorConfigCreate, N, I, 'text2vec-voyageai'> => { + const { name, sourceProperties, vectorIndexConfig, ...config } = opts || {}; + return makeVectorizer(name, { + sourceProperties, + vectorIndexConfig, + vectorizerConfig: { + name: 'text2vec-voyageai', + config: Object.keys(config).length === 0 ? undefined : config, + }, + }); + }, +}; diff --git a/src/collections/data/index.ts b/src/collections/data/index.ts new file mode 100644 index 00000000..0ad0f0b0 --- /dev/null +++ b/src/collections/data/index.ts @@ -0,0 +1,332 @@ +import Connection from '../../connection/grpc.js'; + +import { buildRefsPath } from '../../batch/path.js'; +import { Checker, ConsistencyLevel } from '../../data/index.js'; +import { ObjectsPath, ReferencesPath } from '../../data/path.js'; +import { BatchReference, BatchReferenceResponse, WeaviateObject } from '../../openapi/types.js'; +import { DbVersionSupport } from '../../utils/dbVersion.js'; +import { Deserialize } from '../deserialize/index.js'; +import { FilterValue } from '../filters/index.js'; +import { referenceToBeacons } from '../references/utils.js'; +import { DataGuards, Serialize } from '../serialize/index.js'; +import { + BatchObjectsReturn, + BatchReferencesReturn, + DataObject, + DeleteManyReturn, + ErrorReference, + NonReferenceInputs, + Properties, + ReferenceInput, + ReferenceInputs, + Vectors, +} from '../types/index.js'; + +/** The available options to the `data.deleteMany` method. */ +export type DeleteManyOptions = { + /** Whether to return verbose information about the operation */ + verbose?: V; + /** Whether to perform a dry run of the operation */ + dryRun?: boolean; +}; + +/** The available options to the `data.insert` method. */ +export type InsertObject = { + /** The ID of the object to be inserted. If not provided, a new ID will be generated. */ + id?: string; + /** The properties of the object to be inserted */ + properties?: NonReferenceInputs; + /** The references of the object to be inserted */ + references?: ReferenceInputs; + /** The vector(s) of the object to be inserted */ + vectors?: number[] | Vectors; +}; + +/** The arguments of the `data.referenceX` methods */ +export type ReferenceArgs = { + /** The ID of the object that will have the reference */ + fromUuid: string; + /** The property of the object that will have the reference */ + fromProperty: string; + /** The object(s) to reference */ + to: ReferenceInput; +}; + +/** The available options to the `data.replace` method. */ +export type ReplaceObject = { + /** The ID of the object to be replaced */ + id: string; + /** The properties of the object to be replaced */ + properties?: NonReferenceInputs; + /** The references of the object to be replaced */ + references?: ReferenceInputs; + //* The vector(s) to replace in the object */ + vectors?: number[] | Vectors; +}; + +/** The available options to the `data.update` method. */ +export type UpdateObject = { + /** The ID of the object to be updated */ + id: string; + /** The properties of the object to be updated */ + properties?: NonReferenceInputs; + /** The references of the object to be updated */ + references?: ReferenceInputs; + //* The vector(s) to update in the object */ + vectors?: number[] | Vectors; +}; + +export interface Data { + deleteById: (id: string) => Promise; + deleteMany: ( + where: FilterValue, + opts?: DeleteManyOptions + ) => Promise>; + exists: (id: string) => Promise; + /** + * Insert a single object into the collection. + * + * If you don't provide any options to the function, then an empty object will be created. + * + * @param {InsertArgs | NonReferenceInputs} [args] The object to insert. If an `id` is provided, it will be used as the object's ID. If not, a new ID will be generated. + * @returns {Promise} The ID of the inserted object. + */ + insert: (obj?: InsertObject | NonReferenceInputs) => Promise; + /** + * Insert multiple objects into the collection. + * + * This object does not perform any batching for you. It sends all objects in a single request to Weaviate. + * + * @param {(DataObject | NonReferenceInputs)[]} objects The objects to insert. + * @returns {Promise>} The result of the batch insert. + */ + insertMany: (objects: (DataObject | NonReferenceInputs)[]) => Promise>; + /** + * Create a reference between an object in this collection and any other object in Weaviate. + * + * @param {ReferenceArgs

} args The reference to create. + * @returns {Promise} + */ + referenceAdd:

(args: ReferenceArgs

) => Promise; + /** + * Create multiple references between an object in this collection and any other object in Weaviate. + * + * This method is optimized for performance and sends all references in a single request. + * + * @param {ReferenceArgs

[]} refs The references to create. + * @returns {Promise} The result of the batch reference creation. + */ + referenceAddMany:

(refs: ReferenceArgs

[]) => Promise; + /** + * Delete a reference between an object in this collection and any other object in Weaviate. + * + * @param {ReferenceArgs

} args The reference to delete. + * @returns {Promise} + */ + referenceDelete:

(args: ReferenceArgs

) => Promise; + /** + * Replace a reference between an object in this collection and any other object in Weaviate. + * + * @param {ReferenceArgs

} args The reference to replace. + * @returns {Promise} + */ + referenceReplace:

(args: ReferenceArgs

) => Promise; + /** + * Replace an object in the collection. + * + * This is equivalent to a PUT operation. + * + * @param {ReplaceOptions} [opts] The object attributes to replace. + * @returns {Promise} + */ + replace: (obj: ReplaceObject) => Promise; + /** + * Update an object in the collection. + * + * This is equivalent to a PATCH operation. + * + * @param {UpdateArgs} [opts] The object attributes to replace. + * @returns {Promise} + */ + update: (obj: UpdateObject) => Promise; +} + +interface IBuilder { + withConsistencyLevel(consistencyLevel: ConsistencyLevel): this; + withTenant(tenant: string): this; +} + +const addContext = ( + builder: B, + consistencyLevel?: ConsistencyLevel, + tenant?: string +): B => { + if (consistencyLevel) { + builder = builder.withConsistencyLevel(consistencyLevel); + } + if (tenant) { + builder = builder.withTenant(tenant); + } + return builder; +}; + +const data = ( + connection: Connection, + name: string, + dbVersionSupport: DbVersionSupport, + consistencyLevel?: ConsistencyLevel, + tenant?: string +): Data => { + const objectsPath = new ObjectsPath(dbVersionSupport); + const referencesPath = new ReferencesPath(dbVersionSupport); + + const parseObject = async (object?: InsertObject): Promise> => { + if (!object) { + return {} as WeaviateObject; + } + const obj: WeaviateObject = { + id: object.id, + properties: object.properties + ? (Serialize.restProperties(object.properties, object.references) as T) + : undefined, + }; + if (Array.isArray(object.vectors)) { + const supportsNamedVectors = await dbVersionSupport.supportsNamedVectors(); + if (supportsNamedVectors.supports) { + obj.vectors = { default: object.vectors }; + } else { + obj.vector = object.vectors; + } + } else if (object.vectors) { + obj.vectors = object.vectors; + } + return obj; + }; + + return { + deleteById: (id: string): Promise => + objectsPath + .buildDelete(id, name, consistencyLevel, tenant) + .then((path) => connection.delete(path, undefined, false)) + .then(() => true), + deleteMany: ( + where: FilterValue, + opts?: DeleteManyOptions + ): Promise> => + connection + .batch(name, consistencyLevel, tenant) + .then((batch) => + batch.withDelete({ + filters: Serialize.filtersGRPC(where), + dryRun: opts?.dryRun, + verbose: opts?.verbose, + }) + ) + .then((reply) => Deserialize.deleteMany(reply, opts?.verbose)), + exists: (id: string): Promise => + addContext( + new Checker(connection, objectsPath).withId(id).withClassName(name), + consistencyLevel, + tenant + ).do(), + insert: (obj?: InsertObject | NonReferenceInputs): Promise => + Promise.all([ + objectsPath.buildCreate(consistencyLevel), + parseObject( + obj ? (DataGuards.isDataObject(obj) ? obj : ({ properties: obj } as InsertObject)) : obj + ), + ]).then(([path, object]) => + connection + .postReturn, Required>>(path, { + class: name, + tenant: tenant, + ...object, + }) + .then((obj) => obj.id) + ), + insertMany: (objects: (DataObject | NonReferenceInputs)[]): Promise> => + connection.batch(name, consistencyLevel).then(async (batch) => { + const supportsNamedVectors = await dbVersionSupport.supportsNamedVectors(); + const serialized = await Serialize.batchObjects(name, objects, supportsNamedVectors.supports, tenant); + const start = Date.now(); + const reply = await batch.withObjects({ objects: serialized.mapped }); + const end = Date.now(); + return Deserialize.batchObjects(reply, serialized.batch, serialized.mapped, end - start); + }), + referenceAdd:

(args: ReferenceArgs

): Promise => + referencesPath + .build(args.fromUuid, name, args.fromProperty, consistencyLevel, tenant) + .then((path) => + Promise.all(referenceToBeacons(args.to).map((beacon) => connection.postEmpty(path, beacon))) + ) + .then(() => {}), + referenceAddMany:

(refs: ReferenceArgs

[]): Promise => { + const path = buildRefsPath( + new URLSearchParams(consistencyLevel ? { consistency_level: consistencyLevel } : {}) + ); + const references: BatchReference[] = []; + refs.forEach((ref) => { + referenceToBeacons(ref.to).forEach((beacon) => { + references.push({ + from: `weaviate://localhost/${name}/${ref.fromUuid}/${ref.fromProperty}`, + to: beacon.beacon, + tenant: tenant, + }); + }); + }); + const start = Date.now(); + return connection + .postReturn(path, references) + .then((res) => { + const end = Date.now(); + const errors: Record = {}; + res.forEach((entry, idx) => { + if (entry.result?.status === 'FAILED') { + errors[idx] = { + message: entry.result?.errors?.error?.[0].message + ? entry.result?.errors?.error?.[0].message + : 'unknown error', + reference: references[idx], + }; + } + }); + return { + elapsedSeconds: end - start, + errors: errors, + hasErrors: Object.keys(errors).length > 0, + }; + }); + }, + referenceDelete:

(args: ReferenceArgs

): Promise => + referencesPath + .build(args.fromUuid, name, args.fromProperty, consistencyLevel, tenant) + .then((path) => + Promise.all(referenceToBeacons(args.to).map((beacon) => connection.delete(path, beacon, false))) + ) + .then(() => {}), + referenceReplace:

(args: ReferenceArgs

): Promise => + referencesPath + .build(args.fromUuid, name, args.fromProperty, consistencyLevel, tenant) + .then((path) => connection.put(path, referenceToBeacons(args.to), false)), + replace: (obj: ReplaceObject): Promise => + Promise.all([objectsPath.buildUpdate(obj.id, name, consistencyLevel), parseObject(obj)]).then( + ([path, object]) => + connection.put(path, { + class: name, + tenant: tenant, + ...object, + }) + ), + update: (obj: UpdateObject): Promise => + Promise.all([objectsPath.buildUpdate(obj.id, name, consistencyLevel), parseObject(obj)]).then( + ([path, object]) => + connection.patch(path, { + class: name, + tenant: tenant, + ...object, + }) + ), + }; +}; + +export default data; diff --git a/src/collections/data/integration.test.ts b/src/collections/data/integration.test.ts new file mode 100644 index 00000000..e7722c42 --- /dev/null +++ b/src/collections/data/integration.test.ts @@ -0,0 +1,1002 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import { v4 } from 'uuid'; +import { WeaviateUnsupportedFeatureError } from '../../errors.js'; +import weaviate, { WeaviateClient } from '../../index.js'; +import { GeoCoordinate, PhoneNumber } from '../../proto/v1/properties.js'; +import { Collection } from '../collection/index.js'; +import { CrossReference, CrossReferences, Reference } from '../references/index.js'; +import { DataObject, WeaviateObject } from '../types/index.js'; + +type TestCollectionData = { + testProp: string; + testProps?: string[]; + testProp2?: number; + ref?: CrossReference; + geo?: GeoCoordinate; + phone?: PhoneNumber; + nested?: { + testProp: string; + }; +}; + +describe('Testing of the collection.data methods with a single target reference', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionData'; + + const existingID = v4(); + const toBeReplacedID = v4(); + const toBeUpdatedID = v4(); + const toBeDeletedID = v4(); + const nonExistingID = v4(); + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + tokenization: 'field', + }, + { + name: 'testProp2', + dataType: 'int', + }, + { + name: 'testProps', + dataType: 'text[]', + }, + { + name: 'geo', + dataType: 'geoCoordinates', + }, + { + name: 'phone', + dataType: 'phoneNumber', + }, + ], + references: [ + { + name: 'ref', + targetCollection: collectionName, + }, + ], + }) + .then(async (collection) => { + await collection.data.insert({ + properties: { + testProp: 'Gon get delet', + }, + id: toBeDeletedID, + }); + return collection.data.insertMany([ + { properties: { testProp: 'DELETE ME' } }, + { properties: { testProp: 'DELETE ME' } }, + { properties: { testProp: 'DELETE ME' } }, + { + properties: { + testProp: 'EXISTING', + testProp2: 1, + }, + id: existingID, + }, + { + properties: { + testProp: 'REPLACE ME', + testProp2: 1, + }, + id: toBeReplacedID, + }, + { + properties: { + testProp: 'UPDATE ME', + testProp2: 1, + }, + id: toBeUpdatedID, + }, + ]); + }) + .then(() => { + const one = collection.data.referenceAdd({ + fromProperty: 'ref', + fromUuid: toBeReplacedID, + to: Reference.to(toBeUpdatedID), + }); + const two = collection.data.referenceAdd({ + fromProperty: 'ref', + fromUuid: toBeUpdatedID, + to: Reference.to(toBeReplacedID), + }); + return Promise.all([one, two]); + }) + .catch((err) => { + throw err; + }); + }); + + it('should be able to insert an object without an id', async () => { + const insert = await collection.data.insert({ + properties: { + testProp: 'test', + }, + }); + expect(insert).toBeDefined(); + }); + + it('should be able to insert an object with an id', async () => { + const id = v4(); + const insert = await collection.data.insert({ + properties: { + testProp: 'test', + }, + id: id, + }); + expect(insert).toEqual(id); + }); + + it('should be able to delete an object by id', async () => { + const result = await collection.data.deleteById(toBeDeletedID); + expect(result).toBeTruthy(); + const obj = await collection.query.fetchObjectById(toBeDeletedID); + expect(obj).toBeNull(); + }); + + it('should be able to delete many objects with a filter', async () => { + const result = await collection.data.deleteMany( + collection.filter.byProperty('testProp').equal('DELETE ME') + ); + expect(result.failed).toEqual(0); + expect(result.matches).toEqual(3); + expect(result.successful).toEqual(3); + }); + + it('should be able to replace an object', async () => { + const obj = await collection.query.fetchObjectById(toBeReplacedID); + expect(obj?.properties.testProp).toEqual('REPLACE ME'); + expect(obj?.properties.testProp2).toEqual(1); + return collection.data + .replace({ + id: toBeReplacedID, + properties: { + testProp: 'REPLACED', + }, + }) + .then(async () => { + const obj = await collection.query.fetchObjectById(toBeReplacedID); + expect(obj?.properties.testProp).toEqual('REPLACED'); + expect(obj?.properties.testProp2).toBeUndefined(); + }); + }); + + it('should be able to update an object', async () => { + const obj = await collection.query.fetchObjectById(toBeUpdatedID); + expect(obj?.properties.testProp).toEqual('UPDATE ME'); + expect(obj?.properties.testProp2).toEqual(1); + return collection.data + .update({ + id: toBeUpdatedID, + properties: { + testProp: 'UPDATED', + }, + }) + .then(async () => { + const obj = await collection.query.fetchObjectById(toBeUpdatedID); + expect(obj?.properties.testProp).toEqual('UPDATED'); + expect(obj?.properties.testProp2).toEqual(1); + }); + }); + + it('should be able to insert many (10) objects at once', () => { + const objects: DataObject[] = []; + for (let j = 0; j < 10; j++) { + objects.push({ + properties: { + testProp: 'testInsertMany10', + testProps: [], // test empty array + }, + }); + } + return collection.data.insertMany(objects).then(async (insert) => { + expect(insert.hasErrors).toBeFalsy(); + expect(insert.allResponses.length).toEqual(10); + expect(Object.values(insert.errors).length).toEqual(0); + expect(Object.values(insert.uuids).length).toEqual(10); + const query = await collection.query.fetchObjects({ limit: 100 }); + expect(query.objects.filter((obj) => Object.values(insert.uuids).includes(obj.uuid)).length).toEqual( + 10 + ); + expect(query.objects.filter((obj) => obj.properties.testProp === 'testInsertMany10').length).toEqual( + 10 + ); + expect(query.objects.filter((obj) => obj.properties.testProps?.length === 0).length).toEqual(10); + }); + }); + + it('should be able to insert many (100) objects at once', () => { + const objects: DataObject[] = []; + for (let j = 0; j < 100; j++) { + objects.push({ + properties: { + testProp: 'testInsertMany100', + }, + references: { + ref: existingID, + }, + }); + } + return collection.data.insertMany(objects).then(async (insert) => { + expect(insert.hasErrors).toBeFalsy(); + expect(insert.allResponses.length).toEqual(100); + expect(Object.values(insert.errors).length).toEqual(0); + expect(Object.values(insert.uuids).length).toEqual(100); + const query = await collection.query.fetchObjects({ limit: 1000 }); + expect(query.objects.filter((obj) => Object.values(insert.uuids).includes(obj.uuid)).length).toEqual( + 100 + ); + expect(query.objects.filter((obj) => obj.properties.testProp === 'testInsertMany100').length).toEqual( + 100 + ); + }); + }); + + it('should be able to insert many (1000) objects at once', () => { + const objects: any[] = []; + for (let j = 0; j < 1000; j++) { + objects.push({ + testProp: 'testInsertMany1000', + }); + } + return collection.data.insertMany(objects).then(async (insert) => { + expect(insert.hasErrors).toBeFalsy(); + expect(insert.allResponses.length).toEqual(1000); + expect(Object.values(insert.errors).length).toEqual(0); + expect(Object.values(insert.uuids).length).toEqual(1000); + const query = await collection.query.fetchObjects({ limit: 2000 }); + expect(query.objects.filter((obj) => Object.values(insert.uuids).includes(obj.uuid)).length).toEqual( + 1000 + ); + expect(query.objects.filter((obj) => obj.properties.testProp === 'testInsertMany1000').length).toEqual( + 1000 + ); + }); + }); + + it('should be able to insert a reference between two objects', () => { + return Promise.all([ + collection.data.referenceAdd({ + fromProperty: 'ref', + fromUuid: existingID, + to: Reference.to(existingID), // add using Reference.to syntax + }), + collection.data.referenceAdd({ + fromProperty: 'ref', + fromUuid: existingID, + to: toBeUpdatedID, // add using string syntax + }), + collection.data.referenceAdd({ + fromProperty: 'ref', + fromUuid: existingID, + to: [toBeReplacedID], // add using string array syntax + }), + ]) + .then(() => + collection.query.fetchObjectById(existingID, { + returnReferences: [{ linkOn: 'ref' }], + }) + ) + .then((obj) => { + const ids = obj?.references?.ref?.objects.map((o) => o.uuid); + expect(obj).not.toBeNull(); + expect(ids).toContain(existingID); + expect(ids).toContain(toBeUpdatedID); + expect(ids).toContain(toBeReplacedID); + }); + }); + + it('should be able to replace a reference between two objects', () => { + const replaceOne = () => + collection.data.referenceReplace({ + fromProperty: 'ref', + fromUuid: toBeReplacedID, + to: Reference.to(existingID), // replace using Reference.to syntax + }); + const replaceTwo = () => + collection.data.referenceReplace({ + fromProperty: 'ref', + fromUuid: toBeReplacedID, + to: toBeUpdatedID, // replace using string syntax + }); + const replaceThree = () => + collection.data.referenceReplace({ + fromProperty: 'ref', + fromUuid: toBeReplacedID, + to: [toBeReplacedID], // replace using string array syntax + }); + const get = () => + collection.query.fetchObjectById(toBeReplacedID, { + returnReferences: [{ linkOn: 'ref' }], + }); + const assert = (obj: WeaviateObject | null, id: string) => { + expect(obj).not.toBeNull(); + expect(obj?.references?.ref?.objects[0].uuid).toEqual(id); + }; + return replaceOne() + .then(get) + .then((obj) => assert(obj, existingID)) + .then(replaceTwo) + .then(get) + .then((obj) => assert(obj, toBeUpdatedID)) + .then(replaceThree) + .then(get) + .then((obj) => assert(obj, toBeReplacedID)); + }); + + it('should be able to delete a reference between two objects', () => { + return Promise.all([ + collection.data.referenceDelete({ + fromProperty: 'ref', + fromUuid: toBeUpdatedID, + to: Reference.to(existingID), + }), + collection.data.referenceDelete({ + fromProperty: 'ref', + fromUuid: toBeUpdatedID, + to: toBeUpdatedID, + }), + collection.data.referenceDelete({ + fromProperty: 'ref', + fromUuid: toBeUpdatedID, + to: [toBeReplacedID], + }), + ]) + .then(() => + collection.query.fetchObjectById(toBeUpdatedID, { + returnReferences: [{ linkOn: 'ref' }], + }) + ) + .then((obj) => { + expect(obj).not.toBeNull(); + expect(obj?.references?.ref?.objects).toEqual([]); + }); + }); + + it('should be able to add many references in batch', () => { + return collection.data + .referenceAddMany([ + { + fromProperty: 'ref', + fromUuid: existingID, + to: Reference.to([toBeReplacedID]), + }, + { + fromProperty: 'ref', + fromUuid: existingID, + to: [toBeUpdatedID], + }, + // { + // fromProperty: 'ref', + // fromUuid: toBeUpdatedID, + // reference: Reference.to({ uuids: existingID }), + // }, + // currently causes bug in Weaviate due to first deleting last reference in above test and then adding new one + ]) + .then(async (res) => { + if (res.hasErrors) console.error(res.errors); + expect(res.hasErrors).toEqual(false); + const obj1 = await collection.query.fetchObjectById(existingID, { + returnReferences: [{ linkOn: 'ref' }], + }); + expect(obj1).not.toBeNull(); + expect(obj1?.references?.ref?.objects.map((o) => o.uuid)).toContain(toBeReplacedID); + expect(obj1?.references?.ref?.objects.map((o) => o.uuid)).toContain(toBeUpdatedID); + // const obj2 = await collection.query.fetchObjectById({ + // id: toBeUpdatedID + // }); + // expect(obj2.properties.ref?.objects).toEqual([]); + // expect(obj2.properties.ref?.targetCollection).toEqual(collectionName); + // expect(obj2.properties.ref?.uuids?.includes(existingID)).toEqual(true); + }); + }); + + it.skip('should return appropriate errors from add many references in batch', () => { + // skipped because Weaviate doesn't error if the to UUID doesn't exist + // it does in referenceAdd, but not in referenceAddMany, due to the /batch implementation + // difference on the server, needs fixing + return collection.data + .referenceAddMany([ + { + fromProperty: 'ref', + fromUuid: existingID, + to: Reference.to([nonExistingID]), + }, + ]) + .then((res) => { + expect(res.hasErrors).toEqual(true); + expect(Object.keys(res.errors).length).toEqual(1); + }); + }); + + it('should be able to add objects with a geo coordinate', async () => { + const obj = { + testProp: 'test', + geo: { + latitude: 1, + longitude: 1, + }, + }; + const id = await collection.data.insert(obj); + const res = await collection.data.insertMany([obj]); + const obj1 = await collection.query.fetchObjectById(id, { + returnProperties: ['geo'], + }); + const obj2 = await collection.query.fetchObjectById(res.uuids[0], { + returnProperties: ['geo'], + }); + expect(obj1?.properties.geo).toEqual({ + latitude: 1, + longitude: 1, + }); + expect(obj2?.properties.geo).toEqual({ + latitude: 1, + longitude: 1, + }); + }); + + it('should be able to add objects with a phone number', async () => { + const obj = { + testProp: 'test', + phone: { + number: '+441612345000', + }, + }; + const id = await collection.data.insert(obj); + const res = await collection.data.insertMany([obj]); + const obj1 = await collection.query.fetchObjectById(id, { + returnProperties: ['phone'], + }); + const obj2 = await collection.query.fetchObjectById(res.uuids[0], { + returnProperties: ['phone'], + }); + expect(obj1?.properties.phone?.input).toEqual('+441612345000'); + expect(obj2?.properties.phone?.input).toEqual('+441612345000'); + }); + + it('should be able to verify that an object exists', async () => { + const exists = await collection.data.exists(existingID); + expect(exists).toBeTruthy(); + }); +}); + +describe('Testing of the collection.data methods with a multi target reference', () => { + let client: WeaviateClient; + let collectionOne: Collection; + let collectionTwo: Collection; + + const classNameOne = 'TestCollectionDataMultiOne'; + const classNameTwo = 'TestCollectionDataMultiTwo'; + + type TestCollectionDataMultiOne = { + one: string; + }; + type TestCollectionDataMultiTwo = { + two: string; + refs: CrossReferences<[TestCollectionDataMultiOne, TestCollectionDataMultiTwo]>; + }; + + let oneId: string; + let twoId: string; + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collectionOne = client.collections.get(classNameOne); + collectionTwo = client.collections.get(classNameTwo); + oneId = await client.collections + .create({ + name: classNameOne, + properties: [ + { + name: 'one', + dataType: 'text', + tokenization: 'field', + }, + ], + }) + .then(() => collectionOne.data.insert({ one: 'one' })); + twoId = await client.collections + .create({ + name: classNameTwo, + properties: [ + { + name: 'two', + dataType: 'text', + tokenization: 'field', + }, + ], + references: [ + { + name: 'refs', + targetCollections: [classNameOne, classNameTwo], + }, + ], + }) + .then(() => collectionTwo.data.insert({ two: 'two' })); + }); + + afterAll(() => client.collections.deleteAll()); + + it('should be able to insert an object with a multi target reference', async () => { + const id = await collectionTwo.data.insert({ + properties: { two: 'multi1' }, + references: { + refs: [ + Reference.toMultiTarget(oneId, classNameOne), + { targetCollection: classNameTwo, uuids: twoId }, + ], + }, + }); + await collectionTwo.data.insertMany([ + { + properties: { two: 'multi2' }, + references: { + refs: [ + Reference.toMultiTarget([twoId], classNameTwo), + { targetCollection: classNameOne, uuids: [oneId] }, + ], + }, + }, + ]); + await collectionTwo.query + .fetchObjectById(id, { returnReferences: [{ linkOn: 'refs', targetCollection: classNameOne }] }) + .then((obj) => expect(obj!.references!.refs.objects[0].uuid).toEqual(oneId)); + await collectionTwo.query + .fetchObjectById(id, { returnReferences: [{ linkOn: 'refs', targetCollection: classNameTwo }] }) + .then((obj) => expect(obj!.references!.refs.objects[0].uuid).toEqual(twoId)); + }); +}); + +describe('Testing of the collection.data.insertMany method with all possible types', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionData'; + let id: string; + + type Primitives = { + text?: string; + textArr?: string[]; + int?: number; + intArr?: number[]; + number?: number; + numberArr?: number[]; + bool?: boolean; + boolArr?: boolean[]; + date?: Date; + dateArr?: Date[]; + }; + + type A = Primitives; + + type B = { + child: A; + }; + + type TestCollectionData = Primitives & { + self?: CrossReference; + geo?: GeoCoordinate; + phone?: PhoneNumber; + child?: A; + children?: B[]; + }; + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + const primitives = [ + { + name: 'text' as const, + dataType: 'text' as const, + }, + { + name: 'textArr' as const, + dataType: 'text[]' as const, + }, + { + name: 'int' as const, + dataType: 'int' as const, + }, + { + name: 'intArr' as const, + dataType: 'int[]' as const, + }, + { + name: 'number' as const, + dataType: 'number' as const, + }, + { + name: 'numberArr' as const, + dataType: 'number[]' as const, + }, + { + name: 'bool' as const, + dataType: 'boolean' as const, + }, + { + name: 'boolArr' as const, + dataType: 'boolean[]' as const, + }, + { + name: 'date' as const, + dataType: 'date' as const, + }, + { + name: 'dateArr' as const, + dataType: 'date[]' as const, + }, + ]; + collection = await client.collections.create({ + name: collectionName, + properties: [ + ...primitives, + { + name: 'geo' as const, + dataType: 'geoCoordinates' as const, + }, + { + name: 'phone' as const, + dataType: 'phoneNumber' as const, + }, + { + name: 'child', + dataType: 'object', + nestedProperties: primitives, + }, + { + name: 'children', + dataType: 'object[]', + nestedProperties: [ + { + name: 'child', + dataType: 'object', + nestedProperties: primitives, + }, + ], + }, + ], + references: [ + { + name: 'self', + targetCollection: collectionName, + }, + ], + }); + id = await collection.data.insert(); + }); + + it('should insert many objects with all possible types', async () => { + const date1 = new Date(); + const date2 = new Date(); + const primitives = { + text: 'text', + textArr: ['textArr'], + int: 1, + intArr: [1], + number: 1.1, + numberArr: [1.1], + bool: true, + boolArr: [true], + date: date1, + dateArr: [date2], + }; + const objects: DataObject[] = [ + { + properties: { + ...primitives, + geo: { + latitude: 1, + longitude: 1, + }, + phone: { + number: '+441612345000', + }, + child: primitives, + children: [{ child: primitives }], + }, + references: { + self: id, + }, + }, + ]; + const insert = await collection.data.insertMany(objects); + if (insert.hasErrors) console.error(JSON.stringify(insert.errors)); + expect(insert.hasErrors).toBeFalsy(); + expect(insert.allResponses.length).toEqual(1); + expect(Object.values(insert.errors).length).toEqual(0); + expect(Object.values(insert.uuids).length).toEqual(1); + const obj = await collection.query.fetchObjectById(insert.uuids[0], { + returnReferences: [{ linkOn: 'self' }], + }); + expect(obj?.properties).toEqual({ + text: 'text', + textArr: ['textArr'], + int: 1, + intArr: [1], + number: 1.1, + numberArr: [1.1], + bool: true, + boolArr: [true], + date: date1, + dateArr: [date2], + geo: { + latitude: 1, + longitude: 1, + }, + phone: { + countryCode: 44, + defaultCountry: '', + input: '+441612345000', + internationalFormatted: '+44 161 234 5000', + national: 1612345000, + nationalFormatted: '0161 234 5000', + valid: true, + }, + child: { + text: 'text', + textArr: ['textArr'], + int: 1, + intArr: [1], + number: 1.1, + numberArr: [1.1], + bool: true, + boolArr: [true], + date: date1, + dateArr: [date2], + }, + children: [ + { + child: { + text: 'text', + textArr: ['textArr'], + int: 1, + intArr: [1], + number: 1.1, + numberArr: [1.1], + bool: true, + boolArr: [true], + date: date1, + dateArr: [date2], + }, + }, + ], + }); + expect(obj?.references?.self?.objects[0].uuid).toEqual(id); + }); +}); + +describe('Testing of the collection.data methods with bring your own multi vectors', () => { + let client: WeaviateClient; + let collection: Collection; + let uuid: string; + beforeAll(async () => { + client = await weaviate.connectToLocal(); + const query = () => + client.collections.create({ + name: 'TestCollectionDataMultiVectors', + properties: [ + { + name: 'text', + dataType: 'text', + }, + ], + vectorizers: [ + weaviate.configure.vectorizer.none({ name: 'one' }), + weaviate.configure.vectorizer.none({ name: 'two' }), + ], + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + collection = await query(); + }); + + afterAll(() => client.collections.delete('TestCollectionDataMultiVectors')); + + it('should be able to insert an object with multi vectors', async () => { + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + return; + } + uuid = await collection.data.insert({ + properties: { + text: 'test', + }, + vectors: { + one: [1, 2, 3], + two: [4, 5, 6], + }, + }); + }); + + it('should be able to fetch the object with multi vectors', async () => { + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + return; + } + const obj = await collection.query.fetchObjectById(uuid, { + includeVector: true, + }); + expect(obj?.vectors?.one).toEqual([1, 2, 3]); + expect(obj?.vectors?.two).toEqual([4, 5, 6]); + }); + + it('should be able to update the vectors of an object', async () => { + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + return; + } + await collection.data.update({ + id: uuid, + vectors: { + one: [7, 8, 9], + two: [10, 11, 12], + }, + }); + const obj = await collection.query.fetchObjectById(uuid, { + includeVector: true, + }); + expect(obj?.vectors?.one).toEqual([7, 8, 9]); + expect(obj?.vectors?.two).toEqual([10, 11, 12]); + }); + + it('should be able to replace the vectors of an object', async () => { + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + return; + } + await collection.data.replace({ + id: uuid, + vectors: { + one: [7, 8, 9], + two: [10, 11, 12], + }, + }); + const obj = await collection.query.fetchObjectById(uuid, { + includeVector: true, + }); + expect(obj?.vectors?.one).toEqual([7, 8, 9]); + expect(obj?.vectors?.two).toEqual([10, 11, 12]); + }); + + it('should be able to insert many objects with multi vectors', async () => { + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + return; + } + const objects = [ + { + properties: { + text: 'test', + }, + vectors: { + one: [1, 2, 3], + two: [4, 5, 6], + }, + }, + { + properties: { + text: 'test', + }, + vectors: { + one: [7, 8, 9], + two: [10, 11, 12], + }, + }, + ]; + const insert = await collection.data.insertMany(objects); + expect(insert.hasErrors).toBeFalsy(); + expect(insert.allResponses.length).toEqual(2); + expect(Object.values(insert.errors).length).toEqual(0); + expect(Object.values(insert.uuids).length).toEqual(2); + const obj1 = await collection.query.fetchObjectById(insert.uuids[0], { + includeVector: true, + }); + expect(obj1?.vectors?.one).toEqual([1, 2, 3]); + expect(obj1?.vectors?.two).toEqual([4, 5, 6]); + const obj2 = await collection.query.fetchObjectById(insert.uuids[1], { + includeVector: true, + }); + expect(obj2?.vectors?.one).toEqual([7, 8, 9]); + expect(obj2?.vectors?.two).toEqual([10, 11, 12]); + }); +}); + +describe('Testing of the collection.data methods with a vector index', () => { + let client: WeaviateClient; + let collection: Collection; + let uuid: string; + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = await client.collections.create({ + name: 'TestCollectionDataVectorizer', + properties: [ + { + name: 'text', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.none(), + }); + }); + + afterAll(() => client.collections.delete('TestCollectionDataVectorizer')); + + it('should be able to insert an object with a vector without specifying the name', async () => { + uuid = await collection.data.insert({ + properties: { + text: 'test', + }, + vectors: [1, 2, 3, 4], + }); + const obj = await collection.query.fetchObjectById(uuid, { + includeVector: true, + }); + expect(obj?.vectors.default).toEqual([1, 2, 3, 4]); + }); + + it('should be able to update the vector of an object without specifying the name', async () => { + await collection.data.update({ + id: uuid, + vectors: [5, 6, 7, 8], + }); + const obj = await collection.query.fetchObjectById(uuid, { + includeVector: true, + }); + expect(obj?.vectors.default).toEqual([5, 6, 7, 8]); + }); + + it('should be able to replace the vector of an object without specifying the name', async () => { + await collection.data.replace({ + id: uuid, + vectors: [9, 10, 11, 12], + }); + const obj = await collection.query.fetchObjectById(uuid, { + includeVector: true, + }); + expect(obj?.vectors.default).toEqual([9, 10, 11, 12]); + }); + + it('should be able to insert many objects with a vector without specifying the name', async () => { + const objects = [ + { + properties: { + text: 'test', + }, + vectors: [1, 2, 3, 4], + }, + { + properties: { + text: 'test', + }, + vectors: [5, 6, 7, 8], + }, + ]; + const insert = await collection.data.insertMany(objects); + expect(insert.hasErrors).toBeFalsy(); + expect(insert.allResponses.length).toEqual(2); + expect(Object.values(insert.errors).length).toEqual(0); + expect(Object.values(insert.uuids).length).toEqual(2); + const obj1 = await collection.query.fetchObjectById(insert.uuids[0], { + includeVector: true, + }); + expect(obj1?.vectors.default).toEqual([1, 2, 3, 4]); + const obj2 = await collection.query.fetchObjectById(insert.uuids[1], { + includeVector: true, + }); + expect(obj2?.vectors.default).toEqual([5, 6, 7, 8]); + }); +}); diff --git a/src/collections/deserialize/index.ts b/src/collections/deserialize/index.ts new file mode 100644 index 00000000..91c38817 --- /dev/null +++ b/src/collections/deserialize/index.ts @@ -0,0 +1,301 @@ +import { WeaviateDeserializationError } from '../../errors.js'; +import { BatchObject as BatchObjectGRPC, BatchObjectsReply } from '../../proto/v1/batch.js'; +import { BatchDeleteReply } from '../../proto/v1/batch_delete.js'; +import { ListValue, Properties as PropertiesGrpc, Value } from '../../proto/v1/properties.js'; +import { MetadataResult, PropertiesResult, SearchReply } from '../../proto/v1/search_get.js'; +import { DbVersionSupport } from '../../utils/dbVersion.js'; +import { referenceFromObjects } from '../references/utils.js'; +import { + BatchObject, + BatchObjectsReturn, + DeleteManyReturn, + ErrorObject, + GenerativeGroupByResult, + GenerativeGroupByReturn, + GenerativeReturn, + GroupByObject, + GroupByResult, + GroupByReturn, + Properties, + ReturnMetadata, + WeaviateReturn, +} from '../types/index.js'; + +export class Deserialize { + private supports125ListValue: boolean; + + private constructor(supports125ListValue: boolean) { + this.supports125ListValue = supports125ListValue; + } + + public static async use(support: DbVersionSupport): Promise { + const supports125ListValue = await support.supports125ListValue().then((res) => res.supports); + return new Deserialize(supports125ListValue); + } + + public query(reply: SearchReply): WeaviateReturn { + return { + objects: reply.results.map((result) => { + return { + metadata: Deserialize.metadata(result.metadata), + properties: this.properties(result.properties), + references: this.references(result.properties), + uuid: Deserialize.uuid(result.metadata), + vectors: Deserialize.vectors(result.metadata), + } as any; + }), + }; + } + + public generate(reply: SearchReply): GenerativeReturn { + return { + objects: reply.results.map((result) => { + return { + generated: result.metadata?.generativePresent ? result.metadata?.generative : undefined, + metadata: Deserialize.metadata(result.metadata), + properties: this.properties(result.properties), + references: this.references(result.properties), + uuid: Deserialize.uuid(result.metadata), + vectors: Deserialize.vectors(result.metadata), + } as any; + }), + generated: reply.generativeGroupedResult, + }; + } + + public groupBy(reply: SearchReply): GroupByReturn { + const objects: GroupByObject[] = []; + const groups: Record> = {}; + reply.groupByResults.forEach((result) => { + const objs = result.objects.map((object) => { + return { + belongsToGroup: result.name, + metadata: Deserialize.metadata(object.metadata), + properties: this.properties(object.properties), + references: this.references(object.properties), + uuid: Deserialize.uuid(object.metadata), + vectors: Deserialize.vectors(object.metadata), + } as any; + }); + groups[result.name] = { + maxDistance: result.maxDistance, + minDistance: result.minDistance, + name: result.name, + numberOfObjects: result.numberOfObjects, + objects: objs, + }; + objects.push(...objs); + }); + return { + objects: objects, + groups: groups, + }; + } + + public generateGroupBy(reply: SearchReply): GenerativeGroupByReturn { + const objects: GroupByObject[] = []; + const groups: Record> = {}; + reply.groupByResults.forEach((result) => { + const objs = result.objects.map((object) => { + return { + belongsToGroup: result.name, + metadata: Deserialize.metadata(object.metadata), + properties: this.properties(object.properties), + references: this.references(object.properties), + uuid: Deserialize.uuid(object.metadata), + vectors: Deserialize.vectors(object.metadata), + } as any; + }); + groups[result.name] = { + maxDistance: result.maxDistance, + minDistance: result.minDistance, + name: result.name, + numberOfObjects: result.numberOfObjects, + objects: objs, + generated: result.generative?.result, + }; + objects.push(...objs); + }); + return { + objects: objects, + groups: groups, + generated: reply.generativeGroupedResult, + }; + } + + private properties(properties?: PropertiesResult) { + if (!properties) return {}; + return this.objectProperties(properties.nonRefProps); + } + + private references(properties?: PropertiesResult) { + if (!properties) return undefined; + if (properties.refProps.length === 0) return properties.refPropsRequested ? {} : undefined; + const out: any = {}; + properties.refProps.forEach((property) => { + const uuids: string[] = []; + out[property.propName] = referenceFromObjects( + property.properties.map((property) => { + const uuid = Deserialize.uuid(property.metadata); + uuids.push(uuid); + return { + metadata: Deserialize.metadata(property.metadata), + properties: this.properties(property), + references: this.references(property), + uuid: uuid, + vectors: Deserialize.vectors(property.metadata), + }; + }), + property.properties.length > 0 ? property.properties[0].targetCollection : '', + uuids + ); + }); + return out; + } + + private parsePropertyValue(value: Value): any { + if (value.boolValue !== undefined) return value.boolValue; + if (value.dateValue !== undefined) return new Date(value.dateValue); + if (value.intValue !== undefined) return value.intValue; + if (value.listValue !== undefined) + return this.supports125ListValue + ? this.parseListValue(value.listValue) + : value.listValue.values.map((v) => this.parsePropertyValue(v)); + if (value.numberValue !== undefined) return value.numberValue; + if (value.objectValue !== undefined) return this.objectProperties(value.objectValue); + if (value.stringValue !== undefined) return value.stringValue; + if (value.textValue !== undefined) return value.textValue; + if (value.uuidValue !== undefined) return value.uuidValue; + if (value.blobValue !== undefined) return value.blobValue; + if (value.geoValue !== undefined) return value.geoValue; + if (value.phoneValue !== undefined) return value.phoneValue; + if (value.nullValue !== undefined) return undefined; + throw new WeaviateDeserializationError(`Unknown value type: ${JSON.stringify(value, null, 2)}`); + } + + private parseListValue(value: ListValue): string[] | number[] | boolean[] | Date[] | Properties[] { + if (value.boolValues !== undefined) return value.boolValues.values; + if (value.dateValues !== undefined) return value.dateValues.values.map((date) => new Date(date)); + if (value.intValues !== undefined) return Deserialize.intsFromBytes(value.intValues.values); + if (value.numberValues !== undefined) return Deserialize.numbersFromBytes(value.numberValues.values); + if (value.objectValues !== undefined) + return value.objectValues.values.map((v) => this.objectProperties(v)); + if (value.textValues !== undefined) return value.textValues.values; + if (value.uuidValues !== undefined) return value.uuidValues.values; + throw new Error(`Unknown list value type: ${JSON.stringify(value, null, 2)}`); + } + + private objectProperties(properties?: PropertiesGrpc): Properties { + const out: Properties = {}; + if (properties) { + Object.entries(properties.fields).forEach(([key, value]) => { + out[key] = this.parsePropertyValue(value); + }); + } + return out; + } + + private static metadata(metadata?: MetadataResult): ReturnMetadata | undefined { + const out: ReturnMetadata = {}; + if (!metadata) return undefined; + if (metadata.creationTimeUnixPresent) out.creationTime = new Date(metadata.creationTimeUnix); + if (metadata.lastUpdateTimeUnixPresent) out.updateTime = new Date(metadata.lastUpdateTimeUnix); + if (metadata.distancePresent) out.distance = metadata.distance; + if (metadata.certaintyPresent) out.certainty = metadata.certainty; + if (metadata.scorePresent) out.score = metadata.score; + if (metadata.explainScorePresent) out.explainScore = metadata.explainScore; + if (metadata.rerankScorePresent) out.rerankScore = metadata.rerankScore; + if (metadata.isConsistent) out.isConsistent = metadata.isConsistent; + return out; + } + + private static uuid(metadata?: MetadataResult) { + if (!metadata || !(metadata.id.length > 0)) + throw new WeaviateDeserializationError('No uuid returned from server'); + return metadata.id; + } + + private static vectorFromBytes(bytes: Uint8Array) { + const buffer = Buffer.from(bytes); + const view = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4); // vector is float32 in weaviate + return Array.from(view); + } + + private static intsFromBytes(bytes: Uint8Array) { + const buffer = Buffer.from(bytes); + const view = new BigInt64Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 8); // ints are float64 in weaviate + return Array.from(view).map(Number); + } + + private static numbersFromBytes(bytes: Uint8Array) { + const buffer = Buffer.from(bytes); + const view = new Float64Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 8); // numbers are float64 in weaviate + return Array.from(view); + } + + private static vectors(metadata?: MetadataResult): Record { + if (!metadata) return {}; + if (metadata.vectorBytes.length === 0 && metadata.vector.length === 0 && metadata.vectors.length === 0) + return {}; + if (metadata.vectorBytes.length > 0) + return { default: Deserialize.vectorFromBytes(metadata.vectorBytes) }; + return Object.fromEntries( + metadata.vectors.map((vector) => [vector.name, Deserialize.vectorFromBytes(vector.vectorBytes)]) + ); + } + + public static batchObjects( + reply: BatchObjectsReply, + originalObjs: BatchObject[], + mappedObjs: BatchObjectGRPC[], + elapsed: number + ): BatchObjectsReturn { + const allResponses = []; + const errors: Record> = {}; + const successes: Record = {}; + + const batchErrors: Record = {}; + reply.errors.forEach((error) => { + batchErrors[error.index] = error.error; + }); + + for (const [index, object] of originalObjs.entries()) { + if (index in batchErrors) { + const error: ErrorObject = { + message: batchErrors[index], + object: object, + originalUuid: object.id, + }; + errors[index] = error; + allResponses[index] = error; + } else { + const mappedObj = mappedObjs[index]; + successes[index] = mappedObj.uuid; + allResponses[index] = mappedObj.uuid; + } + } + + return { + uuids: successes, + errors: errors, + hasErrors: reply.errors.length > 0, + allResponses: allResponses, + elapsedSeconds: elapsed, + }; + } + + public static deleteMany(reply: BatchDeleteReply, verbose?: V): DeleteManyReturn { + return { + ...reply, + objects: verbose + ? reply.objects.map((obj) => { + return { + id: obj.uuid.toString(), + successful: obj.successful, + error: obj.error, + }; + }) + : (undefined as any), + }; + } +} diff --git a/src/collections/filters/classes.ts b/src/collections/filters/classes.ts new file mode 100644 index 00000000..b98eb89c --- /dev/null +++ b/src/collections/filters/classes.ts @@ -0,0 +1,410 @@ +import { WeaviateInvalidInputError } from '../../errors.js'; +import { + FilterReferenceCount, + FilterReferenceMultiTarget, + FilterReferenceSingleTarget, + FilterTarget, +} from '../../proto/v1/base.js'; +import { ExtractCrossReferenceType, NonRefKeys, RefKeys } from '../types/internal.js'; +import { + ContainsValue, + CountRef, + Filter, + FilterByProperty, + FilterTargetInternal, + FilterValue, + GeoRangeFilter, + TargetRefs, +} from './types.js'; +import { TargetGuards } from './utils.js'; + +/** + * Use this class when you want to chain filters together using logical operators. + * + * Since JS/TS has no native support for & and | as logical operators, you must use these methods and nest + * the filters you want to combine. + * + * ANDs and ORs can be nested an arbitrary number of times. + * + * @example + * ```ts + * const filter = Filters.and( + * collection.filter.byProperty('name').equal('John'), + * collection.filter.byProperty('age').greaterThan(18), + * ); + * ``` + */ +export class Filters { + /** + * Combine filters using the logical AND operator. + * + * @param {FilterValue[]} filters The filters to combine. + */ + static and(...filters: FilterValue[]): FilterValue { + return { + operator: 'And', + filters: filters, + value: null, + }; + } + /** + * Combine filters using the logical OR operator. + * + * @param {FilterValue[]} filters The filters to combine. + */ + static or(...filters: FilterValue[]): FilterValue { + return { + operator: 'Or', + filters: filters, + value: null, + }; + } +} + +export class FilterBase { + protected target?: TargetRefs; + protected property: string | CountRef; + + constructor(property: string | CountRef, target?: TargetRefs) { + this.property = property; + this.target = target; + } + + protected targetPath(): FilterTarget { + if (!this.target) { + return FilterTarget.fromPartial({ + property: TargetGuards.isProperty(this.property) ? this.property : undefined, + count: TargetGuards.isCountRef(this.property) + ? FilterReferenceCount.fromPartial({ + on: this.property.linkOn, + }) + : undefined, + }); + } + + let target = this.target; + while (target.target !== undefined) { + if (TargetGuards.isTargetRef(target.target)) { + target = target.target; + } else { + throw new WeaviateInvalidInputError('Invalid target reference'); + } + } + target.target = this.property; + return this.resolveTargets(this.target); + } + + private resolveTargets(internal?: FilterTargetInternal): FilterTarget { + return FilterTarget.fromPartial({ + property: TargetGuards.isProperty(internal) ? internal : undefined, + singleTarget: TargetGuards.isSingleTargetRef(internal) + ? FilterReferenceSingleTarget.fromPartial({ + on: internal.linkOn, + target: this.resolveTargets(internal.target), + }) + : undefined, + multiTarget: TargetGuards.isMultiTargetRef(internal) + ? FilterReferenceMultiTarget.fromPartial({ + on: internal.linkOn, + targetCollection: internal.targetCollection, + target: this.resolveTargets(internal.target), + }) + : undefined, + count: TargetGuards.isCountRef(internal) + ? FilterReferenceCount.fromPartial({ + on: internal.linkOn, + }) + : undefined, + }); + } +} + +export class FilterProperty extends FilterBase implements FilterByProperty { + constructor(property: string, length: boolean, target?: TargetRefs) { + super(length ? `len(${property})` : property, target); + } + + public isNull(value: boolean): FilterValue { + return { + operator: 'IsNull', + target: this.targetPath(), + value: value, + }; + } + + public containsAny>(value: U[]): FilterValue { + return { + operator: 'ContainsAny', + target: this.targetPath(), + value: value, + }; + } + + public containsAll>(value: U[]): FilterValue { + return { + operator: 'ContainsAll', + target: this.targetPath(), + value: value, + }; + } + + public equal(value: V): FilterValue { + return { + operator: 'Equal', + target: this.targetPath(), + value: value, + }; + } + + public notEqual(value: V): FilterValue { + return { + operator: 'NotEqual', + target: this.targetPath(), + value: value, + }; + } + + public lessThan(value: U): FilterValue { + return { + operator: 'LessThan', + target: this.targetPath(), + value: value, + }; + } + + public lessOrEqual(value: U): FilterValue { + return { + operator: 'LessThanEqual', + target: this.targetPath(), + value: value, + }; + } + + public greaterThan(value: U): FilterValue { + return { + operator: 'GreaterThan', + target: this.targetPath(), + value: value, + }; + } + + public greaterOrEqual(value: U): FilterValue { + return { + operator: 'GreaterThanEqual', + target: this.targetPath(), + value: value, + }; + } + + public like(value: string): FilterValue { + return { + operator: 'Like', + target: this.targetPath(), + value: value, + }; + } + + public withinGeoRange(value: GeoRangeFilter): FilterValue { + return { + operator: 'WithinGeoRange', + target: this.targetPath(), + value: value, + }; + } +} + +export class FilterRef implements Filter { + private target: TargetRefs; + + constructor(target: TargetRefs) { + this.target = target; + } + + public byRef & string>(linkOn: K): Filter> { + this.target.target = { type_: 'single', linkOn: linkOn }; + return new FilterRef>(this.target); + } + + public byRefMultiTarget & string>(linkOn: K, targetCollection: string) { + this.target.target = { type_: 'multi', linkOn: linkOn, targetCollection: targetCollection }; + return new FilterRef>(this.target); + } + + public byProperty & string>(name: K, length = false) { + return new FilterProperty(name, length, this.target); + } + + public byRefCount & string>(linkOn: K) { + return new FilterCount(linkOn, this.target); + } + + public byId() { + return new FilterId(this.target); + } + + public byCreationTime() { + return new FilterCreationTime(this.target); + } + + public byUpdateTime() { + return new FilterUpdateTime(this.target); + } +} + +export class FilterCount extends FilterBase { + constructor(linkOn: string, target?: TargetRefs) { + super({ type_: 'count', linkOn }, target); + } + + public equal(value: number): FilterValue { + return { + operator: 'Equal', + target: this.targetPath(), + value: value, + }; + } + + public notEqual(value: number): FilterValue { + return { + operator: 'NotEqual', + target: this.targetPath(), + value: value, + }; + } + + public lessThan(value: number): FilterValue { + return { + operator: 'LessThan', + target: this.targetPath(), + value: value, + }; + } + + public lessOrEqual(value: number): FilterValue { + return { + operator: 'LessThanEqual', + target: this.targetPath(), + value: value, + }; + } + + public greaterThan(value: number): FilterValue { + return { + operator: 'GreaterThan', + target: this.targetPath(), + value: value, + }; + } + + public greaterOrEqual(value: number): FilterValue { + return { + operator: 'GreaterThanEqual', + target: this.targetPath(), + value: value, + }; + } +} + +export class FilterId extends FilterBase { + constructor(target?: TargetRefs) { + super('_id', target); + } + + public equal(value: string): FilterValue { + return { + operator: 'Equal', + target: this.targetPath(), + value: value, + }; + } + + public notEqual(value: string): FilterValue { + return { + operator: 'NotEqual', + target: this.targetPath(), + value: value, + }; + } + + public containsAny(value: string[]): FilterValue { + return { + operator: 'ContainsAny', + target: this.targetPath(), + value: value, + }; + } +} + +export class FilterTime extends FilterBase { + public containsAny(value: (string | Date)[]): FilterValue { + return { + operator: 'ContainsAny', + target: this.targetPath(), + value: value.map(this.toValue), + }; + } + + public equal(value: string | Date): FilterValue { + return { + operator: 'Equal', + target: this.targetPath(), + value: this.toValue(value), + }; + } + + public notEqual(value: string | Date): FilterValue { + return { + operator: 'NotEqual', + target: this.targetPath(), + value: this.toValue(value), + }; + } + + public lessThan(value: string | Date): FilterValue { + return { + operator: 'LessThan', + target: this.targetPath(), + value: this.toValue(value), + }; + } + + public lessOrEqual(value: string | Date): FilterValue { + return { + operator: 'LessThanEqual', + target: this.targetPath(), + value: this.toValue(value), + }; + } + + public greaterThan(value: string | Date): FilterValue { + return { + operator: 'GreaterThan', + target: this.targetPath(), + value: this.toValue(value), + }; + } + + public greaterOrEqual(value: string | Date): FilterValue { + return { + operator: 'GreaterThanEqual', + target: this.targetPath(), + value: this.toValue(value), + }; + } + + private toValue(value: string | Date): string { + return value instanceof Date ? value.toISOString() : value; + } +} + +export class FilterCreationTime extends FilterTime { + constructor(target?: TargetRefs) { + super('_creationTimeUnix', target); + } +} + +export class FilterUpdateTime extends FilterTime { + constructor(target?: TargetRefs) { + super('_lastUpdateTimeUnix', target); + } +} diff --git a/src/collections/filters/index.ts b/src/collections/filters/index.ts new file mode 100644 index 00000000..7b6d9d46 --- /dev/null +++ b/src/collections/filters/index.ts @@ -0,0 +1,55 @@ +export { Filters } from './classes.js'; +export type { + Filter, + FilterByCount, + FilterById, + FilterByProperty, + FilterByTime, + FilterValue, + GeoRangeFilter, + Operator, +} from './types.js'; + +import { ExtractCrossReferenceType, NonRefKeys, RefKeys } from '../types/internal.js'; + +import { + FilterCount, + FilterCreationTime, + FilterId, + FilterProperty, + FilterRef, + FilterUpdateTime, +} from './classes.js'; +import { Filter } from './types.js'; + +const filter = (): Filter => { + return { + byProperty: & string>(name: K, length = false) => { + return new FilterProperty(name, length); + }, + byRef: & string>(linkOn: K) => { + return new FilterRef>({ type_: 'single', linkOn: linkOn }); + }, + byRefMultiTarget: & string>(linkOn: K, targetCollection: string) => { + return new FilterRef>({ + type_: 'multi', + linkOn: linkOn, + targetCollection: targetCollection, + }); + }, + byRefCount: & string>(linkOn: K) => { + return new FilterCount(linkOn); + }, + byId: () => { + return new FilterId(); + }, + byCreationTime: () => { + return new FilterCreationTime(); + }, + byUpdateTime: () => { + return new FilterUpdateTime(); + }, + }; +}; + +export default filter; diff --git a/src/collections/filters/integration.test.ts b/src/collections/filters/integration.test.ts new file mode 100644 index 00000000..7d59923a --- /dev/null +++ b/src/collections/filters/integration.test.ts @@ -0,0 +1,376 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import weaviate, { WeaviateClient } from '../../index.js'; +import { Collection } from '../collection/index.js'; +import { CrossReference, Reference } from '../references/index.js'; +import { GeoCoordinate } from '../types/index.js'; +import { Filters } from './index.js'; + +describe('Testing of the filter class with a simple collection', () => { + let client: WeaviateClient; + let collection: Collection; + + const collectionName = 'TestCollectionFilterSimple'; + let ids: string[]; + let vector: number[]; + + type TestType = { + text: string; + int: number; + float: number; + self?: CrossReference; + }; + + const startTime = new Date(); + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + ids = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'text', + dataType: 'text', + }, + { + name: 'int', + dataType: 'int', + }, + { + name: 'float', + dataType: 'number', + }, + ], + references: [ + { + name: 'self', + targetCollection: collectionName, + }, + ], + invertedIndex: weaviate.configure.invertedIndex({ indexTimestamps: true }), + vectorizers: weaviate.configure.vectorizer.text2VecContextionary({ + vectorizeCollectionName: false, + }), + }) + .then(() => + collection.data.insertMany([ + { + text: 'one', + int: 1, + float: 1.1, + }, + { + text: 'two', + int: 2, + float: 2.2, + }, + { + text: 'three', + int: 3, + float: 3.3, + }, + { + text: 'one', + int: 4, + float: 4.4, + }, + ]) + ) + .then(async (res) => { + const uuids = Object.values(res.uuids); + await collection.data.referenceAdd({ + fromUuid: res.uuids[2], + fromProperty: 'self', + to: Reference.to(uuids[3]), + }); + return uuids; + }); + const res = await collection.query.fetchObjectById(ids[0], { includeVector: true }); + vector = res?.vectors.default!; + }); + + it('should filter a fetch objects query with a single filter and generic collection', async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byProperty('text').equal('two'), + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('two'); + expect(obj.properties.int).toEqual(2); + expect(obj.properties.float).toEqual(2.2); + expect(obj.uuid).toEqual(ids[1]); + }); + + it('should filter a fetch objects query with a single filter and non-generic collection', async () => { + const res = await client.collections.get(collectionName).query.fetchObjects({ + filters: client.collections.get(collectionName).filter.byProperty('text').equal('two'), + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('two'); + expect(obj.properties.int).toEqual(2); + expect(obj.properties.float).toEqual(2.2); + expect(obj.uuid).toEqual(ids[1]); + }); + + it('should filter a fetch objects query with an AND filter', async () => { + const res = await collection.query.fetchObjects({ + filters: Filters.and( + collection.filter.byProperty('text').equal('one'), + collection.filter.byProperty('int').equal(1) + ), + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('one'); + expect(obj.properties.int).toEqual(1); + expect(obj.properties.float).toEqual(1.1); + expect(obj.uuid).toEqual(ids[0]); + }); + + it('should filter a fetch objects query with an OR filter', async () => { + const res = await collection.query.fetchObjects({ + filters: Filters.or( + collection.filter.byProperty('text').equal('three'), + collection.filter.byProperty('int').equal(2) + ), + }); + expect(res.objects.length).toEqual(2); + + const obj1 = res.objects[0]; + const obj2 = res.objects[1]; + + expect(obj1.properties.text).toEqual('two'); + expect(obj1.properties.int).toEqual(2); + expect(obj1.properties.float).toEqual(2.2); + expect(obj1.uuid).toEqual(ids[1]); + + expect(obj2.properties.text).toEqual('three'); + expect(obj2.properties.int).toEqual(3); + expect(obj2.properties.float).toEqual(3.3); + expect(obj2.uuid).toEqual(ids[2]); + }); + + it('should filter a fetch objects query with a reference filter', async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byRef('self').byProperty('text').equal('one'), + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('three'); + expect(obj.properties.int).toEqual(3); + expect(obj.properties.float).toEqual(3.3); + expect(obj.uuid).toEqual(ids[2]); + }); + + it('should filter a fetch objects query with a greater than reference count filter', async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byRefCount('self').greaterThan(0), + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('three'); + expect(obj.properties.int).toEqual(3); + expect(obj.properties.float).toEqual(3.3); + expect(obj.uuid).toEqual(ids[2]); + }); + + it('should filter a fetch objects query with a greater than or equal reference count filter', async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byRefCount('self').greaterOrEqual(1), + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('three'); + expect(obj.properties.int).toEqual(3); + expect(obj.properties.float).toEqual(3.3); + expect(obj.uuid).toEqual(ids[2]); + }); + + it('should filter a fetch objects query with an equal reference count filter', async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byRefCount('self').equal(1), + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('three'); + expect(obj.properties.int).toEqual(3); + expect(obj.properties.float).toEqual(3.3); + expect(obj.uuid).toEqual(ids[2]); + }); + + it('should filter a fetch objects query with an equal ID filter', async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byId().equal(ids[0]), + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('one'); + expect(obj.properties.int).toEqual(1); + expect(obj.properties.float).toEqual(1.1); + expect(obj.uuid).toEqual(ids[0]); + }); + + it('should filter a fetch objects query with a less than creation time filter', async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byCreationTime().lessThan(startTime), + }); + expect(res.objects.length).toEqual(0); + }); + + it('should filter a fetch objects query with a greater than last updated time filter', async () => { + const now = new Date(); + const vec = Array.from({ length: 300 }, () => Math.floor(Math.random() * 10)); + await collection.data + .update({ + id: ids[0], + vectors: vec, + }) + .then(async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byUpdateTime().greaterOrEqual(now), + includeVector: true, + }); + expect(res.objects.length).toEqual(1); + const obj = res.objects[0]; + expect(obj.properties.text).toEqual('one'); + expect(obj.properties.int).toEqual(1); + expect(obj.properties.float).toEqual(1.1); + expect(obj.uuid).toEqual(ids[0]); + expect(obj.vectors.default).toEqual(vec); + }); + }); + + it('should filter an aggregate query with a single filter', async () => { + const res = await collection.aggregate.overAll({ + filters: collection.filter.byProperty('text').equal('one'), + returnMetrics: collection.metrics.aggregate('text').text(['count']), + }); + expect(res.properties.text.count).toEqual(2); + }); + + it('should filter an aggregate query with an AND filter', async () => { + const res = await collection.aggregate.overAll({ + filters: Filters.and( + collection.filter.byProperty('text').equal('one'), + collection.filter.byProperty('int').equal(1) + ), + returnMetrics: collection.metrics.aggregate('text').text(['count']), + }); + expect(res.properties.text.count).toEqual(1); + }); + + it('should filter an aggregate query with an OR filter', async () => { + const res = await collection.aggregate.overAll({ + filters: Filters.or( + collection.filter.byProperty('text').equal('one'), + collection.filter.byProperty('int').equal(2) + ), + returnMetrics: collection.metrics.aggregate('text').text(['count']), + }); + expect(res.properties.text.count).toEqual(3); + }); +}); + +describe('Testing of the filter class with complex data types', () => { + let client: WeaviateClient; + let collection: Collection; + + const collectionName = 'TestCollectionFilterComplex'; + type TestCollectionFilterComplex = { + name: string; + location: GeoCoordinate; + date?: Date; + personId: string; + }; + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + await client.collections + .create({ + name: collectionName, + invertedIndex: { + indexNullState: true, + }, + properties: [ + { + name: 'name', + dataType: 'text', + }, + { + name: 'location', + dataType: 'geoCoordinates', + }, + { + name: 'date', + dataType: 'date', + }, + { + name: 'personId', + dataType: 'uuid', + }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecContextionary(), + }) + .then(() => + collection.data.insertMany([ + { + name: 'Tim', + location: { + latitude: 52.52, + longitude: 13.405, + }, + date: new Date('2021-01-01T00:00:00Z'), + personId: '00000000-0000-0000-0000-000000000000', + }, + { + name: 'Tom', + location: { + latitude: 53.55, + longitude: 10.0, + }, + personId: '00000000-0000-0000-0000-000000000001', + }, + ]) + ); + }); + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + it('should filter a fetch objects query with a geo filter', async () => { + const res = await collection.query.fetchObjects({ + filters: collection.filter.byProperty('location').withinGeoRange({ + latitude: 52.52, + longitude: 13.405, + distance: 1, + }), + }); + expect(res.objects.length).toEqual(1); + }); + + it('should filter a fetch objects query with a date filter', async () => { + const res = await collection.query.nearText(['Tom'], { + filters: Filters.and( + collection.filter.byProperty('date').isNull(true), + collection.filter.byProperty('personId').equal('00000000-0000-0000-0000-000000000001') + ), + }); + expect(res.objects.length).toEqual(1); + expect(res.objects[0].properties.name).toEqual('Tom'); + }); +}); diff --git a/src/collections/filters/types.ts b/src/collections/filters/types.ts new file mode 100644 index 00000000..41c52f40 --- /dev/null +++ b/src/collections/filters/types.ts @@ -0,0 +1,318 @@ +import { FilterTarget } from '../../proto/v1/base.js'; +import { ExtractCrossReferenceType, NonRefKeys, RefKeys } from '../types/internal.js'; + +export type Operator = + | 'Equal' + | 'NotEqual' + | 'GreaterThan' + | 'GreaterThanEqual' + | 'LessThan' + | 'LessThanEqual' + | 'Like' + | 'IsNull' + | 'WithinGeoRange' + | 'ContainsAny' + | 'ContainsAll' + | 'And' + | 'Or'; + +export type FilterValue = { + filters?: FilterValue[]; + operator: Operator; + target?: FilterTarget; + value: V; +}; + +export type SingleTargetRef = { + type_: 'single'; + linkOn: string; + target?: FilterTargetInternal; +}; + +export type MultiTargetRef = { + type_: 'multi'; + linkOn: string; + targetCollection: string; + target?: FilterTargetInternal; +}; + +export type CountRef = { + type_: 'count'; + linkOn: string; +}; + +export type FilterTargetInternal = SingleTargetRef | MultiTargetRef | CountRef | string; +export type TargetRefs = SingleTargetRef | MultiTargetRef; + +export type GeoRangeFilter = { + latitude: number; + longitude: number; + distance: number; +}; + +export type FilterValueType = PrimitiveFilterValueType | PrimitiveListFilterValueType; + +export type PrimitiveFilterValueType = number | string | boolean | Date | GeoRangeFilter; +export type PrimitiveListFilterValueType = number[] | string[] | boolean[] | Date[]; + +export type ContainsValue = V extends (infer U)[] ? U : V; + +export interface Filter { + /** + * Define a filter based on a property to be used when querying and deleting from a collection. + * + * @param {K} name The name of the property to filter on. + * @param {boolean} [length] Whether to filter on the length of the property or not, defaults to false. + * @returns {FilterByProperty} An interface exposing methods to filter on the property. + */ + byProperty: & string>(name: K, length?: boolean) => FilterByProperty; + /** + * Define a filter based on a single-target reference to be used when querying and deleting from a collection. + * + * @param {K} linkOn The name of the property to filter on. + * @returns {Filter>} An interface exposing methods to filter on the reference. + */ + byRef: & string>(linkOn: K) => Filter>; + /** + * Define a filter based on a multi-target reference to be used when querying and deleting from a collection. + * + * @param {K} linkOn The name of the property to filter on. + * @param {string} targetCollection The name of the target collection to filter on. + * @returns {Filter>} An interface exposing methods to filter on the reference. + */ + byRefMultiTarget: & string>( + linkOn: K, + targetCollection: string + ) => Filter>; + /** + * Define a filter based on the number of objects in a cross-reference to be used when querying and deleting from a collection. + * + * @param {K} linkOn The name of the property to filter on. + * @returns {FilterByCount} An interface exposing methods to filter on the count. + */ + byRefCount: & string>(linkOn: K) => FilterByCount; + /** + * Define a filter based on the ID to be used when querying and deleting from a collection. + * + * @returns {FilterById} An interface exposing methods to filter on the ID. + */ + byId: () => FilterById; + /** + * Define a filter based on the creation time to be used when querying and deleting from a collection. + * + * @returns {FilterByTime} An interface exposing methods to filter on the creation time. + */ + byCreationTime: () => FilterByTime; + /** + * Define a filter based on the update time to be used when querying and deleting from a collection. + * + * @returns {FilterByTime} An interface exposing methods to filter on the update time. + */ + byUpdateTime: () => FilterByTime; +} + +export interface FilterByProperty { + /** + * Filter on whether the property is `null`. + * + * @param {boolean} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + isNull: (value: boolean) => FilterValue; + /** + * Filter on whether the property contains any of the given values. + * + * @param {U[]} value The values to filter on. + * @returns {FilterValue} The filter value. + */ + containsAny: >(value: U[]) => FilterValue; + /** + * Filter on whether the property contains all of the given values. + * + * @param {U[]} value The values to filter on. + * @returns {FilterValue} The filter value. + */ + containsAll: >(value: U[]) => FilterValue; + /** + * Filter on whether the property is equal to the given value. + * + * @param {V} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + equal: (value: T) => FilterValue; + /** + * Filter on whether the property is not equal to the given value. + * + * @param {V} value The value to filter on. + * @returns {FilterValue} The filter value. + * */ + notEqual: (value: T) => FilterValue; + /** + * Filter on whether the property is less than the given value. + * + * @param {number | Date} value The value to filter on. + * @returns {FilterValue | FilterValue} The filter value. + */ + lessThan: (value: U) => FilterValue; + /** + * Filter on whether the property is less than or equal to the given value. + * + * @param {number | Date} value The value to filter on. + * @returns {FilterValue | FilterValue} The filter value. + */ + lessOrEqual: (value: U) => FilterValue; + /** + * Filter on whether the property is greater than the given value. + * + * @param {number | Date} value The value to filter on. + * @returns {FilterValue | FilterValue} The filter value. + */ + greaterThan: (value: U) => FilterValue; + /** + * Filter on whether the property is greater than or equal to the given value. + * + * @param {number | Date} value The value to filter on. + * @returns {FilterValue | FilterValue} The filter value. + */ + greaterOrEqual: (value: U) => FilterValue; + /** + * Filter on whether the property is like the given value. + * + * This filter can make use of `*` and `?` as wildcards. + * See [the docs](https://weaviate.io/developers/weaviate/search/filters#by-partial-matches-text) for more details. + * + * @param {string} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + like: (value: string) => FilterValue; + /** + * Filter on whether the property is within a given range of a geo-coordinate. + * + * See [the docs](https://weaviate.io/developers/weaviate/search/filters##by-geo-coordinates) for more details. + * + * @param {GeoRangeFilter} value The geo-coordinate range to filter on. + * @returns {FilterValue} The filter value. + */ + withinGeoRange: (value: GeoRangeFilter) => FilterValue; +} + +export interface FilterByCount { + /** + * Filter on whether the number of references is equal to the given integer. + * + * @param {number} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + equal: (value: number) => FilterValue; + /** + * Filter on whether the number of references is not equal to the given integer. + * + * @param {number} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + notEqual: (value: number) => FilterValue; + /** + * Filter on whether the number of references is less than the given integer. + * + * @param {number} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + lessThan: (value: number) => FilterValue; + /** + * Filter on whether the number of references is less than or equal to the given integer. + * + * @param {number} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + lessOrEqual: (value: number) => FilterValue; + /** + * Filter on whether the number of references is greater than the given integer. + * + * @param {number} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + greaterThan: (value: number) => FilterValue; + /** + * Filter on whether the number of references is greater than or equal to the given integer. + * + * @param {number} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + greaterOrEqual: (value: number) => FilterValue; +} + +export interface FilterById { + /** + * Filter on whether the ID is equal to the given string. + * + * @param {string} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + equal: (value: string) => FilterValue; + /** + * Filter on whether the ID is not equal to the given string. + * + * @param {string} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + notEqual: (value: string) => FilterValue; + /** + * Filter on whether the ID is any one of the given strings. + * + * @param {string[]} value The values to filter on. + * @returns {FilterValue} The filter value. + */ + containsAny: (value: string[]) => FilterValue; +} + +export interface FilterByTime { + /** + * Filter on whether the time is any one of the given strings or Dates. + * + * @param {(string | Date)[]} value The values to filter on. + * @returns {FilterValue} The filter value. + */ + containsAny: (value: (string | Date)[]) => FilterValue; + /** + * Filter on whether the time is equal to the given string or Date. + * + * @param {string | Date} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + equal: (value: string | Date) => FilterValue; + /** + * Filter on whether the time is not equal to the given string or Date. + * + * @param {string | Date} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + notEqual: (value: string | Date) => FilterValue; + /** + * Filter on whether the time is less than the given string or Date. + * + * @param {string | Date} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + lessThan: (value: string | Date) => FilterValue; + /** + * Filter on whether the time is less than or equal to the given string or Date. + * + * @param {string | Date} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + lessOrEqual: (value: string | Date) => FilterValue; + /** + * Filter on whether the time is greater than the given string or Date. + * + * @param {string | Date} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + greaterThan: (value: string | Date) => FilterValue; + /** + * Filter on whether the time is greater than or equal to the given string or Date. + * + * @param {string | Date} value The value to filter on. + * @returns {FilterValue} The filter value. + */ + greaterOrEqual: (value: string | Date) => FilterValue; +} diff --git a/src/collections/filters/unit.test.ts b/src/collections/filters/unit.test.ts new file mode 100644 index 00000000..b56580a5 --- /dev/null +++ b/src/collections/filters/unit.test.ts @@ -0,0 +1,897 @@ +import { WhereFilter } from '../../openapi/types.js'; +import { CrossReference } from '../references/index.js'; +import { Serialize } from '../serialize/index.js'; +import maker, { FilterValue, Filters } from './index.js'; +import { GeoRangeFilter } from './types.js'; + +describe('Unit testing of filters', () => { + type Person = { + name: string; + age: number; + friends: string[]; + self: CrossReference; + }; + const filter = maker(); + + describe('for properties', () => { + it('should create an is null filter', () => { + const f = filter.byProperty('name').isNull(true); + expect(f).toEqual>({ + operator: 'IsNull', + target: { + property: 'name', + }, + value: true, + }); + }); + + it('should create a contains all filter with a primitive type', () => { + const f = filter.byProperty('name').containsAll(['John', 'Doe']); + expect(f).toEqual>({ + operator: 'ContainsAll', + target: { + property: 'name', + }, + value: ['John', 'Doe'], + }); + }); + + it('should create a contains all filter with an array type', () => { + const f = filter.byProperty('friends').containsAll(['John', 'Doe']); + expect(f).toEqual>({ + operator: 'ContainsAll', + target: { + property: 'friends', + }, + value: ['John', 'Doe'], + }); + }); + + it('should create a contains any filter with a primitive type', () => { + const f = filter.byProperty('name').containsAny(['John', 'Doe']); + expect(f).toEqual>({ + operator: 'ContainsAny', + target: { + property: 'name', + }, + value: ['John', 'Doe'], + }); + }); + + it('should create a contains any filter with an array type', () => { + const f = filter.byProperty('friends').containsAny(['John', 'Doe']); + expect(f).toEqual>({ + operator: 'ContainsAny', + target: { + property: 'friends', + }, + value: ['John', 'Doe'], + }); + }); + + it('should create an equal filter', () => { + const f = filter.byProperty('name').equal('John'); + expect(f).toEqual>({ + operator: 'Equal', + target: { + property: 'name', + }, + value: 'John', + }); + }); + + it('should create an not equal filter', () => { + const f = filter.byProperty('name').notEqual('John'); + expect(f).toEqual>({ + operator: 'NotEqual', + target: { + property: 'name', + }, + value: 'John', + }); + }); + + it('should create a less than filter', () => { + const f = filter.byProperty('age').lessThan(18); + expect(f).toEqual>({ + operator: 'LessThan', + target: { + property: 'age', + }, + value: 18, + }); + }); + + it('should create a less than or equal filter', () => { + const f = filter.byProperty('age').lessOrEqual(18); + expect(f).toEqual>({ + operator: 'LessThanEqual', + target: { + property: 'age', + }, + value: 18, + }); + }); + + it('should create a greater than filter', () => { + const f = filter.byProperty('age').greaterThan(18); + expect(f).toEqual>({ + operator: 'GreaterThan', + target: { + property: 'age', + }, + value: 18, + }); + }); + + it('should create a greater than or equal filter', () => { + const f = filter.byProperty('age').greaterOrEqual(18); + expect(f).toEqual>({ + operator: 'GreaterThanEqual', + target: { + property: 'age', + }, + value: 18, + }); + }); + + it('should create a like filter', () => { + const f = filter.byProperty('name').like('John'); + expect(f).toEqual>({ + operator: 'Like', + target: { + property: 'name', + }, + value: 'John', + }); + }); + + it('should create a geo range filter', () => { + const f = filter.byProperty('location').withinGeoRange({ + latitude: 1, + longitude: 2, + distance: 3, + }); + expect(f).toEqual>({ + operator: 'WithinGeoRange', + target: { + property: 'location', + }, + value: { + latitude: 1, + longitude: 2, + distance: 3, + }, + }); + }); + }); + + describe('for reference counts', () => { + it('should create an equal filter', () => { + const f = filter.byRefCount('self').equal(2); + expect(f).toEqual>({ + operator: 'Equal', + target: { + count: { + on: 'self', + }, + }, + value: 2, + }); + }); + + it('should create a not equal than filter', () => { + const f = filter.byRefCount('self').notEqual(2); + expect(f).toEqual>({ + operator: 'NotEqual', + target: { + count: { + on: 'self', + }, + }, + value: 2, + }); + }); + + it('should create a less than filter', () => { + const f = filter.byRefCount('self').lessThan(2); + expect(f).toEqual>({ + operator: 'LessThan', + target: { + count: { + on: 'self', + }, + }, + value: 2, + }); + }); + + it('should create a less than or equal filter', () => { + const f = filter.byRefCount('self').lessOrEqual(2); + expect(f).toEqual>({ + operator: 'LessThanEqual', + target: { + count: { + on: 'self', + }, + }, + value: 2, + }); + }); + + it('should create a greater than filter', () => { + const f = filter.byRefCount('self').greaterThan(2); + expect(f).toEqual>({ + operator: 'GreaterThan', + target: { + count: { + on: 'self', + }, + }, + value: 2, + }); + }); + + it('should create a greater than or equal filter', () => { + const f = filter.byRefCount('self').greaterOrEqual(2); + expect(f).toEqual>({ + operator: 'GreaterThanEqual', + target: { + count: { + on: 'self', + }, + }, + value: 2, + }); + }); + }); + + describe('for single target references', () => { + it('should create a property filter', () => { + const f = filter.byRef('self').byProperty('name').isNull(true); + expect(f).toEqual>({ + operator: 'IsNull', + target: { + singleTarget: { + on: 'self', + target: { + property: 'name', + }, + }, + }, + value: true, + }); + }); + + it('should create an ID filter', () => { + const f = filter.byRef('self').byId().equal('123'); + expect(f).toEqual>({ + operator: 'Equal', + target: { + singleTarget: { + on: 'self', + target: { + property: '_id', + }, + }, + }, + value: '123', + }); + }); + + it('should create a creation time filter', () => { + const now = new Date(); + const f = filter.byRef('self').byCreationTime().equal(now); + expect(f).toEqual>({ + operator: 'Equal', + target: { + singleTarget: { + on: 'self', + target: { + property: '_creationTimeUnix', + }, + }, + }, + value: now.toISOString(), + }); + }); + + it('should create a update time filter', () => { + const now = new Date(); + const f = filter.byRef('self').byUpdateTime().equal(now); + expect(f).toEqual>({ + operator: 'Equal', + target: { + singleTarget: { + on: 'self', + target: { + property: '_lastUpdateTimeUnix', + }, + }, + }, + value: now.toISOString(), + }); + }); + + it('should create a nested reference filter', () => { + const f = filter.byRef('self').byRef('self').byProperty('name').isNull(true); + expect(f).toEqual>({ + operator: 'IsNull', + target: { + singleTarget: { + on: 'self', + target: { + singleTarget: { + on: 'self', + target: { + property: 'name', + }, + }, + }, + }, + }, + value: true, + }); + }); + }); + + it('should create a nested reference count filter', () => { + const f = filter.byRef('self').byRefCount('self').equal(2); + expect(f).toEqual>({ + operator: 'Equal', + target: { + singleTarget: { + on: 'self', + target: { + count: { + on: 'self', + }, + }, + }, + }, + value: 2, + }); + }); + + describe('for multiple target references', () => { + it('should create a property filter', () => { + const f = filter.byRefMultiTarget('self', 'Person').byProperty('name').isNull(true); + expect(f).toEqual>({ + operator: 'IsNull', + target: { + multiTarget: { + on: 'self', + targetCollection: 'Person', + target: { + property: 'name', + }, + }, + }, + value: true, + }); + }); + + it('should create an ID filter', () => { + const f = filter.byRefMultiTarget('self', 'Person').byId().equal('123'); + expect(f).toEqual>({ + operator: 'Equal', + target: { + multiTarget: { + on: 'self', + targetCollection: 'Person', + target: { + property: '_id', + }, + }, + }, + value: '123', + }); + }); + + it('should create a creation time filter', () => { + const now = new Date(); + const f = filter.byRefMultiTarget('self', 'Person').byCreationTime().equal(now); + expect(f).toEqual>({ + operator: 'Equal', + target: { + multiTarget: { + on: 'self', + targetCollection: 'Person', + target: { + property: '_creationTimeUnix', + }, + }, + }, + value: now.toISOString(), + }); + }); + + it('should create a update time filter', () => { + const now = new Date(); + const f = filter.byRefMultiTarget('self', 'Person').byUpdateTime().equal(now); + expect(f).toEqual>({ + operator: 'Equal', + target: { + multiTarget: { + on: 'self', + targetCollection: 'Person', + target: { + property: '_lastUpdateTimeUnix', + }, + }, + }, + value: now.toISOString(), + }); + }); + + it('should create a nested single target reference filter', () => { + const f = filter.byRefMultiTarget('self', 'Person').byRef('self').byProperty('name').isNull(true); + expect(f).toEqual>({ + operator: 'IsNull', + target: { + multiTarget: { + on: 'self', + targetCollection: 'Person', + target: { + singleTarget: { + on: 'self', + target: { + property: 'name', + }, + }, + }, + }, + }, + value: true, + }); + }); + + it('should create a nested multi target reference filter', () => { + const f = filter + .byRefMultiTarget('self', 'Person') + .byRefMultiTarget('self', 'Person') + .byProperty('name') + .isNull(true); + expect(f).toEqual>({ + operator: 'IsNull', + target: { + multiTarget: { + on: 'self', + targetCollection: 'Person', + target: { + multiTarget: { + on: 'self', + targetCollection: 'Person', + target: { + property: 'name', + }, + }, + }, + }, + }, + value: true, + }); + }); + + it('should create a nested multi target reference count filter', () => { + const f = filter.byRefMultiTarget('self', 'Person').byRefCount('self').equal(2); + expect(f).toEqual>({ + operator: 'Equal', + target: { + multiTarget: { + on: 'self', + targetCollection: 'Person', + target: { + count: { + on: 'self', + }, + }, + }, + }, + value: 2, + }); + }); + }); + + describe('for ID', () => { + it('should create an equal filter', () => { + const f = filter.byId().equal('123'); + expect(f).toEqual>({ + operator: 'Equal', + target: { + property: '_id', + }, + value: '123', + }); + }); + + it('should create a not equal filter', () => { + const f = filter.byId().notEqual('123'); + expect(f).toEqual>({ + operator: 'NotEqual', + target: { + property: '_id', + }, + value: '123', + }); + }); + + it('should create a contains any filter', () => { + const f = filter.byId().containsAny(['123', '456']); + expect(f).toEqual>({ + operator: 'ContainsAny', + target: { + property: '_id', + }, + value: ['123', '456'], + }); + }); + }); + + describe('for creation time', () => { + it('should create a contains any filter', () => { + const now = new Date(); + const f = filter.byCreationTime().containsAny([now]); + expect(f).toEqual>({ + operator: 'ContainsAny', + target: { + property: '_creationTimeUnix', + }, + value: [now.toISOString()], + }); + }); + + it('should create an equal filter', () => { + const now = new Date(); + const f = filter.byCreationTime().equal(now); + expect(f).toEqual>({ + operator: 'Equal', + target: { + property: '_creationTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a not equal filter', () => { + const now = new Date(); + const f = filter.byCreationTime().notEqual(now); + expect(f).toEqual>({ + operator: 'NotEqual', + target: { + property: '_creationTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a less than filter', () => { + const now = new Date(); + const f = filter.byCreationTime().lessThan(now); + expect(f).toEqual>({ + operator: 'LessThan', + target: { + property: '_creationTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a less than or equal filter', () => { + const now = new Date(); + const f = filter.byCreationTime().lessOrEqual(now); + expect(f).toEqual>({ + operator: 'LessThanEqual', + target: { + property: '_creationTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a greater than filter', () => { + const now = new Date(); + const f = filter.byCreationTime().greaterThan(now); + expect(f).toEqual>({ + operator: 'GreaterThan', + target: { + property: '_creationTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a greater than or equal filter', () => { + const now = new Date(); + const f = filter.byCreationTime().greaterOrEqual(now); + expect(f).toEqual>({ + operator: 'GreaterThanEqual', + target: { + property: '_creationTimeUnix', + }, + value: now.toISOString(), + }); + }); + }); + + describe('for update time', () => { + it('should create a contains any filter', () => { + const now = new Date(); + const f = filter.byUpdateTime().containsAny([now]); + expect(f).toEqual>({ + operator: 'ContainsAny', + target: { + property: '_lastUpdateTimeUnix', + }, + value: [now.toISOString()], + }); + }); + + it('should create an equal filter', () => { + const now = new Date(); + const f = filter.byUpdateTime().equal(now); + expect(f).toEqual>({ + operator: 'Equal', + target: { + property: '_lastUpdateTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a not equal filter', () => { + const now = new Date(); + const f = filter.byUpdateTime().notEqual(now); + expect(f).toEqual>({ + operator: 'NotEqual', + target: { + property: '_lastUpdateTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a less than filter', () => { + const now = new Date(); + const f = filter.byUpdateTime().lessThan(now); + expect(f).toEqual>({ + operator: 'LessThan', + target: { + property: '_lastUpdateTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a less than or equal filter', () => { + const now = new Date(); + const f = filter.byUpdateTime().lessOrEqual(now); + expect(f).toEqual>({ + operator: 'LessThanEqual', + target: { + property: '_lastUpdateTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a greater than filter', () => { + const now = new Date(); + const f = filter.byUpdateTime().greaterThan(now); + expect(f).toEqual>({ + operator: 'GreaterThan', + target: { + property: '_lastUpdateTimeUnix', + }, + value: now.toISOString(), + }); + }); + + it('should create a greater than or equal filter', () => { + const now = new Date(); + const f = filter.byUpdateTime().greaterOrEqual(now); + expect(f).toEqual>({ + operator: 'GreaterThanEqual', + target: { + property: '_lastUpdateTimeUnix', + }, + value: now.toISOString(), + }); + }); + }); + + describe('for the REST schema', () => { + const anyFilter = maker(); + it('should map a text property filter', () => { + const f = filter.byProperty('name').equal('John'); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['name'], + valueText: 'John', + }); + }); + + it('should map an int property filter', () => { + const f = filter.byProperty('age').equal(18); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['age'], + valueInt: 18, + }); + }); + + it('should map a boolean property filter', () => { + const f = anyFilter.byProperty('isAdult').equal(true); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['isAdult'], + valueBoolean: true, + }); + }); + + it('should map a float property filter', () => { + const f = anyFilter.byProperty('age').equal(18.5); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['age'], + valueNumber: 18.5, + }); + }); + + it('should map a date property filter', () => { + const now = new Date(); + const f = anyFilter.byProperty('date').equal(now); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['date'], + valueDate: now.toISOString(), + }); + }); + + it('should map a text array property filter', () => { + const f = anyFilter.byProperty('names').equal(['John', 'Doe']); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['names'], + valueTextArray: ['John', 'Doe'], + }); + }); + + it('should map an int array property filter', () => { + const f = anyFilter.byProperty('ages').equal([18, 19]); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['ages'], + valueIntArray: [18, 19], + }); + }); + + it('should map a boolean array property filter', () => { + const f = anyFilter.byProperty('bools').equal([true, false]); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['bools'], + valueBooleanArray: [true, false], + }); + }); + + it('should map a float array property filter', () => { + const f = anyFilter.byProperty('ages').equal([18.5, 19.5]); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['ages'], + valueNumberArray: [18.5, 19.5], + }); + }); + + it('should map a date array property filter', () => { + const now1 = new Date(); + const now2 = new Date(); + const f = anyFilter.byProperty('dates').equal([now1, now2]); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['dates'], + valueDateArray: [now1.toISOString(), now2.toISOString()], + }); + }); + + it('should map a geo range property filter', () => { + const f = anyFilter.byProperty('location').withinGeoRange({ + latitude: 1, + longitude: 2, + distance: 3, + }); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'WithinGeoRange', + path: ['location'], + valueGeoRange: { + geoCoordinates: { + latitude: 1, + longitude: 2, + }, + distance: { + max: 3, + }, + }, + }); + }); + + it('should map a single target reference filter', () => { + const f = filter.byRef('self').byProperty('name').isNull(true); + expect(() => Serialize.filtersREST(f)).toThrow( + 'Cannot use Filter.byRef() in the aggregate API currently. Instead use Filter.byRefMultiTarget() and specify the target collection explicitly.' + ); + }); + + it('should map a multi target reference filter', () => { + const f = filter.byRefMultiTarget('self', 'Person').byProperty('name').isNull(true); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'IsNull', + path: ['self', 'Person', 'name'], + valueBoolean: true, + }); + }); + + it('should map a reference count filter', () => { + const f = filter.byRefCount('self').equal(2); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Equal', + path: ['self'], + valueInt: 2, + }); + }); + + it('should map an AND filter', () => { + const f = Filters.and(filter.byProperty('name').equal('John'), filter.byProperty('age').equal(18)); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'And', + operands: [ + { + operator: 'Equal', + path: ['name'], + valueText: 'John', + }, + { + operator: 'Equal', + path: ['age'], + valueInt: 18, + }, + ], + }); + }); + + it('should map an OR filter', () => { + const f = Filters.or(filter.byProperty('name').equal('John'), filter.byProperty('age').equal(18)); + const s = Serialize.filtersREST(f); + expect(s).toEqual({ + operator: 'Or', + operands: [ + { + operator: 'Equal', + path: ['name'], + valueText: 'John', + }, + { + operator: 'Equal', + path: ['age'], + valueInt: 18, + }, + ], + }); + }); + }); +}); diff --git a/src/collections/filters/utils.ts b/src/collections/filters/utils.ts new file mode 100644 index 00000000..9a8bf4b1 --- /dev/null +++ b/src/collections/filters/utils.ts @@ -0,0 +1,28 @@ +import { CountRef, FilterTargetInternal, MultiTargetRef, SingleTargetRef } from './types.js'; + +export class TargetGuards { + public static isSingleTargetRef(target?: FilterTargetInternal): target is SingleTargetRef { + if (!target) return false; + return (target as SingleTargetRef).type_ === 'single'; + } + + public static isMultiTargetRef(target?: FilterTargetInternal): target is MultiTargetRef { + if (!target) return false; + return (target as MultiTargetRef).type_ === 'multi'; + } + + public static isCountRef(target?: FilterTargetInternal): target is CountRef { + if (!target) return false; + return (target as CountRef).type_ === 'count'; + } + + public static isProperty(target?: FilterTargetInternal): target is string { + if (!target) return false; + return typeof target === 'string'; + } + + public static isTargetRef(target?: FilterTargetInternal): target is SingleTargetRef | MultiTargetRef { + if (!target) return false; + return TargetGuards.isSingleTargetRef(target) || TargetGuards.isMultiTargetRef(target); + } +} diff --git a/src/collections/generate/index.ts b/src/collections/generate/index.ts new file mode 100644 index 00000000..f3c227dd --- /dev/null +++ b/src/collections/generate/index.ts @@ -0,0 +1,369 @@ +import Connection from '../../connection/grpc.js'; + +import { ConsistencyLevel } from '../../data/index.js'; +import { DbVersionSupport } from '../../utils/dbVersion.js'; + +import { WeaviateInvalidInputError, WeaviateUnsupportedFeatureError } from '../../errors.js'; +import { toBase64FromMedia } from '../../index.js'; +import { SearchReply } from '../../proto/v1/search_get.js'; +import { Deserialize } from '../deserialize/index.js'; +import { + BaseBm25Options, + BaseHybridOptions, + BaseNearOptions, + BaseNearTextOptions, + Bm25Options, + FetchObjectsOptions, + GroupByBm25Options, + GroupByHybridOptions, + GroupByNearOptions, + GroupByNearTextOptions, + HybridOptions, + NearMediaType, + NearOptions, + SearchOptions, +} from '../query/types.js'; +import { Serialize } from '../serialize/index.js'; +import { + GenerateOptions, + GenerateReturn, + GenerativeGroupByReturn, + GenerativeReturn, + GroupByOptions, +} from '../types/index.js'; +import { Generate } from './types.js'; + +class GenerateManager implements Generate { + private connection: Connection; + private name: string; + private dbVersionSupport: DbVersionSupport; + private consistencyLevel?: ConsistencyLevel; + private tenant?: string; + + private constructor( + connection: Connection, + name: string, + dbVersionSupport: DbVersionSupport, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ) { + this.connection = connection; + this.name = name; + this.dbVersionSupport = dbVersionSupport; + this.consistencyLevel = consistencyLevel; + this.tenant = tenant; + } + + public static use( + connection: Connection, + name: string, + dbVersionSupport: DbVersionSupport, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ): GenerateManager { + return new GenerateManager(connection, name, dbVersionSupport, consistencyLevel, tenant); + } + + private checkSupportForNamedVectors = async (opts?: BaseNearOptions) => { + if (!Serialize.isNamedVectors(opts)) return; + const check = await this.dbVersionSupport.supportsNamedVectors(); + if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); + }; + + private checkSupportForBm25AndHybridGroupByQueries = async (query: 'Bm25' | 'Hybrid', opts?: any) => { + if (!Serialize.isGroupBy(opts)) return; + const check = await this.dbVersionSupport.supportsBm25AndHybridGroupByQueries(); + if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message(query)); + }; + + private async parseReply(reply: SearchReply) { + const deserialize = await Deserialize.use(this.dbVersionSupport); + return deserialize.generate(reply); + } + + private async parseGroupByReply( + opts: SearchOptions | GroupByOptions | undefined, + reply: SearchReply + ) { + const deserialize = await Deserialize.use(this.dbVersionSupport); + return Serialize.isGroupBy(opts) ? deserialize.generateGroupBy(reply) : deserialize.generate(reply); + } + + public fetchObjects( + generate: GenerateOptions, + opts?: FetchObjectsOptions + ): Promise> { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withFetch({ + ...Serialize.fetchObjects(opts), + generative: Serialize.generative(generate), + }) + ) + .then((reply) => this.parseReply(reply)); + } + + public bm25( + query: string, + generate: GenerateOptions, + opts?: BaseBm25Options + ): Promise>; + public bm25( + query: string, + generate: GenerateOptions, + opts: GroupByBm25Options + ): Promise>; + public bm25(query: string, generate: GenerateOptions, opts?: Bm25Options): GenerateReturn { + return Promise.all([ + this.checkSupportForNamedVectors(opts), + this.checkSupportForBm25AndHybridGroupByQueries('Bm25', opts), + ]) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withBm25({ + ...Serialize.bm25({ query, ...opts }), + generative: Serialize.generative(generate), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public hybrid( + query: string, + generate: GenerateOptions, + opts?: BaseHybridOptions + ): Promise>; + public hybrid( + query: string, + generate: GenerateOptions, + opts: GroupByHybridOptions + ): Promise>; + public hybrid(query: string, generate: GenerateOptions, opts?: HybridOptions): GenerateReturn { + return Promise.all([ + this.checkSupportForNamedVectors(opts), + this.checkSupportForBm25AndHybridGroupByQueries('Bm25', opts), + ]) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withHybrid({ + ...Serialize.hybrid({ query, ...opts }), + generative: Serialize.generative(generate), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearImage( + image: string | Buffer, + generate: GenerateOptions, + opts?: BaseNearOptions + ): Promise>; + public nearImage( + image: string | Buffer, + generate: GenerateOptions, + opts: GroupByNearOptions + ): Promise>; + public nearImage( + image: string | Buffer, + generate: GenerateOptions, + opts?: NearOptions + ): GenerateReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + toBase64FromMedia(image).then((image) => + search.withNearImage({ + ...Serialize.nearImage({ image, ...(opts ? opts : {}) }), + generative: Serialize.generative(generate), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearObject( + id: string, + generate: GenerateOptions, + opts?: BaseNearOptions + ): Promise>; + public nearObject( + id: string, + generate: GenerateOptions, + opts: GroupByNearOptions + ): Promise>; + public nearObject(id: string, generate: GenerateOptions, opts?: NearOptions): GenerateReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withNearObject({ + ...Serialize.nearObject({ id, ...(opts ? opts : {}) }), + generative: Serialize.generative(generate), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearText( + query: string | string[], + generate: GenerateOptions, + opts?: BaseNearTextOptions + ): Promise>; + public nearText( + query: string | string[], + generate: GenerateOptions, + opts: GroupByNearTextOptions + ): Promise>; + public nearText( + query: string | string[], + generate: GenerateOptions, + opts?: NearOptions + ): GenerateReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withNearText({ + ...Serialize.nearText({ query, ...(opts ? opts : {}) }), + generative: Serialize.generative(generate), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearVector( + vector: number[], + generate: GenerateOptions, + opts?: BaseNearOptions + ): Promise>; + public nearVector( + vector: number[], + generate: GenerateOptions, + opts: GroupByNearOptions + ): Promise>; + public nearVector( + vector: number[], + generate: GenerateOptions, + opts?: NearOptions + ): GenerateReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withNearVector({ + ...Serialize.nearVector({ vector, ...(opts ? opts : {}) }), + generative: Serialize.generative(generate), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearMedia( + media: string | Buffer, + type: NearMediaType, + generate: GenerateOptions, + opts?: BaseNearOptions + ): Promise>; + public nearMedia( + media: string | Buffer, + type: NearMediaType, + generate: GenerateOptions, + opts: GroupByNearOptions + ): Promise>; + public nearMedia( + media: string | Buffer, + type: NearMediaType, + generate: GenerateOptions, + opts?: NearOptions + ): GenerateReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => { + let reply: Promise; + const generative = Serialize.generative(generate); + const groupBy = Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined; + switch (type) { + case 'audio': + reply = toBase64FromMedia(media).then((media) => + search.withNearAudio({ + ...Serialize.nearAudio({ audio: media, ...(opts ? opts : {}) }), + generative, + groupBy, + }) + ); + break; + case 'depth': + reply = toBase64FromMedia(media).then((media) => + search.withNearDepth({ + ...Serialize.nearDepth({ depth: media, ...(opts ? opts : {}) }), + generative, + groupBy, + }) + ); + break; + case 'image': + reply = toBase64FromMedia(media).then((media) => + search.withNearImage({ + ...Serialize.nearImage({ image: media, ...(opts ? opts : {}) }), + generative, + groupBy, + }) + ); + break; + case 'imu': + reply = toBase64FromMedia(media).then((media) => + search.withNearIMU({ + ...Serialize.nearIMU({ imu: media, ...(opts ? opts : {}) }), + generative, + groupBy, + }) + ); + break; + case 'thermal': + reply = toBase64FromMedia(media).then((media) => + search.withNearThermal({ + ...Serialize.nearThermal({ thermal: media, ...(opts ? opts : {}) }), + generative, + groupBy, + }) + ); + break; + case 'video': + reply = toBase64FromMedia(media).then((media) => + search.withNearVideo({ + ...Serialize.nearVideo({ video: media, ...(opts ? opts : {}) }), + generative, + groupBy, + }) + ); + break; + default: + throw new WeaviateInvalidInputError(`Invalid media type: ${type}`); + } + return reply; + }) + .then((reply) => this.parseGroupByReply(opts, reply)); + } +} + +export default GenerateManager.use; + +export { Generate } from './types.js'; diff --git a/src/collections/generate/integration.test.ts b/src/collections/generate/integration.test.ts new file mode 100644 index 00000000..75660a01 --- /dev/null +++ b/src/collections/generate/integration.test.ts @@ -0,0 +1,316 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import { WeaviateUnsupportedFeatureError } from '../../errors.js'; +import weaviate, { WeaviateClient } from '../../index.js'; +import { Collection } from '../collection/index.js'; +import { GenerateOptions, GroupByOptions } from '../types/index.js'; + +const maybe = process.env.OPENAI_APIKEY ? describe : describe.skip; + +maybe('Testing of the collection.generate methods with a simple collection', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionGenerateSimple'; + let id: string; + let vector: number[]; + + type TestCollectionGenerateSimple = { + testProp: string; + }; + + const generateOpts: GenerateOptions = { + singlePrompt: 'Write a haiku about ducks for {testProp}', + groupedTask: 'What is the value of testProp here?', + groupedProperties: ['testProp'], + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal({ + port: 8086, + grpcPort: 50057, + headers: { + 'X-Openai-Api-Key': process.env.OPENAI_APIKEY!, + }, + }); + collection = client.collections.get(collectionName); + id = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + generative: weaviate.configure.generative.openAI(), + vectorizers: weaviate.configure.vectorizer.text2VecOpenAI({ + vectorizeCollectionName: false, + }), + }) + .then(() => { + return collection.data.insert({ + properties: { + testProp: 'test', + }, + }); + }); + const res = await collection.query.fetchObjectById(id, { includeVector: true }); + vector = res?.vectors.default!; + }); + + describe('using a non-generic collection', () => { + it('should generate without search', async () => { + const ret = await client.collections.get(collectionName).generate.fetchObjects({ + singlePrompt: 'Write a haiku about ducks for {testProp}', + groupedTask: 'What is the value of testProp here?', + groupedProperties: ['testProp'], + }); + expect(ret.objects.length).toEqual(1); + expect(ret.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].generated).toBeDefined(); + }); + }); + + describe('using a generic collection', () => { + it('should generate without search', async () => { + const ret = await collection.generate.fetchObjects(generateOpts); + expect(ret.objects.length).toEqual(1); + expect(ret.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].generated).toBeDefined(); + }); + + it('should generate without search specifying return properties', async () => { + const ret = await collection.generate.fetchObjects(generateOpts, { + returnProperties: ['testProp'], + }); + expect(ret.objects.length).toEqual(1); + expect(ret.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].generated).toBeDefined(); + }); + + it('should generate with bm25', async () => { + const ret = await collection.generate.bm25('test', generateOpts); + expect(ret.objects.length).toEqual(1); + expect(ret.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].generated).toBeDefined(); + }); + + it('should generate with hybrid', async () => { + const ret = await collection.generate.hybrid('test', generateOpts); + expect(ret.objects.length).toEqual(1); + expect(ret.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].generated).toBeDefined(); + }); + + it.skip('should generate with nearObject', async () => { + const ret = await collection.generate.nearObject(id, generateOpts); + expect(ret.objects.length).toEqual(1); + expect(ret.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].generated).toBeDefined(); + }); + + it('should generate with nearText', async () => { + const ret = await collection.generate.nearText(['test'], generateOpts); + expect(ret.objects.length).toEqual(1); + expect(ret.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].generated).toBeDefined(); + }); + + it('should generate with nearVector', async () => { + const ret = await collection.generate.nearVector(vector, generateOpts); + expect(ret.objects.length).toEqual(1); + expect(ret.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].generated).toBeDefined(); + }); + }); +}); + +maybe('Testing of the groupBy collection.generate methods with a simple collection', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionGenerateGroupBySimple'; + let id: string; + let vector: number[]; + + type TestCollectionGenerateGroupBySimple = { + testProp: string; + }; + + const generateOpts: GenerateOptions = { + singlePrompt: 'Write a haiku about ducks for {testProp}', + groupedTask: 'What is the value of testProp here?', + groupedProperties: ['testProp'], + }; + + const groupByArgs: GroupByOptions = { + numberOfGroups: 1, + objectsPerGroup: 1, + property: 'testProp', + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal({ + port: 8086, + grpcPort: 50057, + headers: { + 'X-Openai-Api-Key': process.env.OPENAI_APIKEY!, + }, + }); + collection = client.collections.get(collectionName); + id = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + generative: weaviate.configure.generative.openAI(), + vectorizers: weaviate.configure.vectorizer.text2VecOpenAI({ + vectorizeCollectionName: false, + }), + }) + .then(() => { + return collection.data.insert({ + properties: { + testProp: 'test', + }, + }); + }); + const res = await collection.query.fetchObjectById(id, { includeVector: true }); + vector = res?.vectors.default!; + }); + + // it('should groupBy without search', async () => { + // const ret = await collection.groupBy.fetchObjects(groupByArgs); + // expect(ret.objects.length).toEqual(1); + // expect(ret.groups).toBeDefined(); + // expect(Object.keys(ret.groups)).toEqual(['test']); + // expect(ret.objects[0].properties.testProp).toEqual('test'); + // expect(ret.objects[0].metadata.uuid).toEqual(id); + // expect(ret.objects[0].belongsToGroup).toEqual('test'); + // }); + + // it('should groupBy without search specifying return properties', async () => { + // const ret = await collection.groupBy.fetchObjects({ + // returnProperties: ['testProp'], + // returnMetadata: ['uuid'], + // ...groupByArgs, + // }); + // expect(ret.objects.length).toEqual(1); + // expect(ret.groups).toBeDefined(); + // expect(Object.keys(ret.groups)).toEqual(['test']); + // expect(ret.objects[0].properties.testProp).toEqual('test'); + // expect(ret.objects[0].metadata.uuid).toEqual(id); + // expect(ret.objects[0].belongsToGroup).toEqual('test'); + // }); + + it('should groupBy with bm25', async () => { + const query = () => + collection.generate.bm25('test', generateOpts, { + groupBy: groupByArgs, + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const ret = await query(); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it('should groupBy with hybrid', async () => { + const query = () => + collection.generate.hybrid('test', generateOpts, { + groupBy: groupByArgs, + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const ret = await query(); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it.skip('should groupBy with nearObject', async () => { + const ret = await collection.generate.nearObject(id, generateOpts, { + groupBy: groupByArgs, + }); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(ret.generated).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.groups.test.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it('should groupBy with nearText', async () => { + const ret = await collection.generate.nearText(['test'], generateOpts, { + groupBy: groupByArgs, + }); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(ret.generated).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.groups.test.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it('should groupBy with nearVector', async () => { + const ret = await collection.generate.nearVector(vector, generateOpts, { + groupBy: groupByArgs, + }); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(ret.generated).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.groups.test.generated).toBeDefined(); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); +}); diff --git a/src/collections/generate/types.ts b/src/collections/generate/types.ts new file mode 100644 index 00000000..24cccacf --- /dev/null +++ b/src/collections/generate/types.ts @@ -0,0 +1,354 @@ +import { + BaseBm25Options, + BaseHybridOptions, + BaseNearOptions, + BaseNearTextOptions, + Bm25Options, + FetchObjectsOptions, + GroupByBm25Options, + GroupByHybridOptions, + GroupByNearOptions, + GroupByNearTextOptions, + HybridOptions, + NearMediaType, + NearOptions, + NearTextOptions, +} from '../query/types.js'; +import { + GenerateOptions, + GenerateReturn, + GenerativeGroupByReturn, + GenerativeReturn, +} from '../types/index.js'; + +interface Bm25 { + /** + * Perform retrieval-augmented generation (RaG) on the results of a keyword-based BM25 search of objects in this collection. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/bm25) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {BaseBm25Options} [opts] - The available options for performing the BM25 search. + * @return {Promise>} - The results of the search including the generated data. + */ + bm25(query: string, generate: GenerateOptions, opts?: BaseBm25Options): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a keyword-based BM25 search of objects in this collection. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/bm25) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {GroupByBm25Options} opts - The available options for performing the BM25 search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + */ + bm25( + query: string, + generate: GenerateOptions, + opts: GroupByBm25Options + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a keyword-based BM25 search of objects in this collection. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/bm25) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {Bm25Options} [opts] - The available options for performing the BM25 search. + * @return {GenerateReturn} - The results of the search including the generated data. + */ + bm25(query: string, generate: GenerateOptions, opts?: Bm25Options): GenerateReturn; +} + +interface Hybrid { + /** + * Perform retrieval-augmented generation (RaG) on the results of an object search in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/hybrid) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {BaseHybridOptions} [opts] - The available options for performing the hybrid search. + * @return {Promise>} - The results of the search including the generated data. + */ + hybrid( + query: string, + generate: GenerateOptions, + opts?: BaseHybridOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of an object search in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/hybrid) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {GroupByHybridOptions} opts - The available options for performing the hybrid search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + */ + hybrid( + query: string, + generate: GenerateOptions, + opts: GroupByHybridOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of an object search in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/hybrid) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {HybridOptions} [opts] - The available options for performing the hybrid search. + * @return {GenerateReturn} - The results of the search including the generated data. + */ + hybrid(query: string, generate: GenerateOptions, opts?: HybridOptions): GenerateReturn; +} + +interface NearMedia { + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-audio object search in this collection using an audio-capable vectorization module and vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-bind) for a more detailed explanation. + * + * NOTE: You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind`. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string | Buffer} media - The media file to search on. This can be a base64 string, a file path string, or a buffer. + * @param {NearMediaType} type - The type of media to search on. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {BaseNearOptions} [opts] - The available options for performing the near-media search. + * @return {Promise>} - The results of the search including the generated data. + */ + nearMedia( + media: string | Buffer, + type: NearMediaType, + generate: GenerateOptions, + opts?: BaseNearOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-audio object search in this collection using an audio-capable vectorization module and vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-bind) for a more detailed explanation. + * + * NOTE: You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind`. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string | Buffer} media - The media file to search on. This can be a base64 string, a file path string, or a buffer. + * @param {NearMediaType} type - The type of media to search on. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {GroupByNearOptions} opts - The available options for performing the near-media search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + */ + nearMedia( + media: string | Buffer, + type: NearMediaType, + generate: GenerateOptions, + opts: GroupByNearOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-audio object search in this collection using an audio-capable vectorization module and vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-bind) for a more detailed explanation. + * + * NOTE: You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind`. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string | Buffer} media - The media to search on. This can be a base64 string, a file path string, or a buffer. + * @param {NearMediaType} type - The type of media to search on. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {NearOptions} [opts] - The available options for performing the near-media search. + * @return {GenerateReturn} - The results of the search including the generated data. + */ + nearMedia( + media: string | Buffer, + type: NearMediaType, + generate: GenerateOptions, + opts?: NearOptions + ): GenerateReturn; +} + +interface NearObject { + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-object object search in this collection using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#nearobject) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string} id - The ID of the object to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {BaseNearOptions} [opts] - The available options for performing the near-object search. + * @return {Promise>} - The results of the search including the generated data. + */ + nearObject( + id: string, + generate: GenerateOptions, + opts?: BaseNearOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-object object search in this collection using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#nearobject) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string} id - The ID of the object to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {GroupByNearOptions} opts - The available options for performing the near-object search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + */ + nearObject( + id: string, + generate: GenerateOptions, + opts: GroupByNearOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-object object search in this collection using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#nearobject) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string} id - The ID of the object to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {NearOptions} [opts] - The available options for performing the near-object search. + * @return {GenerateReturn} - The results of the search including the generated data. + */ + nearObject(id: string, generate: GenerateOptions, opts?: NearOptions): GenerateReturn; +} + +interface NearText { + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-image object search in this collection using the image-capable vectorization module and vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#neartext) for a more detailed explanation. + * + * NOTE: You must have a text-capable vectorization module installed in order to use this method, e.g. any of the `text2vec-` and `multi2vec-` modules. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string | string[]} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {BaseNearTextOptions} [opts] - The available options for performing the near-text search. + * @return {Promise>} - The results of the search including the generated data. + */ + nearText( + query: string | string[], + generate: GenerateOptions, + opts?: BaseNearTextOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-image object search in this collection using the image-capable vectorization module and vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#neartext) for a more detailed explanation. + * + * NOTE: You must have a text-capable vectorization module installed in order to use this method, e.g. any of the `text2vec-` and `multi2vec-` modules. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string | string[]} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {GroupByNearTextOptions} opts - The available options for performing the near-text search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + */ + nearText( + query: string | string[], + generate: GenerateOptions, + opts: GroupByNearTextOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-image object search in this collection using the image-capable vectorization module and vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#neartext) for a more detailed explanation. + * + * NOTE: You must have a text-capable vectorization module installed in order to use this method, e.g. any of the `text2vec-` and `multi2vec-` modules. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string | string[]} query - The query to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {NearTextOptions} [opts] - The available options for performing the near-text search. + * @return {GenerateReturn} - The results of the search including the generated data. + */ + nearText( + query: string | string[], + generate: GenerateOptions, + opts?: NearTextOptions + ): GenerateReturn; +} + +interface NearVector { + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-vector object search in this collection using vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {number[]} vector - The vector to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {BaseNearOptions} [opts] - The available options for performing the near-vector search. + * @return {Promise>} - The results of the search including the generated data. + */ + nearVector( + vector: number[], + generate: GenerateOptions, + opts?: BaseNearOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-vector object search in this collection using vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {number[]} vector - The vector to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {GroupByNearOptions} opts - The available options for performing the near-vector search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + */ + nearVector( + vector: number[], + generate: GenerateOptions, + opts: GroupByNearOptions + ): Promise>; + /** + * Perform retrieval-augmented generation (RaG) on the results of a by-vector object search in this collection using vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {number[]} vector - The vector to search for. + * @param {GenerateOptions} generate - The available options for performing the generation. + * @param {NearOptions} [opts] - The available options for performing the near-vector search. + * @return {GenerateReturn} - The results of the search including the generated data. + */ + nearVector(vector: number[], generate: GenerateOptions, opts?: NearOptions): GenerateReturn; +} + +export interface Generate + extends Bm25, + Hybrid, + NearMedia, + NearObject, + NearText, + NearVector { + fetchObjects: (generate: GenerateOptions, opts?: FetchObjectsOptions) => Promise>; +} diff --git a/src/collections/index.ts b/src/collections/index.ts new file mode 100644 index 00000000..4acab7ef --- /dev/null +++ b/src/collections/index.ts @@ -0,0 +1,308 @@ +import Connection from '../connection/grpc.js'; +import { WeaviateInvalidInputError, WeaviateUnsupportedFeatureError } from '../errors.js'; +import { WeaviateClass } from '../openapi/types.js'; +import ClassExists from '../schema/classExists.js'; +import { ClassCreator, ClassDeleter, ClassGetter, SchemaGetter } from '../schema/index.js'; +import { DbVersionSupport } from '../utils/dbVersion.js'; +import collection, { Collection } from './collection/index.js'; +import { classToCollection, resolveProperty, resolveReference } from './config/utils.js'; +import { QuantizerGuards } from './configure/parsing.js'; +import { configGuards } from './index.js'; +import { + CollectionConfig, + GenerativeConfig, + GenerativeSearch, + InvertedIndexConfigCreate, + ModuleConfig, + MultiTenancyConfigCreate, + Properties, + PropertyConfigCreate, + ReferenceConfigCreate, + ReplicationConfigCreate, + Reranker, + RerankerConfig, + ShardingConfigCreate, + VectorConfigCreate, + VectorIndexConfigCreate, + VectorIndexConfigDynamicCreate, + VectorIndexConfigFlatCreate, + VectorIndexConfigHNSWCreate, + VectorIndexType, + Vectorizer, + VectorizerConfig, + VectorizersConfigCreate, +} from './types/index.js'; +import { PrimitiveKeys } from './types/internal.js'; + +/** + * All the options available when creating a new collection. + * + * Inspect [the docs](https://weaviate.io/developers/weaviate/configuration) for more information on the + * different configuration options and how they affect the behavior of your collection. + */ +export type CollectionConfigCreate = { + /** The name of the collection. */ + name: N; + /** The description of the collection. */ + description?: string; + /** The configuration for Weaviate's generative capabilities. */ + generative?: ModuleConfig; + /** The configuration for Weaviate's inverted index. */ + invertedIndex?: InvertedIndexConfigCreate; + /** The configuration for Weaviate's multi-tenancy capabilities. */ + multiTenancy?: MultiTenancyConfigCreate; + /** The properties of the objects in the collection. */ + properties?: PropertyConfigCreate[]; + /** The references of the objects in the collection. */ + references?: ReferenceConfigCreate[]; + /** The configuration for Weaviate's replication strategy. Is mutually exclusive with `sharding`. */ + replication?: ReplicationConfigCreate; + /** The configuration for Weaviate's reranking capabilities. */ + reranker?: ModuleConfig; + /** The configuration for Weaviate's sharding strategy. Is mutually exclusive with `replication`. */ + sharding?: ShardingConfigCreate; + /** The configuration for Weaviate's vectorizer(s) capabilities. */ + vectorizers?: VectorizersConfigCreate; +}; + +const parseVectorIndex = (module: ModuleConfig): any => { + if (module.config === undefined) return undefined; + if (module.name === 'dynamic') { + const { hnsw, flat, ...conf } = module.config as VectorIndexConfigDynamicCreate; + return { + ...conf, + hnsw: parseVectorIndex({ name: 'hnsw', config: hnsw }), + flat: parseVectorIndex({ name: 'flat', config: flat }), + }; + } + const { quantizer, ...conf } = module.config as + | VectorIndexConfigFlatCreate + | VectorIndexConfigHNSWCreate + | Record; + if (quantizer === undefined) return conf; + if (QuantizerGuards.isBQCreate(quantizer)) { + const { type, ...quant } = quantizer; + return { + ...conf, + bq: { + ...quant, + enabled: true, + }, + }; + } + if (QuantizerGuards.isPQCreate(quantizer)) { + const { type, ...quant } = quantizer; + return { + ...conf, + pq: { + ...quant, + enabled: true, + }, + }; + } +}; + +const parseVectorizerConfig = (config?: VectorizerConfig): any => { + if (config === undefined) return {}; + const { vectorizeCollectionName, ...rest } = config as any; + return { + ...rest, + vectorizeClassName: vectorizeCollectionName, + }; +}; + +const collections = (connection: Connection, dbVersionSupport: DbVersionSupport) => { + const listAll = () => + new SchemaGetter(connection) + .do() + .then((schema) => (schema.classes ? schema.classes.map(classToCollection) : [])); + const deleteCollection = (name: string) => new ClassDeleter(connection).withClassName(name).do(); + return { + create: async function ( + config: CollectionConfigCreate + ) { + const { name, invertedIndex, multiTenancy, replication, sharding, ...rest } = config; + + const supportsDynamicVectorIndex = await dbVersionSupport.supportsDynamicVectorIndex(); + const supportsNamedVectors = await dbVersionSupport.supportsNamedVectors(); + const supportsHNSWAndBQ = await dbVersionSupport.supportsHNSWAndBQ(); + + const moduleConfig: any = {}; + if (config.generative) { + moduleConfig[config.generative.name] = config.generative.config ? config.generative.config : {}; + } + if (config.reranker) { + moduleConfig[config.reranker.name] = config.reranker.config ? config.reranker.config : {}; + } + + const makeVectorsConfig = (configVectorizers: VectorizersConfigCreate) => { + let vectorizers: string[] = []; + const vectorsConfig: Record = {}; + const vectorizersConfig = Array.isArray(configVectorizers) + ? configVectorizers + : [ + { + ...configVectorizers, + name: 'default', + }, + ]; + vectorizersConfig.forEach((v) => { + if (v.vectorIndex.name === 'dynamic' && !supportsDynamicVectorIndex.supports) { + throw new WeaviateUnsupportedFeatureError(supportsDynamicVectorIndex.message); + } + const vectorConfig: any = { + vectorIndexConfig: parseVectorIndex(v.vectorIndex), + vectorIndexType: v.vectorIndex.name, + vectorizer: {}, + }; + vectorizers = [...vectorizers, v.vectorizer.name]; + vectorConfig.vectorizer[v.vectorizer.name] = { + properties: v.properties, + ...parseVectorizerConfig(v.vectorizer.config), + }; + if (v.name === undefined) { + throw new WeaviateInvalidInputError( + 'vectorName is required for each vectorizer when specifying more than one vectorizer' + ); + } + vectorsConfig[v.name] = vectorConfig; + }); + return { vectorsConfig, vectorizers }; + }; + + const makeLegacyVectorizer = ( + configVectorizers: VectorConfigCreate, undefined, string, Vectorizer> + ) => { + const vectorizer = configVectorizers.vectorizer.name; + const moduleConfig: any = {}; + moduleConfig[vectorizer] = parseVectorizerConfig(configVectorizers.vectorizer.config); + + const vectorIndexConfig = parseVectorIndex(configVectorizers.vectorIndex); + const vectorIndexType = configVectorizers.vectorIndex.name; + + if ( + vectorIndexType === 'hnsw' && + configVectorizers.vectorIndex.config !== undefined && + configGuards.quantizer.isBQ(configVectorizers.vectorIndex.config.quantizer as any) + ) { + if (!supportsHNSWAndBQ.supports) { + throw new WeaviateUnsupportedFeatureError(supportsHNSWAndBQ.message); + } + } + + if (vectorIndexType === 'dynamic' && !supportsDynamicVectorIndex.supports) { + throw new WeaviateUnsupportedFeatureError(supportsDynamicVectorIndex.message); + } + + return { + vectorizer, + moduleConfig, + vectorIndexConfig, + vectorIndexType, + }; + }; + + let schema: any = { + ...rest, + class: name, + invertedIndexConfig: invertedIndex, + moduleConfig: moduleConfig, + multiTenancyConfig: multiTenancy, + replicationConfig: replication, + shardingConfig: sharding, + }; + let vectorizers: string[] = []; + if (supportsNamedVectors.supports) { + const { vectorsConfig, vectorizers: vecs } = config.vectorizers + ? makeVectorsConfig(config.vectorizers) + : { vectorsConfig: undefined, vectorizers: [] }; + schema.vectorConfig = vectorsConfig; + vectorizers = [...vecs]; + } else { + if (config.vectorizers !== undefined && Array.isArray(config.vectorizers)) { + throw new WeaviateUnsupportedFeatureError(supportsNamedVectors.message); + } + const configs = config.vectorizers + ? makeLegacyVectorizer(config.vectorizers) + : { + vectorizer: undefined, + moduleConfig: undefined, + vectorIndexConfig: undefined, + vectorIndexType: undefined, + }; + schema = { + ...schema, + moduleConfig: { + ...schema.moduleConfig, + ...configs.moduleConfig, + }, + vectorizer: configs.vectorizer, + vectorIndexConfig: configs.vectorIndexConfig, + vectorIndexType: configs.vectorIndexType, + }; + if (configs.vectorizer !== undefined) { + vectorizers = [configs.vectorizer]; + } + } + const properties = config.properties + ? config.properties.map((prop) => resolveProperty(prop as any, vectorizers)) + : []; + const references = config.references ? config.references.map(resolveReference) : []; + schema.properties = [...properties, ...references]; + + await new ClassCreator(connection).withClass(schema).do(); + return collection(connection, name, dbVersionSupport); + }, + createFromSchema: async function (config: WeaviateClass) { + const { class: name } = await new ClassCreator(connection).withClass(config).do(); + return collection(connection, name as string, dbVersionSupport); + }, + delete: deleteCollection, + deleteAll: () => listAll().then((configs) => Promise.all(configs?.map((c) => deleteCollection(c.name)))), + exists: (name: string) => new ClassExists(connection).withClassName(name).do(), + export: (name: string) => + new ClassGetter(connection) + .withClassName(name) + .do() + .then(classToCollection), + listAll: listAll, + get: ( + name: TName + ) => collection(connection, name, dbVersionSupport), + }; +}; + +export interface Collections { + create( + config: CollectionConfigCreate + ): Promise>; + createFromSchema(config: WeaviateClass): Promise>; + delete(collection: string): Promise; + deleteAll(): Promise; + exists(name: string): Promise; + export(name: string): Promise; + get( + name: TName + ): Collection; + listAll(): Promise; + // use( + // name: TName + // ): Collection; +} + +export default collections; +export * from './aggregate/index.js'; +export * from './backup/index.js'; +export * from './cluster/index.js'; +export * from './collection/index.js'; +export * from './config/index.js'; +export * from './configure/index.js'; +export * from './data/index.js'; +export * from './filters/index.js'; +export * from './generate/index.js'; +export * from './iterator/index.js'; +export * from './query/index.js'; +export * from './references/index.js'; +export * from './sort/index.js'; +export * from './tenants/index.js'; +export * from './types/index.js'; diff --git a/src/collections/integration.test.ts b/src/collections/integration.test.ts new file mode 100644 index 00000000..308b2764 --- /dev/null +++ b/src/collections/integration.test.ts @@ -0,0 +1,632 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import weaviate, { WeaviateClient } from '../index'; +import { + CollectionConfigCreate, + GeoCoordinate, + PQConfig, + PhoneNumber, + Text2VecContextionaryConfig, + Text2VecOpenAIConfig, + VectorIndexConfigHNSW, +} from './types/index'; + +const fail = (msg: string) => { + throw new Error(msg); +}; + +describe('Testing of the collections.create method', () => { + let cluster: WeaviateClient; + let contextionary: WeaviateClient; + let openai: WeaviateClient; + + beforeAll(async () => { + cluster = await weaviate.connectToLocal({ + port: 8087, + grpcPort: 50051, + }); + contextionary = await weaviate.connectToLocal({ + port: 8080, + grpcPort: 50051, + }); + openai = await weaviate.connectToLocal({ + port: 8086, + grpcPort: 50051, + }); + }); + + it('should be able to create a simple collection with a generic', async () => { + const collectionName = 'TestCollectionSimpleGeneric'; + type TestCollectionSimple = { + testProp: string; + }; + const response = await contextionary.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + }) + .then(() => contextionary.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(1); + expect(response.properties[0].name).toEqual('testProp'); + expect(response.properties[0].dataType).toEqual('text'); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + + await contextionary.collections.delete(collectionName); + }); + + it('should be able to create a simple collection without a generic', async () => { + const collectionName = 'TestCollectionSimpleNonGeneric'; + const response = await contextionary.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + }) + .then(() => contextionary.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(1); + expect(response.properties[0].name).toEqual('testProp'); + expect(response.properties[0].dataType).toEqual('text'); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + + await contextionary.collections.delete(collectionName); + }); + + it('should be able to create a simple collection without a generic and no properties', async () => { + const collectionName = 'TestCollectionSimpleNonGeneric'; + const response = await contextionary.collections + .create({ + name: collectionName, + }) + .then(() => contextionary.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(0); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + + await contextionary.collections.delete(collectionName); + }); + + it('should be able to create a simple collection without a generic using a schema var', async () => { + const collectionName = 'TestCollectionSimpleNonGenericVar'; + const schema = { + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text' as const, + }, + ], + }; + const response = await contextionary.collections + .create(schema) + .then(async (collection) => expect(await collection.exists()).toEqual(true)) + .then(() => contextionary.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(1); + expect(response.properties[0].name).toEqual('testProp'); + expect(response.properties[0].dataType).toEqual('text'); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + + await contextionary.collections.delete(collectionName); + }); + + it('should be able to create a simple collection with a generic using a schema var with const', async () => { + const collectionName = 'TestCollectionSimpleGenericVarConst'; + type TestCollectionSimple = { + testProp: string; + }; + const schema = { + name: collectionName, + properties: [ + { + name: 'testProp' as const, + dataType: 'text' as const, + }, + ], + }; + const response = await contextionary.collections + .create(schema) + .then(async (collection) => expect(await collection.exists()).toEqual(true)) + .then(() => contextionary.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(1); + expect(response.properties[0].name).toEqual('testProp'); + expect(response.properties[0].dataType).toEqual('text'); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + + await contextionary.collections.delete(collectionName); + }); + + it('should be able to create a simple collection with a generic using a schema var with type', async () => { + const collectionName = 'TestCollectionSimpleGenericVarType'; + type TestCollectionSimple = { + testProp: string; + }; + const schema: CollectionConfigCreate = { + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + }; + const response = await contextionary.collections + .create(schema) + .then(async (collection) => expect(await collection.exists()).toEqual(true)) + .then(() => contextionary.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(1); + expect(response.properties[0].name).toEqual('testProp'); + expect(response.properties[0].dataType).toEqual('text'); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + + await contextionary.collections.delete(collectionName); + }); + + it('should be able to create a nested collection', async () => { + const collectionName = 'TestCollectionNested'; + type TestCollectionNested = { + testProp: { + nestedProp: string; + }; + }; + const response = await contextionary.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'object', + nestedProperties: [ + { + name: 'nestedProp', + dataType: 'text', + }, + ], + }, + ], + }) + .then(async (collection) => expect(await collection.exists()).toEqual(true)) + .then(() => contextionary.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties.length).toEqual(1); + expect(response.properties[0].name).toEqual('testProp'); + expect(response.properties[0].dataType).toEqual('object'); + expect(response.properties[0].nestedProperties?.length).toEqual(1); + expect(response.properties[0].nestedProperties?.[0].name).toEqual('nestedProp'); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + + await contextionary.collections.delete(collectionName); + }); + + it('should be able to create a collection with generic properties', () => { + const collectionName = 'TestCollectionGenericProperties'; + + type TestCollectionGenericProperties = { + text: string; + texts: string[]; + number: number; + numbers: number[]; + int: number; + ints: number[]; + date: Date; + dates: Date[]; + boolean: boolean; + booleans: boolean[]; + object: { + nestedProp: string; + }; + objects: { + nestedProp: string; + }[]; + blob: string; + geoCoordinates: GeoCoordinate; + phoneNumber: PhoneNumber; + }; + + cluster.collections.create({ + name: collectionName, + properties: [ + { + name: 'text', + dataType: 'text', + }, + { + name: 'texts', + dataType: 'text[]', + }, + { + name: 'number', + dataType: 'number', + }, + { + name: 'numbers', + dataType: 'number[]', + }, + { + name: 'int', + dataType: 'int', + }, + { + name: 'ints', + dataType: 'int[]', + }, + { + name: 'date', + dataType: 'date', + }, + { + name: 'dates', + dataType: 'date[]', + }, + { + name: 'boolean', + dataType: 'boolean', + }, + { + name: 'booleans', + dataType: 'boolean[]', + }, + { + name: 'object', + dataType: 'object', + nestedProperties: [ + { + name: 'nestedProp', + dataType: 'text', + }, + ], + }, + { + name: 'objects', + dataType: 'object[]', + nestedProperties: [ + { + name: 'nestedProp', + dataType: 'text', + }, + ], + }, + { + name: 'blob', + dataType: 'blob', + }, + { + name: 'geoCoordinates', + dataType: 'geoCoordinates', + }, + { + name: 'phoneNumber', + dataType: 'phoneNumber', + }, + ], + }); + }); + + it('should be able to create a complex collection', async () => { + const collectionName = 'TestCollectionSimple'; + const response = await cluster.collections + .create({ + name: collectionName, + description: 'A test collection', + invertedIndex: { + bm25: { + b: 0.8, + k1: 1.3, + }, + cleanupIntervalSeconds: 10, + indexTimestamps: true, + indexPropertyLength: true, + indexNullState: true, + stopwords: { + preset: 'en', + additions: ['a'], + removals: ['the'], + }, + }, + properties: [ + { + name: 'text', + dataType: weaviate.configure.dataType.TEXT, + }, + { + name: 'texts', + dataType: weaviate.configure.dataType.TEXT_ARRAY, + }, + { + name: 'number', + dataType: weaviate.configure.dataType.NUMBER, + }, + { + name: 'numbers', + dataType: weaviate.configure.dataType.NUMBER_ARRAY, + }, + { + name: 'int', + dataType: weaviate.configure.dataType.INT, + }, + { + name: 'ints', + dataType: weaviate.configure.dataType.INT_ARRAY, + }, + { + name: 'date', + dataType: weaviate.configure.dataType.DATE, + }, + { + name: 'dates', + dataType: weaviate.configure.dataType.DATE_ARRAY, + }, + { + name: 'boolean', + dataType: weaviate.configure.dataType.BOOLEAN, + }, + { + name: 'booleans', + dataType: weaviate.configure.dataType.BOOLEAN_ARRAY, + }, + { + name: 'object', + dataType: weaviate.configure.dataType.OBJECT, + nestedProperties: [ + { + name: 'nestedProp', + dataType: weaviate.configure.dataType.TEXT, + }, + ], + }, + { + name: 'objects', + dataType: weaviate.configure.dataType.OBJECT_ARRAY, + nestedProperties: [ + { + name: 'nestedProp', + dataType: weaviate.configure.dataType.TEXT, + }, + ], + }, + { + name: 'blob', + dataType: weaviate.configure.dataType.BLOB, + }, + { + name: 'geoCoordinates', + dataType: weaviate.configure.dataType.GEO_COORDINATES, + }, + { + name: 'phoneNumber', + dataType: weaviate.configure.dataType.PHONE_NUMBER, + }, + ], + multiTenancy: { + enabled: true, + }, + replication: { + factor: 2, + }, + vectorizers: weaviate.configure.vectorizer.text2VecContextionary({ + vectorIndexConfig: { + name: 'hnsw', + config: { + cleanupIntervalSeconds: 10, + distance: 'dot', + dynamicEfFactor: 6, + dynamicEfMax: 100, + dynamicEfMin: 10, + ef: -2, + efConstruction: 100, + flatSearchCutoff: 41000, + maxConnections: 72, + quantizer: { + bitCompression: true, + centroids: 128, + encoder: { + distribution: 'normal', + type: 'tile', + }, + segments: 4, + trainingLimit: 100001, + type: 'pq', + }, + skip: true, + vectorCacheMaxObjects: 100000, + }, + }, + }), + }) + .then(async (collection) => expect(await collection.exists()).toEqual(true)) + .then(() => cluster.collections.get(collectionName).config.get()); + + expect(response.name).toEqual(collectionName); + expect(response.description).toEqual('A test collection'); + + expect(response.properties?.length).toEqual(15); + expect(response.properties?.[0].name).toEqual('text'); + expect(response.properties?.[0].dataType).toEqual('text'); + expect(response.properties?.[1].name).toEqual('texts'); + expect(response.properties?.[1].dataType).toEqual('text[]'); + expect(response.properties?.[2].name).toEqual('number'); + expect(response.properties?.[2].dataType).toEqual('number'); + expect(response.properties?.[3].name).toEqual('numbers'); + expect(response.properties?.[3].dataType).toEqual('number[]'); + expect(response.properties?.[4].name).toEqual('int'); + expect(response.properties?.[4].dataType).toEqual('int'); + expect(response.properties?.[5].name).toEqual('ints'); + expect(response.properties?.[5].dataType).toEqual('int[]'); + expect(response.properties?.[6].name).toEqual('date'); + expect(response.properties?.[6].dataType).toEqual('date'); + expect(response.properties?.[7].name).toEqual('dates'); + expect(response.properties?.[7].dataType).toEqual('date[]'); + expect(response.properties?.[8].name).toEqual('boolean'); + expect(response.properties?.[8].dataType).toEqual('boolean'); + expect(response.properties?.[9].name).toEqual('booleans'); + expect(response.properties?.[9].dataType).toEqual('boolean[]'); + expect(response.properties?.[10].name).toEqual('object'); + expect(response.properties?.[10].dataType).toEqual('object'); + expect(response.properties?.[10].nestedProperties?.length).toEqual(1); + expect(response.properties?.[10].nestedProperties?.[0].name).toEqual('nestedProp'); + expect(response.properties?.[10].nestedProperties?.[0].dataType).toEqual('text'); + expect(response.properties?.[11].name).toEqual('objects'); + expect(response.properties?.[11].dataType).toEqual('object[]'); + expect(response.properties?.[11].nestedProperties?.length).toEqual(1); + expect(response.properties?.[11].nestedProperties?.[0].name).toEqual('nestedProp'); + expect(response.properties?.[11].nestedProperties?.[0].dataType).toEqual('text'); + expect(response.properties?.[12].name).toEqual('blob'); + expect(response.properties?.[12].dataType).toEqual('blob'); + expect(response.properties?.[13].name).toEqual('geoCoordinates'); + expect(response.properties?.[13].dataType).toEqual('geoCoordinates'); + expect(response.properties?.[14].name).toEqual('phoneNumber'); + expect(response.properties?.[14].dataType).toEqual('phoneNumber'); + + expect(response.invertedIndex.bm25.b).toEqual(0.8); + expect(response.invertedIndex.bm25.k1).toEqual(1.3); + expect(response.invertedIndex.cleanupIntervalSeconds).toEqual(10); + expect(response.invertedIndex.indexTimestamps).toEqual(true); + expect(response.invertedIndex.indexPropertyLength).toEqual(true); + expect(response.invertedIndex.indexNullState).toEqual(true); + // expect(response.invertedIndexConfig?.stopwords?.additions).toEqual(['a']); // potential weaviate bug, this returns as None + expect(response.invertedIndex.stopwords?.preset).toEqual('en'); + expect(response.invertedIndex.stopwords?.removals).toEqual(['the']); + + expect(response.multiTenancy.enabled).toEqual(true); + + expect(response.replication.factor).toEqual(2); + + const indexConfig = response.vectorizers.default.indexConfig as VectorIndexConfigHNSW; + const quantizer = indexConfig.quantizer as PQConfig; + expect(indexConfig.cleanupIntervalSeconds).toEqual(10); + expect(indexConfig.distance).toEqual('dot'); + expect(indexConfig.dynamicEfFactor).toEqual(6); + expect(indexConfig.dynamicEfMax).toEqual(100); + expect(indexConfig.dynamicEfMin).toEqual(10); + expect(indexConfig.ef).toEqual(-2); + expect(indexConfig.efConstruction).toEqual(100); + expect(indexConfig.flatSearchCutoff).toEqual(41000); + expect(indexConfig.maxConnections).toEqual(72); + expect(quantizer.bitCompression).toEqual(true); + expect(quantizer.centroids).toEqual(128); + expect(quantizer.encoder.distribution).toEqual('normal'); + // expect(quantizer.encoder.type).toEqual('tile'); // potential weaviate bug, this returns as PQEncoderType.KMEANS + expect(quantizer.segments).toEqual(4); + expect(quantizer.trainingLimit).toEqual(100001); + expect(indexConfig.skip).toEqual(true); + expect(indexConfig.vectorCacheMaxObjects).toEqual(100000); + + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + + await cluster.collections.delete(collectionName); + }); + + it('should be able to create a collection with the contextionary vectorizer using configure.vectorizer', async () => { + const collectionName = 'ThisOneIsATest'; // must include words in contextionary's vocabulary to pass since vectorizeCollectionName will be true + const response = await contextionary.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecContextionary(), + }) + .then(async (collection) => expect(await collection.exists()).toEqual(true)) + .then(() => contextionary.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(1); + expect(response.properties?.[0].name).toEqual('testProp'); + expect(response.properties?.[0].dataType).toEqual('text'); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect((response.vectorizers.default.indexConfig as VectorIndexConfigHNSW).quantizer).toBeUndefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-contextionary'); + expect( + (response.vectorizers.default.vectorizer.config as Text2VecContextionaryConfig).vectorizeCollectionName + ).toEqual(true); + + await contextionary.collections.delete(collectionName); + }); + + it('should be able to create a collection with an openai vectorizer with configure.vectorizer', async () => { + const collectionName = 'TestCollectionOpenAIVectorizerWithConfigureVectorizer'; + const response = await openai.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecOpenAI(), + }) + .then(async (collection) => expect(await collection.exists()).toEqual(true)) + .then(() => openai.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(1); + expect(response.properties?.[0].name).toEqual('testProp'); + expect(response.properties?.[0].dataType).toEqual('text'); + expect(response.vectorizers.default.indexConfig).toBeDefined(); + expect((response.vectorizers.default.indexConfig as VectorIndexConfigHNSW).quantizer).toBeUndefined(); + expect(response.vectorizers.default.indexType).toEqual('hnsw'); + expect(response.vectorizers.default.vectorizer.name).toEqual('text2vec-openai'); + expect( + (response.vectorizers.default.vectorizer.config as Text2VecOpenAIConfig).vectorizeCollectionName + ).toEqual(true); + + await openai.collections.delete(collectionName); + }); + + it('should be able to create a collection with the openai generative with configure.Generative', async () => { + const collectionName = 'TestCollectionOpenAIGenerativeWithConfigureGenerative'; + const response = await openai.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + generative: weaviate.configure.generative.openAI(), + }) + .then(async (collection) => expect(await collection.exists()).toEqual(true)) + .then(() => openai.collections.get(collectionName).config.get()); + expect(response.name).toEqual(collectionName); + expect(response.properties?.length).toEqual(1); + expect(response.properties?.[0].name).toEqual('testProp'); + expect(response.properties?.[0].dataType).toEqual('text'); + expect(response.generative).toEqual({ + name: 'generative-openai', + config: {}, + }); + + await openai.collections.delete(collectionName); + }); +}); diff --git a/src/collections/iterator/index.ts b/src/collections/iterator/index.ts new file mode 100644 index 00000000..0e631bb9 --- /dev/null +++ b/src/collections/iterator/index.ts @@ -0,0 +1,39 @@ +import { WeaviateDeserializationError } from '../../errors.js'; +import { WeaviateObject } from '../types/index.js'; + +const ITERATOR_CACHE_SIZE = 100; + +export class Iterator { + private cache: WeaviateObject[] = []; + private last: string | undefined = undefined; + constructor(private query: (limit: number, after?: string) => Promise[]>) { + this.query = query; + } + + [Symbol.asyncIterator]() { + return { + next: async (): Promise>> => { + const objects = await this.query(ITERATOR_CACHE_SIZE, this.last); + this.cache = objects; + if (this.cache.length == 0) { + return { + done: true, + value: undefined, + }; + } + const obj = this.cache.shift(); + if (obj === undefined) { + throw new WeaviateDeserializationError('Object iterator returned an object that is undefined'); + } + this.last = obj?.uuid; + if (this.last === undefined) { + throw new WeaviateDeserializationError('Object iterator returned an object without a UUID'); + } + return { + done: false, + value: obj, + }; + }, + }; + } +} diff --git a/src/collections/iterator/integration.test.ts b/src/collections/iterator/integration.test.ts new file mode 100644 index 00000000..6937b270 --- /dev/null +++ b/src/collections/iterator/integration.test.ts @@ -0,0 +1,95 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import weaviate, { WeaviateClient } from '../../index.js'; +import { Collection } from '../collection/index.js'; + +describe('Testing of the collection.iterator method with a simple collection', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionIterator'; + let id: string; + let vector: number[]; + + type TestCollectionIterator = { + testProp: string; + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal({ port: 8080, grpcPort: 50051 }); + collection = client.collections.get(collectionName); + id = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecContextionary({ + vectorizeCollectionName: false, + }), + }) + .then(() => { + return collection.data.insert({ + properties: { + testProp: 'test', + }, + }); + }); + const res = await collection.query.fetchObjectById(id, { includeVector: true }); + vector = res?.vectors.default!; + }); + + it('should iterate through the collection with no options returning the objects', async () => { + let count = 0; + for await (const obj of collection.iterator()) { + expect(obj.properties.testProp).toBe('test'); + expect(obj.uuid).toBe(id); + expect(obj.vectors.default).toBeUndefined(); + count++; // eslint-disable-line no-plusplus + } + expect(count).toBe(1); + }); + + it('should iterate through the collection specifying return properties', async () => { + let count = 0; + for await (const obj of collection.iterator({ returnProperties: ['testProp'] })) { + expect(obj.properties.testProp).toBe('test'); + expect(obj.uuid).toBe(id); + expect(obj.vectors.default).toBeUndefined(); + count++; // eslint-disable-line no-plusplus + } + expect(count).toBe(1); + }); + + it('should iterate through the collection specifying return metadata', async () => { + let count = 0; + for await (const obj of collection.iterator({ returnMetadata: ['creationTime'] })) { + expect(obj.properties.testProp).toBe('test'); + expect(obj.uuid).toBe(id); + expect(obj.vectors.default).toBeUndefined(); + expect(obj.metadata?.creationTime).toBeDefined(); + count++; // eslint-disable-line no-plusplus + } + expect(count).toBe(1); + }); + + it('should iterate through the collection specifying include vector', async () => { + let count = 0; + for await (const obj of collection.iterator({ includeVector: true })) { + expect(obj.properties.testProp).toBe('test'); + expect(obj.uuid).toBe(id); + expect(obj.vectors.default).toEqual(vector); + count++; // eslint-disable-line no-plusplus + } + expect(count).toBe(1); + }); +}); diff --git a/src/collections/journey.test.ts b/src/collections/journey.test.ts new file mode 100644 index 00000000..e76b41a9 --- /dev/null +++ b/src/collections/journey.test.ts @@ -0,0 +1,186 @@ +import weaviate, { CollectionConfig, WeaviateClient } from '../index.js'; +import { GeoCoordinate } from '../proto/v1/properties.js'; + +describe('Journey testing of the client using a WCD cluster', () => { + let client: WeaviateClient; + const collectionName = `MyTSTestingCollection_${Date.now()}`; + + type MyType = { + name: string; + age: number; + location?: GeoCoordinate; + dateOfBirth: Date; + }; + + beforeAll(async () => { + client = await weaviate.connectToWeaviateCloud( + 'https://piblpmmdsiknacjnm1ltla.c1.europe-west3.gcp.weaviate.cloud', + { + authCredentials: 'NOg5AliYnrN6z7dZDuGv7SLVKhTabAaSTKS7', + } + ); + return client.collections.delete(collectionName); + }); + + it('should create the correct config for a collection with vectorizer, generative, and reranker modules', () => { + return client.collections.create({ + name: collectionName, + properties: [ + { + name: 'name', + dataType: 'text', + }, + { + name: 'age', + dataType: 'int', + }, + { + name: 'location', + dataType: 'geoCoordinates', + }, + { + name: 'dateOfBirth', + dataType: 'date', + }, + ], + generative: weaviate.configure.generative.cohere(), + reranker: weaviate.configure.reranker.cohere(), + vectorizers: weaviate.configure.vectorizer.text2VecCohere(), + }); + }); + + it('should get the config for the created collection', () => { + return client.collections + .get(collectionName) + .config.get() + .then((config) => { + expect(config).toEqual({ + name: collectionName, + generative: { + name: 'generative-cohere', + config: {}, + }, + invertedIndex: { + bm25: { + b: 0.75, + k1: 1.2, + }, + cleanupIntervalSeconds: 60, + stopwords: { + additions: [], + preset: 'en', + removals: [], + }, + indexNullState: false, + indexPropertyLength: false, + indexTimestamps: false, + }, + multiTenancy: { + autoTenantActivation: false, + autoTenantCreation: false, + enabled: false, + }, + properties: [ + { + name: 'name', + dataType: 'text', + indexFilterable: true, + indexInverted: false, + indexSearchable: true, + vectorizerConfig: { + 'text2vec-cohere': { + skip: false, + vectorizePropertyName: true, + }, + }, + tokenization: 'word', + }, + { + name: 'age', + dataType: 'int', + indexFilterable: true, + indexInverted: false, + indexSearchable: false, + vectorizerConfig: { + 'text2vec-cohere': { + skip: false, + vectorizePropertyName: true, + }, + }, + tokenization: 'none', + }, + { + name: 'location', + dataType: 'geoCoordinates', + indexFilterable: true, + indexInverted: false, + indexSearchable: false, + vectorizerConfig: { + 'text2vec-cohere': { + skip: false, + vectorizePropertyName: true, + }, + }, + tokenization: 'none', + }, + { + name: 'dateOfBirth', + dataType: 'date', + indexFilterable: true, + indexInverted: false, + indexSearchable: false, + vectorizerConfig: { + 'text2vec-cohere': { + skip: false, + vectorizePropertyName: true, + }, + }, + tokenization: 'none', + }, + ], + references: [], + replication: { + factor: 1, + }, + reranker: { + name: 'reranker-cohere', + config: {}, + }, + sharding: { + virtualPerPhysical: 128, + desiredCount: 1, + actualCount: 1, + desiredVirtualCount: 128, + actualVirtualCount: 128, + key: '_id', + strategy: 'hash', + function: 'murmur3', + }, + vectorizers: { + default: { + vectorizer: { + name: 'text2vec-cohere', + config: {}, + }, + indexConfig: { + cleanupIntervalSeconds: 300, + distance: 'cosine', + dynamicEfMin: 100, + dynamicEfMax: 500, + dynamicEfFactor: 8, + ef: -1, + efConstruction: 128, + flatSearchCutoff: 40000, + maxConnections: 64, + skip: false, + vectorCacheMaxObjects: 1000000000000, + quantizer: undefined, + type: 'hnsw', + }, + indexType: 'hnsw', + }, + }, + }); + }); + }); +}); diff --git a/src/collections/query/index.ts b/src/collections/query/index.ts new file mode 100644 index 00000000..7a67aff0 --- /dev/null +++ b/src/collections/query/index.ts @@ -0,0 +1,300 @@ +import Connection from '../../connection/grpc.js'; + +import { toBase64FromMedia } from '../../utils/base64.js'; + +import { ConsistencyLevel } from '../../data/index.js'; +import { DbVersionSupport } from '../../utils/dbVersion.js'; + +import { SearchReply } from '../../proto/v1/search_get.js'; +import { Deserialize } from '../deserialize/index.js'; +import { Serialize } from '../serialize/index.js'; +import { GroupByOptions, GroupByReturn, WeaviateObject, WeaviateReturn } from '../types/index.js'; + +import { WeaviateInvalidInputError, WeaviateUnsupportedFeatureError } from '../../errors.js'; +import { + BaseBm25Options, + BaseHybridOptions, + BaseNearOptions, + BaseNearTextOptions, + Bm25Options, + FetchObjectByIdOptions, + FetchObjectsOptions, + GroupByBm25Options, + GroupByHybridOptions, + GroupByNearOptions, + GroupByNearTextOptions, + HybridOptions, + NearMediaType, + NearOptions, + NearTextOptions, + Query, + QueryReturn, + SearchOptions, +} from './types.js'; + +class QueryManager implements Query { + private connection: Connection; + private name: string; + private dbVersionSupport: DbVersionSupport; + private consistencyLevel?: ConsistencyLevel; + private tenant?: string; + + private constructor( + connection: Connection, + name: string, + dbVersionSupport: DbVersionSupport, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ) { + this.connection = connection; + this.name = name; + this.dbVersionSupport = dbVersionSupport; + this.consistencyLevel = consistencyLevel; + this.tenant = tenant; + } + + public static use( + connection: Connection, + name: string, + dbVersionSupport: DbVersionSupport, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ): QueryManager { + return new QueryManager(connection, name, dbVersionSupport, consistencyLevel, tenant); + } + + private checkSupportForNamedVectors = async (opts?: BaseNearOptions) => { + if (!Serialize.isNamedVectors(opts)) return; + const check = await this.dbVersionSupport.supportsNamedVectors(); + if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); + }; + + private checkSupportForBm25AndHybridGroupByQueries = async ( + query: 'Bm25' | 'Hybrid', + opts?: SearchOptions | GroupByOptions + ) => { + if (!Serialize.isGroupBy(opts)) return; + const check = await this.dbVersionSupport.supportsBm25AndHybridGroupByQueries(); + if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message(query)); + }; + + private checkSupportForHybridNearTextAndNearVectorSubSearches = async (opts?: HybridOptions) => { + if (opts?.vector === undefined || Array.isArray(opts.vector)) return; + const check = await this.dbVersionSupport.supportsHybridNearTextAndNearVectorSubsearchQueries(); + if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); + }; + + private async parseReply(reply: SearchReply) { + const deserialize = await Deserialize.use(this.dbVersionSupport); + return deserialize.query(reply); + } + + private async parseGroupByReply( + opts: SearchOptions | GroupByOptions | undefined, + reply: SearchReply + ) { + const deserialize = await Deserialize.use(this.dbVersionSupport); + return Serialize.isGroupBy(opts) ? deserialize.groupBy(reply) : deserialize.query(reply); + } + + public fetchObjectById(id: string, opts?: FetchObjectByIdOptions): Promise | null> { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => search.withFetch(Serialize.fetchObjectById({ id, ...opts }))) + .then((reply) => this.parseReply(reply)) + .then((ret) => (ret.objects.length === 1 ? ret.objects[0] : null)); + } + + public fetchObjects(opts?: FetchObjectsOptions): Promise> { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => search.withFetch(Serialize.fetchObjects(opts))) + .then((reply) => this.parseReply(reply)); + } + + public bm25(query: string, opts?: BaseBm25Options): Promise>; + public bm25(query: string, opts: GroupByBm25Options): Promise>; + public bm25(query: string, opts?: Bm25Options): QueryReturn { + return Promise.all([ + this.checkSupportForNamedVectors(opts), + this.checkSupportForBm25AndHybridGroupByQueries('Bm25', opts), + ]) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withBm25({ + ...Serialize.bm25({ query, ...opts }), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public hybrid(query: string, opts?: BaseHybridOptions): Promise>; + public hybrid(query: string, opts: GroupByHybridOptions): Promise>; + public hybrid(query: string, opts?: HybridOptions): QueryReturn { + return Promise.all([ + this.checkSupportForNamedVectors(opts), + this.checkSupportForBm25AndHybridGroupByQueries('Hybrid', opts), + this.checkSupportForHybridNearTextAndNearVectorSubSearches(opts), + ]) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withHybrid({ + ...Serialize.hybrid({ query, ...opts }), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearImage(image: string | Buffer, opts?: BaseNearOptions): Promise>; + public nearImage(image: string | Buffer, opts: GroupByNearOptions): Promise>; + public nearImage(image: string | Buffer, opts?: NearOptions): QueryReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => { + return toBase64FromMedia(image).then((image) => + search.withNearImage({ + ...Serialize.nearImage({ image, ...(opts ? opts : {}) }), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ); + }) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearMedia( + media: string | Buffer, + type: NearMediaType, + opts?: BaseNearOptions + ): Promise>; + public nearMedia( + media: string | Buffer, + type: NearMediaType, + opts: GroupByNearOptions + ): Promise>; + public nearMedia(media: string | Buffer, type: NearMediaType, opts?: NearOptions): QueryReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => { + let reply: Promise; + switch (type) { + case 'audio': + reply = toBase64FromMedia(media).then((media) => + search.withNearAudio(Serialize.nearAudio({ audio: media, ...(opts ? opts : {}) })) + ); + break; + case 'depth': + reply = toBase64FromMedia(media).then((media) => + search.withNearDepth(Serialize.nearDepth({ depth: media, ...(opts ? opts : {}) })) + ); + break; + case 'image': + reply = toBase64FromMedia(media).then((media) => + search.withNearImage(Serialize.nearImage({ image: media, ...(opts ? opts : {}) })) + ); + break; + case 'imu': + reply = toBase64FromMedia(media).then((media) => + search.withNearIMU(Serialize.nearIMU({ imu: media, ...(opts ? opts : {}) })) + ); + break; + case 'thermal': + reply = toBase64FromMedia(media).then((media) => + search.withNearThermal(Serialize.nearThermal({ thermal: media, ...(opts ? opts : {}) })) + ); + break; + case 'video': + reply = toBase64FromMedia(media).then((media) => + search.withNearVideo(Serialize.nearVideo({ video: media, ...(opts ? opts : {}) })) + ); + break; + default: + throw new WeaviateInvalidInputError(`Invalid media type: ${type}`); + } + return reply; + }) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearObject(id: string, opts?: BaseNearOptions): Promise>; + public nearObject(id: string, opts: GroupByNearOptions): Promise>; + public nearObject(id: string, opts?: NearOptions): QueryReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withNearObject({ + ...Serialize.nearObject({ id, ...(opts ? opts : {}) }), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearText(query: string | string[], opts?: BaseNearTextOptions): Promise>; + public nearText(query: string | string[], opts: GroupByNearTextOptions): Promise>; + public nearText(query: string | string[], opts?: NearTextOptions): QueryReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withNearText({ + ...Serialize.nearText({ query, ...(opts ? opts : {}) }), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } + + public nearVector(vector: number[], opts?: BaseNearOptions): Promise>; + public nearVector(vector: number[], opts: GroupByNearOptions): Promise>; + public nearVector(vector: number[], opts?: NearOptions): QueryReturn { + return this.checkSupportForNamedVectors(opts) + .then(() => this.connection.search(this.name, this.consistencyLevel, this.tenant)) + .then((search) => + search.withNearVector({ + ...Serialize.nearVector({ vector, ...(opts ? opts : {}) }), + groupBy: Serialize.isGroupBy>(opts) + ? Serialize.groupBy(opts.groupBy) + : undefined, + }) + ) + .then((reply) => this.parseGroupByReply(opts, reply)); + } +} + +export default QueryManager.use; + +export { + BaseBm25Options, + BaseHybridOptions, + BaseNearOptions, + BaseNearTextOptions, + Bm25Options, + FetchObjectByIdOptions, + FetchObjectsOptions, + GroupByBm25Options, + GroupByHybridOptions, + GroupByNearOptions, + GroupByNearTextOptions, + HybridNearTextSubSearch, + HybridNearVectorSubSearch, + HybridOptions, + HybridSubSearchBase, + MoveOptions, + NearMediaType, + NearOptions, + NearTextOptions, + Query, + QueryReturn, + SearchOptions, +} from './types.js'; diff --git a/src/collections/query/integration.test.ts b/src/collections/query/integration.test.ts new file mode 100644 index 00000000..482475f7 --- /dev/null +++ b/src/collections/query/integration.test.ts @@ -0,0 +1,1080 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import { WeaviateUnsupportedFeatureError } from '../../errors.js'; +import weaviate, { WeaviateClient } from '../../index.js'; +import { Collection } from '../collection/index.js'; +import { CrossReference, Reference } from '../references/index.js'; +import { GroupByOptions } from '../types/index.js'; + +describe('Testing of the collection.query methods with a simple collection', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionQueryMinimalOptions'; + let id: string; + let vector: number[]; + + type TestCollectionQueryMinimalOptions = { + testProp: string; + testProp2: string; + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + id = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + { + name: 'testProp2', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecContextionary({ + vectorizeCollectionName: false, + }), + }) + .then(async () => { + await collection.data.insert({ + properties: { + testProp: 'apple', + testProp2: 'banana', + }, + }); + return collection.data.insert({ + properties: { + testProp: 'test', + testProp2: 'test2', + }, + }); + }); + const res = await collection.query.fetchObjectById(id, { includeVector: true }); + vector = res?.vectors.default!; + }); + + it('should fetch an object by its id', async () => { + const object = await collection.query.fetchObjectById(id); + expect(object?.properties.testProp).toEqual('test'); + expect(object?.uuid).toEqual(id); + }); + + it('should query without search', async () => { + const ret = await collection.query.fetchObjects(); + expect(ret.objects.length).toEqual(2); + expect(ret.objects[0].properties.testProp).toBeDefined(); + expect(ret.objects[0].uuid).toBeDefined(); + }); + + it('should query without search specifying return properties', async () => { + const ret = await collection.query.fetchObjects({ + returnProperties: ['testProp'], + }); + expect(ret.objects.length).toEqual(2); + expect(ret.objects[0].properties.testProp).toBeDefined(); + expect(ret.objects[0].properties.testProp2).toBeUndefined(); + expect(ret.objects[0].uuid).toBeDefined(); + }); + + it('should query with bm25', async () => { + const ret = await collection.query.bm25('test'); + expect(ret.objects.length).toEqual(1); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].properties.testProp2).toEqual('test2'); + expect(ret.objects[0].uuid).toEqual(id); + }); + + it('should query with hybrid', async () => { + const ret = await collection.query.hybrid('test', { limit: 1 }); + expect(ret.objects.length).toEqual(1); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].properties.testProp2).toEqual('test2'); + expect(ret.objects[0].uuid).toEqual(id); + }); + + it('should query with hybrid and vector', async () => { + const ret = await collection.query.hybrid('test', { + limit: 1, + vector: vector, + }); + expect(ret.objects.length).toEqual(1); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].properties.testProp2).toEqual('test2'); + expect(ret.objects[0].uuid).toEqual(id); + }); + + it('should query with hybrid and near text subsearch', async () => { + const query = () => + collection.query.hybrid('test', { + limit: 1, + vector: { + query: 'apple', + distance: 0.9, + moveTo: { + concepts: ['banana'], + force: 0.9, + }, + moveAway: { + concepts: ['test'], + force: 0.1, + }, + }, + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const ret = await query(); + expect(ret.objects.length).toEqual(1); + expect(ret.objects[0].properties.testProp).toEqual('apple'); + expect(ret.objects[0].properties.testProp2).toEqual('banana'); + }); + + it('should query with hybrid and near vector subsearch', async () => { + const query = () => + collection.query.hybrid('test', { + limit: 1, + vector: { + vector: vector, + distance: 0.9, + }, + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const ret = await query(); + expect(ret.objects.length).toEqual(1); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].properties.testProp2).toEqual('test2'); + }); + + it.skip('should query with nearObject', async () => { + const ret = await collection.query.nearObject(id, { limit: 1 }); + expect(ret.objects.length).toEqual(1); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].properties.testProp2).toEqual('test2'); + expect(ret.objects[0].uuid).toEqual(id); + }); + + it('should query with nearText', async () => { + const ret = await collection.query.nearText(['test'], { limit: 1 }); + expect(ret.objects.length).toEqual(1); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].properties.testProp2).toEqual('test2'); + expect(ret.objects[0].uuid).toEqual(id); + }); + + it('should query with nearVector', async () => { + const ret = await collection.query.nearVector(vector, { limit: 1 }); + expect(ret.objects.length).toEqual(1); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].properties.testProp2).toEqual('test2'); + expect(ret.objects[0].uuid).toEqual(id); + }); +}); + +describe('Testing of the collection.query methods with a collection with a reference property', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionQueryWithRefProp'; + + let id1: string; + let id2: string; + + type TestCollectionQueryWithRefProp = { + testProp: string; + refProp?: CrossReference; + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + return client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + vectorizePropertyName: false, + }, + ], + references: [ + { + name: 'refProp', + targetCollection: collectionName, + }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecContextionary({ + vectorizeCollectionName: false, + }), + }) + .then(async () => { + id1 = await collection.data.insert({ + properties: { + testProp: 'test', + }, + }); + id2 = await collection.data.insert({ + properties: { + testProp: 'other', + }, + references: { + refProp: Reference.to(id1), + }, + }); + }); + }); + + describe('using a non-generic collection', () => { + it('should query without searching returning the referenced object', async () => { + const ret = await client.collections.get(collectionName).query.fetchObjects({ + returnProperties: ['testProp'], + returnReferences: [ + { + linkOn: 'refProp', + returnProperties: ['testProp'], + returnReferences: [ + { + linkOn: 'refProp', + returnProperties: ['testProp'], + }, + ], + }, + ], + }); + ret.objects.sort((a, b) => + (a.properties.testProp as string).localeCompare(b.properties.testProp as string) + ); + expect(ret.objects.length).toEqual(2); + expect(ret.objects[0].properties.testProp).toEqual('other'); + expect(ret.objects[0].references?.refProp?.objects[0].properties?.testProp).toEqual('test'); + expect(ret.objects[0].references?.refProp?.objects[0].references).toBeUndefined(); + expect(ret.objects[1].properties.testProp).toEqual('test'); + expect(ret.objects[1].references?.refProp).toBeUndefined(); + }); + }); + + describe('using a generic collection', () => { + it('should query without searching returning the referenced object', async () => { + const ret = await collection.query.fetchObjects({ + returnProperties: ['testProp'], + returnReferences: [ + { + linkOn: 'refProp', + returnProperties: ['testProp'], + returnReferences: [ + { + linkOn: 'refProp', + returnProperties: ['testProp'], + }, + ], + }, + ], + }); + ret.objects.sort((a, b) => a.properties.testProp.localeCompare(b.properties.testProp)); + expect(ret.objects.length).toEqual(2); + expect(ret.objects[0].properties.testProp).toEqual('other'); + expect(ret.objects[0].references?.refProp?.objects[0].properties?.testProp).toEqual('test'); + expect(ret.objects[0].references?.refProp?.objects[0].references).toBeUndefined(); + expect(ret.objects[1].properties.testProp).toEqual('test'); + expect(ret.objects[1].references?.refProp).toBeUndefined(); + }); + + it('should query with bm25 returning the referenced object', async () => { + const ret = await collection.query.bm25('other', { + returnProperties: ['testProp'], + returnReferences: [ + { + linkOn: 'refProp', + returnProperties: ['testProp'], + }, + ], + }); + expect(ret.objects.length).toEqual(1); + expect(ret.objects.map((obj) => obj.properties.testProp).includes('other')).toEqual(true); + expect( + ret.objects.find((obj) => obj.properties.testProp === 'other')?.references?.refProp?.objects.length + ).toEqual(1); + expect( + ret.objects.find((obj) => obj.properties.testProp === 'other')?.references?.refProp?.objects[0] + .properties?.testProp + ).toEqual('test'); + }); + + it('should query with hybrid returning the referenced object', async () => { + const ret = await collection.query.hybrid('other', { + returnProperties: ['testProp'], + returnReferences: [ + { + linkOn: 'refProp', + returnProperties: ['testProp'], + }, + ], + }); + expect(ret.objects.length).toEqual(2); + expect(ret.objects.map((obj) => obj.properties.testProp).includes('other')).toEqual(true); + expect( + ret.objects.find((obj) => obj.properties.testProp === 'other')?.references?.refProp?.objects.length + ).toEqual(1); + expect( + ret.objects.find((obj) => obj.properties.testProp === 'other')?.references?.refProp?.objects[0] + .properties?.testProp + ).toEqual('test'); + }); + + it.skip('should query with nearObject returning the referenced object', async () => { + const ret = await collection.query.nearObject(id2, { + returnProperties: ['testProp'], + returnReferences: [ + { + linkOn: 'refProp', + returnProperties: ['testProp'], + }, + ], + }); + expect(ret.objects.length).toEqual(2); + expect(ret.objects.map((obj) => obj.properties.testProp).includes('other')).toEqual(true); + expect( + ret.objects.find((obj) => obj.properties.testProp === 'other')?.references?.refProp?.objects.length + ).toEqual(1); + expect( + ret.objects.find((obj) => obj.properties.testProp === 'other')?.references?.refProp?.objects[0] + .properties?.testProp + ).toEqual('test'); + }); + + it('should fetch an object by its ID returning its references', async () => { + const res = await collection.query.fetchObjectById(id2, { + returnReferences: [{ linkOn: 'refProp' }], + }); + expect(res?.properties.testProp).toEqual('other'); + expect(res?.references?.refProp?.objects.length).toEqual(1); + expect(res?.references?.refProp?.objects[0].properties?.testProp).toEqual('test'); + }); + + it('should query with nearVector returning the referenced object', async () => { + const res = await collection.query.fetchObjectById(id2, { includeVector: true }); + const ret = await collection.query.nearVector(res?.vectors.default!, { + returnProperties: ['testProp'], + returnReferences: [ + { + linkOn: 'refProp', + returnProperties: ['testProp'], + }, + ], + }); + expect(ret.objects.length).toEqual(2); + expect(ret.objects.map((obj) => obj.properties.testProp).includes('other')).toEqual(true); + expect( + ret.objects.find((obj) => obj.properties.testProp === 'other')?.references?.refProp?.objects.length + ).toEqual(1); + expect( + ret.objects.find((obj) => obj.properties.testProp === 'other')?.references?.refProp?.objects[0] + .properties?.testProp + ).toEqual('test'); + }); + }); + + describe('Testing of the collection.query methods with a collection with a nested property', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionQueryWithNestedProps'; + + let id1: string; + let id2: string; + + type TestCollectionQueryWithNestedProps = { + testProp: string; + nestedProp?: { + one: string; + two: string; + again?: { + three: string; + }; + onceMore?: { + four: string; + }; + }; + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + return client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + vectorizePropertyName: false, + }, + { + name: 'nestedProp', + dataType: 'object', + vectorizePropertyName: false, + nestedProperties: [ + { + name: 'one', + dataType: 'text', + }, + { + name: 'two', + dataType: 'text', + }, + { + name: 'again', + dataType: 'object', + nestedProperties: [ + { + name: 'three', + dataType: 'text', + }, + ], + }, + { + name: 'onceMore', + dataType: 'object', + nestedProperties: [ + { + name: 'four', + dataType: 'text', + }, + ], + }, + ], + }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecContextionary(), + }) + .then(async () => { + id1 = await collection.data.insert({ + properties: { + testProp: 'test', + }, + }); + id2 = await collection.data.insert({ + properties: { + testProp: 'other', + nestedProp: { + one: 'test', + two: 'test', + again: { + three: 'test', + }, + onceMore: { + four: 'test', + }, + }, + }, + }); + }); + }); + + it('should query without searching returning the nested object', async () => { + const ret = await collection.query.fetchObjects({ + returnProperties: [ + 'testProp', + { + name: 'nestedProp', + properties: [ + 'one', + { + name: 'again', + properties: ['three'], + }, + { + name: 'onceMore', + properties: ['four'], + }, + ], + }, + ], + }); + ret.objects.sort((a, b) => a.properties.testProp.localeCompare(b.properties.testProp)); + expect(ret.objects.length).toEqual(2); + expect(ret.objects[0].properties.testProp).toEqual('other'); + expect(ret.objects[0].properties.nestedProp?.one).toEqual('test'); + expect(ret.objects[0].properties.nestedProp?.two).toBeUndefined(); + expect(ret.objects[0].properties.nestedProp?.again?.three).toEqual('test'); + expect(ret.objects[0].properties.nestedProp?.onceMore?.four).toEqual('test'); + expect(ret.objects[1].properties.testProp).toEqual('test'); + expect(ret.objects[1].properties.nestedProp).toBeUndefined(); + }); + }); + + describe('Testing of the collection.query methods with a collection with multiple vectors', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionQueryWithMultiVector'; + + let id1: string; + let id2: string; + + type TestCollectionQueryWithMultiVector = { + title: string; + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + const query = () => + client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'title', + dataType: 'text', + vectorizePropertyName: false, + }, + ], + vectorizers: [ + weaviate.configure.vectorizer.text2VecContextionary({ + name: 'title', + sourceProperties: ['title'], + }), + ], + }) + .then(async () => { + id1 = await collection.data.insert({ + properties: { + title: 'test', + }, + }); + id2 = await collection.data.insert({ + properties: { + title: 'other', + }, + }); + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + return query(); + }); + + it('should query returning the named vector', async () => { + const query = () => + collection.query.fetchObjects({ + returnProperties: ['title'], + includeVector: ['title'], + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const ret = await query(); + ret.objects.sort((a, b) => a.properties.title.localeCompare(b.properties.title)); + expect(ret.objects.length).toEqual(2); + expect(ret.objects[0].properties.title).toEqual('other'); + expect(ret.objects[0].vectors.title).toBeDefined(); + expect(ret.objects[1].properties.title).toEqual('test'); + expect(ret.objects[1].vectors.title).toBeDefined(); + }); + + it('should query without searching returning named vector', async () => { + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + return; + } + const ret = await collection.query.fetchObjects({ + returnProperties: ['title'], + includeVector: ['title'], + }); + ret.objects.sort((a, b) => a.properties.title.localeCompare(b.properties.title)); + expect(ret.objects.length).toEqual(2); + expect(ret.objects[0].properties.title).toEqual('other'); + expect(ret.objects[0].vectors.title).toBeDefined(); + expect(ret.objects[1].properties.title).toEqual('test'); + expect(ret.objects[1].vectors.title).toBeDefined(); + }); + + it('should query with a vector search over the named vector space', async () => { + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { + return; + } + const ret = await collection.query.nearObject(id1, { + returnProperties: ['title'], + targetVector: 'title', + }); + expect(ret.objects.length).toEqual(2); + expect(ret.objects[0].properties.title).toEqual('test'); + expect(ret.objects[1].properties.title).toEqual('other'); + }); + }); +}); + +describe('Testing of the groupBy collection.query methods with a simple collection', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionGroupBySimple'; + let id: string; + let vector: number[]; + + type TestCollectionGroupBySimple = { + testProp: string; + }; + + const groupByArgs: GroupByOptions = { + numberOfGroups: 1, + objectsPerGroup: 1, + property: 'testProp', + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + id = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectorizer.text2VecContextionary({ + vectorizeCollectionName: false, + }), + }) + .then(() => { + return collection.data.insert({ + properties: { + testProp: 'test', + }, + }); + }); + const res = await collection.query.fetchObjectById(id, { includeVector: true }); + vector = res?.vectors.default!; + }); + + // it('should groupBy without search', async () => { + // const ret = await collection.groupBy.fetchObjects(groupByArgs); + // expect(ret.objects.length).toEqual(1); + // expect(ret.groups).toBeDefined(); + // expect(Object.keys(ret.groups)).toEqual(['test']); + // expect(ret.objects[0].properties.testProp).toEqual('test'); + // expect(ret.objects[0].metadata.uuid).toEqual(id); + // expect(ret.objects[0].belongsToGroup).toEqual('test'); + // }); + + // it('should groupBy without search specifying return properties', async () => { + // const ret = await collection.groupBy.fetchObjects({ + // returnProperties: ['testProp'], + // returnMetadata: ['uuid'], + // ...groupByArgs, + // }); + // expect(ret.objects.length).toEqual(1); + // expect(ret.groups).toBeDefined(); + // expect(Object.keys(ret.groups)).toEqual(['test']); + // expect(ret.objects[0].properties.testProp).toEqual('test'); + // expect(ret.objects[0].metadata.uuid).toEqual(id); + // expect(ret.objects[0].belongsToGroup).toEqual('test'); + // }); + + it('should groupBy with bm25', async () => { + const query = () => + collection.query.bm25('test', { + groupBy: groupByArgs, + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const ret = await query(); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it('should groupBy with hybrid', async () => { + const query = () => + collection.query.hybrid('test', { + groupBy: groupByArgs, + }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const ret = await query(); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it.skip('should groupBy with nearObject', async () => { + const ret = await collection.query.nearObject(id, { + groupBy: groupByArgs, + }); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it('should groupBy with nearText', async () => { + const ret = await collection.query.nearText(['test'], { + groupBy: groupByArgs, + }); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it('should groupBy with nearVector', async () => { + const ret = await collection.query.nearVector(vector, { + groupBy: groupByArgs, + }); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); + + it('should groupBy with nearVector and a non-generic collection', async () => { + const ret = await client.collections.get(collectionName).query.nearVector(vector, { + groupBy: { + numberOfGroups: 1, + objectsPerGroup: 1, + property: 'testProp', + }, + }); + expect(ret.objects.length).toEqual(1); + expect(ret.groups).toBeDefined(); + expect(Object.keys(ret.groups)).toEqual(['test']); + expect(ret.objects[0].properties.testProp).toEqual('test'); + expect(ret.objects[0].uuid).toEqual(id); + expect(ret.objects[0].belongsToGroup).toEqual('test'); + }); +}); + +describe('Testing of the collection.query methods with a multi-tenancy collection', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionMultiTenancy'; + let id1: string; + let id2: string; + + const tenantOne = { name: 'one' }; + const tenantTwo = { name: 'two' }; + + type TestCollectionMultiTenancy = { + testProp: string; + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + [id1, id2] = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'testProp', + dataType: 'text', + }, + ], + multiTenancy: weaviate.configure.multiTenancy({ enabled: true }), + vectorizers: weaviate.configure.vectorizer.text2VecContextionary({ + vectorizeCollectionName: false, + }), + }) + .then(async (col) => { + await col.tenants.create([tenantOne, tenantTwo]); + return col; + }) + .then((col) => + Promise.all([ + col.withTenant(tenantOne).data.insert({ + properties: { + testProp: 'one', + }, + }), + col.withTenant(tenantTwo).data.insert({ + properties: { + testProp: 'two', + }, + }), + ]) + ); + }); + + it('should find the objects in their tenants by ID', async () => { + const obj1 = await collection.withTenant(tenantOne).query.fetchObjectById(id1); + const obj2 = await collection.withTenant(tenantTwo).query.fetchObjectById(id2); + expect(obj1?.properties.testProp).toEqual('one'); + expect(obj1?.uuid).toEqual(id1); + expect(obj2?.properties.testProp).toEqual('two'); + expect(obj2?.uuid).toEqual(id2); + }); + + it('should return null if searching in the wrong tenant', async () => { + const obj1 = await collection.withTenant(tenantTwo).query.fetchObjectById(id1); + const obj2 = await collection.withTenant(tenantOne).query.fetchObjectById(id2); + expect(obj1).toBeNull(); + expect(obj2).toBeNull(); + }); + + it('should find the objects in their tenants by fetch', async () => { + const obj1 = await collection.withTenant(tenantOne).query.fetchObjects(); + const obj2 = await collection.withTenant(tenantTwo).query.fetchObjects(); + expect(obj1.objects.length).toEqual(1); + expect(obj1.objects[0].properties.testProp).toEqual('one'); + expect(obj1.objects[0].uuid).toEqual(id1); + expect(obj2.objects.length).toEqual(1); + expect(obj2.objects[0].properties.testProp).toEqual('two'); + expect(obj2.objects[0].uuid).toEqual(id2); + }); + + it('should find the objects in their tenants by bm25', async () => { + const obj1 = await collection.withTenant(tenantOne).query.bm25('one'); + const obj2 = await collection.withTenant(tenantTwo).query.bm25('two'); + expect(obj1.objects.length).toEqual(1); + expect(obj1.objects[0].properties.testProp).toEqual('one'); + expect(obj1.objects[0].uuid).toEqual(id1); + expect(obj2.objects.length).toEqual(1); + expect(obj2.objects[0].properties.testProp).toEqual('two'); + expect(obj2.objects[0].uuid).toEqual(id2); + }); + + it('should find the objects in their tenants by hybrid', async () => { + const obj1 = await collection.withTenant(tenantOne).query.hybrid('one'); + const obj2 = await collection.withTenant(tenantTwo).query.hybrid('two'); + expect(obj1.objects.length).toEqual(1); + expect(obj1.objects[0].properties.testProp).toEqual('one'); + expect(obj1.objects[0].uuid).toEqual(id1); + expect(obj2.objects.length).toEqual(1); + expect(obj2.objects[0].properties.testProp).toEqual('two'); + expect(obj2.objects[0].uuid).toEqual(id2); + }); + + it.skip('should find the objects in their tenants by nearObject', async () => { + const obj1 = await collection.withTenant(tenantOne).query.nearObject(id1); + const obj2 = await collection.withTenant(tenantTwo).query.nearObject(id2); + expect(obj1.objects.length).toEqual(1); + expect(obj1.objects[0].properties.testProp).toEqual('one'); + expect(obj1.objects[0].uuid).toEqual(id1); + expect(obj2.objects.length).toEqual(1); + expect(obj2.objects[0].properties.testProp).toEqual('two'); + expect(obj2.objects[0].uuid).toEqual(id2); + }); + + it('should find the objects in their tenants by nearText', async () => { + const obj1 = await collection.withTenant(tenantOne).query.nearText(['one']); + const obj2 = await collection.withTenant(tenantTwo).query.nearText(['two']); + expect(obj1.objects.length).toEqual(1); + expect(obj1.objects[0].properties.testProp).toEqual('one'); + expect(obj1.objects[0].uuid).toEqual(id1); + expect(obj2.objects.length).toEqual(1); + expect(obj2.objects[0].properties.testProp).toEqual('two'); + expect(obj2.objects[0].uuid).toEqual(id2); + }); + + it('should find the objects in their tenants by nearVector', async () => { + const { vectors: vecs1 } = (await collection + .withTenant(tenantOne) + .query.fetchObjectById(id1, { includeVector: true }))!; + const { vectors: vecs2 } = (await collection + .withTenant(tenantTwo) + .query.fetchObjectById(id2, { includeVector: true }))!; + const obj1 = await collection.withTenant(tenantOne).query.nearVector(vecs1.default); + const obj2 = await collection.withTenant(tenantTwo).query.nearVector(vecs2.default); + expect(obj1.objects.length).toEqual(1); + expect(obj1.objects[0].properties.testProp).toEqual('one'); + expect(obj1.objects[0].uuid).toEqual(id1); + expect(obj2.objects.length).toEqual(1); + expect(obj2.objects[0].properties.testProp).toEqual('two'); + expect(obj2.objects[0].uuid).toEqual(id2); + }); +}); + +const maybe = process.env.OPENAI_APIKEY ? describe : describe.skip; + +maybe('Testing of collection.query using rerank functionality', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionRerank'; + let id1: string; + let id2: string; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal({ + port: 8079, + grpcPort: 50050, + headers: { + 'X-OpenAI-Api-Key': process.env.OPENAI_APIKEY as string, + }, + }); + collection = client.collections.get(collectionName); + [id1, id2] = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'text', + dataType: 'text', + }, + ], + reranker: weaviate.configure.reranker.transformers(), + vectorizers: weaviate.configure.vectorizer.text2VecOpenAI(), + }) + .then(() => + Promise.all([ + collection.data.insert({ + properties: { + text: 'This is a test', + }, + }), + collection.data.insert({ + properties: { + text: 'This is another test', + }, + }), + ]) + ); + }); + + it('should rerank the results in a bm25 query', async () => { + const ret = await collection.query.bm25('test', { + rerank: { + property: 'text', + query: 'another', + }, + }); + const objects = ret.objects; + expect(objects.length).toEqual(2); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[0].properties.text).toEqual('This is another test'); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[1].properties.text).toEqual('This is a test'); + }); + + it('should rerank the results in a hybrid query', async () => { + const ret = await collection.query.hybrid('test', { + rerank: { + property: 'text', + query: 'another', + }, + }); + const objects = ret.objects; + expect(objects.length).toEqual(2); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[0].properties.text).toEqual('This is another test'); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[1].properties.text).toEqual('This is a test'); + }); + + it.skip('should rerank the results in a nearObject query', async () => { + const ret = await collection.query.nearObject(id1, { + rerank: { + property: 'text', + query: 'another', + }, + }); + const objects = ret.objects; + expect(objects.length).toEqual(2); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[0].properties.text).toEqual('This is another test'); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[1].properties.text).toEqual('This is a test'); + }); + + it('should rerank the results in a nearText query', async () => { + const ret = await collection.query.nearText('text', { + rerank: { + property: 'text', + query: 'another', + }, + }); + const objects = ret.objects; + expect(objects.length).toEqual(2); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[0].properties.text).toEqual('This is another test'); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[1].properties.text).toEqual('This is a test'); + }); + + it.skip('should rerank the results in a nearObject query', async () => { + const obj = await collection.query.fetchObjectById(id1, { includeVector: true }); + const ret = await collection.query.nearVector(obj?.vectors.default!, { + rerank: { + property: 'text', + query: 'another', + }, + }); + const objects = ret.objects; + expect(objects.length).toEqual(2); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[0].properties.text).toEqual('This is another test'); + expect(objects[0].metadata?.rerankScore).toBeDefined(); + expect(objects[1].properties.text).toEqual('This is a test'); + }); +}); diff --git a/src/collections/query/types.ts b/src/collections/query/types.ts new file mode 100644 index 00000000..268ce107 --- /dev/null +++ b/src/collections/query/types.ts @@ -0,0 +1,500 @@ +import { FilterValue } from '../filters/index.js'; +import { Sorting } from '../sort/classes.js'; +import { + GroupByOptions, + GroupByReturn, + QueryMetadata, + QueryProperty, + QueryReference, + RerankOptions, + WeaviateObject, + WeaviateReturn, +} from '../types/index.js'; +import { PrimitiveKeys } from '../types/internal.js'; + +/** Options available in the `query.fetchObjectById` method */ +export type FetchObjectByIdOptions = { + /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ + includeVector?: boolean | string[]; + /** + * Which properties of the object to return. Can be primitive, in which case specify their names, or nested, in which case + * use the QueryNested type. If not specified, all properties are returned. + */ + returnProperties?: QueryProperty[]; + /** Which references of the object to return. If not specified, no references are returned. */ + returnReferences?: QueryReference[]; +}; + +/** Options available in the `query.fetchObjects` method */ +export type FetchObjectsOptions = { + /** How many objects to return in the query */ + limit?: number; + /** How many objects to skip in the query. Incompatible with the `after` cursor */ + offset?: number; + /** The cursor to start the query from. Incompatible with the `offset` param */ + after?: string; + /** The filters to be applied to the query. Use `weaviate.filter.*` to create filters */ + filters?: FilterValue; + /** The sorting to be applied to the query. Use `weaviate.sort.*` to create sorting */ + sort?: Sorting; + /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ + includeVector?: boolean | string[]; + /** Which metadata of the object to return. If not specified, no metadata is returned. */ + returnMetadata?: QueryMetadata; + /** + * Which properties of the object to return. Can be primitive, in which case specify their names, or nested, in which case + * use the QueryNested type. If not specified, all properties are returned. + */ + returnProperties?: QueryProperty[]; + /** Which references of the object to return. If not specified, no references are returned. */ + returnReferences?: QueryReference[]; +}; + +/** Base options available to all the query methods that involve searching. */ +export type SearchOptions = { + /** How many objects to return in the query */ + limit?: number; + /** How many objects to skip in the query. Incompatible with the `after` cursor */ + offset?: number; + /** The [autocut](https://weaviate.io/developers/weaviate/api/graphql/additional-operators#autocut) parameter */ + autoLimit?: number; + /** The filters to be applied to the query. Use `weaviate.filter.*` to create filters */ + filters?: FilterValue; + /** How to rerank the query results. Requires a configured [reranking](https://weaviate.io/developers/weaviate/concepts/reranking) module. */ + rerank?: RerankOptions; + /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ + includeVector?: boolean | string[]; + /** Which metadata of the object to return. If not specified, no metadata is returned. */ + returnMetadata?: QueryMetadata; + /** + * Which properties of the object to return. Can be primitive, in which case specify their names, or nested, in which case + * use the QueryNested type. If not specified, all properties are returned. + */ + returnProperties?: QueryProperty[]; + /** Which references of the object to return. If not specified, no references are returned. */ + returnReferences?: QueryReference[]; +}; + +/** Base options available in the `query.bm25` method */ +export type BaseBm25Options = SearchOptions & { + /** Which properties of the collection to perform the keyword search on. */ + queryProperties?: PrimitiveKeys[]; +}; + +/** Options available in the `query.bm25` method when specifying the `groupBy` parameter. */ +export type GroupByBm25Options = BaseBm25Options & { + /** The group by options to apply to the search. */ + groupBy: GroupByOptions; +}; + +/** Options available in the `query.bm25` method */ +export type Bm25Options = BaseBm25Options | GroupByBm25Options | undefined; + +/** Base options available in the `query.hybrid` method */ +export type BaseHybridOptions = SearchOptions & { + /** The weight of the BM25 score. If not specified, the default weight specified by the server is used. */ + alpha?: number; + /** The specific vector to search for or a specific vector subsearch. If not specified, the query is vectorized and used in the similarity search. */ + vector?: number[] | HybridNearTextSubSearch | HybridNearVectorSubSearch; + /** The properties to search in. If not specified, all properties are searched. */ + queryProperties?: PrimitiveKeys[]; + /** The type of fusion to apply. If not specified, the default fusion type specified by the server is used. */ + fusionType?: 'Ranked' | 'RelativeScore'; + /** Specify which vector to search on if using named vectors. */ + targetVector?: string; +}; + +export type HybridSubSearchBase = { + certainty?: number; + distance?: number; +}; + +export type HybridNearTextSubSearch = HybridSubSearchBase & { + query: string | string[]; + moveTo?: MoveOptions; + moveAway?: MoveOptions; +}; + +export type HybridNearVectorSubSearch = HybridSubSearchBase & { + vector: number[]; +}; + +/** Options available in the `query.hybrid` method when specifying the `groupBy` parameter. */ +export type GroupByHybridOptions = BaseHybridOptions & { + /** The group by options to apply to the search. */ + groupBy: GroupByOptions; +}; + +/** Options available in the `query.hybrid` method */ +export type HybridOptions = BaseHybridOptions | GroupByHybridOptions | undefined; + +/** Base options for the near search queries. */ +export type BaseNearOptions = SearchOptions & { + /** The minimum similarity score to return. Incompatible with the `distance` param. */ + certainty?: number; + /** The maximum distance to search. Incompatible with the `certainty` param. */ + distance?: number; + /** Specify which vector to search on if using named vectors. */ + targetVector?: string; +}; + +/** Options available in the near search queries when specifying the `groupBy` parameter. */ +export type GroupByNearOptions = BaseNearOptions & { + /** The group by options to apply to the search. */ + groupBy: GroupByOptions; +}; + +/** Options available when specifying `moveTo` and `moveAway` in the `query.nearText` method. */ +export type MoveOptions = { + force: number; + objects?: string[]; + concepts?: string[]; +}; + +/** Base options for the `query.nearText` method. */ +export type BaseNearTextOptions = BaseNearOptions & { + moveTo?: MoveOptions; + moveAway?: MoveOptions; +}; + +/** Options available in the near text search queries when specifying the `groupBy` parameter. */ +export type GroupByNearTextOptions = BaseNearTextOptions & { + groupBy: GroupByOptions; +}; + +/** The type of the media to search for in the `query.nearMedia` method */ +export type NearMediaType = 'audio' | 'depth' | 'image' | 'imu' | 'thermal' | 'video'; + +interface Bm25 { + /** + * Search for objects in this collection using the keyword-based BM25 algorithm. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/bm25) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string} query - The query to search for. + * @param {BaseBm25Options} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + bm25(query: string, opts?: BaseBm25Options): Promise>; + /** + * Search for objects in this collection using the keyword-based BM25 algorithm. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/bm25) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string} query - The query to search for. + * @param {GroupByBm25Options} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + bm25(query: string, opts: GroupByBm25Options): Promise>; + /** + * Search for objects in this collection using the keyword-based BM25 algorithm. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/bm25) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string} query - The query to search for. + * @param {Bm25Options} [opts] - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + bm25(query: string, opts?: Bm25Options): QueryReturn; +} + +interface Hybrid { + /** + * Search for objects in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/hybrid) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string} query - The query to search for. + * @param {BaseHybridOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + hybrid(query: string, opts?: BaseHybridOptions): Promise>; + /** + * Search for objects in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/hybrid) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string} query - The query to search for. + * @param {GroupByHybridOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + hybrid(query: string, opts: GroupByHybridOptions): Promise>; + /** + * Search for objects in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/hybrid) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string} query - The query to search for. + * @param {HybridOptions} [opts] - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + hybrid(query: string, opts?: HybridOptions): QueryReturn; +} + +interface NearImage { + /** + * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. + * You must have an image-capable vectorization module installed in order to use this method, + * e.g. `img2vec-neural`, `multi2vec-clip`, or `multi2vec-bind. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/image) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string | Buffer} image - The image to search on. This can be a base64 string, a file path string, or a buffer. + * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + nearImage(image: string | Buffer, opts?: BaseNearOptions): Promise>; + /** + * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. + * You must have an image-capable vectorization module installed in order to use this method, + * e.g. `img2vec-neural`, `multi2vec-clip`, or `multi2vec-bind. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string | Buffer} image - The image to search on. This can be a base64 string, a file path string, or a buffer. + * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. + */ + nearImage(image: string | Buffer, opts: GroupByNearOptions): Promise>; + /** + * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. + * You must have an image-capable vectorization module installed in order to use this method, + * e.g. `img2vec-neural`, `multi2vec-clip`, or `multi2vec-bind. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string | Buffer} image - The image to search on. This can be a base64 string, a file path string, or a buffer. + * @param {NearOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. + */ + nearImage(image: string | Buffer, opts?: NearOptions): QueryReturn; +} + +interface NearMedia { + /** + * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. + * You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind` or `multi2vec-palm`. + * + * See the [docs](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-bind) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string | Buffer} media - The media to search on. This can be a base64 string, a file path string, or a buffer. + * @param {NearMediaType} type - The type of media to search for, e.g. 'audio'. + * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + nearMedia( + media: string | Buffer, + type: NearMediaType, + opts?: BaseNearOptions + ): Promise>; + /** + * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. + * You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind` or `multi2vec-palm`. + * + * See the [docs](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-bind) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string | Buffer} media - The media to search on. This can be a base64 string, a file path string, or a buffer. + * @param {NearMediaType} type - The type of media to search for, e.g. 'audio'. + * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. + */ + nearMedia( + media: string | Buffer, + type: NearMediaType, + opts: GroupByNearOptions + ): Promise>; + /** + * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. + * You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind` or `multi2vec-palm`. + * + * See the [docs](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/multi2vec-bind) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string | Buffer} media - The media to search on. This can be a base64 string, a file path string, or a buffer. + * @param {NearMediaType} type - The type of media to search for, e.g. 'audio'. + * @param {NearOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. + */ + nearMedia(media: string | Buffer, type: NearMediaType, opts?: NearOptions): QueryReturn; +} + +interface NearObject { + /** + * Search for objects in this collection by another object using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#nearobject) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string} id - The UUID of the object to search for. + * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + nearObject(id: string, opts?: BaseNearOptions): Promise>; + /** + * Search for objects in this collection by another object using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#nearobject) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string} id - The UUID of the object to search for. + * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. + */ + nearObject(id: string, opts: GroupByNearOptions): Promise>; + /** + * Search for objects in this collection by another object using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {number[]} id - The UUID of the object to search for. + * @param {NearOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. + */ + nearObject(id: string, opts?: NearOptions): QueryReturn; +} + +interface NearText { + /** + * Search for objects in this collection by text using text-capable vectorization module and vector-based similarity search. + * You must have a text-capable vectorization module installed in order to use this method, + * e.g. any of the `text2vec-` and `multi2vec-` modules. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#neartext) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {string | string[]} query - The text query to search for. + * @param {BaseNearTextOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + nearText(query: string | string[], opts?: BaseNearTextOptions): Promise>; + /** + * Search for objects in this collection by text using text-capable vectorization module and vector-based similarity search. + * You must have a text-capable vectorization module installed in order to use this method, + * e.g. any of the `text2vec-` and `multi2vec-` modules. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#neartext) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {string | string[]} query - The text query to search for. + * @param {GroupByNearTextOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. + */ + nearText(query: string | string[], opts: GroupByNearTextOptions): Promise>; + /** + * Search for objects in this collection by text using text-capable vectorization module and vector-based similarity search. + * You must have a text-capable vectorization module installed in order to use this method, + * e.g. any of the `text2vec-` and `multi2vec-` modules. + * + * See the [docs](https://weaviate.io/developers/weaviate/api/graphql/search-operators#neartext) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {string | string[]} query - The text query to search for. + * @param {NearTextOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. + */ + nearText(query: string | string[], opts?: NearTextOptions): QueryReturn; +} + +interface NearVector { + /** + * Search for objects by vector in this collection using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search without the `groupBy` param. + * + * @param {number[]} vector - The vector to search for. + * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. + */ + nearVector(vector: number[], opts?: BaseNearOptions): Promise>; + /** + * Search for objects by vector in this collection using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search with the `groupBy` param. + * + * @param {number[]} vector - The vector to search for. + * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. + */ + nearVector(vector: number[], opts: GroupByNearOptions): Promise>; + /** + * Search for objects by vector in this collection using a vector-based similarity search. + * + * See the [docs](https://weaviate.io/developers/weaviate/search/similarity) for a more detailed explanation. + * + * This overload is for performing a search with a programmatically defined `opts` param. + * + * @param {number[]} vector - The vector to search for. + * @param {NearOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. + */ + nearVector(vector: number[], opts?: NearOptions): QueryReturn; +} + +/** All the available methods on the `.query` namespace. */ +export interface Query + extends Bm25, + Hybrid, + NearImage, + NearMedia, + NearObject, + NearText, + NearVector { + /** + * Retrieve an object from the server by its UUID. + * + * @param {string} id - The UUID of the object to retrieve. + * @param {FetchObjectByIdOptions} [opts] - The available options for fetching the object. + * @returns {Promise | null>} - The object with the given UUID, or null if it does not exist. + */ + fetchObjectById: (id: string, opts?: FetchObjectByIdOptions) => Promise | null>; + + /** + * Retrieve objects from the server without searching. + * + * @param {FetchObjectsOptions} [opts] - The available options for fetching the objects. + * @returns {Promise>} - The objects within the fetched collection. + */ + fetchObjects: (opts?: FetchObjectsOptions) => Promise>; +} +/** Options available in the `query.nearImage`, `query.nearMedia`, `query.nearObject`, and `query.nearVector` methods */ +export type NearOptions = BaseNearOptions | GroupByNearOptions | undefined; + +/** Options available in the `query.nearText` method */ +export type NearTextOptions = BaseNearTextOptions | GroupByNearTextOptions | undefined; + +/** The return type of the `query` methods. It is a union of a standard query and a group by query due to function overloading. */ +export type QueryReturn = Promise> | Promise>; diff --git a/src/collections/references/classes.ts b/src/collections/references/classes.ts new file mode 100644 index 00000000..0200b7dc --- /dev/null +++ b/src/collections/references/classes.ts @@ -0,0 +1,79 @@ +import { Properties, ReferenceInput, ReferenceToMultiTarget, WeaviateObject } from '../types/index.js'; +import { Beacon } from './types.js'; +import { uuidToBeacon } from './utils.js'; + +export class ReferenceManager { + public objects: WeaviateObject[]; + public targetCollection: string; + public uuids?: string[]; + + constructor(targetCollection: string, objects?: WeaviateObject[], uuids?: string[]) { + this.objects = objects ?? []; + this.targetCollection = targetCollection; + this.uuids = uuids; + } + + public toBeaconObjs(): Beacon[] { + return this.uuids ? this.uuids.map((uuid) => uuidToBeacon(uuid, this.targetCollection)) : []; + } + + public toBeaconStrings(): string[] { + return this.uuids ? this.uuids.map((uuid) => uuidToBeacon(uuid, this.targetCollection).beacon) : []; + } + + public isMultiTarget(): boolean { + return this.targetCollection !== ''; + } +} + +/** + * A factory class to create references from objects to other objects. + */ +export class Reference { + /** + * Create a single-target reference with given UUID(s). + * + * @param {string | string[]} uuids The UUID(s) of the target object(s). + * @returns {ReferenceManager} The reference manager object. + */ + public static to( + uuids: string | string[] + ): ReferenceManager { + return new ReferenceManager('', undefined, Array.isArray(uuids) ? uuids : [uuids]); + } + /** + * Create a multi-target reference with given UUID(s) pointing to a specific target collection. + * + * @param {string | string[]} uuids The UUID(s) of the target object(s). + * @param {string} targetCollection The target collection name. + * @returns {ReferenceManager} The reference manager object. + */ + public static toMultiTarget( + uuids: string | string[], + targetCollection: string + ): ReferenceManager { + return new ReferenceManager( + targetCollection, + undefined, + Array.isArray(uuids) ? uuids : [uuids] + ); + } +} + +export class ReferenceGuards { + public static isReferenceManager(arg: ReferenceInput): arg is ReferenceManager { + return arg instanceof ReferenceManager; + } + + public static isUuid(arg: ReferenceInput): arg is string { + return typeof arg === 'string'; + } + + public static isUuids(arg: ReferenceInput): arg is string[] { + return Array.isArray(arg); + } + + public static isMultiTarget(arg: ReferenceInput): arg is ReferenceToMultiTarget { + return (arg as ReferenceToMultiTarget).targetCollection !== undefined; + } +} diff --git a/src/collections/references/index.ts b/src/collections/references/index.ts new file mode 100644 index 00000000..58295777 --- /dev/null +++ b/src/collections/references/index.ts @@ -0,0 +1,2 @@ +export { Reference, ReferenceManager } from './classes.js'; +export type { Beacon, CrossReference, CrossReferenceDefault, CrossReferences, UnionOf } from './types.js'; diff --git a/src/collections/references/types.ts b/src/collections/references/types.ts new file mode 100644 index 00000000..003365fd --- /dev/null +++ b/src/collections/references/types.ts @@ -0,0 +1,16 @@ +import { Properties, WeaviateNonGenericObject } from '../types/index.js'; +import { ReferenceManager } from './classes.js'; + +export type CrossReference = ReferenceManager; + +export type CrossReferenceDefault = { + objects: WeaviateNonGenericObject[]; +}; + +export type CrossReferences = ReferenceManager>; + +export type UnionOf = T extends (infer U)[] ? U : never; + +export type Beacon = { + beacon: string; +}; diff --git a/src/collections/references/utils.ts b/src/collections/references/utils.ts new file mode 100644 index 00000000..703998f7 --- /dev/null +++ b/src/collections/references/utils.ts @@ -0,0 +1,32 @@ +import { ReferenceInput } from '../types/index.js'; +import { ReferenceGuards, ReferenceManager } from './classes.js'; +import { Beacon } from './types.js'; + +export function uuidToBeacon(uuid: string, targetCollection?: string): Beacon { + return { + beacon: `weaviate://localhost/${targetCollection ? `${targetCollection}/` : ''}${uuid}`, + }; +} + +export const referenceFromObjects = ( + objects: any[], + targetCollection: string, + uuids: string[] +): ReferenceManager => { + return new ReferenceManager(targetCollection, objects, uuids); +}; + +export const referenceToBeacons = (ref: ReferenceInput): Beacon[] => { + if (ReferenceGuards.isReferenceManager(ref)) { + return ref.toBeaconObjs(); + } else if (ReferenceGuards.isUuid(ref)) { + return [uuidToBeacon(ref)]; + } else if (ReferenceGuards.isUuids(ref)) { + return ref.map((uuid) => uuidToBeacon(uuid)); + } else if (ReferenceGuards.isMultiTarget(ref)) { + return typeof ref.uuids === 'string' + ? [uuidToBeacon(ref.uuids, ref.targetCollection)] + : ref.uuids.map((uuid) => uuidToBeacon(uuid, ref.targetCollection)); + } + return []; +}; diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts new file mode 100644 index 00000000..2dd40387 --- /dev/null +++ b/src/collections/serialize/index.ts @@ -0,0 +1,1138 @@ +import { v4 as uuidv4 } from 'uuid'; +import { WhereFilter } from '../../openapi/types.js'; +import { + BatchObject as BatchObjectGRPC, + BatchObject_MultiTargetRefProps, + BatchObject_Properties, + BatchObject_SingleTargetRefProps, +} from '../../proto/v1/batch.js'; +import { + BM25, + GenerativeSearch, + GroupBy, + Hybrid, + Hybrid_FusionType, + MetadataRequest, + NearAudioSearch, + NearDepthSearch, + NearIMUSearch, + NearImageSearch, + NearObject, + NearTextSearch, + NearTextSearch_Move, + NearThermalSearch, + NearVector, + NearVideoSearch, + ObjectPropertiesRequest, + PropertiesRequest, + Rerank, + SortBy as SortByGrpc, +} from '../../proto/v1/search_get.js'; + +import { WeaviateInvalidInputError, WeaviateSerializationError } from '../../errors.js'; +import { + BaseSearchArgs, + SearchBm25Args, + SearchFetchArgs, + SearchHybridArgs, + SearchNearAudioArgs, + SearchNearDepthArgs, + SearchNearIMUArgs, + SearchNearImageArgs, + SearchNearObjectArgs, + SearchNearTextArgs, + SearchNearThermalArgs, + SearchNearVectorArgs, + SearchNearVideoArgs, +} from '../../grpc/searcher.js'; +import { + BooleanArrayProperties, + FilterTarget, + Filters as FiltersGRPC, + Filters_Operator, + IntArrayProperties, + NumberArrayProperties, + ObjectArrayProperties, + ObjectProperties, + ObjectPropertiesValue, + TextArrayProperties, + Vectors as VectorsGrpc, +} from '../../proto/v1/base.js'; +import { FilterId } from '../filters/classes.js'; +import { FilterValue, Filters } from '../filters/index.js'; +import { + FilterValueType, + GeoRangeFilter, + PrimitiveFilterValueType, + PrimitiveListFilterValueType, +} from '../filters/types.js'; +import { + BaseHybridOptions, + BaseNearOptions, + Bm25Options, + FetchObjectByIdOptions, + FetchObjectsOptions, + HybridNearTextSubSearch, + HybridNearVectorSubSearch, + HybridOptions, + NearOptions, + NearTextOptions, + SearchOptions, +} from '../query/types.js'; +import { ReferenceGuards } from '../references/classes.js'; +import { Beacon } from '../references/index.js'; +import { uuidToBeacon } from '../references/utils.js'; +import { + BatchObject, + BatchObjects, + DataObject, + GenerateOptions, + GeoCoordinate, + GroupByOptions, + MetadataKeys, + NestedProperties, + NonReferenceInputs, + PhoneNumberInput, + QueryMetadata, + QueryNested, + QueryProperty, + QueryReference, + ReferenceInput, + RerankOptions, + SortBy, + WeaviateField, +} from '../types/index.js'; + +class FilterGuards { + static isFilters = ( + argument?: Filters | PrimitiveFilterValueType | PrimitiveListFilterValueType + ): argument is Filters => { + return argument instanceof Filters; + }; + + static isText = (argument?: FilterValueType): argument is string => { + return typeof argument === 'string'; + }; + + static isTextArray = (argument?: FilterValueType): argument is string[] => { + return ( + argument instanceof Array && + (argument as Array).every((arg) => typeof arg === 'string') + ); + }; + + static isInt = (argument?: FilterValueType): argument is number => { + return typeof argument === 'number' && Number.isInteger(argument); + }; + + static isIntArray = (argument?: FilterValueType): argument is number[] => { + return ( + argument instanceof Array && + (argument as Array).every((arg) => typeof arg === 'number' && Number.isInteger(arg)) + ); + }; + + static isFloat = (argument?: FilterValueType): argument is number => { + return typeof argument === 'number'; + }; + + static isFloatArray = (argument?: FilterValueType): argument is number[] => { + return ( + argument instanceof Array && + (argument as Array).every((arg) => typeof arg === 'number' && !Number.isInteger(arg)) + ); + }; + + static isBoolean = (argument?: FilterValueType): argument is boolean => { + return typeof argument === 'boolean'; + }; + + static isBooleanArray = (argument?: FilterValueType): argument is boolean[] => { + return ( + argument instanceof Array && + (argument as Array).every((arg) => typeof arg === 'boolean') + ); + }; + + static isDate = (argument?: FilterValueType): argument is Date => { + return argument instanceof Date; + }; + + static isDateArray = (argument?: FilterValueType): argument is Date[] => { + return ( + argument instanceof Array && (argument as Array).every((arg) => arg instanceof Date) + ); + }; + + static isGeoRange = (argument?: FilterValueType): argument is GeoRangeFilter => { + const arg = argument as GeoRangeFilter; + return arg.latitude !== undefined && arg.longitude !== undefined && arg.distance !== undefined; + }; +} + +export class DataGuards { + static isText = (argument?: WeaviateField): argument is string => { + return typeof argument === 'string'; + }; + + static isTextArray = (argument?: WeaviateField): argument is string[] => { + return ( + argument instanceof Array && + argument.length > 0 && + (argument as Array).every(DataGuards.isText) + ); + }; + + static isInt = (argument?: WeaviateField): argument is number => { + return ( + typeof argument === 'number' && + Number.isInteger(argument) && + !Number.isNaN(argument) && + Number.isFinite(argument) + ); + }; + + static isIntArray = (argument?: WeaviateField): argument is number[] => { + return ( + argument instanceof Array && + argument.length > 0 && + (argument as Array).every(DataGuards.isInt) + ); + }; + + static isFloat = (argument?: WeaviateField): argument is number => { + return ( + typeof argument === 'number' && + !Number.isInteger(argument) && + !Number.isNaN(argument) && + Number.isFinite(argument) + ); + }; + + static isFloatArray = (argument?: WeaviateField): argument is number[] => { + return ( + argument instanceof Array && + argument.length > 0 && + (argument as Array).every(DataGuards.isFloat) + ); + }; + + static isBoolean = (argument?: WeaviateField): argument is boolean => { + return typeof argument === 'boolean'; + }; + + static isBooleanArray = (argument?: WeaviateField): argument is boolean[] => { + return ( + argument instanceof Array && + argument.length > 0 && + (argument as Array).every(DataGuards.isBoolean) + ); + }; + + static isDate = (argument?: WeaviateField): argument is Date => { + return argument instanceof Date; + }; + + static isDateArray = (argument?: WeaviateField): argument is Date[] => { + return ( + argument instanceof Array && + argument.length > 0 && + (argument as Array).every(DataGuards.isDate) + ); + }; + + static isGeoCoordinate = (argument?: WeaviateField): argument is GeoCoordinate => { + return ( + argument instanceof Object && + (argument as GeoCoordinate).latitude !== undefined && + (argument as GeoCoordinate).longitude !== undefined && + Object.keys(argument).length === 2 + ); + }; + + static isPhoneNumber = (argument?: WeaviateField): argument is PhoneNumberInput => { + return ( + argument instanceof Object && + (argument as PhoneNumberInput).number !== undefined && + (Object.keys(argument).length === 1 || + (Object.keys(argument).length === 2 && (argument as PhoneNumberInput).defaultCountry !== undefined)) + ); + }; + + static isNested = (argument?: WeaviateField): argument is NestedProperties => { + return ( + argument instanceof Object && + !(argument instanceof Array) && + !DataGuards.isDate(argument) && + !DataGuards.isGeoCoordinate(argument) && + !DataGuards.isPhoneNumber(argument) + ); + }; + + static isNestedArray = (argument?: WeaviateField): argument is NestedProperties[] => { + return ( + argument instanceof Array && + argument.length > 0 && + (argument as Array).every(DataGuards.isNested) + ); + }; + + static isEmptyArray = (argument?: WeaviateField): argument is [] => { + return argument instanceof Array && argument.length === 0; + }; + + static isDataObject = (obj: DataObject | NonReferenceInputs): obj is DataObject => { + return ( + (obj as DataObject).id !== undefined || + (obj as DataObject).properties !== undefined || + (obj as DataObject).references !== undefined || + (obj as DataObject).vectors !== undefined + ); + }; +} + +export class MetadataGuards { + static isKeys = (argument?: QueryMetadata): argument is MetadataKeys => { + return argument instanceof Array && argument.length > 0; + }; + + static isAll = (argument?: QueryMetadata): argument is 'all' => { + return argument === 'all'; + }; + + static isUndefined = (argument?: QueryMetadata): argument is undefined => { + return argument === undefined; + }; +} + +export class Serialize { + public static isNamedVectors = (opts?: BaseNearOptions): boolean => { + return Array.isArray(opts?.includeVector) || opts?.targetVector !== undefined; + }; + + private static common = (args?: SearchOptions): BaseSearchArgs => { + const out: BaseSearchArgs = { + limit: args?.limit, + offset: args?.offset, + filters: args?.filters ? Serialize.filtersGRPC(args.filters) : undefined, + properties: + args?.returnProperties || args?.returnReferences + ? Serialize.queryProperties(args.returnProperties, args.returnReferences) + : undefined, + metadata: Serialize.metadata(args?.includeVector, args?.returnMetadata), + }; + if (args?.rerank) { + out.rerank = Serialize.rerank(args.rerank); + } + return out; + }; + + public static fetchObjects = (args?: FetchObjectsOptions): SearchFetchArgs => { + return { + ...Serialize.common(args), + after: args?.after, + sortBy: args?.sort ? Serialize.sortBy(args.sort.sorts) : undefined, + }; + }; + + public static fetchObjectById = (args: { id: string } & FetchObjectByIdOptions): SearchFetchArgs => { + return { + ...Serialize.common({ + filters: new FilterId().equal(args.id), + includeVector: args.includeVector, + returnMetadata: ['creationTime', 'updateTime', 'isConsistent'], + returnProperties: args.returnProperties, + returnReferences: args.returnReferences, + }), + }; + }; + + public static bm25 = (args: { query: string } & Bm25Options): SearchBm25Args => { + return { + ...Serialize.common(args), + bm25Search: BM25.fromPartial({ + query: args.query, + properties: args.queryProperties, + }), + autocut: args.autoLimit, + }; + }; + + private static isHybridVectorSearch = (vector: BaseHybridOptions['vector']): vector is number[] => { + return Array.isArray(vector); + }; + + private static isHybridNearTextSearch = ( + vector: BaseHybridOptions['vector'] + ): vector is HybridNearTextSubSearch => { + return (vector as HybridNearTextSubSearch)?.query !== undefined; + }; + + private static isHybridNearVectorSearch = ( + vector: BaseHybridOptions['vector'] + ): vector is HybridNearVectorSubSearch => { + return (vector as HybridNearVectorSubSearch)?.vector !== undefined; + }; + + private static hybridVector = (vector: BaseHybridOptions['vector']): Uint8Array | undefined => { + return Serialize.isHybridVectorSearch(vector) ? Serialize.vectorToBytes(vector) : undefined; + }; + + private static hybridNearText = (vector: BaseHybridOptions['vector']): NearTextSearch | undefined => { + return Serialize.isHybridNearTextSearch(vector) + ? Serialize.nearTextSearch({ + ...vector, + query: vector.query, + }) + : undefined; + }; + + private static hybridNearVector = (vector: BaseHybridOptions['vector']): NearVector | undefined => { + return Serialize.isHybridNearVectorSearch(vector) + ? NearVector.fromPartial({ + vectorBytes: Serialize.vectorToBytes(vector.vector), + }) + : undefined; + }; + + public static hybrid = (args: { query: string } & HybridOptions): SearchHybridArgs => { + const fusionType = (fusionType?: string): Hybrid_FusionType => { + switch (fusionType) { + case 'Ranked': + return Hybrid_FusionType.FUSION_TYPE_RANKED; + case 'RelativeScore': + return Hybrid_FusionType.FUSION_TYPE_RELATIVE_SCORE; + default: + return Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED; + } + }; + + return { + ...Serialize.common(args), + hybridSearch: Hybrid.fromPartial({ + query: args.query, + alpha: args.alpha ? args.alpha : 0.5, + properties: args.queryProperties, + vectorBytes: Serialize.hybridVector(args.vector), + fusionType: fusionType(args.fusionType), + targetVectors: args.targetVector ? [args.targetVector] : undefined, + nearText: Serialize.hybridNearText(args.vector), + nearVector: Serialize.hybridNearVector(args.vector), + }), + autocut: args.autoLimit, + }; + }; + + public static nearAudio = (args: { audio: string } & NearOptions): SearchNearAudioArgs => { + return { + ...Serialize.common(args), + nearAudio: NearAudioSearch.fromPartial({ + audio: args.audio, + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + }), + autocut: args.autoLimit, + }; + }; + + public static nearDepth = (args: { depth: string } & NearOptions): SearchNearDepthArgs => { + return { + ...Serialize.common(args), + nearDepth: NearDepthSearch.fromPartial({ + depth: args.depth, + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + }), + autocut: args.autoLimit, + }; + }; + + public static nearImage = (args: { image: string } & NearOptions): SearchNearImageArgs => { + return { + ...Serialize.common(args), + nearImage: NearImageSearch.fromPartial({ + image: args.image, + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + }), + autocut: args.autoLimit, + }; + }; + + public static nearIMU = (args: { imu: string } & NearOptions): SearchNearIMUArgs => { + return { + ...Serialize.common(args), + nearIMU: NearIMUSearch.fromPartial({ + imu: args.imu, + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + }), + autocut: args.autoLimit, + }; + }; + + public static nearObject = (args: { id: string } & NearOptions): SearchNearObjectArgs => { + return { + ...Serialize.common(args), + nearObject: NearObject.fromPartial({ + id: args.id, + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + }), + autocut: args.autoLimit, + }; + }; + + private static nearTextSearch = (args: { + query: string | string[]; + certainty?: number; + distance?: number; + targetVector?: string; + moveAway?: { concepts?: string[]; force?: number; objects?: string[] }; + moveTo?: { concepts?: string[]; force?: number; objects?: string[] }; + }) => { + return NearTextSearch.fromPartial({ + query: typeof args.query === 'string' ? [args.query] : args.query, + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + moveAway: args.moveAway + ? NearTextSearch_Move.fromPartial({ + concepts: args.moveAway.concepts, + force: args.moveAway.force, + uuids: args.moveAway.objects, + }) + : undefined, + moveTo: args.moveTo + ? NearTextSearch_Move.fromPartial({ + concepts: args.moveTo.concepts, + force: args.moveTo.force, + uuids: args.moveTo.objects, + }) + : undefined, + }); + }; + + public static nearText = ( + args: { query: string | string[] } & NearTextOptions + ): SearchNearTextArgs => { + return { + ...Serialize.common(args), + nearText: Serialize.nearTextSearch(args), + autocut: args.autoLimit, + }; + }; + + public static nearThermal = (args: { thermal: string } & NearOptions): SearchNearThermalArgs => { + return { + ...Serialize.common(args), + nearThermal: NearThermalSearch.fromPartial({ + thermal: args.thermal, + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + }), + autocut: args.autoLimit, + }; + }; + + private static vectorToBytes = (vector: number[]): Uint8Array => { + return new Uint8Array(new Float32Array(vector).buffer); + }; + + private static nearVectorSearch = (args: { + vector: number[]; + certainty?: number; + distance?: number; + targetVector?: string; + }) => { + return NearVector.fromPartial({ + vectorBytes: Serialize.vectorToBytes(args.vector), + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + }); + }; + + public static nearVector = (args: { vector: number[] } & NearOptions): SearchNearVectorArgs => { + return { + ...Serialize.common(args), + nearVector: Serialize.nearVectorSearch(args), + autocut: args.autoLimit, + }; + }; + + public static nearVideo = (args: { video: string } & NearOptions): SearchNearVideoArgs => { + return { + ...Serialize.common(args), + nearVideo: NearVideoSearch.fromPartial({ + video: args.video, + certainty: args.certainty, + distance: args.distance, + targetVectors: args.targetVector ? [args.targetVector] : undefined, + }), + autocut: args.autoLimit, + }; + }; + + public static filtersGRPC = (filters: FilterValue): FiltersGRPC => { + const resolveFilters = (filters: FilterValue): FiltersGRPC[] => { + const out: FiltersGRPC[] = []; + filters.filters?.forEach((val) => out.push(Serialize.filtersGRPC(val))); + return out; + }; + const { value } = filters; + switch (filters.operator) { + case 'And': + return FiltersGRPC.fromPartial({ + operator: Filters_Operator.OPERATOR_AND, + filters: resolveFilters(filters), + }); + case 'Or': + return FiltersGRPC.fromPartial({ + operator: Filters_Operator.OPERATOR_OR, + filters: resolveFilters(filters), + }); + default: + return FiltersGRPC.fromPartial({ + operator: Serialize.operator(filters.operator), + target: filters.target, + valueText: FilterGuards.isText(value) ? value : undefined, + valueTextArray: FilterGuards.isTextArray(value) ? { values: value } : undefined, + valueInt: FilterGuards.isInt(value) ? value : undefined, + valueIntArray: FilterGuards.isIntArray(value) ? { values: value } : undefined, + valueNumber: FilterGuards.isFloat(value) ? value : undefined, + valueNumberArray: FilterGuards.isFloatArray(value) ? { values: value } : undefined, + valueBoolean: FilterGuards.isBoolean(value) ? value : undefined, + valueBooleanArray: FilterGuards.isBooleanArray(value) ? { values: value } : undefined, + valueGeo: FilterGuards.isGeoRange(value) ? value : undefined, + }); + } + }; + + private static filterTargetToREST = (target: FilterTarget): string[] => { + if (target.property) { + return [target.property]; + } else if (target.singleTarget) { + throw new WeaviateSerializationError( + 'Cannot use Filter.byRef() in the aggregate API currently. Instead use Filter.byRefMultiTarget() and specify the target collection explicitly.' + ); + } else if (target.multiTarget) { + if (target.multiTarget.target === undefined) { + throw new WeaviateSerializationError( + `target of multiTarget filter was unexpectedly undefined: ${target}` + ); + } + return [ + target.multiTarget.on, + target.multiTarget.targetCollection, + ...Serialize.filterTargetToREST(target.multiTarget.target), + ]; + } else if (target.count) { + return [target.count.on]; + } else { + return []; + } + }; + + public static filtersREST = (filters: FilterValue): WhereFilter => { + const { value } = filters; + if (filters.operator === 'And' || filters.operator === 'Or') { + return { + operator: filters.operator, + operands: filters.filters?.map(Serialize.filtersREST), + }; + } else { + if (filters.target === undefined) { + throw new WeaviateSerializationError(`target of filter was unexpectedly undefined: ${filters}`); + } + const out = { + path: Serialize.filterTargetToREST(filters.target), + operator: filters.operator, + }; + if (FilterGuards.isText(value)) { + return { + ...out, + valueText: value, + }; + } else if (FilterGuards.isTextArray(value)) { + return { + ...out, + valueTextArray: value, + }; + } else if (FilterGuards.isInt(value)) { + return { + ...out, + valueInt: value, + }; + } else if (FilterGuards.isIntArray(value)) { + return { + ...out, + valueIntArray: value, + }; + } else if (FilterGuards.isBoolean(value)) { + return { + ...out, + valueBoolean: value, + }; + } else if (FilterGuards.isBooleanArray(value)) { + return { + ...out, + valueBooleanArray: value, + }; + } else if (FilterGuards.isFloat(value)) { + return { + ...out, + valueNumber: value, + }; + } else if (FilterGuards.isFloatArray(value)) { + return { + ...out, + valueNumberArray: value, + }; + } else if (FilterGuards.isDate(value)) { + return { + ...out, + valueDate: value.toISOString(), + }; + } else if (FilterGuards.isDateArray(value)) { + return { + ...out, + valueDateArray: value.map((v) => v.toISOString()), + }; + } else if (FilterGuards.isGeoRange(value)) { + return { + ...out, + valueGeoRange: { + geoCoordinates: { + latitude: value.latitude, + longitude: value.longitude, + }, + distance: { + max: value.distance, + }, + }, + }; + } else { + throw new WeaviateInvalidInputError('Invalid filter value type'); + } + } + }; + + private static operator = (operator: string): Filters_Operator => { + switch (operator) { + case 'Equal': + return Filters_Operator.OPERATOR_EQUAL; + case 'NotEqual': + return Filters_Operator.OPERATOR_NOT_EQUAL; + case 'ContainsAny': + return Filters_Operator.OPERATOR_CONTAINS_ANY; + case 'ContainsAll': + return Filters_Operator.OPERATOR_CONTAINS_ALL; + case 'GreaterThan': + return Filters_Operator.OPERATOR_GREATER_THAN; + case 'GreaterThanEqual': + return Filters_Operator.OPERATOR_GREATER_THAN_EQUAL; + case 'LessThan': + return Filters_Operator.OPERATOR_LESS_THAN; + case 'LessThanEqual': + return Filters_Operator.OPERATOR_LESS_THAN_EQUAL; + case 'Like': + return Filters_Operator.OPERATOR_LIKE; + case 'WithinGeoRange': + return Filters_Operator.OPERATOR_WITHIN_GEO_RANGE; + case 'IsNull': + return Filters_Operator.OPERATOR_IS_NULL; + default: + return Filters_Operator.OPERATOR_UNSPECIFIED; + } + }; + + private static queryProperties = ( + properties?: QueryProperty[], + references?: QueryReference[] + ): PropertiesRequest => { + const nonRefProperties = properties?.filter((property) => typeof property === 'string') as + | string[] + | undefined; + const refProperties = references; + const objectProperties = properties?.filter((property) => typeof property === 'object') as + | QueryNested[] + | undefined; + + const resolveObjectProperty = (property: QueryNested): ObjectPropertiesRequest => { + const objProps = property.properties.filter((property) => typeof property !== 'string') as unknown; // cannot get types to work currently :( + return { + propName: property.name, + primitiveProperties: property.properties.filter( + (property) => typeof property === 'string' + ) as string[], + objectProperties: (objProps as QueryNested[]).map(resolveObjectProperty), + }; + }; + + return { + nonRefProperties: nonRefProperties === undefined ? [] : nonRefProperties, + returnAllNonrefProperties: nonRefProperties === undefined, + refProperties: refProperties + ? refProperties.map((property) => { + return { + referenceProperty: property.linkOn, + properties: Serialize.queryProperties(property.returnProperties as any), + metadata: Serialize.metadata(property.includeVector, property.returnMetadata), + targetCollection: property.targetCollection ? property.targetCollection : '', + }; + }) + : [], + objectProperties: objectProperties + ? objectProperties.map((property) => { + const objProps = property.properties.filter( + (property) => typeof property !== 'string' + ) as unknown; // cannot get types to work currently :( + return { + propName: property.name, + primitiveProperties: property.properties.filter( + (property) => typeof property === 'string' + ) as string[], + objectProperties: (objProps as QueryNested[]).map(resolveObjectProperty), + }; + }) + : [], + }; + }; + + private static metadata = ( + includeVector?: boolean | string[], + metadata?: QueryMetadata + ): MetadataRequest => { + const out: any = { + uuid: true, + vector: typeof includeVector === 'boolean' ? includeVector : false, + vectors: Array.isArray(includeVector) ? includeVector : [], + }; + if (MetadataGuards.isAll(metadata)) { + return { + ...out, + creationTimeUnix: true, + lastUpdateTimeUnix: true, + distance: true, + certainty: true, + score: true, + explainScore: true, + isConsistent: true, + }; + } + metadata?.forEach((key) => { + let weaviateKey: string; + if (key === 'creationTime') { + weaviateKey = 'creationTimeUnix'; + } else if (key === 'updateTime') { + weaviateKey = 'lastUpdateTimeUnix'; + } else { + weaviateKey = key; + } + out[weaviateKey] = true; + }); + return MetadataRequest.fromPartial(out); + }; + + private static sortBy = (sort: SortBy[]): SortByGrpc[] => { + return sort.map((sort) => { + return { + ascending: !!sort.ascending, + path: [sort.property], + }; + }); + }; + + public static rerank = (rerank: RerankOptions): Rerank => { + return Rerank.fromPartial({ + property: rerank.property as string, + query: rerank.query, + }); + }; + + public static generative = (generative?: GenerateOptions): GenerativeSearch => { + return GenerativeSearch.fromPartial({ + singleResponsePrompt: generative?.singlePrompt, + groupedResponseTask: generative?.groupedTask, + groupedProperties: generative?.groupedProperties as string[], + }); + }; + + public static groupBy = (groupBy?: GroupByOptions): GroupBy => { + return GroupBy.fromPartial({ + path: groupBy?.property ? [groupBy.property as string] : undefined, + numberOfGroups: groupBy?.numberOfGroups, + objectsPerGroup: groupBy?.objectsPerGroup, + }); + }; + + public static isGroupBy = (args: any): args is T => { + if (args === undefined) return false; + return args.groupBy !== undefined; + }; + + public static restProperties = ( + properties: Record, + references?: Record> + ): Record => { + const parsedProperties: any = {}; + Object.keys(properties).forEach((key) => { + const value = properties[key]; + if (DataGuards.isDate(value)) { + parsedProperties[key] = value.toISOString(); + } else if (DataGuards.isDateArray(value)) { + parsedProperties[key] = value.map((v) => v.toISOString()); + } else if (DataGuards.isPhoneNumber(value)) { + parsedProperties[key] = { + input: value.number, + defaultCountry: value.defaultCountry, + }; + } else if (DataGuards.isNestedArray(value)) { + parsedProperties[key] = value.map((v) => Serialize.restProperties(v)); + } else if (DataGuards.isNested(value)) { + parsedProperties[key] = Serialize.restProperties(value); + } else { + parsedProperties[key] = value; + } + }); + if (!references) return parsedProperties; + for (const [key, value] of Object.entries(references)) { + if (ReferenceGuards.isReferenceManager(value)) { + parsedProperties[key] = value.toBeaconObjs(); + } else if (ReferenceGuards.isUuid(value)) { + parsedProperties[key] = [uuidToBeacon(value)]; + } else if (ReferenceGuards.isMultiTarget(value)) { + parsedProperties[key] = + typeof value.uuids === 'string' + ? [uuidToBeacon(value.uuids, value.targetCollection)] + : value.uuids.map((uuid) => uuidToBeacon(uuid, value.targetCollection)); + } else { + let out: Beacon[] = []; + value.forEach((v) => { + if (ReferenceGuards.isReferenceManager(v)) { + out = out.concat(v.toBeaconObjs()); + } else if (ReferenceGuards.isUuid(v)) { + out.push(uuidToBeacon(v)); + } else { + out = out.concat( + (ReferenceGuards.isUuid(v.uuids) ? [v.uuids] : v.uuids).map((uuid) => + uuidToBeacon(uuid, v.targetCollection) + ) + ); + } + }); + parsedProperties[key] = out; + } + } + return parsedProperties; + }; + + private static batchProperties = ( + properties?: Record, + references?: Record> + ): BatchObject_Properties => { + const multiTarget: BatchObject_MultiTargetRefProps[] = []; + const singleTarget: BatchObject_SingleTargetRefProps[] = []; + const nonRefProperties: Record = {}; + const emptyArray: string[] = []; + const boolArray: BooleanArrayProperties[] = []; + const textArray: TextArrayProperties[] = []; + const intArray: IntArrayProperties[] = []; + const floatArray: NumberArrayProperties[] = []; + const objectProperties: ObjectProperties[] = []; + const objectArrayProperties: ObjectArrayProperties[] = []; + + const resolveProps = (key: string, value: any) => { + if (DataGuards.isEmptyArray(value)) { + emptyArray.push(key); + } else if (DataGuards.isBooleanArray(value)) { + boolArray.push({ + propName: key, + values: value, + }); + } else if (DataGuards.isDateArray(value)) { + textArray.push({ + propName: key, + values: value.map((v) => v.toISOString()), + }); + } else if (DataGuards.isTextArray(value)) { + textArray.push({ + propName: key, + values: value, + }); + } else if (DataGuards.isIntArray(value)) { + intArray.push({ + propName: key, + values: value, + }); + } else if (DataGuards.isFloatArray(value)) { + floatArray.push({ + propName: key, + values: [], + valuesBytes: new Uint8Array(new Float64Array(value).buffer), // is double in proto => f64 in go + }); + } else if (DataGuards.isDate(value)) { + nonRefProperties[key] = value.toISOString(); + } else if (DataGuards.isPhoneNumber(value)) { + nonRefProperties[key] = { + input: value.number, + defaultCountry: value.defaultCountry, + }; + } else if (DataGuards.isGeoCoordinate(value)) { + nonRefProperties[key] = value; + } else if (DataGuards.isNestedArray(value)) { + objectArrayProperties.push({ + propName: key, + values: value.map((v) => ObjectPropertiesValue.fromPartial(Serialize.batchProperties(v))), + }); + } else if (DataGuards.isNested(value)) { + const parsed = Serialize.batchProperties(value); + objectProperties.push({ + propName: key, + value: ObjectPropertiesValue.fromPartial(parsed), + }); + } else { + nonRefProperties[key] = value; + } + }; + + const resolveRefs = (key: string, value: ReferenceInput) => { + if (ReferenceGuards.isReferenceManager(value)) { + if (value.isMultiTarget()) { + multiTarget.push({ + propName: key, + targetCollection: value.targetCollection, + uuids: value.toBeaconStrings(), + }); + } else { + singleTarget.push({ + propName: key, + uuids: value.toBeaconStrings(), + }); + } + } else if (ReferenceGuards.isUuid(value)) { + singleTarget.push({ + propName: key, + uuids: [value], + }); + } else if (ReferenceGuards.isMultiTarget(value)) { + multiTarget.push({ + propName: key, + targetCollection: value.targetCollection, + uuids: typeof value.uuids === 'string' ? [value.uuids] : value.uuids, + }); + } else { + value.forEach((v) => resolveRefs(key, v)); + } + }; + + if (properties) { + Object.entries(properties).forEach(([key, value]) => resolveProps(key, value)); + } + + if (references) { + Object.entries(references).forEach(([key, value]) => resolveRefs(key, value)); + } + + return { + nonRefProperties: nonRefProperties, + multiTargetRefProps: multiTarget, + singleTargetRefProps: singleTarget, + textArrayProperties: textArray, + intArrayProperties: intArray, + numberArrayProperties: floatArray, + booleanArrayProperties: boolArray, + objectProperties: objectProperties, + objectArrayProperties: objectArrayProperties, + emptyListProps: emptyArray, + }; + }; + + public static batchObjects = ( + collection: string, + objects: (DataObject | NonReferenceInputs)[], + usesNamedVectors: boolean, + tenant?: string + ): Promise> => { + const objs: BatchObjectGRPC[] = []; + const batch: BatchObject[] = []; + + const iterate = (index: number) => { + // This allows the potentially CPU-intensive work to be done in chunks + // releasing control to the event loop after every object so that other + // events can be processed without blocking completely. + + if (index < objects.length) { + setTimeout(() => iterate(index + 1)); + } else { + return; + } + + const object = objects[index]; + const obj = DataGuards.isDataObject(object) + ? object + : { id: undefined, properties: object, references: undefined, vectors: undefined }; + + let vectorBytes: Uint8Array | undefined; + let vectors: VectorsGrpc[] | undefined; + if (obj.vectors !== undefined && !Array.isArray(obj.vectors)) { + vectors = Object.entries(obj.vectors).map(([k, v]) => + VectorsGrpc.fromPartial({ + vectorBytes: Serialize.vectorToBytes(v), + name: k, + }) + ); + } else if (Array.isArray(obj.vectors) && usesNamedVectors) { + vectors = [ + VectorsGrpc.fromPartial({ + vectorBytes: Serialize.vectorToBytes(obj.vectors), + name: 'default', + }), + ]; + } else if (obj.vectors !== undefined) { + vectorBytes = Serialize.vectorToBytes(obj.vectors); + } + + objs.push( + BatchObjectGRPC.fromPartial({ + collection: collection, + properties: Serialize.batchProperties(obj.properties, obj.references), + tenant: tenant, + uuid: obj.id ? obj.id : uuidv4(), + vectorBytes, + vectors, + }) + ); + + batch.push({ + ...obj, + collection: collection, + tenant: tenant, + }); + }; + + const waitFor = () => { + const poll = (resolve: (value: null) => void) => { + if (objs.length < objects.length) { + setTimeout(() => poll(resolve), 500); + } else { + resolve(null); + } + }; + return new Promise(poll); + }; + + iterate(0); + + return waitFor().then(() => { + return { batch: batch, mapped: objs }; + }); + }; +} diff --git a/src/collections/serialize/unit.test.ts b/src/collections/serialize/unit.test.ts new file mode 100644 index 00000000..6ec41cd7 --- /dev/null +++ b/src/collections/serialize/unit.test.ts @@ -0,0 +1,694 @@ +import { + SearchBm25Args, + SearchFetchArgs, + SearchHybridArgs, + SearchNearAudioArgs, + SearchNearDepthArgs, + SearchNearIMUArgs, + SearchNearImageArgs, + SearchNearObjectArgs, + SearchNearTextArgs, + SearchNearThermalArgs, + SearchNearVectorArgs, + SearchNearVideoArgs, +} from '../../grpc/searcher.js'; +import { Filters, Filters_Operator } from '../../proto/v1/base.js'; +import { + BM25, + GenerativeSearch, + GroupBy, + Hybrid, + Hybrid_FusionType, + MetadataRequest, + NearAudioSearch, + NearDepthSearch, + NearIMUSearch, + NearImageSearch, + NearObject, + NearTextSearch, + NearTextSearch_Move, + NearThermalSearch, + NearVector, + NearVideoSearch, + PropertiesRequest, +} from '../../proto/v1/search_get.js'; +import filter from '../filters/index.js'; +import { Reference } from '../references/index.js'; +import sort from '../sort/index.js'; +import { WeaviateField } from '../types/index.js'; +import { DataGuards, Serialize } from './index.js'; + +describe('Unit testing of Serialize', () => { + it('should parse args for fetchObjects', () => { + const args = Serialize.fetchObjects({ + limit: 1, + offset: 0, + after: 'one', + filters: filter().byProperty('name').equal('test'), + sort: sort().byProperty('name'), + includeVector: true, + returnMetadata: 'all', + returnProperties: ['name'], + returnReferences: [{ linkOn: 'ref' }], + }); + expect(args).toEqual({ + limit: 1, + offset: 0, + after: 'one', + filters: Filters.fromPartial({ + operator: Filters_Operator.OPERATOR_EQUAL, + target: { + property: 'name', + }, + valueText: 'test', + }), + sortBy: [{ ascending: true, path: ['name'] }], + metadata: MetadataRequest.fromPartial({ + certainty: true, + distance: true, + uuid: true, + vector: true, + vectors: undefined, + creationTimeUnix: true, + lastUpdateTimeUnix: true, + isConsistent: true, + explainScore: true, + score: true, + }), + properties: PropertiesRequest.fromPartial({ + nonRefProperties: ['name'], + refProperties: [ + { + metadata: MetadataRequest.fromPartial({ uuid: true }), + properties: PropertiesRequest.fromPartial({ returnAllNonrefProperties: true }), + referenceProperty: 'ref', + targetCollection: '', + }, + ], + }), + }); + }); + + it('should parse args for fetchObjectById', () => { + const args = Serialize.fetchObjectById({ + id: '1', + includeVector: ['title'], + returnProperties: ['name'], + returnReferences: [{ linkOn: 'ref' }], + }); + expect(args).toEqual({ + filters: Filters.fromPartial({ + operator: Filters_Operator.OPERATOR_EQUAL, + target: { + property: '_id', + }, + valueText: '1', + }), + metadata: MetadataRequest.fromPartial({ + creationTimeUnix: true, + lastUpdateTimeUnix: true, + isConsistent: true, + uuid: true, + vectors: ['title'], + }), + properties: PropertiesRequest.fromPartial({ + nonRefProperties: ['name'], + refProperties: [ + { + metadata: MetadataRequest.fromPartial({ uuid: true }), + properties: PropertiesRequest.fromPartial({ returnAllNonrefProperties: true }), + referenceProperty: 'ref', + targetCollection: '', + }, + ], + }), + }); + }); + + it('should parse args for bm25', () => { + const args = Serialize.bm25({ + query: 'test', + queryProperties: ['name'], + autoLimit: 1, + }); + expect(args).toEqual({ + bm25Search: BM25.fromPartial({ + query: 'test', + properties: ['name'], + }), + autocut: 1, + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for hybrid', () => { + const args = Serialize.hybrid({ + query: 'test', + queryProperties: ['name'], + alpha: 0.6, + vector: [1, 2, 3], + targetVector: 'title', + fusionType: 'Ranked', + }); + expect(args).toEqual({ + hybridSearch: Hybrid.fromPartial({ + query: 'test', + properties: ['name'], + alpha: 0.6, + vectorBytes: new Uint8Array(new Float32Array([1, 2, 3]).buffer), + targetVectors: ['title'], + fusionType: Hybrid_FusionType.FUSION_TYPE_RANKED, + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearAudio', () => { + const args = Serialize.nearAudio({ + audio: 'audio', + certainty: 0.6, + distance: 0.4, + targetVector: 'audio', + }); + expect(args).toEqual({ + nearAudio: NearAudioSearch.fromPartial({ + audio: 'audio', + certainty: 0.6, + distance: 0.4, + targetVectors: ['audio'], + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearDepth', () => { + const args = Serialize.nearDepth({ + depth: 'depth', + }); + expect(args).toEqual({ + nearDepth: NearDepthSearch.fromPartial({ + depth: 'depth', + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearIMU', () => { + const args = Serialize.nearIMU({ + imu: 'imu', + }); + expect(args).toEqual({ + nearIMU: NearIMUSearch.fromPartial({ + imu: 'imu', + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearImage', () => { + const args = Serialize.nearImage({ + image: 'image', + }); + expect(args).toEqual({ + nearImage: NearImageSearch.fromPartial({ + image: 'image', + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearObject', () => { + const args = Serialize.nearObject({ + id: 'id', + }); + expect(args).toEqual({ + nearObject: NearObject.fromPartial({ + id: 'id', + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearText', () => { + const args = Serialize.nearText({ + query: 'test', + moveAway: { + objects: ['0'], + concepts: ['bad'], + force: 0.4, + }, + moveTo: { + objects: ['1'], + concepts: ['good'], + force: 0.6, + }, + }); + expect(args).toEqual({ + nearText: NearTextSearch.fromPartial({ + query: ['test'], + moveAway: NearTextSearch_Move.fromPartial({ + uuids: ['0'], + concepts: ['bad'], + force: 0.4, + }), + moveTo: NearTextSearch_Move.fromPartial({ + uuids: ['1'], + concepts: ['good'], + force: 0.6, + }), + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearThermal', () => { + const args = Serialize.nearThermal({ + thermal: 'thermal', + }); + expect(args).toEqual({ + nearThermal: NearThermalSearch.fromPartial({ + thermal: 'thermal', + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearVector', () => { + const args = Serialize.nearVector({ + vector: [1, 2, 3], + }); + expect(args).toEqual({ + nearVector: NearVector.fromPartial({ + vectorBytes: new Uint8Array(new Float32Array([1, 2, 3]).buffer), + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearVideo', () => { + const args = Serialize.nearVideo({ + video: 'video', + }); + expect(args).toEqual({ + nearVideo: NearVideoSearch.fromPartial({ + video: 'video', + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for generative', () => { + const args = Serialize.generative({ + singlePrompt: 'test', + groupedProperties: ['name'], + groupedTask: 'testing', + }); + expect(args).toEqual({ + singleResponsePrompt: 'test', + groupedProperties: ['name'], + groupedResponseTask: 'testing', + }); + }); + + it('should parse args for groupBy', () => { + const args = Serialize.groupBy({ + property: 'name', + numberOfGroups: 1, + objectsPerGroup: 2, + }); + expect(args).toEqual({ + path: ['name'], + numberOfGroups: 1, + objectsPerGroup: 2, + }); + }); + + it('should parse args for isGroupBy', () => { + const isGroupBy = Serialize.isGroupBy({ + groupBy: { + property: 'name', + numberOfGroups: 1, + objectsPerGroup: 2, + }, + }); + const isNotGroupBy = Serialize.isGroupBy({}); + expect(isGroupBy).toEqual(true); + expect(isNotGroupBy).toEqual(false); + }); + + it('should parse args for restProperties', () => { + const args = Serialize.restProperties( + { + name: 'John', + age: 30, + height: 1.8, + isHappy: true, + birthday: new Date(), + namedays: [new Date(), new Date()], + location: { + latitude: 1, + longitude: 1, + }, + phoneNumber: { + number: '+44 1234 567890', + }, + clothing: [ + { + type: 'shirt', + color: 'blue', + whenMade: new Date(), + }, + { + type: 'pants', + color: 'black', + whenMade: new Date(), + }, + ], + mindset: { + hopeful: true, + optimistic: true, + }, + }, + { + str: '1', + strs: ['2', '3'], + typeStr: { + targetCollection: 'A', + uuids: '4', + }, + typesStr: [ + { + targetCollection: 'B', + uuids: '5', + }, + { + targetCollection: 'C', + uuids: '6', + }, + ], + typeStrs: { + targetCollection: 'D', + uuids: ['7', '8'], + }, + typesStrs: [ + { + targetCollection: 'E', + uuids: ['9', '10'], + }, + { + targetCollection: 'F', + uuids: ['11', '12'], + }, + ], + mngrStrSngl: Reference.to('13'), + mngrsStrSngl: [Reference.to('14'), Reference.to('15')], + mngrStrMlt: Reference.toMultiTarget('16', 'G'), + mngrsStrMlt: [Reference.toMultiTarget(['17', '18'], 'H'), Reference.toMultiTarget(['19', '20'], 'I')], + mngrStrsSngl: Reference.to(['21', '22']), + mngrsStrsSngl: [Reference.to(['23', '24']), Reference.to(['25', '26'])], + mngrStrsMlt: Reference.toMultiTarget(['27', '28'], 'J'), + mngrsStrsMlt: [ + Reference.toMultiTarget(['29', '30'], 'K'), + Reference.toMultiTarget(['31', '32'], 'L'), + ], + } + ); + expect(args).toEqual({ + name: 'John', + age: 30, + height: 1.8, + isHappy: true, + birthday: expect.any(String), + namedays: [expect.any(String), expect.any(String)], + location: { + latitude: 1, + longitude: 1, + }, + phoneNumber: { + input: '+44 1234 567890', + }, + clothing: [ + { + type: 'shirt', + color: 'blue', + whenMade: expect.any(String), + }, + { + type: 'pants', + color: 'black', + whenMade: expect.any(String), + }, + ], + mindset: { + hopeful: true, + optimistic: true, + }, + str: [{ beacon: 'weaviate://localhost/1' }], + strs: [{ beacon: 'weaviate://localhost/2' }, { beacon: 'weaviate://localhost/3' }], + typeStr: [{ beacon: 'weaviate://localhost/A/4' }], + typesStr: [{ beacon: 'weaviate://localhost/B/5' }, { beacon: 'weaviate://localhost/C/6' }], + typeStrs: [{ beacon: 'weaviate://localhost/D/7' }, { beacon: 'weaviate://localhost/D/8' }], + typesStrs: [ + { beacon: 'weaviate://localhost/E/9' }, + { beacon: 'weaviate://localhost/E/10' }, + { beacon: 'weaviate://localhost/F/11' }, + { beacon: 'weaviate://localhost/F/12' }, + ], + mngrStrSngl: [{ beacon: 'weaviate://localhost/13' }], + mngrsStrSngl: [{ beacon: 'weaviate://localhost/14' }, { beacon: 'weaviate://localhost/15' }], + mngrStrMlt: [{ beacon: 'weaviate://localhost/G/16' }], + mngrsStrMlt: [ + { beacon: 'weaviate://localhost/H/17' }, + { beacon: 'weaviate://localhost/H/18' }, + { beacon: 'weaviate://localhost/I/19' }, + { beacon: 'weaviate://localhost/I/20' }, + ], + mngrStrsSngl: [{ beacon: 'weaviate://localhost/21' }, { beacon: 'weaviate://localhost/22' }], + mngrsStrsSngl: [ + { beacon: 'weaviate://localhost/23' }, + { beacon: 'weaviate://localhost/24' }, + { beacon: 'weaviate://localhost/25' }, + { beacon: 'weaviate://localhost/26' }, + ], + mngrStrsMlt: [{ beacon: 'weaviate://localhost/J/27' }, { beacon: 'weaviate://localhost/J/28' }], + mngrsStrsMlt: [ + { beacon: 'weaviate://localhost/K/29' }, + { beacon: 'weaviate://localhost/K/30' }, + { beacon: 'weaviate://localhost/L/31' }, + { beacon: 'weaviate://localhost/L/32' }, + ], + }); + }); +}); + +describe('Unit testing of DataGuards', () => { + const values: WeaviateField[] = [ + 1, + 1.1, + NaN, + Infinity, + new Date(), + { prop: 'hi' }, + [], + true, + 'text', + { + latitude: 1, + longitude: 1, + }, + { + number: '+44 1234 567890', + }, + [1], + [1.1], + [NaN], + [Infinity], + [new Date()], + [{ prop: 'hi' }], + [true], + ['text'], + ]; + const opposite = + (f: (...args: any[]) => any) => + (...args: any[]): any => + !f(...args); + it('should check isText', () => { + const pred = (v: any) => v === 'text'; + values + .filter(pred) + .map(DataGuards.isText) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isText) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isTextArray', () => { + const pred = (v: any) => Array.isArray(v) && v[0] === 'text'; + values + .filter(pred) + .map(DataGuards.isTextArray) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isTextArray) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isInt', () => { + const pred = (v: any) => v === 1; + values + .filter(pred) + .map(DataGuards.isInt) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isInt) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isIntArray', () => { + const pred = (v: any) => Array.isArray(v) && v[0] === 1; + values + .filter(pred) + .map(DataGuards.isIntArray) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isIntArray) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isFloat', () => { + const pred = (v: any) => v === 1.1; + values + .filter(pred) + .map(DataGuards.isFloat) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isFloat) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isFloatArray', () => { + const pred = (v: any) => Array.isArray(v) && v[0] === 1.1; + values + .filter(pred) + .map(DataGuards.isFloatArray) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isFloatArray) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isBoolean', () => { + const pred = (v: any) => v === true; + values + .filter(pred) + .map(DataGuards.isBoolean) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isBoolean) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isBooleanArray', () => { + const pred = (v: any) => Array.isArray(v) && v[0] === true; + values + .filter(pred) + .map(DataGuards.isBooleanArray) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isBooleanArray) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isDate', () => { + const pred = (v: any) => v instanceof Date; + values + .filter(pred) + .map(DataGuards.isDate) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isDate) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isDateArray', () => { + const pred = (v: any) => Array.isArray(v) && v[0] instanceof Date; + values + .filter(pred) + .map(DataGuards.isDateArray) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isDateArray) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isGeoCoordinate', () => { + const pred = (v: any) => v.latitude && v.longitude; + values + .filter(pred) + .map(DataGuards.isGeoCoordinate) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isGeoCoordinate) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isPhoneNumber', () => { + const pred = (v: any) => v.number; + values + .filter(pred) + .map(DataGuards.isPhoneNumber) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isPhoneNumber) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isNested', () => { + const pred = (v: any) => v.prop === 'hi'; + values + .filter(pred) + .map(DataGuards.isNested) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isNested) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isNestedArray', () => { + const pred = (v: any) => Array.isArray(v) && v[0]?.prop === 'hi'; + values + .filter(pred) + .map(DataGuards.isNestedArray) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isNestedArray) + .forEach((result) => expect(result).toEqual(false)); + }); + + it('should check isEmptyArray', () => { + const pred = (v: any) => Array.isArray(v) && v.length === 0; + values + .filter(pred) + .map(DataGuards.isEmptyArray) + .forEach((result) => expect(result).toEqual(true)); + values + .filter(opposite(pred)) + .map(DataGuards.isEmptyArray) + .forEach((result) => expect(result).toEqual(false)); + }); +}); diff --git a/src/collections/sort/classes.ts b/src/collections/sort/classes.ts new file mode 100644 index 00000000..fe8fb63d --- /dev/null +++ b/src/collections/sort/classes.ts @@ -0,0 +1,34 @@ +import { SortBy } from '../types/index.js'; +import { NonRefKeys } from '../types/internal.js'; + +export class Sorting { + public sorts: SortBy[]; + + constructor() { + this.sorts = []; + } + + /** Sort by the objects' property. */ + public byProperty>(property: K, ascending = true) { + this.sorts.push({ property, ascending }); + return this; + } + + /** Sort by the objects' ID. */ + public byId(ascending = true) { + this.sorts.push({ property: '_id', ascending }); + return this; + } + + /** Sort by the objects' creation time. */ + public byCreationTime(ascending = true) { + this.sorts.push({ property: '_creationTimeUnix', ascending }); + return this; + } + + /** Sort by the objects' last update time. */ + public byUpdateTime(ascending = true) { + this.sorts.push({ property: '_lastUpdateTimeUnix', ascending }); + return this; + } +} diff --git a/src/collections/sort/index.ts b/src/collections/sort/index.ts new file mode 100644 index 00000000..f4ce9ca5 --- /dev/null +++ b/src/collections/sort/index.ts @@ -0,0 +1,26 @@ +export type { Sort } from './types.js'; + +import { NonRefKeys } from '../types/internal.js'; +import { Sorting } from './classes.js'; +import { Sort } from './types.js'; + +const sort = (): Sort => { + return { + byProperty>(property: K, ascending = true) { + return new Sorting().byProperty(property, ascending); + }, + byId(ascending = true) { + return new Sorting().byId(ascending); + }, + byCreationTime(ascending = true) { + return new Sorting().byCreationTime(ascending); + }, + byUpdateTime(ascending = true) { + return new Sorting().byUpdateTime(ascending); + }, + }; +}; + +export default sort; + +export { Sorting }; diff --git a/src/collections/sort/integration.test.ts b/src/collections/sort/integration.test.ts new file mode 100644 index 00000000..d4b2e9c7 --- /dev/null +++ b/src/collections/sort/integration.test.ts @@ -0,0 +1,401 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import weaviate, { WeaviateClient } from '../../index.js'; +import { Collection } from '../collection/index.js'; + +describe('Testing of the Sort class with a simple collection', () => { + let client: WeaviateClient; + type Name = 'TestCollectionSortSimple'; + let collection: Collection; + let collections: (Collection | Collection)[]; + const collectionName = 'TestCollectionSortSimple'; + let ids = [ + 'd9ebd143-83aa-46c6-80ca-98730debe78c', + 'd9ebd143-83aa-46c6-80ca-98730debe78d', + 'd9ebd143-83aa-46c6-80ca-98730debe78e', + 'd9ebd143-83aa-46c6-80ca-98730debe78f', + ]; + + type TestType = { + text: string; + int: number; + float: number; + date: Date; + isCool: boolean; + nullable?: string; + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + collections = [collection, client.collections.get(collectionName)]; + ids = await client.collections + .create({ + name: collectionName, + properties: [ + { + name: 'text', + dataType: 'text', + }, + { + name: 'int', + dataType: 'int', + }, + { + name: 'float', + dataType: 'number', + }, + { + name: 'date', + dataType: 'date', + }, + { + name: 'nullable', + dataType: 'text', + }, + { + name: 'isCool', + dataType: 'boolean', + }, + ], + }) + .catch((err) => { + throw err; + }) + .then(() => + collection.data.insertMany([ + { + properties: { + text: 'one', + int: 1, + float: 1.1, + date: new Date('2021-01-01'), + isCool: true, + }, + id: ids[0], + }, + { + properties: { + text: 'two', + int: 2, + float: 2.2, + date: new Date('2021-01-02'), + isCool: false, + }, + id: ids[1], + }, + { + properties: { + text: 'three', + int: 3, + float: 3.3, + date: new Date('2021-01-03'), + isCool: false, + }, + id: ids[2], + }, + { + properties: { + text: 'four', + int: 4, + float: 4.4, + date: new Date('2021-01-04'), + isCool: false, + nullable: 'oi', + }, + id: ids[3], + }, + ]) + ) + .then((res) => { + if (res.hasErrors) { + console.error(res.errors); + throw new Error('Failed to insert objects'); + } + return Object.values(res.uuids); + }) + .catch((err) => { + throw err; + }); + return ids; + }); + + it('should sort by text ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('text'), + }) + .then((res) => + expect(res.objects.map((o) => o.properties.text)).toEqual(['four', 'one', 'three', 'two']) + ) + ) + ); + }); + + it('should sort by text descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('text', false), + }) + .then((res) => + expect(res.objects.map((o) => o.properties.text)).toEqual(['two', 'three', 'one', 'four']) + ) + ) + ); + }); + + it('should sort by int ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('int'), + }) + .then((res) => expect(res.objects.map((o) => o.properties.int)).toEqual([1, 2, 3, 4])) + ) + ); + }); + + it('should sort by int descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('int', false), + }) + .then((res) => expect(res.objects.map((o) => o.properties.int)).toEqual([4, 3, 2, 1])) + ) + ); + }); + + it('should sort by float ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('float'), + }) + .then((res) => expect(res.objects.map((o) => o.properties.float)).toEqual([1.1, 2.2, 3.3, 4.4])) + ) + ); + }); + + it('should sort by float descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('float', false), + }) + .then((res) => expect(res.objects.map((o) => o.properties.float)).toEqual([4.4, 3.3, 2.2, 1.1])) + ) + ); + }); + + it('should sort by date ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('date'), + }) + .then((res) => + expect(res.objects.map((o) => o.properties.date)).toEqual([ + new Date('2021-01-01'), + new Date('2021-01-02'), + new Date('2021-01-03'), + new Date('2021-01-04'), + ]) + ) + ) + ); + }); + + it('should sort by date descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('date', false), + }) + .then((res) => + expect(res.objects.map((o) => o.properties.date)).toEqual([ + new Date('2021-01-04'), + new Date('2021-01-03'), + new Date('2021-01-02'), + new Date('2021-01-01'), + ]) + ) + ) + ); + }); + + it('should sort by boolean ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('isCool'), + }) + .then((res) => + expect(res.objects.map((o) => o.properties.isCool)).toEqual([false, false, false, true]) + ) + ) + ); + }); + + it('should sort by boolean descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('isCool', false), + }) + .then((res) => + expect(res.objects.map((o) => o.properties.isCool)).toEqual([true, false, false, false]) + ) + ) + ); + }); + + it('should sort with nullable ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('nullable'), + }) + .then((res) => + expect(res.objects.map((o) => o.properties.nullable)).toEqual([ + undefined, + undefined, + undefined, + 'oi', + ]) + ) + ) + ); + }); + + it('should sort with nullable descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byProperty('nullable', false), + }) + .then((res) => + expect(res.objects.map((o) => o.properties.nullable)).toEqual([ + 'oi', + undefined, + undefined, + undefined, + ]) + ) + ) + ); + }); + + it('should sort by id ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byId(), + }) + .then((res) => expect(res.objects.map((o) => o.uuid)).toEqual(ids)) + ) + ); + }); + + it('should sort by id descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byId(false), + }) + .then((res) => expect(res.objects.map((o) => o.uuid)).toEqual(ids.slice().reverse())) + ) + ); + }); + + it('should sort by creation time ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byCreationTime(), + returnMetadata: ['creationTime'], + }) + .then((res) => + expect(res.objects.map((o) => o.metadata?.creationTime?.getTime()!)).toEqual( + res.objects.map((o) => o.metadata?.creationTime?.getTime()!).sort((a, b) => a - b) + ) + ) + ) + ); + }); + + it('should sort by creation time descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byCreationTime(false), + returnMetadata: ['creationTime'], + }) + .then((res) => + expect(res.objects.map((o) => o.metadata?.updateTime?.getTime()!)).toEqual( + res.objects.map((o) => o.metadata?.updateTime?.getTime()!).sort((a, b) => b - a) + ) + ) + ) + ); + }); + + it('should sort by update time ascending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byUpdateTime(), + returnMetadata: ['updateTime'], + }) + .then((res) => + expect(res.objects.map((o) => o.metadata?.updateTime?.getTime()!)).toEqual( + res.objects.map((o) => o.metadata?.updateTime?.getTime()!).sort((a, b) => a - b) + ) + ) + ) + ); + }); + + it('should sort by update time descending', async () => { + await Promise.all( + collections.map((c) => + c.query + .fetchObjects({ + sort: collection.sort.byUpdateTime(false), + returnMetadata: ['updateTime'], + }) + .then((res) => + expect(res.objects.map((o) => o.metadata?.updateTime?.getTime()!)).toEqual( + res.objects.map((o) => o.metadata?.updateTime?.getTime()!).sort((a, b) => b - a) + ) + ) + ) + ); + }); +}); diff --git a/src/collections/sort/types.ts b/src/collections/sort/types.ts new file mode 100644 index 00000000..e78a4131 --- /dev/null +++ b/src/collections/sort/types.ts @@ -0,0 +1,16 @@ +import { NonRefKeys } from '../types/internal.js'; +import { Sorting } from './classes.js'; + +/** + * Define how the query's sort operation should be performed using the available methods. + */ +export interface Sort { + /** Sort by an object property. */ + byProperty>(property: K, ascending?: boolean): Sorting; + /** Sort by the objects' ID. */ + byId(ascending?: boolean): Sorting; + /** Sort by the objects' creation time. */ + byCreationTime(ascending?: boolean): Sorting; + /** Sort by the objects' last update time. */ + byUpdateTime(ascending?: boolean): Sorting; +} diff --git a/src/collections/tenants/index.ts b/src/collections/tenants/index.ts new file mode 100644 index 00000000..1e3994da --- /dev/null +++ b/src/collections/tenants/index.ts @@ -0,0 +1,155 @@ +import { ConnectionGRPC } from '../../connection/index.js'; +import { WeaviateUnsupportedFeatureError } from '../../errors.js'; +import { TenantActivityStatus, TenantsGetReply } from '../../proto/v1/tenants.js'; +import { TenantsCreator, TenantsDeleter, TenantsGetter, TenantsUpdater } from '../../schema/index.js'; +import { DbVersionSupport } from '../../utils/dbVersion.js'; + +export type Tenant = { + name: string; + activityStatus?: 'COLD' | 'HOT'; +}; + +export type TenantsGetOptions = { + tenants?: string; +}; + +class ActivityStatusMapper { + static from(status: TenantActivityStatus): 'COLD' | 'HOT' { + switch (status) { + case TenantActivityStatus.TENANT_ACTIVITY_STATUS_COLD: + return 'COLD'; + case TenantActivityStatus.TENANT_ACTIVITY_STATUS_HOT: + return 'HOT'; + default: + throw new Error(`Unsupported tenant activity status: ${status}`); + } + } +} + +const mapReply = (reply: TenantsGetReply): Record => { + const tenants: Record = {}; + reply.tenants.forEach((t) => { + tenants[t.name] = { + name: t.name, + activityStatus: ActivityStatusMapper.from(t.activityStatus), + }; + }); + return tenants; +}; + +const checkSupportForGRPCTenantsGetEndpoint = async (dbVersionSupport: DbVersionSupport) => { + const check = await dbVersionSupport.supportsTenantsGetGRPCMethod(); + if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); +}; + +const parseTenantOrTenantArray = (tenants: Tenant | Tenant[]) => + Array.isArray(tenants) ? tenants : [tenants]; + +const parseStringOrTenant = (tenant: string | Tenant) => (typeof tenant === 'string' ? tenant : tenant.name); + +const tenants = ( + connection: ConnectionGRPC, + collection: string, + dbVersionSupport: DbVersionSupport +): Tenants => { + const getGRPC = (names?: string[]) => + checkSupportForGRPCTenantsGetEndpoint(dbVersionSupport) + .then(() => connection.tenants(collection)) + .then((builder) => builder.withGet({ names })) + .then(mapReply); + const getREST = () => + new TenantsGetter(connection, collection).do().then((tenants) => { + const result: Record = {}; + tenants.forEach((tenant) => { + if (!tenant.name) return; + result[tenant.name] = tenant as Tenant; + }); + return result; + }); + return { + create: (tenants: Tenant | Tenant[]) => + new TenantsCreator(connection, collection, parseTenantOrTenantArray(tenants)).do() as Promise, + get: async function () { + const check = await dbVersionSupport.supportsTenantsGetGRPCMethod(); + return check.supports ? getGRPC() : getREST(); + }, + getByNames: (tenants: (string | Tenant)[]) => getGRPC(tenants.map(parseStringOrTenant)), + getByName: (tenant: string | Tenant) => { + const tenantName = parseStringOrTenant(tenant); + return getGRPC([tenantName]).then((tenants) => tenants[tenantName] || null); + }, + remove: (tenants: Tenant | Tenant[]) => + new TenantsDeleter( + connection, + collection, + parseTenantOrTenantArray(tenants).map((t) => t.name) + ).do(), + update: (tenants: Tenant | Tenant[]) => + new TenantsUpdater(connection, collection, parseTenantOrTenantArray(tenants)).do() as Promise, + }; +}; + +export default tenants; + +/** + * Represents all the CRUD methods available on a collection's multi-tenancy specification within Weaviate. + + * The collection must have been created with multi-tenancy enabled in order to use any of these methods. This class + * should not be instantiated directly, but is available as a property of the `Collection` class under + * the `collection.tenants` class attribute. + */ +export interface Tenants { + /** + * Create the specified tenants for a collection in Weaviate. + * + * The collection must have been created with multi-tenancy enabled. + * + * @param {Tenant | Tenant[]} tenants The tenant or tenants to create. + * @returns {Promise} The created tenant(s) as a list of Tenant. + */ + create: (tenants: Tenant | Tenant[]) => Promise; + /** + * Return all tenants currently associated with a collection in Weaviate. + * + * The collection must have been created with multi-tenancy enabled. + * + * @returns {Promise>} A list of tenants as an object of Tenant types, where the key is the tenant name. + */ + get: () => Promise>; + /** + * Return the specified tenants from a collection in Weaviate. + * + * The collection must have been created with multi-tenancy enabled. + * + * @param {(string | Tenant)[]} names The tenants to retrieve. + * @returns {Promise} The list of tenants. If the tenant does not exist, it will not be included in the list. + */ + getByNames: (names: (string | Tenant)[]) => Promise>; + /** + * Return the specified tenant from a collection in Weaviate. + * + * The collection must have been created with multi-tenancy enabled. + * + * @param {string | Tenant} name The name of the tenant to retrieve. + * @returns {Promise} The tenant as a Tenant type, or null if the tenant does not exist. + */ + getByName: (name: string | Tenant) => Promise; + /** + * Remove the specified tenants from a collection in Weaviate. + * + * The collection must have been created with multi-tenancy enabled. + * + * @param {Tenant | Tenant[]} tenants The tenant or tenants to remove. + * @returns {Promise} An empty promise. + */ + remove: (tenants: Tenant | Tenant[]) => Promise; + /** + * Update the specified tenants for a collection in Weaviate. + * + * The collection must have been created with multi-tenancy enabled. + * + * @param {Tenant | Tenant[]} tenants The tenant or tenants to update. + * @returns {Promise} The updated tenant(s) as a list of Tenant. + */ + update: (tenants: Tenant | Tenant[]) => Promise; +} diff --git a/src/collections/tenants/integration.test.ts b/src/collections/tenants/integration.test.ts new file mode 100644 index 00000000..96542124 --- /dev/null +++ b/src/collections/tenants/integration.test.ts @@ -0,0 +1,147 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { WeaviateUnsupportedFeatureError } from '../../errors.js'; +import weaviate, { WeaviateClient } from '../../index.js'; +import { Collection } from '../collection/index.js'; + +describe('Testing of the collection.tenants methods', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionTenants'; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.get(collectionName); + return client.collections + .create({ + name: collectionName, + multiTenancy: weaviate.configure.multiTenancy({ enabled: true }), + }) + .then(() => + collection.tenants.create([ + { name: 'hot', activityStatus: 'HOT' }, + { name: 'cold', activityStatus: 'COLD' }, + { name: 'remove-me', activityStatus: 'HOT' }, + ]) + ); + }); + + it('should be able to create a tenant', async () => { + const tenant = 'tenant'; + const result = await collection.tenants.create([{ name: tenant, activityStatus: 'HOT' }]); + expect(result.length).toBe(1); + expect(result[0].name).toBe(tenant); + expect(result[0].activityStatus).toBe('HOT'); + }); + + it('should be able to get existing tenants', async () => { + const result = await collection.tenants.get(); + + expect(result).toHaveProperty('hot'); + expect(result.hot.name).toBe('hot'); + expect(result.hot.activityStatus).toBe('HOT'); + + expect(result).toHaveProperty('cold'); + expect(result.cold.name).toBe('cold'); + // expect(result.tenant.activityStatus).toBe('COLD'); // updated below + }); + + it('should be able to remove a tenant', async () => { + const result = await collection.tenants + .remove([{ name: 'remove-me' }]) + .then(() => collection.tenants.get()); + expect(result).not.toHaveProperty('remove-me'); + }); + + it('should be able to update a tenant', async () => { + const result = await collection.tenants.update([{ name: 'cold', activityStatus: 'HOT' }]); + expect(result.length).toBe(1); + expect(result[0].name).toBe('cold'); + expect(result[0].activityStatus).toBe('HOT'); + }); + + describe('getByName and getByNames', () => { + it('should be able to get a tenant by name string', async () => { + const query = () => collection.tenants.getByName('hot'); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); + expect(result).toHaveProperty('name', 'hot'); + expect(result).toHaveProperty('activityStatus', 'HOT'); + }); + + it('should be able to get a tenant by tenant object', async () => { + const query = () => collection.tenants.getByName({ name: 'hot' }); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); + expect(result).toHaveProperty('name', 'hot'); + expect(result).toHaveProperty('activityStatus', 'HOT'); + }); + + it('should fail to get a non-existing tenant', async () => { + const query = () => collection.tenants.getByName('non-existing'); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); + expect(result).toBeNull(); + }); + + it('should be able to get tenants by name strings', async () => { + const query = () => collection.tenants.getByNames(['hot', 'cold']); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); + expect(result).toHaveProperty('hot'); + expect(result).toHaveProperty('cold'); + }); + + it('should be able to get tenants by tenant objects', async () => { + const query = () => collection.tenants.getByNames([{ name: 'hot' }, { name: 'cold' }]); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); + expect(result).toHaveProperty('hot'); + expect(result).toHaveProperty('cold'); + }); + + it('should be able to get tenants by mixed name strings and tenant objects', async () => { + const query = () => collection.tenants.getByNames(['hot', { name: 'cold' }]); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); + expect(result).toHaveProperty('hot'); + expect(result).toHaveProperty('cold'); + }); + + it('should be able to get partial tenants', async () => { + const query = () => collection.tenants.getByNames(['hot', 'non-existing']); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { + await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); + return; + } + const result = await query(); + expect(result).toHaveProperty('hot'); + expect(result).not.toHaveProperty('cold'); + expect(result).not.toHaveProperty('non-existing'); + }); + }); +}); diff --git a/src/collections/types/batch.ts b/src/collections/types/batch.ts new file mode 100644 index 00000000..0048cb41 --- /dev/null +++ b/src/collections/types/batch.ts @@ -0,0 +1,43 @@ +import { BatchReference } from '../../openapi/types.js'; +import { BatchObject as BatchObjectGRPC } from '../../proto/v1/batch.js'; +import { NonReferenceInputs, ReferenceInputs, Vectors } from '../index.js'; + +export type BatchObjectsReturn = { + allResponses: (string | ErrorObject)[]; + elapsedSeconds: number; + errors: Record>; + hasErrors: boolean; + uuids: Record; +}; + +export type ErrorObject = { + code?: number; + message: string; + object: BatchObject; + originalUuid?: string; +}; + +export type BatchObject = { + collection: string; + properties?: NonReferenceInputs; + references?: ReferenceInputs; + id?: string; + vectors?: number[] | Vectors; + tenant?: string; +}; + +export type BatchObjects = { + batch: BatchObject[]; + mapped: BatchObjectGRPC[]; +}; + +export type ErrorReference = { + message: string; + reference: BatchReference; +}; + +export type BatchReferencesReturn = { + elapsedSeconds: number; + errors: Record; + hasErrors: boolean; +}; diff --git a/src/collections/types/data.ts b/src/collections/types/data.ts new file mode 100644 index 00000000..0a4d33be --- /dev/null +++ b/src/collections/types/data.ts @@ -0,0 +1,27 @@ +import { NonReferenceInputs, ReferenceInputs } from './internal.js'; +import { Vectors } from './query.js'; + +export type DataObject = { + id?: string; + properties?: NonReferenceInputs; + references?: ReferenceInputs; + vectors?: number[] | Vectors; +}; + +export type DeleteManyObject = { + id: string; + successful: boolean; + error?: string; +}; + +export type DeleteManyReturn = { + failed: number; + matches: number; + objects: V extends true ? DeleteManyObject[] : undefined; + successful: number; +}; + +export type ReferenceToMultiTarget = { + targetCollection: string; + uuids: string | string[]; +}; diff --git a/src/collections/types/generate.ts b/src/collections/types/generate.ts new file mode 100644 index 00000000..b3f6bac2 --- /dev/null +++ b/src/collections/types/generate.ts @@ -0,0 +1,54 @@ +import { GroupByObject, GroupByResult, WeaviateGenericObject, WeaviateNonGenericObject } from './query.js'; + +export type GenerativeGenericObject = WeaviateGenericObject & { + /** The LLM-generated output applicable to this single object. */ + generated?: string; +}; + +export type GenerativeNonGenericObject = WeaviateNonGenericObject & { + /** The LLM-generated output applicable to this single object. */ + generated?: string; +}; + +/** An object belonging to a collection as returned by the methods in the `collection.generate` namespace. + * + * Depending on the generic type `T`, the object will have subfields that map from `T`'s specific type definition. + * If not, then the object will be non-generic and have a `properties` field that maps from a generic string to a `WeaviateField`. + */ +export type GenerativeObject = T extends undefined + ? GenerativeNonGenericObject + : GenerativeGenericObject; + +/** The return of a query method in the `collection.generate` namespace. */ +export type GenerativeReturn = { + /** The objects that were found by the query. */ + objects: GenerativeObject[]; + /** The LLM-generated output applicable to this query as a whole. */ + generated?: string; +}; + +export type GenerativeGroupByResult = GroupByResult & { + generated?: string; +}; + +/** The return of a query method in the `collection.generate` namespace where the `groupBy` argument was specified. */ +export type GenerativeGroupByReturn = { + /** The objects that were found by the query. */ + objects: GroupByObject[]; + /** The groups that were created by the query. */ + groups: Record>; + /** The LLM-generated output applicable to this query as a whole. */ + generated?: string; +}; + +/** Options available when defining queries using methods in the `collection.generate` namespace. */ +export type GenerateOptions = { + /** The prompt to use when generating content relevant to each object of the collection individually. */ + singlePrompt?: string; + /** The prompt to use when generating content relevant to objects returned by the query as a whole. */ + groupedTask?: string; + /** The properties to use as context to be injected into the `groupedTask` prompt when performing the grouped generation. */ + groupedProperties?: T extends undefined ? string[] : (keyof T)[]; +}; + +export type GenerateReturn = Promise> | Promise>; diff --git a/src/collections/types/index.ts b/src/collections/types/index.ts new file mode 100644 index 00000000..406eaa09 --- /dev/null +++ b/src/collections/types/index.ts @@ -0,0 +1,98 @@ +export * from '../config/types/index.js'; +export * from '../configure/types/index.js'; +export type { CollectionConfigCreate } from '../index.js'; +export * from './batch.js'; +export * from './data.js'; +export * from './generate.js'; +export type { + IsEmptyType, + IsNestedField, + IsPrimitiveField, + IsWeaviateField, + NestedKeys, + NonRefKeys, + NonReferenceInputs, + PrimitiveKeys, + QueryNested, + QueryProperty, + QueryReference, + RefKeys, + ReferenceInput, + ReferenceInputs, +} from './internal.js'; +export * from './query.js'; + +import { + GeoCoordinate as GeoCoordinateGRPC, + PhoneNumber as PhoneNumberGRPC, +} from '../../proto/v1/properties.js'; + +import { CrossReference } from '../references/index.js'; + +// The order of type resolution is important here since object can be inferred as all other types +// hence it should be the last type in the union +export type DataType = T extends infer U | undefined + ? U extends string + ? 'text' | 'uuid' | 'blob' + : U extends number + ? 'number' | 'int' + : U extends boolean + ? 'boolean' + : U extends Date + ? 'date' + : U extends string[] + ? 'text[]' | 'uuid[]' + : U extends number[] + ? 'number[]' | 'int[]' + : U extends boolean[] + ? 'boolean[]' + : U extends Date[] + ? 'date[]' + : U extends GeoCoordinate + ? 'geoCoordinates' + : U extends PhoneNumber + ? 'phoneNumber' + : U extends object[] + ? 'object[]' + : U extends object + ? 'object' + : never + : never; + +export type GeoCoordinate = Required; + +export type PhoneNumber = Required; + +export type PrimitiveField = + | string + | string[] + | boolean + | boolean[] + | number + | number[] + | Date + | Date[] + | Blob + | GeoCoordinate + | PhoneNumber + | PhoneNumberInput + | null; + +export type NestedField = NestedProperties | NestedProperties[]; + +export type WeaviateField = PrimitiveField | NestedField; + +export type Property = WeaviateField | CrossReference | undefined; + +export interface Properties { + [k: string]: Property; +} + +export interface NestedProperties { + [k: string]: WeaviateField; +} + +export type PhoneNumberInput = { + number: string; + defaultCountry?: string; +}; diff --git a/src/collections/types/internal.ts b/src/collections/types/internal.ts new file mode 100644 index 00000000..a935a789 --- /dev/null +++ b/src/collections/types/internal.ts @@ -0,0 +1,126 @@ +import { + NestedProperties, + PhoneNumber, + PhoneNumberInput, + PrimitiveField, + RefProperty, + RefPropertyDefault, + ReferenceToMultiTarget, + WeaviateField, +} from '../index.js'; +import { ReferenceManager } from '../references/classes.js'; +import { CrossReference } from '../references/index.js'; + +export type ExtractCrossReferenceType = T extends CrossReference ? U : never; + +type ExtractNestedType = T extends (infer U)[] + ? U extends NestedProperties + ? U + : never + : T extends NestedProperties + ? T + : never; + +export type QueryNested = { + [K in NestedKeys]: { + name: K; + properties: QueryProperty>[]; + }; +}[NestedKeys]; + +export type QueryNestedDefault = { + name: string; + properties: (string | QueryNestedDefault)[]; +}; + +export type QueryProperty = T extends undefined + ? string | QueryNestedDefault + : PrimitiveKeys | QueryNested; +export type QueryReference = T extends undefined ? RefPropertyDefault : RefProperty; +export type NonRefProperty = keyof T | QueryNested; +export type NonPrimitiveProperty = RefProperty | QueryNested; + +export type IsEmptyType = keyof T extends never ? true : false; + +export type ReferenceInput = + | string + | ReferenceToMultiTarget + | ReferenceManager + | (string | ReferenceToMultiTarget | ReferenceManager)[]; + +export type ReferenceInputs = Obj extends undefined + ? Record> + : { + [Key in keyof Obj as Key extends RefKeys ? Key : never]: ReferenceInput< + ExtractCrossReferenceType + >; + }; + +export type IsPrimitiveField = T extends PrimitiveField ? T : never; + +export type IsWeaviateField = T extends WeaviateField ? T : never; + +export type IsNestedField = T extends NestedProperties | NestedProperties[] ? T : never; + +/** + * This is an internal type that is used to extract the keys of a user-provided generic type that are primitive fields, e.g. non-nested and non-reference. + */ +export type PrimitiveKeys = Obj extends undefined + ? string + : { + [Key in keyof Obj]-?: undefined extends Obj[Key] + ? IsPrimitiveField> extends never + ? never + : Key + : IsPrimitiveField extends never + ? never + : Key; + }[keyof Obj] & + string; + +/** + * This is an internal type that is used to extract the keys of a user-provided generic type that are references. + */ +export type RefKeys = { + [Key in keyof Obj]: Obj[Key] extends CrossReference | undefined ? Key : never; +}[keyof Obj] & + string; + +/** + * This is an internal type that is used to extract the keys of a user-provided generic type that are not references. + */ +export type NonRefKeys = { + [Key in keyof Obj]-?: undefined extends Obj[Key] + ? IsWeaviateField> extends never + ? never + : Key + : IsWeaviateField extends never + ? never + : Key; +}[keyof Obj] & + string; + +/** + * This is an internal type that is used to extract the keys of a user-provided generic type that are nested properties. + */ +export type NestedKeys = { + [Key in keyof Obj]-?: undefined extends Obj[Key] + ? IsNestedField> extends never + ? never + : Key + : IsNestedField extends never + ? never + : Key; +}[keyof Obj] & + string; + +/** + * This is an internal type that is used to extract the allowed inputs for a non-generic type that is not a reference. + */ +export type NonReferenceInputs = Obj extends undefined + ? Record + : { + [Key in keyof Obj as Key extends NonRefKeys ? Key : never]: MapPhoneNumberType; + }; + +export type MapPhoneNumberType = T extends PhoneNumber ? PhoneNumberInput : T; diff --git a/src/collections/types/query.ts b/src/collections/types/query.ts new file mode 100644 index 00000000..c356aa38 --- /dev/null +++ b/src/collections/types/query.ts @@ -0,0 +1,157 @@ +import { WeaviateField } from '../index.js'; +import { CrossReferenceDefault } from '../references/index.js'; +import { + ExtractCrossReferenceType, + NonRefKeys, + QueryNestedDefault, + QueryProperty, + QueryReference, + RefKeys, +} from './internal.js'; + +export type Metadata = { + creationTime: Date; + updateTime: Date; + distance: number; + certainty: number; + score: number; + explainScore: string; + rerankScore: number; + isConsistent: boolean; +}; + +export type MetadataKeys = (keyof Metadata)[]; + +export type QueryMetadata = 'all' | MetadataKeys | undefined; + +export type ReturnMetadata = Partial; + +export type WeaviateGenericObject = { + /** The generic returned properties of the object derived from the type `T`. */ + properties: ReturnProperties; + /** The returned metadata of the object. */ + metadata: ReturnMetadata | undefined; + /** The returned references of the object derived from the type `T`. */ + references: ReturnReferences | undefined; + /** The UUID of the object. */ + uuid: string; + /** The returned vectors of the object. */ + vectors: Vectors; +}; + +export type WeaviateNonGenericObject = { + /** The returned properties of the object. */ + properties: Record; + /** The returned metadata of the object. */ + metadata: ReturnMetadata | undefined; + /** The returned references of the object. */ + references: Record | undefined; + /** The UUID of the object. */ + uuid: string; + /** The returned vectors of the object. */ + vectors: Vectors; +}; + +export type ReturnProperties = Pick>; + +export type ReturnReferences = Pick>; + +export type Vectors = Record; + +export type ReturnVectors = V extends string[] + ? { [Key in V[number]]: number[] } + : Record; + +/** An object belonging to a collection as returned by the methods in the `collection.query` namespace. + * + * Depending on the generic type `T`, the object will have subfields that map from `T`'s specific type definition. + * If not, then the object will be non-generic and have a `properties` field that maps from a generic string to a `WeaviateField`. + */ +export type WeaviateObject = T extends undefined // need this instead of Properties to avoid circular type reference + ? WeaviateNonGenericObject + : WeaviateGenericObject; + +/** The return of a query method in the `collection.query` namespace. */ +export type WeaviateReturn = { + /** The objects that were found by the query. */ + objects: WeaviateObject[]; +}; + +export type GroupByObject = WeaviateObject & { + belongsToGroup: string; +}; + +export type GroupByResult = { + name: string; + minDistance: number; + maxDistance: number; + numberOfObjects: number; + objects: WeaviateObject[]; +}; + +/** The return of a query method in the `collection.query` namespace where the `groupBy` argument was specified. */ +export type GroupByReturn = { + /** The objects that were found by the query. */ + objects: GroupByObject[]; + /** The groups that were created by the query. */ + groups: Record>; +}; + +export type GroupByOptions = T extends undefined + ? { + property: string; + numberOfGroups: number; + objectsPerGroup: number; + } + : { + property: keyof T; + numberOfGroups: number; + objectsPerGroup: number; + }; + +export type RerankOptions = T extends undefined + ? { + property: string; + query: string; + } + : { + property: keyof T; + query?: string; + }; + +export interface BaseRefProperty { + /** The property to link on when defining the references traversal. */ + linkOn: RefKeys; + /** Whether to return the vector(s) of the referenced objects in the query. */ + includeVector?: boolean | string[]; + /** The metadata to return for the referenced objects. */ + returnMetadata?: QueryMetadata; + /** The properties to return for the referenced objects. */ + returnProperties?: QueryProperty[]; + /** The references to return for the referenced objects. */ + returnReferences?: QueryReference>[]; + /** The collection to target when traversing the references. Required for multi-target references. */ + targetCollection?: string; +} + +export type RefProperty = BaseRefProperty; + +export type RefPropertyDefault = { + /** The property to link on when defining the references traversal. */ + linkOn: string; + /** Whether to return the vector(s) of the referenced objects in the query. */ + includeVector?: boolean | string[]; + /** The metadata to return for the referenced objects. */ + returnMetadata?: QueryMetadata; + /** The properties to return for the referenced objects. */ + returnProperties?: (string | QueryNestedDefault)[]; + /** The references to return for the referenced objects. */ + returnReferences?: RefPropertyDefault[]; + /** The collection to target when traversing the references. Required for multi-target references. */ + targetCollection?: string; +}; + +export type SortBy = { + property: string; + ascending?: boolean; +}; diff --git a/src/connection/auth.ts b/src/connection/auth.ts index afba8755..bd137c4c 100644 --- a/src/connection/auth.ts +++ b/src/connection/auth.ts @@ -1,4 +1,31 @@ -import { HttpClient } from './httpClient'; +import { HttpClient } from './http.js'; + +/** + * The allowed authentication credentials. See [the docs](https://weaviate.io/developers/weaviate/configuration/authentication) for more information. + * + * The following types are allowed: + * - `AuthUserPasswordCredentials` + * - `AuthAccessTokenCredentials` + * - `AuthClientCredentials` + * - `ApiKey` + * - `string` + * + * A string is interpreted as an API key. + */ +export type AuthCredentials = + | AuthUserPasswordCredentials + | AuthAccessTokenCredentials + | AuthClientCredentials + | ApiKey + | string; + +export const isApiKey = (creds?: AuthCredentials): creds is ApiKey | string => { + return typeof creds === 'string' || creds instanceof ApiKey; +}; + +export const mapApiKey = (creds: ApiKey | string): ApiKey => { + return creds instanceof ApiKey ? creds : new ApiKey(creds); +}; interface AuthenticatorResult { accessToken: string; diff --git a/src/connection/gql.ts b/src/connection/gql.ts new file mode 100644 index 00000000..251f305a --- /dev/null +++ b/src/connection/gql.ts @@ -0,0 +1,54 @@ +import { GraphQLClient as Client, Variables } from 'graphql-request'; +import ConnectionREST, { InternalConnectionParams } from './http.js'; + +export default class ConnectionGQL extends ConnectionREST { + private gql: GraphQLClient; + + constructor(params: InternalConnectionParams) { + super(params); + this.gql = gqlClient(params); + } + + query = (query: any, variables?: V) => { + if (this.authEnabled) { + return this.login().then((token) => { + const headers = { Authorization: `Bearer ${token}` }; + return this.gql.query(query, variables, headers); + }); + } + return this.gql.query(query, variables); + }; + + close = () => this.http.close(); +} + +export * from './auth.js'; + +export type TQuery = any; +export interface GraphQLClient { + query: ( + query: TQuery, + variables?: V, + headers?: HeadersInit + ) => Promise<{ data: T }>; +} + +export const gqlClient = (config: InternalConnectionParams): GraphQLClient => { + const version = '/v1/graphql'; + const baseUri = `${config.host}${version}`; + const defaultHeaders = config.headers; + return { + // for backward compatibility with replaced graphql-client lib, + // results are wrapped into { data: data } + query: (query: TQuery, variables?: V, headers?: HeadersInit) => { + return new Client(baseUri, { + headers: { + ...defaultHeaders, + ...headers, + }, + }) + .request(query, variables, headers) + .then((data) => ({ data })); + }, + }; +}; diff --git a/src/connection/gqlClient.ts b/src/connection/gqlClient.ts deleted file mode 100644 index 74874a41..00000000 --- a/src/connection/gqlClient.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { GraphQLClient as Client, Variables } from 'graphql-request'; -import { ConnectionParams } from '..'; - -export type TQuery = any; -export interface GraphQLClient { - query: (query: TQuery, variables?: Variables, headers?: HeadersInit) => Promise<{ data: any }>; -} - -export const gqlClient = (config: ConnectionParams): GraphQLClient => { - const defaultHeaders = config.headers; - const version = '/v1/graphql'; - const baseUri = `${config.host}${version}`; - - return { - // for backward compatibility with replaced graphql-client lib, - // results are wrapped into { data: data } - query: (query: TQuery, variables?: Variables, headers?: HeadersInit) => { - return new Client(baseUri, { - headers: { - ...defaultHeaders, - ...headers, - }, - fetch, - }) - .request(query, variables, headers) - .then((data) => ({ data })); - }, - }; -}; - -export default gqlClient; diff --git a/src/connection/grpc.ts b/src/connection/grpc.ts new file mode 100644 index 00000000..d8195692 --- /dev/null +++ b/src/connection/grpc.ts @@ -0,0 +1,184 @@ +import { isAbortError } from 'abort-controller-x'; + +import ConnectionGQL from './gql.js'; +import { InternalConnectionParams } from './http.js'; + +import { ConsistencyLevel } from '../data/index.js'; + +import { ChannelCredentials, ChannelOptions, createChannel, createClientFactory, Metadata } from 'nice-grpc'; +import { deadlineMiddleware } from 'nice-grpc-client-middleware-deadline'; + +import { HealthCheckResponse_ServingStatus, HealthDefinition } from '../proto/google/health/v1/health.js'; +import { WeaviateDefinition } from '../proto/v1/weaviate.js'; + +import Batcher, { Batch } from '../grpc/batcher.js'; +import Searcher, { Search } from '../grpc/searcher.js'; +import TenantsManager, { Tenants } from '../grpc/tenantsManager.js'; +import { DbVersionSupport, initDbVersionProvider } from '../utils/dbVersion.js'; + +import { WeaviateGRPCUnavailableError, WeaviateUnsupportedFeatureError } from '../errors.js'; + +export interface GrpcConnectionParams extends InternalConnectionParams { + grpcAddress: string; + grpcSecure: boolean; +} + +const clientFactory = createClientFactory().use(deadlineMiddleware); + +const MAX_GRPC_MESSAGE_LENGTH = 104858000; // 10mb, needs to be synchronized with GRPC server + +// Must extend from ConnectionGQL so that it can be passed to all the builder methods, +// which are tightly coupled to ConnectionGQL +export default class ConnectionGRPC extends ConnectionGQL { + private grpc: GrpcClient; + private grpcAddress: string; + + private constructor(params: GrpcConnectionParams) { + super(params); + this.grpc = grpcClient(params); + this.grpcAddress = params.grpcAddress; + } + + static use = async (params: GrpcConnectionParams) => { + const connection = new ConnectionGRPC(params); + const dbVersionProvider = initDbVersionProvider(connection); + const dbVersionSupport = new DbVersionSupport(dbVersionProvider); + if (params.skipInitChecks) { + return { connection, dbVersionProvider, dbVersionSupport }; + } + await Promise.all([ + dbVersionSupport.supportsCompatibleGrpcService().then((check) => { + if (!check.supports) { + throw new WeaviateUnsupportedFeatureError( + `Checking for gRPC compatibility failed with message: ${check.message}` + ); + } + }), + connection.connect(), + ]); + return { connection, dbVersionProvider, dbVersionSupport }; + }; + + private async connect() { + const isHealthy = await this.grpc.health(); + if (!isHealthy) { + await this.close(); + throw new WeaviateGRPCUnavailableError(this.grpcAddress); + } + } + + search = (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string) => { + if (this.authEnabled) { + return this.login().then((token) => + this.grpc.search(collection, consistencyLevel, tenant, `Bearer ${token}`) + ); + } + return new Promise((resolve) => resolve(this.grpc.search(collection, consistencyLevel, tenant))); + }; + + batch = (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string) => { + if (this.authEnabled) { + return this.login().then((token) => + this.grpc.batch(collection, consistencyLevel, tenant, `Bearer ${token}`) + ); + } + return new Promise((resolve) => resolve(this.grpc.batch(collection, consistencyLevel, tenant))); + }; + + tenants = (collection: string) => { + if (this.authEnabled) { + return this.login().then((token) => this.grpc.tenants(collection, `Bearer ${token}`)); + } + return new Promise((resolve) => resolve(this.grpc.tenants(collection))); + }; + + close = () => { + this.grpc.close(); + this.http.close(); + }; +} + +export interface GrpcClient { + close: () => void; + batch: ( + collection: string, + consistencyLevel?: ConsistencyLevel, + tenant?: string, + bearerToken?: string + ) => Batch; + health: () => Promise; + search: ( + collection: string, + consistencyLevel?: ConsistencyLevel, + tenant?: string, + bearerToken?: string + ) => Search; + tenants: (collection: string, bearerToken?: string) => Tenants; +} + +export const grpcClient = (config: GrpcConnectionParams): GrpcClient => { + const channelOptions: ChannelOptions = { + 'grpc.max_send_message_length': MAX_GRPC_MESSAGE_LENGTH, + 'grpc.max_receive_message_length': MAX_GRPC_MESSAGE_LENGTH, + }; + if (config.grpcProxyUrl) { + // grpc.http_proxy is not used by grpc.js under-the-hood + // only uses the env var and whether http_proxy is enabled + process.env.grpc_proxy = config.grpcProxyUrl; + channelOptions['grpc.enabled_http_proxy'] = true; + } + const channel = createChannel( + config.grpcAddress, + config.grpcSecure ? ChannelCredentials.createSsl() : ChannelCredentials.createInsecure(), + channelOptions + ); + const client = clientFactory.create(WeaviateDefinition, channel); + const health = clientFactory.create(HealthDefinition, channel); + return { + close: () => channel.close(), + batch: (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string, bearerToken?: string) => + Batcher.use( + client, + collection, + new Metadata(bearerToken ? { ...config.headers, authorization: bearerToken } : config.headers), + config.timeout?.insert || 90, + consistencyLevel, + tenant + ), + health: () => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), (config.timeout?.init || 2) * 1000); + return health + .check({ service: '/grpc.health.v1.Health/Check' }, { signal: controller.signal }) + .then((res) => res.status === HealthCheckResponse_ServingStatus.SERVING) + .catch((err) => { + if (isAbortError(err)) { + throw new WeaviateGRPCUnavailableError(config.grpcAddress); + } + throw err; + }) + .finally(() => clearTimeout(timeoutId)); + }, + search: ( + collection: string, + consistencyLevel?: ConsistencyLevel, + tenant?: string, + bearerToken?: string + ) => + Searcher.use( + client, + collection, + new Metadata(bearerToken ? { ...config.headers, authorization: bearerToken } : config.headers), + config.timeout?.query || 30, + consistencyLevel, + tenant + ), + tenants: (collection: string, bearerToken?: string) => + TenantsManager.use( + client, + collection, + new Metadata(bearerToken ? { ...config.headers, authorization: bearerToken } : config.headers), + config.timeout?.query || 30 + ), + }; +}; diff --git a/src/connection/helpers.test.ts b/src/connection/helpers.test.ts new file mode 100644 index 00000000..7e0f4276 --- /dev/null +++ b/src/connection/helpers.test.ts @@ -0,0 +1,32 @@ +import weaviate from '../index.js'; + +const WCD_URL = 'https://piblpmmdsiknacjnm1ltla.c1.europe-west3.gcp.weaviate.cloud'; +const WCD_KEY = 'cy4ua772mBlMdfw3YnclqAWzFhQt0RLIN0sl'; + +describe('Testing of the connection helper methods', () => { + it('should connect to a WCS cluster', () => { + return weaviate + .connectToWeaviateCloud(WCD_URL, { + authCredentials: new weaviate.ApiKey(WCD_KEY), + }) + .then((client) => client.getMeta()) + .then((res: any) => { + expect(res.version).toBeDefined(); + }) + .catch((e: any) => { + throw new Error('it should not have errord: ' + e); + }); + }); + + it('should connect to a local cluster', () => { + return weaviate + .connectToLocal() + .then((client) => client.getMeta()) + .then((res: any) => { + expect(res.version).toBeDefined(); + }) + .catch((e: any) => { + throw new Error('it should not have errord: ' + e); + }); + }); +}); diff --git a/src/connection/helpers.ts b/src/connection/helpers.ts new file mode 100644 index 00000000..cc28c0c5 --- /dev/null +++ b/src/connection/helpers.ts @@ -0,0 +1,157 @@ +import { WeaviateStartUpError } from '../errors.js'; +import { ClientParams, WeaviateClient } from '../index.js'; +import { AuthCredentials } from './auth.js'; +import { ProxiesParams, TimeoutParams } from './http.js'; + +/** The options available to the `weaviate.connectToWeaviateCloud` method. */ +export type ConnectToWeaviateCloudOptions = { + /** The authentication credentials to use when connecting to Weaviate, e.g. API key */ + authCredentials?: AuthCredentials; + /** Additional headers to include in the request */ + headers?: Record; + /** The timeouts to use when making requests to Weaviate */ + timeout?: TimeoutParams; + /** Whether to skip the initialization checks */ + skipInitChecks?: boolean; +}; + +/** @deprecated Use `ConnectToWeaviateCloudOptions` instead. */ +export type ConnectToWCDOptions = ConnectToWeaviateCloudOptions; + +/** @deprecated Use `ConnectToWeaviateCloudOptions` instead. */ +export type ConnectToWCSOptions = ConnectToWeaviateCloudOptions; + +export type ConnectToLocalOptions = { + /** The host where Weaviate is served. Assumes that the HTTP/1.1 and HTTP/2 servers are served on the same host */ + host?: string; + /** The port of the HTTP/1.1 server */ + port?: number; + /** The port of the HTTP/2 server */ + grpcPort?: number; + /** The authentication credentials to use when connecting to Weaviate, e.g. API key */ + authCredentials?: AuthCredentials; + /** Additional headers to include in the request */ + headers?: Record; + /** The timeouts to use when making requests to Weaviate */ + timeout?: TimeoutParams; + /** Whether to skip the initialization checks */ + skipInitChecks?: boolean; +}; + +export type ConnectToCustomOptions = { + /** The hostname of the HTTP/1.1 server */ + httpHost?: string; + /** An additional path of the HTTP/1.1 server, e.g. `http://proxy.net/weaviate` */ + httpPath?: string; + /** The port of the HTTP/1.1 server */ + httpPort?: number; + /** Whether to use a secure connection to the HTTP/1.1 server */ + httpSecure?: boolean; + /** The hostname of the HTTP/2 server */ + grpcHost?: string; + /** The port of the HTTP/2 server */ + grpcPort?: number; + /** Whether to use a secure connection to the HTTP/2 server */ + grpcSecure?: boolean; + /** The authentication credentials to use when connecting to Weaviate, e.g. API key */ + authCredentials?: AuthCredentials; + /** Additional headers to include in the request */ + headers?: Record; + /** The proxy configuration to use */ + proxies?: ProxiesParams; + /** The timeouts to use when making requests to Weaviate */ + timeout?: TimeoutParams; + /** Whether to skip the initialization checks */ + skipInitChecks?: boolean; +}; + +export function connectToWeaviateCloud( + clusterURL: string, + clientMaker: (params: ClientParams) => Promise, + options?: ConnectToWeaviateCloudOptions +): Promise { + // check if the URL is set + if (!clusterURL) throw new Error('Missing `clusterURL` parameter'); + + if (!clusterURL.startsWith('http')) { + clusterURL = `https://${clusterURL}`; + } + const url = new URL(clusterURL); + + let grpcHost: string; + if (url.hostname.endsWith('.weaviate.network')) { + const [ident, ...rest] = url.hostname.split('.'); + grpcHost = `${ident}.grpc.${rest.join('.')}`; + } else { + grpcHost = `grpc-${url.hostname}`; + } + + return clientMaker({ + connectionParams: { + http: { + secure: true, + host: url.hostname, + port: 443, + }, + grpc: { + secure: true, + host: grpcHost, + port: 443, + }, + }, + auth: options?.authCredentials, + headers: options?.headers, + }).catch((e) => { + throw new WeaviateStartUpError(`Weaviate failed to startup with message: ${e.message}`); + }); +} + +export function connectToLocal( + clientMaker: (params: ClientParams) => Promise, + options?: ConnectToLocalOptions +): Promise { + return clientMaker({ + connectionParams: { + http: { + secure: false, + host: options?.host || 'localhost', + port: options?.port || 8080, + }, + grpc: { + secure: false, + host: options?.host || 'localhost', + port: options?.grpcPort || 50051, + }, + }, + auth: options?.authCredentials, + headers: options?.headers, + }).catch((e) => { + throw new WeaviateStartUpError(`Weaviate failed to startup with message: ${e.message}`); + }); +} + +export function connectToCustom( + clientMaker: (params: ClientParams) => Promise, + options?: ConnectToCustomOptions +): Promise { + return clientMaker({ + connectionParams: { + http: { + secure: options?.httpSecure || false, + host: options?.httpHost || 'localhost', + path: options?.httpPath || '', + port: options?.httpPort || 8080, + }, + grpc: { + secure: options?.grpcSecure || false, + host: options?.grpcHost || 'localhost', + port: options?.grpcPort || 50051, + }, + }, + auth: options?.authCredentials, + headers: options?.headers, + proxies: options?.proxies, + }).catch((e) => { + throw new WeaviateStartUpError(`Weaviate failed to startup with message: ${e.message}`); + }); +} diff --git a/src/connection/http.ts b/src/connection/http.ts new file mode 100644 index 00000000..aa8b91f3 --- /dev/null +++ b/src/connection/http.ts @@ -0,0 +1,405 @@ +import { isAbortError } from 'abort-controller-x'; +import { Agent } from 'http'; + +import OpenidConfigurationGetter from '../misc/openidConfigurationGetter.js'; + +import { + WeaviateInvalidInputError, + WeaviateRequestTimeoutError, + WeaviateUnexpectedStatusCodeError, +} from '../errors.js'; +import { + ApiKey, + AuthAccessTokenCredentials, + AuthClientCredentials, + AuthUserPasswordCredentials, + OidcAuthenticator, +} from './auth.js'; + +/** + * You can only specify the gRPC proxy URL at this point in time. This is because ProxiesParams should be used to define tunnelling proxies + * and Weaviate does not support tunnelling proxies over HTTP/1.1 at this time. + * + * To use a forwarding proxy you should instead specify its URL as if it were the Weaviate instance itself. + */ +export type ProxiesParams = { + // http?: string; + // https?: string; + grpc?: string; +}; + +export type TimeoutParams = { + /** Define the configured timeout when querying data from Weaviate */ + query?: number; + /** Define the configured timeout when mutating data to Weaviate */ + insert?: number; + /** Define the configured timeout when initially connecting to Weaviate */ + init?: number; +}; + +export type InternalConnectionParams = { + authClientSecret?: AuthClientCredentials | AuthAccessTokenCredentials | AuthUserPasswordCredentials; + apiKey?: ApiKey; + host: string; + scheme?: string; + headers?: HeadersInit; + // http1Agent?: Agent; + grpcProxyUrl?: string; + agent?: Agent; + timeout?: TimeoutParams; + skipInitChecks?: boolean; +}; + +export default class ConnectionREST { + private apiKey?: string; + protected authEnabled: boolean; + public readonly host: string; + public readonly http: HttpClient; + public oidcAuth?: OidcAuthenticator; + + constructor(params: InternalConnectionParams) { + params = this.sanitizeParams(params); + this.host = params.host; + this.http = httpClient(params); + this.authEnabled = this.parseAuthParams(params); + } + + private parseAuthParams(params: InternalConnectionParams): boolean { + if (params.authClientSecret && params.apiKey) { + throw new WeaviateInvalidInputError( + 'must provide one of authClientSecret (OIDC) or apiKey, cannot provide both' + ); + } + if (params.authClientSecret) { + this.oidcAuth = new OidcAuthenticator(this.http, params.authClientSecret); + return true; + } + if (params.apiKey) { + this.apiKey = params.apiKey?.apiKey; + return true; + } + return false; + } + + private sanitizeParams(params: InternalConnectionParams) { + // Remove trailing slashes from the host + while (params.host.endsWith('/')) { + params.host = params.host.slice(0, -1); + } + + const protocolPattern = /^(https?|ftp|file)(?::\/\/)/; + const extractedSchemeMatch = params.host.match(protocolPattern); + + // Check for the existence of scheme in params + if (params.scheme) { + // If the host contains a scheme different than provided scheme, replace it and throw a warning + if (extractedSchemeMatch && extractedSchemeMatch[1] !== `${params.scheme}`) { + throw new WeaviateInvalidInputError( + `The host contains a different protocol than specified in the scheme (scheme: ${params.scheme} != host: ${extractedSchemeMatch[1]})` + ); + } else if (!extractedSchemeMatch) { + // If no scheme in the host, simply prefix with the provided scheme + params.host = `${params.scheme}://${params.host}`; + } + // If there's no scheme in params, ensure the host starts with a recognized protocol + } else if (!extractedSchemeMatch) { + throw new WeaviateInvalidInputError( + 'The host must start with a recognized protocol (e.g., http or https) if no scheme is provided.' + ); + } + + return params; + } + + postReturn = (path: string, payload: B): Promise => { + if (this.authEnabled) { + return this.login().then((token) => + this.http.post(path, payload, true, token).then((res) => res as T) + ); + } + return this.http.post(path, payload, true, '').then((res) => res as T); + }; + + postEmpty = (path: string, payload: B): Promise => { + if (this.authEnabled) { + return this.login().then((token) => this.http.post(path, payload, false, token)); + } + return this.http.post(path, payload, false, ''); + }; + + put = (path: string, payload: any, expectReturnContent = true) => { + if (this.authEnabled) { + return this.login().then((token) => this.http.put(path, payload, expectReturnContent, token)); + } + return this.http.put(path, payload, expectReturnContent); + }; + + patch = (path: string, payload: any) => { + if (this.authEnabled) { + return this.login().then((token) => this.http.patch(path, payload, token)); + } + return this.http.patch(path, payload); + }; + + delete = (path: string, payload: any, expectReturnContent = false) => { + if (this.authEnabled) { + return this.login().then((token) => this.http.delete(path, payload, expectReturnContent, token)); + } + return this.http.delete(path, payload, expectReturnContent); + }; + + head = (path: string, payload: any) => { + if (this.authEnabled) { + return this.login().then((token) => this.http.head(path, payload, token)); + } + return this.http.head(path, payload); + }; + + get = (path: string, expectReturnContent = true) => { + if (this.authEnabled) { + return this.login().then((token) => this.http.get(path, expectReturnContent, token)); + } + return this.http.get(path, expectReturnContent); + }; + + login = async () => { + if (this.apiKey) { + return this.apiKey; + } + + if (!this.oidcAuth) { + return ''; + } + + const localConfig = await new OpenidConfigurationGetter(this.http).do(); + + if (localConfig === undefined) { + console.warn('client is configured for authentication, but server is not'); + return ''; + } + + if (Date.now() >= this.oidcAuth.getExpiresAt()) { + await this.oidcAuth.refresh(localConfig); + } + return this.oidcAuth.getAccessToken(); + }; +} + +export * from './auth.js'; + +export interface HttpClient { + close: () => void; + patch: (path: string, payload: any, bearerToken?: string) => any; + head: (path: string, payload: any, bearerToken?: string) => any; + post: ( + path: string, + payload: B, + expectReturnContent: boolean, + bearerToken: string + ) => Promise; + get: (path: string, expectReturnContent?: boolean, bearerToken?: string) => any; + externalPost: (externalUrl: string, body: any, contentType: any) => any; + getRaw: (path: string, bearerToken?: string) => any; + delete: (path: string, payload: any, expectReturnContent?: boolean, bearerToken?: string) => any; + put: (path: string, payload: any, expectReturnContent?: boolean, bearerToken?: string) => any; + externalGet: (externalUrl: string) => Promise; +} + +const fetchWithTimeout = ( + input: RequestInfo | URL, + timeout: number, + init?: RequestInit | undefined +): Promise => { + const controller = new AbortController(); + // Set a timeout to abort the request + const timeoutId = setTimeout(() => controller.abort(), timeout * 1000); + return fetch(input, { ...init, signal: controller.signal }) + .catch((error) => { + if (isAbortError(error)) { + throw new WeaviateRequestTimeoutError(`Request timed out after ${timeout}ms`); + } + throw error; // For other errors, rethrow them + }) + .finally(() => clearTimeout(timeoutId)); +}; + +export const httpClient = (config: InternalConnectionParams): HttpClient => { + const version = '/v1'; + const baseUri = `${config.host}${version}`; + const url = makeUrl(baseUri); + + return { + close: () => config.agent?.destroy(), + post: ( + path: string, + payload: B, + expectReturnContent: boolean, + bearerToken: string + ): Promise => { + const request = { + method: 'POST', + headers: { + ...config.headers, + 'content-type': 'application/json', + }, + body: JSON.stringify(payload), + agent: config.agent, + }; + addAuthHeaderIfNeeded(request, bearerToken); + return fetchWithTimeout(url(path), config.timeout?.insert || 90, request).then( + checkStatus(expectReturnContent) + ); + }, + put: ( + path: string, + payload: B, + expectReturnContent = true, + bearerToken = '' + ): Promise => { + const request = { + method: 'PUT', + headers: { + ...config.headers, + 'content-type': 'application/json', + }, + body: JSON.stringify(payload), + agent: config.agent, + }; + addAuthHeaderIfNeeded(request, bearerToken); + return fetchWithTimeout(url(path), config.timeout?.insert || 90, request).then( + checkStatus(expectReturnContent) + ); + }, + patch: (path: string, payload: B, bearerToken = ''): Promise => { + const request = { + method: 'PATCH', + headers: { + ...config.headers, + 'content-type': 'application/json', + }, + body: JSON.stringify(payload), + agent: config.agent, + }; + addAuthHeaderIfNeeded(request, bearerToken); + return fetchWithTimeout(url(path), config.timeout?.insert || 90, request).then(checkStatus(false)); + }, + delete: (path: string, payload: B | null = null, expectReturnContent = false, bearerToken = '') => { + const request = { + method: 'DELETE', + headers: { + ...config.headers, + 'content-type': 'application/json', + }, + body: payload ? JSON.stringify(payload) : undefined, + agent: config.agent, + }; + addAuthHeaderIfNeeded(request, bearerToken); + return fetchWithTimeout(url(path), config.timeout?.insert || 90, request).then( + checkStatus(expectReturnContent) + ); + }, + head: (path: string, payload: B | null = null, bearerToken = '') => { + const request = { + method: 'HEAD', + headers: { + ...config.headers, + 'content-type': 'application/json', + }, + body: payload ? JSON.stringify(payload) : undefined, + agent: config.agent, + }; + addAuthHeaderIfNeeded(request, bearerToken); + return fetchWithTimeout(url(path), config.timeout?.query || 30, request).then( + handleHeadResponse(false) + ); + }, + get: (path: string, expectReturnContent = true, bearerToken = ''): Promise => { + const request = { + method: 'GET', + headers: { + ...config.headers, + }, + agent: config.agent, + }; + addAuthHeaderIfNeeded(request, bearerToken); + return fetchWithTimeout(url(path), config.timeout?.query || 30, request).then( + checkStatus(expectReturnContent) + ); + }, + getRaw: (path: string, bearerToken = '') => { + // getRaw does not handle the status leaving this to the caller + const request = { + method: 'GET', + headers: { + ...config.headers, + }, + agent: config.agent, + }; + addAuthHeaderIfNeeded(request, bearerToken); + return fetchWithTimeout(url(path), config.timeout?.query || 30, request); + }, + externalGet: (externalUrl: string) => { + return fetch(externalUrl, { + method: 'GET', + headers: { + ...config.headers, + }, + }).then(checkStatus(true)); + }, + externalPost: (externalUrl: string, body: any, contentType: any) => { + if (contentType == undefined || contentType == '') { + contentType = 'application/json'; + } + const request = { + body: undefined, + method: 'POST', + headers: { + ...config.headers, + 'content-type': contentType, + }, + }; + if (body != null) { + request.body = body; + } + return fetch(externalUrl, request).then(checkStatus(true)); + }, + }; +}; + +const makeUrl = (basePath: string) => (path: string) => basePath + path; + +const checkStatus = + (expectResponseBody: boolean) => + (res: Response) => { + if (res.status >= 400) { + return res.text().then((errText: string) => { + let err: string; + try { + // in case of invalid json response (like empty string) + err = JSON.stringify(JSON.parse(errText)); + } catch (e) { + err = errText; + } + return Promise.reject(new WeaviateUnexpectedStatusCodeError(res.status, err)); + }); + } + if (expectResponseBody) { + return res.json() as Promise; + } + return Promise.resolve(undefined); + }; + +const handleHeadResponse = + (expectResponseBody: boolean) => + (res: Response) => { + if (res.status == 200 || res.status == 204 || res.status == 404) { + return Promise.resolve(res.status == 200 || res.status == 204); + } + return checkStatus(expectResponseBody)(res); + }; + +function addAuthHeaderIfNeeded(request: any, bearerToken: string) { + if (bearerToken !== '') { + request.headers.Authorization = `Bearer ${bearerToken}`; + } +} diff --git a/src/connection/httpClient.ts b/src/connection/httpClient.ts deleted file mode 100644 index 2a03666f..00000000 --- a/src/connection/httpClient.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { ConnectionParams } from '..'; - -export interface HttpClient { - patch: (path: string, payload: any, bearerToken?: string) => any; - head: (path: string, payload: any, bearerToken?: string) => any; - post: (path: string, payload: any, expectReturnContent?: boolean, bearerToken?: string) => any; - get: (path: string, expectReturnContent?: boolean, bearerToken?: string) => any; - externalPost: (externalUrl: string, body: any, contentType: any) => any; - getRaw: (path: string, bearerToken?: string) => any; - delete: (path: string, payload: any, expectReturnContent?: boolean, bearerToken?: string) => any; - put: (path: string, payload: any, expectReturnContent?: boolean, bearerToken?: string) => any; - externalGet: (externalUrl: string) => Promise; -} - -export const httpClient = (config: ConnectionParams): HttpClient => { - const version = '/v1'; - const baseUri = `${config.host}${version}`; - const url = makeUrl(baseUri); - - return { - post: (path: string, payload: any, expectReturnContent = true, bearerToken = '') => { - const request = { - method: 'POST', - headers: { - ...config.headers, - 'content-type': 'application/json', - }, - body: JSON.stringify(payload), - }; - addAuthHeaderIfNeeded(request, bearerToken); - return fetch(url(path), request).then(makeCheckStatus(expectReturnContent)); - }, - put: (path: string, payload: any, expectReturnContent = true, bearerToken = '') => { - const request = { - method: 'PUT', - headers: { - ...config.headers, - 'content-type': 'application/json', - }, - body: JSON.stringify(payload), - }; - addAuthHeaderIfNeeded(request, bearerToken); - return fetch(url(path), request).then(makeCheckStatus(expectReturnContent)); - }, - patch: (path: string, payload: any, bearerToken = '') => { - const request = { - method: 'PATCH', - headers: { - ...config.headers, - 'content-type': 'application/json', - }, - body: JSON.stringify(payload), - }; - addAuthHeaderIfNeeded(request, bearerToken); - return fetch(url(path), request).then(makeCheckStatus(false)); - }, - delete: (path: string, payload: any, expectReturnContent = false, bearerToken = '') => { - const request = { - method: 'DELETE', - headers: { - ...config.headers, - 'content-type': 'application/json', - }, - body: payload ? JSON.stringify(payload) : undefined, - }; - addAuthHeaderIfNeeded(request, bearerToken); - return fetch(url(path), request).then(makeCheckStatus(expectReturnContent)); - }, - head: (path: string, payload: any, bearerToken = '') => { - const request = { - method: 'HEAD', - headers: { - ...config.headers, - 'content-type': 'application/json', - }, - body: payload ? JSON.stringify(payload) : undefined, - }; - addAuthHeaderIfNeeded(request, bearerToken); - return fetch(url(path), request).then(handleHeadResponse(false)); - }, - get: (path: string, expectReturnContent = true, bearerToken = '') => { - const request = { - method: 'GET', - headers: { - ...config.headers, - }, - }; - addAuthHeaderIfNeeded(request, bearerToken); - return fetch(url(path), request).then(makeCheckStatus(expectReturnContent)); - }, - getRaw: (path: string, bearerToken = '') => { - // getRaw does not handle the status leaving this to the caller - const request = { - method: 'GET', - headers: { - ...config.headers, - }, - }; - addAuthHeaderIfNeeded(request, bearerToken); - return fetch(url(path), request); - }, - externalGet: (externalUrl: string) => { - return fetch(externalUrl, { - method: 'GET', - headers: { - ...config.headers, - }, - }).then(makeCheckStatus(true)); - }, - externalPost: (externalUrl: string, body: any, contentType: any) => { - if (contentType == undefined || contentType == '') { - contentType = 'application/json'; - } - const request = { - body: undefined, - method: 'POST', - headers: { - ...config.headers, - 'content-type': contentType, - }, - }; - if (body != null) { - request.body = body; - } - return fetch(externalUrl, request).then(makeCheckStatus(true)); - }, - }; -}; - -const makeUrl = (basePath: string) => (path: string) => basePath + path; - -const makeCheckStatus = (expectResponseBody: boolean) => (res: Response) => { - if (res.status >= 400) { - return res.text().then((errText: string) => { - let err: string; - try { - // in case of invalid json response (like empty string) - err = JSON.stringify(JSON.parse(errText)); - } catch (e) { - err = errText; - } - return Promise.reject(new Error(`usage error (${res.status}): ${err}`)); - }); - } - - if (expectResponseBody) { - return res.json(); - } -}; - -const handleHeadResponse = (expectResponseBody: boolean) => (res: Response) => { - if (res.status == 200 || res.status == 204 || res.status == 404) { - return res.status == 200 || res.status == 204; - } - return makeCheckStatus(expectResponseBody)(res); -}; - -function addAuthHeaderIfNeeded(request: any, bearerToken: string) { - if (bearerToken !== '') { - request.headers.Authorization = `Bearer ${bearerToken}`; - } -} - -export default httpClient; diff --git a/src/connection/index.ts b/src/connection/index.ts index 365fab56..45b60544 100644 --- a/src/connection/index.ts +++ b/src/connection/index.ts @@ -1,145 +1,15 @@ -import { OidcAuthenticator } from './auth'; -import OpenidConfigurationGetter from '../misc/openidConfigurationGetter'; - -import httpClient, { HttpClient } from './httpClient'; -import gqlClient, { GraphQLClient } from './gqlClient'; -import { ConnectionParams } from '..'; -import { Variables } from 'graphql-request'; - -export default class Connection { - private apiKey?: string; - private authEnabled: boolean; - private gql: GraphQLClient; - public readonly host: string; - public readonly http: HttpClient; - public oidcAuth?: OidcAuthenticator; - - constructor(params: ConnectionParams) { - params = this.sanitizeParams(params); - this.host = params.host; - this.http = httpClient(params); - this.gql = gqlClient(params); - this.authEnabled = this.parseAuthParams(params); - } - - private parseAuthParams(params: ConnectionParams): boolean { - if (params.authClientSecret && params.apiKey) { - throw new Error('must provide one of authClientSecret (OIDC) or apiKey, cannot provide both'); - } - if (params.authClientSecret) { - this.oidcAuth = new OidcAuthenticator(this.http, params.authClientSecret); - return true; - } - if (params.apiKey) { - this.apiKey = params.apiKey?.apiKey; - return true; - } - return false; - } - - private sanitizeParams(params: ConnectionParams) { - // Remove trailing slashes from the host - while (params.host.endsWith('/')) { - params.host = params.host.slice(0, -1); - } - - const protocolPattern = /^(https?|ftp|file)(?::\/\/)/; - const extractedSchemeMatch = params.host.match(protocolPattern); - - // Check for the existence of scheme in params - if (params.scheme) { - // If the host contains a scheme different than provided scheme, replace it and throw a warning - if (extractedSchemeMatch && extractedSchemeMatch[1] !== `${params.scheme}`) { - throw new Error( - `The host contains a different protocol than specified in the scheme (scheme: ${params.scheme} != host: ${extractedSchemeMatch[1]})` - ); - } else if (!extractedSchemeMatch) { - // If no scheme in the host, simply prefix with the provided scheme - params.host = `${params.scheme}://${params.host}`; - } - // If there's no scheme in params, ensure the host starts with a recognized protocol - } else if (!extractedSchemeMatch) { - throw new Error( - 'The host must start with a recognized protocol (e.g., http or https) if no scheme is provided.' - ); - } - - return params; - } - - post = (path: string, payload: any, expectReturnContent = true) => { - if (this.authEnabled) { - return this.login().then((token) => this.http.post(path, payload, expectReturnContent, token)); - } - return this.http.post(path, payload, expectReturnContent); - }; - - put = (path: string, payload: any, expectReturnContent = true) => { - if (this.authEnabled) { - return this.login().then((token) => this.http.put(path, payload, expectReturnContent, token)); - } - return this.http.put(path, payload, expectReturnContent); - }; - - patch = (path: string, payload: any) => { - if (this.authEnabled) { - return this.login().then((token) => this.http.patch(path, payload, token)); - } - return this.http.patch(path, payload); - }; - - delete = (path: string, payload: any, expectReturnContent = false) => { - if (this.authEnabled) { - return this.login().then((token) => this.http.delete(path, payload, expectReturnContent, token)); - } - return this.http.delete(path, payload, expectReturnContent); - }; - - head = (path: string, payload: any) => { - if (this.authEnabled) { - return this.login().then((token) => this.http.head(path, payload, token)); - } - return this.http.head(path, payload); - }; - - get = (path: string, expectReturnContent = true) => { - if (this.authEnabled) { - return this.login().then((token) => this.http.get(path, expectReturnContent, token)); - } - return this.http.get(path, expectReturnContent); - }; - - query = (query: any, variables?: Variables) => { - if (this.authEnabled) { - return this.login().then((token) => { - const headers = { Authorization: `Bearer ${token}` }; - return this.gql.query(query, variables, headers); - }); - } - return this.gql.query(query, variables); - }; - - login = async () => { - if (this.apiKey) { - return this.apiKey; - } - - if (!this.oidcAuth) { - return ''; - } - - const localConfig = await new OpenidConfigurationGetter(this.http).do(); - - if (localConfig === undefined) { - console.warn('client is configured for authentication, but server is not'); - return ''; - } - - if (Date.now() >= this.oidcAuth.getExpiresAt()) { - await this.oidcAuth.refresh(localConfig); - } - return this.oidcAuth.getAccessToken(); - }; -} - -export * from './auth'; +import ConnectionGQL from './gql.js'; +import ConnectionGRPC from './grpc.js'; +import ConnectionREST from './http.js'; + +export default ConnectionGQL; + +export type { + ConnectToCustomOptions, + ConnectToLocalOptions, + ConnectToWCDOptions, + ConnectToWCSOptions, + ConnectToWeaviateCloudOptions, +} from './helpers.js'; +export type { InternalConnectionParams } from './http.js'; +export { ConnectionGQL, ConnectionGRPC, ConnectionREST }; diff --git a/src/connection/journey.test.ts b/src/connection/journey.test.ts index e25080ff..52237453 100644 --- a/src/connection/journey.test.ts +++ b/src/connection/journey.test.ts @@ -3,111 +3,104 @@ import { AuthAccessTokenCredentials, AuthClientCredentials, AuthUserPasswordCredentials, -} from './auth'; -import Connection from '.'; +} from './auth.js'; +import Connection from './index.js'; -import weaviate from '..'; +import { WeaviateStartUpError } from '../errors.js'; +import weaviate from '../index.js'; describe('connection', () => { - it('makes a logged-in request when client host param has trailing slashes', () => { + it('makes a logged-in request when client host param has trailing slashes', async () => { if (process.env.WCS_DUMMY_CI_PW == undefined || process.env.WCS_DUMMY_CI_PW == '') { console.warn('Skipping because `WCS_DUMMY_CI_PW` is not set'); - return; + return Promise.resolve(); } - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8085/////', - authClientSecret: new AuthUserPasswordCredentials({ + const client = await weaviate.connectToLocal({ + port: 8085, + authCredentials: new AuthUserPasswordCredentials({ username: 'ms_2d0e007e7136de11d5f29fce7a53dae219a51458@existiert.net', password: process.env.WCS_DUMMY_CI_PW, silentRefresh: false, }), }); - return client.misc - .metaGetter() - .do() - .then((res: any) => { + return client + .getMeta() + .then((res) => { expect(res.version).toBeDefined(); }) - .catch((e: any) => { + .catch((e) => { throw new Error('it should not have errord: ' + e); }); }); - it('makes an Azure logged-in request with client credentials', () => { + it('makes an Azure logged-in request with client credentials', async () => { if (process.env.AZURE_CLIENT_SECRET == undefined || process.env.AZURE_CLIENT_SECRET == '') { console.warn('Skipping because `AZURE_CLIENT_SECRET` is not set'); - return; + return Promise.resolve(); } - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8081', - authClientSecret: new AuthClientCredentials({ + const client = await weaviate.connectToLocal({ + port: 8081, + authCredentials: new AuthClientCredentials({ clientSecret: process.env.AZURE_CLIENT_SECRET, silentRefresh: false, }), }); - return client.misc - .metaGetter() - .do() - .then((res: any) => { + return client + .getMeta() + .then((res) => { expect(res.version).toBeDefined(); }) - .catch((e: any) => { + .catch((e) => { throw new Error('it should not have errord: ' + e); }); }); - it('makes an Okta logged-in request with client credentials', () => { + it('makes an Okta logged-in request with client credentials', async () => { if (process.env.OKTA_CLIENT_SECRET == undefined || process.env.OKTA_CLIENT_SECRET == '') { console.warn('Skipping because `OKTA_CLIENT_SECRET` is not set'); - return; + return Promise.resolve(); } - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8082', - authClientSecret: new AuthClientCredentials({ + const client = await weaviate.connectToLocal({ + port: 8082, + authCredentials: new AuthClientCredentials({ clientSecret: process.env.OKTA_CLIENT_SECRET, scopes: ['some_scope'], silentRefresh: false, }), }); - return client.misc - .metaGetter() - .do() - .then((res: any) => { + return client + .getMeta() + .then((res) => { expect(res.version).toBeDefined(); }) - .catch((e: any) => { + .catch((e) => { throw new Error('it should not have errord: ' + e); }); }); - it('makes an Okta logged-in request with username/password', () => { + it('makes an Okta logged-in request with username/password', async () => { if (process.env.OKTA_DUMMY_CI_PW == undefined || process.env.OKTA_DUMMY_CI_PW == '') { console.warn('Skipping because `OKTA_DUMMY_CI_PW` is not set'); - return; + return Promise.resolve(); } - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8083', - authClientSecret: new AuthUserPasswordCredentials({ + const client = await weaviate.connectToLocal({ + port: 8083, + authCredentials: new AuthUserPasswordCredentials({ username: 'test@test.de', password: process.env.OKTA_DUMMY_CI_PW, silentRefresh: false, }), }); - return client.misc - .metaGetter() - .do() + return client + .getMeta() .then((res: any) => { expect(res.version).toBeDefined(); }) @@ -116,47 +109,59 @@ describe('connection', () => { }); }); - it('makes a WCS logged-in request with username/password', () => { + it('makes a WCS logged-in request with username/password', async () => { if (process.env.WCS_DUMMY_CI_PW == undefined || process.env.WCS_DUMMY_CI_PW == '') { console.warn('Skipping because `WCS_DUMMY_CI_PW` is not set'); - return; + return Promise.resolve(); } - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8085', - authClientSecret: new AuthUserPasswordCredentials({ + const client = await weaviate.connectToLocal({ + port: 8085, + authCredentials: new AuthUserPasswordCredentials({ username: 'ms_2d0e007e7136de11d5f29fce7a53dae219a51458@existiert.net', password: process.env.WCS_DUMMY_CI_PW, silentRefresh: false, }), }); - return client.misc - .metaGetter() - .do() - .then((res: any) => { + return client + .getMeta() + .then((res) => { expect(res.version).toBeDefined(); }) - .catch((e: any) => { + .catch((e) => { throw new Error('it should not have errord: ' + e); }); }); - it('makes a logged-in request with API key', () => { - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8085', - apiKey: new ApiKey('my-secret-key'), + it('makes a logged-in request with API key', async () => { + const client = await weaviate.connectToLocal({ + port: 8085, + authCredentials: new ApiKey('my-secret-key'), }); - return client.misc - .metaGetter() - .do() - .then((res: any) => { + return client + .getMeta() + .then((res) => { expect(res.version).toBeDefined(); }) - .catch((e: any) => { + .catch((e) => { + throw new Error('it should not have errord: ' + e); + }); + }); + + it('makes a logged-in request with API key as string', async () => { + const client = await weaviate.connectToLocal({ + port: 8085, + authCredentials: 'my-secret-key', + }); + + return client + .getMeta() + .then((res) => { + expect(res.version).toBeDefined(); + }) + .catch((e) => { throw new Error('it should not have errord: ' + e); }); }); @@ -181,23 +186,21 @@ describe('connection', () => { await dummy.login(); const accessToken = (dummy as any).oidcAuth?.accessToken || ''; - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8085', - authClientSecret: new AuthAccessTokenCredentials({ + const client = await weaviate.connectToLocal({ + port: 8085, + authCredentials: new AuthAccessTokenCredentials({ accessToken: accessToken, expiresIn: 900, }), }); - return client.misc - .metaGetter() - .do() - .then((res: any) => { + return client + .getMeta() + .then((res) => { expect(res.version).toBeDefined(); client.oidcAuth?.stopTokenRefresh(); }) - .catch((e: any) => { + .catch((e) => { throw new Error('it should not have errord: ' + e); }); }); @@ -246,43 +249,36 @@ describe('connection', () => { }); }); - it('fails to access auth-enabled server without client auth', () => { - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8085', - }); - - return client.misc - .metaGetter() - .do() - .then((res: any) => { - fail(`should not have succeeded. received: ${res}`); - }) - .catch((e: Error) => { - expect(e.message).toContain('401'); - expect(e.message).toContain('anonymous access not enabled'); + it('fails to access auth-enabled server without client auth', async () => { + expect.assertions(3); + try { + await weaviate.connectToLocal({ + port: 8085, }); + throw new Error('Promise should have been rejected'); + } catch (error: any) { + expect(error).toBeInstanceOf(WeaviateStartUpError); + expect(error.message).toContain('401'); + expect(error.message).toContain('anonymous access not enabled'); + } }); it('warns when client auth is configured, but server auth is not', async () => { const logSpy = jest.spyOn(console, 'warn'); - const client = weaviate.client({ - scheme: 'http', - host: 'localhost:8080', - authClientSecret: new AuthUserPasswordCredentials({ + const client = await weaviate.connectToLocal({ + authCredentials: new AuthUserPasswordCredentials({ username: 'some-user', password: 'passwd', }), }); - await client.misc - .metaGetter() - .do() - .then((res: any) => { + await client + .getMeta() + .then((res) => { expect(res.version).toBeDefined(); }) - .catch((e: any) => { + .catch((e) => { throw new Error('it should not have errord: ' + e); }); diff --git a/src/connection/proxy.test.ts b/src/connection/proxy.test.ts new file mode 100644 index 00000000..c23db967 --- /dev/null +++ b/src/connection/proxy.test.ts @@ -0,0 +1,33 @@ +import weaviate from '../index.js'; + +describe('Testing of the client connecting to a proxied Weaviate instance', () => { + // Skip because Envoy Proxy in CI is too flaky with strange error: + // ClientError: /grpc.health.v1.Health/Check INTERNAL: Received RST_STREAM with code 2 triggered by internal client error: Protocol error + it.skip('should connect to a local instance using simultaneous http and grpc proxies', async () => { + const client = await weaviate.connectToCustom({ + httpHost: 'localhost', + httpPath: '/http', + httpPort: 10000, + httpSecure: false, + grpcHost: 'weaviate-proxy', + grpcPort: 8021, + proxies: { + grpc: 'http://localhost:10001', + }, + }); + expect(client).toBeDefined(); + return client.collections + .delete('Test') + .then(() => + client.collections.create({ + name: 'Test', + }) + ) + .then(async (collection) => { + await collection.data.insert(); + return collection; + }) + .then((collection) => collection.query.fetchObjects()) + .then((res) => expect(res.objects).toHaveLength(1)); + }); +}); diff --git a/src/connection/unit.test.ts b/src/connection/unit.test.ts index b19cfcfa..a622a007 100644 --- a/src/connection/unit.test.ts +++ b/src/connection/unit.test.ts @@ -1,11 +1,11 @@ -import { testServer } from '../../test/server'; +import { testServer } from '../../test/server.js'; import { + ApiKey, + AuthAccessTokenCredentials, AuthClientCredentials, AuthUserPasswordCredentials, - AuthAccessTokenCredentials, - ApiKey, -} from './auth'; -import Connection from '.'; +} from './auth.js'; +import Connection from './index.js'; describe('mock server auth tests', () => { const server = testServer(); diff --git a/src/data/checker.ts b/src/data/checker.ts index 31aa2dfc..2e824732 100644 --- a/src/data/checker.ts +++ b/src/data/checker.ts @@ -1,7 +1,7 @@ -import Connection from '../connection'; -import { ConsistencyLevel } from './replication'; -import { ObjectsPath } from './path'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { ObjectsPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class Checker extends CommandBase { private className!: string; diff --git a/src/data/creator.ts b/src/data/creator.ts index bc913b21..43581c66 100644 --- a/src/data/creator.ts +++ b/src/data/creator.ts @@ -1,9 +1,9 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { ObjectsPath } from './path'; -import { CommandBase } from '../validation/commandBase'; -import { Properties, WeaviateObject } from '../openapi/types'; -import { ConsistencyLevel } from './replication'; +import Connection from '../connection/index.js'; +import { Properties, WeaviateObject } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; +import { ObjectsPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class Creator extends CommandBase { private className?: string; @@ -82,6 +82,6 @@ export default class Creator extends CommandBase { return this.objectsPath .buildCreate(this.consistencyLevel) - .then((path: string) => this.client.post(path, this.payload())); + .then((path: string) => this.client.postReturn(path, this.payload())); }; } diff --git a/src/data/deleter.ts b/src/data/deleter.ts index b98d45c2..93cc070d 100644 --- a/src/data/deleter.ts +++ b/src/data/deleter.ts @@ -1,7 +1,7 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { ObjectsPath } from './path'; -import { ConsistencyLevel } from './replication'; +import Connection from '../connection/index.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { ObjectsPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class Deleter extends CommandBase { private className!: string; diff --git a/src/data/getter.ts b/src/data/getter.ts index 7c77491f..e6c5b441 100644 --- a/src/data/getter.ts +++ b/src/data/getter.ts @@ -1,7 +1,7 @@ -import Connection from '../connection'; -import { ObjectsPath } from './path'; -import { CommandBase } from '../validation/commandBase'; -import { WeaviateObjectsList } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { WeaviateObjectsList } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { ObjectsPath } from './path.js'; export default class Getter extends CommandBase { private additional: string[]; diff --git a/src/data/getterById.ts b/src/data/getterById.ts index 55164026..6b0df240 100644 --- a/src/data/getterById.ts +++ b/src/data/getterById.ts @@ -1,8 +1,8 @@ -import Connection from '../connection'; -import { WeaviateObject } from '../openapi/types'; -import { CommandBase } from '../validation/commandBase'; -import { ObjectsPath } from './path'; -import { ConsistencyLevel } from './replication'; +import Connection from '../connection/index.js'; +import { WeaviateObject } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { ObjectsPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class GetterById extends CommandBase { private additional: string[]; diff --git a/src/data/index.ts b/src/data/index.ts index ccf10af0..c0161027 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -1,19 +1,19 @@ -import Creator from './creator'; -import Validator from './validator'; -import Updater from './updater'; -import Merger from './merger'; -import Getter from './getter'; -import GetterById from './getterById'; -import Deleter from './deleter'; -import Checker from './checker'; -import ReferenceCreator from './referenceCreator'; -import ReferenceReplacer from './referenceReplacer'; -import ReferenceDeleter from './referenceDeleter'; -import ReferencePayloadBuilder from './referencePayloadBuilder'; -import { ObjectsPath, ReferencesPath } from './path'; -import { BeaconPath } from '../utils/beaconPath'; -import { DbVersionSupport } from '../utils/dbVersion'; -import Connection from '../connection'; +import Connection from '../connection/index.js'; +import { BeaconPath } from '../utils/beaconPath.js'; +import { DbVersionSupport } from '../utils/dbVersion.js'; +import Checker from './checker.js'; +import Creator from './creator.js'; +import Deleter from './deleter.js'; +import Getter from './getter.js'; +import GetterById from './getterById.js'; +import Merger from './merger.js'; +import { ObjectsPath, ReferencesPath } from './path.js'; +import ReferenceCreator from './referenceCreator.js'; +import ReferenceDeleter from './referenceDeleter.js'; +import ReferencePayloadBuilder from './referencePayloadBuilder.js'; +import ReferenceReplacer from './referenceReplacer.js'; +import Updater from './updater.js'; +import Validator from './validator.js'; export interface Data { creator: () => Creator; @@ -52,17 +52,17 @@ const data = (client: Connection, dbVersionSupport: DbVersionSupport): Data => { }; export default data; -export { default as Creator } from './creator'; -export { default as Validator } from './validator'; -export { default as Updater } from './updater'; -export { default as Merger } from './merger'; -export { default as Getter } from './getter'; -export { default as GetterById } from './getterById'; -export { default as Deleter } from './deleter'; -export { default as Checker } from './checker'; -export { default as ReferenceCreator } from './referenceCreator'; -export { default as ReferenceReplacer } from './referenceReplacer'; -export { default as ReferenceDeleter } from './referenceDeleter'; -export { default as ReferencePayloadBuilder } from './referencePayloadBuilder'; +export { default as Checker } from './checker.js'; +export { default as Creator } from './creator.js'; +export { default as Deleter } from './deleter.js'; +export { default as Getter } from './getter.js'; +export { default as GetterById } from './getterById.js'; +export { default as Merger } from './merger.js'; +export { default as ReferenceCreator } from './referenceCreator.js'; +export { default as ReferenceDeleter } from './referenceDeleter.js'; +export { default as ReferencePayloadBuilder } from './referencePayloadBuilder.js'; +export { default as ReferenceReplacer } from './referenceReplacer.js'; +export { default as Updater } from './updater.js'; +export { default as Validator } from './validator.js'; -export type { ConsistencyLevel } from './replication'; +export type { ConsistencyLevel } from './replication.js'; diff --git a/src/data/journey.test.ts b/src/data/journey.test.ts index 688bdafd..d29e8278 100644 --- a/src/data/journey.test.ts +++ b/src/data/journey.test.ts @@ -1,13 +1,13 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import weaviate, { WeaviateClient } from '..'; import { - WeaviateObject, - WeaviateObjectsList, - WeaviateError, Properties, - WeaviateClass, Tenant, -} from '../openapi/types'; + WeaviateClass, + WeaviateError, + WeaviateObject, + WeaviateObjectsList, +} from '../openapi/types.js'; +import weaviate, { WeaviateClient } from '../v2/index.js'; const thingClassName = 'DataJourneyTestThing'; const refSourceClassName = 'DataJourneyTestRefSource'; @@ -75,7 +75,7 @@ describe('data', () => { .do() .catch((e: Error) => { expect(e.message).toEqual( - `usage error (422): {"error":[{"message":"invalid object: invalid text property 'stringProp' on class 'DataJourneyTestThing': not a string, but json.Number"}]}` + `The request to Weaviate failed with status code: 422 and message: {"error":[{"message":"invalid object: invalid text property 'stringProp' on class 'DataJourneyTestThing': not a string, but json.Number"}]}` ); }); }); @@ -491,7 +491,7 @@ describe('data', () => { .withId('00000000-0000-0000-0000-000000000000') .do() .catch((err: Error) => { - expect(err.message).toEqual('usage error (404): '); + expect(err.message).toEqual('The request to Weaviate failed with status code: 404 and message: '); }); }); diff --git a/src/data/merger.ts b/src/data/merger.ts index e125d781..2d62024f 100644 --- a/src/data/merger.ts +++ b/src/data/merger.ts @@ -1,9 +1,9 @@ -import Connection from '../connection'; -import { isValidStringProperty } from '../validation/string'; -import { ObjectsPath } from './path'; -import { CommandBase } from '../validation/commandBase'; -import { Properties, WeaviateObject } from '../openapi/types'; -import { ConsistencyLevel } from './replication'; +import Connection from '../connection/index.js'; +import { Properties, WeaviateObject } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; +import { ObjectsPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class Merger extends CommandBase { private className!: string; diff --git a/src/data/path.test.ts b/src/data/path.test.ts index d2db5e22..a2b605d4 100644 --- a/src/data/path.test.ts +++ b/src/data/path.test.ts @@ -1,6 +1,6 @@ -import { TestDbVersionProvider } from '../../test/dbVersionProvider'; -import { DbVersionSupport } from '../utils/dbVersion'; -import { ObjectsPath, ReferencesPath } from './path'; +import { TestDbVersionProvider } from '../../test/dbVersionProvider.js'; +import { DbVersionSupport } from '../utils/dbVersion.js'; +import { ObjectsPath, ReferencesPath } from './path.js'; // This can be anything > 1.14.2, to support class-namespaced urls. // The actual value is not used for anything else diff --git a/src/data/path.ts b/src/data/path.ts index 0a3643d3..871c33d4 100644 --- a/src/data/path.ts +++ b/src/data/path.ts @@ -1,7 +1,7 @@ -import { isValidStringProperty } from '../validation/string'; -import { DbVersionSupport } from '../utils/dbVersion'; -import { ConsistencyLevel } from './replication'; -import { isValidWeaviateVersion } from '../validation/version'; +import { DbVersionSupport } from '../utils/dbVersion.js'; +import { isValidStringProperty } from '../validation/string.js'; +import { isValidWeaviateVersion } from '../validation/version.js'; +import { ConsistencyLevel } from './replication.js'; const objectsPathPrefix = '/objects'; diff --git a/src/data/referenceCreator.ts b/src/data/referenceCreator.ts index d07b5f68..b99888e7 100644 --- a/src/data/referenceCreator.ts +++ b/src/data/referenceCreator.ts @@ -1,9 +1,9 @@ -import { BeaconPath } from '../utils/beaconPath'; -import { ReferencesPath } from './path'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Reference } from '../openapi/types'; -import { ConsistencyLevel } from './replication'; +import Connection from '../connection/index.js'; +import { Reference } from '../openapi/types.js'; +import { BeaconPath } from '../utils/beaconPath.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { ReferencesPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class ReferenceCreator extends CommandBase { private beaconPath: BeaconPath; @@ -80,7 +80,7 @@ export default class ReferenceCreator extends CommandBase { ]).then((results) => { const path = results[0]; const beacon = results[1]; - return this.client.post(path, { beacon }, false); + return this.client.postEmpty(path, { beacon }); }); }; } diff --git a/src/data/referenceDeleter.ts b/src/data/referenceDeleter.ts index 50079692..667e5e10 100644 --- a/src/data/referenceDeleter.ts +++ b/src/data/referenceDeleter.ts @@ -1,9 +1,9 @@ -import { BeaconPath } from '../utils/beaconPath'; -import { ReferencesPath } from './path'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Reference } from '../openapi/types'; -import { ConsistencyLevel } from './replication'; +import Connection from '../connection/index.js'; +import { Reference } from '../openapi/types.js'; +import { BeaconPath } from '../utils/beaconPath.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { ReferencesPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class ReferenceDeleter extends CommandBase { private beaconPath: BeaconPath; diff --git a/src/data/referencePayloadBuilder.ts b/src/data/referencePayloadBuilder.ts index f554f9c8..6822d7c0 100644 --- a/src/data/referencePayloadBuilder.ts +++ b/src/data/referencePayloadBuilder.ts @@ -1,7 +1,7 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Reference } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { Reference } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class ReferencePayloadBuilder extends CommandBase { private className?: string; diff --git a/src/data/referenceReplacer.ts b/src/data/referenceReplacer.ts index 77ce1933..98b2e29d 100644 --- a/src/data/referenceReplacer.ts +++ b/src/data/referenceReplacer.ts @@ -1,9 +1,9 @@ -import Connection from '../connection'; -import { BeaconPath } from '../utils/beaconPath'; -import { ReferencesPath } from './path'; -import { CommandBase } from '../validation/commandBase'; -import { Reference } from '../openapi/types'; -import { ConsistencyLevel } from './replication'; +import Connection from '../connection/index.js'; +import { Reference } from '../openapi/types.js'; +import { BeaconPath } from '../utils/beaconPath.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { ReferencesPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class ReferenceReplacer extends CommandBase { private beaconPath: BeaconPath; diff --git a/src/data/updater.ts b/src/data/updater.ts index 45c1985e..05fc77e7 100644 --- a/src/data/updater.ts +++ b/src/data/updater.ts @@ -1,9 +1,9 @@ -import { isValidStringProperty } from '../validation/string'; -import { ObjectsPath } from './path'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Properties, WeaviateObject } from '../openapi/types'; -import { ConsistencyLevel } from './replication'; +import Connection from '../connection/index.js'; +import { Properties, WeaviateObject } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; +import { ObjectsPath } from './path.js'; +import { ConsistencyLevel } from './replication.js'; export default class Updater extends CommandBase { private className!: string; diff --git a/src/data/validator.ts b/src/data/validator.ts index 8a72e256..ff6e6d34 100644 --- a/src/data/validator.ts +++ b/src/data/validator.ts @@ -1,7 +1,7 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Properties } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { Properties } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class Validator extends CommandBase { private className?: string; @@ -49,6 +49,6 @@ export default class Validator extends CommandBase { return Promise.reject(new Error('invalid usage: ' + this.errors.join(', '))); } const path = `/objects/validate`; - return this.client.post(path, this.payload(), false).then(() => true); + return this.client.postEmpty(path, this.payload()).then(() => true); }; } diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 00000000..ea754e5f --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,129 @@ +class WeaviateError extends Error { + public message: string; + constructor(message: string) { + super(message); + this.message = message; + this.name = this.constructor.name; + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, this.constructor); + } + } +} + +/** + * Is thrown if the input to a function is invalid. + */ +export class WeaviateInvalidInputError extends WeaviateError { + constructor(message: string) { + super(`Invalid input provided: ${message}`); + } +} + +/** + * Is thrown if a query (either gRPC or GraphQL) to Weaviate fails in any way. + */ +export class WeaviateQueryError extends WeaviateError { + constructor(message: string, protocolType: 'GraphQL' | 'gRPC') { + super(`Query call with protocol ${protocolType} failed with message: ${message}`); + } +} + +/** + * Is thrown if a gRPC delete many request to Weaviate fails in any way. + */ +export class WeaviateDeleteManyError extends WeaviateError { + constructor(message: string) { + super(`Delete many failed with message: ${message}`); + } +} + +/** + * Is thrown if a gRPC batch query to Weaviate fails in any way. + */ +export class WeaviateBatchError extends WeaviateError { + constructor(message: string) { + super(`Batch objects insert failed with message: ${message}`); + } +} + +/** + * Is thrown if the gRPC health check against Weaviate fails. + */ +export class WeaviateGRPCUnavailableError extends WeaviateError { + constructor(address: string) { + const grpcMsg = `Please check that the server address and port: ${address} are correct.`; + const msg = `Weaviate makes use of a high-speed gRPC API as well as a REST API. + Unfortunately, the gRPC health check against Weaviate could not be completed. + + This error could be due to one of several reasons: + - The gRPC traffic at the specified port is blocked by a firewall. + - gRPC is not enabled or incorrectly configured on the server or the client. + - ${grpcMsg} + - your connection is unstable or has a high latency. In this case you can: + - increase init-timeout in weaviate.connectToLocal({timeout: {init: X}})' + - disable startup checks by connecting using 'skipInitChecks=true' + `; + super(msg); + } +} + +/** + * Is thrown if data returned by Weaviate cannot be processed by the client. + */ +export class WeaviateDeserializationError extends WeaviateError { + constructor(message: string) { + super(`Converting data from Weaviate failed with message: ${message}`); + } +} + +/** + * Is thrown if data to be sent to Weaviate cannot be processed by the client. + */ +export class WeaviateSerializationError extends WeaviateError { + constructor(message: string) { + super(`Converting data to Weaviate failed with message: ${message}`); + } +} + +/** + * Is thrown if Weaviate returns an unexpected status code. + */ +export class WeaviateUnexpectedStatusCodeError extends WeaviateError { + public code: number; + constructor(code: number, message: string) { + super(`The request to Weaviate failed with status code: ${code} and message: ${message}`); + this.code = code; + } +} + +/** + * Is thrown when a backup creation or restoration fails. + */ +export class WeaviateBackupFailed extends WeaviateError { + constructor(message: string, kind: 'creation' | 'restoration') { + super(`Backup ${kind} failed with message: ${message}`); + } +} + +/** + * Is thrown if the Weaviate server does not support a feature that the client is trying to use. + */ +export class WeaviateUnsupportedFeatureError extends WeaviateError {} + +/** + * Is thrown if the Weaviate server was not able to start up. + */ +export class WeaviateStartUpError extends WeaviateError { + constructor(message: string) { + super(`Weaviate startup failed with message: ${message}`); + } +} + +/** + * Is thrown if a request to Weaviate times out. + */ +export class WeaviateRequestTimeoutError extends WeaviateError { + constructor(message: string) { + super(`Weaviate request timed out with message: ${message}`); + } +} diff --git a/src/graphql/aggregator.ts b/src/graphql/aggregator.ts index 73c19e56..aaab6697 100644 --- a/src/graphql/aggregator.ts +++ b/src/graphql/aggregator.ts @@ -1,20 +1,20 @@ -import Where from './where'; +import Connection from '../connection/index.js'; +import { WhereFilter } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidPositiveIntProperty } from '../validation/number.js'; import NearMedia, { - NearMediaArgs, - NearVideoArgs, NearAudioArgs, NearDepthArgs, NearIMUArgs, + NearMediaArgs, NearMediaBase, NearMediaType, -} from './nearMedia'; -import NearText, { NearTextArgs } from './nearText'; -import NearVector, { NearVectorArgs } from './nearVector'; -import NearObject, { NearObjectArgs } from './nearObject'; -import { isValidPositiveIntProperty } from '../validation/number'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { WhereFilter } from '../openapi/types'; + NearVideoArgs, +} from './nearMedia.js'; +import NearObject, { NearObjectArgs } from './nearObject.js'; +import NearText, { NearTextArgs } from './nearText.js'; +import NearVector, { NearVectorArgs } from './nearVector.js'; +import Where from './where.js'; interface NearImageArgs extends NearMediaBase { image: string; diff --git a/src/graphql/bm25.ts b/src/graphql/bm25.ts index b9a77ad7..c1e806af 100644 --- a/src/graphql/bm25.ts +++ b/src/graphql/bm25.ts @@ -1,5 +1,3 @@ -import { isValidStringArray, isValidStringProperty } from '../validation/string'; - export interface Bm25Args { properties?: string[]; query: string; diff --git a/src/graphql/explorer.ts b/src/graphql/explorer.ts index 6a7c5971..6854b1d6 100644 --- a/src/graphql/explorer.ts +++ b/src/graphql/explorer.ts @@ -1,7 +1,7 @@ -import NearText, { NearTextArgs } from './nearText'; -import NearVector, { NearVectorArgs } from './nearVector'; -import NearImage, { NearImageArgs } from './nearImage'; -import NearObject, { NearObjectArgs } from './nearObject'; +import Connection from '../connection/index.js'; +import { CommandBase } from '../validation/commandBase.js'; +import Ask, { AskArgs } from './ask.js'; +import NearImage, { NearImageArgs } from './nearImage.js'; import NearMedia, { NearAudioArgs, NearDepthArgs, @@ -10,10 +10,10 @@ import NearMedia, { NearMediaType, NearThermalArgs, NearVideoArgs, -} from './nearMedia'; -import Ask, { AskArgs } from './ask'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; +} from './nearMedia.js'; +import NearObject, { NearObjectArgs } from './nearObject.js'; +import NearText, { NearTextArgs } from './nearText.js'; +import NearVector, { NearVectorArgs } from './nearVector.js'; export default class Explorer extends CommandBase { private askString?: string; diff --git a/src/graphql/getter.test.ts b/src/graphql/getter.test.ts index 15a64f82..4ff90f51 100644 --- a/src/graphql/getter.test.ts +++ b/src/graphql/getter.test.ts @@ -1,10 +1,10 @@ -import Getter, { FusionType } from './getter'; -import { WhereFilter } from '../openapi/types'; -import { NearObjectArgs } from './nearObject'; -import { AskArgs } from './ask'; -import { SortArgs } from './sort'; -import { NearTextArgs } from './nearText'; -import { NearImageArgs } from './nearImage'; +import { WhereFilter } from '../openapi/types.js'; +import { AskArgs } from './ask.js'; +import Getter, { FusionType } from './getter.js'; +import { NearImageArgs } from './nearImage.js'; +import { NearObjectArgs } from './nearObject.js'; +import { NearTextArgs } from './nearText.js'; +import { SortArgs } from './sort.js'; test('a simple query without params', () => { const mockClient: any = { diff --git a/src/graphql/getter.ts b/src/graphql/getter.ts index 0699b408..1f57d209 100644 --- a/src/graphql/getter.ts +++ b/src/graphql/getter.ts @@ -1,10 +1,14 @@ -import Where from './where'; -import NearText, { NearTextArgs } from './nearText'; -import NearVector, { NearVectorArgs } from './nearVector'; -import Bm25, { Bm25Args } from './bm25'; -import Hybrid, { HybridArgs } from './hybrid'; -import NearImage, { NearImageArgs } from './nearImage'; -import NearObject, { NearObjectArgs } from './nearObject'; +import Connection from '../connection/index.js'; +import { ConsistencyLevel } from '../data/index.js'; +import { WhereFilter } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import Ask, { AskArgs } from './ask.js'; +import Bm25, { Bm25Args } from './bm25.js'; +import { GenerateArgs, GraphQLGenerate } from './generate.js'; +import Group, { GroupArgs } from './group.js'; +import GroupBy, { GroupByArgs } from './groupBy.js'; +import Hybrid, { HybridArgs } from './hybrid.js'; +import NearImage, { NearImageArgs } from './nearImage.js'; import NearMedia, { NearAudioArgs, NearDepthArgs, @@ -13,18 +17,14 @@ import NearMedia, { NearMediaType, NearThermalArgs, NearVideoArgs, -} from './nearMedia'; -import Ask, { AskArgs } from './ask'; -import Group, { GroupArgs } from './group'; -import Sort, { SortArgs } from './sort'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { WhereFilter } from '../openapi/types'; -import { GenerateArgs, GraphQLGenerate } from './generate'; -import { ConsistencyLevel } from '../data'; -import GroupBy, { GroupByArgs } from './groupBy'; - -export { FusionType } from './hybrid'; +} from './nearMedia.js'; +import NearObject, { NearObjectArgs } from './nearObject.js'; +import NearText, { NearTextArgs } from './nearText.js'; +import NearVector, { NearVectorArgs } from './nearVector.js'; +import Sort, { SortArgs } from './sort.js'; +import Where from './where.js'; + +export { FusionType } from './hybrid.js'; export default class GraphQLGetter extends CommandBase { private after?: string; private askString?: string; diff --git a/src/graphql/hybrid.ts b/src/graphql/hybrid.ts index 0e021739..b344041e 100644 --- a/src/graphql/hybrid.ts +++ b/src/graphql/hybrid.ts @@ -1,4 +1,4 @@ -import { Move, parseMove } from './nearText'; +import { Move, parseMove } from './nearText.js'; export interface HybridArgs { alpha?: number; diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 51a980b8..298ef296 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -1,8 +1,8 @@ -import Aggregator from './aggregator'; -import GraphQLGetter from './getter'; -import Explorer from './explorer'; -import Raw from './raw'; -import Connection from '../connection'; +import Connection from '../connection/index.js'; +import Aggregator from './aggregator.js'; +import Explorer from './explorer.js'; +import GraphQLGetter from './getter.js'; +import Raw from './raw.js'; export interface GraphQL { get: () => GraphQLGetter; @@ -21,8 +21,7 @@ const graphql = (client: Connection): GraphQL => { }; export default graphql; -export { default as Aggregator } from './aggregator'; -export { default as GraphQLGetter } from './getter'; -export { default as Explorer } from './explorer'; -export { default as Raw } from './raw'; -export { FusionType } from './getter'; +export { default as Aggregator } from './aggregator.js'; +export { default as Explorer } from './explorer.js'; +export { FusionType, default as GraphQLGetter } from './getter.js'; +export { default as Raw } from './raw.js'; diff --git a/src/graphql/journey.test.ts b/src/graphql/journey.test.ts index b467c4d0..ccb8acac 100644 --- a/src/graphql/journey.test.ts +++ b/src/graphql/journey.test.ts @@ -1,24 +1,30 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import weaviate, { + Meta, Reference, + ReferenceCreator, + Tenant, + WeaviateClass, WeaviateClient, WeaviateError, WeaviateObject, - WeaviateClass, - Tenant, - ReferenceCreator, WhereFilter, -} from '..'; -import { FusionType } from './hybrid'; +} from '../v2/index.js'; +import { FusionType } from './hybrid.js'; describe('the graphql journey', () => { let client: WeaviateClient; + let versionLessThan125: boolean; - beforeEach(() => { + beforeEach(async () => { client = weaviate.client({ scheme: 'http', host: 'localhost:8080', }); + versionLessThan125 = await client.misc + .metaGetter() + .do() + .then((res: Meta) => res.version!.localeCompare('1.25.0') < 0); }); it('creates a schema class', () => { @@ -445,6 +451,9 @@ describe('the graphql journey', () => { }); test('graphql get hybrid with query and groupby', () => { + if (versionLessThan125) { + return Promise.resolve(); + } return client.graphql .get() .withClassName('Article') @@ -465,6 +474,9 @@ describe('the graphql journey', () => { }); test('graphql get hybrid with query and nearText subsearch', () => { + if (versionLessThan125) { + return Promise.resolve(); + } return client.graphql .get() .withClassName('Article') @@ -531,6 +543,10 @@ describe('the graphql journey', () => { -0.081376314, 0.23645392, 0.05198973, 0.09471436, ]; + if (versionLessThan125) { + return Promise.resolve(); + } + return client.graphql .get() .withClassName('Article') @@ -2244,7 +2260,7 @@ describe('named vectors test', () => { }); }); - const className = 'NamedVectorTest'; + const className = 'VectorTest'; const oneUUID = 'abefd256-8574-442b-9293-9205193737e1'; describe('setup', () => { @@ -2285,7 +2301,7 @@ describe('named vectors test', () => { return client.schema.classCreator().withClass(namedVectorTest).do(); }); - it('should insert NamedVectorTest data', () => { + it('should insert VectorTest data', () => { const objects: WeaviateObject[] = [ { class: className, @@ -2328,8 +2344,8 @@ describe('named vectors test', () => { .withFields('title') .do() .then((res) => { - expect(res.data.Get.NamedVectorTest).toHaveLength(3); - expect(res.data.Get.NamedVectorTest[0].title).toBe('Two'); + expect(res.data.Get.VectorTest).toHaveLength(3); + expect(res.data.Get.VectorTest[0].title).toBe('Two'); }); }); @@ -2344,8 +2360,8 @@ describe('named vectors test', () => { .withFields('rating') .do() .then((res) => { - expect(res.data.Get.NamedVectorTest).toHaveLength(3); - expect(res.data.Get.NamedVectorTest[0].rating).toBe('Good'); + expect(res.data.Get.VectorTest).toHaveLength(3); + expect(res.data.Get.VectorTest[0].rating).toBe('Good'); }); }); }); @@ -2369,8 +2385,8 @@ describe('named vectors test', () => { .do() ) .then((res) => { - expect(res.data.Get.NamedVectorTest).toHaveLength(3); - expect(res.data.Get.NamedVectorTest[0].title).toBe('One'); + expect(res.data.Get.VectorTest).toHaveLength(3); + expect(res.data.Get.VectorTest[0].title).toBe('One'); }); }); @@ -2385,13 +2401,13 @@ describe('named vectors test', () => { .withFields('rating') .do() .then((res) => { - expect(res.data.Get.NamedVectorTest).toHaveLength(3); - expect(res.data.Get.NamedVectorTest[0].rating).toBe('Best'); + expect(res.data.Get.VectorTest).toHaveLength(3); + expect(res.data.Get.VectorTest[0].rating).toBe('Best'); }); }); describe('destroy', () => { - it('tears down NamedVectorTest class', () => { + it('tears down VectorTest class', () => { return client.schema.classDeleter().withClassName(className).do(); }); }); diff --git a/src/graphql/nearImage.ts b/src/graphql/nearImage.ts index 76a3a41e..999e2fff 100644 --- a/src/graphql/nearImage.ts +++ b/src/graphql/nearImage.ts @@ -1,4 +1,4 @@ -import { NearMediaBase } from './nearMedia'; +import { NearMediaBase } from './nearMedia.js'; export interface NearImageArgs extends NearMediaBase { image?: string; diff --git a/src/graphql/raw.test.ts b/src/graphql/raw.test.ts index e790dc2e..d1cbd9e6 100644 --- a/src/graphql/raw.test.ts +++ b/src/graphql/raw.test.ts @@ -1,4 +1,4 @@ -import Raw from './raw'; +import Raw from './raw.js'; test('a simple raw query', () => { const mockClient: any = { diff --git a/src/graphql/raw.ts b/src/graphql/raw.ts index 50a25add..689328c7 100644 --- a/src/graphql/raw.ts +++ b/src/graphql/raw.ts @@ -1,5 +1,5 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class RawGraphQL extends CommandBase { private query?: string; diff --git a/src/graphql/where.ts b/src/graphql/where.ts index 996bc161..17531098 100644 --- a/src/graphql/where.ts +++ b/src/graphql/where.ts @@ -1,4 +1,4 @@ -import { WhereFilter } from '../openapi/types'; +import { WhereFilter } from '../openapi/types.js'; export default class GraphQLWhere { private operands?: string; diff --git a/src/grpc/base.ts b/src/grpc/base.ts new file mode 100644 index 00000000..90df3368 --- /dev/null +++ b/src/grpc/base.ts @@ -0,0 +1,58 @@ +import { isAbortError } from 'abort-controller-x'; +import { ConsistencyLevel } from '../data/index.js'; + +import { Metadata } from 'nice-grpc'; +import { WeaviateRequestTimeoutError } from '../errors.js'; +import { ConsistencyLevel as ConsistencyLevelGRPC } from '../proto/v1/base.js'; +import { WeaviateClient } from '../proto/v1/weaviate.js'; + +export default class Base { + protected connection: WeaviateClient; + protected collection: string; + protected timeout: number; + protected consistencyLevel?: ConsistencyLevelGRPC; + protected tenant?: string; + protected metadata?: Metadata; + + protected constructor( + connection: WeaviateClient, + collection: string, + metadata: Metadata, + timeout: number, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ) { + this.connection = connection; + this.collection = collection; + this.metadata = metadata; + this.timeout = timeout; + this.consistencyLevel = this.mapConsistencyLevel(consistencyLevel); + this.tenant = tenant; + } + + private mapConsistencyLevel(consistencyLevel?: ConsistencyLevel): ConsistencyLevelGRPC { + switch (consistencyLevel) { + case 'ALL': + return ConsistencyLevelGRPC.CONSISTENCY_LEVEL_ALL; + case 'QUORUM': + return ConsistencyLevelGRPC.CONSISTENCY_LEVEL_QUORUM; + case 'ONE': + return ConsistencyLevelGRPC.CONSISTENCY_LEVEL_ONE; + default: + return ConsistencyLevelGRPC.CONSISTENCY_LEVEL_UNSPECIFIED; + } + } + + protected sendWithTimeout = (send: (signal: AbortSignal) => Promise): Promise => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), this.timeout * 1000); + return send(controller.signal) + .catch((error) => { + if (isAbortError(error)) { + throw new WeaviateRequestTimeoutError(`timed out after ${this.timeout}ms`); + } + throw error; + }) + .finally(() => clearTimeout(timeoutId)); + }; +} diff --git a/src/grpc/batcher.ts b/src/grpc/batcher.ts new file mode 100644 index 00000000..8fdb882d --- /dev/null +++ b/src/grpc/batcher.ts @@ -0,0 +1,80 @@ +import { Metadata } from 'nice-grpc'; + +import { ConsistencyLevel } from '../data/index.js'; + +import { BatchObject, BatchObjectsReply, BatchObjectsRequest } from '../proto/v1/batch.js'; +import { WeaviateClient } from '../proto/v1/weaviate.js'; + +import { WeaviateBatchError, WeaviateDeleteManyError } from '../errors.js'; +import { Filters } from '../proto/v1/base.js'; +import { BatchDeleteReply, BatchDeleteRequest } from '../proto/v1/batch_delete.js'; +import Base from './base.js'; + +export interface Batch { + withDelete: (args: BatchDeleteArgs) => Promise; + withObjects: (args: BatchObjectsArgs) => Promise; +} + +export interface BatchObjectsArgs { + objects: BatchObject[]; +} + +export interface BatchDeleteArgs { + filters: Filters | undefined; + verbose?: boolean; + dryRun?: boolean; +} + +export default class Batcher extends Base implements Batch { + public static use( + connection: WeaviateClient, + collection: string, + metadata: Metadata, + timeout: number, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ): Batch { + return new Batcher(connection, collection, metadata, timeout, consistencyLevel, tenant); + } + + public withDelete = (args: BatchDeleteArgs) => this.callDelete(BatchDeleteRequest.fromPartial(args)); + public withObjects = (args: BatchObjectsArgs) => this.callObjects(BatchObjectsRequest.fromPartial(args)); + + private callDelete(message: BatchDeleteRequest) { + return this.sendWithTimeout((signal: AbortSignal) => + this.connection.batchDelete( + { + ...message, + collection: this.collection, + consistencyLevel: this.consistencyLevel, + tenant: this.tenant, + }, + { + metadata: this.metadata, + signal, + } + ) + ).catch((err) => { + throw new WeaviateDeleteManyError(err.message); + }); + } + + private callObjects(message: BatchObjectsRequest) { + return this.sendWithTimeout((signal: AbortSignal) => + this.connection + .batchObjects( + { + ...message, + consistencyLevel: this.consistencyLevel, + }, + { + metadata: this.metadata, + signal, + } + ) + .catch((err) => { + throw new WeaviateBatchError(err.message); + }) + ); + } +} diff --git a/src/grpc/searcher.ts b/src/grpc/searcher.ts new file mode 100644 index 00000000..1a7bb754 --- /dev/null +++ b/src/grpc/searcher.ts @@ -0,0 +1,161 @@ +import { ConsistencyLevel } from '../data/index.js'; + +import { Metadata } from 'nice-grpc'; +import { Filters } from '../proto/v1/base.js'; +import { + BM25, + GenerativeSearch, + GroupBy, + Hybrid, + MetadataRequest, + NearAudioSearch, + NearDepthSearch, + NearImageSearch, + NearIMUSearch, + NearObject, + NearTextSearch, + NearThermalSearch, + NearVector, + NearVideoSearch, + PropertiesRequest, + Rerank, + SearchReply, + SearchRequest, + SortBy, +} from '../proto/v1/search_get.js'; +import { WeaviateClient } from '../proto/v1/weaviate.js'; + +import { WeaviateQueryError } from '../errors.js'; +import Base from './base.js'; + +export type SearchFetchArgs = { + limit?: number; + offset?: number; + after?: string; + filters?: Filters; + sortBy?: SortBy[]; + metadata?: MetadataRequest; + properties?: PropertiesRequest; + generative?: GenerativeSearch; + groupBy?: GroupBy; +}; + +export type BaseSearchArgs = { + limit?: number; + offset?: number; + autocut?: number; + filters?: Filters; + rerank?: Rerank; + metadata?: MetadataRequest; + properties?: PropertiesRequest; + generative?: GenerativeSearch; + groupBy?: GroupBy; +}; + +export type SearchBm25Args = BaseSearchArgs & { + bm25Search: BM25; +}; + +export type SearchHybridArgs = BaseSearchArgs & { + hybridSearch: Hybrid; +}; + +export type SearchNearAudioArgs = BaseSearchArgs & { + nearAudio: NearAudioSearch; +}; + +export type SearchNearDepthArgs = BaseSearchArgs & { + nearDepth: NearDepthSearch; +}; + +export type SearchNearImageArgs = BaseSearchArgs & { + nearImage: NearImageSearch; +}; + +export type SearchNearIMUArgs = BaseSearchArgs & { + nearIMU: NearIMUSearch; +}; + +export type SearchNearObjectArgs = BaseSearchArgs & { + nearObject: NearObject; +}; + +export type SearchNearTextArgs = BaseSearchArgs & { + nearText: NearTextSearch; +}; + +export type SearchNearThermalArgs = BaseSearchArgs & { + nearThermal: NearThermalSearch; +}; + +export type SearchNearVectorArgs = BaseSearchArgs & { + nearVector: NearVector; +}; + +export type SearchNearVideoArgs = BaseSearchArgs & { + nearVideo: NearVideoSearch; +}; + +export interface Search { + withFetch: (args: SearchFetchArgs) => Promise; + withBm25: (args: SearchBm25Args) => Promise; + withHybrid: (args: SearchHybridArgs) => Promise; + withNearAudio: (args: SearchNearAudioArgs) => Promise; + withNearDepth: (args: SearchNearDepthArgs) => Promise; + withNearImage: (args: SearchNearImageArgs) => Promise; + withNearIMU: (args: SearchNearIMUArgs) => Promise; + withNearObject: (args: SearchNearObjectArgs) => Promise; + withNearText: (args: SearchNearTextArgs) => Promise; + withNearThermal: (args: SearchNearThermalArgs) => Promise; + withNearVector: (args: SearchNearVectorArgs) => Promise; + withNearVideo: (args: SearchNearVideoArgs) => Promise; +} + +export default class Searcher extends Base implements Search { + public static use( + connection: WeaviateClient, + collection: string, + metadata: Metadata, + timeout: number, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ): Search { + return new Searcher(connection, collection, metadata, timeout, consistencyLevel, tenant); + } + + public withFetch = (args: SearchFetchArgs) => this.call(SearchRequest.fromPartial(args)); + public withBm25 = (args: SearchBm25Args) => this.call(SearchRequest.fromPartial(args)); + public withHybrid = (args: SearchHybridArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearAudio = (args: SearchNearAudioArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearDepth = (args: SearchNearDepthArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearImage = (args: SearchNearImageArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearIMU = (args: SearchNearIMUArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearObject = (args: SearchNearObjectArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearText = (args: SearchNearTextArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearThermal = (args: SearchNearThermalArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearVector = (args: SearchNearVectorArgs) => this.call(SearchRequest.fromPartial(args)); + public withNearVideo = (args: SearchNearVideoArgs) => this.call(SearchRequest.fromPartial(args)); + + private call(message: SearchRequest) { + return this.sendWithTimeout((signal: AbortSignal) => + this.connection + .search( + { + ...message, + collection: this.collection, + consistencyLevel: this.consistencyLevel, + tenant: this.tenant, + uses123Api: true, + uses125Api: true, + }, + { + metadata: this.metadata, + signal, + } + ) + .catch((err) => { + throw new WeaviateQueryError(err.message, 'gRPC'); + }) + ); + } +} diff --git a/src/grpc/tenantsManager.ts b/src/grpc/tenantsManager.ts new file mode 100644 index 00000000..73575ac7 --- /dev/null +++ b/src/grpc/tenantsManager.ts @@ -0,0 +1,41 @@ +import { Metadata } from 'nice-grpc'; +import { TenantsGetReply, TenantsGetRequest } from '../proto/v1/tenants.js'; +import { WeaviateClient } from '../proto/v1/weaviate.js'; +import Base from './base.js'; + +export type TenantsGetArgs = { + names?: string[]; +}; + +export interface Tenants { + withGet: (args: TenantsGetArgs) => Promise; +} + +export default class TenantsManager extends Base implements TenantsManager { + public static use( + connection: WeaviateClient, + collection: string, + metadata: Metadata, + timeout: number + ): Tenants { + return new TenantsManager(connection, collection, metadata, timeout); + } + + public withGet = (args: TenantsGetArgs) => + this.call(TenantsGetRequest.fromPartial({ names: args.names ? { values: args.names } : undefined })); + + private call(message: TenantsGetRequest) { + return this.sendWithTimeout((signal: AbortSignal) => + this.connection.tenantsGet( + { + ...message, + collection: this.collection, + }, + { + metadata: this.metadata, + signal, + } + ) + ); + } +} diff --git a/src/index.ts b/src/index.ts index 9e0fb3f9..a70d4243 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,69 +1,232 @@ -import Connection from './connection'; -import graphql, { GraphQL } from './graphql'; -import schema, { Schema } from './schema'; -import data, { Data } from './data'; -import classifications, { Classifications } from './classifications'; -import batch, { Batch } from './batch'; -import misc, { Misc } from './misc'; -import c11y, { C11y } from './c11y'; -import { DbVersionProvider, DbVersionSupport } from './utils/dbVersion'; -import backup, { Backup } from './backup'; -import cluster, { Cluster } from './cluster'; +import { Backend, BackupCompressionLevel, BackupStatus } from './backup/index.js'; +import { Backup, backup } from './collections/backup/client.js'; +import cluster, { Cluster } from './collections/cluster/index.js'; +import { configGuards } from './collections/config/index.js'; +import { configure, reconfigure } from './collections/configure/index.js'; +import collections, { Collections } from './collections/index.js'; import { + AccessTokenCredentialsInput, ApiKey, AuthAccessTokenCredentials, AuthClientCredentials, + AuthCredentials, AuthUserPasswordCredentials, + ClientCredentialsInput, OidcAuthenticator, -} from './connection/auth'; -import MetaGetter from './misc/metaGetter'; + UserPasswordCredentialsInput, + isApiKey, + mapApiKey, +} from './connection/auth.js'; +import { + ConnectToCustomOptions, + ConnectToLocalOptions, + ConnectToWCDOptions, + ConnectToWCSOptions, + ConnectToWeaviateCloudOptions, + connectToCustom, + connectToLocal, + connectToWeaviateCloud, +} from './connection/helpers.js'; +import { ProxiesParams, TimeoutParams } from './connection/http.js'; +import { ConnectionGRPC } from './connection/index.js'; +import MetaGetter from './misc/metaGetter.js'; +import { Meta } from './openapi/types.js'; +import { DbVersion } from './utils/dbVersion.js'; + +import { Agent as HttpAgent } from 'http'; +import { Agent as HttpsAgent } from 'https'; +import { LiveChecker, OpenidConfigurationGetter, ReadyChecker } from './misc/index.js'; + +import weaviateV2 from './v2/index.js'; + +import { ConsistencyLevel } from './data/replication.js'; -export interface ConnectionParams { - authClientSecret?: AuthClientCredentials | AuthAccessTokenCredentials | AuthUserPasswordCredentials; - apiKey?: ApiKey; +export type ProtocolParams = { + /** + * The host to connect to. E.g., `localhost` or `example.com`. + */ host: string; - scheme?: string; + /** + * The port to connect to. E.g., `8080` or `80`. + */ + port: number; + /** + * Whether to use a secure connection (https). + */ + secure: boolean; + /** + * An optional path in the case that you are using a forwarding proxy. + * + * E.g., http://localhost:8080/weaviate + */ + path?: string; +}; + +export type ConnectionParams = { + /** + * The connection parameters for the REST and GraphQL APIs (http/1.1). + */ + http: ProtocolParams; + /** + * The connection paramaters for the gRPC API (http/2). + */ + grpc: ProtocolParams; +}; + +export type ClientParams = { + /** + * The connection parameters for Weaviate's public APIs. + */ + connectionParams: ConnectionParams; + /** + * The credentials used to authenticate with Weaviate. + * + * Can be any of `AuthUserPasswordCredentials`, `AuthAccessTokenCredentials`, `AuthClientCredentials`, and `ApiKey`. + */ + auth?: AuthCredentials; + /** + * Additional headers that should be passed to Weaviate in the underlying requests. E.g., X-OpenAI-Api-Key + */ headers?: HeadersInit; -} + /** + * The connection parameters for any tunnelling proxies that should be used. + * + * Note, if your proxy is a forwarding proxy then supply its configuration as if it were the Weaviate server itself using `rest` and `grpc`. + */ + proxies?: ProxiesParams; + /** The timeouts to use when making requests to Weaviate */ + timeout?: TimeoutParams; + /** Whether to skip the initialization checks */ + skipInitChecks?: boolean; +}; export interface WeaviateClient { - graphql: GraphQL; - schema: Schema; - data: Data; - classifications: Classifications; - batch: Batch; - misc: Misc; - c11y: C11y; backup: Backup; cluster: Cluster; + collections: Collections; oidcAuth?: OidcAuthenticator; + + close: () => Promise; + getMeta: () => Promise; + getOpenIDConfig?: () => Promise; + getWeaviateVersion: () => Promise; + isLive: () => Promise; + isReady: () => Promise; } +const cleanHost = (host: string, protocol: 'rest' | 'grpc') => { + if (host.includes('http')) { + console.warn( + `The ${protocol}.host parameter should not include the protocol. Please remove the http:// or https:// from the ${protocol}.host parameter.\ + To specify a secure connection, set the secure parameter to true. The protocol will be inferred from the secure parameter instead.` + ); + return host.replace('http://', '').replace('https://', ''); + } + return host; +}; + const app = { - client: function (params: ConnectionParams): WeaviateClient { - // check if the URL is set - if (!params.host) throw new Error('Missing `host` parameter'); + /** + * Connect to a custom Weaviate deployment, e.g. your own self-hosted Kubernetes cluster. + * + * @param {ConnectToCustomOptions} options Options for the connection. + * @returns {Promise} A Promise that resolves to a client connected to your custom Weaviate deployment. + */ + connectToCustom: function (options: ConnectToCustomOptions): Promise { + return connectToCustom(this.client, options); + }, + /** + * Connect to a locally-deployed Weaviate instance, e.g. as a Docker compose stack. + * + * @param {ConnectToLocalOptions} [options] Options for the connection. + * @returns {Promise} A Promise that resolves to a client connected to your local Weaviate instance. + */ + connectToLocal: function (options?: ConnectToLocalOptions): Promise { + return connectToLocal(this.client, options); + }, + /** + * Connect to your own Weaviate Cloud (WCD) instance. + * + * @deprecated Use `connectToWeaviateCloud` instead. + * + * @param {string} clusterURL The URL of your WCD instance. E.g., `https://example.weaviate.network`. + * @param {ConnectToWCDOptions} [options] Additional options for the connection. + * @returns {Promise} A Promise that resolves to a client connected to your WCD instance. + */ + connectToWCD: function (clusterURL: string, options?: ConnectToWCDOptions): Promise { + console.warn( + 'The `connectToWCD` method is deprecated. Please use `connectToWeaviateCloud` instead. This method will be removed in a future release.' + ); + return connectToWeaviateCloud(clusterURL, this.client, options); + }, + /** + * Connect to your own Weaviate Cloud Service (WCS) instance. + * + * @deprecated Use `connectToWeaviateCloud` instead. + * + * @param {string} clusterURL The URL of your WCD instance. E.g., `https://example.weaviate.network`. + * @param {ConnectToWCSOptions} [options] Additional options for the connection. + * @returns {Promise} A Promise that resolves to a client connected to your WCS instance. + */ + connectToWCS: function (clusterURL: string, options?: ConnectToWCSOptions): Promise { + console.warn( + 'The `connectToWCS` method is deprecated. Please use `connectToWeaviateCloud` instead. This method will be removed in a future release.' + ); + return connectToWeaviateCloud(clusterURL, this.client, options); + }, + /** + * Connect to your own Weaviate Cloud (WCD) instance. + * + * @param {string} clusterURL The URL of your WCD instance. E.g., `https://example.weaviate.network`. + * @param {ConnectToWeaviateCloudOptions} [options] Additional options for the connection. + * @returns {Promise} A Promise that resolves to a client connected to your WCD instance. + */ + connectToWeaviateCloud: function ( + clusterURL: string, + options?: ConnectToWeaviateCloudOptions + ): Promise { + return connectToWeaviateCloud(clusterURL, this.client, options); + }, + client: async function (params: ClientParams): Promise { + let { host: httpHost } = params.connectionParams.http; + let { host: grpcHost } = params.connectionParams.grpc; + const { port: httpPort, secure: httpSecure, path: httpPath } = params.connectionParams.http; + const { port: grpcPort, secure: grpcSecure } = params.connectionParams.grpc; + httpHost = cleanHost(httpHost, 'rest'); + grpcHost = cleanHost(grpcHost, 'grpc'); // check if headers are set if (!params.headers) params.headers = {}; - const conn = new Connection(params); - const dbVersionProvider = initDbVersionProvider(conn); - const dbVersionSupport = new DbVersionSupport(dbVersionProvider); + const scheme = httpSecure ? 'https' : 'http'; + const agent = httpSecure ? new HttpsAgent({ keepAlive: true }) : new HttpAgent({ keepAlive: true }); + + const { connection, dbVersionProvider, dbVersionSupport } = await ConnectionGRPC.use({ + host: `${scheme}://${httpHost}:${httpPort}${httpPath || ''}`, + scheme: scheme, + headers: params.headers, + grpcAddress: `${grpcHost}:${grpcPort}`, + grpcSecure: grpcSecure, + grpcProxyUrl: params.proxies?.grpc, + apiKey: isApiKey(params.auth) ? mapApiKey(params.auth) : undefined, + authClientSecret: isApiKey(params.auth) ? undefined : params.auth, + agent, + timeout: params.timeout, + skipInitChecks: params.skipInitChecks, + }); const ifc: WeaviateClient = { - graphql: graphql(conn), - schema: schema(conn), - data: data(conn, dbVersionSupport), - classifications: classifications(conn), - batch: batch(conn, dbVersionSupport), - misc: misc(conn, dbVersionProvider), - c11y: c11y(conn), - backup: backup(conn), - cluster: cluster(conn), + backup: backup(connection), + cluster: cluster(connection), + collections: collections(connection, dbVersionSupport), + close: () => Promise.resolve(connection.close()), // hedge against future changes to add I/O to .close() + getMeta: () => new MetaGetter(connection).do(), + getOpenIDConfig: () => new OpenidConfigurationGetter(connection.http).do(), + getWeaviateVersion: () => dbVersionSupport.getVersion(), + isLive: () => new LiveChecker(connection, dbVersionProvider).do(), + isReady: () => new ReadyChecker(connection, dbVersionProvider).do(), }; - - if (conn.oidcAuth) ifc.oidcAuth = conn.oidcAuth; + if (connection.oidcAuth) ifc.oidcAuth = connection.oidcAuth; return ifc; }, @@ -72,34 +235,30 @@ const app = { AuthUserPasswordCredentials, AuthAccessTokenCredentials, AuthClientCredentials, + configure, + configGuards, + reconfigure, }; -function initDbVersionProvider(conn: Connection) { - const metaGetter = new MetaGetter(conn); - const versionGetter = () => { - return metaGetter - .do() - .then((result: any) => result.version) - .catch(() => Promise.resolve('')); - }; - - const dbVersionProvider = new DbVersionProvider(versionGetter); - dbVersionProvider.refresh(); - - return dbVersionProvider; -} - export default app; -export * from './openapi/types'; -export * from './graphql'; -export * from './schema'; -export * from './data'; -export * from './classifications'; -export * from './batch'; -export * from './misc'; -export * from './c11y'; -export * from './backup'; -export * from './cluster'; -export * from './connection'; -export * from './utils/uuid'; -export * from './utils/base64'; +export * from './collections/index.js'; +export * from './connection/index.js'; +export * from './utils/base64.js'; +export * from './utils/uuid.js'; +export { + AccessTokenCredentialsInput, + ApiKey, + AuthAccessTokenCredentials, + AuthClientCredentials, + AuthCredentials, + AuthUserPasswordCredentials, + Backend, + BackupCompressionLevel, + BackupStatus, + ClientCredentialsInput, + ConsistencyLevel, + ProxiesParams, + TimeoutParams, + UserPasswordCredentialsInput, + weaviateV2, +}; diff --git a/src/integration.test.ts b/src/integration.test.ts new file mode 100644 index 00000000..8bc7e603 --- /dev/null +++ b/src/integration.test.ts @@ -0,0 +1,22 @@ +import weaviate from './index.js'; + +describe('Integration testing of the client methods', () => { + it('should connect using connectToLocal defaults', () => { + return weaviate.connectToLocal(); + }); + + it('should connect using connectToLocal with schema-ed host', () => { + const logSpy = jest.spyOn(console, 'warn'); + return weaviate + .connectToLocal({ + host: 'http://localhost', + }) + .then(() => expect(logSpy).toHaveBeenCalledTimes(2)); + }); + + it('should connect using connectToLocal with non-schema-ed host', () => { + return weaviate.connectToLocal({ + host: 'localhost', + }); + }); +}); diff --git a/src/misc/index.ts b/src/misc/index.ts index 75eb215d..ffe68035 100644 --- a/src/misc/index.ts +++ b/src/misc/index.ts @@ -1,9 +1,9 @@ -import LiveChecker from './liveChecker'; -import ReadyChecker from './readyChecker'; -import MetaGetter from './metaGetter'; -import OpenidConfigurationGetter from './openidConfigurationGetter'; -import Connection from '../connection'; -import { DbVersionProvider } from '../utils/dbVersion'; +import Connection from '../connection/index.js'; +import { DbVersionProvider } from '../utils/dbVersion.js'; +import LiveChecker from './liveChecker.js'; +import MetaGetter from './metaGetter.js'; +import OpenidConfigurationGetter from './openidConfigurationGetter.js'; +import ReadyChecker from './readyChecker.js'; export interface Misc { liveChecker: () => LiveChecker; @@ -22,7 +22,7 @@ const misc = (client: Connection, dbVersionProvider: DbVersionProvider): Misc => }; export default misc; -export { default as LiveChecker } from './liveChecker'; -export { default as ReadyChecker } from './readyChecker'; -export { default as MetaGetter } from './metaGetter'; -export { default as OpenidConfigurationGetter } from './openidConfigurationGetter'; +export { default as LiveChecker } from './liveChecker.js'; +export { default as MetaGetter } from './metaGetter.js'; +export { default as OpenidConfigurationGetter } from './openidConfigurationGetter.js'; +export { default as ReadyChecker } from './readyChecker.js'; diff --git a/src/misc/journey.test.ts b/src/misc/journey.test.ts index dd20aca4..4959f639 100644 --- a/src/misc/journey.test.ts +++ b/src/misc/journey.test.ts @@ -1,4 +1,4 @@ -import weaviate from '..'; +import weaviate from '../v2/index.js'; describe('misc endpoints', () => { const client = weaviate.client({ diff --git a/src/misc/liveChecker.ts b/src/misc/liveChecker.ts index 3e0e4b89..efc172f8 100644 --- a/src/misc/liveChecker.ts +++ b/src/misc/liveChecker.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { DbVersionProvider } from '../utils/dbVersion'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { DbVersionProvider } from '../utils/dbVersion.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class LiveChecker extends CommandBase { private dbVersionProvider: DbVersionProvider; diff --git a/src/misc/metaGetter.ts b/src/misc/metaGetter.ts index a6f8f5c6..9c020da4 100644 --- a/src/misc/metaGetter.ts +++ b/src/misc/metaGetter.ts @@ -1,5 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { Meta } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class MetaGetter extends CommandBase { constructor(client: Connection) { @@ -10,7 +11,7 @@ export default class MetaGetter extends CommandBase { // nothing to validate } - do = () => { + do = (): Promise => { return this.client.get('/meta', true); }; } diff --git a/src/misc/openidConfigurationGetter.ts b/src/misc/openidConfigurationGetter.ts index 5a94b26f..8239243d 100644 --- a/src/misc/openidConfigurationGetter.ts +++ b/src/misc/openidConfigurationGetter.ts @@ -1,4 +1,4 @@ -import { HttpClient } from '../connection/httpClient'; +import { HttpClient } from '../connection/http.js'; export default class OpenidConfigurationGetterGetter { private client: HttpClient; diff --git a/src/misc/readyChecker.ts b/src/misc/readyChecker.ts index 2b3431e2..8831f6c5 100644 --- a/src/misc/readyChecker.ts +++ b/src/misc/readyChecker.ts @@ -1,6 +1,6 @@ -import { DbVersionProvider } from '../utils/dbVersion'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { DbVersionProvider } from '../utils/dbVersion.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class ReadyChecker extends CommandBase { private dbVersionProvider: DbVersionProvider; diff --git a/src/openapi/schema.ts b/src/openapi/schema.ts index a17803d5..8d574a3a 100644 --- a/src/openapi/schema.ts +++ b/src/openapi/schema.ts @@ -371,6 +371,8 @@ export interface definitions { enabled?: boolean; /** @description Nonexistent tenants should (not) be created implicitly */ autoTenantCreation?: boolean; + /** @description Existing tenants should (not) be turned HOT implicitly when they are accessed and in another activity status */ + autoTenantActivation?: boolean; }; /** @description JSON object value. */ JsonObject: { [key: string]: unknown }; diff --git a/src/openapi/types.ts b/src/openapi/types.ts index 6da20594..01dbc77a 100644 --- a/src/openapi/types.ts +++ b/src/openapi/types.ts @@ -1,6 +1,9 @@ -import { definitions } from './schema'; +import { definitions } from './schema.js'; -export type WeaviateObject = definitions['Object']; +type Override = Omit & T2; +type DefaultProperties = { [key: string]: unknown }; + +export type WeaviateObject = Override; export type WeaviateObjectsList = definitions['ObjectsListResponse']; export type WeaviateObjectsGet = definitions['ObjectsGetResponse']; export type Reference = definitions['SingleRef']; @@ -22,7 +25,7 @@ export type BatchDelete = definitions['BatchDelete']; export type BatchDeleteResponse = definitions['BatchDeleteResponse']; export type BatchRequest = { fields?: ('ALL' | 'class' | 'schema' | 'id' | 'creationTimeUnix')[]; - objects?: WeaviateObject[]; + objects?: WeaviateObject[]; }; export type BatchReference = definitions['BatchReference']; export type BatchReferenceResponse = definitions['BatchReferenceResponse']; @@ -36,9 +39,27 @@ export type WhereFilter = definitions['WhereFilter']; // Schema export type WeaviateSchema = definitions['Schema']; export type WeaviateClass = definitions['Class']; +export type WeaviateProperty = definitions['Property']; +export type WeaviateNestedProperty = definitions['NestedProperty']; export type ShardStatus = definitions['ShardStatus']; export type ShardStatusList = definitions['ShardStatusList']; export type Tenant = definitions['Tenant']; export type SchemaClusterStatus = definitions['SchemaClusterStatus']; +export type WeaviateModuleConfig = WeaviateClass['moduleConfig']; +export type WeaviateInvertedIndexConfig = WeaviateClass['invertedIndexConfig']; +export type WeaviateBM25Config = definitions['BM25Config']; +export type WeaviateStopwordConfig = definitions['StopwordConfig']; +export type WeaviateMultiTenancyConfig = WeaviateClass['multiTenancyConfig']; +export type WeaviateReplicationConfig = WeaviateClass['replicationConfig']; +export type WeaviateShardingConfig = WeaviateClass['shardingConfig']; +export type WeaviateShardStatus = definitions['ShardStatusGetResponse']; +export type WeaviateVectorIndexConfig = WeaviateClass['vectorIndexConfig']; +export type WeaviateVectorsConfig = WeaviateClass['vectorConfig']; +export type WeaviateVectorConfig = definitions['VectorConfig']; // Nodes export type NodesStatusResponse = definitions['NodesStatusResponse']; +export type NodeStats = definitions['NodeStats']; +export type BatchStats = definitions['BatchStats']; +export type NodeShardStatus = definitions['NodeShardStatus']; +// Meta +export type Meta = definitions['Meta']; diff --git a/src/proto/google/health/v1/health.ts b/src/proto/google/health/v1/health.ts new file mode 100644 index 00000000..dfd141e1 --- /dev/null +++ b/src/proto/google/health/v1/health.ts @@ -0,0 +1,290 @@ +/* eslint-disable */ +import type { CallContext, CallOptions } from "nice-grpc-common"; +import _m0 from "protobufjs/minimal.js"; + +export const protobufPackage = "grpc.health.v1"; + +export interface HealthCheckRequest { + service: string; +} + +export interface HealthCheckResponse { + status: HealthCheckResponse_ServingStatus; +} + +export enum HealthCheckResponse_ServingStatus { + UNKNOWN = 0, + SERVING = 1, + NOT_SERVING = 2, + /** SERVICE_UNKNOWN - Used only by the Watch method. */ + SERVICE_UNKNOWN = 3, + UNRECOGNIZED = -1, +} + +export function healthCheckResponse_ServingStatusFromJSON(object: any): HealthCheckResponse_ServingStatus { + switch (object) { + case 0: + case "UNKNOWN": + return HealthCheckResponse_ServingStatus.UNKNOWN; + case 1: + case "SERVING": + return HealthCheckResponse_ServingStatus.SERVING; + case 2: + case "NOT_SERVING": + return HealthCheckResponse_ServingStatus.NOT_SERVING; + case 3: + case "SERVICE_UNKNOWN": + return HealthCheckResponse_ServingStatus.SERVICE_UNKNOWN; + case -1: + case "UNRECOGNIZED": + default: + return HealthCheckResponse_ServingStatus.UNRECOGNIZED; + } +} + +export function healthCheckResponse_ServingStatusToJSON(object: HealthCheckResponse_ServingStatus): string { + switch (object) { + case HealthCheckResponse_ServingStatus.UNKNOWN: + return "UNKNOWN"; + case HealthCheckResponse_ServingStatus.SERVING: + return "SERVING"; + case HealthCheckResponse_ServingStatus.NOT_SERVING: + return "NOT_SERVING"; + case HealthCheckResponse_ServingStatus.SERVICE_UNKNOWN: + return "SERVICE_UNKNOWN"; + case HealthCheckResponse_ServingStatus.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +function createBaseHealthCheckRequest(): HealthCheckRequest { + return { service: "" }; +} + +export const HealthCheckRequest = { + encode(message: HealthCheckRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.service !== "") { + writer.uint32(10).string(message.service); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHealthCheckRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.service = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): HealthCheckRequest { + return { service: isSet(object.service) ? globalThis.String(object.service) : "" }; + }, + + toJSON(message: HealthCheckRequest): unknown { + const obj: any = {}; + if (message.service !== "") { + obj.service = message.service; + } + return obj; + }, + + create(base?: DeepPartial): HealthCheckRequest { + return HealthCheckRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): HealthCheckRequest { + const message = createBaseHealthCheckRequest(); + message.service = object.service ?? ""; + return message; + }, +}; + +function createBaseHealthCheckResponse(): HealthCheckResponse { + return { status: 0 }; +} + +export const HealthCheckResponse = { + encode(message: HealthCheckResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.status !== 0) { + writer.uint32(8).int32(message.status); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHealthCheckResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.status = reader.int32() as any; + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): HealthCheckResponse { + return { status: isSet(object.status) ? healthCheckResponse_ServingStatusFromJSON(object.status) : 0 }; + }, + + toJSON(message: HealthCheckResponse): unknown { + const obj: any = {}; + if (message.status !== 0) { + obj.status = healthCheckResponse_ServingStatusToJSON(message.status); + } + return obj; + }, + + create(base?: DeepPartial): HealthCheckResponse { + return HealthCheckResponse.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): HealthCheckResponse { + const message = createBaseHealthCheckResponse(); + message.status = object.status ?? 0; + return message; + }, +}; + +export type HealthDefinition = typeof HealthDefinition; +export const HealthDefinition = { + name: "Health", + fullName: "grpc.health.v1.Health", + methods: { + /** + * If the requested service is unknown, the call will fail with status + * NOT_FOUND. + */ + check: { + name: "Check", + requestType: HealthCheckRequest, + requestStream: false, + responseType: HealthCheckResponse, + responseStream: false, + options: {}, + }, + /** + * Performs a watch for the serving status of the requested service. + * The server will immediately send back a message indicating the current + * serving status. It will then subsequently send a new message whenever + * the service's serving status changes. + * + * If the requested service is unknown when the call is received, the + * server will send a message setting the serving status to + * SERVICE_UNKNOWN but will *not* terminate the call. If at some + * future point, the serving status of the service becomes known, the + * server will send a new message with the service's serving status. + * + * If the call terminates with status UNIMPLEMENTED, then clients + * should assume this method is not supported and should not retry the + * call. If the call terminates with any other status (including OK), + * clients should retry the call with appropriate exponential backoff. + */ + watch: { + name: "Watch", + requestType: HealthCheckRequest, + requestStream: false, + responseType: HealthCheckResponse, + responseStream: true, + options: {}, + }, + }, +} as const; + +export interface HealthServiceImplementation { + /** + * If the requested service is unknown, the call will fail with status + * NOT_FOUND. + */ + check(request: HealthCheckRequest, context: CallContext & CallContextExt): Promise>; + /** + * Performs a watch for the serving status of the requested service. + * The server will immediately send back a message indicating the current + * serving status. It will then subsequently send a new message whenever + * the service's serving status changes. + * + * If the requested service is unknown when the call is received, the + * server will send a message setting the serving status to + * SERVICE_UNKNOWN but will *not* terminate the call. If at some + * future point, the serving status of the service becomes known, the + * server will send a new message with the service's serving status. + * + * If the call terminates with status UNIMPLEMENTED, then clients + * should assume this method is not supported and should not retry the + * call. If the call terminates with any other status (including OK), + * clients should retry the call with appropriate exponential backoff. + */ + watch( + request: HealthCheckRequest, + context: CallContext & CallContextExt, + ): ServerStreamingMethodResult>; +} + +export interface HealthClient { + /** + * If the requested service is unknown, the call will fail with status + * NOT_FOUND. + */ + check(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; + /** + * Performs a watch for the serving status of the requested service. + * The server will immediately send back a message indicating the current + * serving status. It will then subsequently send a new message whenever + * the service's serving status changes. + * + * If the requested service is unknown when the call is received, the + * server will send a message setting the serving status to + * SERVICE_UNKNOWN but will *not* terminate the call. If at some + * future point, the serving status of the service becomes known, the + * server will send a new message with the service's serving status. + * + * If the call terminates with status UNIMPLEMENTED, then clients + * should assume this method is not supported and should not retry the + * call. If the call terminates with any other status (including OK), + * clients should retry the call with appropriate exponential backoff. + */ + watch( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): AsyncIterable; +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export type ServerStreamingMethodResult = { [Symbol.asyncIterator](): AsyncIterator }; diff --git a/src/proto/google/protobuf/struct.ts b/src/proto/google/protobuf/struct.ts new file mode 100644 index 00000000..ef64ac1b --- /dev/null +++ b/src/proto/google/protobuf/struct.ts @@ -0,0 +1,543 @@ +/* eslint-disable */ +import _m0 from "protobufjs/minimal.js"; + +export const protobufPackage = "google.protobuf"; + +/** + * `NullValue` is a singleton enumeration to represent the null value for the + * `Value` type union. + * + * The JSON representation for `NullValue` is JSON `null`. + */ +export enum NullValue { + /** NULL_VALUE - Null value. */ + NULL_VALUE = 0, + UNRECOGNIZED = -1, +} + +export function nullValueFromJSON(object: any): NullValue { + switch (object) { + case 0: + case "NULL_VALUE": + return NullValue.NULL_VALUE; + case -1: + case "UNRECOGNIZED": + default: + return NullValue.UNRECOGNIZED; + } +} + +export function nullValueToJSON(object: NullValue): string { + switch (object) { + case NullValue.NULL_VALUE: + return "NULL_VALUE"; + case NullValue.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** + * `Struct` represents a structured data value, consisting of fields + * which map to dynamically typed values. In some languages, `Struct` + * might be supported by a native representation. For example, in + * scripting languages like JS a struct is represented as an + * object. The details of that representation are described together + * with the proto support for the language. + * + * The JSON representation for `Struct` is JSON object. + */ +export interface Struct { + /** Unordered map of dynamically typed values. */ + fields: { [key: string]: any | undefined }; +} + +export interface Struct_FieldsEntry { + key: string; + value: any | undefined; +} + +/** + * `Value` represents a dynamically typed value which can be either + * null, a number, a string, a boolean, a recursive struct value, or a + * list of values. A producer of value is expected to set one of these + * variants. Absence of any variant indicates an error. + * + * The JSON representation for `Value` is JSON value. + */ +export interface Value { + /** Represents a null value. */ + nullValue?: + | NullValue + | undefined; + /** Represents a double value. */ + numberValue?: + | number + | undefined; + /** Represents a string value. */ + stringValue?: + | string + | undefined; + /** Represents a boolean value. */ + boolValue?: + | boolean + | undefined; + /** Represents a structured value. */ + structValue?: + | { [key: string]: any } + | undefined; + /** Represents a repeated `Value`. */ + listValue?: Array | undefined; +} + +/** + * `ListValue` is a wrapper around a repeated field of values. + * + * The JSON representation for `ListValue` is JSON array. + */ +export interface ListValue { + /** Repeated field of dynamically typed values. */ + values: any[]; +} + +function createBaseStruct(): Struct { + return { fields: {} }; +} + +export const Struct = { + encode(message: Struct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + Object.entries(message.fields).forEach(([key, value]) => { + if (value !== undefined) { + Struct_FieldsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + } + }); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Struct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStruct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + const entry1 = Struct_FieldsEntry.decode(reader, reader.uint32()); + if (entry1.value !== undefined) { + message.fields[entry1.key] = entry1.value; + } + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Struct { + return { + fields: isObject(object.fields) + ? Object.entries(object.fields).reduce<{ [key: string]: any | undefined }>((acc, [key, value]) => { + acc[key] = value as any | undefined; + return acc; + }, {}) + : {}, + }; + }, + + toJSON(message: Struct): unknown { + const obj: any = {}; + if (message.fields) { + const entries = Object.entries(message.fields); + if (entries.length > 0) { + obj.fields = {}; + entries.forEach(([k, v]) => { + obj.fields[k] = v; + }); + } + } + return obj; + }, + + create(base?: DeepPartial): Struct { + return Struct.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Struct { + const message = createBaseStruct(); + message.fields = Object.entries(object.fields ?? {}).reduce<{ [key: string]: any | undefined }>( + (acc, [key, value]) => { + if (value !== undefined) { + acc[key] = value; + } + return acc; + }, + {}, + ); + return message; + }, + + wrap(object: { [key: string]: any } | undefined): Struct { + const struct = createBaseStruct(); + if (object !== undefined) { + Object.keys(object).forEach((key) => { + struct.fields[key] = object[key]; + }); + } + return struct; + }, + + unwrap(message: Struct): { [key: string]: any } { + const object: { [key: string]: any } = {}; + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } + return object; + }, +}; + +function createBaseStruct_FieldsEntry(): Struct_FieldsEntry { + return { key: "", value: undefined }; +} + +export const Struct_FieldsEntry = { + encode(message: Struct_FieldsEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + Value.encode(Value.wrap(message.value), writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Struct_FieldsEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStruct_FieldsEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.value = Value.unwrap(Value.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Struct_FieldsEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object?.value) ? object.value : undefined, + }; + }, + + toJSON(message: Struct_FieldsEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = message.value; + } + return obj; + }, + + create(base?: DeepPartial): Struct_FieldsEntry { + return Struct_FieldsEntry.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Struct_FieldsEntry { + const message = createBaseStruct_FieldsEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? undefined; + return message; + }, +}; + +function createBaseValue(): Value { + return { + nullValue: undefined, + numberValue: undefined, + stringValue: undefined, + boolValue: undefined, + structValue: undefined, + listValue: undefined, + }; +} + +export const Value = { + encode(message: Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.nullValue !== undefined) { + writer.uint32(8).int32(message.nullValue); + } + if (message.numberValue !== undefined) { + writer.uint32(17).double(message.numberValue); + } + if (message.stringValue !== undefined) { + writer.uint32(26).string(message.stringValue); + } + if (message.boolValue !== undefined) { + writer.uint32(32).bool(message.boolValue); + } + if (message.structValue !== undefined) { + Struct.encode(Struct.wrap(message.structValue), writer.uint32(42).fork()).ldelim(); + } + if (message.listValue !== undefined) { + ListValue.encode(ListValue.wrap(message.listValue), writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Value { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.nullValue = reader.int32() as any; + continue; + case 2: + if (tag !== 17) { + break; + } + + message.numberValue = reader.double(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.stringValue = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.boolValue = reader.bool(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.structValue = Struct.unwrap(Struct.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.listValue = ListValue.unwrap(ListValue.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Value { + return { + nullValue: isSet(object.nullValue) ? nullValueFromJSON(object.nullValue) : undefined, + numberValue: isSet(object.numberValue) ? globalThis.Number(object.numberValue) : undefined, + stringValue: isSet(object.stringValue) ? globalThis.String(object.stringValue) : undefined, + boolValue: isSet(object.boolValue) ? globalThis.Boolean(object.boolValue) : undefined, + structValue: isObject(object.structValue) ? object.structValue : undefined, + listValue: globalThis.Array.isArray(object.listValue) ? [...object.listValue] : undefined, + }; + }, + + toJSON(message: Value): unknown { + const obj: any = {}; + if (message.nullValue !== undefined) { + obj.nullValue = nullValueToJSON(message.nullValue); + } + if (message.numberValue !== undefined) { + obj.numberValue = message.numberValue; + } + if (message.stringValue !== undefined) { + obj.stringValue = message.stringValue; + } + if (message.boolValue !== undefined) { + obj.boolValue = message.boolValue; + } + if (message.structValue !== undefined) { + obj.structValue = message.structValue; + } + if (message.listValue !== undefined) { + obj.listValue = message.listValue; + } + return obj; + }, + + create(base?: DeepPartial): Value { + return Value.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Value { + const message = createBaseValue(); + message.nullValue = object.nullValue ?? undefined; + message.numberValue = object.numberValue ?? undefined; + message.stringValue = object.stringValue ?? undefined; + message.boolValue = object.boolValue ?? undefined; + message.structValue = object.structValue ?? undefined; + message.listValue = object.listValue ?? undefined; + return message; + }, + + wrap(value: any): Value { + const result = createBaseValue(); + if (value === null) { + result.nullValue = NullValue.NULL_VALUE; + } else if (typeof value === "boolean") { + result.boolValue = value; + } else if (typeof value === "number") { + result.numberValue = value; + } else if (typeof value === "string") { + result.stringValue = value; + } else if (globalThis.Array.isArray(value)) { + result.listValue = value; + } else if (typeof value === "object") { + result.structValue = value; + } else if (typeof value !== "undefined") { + throw new globalThis.Error("Unsupported any value type: " + typeof value); + } + return result; + }, + + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { + return message.stringValue; + } else if (message?.numberValue !== undefined) { + return message.numberValue; + } else if (message?.boolValue !== undefined) { + return message.boolValue; + } else if (message?.structValue !== undefined) { + return message.structValue as any; + } else if (message?.listValue !== undefined) { + return message.listValue; + } else if (message?.nullValue !== undefined) { + return null; + } + return undefined; + }, +}; + +function createBaseListValue(): ListValue { + return { values: [] }; +} + +export const ListValue = { + encode(message: ListValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + Value.encode(Value.wrap(v!), writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ListValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(Value.unwrap(Value.decode(reader, reader.uint32()))); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ListValue { + return { values: globalThis.Array.isArray(object?.values) ? [...object.values] : [] }; + }, + + toJSON(message: ListValue): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): ListValue { + return ListValue.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ListValue { + const message = createBaseListValue(); + message.values = object.values?.map((e) => e) || []; + return message; + }, + + wrap(array: Array | undefined): ListValue { + const result = createBaseListValue(); + result.values = array ?? []; + return result; + }, + + unwrap(message: ListValue): Array { + if (message?.hasOwnProperty("values") && globalThis.Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/base.ts b/src/proto/v1/base.ts new file mode 100644 index 00000000..e27a47e9 --- /dev/null +++ b/src/proto/v1/base.ts @@ -0,0 +1,2069 @@ +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal.js"; +import { Struct } from "../google/protobuf/struct.js"; + +export const protobufPackage = "weaviate.v1"; + +export enum ConsistencyLevel { + CONSISTENCY_LEVEL_UNSPECIFIED = 0, + CONSISTENCY_LEVEL_ONE = 1, + CONSISTENCY_LEVEL_QUORUM = 2, + CONSISTENCY_LEVEL_ALL = 3, + UNRECOGNIZED = -1, +} + +export function consistencyLevelFromJSON(object: any): ConsistencyLevel { + switch (object) { + case 0: + case "CONSISTENCY_LEVEL_UNSPECIFIED": + return ConsistencyLevel.CONSISTENCY_LEVEL_UNSPECIFIED; + case 1: + case "CONSISTENCY_LEVEL_ONE": + return ConsistencyLevel.CONSISTENCY_LEVEL_ONE; + case 2: + case "CONSISTENCY_LEVEL_QUORUM": + return ConsistencyLevel.CONSISTENCY_LEVEL_QUORUM; + case 3: + case "CONSISTENCY_LEVEL_ALL": + return ConsistencyLevel.CONSISTENCY_LEVEL_ALL; + case -1: + case "UNRECOGNIZED": + default: + return ConsistencyLevel.UNRECOGNIZED; + } +} + +export function consistencyLevelToJSON(object: ConsistencyLevel): string { + switch (object) { + case ConsistencyLevel.CONSISTENCY_LEVEL_UNSPECIFIED: + return "CONSISTENCY_LEVEL_UNSPECIFIED"; + case ConsistencyLevel.CONSISTENCY_LEVEL_ONE: + return "CONSISTENCY_LEVEL_ONE"; + case ConsistencyLevel.CONSISTENCY_LEVEL_QUORUM: + return "CONSISTENCY_LEVEL_QUORUM"; + case ConsistencyLevel.CONSISTENCY_LEVEL_ALL: + return "CONSISTENCY_LEVEL_ALL"; + case ConsistencyLevel.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export interface NumberArrayProperties { + /** + * will be removed in the future, use vector_bytes + * + * @deprecated + */ + values: number[]; + propName: string; + valuesBytes: Uint8Array; +} + +export interface IntArrayProperties { + values: number[]; + propName: string; +} + +export interface TextArrayProperties { + values: string[]; + propName: string; +} + +export interface BooleanArrayProperties { + values: boolean[]; + propName: string; +} + +export interface ObjectPropertiesValue { + nonRefProperties: { [key: string]: any } | undefined; + numberArrayProperties: NumberArrayProperties[]; + intArrayProperties: IntArrayProperties[]; + textArrayProperties: TextArrayProperties[]; + booleanArrayProperties: BooleanArrayProperties[]; + objectProperties: ObjectProperties[]; + objectArrayProperties: ObjectArrayProperties[]; + emptyListProps: string[]; +} + +export interface ObjectArrayProperties { + values: ObjectPropertiesValue[]; + propName: string; +} + +export interface ObjectProperties { + value: ObjectPropertiesValue | undefined; + propName: string; +} + +export interface TextArray { + values: string[]; +} + +export interface IntArray { + values: number[]; +} + +export interface NumberArray { + values: number[]; +} + +export interface BooleanArray { + values: boolean[]; +} + +export interface Filters { + operator: Filters_Operator; + /** + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + * + * @deprecated + */ + on: string[]; + filters: Filters[]; + valueText?: string | undefined; + valueInt?: number | undefined; + valueBoolean?: boolean | undefined; + valueNumber?: number | undefined; + valueTextArray?: TextArray | undefined; + valueIntArray?: IntArray | undefined; + valueBooleanArray?: BooleanArray | undefined; + valueNumberArray?: NumberArray | undefined; + valueGeo?: + | GeoCoordinatesFilter + | undefined; + /** leave space for more filter values */ + target: FilterTarget | undefined; +} + +export enum Filters_Operator { + OPERATOR_UNSPECIFIED = 0, + OPERATOR_EQUAL = 1, + OPERATOR_NOT_EQUAL = 2, + OPERATOR_GREATER_THAN = 3, + OPERATOR_GREATER_THAN_EQUAL = 4, + OPERATOR_LESS_THAN = 5, + OPERATOR_LESS_THAN_EQUAL = 6, + OPERATOR_AND = 7, + OPERATOR_OR = 8, + OPERATOR_WITHIN_GEO_RANGE = 9, + OPERATOR_LIKE = 10, + OPERATOR_IS_NULL = 11, + OPERATOR_CONTAINS_ANY = 12, + OPERATOR_CONTAINS_ALL = 13, + UNRECOGNIZED = -1, +} + +export function filters_OperatorFromJSON(object: any): Filters_Operator { + switch (object) { + case 0: + case "OPERATOR_UNSPECIFIED": + return Filters_Operator.OPERATOR_UNSPECIFIED; + case 1: + case "OPERATOR_EQUAL": + return Filters_Operator.OPERATOR_EQUAL; + case 2: + case "OPERATOR_NOT_EQUAL": + return Filters_Operator.OPERATOR_NOT_EQUAL; + case 3: + case "OPERATOR_GREATER_THAN": + return Filters_Operator.OPERATOR_GREATER_THAN; + case 4: + case "OPERATOR_GREATER_THAN_EQUAL": + return Filters_Operator.OPERATOR_GREATER_THAN_EQUAL; + case 5: + case "OPERATOR_LESS_THAN": + return Filters_Operator.OPERATOR_LESS_THAN; + case 6: + case "OPERATOR_LESS_THAN_EQUAL": + return Filters_Operator.OPERATOR_LESS_THAN_EQUAL; + case 7: + case "OPERATOR_AND": + return Filters_Operator.OPERATOR_AND; + case 8: + case "OPERATOR_OR": + return Filters_Operator.OPERATOR_OR; + case 9: + case "OPERATOR_WITHIN_GEO_RANGE": + return Filters_Operator.OPERATOR_WITHIN_GEO_RANGE; + case 10: + case "OPERATOR_LIKE": + return Filters_Operator.OPERATOR_LIKE; + case 11: + case "OPERATOR_IS_NULL": + return Filters_Operator.OPERATOR_IS_NULL; + case 12: + case "OPERATOR_CONTAINS_ANY": + return Filters_Operator.OPERATOR_CONTAINS_ANY; + case 13: + case "OPERATOR_CONTAINS_ALL": + return Filters_Operator.OPERATOR_CONTAINS_ALL; + case -1: + case "UNRECOGNIZED": + default: + return Filters_Operator.UNRECOGNIZED; + } +} + +export function filters_OperatorToJSON(object: Filters_Operator): string { + switch (object) { + case Filters_Operator.OPERATOR_UNSPECIFIED: + return "OPERATOR_UNSPECIFIED"; + case Filters_Operator.OPERATOR_EQUAL: + return "OPERATOR_EQUAL"; + case Filters_Operator.OPERATOR_NOT_EQUAL: + return "OPERATOR_NOT_EQUAL"; + case Filters_Operator.OPERATOR_GREATER_THAN: + return "OPERATOR_GREATER_THAN"; + case Filters_Operator.OPERATOR_GREATER_THAN_EQUAL: + return "OPERATOR_GREATER_THAN_EQUAL"; + case Filters_Operator.OPERATOR_LESS_THAN: + return "OPERATOR_LESS_THAN"; + case Filters_Operator.OPERATOR_LESS_THAN_EQUAL: + return "OPERATOR_LESS_THAN_EQUAL"; + case Filters_Operator.OPERATOR_AND: + return "OPERATOR_AND"; + case Filters_Operator.OPERATOR_OR: + return "OPERATOR_OR"; + case Filters_Operator.OPERATOR_WITHIN_GEO_RANGE: + return "OPERATOR_WITHIN_GEO_RANGE"; + case Filters_Operator.OPERATOR_LIKE: + return "OPERATOR_LIKE"; + case Filters_Operator.OPERATOR_IS_NULL: + return "OPERATOR_IS_NULL"; + case Filters_Operator.OPERATOR_CONTAINS_ANY: + return "OPERATOR_CONTAINS_ANY"; + case Filters_Operator.OPERATOR_CONTAINS_ALL: + return "OPERATOR_CONTAINS_ALL"; + case Filters_Operator.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export interface FilterReferenceSingleTarget { + on: string; + target: FilterTarget | undefined; +} + +export interface FilterReferenceMultiTarget { + on: string; + target: FilterTarget | undefined; + targetCollection: string; +} + +export interface FilterReferenceCount { + on: string; +} + +export interface FilterTarget { + property?: string | undefined; + singleTarget?: FilterReferenceSingleTarget | undefined; + multiTarget?: FilterReferenceMultiTarget | undefined; + count?: FilterReferenceCount | undefined; +} + +export interface GeoCoordinatesFilter { + latitude: number; + longitude: number; + distance: number; +} + +export interface Vectors { + name: string; + /** for multi-vec */ + index: number; + vectorBytes: Uint8Array; +} + +function createBaseNumberArrayProperties(): NumberArrayProperties { + return { values: [], propName: "", valuesBytes: new Uint8Array(0) }; +} + +export const NumberArrayProperties = { + encode(message: NumberArrayProperties, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.values) { + writer.double(v); + } + writer.ldelim(); + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + if (message.valuesBytes.length !== 0) { + writer.uint32(26).bytes(message.valuesBytes); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NumberArrayProperties { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNumberArrayProperties(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 9) { + message.values.push(reader.double()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.values.push(reader.double()); + } + + continue; + } + + break; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.valuesBytes = reader.bytes(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NumberArrayProperties { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.Number(e)) : [], + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + valuesBytes: isSet(object.valuesBytes) ? bytesFromBase64(object.valuesBytes) : new Uint8Array(0), + }; + }, + + toJSON(message: NumberArrayProperties): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + if (message.propName !== "") { + obj.propName = message.propName; + } + if (message.valuesBytes.length !== 0) { + obj.valuesBytes = base64FromBytes(message.valuesBytes); + } + return obj; + }, + + create(base?: DeepPartial): NumberArrayProperties { + return NumberArrayProperties.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NumberArrayProperties { + const message = createBaseNumberArrayProperties(); + message.values = object.values?.map((e) => e) || []; + message.propName = object.propName ?? ""; + message.valuesBytes = object.valuesBytes ?? new Uint8Array(0); + return message; + }, +}; + +function createBaseIntArrayProperties(): IntArrayProperties { + return { values: [], propName: "" }; +} + +export const IntArrayProperties = { + encode(message: IntArrayProperties, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.values) { + writer.int64(v); + } + writer.ldelim(); + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): IntArrayProperties { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseIntArrayProperties(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.values.push(longToNumber(reader.int64() as Long)); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.values.push(longToNumber(reader.int64() as Long)); + } + + continue; + } + + break; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): IntArrayProperties { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.Number(e)) : [], + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + }; + }, + + toJSON(message: IntArrayProperties): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values.map((e) => Math.round(e)); + } + if (message.propName !== "") { + obj.propName = message.propName; + } + return obj; + }, + + create(base?: DeepPartial): IntArrayProperties { + return IntArrayProperties.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): IntArrayProperties { + const message = createBaseIntArrayProperties(); + message.values = object.values?.map((e) => e) || []; + message.propName = object.propName ?? ""; + return message; + }, +}; + +function createBaseTextArrayProperties(): TextArrayProperties { + return { values: [], propName: "" }; +} + +export const TextArrayProperties = { + encode(message: TextArrayProperties, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + writer.uint32(10).string(v!); + } + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TextArrayProperties { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTextArrayProperties(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(reader.string()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TextArrayProperties { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.String(e)) : [], + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + }; + }, + + toJSON(message: TextArrayProperties): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + if (message.propName !== "") { + obj.propName = message.propName; + } + return obj; + }, + + create(base?: DeepPartial): TextArrayProperties { + return TextArrayProperties.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): TextArrayProperties { + const message = createBaseTextArrayProperties(); + message.values = object.values?.map((e) => e) || []; + message.propName = object.propName ?? ""; + return message; + }, +}; + +function createBaseBooleanArrayProperties(): BooleanArrayProperties { + return { values: [], propName: "" }; +} + +export const BooleanArrayProperties = { + encode(message: BooleanArrayProperties, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.values) { + writer.bool(v); + } + writer.ldelim(); + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BooleanArrayProperties { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBooleanArrayProperties(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.values.push(reader.bool()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.values.push(reader.bool()); + } + + continue; + } + + break; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BooleanArrayProperties { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.Boolean(e)) : [], + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + }; + }, + + toJSON(message: BooleanArrayProperties): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + if (message.propName !== "") { + obj.propName = message.propName; + } + return obj; + }, + + create(base?: DeepPartial): BooleanArrayProperties { + return BooleanArrayProperties.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BooleanArrayProperties { + const message = createBaseBooleanArrayProperties(); + message.values = object.values?.map((e) => e) || []; + message.propName = object.propName ?? ""; + return message; + }, +}; + +function createBaseObjectPropertiesValue(): ObjectPropertiesValue { + return { + nonRefProperties: undefined, + numberArrayProperties: [], + intArrayProperties: [], + textArrayProperties: [], + booleanArrayProperties: [], + objectProperties: [], + objectArrayProperties: [], + emptyListProps: [], + }; +} + +export const ObjectPropertiesValue = { + encode(message: ObjectPropertiesValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.nonRefProperties !== undefined) { + Struct.encode(Struct.wrap(message.nonRefProperties), writer.uint32(10).fork()).ldelim(); + } + for (const v of message.numberArrayProperties) { + NumberArrayProperties.encode(v!, writer.uint32(18).fork()).ldelim(); + } + for (const v of message.intArrayProperties) { + IntArrayProperties.encode(v!, writer.uint32(26).fork()).ldelim(); + } + for (const v of message.textArrayProperties) { + TextArrayProperties.encode(v!, writer.uint32(34).fork()).ldelim(); + } + for (const v of message.booleanArrayProperties) { + BooleanArrayProperties.encode(v!, writer.uint32(42).fork()).ldelim(); + } + for (const v of message.objectProperties) { + ObjectProperties.encode(v!, writer.uint32(50).fork()).ldelim(); + } + for (const v of message.objectArrayProperties) { + ObjectArrayProperties.encode(v!, writer.uint32(58).fork()).ldelim(); + } + for (const v of message.emptyListProps) { + writer.uint32(82).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ObjectPropertiesValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseObjectPropertiesValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.nonRefProperties = Struct.unwrap(Struct.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.numberArrayProperties.push(NumberArrayProperties.decode(reader, reader.uint32())); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.intArrayProperties.push(IntArrayProperties.decode(reader, reader.uint32())); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.textArrayProperties.push(TextArrayProperties.decode(reader, reader.uint32())); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.booleanArrayProperties.push(BooleanArrayProperties.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.objectProperties.push(ObjectProperties.decode(reader, reader.uint32())); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.objectArrayProperties.push(ObjectArrayProperties.decode(reader, reader.uint32())); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.emptyListProps.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ObjectPropertiesValue { + return { + nonRefProperties: isObject(object.nonRefProperties) ? object.nonRefProperties : undefined, + numberArrayProperties: globalThis.Array.isArray(object?.numberArrayProperties) + ? object.numberArrayProperties.map((e: any) => NumberArrayProperties.fromJSON(e)) + : [], + intArrayProperties: globalThis.Array.isArray(object?.intArrayProperties) + ? object.intArrayProperties.map((e: any) => IntArrayProperties.fromJSON(e)) + : [], + textArrayProperties: globalThis.Array.isArray(object?.textArrayProperties) + ? object.textArrayProperties.map((e: any) => TextArrayProperties.fromJSON(e)) + : [], + booleanArrayProperties: globalThis.Array.isArray(object?.booleanArrayProperties) + ? object.booleanArrayProperties.map((e: any) => BooleanArrayProperties.fromJSON(e)) + : [], + objectProperties: globalThis.Array.isArray(object?.objectProperties) + ? object.objectProperties.map((e: any) => ObjectProperties.fromJSON(e)) + : [], + objectArrayProperties: globalThis.Array.isArray(object?.objectArrayProperties) + ? object.objectArrayProperties.map((e: any) => ObjectArrayProperties.fromJSON(e)) + : [], + emptyListProps: globalThis.Array.isArray(object?.emptyListProps) + ? object.emptyListProps.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: ObjectPropertiesValue): unknown { + const obj: any = {}; + if (message.nonRefProperties !== undefined) { + obj.nonRefProperties = message.nonRefProperties; + } + if (message.numberArrayProperties?.length) { + obj.numberArrayProperties = message.numberArrayProperties.map((e) => NumberArrayProperties.toJSON(e)); + } + if (message.intArrayProperties?.length) { + obj.intArrayProperties = message.intArrayProperties.map((e) => IntArrayProperties.toJSON(e)); + } + if (message.textArrayProperties?.length) { + obj.textArrayProperties = message.textArrayProperties.map((e) => TextArrayProperties.toJSON(e)); + } + if (message.booleanArrayProperties?.length) { + obj.booleanArrayProperties = message.booleanArrayProperties.map((e) => BooleanArrayProperties.toJSON(e)); + } + if (message.objectProperties?.length) { + obj.objectProperties = message.objectProperties.map((e) => ObjectProperties.toJSON(e)); + } + if (message.objectArrayProperties?.length) { + obj.objectArrayProperties = message.objectArrayProperties.map((e) => ObjectArrayProperties.toJSON(e)); + } + if (message.emptyListProps?.length) { + obj.emptyListProps = message.emptyListProps; + } + return obj; + }, + + create(base?: DeepPartial): ObjectPropertiesValue { + return ObjectPropertiesValue.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ObjectPropertiesValue { + const message = createBaseObjectPropertiesValue(); + message.nonRefProperties = object.nonRefProperties ?? undefined; + message.numberArrayProperties = object.numberArrayProperties?.map((e) => NumberArrayProperties.fromPartial(e)) || + []; + message.intArrayProperties = object.intArrayProperties?.map((e) => IntArrayProperties.fromPartial(e)) || []; + message.textArrayProperties = object.textArrayProperties?.map((e) => TextArrayProperties.fromPartial(e)) || []; + message.booleanArrayProperties = object.booleanArrayProperties?.map((e) => BooleanArrayProperties.fromPartial(e)) || + []; + message.objectProperties = object.objectProperties?.map((e) => ObjectProperties.fromPartial(e)) || []; + message.objectArrayProperties = object.objectArrayProperties?.map((e) => ObjectArrayProperties.fromPartial(e)) || + []; + message.emptyListProps = object.emptyListProps?.map((e) => e) || []; + return message; + }, +}; + +function createBaseObjectArrayProperties(): ObjectArrayProperties { + return { values: [], propName: "" }; +} + +export const ObjectArrayProperties = { + encode(message: ObjectArrayProperties, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + ObjectPropertiesValue.encode(v!, writer.uint32(10).fork()).ldelim(); + } + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ObjectArrayProperties { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseObjectArrayProperties(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(ObjectPropertiesValue.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ObjectArrayProperties { + return { + values: globalThis.Array.isArray(object?.values) + ? object.values.map((e: any) => ObjectPropertiesValue.fromJSON(e)) + : [], + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + }; + }, + + toJSON(message: ObjectArrayProperties): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values.map((e) => ObjectPropertiesValue.toJSON(e)); + } + if (message.propName !== "") { + obj.propName = message.propName; + } + return obj; + }, + + create(base?: DeepPartial): ObjectArrayProperties { + return ObjectArrayProperties.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ObjectArrayProperties { + const message = createBaseObjectArrayProperties(); + message.values = object.values?.map((e) => ObjectPropertiesValue.fromPartial(e)) || []; + message.propName = object.propName ?? ""; + return message; + }, +}; + +function createBaseObjectProperties(): ObjectProperties { + return { value: undefined, propName: "" }; +} + +export const ObjectProperties = { + encode(message: ObjectProperties, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== undefined) { + ObjectPropertiesValue.encode(message.value, writer.uint32(10).fork()).ldelim(); + } + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ObjectProperties { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseObjectProperties(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.value = ObjectPropertiesValue.decode(reader, reader.uint32()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ObjectProperties { + return { + value: isSet(object.value) ? ObjectPropertiesValue.fromJSON(object.value) : undefined, + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + }; + }, + + toJSON(message: ObjectProperties): unknown { + const obj: any = {}; + if (message.value !== undefined) { + obj.value = ObjectPropertiesValue.toJSON(message.value); + } + if (message.propName !== "") { + obj.propName = message.propName; + } + return obj; + }, + + create(base?: DeepPartial): ObjectProperties { + return ObjectProperties.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ObjectProperties { + const message = createBaseObjectProperties(); + message.value = (object.value !== undefined && object.value !== null) + ? ObjectPropertiesValue.fromPartial(object.value) + : undefined; + message.propName = object.propName ?? ""; + return message; + }, +}; + +function createBaseTextArray(): TextArray { + return { values: [] }; +} + +export const TextArray = { + encode(message: TextArray, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + writer.uint32(10).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TextArray { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTextArray(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TextArray { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: TextArray): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): TextArray { + return TextArray.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): TextArray { + const message = createBaseTextArray(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseIntArray(): IntArray { + return { values: [] }; +} + +export const IntArray = { + encode(message: IntArray, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.values) { + writer.int64(v); + } + writer.ldelim(); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): IntArray { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseIntArray(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.values.push(longToNumber(reader.int64() as Long)); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.values.push(longToNumber(reader.int64() as Long)); + } + + continue; + } + + break; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): IntArray { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.Number(e)) : [], + }; + }, + + toJSON(message: IntArray): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values.map((e) => Math.round(e)); + } + return obj; + }, + + create(base?: DeepPartial): IntArray { + return IntArray.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): IntArray { + const message = createBaseIntArray(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNumberArray(): NumberArray { + return { values: [] }; +} + +export const NumberArray = { + encode(message: NumberArray, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.values) { + writer.double(v); + } + writer.ldelim(); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NumberArray { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNumberArray(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 9) { + message.values.push(reader.double()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.values.push(reader.double()); + } + + continue; + } + + break; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NumberArray { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.Number(e)) : [], + }; + }, + + toJSON(message: NumberArray): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): NumberArray { + return NumberArray.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NumberArray { + const message = createBaseNumberArray(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseBooleanArray(): BooleanArray { + return { values: [] }; +} + +export const BooleanArray = { + encode(message: BooleanArray, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.values) { + writer.bool(v); + } + writer.ldelim(); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BooleanArray { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBooleanArray(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.values.push(reader.bool()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.values.push(reader.bool()); + } + + continue; + } + + break; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BooleanArray { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.Boolean(e)) : [], + }; + }, + + toJSON(message: BooleanArray): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): BooleanArray { + return BooleanArray.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BooleanArray { + const message = createBaseBooleanArray(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseFilters(): Filters { + return { + operator: 0, + on: [], + filters: [], + valueText: undefined, + valueInt: undefined, + valueBoolean: undefined, + valueNumber: undefined, + valueTextArray: undefined, + valueIntArray: undefined, + valueBooleanArray: undefined, + valueNumberArray: undefined, + valueGeo: undefined, + target: undefined, + }; +} + +export const Filters = { + encode(message: Filters, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.operator !== 0) { + writer.uint32(8).int32(message.operator); + } + for (const v of message.on) { + writer.uint32(18).string(v!); + } + for (const v of message.filters) { + Filters.encode(v!, writer.uint32(26).fork()).ldelim(); + } + if (message.valueText !== undefined) { + writer.uint32(34).string(message.valueText); + } + if (message.valueInt !== undefined) { + writer.uint32(40).int64(message.valueInt); + } + if (message.valueBoolean !== undefined) { + writer.uint32(48).bool(message.valueBoolean); + } + if (message.valueNumber !== undefined) { + writer.uint32(57).double(message.valueNumber); + } + if (message.valueTextArray !== undefined) { + TextArray.encode(message.valueTextArray, writer.uint32(74).fork()).ldelim(); + } + if (message.valueIntArray !== undefined) { + IntArray.encode(message.valueIntArray, writer.uint32(82).fork()).ldelim(); + } + if (message.valueBooleanArray !== undefined) { + BooleanArray.encode(message.valueBooleanArray, writer.uint32(90).fork()).ldelim(); + } + if (message.valueNumberArray !== undefined) { + NumberArray.encode(message.valueNumberArray, writer.uint32(98).fork()).ldelim(); + } + if (message.valueGeo !== undefined) { + GeoCoordinatesFilter.encode(message.valueGeo, writer.uint32(106).fork()).ldelim(); + } + if (message.target !== undefined) { + FilterTarget.encode(message.target, writer.uint32(162).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Filters { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFilters(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.operator = reader.int32() as any; + continue; + case 2: + if (tag !== 18) { + break; + } + + message.on.push(reader.string()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.filters.push(Filters.decode(reader, reader.uint32())); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.valueText = reader.string(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.valueInt = longToNumber(reader.int64() as Long); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.valueBoolean = reader.bool(); + continue; + case 7: + if (tag !== 57) { + break; + } + + message.valueNumber = reader.double(); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.valueTextArray = TextArray.decode(reader, reader.uint32()); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.valueIntArray = IntArray.decode(reader, reader.uint32()); + continue; + case 11: + if (tag !== 90) { + break; + } + + message.valueBooleanArray = BooleanArray.decode(reader, reader.uint32()); + continue; + case 12: + if (tag !== 98) { + break; + } + + message.valueNumberArray = NumberArray.decode(reader, reader.uint32()); + continue; + case 13: + if (tag !== 106) { + break; + } + + message.valueGeo = GeoCoordinatesFilter.decode(reader, reader.uint32()); + continue; + case 20: + if (tag !== 162) { + break; + } + + message.target = FilterTarget.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Filters { + return { + operator: isSet(object.operator) ? filters_OperatorFromJSON(object.operator) : 0, + on: globalThis.Array.isArray(object?.on) ? object.on.map((e: any) => globalThis.String(e)) : [], + filters: globalThis.Array.isArray(object?.filters) ? object.filters.map((e: any) => Filters.fromJSON(e)) : [], + valueText: isSet(object.valueText) ? globalThis.String(object.valueText) : undefined, + valueInt: isSet(object.valueInt) ? globalThis.Number(object.valueInt) : undefined, + valueBoolean: isSet(object.valueBoolean) ? globalThis.Boolean(object.valueBoolean) : undefined, + valueNumber: isSet(object.valueNumber) ? globalThis.Number(object.valueNumber) : undefined, + valueTextArray: isSet(object.valueTextArray) ? TextArray.fromJSON(object.valueTextArray) : undefined, + valueIntArray: isSet(object.valueIntArray) ? IntArray.fromJSON(object.valueIntArray) : undefined, + valueBooleanArray: isSet(object.valueBooleanArray) ? BooleanArray.fromJSON(object.valueBooleanArray) : undefined, + valueNumberArray: isSet(object.valueNumberArray) ? NumberArray.fromJSON(object.valueNumberArray) : undefined, + valueGeo: isSet(object.valueGeo) ? GeoCoordinatesFilter.fromJSON(object.valueGeo) : undefined, + target: isSet(object.target) ? FilterTarget.fromJSON(object.target) : undefined, + }; + }, + + toJSON(message: Filters): unknown { + const obj: any = {}; + if (message.operator !== 0) { + obj.operator = filters_OperatorToJSON(message.operator); + } + if (message.on?.length) { + obj.on = message.on; + } + if (message.filters?.length) { + obj.filters = message.filters.map((e) => Filters.toJSON(e)); + } + if (message.valueText !== undefined) { + obj.valueText = message.valueText; + } + if (message.valueInt !== undefined) { + obj.valueInt = Math.round(message.valueInt); + } + if (message.valueBoolean !== undefined) { + obj.valueBoolean = message.valueBoolean; + } + if (message.valueNumber !== undefined) { + obj.valueNumber = message.valueNumber; + } + if (message.valueTextArray !== undefined) { + obj.valueTextArray = TextArray.toJSON(message.valueTextArray); + } + if (message.valueIntArray !== undefined) { + obj.valueIntArray = IntArray.toJSON(message.valueIntArray); + } + if (message.valueBooleanArray !== undefined) { + obj.valueBooleanArray = BooleanArray.toJSON(message.valueBooleanArray); + } + if (message.valueNumberArray !== undefined) { + obj.valueNumberArray = NumberArray.toJSON(message.valueNumberArray); + } + if (message.valueGeo !== undefined) { + obj.valueGeo = GeoCoordinatesFilter.toJSON(message.valueGeo); + } + if (message.target !== undefined) { + obj.target = FilterTarget.toJSON(message.target); + } + return obj; + }, + + create(base?: DeepPartial): Filters { + return Filters.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Filters { + const message = createBaseFilters(); + message.operator = object.operator ?? 0; + message.on = object.on?.map((e) => e) || []; + message.filters = object.filters?.map((e) => Filters.fromPartial(e)) || []; + message.valueText = object.valueText ?? undefined; + message.valueInt = object.valueInt ?? undefined; + message.valueBoolean = object.valueBoolean ?? undefined; + message.valueNumber = object.valueNumber ?? undefined; + message.valueTextArray = (object.valueTextArray !== undefined && object.valueTextArray !== null) + ? TextArray.fromPartial(object.valueTextArray) + : undefined; + message.valueIntArray = (object.valueIntArray !== undefined && object.valueIntArray !== null) + ? IntArray.fromPartial(object.valueIntArray) + : undefined; + message.valueBooleanArray = (object.valueBooleanArray !== undefined && object.valueBooleanArray !== null) + ? BooleanArray.fromPartial(object.valueBooleanArray) + : undefined; + message.valueNumberArray = (object.valueNumberArray !== undefined && object.valueNumberArray !== null) + ? NumberArray.fromPartial(object.valueNumberArray) + : undefined; + message.valueGeo = (object.valueGeo !== undefined && object.valueGeo !== null) + ? GeoCoordinatesFilter.fromPartial(object.valueGeo) + : undefined; + message.target = (object.target !== undefined && object.target !== null) + ? FilterTarget.fromPartial(object.target) + : undefined; + return message; + }, +}; + +function createBaseFilterReferenceSingleTarget(): FilterReferenceSingleTarget { + return { on: "", target: undefined }; +} + +export const FilterReferenceSingleTarget = { + encode(message: FilterReferenceSingleTarget, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.on !== "") { + writer.uint32(10).string(message.on); + } + if (message.target !== undefined) { + FilterTarget.encode(message.target, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FilterReferenceSingleTarget { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFilterReferenceSingleTarget(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.on = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.target = FilterTarget.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FilterReferenceSingleTarget { + return { + on: isSet(object.on) ? globalThis.String(object.on) : "", + target: isSet(object.target) ? FilterTarget.fromJSON(object.target) : undefined, + }; + }, + + toJSON(message: FilterReferenceSingleTarget): unknown { + const obj: any = {}; + if (message.on !== "") { + obj.on = message.on; + } + if (message.target !== undefined) { + obj.target = FilterTarget.toJSON(message.target); + } + return obj; + }, + + create(base?: DeepPartial): FilterReferenceSingleTarget { + return FilterReferenceSingleTarget.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): FilterReferenceSingleTarget { + const message = createBaseFilterReferenceSingleTarget(); + message.on = object.on ?? ""; + message.target = (object.target !== undefined && object.target !== null) + ? FilterTarget.fromPartial(object.target) + : undefined; + return message; + }, +}; + +function createBaseFilterReferenceMultiTarget(): FilterReferenceMultiTarget { + return { on: "", target: undefined, targetCollection: "" }; +} + +export const FilterReferenceMultiTarget = { + encode(message: FilterReferenceMultiTarget, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.on !== "") { + writer.uint32(10).string(message.on); + } + if (message.target !== undefined) { + FilterTarget.encode(message.target, writer.uint32(18).fork()).ldelim(); + } + if (message.targetCollection !== "") { + writer.uint32(26).string(message.targetCollection); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FilterReferenceMultiTarget { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFilterReferenceMultiTarget(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.on = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.target = FilterTarget.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.targetCollection = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FilterReferenceMultiTarget { + return { + on: isSet(object.on) ? globalThis.String(object.on) : "", + target: isSet(object.target) ? FilterTarget.fromJSON(object.target) : undefined, + targetCollection: isSet(object.targetCollection) ? globalThis.String(object.targetCollection) : "", + }; + }, + + toJSON(message: FilterReferenceMultiTarget): unknown { + const obj: any = {}; + if (message.on !== "") { + obj.on = message.on; + } + if (message.target !== undefined) { + obj.target = FilterTarget.toJSON(message.target); + } + if (message.targetCollection !== "") { + obj.targetCollection = message.targetCollection; + } + return obj; + }, + + create(base?: DeepPartial): FilterReferenceMultiTarget { + return FilterReferenceMultiTarget.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): FilterReferenceMultiTarget { + const message = createBaseFilterReferenceMultiTarget(); + message.on = object.on ?? ""; + message.target = (object.target !== undefined && object.target !== null) + ? FilterTarget.fromPartial(object.target) + : undefined; + message.targetCollection = object.targetCollection ?? ""; + return message; + }, +}; + +function createBaseFilterReferenceCount(): FilterReferenceCount { + return { on: "" }; +} + +export const FilterReferenceCount = { + encode(message: FilterReferenceCount, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.on !== "") { + writer.uint32(10).string(message.on); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FilterReferenceCount { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFilterReferenceCount(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.on = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FilterReferenceCount { + return { on: isSet(object.on) ? globalThis.String(object.on) : "" }; + }, + + toJSON(message: FilterReferenceCount): unknown { + const obj: any = {}; + if (message.on !== "") { + obj.on = message.on; + } + return obj; + }, + + create(base?: DeepPartial): FilterReferenceCount { + return FilterReferenceCount.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): FilterReferenceCount { + const message = createBaseFilterReferenceCount(); + message.on = object.on ?? ""; + return message; + }, +}; + +function createBaseFilterTarget(): FilterTarget { + return { property: undefined, singleTarget: undefined, multiTarget: undefined, count: undefined }; +} + +export const FilterTarget = { + encode(message: FilterTarget, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.property !== undefined) { + writer.uint32(10).string(message.property); + } + if (message.singleTarget !== undefined) { + FilterReferenceSingleTarget.encode(message.singleTarget, writer.uint32(18).fork()).ldelim(); + } + if (message.multiTarget !== undefined) { + FilterReferenceMultiTarget.encode(message.multiTarget, writer.uint32(26).fork()).ldelim(); + } + if (message.count !== undefined) { + FilterReferenceCount.encode(message.count, writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FilterTarget { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFilterTarget(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.property = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.singleTarget = FilterReferenceSingleTarget.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.multiTarget = FilterReferenceMultiTarget.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.count = FilterReferenceCount.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FilterTarget { + return { + property: isSet(object.property) ? globalThis.String(object.property) : undefined, + singleTarget: isSet(object.singleTarget) ? FilterReferenceSingleTarget.fromJSON(object.singleTarget) : undefined, + multiTarget: isSet(object.multiTarget) ? FilterReferenceMultiTarget.fromJSON(object.multiTarget) : undefined, + count: isSet(object.count) ? FilterReferenceCount.fromJSON(object.count) : undefined, + }; + }, + + toJSON(message: FilterTarget): unknown { + const obj: any = {}; + if (message.property !== undefined) { + obj.property = message.property; + } + if (message.singleTarget !== undefined) { + obj.singleTarget = FilterReferenceSingleTarget.toJSON(message.singleTarget); + } + if (message.multiTarget !== undefined) { + obj.multiTarget = FilterReferenceMultiTarget.toJSON(message.multiTarget); + } + if (message.count !== undefined) { + obj.count = FilterReferenceCount.toJSON(message.count); + } + return obj; + }, + + create(base?: DeepPartial): FilterTarget { + return FilterTarget.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): FilterTarget { + const message = createBaseFilterTarget(); + message.property = object.property ?? undefined; + message.singleTarget = (object.singleTarget !== undefined && object.singleTarget !== null) + ? FilterReferenceSingleTarget.fromPartial(object.singleTarget) + : undefined; + message.multiTarget = (object.multiTarget !== undefined && object.multiTarget !== null) + ? FilterReferenceMultiTarget.fromPartial(object.multiTarget) + : undefined; + message.count = (object.count !== undefined && object.count !== null) + ? FilterReferenceCount.fromPartial(object.count) + : undefined; + return message; + }, +}; + +function createBaseGeoCoordinatesFilter(): GeoCoordinatesFilter { + return { latitude: 0, longitude: 0, distance: 0 }; +} + +export const GeoCoordinatesFilter = { + encode(message: GeoCoordinatesFilter, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.latitude !== 0) { + writer.uint32(13).float(message.latitude); + } + if (message.longitude !== 0) { + writer.uint32(21).float(message.longitude); + } + if (message.distance !== 0) { + writer.uint32(29).float(message.distance); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GeoCoordinatesFilter { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGeoCoordinatesFilter(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.latitude = reader.float(); + continue; + case 2: + if (tag !== 21) { + break; + } + + message.longitude = reader.float(); + continue; + case 3: + if (tag !== 29) { + break; + } + + message.distance = reader.float(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GeoCoordinatesFilter { + return { + latitude: isSet(object.latitude) ? globalThis.Number(object.latitude) : 0, + longitude: isSet(object.longitude) ? globalThis.Number(object.longitude) : 0, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : 0, + }; + }, + + toJSON(message: GeoCoordinatesFilter): unknown { + const obj: any = {}; + if (message.latitude !== 0) { + obj.latitude = message.latitude; + } + if (message.longitude !== 0) { + obj.longitude = message.longitude; + } + if (message.distance !== 0) { + obj.distance = message.distance; + } + return obj; + }, + + create(base?: DeepPartial): GeoCoordinatesFilter { + return GeoCoordinatesFilter.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GeoCoordinatesFilter { + const message = createBaseGeoCoordinatesFilter(); + message.latitude = object.latitude ?? 0; + message.longitude = object.longitude ?? 0; + message.distance = object.distance ?? 0; + return message; + }, +}; + +function createBaseVectors(): Vectors { + return { name: "", index: 0, vectorBytes: new Uint8Array(0) }; +} + +export const Vectors = { + encode(message: Vectors, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.index !== 0) { + writer.uint32(16).uint64(message.index); + } + if (message.vectorBytes.length !== 0) { + writer.uint32(26).bytes(message.vectorBytes); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Vectors { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVectors(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.index = longToNumber(reader.uint64() as Long); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.vectorBytes = reader.bytes(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Vectors { + return { + name: isSet(object.name) ? globalThis.String(object.name) : "", + index: isSet(object.index) ? globalThis.Number(object.index) : 0, + vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + }; + }, + + toJSON(message: Vectors): unknown { + const obj: any = {}; + if (message.name !== "") { + obj.name = message.name; + } + if (message.index !== 0) { + obj.index = Math.round(message.index); + } + if (message.vectorBytes.length !== 0) { + obj.vectorBytes = base64FromBytes(message.vectorBytes); + } + return obj; + }, + + create(base?: DeepPartial): Vectors { + return Vectors.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Vectors { + const message = createBaseVectors(); + message.name = object.name ?? ""; + message.index = object.index ?? 0; + message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + return message; + }, +}; + +function bytesFromBase64(b64: string): Uint8Array { + if ((globalThis as any).Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); + } else { + const bin = globalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if ((globalThis as any).Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(globalThis.String.fromCharCode(byte)); + }); + return globalThis.btoa(bin.join("")); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/batch.ts b/src/proto/v1/batch.ts new file mode 100644 index 00000000..6bfc738a --- /dev/null +++ b/src/proto/v1/batch.ts @@ -0,0 +1,903 @@ +/* eslint-disable */ +import _m0 from "protobufjs/minimal.js"; +import { Struct } from "../google/protobuf/struct.js"; +import { + BooleanArrayProperties, + ConsistencyLevel, + consistencyLevelFromJSON, + consistencyLevelToJSON, + IntArrayProperties, + NumberArrayProperties, + ObjectArrayProperties, + ObjectProperties, + TextArrayProperties, + Vectors, +} from "./base.js"; + +export const protobufPackage = "weaviate.v1"; + +export interface BatchObjectsRequest { + objects: BatchObject[]; + consistencyLevel?: ConsistencyLevel | undefined; +} + +export interface BatchObject { + uuid: string; + /** + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + * + * @deprecated + */ + vector: number[]; + properties: BatchObject_Properties | undefined; + collection: string; + tenant: string; + vectorBytes: Uint8Array; + /** protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED */ + vectors: Vectors[]; +} + +export interface BatchObject_Properties { + nonRefProperties: { [key: string]: any } | undefined; + singleTargetRefProps: BatchObject_SingleTargetRefProps[]; + multiTargetRefProps: BatchObject_MultiTargetRefProps[]; + numberArrayProperties: NumberArrayProperties[]; + intArrayProperties: IntArrayProperties[]; + textArrayProperties: TextArrayProperties[]; + booleanArrayProperties: BooleanArrayProperties[]; + objectProperties: ObjectProperties[]; + objectArrayProperties: ObjectArrayProperties[]; + /** + * empty lists do not have a type in many languages and clients do not know which datatype the property has. + * Weaviate can get the datatype from its schema + */ + emptyListProps: string[]; +} + +export interface BatchObject_SingleTargetRefProps { + uuids: string[]; + propName: string; +} + +export interface BatchObject_MultiTargetRefProps { + uuids: string[]; + propName: string; + targetCollection: string; +} + +export interface BatchObjectsReply { + took: number; + errors: BatchObjectsReply_BatchError[]; +} + +export interface BatchObjectsReply_BatchError { + index: number; + error: string; +} + +function createBaseBatchObjectsRequest(): BatchObjectsRequest { + return { objects: [], consistencyLevel: undefined }; +} + +export const BatchObjectsRequest = { + encode(message: BatchObjectsRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.objects) { + BatchObject.encode(v!, writer.uint32(10).fork()).ldelim(); + } + if (message.consistencyLevel !== undefined) { + writer.uint32(16).int32(message.consistencyLevel); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchObjectsRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchObjectsRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.objects.push(BatchObject.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.consistencyLevel = reader.int32() as any; + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchObjectsRequest { + return { + objects: globalThis.Array.isArray(object?.objects) ? object.objects.map((e: any) => BatchObject.fromJSON(e)) : [], + consistencyLevel: isSet(object.consistencyLevel) ? consistencyLevelFromJSON(object.consistencyLevel) : undefined, + }; + }, + + toJSON(message: BatchObjectsRequest): unknown { + const obj: any = {}; + if (message.objects?.length) { + obj.objects = message.objects.map((e) => BatchObject.toJSON(e)); + } + if (message.consistencyLevel !== undefined) { + obj.consistencyLevel = consistencyLevelToJSON(message.consistencyLevel); + } + return obj; + }, + + create(base?: DeepPartial): BatchObjectsRequest { + return BatchObjectsRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchObjectsRequest { + const message = createBaseBatchObjectsRequest(); + message.objects = object.objects?.map((e) => BatchObject.fromPartial(e)) || []; + message.consistencyLevel = object.consistencyLevel ?? undefined; + return message; + }, +}; + +function createBaseBatchObject(): BatchObject { + return { + uuid: "", + vector: [], + properties: undefined, + collection: "", + tenant: "", + vectorBytes: new Uint8Array(0), + vectors: [], + }; +} + +export const BatchObject = { + encode(message: BatchObject, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.uuid !== "") { + writer.uint32(10).string(message.uuid); + } + writer.uint32(18).fork(); + for (const v of message.vector) { + writer.float(v); + } + writer.ldelim(); + if (message.properties !== undefined) { + BatchObject_Properties.encode(message.properties, writer.uint32(26).fork()).ldelim(); + } + if (message.collection !== "") { + writer.uint32(34).string(message.collection); + } + if (message.tenant !== "") { + writer.uint32(42).string(message.tenant); + } + if (message.vectorBytes.length !== 0) { + writer.uint32(50).bytes(message.vectorBytes); + } + for (const v of message.vectors) { + Vectors.encode(v!, writer.uint32(186).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchObject { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.uuid = reader.string(); + continue; + case 2: + if (tag === 21) { + message.vector.push(reader.float()); + + continue; + } + + if (tag === 18) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.vector.push(reader.float()); + } + + continue; + } + + break; + case 3: + if (tag !== 26) { + break; + } + + message.properties = BatchObject_Properties.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.collection = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.tenant = reader.string(); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.vectorBytes = reader.bytes(); + continue; + case 23: + if (tag !== 186) { + break; + } + + message.vectors.push(Vectors.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchObject { + return { + uuid: isSet(object.uuid) ? globalThis.String(object.uuid) : "", + vector: globalThis.Array.isArray(object?.vector) ? object.vector.map((e: any) => globalThis.Number(e)) : [], + properties: isSet(object.properties) ? BatchObject_Properties.fromJSON(object.properties) : undefined, + collection: isSet(object.collection) ? globalThis.String(object.collection) : "", + tenant: isSet(object.tenant) ? globalThis.String(object.tenant) : "", + vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + vectors: globalThis.Array.isArray(object?.vectors) ? object.vectors.map((e: any) => Vectors.fromJSON(e)) : [], + }; + }, + + toJSON(message: BatchObject): unknown { + const obj: any = {}; + if (message.uuid !== "") { + obj.uuid = message.uuid; + } + if (message.vector?.length) { + obj.vector = message.vector; + } + if (message.properties !== undefined) { + obj.properties = BatchObject_Properties.toJSON(message.properties); + } + if (message.collection !== "") { + obj.collection = message.collection; + } + if (message.tenant !== "") { + obj.tenant = message.tenant; + } + if (message.vectorBytes.length !== 0) { + obj.vectorBytes = base64FromBytes(message.vectorBytes); + } + if (message.vectors?.length) { + obj.vectors = message.vectors.map((e) => Vectors.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): BatchObject { + return BatchObject.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchObject { + const message = createBaseBatchObject(); + message.uuid = object.uuid ?? ""; + message.vector = object.vector?.map((e) => e) || []; + message.properties = (object.properties !== undefined && object.properties !== null) + ? BatchObject_Properties.fromPartial(object.properties) + : undefined; + message.collection = object.collection ?? ""; + message.tenant = object.tenant ?? ""; + message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + message.vectors = object.vectors?.map((e) => Vectors.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseBatchObject_Properties(): BatchObject_Properties { + return { + nonRefProperties: undefined, + singleTargetRefProps: [], + multiTargetRefProps: [], + numberArrayProperties: [], + intArrayProperties: [], + textArrayProperties: [], + booleanArrayProperties: [], + objectProperties: [], + objectArrayProperties: [], + emptyListProps: [], + }; +} + +export const BatchObject_Properties = { + encode(message: BatchObject_Properties, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.nonRefProperties !== undefined) { + Struct.encode(Struct.wrap(message.nonRefProperties), writer.uint32(10).fork()).ldelim(); + } + for (const v of message.singleTargetRefProps) { + BatchObject_SingleTargetRefProps.encode(v!, writer.uint32(18).fork()).ldelim(); + } + for (const v of message.multiTargetRefProps) { + BatchObject_MultiTargetRefProps.encode(v!, writer.uint32(26).fork()).ldelim(); + } + for (const v of message.numberArrayProperties) { + NumberArrayProperties.encode(v!, writer.uint32(34).fork()).ldelim(); + } + for (const v of message.intArrayProperties) { + IntArrayProperties.encode(v!, writer.uint32(42).fork()).ldelim(); + } + for (const v of message.textArrayProperties) { + TextArrayProperties.encode(v!, writer.uint32(50).fork()).ldelim(); + } + for (const v of message.booleanArrayProperties) { + BooleanArrayProperties.encode(v!, writer.uint32(58).fork()).ldelim(); + } + for (const v of message.objectProperties) { + ObjectProperties.encode(v!, writer.uint32(66).fork()).ldelim(); + } + for (const v of message.objectArrayProperties) { + ObjectArrayProperties.encode(v!, writer.uint32(74).fork()).ldelim(); + } + for (const v of message.emptyListProps) { + writer.uint32(82).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchObject_Properties { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchObject_Properties(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.nonRefProperties = Struct.unwrap(Struct.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.singleTargetRefProps.push(BatchObject_SingleTargetRefProps.decode(reader, reader.uint32())); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.multiTargetRefProps.push(BatchObject_MultiTargetRefProps.decode(reader, reader.uint32())); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.numberArrayProperties.push(NumberArrayProperties.decode(reader, reader.uint32())); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.intArrayProperties.push(IntArrayProperties.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.textArrayProperties.push(TextArrayProperties.decode(reader, reader.uint32())); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.booleanArrayProperties.push(BooleanArrayProperties.decode(reader, reader.uint32())); + continue; + case 8: + if (tag !== 66) { + break; + } + + message.objectProperties.push(ObjectProperties.decode(reader, reader.uint32())); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.objectArrayProperties.push(ObjectArrayProperties.decode(reader, reader.uint32())); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.emptyListProps.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchObject_Properties { + return { + nonRefProperties: isObject(object.nonRefProperties) ? object.nonRefProperties : undefined, + singleTargetRefProps: globalThis.Array.isArray(object?.singleTargetRefProps) + ? object.singleTargetRefProps.map((e: any) => BatchObject_SingleTargetRefProps.fromJSON(e)) + : [], + multiTargetRefProps: globalThis.Array.isArray(object?.multiTargetRefProps) + ? object.multiTargetRefProps.map((e: any) => BatchObject_MultiTargetRefProps.fromJSON(e)) + : [], + numberArrayProperties: globalThis.Array.isArray(object?.numberArrayProperties) + ? object.numberArrayProperties.map((e: any) => NumberArrayProperties.fromJSON(e)) + : [], + intArrayProperties: globalThis.Array.isArray(object?.intArrayProperties) + ? object.intArrayProperties.map((e: any) => IntArrayProperties.fromJSON(e)) + : [], + textArrayProperties: globalThis.Array.isArray(object?.textArrayProperties) + ? object.textArrayProperties.map((e: any) => TextArrayProperties.fromJSON(e)) + : [], + booleanArrayProperties: globalThis.Array.isArray(object?.booleanArrayProperties) + ? object.booleanArrayProperties.map((e: any) => BooleanArrayProperties.fromJSON(e)) + : [], + objectProperties: globalThis.Array.isArray(object?.objectProperties) + ? object.objectProperties.map((e: any) => ObjectProperties.fromJSON(e)) + : [], + objectArrayProperties: globalThis.Array.isArray(object?.objectArrayProperties) + ? object.objectArrayProperties.map((e: any) => ObjectArrayProperties.fromJSON(e)) + : [], + emptyListProps: globalThis.Array.isArray(object?.emptyListProps) + ? object.emptyListProps.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: BatchObject_Properties): unknown { + const obj: any = {}; + if (message.nonRefProperties !== undefined) { + obj.nonRefProperties = message.nonRefProperties; + } + if (message.singleTargetRefProps?.length) { + obj.singleTargetRefProps = message.singleTargetRefProps.map((e) => BatchObject_SingleTargetRefProps.toJSON(e)); + } + if (message.multiTargetRefProps?.length) { + obj.multiTargetRefProps = message.multiTargetRefProps.map((e) => BatchObject_MultiTargetRefProps.toJSON(e)); + } + if (message.numberArrayProperties?.length) { + obj.numberArrayProperties = message.numberArrayProperties.map((e) => NumberArrayProperties.toJSON(e)); + } + if (message.intArrayProperties?.length) { + obj.intArrayProperties = message.intArrayProperties.map((e) => IntArrayProperties.toJSON(e)); + } + if (message.textArrayProperties?.length) { + obj.textArrayProperties = message.textArrayProperties.map((e) => TextArrayProperties.toJSON(e)); + } + if (message.booleanArrayProperties?.length) { + obj.booleanArrayProperties = message.booleanArrayProperties.map((e) => BooleanArrayProperties.toJSON(e)); + } + if (message.objectProperties?.length) { + obj.objectProperties = message.objectProperties.map((e) => ObjectProperties.toJSON(e)); + } + if (message.objectArrayProperties?.length) { + obj.objectArrayProperties = message.objectArrayProperties.map((e) => ObjectArrayProperties.toJSON(e)); + } + if (message.emptyListProps?.length) { + obj.emptyListProps = message.emptyListProps; + } + return obj; + }, + + create(base?: DeepPartial): BatchObject_Properties { + return BatchObject_Properties.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchObject_Properties { + const message = createBaseBatchObject_Properties(); + message.nonRefProperties = object.nonRefProperties ?? undefined; + message.singleTargetRefProps = + object.singleTargetRefProps?.map((e) => BatchObject_SingleTargetRefProps.fromPartial(e)) || []; + message.multiTargetRefProps = + object.multiTargetRefProps?.map((e) => BatchObject_MultiTargetRefProps.fromPartial(e)) || []; + message.numberArrayProperties = object.numberArrayProperties?.map((e) => NumberArrayProperties.fromPartial(e)) || + []; + message.intArrayProperties = object.intArrayProperties?.map((e) => IntArrayProperties.fromPartial(e)) || []; + message.textArrayProperties = object.textArrayProperties?.map((e) => TextArrayProperties.fromPartial(e)) || []; + message.booleanArrayProperties = object.booleanArrayProperties?.map((e) => BooleanArrayProperties.fromPartial(e)) || + []; + message.objectProperties = object.objectProperties?.map((e) => ObjectProperties.fromPartial(e)) || []; + message.objectArrayProperties = object.objectArrayProperties?.map((e) => ObjectArrayProperties.fromPartial(e)) || + []; + message.emptyListProps = object.emptyListProps?.map((e) => e) || []; + return message; + }, +}; + +function createBaseBatchObject_SingleTargetRefProps(): BatchObject_SingleTargetRefProps { + return { uuids: [], propName: "" }; +} + +export const BatchObject_SingleTargetRefProps = { + encode(message: BatchObject_SingleTargetRefProps, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.uuids) { + writer.uint32(10).string(v!); + } + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchObject_SingleTargetRefProps { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchObject_SingleTargetRefProps(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.uuids.push(reader.string()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchObject_SingleTargetRefProps { + return { + uuids: globalThis.Array.isArray(object?.uuids) ? object.uuids.map((e: any) => globalThis.String(e)) : [], + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + }; + }, + + toJSON(message: BatchObject_SingleTargetRefProps): unknown { + const obj: any = {}; + if (message.uuids?.length) { + obj.uuids = message.uuids; + } + if (message.propName !== "") { + obj.propName = message.propName; + } + return obj; + }, + + create(base?: DeepPartial): BatchObject_SingleTargetRefProps { + return BatchObject_SingleTargetRefProps.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchObject_SingleTargetRefProps { + const message = createBaseBatchObject_SingleTargetRefProps(); + message.uuids = object.uuids?.map((e) => e) || []; + message.propName = object.propName ?? ""; + return message; + }, +}; + +function createBaseBatchObject_MultiTargetRefProps(): BatchObject_MultiTargetRefProps { + return { uuids: [], propName: "", targetCollection: "" }; +} + +export const BatchObject_MultiTargetRefProps = { + encode(message: BatchObject_MultiTargetRefProps, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.uuids) { + writer.uint32(10).string(v!); + } + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + if (message.targetCollection !== "") { + writer.uint32(26).string(message.targetCollection); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchObject_MultiTargetRefProps { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchObject_MultiTargetRefProps(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.uuids.push(reader.string()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.targetCollection = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchObject_MultiTargetRefProps { + return { + uuids: globalThis.Array.isArray(object?.uuids) ? object.uuids.map((e: any) => globalThis.String(e)) : [], + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + targetCollection: isSet(object.targetCollection) ? globalThis.String(object.targetCollection) : "", + }; + }, + + toJSON(message: BatchObject_MultiTargetRefProps): unknown { + const obj: any = {}; + if (message.uuids?.length) { + obj.uuids = message.uuids; + } + if (message.propName !== "") { + obj.propName = message.propName; + } + if (message.targetCollection !== "") { + obj.targetCollection = message.targetCollection; + } + return obj; + }, + + create(base?: DeepPartial): BatchObject_MultiTargetRefProps { + return BatchObject_MultiTargetRefProps.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchObject_MultiTargetRefProps { + const message = createBaseBatchObject_MultiTargetRefProps(); + message.uuids = object.uuids?.map((e) => e) || []; + message.propName = object.propName ?? ""; + message.targetCollection = object.targetCollection ?? ""; + return message; + }, +}; + +function createBaseBatchObjectsReply(): BatchObjectsReply { + return { took: 0, errors: [] }; +} + +export const BatchObjectsReply = { + encode(message: BatchObjectsReply, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.took !== 0) { + writer.uint32(13).float(message.took); + } + for (const v of message.errors) { + BatchObjectsReply_BatchError.encode(v!, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchObjectsReply { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchObjectsReply(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.took = reader.float(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.errors.push(BatchObjectsReply_BatchError.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchObjectsReply { + return { + took: isSet(object.took) ? globalThis.Number(object.took) : 0, + errors: globalThis.Array.isArray(object?.errors) + ? object.errors.map((e: any) => BatchObjectsReply_BatchError.fromJSON(e)) + : [], + }; + }, + + toJSON(message: BatchObjectsReply): unknown { + const obj: any = {}; + if (message.took !== 0) { + obj.took = message.took; + } + if (message.errors?.length) { + obj.errors = message.errors.map((e) => BatchObjectsReply_BatchError.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): BatchObjectsReply { + return BatchObjectsReply.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchObjectsReply { + const message = createBaseBatchObjectsReply(); + message.took = object.took ?? 0; + message.errors = object.errors?.map((e) => BatchObjectsReply_BatchError.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseBatchObjectsReply_BatchError(): BatchObjectsReply_BatchError { + return { index: 0, error: "" }; +} + +export const BatchObjectsReply_BatchError = { + encode(message: BatchObjectsReply_BatchError, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.index !== 0) { + writer.uint32(8).int32(message.index); + } + if (message.error !== "") { + writer.uint32(18).string(message.error); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchObjectsReply_BatchError { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchObjectsReply_BatchError(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.index = reader.int32(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.error = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchObjectsReply_BatchError { + return { + index: isSet(object.index) ? globalThis.Number(object.index) : 0, + error: isSet(object.error) ? globalThis.String(object.error) : "", + }; + }, + + toJSON(message: BatchObjectsReply_BatchError): unknown { + const obj: any = {}; + if (message.index !== 0) { + obj.index = Math.round(message.index); + } + if (message.error !== "") { + obj.error = message.error; + } + return obj; + }, + + create(base?: DeepPartial): BatchObjectsReply_BatchError { + return BatchObjectsReply_BatchError.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchObjectsReply_BatchError { + const message = createBaseBatchObjectsReply_BatchError(); + message.index = object.index ?? 0; + message.error = object.error ?? ""; + return message; + }, +}; + +function bytesFromBase64(b64: string): Uint8Array { + if ((globalThis as any).Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); + } else { + const bin = globalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if ((globalThis as any).Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(globalThis.String.fromCharCode(byte)); + }); + return globalThis.btoa(bin.join("")); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/batch_delete.ts b/src/proto/v1/batch_delete.ts new file mode 100644 index 00000000..62662c32 --- /dev/null +++ b/src/proto/v1/batch_delete.ts @@ -0,0 +1,432 @@ +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal.js"; +import { ConsistencyLevel, consistencyLevelFromJSON, consistencyLevelToJSON, Filters } from "./base.js"; + +export const protobufPackage = "weaviate.v1"; + +export interface BatchDeleteRequest { + collection: string; + filters: Filters | undefined; + verbose: boolean; + dryRun: boolean; + consistencyLevel?: ConsistencyLevel | undefined; + tenant?: string | undefined; +} + +export interface BatchDeleteReply { + took: number; + failed: number; + matches: number; + successful: number; + objects: BatchDeleteObject[]; +} + +export interface BatchDeleteObject { + uuid: Uint8Array; + successful: boolean; + /** empty string means no error */ + error?: string | undefined; +} + +function createBaseBatchDeleteRequest(): BatchDeleteRequest { + return { + collection: "", + filters: undefined, + verbose: false, + dryRun: false, + consistencyLevel: undefined, + tenant: undefined, + }; +} + +export const BatchDeleteRequest = { + encode(message: BatchDeleteRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.collection !== "") { + writer.uint32(10).string(message.collection); + } + if (message.filters !== undefined) { + Filters.encode(message.filters, writer.uint32(18).fork()).ldelim(); + } + if (message.verbose === true) { + writer.uint32(24).bool(message.verbose); + } + if (message.dryRun === true) { + writer.uint32(32).bool(message.dryRun); + } + if (message.consistencyLevel !== undefined) { + writer.uint32(40).int32(message.consistencyLevel); + } + if (message.tenant !== undefined) { + writer.uint32(50).string(message.tenant); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchDeleteRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchDeleteRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.collection = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.filters = Filters.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.verbose = reader.bool(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.dryRun = reader.bool(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.consistencyLevel = reader.int32() as any; + continue; + case 6: + if (tag !== 50) { + break; + } + + message.tenant = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchDeleteRequest { + return { + collection: isSet(object.collection) ? globalThis.String(object.collection) : "", + filters: isSet(object.filters) ? Filters.fromJSON(object.filters) : undefined, + verbose: isSet(object.verbose) ? globalThis.Boolean(object.verbose) : false, + dryRun: isSet(object.dryRun) ? globalThis.Boolean(object.dryRun) : false, + consistencyLevel: isSet(object.consistencyLevel) ? consistencyLevelFromJSON(object.consistencyLevel) : undefined, + tenant: isSet(object.tenant) ? globalThis.String(object.tenant) : undefined, + }; + }, + + toJSON(message: BatchDeleteRequest): unknown { + const obj: any = {}; + if (message.collection !== "") { + obj.collection = message.collection; + } + if (message.filters !== undefined) { + obj.filters = Filters.toJSON(message.filters); + } + if (message.verbose === true) { + obj.verbose = message.verbose; + } + if (message.dryRun === true) { + obj.dryRun = message.dryRun; + } + if (message.consistencyLevel !== undefined) { + obj.consistencyLevel = consistencyLevelToJSON(message.consistencyLevel); + } + if (message.tenant !== undefined) { + obj.tenant = message.tenant; + } + return obj; + }, + + create(base?: DeepPartial): BatchDeleteRequest { + return BatchDeleteRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchDeleteRequest { + const message = createBaseBatchDeleteRequest(); + message.collection = object.collection ?? ""; + message.filters = (object.filters !== undefined && object.filters !== null) + ? Filters.fromPartial(object.filters) + : undefined; + message.verbose = object.verbose ?? false; + message.dryRun = object.dryRun ?? false; + message.consistencyLevel = object.consistencyLevel ?? undefined; + message.tenant = object.tenant ?? undefined; + return message; + }, +}; + +function createBaseBatchDeleteReply(): BatchDeleteReply { + return { took: 0, failed: 0, matches: 0, successful: 0, objects: [] }; +} + +export const BatchDeleteReply = { + encode(message: BatchDeleteReply, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.took !== 0) { + writer.uint32(13).float(message.took); + } + if (message.failed !== 0) { + writer.uint32(16).int64(message.failed); + } + if (message.matches !== 0) { + writer.uint32(24).int64(message.matches); + } + if (message.successful !== 0) { + writer.uint32(32).int64(message.successful); + } + for (const v of message.objects) { + BatchDeleteObject.encode(v!, writer.uint32(42).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchDeleteReply { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchDeleteReply(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.took = reader.float(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.failed = longToNumber(reader.int64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.matches = longToNumber(reader.int64() as Long); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.successful = longToNumber(reader.int64() as Long); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.objects.push(BatchDeleteObject.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchDeleteReply { + return { + took: isSet(object.took) ? globalThis.Number(object.took) : 0, + failed: isSet(object.failed) ? globalThis.Number(object.failed) : 0, + matches: isSet(object.matches) ? globalThis.Number(object.matches) : 0, + successful: isSet(object.successful) ? globalThis.Number(object.successful) : 0, + objects: globalThis.Array.isArray(object?.objects) + ? object.objects.map((e: any) => BatchDeleteObject.fromJSON(e)) + : [], + }; + }, + + toJSON(message: BatchDeleteReply): unknown { + const obj: any = {}; + if (message.took !== 0) { + obj.took = message.took; + } + if (message.failed !== 0) { + obj.failed = Math.round(message.failed); + } + if (message.matches !== 0) { + obj.matches = Math.round(message.matches); + } + if (message.successful !== 0) { + obj.successful = Math.round(message.successful); + } + if (message.objects?.length) { + obj.objects = message.objects.map((e) => BatchDeleteObject.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): BatchDeleteReply { + return BatchDeleteReply.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchDeleteReply { + const message = createBaseBatchDeleteReply(); + message.took = object.took ?? 0; + message.failed = object.failed ?? 0; + message.matches = object.matches ?? 0; + message.successful = object.successful ?? 0; + message.objects = object.objects?.map((e) => BatchDeleteObject.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseBatchDeleteObject(): BatchDeleteObject { + return { uuid: new Uint8Array(0), successful: false, error: undefined }; +} + +export const BatchDeleteObject = { + encode(message: BatchDeleteObject, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.uuid.length !== 0) { + writer.uint32(10).bytes(message.uuid); + } + if (message.successful === true) { + writer.uint32(16).bool(message.successful); + } + if (message.error !== undefined) { + writer.uint32(26).string(message.error); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BatchDeleteObject { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatchDeleteObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.uuid = reader.bytes(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.successful = reader.bool(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.error = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatchDeleteObject { + return { + uuid: isSet(object.uuid) ? bytesFromBase64(object.uuid) : new Uint8Array(0), + successful: isSet(object.successful) ? globalThis.Boolean(object.successful) : false, + error: isSet(object.error) ? globalThis.String(object.error) : undefined, + }; + }, + + toJSON(message: BatchDeleteObject): unknown { + const obj: any = {}; + if (message.uuid.length !== 0) { + obj.uuid = base64FromBytes(message.uuid); + } + if (message.successful === true) { + obj.successful = message.successful; + } + if (message.error !== undefined) { + obj.error = message.error; + } + return obj; + }, + + create(base?: DeepPartial): BatchDeleteObject { + return BatchDeleteObject.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BatchDeleteObject { + const message = createBaseBatchDeleteObject(); + message.uuid = object.uuid ?? new Uint8Array(0); + message.successful = object.successful ?? false; + message.error = object.error ?? undefined; + return message; + }, +}; + +function bytesFromBase64(b64: string): Uint8Array { + if ((globalThis as any).Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); + } else { + const bin = globalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if ((globalThis as any).Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(globalThis.String.fromCharCode(byte)); + }); + return globalThis.btoa(bin.join("")); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/properties.ts b/src/proto/v1/properties.ts new file mode 100644 index 00000000..3f21d26e --- /dev/null +++ b/src/proto/v1/properties.ts @@ -0,0 +1,1404 @@ +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal.js"; +import { NullValue, nullValueFromJSON, nullValueToJSON } from "../google/protobuf/struct.js"; + +export const protobufPackage = "weaviate.v1"; + +export interface Properties { + fields: { [key: string]: Value }; +} + +export interface Properties_FieldsEntry { + key: string; + value: Value | undefined; +} + +export interface Value { + numberValue?: + | number + | undefined; + /** @deprecated */ + stringValue?: string | undefined; + boolValue?: boolean | undefined; + objectValue?: Properties | undefined; + listValue?: ListValue | undefined; + dateValue?: string | undefined; + uuidValue?: string | undefined; + intValue?: number | undefined; + geoValue?: GeoCoordinate | undefined; + blobValue?: string | undefined; + phoneValue?: PhoneNumber | undefined; + nullValue?: NullValue | undefined; + textValue?: string | undefined; +} + +export interface ListValue { + /** @deprecated */ + values: Value[]; + numberValues?: NumberValues | undefined; + boolValues?: BoolValues | undefined; + objectValues?: ObjectValues | undefined; + dateValues?: DateValues | undefined; + uuidValues?: UuidValues | undefined; + intValues?: IntValues | undefined; + textValues?: TextValues | undefined; +} + +export interface NumberValues { + /** + * The values are stored as a byte array, where each 8 bytes represent a single float64 value. + * The byte array is stored in little-endian order using uint64 encoding. + */ + values: Uint8Array; +} + +export interface TextValues { + values: string[]; +} + +export interface BoolValues { + values: boolean[]; +} + +export interface ObjectValues { + values: Properties[]; +} + +export interface DateValues { + values: string[]; +} + +export interface UuidValues { + values: string[]; +} + +export interface IntValues { + /** + * The values are stored as a byte array, where each 8 bytes represent a single int64 value. + * The byte array is stored in little-endian order using uint64 encoding. + */ + values: Uint8Array; +} + +export interface GeoCoordinate { + longitude: number; + latitude: number; +} + +export interface PhoneNumber { + countryCode: number; + defaultCountry: string; + input: string; + internationalFormatted: string; + national: number; + nationalFormatted: string; + valid: boolean; +} + +function createBaseProperties(): Properties { + return { fields: {} }; +} + +export const Properties = { + encode(message: Properties, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + Object.entries(message.fields).forEach(([key, value]) => { + Properties_FieldsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + }); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Properties { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProperties(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + const entry1 = Properties_FieldsEntry.decode(reader, reader.uint32()); + if (entry1.value !== undefined) { + message.fields[entry1.key] = entry1.value; + } + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Properties { + return { + fields: isObject(object.fields) + ? Object.entries(object.fields).reduce<{ [key: string]: Value }>((acc, [key, value]) => { + acc[key] = Value.fromJSON(value); + return acc; + }, {}) + : {}, + }; + }, + + toJSON(message: Properties): unknown { + const obj: any = {}; + if (message.fields) { + const entries = Object.entries(message.fields); + if (entries.length > 0) { + obj.fields = {}; + entries.forEach(([k, v]) => { + obj.fields[k] = Value.toJSON(v); + }); + } + } + return obj; + }, + + create(base?: DeepPartial): Properties { + return Properties.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Properties { + const message = createBaseProperties(); + message.fields = Object.entries(object.fields ?? {}).reduce<{ [key: string]: Value }>((acc, [key, value]) => { + if (value !== undefined) { + acc[key] = Value.fromPartial(value); + } + return acc; + }, {}); + return message; + }, +}; + +function createBaseProperties_FieldsEntry(): Properties_FieldsEntry { + return { key: "", value: undefined }; +} + +export const Properties_FieldsEntry = { + encode(message: Properties_FieldsEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + Value.encode(message.value, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Properties_FieldsEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProperties_FieldsEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.value = Value.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Properties_FieldsEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? Value.fromJSON(object.value) : undefined, + }; + }, + + toJSON(message: Properties_FieldsEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = Value.toJSON(message.value); + } + return obj; + }, + + create(base?: DeepPartial): Properties_FieldsEntry { + return Properties_FieldsEntry.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Properties_FieldsEntry { + const message = createBaseProperties_FieldsEntry(); + message.key = object.key ?? ""; + message.value = (object.value !== undefined && object.value !== null) ? Value.fromPartial(object.value) : undefined; + return message; + }, +}; + +function createBaseValue(): Value { + return { + numberValue: undefined, + stringValue: undefined, + boolValue: undefined, + objectValue: undefined, + listValue: undefined, + dateValue: undefined, + uuidValue: undefined, + intValue: undefined, + geoValue: undefined, + blobValue: undefined, + phoneValue: undefined, + nullValue: undefined, + textValue: undefined, + }; +} + +export const Value = { + encode(message: Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.numberValue !== undefined) { + writer.uint32(9).double(message.numberValue); + } + if (message.stringValue !== undefined) { + writer.uint32(18).string(message.stringValue); + } + if (message.boolValue !== undefined) { + writer.uint32(24).bool(message.boolValue); + } + if (message.objectValue !== undefined) { + Properties.encode(message.objectValue, writer.uint32(34).fork()).ldelim(); + } + if (message.listValue !== undefined) { + ListValue.encode(message.listValue, writer.uint32(42).fork()).ldelim(); + } + if (message.dateValue !== undefined) { + writer.uint32(50).string(message.dateValue); + } + if (message.uuidValue !== undefined) { + writer.uint32(58).string(message.uuidValue); + } + if (message.intValue !== undefined) { + writer.uint32(64).int64(message.intValue); + } + if (message.geoValue !== undefined) { + GeoCoordinate.encode(message.geoValue, writer.uint32(74).fork()).ldelim(); + } + if (message.blobValue !== undefined) { + writer.uint32(82).string(message.blobValue); + } + if (message.phoneValue !== undefined) { + PhoneNumber.encode(message.phoneValue, writer.uint32(90).fork()).ldelim(); + } + if (message.nullValue !== undefined) { + writer.uint32(96).int32(message.nullValue); + } + if (message.textValue !== undefined) { + writer.uint32(106).string(message.textValue); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Value { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 9) { + break; + } + + message.numberValue = reader.double(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.stringValue = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.boolValue = reader.bool(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.objectValue = Properties.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.listValue = ListValue.decode(reader, reader.uint32()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.dateValue = reader.string(); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.uuidValue = reader.string(); + continue; + case 8: + if (tag !== 64) { + break; + } + + message.intValue = longToNumber(reader.int64() as Long); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.geoValue = GeoCoordinate.decode(reader, reader.uint32()); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.blobValue = reader.string(); + continue; + case 11: + if (tag !== 90) { + break; + } + + message.phoneValue = PhoneNumber.decode(reader, reader.uint32()); + continue; + case 12: + if (tag !== 96) { + break; + } + + message.nullValue = reader.int32() as any; + continue; + case 13: + if (tag !== 106) { + break; + } + + message.textValue = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Value { + return { + numberValue: isSet(object.numberValue) ? globalThis.Number(object.numberValue) : undefined, + stringValue: isSet(object.stringValue) ? globalThis.String(object.stringValue) : undefined, + boolValue: isSet(object.boolValue) ? globalThis.Boolean(object.boolValue) : undefined, + objectValue: isSet(object.objectValue) ? Properties.fromJSON(object.objectValue) : undefined, + listValue: isSet(object.listValue) ? ListValue.fromJSON(object.listValue) : undefined, + dateValue: isSet(object.dateValue) ? globalThis.String(object.dateValue) : undefined, + uuidValue: isSet(object.uuidValue) ? globalThis.String(object.uuidValue) : undefined, + intValue: isSet(object.intValue) ? globalThis.Number(object.intValue) : undefined, + geoValue: isSet(object.geoValue) ? GeoCoordinate.fromJSON(object.geoValue) : undefined, + blobValue: isSet(object.blobValue) ? globalThis.String(object.blobValue) : undefined, + phoneValue: isSet(object.phoneValue) ? PhoneNumber.fromJSON(object.phoneValue) : undefined, + nullValue: isSet(object.nullValue) ? nullValueFromJSON(object.nullValue) : undefined, + textValue: isSet(object.textValue) ? globalThis.String(object.textValue) : undefined, + }; + }, + + toJSON(message: Value): unknown { + const obj: any = {}; + if (message.numberValue !== undefined) { + obj.numberValue = message.numberValue; + } + if (message.stringValue !== undefined) { + obj.stringValue = message.stringValue; + } + if (message.boolValue !== undefined) { + obj.boolValue = message.boolValue; + } + if (message.objectValue !== undefined) { + obj.objectValue = Properties.toJSON(message.objectValue); + } + if (message.listValue !== undefined) { + obj.listValue = ListValue.toJSON(message.listValue); + } + if (message.dateValue !== undefined) { + obj.dateValue = message.dateValue; + } + if (message.uuidValue !== undefined) { + obj.uuidValue = message.uuidValue; + } + if (message.intValue !== undefined) { + obj.intValue = Math.round(message.intValue); + } + if (message.geoValue !== undefined) { + obj.geoValue = GeoCoordinate.toJSON(message.geoValue); + } + if (message.blobValue !== undefined) { + obj.blobValue = message.blobValue; + } + if (message.phoneValue !== undefined) { + obj.phoneValue = PhoneNumber.toJSON(message.phoneValue); + } + if (message.nullValue !== undefined) { + obj.nullValue = nullValueToJSON(message.nullValue); + } + if (message.textValue !== undefined) { + obj.textValue = message.textValue; + } + return obj; + }, + + create(base?: DeepPartial): Value { + return Value.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Value { + const message = createBaseValue(); + message.numberValue = object.numberValue ?? undefined; + message.stringValue = object.stringValue ?? undefined; + message.boolValue = object.boolValue ?? undefined; + message.objectValue = (object.objectValue !== undefined && object.objectValue !== null) + ? Properties.fromPartial(object.objectValue) + : undefined; + message.listValue = (object.listValue !== undefined && object.listValue !== null) + ? ListValue.fromPartial(object.listValue) + : undefined; + message.dateValue = object.dateValue ?? undefined; + message.uuidValue = object.uuidValue ?? undefined; + message.intValue = object.intValue ?? undefined; + message.geoValue = (object.geoValue !== undefined && object.geoValue !== null) + ? GeoCoordinate.fromPartial(object.geoValue) + : undefined; + message.blobValue = object.blobValue ?? undefined; + message.phoneValue = (object.phoneValue !== undefined && object.phoneValue !== null) + ? PhoneNumber.fromPartial(object.phoneValue) + : undefined; + message.nullValue = object.nullValue ?? undefined; + message.textValue = object.textValue ?? undefined; + return message; + }, +}; + +function createBaseListValue(): ListValue { + return { + values: [], + numberValues: undefined, + boolValues: undefined, + objectValues: undefined, + dateValues: undefined, + uuidValues: undefined, + intValues: undefined, + textValues: undefined, + }; +} + +export const ListValue = { + encode(message: ListValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + Value.encode(v!, writer.uint32(10).fork()).ldelim(); + } + if (message.numberValues !== undefined) { + NumberValues.encode(message.numberValues, writer.uint32(18).fork()).ldelim(); + } + if (message.boolValues !== undefined) { + BoolValues.encode(message.boolValues, writer.uint32(26).fork()).ldelim(); + } + if (message.objectValues !== undefined) { + ObjectValues.encode(message.objectValues, writer.uint32(34).fork()).ldelim(); + } + if (message.dateValues !== undefined) { + DateValues.encode(message.dateValues, writer.uint32(42).fork()).ldelim(); + } + if (message.uuidValues !== undefined) { + UuidValues.encode(message.uuidValues, writer.uint32(50).fork()).ldelim(); + } + if (message.intValues !== undefined) { + IntValues.encode(message.intValues, writer.uint32(58).fork()).ldelim(); + } + if (message.textValues !== undefined) { + TextValues.encode(message.textValues, writer.uint32(66).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ListValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(Value.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.numberValues = NumberValues.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.boolValues = BoolValues.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.objectValues = ObjectValues.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.dateValues = DateValues.decode(reader, reader.uint32()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.uuidValues = UuidValues.decode(reader, reader.uint32()); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.intValues = IntValues.decode(reader, reader.uint32()); + continue; + case 8: + if (tag !== 66) { + break; + } + + message.textValues = TextValues.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ListValue { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => Value.fromJSON(e)) : [], + numberValues: isSet(object.numberValues) ? NumberValues.fromJSON(object.numberValues) : undefined, + boolValues: isSet(object.boolValues) ? BoolValues.fromJSON(object.boolValues) : undefined, + objectValues: isSet(object.objectValues) ? ObjectValues.fromJSON(object.objectValues) : undefined, + dateValues: isSet(object.dateValues) ? DateValues.fromJSON(object.dateValues) : undefined, + uuidValues: isSet(object.uuidValues) ? UuidValues.fromJSON(object.uuidValues) : undefined, + intValues: isSet(object.intValues) ? IntValues.fromJSON(object.intValues) : undefined, + textValues: isSet(object.textValues) ? TextValues.fromJSON(object.textValues) : undefined, + }; + }, + + toJSON(message: ListValue): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values.map((e) => Value.toJSON(e)); + } + if (message.numberValues !== undefined) { + obj.numberValues = NumberValues.toJSON(message.numberValues); + } + if (message.boolValues !== undefined) { + obj.boolValues = BoolValues.toJSON(message.boolValues); + } + if (message.objectValues !== undefined) { + obj.objectValues = ObjectValues.toJSON(message.objectValues); + } + if (message.dateValues !== undefined) { + obj.dateValues = DateValues.toJSON(message.dateValues); + } + if (message.uuidValues !== undefined) { + obj.uuidValues = UuidValues.toJSON(message.uuidValues); + } + if (message.intValues !== undefined) { + obj.intValues = IntValues.toJSON(message.intValues); + } + if (message.textValues !== undefined) { + obj.textValues = TextValues.toJSON(message.textValues); + } + return obj; + }, + + create(base?: DeepPartial): ListValue { + return ListValue.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ListValue { + const message = createBaseListValue(); + message.values = object.values?.map((e) => Value.fromPartial(e)) || []; + message.numberValues = (object.numberValues !== undefined && object.numberValues !== null) + ? NumberValues.fromPartial(object.numberValues) + : undefined; + message.boolValues = (object.boolValues !== undefined && object.boolValues !== null) + ? BoolValues.fromPartial(object.boolValues) + : undefined; + message.objectValues = (object.objectValues !== undefined && object.objectValues !== null) + ? ObjectValues.fromPartial(object.objectValues) + : undefined; + message.dateValues = (object.dateValues !== undefined && object.dateValues !== null) + ? DateValues.fromPartial(object.dateValues) + : undefined; + message.uuidValues = (object.uuidValues !== undefined && object.uuidValues !== null) + ? UuidValues.fromPartial(object.uuidValues) + : undefined; + message.intValues = (object.intValues !== undefined && object.intValues !== null) + ? IntValues.fromPartial(object.intValues) + : undefined; + message.textValues = (object.textValues !== undefined && object.textValues !== null) + ? TextValues.fromPartial(object.textValues) + : undefined; + return message; + }, +}; + +function createBaseNumberValues(): NumberValues { + return { values: new Uint8Array(0) }; +} + +export const NumberValues = { + encode(message: NumberValues, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.values.length !== 0) { + writer.uint32(10).bytes(message.values); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NumberValues { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNumberValues(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values = reader.bytes(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NumberValues { + return { values: isSet(object.values) ? bytesFromBase64(object.values) : new Uint8Array(0) }; + }, + + toJSON(message: NumberValues): unknown { + const obj: any = {}; + if (message.values.length !== 0) { + obj.values = base64FromBytes(message.values); + } + return obj; + }, + + create(base?: DeepPartial): NumberValues { + return NumberValues.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NumberValues { + const message = createBaseNumberValues(); + message.values = object.values ?? new Uint8Array(0); + return message; + }, +}; + +function createBaseTextValues(): TextValues { + return { values: [] }; +} + +export const TextValues = { + encode(message: TextValues, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + writer.uint32(10).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TextValues { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTextValues(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TextValues { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: TextValues): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): TextValues { + return TextValues.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): TextValues { + const message = createBaseTextValues(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseBoolValues(): BoolValues { + return { values: [] }; +} + +export const BoolValues = { + encode(message: BoolValues, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.values) { + writer.bool(v); + } + writer.ldelim(); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BoolValues { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBoolValues(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.values.push(reader.bool()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.values.push(reader.bool()); + } + + continue; + } + + break; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BoolValues { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.Boolean(e)) : [], + }; + }, + + toJSON(message: BoolValues): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): BoolValues { + return BoolValues.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BoolValues { + const message = createBaseBoolValues(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseObjectValues(): ObjectValues { + return { values: [] }; +} + +export const ObjectValues = { + encode(message: ObjectValues, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + Properties.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ObjectValues { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseObjectValues(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(Properties.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ObjectValues { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => Properties.fromJSON(e)) : [], + }; + }, + + toJSON(message: ObjectValues): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values.map((e) => Properties.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): ObjectValues { + return ObjectValues.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ObjectValues { + const message = createBaseObjectValues(); + message.values = object.values?.map((e) => Properties.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseDateValues(): DateValues { + return { values: [] }; +} + +export const DateValues = { + encode(message: DateValues, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + writer.uint32(10).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): DateValues { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDateValues(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): DateValues { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: DateValues): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): DateValues { + return DateValues.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): DateValues { + const message = createBaseDateValues(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseUuidValues(): UuidValues { + return { values: [] }; +} + +export const UuidValues = { + encode(message: UuidValues, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + writer.uint32(10).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UuidValues { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUuidValues(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): UuidValues { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: UuidValues): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): UuidValues { + return UuidValues.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): UuidValues { + const message = createBaseUuidValues(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseIntValues(): IntValues { + return { values: new Uint8Array(0) }; +} + +export const IntValues = { + encode(message: IntValues, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.values.length !== 0) { + writer.uint32(10).bytes(message.values); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): IntValues { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseIntValues(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values = reader.bytes(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): IntValues { + return { values: isSet(object.values) ? bytesFromBase64(object.values) : new Uint8Array(0) }; + }, + + toJSON(message: IntValues): unknown { + const obj: any = {}; + if (message.values.length !== 0) { + obj.values = base64FromBytes(message.values); + } + return obj; + }, + + create(base?: DeepPartial): IntValues { + return IntValues.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): IntValues { + const message = createBaseIntValues(); + message.values = object.values ?? new Uint8Array(0); + return message; + }, +}; + +function createBaseGeoCoordinate(): GeoCoordinate { + return { longitude: 0, latitude: 0 }; +} + +export const GeoCoordinate = { + encode(message: GeoCoordinate, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.longitude !== 0) { + writer.uint32(13).float(message.longitude); + } + if (message.latitude !== 0) { + writer.uint32(21).float(message.latitude); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GeoCoordinate { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGeoCoordinate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.longitude = reader.float(); + continue; + case 2: + if (tag !== 21) { + break; + } + + message.latitude = reader.float(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GeoCoordinate { + return { + longitude: isSet(object.longitude) ? globalThis.Number(object.longitude) : 0, + latitude: isSet(object.latitude) ? globalThis.Number(object.latitude) : 0, + }; + }, + + toJSON(message: GeoCoordinate): unknown { + const obj: any = {}; + if (message.longitude !== 0) { + obj.longitude = message.longitude; + } + if (message.latitude !== 0) { + obj.latitude = message.latitude; + } + return obj; + }, + + create(base?: DeepPartial): GeoCoordinate { + return GeoCoordinate.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GeoCoordinate { + const message = createBaseGeoCoordinate(); + message.longitude = object.longitude ?? 0; + message.latitude = object.latitude ?? 0; + return message; + }, +}; + +function createBasePhoneNumber(): PhoneNumber { + return { + countryCode: 0, + defaultCountry: "", + input: "", + internationalFormatted: "", + national: 0, + nationalFormatted: "", + valid: false, + }; +} + +export const PhoneNumber = { + encode(message: PhoneNumber, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.countryCode !== 0) { + writer.uint32(8).uint64(message.countryCode); + } + if (message.defaultCountry !== "") { + writer.uint32(18).string(message.defaultCountry); + } + if (message.input !== "") { + writer.uint32(26).string(message.input); + } + if (message.internationalFormatted !== "") { + writer.uint32(34).string(message.internationalFormatted); + } + if (message.national !== 0) { + writer.uint32(40).uint64(message.national); + } + if (message.nationalFormatted !== "") { + writer.uint32(50).string(message.nationalFormatted); + } + if (message.valid === true) { + writer.uint32(56).bool(message.valid); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PhoneNumber { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePhoneNumber(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.countryCode = longToNumber(reader.uint64() as Long); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.defaultCountry = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.input = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.internationalFormatted = reader.string(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.national = longToNumber(reader.uint64() as Long); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.nationalFormatted = reader.string(); + continue; + case 7: + if (tag !== 56) { + break; + } + + message.valid = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): PhoneNumber { + return { + countryCode: isSet(object.countryCode) ? globalThis.Number(object.countryCode) : 0, + defaultCountry: isSet(object.defaultCountry) ? globalThis.String(object.defaultCountry) : "", + input: isSet(object.input) ? globalThis.String(object.input) : "", + internationalFormatted: isSet(object.internationalFormatted) + ? globalThis.String(object.internationalFormatted) + : "", + national: isSet(object.national) ? globalThis.Number(object.national) : 0, + nationalFormatted: isSet(object.nationalFormatted) ? globalThis.String(object.nationalFormatted) : "", + valid: isSet(object.valid) ? globalThis.Boolean(object.valid) : false, + }; + }, + + toJSON(message: PhoneNumber): unknown { + const obj: any = {}; + if (message.countryCode !== 0) { + obj.countryCode = Math.round(message.countryCode); + } + if (message.defaultCountry !== "") { + obj.defaultCountry = message.defaultCountry; + } + if (message.input !== "") { + obj.input = message.input; + } + if (message.internationalFormatted !== "") { + obj.internationalFormatted = message.internationalFormatted; + } + if (message.national !== 0) { + obj.national = Math.round(message.national); + } + if (message.nationalFormatted !== "") { + obj.nationalFormatted = message.nationalFormatted; + } + if (message.valid === true) { + obj.valid = message.valid; + } + return obj; + }, + + create(base?: DeepPartial): PhoneNumber { + return PhoneNumber.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): PhoneNumber { + const message = createBasePhoneNumber(); + message.countryCode = object.countryCode ?? 0; + message.defaultCountry = object.defaultCountry ?? ""; + message.input = object.input ?? ""; + message.internationalFormatted = object.internationalFormatted ?? ""; + message.national = object.national ?? 0; + message.nationalFormatted = object.nationalFormatted ?? ""; + message.valid = object.valid ?? false; + return message; + }, +}; + +function bytesFromBase64(b64: string): Uint8Array { + if ((globalThis as any).Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); + } else { + const bin = globalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if ((globalThis as any).Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(globalThis.String.fromCharCode(byte)); + }); + return globalThis.btoa(bin.join("")); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/search_get.ts b/src/proto/v1/search_get.ts new file mode 100644 index 00000000..24cbbc36 --- /dev/null +++ b/src/proto/v1/search_get.ts @@ -0,0 +1,4396 @@ +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal.js"; +import { Struct } from "../google/protobuf/struct.js"; +import { + BooleanArrayProperties, + ConsistencyLevel, + consistencyLevelFromJSON, + consistencyLevelToJSON, + Filters, + IntArrayProperties, + NumberArrayProperties, + ObjectArrayProperties, + ObjectProperties, + TextArrayProperties, + Vectors, +} from "./base.js"; +import { Properties } from "./properties.js"; + +export const protobufPackage = "weaviate.v1"; + +export interface SearchRequest { + /** required */ + collection: string; + /** parameters */ + tenant: string; + consistencyLevel?: + | ConsistencyLevel + | undefined; + /** what is returned */ + properties?: PropertiesRequest | undefined; + metadata?: MetadataRequest | undefined; + groupBy?: + | GroupBy + | undefined; + /** affects order and length of results. 0/empty (default value) means disabled */ + limit: number; + offset: number; + autocut: number; + after: string; + /** protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED */ + sortBy: SortBy[]; + /** matches/searches for objects */ + filters?: Filters | undefined; + hybridSearch?: Hybrid | undefined; + bm25Search?: BM25 | undefined; + nearVector?: NearVector | undefined; + nearObject?: NearObject | undefined; + nearText?: NearTextSearch | undefined; + nearImage?: NearImageSearch | undefined; + nearAudio?: NearAudioSearch | undefined; + nearVideo?: NearVideoSearch | undefined; + nearDepth?: NearDepthSearch | undefined; + nearThermal?: NearThermalSearch | undefined; + nearImu?: NearIMUSearch | undefined; + generative?: GenerativeSearch | undefined; + rerank?: + | Rerank + | undefined; + /** @deprecated */ + uses123Api: boolean; + uses125Api: boolean; +} + +export interface GroupBy { + /** + * currently only supports one entry (eg just properties, no refs). But might + * be extended in the future. + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + */ + path: string[]; + numberOfGroups: number; + objectsPerGroup: number; +} + +export interface SortBy { + ascending: boolean; + /** + * currently only supports one entry (eg just properties, no refs). But the + * weaviate datastructure already has paths in it and this makes it easily + * extendable in the future + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + */ + path: string[]; +} + +export interface GenerativeSearch { + singleResponsePrompt: string; + groupedResponseTask: string; + groupedProperties: string[]; +} + +export interface MetadataRequest { + uuid: boolean; + vector: boolean; + creationTimeUnix: boolean; + lastUpdateTimeUnix: boolean; + distance: boolean; + certainty: boolean; + score: boolean; + explainScore: boolean; + isConsistent: boolean; + vectors: string[]; +} + +export interface PropertiesRequest { + nonRefProperties: string[]; + refProperties: RefPropertiesRequest[]; + objectProperties: ObjectPropertiesRequest[]; + returnAllNonrefProperties: boolean; +} + +export interface ObjectPropertiesRequest { + propName: string; + primitiveProperties: string[]; + objectProperties: ObjectPropertiesRequest[]; +} + +export interface Hybrid { + query: string; + properties: string[]; + /** + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + * + * @deprecated + */ + vector: number[]; + alpha: number; + fusionType: Hybrid_FusionType; + vectorBytes: Uint8Array; + targetVectors: string[]; + /** target_vector in msg is ignored and should not be set for hybrid */ + nearText: + | NearTextSearch + | undefined; + /** same as above. Use the target vector in the hybrid message */ + nearVector: NearVector | undefined; +} + +export enum Hybrid_FusionType { + FUSION_TYPE_UNSPECIFIED = 0, + FUSION_TYPE_RANKED = 1, + FUSION_TYPE_RELATIVE_SCORE = 2, + UNRECOGNIZED = -1, +} + +export function hybrid_FusionTypeFromJSON(object: any): Hybrid_FusionType { + switch (object) { + case 0: + case "FUSION_TYPE_UNSPECIFIED": + return Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED; + case 1: + case "FUSION_TYPE_RANKED": + return Hybrid_FusionType.FUSION_TYPE_RANKED; + case 2: + case "FUSION_TYPE_RELATIVE_SCORE": + return Hybrid_FusionType.FUSION_TYPE_RELATIVE_SCORE; + case -1: + case "UNRECOGNIZED": + default: + return Hybrid_FusionType.UNRECOGNIZED; + } +} + +export function hybrid_FusionTypeToJSON(object: Hybrid_FusionType): string { + switch (object) { + case Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED: + return "FUSION_TYPE_UNSPECIFIED"; + case Hybrid_FusionType.FUSION_TYPE_RANKED: + return "FUSION_TYPE_RANKED"; + case Hybrid_FusionType.FUSION_TYPE_RELATIVE_SCORE: + return "FUSION_TYPE_RELATIVE_SCORE"; + case Hybrid_FusionType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export interface NearTextSearch { + /** protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED */ + query: string[]; + certainty?: number | undefined; + distance?: number | undefined; + moveTo?: NearTextSearch_Move | undefined; + moveAway?: NearTextSearch_Move | undefined; + targetVectors: string[]; +} + +export interface NearTextSearch_Move { + force: number; + concepts: string[]; + uuids: string[]; +} + +export interface NearImageSearch { + image: string; + certainty?: number | undefined; + distance?: number | undefined; + targetVectors: string[]; +} + +export interface NearAudioSearch { + audio: string; + certainty?: number | undefined; + distance?: number | undefined; + targetVectors: string[]; +} + +export interface NearVideoSearch { + video: string; + certainty?: number | undefined; + distance?: number | undefined; + targetVectors: string[]; +} + +export interface NearDepthSearch { + depth: string; + certainty?: number | undefined; + distance?: number | undefined; + targetVectors: string[]; +} + +export interface NearThermalSearch { + thermal: string; + certainty?: number | undefined; + distance?: number | undefined; + targetVectors: string[]; +} + +export interface NearIMUSearch { + imu: string; + certainty?: number | undefined; + distance?: number | undefined; + targetVectors: string[]; +} + +export interface BM25 { + query: string; + properties: string[]; +} + +export interface RefPropertiesRequest { + referenceProperty: string; + properties: PropertiesRequest | undefined; + metadata: MetadataRequest | undefined; + targetCollection: string; +} + +export interface NearVector { + /** + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + * + * @deprecated + */ + vector: number[]; + certainty?: number | undefined; + distance?: number | undefined; + vectorBytes: Uint8Array; + targetVectors: string[]; +} + +export interface NearObject { + id: string; + certainty?: number | undefined; + distance?: number | undefined; + targetVectors: string[]; +} + +export interface Rerank { + property: string; + query?: string | undefined; +} + +export interface SearchReply { + took: number; + results: SearchResult[]; + generativeGroupedResult?: string | undefined; + groupByResults: GroupByResult[]; +} + +export interface RerankReply { + score: number; +} + +export interface GenerativeReply { + result: string; +} + +export interface GroupByResult { + name: string; + minDistance: number; + maxDistance: number; + numberOfObjects: number; + objects: SearchResult[]; + rerank?: RerankReply | undefined; + generative?: GenerativeReply | undefined; +} + +export interface SearchResult { + properties: PropertiesResult | undefined; + metadata: MetadataResult | undefined; +} + +export interface MetadataResult { + id: string; + /** + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + * + * @deprecated + */ + vector: number[]; + creationTimeUnix: number; + creationTimeUnixPresent: boolean; + lastUpdateTimeUnix: number; + lastUpdateTimeUnixPresent: boolean; + distance: number; + distancePresent: boolean; + certainty: number; + certaintyPresent: boolean; + score: number; + scorePresent: boolean; + explainScore: string; + explainScorePresent: boolean; + isConsistent?: boolean | undefined; + generative: string; + generativePresent: boolean; + isConsistentPresent: boolean; + vectorBytes: Uint8Array; + idAsBytes: Uint8Array; + rerankScore: number; + rerankScorePresent: boolean; + vectors: Vectors[]; +} + +export interface PropertiesResult { + /** @deprecated */ + nonRefProperties: { [key: string]: any } | undefined; + refProps: RefPropertiesResult[]; + targetCollection: string; + metadata: + | MetadataResult + | undefined; + /** @deprecated */ + numberArrayProperties: NumberArrayProperties[]; + /** @deprecated */ + intArrayProperties: IntArrayProperties[]; + /** @deprecated */ + textArrayProperties: TextArrayProperties[]; + /** @deprecated */ + booleanArrayProperties: BooleanArrayProperties[]; + /** @deprecated */ + objectProperties: ObjectProperties[]; + /** @deprecated */ + objectArrayProperties: ObjectArrayProperties[]; + nonRefProps: Properties | undefined; + refPropsRequested: boolean; +} + +export interface RefPropertiesResult { + properties: PropertiesResult[]; + propName: string; +} + +function createBaseSearchRequest(): SearchRequest { + return { + collection: "", + tenant: "", + consistencyLevel: undefined, + properties: undefined, + metadata: undefined, + groupBy: undefined, + limit: 0, + offset: 0, + autocut: 0, + after: "", + sortBy: [], + filters: undefined, + hybridSearch: undefined, + bm25Search: undefined, + nearVector: undefined, + nearObject: undefined, + nearText: undefined, + nearImage: undefined, + nearAudio: undefined, + nearVideo: undefined, + nearDepth: undefined, + nearThermal: undefined, + nearImu: undefined, + generative: undefined, + rerank: undefined, + uses123Api: false, + uses125Api: false, + }; +} + +export const SearchRequest = { + encode(message: SearchRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.collection !== "") { + writer.uint32(10).string(message.collection); + } + if (message.tenant !== "") { + writer.uint32(82).string(message.tenant); + } + if (message.consistencyLevel !== undefined) { + writer.uint32(88).int32(message.consistencyLevel); + } + if (message.properties !== undefined) { + PropertiesRequest.encode(message.properties, writer.uint32(162).fork()).ldelim(); + } + if (message.metadata !== undefined) { + MetadataRequest.encode(message.metadata, writer.uint32(170).fork()).ldelim(); + } + if (message.groupBy !== undefined) { + GroupBy.encode(message.groupBy, writer.uint32(178).fork()).ldelim(); + } + if (message.limit !== 0) { + writer.uint32(240).uint32(message.limit); + } + if (message.offset !== 0) { + writer.uint32(248).uint32(message.offset); + } + if (message.autocut !== 0) { + writer.uint32(256).uint32(message.autocut); + } + if (message.after !== "") { + writer.uint32(266).string(message.after); + } + for (const v of message.sortBy) { + SortBy.encode(v!, writer.uint32(274).fork()).ldelim(); + } + if (message.filters !== undefined) { + Filters.encode(message.filters, writer.uint32(322).fork()).ldelim(); + } + if (message.hybridSearch !== undefined) { + Hybrid.encode(message.hybridSearch, writer.uint32(330).fork()).ldelim(); + } + if (message.bm25Search !== undefined) { + BM25.encode(message.bm25Search, writer.uint32(338).fork()).ldelim(); + } + if (message.nearVector !== undefined) { + NearVector.encode(message.nearVector, writer.uint32(346).fork()).ldelim(); + } + if (message.nearObject !== undefined) { + NearObject.encode(message.nearObject, writer.uint32(354).fork()).ldelim(); + } + if (message.nearText !== undefined) { + NearTextSearch.encode(message.nearText, writer.uint32(362).fork()).ldelim(); + } + if (message.nearImage !== undefined) { + NearImageSearch.encode(message.nearImage, writer.uint32(370).fork()).ldelim(); + } + if (message.nearAudio !== undefined) { + NearAudioSearch.encode(message.nearAudio, writer.uint32(378).fork()).ldelim(); + } + if (message.nearVideo !== undefined) { + NearVideoSearch.encode(message.nearVideo, writer.uint32(386).fork()).ldelim(); + } + if (message.nearDepth !== undefined) { + NearDepthSearch.encode(message.nearDepth, writer.uint32(394).fork()).ldelim(); + } + if (message.nearThermal !== undefined) { + NearThermalSearch.encode(message.nearThermal, writer.uint32(402).fork()).ldelim(); + } + if (message.nearImu !== undefined) { + NearIMUSearch.encode(message.nearImu, writer.uint32(410).fork()).ldelim(); + } + if (message.generative !== undefined) { + GenerativeSearch.encode(message.generative, writer.uint32(482).fork()).ldelim(); + } + if (message.rerank !== undefined) { + Rerank.encode(message.rerank, writer.uint32(490).fork()).ldelim(); + } + if (message.uses123Api === true) { + writer.uint32(800).bool(message.uses123Api); + } + if (message.uses125Api === true) { + writer.uint32(808).bool(message.uses125Api); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SearchRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSearchRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.collection = reader.string(); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.tenant = reader.string(); + continue; + case 11: + if (tag !== 88) { + break; + } + + message.consistencyLevel = reader.int32() as any; + continue; + case 20: + if (tag !== 162) { + break; + } + + message.properties = PropertiesRequest.decode(reader, reader.uint32()); + continue; + case 21: + if (tag !== 170) { + break; + } + + message.metadata = MetadataRequest.decode(reader, reader.uint32()); + continue; + case 22: + if (tag !== 178) { + break; + } + + message.groupBy = GroupBy.decode(reader, reader.uint32()); + continue; + case 30: + if (tag !== 240) { + break; + } + + message.limit = reader.uint32(); + continue; + case 31: + if (tag !== 248) { + break; + } + + message.offset = reader.uint32(); + continue; + case 32: + if (tag !== 256) { + break; + } + + message.autocut = reader.uint32(); + continue; + case 33: + if (tag !== 266) { + break; + } + + message.after = reader.string(); + continue; + case 34: + if (tag !== 274) { + break; + } + + message.sortBy.push(SortBy.decode(reader, reader.uint32())); + continue; + case 40: + if (tag !== 322) { + break; + } + + message.filters = Filters.decode(reader, reader.uint32()); + continue; + case 41: + if (tag !== 330) { + break; + } + + message.hybridSearch = Hybrid.decode(reader, reader.uint32()); + continue; + case 42: + if (tag !== 338) { + break; + } + + message.bm25Search = BM25.decode(reader, reader.uint32()); + continue; + case 43: + if (tag !== 346) { + break; + } + + message.nearVector = NearVector.decode(reader, reader.uint32()); + continue; + case 44: + if (tag !== 354) { + break; + } + + message.nearObject = NearObject.decode(reader, reader.uint32()); + continue; + case 45: + if (tag !== 362) { + break; + } + + message.nearText = NearTextSearch.decode(reader, reader.uint32()); + continue; + case 46: + if (tag !== 370) { + break; + } + + message.nearImage = NearImageSearch.decode(reader, reader.uint32()); + continue; + case 47: + if (tag !== 378) { + break; + } + + message.nearAudio = NearAudioSearch.decode(reader, reader.uint32()); + continue; + case 48: + if (tag !== 386) { + break; + } + + message.nearVideo = NearVideoSearch.decode(reader, reader.uint32()); + continue; + case 49: + if (tag !== 394) { + break; + } + + message.nearDepth = NearDepthSearch.decode(reader, reader.uint32()); + continue; + case 50: + if (tag !== 402) { + break; + } + + message.nearThermal = NearThermalSearch.decode(reader, reader.uint32()); + continue; + case 51: + if (tag !== 410) { + break; + } + + message.nearImu = NearIMUSearch.decode(reader, reader.uint32()); + continue; + case 60: + if (tag !== 482) { + break; + } + + message.generative = GenerativeSearch.decode(reader, reader.uint32()); + continue; + case 61: + if (tag !== 490) { + break; + } + + message.rerank = Rerank.decode(reader, reader.uint32()); + continue; + case 100: + if (tag !== 800) { + break; + } + + message.uses123Api = reader.bool(); + continue; + case 101: + if (tag !== 808) { + break; + } + + message.uses125Api = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SearchRequest { + return { + collection: isSet(object.collection) ? globalThis.String(object.collection) : "", + tenant: isSet(object.tenant) ? globalThis.String(object.tenant) : "", + consistencyLevel: isSet(object.consistencyLevel) ? consistencyLevelFromJSON(object.consistencyLevel) : undefined, + properties: isSet(object.properties) ? PropertiesRequest.fromJSON(object.properties) : undefined, + metadata: isSet(object.metadata) ? MetadataRequest.fromJSON(object.metadata) : undefined, + groupBy: isSet(object.groupBy) ? GroupBy.fromJSON(object.groupBy) : undefined, + limit: isSet(object.limit) ? globalThis.Number(object.limit) : 0, + offset: isSet(object.offset) ? globalThis.Number(object.offset) : 0, + autocut: isSet(object.autocut) ? globalThis.Number(object.autocut) : 0, + after: isSet(object.after) ? globalThis.String(object.after) : "", + sortBy: globalThis.Array.isArray(object?.sortBy) ? object.sortBy.map((e: any) => SortBy.fromJSON(e)) : [], + filters: isSet(object.filters) ? Filters.fromJSON(object.filters) : undefined, + hybridSearch: isSet(object.hybridSearch) ? Hybrid.fromJSON(object.hybridSearch) : undefined, + bm25Search: isSet(object.bm25Search) ? BM25.fromJSON(object.bm25Search) : undefined, + nearVector: isSet(object.nearVector) ? NearVector.fromJSON(object.nearVector) : undefined, + nearObject: isSet(object.nearObject) ? NearObject.fromJSON(object.nearObject) : undefined, + nearText: isSet(object.nearText) ? NearTextSearch.fromJSON(object.nearText) : undefined, + nearImage: isSet(object.nearImage) ? NearImageSearch.fromJSON(object.nearImage) : undefined, + nearAudio: isSet(object.nearAudio) ? NearAudioSearch.fromJSON(object.nearAudio) : undefined, + nearVideo: isSet(object.nearVideo) ? NearVideoSearch.fromJSON(object.nearVideo) : undefined, + nearDepth: isSet(object.nearDepth) ? NearDepthSearch.fromJSON(object.nearDepth) : undefined, + nearThermal: isSet(object.nearThermal) ? NearThermalSearch.fromJSON(object.nearThermal) : undefined, + nearImu: isSet(object.nearImu) ? NearIMUSearch.fromJSON(object.nearImu) : undefined, + generative: isSet(object.generative) ? GenerativeSearch.fromJSON(object.generative) : undefined, + rerank: isSet(object.rerank) ? Rerank.fromJSON(object.rerank) : undefined, + uses123Api: isSet(object.uses123Api) ? globalThis.Boolean(object.uses123Api) : false, + uses125Api: isSet(object.uses125Api) ? globalThis.Boolean(object.uses125Api) : false, + }; + }, + + toJSON(message: SearchRequest): unknown { + const obj: any = {}; + if (message.collection !== "") { + obj.collection = message.collection; + } + if (message.tenant !== "") { + obj.tenant = message.tenant; + } + if (message.consistencyLevel !== undefined) { + obj.consistencyLevel = consistencyLevelToJSON(message.consistencyLevel); + } + if (message.properties !== undefined) { + obj.properties = PropertiesRequest.toJSON(message.properties); + } + if (message.metadata !== undefined) { + obj.metadata = MetadataRequest.toJSON(message.metadata); + } + if (message.groupBy !== undefined) { + obj.groupBy = GroupBy.toJSON(message.groupBy); + } + if (message.limit !== 0) { + obj.limit = Math.round(message.limit); + } + if (message.offset !== 0) { + obj.offset = Math.round(message.offset); + } + if (message.autocut !== 0) { + obj.autocut = Math.round(message.autocut); + } + if (message.after !== "") { + obj.after = message.after; + } + if (message.sortBy?.length) { + obj.sortBy = message.sortBy.map((e) => SortBy.toJSON(e)); + } + if (message.filters !== undefined) { + obj.filters = Filters.toJSON(message.filters); + } + if (message.hybridSearch !== undefined) { + obj.hybridSearch = Hybrid.toJSON(message.hybridSearch); + } + if (message.bm25Search !== undefined) { + obj.bm25Search = BM25.toJSON(message.bm25Search); + } + if (message.nearVector !== undefined) { + obj.nearVector = NearVector.toJSON(message.nearVector); + } + if (message.nearObject !== undefined) { + obj.nearObject = NearObject.toJSON(message.nearObject); + } + if (message.nearText !== undefined) { + obj.nearText = NearTextSearch.toJSON(message.nearText); + } + if (message.nearImage !== undefined) { + obj.nearImage = NearImageSearch.toJSON(message.nearImage); + } + if (message.nearAudio !== undefined) { + obj.nearAudio = NearAudioSearch.toJSON(message.nearAudio); + } + if (message.nearVideo !== undefined) { + obj.nearVideo = NearVideoSearch.toJSON(message.nearVideo); + } + if (message.nearDepth !== undefined) { + obj.nearDepth = NearDepthSearch.toJSON(message.nearDepth); + } + if (message.nearThermal !== undefined) { + obj.nearThermal = NearThermalSearch.toJSON(message.nearThermal); + } + if (message.nearImu !== undefined) { + obj.nearImu = NearIMUSearch.toJSON(message.nearImu); + } + if (message.generative !== undefined) { + obj.generative = GenerativeSearch.toJSON(message.generative); + } + if (message.rerank !== undefined) { + obj.rerank = Rerank.toJSON(message.rerank); + } + if (message.uses123Api === true) { + obj.uses123Api = message.uses123Api; + } + if (message.uses125Api === true) { + obj.uses125Api = message.uses125Api; + } + return obj; + }, + + create(base?: DeepPartial): SearchRequest { + return SearchRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): SearchRequest { + const message = createBaseSearchRequest(); + message.collection = object.collection ?? ""; + message.tenant = object.tenant ?? ""; + message.consistencyLevel = object.consistencyLevel ?? undefined; + message.properties = (object.properties !== undefined && object.properties !== null) + ? PropertiesRequest.fromPartial(object.properties) + : undefined; + message.metadata = (object.metadata !== undefined && object.metadata !== null) + ? MetadataRequest.fromPartial(object.metadata) + : undefined; + message.groupBy = (object.groupBy !== undefined && object.groupBy !== null) + ? GroupBy.fromPartial(object.groupBy) + : undefined; + message.limit = object.limit ?? 0; + message.offset = object.offset ?? 0; + message.autocut = object.autocut ?? 0; + message.after = object.after ?? ""; + message.sortBy = object.sortBy?.map((e) => SortBy.fromPartial(e)) || []; + message.filters = (object.filters !== undefined && object.filters !== null) + ? Filters.fromPartial(object.filters) + : undefined; + message.hybridSearch = (object.hybridSearch !== undefined && object.hybridSearch !== null) + ? Hybrid.fromPartial(object.hybridSearch) + : undefined; + message.bm25Search = (object.bm25Search !== undefined && object.bm25Search !== null) + ? BM25.fromPartial(object.bm25Search) + : undefined; + message.nearVector = (object.nearVector !== undefined && object.nearVector !== null) + ? NearVector.fromPartial(object.nearVector) + : undefined; + message.nearObject = (object.nearObject !== undefined && object.nearObject !== null) + ? NearObject.fromPartial(object.nearObject) + : undefined; + message.nearText = (object.nearText !== undefined && object.nearText !== null) + ? NearTextSearch.fromPartial(object.nearText) + : undefined; + message.nearImage = (object.nearImage !== undefined && object.nearImage !== null) + ? NearImageSearch.fromPartial(object.nearImage) + : undefined; + message.nearAudio = (object.nearAudio !== undefined && object.nearAudio !== null) + ? NearAudioSearch.fromPartial(object.nearAudio) + : undefined; + message.nearVideo = (object.nearVideo !== undefined && object.nearVideo !== null) + ? NearVideoSearch.fromPartial(object.nearVideo) + : undefined; + message.nearDepth = (object.nearDepth !== undefined && object.nearDepth !== null) + ? NearDepthSearch.fromPartial(object.nearDepth) + : undefined; + message.nearThermal = (object.nearThermal !== undefined && object.nearThermal !== null) + ? NearThermalSearch.fromPartial(object.nearThermal) + : undefined; + message.nearImu = (object.nearImu !== undefined && object.nearImu !== null) + ? NearIMUSearch.fromPartial(object.nearImu) + : undefined; + message.generative = (object.generative !== undefined && object.generative !== null) + ? GenerativeSearch.fromPartial(object.generative) + : undefined; + message.rerank = (object.rerank !== undefined && object.rerank !== null) + ? Rerank.fromPartial(object.rerank) + : undefined; + message.uses123Api = object.uses123Api ?? false; + message.uses125Api = object.uses125Api ?? false; + return message; + }, +}; + +function createBaseGroupBy(): GroupBy { + return { path: [], numberOfGroups: 0, objectsPerGroup: 0 }; +} + +export const GroupBy = { + encode(message: GroupBy, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.path) { + writer.uint32(10).string(v!); + } + if (message.numberOfGroups !== 0) { + writer.uint32(16).int32(message.numberOfGroups); + } + if (message.objectsPerGroup !== 0) { + writer.uint32(24).int32(message.objectsPerGroup); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupBy { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupBy(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.path.push(reader.string()); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.numberOfGroups = reader.int32(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.objectsPerGroup = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupBy { + return { + path: globalThis.Array.isArray(object?.path) ? object.path.map((e: any) => globalThis.String(e)) : [], + numberOfGroups: isSet(object.numberOfGroups) ? globalThis.Number(object.numberOfGroups) : 0, + objectsPerGroup: isSet(object.objectsPerGroup) ? globalThis.Number(object.objectsPerGroup) : 0, + }; + }, + + toJSON(message: GroupBy): unknown { + const obj: any = {}; + if (message.path?.length) { + obj.path = message.path; + } + if (message.numberOfGroups !== 0) { + obj.numberOfGroups = Math.round(message.numberOfGroups); + } + if (message.objectsPerGroup !== 0) { + obj.objectsPerGroup = Math.round(message.objectsPerGroup); + } + return obj; + }, + + create(base?: DeepPartial): GroupBy { + return GroupBy.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GroupBy { + const message = createBaseGroupBy(); + message.path = object.path?.map((e) => e) || []; + message.numberOfGroups = object.numberOfGroups ?? 0; + message.objectsPerGroup = object.objectsPerGroup ?? 0; + return message; + }, +}; + +function createBaseSortBy(): SortBy { + return { ascending: false, path: [] }; +} + +export const SortBy = { + encode(message: SortBy, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.ascending === true) { + writer.uint32(8).bool(message.ascending); + } + for (const v of message.path) { + writer.uint32(18).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SortBy { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSortBy(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.ascending = reader.bool(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.path.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SortBy { + return { + ascending: isSet(object.ascending) ? globalThis.Boolean(object.ascending) : false, + path: globalThis.Array.isArray(object?.path) ? object.path.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: SortBy): unknown { + const obj: any = {}; + if (message.ascending === true) { + obj.ascending = message.ascending; + } + if (message.path?.length) { + obj.path = message.path; + } + return obj; + }, + + create(base?: DeepPartial): SortBy { + return SortBy.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): SortBy { + const message = createBaseSortBy(); + message.ascending = object.ascending ?? false; + message.path = object.path?.map((e) => e) || []; + return message; + }, +}; + +function createBaseGenerativeSearch(): GenerativeSearch { + return { singleResponsePrompt: "", groupedResponseTask: "", groupedProperties: [] }; +} + +export const GenerativeSearch = { + encode(message: GenerativeSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.singleResponsePrompt !== "") { + writer.uint32(10).string(message.singleResponsePrompt); + } + if (message.groupedResponseTask !== "") { + writer.uint32(18).string(message.groupedResponseTask); + } + for (const v of message.groupedProperties) { + writer.uint32(26).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GenerativeSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGenerativeSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.singleResponsePrompt = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.groupedResponseTask = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.groupedProperties.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GenerativeSearch { + return { + singleResponsePrompt: isSet(object.singleResponsePrompt) ? globalThis.String(object.singleResponsePrompt) : "", + groupedResponseTask: isSet(object.groupedResponseTask) ? globalThis.String(object.groupedResponseTask) : "", + groupedProperties: globalThis.Array.isArray(object?.groupedProperties) + ? object.groupedProperties.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: GenerativeSearch): unknown { + const obj: any = {}; + if (message.singleResponsePrompt !== "") { + obj.singleResponsePrompt = message.singleResponsePrompt; + } + if (message.groupedResponseTask !== "") { + obj.groupedResponseTask = message.groupedResponseTask; + } + if (message.groupedProperties?.length) { + obj.groupedProperties = message.groupedProperties; + } + return obj; + }, + + create(base?: DeepPartial): GenerativeSearch { + return GenerativeSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GenerativeSearch { + const message = createBaseGenerativeSearch(); + message.singleResponsePrompt = object.singleResponsePrompt ?? ""; + message.groupedResponseTask = object.groupedResponseTask ?? ""; + message.groupedProperties = object.groupedProperties?.map((e) => e) || []; + return message; + }, +}; + +function createBaseMetadataRequest(): MetadataRequest { + return { + uuid: false, + vector: false, + creationTimeUnix: false, + lastUpdateTimeUnix: false, + distance: false, + certainty: false, + score: false, + explainScore: false, + isConsistent: false, + vectors: [], + }; +} + +export const MetadataRequest = { + encode(message: MetadataRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.uuid === true) { + writer.uint32(8).bool(message.uuid); + } + if (message.vector === true) { + writer.uint32(16).bool(message.vector); + } + if (message.creationTimeUnix === true) { + writer.uint32(24).bool(message.creationTimeUnix); + } + if (message.lastUpdateTimeUnix === true) { + writer.uint32(32).bool(message.lastUpdateTimeUnix); + } + if (message.distance === true) { + writer.uint32(40).bool(message.distance); + } + if (message.certainty === true) { + writer.uint32(48).bool(message.certainty); + } + if (message.score === true) { + writer.uint32(56).bool(message.score); + } + if (message.explainScore === true) { + writer.uint32(64).bool(message.explainScore); + } + if (message.isConsistent === true) { + writer.uint32(72).bool(message.isConsistent); + } + for (const v of message.vectors) { + writer.uint32(82).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MetadataRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMetadataRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.uuid = reader.bool(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.vector = reader.bool(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.creationTimeUnix = reader.bool(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.lastUpdateTimeUnix = reader.bool(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.distance = reader.bool(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.certainty = reader.bool(); + continue; + case 7: + if (tag !== 56) { + break; + } + + message.score = reader.bool(); + continue; + case 8: + if (tag !== 64) { + break; + } + + message.explainScore = reader.bool(); + continue; + case 9: + if (tag !== 72) { + break; + } + + message.isConsistent = reader.bool(); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.vectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): MetadataRequest { + return { + uuid: isSet(object.uuid) ? globalThis.Boolean(object.uuid) : false, + vector: isSet(object.vector) ? globalThis.Boolean(object.vector) : false, + creationTimeUnix: isSet(object.creationTimeUnix) ? globalThis.Boolean(object.creationTimeUnix) : false, + lastUpdateTimeUnix: isSet(object.lastUpdateTimeUnix) ? globalThis.Boolean(object.lastUpdateTimeUnix) : false, + distance: isSet(object.distance) ? globalThis.Boolean(object.distance) : false, + certainty: isSet(object.certainty) ? globalThis.Boolean(object.certainty) : false, + score: isSet(object.score) ? globalThis.Boolean(object.score) : false, + explainScore: isSet(object.explainScore) ? globalThis.Boolean(object.explainScore) : false, + isConsistent: isSet(object.isConsistent) ? globalThis.Boolean(object.isConsistent) : false, + vectors: globalThis.Array.isArray(object?.vectors) ? object.vectors.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: MetadataRequest): unknown { + const obj: any = {}; + if (message.uuid === true) { + obj.uuid = message.uuid; + } + if (message.vector === true) { + obj.vector = message.vector; + } + if (message.creationTimeUnix === true) { + obj.creationTimeUnix = message.creationTimeUnix; + } + if (message.lastUpdateTimeUnix === true) { + obj.lastUpdateTimeUnix = message.lastUpdateTimeUnix; + } + if (message.distance === true) { + obj.distance = message.distance; + } + if (message.certainty === true) { + obj.certainty = message.certainty; + } + if (message.score === true) { + obj.score = message.score; + } + if (message.explainScore === true) { + obj.explainScore = message.explainScore; + } + if (message.isConsistent === true) { + obj.isConsistent = message.isConsistent; + } + if (message.vectors?.length) { + obj.vectors = message.vectors; + } + return obj; + }, + + create(base?: DeepPartial): MetadataRequest { + return MetadataRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): MetadataRequest { + const message = createBaseMetadataRequest(); + message.uuid = object.uuid ?? false; + message.vector = object.vector ?? false; + message.creationTimeUnix = object.creationTimeUnix ?? false; + message.lastUpdateTimeUnix = object.lastUpdateTimeUnix ?? false; + message.distance = object.distance ?? false; + message.certainty = object.certainty ?? false; + message.score = object.score ?? false; + message.explainScore = object.explainScore ?? false; + message.isConsistent = object.isConsistent ?? false; + message.vectors = object.vectors?.map((e) => e) || []; + return message; + }, +}; + +function createBasePropertiesRequest(): PropertiesRequest { + return { nonRefProperties: [], refProperties: [], objectProperties: [], returnAllNonrefProperties: false }; +} + +export const PropertiesRequest = { + encode(message: PropertiesRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.nonRefProperties) { + writer.uint32(10).string(v!); + } + for (const v of message.refProperties) { + RefPropertiesRequest.encode(v!, writer.uint32(18).fork()).ldelim(); + } + for (const v of message.objectProperties) { + ObjectPropertiesRequest.encode(v!, writer.uint32(26).fork()).ldelim(); + } + if (message.returnAllNonrefProperties === true) { + writer.uint32(88).bool(message.returnAllNonrefProperties); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PropertiesRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePropertiesRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.nonRefProperties.push(reader.string()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.refProperties.push(RefPropertiesRequest.decode(reader, reader.uint32())); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.objectProperties.push(ObjectPropertiesRequest.decode(reader, reader.uint32())); + continue; + case 11: + if (tag !== 88) { + break; + } + + message.returnAllNonrefProperties = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): PropertiesRequest { + return { + nonRefProperties: globalThis.Array.isArray(object?.nonRefProperties) + ? object.nonRefProperties.map((e: any) => globalThis.String(e)) + : [], + refProperties: globalThis.Array.isArray(object?.refProperties) + ? object.refProperties.map((e: any) => RefPropertiesRequest.fromJSON(e)) + : [], + objectProperties: globalThis.Array.isArray(object?.objectProperties) + ? object.objectProperties.map((e: any) => ObjectPropertiesRequest.fromJSON(e)) + : [], + returnAllNonrefProperties: isSet(object.returnAllNonrefProperties) + ? globalThis.Boolean(object.returnAllNonrefProperties) + : false, + }; + }, + + toJSON(message: PropertiesRequest): unknown { + const obj: any = {}; + if (message.nonRefProperties?.length) { + obj.nonRefProperties = message.nonRefProperties; + } + if (message.refProperties?.length) { + obj.refProperties = message.refProperties.map((e) => RefPropertiesRequest.toJSON(e)); + } + if (message.objectProperties?.length) { + obj.objectProperties = message.objectProperties.map((e) => ObjectPropertiesRequest.toJSON(e)); + } + if (message.returnAllNonrefProperties === true) { + obj.returnAllNonrefProperties = message.returnAllNonrefProperties; + } + return obj; + }, + + create(base?: DeepPartial): PropertiesRequest { + return PropertiesRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): PropertiesRequest { + const message = createBasePropertiesRequest(); + message.nonRefProperties = object.nonRefProperties?.map((e) => e) || []; + message.refProperties = object.refProperties?.map((e) => RefPropertiesRequest.fromPartial(e)) || []; + message.objectProperties = object.objectProperties?.map((e) => ObjectPropertiesRequest.fromPartial(e)) || []; + message.returnAllNonrefProperties = object.returnAllNonrefProperties ?? false; + return message; + }, +}; + +function createBaseObjectPropertiesRequest(): ObjectPropertiesRequest { + return { propName: "", primitiveProperties: [], objectProperties: [] }; +} + +export const ObjectPropertiesRequest = { + encode(message: ObjectPropertiesRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.propName !== "") { + writer.uint32(10).string(message.propName); + } + for (const v of message.primitiveProperties) { + writer.uint32(18).string(v!); + } + for (const v of message.objectProperties) { + ObjectPropertiesRequest.encode(v!, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ObjectPropertiesRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseObjectPropertiesRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.propName = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.primitiveProperties.push(reader.string()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.objectProperties.push(ObjectPropertiesRequest.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ObjectPropertiesRequest { + return { + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + primitiveProperties: globalThis.Array.isArray(object?.primitiveProperties) + ? object.primitiveProperties.map((e: any) => globalThis.String(e)) + : [], + objectProperties: globalThis.Array.isArray(object?.objectProperties) + ? object.objectProperties.map((e: any) => ObjectPropertiesRequest.fromJSON(e)) + : [], + }; + }, + + toJSON(message: ObjectPropertiesRequest): unknown { + const obj: any = {}; + if (message.propName !== "") { + obj.propName = message.propName; + } + if (message.primitiveProperties?.length) { + obj.primitiveProperties = message.primitiveProperties; + } + if (message.objectProperties?.length) { + obj.objectProperties = message.objectProperties.map((e) => ObjectPropertiesRequest.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): ObjectPropertiesRequest { + return ObjectPropertiesRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): ObjectPropertiesRequest { + const message = createBaseObjectPropertiesRequest(); + message.propName = object.propName ?? ""; + message.primitiveProperties = object.primitiveProperties?.map((e) => e) || []; + message.objectProperties = object.objectProperties?.map((e) => ObjectPropertiesRequest.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseHybrid(): Hybrid { + return { + query: "", + properties: [], + vector: [], + alpha: 0, + fusionType: 0, + vectorBytes: new Uint8Array(0), + targetVectors: [], + nearText: undefined, + nearVector: undefined, + }; +} + +export const Hybrid = { + encode(message: Hybrid, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.query !== "") { + writer.uint32(10).string(message.query); + } + for (const v of message.properties) { + writer.uint32(18).string(v!); + } + writer.uint32(26).fork(); + for (const v of message.vector) { + writer.float(v); + } + writer.ldelim(); + if (message.alpha !== 0) { + writer.uint32(37).float(message.alpha); + } + if (message.fusionType !== 0) { + writer.uint32(40).int32(message.fusionType); + } + if (message.vectorBytes.length !== 0) { + writer.uint32(50).bytes(message.vectorBytes); + } + for (const v of message.targetVectors) { + writer.uint32(58).string(v!); + } + if (message.nearText !== undefined) { + NearTextSearch.encode(message.nearText, writer.uint32(66).fork()).ldelim(); + } + if (message.nearVector !== undefined) { + NearVector.encode(message.nearVector, writer.uint32(74).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Hybrid { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHybrid(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.query = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.properties.push(reader.string()); + continue; + case 3: + if (tag === 29) { + message.vector.push(reader.float()); + + continue; + } + + if (tag === 26) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.vector.push(reader.float()); + } + + continue; + } + + break; + case 4: + if (tag !== 37) { + break; + } + + message.alpha = reader.float(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.fusionType = reader.int32() as any; + continue; + case 6: + if (tag !== 50) { + break; + } + + message.vectorBytes = reader.bytes(); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 8: + if (tag !== 66) { + break; + } + + message.nearText = NearTextSearch.decode(reader, reader.uint32()); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.nearVector = NearVector.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Hybrid { + return { + query: isSet(object.query) ? globalThis.String(object.query) : "", + properties: globalThis.Array.isArray(object?.properties) + ? object.properties.map((e: any) => globalThis.String(e)) + : [], + vector: globalThis.Array.isArray(object?.vector) ? object.vector.map((e: any) => globalThis.Number(e)) : [], + alpha: isSet(object.alpha) ? globalThis.Number(object.alpha) : 0, + fusionType: isSet(object.fusionType) ? hybrid_FusionTypeFromJSON(object.fusionType) : 0, + vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + nearText: isSet(object.nearText) ? NearTextSearch.fromJSON(object.nearText) : undefined, + nearVector: isSet(object.nearVector) ? NearVector.fromJSON(object.nearVector) : undefined, + }; + }, + + toJSON(message: Hybrid): unknown { + const obj: any = {}; + if (message.query !== "") { + obj.query = message.query; + } + if (message.properties?.length) { + obj.properties = message.properties; + } + if (message.vector?.length) { + obj.vector = message.vector; + } + if (message.alpha !== 0) { + obj.alpha = message.alpha; + } + if (message.fusionType !== 0) { + obj.fusionType = hybrid_FusionTypeToJSON(message.fusionType); + } + if (message.vectorBytes.length !== 0) { + obj.vectorBytes = base64FromBytes(message.vectorBytes); + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.nearText !== undefined) { + obj.nearText = NearTextSearch.toJSON(message.nearText); + } + if (message.nearVector !== undefined) { + obj.nearVector = NearVector.toJSON(message.nearVector); + } + return obj; + }, + + create(base?: DeepPartial): Hybrid { + return Hybrid.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Hybrid { + const message = createBaseHybrid(); + message.query = object.query ?? ""; + message.properties = object.properties?.map((e) => e) || []; + message.vector = object.vector?.map((e) => e) || []; + message.alpha = object.alpha ?? 0; + message.fusionType = object.fusionType ?? 0; + message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.nearText = (object.nearText !== undefined && object.nearText !== null) + ? NearTextSearch.fromPartial(object.nearText) + : undefined; + message.nearVector = (object.nearVector !== undefined && object.nearVector !== null) + ? NearVector.fromPartial(object.nearVector) + : undefined; + return message; + }, +}; + +function createBaseNearTextSearch(): NearTextSearch { + return { + query: [], + certainty: undefined, + distance: undefined, + moveTo: undefined, + moveAway: undefined, + targetVectors: [], + }; +} + +export const NearTextSearch = { + encode(message: NearTextSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.query) { + writer.uint32(10).string(v!); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + if (message.moveTo !== undefined) { + NearTextSearch_Move.encode(message.moveTo, writer.uint32(34).fork()).ldelim(); + } + if (message.moveAway !== undefined) { + NearTextSearch_Move.encode(message.moveAway, writer.uint32(42).fork()).ldelim(); + } + for (const v of message.targetVectors) { + writer.uint32(50).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearTextSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearTextSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.query.push(reader.string()); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.moveTo = NearTextSearch_Move.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.moveAway = NearTextSearch_Move.decode(reader, reader.uint32()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearTextSearch { + return { + query: globalThis.Array.isArray(object?.query) ? object.query.map((e: any) => globalThis.String(e)) : [], + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + moveTo: isSet(object.moveTo) ? NearTextSearch_Move.fromJSON(object.moveTo) : undefined, + moveAway: isSet(object.moveAway) ? NearTextSearch_Move.fromJSON(object.moveAway) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearTextSearch): unknown { + const obj: any = {}; + if (message.query?.length) { + obj.query = message.query; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.moveTo !== undefined) { + obj.moveTo = NearTextSearch_Move.toJSON(message.moveTo); + } + if (message.moveAway !== undefined) { + obj.moveAway = NearTextSearch_Move.toJSON(message.moveAway); + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearTextSearch { + return NearTextSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearTextSearch { + const message = createBaseNearTextSearch(); + message.query = object.query?.map((e) => e) || []; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.moveTo = (object.moveTo !== undefined && object.moveTo !== null) + ? NearTextSearch_Move.fromPartial(object.moveTo) + : undefined; + message.moveAway = (object.moveAway !== undefined && object.moveAway !== null) + ? NearTextSearch_Move.fromPartial(object.moveAway) + : undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearTextSearch_Move(): NearTextSearch_Move { + return { force: 0, concepts: [], uuids: [] }; +} + +export const NearTextSearch_Move = { + encode(message: NearTextSearch_Move, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.force !== 0) { + writer.uint32(13).float(message.force); + } + for (const v of message.concepts) { + writer.uint32(18).string(v!); + } + for (const v of message.uuids) { + writer.uint32(26).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearTextSearch_Move { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearTextSearch_Move(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.force = reader.float(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.concepts.push(reader.string()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.uuids.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearTextSearch_Move { + return { + force: isSet(object.force) ? globalThis.Number(object.force) : 0, + concepts: globalThis.Array.isArray(object?.concepts) ? object.concepts.map((e: any) => globalThis.String(e)) : [], + uuids: globalThis.Array.isArray(object?.uuids) ? object.uuids.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: NearTextSearch_Move): unknown { + const obj: any = {}; + if (message.force !== 0) { + obj.force = message.force; + } + if (message.concepts?.length) { + obj.concepts = message.concepts; + } + if (message.uuids?.length) { + obj.uuids = message.uuids; + } + return obj; + }, + + create(base?: DeepPartial): NearTextSearch_Move { + return NearTextSearch_Move.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearTextSearch_Move { + const message = createBaseNearTextSearch_Move(); + message.force = object.force ?? 0; + message.concepts = object.concepts?.map((e) => e) || []; + message.uuids = object.uuids?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearImageSearch(): NearImageSearch { + return { image: "", certainty: undefined, distance: undefined, targetVectors: [] }; +} + +export const NearImageSearch = { + encode(message: NearImageSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.image !== "") { + writer.uint32(10).string(message.image); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearImageSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearImageSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.image = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearImageSearch { + return { + image: isSet(object.image) ? globalThis.String(object.image) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearImageSearch): unknown { + const obj: any = {}; + if (message.image !== "") { + obj.image = message.image; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearImageSearch { + return NearImageSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearImageSearch { + const message = createBaseNearImageSearch(); + message.image = object.image ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearAudioSearch(): NearAudioSearch { + return { audio: "", certainty: undefined, distance: undefined, targetVectors: [] }; +} + +export const NearAudioSearch = { + encode(message: NearAudioSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.audio !== "") { + writer.uint32(10).string(message.audio); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearAudioSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearAudioSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.audio = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearAudioSearch { + return { + audio: isSet(object.audio) ? globalThis.String(object.audio) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearAudioSearch): unknown { + const obj: any = {}; + if (message.audio !== "") { + obj.audio = message.audio; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearAudioSearch { + return NearAudioSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearAudioSearch { + const message = createBaseNearAudioSearch(); + message.audio = object.audio ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearVideoSearch(): NearVideoSearch { + return { video: "", certainty: undefined, distance: undefined, targetVectors: [] }; +} + +export const NearVideoSearch = { + encode(message: NearVideoSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.video !== "") { + writer.uint32(10).string(message.video); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearVideoSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearVideoSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.video = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearVideoSearch { + return { + video: isSet(object.video) ? globalThis.String(object.video) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearVideoSearch): unknown { + const obj: any = {}; + if (message.video !== "") { + obj.video = message.video; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearVideoSearch { + return NearVideoSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearVideoSearch { + const message = createBaseNearVideoSearch(); + message.video = object.video ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearDepthSearch(): NearDepthSearch { + return { depth: "", certainty: undefined, distance: undefined, targetVectors: [] }; +} + +export const NearDepthSearch = { + encode(message: NearDepthSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.depth !== "") { + writer.uint32(10).string(message.depth); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearDepthSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearDepthSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.depth = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearDepthSearch { + return { + depth: isSet(object.depth) ? globalThis.String(object.depth) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearDepthSearch): unknown { + const obj: any = {}; + if (message.depth !== "") { + obj.depth = message.depth; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearDepthSearch { + return NearDepthSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearDepthSearch { + const message = createBaseNearDepthSearch(); + message.depth = object.depth ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearThermalSearch(): NearThermalSearch { + return { thermal: "", certainty: undefined, distance: undefined, targetVectors: [] }; +} + +export const NearThermalSearch = { + encode(message: NearThermalSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.thermal !== "") { + writer.uint32(10).string(message.thermal); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearThermalSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearThermalSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.thermal = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearThermalSearch { + return { + thermal: isSet(object.thermal) ? globalThis.String(object.thermal) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearThermalSearch): unknown { + const obj: any = {}; + if (message.thermal !== "") { + obj.thermal = message.thermal; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearThermalSearch { + return NearThermalSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearThermalSearch { + const message = createBaseNearThermalSearch(); + message.thermal = object.thermal ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearIMUSearch(): NearIMUSearch { + return { imu: "", certainty: undefined, distance: undefined, targetVectors: [] }; +} + +export const NearIMUSearch = { + encode(message: NearIMUSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.imu !== "") { + writer.uint32(10).string(message.imu); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearIMUSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearIMUSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.imu = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearIMUSearch { + return { + imu: isSet(object.imu) ? globalThis.String(object.imu) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearIMUSearch): unknown { + const obj: any = {}; + if (message.imu !== "") { + obj.imu = message.imu; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearIMUSearch { + return NearIMUSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearIMUSearch { + const message = createBaseNearIMUSearch(); + message.imu = object.imu ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseBM25(): BM25 { + return { query: "", properties: [] }; +} + +export const BM25 = { + encode(message: BM25, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.query !== "") { + writer.uint32(10).string(message.query); + } + for (const v of message.properties) { + writer.uint32(18).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BM25 { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBM25(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.query = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.properties.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BM25 { + return { + query: isSet(object.query) ? globalThis.String(object.query) : "", + properties: globalThis.Array.isArray(object?.properties) + ? object.properties.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: BM25): unknown { + const obj: any = {}; + if (message.query !== "") { + obj.query = message.query; + } + if (message.properties?.length) { + obj.properties = message.properties; + } + return obj; + }, + + create(base?: DeepPartial): BM25 { + return BM25.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BM25 { + const message = createBaseBM25(); + message.query = object.query ?? ""; + message.properties = object.properties?.map((e) => e) || []; + return message; + }, +}; + +function createBaseRefPropertiesRequest(): RefPropertiesRequest { + return { referenceProperty: "", properties: undefined, metadata: undefined, targetCollection: "" }; +} + +export const RefPropertiesRequest = { + encode(message: RefPropertiesRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.referenceProperty !== "") { + writer.uint32(10).string(message.referenceProperty); + } + if (message.properties !== undefined) { + PropertiesRequest.encode(message.properties, writer.uint32(18).fork()).ldelim(); + } + if (message.metadata !== undefined) { + MetadataRequest.encode(message.metadata, writer.uint32(26).fork()).ldelim(); + } + if (message.targetCollection !== "") { + writer.uint32(34).string(message.targetCollection); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): RefPropertiesRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRefPropertiesRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.referenceProperty = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.properties = PropertiesRequest.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.metadata = MetadataRequest.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetCollection = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): RefPropertiesRequest { + return { + referenceProperty: isSet(object.referenceProperty) ? globalThis.String(object.referenceProperty) : "", + properties: isSet(object.properties) ? PropertiesRequest.fromJSON(object.properties) : undefined, + metadata: isSet(object.metadata) ? MetadataRequest.fromJSON(object.metadata) : undefined, + targetCollection: isSet(object.targetCollection) ? globalThis.String(object.targetCollection) : "", + }; + }, + + toJSON(message: RefPropertiesRequest): unknown { + const obj: any = {}; + if (message.referenceProperty !== "") { + obj.referenceProperty = message.referenceProperty; + } + if (message.properties !== undefined) { + obj.properties = PropertiesRequest.toJSON(message.properties); + } + if (message.metadata !== undefined) { + obj.metadata = MetadataRequest.toJSON(message.metadata); + } + if (message.targetCollection !== "") { + obj.targetCollection = message.targetCollection; + } + return obj; + }, + + create(base?: DeepPartial): RefPropertiesRequest { + return RefPropertiesRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): RefPropertiesRequest { + const message = createBaseRefPropertiesRequest(); + message.referenceProperty = object.referenceProperty ?? ""; + message.properties = (object.properties !== undefined && object.properties !== null) + ? PropertiesRequest.fromPartial(object.properties) + : undefined; + message.metadata = (object.metadata !== undefined && object.metadata !== null) + ? MetadataRequest.fromPartial(object.metadata) + : undefined; + message.targetCollection = object.targetCollection ?? ""; + return message; + }, +}; + +function createBaseNearVector(): NearVector { + return { vector: [], certainty: undefined, distance: undefined, vectorBytes: new Uint8Array(0), targetVectors: [] }; +} + +export const NearVector = { + encode(message: NearVector, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.vector) { + writer.float(v); + } + writer.ldelim(); + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + if (message.vectorBytes.length !== 0) { + writer.uint32(34).bytes(message.vectorBytes); + } + for (const v of message.targetVectors) { + writer.uint32(42).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearVector { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearVector(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 13) { + message.vector.push(reader.float()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.vector.push(reader.float()); + } + + continue; + } + + break; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.vectorBytes = reader.bytes(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearVector { + return { + vector: globalThis.Array.isArray(object?.vector) ? object.vector.map((e: any) => globalThis.Number(e)) : [], + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearVector): unknown { + const obj: any = {}; + if (message.vector?.length) { + obj.vector = message.vector; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.vectorBytes.length !== 0) { + obj.vectorBytes = base64FromBytes(message.vectorBytes); + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearVector { + return NearVector.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearVector { + const message = createBaseNearVector(); + message.vector = object.vector?.map((e) => e) || []; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearObject(): NearObject { + return { id: "", certainty: undefined, distance: undefined, targetVectors: [] }; +} + +export const NearObject = { + encode(message: NearObject, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearObject { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearObject { + return { + id: isSet(object.id) ? globalThis.String(object.id) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: NearObject): unknown { + const obj: any = {}; + if (message.id !== "") { + obj.id = message.id; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + return obj; + }, + + create(base?: DeepPartial): NearObject { + return NearObject.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearObject { + const message = createBaseNearObject(); + message.id = object.id ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + return message; + }, +}; + +function createBaseRerank(): Rerank { + return { property: "", query: undefined }; +} + +export const Rerank = { + encode(message: Rerank, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.property !== "") { + writer.uint32(10).string(message.property); + } + if (message.query !== undefined) { + writer.uint32(18).string(message.query); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Rerank { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRerank(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.property = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.query = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Rerank { + return { + property: isSet(object.property) ? globalThis.String(object.property) : "", + query: isSet(object.query) ? globalThis.String(object.query) : undefined, + }; + }, + + toJSON(message: Rerank): unknown { + const obj: any = {}; + if (message.property !== "") { + obj.property = message.property; + } + if (message.query !== undefined) { + obj.query = message.query; + } + return obj; + }, + + create(base?: DeepPartial): Rerank { + return Rerank.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Rerank { + const message = createBaseRerank(); + message.property = object.property ?? ""; + message.query = object.query ?? undefined; + return message; + }, +}; + +function createBaseSearchReply(): SearchReply { + return { took: 0, results: [], generativeGroupedResult: undefined, groupByResults: [] }; +} + +export const SearchReply = { + encode(message: SearchReply, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.took !== 0) { + writer.uint32(13).float(message.took); + } + for (const v of message.results) { + SearchResult.encode(v!, writer.uint32(18).fork()).ldelim(); + } + if (message.generativeGroupedResult !== undefined) { + writer.uint32(26).string(message.generativeGroupedResult); + } + for (const v of message.groupByResults) { + GroupByResult.encode(v!, writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SearchReply { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSearchReply(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.took = reader.float(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.results.push(SearchResult.decode(reader, reader.uint32())); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.generativeGroupedResult = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.groupByResults.push(GroupByResult.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SearchReply { + return { + took: isSet(object.took) ? globalThis.Number(object.took) : 0, + results: globalThis.Array.isArray(object?.results) + ? object.results.map((e: any) => SearchResult.fromJSON(e)) + : [], + generativeGroupedResult: isSet(object.generativeGroupedResult) + ? globalThis.String(object.generativeGroupedResult) + : undefined, + groupByResults: globalThis.Array.isArray(object?.groupByResults) + ? object.groupByResults.map((e: any) => GroupByResult.fromJSON(e)) + : [], + }; + }, + + toJSON(message: SearchReply): unknown { + const obj: any = {}; + if (message.took !== 0) { + obj.took = message.took; + } + if (message.results?.length) { + obj.results = message.results.map((e) => SearchResult.toJSON(e)); + } + if (message.generativeGroupedResult !== undefined) { + obj.generativeGroupedResult = message.generativeGroupedResult; + } + if (message.groupByResults?.length) { + obj.groupByResults = message.groupByResults.map((e) => GroupByResult.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): SearchReply { + return SearchReply.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): SearchReply { + const message = createBaseSearchReply(); + message.took = object.took ?? 0; + message.results = object.results?.map((e) => SearchResult.fromPartial(e)) || []; + message.generativeGroupedResult = object.generativeGroupedResult ?? undefined; + message.groupByResults = object.groupByResults?.map((e) => GroupByResult.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseRerankReply(): RerankReply { + return { score: 0 }; +} + +export const RerankReply = { + encode(message: RerankReply, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.score !== 0) { + writer.uint32(9).double(message.score); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): RerankReply { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRerankReply(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 9) { + break; + } + + message.score = reader.double(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): RerankReply { + return { score: isSet(object.score) ? globalThis.Number(object.score) : 0 }; + }, + + toJSON(message: RerankReply): unknown { + const obj: any = {}; + if (message.score !== 0) { + obj.score = message.score; + } + return obj; + }, + + create(base?: DeepPartial): RerankReply { + return RerankReply.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): RerankReply { + const message = createBaseRerankReply(); + message.score = object.score ?? 0; + return message; + }, +}; + +function createBaseGenerativeReply(): GenerativeReply { + return { result: "" }; +} + +export const GenerativeReply = { + encode(message: GenerativeReply, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.result !== "") { + writer.uint32(10).string(message.result); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GenerativeReply { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGenerativeReply(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.result = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GenerativeReply { + return { result: isSet(object.result) ? globalThis.String(object.result) : "" }; + }, + + toJSON(message: GenerativeReply): unknown { + const obj: any = {}; + if (message.result !== "") { + obj.result = message.result; + } + return obj; + }, + + create(base?: DeepPartial): GenerativeReply { + return GenerativeReply.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GenerativeReply { + const message = createBaseGenerativeReply(); + message.result = object.result ?? ""; + return message; + }, +}; + +function createBaseGroupByResult(): GroupByResult { + return { + name: "", + minDistance: 0, + maxDistance: 0, + numberOfObjects: 0, + objects: [], + rerank: undefined, + generative: undefined, + }; +} + +export const GroupByResult = { + encode(message: GroupByResult, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.minDistance !== 0) { + writer.uint32(21).float(message.minDistance); + } + if (message.maxDistance !== 0) { + writer.uint32(29).float(message.maxDistance); + } + if (message.numberOfObjects !== 0) { + writer.uint32(32).int64(message.numberOfObjects); + } + for (const v of message.objects) { + SearchResult.encode(v!, writer.uint32(42).fork()).ldelim(); + } + if (message.rerank !== undefined) { + RerankReply.encode(message.rerank, writer.uint32(50).fork()).ldelim(); + } + if (message.generative !== undefined) { + GenerativeReply.encode(message.generative, writer.uint32(58).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupByResult { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupByResult(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 21) { + break; + } + + message.minDistance = reader.float(); + continue; + case 3: + if (tag !== 29) { + break; + } + + message.maxDistance = reader.float(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.numberOfObjects = longToNumber(reader.int64() as Long); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.objects.push(SearchResult.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.rerank = RerankReply.decode(reader, reader.uint32()); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.generative = GenerativeReply.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupByResult { + return { + name: isSet(object.name) ? globalThis.String(object.name) : "", + minDistance: isSet(object.minDistance) ? globalThis.Number(object.minDistance) : 0, + maxDistance: isSet(object.maxDistance) ? globalThis.Number(object.maxDistance) : 0, + numberOfObjects: isSet(object.numberOfObjects) ? globalThis.Number(object.numberOfObjects) : 0, + objects: globalThis.Array.isArray(object?.objects) + ? object.objects.map((e: any) => SearchResult.fromJSON(e)) + : [], + rerank: isSet(object.rerank) ? RerankReply.fromJSON(object.rerank) : undefined, + generative: isSet(object.generative) ? GenerativeReply.fromJSON(object.generative) : undefined, + }; + }, + + toJSON(message: GroupByResult): unknown { + const obj: any = {}; + if (message.name !== "") { + obj.name = message.name; + } + if (message.minDistance !== 0) { + obj.minDistance = message.minDistance; + } + if (message.maxDistance !== 0) { + obj.maxDistance = message.maxDistance; + } + if (message.numberOfObjects !== 0) { + obj.numberOfObjects = Math.round(message.numberOfObjects); + } + if (message.objects?.length) { + obj.objects = message.objects.map((e) => SearchResult.toJSON(e)); + } + if (message.rerank !== undefined) { + obj.rerank = RerankReply.toJSON(message.rerank); + } + if (message.generative !== undefined) { + obj.generative = GenerativeReply.toJSON(message.generative); + } + return obj; + }, + + create(base?: DeepPartial): GroupByResult { + return GroupByResult.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GroupByResult { + const message = createBaseGroupByResult(); + message.name = object.name ?? ""; + message.minDistance = object.minDistance ?? 0; + message.maxDistance = object.maxDistance ?? 0; + message.numberOfObjects = object.numberOfObjects ?? 0; + message.objects = object.objects?.map((e) => SearchResult.fromPartial(e)) || []; + message.rerank = (object.rerank !== undefined && object.rerank !== null) + ? RerankReply.fromPartial(object.rerank) + : undefined; + message.generative = (object.generative !== undefined && object.generative !== null) + ? GenerativeReply.fromPartial(object.generative) + : undefined; + return message; + }, +}; + +function createBaseSearchResult(): SearchResult { + return { properties: undefined, metadata: undefined }; +} + +export const SearchResult = { + encode(message: SearchResult, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.properties !== undefined) { + PropertiesResult.encode(message.properties, writer.uint32(10).fork()).ldelim(); + } + if (message.metadata !== undefined) { + MetadataResult.encode(message.metadata, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SearchResult { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSearchResult(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.properties = PropertiesResult.decode(reader, reader.uint32()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.metadata = MetadataResult.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SearchResult { + return { + properties: isSet(object.properties) ? PropertiesResult.fromJSON(object.properties) : undefined, + metadata: isSet(object.metadata) ? MetadataResult.fromJSON(object.metadata) : undefined, + }; + }, + + toJSON(message: SearchResult): unknown { + const obj: any = {}; + if (message.properties !== undefined) { + obj.properties = PropertiesResult.toJSON(message.properties); + } + if (message.metadata !== undefined) { + obj.metadata = MetadataResult.toJSON(message.metadata); + } + return obj; + }, + + create(base?: DeepPartial): SearchResult { + return SearchResult.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): SearchResult { + const message = createBaseSearchResult(); + message.properties = (object.properties !== undefined && object.properties !== null) + ? PropertiesResult.fromPartial(object.properties) + : undefined; + message.metadata = (object.metadata !== undefined && object.metadata !== null) + ? MetadataResult.fromPartial(object.metadata) + : undefined; + return message; + }, +}; + +function createBaseMetadataResult(): MetadataResult { + return { + id: "", + vector: [], + creationTimeUnix: 0, + creationTimeUnixPresent: false, + lastUpdateTimeUnix: 0, + lastUpdateTimeUnixPresent: false, + distance: 0, + distancePresent: false, + certainty: 0, + certaintyPresent: false, + score: 0, + scorePresent: false, + explainScore: "", + explainScorePresent: false, + isConsistent: undefined, + generative: "", + generativePresent: false, + isConsistentPresent: false, + vectorBytes: new Uint8Array(0), + idAsBytes: new Uint8Array(0), + rerankScore: 0, + rerankScorePresent: false, + vectors: [], + }; +} + +export const MetadataResult = { + encode(message: MetadataResult, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + writer.uint32(18).fork(); + for (const v of message.vector) { + writer.float(v); + } + writer.ldelim(); + if (message.creationTimeUnix !== 0) { + writer.uint32(24).int64(message.creationTimeUnix); + } + if (message.creationTimeUnixPresent === true) { + writer.uint32(32).bool(message.creationTimeUnixPresent); + } + if (message.lastUpdateTimeUnix !== 0) { + writer.uint32(40).int64(message.lastUpdateTimeUnix); + } + if (message.lastUpdateTimeUnixPresent === true) { + writer.uint32(48).bool(message.lastUpdateTimeUnixPresent); + } + if (message.distance !== 0) { + writer.uint32(61).float(message.distance); + } + if (message.distancePresent === true) { + writer.uint32(64).bool(message.distancePresent); + } + if (message.certainty !== 0) { + writer.uint32(77).float(message.certainty); + } + if (message.certaintyPresent === true) { + writer.uint32(80).bool(message.certaintyPresent); + } + if (message.score !== 0) { + writer.uint32(93).float(message.score); + } + if (message.scorePresent === true) { + writer.uint32(96).bool(message.scorePresent); + } + if (message.explainScore !== "") { + writer.uint32(106).string(message.explainScore); + } + if (message.explainScorePresent === true) { + writer.uint32(112).bool(message.explainScorePresent); + } + if (message.isConsistent !== undefined) { + writer.uint32(120).bool(message.isConsistent); + } + if (message.generative !== "") { + writer.uint32(130).string(message.generative); + } + if (message.generativePresent === true) { + writer.uint32(136).bool(message.generativePresent); + } + if (message.isConsistentPresent === true) { + writer.uint32(144).bool(message.isConsistentPresent); + } + if (message.vectorBytes.length !== 0) { + writer.uint32(154).bytes(message.vectorBytes); + } + if (message.idAsBytes.length !== 0) { + writer.uint32(162).bytes(message.idAsBytes); + } + if (message.rerankScore !== 0) { + writer.uint32(169).double(message.rerankScore); + } + if (message.rerankScorePresent === true) { + writer.uint32(176).bool(message.rerankScorePresent); + } + for (const v of message.vectors) { + Vectors.encode(v!, writer.uint32(186).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MetadataResult { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMetadataResult(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + case 2: + if (tag === 21) { + message.vector.push(reader.float()); + + continue; + } + + if (tag === 18) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.vector.push(reader.float()); + } + + continue; + } + + break; + case 3: + if (tag !== 24) { + break; + } + + message.creationTimeUnix = longToNumber(reader.int64() as Long); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.creationTimeUnixPresent = reader.bool(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.lastUpdateTimeUnix = longToNumber(reader.int64() as Long); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.lastUpdateTimeUnixPresent = reader.bool(); + continue; + case 7: + if (tag !== 61) { + break; + } + + message.distance = reader.float(); + continue; + case 8: + if (tag !== 64) { + break; + } + + message.distancePresent = reader.bool(); + continue; + case 9: + if (tag !== 77) { + break; + } + + message.certainty = reader.float(); + continue; + case 10: + if (tag !== 80) { + break; + } + + message.certaintyPresent = reader.bool(); + continue; + case 11: + if (tag !== 93) { + break; + } + + message.score = reader.float(); + continue; + case 12: + if (tag !== 96) { + break; + } + + message.scorePresent = reader.bool(); + continue; + case 13: + if (tag !== 106) { + break; + } + + message.explainScore = reader.string(); + continue; + case 14: + if (tag !== 112) { + break; + } + + message.explainScorePresent = reader.bool(); + continue; + case 15: + if (tag !== 120) { + break; + } + + message.isConsistent = reader.bool(); + continue; + case 16: + if (tag !== 130) { + break; + } + + message.generative = reader.string(); + continue; + case 17: + if (tag !== 136) { + break; + } + + message.generativePresent = reader.bool(); + continue; + case 18: + if (tag !== 144) { + break; + } + + message.isConsistentPresent = reader.bool(); + continue; + case 19: + if (tag !== 154) { + break; + } + + message.vectorBytes = reader.bytes(); + continue; + case 20: + if (tag !== 162) { + break; + } + + message.idAsBytes = reader.bytes(); + continue; + case 21: + if (tag !== 169) { + break; + } + + message.rerankScore = reader.double(); + continue; + case 22: + if (tag !== 176) { + break; + } + + message.rerankScorePresent = reader.bool(); + continue; + case 23: + if (tag !== 186) { + break; + } + + message.vectors.push(Vectors.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): MetadataResult { + return { + id: isSet(object.id) ? globalThis.String(object.id) : "", + vector: globalThis.Array.isArray(object?.vector) ? object.vector.map((e: any) => globalThis.Number(e)) : [], + creationTimeUnix: isSet(object.creationTimeUnix) ? globalThis.Number(object.creationTimeUnix) : 0, + creationTimeUnixPresent: isSet(object.creationTimeUnixPresent) + ? globalThis.Boolean(object.creationTimeUnixPresent) + : false, + lastUpdateTimeUnix: isSet(object.lastUpdateTimeUnix) ? globalThis.Number(object.lastUpdateTimeUnix) : 0, + lastUpdateTimeUnixPresent: isSet(object.lastUpdateTimeUnixPresent) + ? globalThis.Boolean(object.lastUpdateTimeUnixPresent) + : false, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : 0, + distancePresent: isSet(object.distancePresent) ? globalThis.Boolean(object.distancePresent) : false, + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : 0, + certaintyPresent: isSet(object.certaintyPresent) ? globalThis.Boolean(object.certaintyPresent) : false, + score: isSet(object.score) ? globalThis.Number(object.score) : 0, + scorePresent: isSet(object.scorePresent) ? globalThis.Boolean(object.scorePresent) : false, + explainScore: isSet(object.explainScore) ? globalThis.String(object.explainScore) : "", + explainScorePresent: isSet(object.explainScorePresent) ? globalThis.Boolean(object.explainScorePresent) : false, + isConsistent: isSet(object.isConsistent) ? globalThis.Boolean(object.isConsistent) : undefined, + generative: isSet(object.generative) ? globalThis.String(object.generative) : "", + generativePresent: isSet(object.generativePresent) ? globalThis.Boolean(object.generativePresent) : false, + isConsistentPresent: isSet(object.isConsistentPresent) ? globalThis.Boolean(object.isConsistentPresent) : false, + vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + idAsBytes: isSet(object.idAsBytes) ? bytesFromBase64(object.idAsBytes) : new Uint8Array(0), + rerankScore: isSet(object.rerankScore) ? globalThis.Number(object.rerankScore) : 0, + rerankScorePresent: isSet(object.rerankScorePresent) ? globalThis.Boolean(object.rerankScorePresent) : false, + vectors: globalThis.Array.isArray(object?.vectors) ? object.vectors.map((e: any) => Vectors.fromJSON(e)) : [], + }; + }, + + toJSON(message: MetadataResult): unknown { + const obj: any = {}; + if (message.id !== "") { + obj.id = message.id; + } + if (message.vector?.length) { + obj.vector = message.vector; + } + if (message.creationTimeUnix !== 0) { + obj.creationTimeUnix = Math.round(message.creationTimeUnix); + } + if (message.creationTimeUnixPresent === true) { + obj.creationTimeUnixPresent = message.creationTimeUnixPresent; + } + if (message.lastUpdateTimeUnix !== 0) { + obj.lastUpdateTimeUnix = Math.round(message.lastUpdateTimeUnix); + } + if (message.lastUpdateTimeUnixPresent === true) { + obj.lastUpdateTimeUnixPresent = message.lastUpdateTimeUnixPresent; + } + if (message.distance !== 0) { + obj.distance = message.distance; + } + if (message.distancePresent === true) { + obj.distancePresent = message.distancePresent; + } + if (message.certainty !== 0) { + obj.certainty = message.certainty; + } + if (message.certaintyPresent === true) { + obj.certaintyPresent = message.certaintyPresent; + } + if (message.score !== 0) { + obj.score = message.score; + } + if (message.scorePresent === true) { + obj.scorePresent = message.scorePresent; + } + if (message.explainScore !== "") { + obj.explainScore = message.explainScore; + } + if (message.explainScorePresent === true) { + obj.explainScorePresent = message.explainScorePresent; + } + if (message.isConsistent !== undefined) { + obj.isConsistent = message.isConsistent; + } + if (message.generative !== "") { + obj.generative = message.generative; + } + if (message.generativePresent === true) { + obj.generativePresent = message.generativePresent; + } + if (message.isConsistentPresent === true) { + obj.isConsistentPresent = message.isConsistentPresent; + } + if (message.vectorBytes.length !== 0) { + obj.vectorBytes = base64FromBytes(message.vectorBytes); + } + if (message.idAsBytes.length !== 0) { + obj.idAsBytes = base64FromBytes(message.idAsBytes); + } + if (message.rerankScore !== 0) { + obj.rerankScore = message.rerankScore; + } + if (message.rerankScorePresent === true) { + obj.rerankScorePresent = message.rerankScorePresent; + } + if (message.vectors?.length) { + obj.vectors = message.vectors.map((e) => Vectors.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): MetadataResult { + return MetadataResult.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): MetadataResult { + const message = createBaseMetadataResult(); + message.id = object.id ?? ""; + message.vector = object.vector?.map((e) => e) || []; + message.creationTimeUnix = object.creationTimeUnix ?? 0; + message.creationTimeUnixPresent = object.creationTimeUnixPresent ?? false; + message.lastUpdateTimeUnix = object.lastUpdateTimeUnix ?? 0; + message.lastUpdateTimeUnixPresent = object.lastUpdateTimeUnixPresent ?? false; + message.distance = object.distance ?? 0; + message.distancePresent = object.distancePresent ?? false; + message.certainty = object.certainty ?? 0; + message.certaintyPresent = object.certaintyPresent ?? false; + message.score = object.score ?? 0; + message.scorePresent = object.scorePresent ?? false; + message.explainScore = object.explainScore ?? ""; + message.explainScorePresent = object.explainScorePresent ?? false; + message.isConsistent = object.isConsistent ?? undefined; + message.generative = object.generative ?? ""; + message.generativePresent = object.generativePresent ?? false; + message.isConsistentPresent = object.isConsistentPresent ?? false; + message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + message.idAsBytes = object.idAsBytes ?? new Uint8Array(0); + message.rerankScore = object.rerankScore ?? 0; + message.rerankScorePresent = object.rerankScorePresent ?? false; + message.vectors = object.vectors?.map((e) => Vectors.fromPartial(e)) || []; + return message; + }, +}; + +function createBasePropertiesResult(): PropertiesResult { + return { + nonRefProperties: undefined, + refProps: [], + targetCollection: "", + metadata: undefined, + numberArrayProperties: [], + intArrayProperties: [], + textArrayProperties: [], + booleanArrayProperties: [], + objectProperties: [], + objectArrayProperties: [], + nonRefProps: undefined, + refPropsRequested: false, + }; +} + +export const PropertiesResult = { + encode(message: PropertiesResult, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.nonRefProperties !== undefined) { + Struct.encode(Struct.wrap(message.nonRefProperties), writer.uint32(10).fork()).ldelim(); + } + for (const v of message.refProps) { + RefPropertiesResult.encode(v!, writer.uint32(18).fork()).ldelim(); + } + if (message.targetCollection !== "") { + writer.uint32(26).string(message.targetCollection); + } + if (message.metadata !== undefined) { + MetadataResult.encode(message.metadata, writer.uint32(34).fork()).ldelim(); + } + for (const v of message.numberArrayProperties) { + NumberArrayProperties.encode(v!, writer.uint32(42).fork()).ldelim(); + } + for (const v of message.intArrayProperties) { + IntArrayProperties.encode(v!, writer.uint32(50).fork()).ldelim(); + } + for (const v of message.textArrayProperties) { + TextArrayProperties.encode(v!, writer.uint32(58).fork()).ldelim(); + } + for (const v of message.booleanArrayProperties) { + BooleanArrayProperties.encode(v!, writer.uint32(66).fork()).ldelim(); + } + for (const v of message.objectProperties) { + ObjectProperties.encode(v!, writer.uint32(74).fork()).ldelim(); + } + for (const v of message.objectArrayProperties) { + ObjectArrayProperties.encode(v!, writer.uint32(82).fork()).ldelim(); + } + if (message.nonRefProps !== undefined) { + Properties.encode(message.nonRefProps, writer.uint32(90).fork()).ldelim(); + } + if (message.refPropsRequested === true) { + writer.uint32(96).bool(message.refPropsRequested); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PropertiesResult { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePropertiesResult(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.nonRefProperties = Struct.unwrap(Struct.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.refProps.push(RefPropertiesResult.decode(reader, reader.uint32())); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.targetCollection = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.metadata = MetadataResult.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.numberArrayProperties.push(NumberArrayProperties.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.intArrayProperties.push(IntArrayProperties.decode(reader, reader.uint32())); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.textArrayProperties.push(TextArrayProperties.decode(reader, reader.uint32())); + continue; + case 8: + if (tag !== 66) { + break; + } + + message.booleanArrayProperties.push(BooleanArrayProperties.decode(reader, reader.uint32())); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.objectProperties.push(ObjectProperties.decode(reader, reader.uint32())); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.objectArrayProperties.push(ObjectArrayProperties.decode(reader, reader.uint32())); + continue; + case 11: + if (tag !== 90) { + break; + } + + message.nonRefProps = Properties.decode(reader, reader.uint32()); + continue; + case 12: + if (tag !== 96) { + break; + } + + message.refPropsRequested = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): PropertiesResult { + return { + nonRefProperties: isObject(object.nonRefProperties) ? object.nonRefProperties : undefined, + refProps: globalThis.Array.isArray(object?.refProps) + ? object.refProps.map((e: any) => RefPropertiesResult.fromJSON(e)) + : [], + targetCollection: isSet(object.targetCollection) ? globalThis.String(object.targetCollection) : "", + metadata: isSet(object.metadata) ? MetadataResult.fromJSON(object.metadata) : undefined, + numberArrayProperties: globalThis.Array.isArray(object?.numberArrayProperties) + ? object.numberArrayProperties.map((e: any) => NumberArrayProperties.fromJSON(e)) + : [], + intArrayProperties: globalThis.Array.isArray(object?.intArrayProperties) + ? object.intArrayProperties.map((e: any) => IntArrayProperties.fromJSON(e)) + : [], + textArrayProperties: globalThis.Array.isArray(object?.textArrayProperties) + ? object.textArrayProperties.map((e: any) => TextArrayProperties.fromJSON(e)) + : [], + booleanArrayProperties: globalThis.Array.isArray(object?.booleanArrayProperties) + ? object.booleanArrayProperties.map((e: any) => BooleanArrayProperties.fromJSON(e)) + : [], + objectProperties: globalThis.Array.isArray(object?.objectProperties) + ? object.objectProperties.map((e: any) => ObjectProperties.fromJSON(e)) + : [], + objectArrayProperties: globalThis.Array.isArray(object?.objectArrayProperties) + ? object.objectArrayProperties.map((e: any) => ObjectArrayProperties.fromJSON(e)) + : [], + nonRefProps: isSet(object.nonRefProps) ? Properties.fromJSON(object.nonRefProps) : undefined, + refPropsRequested: isSet(object.refPropsRequested) ? globalThis.Boolean(object.refPropsRequested) : false, + }; + }, + + toJSON(message: PropertiesResult): unknown { + const obj: any = {}; + if (message.nonRefProperties !== undefined) { + obj.nonRefProperties = message.nonRefProperties; + } + if (message.refProps?.length) { + obj.refProps = message.refProps.map((e) => RefPropertiesResult.toJSON(e)); + } + if (message.targetCollection !== "") { + obj.targetCollection = message.targetCollection; + } + if (message.metadata !== undefined) { + obj.metadata = MetadataResult.toJSON(message.metadata); + } + if (message.numberArrayProperties?.length) { + obj.numberArrayProperties = message.numberArrayProperties.map((e) => NumberArrayProperties.toJSON(e)); + } + if (message.intArrayProperties?.length) { + obj.intArrayProperties = message.intArrayProperties.map((e) => IntArrayProperties.toJSON(e)); + } + if (message.textArrayProperties?.length) { + obj.textArrayProperties = message.textArrayProperties.map((e) => TextArrayProperties.toJSON(e)); + } + if (message.booleanArrayProperties?.length) { + obj.booleanArrayProperties = message.booleanArrayProperties.map((e) => BooleanArrayProperties.toJSON(e)); + } + if (message.objectProperties?.length) { + obj.objectProperties = message.objectProperties.map((e) => ObjectProperties.toJSON(e)); + } + if (message.objectArrayProperties?.length) { + obj.objectArrayProperties = message.objectArrayProperties.map((e) => ObjectArrayProperties.toJSON(e)); + } + if (message.nonRefProps !== undefined) { + obj.nonRefProps = Properties.toJSON(message.nonRefProps); + } + if (message.refPropsRequested === true) { + obj.refPropsRequested = message.refPropsRequested; + } + return obj; + }, + + create(base?: DeepPartial): PropertiesResult { + return PropertiesResult.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): PropertiesResult { + const message = createBasePropertiesResult(); + message.nonRefProperties = object.nonRefProperties ?? undefined; + message.refProps = object.refProps?.map((e) => RefPropertiesResult.fromPartial(e)) || []; + message.targetCollection = object.targetCollection ?? ""; + message.metadata = (object.metadata !== undefined && object.metadata !== null) + ? MetadataResult.fromPartial(object.metadata) + : undefined; + message.numberArrayProperties = object.numberArrayProperties?.map((e) => NumberArrayProperties.fromPartial(e)) || + []; + message.intArrayProperties = object.intArrayProperties?.map((e) => IntArrayProperties.fromPartial(e)) || []; + message.textArrayProperties = object.textArrayProperties?.map((e) => TextArrayProperties.fromPartial(e)) || []; + message.booleanArrayProperties = object.booleanArrayProperties?.map((e) => BooleanArrayProperties.fromPartial(e)) || + []; + message.objectProperties = object.objectProperties?.map((e) => ObjectProperties.fromPartial(e)) || []; + message.objectArrayProperties = object.objectArrayProperties?.map((e) => ObjectArrayProperties.fromPartial(e)) || + []; + message.nonRefProps = (object.nonRefProps !== undefined && object.nonRefProps !== null) + ? Properties.fromPartial(object.nonRefProps) + : undefined; + message.refPropsRequested = object.refPropsRequested ?? false; + return message; + }, +}; + +function createBaseRefPropertiesResult(): RefPropertiesResult { + return { properties: [], propName: "" }; +} + +export const RefPropertiesResult = { + encode(message: RefPropertiesResult, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.properties) { + PropertiesResult.encode(v!, writer.uint32(10).fork()).ldelim(); + } + if (message.propName !== "") { + writer.uint32(18).string(message.propName); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): RefPropertiesResult { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRefPropertiesResult(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.properties.push(PropertiesResult.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.propName = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): RefPropertiesResult { + return { + properties: globalThis.Array.isArray(object?.properties) + ? object.properties.map((e: any) => PropertiesResult.fromJSON(e)) + : [], + propName: isSet(object.propName) ? globalThis.String(object.propName) : "", + }; + }, + + toJSON(message: RefPropertiesResult): unknown { + const obj: any = {}; + if (message.properties?.length) { + obj.properties = message.properties.map((e) => PropertiesResult.toJSON(e)); + } + if (message.propName !== "") { + obj.propName = message.propName; + } + return obj; + }, + + create(base?: DeepPartial): RefPropertiesResult { + return RefPropertiesResult.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): RefPropertiesResult { + const message = createBaseRefPropertiesResult(); + message.properties = object.properties?.map((e) => PropertiesResult.fromPartial(e)) || []; + message.propName = object.propName ?? ""; + return message; + }, +}; + +function bytesFromBase64(b64: string): Uint8Array { + if ((globalThis as any).Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); + } else { + const bin = globalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if ((globalThis as any).Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(globalThis.String.fromCharCode(byte)); + }); + return globalThis.btoa(bin.join("")); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/tenants.ts b/src/proto/v1/tenants.ts new file mode 100644 index 00000000..4b292921 --- /dev/null +++ b/src/proto/v1/tenants.ts @@ -0,0 +1,369 @@ +/* eslint-disable */ +import _m0 from "protobufjs/minimal.js"; + +export const protobufPackage = "weaviate.v1"; + +export enum TenantActivityStatus { + TENANT_ACTIVITY_STATUS_UNSPECIFIED = 0, + TENANT_ACTIVITY_STATUS_HOT = 1, + TENANT_ACTIVITY_STATUS_COLD = 2, + TENANT_ACTIVITY_STATUS_WARM = 3, + TENANT_ACTIVITY_STATUS_FROZEN = 4, + UNRECOGNIZED = -1, +} + +export function tenantActivityStatusFromJSON(object: any): TenantActivityStatus { + switch (object) { + case 0: + case "TENANT_ACTIVITY_STATUS_UNSPECIFIED": + return TenantActivityStatus.TENANT_ACTIVITY_STATUS_UNSPECIFIED; + case 1: + case "TENANT_ACTIVITY_STATUS_HOT": + return TenantActivityStatus.TENANT_ACTIVITY_STATUS_HOT; + case 2: + case "TENANT_ACTIVITY_STATUS_COLD": + return TenantActivityStatus.TENANT_ACTIVITY_STATUS_COLD; + case 3: + case "TENANT_ACTIVITY_STATUS_WARM": + return TenantActivityStatus.TENANT_ACTIVITY_STATUS_WARM; + case 4: + case "TENANT_ACTIVITY_STATUS_FROZEN": + return TenantActivityStatus.TENANT_ACTIVITY_STATUS_FROZEN; + case -1: + case "UNRECOGNIZED": + default: + return TenantActivityStatus.UNRECOGNIZED; + } +} + +export function tenantActivityStatusToJSON(object: TenantActivityStatus): string { + switch (object) { + case TenantActivityStatus.TENANT_ACTIVITY_STATUS_UNSPECIFIED: + return "TENANT_ACTIVITY_STATUS_UNSPECIFIED"; + case TenantActivityStatus.TENANT_ACTIVITY_STATUS_HOT: + return "TENANT_ACTIVITY_STATUS_HOT"; + case TenantActivityStatus.TENANT_ACTIVITY_STATUS_COLD: + return "TENANT_ACTIVITY_STATUS_COLD"; + case TenantActivityStatus.TENANT_ACTIVITY_STATUS_WARM: + return "TENANT_ACTIVITY_STATUS_WARM"; + case TenantActivityStatus.TENANT_ACTIVITY_STATUS_FROZEN: + return "TENANT_ACTIVITY_STATUS_FROZEN"; + case TenantActivityStatus.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export interface TenantsGetRequest { + collection: string; + names?: TenantNames | undefined; +} + +export interface TenantNames { + values: string[]; +} + +export interface TenantsGetReply { + took: number; + tenants: Tenant[]; +} + +export interface Tenant { + name: string; + activityStatus: TenantActivityStatus; +} + +function createBaseTenantsGetRequest(): TenantsGetRequest { + return { collection: "", names: undefined }; +} + +export const TenantsGetRequest = { + encode(message: TenantsGetRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.collection !== "") { + writer.uint32(10).string(message.collection); + } + if (message.names !== undefined) { + TenantNames.encode(message.names, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TenantsGetRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTenantsGetRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.collection = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.names = TenantNames.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TenantsGetRequest { + return { + collection: isSet(object.collection) ? globalThis.String(object.collection) : "", + names: isSet(object.names) ? TenantNames.fromJSON(object.names) : undefined, + }; + }, + + toJSON(message: TenantsGetRequest): unknown { + const obj: any = {}; + if (message.collection !== "") { + obj.collection = message.collection; + } + if (message.names !== undefined) { + obj.names = TenantNames.toJSON(message.names); + } + return obj; + }, + + create(base?: DeepPartial): TenantsGetRequest { + return TenantsGetRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): TenantsGetRequest { + const message = createBaseTenantsGetRequest(); + message.collection = object.collection ?? ""; + message.names = (object.names !== undefined && object.names !== null) + ? TenantNames.fromPartial(object.names) + : undefined; + return message; + }, +}; + +function createBaseTenantNames(): TenantNames { + return { values: [] }; +} + +export const TenantNames = { + encode(message: TenantNames, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.values) { + writer.uint32(10).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TenantNames { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTenantNames(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TenantNames { + return { + values: globalThis.Array.isArray(object?.values) ? object.values.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: TenantNames): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create(base?: DeepPartial): TenantNames { + return TenantNames.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): TenantNames { + const message = createBaseTenantNames(); + message.values = object.values?.map((e) => e) || []; + return message; + }, +}; + +function createBaseTenantsGetReply(): TenantsGetReply { + return { took: 0, tenants: [] }; +} + +export const TenantsGetReply = { + encode(message: TenantsGetReply, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.took !== 0) { + writer.uint32(13).float(message.took); + } + for (const v of message.tenants) { + Tenant.encode(v!, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TenantsGetReply { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTenantsGetReply(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.took = reader.float(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.tenants.push(Tenant.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TenantsGetReply { + return { + took: isSet(object.took) ? globalThis.Number(object.took) : 0, + tenants: globalThis.Array.isArray(object?.tenants) ? object.tenants.map((e: any) => Tenant.fromJSON(e)) : [], + }; + }, + + toJSON(message: TenantsGetReply): unknown { + const obj: any = {}; + if (message.took !== 0) { + obj.took = message.took; + } + if (message.tenants?.length) { + obj.tenants = message.tenants.map((e) => Tenant.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): TenantsGetReply { + return TenantsGetReply.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): TenantsGetReply { + const message = createBaseTenantsGetReply(); + message.took = object.took ?? 0; + message.tenants = object.tenants?.map((e) => Tenant.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseTenant(): Tenant { + return { name: "", activityStatus: 0 }; +} + +export const Tenant = { + encode(message: Tenant, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.activityStatus !== 0) { + writer.uint32(16).int32(message.activityStatus); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Tenant { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTenant(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.activityStatus = reader.int32() as any; + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Tenant { + return { + name: isSet(object.name) ? globalThis.String(object.name) : "", + activityStatus: isSet(object.activityStatus) ? tenantActivityStatusFromJSON(object.activityStatus) : 0, + }; + }, + + toJSON(message: Tenant): unknown { + const obj: any = {}; + if (message.name !== "") { + obj.name = message.name; + } + if (message.activityStatus !== 0) { + obj.activityStatus = tenantActivityStatusToJSON(message.activityStatus); + } + return obj; + }, + + create(base?: DeepPartial): Tenant { + return Tenant.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Tenant { + const message = createBaseTenant(); + message.name = object.name ?? ""; + message.activityStatus = object.activityStatus ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/weaviate.ts b/src/proto/v1/weaviate.ts new file mode 100644 index 00000000..aeb4a23c --- /dev/null +++ b/src/proto/v1/weaviate.ts @@ -0,0 +1,82 @@ +/* eslint-disable */ +import type { CallContext, CallOptions } from "nice-grpc-common"; +import { BatchObjectsReply, BatchObjectsRequest } from "./batch.js"; +import { BatchDeleteReply, BatchDeleteRequest } from "./batch_delete.js"; +import { SearchReply, SearchRequest } from "./search_get.js"; +import { TenantsGetReply, TenantsGetRequest } from "./tenants.js"; + +export const protobufPackage = "weaviate.v1"; + +export type WeaviateDefinition = typeof WeaviateDefinition; +export const WeaviateDefinition = { + name: "Weaviate", + fullName: "weaviate.v1.Weaviate", + methods: { + search: { + name: "Search", + requestType: SearchRequest, + requestStream: false, + responseType: SearchReply, + responseStream: false, + options: {}, + }, + batchObjects: { + name: "BatchObjects", + requestType: BatchObjectsRequest, + requestStream: false, + responseType: BatchObjectsReply, + responseStream: false, + options: {}, + }, + batchDelete: { + name: "BatchDelete", + requestType: BatchDeleteRequest, + requestStream: false, + responseType: BatchDeleteReply, + responseStream: false, + options: {}, + }, + tenantsGet: { + name: "TenantsGet", + requestType: TenantsGetRequest, + requestStream: false, + responseType: TenantsGetReply, + responseStream: false, + options: {}, + }, + }, +} as const; + +export interface WeaviateServiceImplementation { + search(request: SearchRequest, context: CallContext & CallContextExt): Promise>; + batchObjects( + request: BatchObjectsRequest, + context: CallContext & CallContextExt, + ): Promise>; + batchDelete( + request: BatchDeleteRequest, + context: CallContext & CallContextExt, + ): Promise>; + tenantsGet(request: TenantsGetRequest, context: CallContext & CallContextExt): Promise>; +} + +export interface WeaviateClient { + search(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; + batchObjects( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): Promise; + batchDelete( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): Promise; + tenantsGet(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; diff --git a/src/schema/classCreator.ts b/src/schema/classCreator.ts index e3a9444d..7a74cb09 100644 --- a/src/schema/classCreator.ts +++ b/src/schema/classCreator.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { WeaviateClass } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { WeaviateClass } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class ClassCreator extends CommandBase { private class!: WeaviateClass; @@ -30,6 +30,6 @@ export default class ClassCreator extends CommandBase { return Promise.reject(new Error('invalid usage: ' + this.errors.join(', '))); } const path = `/schema`; - return this.client.post(path, this.class); + return this.client.postReturn(path, this.class); }; } diff --git a/src/schema/classDeleter.ts b/src/schema/classDeleter.ts index fbbeaa60..149c076b 100644 --- a/src/schema/classDeleter.ts +++ b/src/schema/classDeleter.ts @@ -1,6 +1,6 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class ClassDeleter extends CommandBase { private className?: string; diff --git a/src/schema/classExists.ts b/src/schema/classExists.ts index ad973b5e..17ee5755 100644 --- a/src/schema/classExists.ts +++ b/src/schema/classExists.ts @@ -1,7 +1,7 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { WeaviateSchema } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { WeaviateSchema } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class ClassExists extends CommandBase { private className?: string; diff --git a/src/schema/classGetter.ts b/src/schema/classGetter.ts index 0718ee33..80e329e9 100644 --- a/src/schema/classGetter.ts +++ b/src/schema/classGetter.ts @@ -1,7 +1,7 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { WeaviateClass } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { WeaviateClass } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class ClassGetter extends CommandBase { private className?: string; diff --git a/src/schema/classUpdater.ts b/src/schema/classUpdater.ts new file mode 100644 index 00000000..2c0ebb4d --- /dev/null +++ b/src/schema/classUpdater.ts @@ -0,0 +1,35 @@ +import Connection from '../connection/index.js'; +import { WeaviateClass } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; + +export default class ClassUpdater extends CommandBase { + private class!: WeaviateClass; + + constructor(client: Connection) { + super(client); + } + + withClass = (classObj: WeaviateClass) => { + this.class = classObj; + return this; + }; + + validateClass = () => { + if (this.class == undefined || this.class == null) { + this.addError('class object must be set - set with .withClass(class)'); + } + }; + + validate() { + this.validateClass(); + } + + do = (): Promise => { + this.validateClass(); + if (this.errors.length > 0) { + return Promise.reject(new Error('invalid usage: ' + this.errors.join(', '))); + } + const path = `/schema/${this.class.class}`; + return this.client.put(path, this.class, false); + }; +} diff --git a/src/schema/deleteAll.ts b/src/schema/deleteAll.ts index c5a95938..24f4ed90 100644 --- a/src/schema/deleteAll.ts +++ b/src/schema/deleteAll.ts @@ -1,6 +1,6 @@ -import SchemaGetter from './getter'; -import ClassDeleter from './classDeleter'; -import Connection from '../connection'; +import Connection from '../connection/index.js'; +import ClassDeleter from './classDeleter.js'; +import SchemaGetter from './getter.js'; export default async (client: Connection) => { const getter = new SchemaGetter(client); diff --git a/src/schema/getter.ts b/src/schema/getter.ts index 4c7ff168..3a43eccc 100644 --- a/src/schema/getter.ts +++ b/src/schema/getter.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { WeaviateSchema } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { WeaviateSchema } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class SchemaGetter extends CommandBase { constructor(client: Connection) { diff --git a/src/schema/index.ts b/src/schema/index.ts index 49a27c0c..94e1db63 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -1,25 +1,27 @@ -import ClassCreator from './classCreator'; -import ClassDeleter from './classDeleter'; -import ClassExists from './classExists'; -import ClassGetter from './classGetter'; -import PropertyCreator from './propertyCreator'; -import SchemaGetter from './getter'; -import ShardsGetter from './shardsGetter'; -import ShardUpdater from './shardUpdater'; -import ShardsUpdater from './shardsUpdater'; -import TenantsCreator from './tenantsCreator'; -import TenantsGetter from './tenantsGetter'; -import TenantsUpdater from './tenantsUpdater'; -import TenantsDeleter from './tenantsDeleter'; -import Connection from '../connection'; -import deleteAll from './deleteAll'; -import { Tenant } from '../openapi/types'; -import TenantsExists from './tenantsExists'; +import Connection from '../connection/index.js'; +import { Tenant } from '../openapi/types.js'; +import ClassCreator from './classCreator.js'; +import ClassDeleter from './classDeleter.js'; +import ClassExists from './classExists.js'; +import ClassGetter from './classGetter.js'; +import ClassUpdater from './classUpdater.js'; +import deleteAll from './deleteAll.js'; +import SchemaGetter from './getter.js'; +import PropertyCreator from './propertyCreator.js'; +import ShardUpdater from './shardUpdater.js'; +import ShardsGetter from './shardsGetter.js'; +import ShardsUpdater from './shardsUpdater.js'; +import TenantsCreator from './tenantsCreator.js'; +import TenantsDeleter from './tenantsDeleter.js'; +import TenantsExists from './tenantsExists.js'; +import TenantsGetter from './tenantsGetter.js'; +import TenantsUpdater from './tenantsUpdater.js'; export interface Schema { classCreator: () => ClassCreator; classDeleter: () => ClassDeleter; classGetter: () => ClassGetter; + classUpdater: () => ClassUpdater; exists: (className: string) => Promise; getter: () => SchemaGetter; propertyCreator: () => PropertyCreator; @@ -39,6 +41,7 @@ const schema = (client: Connection): Schema => { classCreator: () => new ClassCreator(client), classDeleter: () => new ClassDeleter(client), classGetter: () => new ClassGetter(client), + classUpdater: () => new ClassUpdater(client), exists: (className: string) => new ClassExists(client).withClassName(className).do(), getter: () => new SchemaGetter(client), propertyCreator: () => new PropertyCreator(client), @@ -58,15 +61,15 @@ const schema = (client: Connection): Schema => { }; export default schema; -export { default as ClassCreator } from './classCreator'; -export { default as ClassDeleter } from './classDeleter'; -export { default as ClassGetter } from './classGetter'; -export { default as PropertyCreator } from './propertyCreator'; -export { default as SchemaGetter } from './getter'; -export { default as ShardUpdater } from './shardUpdater'; -export { default as ShardsUpdater } from './shardsUpdater'; -export { default as TenantsCreator } from './tenantsCreator'; -export { default as TenantsUpdater } from './tenantsUpdater'; -export { default as TenantsGetter } from './tenantsGetter'; -export { default as TenantsDeleter } from './tenantsDeleter'; -export { default as TenantsExists } from './tenantsExists'; +export { default as ClassCreator } from './classCreator.js'; +export { default as ClassDeleter } from './classDeleter.js'; +export { default as ClassGetter } from './classGetter.js'; +export { default as SchemaGetter } from './getter.js'; +export { default as PropertyCreator } from './propertyCreator.js'; +export { default as ShardUpdater } from './shardUpdater.js'; +export { default as ShardsUpdater } from './shardsUpdater.js'; +export { default as TenantsCreator } from './tenantsCreator.js'; +export { default as TenantsDeleter } from './tenantsDeleter.js'; +export { default as TenantsExists } from './tenantsExists.js'; +export { default as TenantsGetter } from './tenantsGetter.js'; +export { default as TenantsUpdater } from './tenantsUpdater.js'; diff --git a/src/schema/journey.test.ts b/src/schema/journey.test.ts index aba97a0e..21b0dfa8 100644 --- a/src/schema/journey.test.ts +++ b/src/schema/journey.test.ts @@ -1,13 +1,19 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import weaviate, { WeaviateClient } from '..'; -import { - WeaviateClass, - Property, - WeaviateSchema, - ShardStatus, - ShardStatusList, - Tenant, -} from '../openapi/types'; +import { Meta, Property, ShardStatus, ShardStatusList, Tenant, WeaviateClass } from '../openapi/types.js'; +import weaviate, { WeaviateClient } from '../v2/index.js'; + +const isVer = (client: WeaviateClient, minor: number, patch: number) => + client.misc + .metaGetter() + .do() + .then((res: Meta) => res.version) + .then((version: string | undefined) => { + if (!version) { + return false; + } + const semver = version.split('.').map((v) => parseInt(v, 10)); + return semver[1] >= minor && semver[2] >= patch; + }); describe('schema', () => { const client = weaviate.client({ @@ -15,9 +21,10 @@ describe('schema', () => { host: 'localhost:8080', }); - const classObj = newClassObject('MyThingClass'); + const classObjPromise = newClassObject('MyThingClass', isVer(client, 25, 0), isVer(client, 25, 2)); - it('creates a thing class (implicitly)', () => { + it('creates a thing class (implicitly)', async () => { + const classObj = await classObjPromise; return client.schema .classCreator() .withClass(classObj) @@ -27,7 +34,8 @@ describe('schema', () => { }); }); - it('gets an existing class', () => { + it('gets an existing class', async () => { + const classObj = await classObjPromise; return client.schema .classGetter() .withClassName(classObj.class) @@ -37,7 +45,8 @@ describe('schema', () => { }); }); - it('checks class existence', () => { + it('checks class existence', async () => { + const classObj = await classObjPromise; return client.schema.exists(classObj.class).then((res) => expect(res).toEqual(true)); }); @@ -92,118 +101,13 @@ describe('schema', () => { .do() .catch((err: Error) => { expect(err.message).toEqual( - 'usage error (422): {"error":[{"message":"Tokenization is not allowed for data type \'int[]\'"}]}' + 'The request to Weaviate failed with status code: 422 and message: {"error":[{"message":"Tokenization is not allowed for data type \'int[]\'"}]}' ); }); }); - it('retrieves the schema and it matches the expectations', () => { - return client.schema - .getter() - .do() - .then((res: WeaviateSchema) => { - expect(res).toEqual({ - classes: [ - { - class: 'MyThingClass', - properties: [ - { - dataType: ['text'], - name: 'stringProp', - tokenization: 'word', - indexFilterable: true, - indexSearchable: true, - moduleConfig: { - 'text2vec-contextionary': { - skip: false, - vectorizePropertyName: false, - }, - }, - }, - { - dataType: ['text'], - name: 'anotherProp', - tokenization: 'field', - indexFilterable: true, - indexSearchable: true, - moduleConfig: { - 'text2vec-contextionary': { - skip: false, - vectorizePropertyName: false, - }, - }, - }, - ], - vectorIndexType: 'hnsw', - vectorizer: 'text2vec-contextionary', - vectorIndexConfig: { - cleanupIntervalSeconds: 300, - distance: 'cosine', - dynamicEfFactor: 8, - dynamicEfMax: 500, - dynamicEfMin: 100, - ef: -1, - maxConnections: 64, - pq: { - bitCompression: false, - centroids: 256, - enabled: false, - encoder: { - distribution: 'log-normal', - type: 'kmeans', - }, - segments: 0, - trainingLimit: 100000, - }, - bq: { - enabled: false, - }, - skip: false, - efConstruction: 128, - vectorCacheMaxObjects: 500000, - flatSearchCutoff: 40000, - }, - invertedIndexConfig: { - cleanupIntervalSeconds: 60, - bm25: { - b: 0.75, - k1: 1.2, - }, - stopwords: { - preset: 'en', - additions: null, - removals: null, - }, - }, - moduleConfig: { - 'text2vec-contextionary': { - vectorizeClassName: true, - }, - }, - multiTenancyConfig: { - autoTenantCreation: false, - enabled: false, - }, - shardingConfig: { - actualCount: 1, - actualVirtualCount: 128, - desiredCount: 1, - desiredVirtualCount: 128, - function: 'murmur3', - key: '_id', - strategy: 'hash', - virtualPerPhysical: 128, - }, - replicationConfig: { - factor: 1, - }, - }, - ], - }); - }); - }); - - it('gets the shards of an existing class', () => { + it('gets the shards of an existing class', async () => { + const classObj = await classObjPromise; return client.schema .shardsGetter() .withClassName(classObj.class) @@ -216,6 +120,7 @@ describe('schema', () => { }); it('updates a shard of an existing class to readonly', async () => { + const classObj = await classObjPromise; const shards = await getShards(client, classObj.class); expect(Array.isArray(shards)).toBe(true); expect(shards.length).toEqual(1); @@ -232,6 +137,7 @@ describe('schema', () => { }); it('updates a shard of an existing class to ready', async () => { + const classObj = await classObjPromise; const shards = await getShards(client, classObj.class); expect(Array.isArray(shards)).toBe(true); expect(shards.length).toEqual(1); @@ -247,7 +153,8 @@ describe('schema', () => { }); }); - it('deletes an existing class', () => { + it('deletes an existing class', async () => { + const classObj = await classObjPromise; return client.schema .classDeleter() .withClassName(classObj.class) @@ -259,7 +166,7 @@ describe('schema', () => { it('updates all shards in a class', async () => { const shardCount = 3; - const newClass: any = newClassObject('NewClass'); + const newClass: any = await newClassObject('NewClass', isVer(client, 25, 0), isVer(client, 25, 2)); newClass.shardingConfig.desiredCount = shardCount; await client.schema @@ -302,7 +209,7 @@ describe('schema', () => { }); it('has updated values of bm25 config', async () => { - const newClass: any = newClassObject('NewClass'); + const newClass: any = await newClassObject('NewClass', isVer(client, 25, 0), isVer(client, 25, 2)); const bm25Config = { k1: 1.13, b: 0.222 }; newClass.invertedIndexConfig.bm25 = bm25Config; @@ -319,7 +226,7 @@ describe('schema', () => { }); it('has updated values of stopwords config', async () => { - const newClass: any = newClassObject('SpaceClass'); + const newClass: any = await newClassObject('SpaceClass', isVer(client, 25, 0), isVer(client, 25, 2)); const stopwordConfig: any = { preset: 'en', additions: ['star', 'nebula'], @@ -371,7 +278,7 @@ describe('schema', () => { it('creates a class with explicit replication config', async () => { const replicationFactor = 1; - const newClass: any = newClassObject('SomeClass'); + const newClass: any = await newClassObject('SomeClass', isVer(client, 25, 0), isVer(client, 25, 2)); newClass.replicationConfig.factor = replicationFactor; await client.schema @@ -386,7 +293,7 @@ describe('schema', () => { }); it('creates a class with implicit replication config', async () => { - const newClass: any = newClassObject('SomeClass'); + const newClass: any = await newClassObject('SomeClass', isVer(client, 25, 0), isVer(client, 25, 2)); delete newClass.replicationConfig; await client.schema @@ -400,9 +307,17 @@ describe('schema', () => { return deleteClass(client, newClass.class); }); - it('delete all data from the schema', () => { - const newClass: any = newClassObject('LetsDeleteThisClass'); - const newClass2: any = newClassObject('LetsDeleteThisClassToo'); + it('delete all data from the schema', async () => { + const newClass: any = await newClassObject( + 'LetsDeleteThisClass', + isVer(client, 25, 0), + isVer(client, 25, 2) + ); + const newClass2: any = await newClassObject( + 'LetsDeleteThisClassToo', + isVer(client, 25, 0), + isVer(client, 25, 2) + ); const classNames = [newClass.class, newClass2.class]; Promise.all([ client.schema.classCreator().withClass(newClass).do(), @@ -588,7 +503,7 @@ describe('property setting defaults and migrations', () => { const errMsg1 = '`indexInverted` is deprecated and can not be set together with `indexFilterable` or `indexSearchable`'; - const errMsg2 = '`indexSearchable` is allowed only for text/text[] data types'; + const errMsg2 = '`indexSearchable`'; test.each([ ['text', false, null, false, errMsg1], ['text', false, null, true, errMsg1], @@ -663,7 +578,6 @@ describe('multi tenancy', () => { scheme: 'http', host: 'localhost:8080', }); - const classObj: WeaviateClass = { class: 'MultiTenancy', properties: [ @@ -679,13 +593,20 @@ describe('multi tenancy', () => { vectorIndexType: 'hnsw', vectorizer: 'text2vec-contextionary', multiTenancyConfig: { + autoTenantActivation: true, autoTenantCreation: true, enabled: true, }, }; const tenants: Array = [{ name: 'tenantA' }, { name: 'tenantB' }, { name: 'tenantC' }]; - it('creates a MultiTenancy class', () => { + it('creates a MultiTenancy class', async () => { + if (!(await isVer(client, 25, 0))) { + delete classObj.multiTenancyConfig?.autoTenantCreation; + } + if (!(await isVer(client, 25, 2))) { + delete classObj.multiTenancyConfig?.autoTenantActivation; + } return client.schema .classCreator() .withClass(classObj) @@ -733,51 +654,65 @@ describe('multi tenancy', () => { }); }); - it('successfully finds an existing tenant for MultiTenancy class', () => { + it('successfully finds an existing tenant for MultiTenancy class', async () => { + if (!(await isVer(client, 25, 0))) { + return Promise.resolve(); + } return client.schema .tenantsExists(classObj.class!, tenants[1].name!) .do() - .then((res: boolean) => expect(res).toEqual(true)); + .then((res) => expect(res).toEqual(true)); }); - it('successfully fails to find a non-existant tenant for MultiTenancy class', () => { + it('successfully fails to find a non-existant tenant for MultiTenancy class', async () => { + if (!(await isVer(client, 25, 0))) { + return Promise.resolve(); + } return client.schema .tenantsExists(classObj.class!, 'nonExistantTenant') .do() - .then((res: boolean) => expect(res).toEqual(false)); + .then((res) => expect(res).toEqual(false)); }); it('deletes MultiTenancy class', () => { return deleteClass(client, classObj.class!); }); - const classObjWithoutMultiTenancyConfig = newClassObject('NoMultiTenancy'); + const classObjWithoutMultiTenancyConfig = newClassObject( + 'NoMultiTenancy', + isVer(client, 25, 0), + isVer(client, 25, 2) + ); - it('creates a NoMultiTenancy class', () => { + it('creates a NoMultiTenancy class', async () => { return client.schema .classCreator() - .withClass(classObjWithoutMultiTenancyConfig) + .withClass(await classObjWithoutMultiTenancyConfig) .do() - .then((res: WeaviateClass) => { - expect(res).toEqual(classObjWithoutMultiTenancyConfig); + .then(async (res: WeaviateClass) => { + expect(res).toEqual(await classObjWithoutMultiTenancyConfig); }); }); - it('fails to define tenants for NoMultiTenancy class', () => { + it('fails to define tenants for NoMultiTenancy class', async () => { return client.schema - .tenantsCreator(classObjWithoutMultiTenancyConfig.class!, tenants) + .tenantsCreator((await classObjWithoutMultiTenancyConfig).class!, tenants) .do() .catch((e: Error) => { expect(e.message).toContain('multi-tenancy is not enabled for class \\"NoMultiTenancy\\"'); }); }); - it('deletes NoMultiTenancy class', () => { - return deleteClass(client, classObjWithoutMultiTenancyConfig.class); + it('deletes NoMultiTenancy class', async () => { + return deleteClass(client, (await classObjWithoutMultiTenancyConfig).class); }); }); -function newClassObject(className: string) { +async function newClassObject( + className: string, + is1250Promise: Promise, + is1252Promise: Promise +) { return { class: className, properties: [ @@ -842,7 +777,8 @@ function newClassObject(className: string) { }, }, multiTenancyConfig: { - autoTenantCreation: false, + autoTenantActivation: (await is1252Promise) ? false : undefined, + autoTenantCreation: (await is1250Promise) ? false : undefined, enabled: false, }, shardingConfig: { diff --git a/src/schema/propertyCreator.ts b/src/schema/propertyCreator.ts index 46912733..4c1f2d82 100644 --- a/src/schema/propertyCreator.ts +++ b/src/schema/propertyCreator.ts @@ -1,7 +1,7 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Property } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { Property } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class PropertyCreator extends CommandBase { private className!: string; @@ -44,6 +44,6 @@ export default class PropertyCreator extends CommandBase { return Promise.reject(new Error('invalid usage: ' + this.errors.join(', '))); } const path = `/schema/${this.className}/properties`; - return this.client.post(path, this.property); + return this.client.postReturn(path, this.property); }; } diff --git a/src/schema/shardUpdater.ts b/src/schema/shardUpdater.ts index 61893ac2..dbebbdd9 100644 --- a/src/schema/shardUpdater.ts +++ b/src/schema/shardUpdater.ts @@ -1,6 +1,6 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class ShardUpdater extends CommandBase { private className!: string; diff --git a/src/schema/shardsGetter.ts b/src/schema/shardsGetter.ts index 7efe7eb5..13b52343 100644 --- a/src/schema/shardsGetter.ts +++ b/src/schema/shardsGetter.ts @@ -1,10 +1,11 @@ -import { isValidStringProperty } from '../validation/string'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { ShardStatusList } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { ShardStatusList } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; export default class ShardsGetter extends CommandBase { private className?: string; + private tenant?: string; constructor(client: Connection) { super(client); @@ -15,6 +16,11 @@ export default class ShardsGetter extends CommandBase { return this; }; + withTenant = (tenant: string) => { + this.tenant = tenant; + return this; + }; + validateClassName = () => { if (!isValidStringProperty(this.className)) { this.addError('className must be set - set with .withClassName(className)'); @@ -31,11 +37,11 @@ export default class ShardsGetter extends CommandBase { return Promise.reject(new Error(`invalid usage: ${this.errors.join(', ')}`)); } - return getShards(this.client, this.className); + return getShards(this.client, this.className, this.tenant); }; } -export function getShards(client: Connection, className: any) { - const path = `/schema/${className}/shards`; +export function getShards(client: Connection, className: any, tenant?: string) { + const path = `/schema/${className}/shards${tenant ? `?tenant=${tenant}` : ''}`; return client.get(path); } diff --git a/src/schema/shardsUpdater.ts b/src/schema/shardsUpdater.ts index ba24a145..83b5ec5c 100644 --- a/src/schema/shardsUpdater.ts +++ b/src/schema/shardsUpdater.ts @@ -1,9 +1,9 @@ -import { isValidStringProperty } from '../validation/string'; -import { getShards } from './shardsGetter'; -import { updateShard } from './shardUpdater'; -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { ShardStatus, ShardStatusList } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { ShardStatusList } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; +import { isValidStringProperty } from '../validation/string.js'; +import { updateShard } from './shardUpdater.js'; +import { getShards } from './shardsGetter.js'; export default class ShardsUpdater extends CommandBase { private className!: string; diff --git a/src/schema/tenantsCreator.ts b/src/schema/tenantsCreator.ts index f72f3fc7..a2514738 100644 --- a/src/schema/tenantsCreator.ts +++ b/src/schema/tenantsCreator.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Tenant } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { Tenant } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class TenantsCreator extends CommandBase { private className: string; @@ -17,6 +17,6 @@ export default class TenantsCreator extends CommandBase { }; do = (): Promise> => { - return this.client.post(`/schema/${this.className}/tenants`, this.tenants); + return this.client.postReturn(`/schema/${this.className}/tenants`, this.tenants); }; } diff --git a/src/schema/tenantsDeleter.ts b/src/schema/tenantsDeleter.ts index b6c2325f..c929265e 100644 --- a/src/schema/tenantsDeleter.ts +++ b/src/schema/tenantsDeleter.ts @@ -1,5 +1,5 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; +import Connection from '../connection/index.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class TenantsDeleter extends CommandBase { private className: string; diff --git a/src/schema/tenantsExists.ts b/src/schema/tenantsExists.ts index 9abd98c3..752d578a 100644 --- a/src/schema/tenantsExists.ts +++ b/src/schema/tenantsExists.ts @@ -1,6 +1,5 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Tenant } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class TenantsExists extends CommandBase { private className: string; diff --git a/src/schema/tenantsGetter.ts b/src/schema/tenantsGetter.ts index 17c7c7b6..1733a4fa 100644 --- a/src/schema/tenantsGetter.ts +++ b/src/schema/tenantsGetter.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Tenant } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { Tenant } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class TenantsGetter extends CommandBase { private className: string; diff --git a/src/schema/tenantsUpdater.ts b/src/schema/tenantsUpdater.ts index e68978c5..db38ad4f 100644 --- a/src/schema/tenantsUpdater.ts +++ b/src/schema/tenantsUpdater.ts @@ -1,6 +1,6 @@ -import Connection from '../connection'; -import { CommandBase } from '../validation/commandBase'; -import { Tenant } from '../openapi/types'; +import Connection from '../connection/index.js'; +import { Tenant } from '../openapi/types.js'; +import { CommandBase } from '../validation/commandBase.js'; export default class TenantsUpdater extends CommandBase { private className: string; diff --git a/src/utils/base64.ts b/src/utils/base64.ts index bfb940f4..da040beb 100644 --- a/src/utils/base64.ts +++ b/src/utils/base64.ts @@ -1,46 +1,41 @@ +import fs from 'fs'; + +const isFilePromise = (file: string | Buffer): Promise => + new Promise((resolve, reject) => { + if (file instanceof Buffer) { + resolve(false); + } + fs.stat(file, (err, stats) => { + if (err) { + reject(err); + } + resolve(stats.isFile()); + }); + }); + +const isBuffer = (file: string | Buffer): file is Buffer => file instanceof Buffer; + +const fileToBase64 = (file: string | Buffer): Promise => + isFilePromise(file).then((isFile) => + isFile + ? new Promise((resolve, reject) => { + fs.readFile(file, (err, data) => { + if (err) { + reject(err); + } + resolve(data.toString('base64')); + }); + }) + : isBuffer(file) + ? Promise.resolve(file.toString('base64')) + : Promise.resolve(file) + ); + /** - * This function converts a file blob into a base64 string so that it can be + * This function converts a file buffer into a base64 string so that it can be * sent to Weaviate and stored as a media field. * - * This specific function is only applicable within the browser since it depends on - * the FileReader API. It will throw an error if it is called in a Node environment. - * - * @param {Blob} blob The file blob to convert + * @param {string | Buffer} file The media to convert either as a base64 string, a file path string, or as a buffer. If you passed a base64 string, the function does nothing and returns the string as is. * @returns {string} The base64 string - * @throws An error if the function is called outside of the browser - * - * @example - * // Vanilla JS - * const file = document.querySelector('input[type="file"]').files[0]; - * toBase64FromBlob(file).then((base64) => console.log(base64)); - * - * // React - * const [base64, setBase64] = useState(''); - * const onChange = (event) => toBase64FromBlob(event.target.files[0]).then(setBase64); - * - * // Submit - * const onSubmit = (base64: string) => client.data - * .creator() - * .withClassName('MyClass') - * .withProperties({ myMediaField: base64 }) - * .do(); - * */ -export function toBase64FromBlob(blob: Blob): Promise { - if (typeof window === 'undefined') { - throw new Error('This function is only available in the browser'); - } - const reader = new FileReader(); - return new Promise((resolve, reject) => { - reader.onload = resolve; - reader.onerror = reject; - reader.readAsDataURL(blob); - }).then(() => { - if (typeof reader.result !== 'string') { - throw new Error( - `Unexpected result when converting blob to base64 (result is not a string): ${reader.result}` - ); - } - return reader.result; - }); -} +export const toBase64FromMedia = (media: string | Buffer): Promise => fileToBase64(media); diff --git a/src/utils/beaconPath.ts b/src/utils/beaconPath.ts index f6dea64e..d71c3488 100644 --- a/src/utils/beaconPath.ts +++ b/src/utils/beaconPath.ts @@ -1,6 +1,6 @@ -import { isValidStringProperty } from '../validation/string'; -import { DbVersionSupport } from './dbVersion'; -import { isValidWeaviateVersion } from '../validation/version'; +import { isValidStringProperty } from '../validation/string.js'; +import { isValidWeaviateVersion } from '../validation/version.js'; +import { DbVersionSupport } from './dbVersion.js'; const beaconPathPrefix = 'weaviate://localhost'; diff --git a/src/utils/dbVersion.ts b/src/utils/dbVersion.ts index 1d50b051..7de53d15 100644 --- a/src/utils/dbVersion.ts +++ b/src/utils/dbVersion.ts @@ -1,3 +1,6 @@ +import ConnectionGRPC from '../connection/grpc.js'; +import MetaGetter from '../misc/metaGetter.js'; + export class DbVersionSupport { private dbVersionProvider: VersionProvider; @@ -5,45 +8,50 @@ export class DbVersionSupport { this.dbVersionProvider = dbVersionProvider; } + getVersion = () => this.dbVersionProvider.getVersion(); + supportsClassNameNamespacedEndpointsPromise() { - return this.dbVersionProvider.getVersionPromise().then((version?: string) => ({ - version, - supports: this.supportsClassNameNamespacedEndpoints(version), - warns: { - deprecatedNonClassNameNamespacedEndpointsForObjects: () => - console.warn( - `Usage of objects paths without className is deprecated in Weaviate ${version}. Please provide className parameter` - ), - deprecatedNonClassNameNamespacedEndpointsForReferences: () => - console.warn( - `Usage of references paths without className is deprecated in Weaviate ${version}. Please provide className parameter` - ), - deprecatedNonClassNameNamespacedEndpointsForBeacons: () => - console.warn( - `Usage of beacons paths without className is deprecated in Weaviate ${version}. Please provide className parameter` - ), - deprecatedWeaviateTooOld: () => - console.warn( - `Usage of weaviate ${version} is deprecated. Please consider upgrading to the latest version. See https://www.weaviate.io/developers/weaviate for details.` - ), - notSupportedClassNamespacedEndpointsForObjects: () => - console.warn( - `Usage of objects paths with className is not supported in Weaviate ${version}. className parameter is ignored` - ), - notSupportedClassNamespacedEndpointsForReferences: () => - console.warn( - `Usage of references paths with className is not supported in Weaviate ${version}. className parameter is ignored` - ), - notSupportedClassNamespacedEndpointsForBeacons: () => - console.warn( - `Usage of beacons paths with className is not supported in Weaviate ${version}. className parameter is ignored` - ), - notSupportedClassParameterInEndpointsForObjects: () => - console.warn( - `Usage of objects paths with class query parameter is not supported in Weaviate ${version}. class query parameter is ignored` - ), - }, - })); + return this.dbVersionProvider + .getVersion() + .then((version) => version.show()) + .then((version) => ({ + version: version, + supports: this.supportsClassNameNamespacedEndpoints(version), + warns: { + deprecatedNonClassNameNamespacedEndpointsForObjects: () => + console.warn( + `Usage of objects paths without className is deprecated in Weaviate ${version}. Please provide className parameter` + ), + deprecatedNonClassNameNamespacedEndpointsForReferences: () => + console.warn( + `Usage of references paths without className is deprecated in Weaviate ${version}. Please provide className parameter` + ), + deprecatedNonClassNameNamespacedEndpointsForBeacons: () => + console.warn( + `Usage of beacons paths without className is deprecated in Weaviate ${version}. Please provide className parameter` + ), + deprecatedWeaviateTooOld: () => + console.warn( + `Usage of weaviate ${version} is deprecated. Please consider upgrading to the latest version. See https://www.weaviate.io/developers/weaviate for details.` + ), + notSupportedClassNamespacedEndpointsForObjects: () => + console.warn( + `Usage of objects paths with className is not supported in Weaviate ${version}. className parameter is ignored` + ), + notSupportedClassNamespacedEndpointsForReferences: () => + console.warn( + `Usage of references paths with className is not supported in Weaviate ${version}. className parameter is ignored` + ), + notSupportedClassNamespacedEndpointsForBeacons: () => + console.warn( + `Usage of beacons paths with className is not supported in Weaviate ${version}. className parameter is ignored` + ), + notSupportedClassParameterInEndpointsForObjects: () => + console.warn( + `Usage of objects paths with class query parameter is not supported in Weaviate ${version}. class query parameter is ignored` + ), + }, + })); } // >= 1.14 @@ -58,48 +66,199 @@ export class DbVersionSupport { } return false; } + + private errorMessage = (feature: string, current: string, required: string) => + `${feature} is not supported with Weaviate version v${current}. Please use version v${required} or higher.`; + + supportsCompatibleGrpcService = () => + this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 23, 7), + message: this.errorMessage('The gRPC API', version.show(), '1.23.7'), + }; + }); + + supportsHNSWAndBQ = () => + this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 24, 0), + message: this.errorMessage('HNSW index and BQ quantizer', version.show(), '1.24.0'), + }; + }); + + supportsBm25AndHybridGroupByQueries = () => + this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 25, 0), + message: (query: 'Bm25' | 'Hybrid') => + this.errorMessage(`GroupBy with ${query}`, version.show(), '1.25.0'), + }; + }); + + supportsHybridNearTextAndNearVectorSubsearchQueries = () => { + return this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 25, 0), + message: this.errorMessage('Hybrid nearText/nearVector subsearching', version.show(), '1.25.0'), + }; + }); + }; + + supports125ListValue = () => { + return this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 25, 0), + message: undefined, + }; + }); + }; + + supportsNamedVectors = () => { + return this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 24, 0), + message: this.errorMessage('Named vectors', version.show(), '1.24.0'), + }; + }); + }; + + supportsTenantsGetGRPCMethod = () => { + return this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 25, 0), + message: this.errorMessage('Tenants get method', version.show(), '1.25.0'), + }; + }); + }; + + supportsDynamicVectorIndex = () => { + return this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 25, 0), + message: this.errorMessage('Dynamic vector index', version.show(), '1.25.0'), + }; + }); + }; } const EMPTY_VERSION = ''; export interface VersionProvider { - getVersionPromise(): Promise; + getVersionString(): Promise; + getVersion(): Promise; } export class DbVersionProvider implements VersionProvider { - private versionPromise?: Promise; - private readonly emptyVersionPromise: Promise; - private versionGetter: () => Promise; - - constructor(versionGetter: () => Promise) { - this.versionGetter = versionGetter; + private versionPromise?: Promise; + private versionStringGetter: () => Promise; - this.emptyVersionPromise = Promise.resolve(EMPTY_VERSION); + constructor(versionStringGetter: () => Promise) { + this.versionStringGetter = versionStringGetter; this.versionPromise = undefined; } - getVersionPromise(): Promise { + getVersionString(): Promise { + return this.getVersion().then((version) => version.show()); + } + + getVersion(): Promise { if (this.versionPromise) { return this.versionPromise; } - return this.versionGetter().then((version) => this.assignPromise(version)); + return this.versionStringGetter().then((version) => this.cache(version)); } refresh(force = false): Promise { if (force || !this.versionPromise) { this.versionPromise = undefined; - return this.versionGetter() - .then((version) => this.assignPromise(version)) + return this.versionStringGetter() + .then((version) => this.cache(version)) .then(() => Promise.resolve(true)); } return Promise.resolve(false); } - assignPromise(version: string): Promise { + cache(version: string): Promise { if (version === EMPTY_VERSION) { - return this.emptyVersionPromise; + return Promise.resolve(new DbVersion(0, 0, 0)); } - this.versionPromise = Promise.resolve(version); + this.versionPromise = Promise.resolve(DbVersion.fromString(version)); return this.versionPromise; } } + +export function initDbVersionProvider(conn: ConnectionGRPC) { + const metaGetter = new MetaGetter(conn); + const versionGetter = () => { + return metaGetter.do().then((result) => (result.version ? result.version : '')); + }; + return new DbVersionProvider(versionGetter); +} + +export class DbVersion { + private major: number; + private minor: number; + private patch?: number; + + constructor(major: number, minor: number, patch?: number) { + this.major = major; + this.minor = minor; + this.patch = patch; + } + + static fromString = (version: string) => { + let regex = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; + let match = version.match(regex); + if (match) { + const [_, major, minor, patch] = match; + return new DbVersion(parseInt(major, 10), parseInt(minor, 10), parseInt(patch, 10)); + } + + regex = /^v?(\d+)\.(\d+)$/; + match = version.match(regex); + if (match) { + const [_, major, minor] = match; + return new DbVersion(parseInt(major, 10), parseInt(minor, 10)); + } + + throw new Error(`Invalid version string: ${version}`); + }; + + private checkNumber = (num: number) => { + if (!Number.isSafeInteger(num)) { + throw new Error(`Invalid number: ${num}`); + } + }; + + show = () => + this.major === 0 && this.major === this.minor && this.minor === this.patch + ? '' + : `${this.major}.${this.minor}${this.patch !== undefined ? `.${this.patch}` : ''}`; + + isAtLeast = (major: number, minor: number, patch?: number) => { + this.checkNumber(major); + this.checkNumber(minor); + + if (this.major > major) return true; + if (this.major < major) return false; + + if (this.minor > minor) return true; + if (this.minor < minor) return false; + + if (this.patch !== undefined && patch !== undefined && this.patch >= patch) { + this.checkNumber(patch); + return true; + } + return false; + }; + + isLowerThan = (major: number, minor: number, patch: number) => !this.isAtLeast(major, minor, patch); +} diff --git a/src/utils/journey.test.ts b/src/utils/journey.test.ts index 9238a124..832669c0 100644 --- a/src/utils/journey.test.ts +++ b/src/utils/journey.test.ts @@ -1,4 +1,4 @@ -import { DbVersionProvider, DbVersionSupport } from './dbVersion'; +import { DbVersionProvider, DbVersionSupport } from './dbVersion.js'; const EMPTY_VERSION = ''; const VERSION_1 = '1.2.3'; @@ -9,24 +9,14 @@ describe('db version provider', () => { const versionGetter = () => Promise.resolve(EMPTY_VERSION); const dbVersionProvider = new DbVersionProvider(versionGetter); - return dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(EMPTY_VERSION)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); + return dbVersionProvider.getVersionString().then((version) => expect(version).toBe(EMPTY_VERSION)); }); it('should return proper version', () => { const versionGetter = () => Promise.resolve(VERSION_1); const dbVersionProvider = new DbVersionProvider(versionGetter); - return dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_1)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); + return dbVersionProvider.getVersionString().then((version) => expect(version).toBe(VERSION_1)); }); it('should return new version after refresh', async () => { @@ -44,19 +34,9 @@ describe('db version provider', () => { }; const dbVersionProvider = new DbVersionProvider(versionGetter); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_1)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(VERSION_1)); await dbVersionProvider.refresh(true); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_2)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(VERSION_2)); }); it('should fetch version once', async () => { @@ -72,24 +52,9 @@ describe('db version provider', () => { }; const dbVersionProvider = new DbVersionProvider(versionGetter); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_1)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_1)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_1)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(VERSION_1)); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(VERSION_1)); + await dbVersionProvider.getVersion().then((version) => expect(version.show()).toBe(VERSION_1)); expect(callsCounter).toBe(1); }); @@ -110,36 +75,11 @@ describe('db version provider', () => { }; const dbVersionProvider = new DbVersionProvider(versionGetter); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(EMPTY_VERSION)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(EMPTY_VERSION)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_1)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_1)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); - await dbVersionProvider - .getVersionPromise() - .then((version) => expect(version).toBe(VERSION_1)) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(EMPTY_VERSION)); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(EMPTY_VERSION)); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(VERSION_1)); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(VERSION_1)); + await dbVersionProvider.getVersionString().then((version) => expect(version).toBe(VERSION_1)); expect(callsCounter).toBe(3); }); @@ -152,33 +92,47 @@ describe('db version support', () => { const dbVersionProvider = new DbVersionProvider(() => Promise.resolve(version)); const dbVersionSupport = new DbVersionSupport(dbVersionProvider); - await dbVersionSupport - .supportsClassNameNamespacedEndpointsPromise() - .then((support) => { - expect(support.supports).toBe(false); - expect(support.version).toBe(version); - }) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); + const support = await dbVersionSupport.supportsClassNameNamespacedEndpointsPromise(); + expect(support.supports).toBe(false); + expect(support.version).toBe(version); }); }); it('should support', () => { - const supportedVersions = ['1.14.0', '1.14.9', '1.100', '2.0', '10.11.12']; - supportedVersions.forEach(async (version) => { - const dbVersionProvider = new DbVersionProvider(() => Promise.resolve(version)); + const supportedVersions = [ + { + in: '1.14.0', + exp: '1.14.0', + }, + { + in: '1.14.9', + exp: '1.14.9', + }, + { + in: '1.100', + exp: '1.100', + }, + { + in: '2.0', + exp: '2.0', + }, + { + in: '10.11.12', + exp: '10.11.12', + }, + { + in: '1.25.0-raft', + exp: '1.25.0', + }, + ]; + return supportedVersions.forEach(async (version) => { + const dbVersionProvider = new DbVersionProvider(() => Promise.resolve(version.in)); const dbVersionSupport = new DbVersionSupport(dbVersionProvider); - await dbVersionSupport - .supportsClassNameNamespacedEndpointsPromise() - .then((support) => { - expect(support.supports).toBe(true); - expect(support.version).toBe(version); - }) - .catch(() => { - throw new Error('version should always resolve successfully'); - }); + const support = await dbVersionSupport.supportsClassNameNamespacedEndpointsPromise(); + + expect(support.supports).toBe(true); + expect(support.version).toBe(version.exp); }); }); }); diff --git a/src/utils/testData.ts b/src/utils/testData.ts index f332c2a4..9e965e15 100644 --- a/src/utils/testData.ts +++ b/src/utils/testData.ts @@ -1,5 +1,5 @@ -import { WeaviateClient } from '..'; -import { WeaviateObject, Property } from '../openapi/types'; +import { Property, WeaviateObject } from '../openapi/types.js'; +import { WeaviateClient } from '../v2/index.js'; export const PIZZA_CLASS_NAME = 'Pizza'; export const SOUP_CLASS_NAME = 'Soup'; diff --git a/src/v2/index.ts b/src/v2/index.ts new file mode 100644 index 00000000..57c0e51b --- /dev/null +++ b/src/v2/index.ts @@ -0,0 +1,97 @@ +import backup, { Backup } from '../backup/index.js'; +import batch, { Batch } from '../batch/index.js'; +import c11y, { C11y } from '../c11y/index.js'; +import classifications, { Classifications } from '../classifications/index.js'; +import cluster, { Cluster } from '../cluster/index.js'; +import { + ApiKey, + AuthAccessTokenCredentials, + AuthClientCredentials, + AuthUserPasswordCredentials, + OidcAuthenticator, +} from '../connection/auth.js'; +import { ConnectionGQL, InternalConnectionParams as ConnectionParams } from '../connection/index.js'; +import data, { Data } from '../data/index.js'; +import graphql, { GraphQL } from '../graphql/index.js'; +import misc, { Misc } from '../misc/index.js'; +import MetaGetter from '../misc/metaGetter.js'; +import schema, { Schema } from '../schema/index.js'; +import { DbVersionProvider, DbVersionSupport } from '../utils/dbVersion.js'; + +export interface WeaviateClient { + graphql: GraphQL; + schema: Schema; + data: Data; + classifications: Classifications; + batch: Batch; + misc: Misc; + c11y: C11y; + backup: Backup; + cluster: Cluster; + oidcAuth?: OidcAuthenticator; +} + +const app = { + client: function (params: ConnectionParams): WeaviateClient { + // check if the URL is set + if (!params.host) throw new Error('Missing `host` parameter'); + + // check if headers are set + if (!params.headers) params.headers = {}; + + const conn = new ConnectionGQL(params); + const dbVersionProvider = initDbVersionProvider(conn); + const dbVersionSupport = new DbVersionSupport(dbVersionProvider); + + const ifc: WeaviateClient = { + graphql: graphql(conn), + schema: schema(conn), + data: data(conn, dbVersionSupport), + classifications: classifications(conn), + batch: batch(conn, dbVersionSupport), + misc: misc(conn, dbVersionProvider), + c11y: c11y(conn), + backup: backup(conn), + cluster: cluster(conn), + }; + + if (conn.oidcAuth) ifc.oidcAuth = conn.oidcAuth; + + return ifc; + }, + + ApiKey, + AuthUserPasswordCredentials, + AuthAccessTokenCredentials, + AuthClientCredentials, +}; + +function initDbVersionProvider(conn: ConnectionGQL) { + const metaGetter = new MetaGetter(conn); + const versionGetter = () => { + return metaGetter + .do() + .then((result: any) => result.version) + .catch(() => Promise.resolve('')); + }; + + const dbVersionProvider = new DbVersionProvider(versionGetter); + dbVersionProvider.refresh(); + + return dbVersionProvider; +} + +export default app; +export * from '../backup/index.js'; +export * from '../batch/index.js'; +export * from '../c11y/index.js'; +export * from '../classifications/index.js'; +export * from '../cluster/index.js'; +export * from '../connection/index.js'; +export * from '../data/index.js'; +export * from '../graphql/index.js'; +export * from '../misc/index.js'; +export * from '../openapi/types.js'; +export * from '../schema/index.js'; +export * from '../utils/base64.js'; +export * from '../utils/uuid.js'; diff --git a/src/validation/commandBase.ts b/src/validation/commandBase.ts index 22874092..c0abbf59 100644 --- a/src/validation/commandBase.ts +++ b/src/validation/commandBase.ts @@ -1,4 +1,4 @@ -import Connection from '../connection'; +import Connection from '../connection/index.js'; export interface ICommandBase { /** diff --git a/test/dbVersionProvider.ts b/test/dbVersionProvider.ts index 610336d4..54a18c25 100644 --- a/test/dbVersionProvider.ts +++ b/test/dbVersionProvider.ts @@ -1,4 +1,4 @@ -import { VersionProvider } from '../src/utils/dbVersion'; +import { DbVersion, VersionProvider } from '../src/utils/dbVersion.js'; export class TestDbVersionProvider implements VersionProvider { private version: string; @@ -7,7 +7,11 @@ export class TestDbVersionProvider implements VersionProvider { this.version = version; } - getVersionPromise(): Promise { + getVersionString(): Promise { return Promise.resolve(this.version); } + + getVersion(): Promise { + return Promise.resolve(DbVersion.fromString(this.version)); + } } diff --git a/test/images.journey.test.ts b/test/images.journey.test.ts new file mode 100644 index 00000000..5f47f0d0 --- /dev/null +++ b/test/images.journey.test.ts @@ -0,0 +1,56 @@ +import fs from 'fs'; +import weaviate, { toBase64FromMedia } from '../src/'; + +describe('Journey testing of the image functionality', () => { + const collectionName = 'ImageJourneyTesting'; + + beforeAll(async () => { + const client = await weaviate.connectToLocal(); + await client.collections.delete(collectionName); + }); + + it('should create a collection with an image property and capable vectorizer', async () => { + const client = await weaviate.connectToLocal(); + await client.collections.create({ + name: collectionName, + properties: [ + { + name: 'image', + dataType: 'blob', + }, + ], + vectorizers: weaviate.configure.vectorizer.img2VecNeural({ + imageFields: ['image'], + }), + }); + }); + + it('should insert an encoded image', async () => { + const client = await weaviate.connectToLocal(); + await client.collections.get(collectionName).data.insert({ + image: await toBase64FromMedia('./public/favicon.ico'), + }); + }); + + it('should retrieve the encoded image', async () => { + const client = await weaviate.connectToLocal(); + const res = await client.collections + .get(collectionName) + .query.fetchObjects({ returnProperties: ['image'] }); + expect(res.objects[0].properties.image).toBeDefined(); + }); + + it('should search on the encoded image vector with a file path string', async () => { + const client = await weaviate.connectToLocal(); + const res = await client.collections.get(collectionName).query.nearImage('./public/favicon.ico'); + expect(res.objects.length).toEqual(1); + }); + + it('should search on the encoded image vector with a buffer', async () => { + const client = await weaviate.connectToLocal(); + const res = await client.collections + .get(collectionName) + .query.nearImage(fs.readFileSync('./public/favicon.ico')); // eslint-disable-line no-sync + expect(res.objects.length).toEqual(1); + }); +}); diff --git a/test/server.ts b/test/server.ts index 2627acdf..8d1b5c8c 100644 --- a/test/server.ts +++ b/test/server.ts @@ -1,6 +1,6 @@ -import { Application } from '@curveball/core'; import bodyParser from '@curveball/bodyparser'; -import { Server, IncomingMessage, ServerResponse } from 'http'; +import { Application } from '@curveball/core'; +import { IncomingMessage, Server, ServerResponse } from 'http'; export interface IServerCache { server: Server; @@ -22,7 +22,7 @@ export function testServer() { let lastRequest: any = null; const app = new Application(); - app.use(bodyParser()); + app.use((bodyParser as any)()); app.use((ctx, next) => { lastRequest = ctx.request; return next(); diff --git a/tools/health.proto b/tools/health.proto new file mode 100644 index 00000000..78dde9d7 --- /dev/null +++ b/tools/health.proto @@ -0,0 +1,63 @@ +// Copyright 2015 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The canonical version of this proto can be found at +// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto + +syntax = "proto3"; + +package grpc.health.v1; + +option csharp_namespace = "Grpc.Health.V1"; +option go_package = "google.golang.org/grpc/health/grpc_health_v1"; +option java_multiple_files = true; +option java_outer_classname = "HealthProto"; +option java_package = "io.grpc.health.v1"; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + SERVICE_UNKNOWN = 3; // Used only by the Watch method. + } + ServingStatus status = 1; +} + +service Health { + // If the requested service is unknown, the call will fail with status + // NOT_FOUND. + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); + + // Performs a watch for the serving status of the requested service. + // The server will immediately send back a message indicating the current + // serving status. It will then subsequently send a new message whenever + // the service's serving status changes. + // + // If the requested service is unknown when the call is received, the + // server will send a message setting the serving status to + // SERVICE_UNKNOWN but will *not* terminate the call. If at some + // future point, the serving status of the service becomes known, the + // server will send a new message with the service's serving status. + // + // If the call terminates with status UNIMPLEMENTED, then clients + // should assume this method is not supported and should not retry the + // call. If the call terminates with any other status (including OK), + // clients should retry the call with appropriate exponential backoff. + rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); +} \ No newline at end of file diff --git a/tools/refresh_protos.sh b/tools/refresh_protos.sh new file mode 100755 index 00000000..b8857b1f --- /dev/null +++ b/tools/refresh_protos.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +echo "this script assumes that you have checked out weaviate next to the client" +cd "${0%/*}/.." + + +./node_modules/.bin/grpc_tools_node_protoc -I ../weaviate/grpc/proto \ + --ts_proto_out=./src/proto \ + --ts_proto_opt=forceLong==bigint \ + --ts_proto_opt=esModuleInterop=true \ + --ts_proto_opt=outputServices=nice-grpc,outputServices=generic-definitions,useExactTypes=false \ + ../weaviate/grpc/proto/v1/*.proto + +./node_modules/.bin/grpc_tools_node_protoc -I ./tools \ + --ts_proto_out=./src/proto/google/health/v1 \ + --ts_proto_opt=forceLong==bigint \ + --ts_proto_opt=esModuleInterop=true \ + --ts_proto_opt=outputServices=nice-grpc,outputServices=generic-definitions,useExactTypes=false \ + ./tools/health.proto + + +# sed -i '' 's/import * as _m0 from/import _m0 from/g' src/proto/v1/*.ts +# sed -i '' 's/import * as _m0 from/import _m0 from/g' src/proto/google/protobuf/struct.ts + +sed -i '' 's/\"protobufjs\/minimal\"/\"protobufjs\/minimal.js\"/g' src/proto/v1/*.ts +sed -i '' 's/\"protobufjs\/minimal\"/\"protobufjs\/minimal.js\"/g' src/proto/google/protobuf/struct.ts +sed -i '' 's/\"protobufjs\/minimal\"/\"protobufjs\/minimal.js\"/g' src/proto/google/health/v1/health.ts + +sed -i '' 's/google\/protobuf\/struct"/google\/protobuf\/struct.js"/g' src/proto/v1/*.ts + +sed -i '' 's/\".\/base\"/\".\/base.js\"/g' src/proto/v1/*.ts +sed -i '' 's/\".\/batch\"/\".\/batch.js\"/g' src/proto/v1/*.ts +sed -i '' 's/\".\/batch_delete\"/\".\/batch_delete.js\"/g' src/proto/v1/*.ts +sed -i '' 's/\".\/properties\"/\".\/properties.js\"/g' src/proto/v1/*.ts +sed -i '' 's/\".\/search_get\"/\".\/search_get.js\"/g' src/proto/v1/*.ts +sed -i '' 's/\".\/tenants\"/\".\/tenants.js\"/g' src/proto/v1/*.ts + +echo "done" diff --git a/tsconfig-test.json b/tsconfig-test.json index 1dc6c1c4..ae7b12c8 100644 --- a/tsconfig-test.json +++ b/tsconfig-test.json @@ -1,18 +1,16 @@ { "compilerOptions": { - "target": "es2020", + "target": "ES6", "strict": true, - "preserveConstEnums": true, - "noEmit": false, - "sourceMap": true, - "module":"es2020", - "moduleResolution":"node", - "esModuleInterop": true, - "skipLibCheck": true, + "noImplicitAny": true, + "noImplicitThis": true, + "strictFunctionTypes": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", "forceConsistentCasingInFileNames": true, - "isolatedModules": true, + "allowSyntheticDefaultImports": true, "outDir": "./dist" }, - "include": ["**/*"], + "include": ["./test/**/*.ts", "./src/**/*.ts"], "exclude": ["node_modules", "examples"] -} +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 28fea5bf..b31935ca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,21 @@ { "compilerOptions": { - "target": "es2020", + "target": "ES6", "strict": true, "preserveConstEnums": true, - "noEmit": false, "alwaysStrict": true, "noImplicitAny": true, "noImplicitThis": true, "strictFunctionTypes": true, - "allowJs": true, - "checkJs": false, - "sourceMap": false, - "module": "es2020", - "moduleResolution": "node", - "esModuleInterop": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "outDir": "./types", + "esModuleInterop": true, + "outDir": "./dist/node", "declaration": true, "rootDir": "./src", - "types": ["node"] + "types": ["node", "uuid", "jest"] }, "include": ["./src/**/*"], "exclude": ["**/*.test.ts"] diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 00000000..f66a2362 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,34 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig([ + { + entry: [ + 'src/v2/index.ts', + '!src/index.ts', + '!src/**/*.test.ts', + '!src/collections/**/*.ts', + '!src/connection/grpc.ts', + '!src/connection/helpers.ts', + '!src/proto/**/*.ts', + '!src/grpc', + ], + format: ['cjs', 'esm'], + outDir: 'dist/web', + clean: true, + platform: 'browser', + minify: true, + dts: true, + splitting: true, + treeshake: true, + }, + // { + // entry: { + // index: 'src/index.ts', + // }, + // format: ['cjs'], + // outDir: 'dist/node/cjs', + // dts: true, + // target: 'node16', + // platform: 'node', + // }, +]);