axiodb 18.1.4 → 20.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/README.md +79 -7
  2. package/cli/VERSION +1 -1
  3. package/cli/cmd/auth_management.go +68 -0
  4. package/cli/cmd/document.go +33 -0
  5. package/cli/cmd/health.go +19 -0
  6. package/cli/cmd/transaction.go +191 -0
  7. package/cli/cmd/version.go +1 -1
  8. package/cli/pkg/commands/document.go +19 -0
  9. package/cli/pkg/commands/health.go +18 -0
  10. package/cli/pkg/commands/transaction.go +84 -0
  11. package/cli/pkg/httpclient/client.go +76 -0
  12. package/lib/Helper/Console.helper.js.map +1 -1
  13. package/lib/Helper/Converter.helper.d.ts +1 -1
  14. package/lib/Helper/Converter.helper.js +0 -1
  15. package/lib/Helper/Converter.helper.js.map +1 -1
  16. package/lib/Helper/DocumentLoader.helper.js +2 -1
  17. package/lib/Helper/DocumentLoader.helper.js.map +1 -1
  18. package/lib/Helper/Logger.helper.d.ts +5 -0
  19. package/lib/Helper/Logger.helper.js +45 -0
  20. package/lib/Helper/Logger.helper.js.map +1 -0
  21. package/lib/Helper/PathSanitizer.helper.js.map +1 -1
  22. package/lib/Helper/SortedIndexValues.helper.js.map +1 -1
  23. package/lib/Helper/UniqueGenerator.helper.js.map +1 -1
  24. package/lib/Helper/response.helper.d.ts +2 -2
  25. package/lib/Helper/response.helper.js +6 -3
  26. package/lib/Helper/response.helper.js.map +1 -1
  27. package/lib/Memory/memory.operation.d.ts +9 -4
  28. package/lib/Memory/memory.operation.js +3 -3
  29. package/lib/Memory/memory.operation.js.map +1 -1
  30. package/lib/Services/Aggregation/Aggregation.Operation.d.ts +1 -1
  31. package/lib/Services/Aggregation/Aggregation.Operation.js +0 -1
  32. package/lib/Services/Aggregation/Aggregation.Operation.js.map +1 -1
  33. package/lib/Services/Aggregation/OperatorRegistry.js.map +1 -1
  34. package/lib/Services/Aggregation/operators/accumulatorOperators.d.ts +12 -12
  35. package/lib/Services/Aggregation/operators/accumulatorOperators.js +2 -3
  36. package/lib/Services/Aggregation/operators/accumulatorOperators.js.map +1 -1
  37. package/lib/Services/Aggregation/operators/expressionOperators.d.ts +3 -3
  38. package/lib/Services/Aggregation/operators/expressionOperators.js +111 -108
  39. package/lib/Services/Aggregation/operators/expressionOperators.js.map +1 -1
  40. package/lib/Services/Aggregation/operators/stageOperators.d.ts +20 -37
  41. package/lib/Services/Aggregation/operators/stageOperators.js +11 -11
  42. package/lib/Services/Aggregation/operators/stageOperators.js.map +1 -1
  43. package/lib/Services/Auth/AuthEvents.service.js.map +1 -1
  44. package/lib/Services/Auth/AuthSeeder.service.js +2 -2
  45. package/lib/Services/Auth/AuthSeeder.service.js.map +1 -1
  46. package/lib/Services/Auth/AuthService.service.js +5 -8
  47. package/lib/Services/Auth/AuthService.service.js.map +1 -1
  48. package/lib/Services/Auth/ConfigDatabase.service.js.map +1 -1
  49. package/lib/Services/Auth/CookieCodec.helper.js.map +1 -1
  50. package/lib/Services/Auth/LoginRateLimiter.service.js.map +1 -1
  51. package/lib/Services/Auth/PasswordHasher.helper.js.map +1 -1
  52. package/lib/Services/Auth/PermissionChecker.helper.js.map +1 -1
  53. package/lib/Services/Auth/SessionStore.service.js.map +1 -1
  54. package/lib/Services/CRUD Operation/Create.operation.js +0 -1
  55. package/lib/Services/CRUD Operation/Create.operation.js.map +1 -1
  56. package/lib/Services/CRUD Operation/Delete.operation.d.ts +2 -2
  57. package/lib/Services/CRUD Operation/Delete.operation.js +2 -3
  58. package/lib/Services/CRUD Operation/Delete.operation.js.map +1 -1
  59. package/lib/Services/CRUD Operation/Reader.operation.d.ts +11 -3
  60. package/lib/Services/CRUD Operation/Reader.operation.js +76 -40
  61. package/lib/Services/CRUD Operation/Reader.operation.js.map +1 -1
  62. package/lib/Services/CRUD Operation/Update.operation.d.ts +4 -4
  63. package/lib/Services/CRUD Operation/Update.operation.js +2 -2
  64. package/lib/Services/CRUD Operation/Update.operation.js.map +1 -1
  65. package/lib/Services/Collection/collection.operation.d.ts +10 -3
  66. package/lib/Services/Collection/collection.operation.js +32 -7
  67. package/lib/Services/Collection/collection.operation.js.map +1 -1
  68. package/lib/Services/Database/database.operation.js +13 -9
  69. package/lib/Services/Database/database.operation.js.map +1 -1
  70. package/lib/Services/Index/Index.service.d.ts +1 -1
  71. package/lib/Services/Index/Index.service.js +1 -1
  72. package/lib/Services/Index/Index.service.js.map +1 -1
  73. package/lib/Services/Index/IndexCache.service.js +4 -5
  74. package/lib/Services/Index/IndexCache.service.js.map +1 -1
  75. package/lib/Services/Index/ReadIndex.service.d.ts +4 -4
  76. package/lib/Services/Index/ReadIndex.service.js +10 -9
  77. package/lib/Services/Index/ReadIndex.service.js.map +1 -1
  78. package/lib/Services/Indexation.operation.d.ts +1 -1
  79. package/lib/Services/Indexation.operation.js +7 -6
  80. package/lib/Services/Indexation.operation.js.map +1 -1
  81. package/lib/Services/Transaction/LockManager.service.js +3 -4
  82. package/lib/Services/Transaction/LockManager.service.js.map +1 -1
  83. package/lib/Services/Transaction/Session.service.js +9 -7
  84. package/lib/Services/Transaction/Session.service.js.map +1 -1
  85. package/lib/Services/Transaction/Transaction.service.d.ts +4 -2
  86. package/lib/Services/Transaction/Transaction.service.js +7 -2
  87. package/lib/Services/Transaction/Transaction.service.js.map +1 -1
  88. package/lib/Services/Transaction/TransactionIndexManager.service.d.ts +1 -1
  89. package/lib/Services/Transaction/TransactionIndexManager.service.js +11 -6
  90. package/lib/Services/Transaction/TransactionIndexManager.service.js.map +1 -1
  91. package/lib/Services/Transaction/TransactionRegistry.service.js.map +1 -1
  92. package/lib/Services/Transaction/WriteAheadLog.service.js +0 -1
  93. package/lib/Services/Transaction/WriteAheadLog.service.js.map +1 -1
  94. package/lib/client/AggregationProxy.js.map +1 -1
  95. package/lib/client/AxioDBCloud.client.d.ts +15 -2
  96. package/lib/client/AxioDBCloud.client.js +24 -2
  97. package/lib/client/AxioDBCloud.client.js.map +1 -1
  98. package/lib/client/CollectionProxy.d.ts +8 -0
  99. package/lib/client/CollectionProxy.js +26 -0
  100. package/lib/client/CollectionProxy.js.map +1 -1
  101. package/lib/client/DatabaseProxy.js.map +1 -1
  102. package/lib/client/DeleteOperationProxy.js.map +1 -1
  103. package/lib/client/PooledConnection.d.ts +1 -1
  104. package/lib/client/PooledConnection.js +6 -3
  105. package/lib/client/PooledConnection.js.map +1 -1
  106. package/lib/client/ReaderProxy.d.ts +3 -1
  107. package/lib/client/ReaderProxy.js +7 -0
  108. package/lib/client/ReaderProxy.js.map +1 -1
  109. package/lib/client/TransactionProxy.d.ts +37 -0
  110. package/lib/client/TransactionProxy.js +101 -0
  111. package/lib/client/TransactionProxy.js.map +1 -0
  112. package/lib/client/UpdateOperationProxy.js.map +1 -1
  113. package/lib/client/types/client.types.js.map +1 -1
  114. package/lib/config/DB.js.map +1 -1
  115. package/lib/config/Interfaces/Helper/response.helper.interface.d.ts +2 -2
  116. package/lib/config/Interfaces/Helper/response.helper.interface.js +0 -1
  117. package/lib/config/Interfaces/Helper/response.helper.interface.js.map +1 -1
  118. package/lib/config/Interfaces/Operation/aggregation.interface.d.ts +4 -4
  119. package/lib/config/Interfaces/Operation/aggregation.interface.js +0 -1
  120. package/lib/config/Interfaces/Operation/aggregation.interface.js.map +1 -1
  121. package/lib/config/Interfaces/Operation/database.operation.interface.d.ts +4 -1
  122. package/lib/config/Interfaces/Operation/database.operation.interface.js +0 -1
  123. package/lib/config/Interfaces/Operation/database.operation.interface.js.map +1 -1
  124. package/lib/config/Interfaces/Transaction/transaction.interface.d.ts +3 -3
  125. package/lib/config/Interfaces/shared.types.d.ts +36 -0
  126. package/lib/config/Interfaces/shared.types.js +7 -0
  127. package/lib/config/Interfaces/shared.types.js.map +1 -0
  128. package/lib/config/Keys/Keys.js.map +1 -1
  129. package/lib/config/Keys/Permissions.js.map +1 -1
  130. package/lib/config/Keys/StatusCode.js.map +1 -1
  131. package/lib/engine/Filesystem/FileManager.js +3 -4
  132. package/lib/engine/Filesystem/FileManager.js.map +1 -1
  133. package/lib/engine/Filesystem/FolderManager.js +11 -11
  134. package/lib/engine/Filesystem/FolderManager.js.map +1 -1
  135. package/lib/engine/cli/worker_process.js.map +1 -1
  136. package/lib/engine/node/WorkerForDataLoad.engine.js +7 -6
  137. package/lib/engine/node/WorkerForDataLoad.engine.js.map +1 -1
  138. package/lib/engine/node/WorkerForSearch.engine.js +0 -1
  139. package/lib/engine/node/WorkerForSearch.engine.js.map +1 -1
  140. package/lib/server/config/PortFreeChecker.js +0 -1
  141. package/lib/server/config/PortFreeChecker.js.map +1 -1
  142. package/lib/server/config/keys.js +0 -1
  143. package/lib/server/config/keys.js.map +1 -1
  144. package/lib/server/config/server.js +2 -2
  145. package/lib/server/config/server.js.map +1 -1
  146. package/lib/server/controller/Auth/Auth.controller.js.map +1 -1
  147. package/lib/server/controller/Auth/RoleManagement.controller.js.map +1 -1
  148. package/lib/server/controller/Auth/UserManagement.controller.js.map +1 -1
  149. package/lib/server/controller/Collections/Collection.controller.js +9 -7
  150. package/lib/server/controller/Collections/Collection.controller.js.map +1 -1
  151. package/lib/server/controller/Database/Databse.controller.js +12 -13
  152. package/lib/server/controller/Database/Databse.controller.js.map +1 -1
  153. package/lib/server/controller/Index/Index.controller.js +4 -3
  154. package/lib/server/controller/Index/Index.controller.js.map +1 -1
  155. package/lib/server/controller/Operation/CRUD.controller.d.ts +1 -0
  156. package/lib/server/controller/Operation/CRUD.controller.js +42 -18
  157. package/lib/server/controller/Operation/CRUD.controller.js.map +1 -1
  158. package/lib/server/controller/Stats.controller.js +21 -15
  159. package/lib/server/controller/Stats.controller.js.map +1 -1
  160. package/lib/server/helper/databaseImport.helper.js.map +1 -1
  161. package/lib/server/helper/documentCounterInFolder.helper.js.map +1 -1
  162. package/lib/server/helper/responseBuilder.helper.d.ts +2 -2
  163. package/lib/server/helper/responseBuilder.helper.js.map +1 -1
  164. package/lib/server/middleware/auth.middleware.js.map +1 -1
  165. package/lib/server/middleware/permission.middleware.js.map +1 -1
  166. package/lib/server/router/Router.js +8 -7
  167. package/lib/server/router/Router.js.map +1 -1
  168. package/lib/server/router/Routers/Auth.routes.js.map +1 -1
  169. package/lib/server/router/Routers/Collection.routes.js +3 -3
  170. package/lib/server/router/Routers/Collection.routes.js.map +1 -1
  171. package/lib/server/router/Routers/DB.routes.js +1 -1
  172. package/lib/server/router/Routers/DB.routes.js.map +1 -1
  173. package/lib/server/router/Routers/Index.routes.js.map +1 -1
  174. package/lib/server/router/Routers/Operation.routes.js +25 -10
  175. package/lib/server/router/Routers/Operation.routes.js.map +1 -1
  176. package/lib/server/router/Routers/RoleManagement.routes.js.map +1 -1
  177. package/lib/server/router/Routers/UserManagement.routes.js.map +1 -1
  178. package/lib/tcp/config/keys.d.ts +32 -32
  179. package/lib/tcp/config/keys.js.map +1 -1
  180. package/lib/tcp/config/permissions.d.ts +1 -4
  181. package/lib/tcp/config/permissions.js +8 -4
  182. package/lib/tcp/config/permissions.js.map +1 -1
  183. package/lib/tcp/config/protocol.d.ts +3 -3
  184. package/lib/tcp/config/protocol.js +31 -9
  185. package/lib/tcp/config/protocol.js.map +1 -1
  186. package/lib/tcp/config/server.js +17 -12
  187. package/lib/tcp/config/server.js.map +1 -1
  188. package/lib/tcp/connection/ConnectionManager.js.map +1 -1
  189. package/lib/tcp/connection/ConnectionRateLimiter.js.map +1 -1
  190. package/lib/tcp/connection/RequestContext.d.ts +2 -2
  191. package/lib/tcp/connection/RequestContext.js.map +1 -1
  192. package/lib/tcp/connection/TransactionManager.d.ts +26 -0
  193. package/lib/tcp/connection/TransactionManager.js +114 -0
  194. package/lib/tcp/connection/TransactionManager.js.map +1 -0
  195. package/lib/tcp/handler/CommandHandler.d.ts +3 -0
  196. package/lib/tcp/handler/CommandHandler.js +32 -8
  197. package/lib/tcp/handler/CommandHandler.js.map +1 -1
  198. package/lib/tcp/handler/handlers/Auth.handler.d.ts +4 -2
  199. package/lib/tcp/handler/handlers/Auth.handler.js.map +1 -1
  200. package/lib/tcp/handler/handlers/Collection.handler.d.ts +7 -5
  201. package/lib/tcp/handler/handlers/Collection.handler.js +1 -3
  202. package/lib/tcp/handler/handlers/Collection.handler.js.map +1 -1
  203. package/lib/tcp/handler/handlers/DB.handler.d.ts +6 -4
  204. package/lib/tcp/handler/handlers/DB.handler.js +1 -2
  205. package/lib/tcp/handler/handlers/DB.handler.js.map +1 -1
  206. package/lib/tcp/handler/handlers/Operation.handler.d.ts +19 -57
  207. package/lib/tcp/handler/handlers/Operation.handler.js +138 -167
  208. package/lib/tcp/handler/handlers/Operation.handler.js.map +1 -1
  209. package/lib/tcp/handler/handlers/Transaction.handler.d.ts +18 -0
  210. package/lib/tcp/handler/handlers/Transaction.handler.js +199 -0
  211. package/lib/tcp/handler/handlers/Transaction.handler.js.map +1 -0
  212. package/lib/tcp/types/command.types.d.ts +6 -1
  213. package/lib/tcp/types/command.types.js +11 -1
  214. package/lib/tcp/types/command.types.js.map +1 -1
  215. package/lib/tcp/types/protocol.types.d.ts +10 -7
  216. package/lib/tcp/types/protocol.types.js.map +1 -1
  217. package/lib/utility/BufferLoaderWithWorker.utils.js +6 -5
  218. package/lib/utility/BufferLoaderWithWorker.utils.js.map +1 -1
  219. package/lib/utility/Searcher.utils.d.ts +3 -7
  220. package/lib/utility/Searcher.utils.js +49 -49
  221. package/lib/utility/Searcher.utils.js.map +1 -1
  222. package/lib/utility/SortData.utils.d.ts +3 -2
  223. package/lib/utility/SortData.utils.js +3 -19
  224. package/lib/utility/SortData.utils.js.map +1 -1
  225. package/lib/utility/ZipUnzip.utils.d.ts +2 -2
  226. package/lib/utility/ZipUnzip.utils.js +2 -2
  227. package/lib/utility/ZipUnzip.utils.js.map +1 -1
  228. package/package.json +8 -3
package/README.md CHANGED
@@ -156,6 +156,8 @@ const db = new AxioDB({
156
156
 
157
157
  ### Querying
158
158
  - **Chainable Query API:** `.query()`, `.Sort()`, `.Limit()`, `.Skip()`, `.setCount()`, `.setProject()`, `.exec()` / `.findOne()`
159
+ - **Index Hints:** `.hint('fieldName')` to force a specific index on any query for predictable performance
160
+ - **Batch Read:** `.findByIds(['id1', 'id2'])` to retrieve multiple documents by ID in a single call
159
161
  - **MongoDB-style Query Operators:** `$gt`, `$gte`, `$lt`, `$lte`, `$ne`, `$in`, `$nin`, `$exists`, `$regex`, `$or`, `$and`
160
162
  - **Aggregation Pipelines:** 60+ MongoDB-compatible stages (`$match`, `$group`, `$sort`, `$project`, `$limit`, `$skip`, `$unwind`, `$addFields`, `$lookup`, `$facet`, `$bucket`, `$count`, `$sample`, ...) with full expression evaluator, cross-collection `$lookup` joins, and custom operator registration via `OperatorRegistry`
161
163
  - **Bulk Operations:** high-performance `insertMany`, `UpdateMany`, `deleteMany`
@@ -170,14 +172,23 @@ const db = new AxioDB({
170
172
  ### Transactions
171
173
  - **ACID-compliant, single-collection transactions** with savepoints, rollback, and Write-Ahead Logging (WAL) for crash recovery
172
174
  - **Session Management:** scoped transactions with timeout support
175
+ - **TCP Transactions:** BEGIN/COMMIT/ROLLBACK over TCP with savepoints, connection-pinned client proxy, and auto-rollback on disconnect
173
176
 
174
177
  ```javascript
178
+ // Local transaction
175
179
  const session = collection.startSession();
176
180
  await session.withTransaction(async (tx) => {
177
181
  await tx.insert({ name: 'Alice', balance: 1000 });
178
- await tx.update({ name: 'Bob' }, { $inc: { balance: -100 } });
182
+ // Updates are flat merges; AxioDB does not implement MongoDB's $inc operator.
183
+ await tx.update({ name: 'Bob' }, { balance: 900 });
179
184
  // Auto-commits on success, auto-rolls-back on error
180
185
  });
186
+
187
+ // TCP transaction
188
+ const tx = await client.db('mydb').collection('users').beginTransaction();
189
+ await tx.insert({ name: 'Alice', balance: 1000 });
190
+ await tx.update({ name: 'Bob' }, { balance: 900 });
191
+ await tx.commit(); // or await tx.rollback();
181
192
  ```
182
193
 
183
194
  ### Caching
@@ -211,7 +222,7 @@ await session.withTransaction(async (tx) => {
211
222
  - **🔄 Zero Code Changes:** same `createDB`/`createCollection`/`insert`/`query` API as embedded AxioDB
212
223
  - **⚡ Fast Binary Protocol:** length-prefixed JSON framing, with automatic reconnection
213
224
  - **🔐 Optional Authentication:** shared RBAC with the GUI, per-IP rate limiting (see [Advanced](#advanced-tcp-authentication) below)
214
- - **📦 35+ Commands:** full CRUD, aggregation, and indexing over the wire
225
+ - **📦 32 Commands:** full CRUD, aggregation, indexing, and transactions over the wire
215
226
  - **🔁 Auto-Reconnect:** exponential backoff, up to 10 retry attempts
216
227
  - **💓 Heartbeat Monitoring:** `PING`/`PONG` every 30 seconds
217
228
  - **🆔 Request Correlation:** UUID-based request/response matching
@@ -371,12 +382,17 @@ irm https://raw.githubusercontent.com/nexoral/AxioDB/main/cli/Scripts/install.ps
371
382
 
372
383
  ### Usage
373
384
 
385
+ > **Activation notice:** Data commands (`db`/`collection`/`document`/`index`/`transaction`/`ping`/`health`/`connect`) require the server started with `TCP: true` (`AXIODB_TCP=true`, port 27019). Management commands (`user`/`role`/`user change-password`/`export`/`import`) require `GUI: true`/`HTTP: true` (`AXIODB_GUI=true`, port 27018). TCP is data-plane only — no management over TCP by design.
386
+
374
387
  **Single commands:**
375
388
  ```bash
376
389
  axiodb -c axiodb://127.0.0.1:27019 ping
377
390
  axiodb -c axiodb://127.0.0.1:27019 db list
378
391
  axiodb -c axiodb://127.0.0.1:27019 document insert '{"name":"Alice"}' --db mydb --collection users
379
392
  axiodb -c axiodb://127.0.0.1:27019 document query '{}' --db mydb --collection users
393
+ axiodb -c axiodb://127.0.0.1:27019 document query '{}' --hint email --db mydb --collection users
394
+ axiodb -c axiodb://127.0.0.1:27019 health
395
+ axiodb -c axiodb://127.0.0.1:27019 transaction begin --db mydb --collection users
380
396
  ```
381
397
 
382
398
  **Interactive REPL (MongoDB shell style):**
@@ -412,9 +428,36 @@ axiodb import ./backups/mydb.tar.gz --http-host localhost --http-port 27018 -u a
412
428
  # Tab completes .tar.gz file paths
413
429
  ```
414
430
 
431
+ **User and role administration (via HTTP API):**
432
+ ```bash
433
+ axiodb user list --http-host localhost --http-port 27018 -u admin -p secret
434
+ axiodb user create analyst analyst123 View --http-host localhost --http-port 27018 -u admin -p secret
435
+ axiodb user change-password oldPass newPass --http-host localhost --http-port 27018 -u analyst -p analyst123
436
+ axiodb role list --http-host localhost --http-port 27018 -u admin -p secret
437
+ axiodb role create Auditor document:view,document:query --http-host localhost --http-port 27018 -u admin -p secret
438
+ ```
439
+
440
+ **Transactions (via TCP, full lifecycle):**
441
+ ```bash
442
+ # File-based batch (auto-commit/rollback)
443
+ axiodb transaction run operations.json --db mydb --collection users
444
+ # Manual control
445
+ axiodb transaction begin --db mydb --collection users # → transactionId
446
+ axiodb transaction savepoint <txnId> sp1
447
+ axiodb transaction rollback-to <txnId> sp1
448
+ axiodb transaction release <txnId> sp1
449
+ axiodb transaction commit <txnId>
450
+ axiodb transaction rollback <txnId>
451
+ ```
452
+
453
+ Management commands use the authenticated HTTP API; they do not add management commands to the
454
+ TCP client protocol, which remains focused on database, collection, document, index, aggregation,
455
+ count, and transaction operations.
456
+
415
457
  ### Features
416
458
 
417
- - **All 23 commands:** database, collection, document CRUD, aggregation, indexing, export, import
459
+ - **TCP data operations:** database, collection, document CRUD, aggregation, indexing, counts, and transactions
460
+ - **CLI transactions and diagnostics:** run a transaction operation file, use query index hints, find documents by IDs, and check TCP health
418
461
  - **Interactive REPL:** MongoDB shell syntax (`use`, `show dbs`, `db.coll.find()`)
419
462
  - **Export & Import:** backup/restore databases via HTTP API, tab-completes file paths
420
463
  - **TLS support:** `--tls`, `--tls-cert`, `--tls-skip-verify`
@@ -645,7 +688,7 @@ volumes:
645
688
  ## 🤖 MCP Server — AI Agent Integration
646
689
 
647
690
  Spin up the same Docker container with `AXIODB_MCP=true` and let Claude (or any MCP-compatible
648
- AI agent) talk to your AxioDB instance directly — 32 tools covering databases, collections,
691
+ AI agent) talk to your AxioDB instance directly — 43 tools covering databases, collections,
649
692
  documents, aggregation, indexes, dashboard stats, and user/role management, all gated by the
650
693
  same RBAC as the web GUI. It runs in the same process as your existing container; nothing new
651
694
  to install, no second database instance.
@@ -804,17 +847,21 @@ const electronics = await products
804
847
 
805
848
  - `createDB(dbName: string): Promise<Database>`
806
849
  - `deleteDatabase(dbName: string): Promise<SuccessInterface | ErrorInterface>`
850
+ - `isDatabaseExists(dbName: string): Promise<boolean>`
851
+ - `getInstanceInfo(): Promise<SuccessInterface | undefined>`
807
852
 
808
853
  ### Database
809
854
 
810
855
  - `createCollection(name: string): Promise<Collection>`
811
856
  - `deleteCollection(name: string): Promise<SuccessInterface | ErrorInterface>`
857
+ - `isCollectionExists(name: string): Promise<boolean>`
812
858
  - `getCollectionInfo(): Promise<SuccessInterface>`
813
859
 
814
860
  ### Collection
815
861
 
816
862
  - `insert(document: object): Promise<SuccessInterface | ErrorInterface>`
817
- - `insertMany(documents: object[]): Promise<SuccessInterface | ErrorInterface>`
863
+ - `insertMany(documents: object | object[]): Promise<SuccessInterface | ErrorInterface>`
864
+ - `totalDocuments(): Promise<SuccessInterface | ErrorInterface>`
818
865
  - `query(query: object): Reader`
819
866
  - `update(query: object): Updater`
820
867
  - `delete(query: object): Deleter`
@@ -828,6 +875,8 @@ const electronics = await products
828
875
 
829
876
  `update(query)` and `delete(query)` on their own don't change anything — they return a chainable object. Call one of the methods below to actually apply the change:
830
877
 
878
+ Updates are flat shallow merges. AxioDB does not implement MongoDB update operators such as `$inc`, `$set`, or `$push`; keys beginning with `$` are stored as ordinary field names.
879
+
831
880
  - `updater.UpdateOne(data: object): Promise<SuccessInterface | ErrorInterface>` — applies `data` to the first document matching `query`
832
881
  - `updater.UpdateMany(data: object): Promise<SuccessInterface | ErrorInterface>` — applies `data` to every document matching `query`
833
882
  - `deleter.deleteOne(): Promise<SuccessInterface | ErrorInterface>` — deletes the first document matching `query`
@@ -860,7 +909,7 @@ await collection.delete({ status: 'inactive' }).deleteMany();
860
909
  ### Transaction (Session)
861
910
 
862
911
  - `startSession(options?: { timeout?: number }): Session`
863
- - `session.withTransaction(callback: Function): Promise<any>`
912
+ - `session.withTransaction(callback: (transaction: Transaction) => Promise<void>): Promise<SuccessInterface | ErrorInterface>`
864
913
  - `session.startTransaction(): Transaction`
865
914
  - `transaction.insert(document: object): Transaction`
866
915
  - `transaction.update(query: object, update: object): Transaction`
@@ -868,7 +917,30 @@ await collection.delete({ status: 'inactive' }).deleteMany();
868
917
  - `transaction.savepoint(name: string): Transaction`
869
918
  - `transaction.rollbackToSavepoint(name: string): Transaction`
870
919
  - `transaction.commit(): Promise<SuccessInterface>`
871
- - `transaction.rollback(): Promise<void>`
920
+ - `transaction.rollback(): Promise<SuccessInterface | ErrorInterface>`
921
+
922
+ ### MCP transactions (Docker image)
923
+
924
+ The Docker-only MCP server exposes authenticated, single-collection ACID transactions with
925
+ insert, update, delete, savepoint, commit, and rollback tools. Begin with
926
+ `axiodb_begin_transaction`, then pass its `transactionId` to the other transaction tools.
927
+
928
+ ### TCP Transaction Proxy
929
+
930
+ - `collection.beginTransaction(): Promise<TransactionProxy>`
931
+ - `transactionProxy.insert(data: object): Promise<unknown>`
932
+ - `transactionProxy.insertMany(documents: object[]): Promise<unknown>`
933
+ - `transactionProxy.query(query: object, options?): Promise<unknown>`
934
+ - `transactionProxy.findByIds(ids: string[]): Promise<unknown>`
935
+ - `transactionProxy.updateById(documentId: string, updateData: object): Promise<unknown>`
936
+ - `transactionProxy.updateByQuery(query: object, updateData: object, updateOne?: boolean): Promise<unknown>`
937
+ - `transactionProxy.deleteById(documentId: string): Promise<unknown>`
938
+ - `transactionProxy.deleteByQuery(query: object, deleteOne?: boolean): Promise<unknown>`
939
+ - `transactionProxy.savepoint(name: string): Promise<unknown>`
940
+ - `transactionProxy.rollbackToSavepoint(name: string): Promise<unknown>`
941
+ - `transactionProxy.releaseSavepoint(name: string): Promise<unknown>`
942
+ - `transactionProxy.commit(): Promise<unknown>`
943
+ - `transactionProxy.rollback(): Promise<unknown>`
872
944
 
873
945
  ---
874
946
 
package/cli/VERSION CHANGED
@@ -1 +1 @@
1
- 18.1.4
1
+ 20.3.0
@@ -0,0 +1,68 @@
1
+ package cmd
2
+
3
+ import (
4
+ "fmt"
5
+ "strings"
6
+
7
+ "github.com/nexoral/axiodb-cli/internal/config"
8
+ "github.com/nexoral/axiodb-cli/pkg/httpclient"
9
+ "github.com/spf13/cobra"
10
+ )
11
+
12
+ var userCmd = &cobra.Command{Use: "user", Short: "Manage HTTP API users"}
13
+ var roleCmd = &cobra.Command{Use: "role", Short: "Manage HTTP API roles"}
14
+
15
+ func httpClientFor(cmd *cobra.Command) (*config.Config, *httpclient.HTTPClient, error) {
16
+ cfg, err := config.FromFlags(cmd)
17
+ if err != nil { return nil, nil, err }
18
+ if cfg.Username == "" || cfg.Password == "" { return nil, nil, fmt.Errorf("--username and --password are required for HTTP administration") }
19
+ client := cfg.NewHTTPClient()
20
+ if err := client.Login(cfg.Username, cfg.Password); err != nil { return nil, nil, err }
21
+ return cfg, client, nil
22
+ }
23
+
24
+ var userListCmd = &cobra.Command{Use: "list", Short: "List users", RunE: func(cmd *cobra.Command, args []string) error {
25
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.ListUsers(); if err != nil { return err }; return printOutput(cfg.Output, result)
26
+ }}
27
+
28
+ var userCreateCmd = &cobra.Command{Use: "create <username> <password> <role>", Short: "Create a user", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error {
29
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.CreateUser(args[0], args[1], args[2]); if err != nil { return err }; return printOutput(cfg.Output, result)
30
+ }}
31
+
32
+ var userRoleCmd = &cobra.Command{Use: "set-role <username> <role>", Short: "Change a user's role", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error {
33
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.UpdateUserRole(args[0], args[1]); if err != nil { return err }; return printOutput(cfg.Output, result)
34
+ }}
35
+
36
+ var userResetCmd = &cobra.Command{Use: "reset-password <username> <password>", Short: "Reset a user's password", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error {
37
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.ResetUserPassword(args[0], args[1]); if err != nil { return err }; return printOutput(cfg.Output, result)
38
+ }}
39
+
40
+ var userDeleteCmd = &cobra.Command{Use: "delete <username>", Short: "Delete a user", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error {
41
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.DeleteUser(args[0]); if err != nil { return err }; return printOutput(cfg.Output, result)
42
+ }}
43
+
44
+ var userChangePasswordCmd = &cobra.Command{Use: "change-password <currentPassword> <newPassword>", Short: "Change own password (self-service)", Long: "Change your own password. Requires HTTP API (port 27018) to be enabled on the server (GUI/HTTP). Uses PATCH /api/auth/change-password.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error {
45
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.ChangeOwnPassword(args[0], args[1]); if err != nil { return err }; return printOutput(cfg.Output, result)
46
+ }}
47
+
48
+ var roleListCmd = &cobra.Command{Use: "list", Short: "List roles", RunE: func(cmd *cobra.Command, args []string) error {
49
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.ListRoles(); if err != nil { return err }; return printOutput(cfg.Output, result)
50
+ }}
51
+
52
+ var roleCreateCmd = &cobra.Command{Use: "create <name> <permission,...>", Short: "Create a role", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error {
53
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; permissions := strings.Split(args[1], ","); result, err := client.CreateRole(args[0], permissions); if err != nil { return err }; return printOutput(cfg.Output, result)
54
+ }}
55
+
56
+ var roleDeleteCmd = &cobra.Command{Use: "delete <name>", Short: "Delete a role", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error {
57
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.DeleteRole(args[0]); if err != nil { return err }; return printOutput(cfg.Output, result)
58
+ }}
59
+
60
+ var rolePermissionsCmd = &cobra.Command{Use: "permissions", Short: "List available permissions", RunE: func(cmd *cobra.Command, args []string) error {
61
+ cfg, client, err := httpClientFor(cmd); if err != nil { return err }; result, err := client.ListPermissions(); if err != nil { return err }; return printOutput(cfg.Output, result)
62
+ }}
63
+
64
+ func init() {
65
+ userCmd.AddCommand(userListCmd, userCreateCmd, userRoleCmd, userResetCmd, userDeleteCmd, userChangePasswordCmd)
66
+ roleCmd.AddCommand(roleListCmd, roleCreateCmd, roleDeleteCmd, rolePermissionsCmd)
67
+ rootCmd.AddCommand(userCmd, roleCmd)
68
+ }
@@ -101,11 +101,13 @@ var documentQueryCmd = &cobra.Command{
101
101
  skip, _ := cmd.Flags().GetInt("skip")
102
102
  sortStr, _ := cmd.Flags().GetString("sort")
103
103
  findOne, _ := cmd.Flags().GetBool("find-one")
104
+ hint, _ := cmd.Flags().GetString("hint")
104
105
 
105
106
  opts := commands.QueryOptions{
106
107
  Limit: limit,
107
108
  Skip: skip,
108
109
  FindOne: findOne,
110
+ Hint: hint,
109
111
  }
110
112
 
111
113
  if len(args) > 0 {
@@ -138,6 +140,35 @@ var documentQueryCmd = &cobra.Command{
138
140
  },
139
141
  }
140
142
 
143
+ var documentFindByIDsCmd = &cobra.Command{
144
+ Use: "find-by-ids <ids-json>",
145
+ Short: "Find multiple documents by ID",
146
+ Args: cobra.ExactArgs(1),
147
+ RunE: func(cmd *cobra.Command, args []string) error {
148
+ cfg, err := config.FromFlags(cmd)
149
+ if err != nil {
150
+ return err
151
+ }
152
+ if cfg.DB == "" || cfg.Collection == "" {
153
+ return fmt.Errorf("--db and --collection flags are required")
154
+ }
155
+ var ids []string
156
+ if err := json.Unmarshal([]byte(args[0]), &ids); err != nil || len(ids) == 0 {
157
+ return fmt.Errorf("ids must be a non-empty JSON array of strings")
158
+ }
159
+ client, err := cfg.ConnectAndAuth()
160
+ if err != nil {
161
+ return err
162
+ }
163
+ defer client.Disconnect()
164
+ resp, err := commands.FindDocumentsByIDs(client, cfg.DB, cfg.Collection, ids)
165
+ if err != nil {
166
+ return err
167
+ }
168
+ return printOutput(cfg.Output, resp)
169
+ },
170
+ }
171
+
141
172
  var documentGetCmd = &cobra.Command{
142
173
  Use: "get <id>",
143
174
  Short: "Get a document by ID",
@@ -358,6 +389,7 @@ func init() {
358
389
  documentQueryCmd.Flags().Int("skip", 0, "Skip results")
359
390
  documentQueryCmd.Flags().String("sort", "", "Sort JSON (e.g., '{\"name\":1}')")
360
391
  documentQueryCmd.Flags().Bool("find-one", false, "Find single document")
392
+ documentQueryCmd.Flags().String("hint", "", "Index field name to use")
361
393
 
362
394
  documentUpdateByQueryCmd.Flags().Bool("many", false, "Update multiple documents")
363
395
  documentDeleteByQueryCmd.Flags().Bool("many", false, "Delete multiple documents")
@@ -366,6 +398,7 @@ func init() {
366
398
  documentCmd.AddCommand(documentInsertManyCmd)
367
399
  documentCmd.AddCommand(documentQueryCmd)
368
400
  documentCmd.AddCommand(documentGetCmd)
401
+ documentCmd.AddCommand(documentFindByIDsCmd)
369
402
  documentCmd.AddCommand(documentUpdateCmd)
370
403
  documentCmd.AddCommand(documentUpdateByQueryCmd)
371
404
  documentCmd.AddCommand(documentDeleteCmd)
@@ -0,0 +1,19 @@
1
+ package cmd
2
+
3
+ import (
4
+ "github.com/nexoral/axiodb-cli/internal/config"
5
+ "github.com/nexoral/axiodb-cli/pkg/commands"
6
+ "github.com/spf13/cobra"
7
+ )
8
+
9
+ var healthCmd = &cobra.Command{
10
+ Use: "health", Short: "Check AxioDB service health",
11
+ RunE: func(cmd *cobra.Command, _ []string) error {
12
+ cfg, err := config.FromFlags(cmd); if err != nil { return err }
13
+ client, err := cfg.ConnectAndAuth(); if err != nil { return err }; defer client.Disconnect()
14
+ response, err := commands.Health(client); if err != nil { return err }
15
+ return printOutput(cfg.Output, response)
16
+ },
17
+ }
18
+
19
+ func init() { rootCmd.AddCommand(healthCmd) }
@@ -0,0 +1,191 @@
1
+ package cmd
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "os"
7
+
8
+ "github.com/nexoral/axiodb-cli/internal/config"
9
+ "github.com/nexoral/axiodb-cli/pkg/commands"
10
+ "github.com/spf13/cobra"
11
+ )
12
+
13
+ var transactionCmd = &cobra.Command{
14
+ Use: "transaction",
15
+ Short: "Run ACID transaction operations",
16
+ Long: "ACID transactions over TCP (port 27019). Requires TCP server enabled (AXIODB_TCP=true). Use 'transaction run' for file-based batch, or begin/commit/rollback/savepoint for manual control.",
17
+ }
18
+
19
+ var transactionRunCmd = &cobra.Command{
20
+ Use: "run <operations-json-file>",
21
+ Short: "Run a transaction from a JSON operation list",
22
+ Long: "Run a full transaction from a JSON file. Requires TCP (AXIODB_TCP=true) and --db/--collection. Auto-commits on success, auto-rollbacks on failure.",
23
+ Args: cobra.ExactArgs(1),
24
+ RunE: func(cmd *cobra.Command, args []string) error {
25
+ cfg, err := config.FromFlags(cmd)
26
+ if err != nil {
27
+ return err
28
+ }
29
+ if cfg.DB == "" || cfg.Collection == "" {
30
+ return fmt.Errorf("--db and --collection flags are required")
31
+ }
32
+ content, err := os.ReadFile(args[0])
33
+ if err != nil {
34
+ return fmt.Errorf("read transaction file: %w", err)
35
+ }
36
+ var steps []commands.TransactionStep
37
+ if err := json.Unmarshal(content, &steps); err != nil {
38
+ return fmt.Errorf("invalid transaction JSON: %w", err)
39
+ }
40
+ client, err := cfg.ConnectAndAuth()
41
+ if err != nil {
42
+ return err
43
+ }
44
+ defer client.Disconnect()
45
+ response, err := commands.RunTransaction(client, cfg.DB, cfg.Collection, steps)
46
+ if err != nil {
47
+ return err
48
+ }
49
+ return printOutput(cfg.Output, response)
50
+ },
51
+ }
52
+
53
+ var transactionBeginCmd = &cobra.Command{
54
+ Use: "begin",
55
+ Short: "Begin a transaction (returns transactionId)",
56
+ Long: "Begin a transaction on --db/--collection. Requires TCP. Returns transactionId for subsequent commit/rollback/savepoint calls.",
57
+ RunE: func(cmd *cobra.Command, args []string) error {
58
+ cfg, err := config.FromFlags(cmd)
59
+ if err != nil {
60
+ return err
61
+ }
62
+ if cfg.DB == "" || cfg.Collection == "" {
63
+ return fmt.Errorf("--db and --collection flags are required")
64
+ }
65
+ client, err := cfg.ConnectAndAuth()
66
+ if err != nil {
67
+ return err
68
+ }
69
+ defer client.Disconnect()
70
+ resp, err := commands.BeginTransaction(client, cfg.DB, cfg.Collection)
71
+ if err != nil {
72
+ return err
73
+ }
74
+ return printOutput(cfg.Output, resp)
75
+ },
76
+ }
77
+
78
+ var transactionCommitCmd = &cobra.Command{
79
+ Use: "commit <transactionId>",
80
+ Short: "Commit a transaction",
81
+ Args: cobra.ExactArgs(1),
82
+ RunE: func(cmd *cobra.Command, args []string) error {
83
+ cfg, err := config.FromFlags(cmd)
84
+ if err != nil {
85
+ return err
86
+ }
87
+ client, err := cfg.ConnectAndAuth()
88
+ if err != nil {
89
+ return err
90
+ }
91
+ defer client.Disconnect()
92
+ resp, err := commands.CommitTransaction(client, args[0])
93
+ if err != nil {
94
+ return err
95
+ }
96
+ return printOutput(cfg.Output, resp)
97
+ },
98
+ }
99
+
100
+ var transactionRollbackCmd = &cobra.Command{
101
+ Use: "rollback <transactionId>",
102
+ Short: "Rollback a transaction",
103
+ Args: cobra.ExactArgs(1),
104
+ RunE: func(cmd *cobra.Command, args []string) error {
105
+ cfg, err := config.FromFlags(cmd)
106
+ if err != nil {
107
+ return err
108
+ }
109
+ client, err := cfg.ConnectAndAuth()
110
+ if err != nil {
111
+ return err
112
+ }
113
+ defer client.Disconnect()
114
+ resp, err := commands.RollbackTransaction(client, args[0])
115
+ if err != nil {
116
+ return err
117
+ }
118
+ return printOutput(cfg.Output, resp)
119
+ },
120
+ }
121
+
122
+ var transactionSavepointCmd = &cobra.Command{
123
+ Use: "savepoint <transactionId> <name>",
124
+ Short: "Create a savepoint",
125
+ Args: cobra.ExactArgs(2),
126
+ RunE: func(cmd *cobra.Command, args []string) error {
127
+ cfg, err := config.FromFlags(cmd)
128
+ if err != nil {
129
+ return err
130
+ }
131
+ client, err := cfg.ConnectAndAuth()
132
+ if err != nil {
133
+ return err
134
+ }
135
+ defer client.Disconnect()
136
+ resp, err := commands.Savepoint(client, args[0], args[1])
137
+ if err != nil {
138
+ return err
139
+ }
140
+ return printOutput(cfg.Output, resp)
141
+ },
142
+ }
143
+
144
+ var transactionRollbackToCmd = &cobra.Command{
145
+ Use: "rollback-to <transactionId> <name>",
146
+ Short: "Rollback to a savepoint",
147
+ Args: cobra.ExactArgs(2),
148
+ RunE: func(cmd *cobra.Command, args []string) error {
149
+ cfg, err := config.FromFlags(cmd)
150
+ if err != nil {
151
+ return err
152
+ }
153
+ client, err := cfg.ConnectAndAuth()
154
+ if err != nil {
155
+ return err
156
+ }
157
+ defer client.Disconnect()
158
+ resp, err := commands.RollbackToSavepoint(client, args[0], args[1])
159
+ if err != nil {
160
+ return err
161
+ }
162
+ return printOutput(cfg.Output, resp)
163
+ },
164
+ }
165
+
166
+ var transactionReleaseCmd = &cobra.Command{
167
+ Use: "release <transactionId> <name>",
168
+ Short: "Release a savepoint",
169
+ Args: cobra.ExactArgs(2),
170
+ RunE: func(cmd *cobra.Command, args []string) error {
171
+ cfg, err := config.FromFlags(cmd)
172
+ if err != nil {
173
+ return err
174
+ }
175
+ client, err := cfg.ConnectAndAuth()
176
+ if err != nil {
177
+ return err
178
+ }
179
+ defer client.Disconnect()
180
+ resp, err := commands.ReleaseSavepoint(client, args[0], args[1])
181
+ if err != nil {
182
+ return err
183
+ }
184
+ return printOutput(cfg.Output, resp)
185
+ },
186
+ }
187
+
188
+ func init() {
189
+ transactionCmd.AddCommand(transactionRunCmd, transactionBeginCmd, transactionCommitCmd, transactionRollbackCmd, transactionSavepointCmd, transactionRollbackToCmd, transactionReleaseCmd)
190
+ rootCmd.AddCommand(transactionCmd)
191
+ }
@@ -6,7 +6,7 @@ import (
6
6
  "github.com/spf13/cobra"
7
7
  )
8
8
 
9
- var cliVersion = "18.1.4"
9
+ var cliVersion = "20.3.0"
10
10
 
11
11
  var versionCmd = &cobra.Command{
12
12
  Use: "version",
@@ -42,6 +42,7 @@ type QueryOptions struct {
42
42
  Skip int
43
43
  Sort interface{}
44
44
  FindOne bool
45
+ Hint string
45
46
  }
46
47
 
47
48
  func QueryDocuments(client *protocol.Client, dbName, collectionName string, opts QueryOptions) (*protocol.Response, error) {
@@ -64,6 +65,9 @@ func QueryDocuments(client *protocol.Client, dbName, collectionName string, opts
64
65
  if opts.FindOne {
65
66
  params["findOne"] = true
66
67
  }
68
+ if opts.Hint != "" {
69
+ params["hint"] = opts.Hint
70
+ }
67
71
 
68
72
  resp, err := client.Send("QUERY_DOCUMENTS", params)
69
73
  if err != nil {
@@ -90,6 +94,21 @@ func QueryByID(client *protocol.Client, dbName, collectionName, id string) (*pro
90
94
  return resp, nil
91
95
  }
92
96
 
97
+ func FindDocumentsByIDs(client *protocol.Client, dbName, collectionName string, ids interface{}) (*protocol.Response, error) {
98
+ resp, err := client.Send("FIND_BY_IDS", map[string]interface{}{
99
+ "dbName": dbName,
100
+ "collectionName": collectionName,
101
+ "ids": ids,
102
+ })
103
+ if err != nil {
104
+ return nil, fmt.Errorf("find documents by ids: %w", err)
105
+ }
106
+ if resp.StatusCode >= 400 {
107
+ return nil, fmt.Errorf("find documents by ids failed: %s", resp.Error)
108
+ }
109
+ return resp, nil
110
+ }
111
+
93
112
  func UpdateDocumentByID(client *protocol.Client, dbName, collectionName, id string, updateData interface{}) (*protocol.Response, error) {
94
113
  resp, err := client.Send("UPDATE_DOCUMENT_BY_ID", map[string]interface{}{
95
114
  "dbName": dbName,
@@ -0,0 +1,18 @@
1
+ package commands
2
+
3
+ import (
4
+ "fmt"
5
+
6
+ "github.com/nexoral/axiodb-cli/pkg/protocol"
7
+ )
8
+
9
+ func Health(client *protocol.Client) (*protocol.Response, error) {
10
+ resp, err := client.Send("HEALTH", nil)
11
+ if err != nil {
12
+ return nil, fmt.Errorf("get health: %w", err)
13
+ }
14
+ if resp.StatusCode >= 400 {
15
+ return nil, fmt.Errorf("get health failed: %s", resp.Error)
16
+ }
17
+ return resp, nil
18
+ }