├── .changeset ├── README.md ├── angry-bags-fail.md ├── beige-eyes-lose.md ├── beige-groups-tell.md ├── chubby-jeans-cheat.md ├── clever-badgers-lose.md ├── cold-donkeys-wash.md ├── common-facts-kneel.md ├── common-squids-buy.md ├── config.json ├── cuddly-cougars-cough.md ├── cute-cloths-care.md ├── easy-lands-cut.md ├── evil-dogs-drive.md ├── fresh-bushes-know.md ├── fruity-chairs-brush.md ├── good-berries-share.md ├── khaki-sloths-sing.md ├── late-baths-push.md ├── lazy-dolls-dream.md ├── major-aliens-go.md ├── major-poets-work.md ├── moody-houses-divide.md ├── polite-eggs-fetch.md ├── pre.json ├── pretty-zebras-enter.md ├── quick-bananas-sip.md ├── shy-rocks-find.md ├── small-pigs-divide.md ├── sweet-carpets-talk.md ├── tall-colts-follow.md ├── tender-symbols-drive.md ├── tough-fans-watch.md ├── twenty-hands-write.md ├── violet-words-wave.md ├── wicked-carrots-own.md ├── yellow-banks-burn.md └── yellow-brooms-care.md ├── .devcontainer ├── Dockerfile ├── cluster │ ├── compose.yml │ ├── devcontainer.json │ └── ydb.yaml ├── commands │ ├── initialize.sh │ ├── postCreate.sh │ └── postStart.sh ├── compose.yml ├── devcontainer.json └── prometheus.yml ├── .github ├── actions │ └── setup │ │ └── action.yml └── workflows │ └── ci.yml ├── .gitignore ├── .oxlintrc.json ├── LICENSE ├── README.md ├── examples ├── api.js ├── package-lock.json ├── package.json ├── query.js ├── retry.js └── sls │ ├── index.js │ └── package.json ├── package-lock.json ├── package.json ├── packages ├── api │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── buf.gen.grpc.yaml │ ├── buf.gen.yaml │ ├── package.json │ ├── plugins │ │ └── protoc-gen-nice-grpc.ts │ ├── src │ │ ├── auth.ts │ │ ├── cms.ts │ │ ├── coordination.ts │ │ ├── discovery.ts │ │ ├── export.ts │ │ ├── federation_discovery.ts │ │ ├── gen │ │ │ ├── google │ │ │ │ └── protobuf │ │ │ │ │ ├── any_pb.ts │ │ │ │ │ ├── descriptor_pb.ts │ │ │ │ │ ├── duration_pb.ts │ │ │ │ │ ├── empty_pb.ts │ │ │ │ │ ├── struct_pb.ts │ │ │ │ │ └── timestamp_pb.ts │ │ │ ├── protos │ │ │ │ ├── annotations │ │ │ │ │ ├── sensitive_pb.ts │ │ │ │ │ └── validation_pb.ts │ │ │ │ ├── ydb_auth_pb.ts │ │ │ │ ├── ydb_cms_pb.ts │ │ │ │ ├── ydb_common_pb.ts │ │ │ │ ├── ydb_coordination_pb.ts │ │ │ │ ├── ydb_discovery_pb.ts │ │ │ │ ├── ydb_export_pb.ts │ │ │ │ ├── ydb_federation_discovery_pb.ts │ │ │ │ ├── ydb_formats_pb.ts │ │ │ │ ├── ydb_import_pb.ts │ │ │ │ ├── ydb_issue_message_pb.ts │ │ │ │ ├── ydb_monitoring_pb.ts │ │ │ │ ├── ydb_operation_pb.ts │ │ │ │ ├── ydb_query_pb.ts │ │ │ │ ├── ydb_query_stats_pb.ts │ │ │ │ ├── ydb_rate_limiter_pb.ts │ │ │ │ ├── ydb_scheme_pb.ts │ │ │ │ ├── ydb_scripting_pb.ts │ │ │ │ ├── ydb_status_codes_pb.ts │ │ │ │ ├── ydb_table_pb.ts │ │ │ │ ├── ydb_topic_pb.ts │ │ │ │ └── ydb_value_pb.ts │ │ │ ├── ydb_auth_v1_grpc_pb.ts │ │ │ ├── ydb_cms_v1_grpc_pb.ts │ │ │ ├── ydb_coordination_v1_grpc_pb.ts │ │ │ ├── ydb_discovery_v1_grpc_pb.ts │ │ │ ├── ydb_export_v1_grpc_pb.ts │ │ │ ├── ydb_federation_discovery_v1_grpc_pb.ts │ │ │ ├── ydb_import_v1_grpc_pb.ts │ │ │ ├── ydb_monitoring_v1_grpc_pb.ts │ │ │ ├── ydb_operation_v1_grpc_pb.ts │ │ │ ├── ydb_query_v1_grpc_pb.ts │ │ │ ├── ydb_rate_limiter_v1_grpc_pb.ts │ │ │ ├── ydb_scheme_v1_grpc_pb.ts │ │ │ ├── ydb_scripting_v1_grpc_pb.ts │ │ │ ├── ydb_table_v1_grpc_pb.ts │ │ │ └── ydb_topic_v1_grpc_pb.ts │ │ ├── import.ts │ │ ├── monitoring.ts │ │ ├── operation.ts │ │ ├── query.ts │ │ ├── rate_limiter.ts │ │ ├── scheme.ts │ │ ├── scripting.ts │ │ ├── table.ts │ │ ├── topic.ts │ │ └── value.ts │ └── tsconfig.json ├── auth │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── access-token.ts │ │ ├── accestoken.test.ts │ │ ├── anonymous.ts │ │ ├── dbg.ts │ │ ├── index.ts │ │ ├── metadata.test.ts │ │ ├── metadata.ts │ │ └── static.ts │ ├── tests │ │ └── static.e2e.test.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── core │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── conn.ts │ │ ├── dbg.ts │ │ ├── driver.test.ts │ │ ├── driver.ts │ │ ├── index.ts │ │ ├── middleware.ts │ │ └── pool.ts │ ├── tests │ │ └── driver.e2e.test.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── error │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.test.ts │ │ └── index.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── query │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── ctx.ts │ │ ├── index.ts │ │ ├── query.ts │ │ ├── yql.test.ts │ │ └── yql.ts │ ├── tests │ │ └── query.e2e.test.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── retry │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── budget.ts │ │ ├── config.ts │ │ ├── context.ts │ │ ├── index.test.ts │ │ ├── index.ts │ │ ├── strategy.test.ts │ │ └── strategy.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── topic │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── aee.test.ts │ │ ├── aee.ts │ │ ├── dbg.ts │ │ ├── index.ts │ │ ├── message.ts │ │ ├── partition-session.ts │ │ ├── queue.test.ts │ │ ├── queue.ts │ │ ├── reader.ts │ │ └── writer │ │ │ ├── _batch_messages.ts │ │ │ ├── _flush.ts │ │ │ ├── _gen_producer_id.ts │ │ │ ├── _init_reponse.ts │ │ │ ├── _init_request.ts │ │ │ ├── _update_token.ts │ │ │ ├── _write.ts │ │ │ ├── _write_request.ts │ │ │ ├── _write_response.ts │ │ │ ├── constants.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ ├── tsconfig.json │ └── vitest.config.ts └── value │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ ├── dict.ts │ ├── index.ts │ ├── list.ts │ ├── null.ts │ ├── optional.ts │ ├── primitive.ts │ ├── print.ts │ ├── struct.ts │ ├── tuple.ts │ ├── type.ts │ ├── uuid.ts │ └── value.ts │ ├── tests │ ├── list.test.ts │ ├── primitive.test.ts │ ├── print.test.ts │ ├── transform.test.ts │ └── uudi.test.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── tests ├── package-lock.json ├── package.json └── slo │ ├── slo.test.ts │ └── telemetry.ts ├── tsconfig.json ├── turbo.json ├── vitest.config.ts └── vitest.setup.e2e.ts /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/angry-bags-fail.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/retry': patch 3 | --- 4 | 5 | Return original error when budget exceeded 6 | -------------------------------------------------------------------------------- /.changeset/beige-eyes-lose.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/query': patch 3 | --- 4 | 5 | Encode falsy values in yql template string 6 | -------------------------------------------------------------------------------- /.changeset/beige-groups-tell.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/query': patch 3 | '@ydbjs/value': patch 4 | --- 5 | 6 | Fix cjs bundle issues 7 | -------------------------------------------------------------------------------- /.changeset/chubby-jeans-cheat.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/query': patch 3 | --- 4 | 5 | Fix issues when aborting session attach stream 6 | -------------------------------------------------------------------------------- /.changeset/clever-badgers-lose.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/error': patch 3 | '@ydbjs/retry': patch 4 | '@ydbjs/topic': patch 5 | '@ydbjs/auth': patch 6 | '@ydbjs/core': patch 7 | '@ydbjs/api': patch 8 | --- 9 | 10 | Fix build cjs issues 11 | -------------------------------------------------------------------------------- /.changeset/cold-donkeys-wash.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/auth': patch 3 | --- 4 | 5 | Update metadata token refresh logic 6 | -------------------------------------------------------------------------------- /.changeset/common-facts-kneel.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/query': patch 3 | --- 4 | 5 | Add isolation option for single query execution 6 | -------------------------------------------------------------------------------- /.changeset/common-squids-buy.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | Add await for commit 6 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.changeset/cuddly-cougars-cough.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/auth': patch 3 | --- 4 | 5 | Enhanced authentication mechanisms and usage examples. 6 | Improved documentation and structure. 7 | -------------------------------------------------------------------------------- /.changeset/cute-cloths-care.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/core': patch 3 | --- 4 | 5 | Fix build issues 6 | -------------------------------------------------------------------------------- /.changeset/easy-lands-cut.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/query': patch 3 | --- 4 | 5 | Improved resource management and cleanup methods. 6 | Enhanced event emission and transaction handling. 7 | Updated documentation with advanced usage and error handling examples. 8 | Added support for more transaction isolation levels and statistics configuration. 9 | -------------------------------------------------------------------------------- /.changeset/evil-dogs-drive.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/core': patch 3 | --- 4 | 5 | correct typo in credentialsProvider assignment in Driver class 6 | -------------------------------------------------------------------------------- /.changeset/fresh-bushes-know.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/core': patch 3 | --- 4 | 5 | Refactored async functions and concurrency handling for better performance. 6 | Expanded configuration options and documentation. 7 | Updated README for clarity and structure. 8 | -------------------------------------------------------------------------------- /.changeset/fruity-chairs-brush.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | Migrate to PQueue for storing end sending message from client 6 | -------------------------------------------------------------------------------- /.changeset/good-berries-share.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | Publish topic reader 6 | -------------------------------------------------------------------------------- /.changeset/khaki-sloths-sing.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | Resend commit offsets after retry 6 | -------------------------------------------------------------------------------- /.changeset/late-baths-push.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | Add TopicWriter 6 | -------------------------------------------------------------------------------- /.changeset/lazy-dolls-dream.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/value': patch 3 | --- 4 | 5 | Enhance handling UUID values from ydb proto repr 6 | -------------------------------------------------------------------------------- /.changeset/major-aliens-go.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/auth': patch 3 | '@ydbjs/core': patch 4 | --- 5 | 6 | Fix metadata credentials provider 7 | -------------------------------------------------------------------------------- /.changeset/major-poets-work.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/core': patch 3 | --- 4 | 5 | Fix driver options parsing 6 | -------------------------------------------------------------------------------- /.changeset/moody-houses-divide.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/api': patch 3 | '@ydbjs/auth': patch 4 | '@ydbjs/core': patch 5 | '@ydbjs/error': patch 6 | '@ydbjs/query': patch 7 | '@ydbjs/retry': patch 8 | '@ydbjs/value': patch 9 | --- 10 | 11 | Update usage examples 12 | -------------------------------------------------------------------------------- /.changeset/polite-eggs-fetch.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/value': patch 3 | --- 4 | 5 | Updated documentation with more conversion stage details and examples. 6 | -------------------------------------------------------------------------------- /.changeset/pre.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "pre", 3 | "tag": "alpha", 4 | "initialVersions": { 5 | "@ydbjs/api": "6.0.0-alpha.2", 6 | "@ydbjs/auth": "6.0.0-alpha.2", 7 | "@ydbjs/core": "6.0.0-alpha.2", 8 | "@ydbjs/error": "6.0.0-alpha.2", 9 | "@ydbjs/query": "6.0.0-alpha.2", 10 | "@ydbjs/retry": "6.0.0-alpha.2", 11 | "@ydbjs/topic": "6.0.0-alpha.2", 12 | "@ydbjs/value": "6.0.0-alpha.2" 13 | }, 14 | "changesets": [ 15 | "angry-bags-fail", 16 | "beige-eyes-lose", 17 | "beige-groups-tell", 18 | "chubby-jeans-cheat", 19 | "clever-badgers-lose", 20 | "cold-donkeys-wash", 21 | "common-facts-kneel", 22 | "common-squids-buy", 23 | "cuddly-cougars-cough", 24 | "cute-cloths-care", 25 | "easy-lands-cut", 26 | "evil-dogs-drive", 27 | "fresh-bushes-know", 28 | "fruity-chairs-brush", 29 | "good-berries-share", 30 | "khaki-sloths-sing", 31 | "late-baths-push", 32 | "lazy-dolls-dream", 33 | "major-aliens-go", 34 | "major-poets-work", 35 | "moody-houses-divide", 36 | "polite-eggs-fetch", 37 | "pretty-zebras-enter", 38 | "quick-bananas-sip", 39 | "shy-rocks-find", 40 | "small-pigs-divide", 41 | "sweet-carpets-talk", 42 | "tall-colts-follow", 43 | "tender-symbols-drive", 44 | "tough-fans-watch", 45 | "twenty-hands-write", 46 | "violet-words-wave", 47 | "wicked-carrots-own", 48 | "yellow-banks-burn", 49 | "yellow-brooms-care" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.changeset/pretty-zebras-enter.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/error': patch 3 | '@ydbjs/query': patch 4 | '@ydbjs/retry': patch 5 | '@ydbjs/value': patch 6 | '@ydbjs/auth': patch 7 | '@ydbjs/core': patch 8 | '@ydbjs/api': patch 9 | --- 10 | 11 | Update packages description 12 | -------------------------------------------------------------------------------- /.changeset/quick-bananas-sip.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/value': patch 3 | --- 4 | 5 | Export primitive types 6 | -------------------------------------------------------------------------------- /.changeset/shy-rocks-find.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | Prevent memory leacks 6 | -------------------------------------------------------------------------------- /.changeset/small-pigs-divide.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/api': patch 3 | '@ydbjs/auth': patch 4 | '@ydbjs/core': patch 5 | '@ydbjs/error': patch 6 | '@ydbjs/query': patch 7 | '@ydbjs/retry': patch 8 | '@ydbjs/value': patch 9 | --- 10 | 11 | Consolidated and clarified README files across all packages. 12 | -------------------------------------------------------------------------------- /.changeset/sweet-carpets-talk.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/retry': patch 3 | --- 4 | 5 | - Refined retry logic: now throws the original AbortError, supports AbortSignal, and adds onRetry hook. 6 | - Expanded configuration options and documentation. 7 | - Improved test coverage for retry scenarios. 8 | -------------------------------------------------------------------------------- /.changeset/tall-colts-follow.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/api': patch 3 | '@ydbjs/auth': patch 4 | '@ydbjs/core': patch 5 | '@ydbjs/error': patch 6 | '@ydbjs/query': patch 7 | '@ydbjs/retry': patch 8 | '@ydbjs/value': patch 9 | --- 10 | 11 | Add transaction support 12 | -------------------------------------------------------------------------------- /.changeset/tender-symbols-drive.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/retry': patch 3 | --- 4 | 5 | Ensure function execution in retry is awaited for proper error handling 6 | -------------------------------------------------------------------------------- /.changeset/tough-fans-watch.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | Refactor writer: split single class into small parts for better testing 6 | -------------------------------------------------------------------------------- /.changeset/twenty-hands-write.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | Update cleanup logic on retries 6 | -------------------------------------------------------------------------------- /.changeset/violet-words-wave.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/topic': patch 3 | --- 4 | 5 | enhance TopicWriter and TopicReader with compression options 6 | -------------------------------------------------------------------------------- /.changeset/wicked-carrots-own.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/core': patch 3 | --- 4 | 5 | Support for database in querystring 6 | -------------------------------------------------------------------------------- /.changeset/yellow-banks-burn.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/api': patch 3 | '@ydbjs/auth': patch 4 | '@ydbjs/core': patch 5 | '@ydbjs/error': patch 6 | '@ydbjs/query': patch 7 | '@ydbjs/retry': patch 8 | '@ydbjs/value': patch 9 | --- 10 | 11 | Added support to ignore the `.turbo` folder for better compatibility and cleaner workflows. 12 | -------------------------------------------------------------------------------- /.changeset/yellow-brooms-care.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@ydbjs/error': patch 3 | --- 4 | 5 | Print ydb error issues 6 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm 2 | 3 | # [Optional] Uncomment if you want to install more tools 4 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 5 | # && apt-get -y install --no-install-recommends 6 | 7 | RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install --lts=hydrogen" 8 | RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install --lts=iron" 9 | RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install --lts=jod" 10 | 11 | # [Optional] Uncomment if you want to install more global node modules 12 | ARG NODE_MODULES="turbo prettier typescript @changesets/cli @arethetypeswrong/cli" 13 | RUN su node -c "npm install -g ${NODE_MODULES}" \ 14 | && npm cache clean --force > /dev/null 2>&1 15 | 16 | # [Optional] Uncomment if you want to install bun 17 | RUN curl -fsSL https://bun.sh/install | bash 18 | 19 | # [Optional] Uncomment if you want to install yc cli 20 | RUN curl -fsSL https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash 21 | 22 | # [Optional] Uncomment if you want to install ydb cli 23 | RUN curl -fsSL https://raw.githubusercontent.com/ydb-platform/ydb/refs/heads/main/ydb/apps/ydb/install/install.sh | bash 24 | -------------------------------------------------------------------------------- /.devcontainer/cluster/compose.yml: -------------------------------------------------------------------------------- 1 | volumes: 2 | ydb-certs: 3 | 4 | x-ydb-node: &ydb-node 5 | image: cr.yandex/crptqonuodf51kdj7a7d/ydb:24.4.4 6 | restart: always 7 | platform: linux/amd64 8 | privileged: true 9 | volumes: 10 | - ./ydb.yaml:/opt/ydb/cfg/config.yaml 11 | 12 | services: 13 | sdk: 14 | build: 15 | context: .. 16 | dockerfile: Dockerfile 17 | hostname: sdk 18 | 19 | volumes: 20 | - ydb-certs:/ydb_certs 21 | - ../../:/workspaces/ydb-js-sdk:cached 22 | 23 | environment: 24 | - YDB_VERSION=24.4.4 25 | - YDB_STATIC_CREDENTIALS_USER=root 26 | - YDB_STATIC_CREDENTIALS_PASSWORD= 27 | - YDB_STATIC_CREDENTIALS_ENDPOINT=grpc://ydb:2136 28 | - YDB_CONNECTION_STRING=grpc://ydb:2136/Root/testdb 29 | 30 | # Overrides default command so things don't shut down after the process ends. 31 | command: sleep infinity 32 | 33 | ydb-storage: 34 | <<: *ydb-node 35 | hostname: ydb 36 | command: 37 | - /opt/ydb/bin/ydbd 38 | - server 39 | - --grpc-port 40 | - '2136' 41 | - --mon-port 42 | - '8765' 43 | - --ic-port 44 | - '19001' 45 | - --yaml-config 46 | - /opt/ydb/cfg/config.yaml 47 | - --node 48 | - static 49 | - --label 50 | - deployment=docker 51 | 52 | ports: 53 | - 2136 54 | - 8765 55 | 56 | volumes: 57 | - ydb-certs:/ydb_certs 58 | - ./ydb.yaml:/opt/ydb/cfg/config.yaml 59 | 60 | healthcheck: 61 | test: bash -c "exec 6<> /dev/tcp/ydb/2136" 62 | interval: 10s 63 | timeout: 1s 64 | retries: 3 65 | start_period: 30s 66 | 67 | deploy: 68 | replicas: 1 69 | 70 | ydb-storage-init: 71 | <<: *ydb-node 72 | restart: no 73 | command: 74 | - /bin/sh 75 | - -c 76 | - "/opt/ydb/bin/ydbd -s 'grpc://ydb:2136' admin blobstorage config init --yaml-file /opt/ydb/cfg/config.yaml || exit 0" 77 | depends_on: 78 | ydb-storage: 79 | condition: service_healthy 80 | 81 | ydb-database-init: 82 | <<: *ydb-node 83 | restart: no 84 | command: 85 | - /bin/sh 86 | - -c 87 | - "/opt/ydb/bin/ydbd -s 'grpc://ydb:2136' admin database '/Root/testdb' create ssd:1 || exit 0" 88 | depends_on: 89 | ydb-storage: 90 | condition: service_healthy 91 | ydb-storage-init: 92 | condition: service_completed_successfully 93 | 94 | ydb-database: 95 | <<: *ydb-node 96 | command: 97 | - /opt/ydb/bin/ydbd 98 | - server 99 | - --grpc-port 100 | - '2136' 101 | - --mon-port 102 | - '8765' 103 | - --ic-port 104 | - '19001' 105 | - --yaml-config 106 | - /opt/ydb/cfg/config.yaml 107 | - --tenant 108 | - '/Root/testdb' 109 | - --node-broker 110 | - 'grpc://ydb:2136' 111 | - --label 112 | - deployment=docker 113 | depends_on: 114 | ydb-storage: 115 | condition: service_healthy 116 | ydb-storage-init: 117 | condition: service_completed_successfully 118 | ydb-database-init: 119 | condition: service_completed_successfully 120 | deploy: 121 | replicas: 3 122 | 123 | prometheus: 124 | image: prom/prometheus:v3.3.0 125 | restart: unless-stopped 126 | hostname: prometheus 127 | platform: linux/amd64 128 | 129 | ports: 130 | - 9090 131 | 132 | volumes: 133 | - ../prometheus.yml:/etc/prometheus/prometheus.yml 134 | - ydb-certs:/ydb_certs 135 | -------------------------------------------------------------------------------- /.devcontainer/cluster/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node 3 | { 4 | "name": "JavaScript & YDB Cluster", 5 | "service": "sdk", 6 | "dockerComposeFile": "compose.yml", 7 | "workspaceFolder": "/workspaces/ydb-js-sdk", 8 | // Allows the container to use ptrace, which is useful for debugging. 9 | "capAdd": [ 10 | "SYS_PTRACE" 11 | ], 12 | // Disables seccomp, which can be necessary for some debugging tools to function correctly. 13 | "securityOpt": [ 14 | "seccomp=unconfined" 15 | ], 16 | // Features to add to the dev container. More info: https://containers.dev/features. 17 | "features": { 18 | "ghcr.io/devcontainers/features/git": {}, 19 | "ghcr.io/devcontainers/features/common-utils": {}, 20 | "ghcr.io/devcontainers/features/github-cli:1": {}, 21 | "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {} 22 | }, 23 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 24 | "forwardPorts": [ 25 | "ydb-storage:2136", 26 | "ydb-storage:8765", 27 | "prometheus:9090" 28 | ], 29 | "otherPortsAttributes": { 30 | "onAutoForward": "ignore" 31 | }, 32 | // Use 'initializeCommand' to run commands before the container is created. 33 | "initializeCommand": "chmod +x .devcontainer/commands/initialize.sh && .devcontainer/commands/initialize.sh", 34 | // Use 'postCreateCommand' to run commands after the container is created. 35 | "postCreateCommand": "chmod +x .devcontainer/commands/postCreate.sh && .devcontainer/commands/postCreate.sh", 36 | // Use 'postStartCommand' to run commands after the container is started. 37 | "postStartCommand": "chmod +x .devcontainer/commands/postStart.sh && .devcontainer/commands/postStart.sh", 38 | // Configure tool-specific properties. 39 | "customizations": { 40 | "vscode": { 41 | "extensions": [ 42 | "esbenp.prettier-vscode", 43 | "mikestead.dotenv", 44 | "oven.bun-vscode", 45 | "oxc.oxc-vscode", 46 | "vitest.explorer" 47 | ] 48 | } 49 | }, 50 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 51 | "remoteUser": "root", 52 | "mounts": [ 53 | "source=${localEnv:HOME}/.ssh/id_ed25519_signing,target=/root/.ssh/id_ed25519_signing,type=bind,consistency=cached,source-exists=true" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /.devcontainer/cluster/ydb.yaml: -------------------------------------------------------------------------------- 1 | actor_system_config: 2 | cpu_count: 1 3 | node_type: STORAGE 4 | use_auto_config: true 5 | blob_storage_config: 6 | service_set: 7 | groups: 8 | - erasure_species: none 9 | rings: 10 | - fail_domains: 11 | - vdisk_locations: 12 | - node_id: 1 13 | path: SectorMap:1:64 14 | pdisk_category: SSD 15 | channel_profile_config: 16 | profile: 17 | - channel: 18 | - erasure_species: none 19 | pdisk_category: 0 20 | storage_pool_kind: ssd 21 | - erasure_species: none 22 | pdisk_category: 0 23 | storage_pool_kind: ssd 24 | - erasure_species: none 25 | pdisk_category: 0 26 | storage_pool_kind: ssd 27 | profile_id: 0 28 | column_shard_config: 29 | disabled_on_scheme_shard: False 30 | domains_config: 31 | domain: 32 | - name: Root 33 | storage_pool_types: 34 | - kind: ssd 35 | pool_config: 36 | box_id: 1 37 | erasure_species: none 38 | kind: ssd 39 | pdisk_filter: 40 | - property: 41 | - type: SSD 42 | vdisk_kind: Default 43 | state_storage: 44 | - ring: 45 | node: [1] 46 | nto_select: 1 47 | ssid: 1 48 | host_configs: 49 | - drive: 50 | - path: SectorMap:1:64 51 | type: SSD 52 | host_config_id: 1 53 | hosts: 54 | - host: ydb 55 | host_config_id: 1 56 | node_id: 1 57 | port: 19001 58 | walle_location: 59 | body: 1 60 | data_center: az-1 61 | rack: '0' 62 | static_erasure: none 63 | 64 | feature_flags: 65 | enable_external_data_sources: true 66 | enable_script_execution_operations: true 67 | -------------------------------------------------------------------------------- /.devcontainer/commands/initialize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | git config --local format.signoff true 5 | git config --local user.name "$(git config user.name)" 6 | git config --local user.email "$(git config user.email)" 7 | -------------------------------------------------------------------------------- /.devcontainer/commands/postCreate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source /usr/local/share/nvm/nvm.sh && nvm use --lts 5 | 6 | if git config --get commit.gpgsign | grep -q true; then 7 | git config --global gpg.format ssh 8 | git config --global gpg.ssh.defaultKeyCommand 'ssh-add -L' 9 | git config --global gpg.ssh.allowedSigners '~/.ssh/allowed_signers' 10 | fi 11 | 12 | # Set up YDB profile if ydb cli exists 13 | if which ydb > /dev/null 2>&1; then 14 | ENDPOINT=$(echo ${YDB_CONNECTION_STRING_SECURE:-$YDB_CONNECTION_STRING} | awk -F/ '{print $1 "//" $3}') 15 | DATABASE=$(echo ${YDB_CONNECTION_STRING_SECURE:-$YDB_CONNECTION_STRING} | cut -d/ -f4-) 16 | CA_FILE_OPTION="" 17 | 18 | if [ -n "$YDB_SSL_ROOT_CERTIFICATES_FILE" ]; then 19 | ENDPOINT="${ENDPOINT/grpc:/grpcs:}" 20 | CA_FILE_OPTION="--ca-file ${YDB_SSL_ROOT_CERTIFICATES_FILE}" 21 | fi 22 | 23 | ydb config profile replace local \ 24 | --endpoint "$ENDPOINT" \ 25 | --database "/$DATABASE" \ 26 | $CA_FILE_OPTION 27 | 28 | ydb config profile activate local 29 | fi 30 | -------------------------------------------------------------------------------- /.devcontainer/commands/postStart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | 5 | # Install dependencies 6 | npm install 7 | -------------------------------------------------------------------------------- /.devcontainer/compose.yml: -------------------------------------------------------------------------------- 1 | volumes: 2 | ydb-certs: 3 | 4 | networks: 5 | default: 6 | driver: bridge 7 | enable_ipv6: true 8 | 9 | services: 10 | sdk: 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | hostname: sdk 15 | 16 | volumes: 17 | - ydb-certs:/ydb_certs 18 | - ../:/workspaces/ydb-js-sdk:cached 19 | 20 | environment: 21 | - YDB_VERSION=trunk 22 | - YDB_STATIC_CREDENTIALS_USER=root 23 | - YDB_STATIC_CREDENTIALS_PASSWORD=1234 24 | - YDB_STATIC_CREDENTIALS_ENDPOINT=grpc://ydb:2136 25 | - YDB_CONNECTION_STRING=grpc://ydb:2136/local 26 | - YDB_CONNECTION_STRING_SECURE=grpcs://ydb:2135/local 27 | - YDB_SSL_ROOT_CERTIFICATES_FILE=/ydb_certs/ca.pem 28 | 29 | # Overrides default command so things don't shut down after the process ends. 30 | command: sleep infinity 31 | 32 | ydb: 33 | image: ydbplatform/local-ydb:trunk@sha256:74a086d193a117e911115a830e3f1f01816b9d85df1305b0bc923de14b1bcac5 34 | restart: unless-stopped 35 | hostname: ydb 36 | platform: linux/amd64 37 | 38 | ports: 39 | - 2135 40 | - 2136 41 | - 8765 42 | 43 | volumes: 44 | - ydb-certs:/ydb_certs 45 | 46 | environment: 47 | - YDB_USE_IN_MEMORY_PDISKS=true 48 | - YDB_ENABLE_COLUMN_TABLES=true 49 | - GRPC_TLS_PORT=2135 50 | - GRPC_PORT=2136 51 | - MON_PORT=8765 52 | 53 | prometheus: 54 | image: prom/prometheus:v3.3.0 55 | restart: unless-stopped 56 | hostname: prometheus 57 | platform: linux/amd64 58 | 59 | ports: 60 | - 9090 61 | 62 | volumes: 63 | - ydb-certs:/ydb_certs 64 | - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro 65 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node 3 | { 4 | "name": "JavaScript & YDB", 5 | "service": "sdk", 6 | "dockerComposeFile": "compose.yml", 7 | "workspaceFolder": "/workspaces/ydb-js-sdk", 8 | // Allows the container to use ptrace, which is useful for debugging. 9 | "capAdd": [ 10 | "SYS_PTRACE" 11 | ], 12 | // Disables seccomp, which can be necessary for some debugging tools to function correctly. 13 | "securityOpt": [ 14 | "seccomp=unconfined" 15 | ], 16 | // Features to add to the dev container. More info: https://containers.dev/features. 17 | "features": { 18 | "ghcr.io/devcontainers/features/git": {}, 19 | "ghcr.io/devcontainers/features/common-utils": {}, 20 | "ghcr.io/devcontainers/features/github-cli:1": {}, 21 | "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {} 22 | }, 23 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 24 | "forwardPorts": [ 25 | "ydb:2135", 26 | "ydb:2136", 27 | "ydb:8765", 28 | "prometheus:9090" 29 | ], 30 | "otherPortsAttributes": { 31 | "onAutoForward": "ignore" 32 | }, 33 | // Use 'initializeCommand' to run commands before the container is created. 34 | "initializeCommand": "chmod +x .devcontainer/commands/initialize.sh && .devcontainer/commands/initialize.sh", 35 | // Use 'postCreateCommand' to run commands after the container is created. 36 | "postCreateCommand": "chmod +x .devcontainer/commands/postCreate.sh && .devcontainer/commands/postCreate.sh", 37 | // Use 'postStartCommand' to run commands after the container is started. 38 | "postStartCommand": "chmod +x .devcontainer/commands/postStart.sh && .devcontainer/commands/postStart.sh", 39 | // Configure tool-specific properties. 40 | "customizations": { 41 | "vscode": { 42 | "extensions": [ 43 | "esbenp.prettier-vscode", 44 | "mikestead.dotenv", 45 | "oven.bun-vscode", 46 | "oxc.oxc-vscode", 47 | "PeterSchmalfeldt.explorer-exclude", 48 | "vitest.explorer", 49 | ] 50 | } 51 | }, 52 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 53 | "remoteUser": "root", 54 | "mounts": [ 55 | "source=${localEnv:HOME}/.ssh/id_ed25519_signing,target=/root/.ssh/id_ed25519_signing,type=bind,readonly" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /.devcontainer/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | evaluation_interval: 15s 4 | 5 | # Alertmanager configuration 6 | alerting: 7 | alertmanagers: 8 | - static_configs: 9 | - targets: ['localhost:9093'] 10 | 11 | scrape_configs: 12 | - job_name: ydb-js-sdk 13 | scrape_interval: 1s 14 | scrape_timeout: 1s 15 | static_configs: 16 | - targets: ['sdk:9464'] 17 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup CI 2 | description: | 3 | This action sets up Node.js and installs dependencies for the project. 4 | It also configures caching for TurboRepo to speed up the build process. 5 | 6 | inputs: 7 | node-version: 8 | description: 'Node.js version' 9 | required: true 10 | default: '22' 11 | 12 | runs: 13 | using: composite 14 | steps: 15 | - name: Setup Node.js 16 | uses: actions/setup-node@v4 17 | with: 18 | cache: 'npm' 19 | node-version: ${{ inputs.node-version }} 20 | 21 | - name: Setup turbo 22 | uses: actions/cache@v4 23 | with: 24 | path: .turbo 25 | key: ${{ inputs.node-version }}-turbo-${{ github.sha }} 26 | restore-keys: | 27 | ${{ inputs.node-version }}-turbo- 28 | 29 | - name: Install global dependencies 30 | shell: bash 31 | run: npm i -g turbo prettier typescript @changesets/cli @arethetypeswrong/cli 32 | 33 | - name: Install dependencies 34 | shell: bash 35 | run: npm ci 36 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | name: Test 15 | runs-on: ubuntu-latest 16 | 17 | concurrency: 18 | group: ${{ github.event_name }}-${{ github.ref }}-${{ matrix.node-version }} 19 | cancel-in-progress: true 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | node-version: [18, 20, 22] 25 | 26 | services: 27 | ydb: 28 | image: ghcr.io/ydb-platform/local-ydb:24.3 29 | ports: 30 | - 2135:2135 31 | - 2136:2136 32 | - 8765:8765 33 | volumes: 34 | - /tmp/ydb_certs:/ydb_certs 35 | env: 36 | YDB_ENABLE_COLUMN_TABLES: true 37 | YDB_USE_IN_MEMORY_PDISKS: true 38 | YDB_LOCAL_SURVIVE_RESTART: true 39 | YDB_TABLE_ENABLE_PREPARED_DDL: true 40 | YDB_FEATURE_FLAGS: enable_topic_service_tx 41 | options: '-h localhost' 42 | 43 | env: 44 | YDB_STATIC_CREDENTIALS_USER: root 45 | YDB_STATIC_CREDENTIALS_PASSWORD: 1234 46 | YDB_STATIC_CREDENTIALS_ENDPOINT: grpc://localhost:2136 47 | YDB_CONNECTION_STRING: grpc://localhost:2136/local 48 | YDB_CONNECTION_STRING_SECURE: grpcs://localhost:2135/local 49 | YDB_SSL_ROOT_CERTIFICATES_FILE: /ydb_certs/ca.pem 50 | 51 | steps: 52 | # Checkout the repository 53 | - uses: actions/checkout@v4 54 | - uses: ./.github/actions/setup 55 | with: 56 | node-version: ${{ matrix.node-version }} 57 | 58 | - run: npm run build 59 | - run: npm run test -- --no-color 60 | - run: npm run test:e2e -- --no-color 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Local env files 5 | .env 6 | .env.local 7 | .env.development.local 8 | .env.test.local 9 | .env.production.local 10 | 11 | # Testing 12 | coverage 13 | 14 | # Turbo 15 | .turbo 16 | -------------------------------------------------------------------------------- /.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "sort-imports": [ 4 | "warn", 5 | { 6 | "allowSeparatedGroups": true, 7 | "ignoreDeclarationSort": true, 8 | "ignoreMemberSort": false, 9 | "ignoreCase": false 10 | } 11 | ], 12 | "unicorn/require-post-message-target-origin": "off" 13 | }, 14 | // Enable all rules in the `correctness` and `suspicious` categories 15 | "ignorePatterns": [ 16 | "node_modules", 17 | "**/node_modules/**", 18 | "**/dist/**", 19 | "**/*.test.ts" 20 | ], 21 | "categories": { 22 | "correctness": "error", 23 | "suspicious": "warn", 24 | "perf": "warn" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YDB JavaScript SDK 2 | 3 | A modular, modern SDK for working with YDB in JavaScript/TypeScript. Supports queries, transactions, types, error handling, authentication, and more. 4 | 5 | ## Other versions 6 | 7 | - [v5](https://github.com/ydb-platform/ydb-js-sdk/tree/v5) 8 | - [v4](https://github.com/ydb-platform/ydb-js-sdk/tree/v4.7.0) 9 | 10 | --- 11 | 12 | ## Packages 13 | 14 | - [`@ydbjs/core`](./packages/core): Core connection and utilities 15 | - [`@ydbjs/query`](./packages/query): YQL queries, transactions, parameters 16 | - [`@ydbjs/value`](./packages/value): YDB types and values 17 | - [`@ydbjs/api`](./packages/api): gRPC/Protobuf service definitions 18 | - [`@ydbjs/error`](./packages/error): YDB error handling 19 | - [`@ydbjs/auth`](./packages/auth): Authentication (tokens, anonymous, metadata) 20 | - [`@ydbjs/retry`](./packages/retry): Flexible retry policies 21 | 22 | --- 23 | 24 | ## Quick Start 25 | 26 | ### 1. Run YDB Locally 27 | 28 | Start a local YDB instance with Docker: https://ydb.tech/docs/en/quickstart 29 | 30 | ### 2. Install Required Packages 31 | 32 | ```sh 33 | npm install @ydbjs/core @ydbjs/query 34 | ``` 35 | 36 | ### 3. Connect and Query 37 | 38 | ```ts 39 | import { Driver } from '@ydbjs/core' 40 | import { query } from '@ydbjs/query' 41 | 42 | const driver = new Driver('grpc://localhost:2136/local') 43 | await driver.ready() 44 | 45 | const sql = query(driver) 46 | const resultSets = await sql`SELECT 1 + 1 AS sum` 47 | console.log(resultSets) // [ [ { sum: 2 } ] ] 48 | ``` 49 | 50 | --- 51 | 52 | ## Installation 53 | 54 | ```sh 55 | npm install @ydbjs/core @ydbjs/query @ydbjs/value @ydbjs/api @ydbjs/error 56 | ``` 57 | 58 | --- 59 | 60 | ## Documentation 61 | 62 | - [@ydbjs/core](./packages/core/README.md) 63 | - [@ydbjs/query](./packages/query/README.md) 64 | - [@ydbjs/value](./packages/value/README.md) 65 | - [@ydbjs/api](./packages/api/README.md) 66 | - [@ydbjs/error](./packages/error/README.md) 67 | - [@ydbjs/auth](./packages/auth/README.md) 68 | - [@ydbjs/retry](./packages/retry/README.md) 69 | 70 | --- 71 | 72 | ## Examples 73 | 74 | **Parameterized Query:** 75 | 76 | ```ts 77 | import { Int64, Optional, PrimitiveType } from '@ydbjs/value' 78 | const sql = query(driver) 79 | await sql`SELECT ${new Optional(new Int64(100n), new PrimitiveType('INT64'))};` 80 | ``` 81 | 82 | **Transactions:** 83 | 84 | ```ts 85 | await sql.begin(async (tx, signal) => { 86 | await tx`INSERT INTO users (id, name) VALUES (1, 'Alice')` 87 | await tx`UPDATE users SET name = 'Bob' WHERE id = 1` 88 | }) 89 | ``` 90 | 91 | **Error Handling:** 92 | 93 | ```ts 94 | import { YdbError } from '@ydbjs/error' 95 | try { 96 | await sql`SELECT * FROM non_existent_table` 97 | } catch (e) { 98 | if (e instanceof YdbError) { 99 | console.error('YDB Error:', e.message) 100 | } 101 | } 102 | ``` 103 | 104 | --- 105 | 106 | ## FAQ 107 | 108 | - **Add a new service?** Use `@ydbjs/api` for gRPC definitions. 109 | - **Work with YDB types?** Use `@ydbjs/value`. 110 | - **Implement retries?** Use `@ydbjs/retry`. 111 | - **More examples?** See package docs and [GitHub Examples](https://github.com/ydb-platform/ydb-js-sdk/tree/main/examples). 112 | 113 | --- 114 | 115 | ## Developer Guide 116 | 117 | - Build all packages: `npm run build` 118 | - Run all tests: `npm test` 119 | -------------------------------------------------------------------------------- /examples/api.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import * as assert from 'node:assert' 3 | 4 | import { anyUnpack } from '@bufbuild/protobuf/wkt' 5 | import { DiscoveryServiceDefinition, ListEndpointsResultSchema, WhoAmIResultSchema } from '@ydbjs/api/discovery' 6 | import { StaticCredentialsProvider } from '@ydbjs/auth/static' 7 | import { Driver } from '@ydbjs/core' 8 | 9 | let connectionString = process.env.YDB_CONNECTION_STRING || 'grpc://localhost:2136/local' 10 | let driver = new Driver(connectionString, { 11 | credentialsProvier: new StaticCredentialsProvider({ username: 'root', password: '1234' }, connectionString), 12 | }) 13 | 14 | await driver.ready() 15 | let discovery = driver.createClient(DiscoveryServiceDefinition) 16 | 17 | { 18 | let response = await discovery.listEndpoints({ database: driver.database }) 19 | 20 | assert.ok(response.operation, 'Operation is not defined') 21 | assert.ok(response.operation.result, 'Result is not defined') 22 | 23 | console.log('Endpoints:', anyUnpack(response.operation.result, ListEndpointsResultSchema)) 24 | } 25 | 26 | { 27 | let response = await discovery.whoAmI({}) 28 | 29 | assert.ok(response.operation, 'Operation is not defined') 30 | assert.ok(response.operation.result, 'Result is not defined') 31 | 32 | console.log('WhoAmI:', anyUnpack(response.operation.result, WhoAmIResultSchema)) 33 | } 34 | 35 | driver.close() 36 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "dependencies": { 4 | "@ydbjs/api": "alpha", 5 | "@ydbjs/auth": "alpha", 6 | "@ydbjs/core": "alpha", 7 | "@ydbjs/error": "alpha", 8 | "@ydbjs/query": "alpha", 9 | "@ydbjs/retry": "alpha", 10 | "@ydbjs/value": "alpha" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/query.js: -------------------------------------------------------------------------------- 1 | /** 2 | * How to start 3 | * 1. Install YDB https://ydb.tech/docs/en/quickstart 4 | * 2. Start YDB 5 | * 3. Run this example with node.js 6 | * 4. Make sure to set the YDB_CONNECTION_STRING environment variable to your YDB connection string 7 | * 5. Run the example with `node examples/query.js` 8 | * 6. You should see the output `Count: 3` 9 | * 10 | * With DEBUG=* you can see the debug output 11 | * 7. You can also run the example with `DEBUG=ydbjs:* node examples/query.js` 12 | * 8. You should see the debug output 13 | */ 14 | 15 | import { Driver } from '@ydbjs/core' 16 | import { query } from '@ydbjs/query' 17 | import { Json, Text, Uint8 } from '@ydbjs/value/primitive' 18 | import { Optional } from '@ydbjs/value/optional' 19 | import { fromJs } from '@ydbjs/value' 20 | 21 | let connectionString = 'grpc://localhost:2136/local' 22 | let driver = new Driver(connectionString) 23 | let sql = query(driver) 24 | 25 | await driver.ready() 26 | 27 | sql`SELECT $myP1, $myP2, $myP3, $myP4` 28 | .param('myP1', new Text('Hello World')) 29 | .param('myP2', new Uint8(8)) 30 | .param('myP3', new Optional(new Json({ hello: 'world' }))) 31 | .param('myP4', fromJs([{ key: 1, value: 'Hello' }, { key: 2, value: null }, { key: 3 }])) 32 | 33 | // Create your first table 34 | await sql`CREATE TABLE example ( 35 | key UInt64, 36 | value String, 37 | PRIMARY KEY (key) 38 | );` 39 | 40 | // Add sample data 41 | let data = [ 42 | { key: 1, value: 'Hello' }, 43 | { key: 2, value: 'World' }, 44 | { key: 3, value: '!' }, 45 | ] 46 | 47 | await sql`INSERT INTO example SELECT * FROM AS_TABLE(${data})` 48 | 49 | // Query the data 50 | let [[result]] = await sql`SELECT COUNT(*) as count FROM example;` 51 | console.log(`Result:`, result) // {count: 3} 52 | 53 | // Clean up 54 | await sql`DROP TABLE example;` 55 | driver.close() 56 | -------------------------------------------------------------------------------- /examples/retry.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { StatusIds_StatusCode } from '@ydbjs/api/operation' 3 | import { Driver } from '@ydbjs/core' 4 | import { YDBError } from '@ydbjs/error' 5 | import { query } from '@ydbjs/query' 6 | import { defaultRetryConfig, retry } from '@ydbjs/retry' 7 | 8 | let driver = new Driver(process.env.YDB_CONNECTION_STRING || 'grpc://localhost:2136/local') 9 | let sql = query(driver) 10 | 11 | let result = await retry(defaultRetryConfig, async () => { 12 | let [[result]] = await sql`SELECT CAST(version() as Text) as version;` 13 | 14 | if (Math.random() > 0.5) { 15 | throw new YDBError(StatusIds_StatusCode.UNAVAILABLE, []) 16 | } 17 | 18 | return result 19 | }) 20 | 21 | console.log(`Result:`, result) // { version: 'stable-24-3' } 22 | -------------------------------------------------------------------------------- /examples/sls/index.js: -------------------------------------------------------------------------------- 1 | import { MetadataCredentialsProvider } from '@ydbjs/auth/metadata' 2 | import { Driver } from '@ydbjs/core' 3 | import { query } from '@ydbjs/query' 4 | 5 | export async function handler() { 6 | const provider = new MetadataCredentialsProvider({}) 7 | 8 | const driver = new Driver( 9 | 'grpcs://ydb.serverless.yandexcloud.net:2135/ru-central1/b1gh9qpnleo6mg7ov83v/etndqej6hqst8mklliso', 10 | { 11 | credentialsProvider: provider, 12 | 'ydb.sdk.enable_discovery': false, 13 | } 14 | ) 15 | 16 | await driver.ready() 17 | const sql = query(driver) 18 | const resultSets = await sql`SELECT 1 + 1 AS sum` 19 | console.log(resultSets) // [ [ { sum: 2 } ] ] 20 | 21 | return { 22 | statusCode: 200, 23 | body: resultSets, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/sls/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "dependencies": { 4 | "@ydbjs/auth": "6.0.0-alpha.12", 5 | "@ydbjs/core": "6.0.0-alpha.14", 6 | "@ydbjs/query": "6.0.0-alpha.17" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ydbjs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "workspaces": [ 7 | "packages/*", 8 | "third-party/*" 9 | ], 10 | "scripts": { 11 | "build": "turbo run build", 12 | "attw": "turbo run attw", 13 | "lint": "oxlint", 14 | "test": "vitest run --project !e2e", 15 | "test:watch": "vitest --watch --project !e2e", 16 | "test:e2e": "vitest --run --project e2e", 17 | "test:e2e:watch": "vitest --watch --project e2e", 18 | "publish-packages": "turbo run test && turbo run clean build attw && changeset version && changeset publish" 19 | }, 20 | "dependencies": { 21 | "@types/node": "^22.15.30" 22 | }, 23 | "devDependencies": { 24 | "oxlint": "^0.18.0", 25 | "prettier": "^3.5.3", 26 | "tsx": "^4.19.4", 27 | "turbo": "^2.5.4", 28 | "typescript": "^5.8.3", 29 | "vitest": "^3.2.2", 30 | "zx": "^8.5.5" 31 | }, 32 | "engineStrict": true, 33 | "engines": { 34 | "node": ">=20", 35 | "npm": ">=10" 36 | }, 37 | "overrides": { 38 | "@grpc/proto-loader": "npm:dry-uninstall" 39 | }, 40 | "packageManager": "npm@11.3.0", 41 | "prettier": { 42 | "printWidth": 120, 43 | "tabWidth": 4, 44 | "useTabs": true, 45 | "semi": false, 46 | "singleQuote": true, 47 | "trailingComma": "es5", 48 | "bracketSpacing": true, 49 | "bracketSameLine": false, 50 | "arrowParens": "always", 51 | "endOfLine": "lf", 52 | "overrides": [ 53 | { 54 | "files": "*.{md,yml,yaml}", 55 | "options": { 56 | "tabWidth": 2, 57 | "useTabs": false 58 | } 59 | } 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/api/.gitignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | -------------------------------------------------------------------------------- /packages/api/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | 3 | /src 4 | /tests 5 | /plugins 6 | 7 | /*.yaml 8 | /*.json 9 | -------------------------------------------------------------------------------- /packages/api/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/api 2 | 3 | ## 6.0.0-alpha.8 4 | 5 | ### Patch Changes 6 | 7 | - Fix build cjs issues 8 | 9 | ## 6.0.0-alpha.7 10 | 11 | ### Patch Changes 12 | 13 | - Update packages description 14 | 15 | ## 6.0.0-alpha.6 16 | 17 | ### Patch Changes 18 | 19 | - Consolidated and clarified README files across all packages. 20 | 21 | ## 6.0.0-alpha.5 22 | 23 | ### Patch Changes 24 | 25 | - Add transaction support 26 | 27 | ## 6.0.0-alpha.4 28 | 29 | ### Patch Changes 30 | 31 | - Added support to ignore the `.turbo` folder for better compatibility and cleaner workflows. 32 | 33 | ## 6.0.0-alpha.3 34 | 35 | ### Patch Changes 36 | 37 | - Update usage examples 38 | -------------------------------------------------------------------------------- /packages/api/README.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/api 2 | 3 | The `@ydbjs/api` package provides TypeScript/JavaScript bindings for interacting with YDB services. It includes generated gRPC service definitions and protocol buffer types for various YDB APIs. 4 | 5 | ## Features 6 | 7 | - gRPC service definitions for YDB APIs 8 | - Protocol buffer types for YDB entities 9 | - TypeScript support with type definitions 10 | 11 | ## Installation 12 | 13 | Install the package using npm: 14 | 15 | ```sh 16 | npm install @ydbjs/api@alpha 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### Importing Services 22 | 23 | ```ts 24 | import { DiscoveryServiceDefinition } from '@ydbjs/api/discovery'; 25 | import { CmsServiceDefinition } from '@ydbjs/api/cms'; 26 | import { QueryServiceDefinition } from '@ydbjs/api/query'; 27 | ``` 28 | 29 | ### Example: Using a Service 30 | 31 | ```ts 32 | import { createClientFactory, createChannel } from 'nice-grpc'; 33 | import { DiscoveryServiceDefinition } from '@ydbjs/api/discovery'; 34 | 35 | const clientFactory = createClientFactory(); 36 | const discoveryClient = clientFactory.create(DiscoveryServiceDefinition, createChannel('http://localhost:2136')); 37 | 38 | async function listEndpoints() { 39 | const response = await discoveryClient.listEndpoints({ database: '/local' }); 40 | console.log(response); 41 | } 42 | 43 | listEndpoints().catch(console.error); 44 | ``` 45 | 46 | ### Using with @ydbjs/core 47 | 48 | ```ts 49 | import { DiscoveryServiceDefinition } from '@ydbjs/api/discovery'; 50 | import { Driver } from '@ydbjs/core'; 51 | 52 | const driver = new Driver('grpc://localhost:2136'); 53 | const client = driver.createClient(DiscoveryServiceDefinition); 54 | client.listEndpoints({ database: '/local' }); 55 | ``` 56 | 57 | ## Development 58 | 59 | ### Generating gRPC and Protocol Buffer Files 60 | 61 | ```sh 62 | npm run generate 63 | ``` 64 | 65 | ## License 66 | 67 | This project is licensed under the [Apache 2.0 License](../../LICENSE). 68 | 69 | ## Links 70 | 71 | - [YDB Documentation](https://ydb.tech) 72 | - [GitHub Repository](https://github.com/ydb-platform/ydb-js-sdk) 73 | - [Issues](https://github.com/ydb-platform/ydb-js-sdk/issues) 74 | -------------------------------------------------------------------------------- /packages/api/buf.gen.grpc.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | plugins: 3 | - local: 4 | [ 5 | 'node', 6 | '--experimental-strip-types', 7 | './plugins/protoc-gen-nice-grpc.ts', 8 | ] 9 | opt: target=ts,import_extension=js 10 | out: ./src/gen 11 | inputs: 12 | - git_repo: https://github.com/ydb-platform/ydb-api-protos.git 13 | branch: master 14 | depth: 1 15 | exclude_paths: 16 | - draft 17 | - protos 18 | -------------------------------------------------------------------------------- /packages/api/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | clean: true 3 | plugins: 4 | - local: protoc-gen-es 5 | opt: target=ts,import_extension=js 6 | out: ./src/gen 7 | include_imports: true 8 | include_wkt: true 9 | inputs: 10 | - git_repo: https://github.com/ydb-platform/ydb-api-protos.git 11 | branch: master 12 | depth: 1 13 | paths: 14 | - protos 15 | -------------------------------------------------------------------------------- /packages/api/plugins/protoc-gen-nice-grpc.ts: -------------------------------------------------------------------------------- 1 | import { type Schema, createEcmaScriptPlugin, runNodeJs, safeIdentifier } from '@bufbuild/protoplugin' 2 | 3 | let plugin = createEcmaScriptPlugin({ 4 | name: 'protoc-gen-nice-grpc', 5 | version: 'v1', 6 | 7 | generateTs(schema: Schema) { 8 | // Loop through all Protobuf files in the schema 9 | for (let file of schema.files) { 10 | let f = schema.generateFile(file.name + '_grpc_pb.ts') 11 | f.preamble(file) 12 | 13 | let niceGRPCServiceDefinition = f.import('ServiceDefinition', 'nice-grpc', true) 14 | let bufProtoCreate = f.import('create', '@bufbuild/protobuf') 15 | let bufProtoMessageInitShape = f.import('MessageInitShape', '@bufbuild/protobuf', true) 16 | let bufProtoToBinary = f.import('toBinary', '@bufbuild/protobuf') 17 | let bufProtoFromBinary = f.import('fromBinary', '@bufbuild/protobuf') 18 | 19 | // Create a service definition based on the Nice-GRPC one 20 | // (see https://github.com/deeplay-io/nice-grpc/blob/7458e8a57aec763d854c2e6eb119bfe6820b17dd/packages/nice-grpc/src/service-definitions/index.ts#L20) 21 | for (let service of file.services) { 22 | f.print(f.jsDoc(service)) 23 | f.print(f.export('const', safeIdentifier(service.name + 'Definition')), ' = {') 24 | for (let method of service.methods) { 25 | let requestSchema = f.importSchema(method.input) 26 | let responseSchema = f.importSchema(method.output) 27 | let requestStream = method.methodKind === 'client_streaming' || method.methodKind === 'bidi_streaming' 28 | let responseStream = method.methodKind === 'server_streaming' || method.methodKind === 'bidi_streaming' 29 | 30 | f.print(f.jsDoc(method, ' ')) 31 | f.print(' ', safeIdentifier(method.localName), ': {') 32 | f.print(' path: ', f.string(`/${service.typeName}/${method.name}`), ',') 33 | f.print(' requestStream: ' + (requestStream ? 'true' : 'false') + ',') 34 | f.print( 35 | ' requestSerialize: (message: ', 36 | bufProtoMessageInitShape, 37 | ') => ', 40 | bufProtoToBinary, 41 | '(', 42 | requestSchema, 43 | ', ', 44 | bufProtoCreate, 45 | '(', 46 | requestSchema, 47 | ', message)),' 48 | ) 49 | f.print(' requestDeserialize: (bytes: Uint8Array) => ', bufProtoFromBinary, '(', requestSchema, ',bytes),') 50 | f.print(' responseStream: ' + (responseStream ? 'true' : 'false') + ', ') 51 | f.print( 52 | ' responseSerialize: (message: ', 53 | bufProtoMessageInitShape, 54 | ') => ', 57 | bufProtoToBinary, 58 | '(', 59 | responseSchema, 60 | ', ', 61 | bufProtoCreate, 62 | '(', 63 | responseSchema, 64 | ', message)),' 65 | ) 66 | f.print(' responseDeserialize: (bytes: Uint8Array) => ', bufProtoFromBinary, '(', responseSchema, ',bytes),') 67 | f.print(' options: {},') 68 | f.print(' },') 69 | } 70 | f.print('} as const satisfies ', niceGRPCServiceDefinition) 71 | } 72 | } 73 | }, 74 | }) 75 | 76 | // Reads the schema from stdin, runs the plugin, and writes the generated files to stdout. 77 | runNodeJs(plugin) 78 | -------------------------------------------------------------------------------- /packages/api/src/auth.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_auth_pb.js' 2 | export * from './gen/ydb_auth_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/cms.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_cms_pb.js' 2 | export * from './gen/ydb_cms_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/coordination.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_coordination_pb.js' 2 | export * from './gen/ydb_coordination_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/discovery.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_discovery_pb.js' 2 | export * from './gen/ydb_discovery_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/export.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_export_pb.js' 2 | export * from './gen/ydb_export_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/federation_discovery.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_federation_discovery_pb.js' 2 | export * from './gen/ydb_federation_discovery_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/gen/google/protobuf/empty_pb.ts: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | // @generated by protoc-gen-es v2.2.5 with parameter "target=ts,import_extension=js" 32 | // @generated from file google/protobuf/empty.proto (package google.protobuf, syntax proto3) 33 | /* eslint-disable */ 34 | 35 | import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv1"; 36 | import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv1"; 37 | import type { Message } from "@bufbuild/protobuf"; 38 | 39 | /** 40 | * Describes the file google/protobuf/empty.proto. 41 | */ 42 | export const file_google_protobuf_empty: GenFile = /*@__PURE__*/ 43 | fileDesc("Chtnb29nbGUvcHJvdG9idWYvZW1wdHkucHJvdG8SD2dvb2dsZS5wcm90b2J1ZiIHCgVFbXB0eUJ9ChNjb20uZ29vZ2xlLnByb3RvYnVmQgpFbXB0eVByb3RvUAFaLmdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3R5cGVzL2tub3duL2VtcHR5cGL4AQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw"); 44 | 45 | /** 46 | * A generic empty message that you can re-use to avoid defining duplicated 47 | * empty messages in your APIs. A typical example is to use it as the request 48 | * or the response type of an API method. For instance: 49 | * 50 | * service Foo { 51 | * rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); 52 | * } 53 | * 54 | * 55 | * @generated from message google.protobuf.Empty 56 | */ 57 | export type Empty = Message<"google.protobuf.Empty"> & { 58 | }; 59 | 60 | /** 61 | * Describes the message google.protobuf.Empty. 62 | * Use `create(EmptySchema)` to create a new message. 63 | */ 64 | export const EmptySchema: GenMessage = /*@__PURE__*/ 65 | messageDesc(file_google_protobuf_empty, 0); 66 | 67 | -------------------------------------------------------------------------------- /packages/api/src/gen/protos/annotations/sensitive_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v2.2.5 with parameter "target=ts,import_extension=js" 2 | // @generated from file protos/annotations/sensitive.proto (package Ydb, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { GenExtension, GenFile } from "@bufbuild/protobuf/codegenv1"; 6 | import { extDesc, fileDesc } from "@bufbuild/protobuf/codegenv1"; 7 | import type { FieldOptions } from "@bufbuild/protobuf/wkt"; 8 | import { file_google_protobuf_descriptor } from "@bufbuild/protobuf/wkt"; 9 | 10 | /** 11 | * Describes the file protos/annotations/sensitive.proto. 12 | */ 13 | export const file_protos_annotations_sensitive: GenFile = /*@__PURE__*/ 14 | fileDesc("CiJwcm90b3MvYW5ub3RhdGlvbnMvc2Vuc2l0aXZlLnByb3RvEgNZZGI6PQoJc2Vuc2l0aXZlEh0uZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucxjnrAUgASgIUglzZW5zaXRpdmVCRwoOdGVjaC55ZGIucHJvdG9aMmdpdGh1Yi5jb20veWRiLXBsYXRmb3JtL3lkYi1nby1nZW5wcm90by9wcm90b3MvWWRi+AEBYgZwcm90bzM", [file_google_protobuf_descriptor]); 15 | 16 | /** 17 | * do not print this field 18 | * 19 | * @generated from extension: bool sensitive = 87655; 20 | */ 21 | export const sensitive: GenExtension = /*@__PURE__*/ 22 | extDesc(file_protos_annotations_sensitive, 0); 23 | 24 | -------------------------------------------------------------------------------- /packages/api/src/gen/protos/ydb_auth_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v2.2.5 with parameter "target=ts,import_extension=js" 2 | // @generated from file protos/ydb_auth.proto (package Ydb.Auth, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv1"; 6 | import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv1"; 7 | import type { Operation, OperationParams } from "./ydb_operation_pb.js"; 8 | import { file_protos_ydb_operation } from "./ydb_operation_pb.js"; 9 | import type { Message } from "@bufbuild/protobuf"; 10 | 11 | /** 12 | * Describes the file protos/ydb_auth.proto. 13 | */ 14 | export const file_protos_ydb_auth: GenFile = /*@__PURE__*/ 15 | fileDesc("ChVwcm90b3MveWRiX2F1dGgucHJvdG8SCFlkYi5BdXRoImkKDExvZ2luUmVxdWVzdBI5ChBvcGVyYXRpb25fcGFyYW1zGAEgASgLMh8uWWRiLk9wZXJhdGlvbnMuT3BlcmF0aW9uUGFyYW1zEgwKBHVzZXIYAiABKAkSEAoIcGFzc3dvcmQYAyABKAkiPQoNTG9naW5SZXNwb25zZRIsCglvcGVyYXRpb24YASABKAsyGS5ZZGIuT3BlcmF0aW9ucy5PcGVyYXRpb24iHAoLTG9naW5SZXN1bHQSDQoFdG9rZW4YASABKAlCUQoTdGVjaC55ZGIucHJvdG8uYXV0aFo3Z2l0aHViLmNvbS95ZGItcGxhdGZvcm0veWRiLWdvLWdlbnByb3RvL3Byb3Rvcy9ZZGJfQXV0aPgBAWIGcHJvdG8z", [file_protos_ydb_operation]); 16 | 17 | /** 18 | * @generated from message Ydb.Auth.LoginRequest 19 | */ 20 | export type LoginRequest = Message<"Ydb.Auth.LoginRequest"> & { 21 | /** 22 | * @generated from field: Ydb.Operations.OperationParams operation_params = 1; 23 | */ 24 | operationParams?: OperationParams; 25 | 26 | /** 27 | * @generated from field: string user = 2; 28 | */ 29 | user: string; 30 | 31 | /** 32 | * @generated from field: string password = 3; 33 | */ 34 | password: string; 35 | }; 36 | 37 | /** 38 | * Describes the message Ydb.Auth.LoginRequest. 39 | * Use `create(LoginRequestSchema)` to create a new message. 40 | */ 41 | export const LoginRequestSchema: GenMessage = /*@__PURE__*/ 42 | messageDesc(file_protos_ydb_auth, 0); 43 | 44 | /** 45 | * @generated from message Ydb.Auth.LoginResponse 46 | */ 47 | export type LoginResponse = Message<"Ydb.Auth.LoginResponse"> & { 48 | /** 49 | * After successfull completion must contain LoginResult. 50 | * 51 | * @generated from field: Ydb.Operations.Operation operation = 1; 52 | */ 53 | operation?: Operation; 54 | }; 55 | 56 | /** 57 | * Describes the message Ydb.Auth.LoginResponse. 58 | * Use `create(LoginResponseSchema)` to create a new message. 59 | */ 60 | export const LoginResponseSchema: GenMessage = /*@__PURE__*/ 61 | messageDesc(file_protos_ydb_auth, 1); 62 | 63 | /** 64 | * @generated from message Ydb.Auth.LoginResult 65 | */ 66 | export type LoginResult = Message<"Ydb.Auth.LoginResult"> & { 67 | /** 68 | * @generated from field: string token = 1; 69 | */ 70 | token: string; 71 | }; 72 | 73 | /** 74 | * Describes the message Ydb.Auth.LoginResult. 75 | * Use `create(LoginResultSchema)` to create a new message. 76 | */ 77 | export const LoginResultSchema: GenMessage = /*@__PURE__*/ 78 | messageDesc(file_protos_ydb_auth, 2); 79 | 80 | -------------------------------------------------------------------------------- /packages/api/src/gen/protos/ydb_common_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v2.2.5 with parameter "target=ts,import_extension=js" 2 | // @generated from file protos/ydb_common.proto (package Ydb, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv1"; 6 | import { enumDesc, fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv1"; 7 | import type { Message } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * Describes the file protos/ydb_common.proto. 11 | */ 12 | export const file_protos_ydb_common: GenFile = /*@__PURE__*/ 13 | fileDesc("Chdwcm90b3MveWRiX2NvbW1vbi5wcm90bxIDWWRiIkoKC0ZlYXR1cmVGbGFnIjsKBlN0YXR1cxIWChJTVEFUVVNfVU5TUEVDSUZJRUQQABILCgdFTkFCTEVEEAESDAoIRElTQUJMRUQQAiIiCghDb3N0SW5mbxIWCg5jb25zdW1lZF91bml0cxgBIAEoASIdCg1RdW90YUV4Y2VlZGVkEgwKBGRpc2sYASABKAgiNAoQVmlydHVhbFRpbWVzdGFtcBIRCglwbGFuX3N0ZXAYASABKAQSDQoFdHhfaWQYAiABKARCXAoVdGVjaC55ZGIucHJvdG8uY29tbW9uQgxDb21tb25Qcm90b3NaMmdpdGh1Yi5jb20veWRiLXBsYXRmb3JtL3lkYi1nby1nZW5wcm90by9wcm90b3MvWWRi+AEBYgZwcm90bzM"); 14 | 15 | /** 16 | * @generated from message Ydb.FeatureFlag 17 | */ 18 | export type FeatureFlag = Message<"Ydb.FeatureFlag"> & { 19 | }; 20 | 21 | /** 22 | * Describes the message Ydb.FeatureFlag. 23 | * Use `create(FeatureFlagSchema)` to create a new message. 24 | */ 25 | export const FeatureFlagSchema: GenMessage = /*@__PURE__*/ 26 | messageDesc(file_protos_ydb_common, 0); 27 | 28 | /** 29 | * @generated from enum Ydb.FeatureFlag.Status 30 | */ 31 | export enum FeatureFlag_Status { 32 | /** 33 | * @generated from enum value: STATUS_UNSPECIFIED = 0; 34 | */ 35 | STATUS_UNSPECIFIED = 0, 36 | 37 | /** 38 | * @generated from enum value: ENABLED = 1; 39 | */ 40 | ENABLED = 1, 41 | 42 | /** 43 | * @generated from enum value: DISABLED = 2; 44 | */ 45 | DISABLED = 2, 46 | } 47 | 48 | /** 49 | * Describes the enum Ydb.FeatureFlag.Status. 50 | */ 51 | export const FeatureFlag_StatusSchema: GenEnum = /*@__PURE__*/ 52 | enumDesc(file_protos_ydb_common, 0, 0); 53 | 54 | /** 55 | * @generated from message Ydb.CostInfo 56 | */ 57 | export type CostInfo = Message<"Ydb.CostInfo"> & { 58 | /** 59 | * Total amount of request units (RU), consumed by the operation. 60 | * 61 | * @generated from field: double consumed_units = 1; 62 | */ 63 | consumedUnits: number; 64 | }; 65 | 66 | /** 67 | * Describes the message Ydb.CostInfo. 68 | * Use `create(CostInfoSchema)` to create a new message. 69 | */ 70 | export const CostInfoSchema: GenMessage = /*@__PURE__*/ 71 | messageDesc(file_protos_ydb_common, 1); 72 | 73 | /** 74 | * @generated from message Ydb.QuotaExceeded 75 | */ 76 | export type QuotaExceeded = Message<"Ydb.QuotaExceeded"> & { 77 | /** 78 | * @generated from field: bool disk = 1; 79 | */ 80 | disk: boolean; 81 | }; 82 | 83 | /** 84 | * Describes the message Ydb.QuotaExceeded. 85 | * Use `create(QuotaExceededSchema)` to create a new message. 86 | */ 87 | export const QuotaExceededSchema: GenMessage = /*@__PURE__*/ 88 | messageDesc(file_protos_ydb_common, 2); 89 | 90 | /** 91 | * Specifies a point in database time 92 | * 93 | * @generated from message Ydb.VirtualTimestamp 94 | */ 95 | export type VirtualTimestamp = Message<"Ydb.VirtualTimestamp"> & { 96 | /** 97 | * @generated from field: uint64 plan_step = 1; 98 | */ 99 | planStep: bigint; 100 | 101 | /** 102 | * @generated from field: uint64 tx_id = 2; 103 | */ 104 | txId: bigint; 105 | }; 106 | 107 | /** 108 | * Describes the message Ydb.VirtualTimestamp. 109 | * Use `create(VirtualTimestampSchema)` to create a new message. 110 | */ 111 | export const VirtualTimestampSchema: GenMessage = /*@__PURE__*/ 112 | messageDesc(file_protos_ydb_common, 3); 113 | 114 | -------------------------------------------------------------------------------- /packages/api/src/gen/protos/ydb_formats_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v2.2.5 with parameter "target=ts,import_extension=js" 2 | // @generated from file protos/ydb_formats.proto (package Ydb.Formats, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv1"; 6 | import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv1"; 7 | import type { Message } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * Describes the file protos/ydb_formats.proto. 11 | */ 12 | export const file_protos_ydb_formats: GenFile = /*@__PURE__*/ 13 | fileDesc("Chhwcm90b3MveWRiX2Zvcm1hdHMucHJvdG8SC1lkYi5Gb3JtYXRzIiQKEkFycm93QmF0Y2hTZXR0aW5ncxIOCgZzY2hlbWEYASABKAwi2gEKC0NzdlNldHRpbmdzEhEKCXNraXBfcm93cxgBIAEoDRIRCglkZWxpbWl0ZXIYAiABKAwSEgoKbnVsbF92YWx1ZRgDIAEoDBIOCgZoZWFkZXIYBCABKAgSMQoHcXVvdGluZxgFIAEoCzIgLllkYi5Gb3JtYXRzLkNzdlNldHRpbmdzLlF1b3RpbmcaTgoHUXVvdGluZxIQCghkaXNhYmxlZBgBIAEoCBISCgpxdW90ZV9jaGFyGAIgASgMEh0KFWRvdWJsZV9xdW90ZV9kaXNhYmxlZBgDIAEoCEJXChZ0ZWNoLnlkYi5wcm90by5mb3JtYXRzWjpnaXRodWIuY29tL3lkYi1wbGF0Zm9ybS95ZGItZ28tZ2VucHJvdG8vcHJvdG9zL1lkYl9Gb3JtYXRz+AEBYgZwcm90bzM"); 14 | 15 | /** 16 | * @generated from message Ydb.Formats.ArrowBatchSettings 17 | */ 18 | export type ArrowBatchSettings = Message<"Ydb.Formats.ArrowBatchSettings"> & { 19 | /** 20 | * @generated from field: bytes schema = 1; 21 | */ 22 | schema: Uint8Array; 23 | }; 24 | 25 | /** 26 | * Describes the message Ydb.Formats.ArrowBatchSettings. 27 | * Use `create(ArrowBatchSettingsSchema)` to create a new message. 28 | */ 29 | export const ArrowBatchSettingsSchema: GenMessage = /*@__PURE__*/ 30 | messageDesc(file_protos_ydb_formats, 0); 31 | 32 | /** 33 | * @generated from message Ydb.Formats.CsvSettings 34 | */ 35 | export type CsvSettings = Message<"Ydb.Formats.CsvSettings"> & { 36 | /** 37 | * Number of rows to skip before CSV data. It should be present only in the first upsert of CSV file. 38 | * 39 | * @generated from field: uint32 skip_rows = 1; 40 | */ 41 | skipRows: number; 42 | 43 | /** 44 | * Fields delimiter in CSV file. It's "," if not set. 45 | * 46 | * @generated from field: bytes delimiter = 2; 47 | */ 48 | delimiter: Uint8Array; 49 | 50 | /** 51 | * String value that would be interpreted as NULL. 52 | * 53 | * @generated from field: bytes null_value = 3; 54 | */ 55 | nullValue: Uint8Array; 56 | 57 | /** 58 | * First not skipped line is a CSV header (list of column names). 59 | * 60 | * @generated from field: bool header = 4; 61 | */ 62 | header: boolean; 63 | 64 | /** 65 | * @generated from field: Ydb.Formats.CsvSettings.Quoting quoting = 5; 66 | */ 67 | quoting?: CsvSettings_Quoting; 68 | }; 69 | 70 | /** 71 | * Describes the message Ydb.Formats.CsvSettings. 72 | * Use `create(CsvSettingsSchema)` to create a new message. 73 | */ 74 | export const CsvSettingsSchema: GenMessage = /*@__PURE__*/ 75 | messageDesc(file_protos_ydb_formats, 1); 76 | 77 | /** 78 | * @generated from message Ydb.Formats.CsvSettings.Quoting 79 | */ 80 | export type CsvSettings_Quoting = Message<"Ydb.Formats.CsvSettings.Quoting"> & { 81 | /** 82 | * @generated from field: bool disabled = 1; 83 | */ 84 | disabled: boolean; 85 | 86 | /** 87 | * @generated from field: bytes quote_char = 2; 88 | */ 89 | quoteChar: Uint8Array; 90 | 91 | /** 92 | * @generated from field: bool double_quote_disabled = 3; 93 | */ 94 | doubleQuoteDisabled: boolean; 95 | }; 96 | 97 | /** 98 | * Describes the message Ydb.Formats.CsvSettings.Quoting. 99 | * Use `create(CsvSettings_QuotingSchema)` to create a new message. 100 | */ 101 | export const CsvSettings_QuotingSchema: GenMessage = /*@__PURE__*/ 102 | messageDesc(file_protos_ydb_formats, 1, 0); 103 | 104 | -------------------------------------------------------------------------------- /packages/api/src/gen/protos/ydb_issue_message_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v2.2.5 with parameter "target=ts,import_extension=js" 2 | // @generated from file protos/ydb_issue_message.proto (package Ydb.Issue, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv1"; 6 | import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv1"; 7 | import type { Message } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * Describes the file protos/ydb_issue_message.proto. 11 | */ 12 | export const file_protos_ydb_issue_message: GenFile = /*@__PURE__*/ 13 | fileDesc("Ch5wcm90b3MveWRiX2lzc3VlX21lc3NhZ2UucHJvdG8SCVlkYi5Jc3N1ZSKRAgoMSXNzdWVNZXNzYWdlEjIKCHBvc2l0aW9uGAEgASgLMiAuWWRiLklzc3VlLklzc3VlTWVzc2FnZS5Qb3NpdGlvbhIPCgdtZXNzYWdlGAIgASgJEjYKDGVuZF9wb3NpdGlvbhgDIAEoCzIgLllkYi5Jc3N1ZS5Jc3N1ZU1lc3NhZ2UuUG9zaXRpb24SEgoKaXNzdWVfY29kZRgEIAEoDRIQCghzZXZlcml0eRgFIAEoDRInCgZpc3N1ZXMYBiADKAsyFy5ZZGIuSXNzdWUuSXNzdWVNZXNzYWdlGjUKCFBvc2l0aW9uEgsKA3JvdxgBIAEoDRIOCgZjb2x1bW4YAiABKA0SDAoEZmlsZRgDIAEoCUJNCg50ZWNoLnlkYi5wcm90b1o4Z2l0aHViLmNvbS95ZGItcGxhdGZvcm0veWRiLWdvLWdlbnByb3RvL3Byb3Rvcy9ZZGJfSXNzdWX4AQFiBnByb3RvMw"); 14 | 15 | /** 16 | * IssueMessage is a transport format for ydb/library/yql/public/issue library 17 | * 18 | * @generated from message Ydb.Issue.IssueMessage 19 | */ 20 | export type IssueMessage = Message<"Ydb.Issue.IssueMessage"> & { 21 | /** 22 | * @generated from field: Ydb.Issue.IssueMessage.Position position = 1; 23 | */ 24 | position?: IssueMessage_Position; 25 | 26 | /** 27 | * @generated from field: string message = 2; 28 | */ 29 | message: string; 30 | 31 | /** 32 | * @generated from field: Ydb.Issue.IssueMessage.Position end_position = 3; 33 | */ 34 | endPosition?: IssueMessage_Position; 35 | 36 | /** 37 | * @generated from field: uint32 issue_code = 4; 38 | */ 39 | issueCode: number; 40 | 41 | /** 42 | * Severity values from ydb/library/yql/public/issue/protos/issue_severity.proto 43 | * FATAL = 0; 44 | * ERROR = 1; 45 | * WARNING = 2; 46 | * INFO = 3; 47 | * 48 | * @generated from field: uint32 severity = 5; 49 | */ 50 | severity: number; 51 | 52 | /** 53 | * @generated from field: repeated Ydb.Issue.IssueMessage issues = 6; 54 | */ 55 | issues: IssueMessage[]; 56 | }; 57 | 58 | /** 59 | * Describes the message Ydb.Issue.IssueMessage. 60 | * Use `create(IssueMessageSchema)` to create a new message. 61 | */ 62 | export const IssueMessageSchema: GenMessage = /*@__PURE__*/ 63 | messageDesc(file_protos_ydb_issue_message, 0); 64 | 65 | /** 66 | * @generated from message Ydb.Issue.IssueMessage.Position 67 | */ 68 | export type IssueMessage_Position = Message<"Ydb.Issue.IssueMessage.Position"> & { 69 | /** 70 | * @generated from field: uint32 row = 1; 71 | */ 72 | row: number; 73 | 74 | /** 75 | * @generated from field: uint32 column = 2; 76 | */ 77 | column: number; 78 | 79 | /** 80 | * @generated from field: string file = 3; 81 | */ 82 | file: string; 83 | }; 84 | 85 | /** 86 | * Describes the message Ydb.Issue.IssueMessage.Position. 87 | * Use `create(IssueMessage_PositionSchema)` to create a new message. 88 | */ 89 | export const IssueMessage_PositionSchema: GenMessage = /*@__PURE__*/ 90 | messageDesc(file_protos_ydb_issue_message, 0, 0); 91 | 92 | -------------------------------------------------------------------------------- /packages/api/src/gen/ydb_auth_v1_grpc_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-nice-grpc v1 with parameter "target=ts,import_extension=js" 2 | // @generated from file ydb_auth_v1.proto (package Ydb.Auth.V1, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { MessageInitShape } from "@bufbuild/protobuf"; 6 | import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; 7 | import { LoginRequestSchema, LoginResponseSchema } from "./protos/ydb_auth_pb.js"; 8 | import type { ServiceDefinition } from "nice-grpc"; 9 | 10 | /** 11 | * @generated from service Ydb.Auth.V1.AuthService 12 | */ 13 | export const AuthServiceDefinition = { 14 | /** 15 | * Perform login using built-in auth system 16 | * 17 | * @generated from rpc Ydb.Auth.V1.AuthService.Login 18 | */ 19 | login: { 20 | path: "/Ydb.Auth.V1.AuthService/Login", 21 | requestStream: false, 22 | requestSerialize: (message: MessageInitShape) => toBinary(LoginRequestSchema, create(LoginRequestSchema, message)), 23 | requestDeserialize: (bytes: Uint8Array) => fromBinary(LoginRequestSchema,bytes), 24 | responseStream: false, 25 | responseSerialize: (message: MessageInitShape) => toBinary(LoginResponseSchema, create(LoginResponseSchema, message)), 26 | responseDeserialize: (bytes: Uint8Array) => fromBinary(LoginResponseSchema,bytes), 27 | options: {}, 28 | }, 29 | } as const satisfies ServiceDefinition 30 | -------------------------------------------------------------------------------- /packages/api/src/gen/ydb_discovery_v1_grpc_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-nice-grpc v1 with parameter "target=ts,import_extension=js" 2 | // @generated from file ydb_discovery_v1.proto (package Ydb.Discovery.V1, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { MessageInitShape } from "@bufbuild/protobuf"; 6 | import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; 7 | import { ListEndpointsRequestSchema, ListEndpointsResponseSchema, WhoAmIRequestSchema, WhoAmIResponseSchema } from "./protos/ydb_discovery_pb.js"; 8 | import type { ServiceDefinition } from "nice-grpc"; 9 | 10 | /** 11 | * @generated from service Ydb.Discovery.V1.DiscoveryService 12 | */ 13 | export const DiscoveryServiceDefinition = { 14 | /** 15 | * @generated from rpc Ydb.Discovery.V1.DiscoveryService.ListEndpoints 16 | */ 17 | listEndpoints: { 18 | path: "/Ydb.Discovery.V1.DiscoveryService/ListEndpoints", 19 | requestStream: false, 20 | requestSerialize: (message: MessageInitShape) => toBinary(ListEndpointsRequestSchema, create(ListEndpointsRequestSchema, message)), 21 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ListEndpointsRequestSchema,bytes), 22 | responseStream: false, 23 | responseSerialize: (message: MessageInitShape) => toBinary(ListEndpointsResponseSchema, create(ListEndpointsResponseSchema, message)), 24 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ListEndpointsResponseSchema,bytes), 25 | options: {}, 26 | }, 27 | /** 28 | * @generated from rpc Ydb.Discovery.V1.DiscoveryService.WhoAmI 29 | */ 30 | whoAmI: { 31 | path: "/Ydb.Discovery.V1.DiscoveryService/WhoAmI", 32 | requestStream: false, 33 | requestSerialize: (message: MessageInitShape) => toBinary(WhoAmIRequestSchema, create(WhoAmIRequestSchema, message)), 34 | requestDeserialize: (bytes: Uint8Array) => fromBinary(WhoAmIRequestSchema,bytes), 35 | responseStream: false, 36 | responseSerialize: (message: MessageInitShape) => toBinary(WhoAmIResponseSchema, create(WhoAmIResponseSchema, message)), 37 | responseDeserialize: (bytes: Uint8Array) => fromBinary(WhoAmIResponseSchema,bytes), 38 | options: {}, 39 | }, 40 | } as const satisfies ServiceDefinition 41 | -------------------------------------------------------------------------------- /packages/api/src/gen/ydb_export_v1_grpc_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-nice-grpc v1 with parameter "target=ts,import_extension=js" 2 | // @generated from file ydb_export_v1.proto (package Ydb.Export.V1, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { MessageInitShape } from "@bufbuild/protobuf"; 6 | import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; 7 | import { ExportToS3RequestSchema, ExportToS3ResponseSchema, ExportToYtRequestSchema, ExportToYtResponseSchema } from "./protos/ydb_export_pb.js"; 8 | import type { ServiceDefinition } from "nice-grpc"; 9 | 10 | /** 11 | * @generated from service Ydb.Export.V1.ExportService 12 | */ 13 | export const ExportServiceDefinition = { 14 | /** 15 | * Exports data to YT. 16 | * Method starts an asynchronous operation that can be cancelled while it is in progress. 17 | * 18 | * @generated from rpc Ydb.Export.V1.ExportService.ExportToYt 19 | */ 20 | exportToYt: { 21 | path: "/Ydb.Export.V1.ExportService/ExportToYt", 22 | requestStream: false, 23 | requestSerialize: (message: MessageInitShape) => toBinary(ExportToYtRequestSchema, create(ExportToYtRequestSchema, message)), 24 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ExportToYtRequestSchema,bytes), 25 | responseStream: false, 26 | responseSerialize: (message: MessageInitShape) => toBinary(ExportToYtResponseSchema, create(ExportToYtResponseSchema, message)), 27 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ExportToYtResponseSchema,bytes), 28 | options: {}, 29 | }, 30 | /** 31 | * Exports data to S3. 32 | * Method starts an asynchronous operation that can be cancelled while it is in progress. 33 | * 34 | * @generated from rpc Ydb.Export.V1.ExportService.ExportToS3 35 | */ 36 | exportToS3: { 37 | path: "/Ydb.Export.V1.ExportService/ExportToS3", 38 | requestStream: false, 39 | requestSerialize: (message: MessageInitShape) => toBinary(ExportToS3RequestSchema, create(ExportToS3RequestSchema, message)), 40 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ExportToS3RequestSchema,bytes), 41 | responseStream: false, 42 | responseSerialize: (message: MessageInitShape) => toBinary(ExportToS3ResponseSchema, create(ExportToS3ResponseSchema, message)), 43 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ExportToS3ResponseSchema,bytes), 44 | options: {}, 45 | }, 46 | } as const satisfies ServiceDefinition 47 | -------------------------------------------------------------------------------- /packages/api/src/gen/ydb_federation_discovery_v1_grpc_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-nice-grpc v1 with parameter "target=ts,import_extension=js" 2 | // @generated from file ydb_federation_discovery_v1.proto (package Ydb.FederationDiscovery.V1, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { MessageInitShape } from "@bufbuild/protobuf"; 6 | import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; 7 | import { ListFederationDatabasesRequestSchema, ListFederationDatabasesResponseSchema } from "./protos/ydb_federation_discovery_pb.js"; 8 | import type { ServiceDefinition } from "nice-grpc"; 9 | 10 | /** 11 | * @generated from service Ydb.FederationDiscovery.V1.FederationDiscoveryService 12 | */ 13 | export const FederationDiscoveryServiceDefinition = { 14 | /** 15 | * Get list of databases. 16 | * 17 | * @generated from rpc Ydb.FederationDiscovery.V1.FederationDiscoveryService.ListFederationDatabases 18 | */ 19 | listFederationDatabases: { 20 | path: "/Ydb.FederationDiscovery.V1.FederationDiscoveryService/ListFederationDatabases", 21 | requestStream: false, 22 | requestSerialize: (message: MessageInitShape) => toBinary(ListFederationDatabasesRequestSchema, create(ListFederationDatabasesRequestSchema, message)), 23 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ListFederationDatabasesRequestSchema,bytes), 24 | responseStream: false, 25 | responseSerialize: (message: MessageInitShape) => toBinary(ListFederationDatabasesResponseSchema, create(ListFederationDatabasesResponseSchema, message)), 26 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ListFederationDatabasesResponseSchema,bytes), 27 | options: {}, 28 | }, 29 | } as const satisfies ServiceDefinition 30 | -------------------------------------------------------------------------------- /packages/api/src/gen/ydb_import_v1_grpc_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-nice-grpc v1 with parameter "target=ts,import_extension=js" 2 | // @generated from file ydb_import_v1.proto (package Ydb.Import.V1, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { MessageInitShape } from "@bufbuild/protobuf"; 6 | import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; 7 | import { ImportDataRequestSchema, ImportDataResponseSchema, ImportFromS3RequestSchema, ImportFromS3ResponseSchema } from "./protos/ydb_import_pb.js"; 8 | import type { ServiceDefinition } from "nice-grpc"; 9 | 10 | /** 11 | * @generated from service Ydb.Import.V1.ImportService 12 | */ 13 | export const ImportServiceDefinition = { 14 | /** 15 | * Imports data from S3. 16 | * Method starts an asynchronous operation that can be cancelled while it is in progress. 17 | * 18 | * @generated from rpc Ydb.Import.V1.ImportService.ImportFromS3 19 | */ 20 | importFromS3: { 21 | path: "/Ydb.Import.V1.ImportService/ImportFromS3", 22 | requestStream: false, 23 | requestSerialize: (message: MessageInitShape) => toBinary(ImportFromS3RequestSchema, create(ImportFromS3RequestSchema, message)), 24 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ImportFromS3RequestSchema,bytes), 25 | responseStream: false, 26 | responseSerialize: (message: MessageInitShape) => toBinary(ImportFromS3ResponseSchema, create(ImportFromS3ResponseSchema, message)), 27 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ImportFromS3ResponseSchema,bytes), 28 | options: {}, 29 | }, 30 | /** 31 | * Writes data to a table. 32 | * Method accepts serialized data in the selected format and writes it non-transactionally. 33 | * 34 | * @generated from rpc Ydb.Import.V1.ImportService.ImportData 35 | */ 36 | importData: { 37 | path: "/Ydb.Import.V1.ImportService/ImportData", 38 | requestStream: false, 39 | requestSerialize: (message: MessageInitShape) => toBinary(ImportDataRequestSchema, create(ImportDataRequestSchema, message)), 40 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ImportDataRequestSchema,bytes), 41 | responseStream: false, 42 | responseSerialize: (message: MessageInitShape) => toBinary(ImportDataResponseSchema, create(ImportDataResponseSchema, message)), 43 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ImportDataResponseSchema,bytes), 44 | options: {}, 45 | }, 46 | } as const satisfies ServiceDefinition 47 | -------------------------------------------------------------------------------- /packages/api/src/gen/ydb_monitoring_v1_grpc_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-nice-grpc v1 with parameter "target=ts,import_extension=js" 2 | // @generated from file ydb_monitoring_v1.proto (package Ydb.Monitoring.V1, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { MessageInitShape } from "@bufbuild/protobuf"; 6 | import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; 7 | import { NodeCheckRequestSchema, NodeCheckResponseSchema, SelfCheckRequestSchema, SelfCheckResponseSchema } from "./protos/ydb_monitoring_pb.js"; 8 | import type { ServiceDefinition } from "nice-grpc"; 9 | 10 | /** 11 | * @generated from service Ydb.Monitoring.V1.MonitoringService 12 | */ 13 | export const MonitoringServiceDefinition = { 14 | /** 15 | * Gets the health status of the database. 16 | * 17 | * @generated from rpc Ydb.Monitoring.V1.MonitoringService.SelfCheck 18 | */ 19 | selfCheck: { 20 | path: "/Ydb.Monitoring.V1.MonitoringService/SelfCheck", 21 | requestStream: false, 22 | requestSerialize: (message: MessageInitShape) => toBinary(SelfCheckRequestSchema, create(SelfCheckRequestSchema, message)), 23 | requestDeserialize: (bytes: Uint8Array) => fromBinary(SelfCheckRequestSchema,bytes), 24 | responseStream: false, 25 | responseSerialize: (message: MessageInitShape) => toBinary(SelfCheckResponseSchema, create(SelfCheckResponseSchema, message)), 26 | responseDeserialize: (bytes: Uint8Array) => fromBinary(SelfCheckResponseSchema,bytes), 27 | options: {}, 28 | }, 29 | /** 30 | * Checks current node health 31 | * 32 | * @generated from rpc Ydb.Monitoring.V1.MonitoringService.NodeCheck 33 | */ 34 | nodeCheck: { 35 | path: "/Ydb.Monitoring.V1.MonitoringService/NodeCheck", 36 | requestStream: false, 37 | requestSerialize: (message: MessageInitShape) => toBinary(NodeCheckRequestSchema, create(NodeCheckRequestSchema, message)), 38 | requestDeserialize: (bytes: Uint8Array) => fromBinary(NodeCheckRequestSchema,bytes), 39 | responseStream: false, 40 | responseSerialize: (message: MessageInitShape) => toBinary(NodeCheckResponseSchema, create(NodeCheckResponseSchema, message)), 41 | responseDeserialize: (bytes: Uint8Array) => fromBinary(NodeCheckResponseSchema,bytes), 42 | options: {}, 43 | }, 44 | } as const satisfies ServiceDefinition 45 | -------------------------------------------------------------------------------- /packages/api/src/gen/ydb_scripting_v1_grpc_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-nice-grpc v1 with parameter "target=ts,import_extension=js" 2 | // @generated from file ydb_scripting_v1.proto (package Ydb.Scripting.V1, syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { MessageInitShape } from "@bufbuild/protobuf"; 6 | import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; 7 | import { ExecuteYqlPartialResponseSchema, ExecuteYqlRequestSchema, ExecuteYqlResponseSchema, ExplainYqlRequestSchema, ExplainYqlResponseSchema } from "./protos/ydb_scripting_pb.js"; 8 | import type { ServiceDefinition } from "nice-grpc"; 9 | 10 | /** 11 | * @generated from service Ydb.Scripting.V1.ScriptingService 12 | */ 13 | export const ScriptingServiceDefinition = { 14 | /** 15 | * @generated from rpc Ydb.Scripting.V1.ScriptingService.ExecuteYql 16 | */ 17 | executeYql: { 18 | path: "/Ydb.Scripting.V1.ScriptingService/ExecuteYql", 19 | requestStream: false, 20 | requestSerialize: (message: MessageInitShape) => toBinary(ExecuteYqlRequestSchema, create(ExecuteYqlRequestSchema, message)), 21 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ExecuteYqlRequestSchema,bytes), 22 | responseStream: false, 23 | responseSerialize: (message: MessageInitShape) => toBinary(ExecuteYqlResponseSchema, create(ExecuteYqlResponseSchema, message)), 24 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ExecuteYqlResponseSchema,bytes), 25 | options: {}, 26 | }, 27 | /** 28 | * Executes yql request with streaming result. 29 | * 30 | * @generated from rpc Ydb.Scripting.V1.ScriptingService.StreamExecuteYql 31 | */ 32 | streamExecuteYql: { 33 | path: "/Ydb.Scripting.V1.ScriptingService/StreamExecuteYql", 34 | requestStream: false, 35 | requestSerialize: (message: MessageInitShape) => toBinary(ExecuteYqlRequestSchema, create(ExecuteYqlRequestSchema, message)), 36 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ExecuteYqlRequestSchema,bytes), 37 | responseStream: true, 38 | responseSerialize: (message: MessageInitShape) => toBinary(ExecuteYqlPartialResponseSchema, create(ExecuteYqlPartialResponseSchema, message)), 39 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ExecuteYqlPartialResponseSchema,bytes), 40 | options: {}, 41 | }, 42 | /** 43 | * @generated from rpc Ydb.Scripting.V1.ScriptingService.ExplainYql 44 | */ 45 | explainYql: { 46 | path: "/Ydb.Scripting.V1.ScriptingService/ExplainYql", 47 | requestStream: false, 48 | requestSerialize: (message: MessageInitShape) => toBinary(ExplainYqlRequestSchema, create(ExplainYqlRequestSchema, message)), 49 | requestDeserialize: (bytes: Uint8Array) => fromBinary(ExplainYqlRequestSchema,bytes), 50 | responseStream: false, 51 | responseSerialize: (message: MessageInitShape) => toBinary(ExplainYqlResponseSchema, create(ExplainYqlResponseSchema, message)), 52 | responseDeserialize: (bytes: Uint8Array) => fromBinary(ExplainYqlResponseSchema,bytes), 53 | options: {}, 54 | }, 55 | } as const satisfies ServiceDefinition 56 | -------------------------------------------------------------------------------- /packages/api/src/import.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_import_pb.js' 2 | export * from './gen/ydb_import_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/monitoring.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_monitoring_pb.js' 2 | export * from './gen/ydb_monitoring_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/operation.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_operation_pb.js' 2 | export * from './gen/protos/ydb_status_codes_pb.ts' 3 | export * from './gen/protos/ydb_issue_message_pb.ts' 4 | export * from './gen/ydb_operation_v1_grpc_pb.js' 5 | -------------------------------------------------------------------------------- /packages/api/src/query.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_query_stats_pb.ts' 2 | export * from './gen/protos/ydb_query_pb.js' 3 | export * from './gen/ydb_query_v1_grpc_pb.js' 4 | -------------------------------------------------------------------------------- /packages/api/src/rate_limiter.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_rate_limiter_pb.js' 2 | export * from './gen/ydb_rate_limiter_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/scheme.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_scheme_pb.js' 2 | export * from './gen/ydb_scheme_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/scripting.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_scripting_pb.js' 2 | export * from './gen/ydb_scripting_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/table.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_table_pb.js' 2 | export * from './gen/ydb_table_v1_grpc_pb.js' 3 | -------------------------------------------------------------------------------- /packages/api/src/topic.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_topic_pb.js'; 2 | export * from './gen/ydb_topic_v1_grpc_pb.js'; 3 | -------------------------------------------------------------------------------- /packages/api/src/value.ts: -------------------------------------------------------------------------------- 1 | export * from './gen/protos/ydb_value_pb.js' 2 | -------------------------------------------------------------------------------- /packages/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "exclude": [ 7 | "**/*.test.ts", 8 | "./dist/**/*", 9 | "./plugins/**/*", 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/auth/.gitignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | -------------------------------------------------------------------------------- /packages/auth/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | 3 | /src 4 | /tests 5 | /plugins 6 | 7 | /*.yaml 8 | /*.json 9 | -------------------------------------------------------------------------------- /packages/auth/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/auth 2 | 3 | ## 6.0.0-alpha.15 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies 8 | - @ydbjs/error@6.0.0-alpha.9 9 | - @ydbjs/retry@6.0.0-alpha.13 10 | 11 | ## 6.0.0-alpha.14 12 | 13 | ### Patch Changes 14 | 15 | - Fix build cjs issues 16 | - Updated dependencies 17 | - @ydbjs/error@6.0.0-alpha.8 18 | - @ydbjs/retry@6.0.0-alpha.12 19 | - @ydbjs/api@6.0.0-alpha.8 20 | 21 | ## 6.0.0-alpha.13 22 | 23 | ### Patch Changes 24 | 25 | - Update metadata token refresh logic 26 | 27 | ## 6.0.0-alpha.12 28 | 29 | ### Patch Changes 30 | 31 | - Fix metadata credentials provider 32 | 33 | ## 6.0.0-alpha.11 34 | 35 | ### Patch Changes 36 | 37 | - Updated dependencies 38 | - @ydbjs/retry@6.0.0-alpha.10 39 | 40 | ## 6.0.0-alpha.10 41 | 42 | ### Patch Changes 43 | 44 | - Update packages description 45 | - Updated dependencies 46 | - @ydbjs/error@6.0.0-alpha.7 47 | - @ydbjs/retry@6.0.0-alpha.9 48 | - @ydbjs/api@6.0.0-alpha.7 49 | 50 | ## 6.0.0-alpha.9 51 | 52 | ### Patch Changes 53 | 54 | - Consolidated and clarified README files across all packages. 55 | - Updated dependencies 56 | - @ydbjs/api@6.0.0-alpha.6 57 | - @ydbjs/error@6.0.0-alpha.6 58 | - @ydbjs/retry@6.0.0-alpha.8 59 | 60 | ## 6.0.0-alpha.8 61 | 62 | ### Patch Changes 63 | 64 | - Enhanced authentication mechanisms and usage examples. 65 | - Improved documentation and structure. 66 | 67 | ## 6.0.0-alpha.7 68 | 69 | ### Patch Changes 70 | 71 | - Updated dependencies 72 | - @ydbjs/retry@6.0.0-alpha.7 73 | 74 | ## 6.0.0-alpha.6 75 | 76 | ### Patch Changes 77 | 78 | - Add transaction support 79 | - Updated dependencies 80 | - @ydbjs/api@6.0.0-alpha.5 81 | - @ydbjs/error@6.0.0-alpha.5 82 | - @ydbjs/retry@6.0.0-alpha.6 83 | 84 | ## 6.0.0-alpha.5 85 | 86 | ### Patch Changes 87 | 88 | - Updated dependencies 89 | - @ydbjs/retry@6.0.0-alpha.5 90 | 91 | ## 6.0.0-alpha.4 92 | 93 | ### Patch Changes 94 | 95 | - Added support to ignore the `.turbo` folder for better compatibility and cleaner workflows. 96 | - Updated dependencies 97 | - @ydbjs/api@6.0.0-alpha.4 98 | - @ydbjs/error@6.0.0-alpha.4 99 | - @ydbjs/retry@6.0.0-alpha.4 100 | 101 | ## 6.0.0-alpha.3 102 | 103 | ### Patch Changes 104 | 105 | - Update usage examples 106 | - Updated dependencies 107 | - @ydbjs/api@6.0.0-alpha.3 108 | - @ydbjs/error@6.0.0-alpha.3 109 | - @ydbjs/retry@6.0.0-alpha.3 110 | -------------------------------------------------------------------------------- /packages/auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ydbjs/auth", 3 | "version": "6.0.0-alpha.15", 4 | "description": "Authentication providers for YDB: static credentials, tokens, anonymous, and cloud metadata. Integrates with the core driver for secure access.", 5 | "license": "Apache-2.0", 6 | "type": "module", 7 | "sideEffects": false, 8 | "module": "dist/esm/index.js", 9 | "main": "dist/cjs/index.js", 10 | "scripts": { 11 | "clean": "rm -rf dist", 12 | "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", 13 | "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm", 14 | "test": "vitest --run", 15 | "test:watch": "vitest --watch", 16 | "attw": "attw --pack" 17 | }, 18 | "exports": { 19 | ".": { 20 | "require": "./dist/cjs/index.js", 21 | "import": "./dist/esm/index.js" 22 | }, 23 | "./access-token": { 24 | "require": "./dist/cjs/access-token.js", 25 | "import": "./dist/esm/access-token.js" 26 | }, 27 | "./anonymous": { 28 | "require": "./dist/cjs/anonymous.js", 29 | "import": "./dist/esm/anonymous.js" 30 | }, 31 | "./metadata": { 32 | "require": "./dist/cjs/metadata.js", 33 | "import": "./dist/esm/metadata.js" 34 | }, 35 | "./static": { 36 | "require": "./dist/cjs/static.js", 37 | "import": "./dist/esm/static.js" 38 | } 39 | }, 40 | "types": "dist/esm/index.d.ts", 41 | "typesVersions": { 42 | "*": { 43 | ".": [ 44 | "./dist/cjs/index.d.ts" 45 | ], 46 | "access-token": [ 47 | "./dist/cjs/access-token.d.ts" 48 | ], 49 | "anonymous": [ 50 | "./dist/cjs/anonymous.d.ts" 51 | ], 52 | "metadata": [ 53 | "./dist/cjs/metadata.d.ts" 54 | ], 55 | "static": [ 56 | "./dist/cjs/static.d.ts" 57 | ] 58 | } 59 | }, 60 | "dependencies": { 61 | "@bufbuild/protobuf": "^2.5.2", 62 | "@ydbjs/api": "6.0.0-alpha.8", 63 | "@ydbjs/error": "6.0.0-alpha.9", 64 | "@ydbjs/retry": "6.0.0-alpha.13", 65 | "nice-grpc": "^2.1.12" 66 | }, 67 | "publishConfig": { 68 | "tag": "alpha", 69 | "access": "public" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/auth/src/access-token.ts: -------------------------------------------------------------------------------- 1 | import { CredentialsProvider } from './index.js' 2 | 3 | export type AccessTokenCredentials = { 4 | // TODO: support read from file 5 | // source: 'file' | 'inline' 6 | token: string 7 | } 8 | 9 | /** 10 | * Provides access token-based authentication credentials. 11 | * 12 | * @class AccessTokenCredentialsProvider 13 | * @extends CredentialsProvider 14 | */ 15 | export class AccessTokenCredentialsProvider extends CredentialsProvider { 16 | #token: string 17 | 18 | constructor(credentials: AccessTokenCredentials) { 19 | super() 20 | this.#token = credentials.token 21 | } 22 | 23 | /** 24 | * Returns the token from the credentials. 25 | * @param force - ignored 26 | * @param signal - ignored 27 | * @returns the token 28 | */ 29 | getToken(): Promise { 30 | return Promise.resolve(this.#token) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/auth/src/accestoken.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import { AccessTokenCredentialsProvider } from "./access-token.ts"; 4 | 5 | test('valid token', async () => { 6 | let provider = new AccessTokenCredentialsProvider({ token: 'test-token' }); 7 | let token = await provider.getToken(); 8 | expect(token).eq('test-token'); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/auth/src/anonymous.ts: -------------------------------------------------------------------------------- 1 | import { CredentialsProvider } from './index.js' 2 | 3 | /** 4 | * Provides anonymous credentials for authentication. 5 | * The token returned by this provider is always an empty string. 6 | */ 7 | export class AnonymousCredentialsProvider extends CredentialsProvider { 8 | getToken(): Promise { 9 | return Promise.resolve('') 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/auth/src/dbg.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | 3 | export const dbg = debug('ydbjs').extend('auth'); 4 | -------------------------------------------------------------------------------- /packages/auth/src/index.ts: -------------------------------------------------------------------------------- 1 | import { type CallOptions, type ClientMiddlewareCall, Metadata } from 'nice-grpc' 2 | 3 | export abstract class CredentialsProvider { 4 | constructor() { 5 | // @ts-expect-error Inside middleware perform `this.getToken` call 6 | // to get the token. This is a workaround for the fact that 7 | // `this` is not bound to the class instance inside the middleware. 8 | this.middleware = this.middleware.bind(this) 9 | } 10 | 11 | abstract getToken(force?: boolean, signal?: AbortSignal): Promise 12 | 13 | readonly middleware = async function* ( 14 | this: CredentialsProvider, 15 | call: ClientMiddlewareCall, 16 | options: CallOptions 17 | ) { 18 | let token = await this.getToken(false, options.signal) 19 | 20 | return yield* call.next(call.request, { 21 | ...options, 22 | metadata: Metadata(options.metadata).set('x-ydb-auth-ticket', token), 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/auth/src/metadata.test.ts: -------------------------------------------------------------------------------- 1 | import { afterEach, expect, test, vi } from "vitest"; 2 | 3 | import { MetadataCredentialsProvider } from "./metadata.ts"; 4 | 5 | afterEach(() => { 6 | vi.restoreAllMocks() 7 | }) 8 | 9 | test('valid token', async () => { 10 | global.fetch = vi.fn(async () => { 11 | let response = new Response(JSON.stringify({ access_token: 'test-token' })) 12 | response.headers.set('Content-Type', 'application/json') 13 | 14 | return response 15 | }) 16 | 17 | let provider = new MetadataCredentialsProvider({}) 18 | 19 | let token = await provider.getToken(true) 20 | expect(token, 'Token is not empty').eq('test-token') 21 | }) 22 | 23 | test('invalid response', async () => { 24 | global.fetch = vi.fn(async () => { 25 | let response = new Response('404 Not Found', { status: 404 }) 26 | 27 | return response 28 | }) 29 | 30 | let provider = new MetadataCredentialsProvider({}) 31 | 32 | let result = provider.getToken(true, AbortSignal.timeout(100)) 33 | await expect(result).rejects.toThrow('The operation was aborted due to timeout') 34 | }) 35 | -------------------------------------------------------------------------------- /packages/auth/src/metadata.ts: -------------------------------------------------------------------------------- 1 | import { type RetryConfig, retry } from "@ydbjs/retry"; 2 | import { backoff } from "@ydbjs/retry/strategy"; 3 | 4 | import { CredentialsProvider } from "./index.js"; 5 | import { dbg } from "./dbg.js"; 6 | 7 | export type MetadataCredentialsToken = { 8 | value: string 9 | expired_at: number 10 | } 11 | 12 | export type MetadataCredentials = { 13 | endpoint?: string 14 | flavor?: string 15 | } 16 | 17 | /** 18 | * A credentials provider that retrieves tokens from a metadata service. 19 | * 20 | * This class extends the `CredentialsProvider` class and implements the `getToken` method 21 | * to fetch tokens from a specified metadata endpoint. It supports optional retry logic 22 | * and allows customization of the metadata flavor and endpoint. 23 | * 24 | * @extends CredentialsProvider 25 | */ 26 | export class MetadataCredentialsProvider extends CredentialsProvider { 27 | #promise: Promise | null = null 28 | 29 | #token: MetadataCredentialsToken | null = null 30 | #flavor: string = 'Google' 31 | #endpoint: string = 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token' 32 | 33 | /** 34 | * Creates an instance of `MetadataCredentialsProvider`. 35 | * 36 | * @param credentials - An optional object containing metadata credentials. 37 | * @param credentials.flavor - The metadata flavor (default: 'Google'). 38 | * @param credentials.endpoint - The metadata endpoint URL (default: 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'). 39 | */ 40 | constructor(credentials: MetadataCredentials = {}) { 41 | super() 42 | if (credentials.flavor) { 43 | this.#flavor = credentials.flavor 44 | } 45 | 46 | if (credentials.endpoint) { 47 | this.#endpoint = credentials.endpoint 48 | } 49 | } 50 | 51 | /** 52 | * Retrieves an authentication token from the specified endpoint. 53 | * If a valid token is already available and `force` is not true, it returns the cached token. 54 | * Otherwise, it fetches a new token with optional retry logic based on the provided configuration. 55 | * 56 | * @param force - A flag indicating whether to force fetching a new token regardless of the existing one's validity. 57 | * @param signal - An AbortSignal to cancel the operation if needed. 58 | * @returns A promise resolving to the authentication token as a string. 59 | * @throws Will throw an error if the token fetch fails, the response is not OK, or the content type is incorrect. 60 | */ 61 | async getToken(force?: boolean, signal?: AbortSignal): Promise { 62 | if (!force && this.#token && this.#token.expired_at > Date.now()) { 63 | return this.#token.value 64 | } 65 | 66 | if (this.#promise) { 67 | return this.#promise 68 | } 69 | 70 | let retryConfig: RetryConfig = { 71 | retry: (err) => (err instanceof Error), 72 | signal, 73 | budget: 5, 74 | strategy: backoff(10, 1000), 75 | } 76 | 77 | this.#promise = retry(retryConfig, async (signal) => { 78 | let response = await fetch(this.#endpoint, { 79 | headers: { 80 | 'Metadata-Flavor': this.#flavor, 81 | }, 82 | signal, 83 | }) 84 | 85 | dbg('%s %s %s', this.#endpoint, response.status, response.headers.get('Content-Type')) 86 | 87 | if (!response.ok) { 88 | throw new Error(`Failed to fetch token: ${response.status} ${response.statusText}`) 89 | } 90 | 91 | let token = JSON.parse(await response.text()) as { access_token?: string, expires_in?: number } 92 | if (!token.access_token) { 93 | dbg('missing access token in response, response=%O', token) 94 | throw new Error('No access token exists in response'); 95 | } 96 | 97 | this.#token = { 98 | value: token.access_token, 99 | expired_at: Date.now() + (token.expires_in ?? 3600) * 1000, 100 | } 101 | 102 | return this.#token.value 103 | }).finally(() => { 104 | this.#promise = null 105 | }) 106 | 107 | return this.#promise 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /packages/auth/src/static.ts: -------------------------------------------------------------------------------- 1 | import { anyUnpack } from '@bufbuild/protobuf/wkt' 2 | import { AuthServiceDefinition, LoginResultSchema } from '@ydbjs/api/auth' 3 | import { StatusIds_StatusCode } from '@ydbjs/api/operation' 4 | import { type Client, ClientError, type ClientFactory, Status, createChannel, createClientFactory } from 'nice-grpc' 5 | 6 | import { CredentialsProvider } from './index.js' 7 | import { defaultRetryConfig, retry } from '@ydbjs/retry' 8 | import { YDBError } from '@ydbjs/error' 9 | 10 | export type StaticCredentialsToken = { 11 | value: string 12 | aud: string[] 13 | exp: number 14 | iat: number 15 | sub: string 16 | } 17 | 18 | export type StaticCredentials = { 19 | // TODO: support read from file 20 | // source: 'file' | 'inline' 21 | username: string 22 | password: string 23 | } 24 | 25 | /** 26 | * A credentials provider that uses static username and password to authenticate. 27 | * It fetches and caches a token from the specified authentication service. 28 | * 29 | * @extends CredentialsProvider 30 | */ 31 | export class StaticCredentialsProvider extends CredentialsProvider { 32 | #client: Client 33 | #promise: Promise | null = null 34 | 35 | #token: StaticCredentialsToken | null = null 36 | #username: string 37 | #password: string 38 | 39 | constructor( 40 | credentials: StaticCredentials, 41 | endpoint: string, 42 | clientFactory: ClientFactory = createClientFactory() 43 | ) { 44 | super() 45 | this.#username = credentials.username 46 | this.#password = credentials.password 47 | 48 | let cs = new URL(endpoint.replace(/^grpc/, 'http')) 49 | this.#client = clientFactory.create(AuthServiceDefinition, createChannel(cs.origin)) 50 | } 51 | 52 | /** 53 | * Returns the token from the credentials. 54 | * @param force - if true, forces a new token to be fetched 55 | * @param signal - an optional AbortSignal to cancel the request 56 | * @returns the token 57 | */ 58 | async getToken(force = false, signal?: AbortSignal): Promise { 59 | if (!force && this.#token && this.#token.exp > Date.now() / 1000) { 60 | return this.#token.value 61 | } 62 | 63 | if (this.#promise) { 64 | return this.#promise 65 | } 66 | 67 | this.#promise = retry({ ...defaultRetryConfig, signal, idempotent: true }, async () => { 68 | let response = await this.#client.login({ user: this.#username, password: this.#password }, { signal }) 69 | 70 | if (!response.operation) { 71 | throw new ClientError(AuthServiceDefinition.login.path, Status.UNKNOWN, 'No operation in response') 72 | } 73 | 74 | if (response.operation.status !== StatusIds_StatusCode.SUCCESS) { 75 | throw new YDBError(response.operation.status, response.operation.issues) 76 | } 77 | 78 | let result = anyUnpack(response.operation.result!, LoginResultSchema) 79 | if (!result) { 80 | throw new ClientError(AuthServiceDefinition.login.path, Status.UNKNOWN, 'No result in operation') 81 | } 82 | 83 | // Parse the JWT token to extract expiration time 84 | const [, payload] = result.token.split('.') 85 | const decodedPayload = JSON.parse(Buffer.from(payload, 'base64').toString()) 86 | 87 | this.#token = { 88 | value: result.token, 89 | ...decodedPayload, 90 | } 91 | 92 | return this.#token!.value! 93 | }).finally(() => { 94 | this.#promise = null 95 | }) 96 | 97 | return this.#promise 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /packages/auth/tests/static.e2e.test.ts: -------------------------------------------------------------------------------- 1 | import { afterEach, describe, expect, inject, test, vi } from 'vitest' 2 | import { createClientFactory } from 'nice-grpc' 3 | 4 | import { StaticCredentialsProvider } from '../dist/esm/static.js' 5 | 6 | let username = inject('credentialsUsername') 7 | let password = inject('credentialsPassword') 8 | let endpoint = inject('credentialsEndpoint') 9 | 10 | describe('StaticCredentialsProvider', async () => { 11 | let calls = 0 12 | let clientFactory = createClientFactory().use((call, options) => { 13 | calls++ 14 | return call.next(call.request, options) 15 | }) 16 | 17 | afterEach(() => { 18 | calls = 0 19 | }) 20 | 21 | test('valid token', async () => { 22 | let provider = new StaticCredentialsProvider({ username, password }, endpoint, clientFactory) 23 | 24 | let token = await provider.getToken(false) 25 | expect(token, 'Token is not empty').not.empty 26 | }) 27 | 28 | test('reuse token', async () => { 29 | let provider = new StaticCredentialsProvider({ username, password }, endpoint, clientFactory) 30 | 31 | let token = await provider.getToken(false) 32 | let token2 = await provider.getToken(false) 33 | 34 | expect(token, 'Token is the same').eq(token2) 35 | expect(calls, 'Only one call was made').eq(1) 36 | }) 37 | 38 | test('force refresh token', async () => { 39 | let provider = new StaticCredentialsProvider({ username, password }, endpoint, clientFactory) 40 | 41 | let token = await provider.getToken(false) 42 | let token2 = await provider.getToken(true) 43 | 44 | expect(token, 'Token is different').not.eq(token2) 45 | expect(calls, 'Two calls were made').eq(2) 46 | }) 47 | 48 | test('auto refresh expired token', async () => { 49 | let provider = new StaticCredentialsProvider({ username, password }, endpoint, clientFactory) 50 | 51 | let token = await provider.getToken(false) 52 | vi.useFakeTimers({ now: new Date(2100, 0, 1) }) 53 | let token2 = await provider.getToken(false) 54 | vi.useRealTimers() 55 | 56 | expect(token, 'Token is different').not.eq(token2) 57 | expect(calls, 'Two calls were made').eq(2) 58 | }) 59 | 60 | test('multiple token aquisition', async () => { 61 | let provider = new StaticCredentialsProvider({ username, password }, endpoint, clientFactory) 62 | 63 | let tokens = await Promise.all([provider.getToken(false), provider.getToken(false), provider.getToken(false)]) 64 | 65 | expect(new Set(tokens).size, 'All tokens are the same').eq(1) 66 | expect(calls, 'Only one call was made').eq(1) 67 | }) 68 | 69 | test('abort token aquisition', async () => { 70 | let provider = new StaticCredentialsProvider({ username, password }, endpoint, clientFactory) 71 | 72 | let controller = new AbortController() 73 | controller.abort() 74 | let token = provider.getToken(false, controller.signal) 75 | 76 | await expect(() => token, 'Token aquisition was canceled').rejects.toThrow('This operation was aborted') 77 | }) 78 | 79 | test('timeout token aquisition', async () => { 80 | let provider = new StaticCredentialsProvider({ username, password }, endpoint, clientFactory) 81 | 82 | let token = provider.getToken(false, AbortSignal.timeout(0)) 83 | 84 | await expect(() => token, 'Token aquisition was canceled').rejects.toThrow('The operation has been aborted') 85 | }) 86 | }) 87 | -------------------------------------------------------------------------------- /packages/auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "exclude": [ 7 | "vitest.config.ts", 8 | "**/*.test.ts", 9 | "dist/**/*" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/auth/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineProject } from 'vitest/config' 2 | 3 | export default defineProject({ 4 | test: { 5 | exclude: ['**/*.e2e.test.ts'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/core/.gitignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | 3 | /src 4 | /tests 5 | /plugins 6 | 7 | /*.yaml 8 | /*.json 9 | -------------------------------------------------------------------------------- /packages/core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/core 2 | 3 | ## 6.0.0-alpha.18 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies 8 | - @ydbjs/error@6.0.0-alpha.9 9 | - @ydbjs/auth@6.0.0-alpha.15 10 | - @ydbjs/retry@6.0.0-alpha.13 11 | 12 | ## 6.0.0-alpha.17 13 | 14 | ### Patch Changes 15 | 16 | - Fix build cjs issues 17 | - Updated dependencies 18 | - @ydbjs/error@6.0.0-alpha.8 19 | - @ydbjs/retry@6.0.0-alpha.12 20 | - @ydbjs/auth@6.0.0-alpha.14 21 | - @ydbjs/api@6.0.0-alpha.8 22 | 23 | ## 6.0.0-alpha.16 24 | 25 | ### Patch Changes 26 | 27 | - Support for database in querystring 28 | 29 | ## 6.0.0-alpha.15 30 | 31 | ### Patch Changes 32 | 33 | - Fix build issues 34 | 35 | ## 6.0.0-alpha.14 36 | 37 | ### Patch Changes 38 | 39 | - Fix driver options parsing 40 | 41 | ## 6.0.0-alpha.13 42 | 43 | ### Patch Changes 44 | 45 | - correct typo in credentialsProvider assignment in Driver class 46 | 47 | ## 6.0.0-alpha.12 48 | 49 | ### Patch Changes 50 | 51 | - Fix metadata credentials provider 52 | - Updated dependencies 53 | - @ydbjs/auth@6.0.0-alpha.12 54 | 55 | ## 6.0.0-alpha.11 56 | 57 | ### Patch Changes 58 | 59 | - Updated dependencies 60 | - @ydbjs/retry@6.0.0-alpha.10 61 | - @ydbjs/auth@6.0.0-alpha.11 62 | 63 | ## 6.0.0-alpha.10 64 | 65 | ### Patch Changes 66 | 67 | - Update packages description 68 | - Updated dependencies 69 | - @ydbjs/error@6.0.0-alpha.7 70 | - @ydbjs/retry@6.0.0-alpha.9 71 | - @ydbjs/auth@6.0.0-alpha.10 72 | - @ydbjs/api@6.0.0-alpha.7 73 | 74 | ## 6.0.0-alpha.9 75 | 76 | ### Patch Changes 77 | 78 | - Consolidated and clarified README files across all packages. 79 | - Updated dependencies 80 | - @ydbjs/api@6.0.0-alpha.6 81 | - @ydbjs/auth@6.0.0-alpha.9 82 | - @ydbjs/error@6.0.0-alpha.6 83 | - @ydbjs/retry@6.0.0-alpha.8 84 | 85 | ## 6.0.0-alpha.8 86 | 87 | ### Patch Changes 88 | 89 | - Refactored async functions and concurrency handling for better performance. 90 | - Expanded configuration options and documentation. 91 | - Updated README for clarity and structure. 92 | 93 | ## 6.0.0-alpha.7 94 | 95 | ### Patch Changes 96 | 97 | - Updated dependencies 98 | - @ydbjs/retry@6.0.0-alpha.7 99 | - @ydbjs/auth@6.0.0-alpha.7 100 | 101 | ## 6.0.0-alpha.6 102 | 103 | ### Patch Changes 104 | 105 | - Add transaction support 106 | - Updated dependencies 107 | - @ydbjs/api@6.0.0-alpha.5 108 | - @ydbjs/auth@6.0.0-alpha.6 109 | - @ydbjs/error@6.0.0-alpha.5 110 | - @ydbjs/retry@6.0.0-alpha.6 111 | 112 | ## 6.0.0-alpha.5 113 | 114 | ### Patch Changes 115 | 116 | - Updated dependencies 117 | - @ydbjs/retry@6.0.0-alpha.5 118 | - @ydbjs/auth@6.0.0-alpha.5 119 | 120 | ## 6.0.0-alpha.4 121 | 122 | ### Patch Changes 123 | 124 | - Added support to ignore the `.turbo` folder for better compatibility and cleaner workflows. 125 | - Updated dependencies 126 | - @ydbjs/api@6.0.0-alpha.4 127 | - @ydbjs/auth@6.0.0-alpha.4 128 | - @ydbjs/error@6.0.0-alpha.4 129 | - @ydbjs/retry@6.0.0-alpha.4 130 | 131 | ## 6.0.0-alpha.3 132 | 133 | ### Patch Changes 134 | 135 | - Update usage examples 136 | - Updated dependencies 137 | - @ydbjs/api@6.0.0-alpha.3 138 | - @ydbjs/auth@6.0.0-alpha.3 139 | - @ydbjs/error@6.0.0-alpha.3 140 | - @ydbjs/retry@6.0.0-alpha.3 141 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/core 2 | 3 | The `@ydbjs/core` package provides the core driver and connection management for YDB in JavaScript/TypeScript. It is the foundation for all YDB client operations, handling connection pooling, service client creation, authentication, and middleware. 4 | 5 | ## Features 6 | 7 | - Connection pooling and load balancing for YDB endpoints 8 | - Service client creation for any YDB gRPC API 9 | - Pluggable authentication via `@ydbjs/auth` providers 10 | - Automatic endpoint discovery and failover 11 | - TypeScript support with type definitions 12 | - Compatible with Node.js and modern runtimes 13 | 14 | ## Installation 15 | 16 | ```sh 17 | npm install @ydbjs/core@alpha 18 | ``` 19 | 20 | ## How It Works 21 | 22 | - **Driver**: The main entry point. Manages connections, endpoint discovery, and authentication. 23 | - **Connection Pool**: Maintains and balances gRPC channels to YDB endpoints. 24 | - **Service Clients**: Use `driver.createClient(ServiceDefinition)` to get a typed client for any YDB gRPC service (from `@ydbjs/api`). 25 | - **Authentication**: Pass a credentials provider from `@ydbjs/auth` to the driver for static, token, anonymous, or cloud metadata authentication. 26 | - **Middleware**: Internal middleware handles metadata, authentication, and debugging. 27 | 28 | ## Usage 29 | 30 | ### Basic Example 31 | 32 | ```ts 33 | import { Driver } from '@ydbjs/core'; 34 | import { DiscoveryServiceDefinition } from '@ydbjs/api/discovery'; 35 | 36 | const driver = new Driver('grpc://localhost:2136/local'); 37 | await driver.ready(); 38 | 39 | const discovery = driver.createClient(DiscoveryServiceDefinition); 40 | const endpoints = await discovery.listEndpoints({ database: '/local' }); 41 | console.log(endpoints); 42 | 43 | await driver.close(); 44 | ``` 45 | 46 | ### Using Authentication Providers 47 | 48 | ```ts 49 | import { Driver } from '@ydbjs/core'; 50 | import { StaticCredentialsProvider } from '@ydbjs/auth/static'; 51 | 52 | const driver = new Driver('grpc://localhost:2136/local', { 53 | credentialsProvider: new StaticCredentialsProvider({ 54 | username: 'user', 55 | password: 'pass', 56 | }), 57 | }); 58 | await driver.ready(); 59 | // ... 60 | ``` 61 | 62 | You can also use `AccessTokenCredentialsProvider`, `AnonymousCredentialsProvider`, or `MetadataCredentialsProvider` from `@ydbjs/auth`. 63 | 64 | ### Closing the Driver 65 | 66 | Always close the driver when done to release resources: 67 | 68 | ```ts 69 | driver.close(); 70 | ``` 71 | 72 | ## Development 73 | 74 | ### Building the Package 75 | 76 | ```sh 77 | npm run build 78 | ``` 79 | 80 | ### Running Tests 81 | 82 | ```sh 83 | npm test 84 | ``` 85 | 86 | For watch mode during development: 87 | 88 | ```sh 89 | npm run test:watch 90 | ``` 91 | 92 | ## License 93 | 94 | This project is licensed under the [Apache 2.0 License](../../LICENSE). 95 | 96 | ## Links 97 | 98 | - [YDB Documentation](https://ydb.tech) 99 | - [GitHub Repository](https://github.com/ydb-platform/ydb-js-sdk) 100 | - [Issues](https://github.com/ydb-platform/ydb-js-sdk/issues) 101 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ydbjs/core", 3 | "version": "6.0.0-alpha.18", 4 | "description": "Core driver for YDB: manages connections, endpoint discovery, authentication, and service client creation. Foundation for all YDB client operations.", 5 | "license": "Apache-2.0", 6 | "type": "module", 7 | "sideEffects": false, 8 | "module": "dist/esm/index.js", 9 | "main": "dist/cjs/index.js", 10 | "scripts": { 11 | "clean": "rm -rf dist", 12 | "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", 13 | "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm", 14 | "test": "vitest --run", 15 | "test:watch": "vitest --watch", 16 | "attw": "attw --pack" 17 | }, 18 | "exports": { 19 | ".": { 20 | "require": "./dist/cjs/index.js", 21 | "import": "./dist/esm/index.js" 22 | } 23 | }, 24 | "types": "dist/esm/index.d.ts", 25 | "dependencies": { 26 | "@bufbuild/protobuf": "^2.5.2", 27 | "@ydbjs/api": "6.0.0-alpha.8", 28 | "@ydbjs/auth": "^6.0.0-alpha.15", 29 | "@ydbjs/error": "6.0.0-alpha.9", 30 | "@ydbjs/retry": "6.0.0-alpha.13", 31 | "debug": "^4.4.0", 32 | "nice-grpc": "^2.1.12" 33 | }, 34 | "devDependencies": { 35 | "@types/debug": "^4.1.12" 36 | }, 37 | "publishConfig": { 38 | "tag": "alpha", 39 | "access": "public" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/src/conn.ts: -------------------------------------------------------------------------------- 1 | import type { EndpointInfo } from '@ydbjs/api/discovery' 2 | import { 3 | type Channel, 4 | type ChannelCredentials, 5 | type ChannelOptions, 6 | createChannel, 7 | } from 'nice-grpc' 8 | 9 | import { dbg } from './dbg.ts' 10 | 11 | export interface Connection { 12 | readonly nodeId: bigint 13 | readonly address: string 14 | readonly channel: Channel 15 | pessimizedUntil?: number 16 | 17 | close(): void 18 | } 19 | 20 | export class LazyConnection implements Connection { 21 | #endpoint: EndpointInfo 22 | #channel: Channel | null = null 23 | #channelOptions: ChannelOptions 24 | #channelCredentials: ChannelCredentials 25 | pessimizedUntil?: number 26 | 27 | constructor(endpoint: EndpointInfo, channelCredentials: ChannelCredentials, channelOptions?: ChannelOptions) { 28 | this.#endpoint = endpoint 29 | 30 | this.#channelOptions = { 31 | ...channelOptions, 32 | 'grpc.ssl_target_name_override': endpoint.sslTargetNameOverride, 33 | } 34 | this.#channelCredentials = channelCredentials 35 | } 36 | 37 | get nodeId(): bigint { 38 | return BigInt(this.#endpoint.nodeId) 39 | } 40 | 41 | get address(): string { 42 | return `${this.#endpoint.address}:${this.#endpoint.port}` 43 | } 44 | 45 | get channel(): Channel { 46 | if (this.#channel === null) { 47 | dbg.extend('conn')('create channel to node id=%d address=%s', this.nodeId, this.address) 48 | 49 | this.#channel = createChannel(this.address, this.#channelCredentials, this.#channelOptions) 50 | } 51 | 52 | return this.#channel 53 | } 54 | 55 | close() { 56 | if (this.#channel) { 57 | dbg.extend('conn')('close channel to node id=%d address=%s', this.nodeId, this.address) 58 | this.#channel.close() 59 | this.#channel = null 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/core/src/dbg.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | 3 | export const dbg = debug('ydbjs'); 4 | -------------------------------------------------------------------------------- /packages/core/src/driver.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest' 2 | 3 | import { Driver } from './driver.ts' 4 | 5 | test('database in pathname', async () => { 6 | let driver = new Driver('grpc://localhost:1234/path', { 7 | 'ydb.sdk.enable_discovery': false, 8 | }) 9 | 10 | expect(driver.database, 'Database is not set').toBe('/path') 11 | }) 12 | 13 | test('database in querystring', async () => { 14 | let driver = new Driver('grpc://localhost:1234?database=/query', { 15 | 'ydb.sdk.enable_discovery': false, 16 | }) 17 | 18 | expect(driver.database, 'Database is not set').toBe('/query') 19 | }) 20 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './driver.js' 2 | -------------------------------------------------------------------------------- /packages/core/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { isAbortError } from 'abort-controller-x'; 2 | import { ClientError, type ClientMiddleware } from "nice-grpc" 3 | 4 | import { dbg } from "./dbg.js" 5 | 6 | let log = dbg.extend('grpc') 7 | 8 | export const debug: ClientMiddleware = async function* (call, options) { 9 | let hasError = false 10 | try { 11 | return yield* call.next(call.request, options) 12 | } catch (error) { 13 | hasError = true 14 | if (error instanceof ClientError) { 15 | log('%s', error.message) 16 | } else if (isAbortError(error)) { 17 | log('%s %s: %s', call.method.path, 'CANCELLED', error.message) 18 | } else { 19 | log('%s %s: %s', call.method.path, 'UNKNOWN', error) 20 | } 21 | 22 | throw error 23 | } finally { 24 | if (!hasError) { 25 | log('%s %s', call.method.path, 'OK') 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/tests/driver.e2e.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, inject, test } from 'vitest' 2 | 3 | import { Driver } from '../dist/esm/driver.js' 4 | 5 | test('driver ready', async () => { 6 | let driver = new Driver(inject('connectionString'), { 7 | 'ydb.sdk.discovery_timeout_ms': 1000, 8 | }) 9 | 10 | expect(() => driver.ready(), 'Driver is not ready').not.throw() 11 | }) 12 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "exclude": [ 7 | "vitest.config.ts", 8 | "**/*.test.ts", 9 | "dist/**/*" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineProject } from 'vitest/config' 2 | 3 | export default defineProject({ 4 | test: { 5 | exclude: ['**/*.e2e.test.ts'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/error/.gitignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | -------------------------------------------------------------------------------- /packages/error/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | 3 | /src 4 | /tests 5 | /plugins 6 | 7 | /*.yaml 8 | /*.json 9 | -------------------------------------------------------------------------------- /packages/error/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/error 2 | 3 | ## 6.0.0-alpha.9 4 | 5 | ### Patch Changes 6 | 7 | - Print ydb error issues 8 | 9 | ## 6.0.0-alpha.8 10 | 11 | ### Patch Changes 12 | 13 | - Fix build cjs issues 14 | - Updated dependencies 15 | - @ydbjs/api@6.0.0-alpha.8 16 | 17 | ## 6.0.0-alpha.7 18 | 19 | ### Patch Changes 20 | 21 | - Update packages description 22 | - Updated dependencies 23 | - @ydbjs/api@6.0.0-alpha.7 24 | 25 | ## 6.0.0-alpha.6 26 | 27 | ### Patch Changes 28 | 29 | - Consolidated and clarified README files across all packages. 30 | - Updated dependencies 31 | - @ydbjs/api@6.0.0-alpha.6 32 | 33 | ## 6.0.0-alpha.5 34 | 35 | ### Patch Changes 36 | 37 | - Add transaction support 38 | - Updated dependencies 39 | - @ydbjs/api@6.0.0-alpha.5 40 | 41 | ## 6.0.0-alpha.4 42 | 43 | ### Patch Changes 44 | 45 | - Added support to ignore the `.turbo` folder for better compatibility and cleaner workflows. 46 | - Updated dependencies 47 | - @ydbjs/api@6.0.0-alpha.4 48 | 49 | ## 6.0.0-alpha.3 50 | 51 | ### Patch Changes 52 | 53 | - Update usage examples 54 | - Updated dependencies 55 | - @ydbjs/api@6.0.0-alpha.3 56 | -------------------------------------------------------------------------------- /packages/error/README.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/error 2 | 3 | The `@ydbjs/error` package provides utilities for handling YDB-specific errors in JavaScript/TypeScript applications. It simplifies error classification and provides detailed error messages for better debugging and troubleshooting. 4 | 5 | ## Features 6 | 7 | - Error classification for YDB-specific error codes 8 | - Detailed error messages with severity levels 9 | - TypeScript support with type definitions 10 | 11 | ## Installation 12 | 13 | Install the package using npm: 14 | 15 | ```sh 16 | npm install @ydbjs/error@6.0.0-alpha.2 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### Handling YDB Errors 22 | 23 | ```ts 24 | import { YDBError } from '@ydbjs/error'; 25 | import { StatusIds_StatusCode } from '@ydbjs/api/operation'; 26 | 27 | try { 28 | throw new YDBError(StatusIds_StatusCode.ABORTED, [ 29 | { severity: 0, issueCode: 14, message: 'Some error message' }, 30 | ]); 31 | } catch (error) { 32 | if (error instanceof YDBError) { 33 | console.error('YDB Error:', error.message); 34 | console.error('Error Code:', error.code); 35 | } 36 | } 37 | ``` 38 | 39 | ## Development 40 | 41 | ### Building the Package 42 | 43 | ```sh 44 | npm run build 45 | ``` 46 | 47 | ### Running Tests 48 | 49 | ```sh 50 | npm test 51 | ``` 52 | 53 | For watch mode during development: 54 | 55 | ```sh 56 | npm run test:watch 57 | ``` 58 | 59 | ## License 60 | 61 | This project is licensed under the [Apache 2.0 License](../../LICENSE). 62 | 63 | ## Links 64 | 65 | - [YDB Documentation](https://ydb.tech) 66 | - [GitHub Repository](https://github.com/ydb-platform/ydb-js-sdk) 67 | - [Issues](https://github.com/ydb-platform/ydb-js-sdk/issues) 68 | -------------------------------------------------------------------------------- /packages/error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ydbjs/error", 3 | "version": "6.0.0-alpha.9", 4 | "description": "Error utilities for YDB: error classification, rich messages, and TypeScript support for robust error handling in YDB applications.", 5 | "license": "Apache-2.0", 6 | "type": "module", 7 | "sideEffects": false, 8 | "module": "dist/esm/index.js", 9 | "main": "dist/cjs/index.js", 10 | "scripts": { 11 | "clean": "rm -rf dist", 12 | "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", 13 | "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm", 14 | "test": "vitest --run", 15 | "test:watch": "vitest --watch", 16 | "attw": "attw --pack" 17 | }, 18 | "exports": { 19 | ".": { 20 | "require": "./dist/cjs/index.js", 21 | "import": "./dist/esm/index.js" 22 | } 23 | }, 24 | "types": "dist/esm/index.d.ts", 25 | "dependencies": { 26 | "@bufbuild/protobuf": "^2.5.2", 27 | "@ydbjs/api": "6.0.0-alpha.8" 28 | }, 29 | "publishConfig": { 30 | "tag": "alpha", 31 | "access": "public" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/error/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest' 2 | 3 | import { create } from '@bufbuild/protobuf' 4 | import { IssueMessageSchema, StatusIds_StatusCode } from '@ydbjs/api/operation' 5 | import { CommitError, YDBError } from './index.ts' 6 | 7 | test('single issue', () => { 8 | let error = new YDBError(StatusIds_StatusCode.ABORTED, [ 9 | create(IssueMessageSchema, { 10 | severity: 1, 11 | issueCode: 1030, 12 | message: 'Type annotation', 13 | issues: [ 14 | create(IssueMessageSchema, { 15 | severity: 1, 16 | issueCode: 0, 17 | message: 'At function: KiWriteTable!', 18 | issues: [ 19 | create(IssueMessageSchema, { 20 | severity: 0, 21 | issueCode: 1, 22 | message: `Failed to convert type: Struct<'created_at':Int32> to Struct<'created_at':Uint32>`, 23 | }), 24 | create(IssueMessageSchema, { 25 | severity: 0, 26 | issueCode: 2031, 27 | message: `Failed to convert input columns types to scheme types`, 28 | }), 29 | ], 30 | }), 31 | ], 32 | }), 33 | ]) 34 | 35 | expect(error.code).eq(StatusIds_StatusCode.ABORTED) 36 | expect(error.message).eq('ABORTED, Issues: ERROR(1030): Type annotation') 37 | }) 38 | 39 | test('multiple issues', () => { 40 | let error = new YDBError(StatusIds_StatusCode.ABORTED, [ 41 | create(IssueMessageSchema, { 42 | severity: 0, 43 | issueCode: 14, 44 | message: 'Some error message', 45 | }), 46 | create(IssueMessageSchema, { 47 | severity: 1, 48 | issueCode: 15, 49 | message: 'Another error message', 50 | }), 51 | ]) 52 | 53 | expect(error.code).eq(StatusIds_StatusCode.ABORTED) 54 | expect(error.message).eq('ABORTED, Issues: FATAL(14): Some error message; ERROR(15): Another error message') 55 | }) 56 | 57 | test('commit error', () => { 58 | let error = new CommitError('Commit failed', new YDBError(StatusIds_StatusCode.ABORTED, [])) 59 | 60 | expect(error.message).eq('Commit failed') 61 | }) 62 | -------------------------------------------------------------------------------- /packages/error/src/index.ts: -------------------------------------------------------------------------------- 1 | import { type MessageJsonType, toJson } from '@bufbuild/protobuf' 2 | import { type IssueMessage, IssueMessageSchema, StatusIds_StatusCode } from '@ydbjs/api/operation' 3 | 4 | export class YDBIssue { 5 | constructor( 6 | public code: number, 7 | public message: string, 8 | public severity: number, 9 | public issues: YDBIssue[] = [] 10 | ) { } 11 | 12 | static severity: Record = { 13 | 0: "FATAL", 14 | 1: "ERROR", 15 | 2: "WARNING", 16 | 3: "INFO", 17 | } as const 18 | 19 | static fromIssueMessage(issue: IssueMessage): YDBIssue { 20 | return new YDBIssue( 21 | issue.issueCode, 22 | issue.message, 23 | issue.severity, 24 | issue.issues.map(YDBIssue.fromIssueMessage) 25 | ) 26 | } 27 | 28 | toString(): string { 29 | return `${YDBIssue.severity[this.severity]}(${this.code}): ${this.message}` 30 | } 31 | 32 | [Symbol.toStringTag]() { 33 | return this.toString() 34 | } 35 | } 36 | 37 | export class YDBError extends Error { 38 | readonly code: StatusIds_StatusCode 39 | readonly issues: MessageJsonType[] 40 | 41 | constructor(code: StatusIds_StatusCode, issues: IssueMessage[]) { 42 | super(`${YDBError.codes[code]}` + (issues.length ? `, Issues: ${issues.map(YDBIssue.fromIssueMessage).join('; ')}` : '')) 43 | this.code = code 44 | this.issues = issues.map((issue) => toJson(IssueMessageSchema, issue as IssueMessage)) 45 | } 46 | 47 | static codes: Record = { 48 | [StatusIds_StatusCode.STATUS_CODE_UNSPECIFIED]: "UNSPECIFIED", 49 | [StatusIds_StatusCode.SUCCESS]: "SUCCESS", 50 | [StatusIds_StatusCode.BAD_REQUEST]: "BAD_REQUEST", 51 | [StatusIds_StatusCode.UNAUTHORIZED]: "UNAUTHORIZED", 52 | [StatusIds_StatusCode.INTERNAL_ERROR]: "INTERNAL_ERROR", 53 | [StatusIds_StatusCode.ABORTED]: "ABORTED", 54 | [StatusIds_StatusCode.UNAVAILABLE]: "UNAVAILABLE", 55 | [StatusIds_StatusCode.OVERLOADED]: "OVERLOADED", 56 | [StatusIds_StatusCode.SCHEME_ERROR]: "SCHEME_ERROR", 57 | [StatusIds_StatusCode.GENERIC_ERROR]: "GENERIC_ERROR", 58 | [StatusIds_StatusCode.TIMEOUT]: "TIMEOUT", 59 | [StatusIds_StatusCode.BAD_SESSION]: "BAD_SESSION", 60 | [StatusIds_StatusCode.PRECONDITION_FAILED]: "PRECONDITION_FAILED", 61 | [StatusIds_StatusCode.ALREADY_EXISTS]: "ALREADY_EXISTS", 62 | [StatusIds_StatusCode.NOT_FOUND]: "NOT_FOUND", 63 | [StatusIds_StatusCode.SESSION_EXPIRED]: "SESSION_EXPIRED", 64 | [StatusIds_StatusCode.CANCELLED]: "CANCELLED", 65 | [StatusIds_StatusCode.UNDETERMINED]: "UNDETERMINED", 66 | [StatusIds_StatusCode.UNSUPPORTED]: "UNSUPPORTED", 67 | [StatusIds_StatusCode.SESSION_BUSY]: "SESSION_BUSY", 68 | [StatusIds_StatusCode.EXTERNAL_ERROR]: "EXTERNAL_ERROR" 69 | } as const 70 | 71 | get retryable(): boolean | 'conditionally' { 72 | if ([ 73 | StatusIds_StatusCode.ABORTED, 74 | StatusIds_StatusCode.OVERLOADED, 75 | StatusIds_StatusCode.UNAVAILABLE, 76 | StatusIds_StatusCode.BAD_SESSION, 77 | StatusIds_StatusCode.SESSION_BUSY, 78 | ].includes(this.code)) { 79 | return true 80 | } 81 | 82 | if ([ 83 | StatusIds_StatusCode.SESSION_EXPIRED, 84 | StatusIds_StatusCode.UNDETERMINED, 85 | StatusIds_StatusCode.TIMEOUT 86 | ].includes(this.code)) { 87 | return 'conditionally' 88 | } 89 | 90 | return false 91 | } 92 | } 93 | 94 | export class CommitError extends Error { 95 | constructor(message: string, public override cause?: unknown) { 96 | super(message) 97 | } 98 | 99 | retryable(idempotent: boolean = false): boolean { 100 | return (this.cause instanceof YDBError && this.cause.retryable === true) 101 | || (this.cause instanceof YDBError && this.cause.retryable === "conditionally" && idempotent) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packages/error/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "exclude": [ 7 | "vitest.config.ts", 8 | "**/*.test.ts", 9 | "dist/**/*" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/error/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineProject } from 'vitest/config' 2 | 3 | export default defineProject({ 4 | test: { 5 | exclude: ['**/*.e2e.test.ts'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/query/.gitignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | -------------------------------------------------------------------------------- /packages/query/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | 3 | /src 4 | /tests 5 | /plugins 6 | 7 | /*.yaml 8 | /*.json 9 | -------------------------------------------------------------------------------- /packages/query/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ydbjs/query", 3 | "version": "6.0.0-alpha.25", 4 | "description": "High-level, type-safe YQL query and transaction client for YDB. Supports tagged template syntax, parameter binding, transactions, and statistics.", 5 | "license": "Apache-2.0", 6 | "type": "module", 7 | "sideEffects": false, 8 | "module": "dist/esm/index.js", 9 | "main": "dist/cjs/index.js", 10 | "scripts": { 11 | "clean": "rm -rf dist", 12 | "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", 13 | "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm", 14 | "test": "vitest --run", 15 | "test:watch": "vitest --watch", 16 | "attw": "attw --pack" 17 | }, 18 | "exports": { 19 | ".": { 20 | "require": "./dist/cjs/index.js", 21 | "import": "./dist/esm/index.js" 22 | }, 23 | "./yql": { 24 | "require": "./dist/cjs/yql.js", 25 | "import": "./dist/esm/yql.js" 26 | } 27 | }, 28 | "types": "dist/esm/index.d.ts", 29 | "typesVersions": { 30 | "*": { 31 | ".": [ 32 | "./dist/cjs/index.d.ts" 33 | ], 34 | "yql": [ 35 | "./dist/cjs/yql.d.ts" 36 | ] 37 | } 38 | }, 39 | "dependencies": { 40 | "@bufbuild/protobuf": "^2.5.2", 41 | "@ydbjs/api": "6.0.0-alpha.8", 42 | "@ydbjs/core": "6.0.0-alpha.18", 43 | "@ydbjs/error": "6.0.0-alpha.9", 44 | "@ydbjs/retry": "6.0.0-alpha.13", 45 | "@ydbjs/value": "6.0.0-alpha.12", 46 | "debug": "^4.4.0", 47 | "nice-grpc": "^2.1.12" 48 | }, 49 | "peerDependencies": { 50 | "@ydbjs/api": "6.0.0-alpha.8", 51 | "@ydbjs/core": "6.0.0-alpha.18" 52 | }, 53 | "devDependencies": { 54 | "@types/debug": "^4.1.12" 55 | }, 56 | "publishConfig": { 57 | "tag": "alpha", 58 | "access": "public" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/query/src/ctx.ts: -------------------------------------------------------------------------------- 1 | import { AsyncLocalStorage } from "node:async_hooks"; 2 | import type { Abortable } from "node:events"; 3 | 4 | type Context = Abortable & { 5 | nodeId?: bigint; 6 | sessionId?: string; 7 | transactionId?: string; 8 | } 9 | 10 | export const ctx = new AsyncLocalStorage() 11 | -------------------------------------------------------------------------------- /packages/query/src/yql.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest' 2 | 3 | import { Int32 } from '@ydbjs/value/primitive' 4 | import { identifier, yql } from './yql.ts' 5 | 6 | test('string', () => { 7 | let { text } = yql`SELECT 1;` 8 | 9 | expect(text).eq('SELECT 1;') 10 | }) 11 | 12 | test('string with js value as parameter', () => { 13 | let { text, params } = yql`SELECT ${1};` 14 | 15 | expect(text).eq('SELECT $p0;') 16 | expect(params).toMatchInlineSnapshot(` 17 | { 18 | "$p0": Int32 { 19 | "type": Int32Type {}, 20 | "value": 1, 21 | }, 22 | } 23 | `) 24 | }) 25 | 26 | test('string with ydb value as parameter', () => { 27 | let { text, params } = yql`SELECT ${new Int32(1)};` 28 | 29 | expect(text).eq('SELECT $p0;') 30 | expect(params).toMatchInlineSnapshot(` 31 | { 32 | "$p0": Int32 { 33 | "type": Int32Type {}, 34 | "value": 1, 35 | }, 36 | } 37 | `) 38 | }) 39 | 40 | test('string with parameters and identifiers', () => { 41 | let { text, params } = yql`FROM ${identifier('my_table')}.${identifier('my_column')} SELECT ${1}, ${2};` 42 | 43 | expect(text).eq('FROM `my_table`.`my_column` SELECT $p0, $p1;') 44 | expect(params).toMatchInlineSnapshot(` 45 | { 46 | "$p0": Int32 { 47 | "type": Int32Type {}, 48 | "value": 1, 49 | }, 50 | "$p1": Int32 { 51 | "type": Int32Type {}, 52 | "value": 2, 53 | }, 54 | } 55 | `) 56 | }) 57 | 58 | test(`string with falsy parameters`, () => { 59 | let { text, params } = yql`SELECT * FROM table WHERE str = ${""} AND int = ${0} AND int64 = ${0n} AND bool = ${false};` 60 | 61 | expect(text).eq('SELECT * FROM table WHERE str = $p0 AND int = $p1 AND int64 = $p2 AND bool = $p3;') 62 | expect(params).toMatchInlineSnapshot(` 63 | { 64 | "$p0": Text { 65 | "type": TextType {}, 66 | "value": "", 67 | }, 68 | "$p1": Int32 { 69 | "type": Int32Type {}, 70 | "value": 0, 71 | }, 72 | "$p2": Int64 { 73 | "type": Int64Type {}, 74 | "value": 0n, 75 | }, 76 | "$p3": Bool { 77 | "type": BoolType {}, 78 | "value": false, 79 | }, 80 | } 81 | `) 82 | }) 83 | -------------------------------------------------------------------------------- /packages/query/src/yql.ts: -------------------------------------------------------------------------------- 1 | import { type Value, fromJs } from "@ydbjs/value" 2 | 3 | const SymbolUnsafe = Symbol("unsafe") 4 | 5 | function isObject(value: unknown): boolean { 6 | return typeof value === 'object' && value !== null && !Array.isArray(value) 7 | } 8 | 9 | export class UnsafeString extends String { 10 | [SymbolUnsafe] = true 11 | } 12 | 13 | export function yql

( 14 | strings: string | TemplateStringsArray, 15 | ...values: P 16 | ): { text: string, params: Record } { 17 | let text = '' 18 | let params: Record = Object.assign({}, null) 19 | 20 | if (Array.isArray(values)) { 21 | let skip: number = 0 22 | values.forEach((value, i) => { 23 | if (value === undefined || value === null) { 24 | throw new Error(`Undefined or null value passed to yql. For null use YDB Optional type.`); 25 | } 26 | 27 | if (value[SymbolUnsafe]) { 28 | skip += 1 29 | return 30 | } 31 | 32 | let ydbValue = isObject(value) && 'type' in value && 'kind' in value['type'] ? value : fromJs(value) 33 | 34 | params[`$p${i - skip}`] = ydbValue 35 | }) 36 | } 37 | 38 | if (typeof strings === 'string') { 39 | text += strings 40 | } 41 | 42 | if (Array.isArray(strings)) { 43 | let skip: number = 0 44 | text += strings.reduce((prev, curr, i) => { 45 | let value = values[i] 46 | if (value === undefined || value === null) { 47 | return prev + curr 48 | } 49 | 50 | if (value[SymbolUnsafe]) { 51 | skip += 1 52 | } 53 | 54 | return prev + curr + (value[SymbolUnsafe] ? value.toString() : `$p${i - skip}`) 55 | }, '') 56 | } 57 | 58 | return { text, params } 59 | } 60 | 61 | export function unsafe(value: string | { toString(): string }) { 62 | return new UnsafeString(value.toString()) 63 | } 64 | 65 | export function identifier(path: string) { 66 | return unsafe("`" + path + "`") 67 | } 68 | -------------------------------------------------------------------------------- /packages/query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "exclude": [ 7 | "vitest.config.ts", 8 | "**/*.test.ts", 9 | "dist/**/*" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/query/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineProject } from 'vitest/config' 2 | 3 | export default defineProject({ 4 | test: { 5 | exclude: ['**/*.e2e.test.ts'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/retry/.gitignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | -------------------------------------------------------------------------------- /packages/retry/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | 3 | /src 4 | /tests 5 | /plugins 6 | 7 | /*.yaml 8 | /*.json 9 | -------------------------------------------------------------------------------- /packages/retry/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/retry 2 | 3 | ## 6.0.0-alpha.13 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies 8 | - @ydbjs/error@6.0.0-alpha.9 9 | 10 | ## 6.0.0-alpha.12 11 | 12 | ### Patch Changes 13 | 14 | - Fix build cjs issues 15 | - Updated dependencies 16 | - @ydbjs/error@6.0.0-alpha.8 17 | - @ydbjs/api@6.0.0-alpha.8 18 | 19 | ## 6.0.0-alpha.10 20 | 21 | ### Patch Changes 22 | 23 | - Return original error when budget exceeded 24 | 25 | ## 6.0.0-alpha.9 26 | 27 | ### Patch Changes 28 | 29 | - Update packages description 30 | - Updated dependencies 31 | - @ydbjs/error@6.0.0-alpha.7 32 | - @ydbjs/api@6.0.0-alpha.7 33 | 34 | ## 6.0.0-alpha.8 35 | 36 | ### Patch Changes 37 | 38 | - Consolidated and clarified README files across all packages. 39 | - Updated dependencies 40 | - @ydbjs/api@6.0.0-alpha.6 41 | - @ydbjs/error@6.0.0-alpha.6 42 | 43 | ## 6.0.0-alpha.7 44 | 45 | ### Patch Changes 46 | 47 | - Refined retry logic: now throws the original AbortError, supports AbortSignal, and adds onRetry hook. 48 | - Expanded configuration options and documentation. 49 | - Improved test coverage for retry scenarios. 50 | 51 | ## 6.0.0-alpha.6 52 | 53 | ### Patch Changes 54 | 55 | - Add transaction support 56 | - Updated dependencies 57 | - @ydbjs/api@6.0.0-alpha.5 58 | - @ydbjs/error@6.0.0-alpha.5 59 | 60 | ## 6.0.0-alpha.5 61 | 62 | ### Patch Changes 63 | 64 | - Ensure function execution in retry is awaited for proper error handling 65 | 66 | ## 6.0.0-alpha.4 67 | 68 | ### Patch Changes 69 | 70 | - Added support to ignore the `.turbo` folder for better compatibility and cleaner workflows. 71 | - Updated dependencies 72 | - @ydbjs/api@6.0.0-alpha.4 73 | - @ydbjs/error@6.0.0-alpha.4 74 | 75 | ## 6.0.0-alpha.3 76 | 77 | ### Patch Changes 78 | 79 | - Update usage examples 80 | - Updated dependencies 81 | - @ydbjs/api@6.0.0-alpha.3 82 | - @ydbjs/error@6.0.0-alpha.3 83 | -------------------------------------------------------------------------------- /packages/retry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ydbjs/retry", 3 | "version": "6.0.0-alpha.13", 4 | "description": "Flexible, configurable retry logic for YDB operations. Supports custom strategies, budgets, and integration with async workflows.", 5 | "license": "Apache-2.0", 6 | "type": "module", 7 | "sideEffects": false, 8 | "module": "dist/esm/index.js", 9 | "main": "dist/cjs/index.js", 10 | "scripts": { 11 | "clean": "rm -rf dist", 12 | "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", 13 | "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm", 14 | "test": "vitest --run", 15 | "test:watch": "vitest --watch", 16 | "attw": "attw --pack" 17 | }, 18 | "exports": { 19 | ".": { 20 | "require": "./dist/cjs/index.js", 21 | "import": "./dist/esm/index.js" 22 | }, 23 | "./budget": { 24 | "require": "./dist/cjs/budget.js", 25 | "import": "./dist/esm/budget.js" 26 | }, 27 | "./strategy": { 28 | "require": "./dist/cjs/strategy.js", 29 | "import": "./dist/esm/strategy.js" 30 | } 31 | }, 32 | "types": "dist/esm/index.d.ts", 33 | "typesVersions": { 34 | "*": { 35 | "budget": [ 36 | "./dist/cjs/budget.d.ts" 37 | ], 38 | "strategy": [ 39 | "./dist/cjs/strategy.d.ts" 40 | ] 41 | } 42 | }, 43 | "dependencies": { 44 | "@ydbjs/api": "6.0.0-alpha.8", 45 | "@ydbjs/error": "6.0.0-alpha.9", 46 | "debug": "^4.4.0", 47 | "nice-grpc": "^2.1.12" 48 | }, 49 | "publishConfig": { 50 | "tag": "alpha", 51 | "access": "public" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/retry/src/budget.ts: -------------------------------------------------------------------------------- 1 | import type { RetryConfig } from "./config.js"; 2 | import type { RetryContext } from "./context.js"; 3 | 4 | export interface RetryBudget { 5 | (ctx: RetryContext, cfg: RetryConfig): number 6 | } 7 | -------------------------------------------------------------------------------- /packages/retry/src/config.ts: -------------------------------------------------------------------------------- 1 | import type { Abortable } from "node:events"; 2 | 3 | import type { RetryBudget } from "./budget.js"; 4 | import type { RetryContext } from "./context.js"; 5 | import type { RetryStrategy } from "./strategy.js"; 6 | 7 | /** 8 | * Options for retry configuration 9 | */ 10 | export interface RetryConfig extends Abortable { 11 | /** Predicate to determine if an error is retryable */ 12 | retry?: boolean | ((error: RetryContext['error'], idempotent: boolean) => boolean); 13 | /** Budget for retry attempts */ 14 | budget?: number | RetryBudget; 15 | /** Strategy to calculate delay */ 16 | strategy?: number | RetryStrategy; 17 | /** Idempotent operation */ 18 | idempotent?: boolean; 19 | 20 | /** Hook to be called before retrying */ 21 | onRetry?: (ctx: RetryContext) => void; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/retry/src/context.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Context for retry operation 3 | */ 4 | export type RetryContext = { 5 | attempt: number; 6 | error: unknown; 7 | } 8 | -------------------------------------------------------------------------------- /packages/retry/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest' 2 | 3 | import { retry } from './index.ts' 4 | 5 | let isError = (error: unknown) => error instanceof Error 6 | 7 | test('do retry', async () => { 8 | let attempts = 0 9 | 10 | let result = retry({ retry: isError, budget: Infinity }, async () => { 11 | if (attempts > 2) { 12 | return 13 | } 14 | 15 | attempts++ 16 | throw new Error() 17 | }) 18 | 19 | await expect(result).resolves.eq(void 0) 20 | expect(attempts).eq(3) 21 | }) 22 | 23 | test('budget exceeded', async () => { 24 | let attempts = 0 25 | 26 | let result = retry({ retry: isError, budget: 0 }, async () => { 27 | if (attempts > 2) { 28 | return 29 | } 30 | 31 | attempts++ 32 | throw new Error() 33 | }) 34 | 35 | await expect(result).rejects.toThrow('Retry budget exceeded') 36 | expect(attempts).eq(0) 37 | }) 38 | 39 | test('retry with signal', async () => { 40 | let attempts = 0 41 | let controller = new AbortController() 42 | 43 | controller.abort() 44 | 45 | let result = retry({ retry: isError, signal: controller.signal }, async () => { 46 | if (attempts > 2) { 47 | return 48 | } 49 | 50 | attempts++ 51 | throw new Error() 52 | }) 53 | 54 | await expect(result).rejects.toThrow('This operation was aborted') 55 | expect(attempts).eq(0) 56 | }) 57 | -------------------------------------------------------------------------------- /packages/retry/src/strategy.ts: -------------------------------------------------------------------------------- 1 | import type { RetryConfig } from "./config.js"; 2 | import type { RetryContext } from "./context.js"; 3 | 4 | /** 5 | * Strategy to calculate delay. 6 | * @param ctx - Context for retry operation 7 | * @param cfg - Options for retry configuration 8 | * @returns Delay in milliseconds 9 | * 10 | * @example 11 | * ```ts 12 | * import { retry, fixed } from '@ydbjs/retry' 13 | * 14 | * await retry(() => fetch('https://example.com'), { 15 | * strategy: fixed(1000), 16 | * }) 17 | * ``` 18 | */ 19 | export interface RetryStrategy { 20 | (ctx: RetryContext, cfg: RetryConfig): number; 21 | } 22 | 23 | export function fixed(ms: number): RetryStrategy { 24 | return () => ms 25 | } 26 | 27 | export function linear(ms: number): RetryStrategy { 28 | return (ctx) => ctx.attempt * ms 29 | } 30 | 31 | export function exponential(ms: number): RetryStrategy { 32 | return (ctx) => Math.pow(2, ctx.attempt) * ms 33 | } 34 | 35 | export function random(min: number, max: number): RetryStrategy { 36 | return () => Math.floor(Math.random() * (max - min + 1) + min) 37 | } 38 | 39 | export function jitter(ms: number): RetryStrategy { 40 | return (ctx) => Math.floor(Math.random() * ms) + ctx.attempt 41 | } 42 | 43 | export function backoff(base: number, max: number): RetryStrategy { 44 | return (ctx) => Math.min(Math.pow(2, ctx.attempt) * base, max) 45 | } 46 | 47 | export function combine(...strategies: RetryStrategy[]): RetryStrategy { 48 | return (ctx, cfg) => strategies.reduce((acc, strategy) => acc + strategy(ctx, cfg), 0) 49 | } 50 | 51 | export function compose(...strategies: RetryStrategy[]): RetryStrategy { 52 | return (ctx, cfg) => strategies.reduce((acc, strategy) => Math.max(acc, strategy(ctx, cfg)), 0) 53 | } 54 | -------------------------------------------------------------------------------- /packages/retry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "exclude": [ 7 | "vitest.config.ts", 8 | "**/*.test.ts", 9 | "dist/**/*" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/retry/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineProject } from 'vitest/config' 2 | 3 | export default defineProject({ 4 | test: { 5 | exclude: ['**/*.e2e.test.ts'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/topic/.gitignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | -------------------------------------------------------------------------------- /packages/topic/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | 3 | /src 4 | /tests 5 | /plugins 6 | 7 | /*.yaml 8 | /*.json 9 | -------------------------------------------------------------------------------- /packages/topic/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/topic 2 | 3 | ## 6.0.0-alpha.18 4 | 5 | ### Patch Changes 6 | 7 | - Migrate to PQueue for storing end sending message from client 8 | 9 | ## 6.0.0-alpha.17 10 | 11 | ### Patch Changes 12 | 13 | - Updated dependencies 14 | - @ydbjs/error@6.0.0-alpha.9 15 | - @ydbjs/core@6.0.0-alpha.18 16 | - @ydbjs/retry@6.0.0-alpha.13 17 | 18 | ## 6.0.0-alpha.16 19 | 20 | ### Patch Changes 21 | 22 | - Fix build cjs issues 23 | - Updated dependencies 24 | - @ydbjs/error@6.0.0-alpha.8 25 | - @ydbjs/retry@6.0.0-alpha.12 26 | - @ydbjs/auth@6.0.0-alpha.14 27 | - @ydbjs/core@6.0.0-alpha.17 28 | - @ydbjs/api@6.0.0-alpha.8 29 | - @ydbjs/value@6.0.0-alpha.12 30 | 31 | ## 6.0.0-alpha.15 32 | 33 | ### Patch Changes 34 | 35 | - Refactor writer: split single class into small parts for better testing 36 | 37 | ## 6.0.0-alpha.14 38 | 39 | ### Patch Changes 40 | 41 | - Update cleanup logic on retries 42 | 43 | ## 6.0.0-alpha.13 44 | 45 | ### Patch Changes 46 | 47 | - Prevent memory leacks 48 | 49 | ## 6.0.0-alpha.12 50 | 51 | ### Patch Changes 52 | 53 | - Resend commit offsets after retry 54 | 55 | ## 6.0.0-alpha.11 56 | 57 | ### Patch Changes 58 | 59 | - Enhance TopicWriter and TopicReader with compression options 60 | 61 | ## 6.0.0-alpha.10 62 | 63 | ### Patch Changes 64 | 65 | - Add TopicWriter 66 | 67 | ## 6.0.0-alpha.9 68 | 69 | ### Patch Changes 70 | 71 | - Add await for commit 72 | 73 | ## 6.0.0-alpha.8 74 | 75 | ### Patch Changes 76 | 77 | - Publish topic reader 78 | 79 | ## 6.0.0-alpha.7 80 | 81 | ### Patch Changes 82 | 83 | - Updated dependencies 84 | - @ydbjs/core@6.0.0-alpha.16 85 | 86 | ## 6.0.0-alpha.6 87 | 88 | ### Patch Changes 89 | 90 | - Updated dependencies 91 | - @ydbjs/auth@6.0.0-alpha.13 92 | 93 | ## 6.0.0-alpha.5 94 | 95 | ### Patch Changes 96 | 97 | - Updated dependencies 98 | - @ydbjs/core@6.0.0-alpha.15 99 | 100 | ## 6.0.0-alpha.4 101 | 102 | ### Patch Changes 103 | 104 | - Updated dependencies 105 | - @ydbjs/core@6.0.0-alpha.14 106 | 107 | ## 6.0.0-alpha.3 108 | 109 | ### Patch Changes 110 | 111 | - Updated dependencies 112 | - @ydbjs/core@6.0.0-alpha.13 113 | 114 | ## 6.0.0-alpha.2 115 | 116 | ### Patch Changes 117 | 118 | - Updated dependencies 119 | - @ydbjs/auth@6.0.0-alpha.12 120 | - @ydbjs/core@6.0.0-alpha.12 121 | -------------------------------------------------------------------------------- /packages/topic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ydbjs/topic", 3 | "version": "6.0.0-alpha.18", 4 | "description": "", 5 | "license": "Apache-2.0", 6 | "type": "module", 7 | "sideEffects": false, 8 | "module": "dist/esm/index.js", 9 | "main": "dist/cjs/index.js", 10 | "scripts": { 11 | "clean": "rm -rf dist", 12 | "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --esModuleInterop --outDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", 13 | "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm", 14 | "test": "vitest --run", 15 | "test:watch": "vitest --watch", 16 | "attw": "attw --pack" 17 | }, 18 | "exports": { 19 | ".": { 20 | "require": "./dist/cjs/index.js", 21 | "import": "./dist/esm/index.js" 22 | }, 23 | "./reader": { 24 | "require": "./dist/cjs/reader.js", 25 | "import": "./dist/esm/reader.js" 26 | }, 27 | "./writer": { 28 | "require": "./dist/cjs/writer/index.js", 29 | "import": "./dist/esm/writer/index.js" 30 | } 31 | }, 32 | "types": "dist/esm/index.d.ts", 33 | "typesVersions": { 34 | "*": { 35 | ".": [ 36 | "./dist/cjs/index.d.ts" 37 | ], 38 | "reader": [ 39 | "./dist/cjs/reader.d.ts" 40 | ], 41 | "writer": [ 42 | "./dist/cjs/writer/index.d.ts" 43 | ] 44 | } 45 | }, 46 | "dependencies": { 47 | "@bufbuild/protobuf": "^2.5.2", 48 | "@ydbjs/api": "6.0.0-alpha.8", 49 | "@ydbjs/core": "6.0.0-alpha.18", 50 | "@ydbjs/error": "6.0.0-alpha.9", 51 | "@ydbjs/retry": "6.0.0-alpha.13", 52 | "@ydbjs/value": "6.0.0-alpha.12", 53 | "debug": "^4.4.0", 54 | "ms": "^2.1.3", 55 | "nice-grpc": "^2.1.12" 56 | }, 57 | "devDependencies": { 58 | "@types/debug": "^4.1.12", 59 | "@types/ms": "^2.1.0" 60 | }, 61 | "publishConfig": { 62 | "tag": "alpha", 63 | "access": "public" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/topic/src/aee.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import { EventEmitter } from 'node:events'; 3 | 4 | import { AsyncEventEmitter } from './aee.ts'; 5 | 6 | 7 | test('AsyncEventEmitter yields events as they are emitted', async () => { 8 | let emitter = new EventEmitter(); 9 | let asyncEmitter = new AsyncEventEmitter(emitter, 'data'); 10 | let results: number[] = []; 11 | 12 | setTimeout(() => { 13 | emitter.emit('data', 1); 14 | emitter.emit('data', 2); 15 | emitter.emit('end'); 16 | }, 0); 17 | 18 | for await (let value of asyncEmitter) { 19 | results.push(value); 20 | } 21 | 22 | expect(results).toEqual([1, 2]); 23 | }); 24 | 25 | test('AsyncEventEmitter handles errors emitted by the emitter', async () => { 26 | let emitter = new EventEmitter(); 27 | let asyncEmitter = new AsyncEventEmitter(emitter, 'data'); 28 | let iter = asyncEmitter[Symbol.asyncIterator](); 29 | 30 | setTimeout(() => { 31 | emitter.emit('data', 1); 32 | emitter.emit('error', new Error('fail')); 33 | }, 0); 34 | 35 | await expect(iter.next()).resolves.toEqual({ value: 1, done: false }); 36 | await expect(iter.next()).rejects.toThrow('fail'); 37 | }); 38 | 39 | test('AsyncEventEmitter can be manually closed with return()', async () => { 40 | let emitter = new EventEmitter(); 41 | let asyncEmitter = new AsyncEventEmitter(emitter, 'data'); 42 | let iter = asyncEmitter[Symbol.asyncIterator](); 43 | 44 | setTimeout(() => { 45 | emitter.emit('data', 1); 46 | }, 0); 47 | 48 | await expect(iter.next()).resolves.toEqual({ value: 1, done: false }); 49 | await expect(iter.return!()).resolves.toEqual({ value: undefined, done: true }); 50 | }); 51 | 52 | test('AsyncEventEmitter can be manually closed with throw()', async () => { 53 | let emitter = new EventEmitter(); 54 | let asyncEmitter = new AsyncEventEmitter(emitter, 'data'); 55 | let iter = asyncEmitter[Symbol.asyncIterator](); 56 | 57 | setTimeout(() => { 58 | emitter.emit('data', 1); 59 | }, 0); 60 | 61 | await expect(iter.next()).resolves.toEqual({ value: 1, done: false }); 62 | await expect(iter.throw!(new Error('manual'))).resolves.toEqual({ value: undefined, done: true }); 63 | }); 64 | -------------------------------------------------------------------------------- /packages/topic/src/aee.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | 3 | export class AsyncEventEmitter implements Disposable, AsyncIterable { 4 | private emitter: EventEmitter; 5 | private eventName: string; 6 | private queue: Array = []; 7 | private resolvers: Array<(value: IteratorResult) => void> = []; 8 | private rejecters: Array<(err: any) => void> = []; 9 | private ended = false; 10 | private error: any = null; 11 | 12 | constructor(emitter: EventEmitter, eventName: string) { 13 | this.emitter = emitter; 14 | this.eventName = eventName; 15 | 16 | this.emitter.on(this.eventName, (data: T) => { 17 | if (this.resolvers.length > 0) { 18 | const resolve = this.resolvers.shift()!; 19 | resolve({ value: data, done: false }); 20 | } else { 21 | this.queue.push(data); 22 | } 23 | }); 24 | 25 | this.emitter.once('end', () => { 26 | this.ended = true; 27 | while (this.resolvers.length > 0) { 28 | const resolve = this.resolvers.shift()!; 29 | resolve({ value: undefined, done: true }); 30 | } 31 | }); 32 | 33 | this.emitter.once('error', (err: any) => { 34 | this.error = err; 35 | while (this.rejecters.length > 0) { 36 | const reject = this.rejecters.shift()!; 37 | reject(err); 38 | } 39 | while (this.resolvers.length > 0) { 40 | const reject = this.rejecters.shift(); 41 | if (reject) reject(err); 42 | } 43 | }); 44 | } 45 | 46 | next(): Promise> { 47 | if (this.error) { 48 | return Promise.reject(this.error); 49 | } 50 | if (this.queue.length > 0) { 51 | return Promise.resolve({ value: this.queue.shift()!, done: false }); 52 | } 53 | if (this.ended) { 54 | return Promise.resolve({ value: undefined, done: true }); 55 | } 56 | return new Promise>((resolve, reject) => { 57 | this.resolvers.push(resolve); 58 | this.rejecters.push(reject); 59 | }); 60 | } 61 | 62 | return(): Promise> { 63 | this.ended = true; 64 | while (this.resolvers.length > 0) { 65 | const resolve = this.resolvers.shift()!; 66 | resolve({ value: undefined, done: true }); 67 | } 68 | return Promise.resolve({ value: undefined, done: true }); 69 | } 70 | 71 | throw(err?: any): Promise> { 72 | this.error = err; 73 | while (this.rejecters.length > 0) { 74 | const reject = this.rejecters.shift()!; 75 | reject(err); 76 | } 77 | while (this.resolvers.length > 0) { 78 | const reject = this.rejecters.shift(); 79 | if (reject) reject(err); 80 | } 81 | return Promise.resolve({ value: undefined, done: true }); 82 | } 83 | 84 | [Symbol.asyncIterator]() { 85 | return this; 86 | } 87 | 88 | dispose(): void { 89 | this.emitter.removeAllListeners(this.eventName); 90 | this.emitter.removeAllListeners('end'); 91 | this.emitter.removeAllListeners('error'); 92 | this.resolvers.length = 0; 93 | this.rejecters.length = 0; 94 | this.queue.length = 0; 95 | this.ended = true; 96 | this.error = null; 97 | } 98 | 99 | [Symbol.dispose](): void { 100 | this.dispose(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /packages/topic/src/dbg.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | 3 | export const dbg = debug('ydbjs').extend('topic'); 4 | -------------------------------------------------------------------------------- /packages/topic/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Driver } from "@ydbjs/core"; 2 | 3 | import { TopicReader, type TopicReaderOptions } from "./reader.js"; 4 | import { type TopicWriter, type TopicWriterOptions, createTopicWriter } from "./writer/index.ts"; 5 | 6 | export interface TopicClient { 7 | createReader(options: TopicReaderOptions): TopicReader; 8 | createWriter(options: TopicWriterOptions): TopicWriter; 9 | } 10 | 11 | export function topic(driver: Driver): TopicClient { 12 | return { 13 | createReader(options) { 14 | return new TopicReader(driver, options); 15 | }, 16 | createWriter(options: TopicWriterOptions) { 17 | return createTopicWriter(driver, options); 18 | }, 19 | } as TopicClient 20 | } 21 | -------------------------------------------------------------------------------- /packages/topic/src/message.ts: -------------------------------------------------------------------------------- 1 | import type { Codec } from "@ydbjs/api/topic"; 2 | 3 | export interface TopicMessage { 4 | partitionSessionId?: bigint; 5 | partitionId: bigint; 6 | producerId: string; 7 | 8 | codec: Codec, 9 | seqNo: bigint; 10 | offset?: bigint; 11 | payload: Payload; 12 | uncompressedSize?: bigint; 13 | 14 | createdAt?: Date; 15 | writtenAt?: Date; 16 | metadataItems?: Record; 17 | } 18 | -------------------------------------------------------------------------------- /packages/topic/src/partition-session.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "node:stream" 2 | 3 | type TopicPartitionSessionEmitterMap = { 4 | "stop": [] 5 | "end": [] 6 | } 7 | 8 | export class TopicPartitionSession extends EventEmitter { 9 | /** 10 | * Partition session identifier. 11 | */ 12 | readonly partitionSessionId: bigint 13 | /** 14 | * Partition identifier. 15 | */ 16 | readonly partitionId: bigint 17 | /** 18 | * Topic path. 19 | */ 20 | readonly topicPath: string 21 | /** 22 | * Partition offsets. 23 | */ 24 | partitionOffsets = { start: 0n, end: 0n } 25 | /** 26 | * Offset of the last committed message from the partition. 27 | */ 28 | partitionCommittedOffset: bigint = 0n 29 | /** 30 | * Flag indicating whether the session is currently active. 31 | */ 32 | #stopped: boolean = false 33 | /** 34 | * Flag indicating whether the session has ended. 35 | */ 36 | #ended: boolean = false 37 | 38 | /** 39 | * Creates a new instance of TopicPartitionSession. 40 | * @param partitionSessionId - The identifier of the partition session. 41 | * @param partitionId - The identifier of the partition. 42 | * @param topicPath - The path of the topic. 43 | */ 44 | constructor(partitionSessionId: bigint, partitionId: bigint, topicPath: string) { 45 | super(); 46 | 47 | this.partitionSessionId = partitionSessionId; 48 | this.partitionId = partitionId; 49 | this.topicPath = topicPath; 50 | } 51 | 52 | get isStopped(): boolean { 53 | return this.#stopped; 54 | } 55 | 56 | get isEnded(): boolean { 57 | return this.#ended; 58 | } 59 | 60 | stop(): void { 61 | this.emit("stop"); 62 | this.#stopped = true; 63 | } 64 | 65 | end(): void { 66 | this.emit("end"); 67 | this.#ended = true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/topic/src/queue.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { PQueue } from './queue.ts'; 3 | 4 | describe('PQueue', () => { 5 | it('should process items in priority order', async () => { 6 | const queue = new PQueue(); 7 | 8 | queue.push(1, 1); // Priority 1 9 | queue.push(2, 3); // Priority 3 10 | queue.push(3, 2); // Priority 2 11 | 12 | expect(await queue.shift()).toBe(2); // Highest priority 13 | expect(await queue.shift()).toBe(3); // Next highest priority 14 | expect(await queue.shift()).toBe(1); // Lowest priority 15 | }); 16 | 17 | it('should handle async iteration', async () => { 18 | const queue = new PQueue(); 19 | 20 | queue.push(1, 1); 21 | queue.push(2, 2); 22 | queue.push(3, 3); 23 | 24 | const results: number[] = []; 25 | for await (const item of queue) { 26 | results.push(item); 27 | if (results.length === 3) break; 28 | } 29 | 30 | expect(results).toEqual([3, 2, 1]); 31 | }); 32 | 33 | it('should resolve pending shifts when items are added', async () => { 34 | const queue = new PQueue(); 35 | 36 | const shiftPromise = queue.shift(); 37 | queue.push(42, 1); 38 | 39 | expect(await shiftPromise).toBe(42); 40 | }); 41 | 42 | it('should throw an error when pushing to a closed queue', () => { 43 | const queue = new PQueue(); 44 | queue.close(); 45 | 46 | expect(() => queue.push(1)).toThrow('Queue closed'); 47 | }); 48 | 49 | it('should throw an error when shifting from a closed queue', async () => { 50 | const queue = new PQueue(); 51 | queue.close(); 52 | 53 | await expect(queue.shift()).rejects.toThrow('Queue closed'); 54 | }); 55 | 56 | it('should keep unconsumed messages after consumer restart', async () => { 57 | const queue = new PQueue(); 58 | 59 | const shiftPromise = queue.shift(); 60 | queue.restartConsumer(); 61 | queue.push(1, 1); 62 | queue.push(2, 2); 63 | 64 | await expect(shiftPromise).rejects.toThrow('Consumer restarted'); 65 | 66 | expect(queue.size).toBe(2); 67 | expect(await queue.shift()).toBe(2); 68 | expect(await queue.shift()).toBe(1); 69 | expect(queue.size).toBe(0); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /packages/topic/src/queue.ts: -------------------------------------------------------------------------------- 1 | export class PQueue { 2 | private heap: { value: T; priority: number }[] = []; 3 | private closed = false; 4 | private pendingShifts: ((value: IteratorResult) => void)[] = []; 5 | private pendingRejects: ((reason?: any) => void)[] = []; 6 | 7 | get size(): number { 8 | return this.heap.length; 9 | } 10 | 11 | push(value: T, priority: number = 0) { 12 | if (this.closed) { 13 | throw new Error('Queue closed'); 14 | } 15 | 16 | let left = 0; 17 | let right = this.heap.length; 18 | while (left < right) { 19 | let mid = (left + right) >> 1; 20 | if (this.heap[mid].priority < priority) { 21 | right = mid; 22 | } else { 23 | left = mid + 1; 24 | } 25 | } 26 | this.heap.splice(left, 0, { value, priority }); 27 | 28 | if (this.pendingShifts.length > 0) { 29 | this.pendingRejects.shift()!; 30 | let next = this.heap.shift()!; 31 | let resolve = this.pendingShifts.shift()!; 32 | resolve({ value: next.value, done: false }); 33 | } 34 | } 35 | 36 | async shift(): Promise { 37 | if (this.closed && this.heap.length === 0) { 38 | throw new Error('Queue closed'); 39 | } 40 | 41 | if (this.heap.length > 0) { 42 | let next = this.heap.shift()!; 43 | return next.value; 44 | } 45 | 46 | return new Promise((resolve, reject) => { 47 | this.pendingRejects.push(reject); 48 | this.pendingShifts.push(({ value, done }) => { 49 | if (done) { 50 | reject(new Error('Queue closed')); 51 | } else { 52 | resolve(value); 53 | } 54 | }); 55 | }); 56 | } 57 | 58 | close() { 59 | this.closed = true; 60 | while (this.pendingShifts.length > 0) { 61 | this.pendingRejects.shift()!; 62 | let resolve = this.pendingShifts.shift()!; 63 | resolve({ value: undefined as any, done: true }); 64 | } 65 | } 66 | 67 | restartConsumer() { 68 | while (this.pendingShifts.length > 0) { 69 | this.pendingShifts.shift()!; 70 | let reject = this.pendingRejects.shift()!; 71 | reject(new Error('Consumer restarted')); 72 | } 73 | } 74 | 75 | async next(): Promise> { 76 | if (this.closed && this.heap.length === 0) { 77 | return { value: undefined as any, done: true }; 78 | } 79 | 80 | if (this.heap.length > 0) { 81 | let next = this.heap.shift()!; 82 | return { value: next.value, done: false }; 83 | } 84 | 85 | return new Promise>((resolve, reject) => { 86 | this.pendingShifts.push(resolve); 87 | this.pendingRejects.push(reject); 88 | }); 89 | } 90 | 91 | async *[Symbol.asyncIterator]() { 92 | while (true) { 93 | // eslint-disable-next-line no-await-in-loop 94 | let { value, done } = await this.next(); 95 | if (done) break; 96 | yield value; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_batch_messages.ts: -------------------------------------------------------------------------------- 1 | import type { StreamWriteMessage_WriteRequest_MessageData } from "@ydbjs/api/topic"; 2 | import { MAX_BATCH_SIZE } from "./constants.js"; 3 | 4 | export function _batch_messages( 5 | messages: StreamWriteMessage_WriteRequest_MessageData[] 6 | ): StreamWriteMessage_WriteRequest_MessageData[][] { 7 | let batches: StreamWriteMessage_WriteRequest_MessageData[][] = []; 8 | 9 | // Build batch until size limit or no more messages 10 | while (messages.length > 0) { 11 | let batch: StreamWriteMessage_WriteRequest_MessageData[] = []; 12 | let batchSize = 0n; 13 | 14 | // Build batch until size limit or no more messages 15 | while (messages.length > 0) { 16 | let message = messages[0]; 17 | 18 | // Check if adding this message would exceed the batch size limit 19 | if (batchSize + BigInt(message.data.length) > MAX_BATCH_SIZE) { 20 | // If the batch already has messages, send it 21 | if (batch.length > 0) break; 22 | 23 | // If this is a single message exceeding the limit, we still need to send it 24 | batch.push(messages.shift()!); 25 | break; 26 | } 27 | 28 | // Add message to current batch 29 | batch.push(messages.shift()!); 30 | batchSize += BigInt(message.data.length); 31 | } 32 | 33 | // If the batch is not empty, add it to the batches array 34 | // This ensures that we always send at least one message, even if it exceeds the batch size limit. 35 | if (batch.length > 0) { 36 | batches.push(batch); 37 | } 38 | } 39 | 40 | return batches; 41 | } 42 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_flush.ts: -------------------------------------------------------------------------------- 1 | import type { Codec, StreamWriteMessage_FromClient, StreamWriteMessage_WriteRequest_MessageData } from "@ydbjs/api/topic"; 2 | import type { PQueue } from "../queue.js"; 3 | import { _batch_messages } from "./_batch_messages.js"; 4 | import { _emit_write_request } from "./_write_request.js"; 5 | import { MAX_INFLIGHT_COUNT } from "./constants.js"; 6 | 7 | export function _flush(ctx: { 8 | readonly queue: PQueue, 9 | readonly codec: Codec, 10 | readonly buffer: Map; // Map of sequence numbers to messages in the buffer 11 | readonly inflight: Set; // Set of sequence numbers that are currently in-flight 12 | updateBufferSize: (bytes: bigint) => void; // Function to update the buffer size 13 | }) { 14 | if (!ctx.buffer.size) { 15 | return; // Nothing to flush 16 | } 17 | 18 | let iterator = ctx.buffer.values() 19 | let messagesToSend: StreamWriteMessage_WriteRequest_MessageData[] = []; 20 | 21 | while (ctx.inflight.size < MAX_INFLIGHT_COUNT) { 22 | let next = iterator.next(); 23 | if (next.done) { 24 | break; // No more messages to send 25 | } 26 | 27 | let message = next.value; 28 | messagesToSend.push(message); 29 | ctx.inflight.add(message.seqNo); 30 | } 31 | 32 | if (!messagesToSend.length) { 33 | return; // No messages to send 34 | } 35 | 36 | for (let batch of _batch_messages(messagesToSend)) { 37 | _emit_write_request(ctx, batch); // Emit the write request with the batch of messages 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_gen_producer_id.ts: -------------------------------------------------------------------------------- 1 | export function _get_producer_id() { 2 | let processId = process.pid; 3 | let currentTime = new Date().getTime(); 4 | let randomSuffix = Math.floor(Math.random() * 1000000); 5 | return `producer-${processId}-${currentTime}-${randomSuffix}`; 6 | } 7 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_init_reponse.ts: -------------------------------------------------------------------------------- 1 | import type { Codec, StreamWriteMessage_FromClient, StreamWriteMessage_InitResponse, StreamWriteMessage_WriteRequest_MessageData } from "@ydbjs/api/topic"; 2 | import type { PQueue } from "../queue.js"; 3 | import { _flush } from "./_flush.js"; 4 | 5 | export function _on_init_response(ctx: { 6 | readonly queue: PQueue, 7 | readonly codec: Codec, 8 | readonly buffer: Map; // Map of sequence numbers to messages in the buffer 9 | readonly inflight: Set; // Set of sequence numbers that are currently in-flight 10 | readonly lastSeqNo?: bigint; // The last sequence number acknowledged by the server 11 | 12 | updateLastSeqNo: (seqNo: bigint) => void; 13 | updateBufferSize: (bytes: bigint) => void; // Function to update the buffer size 14 | }, input: StreamWriteMessage_InitResponse) { 15 | if (!ctx.lastSeqNo) { 16 | // Store the last sequence number from the server. 17 | ctx.updateLastSeqNo(input.lastSeqNo); 18 | } 19 | 20 | if (ctx.inflight.size > 0) { 21 | ctx.inflight.clear(); // Clear the in-flight set if there are any messages in-flight 22 | } 23 | 24 | _flush(ctx); // Flush the buffer to send any pending messages. 25 | } 26 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_init_request.ts: -------------------------------------------------------------------------------- 1 | import { create } from "@bufbuild/protobuf"; 2 | import { type StreamWriteMessage_FromClient, StreamWriteMessage_FromClientSchema } from "@ydbjs/api/topic"; 3 | import type { PQueue } from "../queue.js"; 4 | 5 | export function _send_init_request(ctx: { 6 | readonly queue: PQueue, 7 | readonly topic: string; 8 | readonly producer?: string; 9 | readonly getLastSeqNo?: boolean; 10 | }) { 11 | return ctx.queue.push(create(StreamWriteMessage_FromClientSchema, { 12 | clientMessage: { 13 | case: 'initRequest', 14 | value: { 15 | path: ctx.topic, 16 | producerId: ctx.producer, 17 | getLastSeqNo: ctx.getLastSeqNo, 18 | } 19 | } 20 | }), 100); 21 | } 22 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_update_token.ts: -------------------------------------------------------------------------------- 1 | import { create } from "@bufbuild/protobuf"; 2 | import { type StreamWriteMessage_FromClient, StreamWriteMessage_FromClientSchema } from "@ydbjs/api/topic"; 3 | import type { PQueue } from "../queue.js"; 4 | 5 | export function _send_update_token_request(ctx: { 6 | readonly queue: PQueue, 7 | readonly token: string 8 | }) { 9 | return ctx.queue.push(create(StreamWriteMessage_FromClientSchema, { 10 | clientMessage: { 11 | case: 'updateTokenRequest', 12 | value: { 13 | token: ctx.token 14 | } 15 | } 16 | }), 10); 17 | } 18 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_write.ts: -------------------------------------------------------------------------------- 1 | import { create } from "@bufbuild/protobuf"; 2 | import { timestampFromDate } from "@bufbuild/protobuf/wkt"; 3 | import { Codec, type StreamWriteMessage_FromClient, type StreamWriteMessage_WriteRequest_MessageData, StreamWriteMessage_WriteRequest_MessageDataSchema } from "@ydbjs/api/topic"; 4 | import type { PQueue } from "../queue.js"; 5 | import { _flush } from "./_flush.js"; 6 | import { MAX_PAYLOAD_SIZE } from "./constants.js"; 7 | 8 | export function _write(ctx: { 9 | readonly queue: PQueue, 10 | 11 | readonly codec: Codec, // Codec to use for compression 12 | readonly lastSeqNo: bigint, // Last sequence number used 13 | 14 | readonly buffer: Map; // Map of sequence numbers to messages in the buffer 15 | readonly inflight: Set; // Set of sequence numbers that are currently in-flight 16 | readonly pendingAcks: Map void }>; // Map of sequence numbers to pending ack resolvers 17 | 18 | readonly bufferSize: bigint, // Current size of the buffer in bytes 19 | readonly maxBufferSize: bigint, // Maximum size of the buffer in bytes 20 | 21 | encode: (data: Payload) => Uint8Array, // Optional encoding function for the payload 22 | compress: (data: Uint8Array) => Uint8Array // Compression function if codec is not RAW 23 | 24 | updateLastSeqNo: (seqNo: bigint) => void; 25 | updateBufferSize: (bytes: bigint) => void; // Function to update the buffer size 26 | }, msg: { 27 | data: Payload, 28 | seqNo?: bigint, 29 | createdAt?: Date, 30 | metadataItems?: Record 31 | }): Promise { 32 | if (ctx.codec === Codec.UNSPECIFIED) { 33 | throw new Error('Codec must be specified.'); 34 | } 35 | 36 | let data = ctx.encode(msg.data); 37 | 38 | // If compression is enabled, check if the payload should be compressed 39 | if (ctx.codec !== Codec.RAW) { 40 | if (!ctx.compress) { 41 | throw new Error('Compression function is required when codec is not RAW'); 42 | } 43 | 44 | data = ctx.compress(data) 45 | } 46 | 47 | // Validate the payload size, it should not exceed MAX_PAYLOAD_SIZE 48 | // This is a YDB limitation for single message size. 49 | if (data.length > MAX_PAYLOAD_SIZE) { 50 | throw new Error(`Payload size exceeds ${Number(MAX_PAYLOAD_SIZE / (1024n * 1024n))}MiB limit.`); 51 | } 52 | 53 | // Check if the buffer size exceeds the maximum allowed size 54 | // If it does, flush the buffer to send the messages before adding new ones. 55 | // This is to prevent memory overflow and ensure that the buffer does not grow indefinitely. 56 | if (ctx.bufferSize + BigInt(data.length) > ctx.maxBufferSize) { 57 | _flush(ctx); // Flush the buffer if it exceeds the maximum size. 58 | } 59 | 60 | let seqNo = msg.seqNo ?? ((ctx.lastSeqNo ?? 0n) + 1n); 61 | let createdAt = timestampFromDate(msg.createdAt ?? new Date()); 62 | let metadataItems = Object.entries(msg.metadataItems || {}).map(([key, value]) => ({ key, value })); 63 | let uncompressedSize = BigInt(data.length); 64 | 65 | let message = create(StreamWriteMessage_WriteRequest_MessageDataSchema, { 66 | data, 67 | seqNo, 68 | createdAt, 69 | metadataItems, 70 | uncompressedSize, 71 | }); 72 | 73 | ctx.buffer.set(seqNo, message); // Store the message in the buffer 74 | ctx.updateBufferSize(BigInt(data.length)); // Update the buffer size 75 | ctx.updateLastSeqNo(seqNo); // Update the last sequence number 76 | 77 | let pendingAck = Promise.withResolvers() 78 | ctx.pendingAcks.set(seqNo, pendingAck); // Store the pending ack resolver for this sequence number. 79 | 80 | return pendingAck.promise 81 | } 82 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_write_request.ts: -------------------------------------------------------------------------------- 1 | import { create } from "@bufbuild/protobuf"; 2 | import { Codec, type StreamWriteMessage_FromClient, StreamWriteMessage_FromClientSchema, type StreamWriteMessage_WriteRequest_MessageData } from "@ydbjs/api/topic"; 3 | import type { PQueue } from "../queue.js"; 4 | 5 | export function _emit_write_request(ctx: { 6 | readonly queue: PQueue, 7 | readonly codec: Codec, 8 | }, messages: StreamWriteMessage_WriteRequest_MessageData[]) { 9 | return ctx.queue.push(create(StreamWriteMessage_FromClientSchema, { 10 | clientMessage: { 11 | case: 'writeRequest', 12 | value: { 13 | messages, 14 | codec: ctx.codec, 15 | } 16 | } 17 | })); 18 | } 19 | -------------------------------------------------------------------------------- /packages/topic/src/writer/_write_response.ts: -------------------------------------------------------------------------------- 1 | import type { Codec, StreamWriteMessage_FromClient, StreamWriteMessage_WriteRequest_MessageData, StreamWriteMessage_WriteResponse } from "@ydbjs/api/topic"; 2 | import type { PQueue } from "../queue.js"; 3 | import { _flush } from "./_flush.js"; 4 | 5 | export function _on_write_response(ctx: { 6 | readonly queue: PQueue, 7 | readonly codec: Codec, 8 | readonly buffer: Map; // Map of sequence numbers to messages in the buffer 9 | readonly inflight: Set; // Set of sequence numbers that are currently in-flight 10 | readonly pendingAcks: Map void }>; // Map of sequence numbers to pending ack resolvers 11 | onAck?: (seqNo: bigint, status?: 'skipped' | 'written' | 'writtenInTx') => void // Callback for handling acknowledgments 12 | updateBufferSize: (bytes: bigint) => void; // Function to update the buffer size 13 | }, input: StreamWriteMessage_WriteResponse) { 14 | // Process each acknowledgment in the response. 15 | // This will resolve the pending ack promises and remove the messages from the buffer. 16 | for (let ack of input.acks) { 17 | ctx.onAck?.(ack.seqNo, ack.messageWriteStatus.case); 18 | 19 | // Remove the acknowledged message from the buffer. 20 | let message = ctx.buffer.get(ack.seqNo); 21 | if (message) { 22 | ctx.buffer.delete(ack.seqNo); 23 | ctx.updateBufferSize(-BigInt(message.data.length)); 24 | 25 | // Resolve the pending ack promise for this sequence number. 26 | let pendingAck = ctx.pendingAcks.get(ack.seqNo); 27 | if (pendingAck) { 28 | pendingAck.resolve(ack.seqNo); 29 | ctx.pendingAcks.delete(ack.seqNo); 30 | } 31 | 32 | // Decrease the in-flight count. 33 | ctx.inflight.delete(ack.seqNo); 34 | } 35 | } 36 | 37 | // If there are still messages in the buffer, flush them. 38 | _flush(ctx) 39 | } 40 | -------------------------------------------------------------------------------- /packages/topic/src/writer/constants.ts: -------------------------------------------------------------------------------- 1 | export const MAX_INFLIGHT_COUNT = 1000n; // Default maximum in-flight messages count 2 | export const MAX_PAYLOAD_SIZE = 48n * 1024n * 1024n; // Maximum payload size in bytes, default is 48MiB 3 | export const MAX_BUFFER_SIZE = 256n * 1024n * 1024n; // Maximum buffer size in bytes, default is 256MiB 4 | export const MAX_BATCH_SIZE = 48n * 1024n * 1024n; // Maximum batch size in bytes, default is 48MiB 5 | -------------------------------------------------------------------------------- /packages/topic/src/writer/types.ts: -------------------------------------------------------------------------------- 1 | import type { StreamWriteMessage_FromClient } from "@ydbjs/api/topic"; 2 | 3 | export type OutgoingEventMap = { 4 | 'message': [StreamWriteMessage_FromClient], 5 | 'close': [void], 6 | } 7 | -------------------------------------------------------------------------------- /packages/topic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "exclude": [ 7 | "dist/**/*", 8 | "**/*.test.ts", 9 | "vitest.config.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/topic/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineProject } from 'vitest/config' 2 | 3 | export default defineProject({ 4 | test: { 5 | exclude: ['**/*.e2e.test.ts'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/value/.gitignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | -------------------------------------------------------------------------------- /packages/value/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | 3 | /src 4 | /tests 5 | /plugins 6 | 7 | /*.yaml 8 | /*.json 9 | -------------------------------------------------------------------------------- /packages/value/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ydbjs/value 2 | 3 | ## 6.0.0-alpha.12 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies 8 | - @ydbjs/api@6.0.0-alpha.8 9 | 10 | ## 6.0.0-alpha.11 11 | 12 | ### Patch Changes 13 | 14 | - Fix cjs bundle issues 15 | 16 | ## 6.0.0-alpha.10 17 | 18 | ### Patch Changes 19 | 20 | - Enhance handling UUID values from ydb proto repr 21 | 22 | ## 6.0.0-alpha.9 23 | 24 | ### Patch Changes 25 | 26 | - Export primitive types 27 | 28 | ## 6.0.0-alpha.8 29 | 30 | ### Patch Changes 31 | 32 | - Update packages description 33 | - Updated dependencies 34 | - @ydbjs/api@6.0.0-alpha.7 35 | 36 | ## 6.0.0-alpha.7 37 | 38 | ### Patch Changes 39 | 40 | - Consolidated and clarified README files across all packages. 41 | - Updated dependencies 42 | - @ydbjs/api@6.0.0-alpha.6 43 | 44 | ## 6.0.0-alpha.6 45 | 46 | ### Patch Changes 47 | 48 | - Updated documentation with more conversion stage details and examples. 49 | 50 | ## 6.0.0-alpha.5 51 | 52 | ### Patch Changes 53 | 54 | - Add transaction support 55 | - Updated dependencies 56 | - @ydbjs/api@6.0.0-alpha.5 57 | 58 | ## 6.0.0-alpha.4 59 | 60 | ### Patch Changes 61 | 62 | - Added support to ignore the `.turbo` folder for better compatibility and cleaner workflows. 63 | - Updated dependencies 64 | - @ydbjs/api@6.0.0-alpha.4 65 | 66 | ## 6.0.0-alpha.3 67 | 68 | ### Patch Changes 69 | 70 | - Update usage examples 71 | - Updated dependencies 72 | - @ydbjs/api@6.0.0-alpha.3 73 | -------------------------------------------------------------------------------- /packages/value/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ydbjs/value", 3 | "version": "6.0.0-alpha.12", 4 | "description": "Type-safe conversion and manipulation of YDB values and types. Encode/decode between native JS and YDB, with full support for primitives and complex types.", 5 | "license": "Apache-2.0", 6 | "type": "module", 7 | "sideEffects": false, 8 | "module": "dist/esm/index.js", 9 | "main": "dist/cjs/index.js", 10 | "scripts": { 11 | "clean": "rm -rf dist", 12 | "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", 13 | "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm", 14 | "test": "vitest --run", 15 | "test:watch": "vitest --watch", 16 | "attw": "attw --pack" 17 | }, 18 | "exports": { 19 | ".": { 20 | "require": "./dist/cjs/index.js", 21 | "import": "./dist/esm/index.js" 22 | }, 23 | "./primitive": { 24 | "require": "./dist/cjs/primitive.js", 25 | "import": "./dist/esm/primitive.js" 26 | }, 27 | "./optional": { 28 | "require": "./dist/cjs/optional.js", 29 | "import": "./dist/esm/optional.js" 30 | }, 31 | "./struct": { 32 | "require": "./dist/cjs/struct.js", 33 | "import": "./dist/esm/struct.js" 34 | }, 35 | "./dict": { 36 | "require": "./dist/cjs/dict.js", 37 | "import": "./dist/esm/dict.js" 38 | }, 39 | "./tuple": { 40 | "require": "./dist/cjs/tuple.js", 41 | "import": "./dist/esm/tuple.js" 42 | }, 43 | "./list": { 44 | "require": "./dist/cjs/list.js", 45 | "import": "./dist/esm/list.js" 46 | }, 47 | "./null": { 48 | "require": "./dist/cjs/null.js", 49 | "import": "./dist/esm/null.js" 50 | }, 51 | "./uuid": { 52 | "require": "./dist/cjs/uuid.js", 53 | "import": "./dist/esm/uuid.js" 54 | }, 55 | "./print": { 56 | "require": "./dist/cjs/print.js", 57 | "import": "./dist/esm/print.js" 58 | } 59 | }, 60 | "types": "dist/esm/index.d.ts", 61 | "typesVersions": { 62 | "*": { 63 | ".": [ 64 | "./dist/cjs/index.d.ts" 65 | ], 66 | "primitive": [ 67 | "./dist/cjs/primitive.d.ts" 68 | ], 69 | "optional": [ 70 | "./dist/cjs/optional.d.ts" 71 | ], 72 | "struct": [ 73 | "./dist/cjs/struct.d.ts" 74 | ], 75 | "dict": [ 76 | "./dist/cjs/dict.d.ts" 77 | ], 78 | "tuple": [ 79 | "./dist/cjs/tuple.d.ts" 80 | ], 81 | "list": [ 82 | "./dist/cjs/list.d.ts" 83 | ], 84 | "null": [ 85 | "./dist/cjs/null.d.ts" 86 | ], 87 | "uuid": [ 88 | "./dist/cjs/uuid.d.ts" 89 | ], 90 | "print": [ 91 | "./dist/cjs/print.d.ts" 92 | ] 93 | } 94 | }, 95 | "dependencies": { 96 | "@bufbuild/protobuf": "^2.5.2", 97 | "@date-fns/tz": "^1.2.0", 98 | "@ydbjs/api": "6.0.0-alpha.8", 99 | "date-fns": "^4.1.0", 100 | "debug": "^4.4.0", 101 | "proposal-decimal": "^20250528.1.0" 102 | }, 103 | "publishConfig": { 104 | "tag": "alpha", 105 | "access": "public" 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /packages/value/src/dict.ts: -------------------------------------------------------------------------------- 1 | import { create } from '@bufbuild/protobuf'; 2 | import * as Ydb from '@ydbjs/api/value' 3 | 4 | import { type Type, TypeKind } from "./type.js"; 5 | import type { Value } from './value.js'; 6 | import { NullType } from './null.js'; 7 | 8 | export class DictType implements Type { 9 | readonly key: Type; 10 | readonly value: Type; 11 | #typeInstance?: Ydb.Type; 12 | 13 | constructor(keyType: Type, valueType: Type) { 14 | this.key = keyType; 15 | this.value = valueType; 16 | } 17 | 18 | get kind(): TypeKind.DICT { 19 | return TypeKind.DICT; 20 | } 21 | 22 | encode(): Ydb.Type { 23 | if (!this.#typeInstance) { 24 | this.#typeInstance = create(Ydb.TypeSchema, { type: { case: 'dictType', value: { key: this.key.encode(), payload: this.value.encode() } } }); 25 | } 26 | 27 | return this.#typeInstance; 28 | } 29 | } 30 | 31 | export class Dict implements Value { 32 | readonly type: DictType; 33 | readonly pairs: [K, V][] = []; 34 | #valueInstance?: Ydb.Value; 35 | 36 | constructor(...items: [K, V][]) { 37 | let keyType: Type = new NullType(); 38 | let valueType: Type = new NullType(); 39 | 40 | for (let [k, v] of items) { 41 | keyType = k.type; 42 | valueType = v.type; 43 | 44 | this.pairs.push([k, v]); 45 | } 46 | 47 | this.type = new DictType(keyType, valueType); 48 | } 49 | 50 | encode(): Ydb.Value { 51 | if (!this.#valueInstance) { 52 | let pairs: { key: Ydb.Value, payload: Ydb.Value }[] = []; 53 | 54 | for (let [k, v] of this.pairs) { 55 | pairs.push({ key: k.encode(), payload: v.encode() }); 56 | } 57 | 58 | this.#valueInstance = create(Ydb.ValueSchema, { pairs }); 59 | } 60 | 61 | return this.#valueInstance; 62 | } 63 | 64 | *[Symbol.iterator](): Iterator<[K, V]> { 65 | for (let i = 0; i < this.pairs.length; i++) { 66 | yield this.pairs[i]; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/value/src/list.ts: -------------------------------------------------------------------------------- 1 | import { create } from '@bufbuild/protobuf'; 2 | import * as Ydb from '@ydbjs/api/value' 3 | 4 | import { type Type, TypeKind } from './type.js'; 5 | import type { Value } from './value.js'; 6 | import { NullType } from './null.js'; 7 | 8 | export class ListType implements Type { 9 | readonly item: Type; 10 | #typeInstance?: Ydb.Type; 11 | 12 | constructor(item: Type) { 13 | this.item = item; 14 | } 15 | 16 | get kind(): TypeKind.LIST { 17 | return TypeKind.LIST; 18 | } 19 | 20 | encode(): Ydb.Type { 21 | if (!this.#typeInstance) { 22 | this.#typeInstance = create(Ydb.TypeSchema, { type: { case: 'listType', value: { item: this.item.encode() } } }); 23 | } 24 | 25 | return this.#typeInstance; 26 | } 27 | } 28 | 29 | export class List implements Value { 30 | readonly type: ListType; 31 | readonly items: T[] = []; 32 | #valueInstance?: Ydb.Value; 33 | 34 | constructor(...items: T[]) { 35 | let type: Type = new NullType(); 36 | 37 | for (let item of items) { 38 | type = item.type 39 | this.items.push(item); 40 | } 41 | 42 | this.type = new ListType(type); 43 | } 44 | 45 | encode(): Ydb.Value { 46 | if (!this.#valueInstance) { 47 | this.#valueInstance = create(Ydb.ValueSchema, { items: this.items.map(item => item.encode()) }); 48 | } 49 | 50 | return this.#valueInstance; 51 | } 52 | 53 | *[Symbol.iterator](): Iterator { 54 | for (let i = 0; i < this.items.length; i++) { 55 | yield this.items[i]; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/value/src/null.ts: -------------------------------------------------------------------------------- 1 | import { create } from "@bufbuild/protobuf"; 2 | import * as wkt from "@bufbuild/protobuf/wkt"; 3 | import * as Ydb from "@ydbjs/api/value"; 4 | 5 | import { type Type, TypeKind } from "./type.js"; 6 | import type { Value } from "./value.js"; 7 | 8 | export class NullType implements Type { 9 | #typeInstance?: Ydb.Type; 10 | 11 | constructor() { 12 | if (NullType.instance) { 13 | return NullType.instance; 14 | } 15 | 16 | NullType.instance = this; 17 | } 18 | 19 | private static instance: NullType; 20 | 21 | get kind(): TypeKind.NULL { 22 | return TypeKind.NULL; 23 | } 24 | 25 | encode(): Ydb.Type { 26 | if (!this.#typeInstance) { 27 | this.#typeInstance = create(Ydb.TypeSchema, { type: { case: "nullType", value: wkt.NullValue.NULL_VALUE } }); 28 | } 29 | 30 | return this.#typeInstance; 31 | } 32 | } 33 | 34 | export class Null implements Value { 35 | readonly type: NullType = new NullType(); 36 | #valueInstance?: Ydb.Value; 37 | 38 | constructor() { 39 | if (Null.instance) { 40 | return Null.instance; 41 | } 42 | 43 | Null.instance = this; 44 | } 45 | 46 | private static instance: Null; 47 | 48 | encode(): Ydb.Value { 49 | if (!this.#valueInstance) { 50 | this.#valueInstance = create(Ydb.ValueSchema, { value: { case: "nullFlagValue", value: wkt.NullValue.NULL_VALUE } }); 51 | } 52 | 53 | return this.#valueInstance; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/value/src/optional.ts: -------------------------------------------------------------------------------- 1 | import { type MessageInitShape, create } from "@bufbuild/protobuf"; 2 | import type { GenMessage } from "@bufbuild/protobuf/codegenv1"; 3 | import * as Ydb from "@ydbjs/api/value"; 4 | 5 | import { Null } from "./null.js"; 6 | import { type Type, TypeKind } from "./type.js"; 7 | import type { Value } from "./value.js"; 8 | 9 | export class OptionalType implements Type { 10 | readonly itemType: Type; 11 | #type: MessageInitShape>; 12 | #typeInstance?: Ydb.Type; 13 | 14 | constructor(itemType: Type) { 15 | this.itemType = itemType; 16 | this.#type = { type: { case: "optionalType", value: { item: itemType.encode() } } }; 17 | } 18 | 19 | get kind(): TypeKind.OPTIONAL { 20 | return TypeKind.OPTIONAL; 21 | } 22 | 23 | encode(): Ydb.Type { 24 | if (!this.#typeInstance) { 25 | this.#typeInstance = create(Ydb.TypeSchema, this.#type); 26 | } 27 | 28 | return this.#typeInstance; 29 | } 30 | } 31 | 32 | export class Optional implements Value { 33 | readonly type: OptionalType; 34 | readonly item: Value | null; 35 | #valueInstance?: Ydb.Value; 36 | 37 | constructor(item: Value | null, itemType?: T) { 38 | if ((!itemType || itemType instanceof Null) && !item?.type) { 39 | throw new Error("Missing item type for optional value. Please provide an item type. Or provide an item with a type."); 40 | } 41 | 42 | this.item = item; 43 | this.type = new OptionalType((item?.type || itemType) as Type); 44 | } 45 | 46 | encode(): Ydb.Value { 47 | if (!this.#valueInstance) { 48 | this.#valueInstance = create(Ydb.ValueSchema, this.item === null ? new Null().encode() : this.item.encode()); 49 | } 50 | 51 | return this.#valueInstance; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/value/src/print.ts: -------------------------------------------------------------------------------- 1 | import * as Ydb from '@ydbjs/api/value' 2 | 3 | import type { DictType } from './dict.js' 4 | import type { ListType } from './list.js' 5 | import type { OptionalType } from './optional.ts' 6 | import type { PrimitiveType } from './primitive.js' 7 | import type { StructType } from './struct.js' 8 | import type { TupleType } from './tuple.js' 9 | import { type Type, TypeKind } from './type.js' 10 | 11 | function primitiveTypeToString(pTypeId: Ydb.Type_PrimitiveTypeId) { 12 | switch (pTypeId) { 13 | case Ydb.Type_PrimitiveTypeId.BOOL: 14 | return 'Bool' 15 | case Ydb.Type_PrimitiveTypeId.UINT8: 16 | return 'Uint8' 17 | case Ydb.Type_PrimitiveTypeId.UINT16: 18 | return 'Uint16' 19 | case Ydb.Type_PrimitiveTypeId.UINT32: 20 | return 'Uint32' 21 | case Ydb.Type_PrimitiveTypeId.UINT64: 22 | return 'Uint64' 23 | case Ydb.Type_PrimitiveTypeId.INT8: 24 | return 'Int8' 25 | case Ydb.Type_PrimitiveTypeId.INT16: 26 | return 'Int16' 27 | case Ydb.Type_PrimitiveTypeId.INT32: 28 | return 'Int32' 29 | case Ydb.Type_PrimitiveTypeId.INT64: 30 | return 'Int64' 31 | case Ydb.Type_PrimitiveTypeId.FLOAT: 32 | return 'Float' 33 | case Ydb.Type_PrimitiveTypeId.DOUBLE: 34 | return 'Double' 35 | case Ydb.Type_PrimitiveTypeId.STRING: 36 | return 'String' 37 | case Ydb.Type_PrimitiveTypeId.UTF8: 38 | return 'Utf8' 39 | case Ydb.Type_PrimitiveTypeId.YSON: 40 | return 'Yson' 41 | case Ydb.Type_PrimitiveTypeId.JSON: 42 | return 'Json' 43 | case Ydb.Type_PrimitiveTypeId.JSON_DOCUMENT: 44 | return 'JsonDocument' 45 | case Ydb.Type_PrimitiveTypeId.UUID: 46 | return 'Uuid' 47 | case Ydb.Type_PrimitiveTypeId.DATE: 48 | return 'Date' 49 | case Ydb.Type_PrimitiveTypeId.TZ_DATE: 50 | return 'TzDate' 51 | case Ydb.Type_PrimitiveTypeId.DATETIME: 52 | return 'Datetime' 53 | case Ydb.Type_PrimitiveTypeId.TZ_DATETIME: 54 | return 'TzDatetime' 55 | case Ydb.Type_PrimitiveTypeId.INTERVAL: 56 | return 'Interval' 57 | case Ydb.Type_PrimitiveTypeId.TIMESTAMP: 58 | return 'Timestamp' 59 | case Ydb.Type_PrimitiveTypeId.TZ_TIMESTAMP: 60 | return 'TzTimestamp' 61 | case Ydb.Type_PrimitiveTypeId.DYNUMBER: 62 | return 'DyNumber' 63 | default: 64 | throw new Error(`Unknown primitive type id: ${pTypeId}`) 65 | } 66 | } 67 | 68 | export function typeToString(type: Type): string { 69 | switch (type.kind) { 70 | case TypeKind.PRIMITIVE: 71 | return primitiveTypeToString((type as PrimitiveType).id) 72 | case TypeKind.DECIMAL: 73 | return 'Decimal' 74 | case TypeKind.LIST: 75 | return `List<${typeToString((type as ListType).item)}>` 76 | case TypeKind.DICT: 77 | return `Dict<${typeToString((type as DictType).key)},${typeToString((type as DictType).value)}>` 78 | case TypeKind.TUPLE: 79 | return `Tuple<${(type as TupleType).elements.map((element) => typeToString(element)).join(',')}>` 80 | case TypeKind.STRUCT: 81 | return `Struct<${(type as StructType).names.map((name, index) => `${name}:${typeToString((type as StructType).types[index])}`).join(',')}>` 82 | case TypeKind.OPTIONAL: 83 | return `Optional<${typeToString((type as OptionalType).itemType)}>` 84 | case TypeKind.NULL: 85 | return 'Null' 86 | } 87 | 88 | throw new Error(`Unsupported type: ${type}`) 89 | } 90 | -------------------------------------------------------------------------------- /packages/value/src/struct.ts: -------------------------------------------------------------------------------- 1 | import { create } from '@bufbuild/protobuf' 2 | import * as Ydb from '@ydbjs/api/value' 3 | 4 | import { Optional } from './optional.js' 5 | import { type Type, TypeKind } from './type.js' 6 | import type { Value } from './value.js' 7 | 8 | export class StructType implements Type { 9 | readonly names: string[] = [] 10 | readonly types: Type[] = [] 11 | #typeInstance?: Ydb.Type 12 | 13 | constructor(names: string[], types: Type[], sorted = false) { 14 | if (sorted) { 15 | this.names = names 16 | this.types = types 17 | 18 | return 19 | } 20 | 21 | const indices = names.map((_, i) => i) 22 | indices.sort((a, b) => names[a].localeCompare(names[b])) 23 | 24 | for (let i of indices) { 25 | this.names.push(names[i]) 26 | this.types.push(types[i]) 27 | } 28 | } 29 | 30 | get kind(): TypeKind.STRUCT { 31 | return TypeKind.STRUCT 32 | } 33 | 34 | encode(): Ydb.Type { 35 | if (!this.#typeInstance) { 36 | let members: { name: string; type: Ydb.Type }[] = [] 37 | for (let i = 0; i < this.names.length; i++) { 38 | members.push({ name: this.names[i], type: this.types[i].encode() }) 39 | } 40 | 41 | this.#typeInstance = create(Ydb.TypeSchema, { type: { case: 'structType', value: { members } } }) 42 | } 43 | 44 | return this.#typeInstance 45 | } 46 | 47 | *[Symbol.iterator](): Iterator<[string, Type]> { 48 | for (let i = 0; i < this.names.length; i++) { 49 | yield [this.names[i], this.types[i]] 50 | } 51 | } 52 | } 53 | 54 | export class Struct = Record> implements Value { 55 | readonly type: StructType 56 | readonly items: Value[] = [] 57 | #valueInstance?: Ydb.Value 58 | 59 | constructor(obj: T, def?: StructType) { 60 | if (def) { 61 | this.type = def 62 | 63 | for (let [name, type] of def) { 64 | let value = obj[name] 65 | if (value && value.type.kind !== type.kind) { 66 | throw new Error(`Invalid type for ${name}: expected ${type.kind}, got ${value.type.kind}`) 67 | } 68 | 69 | this.items.push(new Optional((value ??= null), type)) 70 | } 71 | 72 | return 73 | } 74 | 75 | let keys = Object.keys(obj) 76 | let names: string[] = [] 77 | let types: Type[] = [] 78 | 79 | // Sort both arrays based on names 80 | const indices = keys.map((_, i) => i) 81 | indices.sort((a, b) => keys[a].localeCompare(keys[b])) 82 | 83 | for (let i of indices) { 84 | if (obj[keys[i]] === null) { 85 | throw new Error(`Invalid value for ${keys[i]}: expected a value, got null. Please provide a type definition.`) 86 | } 87 | 88 | names.push(keys[i]) 89 | types.push(obj[keys[i]]!.type) 90 | this.items.push(obj[keys[i]]!) 91 | } 92 | 93 | this.type = new StructType(names, types, true) 94 | } 95 | 96 | encode(): Ydb.Value { 97 | if (!this.#valueInstance) { 98 | this.#valueInstance = create(Ydb.ValueSchema, { items: this.items.map((i) => i.encode()) }) 99 | } 100 | 101 | return this.#valueInstance 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packages/value/src/tuple.ts: -------------------------------------------------------------------------------- 1 | import { create } from '@bufbuild/protobuf' 2 | import * as Ydb from '@ydbjs/api/value' 3 | 4 | import { type Type, TypeKind } from './type.js' 5 | import type { Value } from './value.js' 6 | 7 | export class TupleType implements Type { 8 | readonly elements: Type[] 9 | #typeInstance?: Ydb.Type 10 | 11 | constructor(elements: Type[]) { 12 | this.elements = elements 13 | } 14 | 15 | get kind(): TypeKind.TUPLE { 16 | return TypeKind.TUPLE 17 | } 18 | 19 | encode(): Ydb.Type { 20 | if (!this.#typeInstance) { 21 | this.#typeInstance = create(Ydb.TypeSchema, { 22 | type: { case: 'tupleType', value: { elements: this.elements.map((e) => e.encode()) } }, 23 | }) 24 | } 25 | 26 | return this.#typeInstance 27 | } 28 | } 29 | 30 | export class Tuple implements Value { 31 | readonly type: TupleType 32 | readonly items: T[] = [] 33 | #valueInstance?: Ydb.Value 34 | 35 | constructor(...items: T[]) { 36 | let elements: Type[] = [] 37 | 38 | for (let item of items) { 39 | this.items.push(item) 40 | elements.push(item.type) 41 | } 42 | 43 | this.type = new TupleType(elements) 44 | } 45 | 46 | encode(): Ydb.Value { 47 | if (!this.#valueInstance) { 48 | this.#valueInstance = create(Ydb.ValueSchema, { items: this.items.map((item) => item.encode()) }) 49 | } 50 | 51 | return this.#valueInstance 52 | } 53 | 54 | *[Symbol.iterator](): Iterator { 55 | for (let i = 0; i < this.items.length; i++) { 56 | yield this.items[i] 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/value/src/type.ts: -------------------------------------------------------------------------------- 1 | import * as Ydb from '@ydbjs/api/value' 2 | 3 | export enum TypeKind { 4 | PRIMITIVE = 1, 5 | DECIMAL = 2, 6 | OPTIONAL = 3, 7 | LIST = 4, 8 | TUPLE = 5, 9 | STRUCT = 6, 10 | DICT = 7, 11 | VARIANT = 8, 12 | VOID = 9, 13 | NULL = 10, 14 | PG_TYPE = 11, 15 | } 16 | 17 | export interface Type { 18 | kind: TypeKind 19 | encode(): Ydb.Type 20 | } 21 | -------------------------------------------------------------------------------- /packages/value/src/uuid.ts: -------------------------------------------------------------------------------- 1 | export function uuidFromBigInts(low128: bigint, high128: bigint = 0n): string { 2 | // Create a 16-byte buffer 3 | let bytes = Buffer.alloc(16); 4 | 5 | // Write low128 and high128 values to the buffer in little-endian format 6 | bytes.writeBigUInt64LE(low128, 0); 7 | bytes.writeBigUInt64LE(high128, 8); 8 | 9 | // Swap byte order for the first three fields of the UUID (little-endian to big-endian) 10 | // First 4 bytes (indices 0-3) 11 | bytes[0] ^= bytes[3]; 12 | bytes[3] ^= bytes[0]; 13 | bytes[0] ^= bytes[3]; 14 | 15 | bytes[1] ^= bytes[2]; 16 | bytes[2] ^= bytes[1]; 17 | bytes[1] ^= bytes[2]; 18 | 19 | // Next 2 bytes (indices 4-5) 20 | bytes[4] ^= bytes[5]; 21 | bytes[5] ^= bytes[4]; 22 | bytes[4] ^= bytes[5]; 23 | 24 | // Another 2 bytes (indices 6-7) 25 | bytes[6] ^= bytes[7]; 26 | bytes[7] ^= bytes[6]; 27 | bytes[6] ^= bytes[7]; 28 | 29 | // Convert the buffer to a hexadecimal string 30 | let hex = bytes.toString('hex'); 31 | 32 | // Form the UUID string 33 | return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; 34 | } 35 | 36 | export function bigIntsFromUuid(uuid: string): { low128: bigint, high128: bigint } { 37 | // Remove dashes from the UUID string 38 | let hex = uuid.replaceAll("-", ''); 39 | 40 | // Create a buffer from the hexadecimal string 41 | let bytes = Buffer.from(hex, 'hex'); 42 | 43 | // Swap byte order for the first three fields of the UUID (big-endian to little-endian) 44 | // First 4 bytes (indices 0-3) 45 | bytes[0] ^= bytes[3]; 46 | bytes[3] ^= bytes[0]; 47 | bytes[0] ^= bytes[3]; 48 | 49 | bytes[1] ^= bytes[2]; 50 | bytes[2] ^= bytes[1]; 51 | bytes[1] ^= bytes[2]; 52 | 53 | // Next 2 bytes (indices 4-5) 54 | bytes[4] ^= bytes[5]; 55 | bytes[5] ^= bytes[4]; 56 | bytes[4] ^= bytes[5]; 57 | 58 | // Another 2 bytes (indices 6-7) 59 | bytes[6] ^= bytes[7]; 60 | bytes[7] ^= bytes[6]; 61 | bytes[6] ^= bytes[7]; 62 | 63 | // Read low128 and high128 values from the buffer in little-endian format 64 | let low128 = bytes.readBigUInt64LE(0); 65 | let high128 = bytes.readBigUInt64LE(8); 66 | 67 | return { low128, high128 }; 68 | } 69 | -------------------------------------------------------------------------------- /packages/value/src/value.ts: -------------------------------------------------------------------------------- 1 | import * as Ydb from "@ydbjs/api/value"; 2 | 3 | import type { Type } from "./type.js"; 4 | 5 | export interface Value { 6 | type: T; 7 | encode(): Ydb.Value 8 | } 9 | -------------------------------------------------------------------------------- /packages/value/tests/list.test.ts: -------------------------------------------------------------------------------- 1 | import { test } from 'vitest' 2 | 3 | import { List } from '../dist/esm/list.js' 4 | import { Uint32 } from '../dist/esm/primitive.js' 5 | 6 | test('empty list', async (t) => { 7 | let list = new List() 8 | 9 | t.expect(list).toMatchInlineSnapshot(` 10 | List { 11 | "items": [], 12 | "type": ListType { 13 | "item": NullType {}, 14 | }, 15 | } 16 | `) 17 | }) 18 | 19 | test('list of values', async (t) => { 20 | let list = new List(new Uint32(1), new Uint32(2)) 21 | 22 | t.expect(list).toMatchInlineSnapshot(` 23 | List { 24 | "items": [ 25 | Uint32 { 26 | "type": Uint32Type {}, 27 | "value": 1, 28 | }, 29 | Uint32 { 30 | "type": Uint32Type {}, 31 | "value": 2, 32 | }, 33 | ], 34 | "type": ListType { 35 | "item": Uint32Type {}, 36 | }, 37 | } 38 | `) 39 | }) 40 | -------------------------------------------------------------------------------- /packages/value/tests/primitive.test.ts: -------------------------------------------------------------------------------- 1 | import { test } from 'vitest' 2 | import { 3 | Bool, 4 | Bytes, 5 | Date, 6 | Datetime, 7 | Double, 8 | Int32, 9 | Int64, 10 | Text, 11 | Timestamp, 12 | Uint32, 13 | Uint64, 14 | Uuid, 15 | } from '../dist/esm/primitive.js' 16 | 17 | test('Bool primitive', async (t) => { 18 | const boolTrue = new Bool(true) 19 | const boolFalse = new Bool(false) 20 | 21 | t.expect(boolTrue).toMatchInlineSnapshot(` 22 | Bool { 23 | "type": BoolType {}, 24 | "value": true, 25 | } 26 | `) 27 | t.expect(boolFalse).toMatchInlineSnapshot(` 28 | Bool { 29 | "type": BoolType {}, 30 | "value": false, 31 | } 32 | `) 33 | }) 34 | 35 | test('Int32 primitive', async (t) => { 36 | const int = new Int32(42) 37 | 38 | t.expect(int).toMatchInlineSnapshot(` 39 | Int32 { 40 | "type": Int32Type {}, 41 | "value": 42, 42 | } 43 | `) 44 | }) 45 | 46 | test('Uint32 primitive', async (t) => { 47 | const uint = new Uint32(42) 48 | 49 | t.expect(uint).toMatchInlineSnapshot(` 50 | Uint32 { 51 | "type": Uint32Type {}, 52 | "value": 42, 53 | } 54 | `) 55 | }) 56 | 57 | test('Text primitive', async (t) => { 58 | const text = new Text('hello') 59 | 60 | t.expect(text).toMatchInlineSnapshot(` 61 | Text { 62 | "type": TextType {}, 63 | "value": "hello", 64 | } 65 | `) 66 | }) 67 | 68 | test('Double primitive', async (t) => { 69 | const double = new Double(3.14) 70 | 71 | t.expect(double).toMatchInlineSnapshot(` 72 | Double { 73 | "type": DoubleType {}, 74 | "value": 3.14, 75 | } 76 | `) 77 | }) 78 | 79 | test('Bytes primitive', async (t) => { 80 | const bytes = new Bytes(new Uint8Array([0x01, 0x02, 0x03])) 81 | 82 | t.expect(bytes).toMatchInlineSnapshot(` 83 | Bytes { 84 | "type": BytesType {}, 85 | "value": Uint8Array [ 86 | 1, 87 | 2, 88 | 3, 89 | ], 90 | } 91 | `) 92 | }) 93 | 94 | test('Int64 primitive', async (t) => { 95 | const int64 = new Int64(9007199254740991n) 96 | 97 | t.expect(int64).toMatchInlineSnapshot(` 98 | Int64 { 99 | "type": Int64Type {}, 100 | "value": 9007199254740991n, 101 | } 102 | `) 103 | }) 104 | 105 | test('Uint64 primitive', async (t) => { 106 | const uint64 = new Uint64(9007199254740991n) 107 | 108 | t.expect(uint64).toMatchInlineSnapshot(` 109 | Uint64 { 110 | "type": Uint64Type {}, 111 | "value": 9007199254740991n, 112 | } 113 | `) 114 | }) 115 | 116 | test('Date primitive', async (t) => { 117 | const date = new Date(new globalThis.Date('2025-01-01')) 118 | 119 | t.expect(date).toMatchInlineSnapshot(` 120 | Date { 121 | "type": DateType {}, 122 | "value": 20089, 123 | } 124 | `) 125 | }) 126 | 127 | test('Datetime primitive', async (t) => { 128 | const datetime = new Datetime(new globalThis.Date('2025-01-01T00:00:00Z')) 129 | 130 | t.expect(datetime).toMatchInlineSnapshot(` 131 | Datetime { 132 | "type": DatetimeType {}, 133 | "value": 1735689600, 134 | } 135 | `) 136 | }) 137 | 138 | test('Timestamp primitive', async (t) => { 139 | const timestamp = new Timestamp(new globalThis.Date('2025-01-01T00:00:00Z')) 140 | 141 | t.expect(timestamp).toMatchInlineSnapshot(` 142 | Timestamp { 143 | "type": TimestampType {}, 144 | "value": 1735689600000000n, 145 | } 146 | `) 147 | }) 148 | 149 | test('UUID primitive', async (t) => { 150 | const uuid = new Uuid('00112233-4455-6677-8899-aabbccddeeff') 151 | 152 | t.expect(uuid).toMatchInlineSnapshot(` 153 | Uuid { 154 | "high128": 18441921395520346504n, 155 | "low128": 7383445245961249331n, 156 | "type": UuidType {}, 157 | "value": 7383445245961249331n, 158 | } 159 | `) 160 | 161 | t.expect(uuid.toString()).toBe('00112233-4455-6677-8899-aabbccddeeff') 162 | }) 163 | -------------------------------------------------------------------------------- /packages/value/tests/uudi.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import { bigIntsFromUuid, uuidFromBigInts } from "../dist/esm/uuid.js"; 4 | import { Uuid } from "../dist/esm/primitive.js"; 5 | 6 | test('UUID', () => { 7 | let uuid = '123e4567-e89b-12d3-a456-426614174000'; 8 | let { low128, high128 } = bigIntsFromUuid(uuid); 9 | 10 | expect(uuid).toEqual(uuidFromBigInts(low128, high128)); 11 | }) 12 | 13 | test('YDB Uuid Type', () => { 14 | let uuid = new Uuid('123e4567-e89b-12d3-a456-426614174000'); 15 | expect(uuid.toString()).toEqual('123e4567-e89b-12d3-a456-426614174000'); 16 | }) 17 | -------------------------------------------------------------------------------- /packages/value/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "exclude": [ 7 | "vitest.config.ts", 8 | "**/*.test.ts", 9 | "dist/**/*" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/value/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineProject } from 'vitest/config' 2 | 3 | export default defineProject({ 4 | test: { 5 | exclude: ['**/*.e2e.test.ts'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "dependencies": { 4 | "@opentelemetry/exporter-metrics-otlp-http": "^0.200.0", 5 | "@opentelemetry/exporter-prometheus": "^0.200.0", 6 | "@opentelemetry/sdk-metrics": "^2.0.0", 7 | "@ydbjs/api": "../packages/api", 8 | "@ydbjs/core": "../packages/core", 9 | "@ydbjs/query": "../packages/query", 10 | "@ydbjs/value": "../packages/value" 11 | }, 12 | "overrides": { 13 | "@grpc/proto-loader": "npm:dry-uninstall" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/slo/telemetry.ts: -------------------------------------------------------------------------------- 1 | import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'; 2 | import { AggregationType, ConsoleMetricExporter, MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; 3 | 4 | 5 | export const meterProvider = new MeterProvider({ 6 | readers: [ 7 | new PeriodicExportingMetricReader({ 8 | exporter: new ConsoleMetricExporter({}), 9 | exportIntervalMillis: 30 * 1000, 10 | }), 11 | new PrometheusExporter({ 12 | prefix: 'ydbjs', 13 | appendTimestamp: true, 14 | }), 15 | ], 16 | views: [ 17 | { 18 | instrumentUnit: "seconds", 19 | aggregation: { 20 | type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, 21 | options: { 22 | recordMinMax: true, 23 | boundaries: [ 24 | 0.005, // 5 ms 25 | 0.010, // 10 ms 26 | 0.015, // 15 ms 27 | 0.020, // 20 ms 28 | 0.025, // 25 ms 29 | 0.050, // 50 ms 30 | 0.075, // 75 ms 31 | 0.100, // 100 ms 32 | 0.150, // 150 ms 33 | 0.200, // 200 ms 34 | 0.250, // 250 ms 35 | 0.500, // 500 ms 36 | 0.750, // 750 ms 37 | 1.000, // 1 s 38 | 1.500, // 1 s 500 ms 39 | 2.000, // 2 s 40 | 5.000, // 5 s 41 | 10.000, // 10 s 42 | 30.000, // 30 s 43 | 60.000, // 60 s 44 | ] 45 | } 46 | } 47 | } 48 | ] 49 | }); 50 | 51 | ['SIGINT', 'SIGTERM'].forEach(signal => { 52 | process.on(signal, () => meterProvider.shutdown().catch(console.error)); 53 | }); 54 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // We target ES2020, but require the Text Encoding API, BigInt and AsyncIterable, see below. 4 | "target": "ES2020", 5 | "lib": [ 6 | // DOM for the HeadersInit type. 7 | "DOM", 8 | "ES2021", 9 | "ES2022.Error", 10 | // ES2024.Promise for the Promise.withResolvers(). 11 | "ES2024.Promise", 12 | ], 13 | "rewriteRelativeImportExtensions": true, 14 | "declaration": true, 15 | "types": [ 16 | "node" 17 | ], 18 | // We don't have dependencies that require interop 19 | "esModuleInterop": false, 20 | // As strict as possible 21 | "strict": true, 22 | "skipLibCheck": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "noImplicitAny": true, 25 | "strictNullChecks": true, 26 | "strictFunctionTypes": true, 27 | "strictBindCallApply": true, 28 | "strictPropertyInitialization": true, 29 | "noImplicitThis": true, 30 | "useUnknownInCatchVariables": true, 31 | "noUnusedLocals": true, 32 | "noImplicitReturns": true, 33 | "noFallthroughCasesInSwitch": true, 34 | "noImplicitOverride": true, 35 | // We're building with Node16 module resolution 36 | "moduleResolution": "Node16", 37 | "module": "Node16", 38 | "verbatimModuleSyntax": true, 39 | }, 40 | "exclude": [ 41 | "node_modules" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "tasks": { 4 | "codegen": { 5 | "dependsOn": [ 6 | "^codegen" 7 | ], 8 | "outputs": [ 9 | "./src/gen/**" 10 | ] 11 | }, 12 | "clean:esm": { 13 | "outputs": [ 14 | "./dist/esm/**" 15 | ] 16 | }, 17 | "build:esm": { 18 | "dependsOn": [ 19 | "^build:esm", 20 | "codegen", 21 | "clean:esm" 22 | ], 23 | "inputs": [ 24 | "./src/**" 25 | ], 26 | "outputs": [ 27 | "./dist/esm/**" 28 | ] 29 | }, 30 | "clean:cjs": { 31 | "outputs": [ 32 | "./dist/cjs/**" 33 | ] 34 | }, 35 | "build:cjs": { 36 | "dependsOn": [ 37 | "^build:cjs", 38 | "codegen", 39 | "clean:cjs" 40 | ], 41 | "inputs": [ 42 | "./src/**" 43 | ], 44 | "outputs": [ 45 | "./dist/cjs/**" 46 | ] 47 | }, 48 | "clean": { 49 | "dependsOn": [ 50 | "clean:esm", 51 | "clean:cjs" 52 | ] 53 | }, 54 | "build": { 55 | "dependsOn": [ 56 | "clean", 57 | "build:esm", 58 | "build:cjs" 59 | ] 60 | }, 61 | "attw": { 62 | "dependsOn": [ 63 | "^attw", 64 | "build" 65 | ], 66 | "inputs": [ 67 | "./dist/**", 68 | "package.json" 69 | ] 70 | }, 71 | "//#test": { 72 | "env": [ 73 | "DEBUG", 74 | "GRPC_TRACE", 75 | "GRPC_VERBOSITY", 76 | "YDB_STATIC_CREDENTIALS_USER", 77 | "YDB_STATIC_CREDENTIALS_PASSWORD", 78 | "YDB_STATIC_CREDENTIALS_ENDPOINT", 79 | "YDB_CONNECTION_STRING", 80 | "YDB_CONNECTION_STRING_SECURE", 81 | "YDB_SSL_ROOT_CERTIFICATES_FILE" 82 | ], 83 | "outputs": [ 84 | "coverage/**" 85 | ] 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | projects: [ 6 | 'packages/*', 7 | 'third-party/*', 8 | { 9 | extends: true, 10 | test: { 11 | name: 'e2e', 12 | include: ['**/*.e2e.test.ts'], 13 | globalSetup: './vitest.setup.e2e.js', 14 | }, 15 | }, 16 | ], 17 | passWithNoTests: true, 18 | }, 19 | }) 20 | -------------------------------------------------------------------------------- /vitest.setup.e2e.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable no-await-in-loop 2 | import type { TestProject } from 'vitest/node' 3 | import { $ } from 'zx' 4 | 5 | declare module 'vitest' { 6 | export interface ProvidedContext { 7 | connectionString: string 8 | credentialsUsername: string 9 | credentialsPassword: string 10 | credentialsEndpoint: string 11 | } 12 | } 13 | 14 | let configured = false 15 | 16 | /** 17 | * Sets up the test project by providing necessary environment variables for YDB connection. 18 | * If the YDB_CONNECTION_STRING environment variable is set, it uses the provided credentials. 19 | * Otherwise, it starts a local YDB Docker container, waits for it to become healthy, and then provides 20 | * the connection details. 21 | * 22 | * @param project - The test project to configure with YDB connection details. 23 | */ 24 | export async function setup(project: TestProject) { 25 | if (process.env['YDB_CONNECTION_STRING']) { 26 | project.provide('connectionString', process.env['YDB_CONNECTION_STRING']) 27 | project.provide('credentialsUsername', process.env['YDB_STATIC_CREDENTIALS_USER']!) 28 | project.provide('credentialsPassword', process.env['YDB_STATIC_CREDENTIALS_PASSWORD']!) 29 | project.provide('credentialsEndpoint', process.env['YDB_STATIC_CREDENTIALS_ENDPOINT']!) 30 | 31 | return 32 | } 33 | 34 | let containerID = await $`docker run --rm --detach --publish 2135 --publish 2136 --publish 8765 --publish 9092 --hostname localhost --platform linux/amd64 ydbplatform/local-ydb:25.1.1.3`.text() 35 | containerID = containerID.trim() 36 | 37 | let signal = AbortSignal.timeout(30 * 1000) 38 | while ((await $`docker inspect -f {{.State.Health.Status}} ${containerID}`.text()).trim() !== 'healthy') { 39 | signal.throwIfAborted() 40 | await $`sleep 1` 41 | } 42 | 43 | let [ipv4, _ipv6] = await $`docker port ${containerID} 2136/tcp`.lines() 44 | 45 | project.provide('connectionString', `grpc://${ipv4}/local`) 46 | project.provide('credentialsUsername', 'root') 47 | project.provide('credentialsPassword', '1234') 48 | project.provide('credentialsEndpoint', `grpc://${ipv4}`) 49 | 50 | configured = true 51 | } 52 | 53 | /** 54 | * Tears down the YDB Docker container if it has been configured. 55 | * 56 | * This function checks if the YDB environment has been configured. 57 | * If configured, it removes the YDB Docker container forcefully. 58 | */ 59 | export async function teardown() { 60 | if (!configured) { 61 | return 62 | } 63 | 64 | await $`docker rm -f ydb` 65 | } 66 | --------------------------------------------------------------------------------