@adofai-ipc/client 0.1.0 → 0.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.
package/README.md CHANGED
@@ -40,12 +40,43 @@ await tufhelper.call("level.open-from-id", {
40
40
 
41
41
  Finds a running AdofaiIpc server by probing `/ipc/health`.
42
42
 
43
+ The client and server product versions must match exactly. A mismatch throws
44
+ `IpcVersionMismatchError` and stops port probing. Use `onVersionMismatch` to display application UI;
45
+ the callback runs at most once per `tryConnect` call and does not suppress the typed error.
46
+
47
+ ```ts
48
+ await tryConnect({
49
+ onVersionMismatch(error) {
50
+ if (error.direction === "client_outdated") location.reload();
51
+ else openAdofaiIpcDownloadNotice(error);
52
+ }
53
+ });
54
+ ```
55
+
56
+ `server_outdated` means the mod must be updated, `client_outdated` means the web bundle must be
57
+ updated, and `legacy_server` means the server did not provide a valid product version. React
58
+ StrictMode and consumer retry loops can call `tryConnect` more than once, so applications should
59
+ store mismatch as a terminal connection state and deduplicate their own modal or banner.
60
+
43
61
  Defaults:
44
62
 
45
63
  - host: `127.0.0.1`
46
64
  - startPort: `32145`
47
65
  - endPort: `32155`
48
- - timeoutMs: `500`
66
+ - probeTimeoutMs: `500`
67
+ - requestTimeoutMs: `10000`
68
+
69
+ `tryConnect` only confirms that the AdofaiIpc listener is running. It does not mean that a target
70
+ namespace is registered or that the owning mod has finished initializing.
71
+
72
+ ```ts
73
+ const client = await tryConnect({
74
+ probeTimeoutMs: 500,
75
+ requestTimeoutMs: 10_000
76
+ });
77
+ ```
78
+
79
+ The legacy `timeoutMs` option remains as a deprecated alias for both values.
49
80
 
50
81
  ### `new AdofaiIpcClient(options?)`
51
82
 
@@ -61,6 +92,14 @@ const client = new AdofaiIpcClient({
61
92
 
62
93
  Calls a namespace method through `POST /ipc`.
63
94
 
95
+ ```ts
96
+ await client.call({
97
+ namespace: "tufhelper2",
98
+ method: "activity.get",
99
+ timeoutMs: 30_000
100
+ });
101
+ ```
102
+
64
103
  ### `client.namespace(name)`
65
104
 
66
105
  Creates a namespace-bound helper.
@@ -77,6 +116,52 @@ Calls `GET /ipc/namespaces`.
77
116
 
78
117
  Calls `GET /ipc/namespaces/{name}`.
79
118
 
119
+ ### `client.waitForNamespace(name, options?)`
120
+
121
+ Polls namespace discovery until the target namespace is registered. Set `status: "ready"` to also
122
+ wait until the namespace owner explicitly marks initialization complete.
123
+
124
+ ```ts
125
+ await client.waitForNamespace("tufhelper2", {
126
+ status: "ready",
127
+ timeoutMs: 15_000,
128
+ pollIntervalMs: 100
129
+ });
130
+ ```
131
+
132
+ AdofaiIpc namespaces have the strict states `initializing`, `ready`, and `error`. A ready wait that
133
+ expires reports `namespace_initializing`; an initialization failure reports `namespace_error`.
134
+
135
+ ## Error handling
136
+
137
+ Connection failures are reported as `IpcConnectionError` with code `UNAVAILABLE`. Request
138
+ timeouts use the more specific `IpcTimeoutError`, which extends `IpcConnectionError` and has code
139
+ `TIMEOUT`. Protocol failures, including `namespace_not_found`, are reported as
140
+ `IpcResponseError`. `isIpcUnavailable` only matches the `UNAVAILABLE` state, not timeouts.
141
+
142
+ ```ts
143
+ import {
144
+ IpcTimeoutError,
145
+ isIpcUnavailable,
146
+ tryConnect
147
+ } from "@adofai-ipc/client";
148
+
149
+ try {
150
+ const client = await tryConnect();
151
+ await client.health();
152
+ } catch (error) {
153
+ if (error instanceof IpcTimeoutError) {
154
+ console.warn(`AdofaiIpc timed out after ${error.timeoutMs} ms.`);
155
+ } else if (isIpcUnavailable(error)) {
156
+ console.warn("AdofaiIpc is unavailable.");
157
+ }
158
+ }
159
+ ```
160
+
161
+ `tryConnect` treats individual probe failures as expected and only throws an `UNAVAILABLE`
162
+ `IpcConnectionError` after every candidate port has failed. A successful probe does not guarantee
163
+ that any specific namespace or mode feature is ready.
164
+
80
165
  ## Notes
81
166
 
82
167
  This package uses the global `fetch` API. Node.js 18 or newer is recommended.
package/dist/index.cjs CHANGED
@@ -23,9 +23,13 @@ __export(index_exports, {
23
23
  AdofaiIpcClient: () => AdofaiIpcClient,
24
24
  AdofaiIpcError: () => AdofaiIpcError,
25
25
  AdofaiIpcNamespaceClient: () => AdofaiIpcNamespaceClient,
26
+ CLIENT_VERSION: () => CLIENT_VERSION,
26
27
  IpcConnectionError: () => IpcConnectionError,
27
28
  IpcHttpError: () => IpcHttpError,
28
29
  IpcResponseError: () => IpcResponseError,
30
+ IpcTimeoutError: () => IpcTimeoutError,
31
+ IpcVersionMismatchError: () => IpcVersionMismatchError,
32
+ isIpcUnavailable: () => isIpcUnavailable,
29
33
  tryConnect: () => tryConnect
30
34
  });
31
35
  module.exports = __toCommonJS(index_exports);
@@ -38,11 +42,38 @@ var AdofaiIpcError = class extends Error {
38
42
  }
39
43
  };
40
44
  var IpcConnectionError = class extends AdofaiIpcError {
41
- constructor(message = "Could not connect to AdofaiIpc.") {
45
+ constructor(message = "Could not connect to AdofaiIpc.", options = {}) {
42
46
  super(message);
43
47
  this.name = "IpcConnectionError";
48
+ this.code = options.code ?? "UNAVAILABLE";
49
+ this.cause = options.cause;
44
50
  }
45
51
  };
52
+ var IpcTimeoutError = class extends IpcConnectionError {
53
+ constructor(timeoutMs, options = {}) {
54
+ super(`AdofaiIpc request timed out after ${timeoutMs} ms.`, {
55
+ code: "TIMEOUT",
56
+ cause: options.cause
57
+ });
58
+ this.name = "IpcTimeoutError";
59
+ this.timeoutMs = timeoutMs;
60
+ }
61
+ };
62
+ var IpcVersionMismatchError = class extends AdofaiIpcError {
63
+ constructor(options) {
64
+ const server = options.serverVersion ?? "legacy/unknown";
65
+ super(`AdofaiIpc version mismatch: client ${options.clientVersion}, server ${server}.`);
66
+ this.code = "VERSION_MISMATCH";
67
+ this.name = "IpcVersionMismatchError";
68
+ this.clientVersion = options.clientVersion;
69
+ this.serverVersion = options.serverVersion;
70
+ this.direction = options.direction;
71
+ this.protocolVersion = options.protocolVersion;
72
+ }
73
+ };
74
+ function isIpcUnavailable(error) {
75
+ return error instanceof IpcConnectionError && error.code === "UNAVAILABLE";
76
+ }
46
77
  var IpcHttpError = class extends AdofaiIpcError {
47
78
  constructor(status, message) {
48
79
  super(message);
@@ -59,16 +90,56 @@ var IpcResponseError = class extends AdofaiIpcError {
59
90
  }
60
91
  };
61
92
 
93
+ // src/version.ts
94
+ var CLIENT_VERSION = "0.3.0";
95
+ var CANONICAL_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?$/;
96
+ function compareProductVersions(left, right) {
97
+ const leftVersion = parseProductVersion(left);
98
+ const rightVersion = parseProductVersion(right);
99
+ if (!leftVersion || !rightVersion) return null;
100
+ for (let index = 0; index < leftVersion.core.length; index++) {
101
+ const difference = leftVersion.core[index] - rightVersion.core[index];
102
+ if (difference !== 0) return difference < 0 ? -1 : 1;
103
+ }
104
+ if (leftVersion.prerelease === null) return rightVersion.prerelease === null ? 0 : 1;
105
+ if (rightVersion.prerelease === null) return -1;
106
+ const length = Math.max(leftVersion.prerelease.length, rightVersion.prerelease.length);
107
+ for (let index = 0; index < length; index++) {
108
+ const leftPart = leftVersion.prerelease[index];
109
+ const rightPart = rightVersion.prerelease[index];
110
+ if (leftPart === void 0) return -1;
111
+ if (rightPart === void 0) return 1;
112
+ if (leftPart === rightPart) continue;
113
+ const leftNumeric = /^\d+$/.test(leftPart);
114
+ const rightNumeric = /^\d+$/.test(rightPart);
115
+ if (leftNumeric && rightNumeric) return Number(leftPart) < Number(rightPart) ? -1 : 1;
116
+ if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;
117
+ return leftPart < rightPart ? -1 : 1;
118
+ }
119
+ return 0;
120
+ }
121
+ function parseProductVersion(value) {
122
+ const match = CANONICAL_SEMVER.exec(value);
123
+ if (!match) return null;
124
+ return {
125
+ core: [Number(match[1]), Number(match[2]), Number(match[3])],
126
+ prerelease: match[4] ? match[4].split(".") : null
127
+ };
128
+ }
129
+
62
130
  // src/client.ts
63
131
  var DEFAULT_HOST = "127.0.0.1";
64
132
  var DEFAULT_START_PORT = 32145;
65
133
  var DEFAULT_END_PORT = 32155;
66
- var DEFAULT_TIMEOUT_MS = 500;
134
+ var DEFAULT_PROBE_TIMEOUT_MS = 500;
135
+ var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
136
+ var DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS = 1e4;
137
+ var DEFAULT_NAMESPACE_POLL_INTERVAL_MS = 100;
67
138
  var AdofaiIpcClient = class {
68
139
  constructor(options = {}) {
69
140
  this.baseUrl = normalizeBaseUrl(options.baseUrl ?? `http://${DEFAULT_HOST}:${DEFAULT_START_PORT}`);
70
141
  this.fetchImpl = options.fetch ?? globalThis.fetch;
71
- this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
142
+ this.requestTimeoutMs = options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
72
143
  if (!this.fetchImpl) {
73
144
  throw new IpcConnectionError("A fetch implementation is required.");
74
145
  }
@@ -76,47 +147,102 @@ var AdofaiIpcClient = class {
76
147
  static async connect(options = {}) {
77
148
  return tryConnect(options);
78
149
  }
79
- async health() {
80
- return this.get("/ipc/health");
150
+ async health(options = {}) {
151
+ return this.get("/ipc/health", options);
152
+ }
153
+ async listNamespaces(options = {}) {
154
+ return this.get("/ipc/namespaces", options);
81
155
  }
82
- async listNamespaces() {
83
- return this.get("/ipc/namespaces");
156
+ async getNamespace(namespace, options = {}) {
157
+ return this.get(
158
+ `/ipc/namespaces/${encodeURIComponent(namespace)}`,
159
+ options
160
+ );
84
161
  }
85
- async getNamespace(namespace) {
86
- return this.get(`/ipc/namespaces/${encodeURIComponent(namespace)}`);
162
+ async waitForNamespace(namespace, options = {}) {
163
+ const timeoutMs = options.timeoutMs ?? DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS;
164
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_NAMESPACE_POLL_INTERVAL_MS;
165
+ const requiredStatus = options.status ?? "registered";
166
+ const deadline = Date.now() + timeoutMs;
167
+ let stateError;
168
+ while (true) {
169
+ try {
170
+ const detail = await this.getNamespace(namespace, {
171
+ timeoutMs: options.requestTimeoutMs
172
+ });
173
+ if (requiredStatus === "registered" || detail.status === "ready") {
174
+ return detail;
175
+ }
176
+ if (detail.status === "error") {
177
+ throw new IpcResponseError({
178
+ code: "namespace_error",
179
+ message: detail.error?.message ?? `Namespace initialization failed: ${namespace}`
180
+ });
181
+ }
182
+ if (detail.status !== "initializing") {
183
+ throw new IpcResponseError({
184
+ code: "namespace_status_unavailable",
185
+ message: `Namespace status is unavailable: ${namespace}`
186
+ });
187
+ }
188
+ stateError = new IpcResponseError({
189
+ code: "namespace_initializing",
190
+ message: `Namespace is initializing: ${namespace}`
191
+ });
192
+ } catch (error) {
193
+ if (!(error instanceof IpcResponseError) || error.code !== "namespace_not_found" && error.code !== "namespace_initializing") {
194
+ throw error;
195
+ }
196
+ stateError = error;
197
+ }
198
+ const remainingMs = deadline - Date.now();
199
+ if (remainingMs <= 0) {
200
+ throw stateError ?? new IpcResponseError({
201
+ code: "namespace_initializing",
202
+ message: `Namespace is initializing: ${namespace}`
203
+ });
204
+ }
205
+ await delay(Math.min(pollIntervalMs, remainingMs));
206
+ }
87
207
  }
88
208
  namespace(namespace) {
89
209
  return new AdofaiIpcNamespaceClient(this, namespace);
90
210
  }
91
211
  async call(options) {
92
- const response = await this.post("/ipc", {
93
- namespace: options.namespace,
94
- method: options.method,
95
- params: options.params ?? {},
96
- id: options.id ?? createRequestId()
97
- });
212
+ const response = await this.post(
213
+ "/ipc",
214
+ {
215
+ namespace: options.namespace,
216
+ method: options.method,
217
+ params: options.params ?? {},
218
+ id: options.id ?? createRequestId()
219
+ },
220
+ { timeoutMs: options.timeoutMs }
221
+ );
98
222
  if (!response.ok) {
99
223
  throw new IpcResponseError(response.error);
100
224
  }
101
225
  return response.result;
102
226
  }
103
- async get(path) {
227
+ async get(path, options = {}) {
104
228
  return this.request(path, {
105
229
  method: "GET"
106
- });
230
+ }, options);
107
231
  }
108
- async post(path, body) {
232
+ async post(path, body, options = {}) {
109
233
  return this.request(path, {
110
234
  method: "POST",
111
235
  headers: {
112
236
  "Content-Type": "application/json"
113
237
  },
114
238
  body: JSON.stringify(body)
115
- });
239
+ }, options);
116
240
  }
117
- async request(path, init) {
241
+ async request(path, init, options) {
242
+ const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
118
243
  const controller = new AbortController();
119
- const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
244
+ const timeoutError = new IpcTimeoutError(timeoutMs);
245
+ const timeout = setTimeout(() => controller.abort(timeoutError), timeoutMs);
120
246
  try {
121
247
  const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
122
248
  ...init,
@@ -124,13 +250,18 @@ var AdofaiIpcClient = class {
124
250
  });
125
251
  if (!response.ok) {
126
252
  const text = await response.text();
253
+ const responseError = parseIpcResponseError(text);
254
+ if (responseError) throw new IpcResponseError(responseError);
127
255
  throw new IpcHttpError(response.status, text || response.statusText);
128
256
  }
129
257
  return await response.json();
130
258
  } catch (error) {
131
- if (error instanceof IpcHttpError) throw error;
132
- if (error instanceof Error) throw new IpcConnectionError(error.message);
133
- throw new IpcConnectionError();
259
+ if (error instanceof IpcHttpError || error instanceof IpcResponseError) throw error;
260
+ if (controller.signal.aborted) {
261
+ if (error === timeoutError) throw timeoutError;
262
+ throw new IpcTimeoutError(timeoutMs, { cause: error });
263
+ }
264
+ throw new IpcConnectionError(getErrorMessage(error), { cause: error });
134
265
  } finally {
135
266
  clearTimeout(timeout);
136
267
  }
@@ -141,12 +272,15 @@ var AdofaiIpcNamespaceClient = class {
141
272
  this.client = client;
142
273
  this.namespace = namespace;
143
274
  }
144
- async call(method, params, id) {
275
+ async call(method, params, idOrOptions, options = {}) {
276
+ const requestOptions = isIpcRequestOptions(idOrOptions) ? idOrOptions : options;
277
+ const id = isIpcRequestOptions(idOrOptions) ? void 0 : idOrOptions;
145
278
  return this.client.call({
146
279
  namespace: this.namespace,
147
280
  method,
148
281
  params,
149
- id
282
+ id,
283
+ timeoutMs: requestOptions.timeoutMs
150
284
  });
151
285
  }
152
286
  };
@@ -154,27 +288,80 @@ async function tryConnect(options = {}) {
154
288
  const host = options.host ?? DEFAULT_HOST;
155
289
  const startPort = options.startPort ?? DEFAULT_START_PORT;
156
290
  const endPort = options.endPort ?? DEFAULT_END_PORT;
291
+ const probeTimeoutMs = options.probeTimeoutMs ?? options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
292
+ const requestTimeoutMs = options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
293
+ let lastProbeError;
157
294
  for (let port = startPort; port <= endPort; port++) {
158
295
  const client = new AdofaiIpcClient({
159
296
  baseUrl: `http://${host}:${port}`,
160
297
  fetch: options.fetch,
161
- timeoutMs: options.timeoutMs
298
+ requestTimeoutMs: probeTimeoutMs
162
299
  });
163
300
  try {
164
301
  const health = await client.health();
165
302
  if (health.ok && health.server === "AdofaiIpc") {
166
- return client;
303
+ const mismatch = getVersionMismatch(health);
304
+ if (mismatch) {
305
+ try {
306
+ options.onVersionMismatch?.(mismatch);
307
+ } catch {
308
+ }
309
+ throw mismatch;
310
+ }
311
+ return new AdofaiIpcClient({
312
+ baseUrl: client.baseUrl,
313
+ fetch: options.fetch,
314
+ requestTimeoutMs
315
+ });
167
316
  }
168
- } catch {
317
+ } catch (error) {
318
+ if (error instanceof IpcVersionMismatchError) throw error;
319
+ lastProbeError = error;
169
320
  }
170
321
  }
322
+ if (lastProbeError instanceof IpcTimeoutError) throw lastProbeError;
171
323
  throw new IpcConnectionError(
172
324
  `Could not connect to AdofaiIpc on ${host}:${startPort}-${endPort}.`
173
325
  );
174
326
  }
327
+ function getVersionMismatch(health) {
328
+ const protocolVersion = Number.isInteger(health.protocolVersion) ? health.protocolVersion : null;
329
+ const serverVersion = typeof health.serverVersion === "string" ? health.serverVersion : null;
330
+ const comparison = serverVersion === null ? null : compareProductVersions(serverVersion, CLIENT_VERSION);
331
+ if (comparison === 0 && serverVersion === CLIENT_VERSION) return null;
332
+ return new IpcVersionMismatchError({
333
+ clientVersion: CLIENT_VERSION,
334
+ serverVersion,
335
+ direction: comparison === null ? "legacy_server" : comparison < 0 ? "server_outdated" : "client_outdated",
336
+ protocolVersion
337
+ });
338
+ }
175
339
  function normalizeBaseUrl(value) {
176
340
  return value.replace(/\/+$/, "");
177
341
  }
342
+ function getErrorMessage(error) {
343
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
344
+ return error.message;
345
+ }
346
+ return void 0;
347
+ }
348
+ function parseIpcResponseError(text) {
349
+ try {
350
+ const value = JSON.parse(text);
351
+ const error = value?.error;
352
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && "message" in error && typeof error.message === "string") {
353
+ return { code: error.code, message: error.message };
354
+ }
355
+ } catch {
356
+ }
357
+ return void 0;
358
+ }
359
+ function isIpcRequestOptions(value) {
360
+ return typeof value === "object" && value !== null;
361
+ }
362
+ function delay(timeoutMs) {
363
+ return new Promise((resolve) => setTimeout(resolve, timeoutMs));
364
+ }
178
365
  function createRequestId() {
179
366
  return `adofai-ipc-${Date.now()}-${Math.random().toString(36).slice(2)}`;
180
367
  }
@@ -183,9 +370,13 @@ function createRequestId() {
183
370
  AdofaiIpcClient,
184
371
  AdofaiIpcError,
185
372
  AdofaiIpcNamespaceClient,
373
+ CLIENT_VERSION,
186
374
  IpcConnectionError,
187
375
  IpcHttpError,
188
376
  IpcResponseError,
377
+ IpcTimeoutError,
378
+ IpcVersionMismatchError,
379
+ isIpcUnavailable,
189
380
  tryConnect
190
381
  });
191
382
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["export {\n AdofaiIpcClient,\n AdofaiIpcNamespaceClient,\n tryConnect\n} from \"./client\";\n\nexport {\n AdofaiIpcError,\n IpcConnectionError,\n IpcHttpError,\n IpcResponseError\n} from \"./errors\";\n\nexport type {\n AdofaiIpcClientOptions,\n IpcCallOptions,\n IpcErrorInfo,\n IpcErrorResponse,\n IpcHealthResponse,\n IpcNamespaceDetail,\n IpcNamespacesResponse,\n IpcNamespaceSummary,\n IpcRequestId,\n IpcResponse,\n IpcSuccessResponse,\n TryConnectOptions\n} from \"./types\";\n","import type { IpcErrorInfo } from \"./types\";\n\nexport class AdofaiIpcError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AdofaiIpcError\";\n }\n}\n\nexport class IpcConnectionError extends AdofaiIpcError {\n constructor(message = \"Could not connect to AdofaiIpc.\") {\n super(message);\n this.name = \"IpcConnectionError\";\n }\n}\n\nexport class IpcHttpError extends AdofaiIpcError {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = \"IpcHttpError\";\n this.status = status;\n }\n}\n\nexport class IpcResponseError extends AdofaiIpcError {\n readonly code: string;\n readonly error: IpcErrorInfo;\n\n constructor(error: IpcErrorInfo) {\n super(error.message);\n this.name = \"IpcResponseError\";\n this.code = error.code;\n this.error = error;\n }\n}\n","import { IpcConnectionError, IpcHttpError, IpcResponseError } from \"./errors\";\nimport type {\n AdofaiIpcClientOptions,\n IpcCallOptions,\n IpcHealthResponse,\n IpcNamespaceDetail,\n IpcNamespacesResponse,\n IpcResponse,\n TryConnectOptions\n} from \"./types\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_START_PORT = 32145;\nconst DEFAULT_END_PORT = 32155;\nconst DEFAULT_TIMEOUT_MS = 500;\n\nexport class AdofaiIpcClient {\n readonly baseUrl: string;\n\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n\n constructor(options: AdofaiIpcClientOptions = {}) {\n this.baseUrl = normalizeBaseUrl(options.baseUrl ?? `http://${DEFAULT_HOST}:${DEFAULT_START_PORT}`);\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n if (!this.fetchImpl) {\n throw new IpcConnectionError(\"A fetch implementation is required.\");\n }\n }\n\n static async connect(options: TryConnectOptions = {}): Promise<AdofaiIpcClient> {\n return tryConnect(options);\n }\n\n async health(): Promise<IpcHealthResponse> {\n return this.get<IpcHealthResponse>(\"/ipc/health\");\n }\n\n async listNamespaces(): Promise<IpcNamespacesResponse> {\n return this.get<IpcNamespacesResponse>(\"/ipc/namespaces\");\n }\n\n async getNamespace(namespace: string): Promise<IpcNamespaceDetail> {\n return this.get<IpcNamespaceDetail>(`/ipc/namespaces/${encodeURIComponent(namespace)}`);\n }\n\n namespace(namespace: string): AdofaiIpcNamespaceClient {\n return new AdofaiIpcNamespaceClient(this, namespace);\n }\n\n async call<TResult = unknown, TParams = unknown>(\n options: IpcCallOptions<TParams>\n ): Promise<TResult> {\n const response = await this.post<IpcResponse<TResult>>(\"/ipc\", {\n namespace: options.namespace,\n method: options.method,\n params: options.params ?? {},\n id: options.id ?? createRequestId()\n });\n\n if (!response.ok) {\n throw new IpcResponseError(response.error);\n }\n\n return response.result;\n }\n\n private async get<TResult>(path: string): Promise<TResult> {\n return this.request<TResult>(path, {\n method: \"GET\"\n });\n }\n\n private async post<TResult>(path: string, body: unknown): Promise<TResult> {\n return this.request<TResult>(path, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(body)\n });\n }\n\n private async request<TResult>(path: string, init: RequestInit): Promise<TResult> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n signal: controller.signal\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new IpcHttpError(response.status, text || response.statusText);\n }\n\n return (await response.json()) as TResult;\n } catch (error) {\n if (error instanceof IpcHttpError) throw error;\n if (error instanceof Error) throw new IpcConnectionError(error.message);\n throw new IpcConnectionError();\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n\nexport class AdofaiIpcNamespaceClient {\n constructor(\n private readonly client: AdofaiIpcClient,\n readonly namespace: string\n ) {\n }\n\n async call<TResult = unknown, TParams = unknown>(\n method: string,\n params?: TParams,\n id?: string\n ): Promise<TResult> {\n return this.client.call<TResult, TParams>({\n namespace: this.namespace,\n method,\n params,\n id\n });\n }\n}\n\nexport async function tryConnect(options: TryConnectOptions = {}): Promise<AdofaiIpcClient> {\n const host = options.host ?? DEFAULT_HOST;\n const startPort = options.startPort ?? DEFAULT_START_PORT;\n const endPort = options.endPort ?? DEFAULT_END_PORT;\n\n for (let port = startPort; port <= endPort; port++) {\n const client = new AdofaiIpcClient({\n baseUrl: `http://${host}:${port}`,\n fetch: options.fetch,\n timeoutMs: options.timeoutMs\n });\n\n try {\n const health = await client.health();\n\n if (health.ok && health.server === \"AdofaiIpc\") {\n return client;\n }\n } catch {\n }\n }\n\n throw new IpcConnectionError(\n `Could not connect to AdofaiIpc on ${host}:${startPort}-${endPort}.`\n );\n}\n\nfunction normalizeBaseUrl(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\nfunction createRequestId(): string {\n return `adofai-ipc-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/version.ts","../src/client.ts"],"sourcesContent":["export {\n AdofaiIpcClient,\n AdofaiIpcNamespaceClient,\n tryConnect\n} from \"./client\";\n\nexport {\n AdofaiIpcError,\n IpcConnectionError,\n IpcHttpError,\n IpcResponseError,\n IpcTimeoutError,\n IpcVersionMismatchError,\n isIpcUnavailable\n} from \"./errors\";\n\nexport type {\n IpcConnectionErrorCode,\n IpcConnectionErrorOptions,\n IpcVersionMismatchDirection\n} from \"./errors\";\n\nexport { CLIENT_VERSION } from \"./version\";\n\nexport type {\n AdofaiIpcClientOptions,\n IpcCallOptions,\n IpcErrorInfo,\n IpcErrorResponse,\n IpcHealthResponse,\n IpcNamespaceDetail,\n IpcNamespaceErrorInfo,\n IpcNamespaceStatus,\n IpcNamespacesResponse,\n IpcNamespaceSummary,\n IpcRequestId,\n IpcRequestOptions,\n IpcResponse,\n IpcSuccessResponse,\n TryConnectOptions,\n WaitForNamespaceOptions\n} from \"./types\";\n","import type { IpcErrorInfo } from \"./types\";\n\nexport type IpcConnectionErrorCode = \"UNAVAILABLE\" | \"TIMEOUT\";\nexport type IpcVersionMismatchDirection =\n | \"server_outdated\"\n | \"client_outdated\"\n | \"legacy_server\";\n\nexport interface IpcConnectionErrorOptions {\n code?: IpcConnectionErrorCode;\n cause?: unknown;\n}\n\nexport class AdofaiIpcError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AdofaiIpcError\";\n }\n}\n\nexport class IpcConnectionError extends AdofaiIpcError {\n readonly code: IpcConnectionErrorCode;\n readonly cause?: unknown;\n\n constructor(\n message = \"Could not connect to AdofaiIpc.\",\n options: IpcConnectionErrorOptions = {}\n ) {\n super(message);\n this.name = \"IpcConnectionError\";\n this.code = options.code ?? \"UNAVAILABLE\";\n this.cause = options.cause;\n }\n}\n\nexport class IpcTimeoutError extends IpcConnectionError {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number, options: Pick<IpcConnectionErrorOptions, \"cause\"> = {}) {\n super(`AdofaiIpc request timed out after ${timeoutMs} ms.`, {\n code: \"TIMEOUT\",\n cause: options.cause\n });\n this.name = \"IpcTimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class IpcVersionMismatchError extends AdofaiIpcError {\n readonly code = \"VERSION_MISMATCH\" as const;\n readonly clientVersion: string;\n readonly serverVersion: string | null;\n readonly direction: IpcVersionMismatchDirection;\n readonly protocolVersion: number | null;\n\n constructor(options: {\n clientVersion: string;\n serverVersion: string | null;\n direction: IpcVersionMismatchDirection;\n protocolVersion: number | null;\n }) {\n const server = options.serverVersion ?? \"legacy/unknown\";\n super(`AdofaiIpc version mismatch: client ${options.clientVersion}, server ${server}.`);\n this.name = \"IpcVersionMismatchError\";\n this.clientVersion = options.clientVersion;\n this.serverVersion = options.serverVersion;\n this.direction = options.direction;\n this.protocolVersion = options.protocolVersion;\n }\n}\n\nexport function isIpcUnavailable(error: unknown): error is IpcConnectionError {\n return error instanceof IpcConnectionError && error.code === \"UNAVAILABLE\";\n}\n\nexport class IpcHttpError extends AdofaiIpcError {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = \"IpcHttpError\";\n this.status = status;\n }\n}\n\nexport class IpcResponseError extends AdofaiIpcError {\n readonly code: string;\n readonly error: IpcErrorInfo;\n\n constructor(error: IpcErrorInfo) {\n super(error.message);\n this.name = \"IpcResponseError\";\n this.code = error.code;\n this.error = error;\n }\n}\n","declare const __CLIENT_VERSION__: string;\n\nexport const CLIENT_VERSION = __CLIENT_VERSION__;\n\nconst CANONICAL_SEMVER = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*))?$/;\n\ninterface ParsedVersion {\n core: [number, number, number];\n prerelease: string[] | null;\n}\n\nexport function compareProductVersions(left: string, right: string): number | null {\n const leftVersion = parseProductVersion(left);\n const rightVersion = parseProductVersion(right);\n if (!leftVersion || !rightVersion) return null;\n\n for (let index = 0; index < leftVersion.core.length; index++) {\n const difference = leftVersion.core[index]! - rightVersion.core[index]!;\n if (difference !== 0) return difference < 0 ? -1 : 1;\n }\n\n if (leftVersion.prerelease === null) return rightVersion.prerelease === null ? 0 : 1;\n if (rightVersion.prerelease === null) return -1;\n\n const length = Math.max(leftVersion.prerelease.length, rightVersion.prerelease.length);\n for (let index = 0; index < length; index++) {\n const leftPart = leftVersion.prerelease[index];\n const rightPart = rightVersion.prerelease[index];\n if (leftPart === undefined) return -1;\n if (rightPart === undefined) return 1;\n if (leftPart === rightPart) continue;\n\n const leftNumeric = /^\\d+$/.test(leftPart);\n const rightNumeric = /^\\d+$/.test(rightPart);\n if (leftNumeric && rightNumeric) return Number(leftPart) < Number(rightPart) ? -1 : 1;\n if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;\n return leftPart < rightPart ? -1 : 1;\n }\n\n return 0;\n}\n\nfunction parseProductVersion(value: string): ParsedVersion | null {\n const match = CANONICAL_SEMVER.exec(value);\n if (!match) return null;\n return {\n core: [Number(match[1]), Number(match[2]), Number(match[3])],\n prerelease: match[4] ? match[4].split(\".\") : null\n };\n}\n","import {\n IpcConnectionError,\n IpcHttpError,\n IpcResponseError,\n IpcTimeoutError,\n IpcVersionMismatchError\n} from \"./errors\";\nimport { CLIENT_VERSION, compareProductVersions } from \"./version\";\nimport type {\n AdofaiIpcClientOptions,\n IpcCallOptions,\n IpcErrorInfo,\n IpcHealthResponse,\n IpcNamespaceDetail,\n IpcNamespacesResponse,\n IpcRequestId,\n IpcRequestOptions,\n IpcResponse,\n TryConnectOptions,\n WaitForNamespaceOptions\n} from \"./types\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_START_PORT = 32145;\nconst DEFAULT_END_PORT = 32155;\nconst DEFAULT_PROBE_TIMEOUT_MS = 500;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nconst DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS = 10_000;\nconst DEFAULT_NAMESPACE_POLL_INTERVAL_MS = 100;\n\nexport class AdofaiIpcClient {\n readonly baseUrl: string;\n\n private readonly fetchImpl: typeof fetch;\n private readonly requestTimeoutMs: number;\n\n constructor(options: AdofaiIpcClientOptions = {}) {\n this.baseUrl = normalizeBaseUrl(options.baseUrl ?? `http://${DEFAULT_HOST}:${DEFAULT_START_PORT}`);\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.requestTimeoutMs =\n options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n if (!this.fetchImpl) {\n throw new IpcConnectionError(\"A fetch implementation is required.\");\n }\n }\n\n static async connect(options: TryConnectOptions = {}): Promise<AdofaiIpcClient> {\n return tryConnect(options);\n }\n\n async health(options: IpcRequestOptions = {}): Promise<IpcHealthResponse> {\n return this.get<IpcHealthResponse>(\"/ipc/health\", options);\n }\n\n async listNamespaces(options: IpcRequestOptions = {}): Promise<IpcNamespacesResponse> {\n return this.get<IpcNamespacesResponse>(\"/ipc/namespaces\", options);\n }\n\n async getNamespace(\n namespace: string,\n options: IpcRequestOptions = {}\n ): Promise<IpcNamespaceDetail> {\n return this.get<IpcNamespaceDetail>(\n `/ipc/namespaces/${encodeURIComponent(namespace)}`,\n options\n );\n }\n\n async waitForNamespace(\n namespace: string,\n options: WaitForNamespaceOptions = {}\n ): Promise<IpcNamespaceDetail> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS;\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_NAMESPACE_POLL_INTERVAL_MS;\n const requiredStatus = options.status ?? \"registered\";\n const deadline = Date.now() + timeoutMs;\n let stateError: IpcResponseError | undefined;\n\n while (true) {\n try {\n const detail = await this.getNamespace(namespace, {\n timeoutMs: options.requestTimeoutMs\n });\n\n if (requiredStatus === \"registered\" || detail.status === \"ready\") {\n return detail;\n }\n\n if (detail.status === \"error\") {\n throw new IpcResponseError({\n code: \"namespace_error\",\n message: detail.error?.message ?? `Namespace initialization failed: ${namespace}`\n });\n }\n\n if (detail.status !== \"initializing\") {\n throw new IpcResponseError({\n code: \"namespace_status_unavailable\",\n message: `Namespace status is unavailable: ${namespace}`\n });\n }\n\n stateError = new IpcResponseError({\n code: \"namespace_initializing\",\n message: `Namespace is initializing: ${namespace}`\n });\n } catch (error) {\n if (\n !(error instanceof IpcResponseError) ||\n (error.code !== \"namespace_not_found\" && error.code !== \"namespace_initializing\")\n ) {\n throw error;\n }\n\n stateError = error;\n }\n\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n throw stateError ?? new IpcResponseError({\n code: \"namespace_initializing\",\n message: `Namespace is initializing: ${namespace}`\n });\n }\n\n await delay(Math.min(pollIntervalMs, remainingMs));\n }\n }\n\n namespace(namespace: string): AdofaiIpcNamespaceClient {\n return new AdofaiIpcNamespaceClient(this, namespace);\n }\n\n async call<TResult = unknown, TParams = unknown>(\n options: IpcCallOptions<TParams>\n ): Promise<TResult> {\n const response = await this.post<IpcResponse<TResult>>(\n \"/ipc\",\n {\n namespace: options.namespace,\n method: options.method,\n params: options.params ?? {},\n id: options.id ?? createRequestId()\n },\n { timeoutMs: options.timeoutMs }\n );\n\n if (!response.ok) {\n throw new IpcResponseError(response.error);\n }\n\n return response.result;\n }\n\n private async get<TResult>(\n path: string,\n options: IpcRequestOptions = {}\n ): Promise<TResult> {\n return this.request<TResult>(path, {\n method: \"GET\"\n }, options);\n }\n\n private async post<TResult>(\n path: string,\n body: unknown,\n options: IpcRequestOptions = {}\n ): Promise<TResult> {\n return this.request<TResult>(path, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(body)\n }, options);\n }\n\n private async request<TResult>(\n path: string,\n init: RequestInit,\n options: IpcRequestOptions\n ): Promise<TResult> {\n const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;\n const controller = new AbortController();\n const timeoutError = new IpcTimeoutError(timeoutMs);\n const timeout = setTimeout(() => controller.abort(timeoutError), timeoutMs);\n\n try {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n signal: controller.signal\n });\n\n if (!response.ok) {\n const text = await response.text();\n const responseError = parseIpcResponseError(text);\n if (responseError) throw new IpcResponseError(responseError);\n throw new IpcHttpError(response.status, text || response.statusText);\n }\n\n return (await response.json()) as TResult;\n } catch (error) {\n if (error instanceof IpcHttpError || error instanceof IpcResponseError) throw error;\n if (controller.signal.aborted) {\n if (error === timeoutError) throw timeoutError;\n throw new IpcTimeoutError(timeoutMs, { cause: error });\n }\n throw new IpcConnectionError(getErrorMessage(error), { cause: error });\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n\nexport class AdofaiIpcNamespaceClient {\n constructor(\n private readonly client: AdofaiIpcClient,\n readonly namespace: string\n ) {\n }\n\n async call<TResult = unknown, TParams = unknown>(\n method: string,\n params?: TParams,\n idOrOptions?: IpcRequestId | IpcRequestOptions,\n options: IpcRequestOptions = {}\n ): Promise<TResult> {\n const requestOptions = isIpcRequestOptions(idOrOptions) ? idOrOptions : options;\n const id = isIpcRequestOptions(idOrOptions) ? undefined : idOrOptions;\n\n return this.client.call<TResult, TParams>({\n namespace: this.namespace,\n method,\n params,\n id,\n timeoutMs: requestOptions.timeoutMs\n });\n }\n}\n\nexport async function tryConnect(options: TryConnectOptions = {}): Promise<AdofaiIpcClient> {\n const host = options.host ?? DEFAULT_HOST;\n const startPort = options.startPort ?? DEFAULT_START_PORT;\n const endPort = options.endPort ?? DEFAULT_END_PORT;\n const probeTimeoutMs =\n options.probeTimeoutMs ?? options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;\n const requestTimeoutMs =\n options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n let lastProbeError: unknown;\n\n for (let port = startPort; port <= endPort; port++) {\n const client = new AdofaiIpcClient({\n baseUrl: `http://${host}:${port}`,\n fetch: options.fetch,\n requestTimeoutMs: probeTimeoutMs\n });\n\n try {\n const health = await client.health();\n\n if (health.ok && health.server === \"AdofaiIpc\") {\n const mismatch = getVersionMismatch(health);\n if (mismatch) {\n try {\n options.onVersionMismatch?.(mismatch);\n } catch {\n }\n throw mismatch;\n }\n return new AdofaiIpcClient({\n baseUrl: client.baseUrl,\n fetch: options.fetch,\n requestTimeoutMs\n });\n }\n } catch (error) {\n if (error instanceof IpcVersionMismatchError) throw error;\n lastProbeError = error;\n }\n }\n\n if (lastProbeError instanceof IpcTimeoutError) throw lastProbeError;\n\n throw new IpcConnectionError(\n `Could not connect to AdofaiIpc on ${host}:${startPort}-${endPort}.`\n );\n}\n\nfunction getVersionMismatch(health: IpcHealthResponse): IpcVersionMismatchError | null {\n const protocolVersion = Number.isInteger(health.protocolVersion) ? health.protocolVersion : null;\n const serverVersion = typeof health.serverVersion === \"string\" ? health.serverVersion : null;\n const comparison = serverVersion === null\n ? null\n : compareProductVersions(serverVersion, CLIENT_VERSION);\n\n if (comparison === 0 && serverVersion === CLIENT_VERSION) return null;\n\n return new IpcVersionMismatchError({\n clientVersion: CLIENT_VERSION,\n serverVersion,\n direction: comparison === null\n ? \"legacy_server\"\n : comparison < 0\n ? \"server_outdated\"\n : \"client_outdated\",\n protocolVersion\n });\n}\n\nfunction normalizeBaseUrl(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\nfunction getErrorMessage(error: unknown): string | undefined {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof error.message === \"string\"\n ) {\n return error.message;\n }\n\n return undefined;\n}\n\nfunction parseIpcResponseError(text: string): IpcErrorInfo | undefined {\n try {\n const value = JSON.parse(text) as { error?: unknown };\n const error = value?.error;\n\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n typeof error.code === \"string\" &&\n \"message\" in error &&\n typeof error.message === \"string\"\n ) {\n return { code: error.code, message: error.message };\n }\n } catch {\n }\n\n return undefined;\n}\n\nfunction isIpcRequestOptions(value: unknown): value is IpcRequestOptions {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction delay(timeoutMs: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, timeoutMs));\n}\n\nfunction createRequestId(): string {\n return `adofai-ipc-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,61 @@
1
+ type IpcConnectionErrorCode = "UNAVAILABLE" | "TIMEOUT";
2
+ type IpcVersionMismatchDirection = "server_outdated" | "client_outdated" | "legacy_server";
3
+ interface IpcConnectionErrorOptions {
4
+ code?: IpcConnectionErrorCode;
5
+ cause?: unknown;
6
+ }
7
+ declare class AdofaiIpcError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ declare class IpcConnectionError extends AdofaiIpcError {
11
+ readonly code: IpcConnectionErrorCode;
12
+ readonly cause?: unknown;
13
+ constructor(message?: string, options?: IpcConnectionErrorOptions);
14
+ }
15
+ declare class IpcTimeoutError extends IpcConnectionError {
16
+ readonly timeoutMs: number;
17
+ constructor(timeoutMs: number, options?: Pick<IpcConnectionErrorOptions, "cause">);
18
+ }
19
+ declare class IpcVersionMismatchError extends AdofaiIpcError {
20
+ readonly code: "VERSION_MISMATCH";
21
+ readonly clientVersion: string;
22
+ readonly serverVersion: string | null;
23
+ readonly direction: IpcVersionMismatchDirection;
24
+ readonly protocolVersion: number | null;
25
+ constructor(options: {
26
+ clientVersion: string;
27
+ serverVersion: string | null;
28
+ direction: IpcVersionMismatchDirection;
29
+ protocolVersion: number | null;
30
+ });
31
+ }
32
+ declare function isIpcUnavailable(error: unknown): error is IpcConnectionError;
33
+ declare class IpcHttpError extends AdofaiIpcError {
34
+ readonly status: number;
35
+ constructor(status: number, message: string);
36
+ }
37
+ declare class IpcResponseError extends AdofaiIpcError {
38
+ readonly code: string;
39
+ readonly error: IpcErrorInfo;
40
+ constructor(error: IpcErrorInfo);
41
+ }
42
+
1
43
  type IpcRequestId = string | number | null;
2
44
  interface IpcCallOptions<TParams = unknown> {
3
45
  namespace: string;
4
46
  method: string;
5
47
  params?: TParams;
6
48
  id?: IpcRequestId;
49
+ timeoutMs?: number;
50
+ }
51
+ interface IpcRequestOptions {
52
+ timeoutMs?: number;
53
+ }
54
+ interface WaitForNamespaceOptions {
55
+ timeoutMs?: number;
56
+ pollIntervalMs?: number;
57
+ requestTimeoutMs?: number;
58
+ status?: "registered" | "ready";
7
59
  }
8
60
  interface IpcSuccessResponse<TResult = unknown> {
9
61
  ok: true;
@@ -24,13 +76,20 @@ type IpcResponse<TResult = unknown> = IpcSuccessResponse<TResult> | IpcErrorResp
24
76
  interface IpcHealthResponse {
25
77
  ok: true;
26
78
  server: "AdofaiIpc";
79
+ serverVersion?: string;
27
80
  protocolVersion: number;
28
81
  port: number;
29
82
  }
83
+ type IpcNamespaceStatus = "initializing" | "ready" | "error";
84
+ interface IpcNamespaceErrorInfo {
85
+ code: string;
86
+ message: string;
87
+ }
30
88
  interface IpcNamespaceSummary {
31
89
  name: string;
32
90
  displayName: string;
33
91
  version: string;
92
+ status: IpcNamespaceStatus;
34
93
  }
35
94
  interface IpcNamespacesResponse {
36
95
  namespaces: IpcNamespaceSummary[];
@@ -39,11 +98,15 @@ interface IpcNamespaceDetail {
39
98
  namespace: string;
40
99
  displayName: string;
41
100
  version: string;
101
+ status: IpcNamespaceStatus;
102
+ error?: IpcNamespaceErrorInfo | null;
42
103
  methods: string[];
43
104
  }
44
105
  interface AdofaiIpcClientOptions {
45
106
  baseUrl?: string;
46
107
  fetch?: typeof fetch;
108
+ requestTimeoutMs?: number;
109
+ /** @deprecated Use requestTimeoutMs instead. */
47
110
  timeoutMs?: number;
48
111
  }
49
112
  interface TryConnectOptions {
@@ -51,18 +114,23 @@ interface TryConnectOptions {
51
114
  startPort?: number;
52
115
  endPort?: number;
53
116
  fetch?: typeof fetch;
117
+ probeTimeoutMs?: number;
118
+ requestTimeoutMs?: number;
119
+ onVersionMismatch?: (error: IpcVersionMismatchError) => void;
120
+ /** @deprecated Use probeTimeoutMs and requestTimeoutMs instead. */
54
121
  timeoutMs?: number;
55
122
  }
56
123
 
57
124
  declare class AdofaiIpcClient {
58
125
  readonly baseUrl: string;
59
126
  private readonly fetchImpl;
60
- private readonly timeoutMs;
127
+ private readonly requestTimeoutMs;
61
128
  constructor(options?: AdofaiIpcClientOptions);
62
129
  static connect(options?: TryConnectOptions): Promise<AdofaiIpcClient>;
63
- health(): Promise<IpcHealthResponse>;
64
- listNamespaces(): Promise<IpcNamespacesResponse>;
65
- getNamespace(namespace: string): Promise<IpcNamespaceDetail>;
130
+ health(options?: IpcRequestOptions): Promise<IpcHealthResponse>;
131
+ listNamespaces(options?: IpcRequestOptions): Promise<IpcNamespacesResponse>;
132
+ getNamespace(namespace: string, options?: IpcRequestOptions): Promise<IpcNamespaceDetail>;
133
+ waitForNamespace(namespace: string, options?: WaitForNamespaceOptions): Promise<IpcNamespaceDetail>;
66
134
  namespace(namespace: string): AdofaiIpcNamespaceClient;
67
135
  call<TResult = unknown, TParams = unknown>(options: IpcCallOptions<TParams>): Promise<TResult>;
68
136
  private get;
@@ -73,24 +141,10 @@ declare class AdofaiIpcNamespaceClient {
73
141
  private readonly client;
74
142
  readonly namespace: string;
75
143
  constructor(client: AdofaiIpcClient, namespace: string);
76
- call<TResult = unknown, TParams = unknown>(method: string, params?: TParams, id?: string): Promise<TResult>;
144
+ call<TResult = unknown, TParams = unknown>(method: string, params?: TParams, idOrOptions?: IpcRequestId | IpcRequestOptions, options?: IpcRequestOptions): Promise<TResult>;
77
145
  }
78
146
  declare function tryConnect(options?: TryConnectOptions): Promise<AdofaiIpcClient>;
79
147
 
80
- declare class AdofaiIpcError extends Error {
81
- constructor(message: string);
82
- }
83
- declare class IpcConnectionError extends AdofaiIpcError {
84
- constructor(message?: string);
85
- }
86
- declare class IpcHttpError extends AdofaiIpcError {
87
- readonly status: number;
88
- constructor(status: number, message: string);
89
- }
90
- declare class IpcResponseError extends AdofaiIpcError {
91
- readonly code: string;
92
- readonly error: IpcErrorInfo;
93
- constructor(error: IpcErrorInfo);
94
- }
148
+ declare const CLIENT_VERSION: string;
95
149
 
96
- export { AdofaiIpcClient, type AdofaiIpcClientOptions, AdofaiIpcError, AdofaiIpcNamespaceClient, type IpcCallOptions, IpcConnectionError, type IpcErrorInfo, type IpcErrorResponse, type IpcHealthResponse, IpcHttpError, type IpcNamespaceDetail, type IpcNamespaceSummary, type IpcNamespacesResponse, type IpcRequestId, type IpcResponse, IpcResponseError, type IpcSuccessResponse, type TryConnectOptions, tryConnect };
150
+ export { AdofaiIpcClient, type AdofaiIpcClientOptions, AdofaiIpcError, AdofaiIpcNamespaceClient, CLIENT_VERSION, type IpcCallOptions, IpcConnectionError, type IpcConnectionErrorCode, type IpcConnectionErrorOptions, type IpcErrorInfo, type IpcErrorResponse, type IpcHealthResponse, IpcHttpError, type IpcNamespaceDetail, type IpcNamespaceErrorInfo, type IpcNamespaceStatus, type IpcNamespaceSummary, type IpcNamespacesResponse, type IpcRequestId, type IpcRequestOptions, type IpcResponse, IpcResponseError, type IpcSuccessResponse, IpcTimeoutError, type IpcVersionMismatchDirection, IpcVersionMismatchError, type TryConnectOptions, type WaitForNamespaceOptions, isIpcUnavailable, tryConnect };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,61 @@
1
+ type IpcConnectionErrorCode = "UNAVAILABLE" | "TIMEOUT";
2
+ type IpcVersionMismatchDirection = "server_outdated" | "client_outdated" | "legacy_server";
3
+ interface IpcConnectionErrorOptions {
4
+ code?: IpcConnectionErrorCode;
5
+ cause?: unknown;
6
+ }
7
+ declare class AdofaiIpcError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ declare class IpcConnectionError extends AdofaiIpcError {
11
+ readonly code: IpcConnectionErrorCode;
12
+ readonly cause?: unknown;
13
+ constructor(message?: string, options?: IpcConnectionErrorOptions);
14
+ }
15
+ declare class IpcTimeoutError extends IpcConnectionError {
16
+ readonly timeoutMs: number;
17
+ constructor(timeoutMs: number, options?: Pick<IpcConnectionErrorOptions, "cause">);
18
+ }
19
+ declare class IpcVersionMismatchError extends AdofaiIpcError {
20
+ readonly code: "VERSION_MISMATCH";
21
+ readonly clientVersion: string;
22
+ readonly serverVersion: string | null;
23
+ readonly direction: IpcVersionMismatchDirection;
24
+ readonly protocolVersion: number | null;
25
+ constructor(options: {
26
+ clientVersion: string;
27
+ serverVersion: string | null;
28
+ direction: IpcVersionMismatchDirection;
29
+ protocolVersion: number | null;
30
+ });
31
+ }
32
+ declare function isIpcUnavailable(error: unknown): error is IpcConnectionError;
33
+ declare class IpcHttpError extends AdofaiIpcError {
34
+ readonly status: number;
35
+ constructor(status: number, message: string);
36
+ }
37
+ declare class IpcResponseError extends AdofaiIpcError {
38
+ readonly code: string;
39
+ readonly error: IpcErrorInfo;
40
+ constructor(error: IpcErrorInfo);
41
+ }
42
+
1
43
  type IpcRequestId = string | number | null;
2
44
  interface IpcCallOptions<TParams = unknown> {
3
45
  namespace: string;
4
46
  method: string;
5
47
  params?: TParams;
6
48
  id?: IpcRequestId;
49
+ timeoutMs?: number;
50
+ }
51
+ interface IpcRequestOptions {
52
+ timeoutMs?: number;
53
+ }
54
+ interface WaitForNamespaceOptions {
55
+ timeoutMs?: number;
56
+ pollIntervalMs?: number;
57
+ requestTimeoutMs?: number;
58
+ status?: "registered" | "ready";
7
59
  }
8
60
  interface IpcSuccessResponse<TResult = unknown> {
9
61
  ok: true;
@@ -24,13 +76,20 @@ type IpcResponse<TResult = unknown> = IpcSuccessResponse<TResult> | IpcErrorResp
24
76
  interface IpcHealthResponse {
25
77
  ok: true;
26
78
  server: "AdofaiIpc";
79
+ serverVersion?: string;
27
80
  protocolVersion: number;
28
81
  port: number;
29
82
  }
83
+ type IpcNamespaceStatus = "initializing" | "ready" | "error";
84
+ interface IpcNamespaceErrorInfo {
85
+ code: string;
86
+ message: string;
87
+ }
30
88
  interface IpcNamespaceSummary {
31
89
  name: string;
32
90
  displayName: string;
33
91
  version: string;
92
+ status: IpcNamespaceStatus;
34
93
  }
35
94
  interface IpcNamespacesResponse {
36
95
  namespaces: IpcNamespaceSummary[];
@@ -39,11 +98,15 @@ interface IpcNamespaceDetail {
39
98
  namespace: string;
40
99
  displayName: string;
41
100
  version: string;
101
+ status: IpcNamespaceStatus;
102
+ error?: IpcNamespaceErrorInfo | null;
42
103
  methods: string[];
43
104
  }
44
105
  interface AdofaiIpcClientOptions {
45
106
  baseUrl?: string;
46
107
  fetch?: typeof fetch;
108
+ requestTimeoutMs?: number;
109
+ /** @deprecated Use requestTimeoutMs instead. */
47
110
  timeoutMs?: number;
48
111
  }
49
112
  interface TryConnectOptions {
@@ -51,18 +114,23 @@ interface TryConnectOptions {
51
114
  startPort?: number;
52
115
  endPort?: number;
53
116
  fetch?: typeof fetch;
117
+ probeTimeoutMs?: number;
118
+ requestTimeoutMs?: number;
119
+ onVersionMismatch?: (error: IpcVersionMismatchError) => void;
120
+ /** @deprecated Use probeTimeoutMs and requestTimeoutMs instead. */
54
121
  timeoutMs?: number;
55
122
  }
56
123
 
57
124
  declare class AdofaiIpcClient {
58
125
  readonly baseUrl: string;
59
126
  private readonly fetchImpl;
60
- private readonly timeoutMs;
127
+ private readonly requestTimeoutMs;
61
128
  constructor(options?: AdofaiIpcClientOptions);
62
129
  static connect(options?: TryConnectOptions): Promise<AdofaiIpcClient>;
63
- health(): Promise<IpcHealthResponse>;
64
- listNamespaces(): Promise<IpcNamespacesResponse>;
65
- getNamespace(namespace: string): Promise<IpcNamespaceDetail>;
130
+ health(options?: IpcRequestOptions): Promise<IpcHealthResponse>;
131
+ listNamespaces(options?: IpcRequestOptions): Promise<IpcNamespacesResponse>;
132
+ getNamespace(namespace: string, options?: IpcRequestOptions): Promise<IpcNamespaceDetail>;
133
+ waitForNamespace(namespace: string, options?: WaitForNamespaceOptions): Promise<IpcNamespaceDetail>;
66
134
  namespace(namespace: string): AdofaiIpcNamespaceClient;
67
135
  call<TResult = unknown, TParams = unknown>(options: IpcCallOptions<TParams>): Promise<TResult>;
68
136
  private get;
@@ -73,24 +141,10 @@ declare class AdofaiIpcNamespaceClient {
73
141
  private readonly client;
74
142
  readonly namespace: string;
75
143
  constructor(client: AdofaiIpcClient, namespace: string);
76
- call<TResult = unknown, TParams = unknown>(method: string, params?: TParams, id?: string): Promise<TResult>;
144
+ call<TResult = unknown, TParams = unknown>(method: string, params?: TParams, idOrOptions?: IpcRequestId | IpcRequestOptions, options?: IpcRequestOptions): Promise<TResult>;
77
145
  }
78
146
  declare function tryConnect(options?: TryConnectOptions): Promise<AdofaiIpcClient>;
79
147
 
80
- declare class AdofaiIpcError extends Error {
81
- constructor(message: string);
82
- }
83
- declare class IpcConnectionError extends AdofaiIpcError {
84
- constructor(message?: string);
85
- }
86
- declare class IpcHttpError extends AdofaiIpcError {
87
- readonly status: number;
88
- constructor(status: number, message: string);
89
- }
90
- declare class IpcResponseError extends AdofaiIpcError {
91
- readonly code: string;
92
- readonly error: IpcErrorInfo;
93
- constructor(error: IpcErrorInfo);
94
- }
148
+ declare const CLIENT_VERSION: string;
95
149
 
96
- export { AdofaiIpcClient, type AdofaiIpcClientOptions, AdofaiIpcError, AdofaiIpcNamespaceClient, type IpcCallOptions, IpcConnectionError, type IpcErrorInfo, type IpcErrorResponse, type IpcHealthResponse, IpcHttpError, type IpcNamespaceDetail, type IpcNamespaceSummary, type IpcNamespacesResponse, type IpcRequestId, type IpcResponse, IpcResponseError, type IpcSuccessResponse, type TryConnectOptions, tryConnect };
150
+ export { AdofaiIpcClient, type AdofaiIpcClientOptions, AdofaiIpcError, AdofaiIpcNamespaceClient, CLIENT_VERSION, type IpcCallOptions, IpcConnectionError, type IpcConnectionErrorCode, type IpcConnectionErrorOptions, type IpcErrorInfo, type IpcErrorResponse, type IpcHealthResponse, IpcHttpError, type IpcNamespaceDetail, type IpcNamespaceErrorInfo, type IpcNamespaceStatus, type IpcNamespaceSummary, type IpcNamespacesResponse, type IpcRequestId, type IpcRequestOptions, type IpcResponse, IpcResponseError, type IpcSuccessResponse, IpcTimeoutError, type IpcVersionMismatchDirection, IpcVersionMismatchError, type TryConnectOptions, type WaitForNamespaceOptions, isIpcUnavailable, tryConnect };
package/dist/index.js CHANGED
@@ -6,11 +6,38 @@ var AdofaiIpcError = class extends Error {
6
6
  }
7
7
  };
8
8
  var IpcConnectionError = class extends AdofaiIpcError {
9
- constructor(message = "Could not connect to AdofaiIpc.") {
9
+ constructor(message = "Could not connect to AdofaiIpc.", options = {}) {
10
10
  super(message);
11
11
  this.name = "IpcConnectionError";
12
+ this.code = options.code ?? "UNAVAILABLE";
13
+ this.cause = options.cause;
12
14
  }
13
15
  };
16
+ var IpcTimeoutError = class extends IpcConnectionError {
17
+ constructor(timeoutMs, options = {}) {
18
+ super(`AdofaiIpc request timed out after ${timeoutMs} ms.`, {
19
+ code: "TIMEOUT",
20
+ cause: options.cause
21
+ });
22
+ this.name = "IpcTimeoutError";
23
+ this.timeoutMs = timeoutMs;
24
+ }
25
+ };
26
+ var IpcVersionMismatchError = class extends AdofaiIpcError {
27
+ constructor(options) {
28
+ const server = options.serverVersion ?? "legacy/unknown";
29
+ super(`AdofaiIpc version mismatch: client ${options.clientVersion}, server ${server}.`);
30
+ this.code = "VERSION_MISMATCH";
31
+ this.name = "IpcVersionMismatchError";
32
+ this.clientVersion = options.clientVersion;
33
+ this.serverVersion = options.serverVersion;
34
+ this.direction = options.direction;
35
+ this.protocolVersion = options.protocolVersion;
36
+ }
37
+ };
38
+ function isIpcUnavailable(error) {
39
+ return error instanceof IpcConnectionError && error.code === "UNAVAILABLE";
40
+ }
14
41
  var IpcHttpError = class extends AdofaiIpcError {
15
42
  constructor(status, message) {
16
43
  super(message);
@@ -27,16 +54,56 @@ var IpcResponseError = class extends AdofaiIpcError {
27
54
  }
28
55
  };
29
56
 
57
+ // src/version.ts
58
+ var CLIENT_VERSION = "0.3.0";
59
+ var CANONICAL_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?$/;
60
+ function compareProductVersions(left, right) {
61
+ const leftVersion = parseProductVersion(left);
62
+ const rightVersion = parseProductVersion(right);
63
+ if (!leftVersion || !rightVersion) return null;
64
+ for (let index = 0; index < leftVersion.core.length; index++) {
65
+ const difference = leftVersion.core[index] - rightVersion.core[index];
66
+ if (difference !== 0) return difference < 0 ? -1 : 1;
67
+ }
68
+ if (leftVersion.prerelease === null) return rightVersion.prerelease === null ? 0 : 1;
69
+ if (rightVersion.prerelease === null) return -1;
70
+ const length = Math.max(leftVersion.prerelease.length, rightVersion.prerelease.length);
71
+ for (let index = 0; index < length; index++) {
72
+ const leftPart = leftVersion.prerelease[index];
73
+ const rightPart = rightVersion.prerelease[index];
74
+ if (leftPart === void 0) return -1;
75
+ if (rightPart === void 0) return 1;
76
+ if (leftPart === rightPart) continue;
77
+ const leftNumeric = /^\d+$/.test(leftPart);
78
+ const rightNumeric = /^\d+$/.test(rightPart);
79
+ if (leftNumeric && rightNumeric) return Number(leftPart) < Number(rightPart) ? -1 : 1;
80
+ if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;
81
+ return leftPart < rightPart ? -1 : 1;
82
+ }
83
+ return 0;
84
+ }
85
+ function parseProductVersion(value) {
86
+ const match = CANONICAL_SEMVER.exec(value);
87
+ if (!match) return null;
88
+ return {
89
+ core: [Number(match[1]), Number(match[2]), Number(match[3])],
90
+ prerelease: match[4] ? match[4].split(".") : null
91
+ };
92
+ }
93
+
30
94
  // src/client.ts
31
95
  var DEFAULT_HOST = "127.0.0.1";
32
96
  var DEFAULT_START_PORT = 32145;
33
97
  var DEFAULT_END_PORT = 32155;
34
- var DEFAULT_TIMEOUT_MS = 500;
98
+ var DEFAULT_PROBE_TIMEOUT_MS = 500;
99
+ var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
100
+ var DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS = 1e4;
101
+ var DEFAULT_NAMESPACE_POLL_INTERVAL_MS = 100;
35
102
  var AdofaiIpcClient = class {
36
103
  constructor(options = {}) {
37
104
  this.baseUrl = normalizeBaseUrl(options.baseUrl ?? `http://${DEFAULT_HOST}:${DEFAULT_START_PORT}`);
38
105
  this.fetchImpl = options.fetch ?? globalThis.fetch;
39
- this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
106
+ this.requestTimeoutMs = options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
40
107
  if (!this.fetchImpl) {
41
108
  throw new IpcConnectionError("A fetch implementation is required.");
42
109
  }
@@ -44,47 +111,102 @@ var AdofaiIpcClient = class {
44
111
  static async connect(options = {}) {
45
112
  return tryConnect(options);
46
113
  }
47
- async health() {
48
- return this.get("/ipc/health");
114
+ async health(options = {}) {
115
+ return this.get("/ipc/health", options);
116
+ }
117
+ async listNamespaces(options = {}) {
118
+ return this.get("/ipc/namespaces", options);
49
119
  }
50
- async listNamespaces() {
51
- return this.get("/ipc/namespaces");
120
+ async getNamespace(namespace, options = {}) {
121
+ return this.get(
122
+ `/ipc/namespaces/${encodeURIComponent(namespace)}`,
123
+ options
124
+ );
52
125
  }
53
- async getNamespace(namespace) {
54
- return this.get(`/ipc/namespaces/${encodeURIComponent(namespace)}`);
126
+ async waitForNamespace(namespace, options = {}) {
127
+ const timeoutMs = options.timeoutMs ?? DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS;
128
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_NAMESPACE_POLL_INTERVAL_MS;
129
+ const requiredStatus = options.status ?? "registered";
130
+ const deadline = Date.now() + timeoutMs;
131
+ let stateError;
132
+ while (true) {
133
+ try {
134
+ const detail = await this.getNamespace(namespace, {
135
+ timeoutMs: options.requestTimeoutMs
136
+ });
137
+ if (requiredStatus === "registered" || detail.status === "ready") {
138
+ return detail;
139
+ }
140
+ if (detail.status === "error") {
141
+ throw new IpcResponseError({
142
+ code: "namespace_error",
143
+ message: detail.error?.message ?? `Namespace initialization failed: ${namespace}`
144
+ });
145
+ }
146
+ if (detail.status !== "initializing") {
147
+ throw new IpcResponseError({
148
+ code: "namespace_status_unavailable",
149
+ message: `Namespace status is unavailable: ${namespace}`
150
+ });
151
+ }
152
+ stateError = new IpcResponseError({
153
+ code: "namespace_initializing",
154
+ message: `Namespace is initializing: ${namespace}`
155
+ });
156
+ } catch (error) {
157
+ if (!(error instanceof IpcResponseError) || error.code !== "namespace_not_found" && error.code !== "namespace_initializing") {
158
+ throw error;
159
+ }
160
+ stateError = error;
161
+ }
162
+ const remainingMs = deadline - Date.now();
163
+ if (remainingMs <= 0) {
164
+ throw stateError ?? new IpcResponseError({
165
+ code: "namespace_initializing",
166
+ message: `Namespace is initializing: ${namespace}`
167
+ });
168
+ }
169
+ await delay(Math.min(pollIntervalMs, remainingMs));
170
+ }
55
171
  }
56
172
  namespace(namespace) {
57
173
  return new AdofaiIpcNamespaceClient(this, namespace);
58
174
  }
59
175
  async call(options) {
60
- const response = await this.post("/ipc", {
61
- namespace: options.namespace,
62
- method: options.method,
63
- params: options.params ?? {},
64
- id: options.id ?? createRequestId()
65
- });
176
+ const response = await this.post(
177
+ "/ipc",
178
+ {
179
+ namespace: options.namespace,
180
+ method: options.method,
181
+ params: options.params ?? {},
182
+ id: options.id ?? createRequestId()
183
+ },
184
+ { timeoutMs: options.timeoutMs }
185
+ );
66
186
  if (!response.ok) {
67
187
  throw new IpcResponseError(response.error);
68
188
  }
69
189
  return response.result;
70
190
  }
71
- async get(path) {
191
+ async get(path, options = {}) {
72
192
  return this.request(path, {
73
193
  method: "GET"
74
- });
194
+ }, options);
75
195
  }
76
- async post(path, body) {
196
+ async post(path, body, options = {}) {
77
197
  return this.request(path, {
78
198
  method: "POST",
79
199
  headers: {
80
200
  "Content-Type": "application/json"
81
201
  },
82
202
  body: JSON.stringify(body)
83
- });
203
+ }, options);
84
204
  }
85
- async request(path, init) {
205
+ async request(path, init, options) {
206
+ const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
86
207
  const controller = new AbortController();
87
- const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
208
+ const timeoutError = new IpcTimeoutError(timeoutMs);
209
+ const timeout = setTimeout(() => controller.abort(timeoutError), timeoutMs);
88
210
  try {
89
211
  const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
90
212
  ...init,
@@ -92,13 +214,18 @@ var AdofaiIpcClient = class {
92
214
  });
93
215
  if (!response.ok) {
94
216
  const text = await response.text();
217
+ const responseError = parseIpcResponseError(text);
218
+ if (responseError) throw new IpcResponseError(responseError);
95
219
  throw new IpcHttpError(response.status, text || response.statusText);
96
220
  }
97
221
  return await response.json();
98
222
  } catch (error) {
99
- if (error instanceof IpcHttpError) throw error;
100
- if (error instanceof Error) throw new IpcConnectionError(error.message);
101
- throw new IpcConnectionError();
223
+ if (error instanceof IpcHttpError || error instanceof IpcResponseError) throw error;
224
+ if (controller.signal.aborted) {
225
+ if (error === timeoutError) throw timeoutError;
226
+ throw new IpcTimeoutError(timeoutMs, { cause: error });
227
+ }
228
+ throw new IpcConnectionError(getErrorMessage(error), { cause: error });
102
229
  } finally {
103
230
  clearTimeout(timeout);
104
231
  }
@@ -109,12 +236,15 @@ var AdofaiIpcNamespaceClient = class {
109
236
  this.client = client;
110
237
  this.namespace = namespace;
111
238
  }
112
- async call(method, params, id) {
239
+ async call(method, params, idOrOptions, options = {}) {
240
+ const requestOptions = isIpcRequestOptions(idOrOptions) ? idOrOptions : options;
241
+ const id = isIpcRequestOptions(idOrOptions) ? void 0 : idOrOptions;
113
242
  return this.client.call({
114
243
  namespace: this.namespace,
115
244
  method,
116
245
  params,
117
- id
246
+ id,
247
+ timeoutMs: requestOptions.timeoutMs
118
248
  });
119
249
  }
120
250
  };
@@ -122,27 +252,80 @@ async function tryConnect(options = {}) {
122
252
  const host = options.host ?? DEFAULT_HOST;
123
253
  const startPort = options.startPort ?? DEFAULT_START_PORT;
124
254
  const endPort = options.endPort ?? DEFAULT_END_PORT;
255
+ const probeTimeoutMs = options.probeTimeoutMs ?? options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
256
+ const requestTimeoutMs = options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
257
+ let lastProbeError;
125
258
  for (let port = startPort; port <= endPort; port++) {
126
259
  const client = new AdofaiIpcClient({
127
260
  baseUrl: `http://${host}:${port}`,
128
261
  fetch: options.fetch,
129
- timeoutMs: options.timeoutMs
262
+ requestTimeoutMs: probeTimeoutMs
130
263
  });
131
264
  try {
132
265
  const health = await client.health();
133
266
  if (health.ok && health.server === "AdofaiIpc") {
134
- return client;
267
+ const mismatch = getVersionMismatch(health);
268
+ if (mismatch) {
269
+ try {
270
+ options.onVersionMismatch?.(mismatch);
271
+ } catch {
272
+ }
273
+ throw mismatch;
274
+ }
275
+ return new AdofaiIpcClient({
276
+ baseUrl: client.baseUrl,
277
+ fetch: options.fetch,
278
+ requestTimeoutMs
279
+ });
135
280
  }
136
- } catch {
281
+ } catch (error) {
282
+ if (error instanceof IpcVersionMismatchError) throw error;
283
+ lastProbeError = error;
137
284
  }
138
285
  }
286
+ if (lastProbeError instanceof IpcTimeoutError) throw lastProbeError;
139
287
  throw new IpcConnectionError(
140
288
  `Could not connect to AdofaiIpc on ${host}:${startPort}-${endPort}.`
141
289
  );
142
290
  }
291
+ function getVersionMismatch(health) {
292
+ const protocolVersion = Number.isInteger(health.protocolVersion) ? health.protocolVersion : null;
293
+ const serverVersion = typeof health.serverVersion === "string" ? health.serverVersion : null;
294
+ const comparison = serverVersion === null ? null : compareProductVersions(serverVersion, CLIENT_VERSION);
295
+ if (comparison === 0 && serverVersion === CLIENT_VERSION) return null;
296
+ return new IpcVersionMismatchError({
297
+ clientVersion: CLIENT_VERSION,
298
+ serverVersion,
299
+ direction: comparison === null ? "legacy_server" : comparison < 0 ? "server_outdated" : "client_outdated",
300
+ protocolVersion
301
+ });
302
+ }
143
303
  function normalizeBaseUrl(value) {
144
304
  return value.replace(/\/+$/, "");
145
305
  }
306
+ function getErrorMessage(error) {
307
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
308
+ return error.message;
309
+ }
310
+ return void 0;
311
+ }
312
+ function parseIpcResponseError(text) {
313
+ try {
314
+ const value = JSON.parse(text);
315
+ const error = value?.error;
316
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && "message" in error && typeof error.message === "string") {
317
+ return { code: error.code, message: error.message };
318
+ }
319
+ } catch {
320
+ }
321
+ return void 0;
322
+ }
323
+ function isIpcRequestOptions(value) {
324
+ return typeof value === "object" && value !== null;
325
+ }
326
+ function delay(timeoutMs) {
327
+ return new Promise((resolve) => setTimeout(resolve, timeoutMs));
328
+ }
146
329
  function createRequestId() {
147
330
  return `adofai-ipc-${Date.now()}-${Math.random().toString(36).slice(2)}`;
148
331
  }
@@ -150,9 +333,13 @@ export {
150
333
  AdofaiIpcClient,
151
334
  AdofaiIpcError,
152
335
  AdofaiIpcNamespaceClient,
336
+ CLIENT_VERSION,
153
337
  IpcConnectionError,
154
338
  IpcHttpError,
155
339
  IpcResponseError,
340
+ IpcTimeoutError,
341
+ IpcVersionMismatchError,
342
+ isIpcUnavailable,
156
343
  tryConnect
157
344
  };
158
345
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["import type { IpcErrorInfo } from \"./types\";\n\nexport class AdofaiIpcError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AdofaiIpcError\";\n }\n}\n\nexport class IpcConnectionError extends AdofaiIpcError {\n constructor(message = \"Could not connect to AdofaiIpc.\") {\n super(message);\n this.name = \"IpcConnectionError\";\n }\n}\n\nexport class IpcHttpError extends AdofaiIpcError {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = \"IpcHttpError\";\n this.status = status;\n }\n}\n\nexport class IpcResponseError extends AdofaiIpcError {\n readonly code: string;\n readonly error: IpcErrorInfo;\n\n constructor(error: IpcErrorInfo) {\n super(error.message);\n this.name = \"IpcResponseError\";\n this.code = error.code;\n this.error = error;\n }\n}\n","import { IpcConnectionError, IpcHttpError, IpcResponseError } from \"./errors\";\nimport type {\n AdofaiIpcClientOptions,\n IpcCallOptions,\n IpcHealthResponse,\n IpcNamespaceDetail,\n IpcNamespacesResponse,\n IpcResponse,\n TryConnectOptions\n} from \"./types\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_START_PORT = 32145;\nconst DEFAULT_END_PORT = 32155;\nconst DEFAULT_TIMEOUT_MS = 500;\n\nexport class AdofaiIpcClient {\n readonly baseUrl: string;\n\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n\n constructor(options: AdofaiIpcClientOptions = {}) {\n this.baseUrl = normalizeBaseUrl(options.baseUrl ?? `http://${DEFAULT_HOST}:${DEFAULT_START_PORT}`);\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n if (!this.fetchImpl) {\n throw new IpcConnectionError(\"A fetch implementation is required.\");\n }\n }\n\n static async connect(options: TryConnectOptions = {}): Promise<AdofaiIpcClient> {\n return tryConnect(options);\n }\n\n async health(): Promise<IpcHealthResponse> {\n return this.get<IpcHealthResponse>(\"/ipc/health\");\n }\n\n async listNamespaces(): Promise<IpcNamespacesResponse> {\n return this.get<IpcNamespacesResponse>(\"/ipc/namespaces\");\n }\n\n async getNamespace(namespace: string): Promise<IpcNamespaceDetail> {\n return this.get<IpcNamespaceDetail>(`/ipc/namespaces/${encodeURIComponent(namespace)}`);\n }\n\n namespace(namespace: string): AdofaiIpcNamespaceClient {\n return new AdofaiIpcNamespaceClient(this, namespace);\n }\n\n async call<TResult = unknown, TParams = unknown>(\n options: IpcCallOptions<TParams>\n ): Promise<TResult> {\n const response = await this.post<IpcResponse<TResult>>(\"/ipc\", {\n namespace: options.namespace,\n method: options.method,\n params: options.params ?? {},\n id: options.id ?? createRequestId()\n });\n\n if (!response.ok) {\n throw new IpcResponseError(response.error);\n }\n\n return response.result;\n }\n\n private async get<TResult>(path: string): Promise<TResult> {\n return this.request<TResult>(path, {\n method: \"GET\"\n });\n }\n\n private async post<TResult>(path: string, body: unknown): Promise<TResult> {\n return this.request<TResult>(path, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(body)\n });\n }\n\n private async request<TResult>(path: string, init: RequestInit): Promise<TResult> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n signal: controller.signal\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new IpcHttpError(response.status, text || response.statusText);\n }\n\n return (await response.json()) as TResult;\n } catch (error) {\n if (error instanceof IpcHttpError) throw error;\n if (error instanceof Error) throw new IpcConnectionError(error.message);\n throw new IpcConnectionError();\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n\nexport class AdofaiIpcNamespaceClient {\n constructor(\n private readonly client: AdofaiIpcClient,\n readonly namespace: string\n ) {\n }\n\n async call<TResult = unknown, TParams = unknown>(\n method: string,\n params?: TParams,\n id?: string\n ): Promise<TResult> {\n return this.client.call<TResult, TParams>({\n namespace: this.namespace,\n method,\n params,\n id\n });\n }\n}\n\nexport async function tryConnect(options: TryConnectOptions = {}): Promise<AdofaiIpcClient> {\n const host = options.host ?? DEFAULT_HOST;\n const startPort = options.startPort ?? DEFAULT_START_PORT;\n const endPort = options.endPort ?? DEFAULT_END_PORT;\n\n for (let port = startPort; port <= endPort; port++) {\n const client = new AdofaiIpcClient({\n baseUrl: `http://${host}:${port}`,\n fetch: options.fetch,\n timeoutMs: options.timeoutMs\n });\n\n try {\n const health = await client.health();\n\n if (health.ok && health.server === \"AdofaiIpc\") {\n return client;\n }\n } catch {\n }\n }\n\n throw new IpcConnectionError(\n `Could not connect to AdofaiIpc on ${host}:${startPort}-${endPort}.`\n );\n}\n\nfunction normalizeBaseUrl(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\nfunction createRequestId(): string {\n return `adofai-ipc-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/version.ts","../src/client.ts"],"sourcesContent":["import type { IpcErrorInfo } from \"./types\";\n\nexport type IpcConnectionErrorCode = \"UNAVAILABLE\" | \"TIMEOUT\";\nexport type IpcVersionMismatchDirection =\n | \"server_outdated\"\n | \"client_outdated\"\n | \"legacy_server\";\n\nexport interface IpcConnectionErrorOptions {\n code?: IpcConnectionErrorCode;\n cause?: unknown;\n}\n\nexport class AdofaiIpcError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AdofaiIpcError\";\n }\n}\n\nexport class IpcConnectionError extends AdofaiIpcError {\n readonly code: IpcConnectionErrorCode;\n readonly cause?: unknown;\n\n constructor(\n message = \"Could not connect to AdofaiIpc.\",\n options: IpcConnectionErrorOptions = {}\n ) {\n super(message);\n this.name = \"IpcConnectionError\";\n this.code = options.code ?? \"UNAVAILABLE\";\n this.cause = options.cause;\n }\n}\n\nexport class IpcTimeoutError extends IpcConnectionError {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number, options: Pick<IpcConnectionErrorOptions, \"cause\"> = {}) {\n super(`AdofaiIpc request timed out after ${timeoutMs} ms.`, {\n code: \"TIMEOUT\",\n cause: options.cause\n });\n this.name = \"IpcTimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class IpcVersionMismatchError extends AdofaiIpcError {\n readonly code = \"VERSION_MISMATCH\" as const;\n readonly clientVersion: string;\n readonly serverVersion: string | null;\n readonly direction: IpcVersionMismatchDirection;\n readonly protocolVersion: number | null;\n\n constructor(options: {\n clientVersion: string;\n serverVersion: string | null;\n direction: IpcVersionMismatchDirection;\n protocolVersion: number | null;\n }) {\n const server = options.serverVersion ?? \"legacy/unknown\";\n super(`AdofaiIpc version mismatch: client ${options.clientVersion}, server ${server}.`);\n this.name = \"IpcVersionMismatchError\";\n this.clientVersion = options.clientVersion;\n this.serverVersion = options.serverVersion;\n this.direction = options.direction;\n this.protocolVersion = options.protocolVersion;\n }\n}\n\nexport function isIpcUnavailable(error: unknown): error is IpcConnectionError {\n return error instanceof IpcConnectionError && error.code === \"UNAVAILABLE\";\n}\n\nexport class IpcHttpError extends AdofaiIpcError {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = \"IpcHttpError\";\n this.status = status;\n }\n}\n\nexport class IpcResponseError extends AdofaiIpcError {\n readonly code: string;\n readonly error: IpcErrorInfo;\n\n constructor(error: IpcErrorInfo) {\n super(error.message);\n this.name = \"IpcResponseError\";\n this.code = error.code;\n this.error = error;\n }\n}\n","declare const __CLIENT_VERSION__: string;\n\nexport const CLIENT_VERSION = __CLIENT_VERSION__;\n\nconst CANONICAL_SEMVER = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*))?$/;\n\ninterface ParsedVersion {\n core: [number, number, number];\n prerelease: string[] | null;\n}\n\nexport function compareProductVersions(left: string, right: string): number | null {\n const leftVersion = parseProductVersion(left);\n const rightVersion = parseProductVersion(right);\n if (!leftVersion || !rightVersion) return null;\n\n for (let index = 0; index < leftVersion.core.length; index++) {\n const difference = leftVersion.core[index]! - rightVersion.core[index]!;\n if (difference !== 0) return difference < 0 ? -1 : 1;\n }\n\n if (leftVersion.prerelease === null) return rightVersion.prerelease === null ? 0 : 1;\n if (rightVersion.prerelease === null) return -1;\n\n const length = Math.max(leftVersion.prerelease.length, rightVersion.prerelease.length);\n for (let index = 0; index < length; index++) {\n const leftPart = leftVersion.prerelease[index];\n const rightPart = rightVersion.prerelease[index];\n if (leftPart === undefined) return -1;\n if (rightPart === undefined) return 1;\n if (leftPart === rightPart) continue;\n\n const leftNumeric = /^\\d+$/.test(leftPart);\n const rightNumeric = /^\\d+$/.test(rightPart);\n if (leftNumeric && rightNumeric) return Number(leftPart) < Number(rightPart) ? -1 : 1;\n if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;\n return leftPart < rightPart ? -1 : 1;\n }\n\n return 0;\n}\n\nfunction parseProductVersion(value: string): ParsedVersion | null {\n const match = CANONICAL_SEMVER.exec(value);\n if (!match) return null;\n return {\n core: [Number(match[1]), Number(match[2]), Number(match[3])],\n prerelease: match[4] ? match[4].split(\".\") : null\n };\n}\n","import {\n IpcConnectionError,\n IpcHttpError,\n IpcResponseError,\n IpcTimeoutError,\n IpcVersionMismatchError\n} from \"./errors\";\nimport { CLIENT_VERSION, compareProductVersions } from \"./version\";\nimport type {\n AdofaiIpcClientOptions,\n IpcCallOptions,\n IpcErrorInfo,\n IpcHealthResponse,\n IpcNamespaceDetail,\n IpcNamespacesResponse,\n IpcRequestId,\n IpcRequestOptions,\n IpcResponse,\n TryConnectOptions,\n WaitForNamespaceOptions\n} from \"./types\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_START_PORT = 32145;\nconst DEFAULT_END_PORT = 32155;\nconst DEFAULT_PROBE_TIMEOUT_MS = 500;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nconst DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS = 10_000;\nconst DEFAULT_NAMESPACE_POLL_INTERVAL_MS = 100;\n\nexport class AdofaiIpcClient {\n readonly baseUrl: string;\n\n private readonly fetchImpl: typeof fetch;\n private readonly requestTimeoutMs: number;\n\n constructor(options: AdofaiIpcClientOptions = {}) {\n this.baseUrl = normalizeBaseUrl(options.baseUrl ?? `http://${DEFAULT_HOST}:${DEFAULT_START_PORT}`);\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.requestTimeoutMs =\n options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n if (!this.fetchImpl) {\n throw new IpcConnectionError(\"A fetch implementation is required.\");\n }\n }\n\n static async connect(options: TryConnectOptions = {}): Promise<AdofaiIpcClient> {\n return tryConnect(options);\n }\n\n async health(options: IpcRequestOptions = {}): Promise<IpcHealthResponse> {\n return this.get<IpcHealthResponse>(\"/ipc/health\", options);\n }\n\n async listNamespaces(options: IpcRequestOptions = {}): Promise<IpcNamespacesResponse> {\n return this.get<IpcNamespacesResponse>(\"/ipc/namespaces\", options);\n }\n\n async getNamespace(\n namespace: string,\n options: IpcRequestOptions = {}\n ): Promise<IpcNamespaceDetail> {\n return this.get<IpcNamespaceDetail>(\n `/ipc/namespaces/${encodeURIComponent(namespace)}`,\n options\n );\n }\n\n async waitForNamespace(\n namespace: string,\n options: WaitForNamespaceOptions = {}\n ): Promise<IpcNamespaceDetail> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS;\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_NAMESPACE_POLL_INTERVAL_MS;\n const requiredStatus = options.status ?? \"registered\";\n const deadline = Date.now() + timeoutMs;\n let stateError: IpcResponseError | undefined;\n\n while (true) {\n try {\n const detail = await this.getNamespace(namespace, {\n timeoutMs: options.requestTimeoutMs\n });\n\n if (requiredStatus === \"registered\" || detail.status === \"ready\") {\n return detail;\n }\n\n if (detail.status === \"error\") {\n throw new IpcResponseError({\n code: \"namespace_error\",\n message: detail.error?.message ?? `Namespace initialization failed: ${namespace}`\n });\n }\n\n if (detail.status !== \"initializing\") {\n throw new IpcResponseError({\n code: \"namespace_status_unavailable\",\n message: `Namespace status is unavailable: ${namespace}`\n });\n }\n\n stateError = new IpcResponseError({\n code: \"namespace_initializing\",\n message: `Namespace is initializing: ${namespace}`\n });\n } catch (error) {\n if (\n !(error instanceof IpcResponseError) ||\n (error.code !== \"namespace_not_found\" && error.code !== \"namespace_initializing\")\n ) {\n throw error;\n }\n\n stateError = error;\n }\n\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n throw stateError ?? new IpcResponseError({\n code: \"namespace_initializing\",\n message: `Namespace is initializing: ${namespace}`\n });\n }\n\n await delay(Math.min(pollIntervalMs, remainingMs));\n }\n }\n\n namespace(namespace: string): AdofaiIpcNamespaceClient {\n return new AdofaiIpcNamespaceClient(this, namespace);\n }\n\n async call<TResult = unknown, TParams = unknown>(\n options: IpcCallOptions<TParams>\n ): Promise<TResult> {\n const response = await this.post<IpcResponse<TResult>>(\n \"/ipc\",\n {\n namespace: options.namespace,\n method: options.method,\n params: options.params ?? {},\n id: options.id ?? createRequestId()\n },\n { timeoutMs: options.timeoutMs }\n );\n\n if (!response.ok) {\n throw new IpcResponseError(response.error);\n }\n\n return response.result;\n }\n\n private async get<TResult>(\n path: string,\n options: IpcRequestOptions = {}\n ): Promise<TResult> {\n return this.request<TResult>(path, {\n method: \"GET\"\n }, options);\n }\n\n private async post<TResult>(\n path: string,\n body: unknown,\n options: IpcRequestOptions = {}\n ): Promise<TResult> {\n return this.request<TResult>(path, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(body)\n }, options);\n }\n\n private async request<TResult>(\n path: string,\n init: RequestInit,\n options: IpcRequestOptions\n ): Promise<TResult> {\n const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;\n const controller = new AbortController();\n const timeoutError = new IpcTimeoutError(timeoutMs);\n const timeout = setTimeout(() => controller.abort(timeoutError), timeoutMs);\n\n try {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n signal: controller.signal\n });\n\n if (!response.ok) {\n const text = await response.text();\n const responseError = parseIpcResponseError(text);\n if (responseError) throw new IpcResponseError(responseError);\n throw new IpcHttpError(response.status, text || response.statusText);\n }\n\n return (await response.json()) as TResult;\n } catch (error) {\n if (error instanceof IpcHttpError || error instanceof IpcResponseError) throw error;\n if (controller.signal.aborted) {\n if (error === timeoutError) throw timeoutError;\n throw new IpcTimeoutError(timeoutMs, { cause: error });\n }\n throw new IpcConnectionError(getErrorMessage(error), { cause: error });\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n\nexport class AdofaiIpcNamespaceClient {\n constructor(\n private readonly client: AdofaiIpcClient,\n readonly namespace: string\n ) {\n }\n\n async call<TResult = unknown, TParams = unknown>(\n method: string,\n params?: TParams,\n idOrOptions?: IpcRequestId | IpcRequestOptions,\n options: IpcRequestOptions = {}\n ): Promise<TResult> {\n const requestOptions = isIpcRequestOptions(idOrOptions) ? idOrOptions : options;\n const id = isIpcRequestOptions(idOrOptions) ? undefined : idOrOptions;\n\n return this.client.call<TResult, TParams>({\n namespace: this.namespace,\n method,\n params,\n id,\n timeoutMs: requestOptions.timeoutMs\n });\n }\n}\n\nexport async function tryConnect(options: TryConnectOptions = {}): Promise<AdofaiIpcClient> {\n const host = options.host ?? DEFAULT_HOST;\n const startPort = options.startPort ?? DEFAULT_START_PORT;\n const endPort = options.endPort ?? DEFAULT_END_PORT;\n const probeTimeoutMs =\n options.probeTimeoutMs ?? options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;\n const requestTimeoutMs =\n options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n let lastProbeError: unknown;\n\n for (let port = startPort; port <= endPort; port++) {\n const client = new AdofaiIpcClient({\n baseUrl: `http://${host}:${port}`,\n fetch: options.fetch,\n requestTimeoutMs: probeTimeoutMs\n });\n\n try {\n const health = await client.health();\n\n if (health.ok && health.server === \"AdofaiIpc\") {\n const mismatch = getVersionMismatch(health);\n if (mismatch) {\n try {\n options.onVersionMismatch?.(mismatch);\n } catch {\n }\n throw mismatch;\n }\n return new AdofaiIpcClient({\n baseUrl: client.baseUrl,\n fetch: options.fetch,\n requestTimeoutMs\n });\n }\n } catch (error) {\n if (error instanceof IpcVersionMismatchError) throw error;\n lastProbeError = error;\n }\n }\n\n if (lastProbeError instanceof IpcTimeoutError) throw lastProbeError;\n\n throw new IpcConnectionError(\n `Could not connect to AdofaiIpc on ${host}:${startPort}-${endPort}.`\n );\n}\n\nfunction getVersionMismatch(health: IpcHealthResponse): IpcVersionMismatchError | null {\n const protocolVersion = Number.isInteger(health.protocolVersion) ? health.protocolVersion : null;\n const serverVersion = typeof health.serverVersion === \"string\" ? health.serverVersion : null;\n const comparison = serverVersion === null\n ? null\n : compareProductVersions(serverVersion, CLIENT_VERSION);\n\n if (comparison === 0 && serverVersion === CLIENT_VERSION) return null;\n\n return new IpcVersionMismatchError({\n clientVersion: CLIENT_VERSION,\n serverVersion,\n direction: comparison === null\n ? \"legacy_server\"\n : comparison < 0\n ? \"server_outdated\"\n : \"client_outdated\",\n protocolVersion\n });\n}\n\nfunction normalizeBaseUrl(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\nfunction getErrorMessage(error: unknown): string | undefined {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof error.message === \"string\"\n ) {\n return error.message;\n }\n\n return undefined;\n}\n\nfunction parseIpcResponseError(text: string): IpcErrorInfo | undefined {\n try {\n const value = JSON.parse(text) as { error?: unknown };\n const error = value?.error;\n\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n typeof error.code === \"string\" &&\n \"message\" in error &&\n typeof error.message === \"string\"\n ) {\n return { code: error.code, message: error.message };\n }\n } catch {\n }\n\n return undefined;\n}\n\nfunction isIpcRequestOptions(value: unknown): value is IpcRequestOptions {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction delay(timeoutMs: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, timeoutMs));\n}\n\nfunction createRequestId(): string {\n return `adofai-ipc-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adofai-ipc/client",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript client for the AdofaiIpc local HTTP IPC gateway.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -42,12 +42,14 @@
42
42
  "access": "public"
43
43
  },
44
44
  "devDependencies": {
45
+ "@types/node": "^22.10.2",
45
46
  "tsup": "^8.3.5",
46
47
  "typescript": "^5.6.3"
47
48
  },
48
49
  "scripts": {
49
50
  "build": "tsup",
50
51
  "check": "tsc --noEmit",
51
- "clean": "rm -rf dist"
52
+ "clean": "rm -rf dist",
53
+ "test": "pnpm run build && node --test test/*.test.mjs"
52
54
  }
53
55
  }