diff --git a/.github/workflows/studio-security-audit.yml b/.github/workflows/studio-security-audit.yml
index c0428a025a..8c34311c1d 100644
--- a/.github/workflows/studio-security-audit.yml
+++ b/.github/workflows/studio-security-audit.yml
@@ -146,7 +146,7 @@ jobs:
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: "Dependency Review"
- uses: actions/dependency-review-action@595b5aeba73380359d98a5e087f648dbb0edce1b # v4.7.3
+ uses: actions/dependency-review-action@bc41886e18ea39df68b1b1245f4184881938e050 # v4.7.2
with:
fail-on-severity: moderate
allow-dependencies-licenses: "MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, 0BSD"
diff --git a/e2e-js/package-lock.json b/e2e-js/package-lock.json
index ca0a6a7a77..026a23e709 100644
--- a/e2e-js/package-lock.json
+++ b/e2e-js/package-lock.json
@@ -57,22 +57,22 @@
}
},
"node_modules/@babel/core": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
- "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
+ "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
+ "@babel/generator": "^7.28.0",
"@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.28.3",
- "@babel/helpers": "^7.28.3",
- "@babel/parser": "^7.28.3",
+ "@babel/helper-module-transforms": "^7.27.3",
+ "@babel/helpers": "^7.27.6",
+ "@babel/parser": "^7.28.0",
"@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/traverse": "^7.28.0",
+ "@babel/types": "^7.28.0",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -88,14 +88,14 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
- "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
+ "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/parser": "^7.28.0",
+ "@babel/types": "^7.28.0",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -146,15 +146,15 @@
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
- "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "version": "7.27.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
+ "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.28.3"
+ "@babel/traverse": "^7.27.3"
},
"engines": {
"node": ">=6.9.0"
@@ -204,9 +204,9 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
- "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
+ "version": "7.28.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz",
+ "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -218,13 +218,13 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
- "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
+ "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.2"
+ "@babel/types": "^7.28.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -488,18 +488,18 @@
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
- "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
+ "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
+ "@babel/generator": "^7.28.0",
"@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.3",
+ "@babel/parser": "^7.28.0",
"@babel/template": "^7.27.2",
- "@babel/types": "^7.28.2",
+ "@babel/types": "^7.28.0",
"debug": "^4.3.1"
},
"engines": {
@@ -534,21 +534,21 @@
"license": "MIT"
},
"node_modules/@emnapi/core": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
- "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==",
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz",
+ "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/wasi-threads": "1.1.0",
+ "@emnapi/wasi-threads": "1.0.4",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz",
- "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==",
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
+ "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -557,9 +557,9 @@
}
},
"node_modules/@emnapi/wasi-threads": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
- "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz",
+ "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -729,16 +729,16 @@
}
},
"node_modules/@jest/console": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.1.1.tgz",
- "integrity": "sha512-f7TGqR1k4GtN5pyFrKmq+ZVndesiwLU33yDpJIGMS9aW+j6hKjue7ljeAdznBsH9kAnxUWe2Y+Y3fLV/FJt3gA==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.5.tgz",
+ "integrity": "sha512-xY6b0XiL0Nav3ReresUarwl2oIz1gTnxGbGpho9/rbUWsLH0f1OD/VT84xs8c7VmH7MChnLb0pag6PhZhAdDiA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "30.0.5",
"@types/node": "*",
"chalk": "^4.1.2",
- "jest-message-util": "30.1.0",
+ "jest-message-util": "30.0.5",
"jest-util": "30.0.5",
"slash": "^3.0.0"
},
@@ -747,17 +747,17 @@
}
},
"node_modules/@jest/core": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.1.1.tgz",
- "integrity": "sha512-3ncU9peZ3D2VdgRkdZtUceTrDgX5yiDRwAFjtxNfU22IiZrpVWlv/FogzDLYSJQptQGfFo3PcHK86a2oG6WUGg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.5.tgz",
+ "integrity": "sha512-fKD0OulvRsXF1hmaFgHhVJzczWzA1RXMMo9LTPuFXo9q/alDbME3JIyWYqovWsUBWSoBcsHaGPSLF9rz4l9Qeg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/console": "30.1.1",
+ "@jest/console": "30.0.5",
"@jest/pattern": "30.0.1",
- "@jest/reporters": "30.1.1",
- "@jest/test-result": "30.1.1",
- "@jest/transform": "30.1.1",
+ "@jest/reporters": "30.0.5",
+ "@jest/test-result": "30.0.5",
+ "@jest/transform": "30.0.5",
"@jest/types": "30.0.5",
"@types/node": "*",
"ansi-escapes": "^4.3.2",
@@ -766,18 +766,18 @@
"exit-x": "^0.2.2",
"graceful-fs": "^4.2.11",
"jest-changed-files": "30.0.5",
- "jest-config": "30.1.1",
- "jest-haste-map": "30.1.0",
- "jest-message-util": "30.1.0",
+ "jest-config": "30.0.5",
+ "jest-haste-map": "30.0.5",
+ "jest-message-util": "30.0.5",
"jest-regex-util": "30.0.1",
- "jest-resolve": "30.1.0",
- "jest-resolve-dependencies": "30.1.1",
- "jest-runner": "30.1.1",
- "jest-runtime": "30.1.1",
- "jest-snapshot": "30.1.1",
+ "jest-resolve": "30.0.5",
+ "jest-resolve-dependencies": "30.0.5",
+ "jest-runner": "30.0.5",
+ "jest-runtime": "30.0.5",
+ "jest-snapshot": "30.0.5",
"jest-util": "30.0.5",
- "jest-validate": "30.1.0",
- "jest-watcher": "30.1.1",
+ "jest-validate": "30.0.5",
+ "jest-watcher": "30.0.5",
"micromatch": "^4.0.8",
"pretty-format": "30.0.5",
"slash": "^3.0.0"
@@ -805,13 +805,13 @@
}
},
"node_modules/@jest/environment": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.1.1.tgz",
- "integrity": "sha512-yWHbU+3j7ehQE+NRpnxRvHvpUhoohIjMePBbIr8lfe0cWVb0WeTf80DNux1GPJa18CDHiIU5DtksGUfxcDE+Rw==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.5.tgz",
+ "integrity": "sha512-aRX7WoaWx1oaOkDQvCWImVQ8XNtdv5sEWgk4gxR6NXb7WBUnL5sRak4WRzIQRZ1VTWPvV4VI4mgGjNL9TeKMYA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/fake-timers": "30.1.1",
+ "@jest/fake-timers": "30.0.5",
"@jest/types": "30.0.5",
"@types/node": "*",
"jest-mock": "30.0.5"
@@ -821,43 +821,43 @@
}
},
"node_modules/@jest/expect": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.1.1.tgz",
- "integrity": "sha512-3vHIHsF+qd3D8FU2c7U5l3rg1fhDwAYcGyHyZAi94YIlTwcJ+boNhRyJf373cl4wxbOX+0Q7dF40RTrTFTSuig==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.5.tgz",
+ "integrity": "sha512-6udac8KKrtTtC+AXZ2iUN/R7dp7Ydry+Fo6FPFnDG54wjVMnb6vW/XNlf7Xj8UDjAE3aAVAsR4KFyKk3TCXmTA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "expect": "30.1.1",
- "jest-snapshot": "30.1.1"
+ "expect": "30.0.5",
+ "jest-snapshot": "30.0.5"
},
"engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
"node_modules/@jest/expect-utils": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.1.1.tgz",
- "integrity": "sha512-5YUHr27fpJ64dnvtu+tt11ewATynrHkGYD+uSFgRr8V2eFJis/vEXgToyLwccIwqBihVfz9jwio+Zr1ab1Zihw==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.5.tgz",
+ "integrity": "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/get-type": "30.1.0"
+ "@jest/get-type": "30.0.1"
},
"engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
"node_modules/@jest/fake-timers": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.1.1.tgz",
- "integrity": "sha512-fK/25dNgBNYPw3eLi2CRs57g1H04qBAFNMsUY3IRzkfx/m4THe0E1zF+yGQBOMKKc2XQVdc9EYbJ4hEm7/2UtA==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.5.tgz",
+ "integrity": "sha512-ZO5DHfNV+kgEAeP3gK3XlpJLL4U3Sz6ebl/n68Uwt64qFFs5bv4bfEEjyRGK5uM0C90ewooNgFuKMdkbEoMEXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "30.0.5",
"@sinonjs/fake-timers": "^13.0.0",
"@types/node": "*",
- "jest-message-util": "30.1.0",
+ "jest-message-util": "30.0.5",
"jest-mock": "30.0.5",
"jest-util": "30.0.5"
},
@@ -866,9 +866,9 @@
}
},
"node_modules/@jest/get-type": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz",
- "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==",
+ "version": "30.0.1",
+ "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz",
+ "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -876,14 +876,14 @@
}
},
"node_modules/@jest/globals": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.1.1.tgz",
- "integrity": "sha512-NNUUkHT2TU/xztZl6r1UXvJL+zvCwmZsQDmK69fVHHcB9fBtlu3FInnzOve/ZoyKnWY8JXWJNT+Lkmu1+ubXUA==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.5.tgz",
+ "integrity": "sha512-7oEJT19WW4oe6HR7oLRvHxwlJk2gev0U9px3ufs8sX9PoD1Eza68KF0/tlN7X0dq/WVsBScXQGgCldA1V9Y/jA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/environment": "30.1.1",
- "@jest/expect": "30.1.1",
+ "@jest/environment": "30.0.5",
+ "@jest/expect": "30.0.5",
"@jest/types": "30.0.5",
"jest-mock": "30.0.5"
},
@@ -906,16 +906,16 @@
}
},
"node_modules/@jest/reporters": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.1.1.tgz",
- "integrity": "sha512-Hb2Bq80kahOC6Sv2waEaH1rEU6VdFcM6WHaRBWQF9tf30+nJHxhl/Upbgo9+25f0mOgbphxvbwSMjSgy9gW/FA==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.5.tgz",
+ "integrity": "sha512-mafft7VBX4jzED1FwGC1o/9QUM2xebzavImZMeqnsklgcyxBto8mV4HzNSzUrryJ+8R9MFOM3HgYuDradWR+4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "30.1.1",
- "@jest/test-result": "30.1.1",
- "@jest/transform": "30.1.1",
+ "@jest/console": "30.0.5",
+ "@jest/test-result": "30.0.5",
+ "@jest/transform": "30.0.5",
"@jest/types": "30.0.5",
"@jridgewell/trace-mapping": "^0.3.25",
"@types/node": "*",
@@ -929,9 +929,9 @@
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-source-maps": "^5.0.0",
"istanbul-reports": "^3.1.3",
- "jest-message-util": "30.1.0",
+ "jest-message-util": "30.0.5",
"jest-util": "30.0.5",
- "jest-worker": "30.1.0",
+ "jest-worker": "30.0.5",
"slash": "^3.0.0",
"string-length": "^4.0.2",
"v8-to-istanbul": "^9.0.1"
@@ -962,9 +962,9 @@
}
},
"node_modules/@jest/snapshot-utils": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.1.1.tgz",
- "integrity": "sha512-TkVBc9wuN22TT8hESRFmjjg/xIMu7z0J3UDYtIRydzCqlLPTB7jK1DDBKdnTUZ4zL3z3rnPpzV6rL1Uzh87sXg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.5.tgz",
+ "integrity": "sha512-XcCQ5qWHLvi29UUrowgDFvV4t7ETxX91CbDczMnoqXPOIcZOxyNdSjm6kV5XMc8+HkxfRegU/MUmnTbJRzGrUQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -993,13 +993,13 @@
}
},
"node_modules/@jest/test-result": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.1.1.tgz",
- "integrity": "sha512-bMdj7fNu8iZuBPSnbVir5ezvWmVo4jrw7xDE+A33Yb3ENCoiJK9XgOLgal+rJ9XSKjsL7aPUMIo87zhN7I5o2w==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.5.tgz",
+ "integrity": "sha512-wPyztnK0gbDMQAJZ43tdMro+qblDHH1Ru/ylzUo21TBKqt88ZqnKKK2m30LKmLLoKtR2lxdpCC/P3g1vfKcawQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/console": "30.1.1",
+ "@jest/console": "30.0.5",
"@jest/types": "30.0.5",
"@types/istanbul-lib-coverage": "^2.0.6",
"collect-v8-coverage": "^1.0.2"
@@ -1009,15 +1009,15 @@
}
},
"node_modules/@jest/test-sequencer": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.1.1.tgz",
- "integrity": "sha512-yruRdLXSA3HYD/MTNykgJ6VYEacNcXDFRMqKVAwlYegmxICUiT/B++CNuhJnYJzKYks61iYnjVsMwbUqmmAYJg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.5.tgz",
+ "integrity": "sha512-Aea/G1egWoIIozmDD7PBXUOxkekXl7ueGzrsGGi1SbeKgQqCYCIf+wfbflEbf2LiPxL8j2JZGLyrzZagjvW4YQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/test-result": "30.1.1",
+ "@jest/test-result": "30.0.5",
"graceful-fs": "^4.2.11",
- "jest-haste-map": "30.1.0",
+ "jest-haste-map": "30.0.5",
"slash": "^3.0.0"
},
"engines": {
@@ -1025,9 +1025,9 @@
}
},
"node_modules/@jest/transform": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.1.1.tgz",
- "integrity": "sha512-PHIA2AbAASBfk6evkNifvmx9lkOSkmvaQoO6VSpuL8+kQqDMHeDoJ7RU3YP1wWAMD7AyQn9UL5iheuFYCC4lqQ==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.5.tgz",
+ "integrity": "sha512-Vk8amLQCmuZyy6GbBht1Jfo9RSdBtg7Lks+B0PecnjI8J+PCLQPGh7uI8Q/2wwpW2gLdiAfiHNsmekKlywULqg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1039,7 +1039,7 @@
"convert-source-map": "^2.0.0",
"fast-json-stable-stringify": "^2.1.0",
"graceful-fs": "^4.2.11",
- "jest-haste-map": "30.1.0",
+ "jest-haste-map": "30.0.5",
"jest-regex-util": "30.0.1",
"jest-util": "30.0.5",
"micromatch": "^4.0.8",
@@ -1071,9 +1071,9 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.13",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
- "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "version": "0.3.12",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
+ "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1092,16 +1092,16 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
+ "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.30",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
- "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
+ "version": "0.3.29",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
+ "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1221,9 +1221,9 @@
"dev": true
},
"node_modules/@sinclair/typebox": {
- "version": "0.34.41",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
- "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==",
+ "version": "0.34.38",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz",
+ "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==",
"dev": true,
"license": "MIT"
},
@@ -1294,13 +1294,13 @@
}
},
"node_modules/@types/babel__traverse": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
- "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz",
+ "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.2"
+ "@babel/types": "^7.20.7"
}
},
"node_modules/@types/docker-modem": {
@@ -1844,13 +1844,13 @@
"license": "Apache-2.0"
},
"node_modules/babel-jest": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.1.1.tgz",
- "integrity": "sha512-1bZfC/V03qBCzASvZpNFhx3Ouj6LgOd4KFJm4br/fYOS+tSSvVCE61QmcAVbMTwq/GoB7KN4pzGMoyr9cMxSvQ==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.5.tgz",
+ "integrity": "sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/transform": "30.1.1",
+ "@jest/transform": "30.0.5",
"@types/babel__core": "^7.20.5",
"babel-plugin-istanbul": "^7.0.0",
"babel-preset-jest": "30.0.1",
@@ -1898,9 +1898,9 @@
}
},
"node_modules/babel-preset-current-node-syntax": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz",
- "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz",
+ "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1921,7 +1921,7 @@
"@babel/plugin-syntax-top-level-await": "^7.14.5"
},
"peerDependencies": {
- "@babel/core": "^7.0.0 || ^8.0.0-0"
+ "@babel/core": "^7.0.0"
}
},
"node_modules/babel-preset-jest": {
@@ -2125,9 +2125,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.25.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz",
- "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==",
+ "version": "4.25.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
+ "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
"dev": true,
"funding": [
{
@@ -2145,8 +2145,8 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001737",
- "electron-to-chromium": "^1.5.211",
+ "caniuse-lite": "^1.0.30001726",
+ "electron-to-chromium": "^1.5.173",
"node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.3"
},
@@ -2250,9 +2250,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001739",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
- "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==",
+ "version": "1.0.30001727",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
+ "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
"dev": true,
"funding": [
{
@@ -2649,9 +2649,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.211",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz",
- "integrity": "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==",
+ "version": "1.5.191",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz",
+ "integrity": "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA==",
"dev": true,
"license": "ISC"
},
@@ -2783,16 +2783,16 @@
}
},
"node_modules/expect": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/expect/-/expect-30.1.1.tgz",
- "integrity": "sha512-OKe7cdic4qbfWd/CcgwJvvCrNX2KWfuMZee9AfJHL1gTYmvqjBjZG1a2NwfhspBzxzlXwsN75WWpKTYfsJpBxg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.5.tgz",
+ "integrity": "sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/expect-utils": "30.1.1",
- "@jest/get-type": "30.1.0",
- "jest-matcher-utils": "30.1.1",
- "jest-message-util": "30.1.0",
+ "@jest/expect-utils": "30.0.5",
+ "@jest/get-type": "30.0.1",
+ "jest-matcher-utils": "30.0.5",
+ "jest-message-util": "30.0.5",
"jest-mock": "30.0.5",
"jest-util": "30.0.5"
},
@@ -3225,9 +3225,9 @@
}
},
"node_modules/istanbul-reports": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
- "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+ "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -3255,16 +3255,16 @@
}
},
"node_modules/jest": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest/-/jest-30.1.1.tgz",
- "integrity": "sha512-yC3JvpP/ZcAZX5rYCtXO/g9k6VTCQz0VFE2v1FpxytWzUqfDtu0XL/pwnNvptzYItvGwomh1ehomRNMOyhCJKw==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.5.tgz",
+ "integrity": "sha512-y2mfcJywuTUkvLm2Lp1/pFX8kTgMO5yyQGq/Sk/n2mN7XWYp4JsCZ/QXW34M8YScgk8bPZlREH04f6blPnoHnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/core": "30.1.1",
+ "@jest/core": "30.0.5",
"@jest/types": "30.0.5",
"import-local": "^3.2.0",
- "jest-cli": "30.1.1"
+ "jest-cli": "30.0.5"
},
"bin": {
"jest": "bin/jest.js"
@@ -3297,26 +3297,26 @@
}
},
"node_modules/jest-circus": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.1.1.tgz",
- "integrity": "sha512-M3Vd4x5wD7eSJspuTvRF55AkOOBndRxgW3gqQBDlFvbH3X+ASdi8jc+EqXEeAFd/UHulVYIlC4XKJABOhLw6UA==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.5.tgz",
+ "integrity": "sha512-h/sjXEs4GS+NFFfqBDYT7y5Msfxh04EwWLhQi0F8kuWpe+J/7tICSlswU8qvBqumR3kFgHbfu7vU6qruWWBPug==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/environment": "30.1.1",
- "@jest/expect": "30.1.1",
- "@jest/test-result": "30.1.1",
+ "@jest/environment": "30.0.5",
+ "@jest/expect": "30.0.5",
+ "@jest/test-result": "30.0.5",
"@jest/types": "30.0.5",
"@types/node": "*",
"chalk": "^4.1.2",
"co": "^4.6.0",
"dedent": "^1.6.0",
"is-generator-fn": "^2.1.0",
- "jest-each": "30.1.0",
- "jest-matcher-utils": "30.1.1",
- "jest-message-util": "30.1.0",
- "jest-runtime": "30.1.1",
- "jest-snapshot": "30.1.1",
+ "jest-each": "30.0.5",
+ "jest-matcher-utils": "30.0.5",
+ "jest-message-util": "30.0.5",
+ "jest-runtime": "30.0.5",
+ "jest-snapshot": "30.0.5",
"jest-util": "30.0.5",
"p-limit": "^3.1.0",
"pretty-format": "30.0.5",
@@ -3329,21 +3329,21 @@
}
},
"node_modules/jest-cli": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.1.1.tgz",
- "integrity": "sha512-xm9llxuh5OoI5KZaYzlMhklryHBwg9LZy/gEaaMlXlxb+cZekGNzukU0iblbDo3XOBuN6N0CgK4ykgNRYSEb6g==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.5.tgz",
+ "integrity": "sha512-Sa45PGMkBZzF94HMrlX4kUyPOwUpdZasaliKN3mifvDmkhLYqLLg8HQTzn6gq7vJGahFYMQjXgyJWfYImKZzOw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/core": "30.1.1",
- "@jest/test-result": "30.1.1",
+ "@jest/core": "30.0.5",
+ "@jest/test-result": "30.0.5",
"@jest/types": "30.0.5",
"chalk": "^4.1.2",
"exit-x": "^0.2.2",
"import-local": "^3.2.0",
- "jest-config": "30.1.1",
+ "jest-config": "30.0.5",
"jest-util": "30.0.5",
- "jest-validate": "30.1.0",
+ "jest-validate": "30.0.5",
"yargs": "^17.7.2"
},
"bin": {
@@ -3362,31 +3362,31 @@
}
},
"node_modules/jest-config": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.1.1.tgz",
- "integrity": "sha512-xuPGUGDw+9fPPnGmddnLnHS/mhKUiJOW7K65vErYmglEPKq65NKwSRchkQ7iv6gqjs2l+YNEsAtbsplxozdOWg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.5.tgz",
+ "integrity": "sha512-aIVh+JNOOpzUgzUnPn5FLtyVnqc3TQHVMupYtyeURSb//iLColiMIR8TxCIDKyx9ZgjKnXGucuW68hCxgbrwmA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.27.4",
- "@jest/get-type": "30.1.0",
+ "@jest/get-type": "30.0.1",
"@jest/pattern": "30.0.1",
- "@jest/test-sequencer": "30.1.1",
+ "@jest/test-sequencer": "30.0.5",
"@jest/types": "30.0.5",
- "babel-jest": "30.1.1",
+ "babel-jest": "30.0.5",
"chalk": "^4.1.2",
"ci-info": "^4.2.0",
"deepmerge": "^4.3.1",
"glob": "^10.3.10",
"graceful-fs": "^4.2.11",
- "jest-circus": "30.1.1",
+ "jest-circus": "30.0.5",
"jest-docblock": "30.0.1",
- "jest-environment-node": "30.1.1",
+ "jest-environment-node": "30.0.5",
"jest-regex-util": "30.0.1",
- "jest-resolve": "30.1.0",
- "jest-runner": "30.1.1",
+ "jest-resolve": "30.0.5",
+ "jest-runner": "30.0.5",
"jest-util": "30.0.5",
- "jest-validate": "30.1.0",
+ "jest-validate": "30.0.5",
"micromatch": "^4.0.8",
"parse-json": "^5.2.0",
"pretty-format": "30.0.5",
@@ -3414,14 +3414,14 @@
}
},
"node_modules/jest-diff": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.1.1.tgz",
- "integrity": "sha512-LUU2Gx8EhYxpdzTR6BmjL1ifgOAQJQELTHOiPv9KITaKjZvJ9Jmgigx01tuZ49id37LorpGc9dPBPlXTboXScw==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.5.tgz",
+ "integrity": "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jest/diff-sequences": "30.0.1",
- "@jest/get-type": "30.1.0",
+ "@jest/get-type": "30.0.1",
"chalk": "^4.1.2",
"pretty-format": "30.0.5"
},
@@ -3443,13 +3443,13 @@
}
},
"node_modules/jest-each": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.1.0.tgz",
- "integrity": "sha512-A+9FKzxPluqogNahpCv04UJvcZ9B3HamqpDNWNKDjtxVRYB8xbZLFuCr8JAJFpNp83CA0anGQFlpQna9Me+/tQ==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.5.tgz",
+ "integrity": "sha512-dKjRsx1uZ96TVyejD3/aAWcNKy6ajMaN531CwWIsrazIqIoXI9TnnpPlkrEYku/8rkS3dh2rbH+kMOyiEIv0xQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/get-type": "30.1.0",
+ "@jest/get-type": "30.0.1",
"@jest/types": "30.0.5",
"chalk": "^4.1.2",
"jest-util": "30.0.5",
@@ -3460,28 +3460,28 @@
}
},
"node_modules/jest-environment-node": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.1.1.tgz",
- "integrity": "sha512-IaMoaA6saxnJimqCppUDqKck+LKM0Jg+OxyMUIvs1yGd2neiC22o8zXo90k04+tO+49OmgMR4jTgM5e4B0S62Q==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.5.tgz",
+ "integrity": "sha512-ppYizXdLMSvciGsRsMEnv/5EFpvOdXBaXRBzFUDPWrsfmog4kYrOGWXarLllz6AXan6ZAA/kYokgDWuos1IKDA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/environment": "30.1.1",
- "@jest/fake-timers": "30.1.1",
+ "@jest/environment": "30.0.5",
+ "@jest/fake-timers": "30.0.5",
"@jest/types": "30.0.5",
"@types/node": "*",
"jest-mock": "30.0.5",
"jest-util": "30.0.5",
- "jest-validate": "30.1.0"
+ "jest-validate": "30.0.5"
},
"engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
"node_modules/jest-haste-map": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.1.0.tgz",
- "integrity": "sha512-JLeM84kNjpRkggcGpQLsV7B8W4LNUWz7oDNVnY1Vjj22b5/fAb3kk3htiD+4Na8bmJmjJR7rBtS2Rmq/NEcADg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.5.tgz",
+ "integrity": "sha512-dkmlWNlsTSR0nH3nRfW5BKbqHefLZv0/6LCccG0xFCTWcJu8TuEwG+5Cm75iBfjVoockmO6J35o5gxtFSn5xeg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3492,7 +3492,7 @@
"graceful-fs": "^4.2.11",
"jest-regex-util": "30.0.1",
"jest-util": "30.0.5",
- "jest-worker": "30.1.0",
+ "jest-worker": "30.0.5",
"micromatch": "^4.0.8",
"walker": "^1.0.8"
},
@@ -3520,13 +3520,13 @@
}
},
"node_modules/jest-leak-detector": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.1.0.tgz",
- "integrity": "sha512-AoFvJzwxK+4KohH60vRuHaqXfWmeBATFZpzpmzNmYTtmRMiyGPVhkXpBqxUQunw+dQB48bDf4NpUs6ivVbRv1g==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.5.tgz",
+ "integrity": "sha512-3Uxr5uP8jmHMcsOtYMRB/zf1gXN3yUIc+iPorhNETG54gErFIiUhLvyY/OggYpSMOEYqsmRxmuU4ZOoX5jpRFg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/get-type": "30.1.0",
+ "@jest/get-type": "30.0.1",
"pretty-format": "30.0.5"
},
"engines": {
@@ -3534,15 +3534,15 @@
}
},
"node_modules/jest-matcher-utils": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.1.1.tgz",
- "integrity": "sha512-SuH2QVemK48BNTqReti6FtjsMPFsSOD/ZzRxU1TttR7RiRsRSe78d03bb4Cx6D4bQC/80Q8U4VnaaAH9FlbZ9w==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz",
+ "integrity": "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/get-type": "30.1.0",
+ "@jest/get-type": "30.0.1",
"chalk": "^4.1.2",
- "jest-diff": "30.1.1",
+ "jest-diff": "30.0.5",
"pretty-format": "30.0.5"
},
"engines": {
@@ -3550,9 +3550,9 @@
}
},
"node_modules/jest-message-util": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.1.0.tgz",
- "integrity": "sha512-HizKDGG98cYkWmaLUHChq4iN+oCENohQLb7Z5guBPumYs+/etonmNFlg1Ps6yN9LTPyZn+M+b/9BbnHx3WTMDg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.5.tgz",
+ "integrity": "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3614,18 +3614,18 @@
}
},
"node_modules/jest-resolve": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.1.0.tgz",
- "integrity": "sha512-hASe7D/wRtZw8Cm607NrlF7fi3HWC5wmA5jCVc2QjQAB2pTwP9eVZILGEi6OeSLNUtE1zb04sXRowsdh5CUjwA==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.5.tgz",
+ "integrity": "sha512-d+DjBQ1tIhdz91B79mywH5yYu76bZuE96sSbxj8MkjWVx5WNdt1deEFRONVL4UkKLSrAbMkdhb24XN691yDRHg==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"graceful-fs": "^4.2.11",
- "jest-haste-map": "30.1.0",
+ "jest-haste-map": "30.0.5",
"jest-pnp-resolver": "^1.2.3",
"jest-util": "30.0.5",
- "jest-validate": "30.1.0",
+ "jest-validate": "30.0.5",
"slash": "^3.0.0",
"unrs-resolver": "^1.7.11"
},
@@ -3634,30 +3634,30 @@
}
},
"node_modules/jest-resolve-dependencies": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.1.1.tgz",
- "integrity": "sha512-tRtaaoH8Ws1Gn1o/9pedt19dvVgr81WwdmvJSP9Ow3amOUOP2nN9j94u5jC9XlIfa2Q1FQKIWWQwL4ajqsjCGQ==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.5.tgz",
+ "integrity": "sha512-/xMvBR4MpwkrHW4ikZIWRttBBRZgWK4d6xt3xW1iRDSKt4tXzYkMkyPfBnSCgv96cpkrctfXs6gexeqMYqdEpw==",
"dev": true,
"license": "MIT",
"dependencies": {
"jest-regex-util": "30.0.1",
- "jest-snapshot": "30.1.1"
+ "jest-snapshot": "30.0.5"
},
"engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
"node_modules/jest-runner": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.1.1.tgz",
- "integrity": "sha512-ATe6372SOfJvCRExtCAr06I4rGujwFdKg44b6i7/aOgFnULwjxzugJ0Y4AnG+jeSeQi8dU7R6oqLGmsxRUbErQ==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.5.tgz",
+ "integrity": "sha512-JcCOucZmgp+YuGgLAXHNy7ualBx4wYSgJVWrYMRBnb79j9PD0Jxh0EHvR5Cx/r0Ce+ZBC4hCdz2AzFFLl9hCiw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/console": "30.1.1",
- "@jest/environment": "30.1.1",
- "@jest/test-result": "30.1.1",
- "@jest/transform": "30.1.1",
+ "@jest/console": "30.0.5",
+ "@jest/environment": "30.0.5",
+ "@jest/test-result": "30.0.5",
+ "@jest/transform": "30.0.5",
"@jest/types": "30.0.5",
"@types/node": "*",
"chalk": "^4.1.2",
@@ -3665,15 +3665,15 @@
"exit-x": "^0.2.2",
"graceful-fs": "^4.2.11",
"jest-docblock": "30.0.1",
- "jest-environment-node": "30.1.1",
- "jest-haste-map": "30.1.0",
- "jest-leak-detector": "30.1.0",
- "jest-message-util": "30.1.0",
- "jest-resolve": "30.1.0",
- "jest-runtime": "30.1.1",
+ "jest-environment-node": "30.0.5",
+ "jest-haste-map": "30.0.5",
+ "jest-leak-detector": "30.0.5",
+ "jest-message-util": "30.0.5",
+ "jest-resolve": "30.0.5",
+ "jest-runtime": "30.0.5",
"jest-util": "30.0.5",
- "jest-watcher": "30.1.1",
- "jest-worker": "30.1.0",
+ "jest-watcher": "30.0.5",
+ "jest-worker": "30.0.5",
"p-limit": "^3.1.0",
"source-map-support": "0.5.13"
},
@@ -3682,18 +3682,18 @@
}
},
"node_modules/jest-runtime": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.1.1.tgz",
- "integrity": "sha512-7sOyR0Oekw4OesQqqBHuYJRB52QtXiq0NNgLRzVogiMSxKCMiliUd6RrXHCnG5f12Age/ggidCBiQftzcA9XKw==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.5.tgz",
+ "integrity": "sha512-7oySNDkqpe4xpX5PPiJTe5vEa+Ak/NnNz2bGYZrA1ftG3RL3EFlHaUkA1Cjx+R8IhK0Vg43RML5mJedGTPNz3A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/environment": "30.1.1",
- "@jest/fake-timers": "30.1.1",
- "@jest/globals": "30.1.1",
+ "@jest/environment": "30.0.5",
+ "@jest/fake-timers": "30.0.5",
+ "@jest/globals": "30.0.5",
"@jest/source-map": "30.0.1",
- "@jest/test-result": "30.1.1",
- "@jest/transform": "30.1.1",
+ "@jest/test-result": "30.0.5",
+ "@jest/transform": "30.0.5",
"@jest/types": "30.0.5",
"@types/node": "*",
"chalk": "^4.1.2",
@@ -3701,12 +3701,12 @@
"collect-v8-coverage": "^1.0.2",
"glob": "^10.3.10",
"graceful-fs": "^4.2.11",
- "jest-haste-map": "30.1.0",
- "jest-message-util": "30.1.0",
+ "jest-haste-map": "30.0.5",
+ "jest-message-util": "30.0.5",
"jest-mock": "30.0.5",
"jest-regex-util": "30.0.1",
- "jest-resolve": "30.1.0",
- "jest-snapshot": "30.1.1",
+ "jest-resolve": "30.0.5",
+ "jest-snapshot": "30.0.5",
"jest-util": "30.0.5",
"slash": "^3.0.0",
"strip-bom": "^4.0.0"
@@ -3716,9 +3716,9 @@
}
},
"node_modules/jest-snapshot": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.1.1.tgz",
- "integrity": "sha512-7/iBEzoJqEt2TjkQY+mPLHP8cbPhLReZVkkxjTMzIzoTC4cZufg7HzKo/n9cIkXKj2LG0x3mmBHsZto+7TOmFg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.5.tgz",
+ "integrity": "sha512-T00dWU/Ek3LqTp4+DcW6PraVxjk28WY5Ua/s+3zUKSERZSNyxTqhDXCWKG5p2HAJ+crVQ3WJ2P9YVHpj1tkW+g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3727,18 +3727,18 @@
"@babel/plugin-syntax-jsx": "^7.27.1",
"@babel/plugin-syntax-typescript": "^7.27.1",
"@babel/types": "^7.27.3",
- "@jest/expect-utils": "30.1.1",
- "@jest/get-type": "30.1.0",
- "@jest/snapshot-utils": "30.1.1",
- "@jest/transform": "30.1.1",
+ "@jest/expect-utils": "30.0.5",
+ "@jest/get-type": "30.0.1",
+ "@jest/snapshot-utils": "30.0.5",
+ "@jest/transform": "30.0.5",
"@jest/types": "30.0.5",
"babel-preset-current-node-syntax": "^1.1.0",
"chalk": "^4.1.2",
- "expect": "30.1.1",
+ "expect": "30.0.5",
"graceful-fs": "^4.2.11",
- "jest-diff": "30.1.1",
- "jest-matcher-utils": "30.1.1",
- "jest-message-util": "30.1.0",
+ "jest-diff": "30.0.5",
+ "jest-matcher-utils": "30.0.5",
+ "jest-message-util": "30.0.5",
"jest-util": "30.0.5",
"pretty-format": "30.0.5",
"semver": "^7.7.2",
@@ -3793,13 +3793,13 @@
}
},
"node_modules/jest-validate": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.1.0.tgz",
- "integrity": "sha512-7P3ZlCFW/vhfQ8pE7zW6Oi4EzvuB4sgR72Q1INfW9m0FGo0GADYlPwIkf4CyPq7wq85g+kPMtPOHNAdWHeBOaA==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.5.tgz",
+ "integrity": "sha512-ouTm6VFHaS2boyl+k4u+Qip4TSH7Uld5tyD8psQ8abGgt2uYYB8VwVfAHWHjHc0NWmGGbwO5h0sCPOGHHevefw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/get-type": "30.1.0",
+ "@jest/get-type": "30.0.1",
"@jest/types": "30.0.5",
"camelcase": "^6.3.0",
"chalk": "^4.1.2",
@@ -3824,13 +3824,13 @@
}
},
"node_modules/jest-watcher": {
- "version": "30.1.1",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.1.1.tgz",
- "integrity": "sha512-CrAQ73LlaS6KGQQw6NBi71g7qvP7scy+4+2c0jKX6+CWaYg85lZiig5nQQVTsS5a5sffNPL3uxXnaE9d7v9eQg==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.5.tgz",
+ "integrity": "sha512-z9slj/0vOwBDBjN3L4z4ZYaA+pG56d6p3kTUhFRYGvXbXMWhXmb/FIxREZCD06DYUwDKKnj2T80+Pb71CQ0KEg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jest/test-result": "30.1.1",
+ "@jest/test-result": "30.0.5",
"@jest/types": "30.0.5",
"@types/node": "*",
"ansi-escapes": "^4.3.2",
@@ -3844,9 +3844,9 @@
}
},
"node_modules/jest-worker": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.1.0.tgz",
- "integrity": "sha512-uvWcSjlwAAgIu133Tt77A05H7RIk3Ho8tZL50bQM2AkvLdluw9NG48lRCl3Dt+MOH719n/0nnb5YxUwcuJiKRA==",
+ "version": "30.0.5",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.5.tgz",
+ "integrity": "sha512-ojRXsWzEP16NdUuBw/4H/zkZdHOa7MMYCk4E430l+8fELeLg/mqmMlRhjL7UNZvQrDmnovWZV4DxX03fZF48fQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4166,9 +4166,9 @@
"optional": true
},
"node_modules/napi-postinstall": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz",
- "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.2.tgz",
+ "integrity": "sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==",
"dev": true,
"license": "MIT",
"bin": {
diff --git a/grpc-client/docs/Readme.txt b/grpc-client/docs/Readme.txt
new file mode 100644
index 0000000000..9ea64f2f92
--- /dev/null
+++ b/grpc-client/docs/Readme.txt
@@ -0,0 +1,47 @@
+
+PWD: /Users/ocohen/git/Verdance/ArcadeDB-GRPC/arcadedb-25.8.1-SNAPSHOT
+
+
+./bin/server.sh -Darcadedb.server.rootPassword=root1234 -Darcadedb.server.name=Arcade_GRPC_Test -Darcadedb.dumpConfigAtStartup=true -Darcadedb.server.mode=development -Darcadedb.server.rootPath=../var/arcadedb -Darcadedb.server.plugins=GRPC:com.arcadedb.server.grpc.GrpcServerPlugin -Xms512M -Xmx4096M -XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=75.0 -Darcadedb.server.httpIncomingPort=2489 -Darcadedb.grpc.enabled=true -Darcadedb.grpc.port=50059 -Darcadedb.grpc.mode=standard -Darcadedb.grpc.reflection.enabled=true -Darcadedb.grpc.health.enabled=true
+
+
+Ports:
+
+HTTP: 2489
+GTPC: 50059
+
+root
+root1234
+
+Logging:
+
+d exec -it arcadedb1-vulcan sh
+
+
+vi /home/arcadedb/config/arcadedb-log.properties
+
+
+handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler
+
+.level = INFO
+com.arcadedb.level = INFO
+com.arcadedb.server.grpc.level = FINE
+
+java.util.logging.ConsoleHandler.level = INFO
+java.util.logging.ConsoleHandler.formatter = com.arcadedb.utility.AnsiLogFormatter
+
+java.util.logging.FileHandler.level = FINE
+java.util.logging.FileHandler.pattern=./log/arcadedb.log
+java.util.logging.FileHandler.formatter = com.arcadedb.log.LogFormatter
+java.util.logging.FileHandler.limit=100000000
+java.util.logging.FileHandler.count=10
+
+
+
+
+tail -f /home/arcadedb/log/arcadedb.log.0
+
+
+
+
+d exec -it arcadedb1-vulcan sh
\ No newline at end of file
diff --git a/grpc-client/pom.xml b/grpc-client/pom.xml
new file mode 100644
index 0000000000..f95c91a141
--- /dev/null
+++ b/grpc-client/pom.xml
@@ -0,0 +1,150 @@
+
+
+
+ 4.0.0
+
+
+ com.arcadedb
+ arcadedb-parent
+ 25.8.1-SNAPSHOT
+ ../pom.xml
+
+
+ arcadedb-grpc-client
+ jar
+ ArcadeDB gRPC Client
+
+
+ 5.15.2
+
+
+
+
+
+ com.arcadedb
+ arcadedb-engine
+ ${project.parent.version}
+ compile
+
+
+
+ com.arcadedb
+ arcadedb-network
+ ${project.parent.version}
+ compile
+
+
+
+ com.arcadedb
+ arcadedb-grpc
+ ${project.parent.version}
+ grpc-interface
+
+
+ *
+ *
+
+
+
+
+
+
+ io.grpc
+ grpc-netty-shaded
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-protobuf
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-stub
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-services
+ ${grpc.version}
+
+
+
+
+ com.google.protobuf
+ protobuf-java
+ ${protobuf-java.version}
+
+
+ com.google.protobuf
+ protobuf-java-util
+ ${protobuf-java.version}
+
+
+
+
+ com.google.api.grpc
+ proto-google-common-protos
+ ${com.google.api.grpc.version}
+
+
+
+
+ javax.annotation
+ javax.annotation-api
+ ${javax.annotation-api.version}
+
+
+
+
+ io.grpc
+ grpc-testing
+ ${grpc.version}
+ test
+
+
+
+ org.slf4j
+ slf4j-simple
+ 2.0.17
+ provided
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.19.2
+ provided
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcConfig.java b/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcConfig.java
new file mode 100644
index 0000000000..f055e10a1e
--- /dev/null
+++ b/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcConfig.java
@@ -0,0 +1,41 @@
+package com.arcadedb.remote.grpc;
+
+import com.arcadedb.server.grpc.ProjectionSettings.ProjectionEncoding;
+
+public class RemoteGrpcConfig {
+
+ private boolean includeProjections;
+ private ProjectionEncoding projectionEncoding;
+ private int softLimitBytes;
+
+ public RemoteGrpcConfig(boolean includeProjections, ProjectionEncoding projectionEncoding, int softLimitBytes) {
+ this.includeProjections = includeProjections;
+ this.projectionEncoding = projectionEncoding;
+ this.softLimitBytes = softLimitBytes;
+ }
+
+ public boolean isIncludeProjections() {
+ return includeProjections;
+ }
+
+ public void setIncludeProjections(boolean includeProjections) {
+ this.includeProjections = includeProjections;
+ }
+
+ public ProjectionEncoding getProjectionEncoding() {
+ return projectionEncoding;
+ }
+
+ public void setProjectionEncoding(ProjectionEncoding projectionEncoding) {
+ this.projectionEncoding = projectionEncoding;
+ }
+
+ public int getSoftLimitBytes() {
+ return softLimitBytes;
+ }
+
+ public void setSoftLimitBytes(int softLimitBytes) {
+ this.softLimitBytes = softLimitBytes;
+ }
+
+}
diff --git a/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcDatabase.java b/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcDatabase.java
new file mode 100644
index 0000000000..feeaa1f0de
--- /dev/null
+++ b/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcDatabase.java
@@ -0,0 +1,2455 @@
+package com.arcadedb.remote.grpc;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.arcadedb.ContextConfiguration;
+import com.arcadedb.database.Database;
+import com.arcadedb.database.MutableDocument;
+import com.arcadedb.database.RID;
+import com.arcadedb.database.Record;
+import com.arcadedb.exception.ConcurrentModificationException;
+import com.arcadedb.exception.DatabaseOperationException;
+import com.arcadedb.exception.DuplicatedKeyException;
+import com.arcadedb.exception.NeedRetryException;
+import com.arcadedb.exception.RecordNotFoundException;
+import com.arcadedb.exception.TimeoutException;
+import com.arcadedb.exception.TransactionException;
+import com.arcadedb.query.sql.executor.InternalResultSet;
+import com.arcadedb.query.sql.executor.Result;
+import com.arcadedb.query.sql.executor.ResultInternal;
+import com.arcadedb.query.sql.executor.ResultSet;
+import com.arcadedb.remote.RemoteDatabase;
+import com.arcadedb.remote.RemoteException;
+import com.arcadedb.remote.RemoteImmutableDocument;
+import com.arcadedb.remote.RemoteImmutableEdge;
+import com.arcadedb.remote.RemoteImmutableVertex;
+import com.arcadedb.remote.RemoteSchema;
+import com.arcadedb.remote.RemoteTransactionExplicitLock;
+import com.arcadedb.remote.grpc.utils.ProtoUtils;
+import com.arcadedb.server.grpc.ArcadeDbServiceGrpc;
+import com.arcadedb.server.grpc.BatchAck;
+import com.arcadedb.server.grpc.BeginTransactionRequest;
+import com.arcadedb.server.grpc.BeginTransactionResponse;
+import com.arcadedb.server.grpc.BulkInsertRequest;
+import com.arcadedb.server.grpc.Commit;
+import com.arcadedb.server.grpc.CommitTransactionRequest;
+import com.arcadedb.server.grpc.CommitTransactionResponse;
+import com.arcadedb.server.grpc.CreateRecordRequest;
+import com.arcadedb.server.grpc.CreateRecordResponse;
+import com.arcadedb.server.grpc.DatabaseCredentials;
+import com.arcadedb.server.grpc.DeleteRecordRequest;
+import com.arcadedb.server.grpc.DeleteRecordResponse;
+import com.arcadedb.server.grpc.ExecuteCommandRequest;
+import com.arcadedb.server.grpc.ExecuteCommandResponse;
+import com.arcadedb.server.grpc.ExecuteQueryRequest;
+import com.arcadedb.server.grpc.ExecuteQueryResponse;
+import com.arcadedb.server.grpc.GrpcRecord;
+import com.arcadedb.server.grpc.GrpcValue;
+import com.arcadedb.server.grpc.InsertChunk;
+import com.arcadedb.server.grpc.InsertOptions;
+import com.arcadedb.server.grpc.InsertRequest;
+import com.arcadedb.server.grpc.InsertResponse;
+import com.arcadedb.server.grpc.InsertSummary;
+import com.arcadedb.server.grpc.LookupByRidRequest;
+import com.arcadedb.server.grpc.LookupByRidResponse;
+import com.arcadedb.server.grpc.ProjectionSettings;
+import com.arcadedb.server.grpc.ProjectionSettings.ProjectionEncoding;
+import com.arcadedb.server.grpc.PropertiesUpdate;
+import com.arcadedb.server.grpc.QueryResult;
+import com.arcadedb.server.grpc.RollbackTransactionRequest;
+import com.arcadedb.server.grpc.RollbackTransactionResponse;
+import com.arcadedb.server.grpc.Start;
+import com.arcadedb.server.grpc.StreamQueryRequest;
+import com.arcadedb.server.grpc.TransactionContext;
+import com.arcadedb.server.grpc.TransactionIsolation;
+import com.arcadedb.server.grpc.UpdateRecordRequest;
+import com.arcadedb.server.grpc.UpdateRecordResponse;
+import com.google.protobuf.Int32Value;
+
+import io.grpc.CallCredentials;
+import io.grpc.Metadata;
+import io.grpc.StatusException;
+import io.grpc.StatusRuntimeException;
+import io.grpc.stub.BlockingClientCall;
+import io.grpc.stub.ClientCallStreamObserver;
+import io.grpc.stub.ClientResponseObserver;
+import io.grpc.stub.StreamObserver;
+
+/**
+ * Remote Database implementation using gRPC protocol instead of HTTP. Extends
+ * RemoteDatabase to maintain compatibility while overriding network operations.
+ * It's not thread safe. For multi-thread usage create one instance of
+ * RemoteGrpcDatabase per thread.
+ *
+ * @author Oleg Cohen (oleg.cohen@gmail.com)
+ */
+public class RemoteGrpcDatabase extends RemoteDatabase {
+
+ private static final Logger logger = LoggerFactory.getLogger(RemoteGrpcDatabase.class);
+
+ private final ArcadeDbServiceGrpc.ArcadeDbServiceBlockingV2Stub blockingStub;
+ private final ArcadeDbServiceGrpc.ArcadeDbServiceStub asyncStub;
+
+ private String transactionId;
+
+ private RemoteSchema schema;
+
+ private final String userName;
+ private final String userPassword;
+ private String databaseName;
+ private RemoteTransactionExplicitLock explicitLock;
+
+ protected RemoteGrpcServer remoteGrpcServer;
+
+ public RemoteGrpcDatabase(final RemoteGrpcServer remoteGrpcServer, final String server, final int grpcPort, final int httpPort,
+ final String databaseName, final String userName, final String userPassword) {
+ this(remoteGrpcServer, server, grpcPort, httpPort, databaseName, userName, userPassword, new ContextConfiguration());
+ }
+
+ public RemoteGrpcDatabase(final RemoteGrpcServer remoteGrpcServer, final String host, final int grpcPort, final int httpPort,
+ final String databaseName, final String userName, final String userPassword, final ContextConfiguration configuration) {
+
+ super(host, httpPort, databaseName, userName, userPassword, configuration);
+
+ this.remoteGrpcServer = remoteGrpcServer;
+
+ this.userName = userName;
+ this.userPassword = userPassword;
+
+ this.databaseName = databaseName;
+
+ this.blockingStub = createBlockingStub();
+
+ this.asyncStub = createAsyncStub();
+
+ this.schema = new RemoteSchema(this);
+ }
+
+ /**
+ * Creates call credentials for authentication
+ */
+ protected CallCredentials createCallCredentials(String userName, String userPassword) {
+ return new CallCredentials() {
+ @Override
+ public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) {
+ Metadata headers = new Metadata();
+ headers.put(Metadata.Key.of("username", Metadata.ASCII_STRING_MARSHALLER), userName);
+ headers.put(Metadata.Key.of("password", Metadata.ASCII_STRING_MARSHALLER), userPassword);
+ headers.put(Metadata.Key.of("x-arcade-user", Metadata.ASCII_STRING_MARSHALLER), userName);
+ headers.put(Metadata.Key.of("x-arcade-password", Metadata.ASCII_STRING_MARSHALLER), userPassword);
+ headers.put(Metadata.Key.of("x-arcade-database", Metadata.ASCII_STRING_MARSHALLER), databaseName);
+ applier.apply(headers);
+ }
+
+ @Override
+ public void thisUsesUnstableApi() {
+ // Required by the interface
+ }
+ };
+ }
+
+ protected CallCredentials createCredentials() {
+ return createCallCredentials(userName, userPassword);
+ }
+
+ /**
+ * Override this method to customize blocking stub creation
+ */
+ protected ArcadeDbServiceGrpc.ArcadeDbServiceBlockingV2Stub createBlockingStub() {
+ return this.remoteGrpcServer.newBlockingStub(getTimeout());
+ }
+
+ protected ArcadeDbServiceGrpc.ArcadeDbServiceStub createAsyncStub() {
+ return this.remoteGrpcServer.newAsyncStub(getTimeout());
+ }
+
+ @Override
+ public String getDatabasePath() {
+ return "grpc://" + this.remoteGrpcServer.channel().authority() + "/" + getName();
+ }
+
+ @Override
+ public RemoteSchema getSchema() {
+ return schema;
+ }
+
+ @Override
+ public void close() {
+
+ if (transactionId != null) {
+ rollback();
+ }
+ super.close();
+ }
+
+ @Override
+ public void begin(final Database.TRANSACTION_ISOLATION_LEVEL isolationLevel) {
+ checkDatabaseIsOpen();
+ if (transactionId != null)
+ throw new TransactionException("Transaction already begun");
+
+ BeginTransactionRequest request = BeginTransactionRequest.newBuilder().setDatabase(getName()).setCredentials(buildCredentials())
+ .setIsolation(mapIsolationLevel(isolationLevel)).build();
+
+ callUnaryVoid("BeginTransaction", () -> {
+
+ try {
+ BeginTransactionResponse response = blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS)
+ .beginTransaction(request);
+ transactionId = response.getTransactionId();
+ // Store transaction ID in parent class session management
+ setSessionId(transactionId);
+ }
+ catch (StatusRuntimeException e) {
+ throw new TransactionException("Error on transaction begin", e);
+ }
+ catch (StatusException e) {
+ throw new TransactionException("Error on transaction begin", e);
+ }
+
+ debugTx = new TxDebug(this.getName(), /* label set by TM */ null);
+ logTx("BEGIN(local)", null);
+ try {
+ checkCrossThreadUse("before BeginTransaction");
+ // send BeginTransaction RPC ...
+ debugTx.beginRpcSent = true;
+ debugTx.rpcSeq.incrementAndGet();
+ logTx("BEGIN sent", "BeginTransaction");
+ }
+ catch (RuntimeException e) {
+ logTx("BEGIN failed", "BeginTransaction");
+ debugTx = null;
+ throw e;
+ }
+ });
+ }
+
+ @Override
+ public void commit() {
+
+ checkDatabaseIsOpen();
+ stats.txCommits.incrementAndGet();
+
+ if (transactionId == null) {
+ throw new TransactionException("Transaction not begun");
+ }
+
+ logTx("COMMIT(local)", null);
+ checkCrossThreadUse("before CommitTransaction");
+
+ callUnaryVoid("CommitTransaction", () -> {
+
+ try {
+
+ CommitTransactionRequest request = CommitTransactionRequest.newBuilder()
+ .setTransaction(TransactionContext.newBuilder().setTransactionId(transactionId).setDatabase(getName()).build())
+ .setCredentials(buildCredentials()).build();
+
+ try {
+
+ CommitTransactionResponse response = blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS)
+ .commitTransaction(request);
+
+ logger.debug("[After commit] Success: {} Committed: {}", response.getSuccess(), response.getCommitted());
+
+ if (!response.getSuccess()) {
+ throw new TransactionException("Failed to commit transaction: " + response.getMessage());
+ }
+ }
+ catch (StatusRuntimeException e) {
+
+ handleGrpcException(e);
+ }
+ catch (StatusException e) {
+
+ handleGrpcException(e);
+ }
+ finally {
+
+ transactionId = null;
+ setSessionId(null);
+ }
+
+ if (debugTx != null) {
+
+ debugTx.committed = true;
+ debugTx.rpcSeq.incrementAndGet();
+ }
+
+ logTx("COMMIT sent", "CommitTransaction");
+ }
+ finally {
+ debugTx = null;
+ }
+
+ });
+ }
+
+ @Override
+ public void rollback() {
+
+ checkDatabaseIsOpen();
+ stats.txRollbacks.incrementAndGet();
+
+ if (transactionId == null)
+ throw new TransactionException("Transaction not begun");
+
+ logTx("ROLLBACK(local)", null);
+ checkCrossThreadUse("before RollbackTransaction");
+
+ callUnaryVoid("RollbackTransaction", () -> {
+
+ try {
+
+ RollbackTransactionRequest request = RollbackTransactionRequest.newBuilder()
+ .setTransaction(TransactionContext.newBuilder().setTransactionId(transactionId).setDatabase(getName()).build())
+ .setCredentials(buildCredentials()).build();
+
+ try {
+
+ RollbackTransactionResponse response = blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS)
+ .rollbackTransaction(request);
+
+ logger.debug("[After rollback] Success: {} Committed: {}", response.getSuccess(), response.getRolledBack());
+
+ if (!response.getSuccess()) {
+ throw new TransactionException("Failed to rollback transaction: " + response.getMessage());
+ }
+ }
+ catch (StatusRuntimeException e) {
+ throw new TransactionException("Error on transaction rollback", e);
+ }
+ catch (StatusException e) {
+ throw new TransactionException("Error on transaction rollback", e);
+ }
+ finally {
+ transactionId = null;
+ setSessionId(null);
+ }
+
+ if (debugTx != null) {
+ debugTx.rolledBack = true;
+ debugTx.rpcSeq.incrementAndGet();
+ }
+ logTx("ROLLBACK sent", "RollbackTransaction");
+ }
+ finally {
+ debugTx = null;
+ }
+
+ });
+ }
+
+ @Override
+ public void deleteRecord(final Record record) {
+ checkDatabaseIsOpen();
+ stats.deleteRecord.incrementAndGet();
+
+ if (record.getIdentity() == null)
+ throw new IllegalArgumentException("Cannot delete a non persistent record");
+
+ final DeleteRecordRequest req = DeleteRecordRequest.newBuilder().setDatabase(getName()).setRid(record.getIdentity().toString())
+ .setCredentials(buildCredentials()).build();
+
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("CLIENT deleteRecord: db={}, tx={}, rid={}", getName(), (transactionId != null), record.getIdentity());
+ }
+
+ final DeleteRecordResponse resp = callUnary("DeleteRecord",
+ () -> blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).deleteRecord(req));
+
+ // Prefer the proto's 'deleted' flag (your other overload uses it)
+ if (!resp.getDeleted()) {
+ throw new DatabaseOperationException(
+ "Failed to delete record: " + (resp.getMessage().isEmpty() ? "unknown error" : resp.getMessage()));
+ }
+
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ handleGrpcException(e); // rethrows mapped domain exception
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ public boolean deleteRecord(final String rid, final long timeoutMs) {
+ checkDatabaseIsOpen();
+ stats.deleteRecord.incrementAndGet();
+
+ final DeleteRecordRequest req = DeleteRecordRequest.newBuilder().setDatabase(getName()).setRid(rid).setCredentials(buildCredentials())
+ .build();
+
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("CLIENT deleteRecord: db={}, tx={}, rid={}, timeoutMs={}", getName(), (transactionId != null), rid, timeoutMs);
+ }
+
+ final DeleteRecordResponse res = callUnary("DeleteRecord",
+ () -> blockingStub.withDeadlineAfter(timeoutMs, TimeUnit.MILLISECONDS).deleteRecord(req));
+
+ return res.getDeleted();
+
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ handleGrpcException(e); // rethrows mapped domain exception
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ @Override
+ public ResultSet command(final String language, final String command, final ContextConfiguration configuration, final Object... args) {
+
+ checkDatabaseIsOpen();
+ stats.commands.incrementAndGet();
+
+ final Map params = mapArgs(args);
+
+ return commandInternal(language, command, params);
+ }
+
+ @Override
+ public ResultSet command(final String language, final String command, final ContextConfiguration configuration,
+ final Map params) {
+ checkDatabaseIsOpen();
+ stats.commands.incrementAndGet();
+
+ return commandInternal(language, command, params);
+ }
+
+ @Override
+ public ResultSet command(final String language, final String command, final Object... args) {
+ checkDatabaseIsOpen();
+ stats.commands.incrementAndGet();
+
+ final Map params = mapArgs(args);
+
+ return commandInternal(language, command, params);
+ }
+
+ @Override
+ public ResultSet command(final String language, final String command, final Map params) {
+ checkDatabaseIsOpen();
+ stats.commands.incrementAndGet();
+
+ return commandInternal(language, command, params);
+ }
+
+ private ResultSet commandInternal(final String language, final String command, final Map params) {
+
+ ExecuteCommandRequest.Builder requestBuilder = ExecuteCommandRequest.newBuilder().setDatabase(getName()).setCommand(command)
+ .setLanguage(language).setCredentials(buildCredentials());
+
+ if (transactionId != null) {
+ requestBuilder.setTransaction(TransactionContext.newBuilder().setTransactionId(transactionId).setDatabase(getName()).build());
+ }
+
+ if (params != null && !params.isEmpty()) {
+ requestBuilder.putAllParameters(convertParamsToGrpcValue(params));
+ }
+
+ boolean returnRows = true;
+
+ requestBuilder.setReturnRows(returnRows);
+
+ try {
+
+ if (logger.isDebugEnabled())
+ logger.debug("CLIENT executeCommand: db={}, tx={}, cmdLen={}, params={}", getName(), (transactionId != null),
+ requestBuilder.getCommand().length(), requestBuilder.getParametersCount());
+
+ final ExecuteCommandResponse response = callUnary("ExecuteCommand",
+ () -> blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).executeCommand(requestBuilder.build()));
+
+ if (logger.isDebugEnabled())
+ logger.debug("CLIENT executeCommand: success = {}", response.getSuccess());
+
+ if (!response.getSuccess()) {
+ throw new DatabaseOperationException("Failed to execute command: " + response.getMessage());
+ }
+
+ ResultSet resultSet;
+
+ if (returnRows) {
+ resultSet = createGrpcResultSet(response);
+ }
+ else {
+
+ resultSet = new InternalResultSet();
+
+ if (response.getAffectedRecords() > 0) {
+
+ Map result = new HashMap<>();
+ result.put("affected", response.getAffectedRecords());
+ result.put("executionTime", response.getExecutionTimeMs());
+
+ ((InternalResultSet) resultSet).add(new ResultInternal(result));
+ }
+ }
+
+ return resultSet;
+ }
+ catch (StatusRuntimeException e) {
+ handleGrpcException(e);
+ return new InternalResultSet();
+ }
+ catch (StatusException e) {
+ handleGrpcException(e);
+ return new InternalResultSet();
+ }
+ }
+
+ @Override
+ public ResultSet query(final String language, final String query, final Object... args) {
+ checkDatabaseIsOpen();
+ stats.queries.incrementAndGet();
+
+ final Map params = mapArgs(args);
+
+ RemoteGrpcConfig remoteGrpcConfig = new RemoteGrpcConfig(true, ProjectionEncoding.PROJECTION_AS_JSON, 0);
+
+ return query(language, query, remoteGrpcConfig, params);
+ }
+
+ @Override
+ public ResultSet query(final String language, final String query, final Map params) {
+
+ RemoteGrpcConfig remoteGrpcConfig = new RemoteGrpcConfig(true, ProjectionEncoding.PROJECTION_AS_JSON, 0);
+
+ return query(language, query, remoteGrpcConfig, params);
+ }
+
+ public ResultSet query(final String language, final String query, RemoteGrpcConfig remoteGrpcConfig, final Object... args) {
+
+ final Map params = mapArgs(args);
+
+ return query(language, query, remoteGrpcConfig, params);
+ }
+
+ public ResultSet query(final String language, final String query, RemoteGrpcConfig remoteGrpcConfig, final Map params) {
+
+ checkDatabaseIsOpen();
+ stats.queries.incrementAndGet();
+
+ ExecuteQueryRequest.Builder requestBuilder = ExecuteQueryRequest.newBuilder().setDatabase(getName()).setQuery(query)
+ .setCredentials(buildCredentials());
+
+ if (transactionId != null) {
+ requestBuilder.setTransaction(TransactionContext.newBuilder().setTransactionId(transactionId).setDatabase(getName()).build());
+ }
+
+ if (params != null && !params.isEmpty()) {
+ requestBuilder.putAllParameters(convertParamsToGrpcValue(params));
+ }
+
+ ProjectionSettings projectionSettings = ProjectionSettings.newBuilder().setIncludeProjections(remoteGrpcConfig.isIncludeProjections())
+ .setProjectionEncoding(remoteGrpcConfig.getProjectionEncoding())
+ .setSoftLimitBytes(Int32Value.newBuilder().setValue(remoteGrpcConfig.getSoftLimitBytes()).build()).build();
+
+ requestBuilder.setProjectionSettings(projectionSettings);
+
+ try {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("CLIENT executeQuery: db={}, tx={}, queryLen={}, params={}", getName(), (transactionId != null),
+ requestBuilder.getQuery().length(), requestBuilder.getParametersCount());
+ }
+
+ final ExecuteQueryRequest req = requestBuilder.build();
+
+ final ExecuteQueryResponse response = callUnary("ExecuteQuery",
+ () -> blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS) // or getTimeout(MODE_QUERY)
+ .executeQuery(req));
+
+ if (logger.isDebugEnabled()) {
+ int _r = 0;
+ for (var qr : response.getResultsList())
+ _r += qr.getRecordsCount();
+ logger.debug("CLIENT executeQuery: results={}", _r);
+ }
+ return createGrpcResultSet(response);
+ }
+ catch (io.grpc.StatusException | io.grpc.StatusRuntimeException e) {
+ handleGrpcException(e);
+ return new InternalResultSet();
+ }
+ }
+
+ public ExecuteCommandResponse execSql(String db, String sql, Map params, long timeoutMs) {
+ return executeCommand(db, "sql", sql, params, /* returnRows */ false, /* maxRows */ 0, txBeginCommit(), timeoutMs);
+ }
+
+ public ExecuteCommandResponse execSql(String sql, Map params, long timeoutMs) {
+ return executeCommand(databaseName, "sql", sql, params, /* returnRows */ false, /* maxRows */ 0, txBeginCommit(), timeoutMs);
+ }
+
+ public ExecuteCommandResponse executeCommand(String language, String command, Map params, boolean returnRows, int maxRows,
+ TransactionContext tx, long timeoutMs) {
+
+ var reqB = ExecuteCommandRequest.newBuilder().setDatabase(databaseName).setCommand(command)
+ .putAllParameters(convertParamsToGrpcValue(params)).setLanguage(langOrDefault(language)).setReturnRows(returnRows)
+ .setMaxRows(maxRows > 0 ? maxRows : 0);
+
+ if (tx != null)
+ reqB.setTransaction(tx);
+ reqB.setCredentials(buildCredentials());
+
+ try {
+ return callUnary("ExecuteCommand",
+ () -> blockingStub.withDeadlineAfter(timeoutMs, TimeUnit.MILLISECONDS).executeCommand(reqB.build()));
+ }
+ catch (StatusException | StatusRuntimeException e) {
+ // handleGrpcException already called in callUnary, this is unreachable
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ public ExecuteCommandResponse executeCommand(String database, String language, String command, Map params,
+ boolean returnRows, int maxRows, TransactionContext tx, long timeoutMs) {
+
+ var reqB = ExecuteCommandRequest.newBuilder().setDatabase(database).setCommand(command)
+ .putAllParameters(convertParamsToGrpcValue(params)).setLanguage(langOrDefault(language)).setReturnRows(returnRows)
+ .setMaxRows(maxRows > 0 ? maxRows : 0);
+
+ if (tx != null)
+ reqB.setTransaction(tx);
+ reqB.setCredentials(buildCredentials());
+
+ try {
+ return callUnary("ExecuteCommand",
+ () -> blockingStub.withDeadlineAfter(timeoutMs, TimeUnit.MILLISECONDS).executeCommand(reqB.build()));
+ }
+ catch (StatusException | StatusRuntimeException e) {
+ // handleGrpcException already called in callUnary, this is unreachable
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ @Override
+ protected RID saveRecord(final MutableDocument record) {
+ stats.createRecord.incrementAndGet();
+
+ final RID rid = record.getIdentity();
+
+ if (rid != null) {
+ // -------- UPDATE (partial) --------
+ PropertiesUpdate partial = PropertiesUpdate.newBuilder().putAllProperties(convertParamsToGrpcValue(record.toMap(false))).build();
+
+ UpdateRecordRequest request = UpdateRecordRequest.newBuilder().setDatabase(getName()).setRid(rid.toString()).setPartial(partial)
+ .setDatabase(databaseName).setCredentials(buildCredentials()).build();
+
+ try {
+
+ @SuppressWarnings("unused")
+ UpdateRecordResponse response = blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).updateRecord(request);
+
+ // If your proto has flags, you can check response.getSuccess()/getUpdated()
+ // Otherwise, treat non-exception as success.
+ return rid;
+ }
+ catch (StatusRuntimeException e) {
+ handleGrpcException(e);
+ return null;
+ }
+ catch (StatusException e) {
+ handleGrpcException(e);
+ return null;
+ }
+ }
+ else {
+ // -------- CREATE --------
+ GrpcRecord recMsg = GrpcRecord.newBuilder().putAllProperties(convertParamsToGrpcValue(record.toMap(false))).build();
+
+ CreateRecordRequest request = CreateRecordRequest.newBuilder().setDatabase(getName()).setType(record.getTypeName()).setRecord(recMsg) // nested
+ // GrpcRecord
+ // payload
+ .setCredentials(buildCredentials()).build();
+
+ try {
+ CreateRecordResponse response = blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).createRecord(request);
+
+ // Proto returns the newly created RID as a string
+ final String ridStr = response.getRid();
+ if (ridStr == null || ridStr.isEmpty()) {
+ throw new DatabaseOperationException("Failed to create record (empty RID)");
+ }
+
+ // Construct a RID from the returned string
+ try {
+ return new RID(ridStr);
+ }
+ catch (NoSuchMethodError | IllegalArgumentException ex) {
+ // Fallback for older APIs expecting (Database, String)
+ return new RID(this, ridStr);
+ }
+ }
+ catch (StatusRuntimeException e) {
+ handleGrpcException(e);
+ return null;
+ }
+ catch (StatusException e) {
+ handleGrpcException(e);
+ return null;
+ }
+ }
+ }
+
+ private RemoteGrpcConfig getDefaultRemoteGrpcConfig() {
+
+ return new RemoteGrpcConfig(true, ProjectionEncoding.PROJECTION_AS_JSON, 0);
+ }
+
+ // Convenience: default batch size stays 100, default mode = CURSOR
+
+ public ResultSet queryStream(final String language, final String query) {
+ return queryStream(language, query, getDefaultRemoteGrpcConfig(), /* batchSize */100, StreamQueryRequest.RetrievalMode.CURSOR);
+ }
+
+ public ResultSet queryStream(final String language, final String query, final RemoteGrpcConfig config) {
+ return queryStream(language, query, config, /* batchSize */100, StreamQueryRequest.RetrievalMode.CURSOR);
+ }
+
+ public ResultSet queryStream(final String language, final String query, final int batchSize) {
+ return queryStream(language, query, getDefaultRemoteGrpcConfig(), batchSize, StreamQueryRequest.RetrievalMode.CURSOR);
+ }
+
+ public ResultSet queryStream(final String language, final String query, RemoteGrpcConfig config, final int batchSize) {
+ return queryStream(language, query, config, batchSize, StreamQueryRequest.RetrievalMode.CURSOR);
+ }
+
+ public ResultSet queryStream(final String language, final String query, final int batchSize, final StreamQueryRequest.RetrievalMode mode) {
+ return queryStream(language, query, getDefaultRemoteGrpcConfig(), batchSize, mode);
+ }
+
+ // NEW: choose retrieval mode
+
+ public ResultSet queryStream(final String language, final String query, final RemoteGrpcConfig config, final int batchSize,
+ final StreamQueryRequest.RetrievalMode mode) {
+
+ return queryStream(language, query, config, Map.of(), batchSize, mode);
+ }
+
+ /**
+ * Stream query results as a ResultSet that fetches data lazily in batches. This
+ * provides consistency with non-streaming query methods and supports non-Record
+ * results like projections and aggregations.
+ */
+ public ResultSet queryStream(final String language, final String query, final RemoteGrpcConfig config, final Map params,
+ final int batchSize, final StreamQueryRequest.RetrievalMode mode) {
+
+ checkDatabaseIsOpen();
+ stats.queries.incrementAndGet();
+
+ StreamQueryRequest.Builder b = StreamQueryRequest.newBuilder().setDatabase(getName()).setQuery(query).setCredentials(buildCredentials())
+ .setBatchSize(batchSize > 0 ? batchSize : 100).setRetrievalMode(mode);
+
+ if (params != null && !params.isEmpty()) {
+ b.putAllParameters(convertParamsToGrpcValue(params));
+ }
+
+ final BlockingClientCall, QueryResult> responseIterator = callServerStreaming("StreamQuery",
+ () -> blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).streamQuery(b.build()));
+
+ // Return a streaming ResultSet implementation
+ return new StreamingResultSet(responseIterator, this);
+ }
+
+ // Keep the old signature working (defaults to CURSOR)
+ public ResultSet queryStream(final String language, final String query, final Map params, final int batchSize) {
+
+ return queryStream(language, query, getDefaultRemoteGrpcConfig(), params, batchSize, StreamQueryRequest.RetrievalMode.CURSOR);
+ }
+
+ public ResultSet queryStream(final String language, final String query, final RemoteGrpcConfig config, final Map params,
+ final int batchSize) {
+
+ return queryStream(language, query, config, params, batchSize, StreamQueryRequest.RetrievalMode.CURSOR);
+ }
+
+ /**
+ * A ResultSet implementation that lazily fetches results from a gRPC stream.
+ * Supports both Record results and projection/aggregation results.
+ */
+ private static class StreamingResultSet implements ResultSet {
+ private static final Logger logger = LoggerFactory.getLogger(StreamingResultSet.class);
+
+ private final BlockingClientCall, QueryResult> stream;
+ private final RemoteGrpcDatabase db;
+ protected Iterator currentBatch = Collections.emptyIterator();
+ private boolean streamExhausted = false;
+ private Result nextResult = null;
+ private final AtomicLong totalProcessed = new AtomicLong(0);
+
+ StreamingResultSet(BlockingClientCall, QueryResult> stream, RemoteGrpcDatabase db) {
+ this.stream = stream;
+ this.db = db;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (nextResult != null) {
+ return true;
+ }
+
+ // Try to get next from current batch
+ if (currentBatch.hasNext()) {
+ nextResult = currentBatch.next();
+ return true;
+ }
+
+ // Current batch exhausted, try to fetch next batch
+ if (streamExhausted) {
+ return false;
+ }
+
+ if (db.debugTx != null) {
+ db.checkCrossThreadUse("streamQuery.hasNext");
+ }
+
+ try {
+ while (stream.hasNext()) {
+ final QueryResult queryResult = stream.read();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received batch with {} records, isLastBatch={}", queryResult.getRecordsCount(),
+ queryResult.getIsLastBatch());
+ }
+
+ if (queryResult.getRecordsCount() == 0) {
+ if (queryResult.getIsLastBatch()) {
+ streamExhausted = true;
+ return false;
+ }
+ continue; // empty non-terminal batch
+ }
+
+ // Convert GrpcRecords to Results
+ currentBatch = convertBatchToResults(queryResult);
+
+ if (currentBatch.hasNext()) {
+ nextResult = currentBatch.next();
+ return true;
+ }
+ }
+
+ streamExhausted = true;
+ return false;
+
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ db.handleGrpcException(e);
+ throw new IllegalStateException("unreachable");
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Stream interrupted", e);
+ }
+ catch (RuntimeException re) {
+ throw re;
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Stream failed", e);
+ }
+ }
+
+ /**
+ * Convert a QueryResult batch to an Iterator of Result objects
+ */
+ protected Iterator convertBatchToResults(QueryResult queryResult) {
+ List results = new ArrayList<>(queryResult.getRecordsCount());
+
+ for (GrpcRecord grpcRecord : queryResult.getRecordsList()) {
+ // Use the existing grpcRecordToResult method from RemoteGrpcDatabase
+ Result result = db.grpcRecordToResult(grpcRecord);
+ results.add(result);
+ }
+
+ return results.iterator();
+ }
+
+ @Override
+ public Result next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ if (db.debugTx != null) {
+ db.checkCrossThreadUse("streamQuery.next");
+ }
+
+ Result result = nextResult;
+ nextResult = null;
+ totalProcessed.incrementAndGet();
+ return result;
+ }
+
+ @Override
+ public void close() {
+
+ try {
+ // Drain any remaining results
+ while (stream.hasNext()) {
+ stream.read();
+ }
+ }
+ catch (Exception e) {
+ logger.debug("Exception while draining stream during close", e);
+ }
+
+ // BlockingClientCall doesn't implement AutoCloseable
+ // No need to cast or check instanceof
+ }
+ }
+
+ /**
+ * Enhanced streaming with batch-aware ResultSet for better memory control and
+ * performance monitoring.
+ */
+ public ResultSet queryStreamBatched(final String language, final String query, final Map params, final int batchSize,
+ final StreamQueryRequest.RetrievalMode mode) {
+
+ checkDatabaseIsOpen();
+ stats.queries.incrementAndGet();
+
+ final StreamQueryRequest.Builder b = StreamQueryRequest.newBuilder().setDatabase(getName()).setQuery(query)
+ .setCredentials(buildCredentials()).setBatchSize(batchSize > 0 ? batchSize : 100).setRetrievalMode(mode);
+
+ if (params != null && !params.isEmpty()) {
+ b.putAllParameters(convertParamsToGrpcValue(params));
+ }
+
+ final BlockingClientCall, QueryResult> responseIterator = callServerStreaming("StreamQuery",
+ () -> blockingStub.withWaitForReady().withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).streamQuery(b.build()));
+
+ return new BatchedStreamingResultSet(responseIterator, this);
+ }
+
+ /**
+ * A ResultSet that exposes batch boundaries for advanced use cases while
+ * maintaining the standard ResultSet interface.
+ */
+ private static class BatchedStreamingResultSet extends StreamingResultSet {
+
+ private int currentBatchSize = 0;
+ private boolean isLastBatch = false;
+ private long runningTotal = 0;
+
+ BatchedStreamingResultSet(BlockingClientCall, QueryResult> stream, RemoteGrpcDatabase db) {
+ super(stream, db);
+ }
+
+ @Override
+ public boolean hasNext() {
+ boolean hasMore = super.hasNext();
+
+ // Update batch info when we fetch a new batch
+ if (hasMore && currentBatch != null) {
+ // Track batch metadata here if needed
+ }
+
+ return hasMore;
+ }
+
+ @Override
+ protected Iterator convertBatchToResults(QueryResult queryResult) {
+ // Capture batch metadata
+ this.currentBatchSize = queryResult.getRecordsCount();
+ this.isLastBatch = queryResult.getIsLastBatch();
+ this.runningTotal = queryResult.getRunningTotalEmitted();
+
+ // Call parent implementation
+ return super.convertBatchToResults(queryResult);
+ }
+
+ // Additional methods for batch-aware processing
+ public long getRunningTotal() {
+ return runningTotal;
+ }
+
+ public int getCurrentBatchSize() {
+ return currentBatchSize;
+ }
+
+ public boolean isLastBatch() {
+ return isLastBatch;
+ }
+ }
+
+ public Iterator queryStreamBatchesIterator(final String language, final String query, final Map params,
+ final int batchSize, final StreamQueryRequest.RetrievalMode mode) {
+
+ checkDatabaseIsOpen();
+ stats.queries.incrementAndGet();
+
+ final StreamQueryRequest.Builder b = StreamQueryRequest.newBuilder().setDatabase(getName()).setQuery(query)
+ .setCredentials(buildCredentials()).setBatchSize(batchSize > 0 ? batchSize : 100).setRetrievalMode(mode);
+
+ if (params != null && !params.isEmpty()) {
+ b.putAllParameters(convertParamsToGrpcValue(params));
+ }
+
+ final BlockingClientCall, QueryResult> responseIterator = callServerStreaming("StreamQuery",
+ () -> blockingStub.withWaitForReady().withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).streamQuery(b.build()));
+
+ return new Iterator() {
+ private QueryBatch nextBatch = null;
+ private boolean drained = false;
+
+ @Override
+ public boolean hasNext() {
+ if (nextBatch != null)
+ return true;
+ if (drained)
+ return false;
+
+ if (debugTx != null) {
+ checkCrossThreadUse("streamQueryBatches.hasNext");
+ }
+
+ try {
+
+ while (responseIterator.hasNext()) {
+ final QueryResult qr = responseIterator.read();
+
+ int totalInBatch = qr.getTotalRecordsInBatch();
+ if (totalInBatch == 0) {
+ totalInBatch = qr.getRecordsCount();
+ }
+
+ if (qr.getRecordsCount() == 0 && !qr.getIsLastBatch()) {
+ continue; // empty non-terminal batch
+ }
+
+ // Convert GrpcRecords to Results (not Records)
+ final List convertedResults = new ArrayList<>(qr.getRecordsCount());
+ for (GrpcRecord gr : qr.getRecordsList()) {
+ Result result = grpcRecordToResult(gr);
+ convertedResults.add(result);
+ }
+
+ // Create QueryBatch with Results instead of Records
+ nextBatch = new QueryBatch(convertedResults, totalInBatch, qr.getRunningTotalEmitted(), qr.getIsLastBatch());
+
+ if (qr.getIsLastBatch()) {
+ drained = true;
+ }
+ return true;
+ }
+
+ drained = true;
+ return false;
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ handleGrpcException(e);
+ throw new IllegalStateException("unreachable");
+ }
+ catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Stream interrupted", ie);
+ }
+ catch (RuntimeException re) {
+ throw re;
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Stream failed", e);
+ }
+ }
+
+ @Override
+ public QueryBatch next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ final QueryBatch out = nextBatch;
+ nextBatch = null;
+ return out;
+ }
+ };
+ }
+
+ public static final class QueryBatch {
+ private final List results; // Changed from List
+ private final int totalInBatch;
+ private final long runningTotal;
+ private final boolean lastBatch;
+
+ public QueryBatch(List results, int totalInBatch, long runningTotal, boolean lastBatch) {
+ this.results = results;
+ this.totalInBatch = totalInBatch;
+ this.runningTotal = runningTotal;
+ this.lastBatch = lastBatch;
+ }
+
+ public List results() {
+ return results;
+ }
+
+ // Backward compatibility: provide records() method that extracts Records from
+ // Results
+ @Deprecated
+ public List records() {
+ List records = new ArrayList<>(results.size());
+ for (Result result : results) {
+ if (result.isElement()) {
+ result.getRecord().ifPresent(records::add);
+ }
+ }
+ return records;
+ }
+
+ public int totalInBatch() {
+ return totalInBatch;
+ }
+
+ public long runningTotal() {
+ return runningTotal;
+ }
+
+ public boolean isLastBatch() {
+ return lastBatch;
+ }
+ }
+
+ public Iterator queryStream(final String database, final String sql, final Map params, final int batchSize,
+ final StreamQueryRequest.RetrievalMode mode, final TransactionContext tx, final long timeoutMs) {
+ final StreamQueryRequest.Builder reqB = StreamQueryRequest.newBuilder().setDatabase(database).setQuery(sql)
+ .putAllParameters(convertParamsToGrpcValue(params)).setCredentials(buildCredentials())
+ .setBatchSize(batchSize > 0 ? batchSize : 100).setRetrievalMode(mode);
+
+ if (tx != null)
+ reqB.setTransaction(tx);
+
+ final BlockingClientCall, QueryResult> it = callServerStreaming("StreamQuery",
+ () -> blockingStub.withDeadlineAfter(timeoutMs, TimeUnit.MILLISECONDS).streamQuery(reqB.build()));
+
+ return new Iterator() {
+ private Iterator curr = Collections.emptyIterator();
+
+ @Override
+ public boolean hasNext() {
+ if (curr.hasNext())
+ return true;
+
+ if (debugTx != null) {
+ checkCrossThreadUse("streamQuery.hasNext");
+ }
+
+ try {
+ while (it.hasNext()) {
+ final QueryResult qr = it.read();
+ curr = qr.getRecordsList().iterator();
+ if (curr.hasNext())
+ return true;
+ // else loop to fetch next batch (handles empty batches)
+ }
+ return false;
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ handleGrpcException(e);
+ throw new IllegalStateException("unreachable");
+ }
+ catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Stream interrupted", ie);
+ }
+ catch (RuntimeException re) {
+ throw re;
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Stream failed", e);
+ }
+ }
+
+ @Override
+ public GrpcRecord next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ if (debugTx != null) {
+ checkCrossThreadUse("streamQuery.next");
+ }
+ return curr.next();
+ }
+ };
+ }
+
+ @Deprecated
+ public Iterator queryStreamAsRecordIterator(final String language, final String query, final Map params,
+ final int batchSize) {
+ ResultSet rs = queryStream(language, query, params, batchSize);
+
+ // Convert ResultSet to Iterator
+ return new Iterator() {
+ @Override
+ public boolean hasNext() {
+ return rs.hasNext();
+ }
+
+ @Override
+ public Record next() {
+ Result result = rs.next();
+ // Try to get Record from Result
+ if (result.isElement()) {
+ return result.getRecord().orElseThrow(() -> new IllegalStateException("Result claims to be element but has no Record"));
+ }
+ // For non-Record results, throw or skip
+ throw new IllegalStateException("Result is not a Record: " + result);
+ }
+ };
+ }
+
+ @Override
+ public Iterator iterateType(final String typeName, final boolean polymorphic) {
+ String query = "select from `" + typeName + "`";
+ if (!polymorphic)
+ query += " where @type = '" + typeName + "'";
+ return streamQuery(query);
+ }
+
+ @Override
+ public Iterator iterateBucket(final String bucketName) {
+ return streamQuery("select from bucket:`" + bucketName + "`");
+ }
+
+ public String createRecord(final String cls, final Map props, final long timeoutMs) {
+ checkDatabaseIsOpen();
+
+ if (cls == null || cls.isBlank())
+ throw new IllegalArgumentException("cls must be non-empty");
+ if (props == null)
+ throw new IllegalArgumentException("props must be non-null");
+
+ // Build payload
+ final GrpcRecord recMsg = GrpcRecord.newBuilder().putAllProperties(convertParamsToGrpcValue(props)).build();
+
+ final CreateRecordRequest req = CreateRecordRequest.newBuilder().setDatabase(getName()).setType(cls).setRecord(recMsg)
+ .setCredentials(buildCredentials()).build();
+
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("CLIENT createRecord: db={}, txOpen={}, type={}, propCount={}, timeoutMs={}", getName(), (transactionId != null),
+ cls, props.size(), timeoutMs);
+ }
+
+ final CreateRecordResponse res = callUnary("CreateRecord",
+ () -> blockingStub.withDeadlineAfter(timeoutMs, TimeUnit.MILLISECONDS).createRecord(req));
+
+ return res.getRid(); // e.g. "#12:0"
+
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ handleGrpcException(e); // rethrows mapped domain exception
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ public String createRecordTx(final String cls, final Map props, final long timeoutMs) {
+ checkDatabaseIsOpen();
+
+ final GrpcRecord recMsg = GrpcRecord.newBuilder().putAllProperties(convertParamsToGrpcValue(props)).build();
+
+ final CreateRecordRequest req = CreateRecordRequest.newBuilder().setDatabase(getName()).setType(cls).setRecord(recMsg)
+ .setTransaction(TransactionContext.newBuilder().setBegin(true).setCommit(true)).setCredentials(buildCredentials()).build();
+
+ try {
+ final CreateRecordResponse res = callUnary("CreateRecord",
+ () -> blockingStub.withDeadlineAfter(timeoutMs, TimeUnit.MILLISECONDS).createRecord(req));
+ return res.getRid();
+
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ handleGrpcException(e);
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ public boolean updateRecord(String rid, Map props, long timeoutMs) {
+ PropertiesUpdate partial = PropertiesUpdate.newBuilder().putAllProperties(convertParamsToGrpcValue(props)).build();
+
+ return updateRecord(rid, partial, timeoutMs);
+ }
+
+ public boolean updateRecord(String rid, Record dbRecord, long timeoutMs) {
+ GrpcRecord record = ProtoUtils.toProtoRecord(dbRecord);
+ return updateRecordFull(rid, record, timeoutMs);
+ }
+
+ private boolean updateRecord(final String rid, final PropertiesUpdate partial, final long timeoutMs) {
+ checkDatabaseIsOpen();
+
+ if (rid == null || rid.isBlank())
+ throw new IllegalArgumentException("rid must be non-empty");
+ if (partial == null)
+ throw new IllegalArgumentException("partial must be non-null");
+
+ final UpdateRecordRequest req = UpdateRecordRequest.newBuilder().setDatabase(getName()).setRid(rid).setPartial(partial)
+ // Per-call tx: begin+commit. If you have an outer tx, pass it instead.
+ .setTransaction(TransactionContext.newBuilder().setBegin(true).setCommit(true)).setCredentials(buildCredentials()).build();
+
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("CLIENT updateRecord(partial): db={}, txOpen={}, rid={}, timeoutMs={}", getName(), (transactionId != null), rid,
+ timeoutMs);
+ }
+
+ final UpdateRecordResponse res = callUnary("UpdateRecord",
+ () -> blockingStub.withDeadlineAfter(timeoutMs, TimeUnit.MILLISECONDS).updateRecord(req));
+
+ // Most builds expose getSuccess(); if your proto has getUpdated(), swap here.
+ return res.getSuccess();
+
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ handleGrpcException(e); // rethrows mapped domain exception
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ private boolean updateRecordFull(final String rid, final GrpcRecord record, final long timeoutMs) {
+ checkDatabaseIsOpen();
+
+ if (rid == null || rid.isBlank())
+ throw new IllegalArgumentException("rid must be non-empty");
+ if (record == null)
+ throw new IllegalArgumentException("record must be non-null");
+
+ final UpdateRecordRequest req = UpdateRecordRequest.newBuilder().setDatabase(getName()).setRid(rid).setRecord(record)
+ // Per-call tx: begin+commit. If you have an outer tx, pass it instead.
+ .setTransaction(TransactionContext.newBuilder().setBegin(true).setCommit(true)).setCredentials(buildCredentials()).build();
+
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("CLIENT updateRecord(full): db={}, txOpen={}, rid={}, timeoutMs={}", getName(), (transactionId != null), rid,
+ timeoutMs);
+ }
+
+ final UpdateRecordResponse res = callUnary("UpdateRecord",
+ () -> blockingStub.withDeadlineAfter(timeoutMs, TimeUnit.MILLISECONDS).updateRecord(req));
+
+ return res.getSuccess();
+
+ }
+ catch (io.grpc.StatusRuntimeException | io.grpc.StatusException e) {
+ handleGrpcException(e); // rethrows mapped domain exception
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ @Override
+ public RemoteTransactionExplicitLock acquireLock() {
+ // Need gRPC-specific implementation
+ if (explicitLock == null)
+ explicitLock = new RemoteGrpcTransactionExplicitLock(this);
+
+ return explicitLock;
+ }
+
+ @Override
+ public long countBucket(final String bucketName) {
+ checkDatabaseIsOpen();
+ stats.countBucket.incrementAndGet();
+ ResultSet result = query("sql", "select count(*) as count from bucket:" + bucketName);
+ if (result.hasNext()) {
+ Number count = result.next().getProperty("count");
+ return count != null ? count.longValue() : 0;
+ }
+ return 0;
+ }
+
+ @Override
+ public long countType(final String typeName, final boolean polymorphic) {
+ checkDatabaseIsOpen();
+ stats.countType.incrementAndGet();
+ final String appendix = polymorphic ? "" : " where @type = '" + typeName + "'";
+ ResultSet result = query("sql", "select count(*) as count from " + typeName + appendix);
+ if (result.hasNext()) {
+ Number count = result.next().getProperty("count");
+ return count != null ? count.longValue() : 0;
+ }
+ return 0;
+ }
+
+ @Override
+ public Record lookupByRID(final RID rid) {
+ return lookupByRID(rid, true);
+ }
+
+ @Override
+ public Record lookupByRID(final RID rid, final boolean loadContent) {
+ checkDatabaseIsOpen();
+ stats.readRecord.incrementAndGet();
+
+ if (rid == null)
+ throw new IllegalArgumentException("Record is null");
+
+ final LookupByRidRequest req = LookupByRidRequest.newBuilder().setDatabase(getName()).setRid(rid.toString())
+ .setCredentials(buildCredentials()).build();
+
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("CLIENT lookupByRID: db={}, txOpen={}, rid={}, loadContent={}, timeoutMs={}", getName(), (transactionId != null),
+ rid, loadContent, getTimeout());
+ }
+
+ final LookupByRidResponse resp = callUnary("LookupByRid",
+ () -> blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).lookupByRid(req));
+
+ if (!resp.getFound())
+ throw new RecordNotFoundException("Record " + rid + " not found", rid);
+
+ // Note: loadContent is currently a no-op for gRPC; response already carries the
+ // record.
+ return grpcRecordToDBRecord(resp.getRecord());
+
+ }
+ catch (io.grpc.StatusException | io.grpc.StatusRuntimeException e) {
+ handleGrpcException(e); // maps & rethrows proper domain exception
+ throw new IllegalStateException("unreachable");
+ }
+ }
+
+ @Override
+ public boolean existsRecord(RID rid) {
+ stats.existsRecord.incrementAndGet();
+ if (rid == null)
+ throw new IllegalArgumentException("Record is null");
+ try {
+ return lookupByRID(rid, false) != null;
+ }
+ catch (RecordNotFoundException e) {
+ return false;
+ }
+ }
+
+ // Convenience overload that accepts domain rows (convert first)
+ public InsertSummary insertBulkAsListOfMaps(final InsertOptions options, final List