@ekodb/ekodb-client 0.24.0 → 0.26.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.
@@ -1045,6 +1045,47 @@ function mockErrorResponse(status, message) {
1045
1045
  (0, vitest_1.expect)(result.anthropic).toHaveLength(2);
1046
1046
  (0, vitest_1.expect)(result.perplexity).toHaveLength(1);
1047
1047
  });
1048
+ (0, vitest_1.it)("carries gemini and the per-provider status from the server", async () => {
1049
+ const client = createTestClient();
1050
+ mockTokenResponse();
1051
+ mockJsonResponse({
1052
+ openai: [],
1053
+ anthropic: ["claude-sonnet-4-5"],
1054
+ perplexity: ["sonar"],
1055
+ gemini: ["gemini-2.5-flash"],
1056
+ providers: {
1057
+ anthropic: { status: "ok", verified: true, model_count: 1 },
1058
+ gemini: { status: "ok", verified: true, model_count: 1 },
1059
+ openai: {
1060
+ status: "auth_failed",
1061
+ verified: true,
1062
+ http_status: 401,
1063
+ message: "Failed to fetch OpenAI models: 401 Unauthorized",
1064
+ },
1065
+ perplexity: {
1066
+ status: "ok",
1067
+ verified: false,
1068
+ message: "static model list; key not verified",
1069
+ },
1070
+ },
1071
+ });
1072
+ const result = await client.getChatModels();
1073
+ (0, vitest_1.expect)(result.gemini).toEqual(["gemini-2.5-flash"]);
1074
+ (0, vitest_1.expect)(result.providers?.openai.status).toBe("auth_failed");
1075
+ (0, vitest_1.expect)(result.providers?.openai.http_status).toBe(401);
1076
+ (0, vitest_1.expect)(result.providers?.openai.verified).toBe(true);
1077
+ (0, vitest_1.expect)(result.providers?.perplexity.verified).toBe(false);
1078
+ (0, vitest_1.expect)(result.providers?.anthropic.model_count).toBe(1);
1079
+ });
1080
+ (0, vitest_1.it)("tolerates a server that predates gemini and providers", async () => {
1081
+ const client = createTestClient();
1082
+ mockTokenResponse();
1083
+ mockJsonResponse({ openai: ["gpt-4o"], anthropic: [], perplexity: [] });
1084
+ const result = await client.getChatModels();
1085
+ (0, vitest_1.expect)(result.openai).toEqual(["gpt-4o"]);
1086
+ (0, vitest_1.expect)(result.gemini).toBeUndefined();
1087
+ (0, vitest_1.expect)(result.providers).toBeUndefined();
1088
+ });
1048
1089
  (0, vitest_1.it)("gets models for specific provider", async () => {
1049
1090
  const client = createTestClient();
1050
1091
  mockTokenResponse();
@@ -1909,6 +1950,112 @@ function mockErrorResponse(status, message) {
1909
1950
  (0, vitest_1.expect)(events[2].messageId).toBe("msg_1");
1910
1951
  (0, vitest_1.expect)(events[2].executionTimeMs).toBe(42);
1911
1952
  });
1953
+ (0, vitest_1.it)("treats a frame named error as an error even when its payload says message", async () => {
1954
+ const client = createTestClient();
1955
+ mockTokenResponse();
1956
+ const sseBody = 'event: token\ndata: {"token":"Hel"}\n\nevent: error\ndata: {"message":"boom"}\n\n';
1957
+ mockFetch.mockResolvedValueOnce({
1958
+ ok: true,
1959
+ status: 200,
1960
+ text: async () => sseBody,
1961
+ headers: new Headers({ "content-type": "text/event-stream" }),
1962
+ });
1963
+ const events = [];
1964
+ const stream = client.chatMessageStream("chat_123", {
1965
+ message: "Hello",
1966
+ });
1967
+ stream.on("event", (evt) => events.push(evt));
1968
+ await new Promise((resolve) => setTimeout(resolve, 50));
1969
+ (0, vitest_1.expect)(events).toEqual([
1970
+ { type: "chunk", content: "Hel" },
1971
+ { type: "error", error: "boom" },
1972
+ ]);
1973
+ });
1974
+ (0, vitest_1.it)("stops reading after an error frame and emits nothing that follows it", async () => {
1975
+ const client = createTestClient();
1976
+ mockTokenResponse();
1977
+ const sseBody = 'event: error\ndata: {"message":"boom"}\n\nevent: token\ndata: {"token":"late"}\n\n';
1978
+ mockFetch.mockResolvedValueOnce({
1979
+ ok: true,
1980
+ status: 200,
1981
+ text: async () => sseBody,
1982
+ headers: new Headers({ "content-type": "text/event-stream" }),
1983
+ });
1984
+ const events = [];
1985
+ const stream = client.chatMessageStream("chat_123", {
1986
+ message: "Hello",
1987
+ });
1988
+ stream.on("event", (evt) => events.push(evt));
1989
+ await new Promise((resolve) => setTimeout(resolve, 50));
1990
+ (0, vitest_1.expect)(events).toEqual([{ type: "error", error: "boom" }]);
1991
+ });
1992
+ (0, vitest_1.it)("cancels the body reader after an error frame instead of waiting for the server to close", async () => {
1993
+ const client = createTestClient();
1994
+ mockTokenResponse();
1995
+ const encoder = new TextEncoder();
1996
+ const chunks = [
1997
+ encoder.encode('event: error\ndata: {"message":"boom"}\n\n'),
1998
+ encoder.encode('event: token\ndata: {"token":"late"}\n\n'),
1999
+ ];
2000
+ const cancel = vitest_1.vi.fn(async () => { });
2001
+ let reads = 0;
2002
+ const reader = {
2003
+ read: vitest_1.vi.fn(async () => {
2004
+ // A server (or proxy) that does not close after the error frame: it
2005
+ // keeps sending frames. Bounded so a client that never stops fails
2006
+ // the assertions below instead of looping forever.
2007
+ if (reads >= 50)
2008
+ return { done: true, value: undefined };
2009
+ const value = chunks[Math.min(reads, chunks.length - 1)];
2010
+ reads += 1;
2011
+ return { done: false, value };
2012
+ }),
2013
+ cancel,
2014
+ };
2015
+ mockFetch.mockResolvedValueOnce({
2016
+ ok: true,
2017
+ status: 200,
2018
+ body: { getReader: () => reader },
2019
+ text: async () => "",
2020
+ headers: new Headers({ "content-type": "text/event-stream" }),
2021
+ });
2022
+ const events = [];
2023
+ const stream = client.chatMessageStream("chat_123", {
2024
+ message: "Hello",
2025
+ });
2026
+ stream.on("event", (evt) => events.push(evt));
2027
+ await new Promise((resolve) => setTimeout(resolve, 50));
2028
+ (0, vitest_1.expect)(events).toEqual([{ type: "error", error: "boom" }]);
2029
+ // One read delivered the error frame; the reader was cancelled rather
2030
+ // than read until the server closed.
2031
+ (0, vitest_1.expect)(reader.read).toHaveBeenCalledTimes(1);
2032
+ (0, vitest_1.expect)(cancel).toHaveBeenCalledTimes(1);
2033
+ });
2034
+ (0, vitest_1.it)("keeps the error text a string when the server sends a structured error", async () => {
2035
+ const client = createTestClient();
2036
+ mockTokenResponse();
2037
+ const sseBody = 'event: error\ndata: {"error":{"code":"upstream_down","status":503},"error_kind":"provider_unavailable","provider":"openai"}\n\n';
2038
+ mockFetch.mockResolvedValueOnce({
2039
+ ok: true,
2040
+ status: 200,
2041
+ text: async () => sseBody,
2042
+ headers: new Headers({ "content-type": "text/event-stream" }),
2043
+ });
2044
+ const events = [];
2045
+ const stream = client.chatMessageStream("chat_123", {
2046
+ message: "Hello",
2047
+ });
2048
+ stream.on("event", (evt) => events.push(evt));
2049
+ await new Promise((resolve) => setTimeout(resolve, 50));
2050
+ (0, vitest_1.expect)(events).toEqual([
2051
+ {
2052
+ type: "error",
2053
+ error: "Unknown error",
2054
+ errorKind: "provider_unavailable",
2055
+ provider: "openai",
2056
+ },
2057
+ ]);
2058
+ });
1912
2059
  (0, vitest_1.it)("emits error event on SSE error", async () => {
1913
2060
  const client = createTestClient();
1914
2061
  mockTokenResponse();
@@ -1928,6 +2075,36 @@ function mockErrorResponse(status, message) {
1928
2075
  (0, vitest_1.expect)(events).toHaveLength(1);
1929
2076
  (0, vitest_1.expect)(events[0]).toEqual({ type: "error", error: "LLM timeout" });
1930
2077
  });
2078
+ (0, vitest_1.it)("carries the provider failure classification on an error event", async () => {
2079
+ // The deployment classifies a provider failure (`error_kind`, `provider`,
2080
+ // `provider_status`, `retry_after_secs` on the wire); the event carries
2081
+ // every one of them, in this shape's camelCase, so a consumer can act on
2082
+ // it without string-matching.
2083
+ const client = createTestClient();
2084
+ mockTokenResponse();
2085
+ const sseBody = 'data: {"error":"OpenAI API error: Incorrect API key provided","error_kind":"provider_auth_failed","provider":"openai","provider_status":401}\n';
2086
+ mockFetch.mockResolvedValueOnce({
2087
+ ok: true,
2088
+ status: 200,
2089
+ text: async () => sseBody,
2090
+ headers: new Headers({ "content-type": "text/event-stream" }),
2091
+ });
2092
+ const events = [];
2093
+ const stream = client.chatMessageStream("chat_123", {
2094
+ message: "Hello",
2095
+ });
2096
+ stream.on("event", (evt) => events.push(evt));
2097
+ await new Promise((resolve) => setTimeout(resolve, 50));
2098
+ (0, vitest_1.expect)(events).toEqual([
2099
+ {
2100
+ type: "error",
2101
+ error: "OpenAI API error: Incorrect API key provided",
2102
+ errorKind: "provider_auth_failed",
2103
+ provider: "openai",
2104
+ providerStatus: 401,
2105
+ },
2106
+ ]);
2107
+ });
1931
2108
  (0, vitest_1.it)("emits error event on non-200 HTTP response", async () => {
1932
2109
  const client = createTestClient();
1933
2110
  mockTokenResponse();
@@ -2671,3 +2848,90 @@ function mockErrorResponse(status, message) {
2671
2848
  await (0, vitest_1.expect)(client.find("users")).rejects.toThrow(/timed out after 20ms/);
2672
2849
  });
2673
2850
  });
2851
+ // ============================================================================
2852
+ // Health Contract Tests
2853
+ // ============================================================================
2854
+ (0, vitest_1.describe)("health / healthStatus", () => {
2855
+ (0, vitest_1.it)("health() returns true for a reachable degraded server (bug fix)", async () => {
2856
+ const client = createTestClient();
2857
+ mockTokenResponse();
2858
+ mockJsonResponse({ status: "degraded", integrity_ok: false });
2859
+ (0, vitest_1.expect)(await client.health()).toBe(true);
2860
+ });
2861
+ (0, vitest_1.it)("health() returns false when unreachable", async () => {
2862
+ const client = createTestClient();
2863
+ mockTokenResponse();
2864
+ mockErrorResponse(503, "unavailable");
2865
+ (0, vitest_1.expect)(await client.health()).toBe(false);
2866
+ });
2867
+ (0, vitest_1.it)("healthStatus() surfaces degraded without erroring", async () => {
2868
+ const client = createTestClient();
2869
+ mockTokenResponse();
2870
+ mockJsonResponse({ status: "degraded", integrity_ok: false });
2871
+ const hs = await client.healthStatus();
2872
+ (0, vitest_1.expect)(hs.reachable).toBe(true);
2873
+ (0, vitest_1.expect)(hs.status).toBe("degraded");
2874
+ (0, vitest_1.expect)(hs.integrityOk).toBe(false);
2875
+ });
2876
+ (0, vitest_1.it)("healthStatus() unreachable -> reachable=false, status=unknown", async () => {
2877
+ const client = createTestClient();
2878
+ mockTokenResponse();
2879
+ mockErrorResponse(503, "unavailable");
2880
+ const hs = await client.healthStatus();
2881
+ (0, vitest_1.expect)(hs.reachable).toBe(false);
2882
+ (0, vitest_1.expect)(hs.status).toBe("unknown");
2883
+ });
2884
+ (0, vitest_1.it)("healthStatus() reads integrity from the admin nested shape", async () => {
2885
+ const client = createTestClient();
2886
+ mockTokenResponse();
2887
+ mockJsonResponse({
2888
+ status: "ok",
2889
+ integrity: { healthy: true, manifest_load_failed: [] },
2890
+ });
2891
+ const hs = await client.healthStatus();
2892
+ (0, vitest_1.expect)(hs.status).toBe("ok");
2893
+ (0, vitest_1.expect)(hs.integrityOk).toBe(true);
2894
+ });
2895
+ (0, vitest_1.it)("parseHealthStatus fails safe to degraded on a missing status", () => {
2896
+ const hs = (0, client_1.parseHealthStatus)({ integrity_ok: true });
2897
+ (0, vitest_1.expect)(hs.reachable).toBe(true);
2898
+ (0, vitest_1.expect)(hs.status).toBe("degraded");
2899
+ });
2900
+ (0, vitest_1.it)("parseHealthStatus yields an unknown snapshot for a non-object body", () => {
2901
+ const hs = (0, client_1.parseHealthStatus)("not json");
2902
+ (0, vitest_1.expect)(hs.reachable).toBe(false);
2903
+ (0, vitest_1.expect)(hs.status).toBe("unknown");
2904
+ });
2905
+ (0, vitest_1.it)("JSON serialization is a safe summary that excludes detail", () => {
2906
+ const hs = (0, client_1.parseHealthStatus)({
2907
+ status: "degraded",
2908
+ integrity: {
2909
+ healthy: false,
2910
+ manifest_load_failed: ["secret_collection"],
2911
+ },
2912
+ });
2913
+ const json = JSON.stringify(hs);
2914
+ (0, vitest_1.expect)(json).not.toContain("detail");
2915
+ (0, vitest_1.expect)(json).not.toContain("secret_collection");
2916
+ const parsed = JSON.parse(json);
2917
+ (0, vitest_1.expect)(parsed).toEqual({
2918
+ reachable: true,
2919
+ status: "degraded",
2920
+ integrity_ok: false,
2921
+ });
2922
+ // detail is still readable in-process
2923
+ (0, vitest_1.expect)(hs.detail).toBeDefined();
2924
+ });
2925
+ (0, vitest_1.it)("parseHealthStatus preserves a raw off-contract status (matches Go)", () => {
2926
+ (0, vitest_1.expect)((0, client_1.parseHealthStatus)({ status: "healthy" }).status).toBe("healthy");
2927
+ (0, vitest_1.expect)((0, client_1.parseHealthStatus)({ status: "ok" }).status).toBe("ok");
2928
+ (0, vitest_1.expect)((0, client_1.parseHealthStatus)({ status: "degraded" }).status).toBe("degraded");
2929
+ });
2930
+ (0, vitest_1.it)("parseHealthStatus fails safe to degraded on a non-string status", () => {
2931
+ (0, vitest_1.expect)((0, client_1.parseHealthStatus)({ status: { nested: 1 } }).status).toBe("degraded");
2932
+ });
2933
+ (0, vitest_1.it)("parseHealthStatus treats a non-object (array) body as unknown", () => {
2934
+ (0, vitest_1.expect)((0, client_1.parseHealthStatus)([1, 2, 3]).reachable).toBe(false);
2935
+ (0, vitest_1.expect)((0, client_1.parseHealthStatus)([1, 2, 3]).status).toBe("unknown");
2936
+ });
2937
+ });
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { EkoDBClient, WebSocketClient, EventStream, SerializationFormat, MergeStrategy, RateLimitError, SchemaCache, extractRecordId, } from "./client";
1
+ export { EkoDBClient, WebSocketClient, EventStream, SerializationFormat, MergeStrategy, RateLimitError, SchemaCache, extractRecordId, HealthStatus, parseHealthStatus, HealthOK, HealthDegraded, HealthUnknown, } from "./client";
2
+ export type { HealthState } from "./client";
2
3
  export { QueryBuilder, SortOrder } from "./query-builder";
3
4
  export { SearchQueryBuilder } from "./search";
4
5
  export { SchemaBuilder, FieldTypeSchemaBuilder, VectorIndexAlgorithm, DistanceMetric, } from "./schema";
@@ -12,4 +13,4 @@ export type { Schema, FieldTypeSchema, IndexConfig, CollectionMetadata, } from "
12
13
  export type { JoinConfig } from "./join";
13
14
  export type { UserFunction, ParameterDefinition, FunctionStageConfig, GroupFunctionConfig, SortFieldConfig, FunctionResult, FunctionStats, StageStats, } from "./functions";
14
15
  export type { MutationNotification, ChatStreamEvent, ClientToolDefinition, ChatSendOptions, SubscribeOptions, } from "./client";
15
- export type { Record, Query, BatchOperationResult, ClientConfig, RateLimitInfo, CollectionConfig, ChatRequest, CreateChatSessionRequest, ChatMessageRequest, TokenUsage, ChatResponse, ChatSession, ChatSessionResponse, ListSessionsQuery, ListSessionsResponse, GetMessagesQuery, GetMessagesResponse, UpdateSessionRequest, MergeSessionsRequest, ChatModels, CompactChatRequest, CompactChatResponse, EmbedRequest, EmbedResponse, RawCompletionRequest, RawCompletionResponse, ToolChoice, ToolConfig, } from "./client";
16
+ export type { Record, Query, BatchOperationResult, ClientConfig, RateLimitInfo, CollectionConfig, ChatRequest, CreateChatSessionRequest, ChatMessageRequest, TokenUsage, ChatResponse, ChatSession, ChatSessionResponse, ListSessionsQuery, ListSessionsResponse, GetMessagesQuery, GetMessagesResponse, UpdateSessionRequest, MergeSessionsRequest, ChatModels, ChatProviderState, ChatProviderStatus, CompactChatRequest, CompactChatResponse, EmbedRequest, EmbedResponse, RawCompletionRequest, RawCompletionResponse, ToolChoice, ToolConfig, } from "./client";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Field = exports.getObjectValue = exports.getVectorValue = exports.getSetValue = exports.getArrayValue = exports.getBinaryValue = exports.getBytesValue = exports.getDurationValue = exports.getDecimalValue = exports.getUUIDValue = exports.getDateTimeValue = exports.extractRecord = exports.getValues = exports.getValue = exports.parameterRef = exports.ChatMessage = exports.Stage = exports.JoinBuilder = exports.DistanceMetric = exports.VectorIndexAlgorithm = exports.FieldTypeSchemaBuilder = exports.SchemaBuilder = exports.SearchQueryBuilder = exports.SortOrder = exports.QueryBuilder = exports.extractRecordId = exports.SchemaCache = exports.RateLimitError = exports.MergeStrategy = exports.SerializationFormat = exports.EventStream = exports.WebSocketClient = exports.EkoDBClient = void 0;
3
+ exports.Field = exports.getObjectValue = exports.getVectorValue = exports.getSetValue = exports.getArrayValue = exports.getBinaryValue = exports.getBytesValue = exports.getDurationValue = exports.getDecimalValue = exports.getUUIDValue = exports.getDateTimeValue = exports.extractRecord = exports.getValues = exports.getValue = exports.parameterRef = exports.ChatMessage = exports.Stage = exports.JoinBuilder = exports.DistanceMetric = exports.VectorIndexAlgorithm = exports.FieldTypeSchemaBuilder = exports.SchemaBuilder = exports.SearchQueryBuilder = exports.SortOrder = exports.QueryBuilder = exports.HealthUnknown = exports.HealthDegraded = exports.HealthOK = exports.parseHealthStatus = exports.HealthStatus = exports.extractRecordId = exports.SchemaCache = exports.RateLimitError = exports.MergeStrategy = exports.SerializationFormat = exports.EventStream = exports.WebSocketClient = exports.EkoDBClient = void 0;
4
4
  var client_1 = require("./client");
5
5
  Object.defineProperty(exports, "EkoDBClient", { enumerable: true, get: function () { return client_1.EkoDBClient; } });
6
6
  Object.defineProperty(exports, "WebSocketClient", { enumerable: true, get: function () { return client_1.WebSocketClient; } });
@@ -10,6 +10,11 @@ Object.defineProperty(exports, "MergeStrategy", { enumerable: true, get: functio
10
10
  Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return client_1.RateLimitError; } });
11
11
  Object.defineProperty(exports, "SchemaCache", { enumerable: true, get: function () { return client_1.SchemaCache; } });
12
12
  Object.defineProperty(exports, "extractRecordId", { enumerable: true, get: function () { return client_1.extractRecordId; } });
13
+ Object.defineProperty(exports, "HealthStatus", { enumerable: true, get: function () { return client_1.HealthStatus; } });
14
+ Object.defineProperty(exports, "parseHealthStatus", { enumerable: true, get: function () { return client_1.parseHealthStatus; } });
15
+ Object.defineProperty(exports, "HealthOK", { enumerable: true, get: function () { return client_1.HealthOK; } });
16
+ Object.defineProperty(exports, "HealthDegraded", { enumerable: true, get: function () { return client_1.HealthDegraded; } });
17
+ Object.defineProperty(exports, "HealthUnknown", { enumerable: true, get: function () { return client_1.HealthUnknown; } });
13
18
  var query_builder_1 = require("./query-builder");
14
19
  Object.defineProperty(exports, "QueryBuilder", { enumerable: true, get: function () { return query_builder_1.QueryBuilder; } });
15
20
  Object.defineProperty(exports, "SortOrder", { enumerable: true, get: function () { return query_builder_1.SortOrder; } });
@@ -300,6 +300,72 @@ function waitForMessage(ws) {
300
300
  (0, vitest_1.expect)(stream.closed).toBe(true);
301
301
  client.close();
302
302
  });
303
+ // The WebSocket route carries the provider-failure classification like
304
+ // the SSE route does, in the event's camelCase shape.
305
+ (0, vitest_1.it)("carries the provider failure classification on a chat stream error", async () => {
306
+ const client = new client_1.WebSocketClient(`ws://localhost:${port}/api/ws`, "test-token");
307
+ const streamPromise = client.chatSend("chat-4", "test");
308
+ await new Promise((r) => wss.once("connection", r));
309
+ const ws = getLastConnection();
310
+ await waitForMessage(ws);
311
+ const stream = await streamPromise;
312
+ const events = [];
313
+ stream.on("event", (e) => events.push(e));
314
+ ws.send(JSON.stringify({
315
+ type: "ChatStreamError",
316
+ payload: {
317
+ chat_id: "chat-4",
318
+ error: "OpenAI API error 429 Too Many Requests",
319
+ error_kind: "provider_rate_limited",
320
+ provider: "openai",
321
+ provider_status: 429,
322
+ retry_after_secs: 7,
323
+ },
324
+ }));
325
+ await new Promise((r) => stream.on("close", r));
326
+ (0, vitest_1.expect)(events).toEqual([
327
+ {
328
+ type: "error",
329
+ error: "OpenAI API error 429 Too Many Requests",
330
+ errorKind: "provider_rate_limited",
331
+ provider: "openai",
332
+ providerStatus: 429,
333
+ retryAfterSecs: 7,
334
+ },
335
+ ]);
336
+ client.close();
337
+ });
338
+ // A structured `error` value is still an error, with string text — the
339
+ // WebSocket route guards the shape exactly as the SSE route does.
340
+ (0, vitest_1.it)("keeps the error text a string when the payload's error is an object", async () => {
341
+ const client = new client_1.WebSocketClient(`ws://localhost:${port}/api/ws`, "test-token");
342
+ const streamPromise = client.chatSend("chat-5", "test");
343
+ await new Promise((r) => wss.once("connection", r));
344
+ const ws = getLastConnection();
345
+ await waitForMessage(ws);
346
+ const stream = await streamPromise;
347
+ const events = [];
348
+ stream.on("event", (e) => events.push(e));
349
+ ws.send(JSON.stringify({
350
+ type: "ChatStreamError",
351
+ payload: {
352
+ chat_id: "chat-5",
353
+ error: { code: "upstream_down", status: 503 },
354
+ error_kind: "provider_unavailable",
355
+ provider: "gemini",
356
+ },
357
+ }));
358
+ await new Promise((r) => stream.on("close", r));
359
+ (0, vitest_1.expect)(events).toEqual([
360
+ {
361
+ type: "error",
362
+ error: "Unknown error",
363
+ errorKind: "provider_unavailable",
364
+ provider: "gemini",
365
+ },
366
+ ]);
367
+ client.close();
368
+ });
303
369
  (0, vitest_1.it)("sends options with ChatSend", async () => {
304
370
  const client = new client_1.WebSocketClient(`ws://localhost:${port}/api/ws`, "test-token");
305
371
  const streamPromise = client.chatSend("chat-3", "Hello", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekodb/ekodb-client",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "Official TypeScript/JavaScript client for ekoDB",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "devDependencies": {
23
23
  "@types/node": "^24.13.2",
24
24
  "@types/ws": "^8.18.1",
25
- "typescript": "^5.9.3",
25
+ "typescript": "^6.0.3",
26
26
  "vitest": "^4.0.18"
27
27
  },
28
28
  "dependencies": {