@adofai-ipc/client 0.1.0 → 0.2.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
@@ -45,7 +45,20 @@ Defaults:
45
45
  - host: `127.0.0.1`
46
46
  - startPort: `32145`
47
47
  - endPort: `32155`
48
- - timeoutMs: `500`
48
+ - probeTimeoutMs: `500`
49
+ - requestTimeoutMs: `10000`
50
+
51
+ `tryConnect` only confirms that the AdofaiIpc listener is running. It does not mean that a target
52
+ namespace is registered or that the owning mod has finished initializing.
53
+
54
+ ```ts
55
+ const client = await tryConnect({
56
+ probeTimeoutMs: 500,
57
+ requestTimeoutMs: 10_000
58
+ });
59
+ ```
60
+
61
+ The legacy `timeoutMs` option remains as a deprecated alias for both values.
49
62
 
50
63
  ### `new AdofaiIpcClient(options?)`
51
64
 
@@ -61,6 +74,14 @@ const client = new AdofaiIpcClient({
61
74
 
62
75
  Calls a namespace method through `POST /ipc`.
63
76
 
77
+ ```ts
78
+ await client.call({
79
+ namespace: "tufhelper2",
80
+ method: "activity.get",
81
+ timeoutMs: 30_000
82
+ });
83
+ ```
84
+
64
85
  ### `client.namespace(name)`
65
86
 
66
87
  Creates a namespace-bound helper.
@@ -77,6 +98,52 @@ Calls `GET /ipc/namespaces`.
77
98
 
78
99
  Calls `GET /ipc/namespaces/{name}`.
79
100
 
101
+ ### `client.waitForNamespace(name, options?)`
102
+
103
+ Polls namespace discovery until the target namespace is registered. Set `status: "ready"` to also
104
+ wait until the namespace owner explicitly marks initialization complete.
105
+
106
+ ```ts
107
+ await client.waitForNamespace("tufhelper2", {
108
+ status: "ready",
109
+ timeoutMs: 15_000,
110
+ pollIntervalMs: 100
111
+ });
112
+ ```
113
+
114
+ AdofaiIpc namespaces have the strict states `initializing`, `ready`, and `error`. A ready wait that
115
+ expires reports `namespace_initializing`; an initialization failure reports `namespace_error`.
116
+
117
+ ## Error handling
118
+
119
+ Connection failures are reported as `IpcConnectionError` with code `UNAVAILABLE`. Request
120
+ timeouts use the more specific `IpcTimeoutError`, which extends `IpcConnectionError` and has code
121
+ `TIMEOUT`. Protocol failures, including `namespace_not_found`, are reported as
122
+ `IpcResponseError`. `isIpcUnavailable` only matches the `UNAVAILABLE` state, not timeouts.
123
+
124
+ ```ts
125
+ import {
126
+ IpcTimeoutError,
127
+ isIpcUnavailable,
128
+ tryConnect
129
+ } from "@adofai-ipc/client";
130
+
131
+ try {
132
+ const client = await tryConnect();
133
+ await client.health();
134
+ } catch (error) {
135
+ if (error instanceof IpcTimeoutError) {
136
+ console.warn(`AdofaiIpc timed out after ${error.timeoutMs} ms.`);
137
+ } else if (isIpcUnavailable(error)) {
138
+ console.warn("AdofaiIpc is unavailable.");
139
+ }
140
+ }
141
+ ```
142
+
143
+ `tryConnect` treats individual probe failures as expected and only throws an `UNAVAILABLE`
144
+ `IpcConnectionError` after every candidate port has failed. A successful probe does not guarantee
145
+ that any specific namespace or mode feature is ready.
146
+
80
147
  ## Notes
81
148
 
82
149
  This package uses the global `fetch` API. Node.js 18 or newer is recommended.
package/dist/index.cjs CHANGED
@@ -26,6 +26,8 @@ __export(index_exports, {
26
26
  IpcConnectionError: () => IpcConnectionError,
27
27
  IpcHttpError: () => IpcHttpError,
28
28
  IpcResponseError: () => IpcResponseError,
29
+ IpcTimeoutError: () => IpcTimeoutError,
30
+ isIpcUnavailable: () => isIpcUnavailable,
29
31
  tryConnect: () => tryConnect
30
32
  });
31
33
  module.exports = __toCommonJS(index_exports);
@@ -38,11 +40,26 @@ var AdofaiIpcError = class extends Error {
38
40
  }
39
41
  };
40
42
  var IpcConnectionError = class extends AdofaiIpcError {
41
- constructor(message = "Could not connect to AdofaiIpc.") {
43
+ constructor(message = "Could not connect to AdofaiIpc.", options = {}) {
42
44
  super(message);
43
45
  this.name = "IpcConnectionError";
46
+ this.code = options.code ?? "UNAVAILABLE";
47
+ this.cause = options.cause;
44
48
  }
45
49
  };
50
+ var IpcTimeoutError = class extends IpcConnectionError {
51
+ constructor(timeoutMs, options = {}) {
52
+ super(`AdofaiIpc request timed out after ${timeoutMs} ms.`, {
53
+ code: "TIMEOUT",
54
+ cause: options.cause
55
+ });
56
+ this.name = "IpcTimeoutError";
57
+ this.timeoutMs = timeoutMs;
58
+ }
59
+ };
60
+ function isIpcUnavailable(error) {
61
+ return error instanceof IpcConnectionError && error.code === "UNAVAILABLE";
62
+ }
46
63
  var IpcHttpError = class extends AdofaiIpcError {
47
64
  constructor(status, message) {
48
65
  super(message);
@@ -63,12 +80,15 @@ var IpcResponseError = class extends AdofaiIpcError {
63
80
  var DEFAULT_HOST = "127.0.0.1";
64
81
  var DEFAULT_START_PORT = 32145;
65
82
  var DEFAULT_END_PORT = 32155;
66
- var DEFAULT_TIMEOUT_MS = 500;
83
+ var DEFAULT_PROBE_TIMEOUT_MS = 500;
84
+ var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
85
+ var DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS = 1e4;
86
+ var DEFAULT_NAMESPACE_POLL_INTERVAL_MS = 100;
67
87
  var AdofaiIpcClient = class {
68
88
  constructor(options = {}) {
69
89
  this.baseUrl = normalizeBaseUrl(options.baseUrl ?? `http://${DEFAULT_HOST}:${DEFAULT_START_PORT}`);
70
90
  this.fetchImpl = options.fetch ?? globalThis.fetch;
71
- this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
91
+ this.requestTimeoutMs = options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
72
92
  if (!this.fetchImpl) {
73
93
  throw new IpcConnectionError("A fetch implementation is required.");
74
94
  }
@@ -76,47 +96,102 @@ var AdofaiIpcClient = class {
76
96
  static async connect(options = {}) {
77
97
  return tryConnect(options);
78
98
  }
79
- async health() {
80
- return this.get("/ipc/health");
99
+ async health(options = {}) {
100
+ return this.get("/ipc/health", options);
101
+ }
102
+ async listNamespaces(options = {}) {
103
+ return this.get("/ipc/namespaces", options);
81
104
  }
82
- async listNamespaces() {
83
- return this.get("/ipc/namespaces");
105
+ async getNamespace(namespace, options = {}) {
106
+ return this.get(
107
+ `/ipc/namespaces/${encodeURIComponent(namespace)}`,
108
+ options
109
+ );
84
110
  }
85
- async getNamespace(namespace) {
86
- return this.get(`/ipc/namespaces/${encodeURIComponent(namespace)}`);
111
+ async waitForNamespace(namespace, options = {}) {
112
+ const timeoutMs = options.timeoutMs ?? DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS;
113
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_NAMESPACE_POLL_INTERVAL_MS;
114
+ const requiredStatus = options.status ?? "registered";
115
+ const deadline = Date.now() + timeoutMs;
116
+ let stateError;
117
+ while (true) {
118
+ try {
119
+ const detail = await this.getNamespace(namespace, {
120
+ timeoutMs: options.requestTimeoutMs
121
+ });
122
+ if (requiredStatus === "registered" || detail.status === "ready") {
123
+ return detail;
124
+ }
125
+ if (detail.status === "error") {
126
+ throw new IpcResponseError({
127
+ code: "namespace_error",
128
+ message: detail.error?.message ?? `Namespace initialization failed: ${namespace}`
129
+ });
130
+ }
131
+ if (detail.status !== "initializing") {
132
+ throw new IpcResponseError({
133
+ code: "namespace_status_unavailable",
134
+ message: `Namespace status is unavailable: ${namespace}`
135
+ });
136
+ }
137
+ stateError = new IpcResponseError({
138
+ code: "namespace_initializing",
139
+ message: `Namespace is initializing: ${namespace}`
140
+ });
141
+ } catch (error) {
142
+ if (!(error instanceof IpcResponseError) || error.code !== "namespace_not_found" && error.code !== "namespace_initializing") {
143
+ throw error;
144
+ }
145
+ stateError = error;
146
+ }
147
+ const remainingMs = deadline - Date.now();
148
+ if (remainingMs <= 0) {
149
+ throw stateError ?? new IpcResponseError({
150
+ code: "namespace_initializing",
151
+ message: `Namespace is initializing: ${namespace}`
152
+ });
153
+ }
154
+ await delay(Math.min(pollIntervalMs, remainingMs));
155
+ }
87
156
  }
88
157
  namespace(namespace) {
89
158
  return new AdofaiIpcNamespaceClient(this, namespace);
90
159
  }
91
160
  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
- });
161
+ const response = await this.post(
162
+ "/ipc",
163
+ {
164
+ namespace: options.namespace,
165
+ method: options.method,
166
+ params: options.params ?? {},
167
+ id: options.id ?? createRequestId()
168
+ },
169
+ { timeoutMs: options.timeoutMs }
170
+ );
98
171
  if (!response.ok) {
99
172
  throw new IpcResponseError(response.error);
100
173
  }
101
174
  return response.result;
102
175
  }
103
- async get(path) {
176
+ async get(path, options = {}) {
104
177
  return this.request(path, {
105
178
  method: "GET"
106
- });
179
+ }, options);
107
180
  }
108
- async post(path, body) {
181
+ async post(path, body, options = {}) {
109
182
  return this.request(path, {
110
183
  method: "POST",
111
184
  headers: {
112
185
  "Content-Type": "application/json"
113
186
  },
114
187
  body: JSON.stringify(body)
115
- });
188
+ }, options);
116
189
  }
117
- async request(path, init) {
190
+ async request(path, init, options) {
191
+ const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
118
192
  const controller = new AbortController();
119
- const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
193
+ const timeoutError = new IpcTimeoutError(timeoutMs);
194
+ const timeout = setTimeout(() => controller.abort(timeoutError), timeoutMs);
120
195
  try {
121
196
  const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
122
197
  ...init,
@@ -124,13 +199,18 @@ var AdofaiIpcClient = class {
124
199
  });
125
200
  if (!response.ok) {
126
201
  const text = await response.text();
202
+ const responseError = parseIpcResponseError(text);
203
+ if (responseError) throw new IpcResponseError(responseError);
127
204
  throw new IpcHttpError(response.status, text || response.statusText);
128
205
  }
129
206
  return await response.json();
130
207
  } catch (error) {
131
- if (error instanceof IpcHttpError) throw error;
132
- if (error instanceof Error) throw new IpcConnectionError(error.message);
133
- throw new IpcConnectionError();
208
+ if (error instanceof IpcHttpError || error instanceof IpcResponseError) throw error;
209
+ if (controller.signal.aborted) {
210
+ if (error === timeoutError) throw timeoutError;
211
+ throw new IpcTimeoutError(timeoutMs, { cause: error });
212
+ }
213
+ throw new IpcConnectionError(getErrorMessage(error), { cause: error });
134
214
  } finally {
135
215
  clearTimeout(timeout);
136
216
  }
@@ -141,12 +221,15 @@ var AdofaiIpcNamespaceClient = class {
141
221
  this.client = client;
142
222
  this.namespace = namespace;
143
223
  }
144
- async call(method, params, id) {
224
+ async call(method, params, idOrOptions, options = {}) {
225
+ const requestOptions = isIpcRequestOptions(idOrOptions) ? idOrOptions : options;
226
+ const id = isIpcRequestOptions(idOrOptions) ? void 0 : idOrOptions;
145
227
  return this.client.call({
146
228
  namespace: this.namespace,
147
229
  method,
148
230
  params,
149
- id
231
+ id,
232
+ timeoutMs: requestOptions.timeoutMs
150
233
  });
151
234
  }
152
235
  };
@@ -154,16 +237,22 @@ async function tryConnect(options = {}) {
154
237
  const host = options.host ?? DEFAULT_HOST;
155
238
  const startPort = options.startPort ?? DEFAULT_START_PORT;
156
239
  const endPort = options.endPort ?? DEFAULT_END_PORT;
240
+ const probeTimeoutMs = options.probeTimeoutMs ?? options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
241
+ const requestTimeoutMs = options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
157
242
  for (let port = startPort; port <= endPort; port++) {
158
243
  const client = new AdofaiIpcClient({
159
244
  baseUrl: `http://${host}:${port}`,
160
245
  fetch: options.fetch,
161
- timeoutMs: options.timeoutMs
246
+ requestTimeoutMs: probeTimeoutMs
162
247
  });
163
248
  try {
164
249
  const health = await client.health();
165
250
  if (health.ok && health.server === "AdofaiIpc") {
166
- return client;
251
+ return new AdofaiIpcClient({
252
+ baseUrl: client.baseUrl,
253
+ fetch: options.fetch,
254
+ requestTimeoutMs
255
+ });
167
256
  }
168
257
  } catch {
169
258
  }
@@ -175,6 +264,29 @@ async function tryConnect(options = {}) {
175
264
  function normalizeBaseUrl(value) {
176
265
  return value.replace(/\/+$/, "");
177
266
  }
267
+ function getErrorMessage(error) {
268
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
269
+ return error.message;
270
+ }
271
+ return void 0;
272
+ }
273
+ function parseIpcResponseError(text) {
274
+ try {
275
+ const value = JSON.parse(text);
276
+ const error = value?.error;
277
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && "message" in error && typeof error.message === "string") {
278
+ return { code: error.code, message: error.message };
279
+ }
280
+ } catch {
281
+ }
282
+ return void 0;
283
+ }
284
+ function isIpcRequestOptions(value) {
285
+ return typeof value === "object" && value !== null;
286
+ }
287
+ function delay(timeoutMs) {
288
+ return new Promise((resolve) => setTimeout(resolve, timeoutMs));
289
+ }
178
290
  function createRequestId() {
179
291
  return `adofai-ipc-${Date.now()}-${Math.random().toString(36).slice(2)}`;
180
292
  }
@@ -186,6 +298,8 @@ function createRequestId() {
186
298
  IpcConnectionError,
187
299
  IpcHttpError,
188
300
  IpcResponseError,
301
+ IpcTimeoutError,
302
+ isIpcUnavailable,
189
303
  tryConnect
190
304
  });
191
305
  //# 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/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 isIpcUnavailable\n} from \"./errors\";\n\nexport type {\n IpcConnectionErrorCode,\n IpcConnectionErrorOptions\n} from \"./errors\";\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\";\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 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","import {\n IpcConnectionError,\n IpcHttpError,\n IpcResponseError,\n IpcTimeoutError\n} from \"./errors\";\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\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 return new AdofaiIpcClient({\n baseUrl: client.baseUrl,\n fetch: options.fetch,\n requestTimeoutMs\n });\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 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
@@ -4,6 +4,16 @@ interface IpcCallOptions<TParams = unknown> {
4
4
  method: string;
5
5
  params?: TParams;
6
6
  id?: IpcRequestId;
7
+ timeoutMs?: number;
8
+ }
9
+ interface IpcRequestOptions {
10
+ timeoutMs?: number;
11
+ }
12
+ interface WaitForNamespaceOptions {
13
+ timeoutMs?: number;
14
+ pollIntervalMs?: number;
15
+ requestTimeoutMs?: number;
16
+ status?: "registered" | "ready";
7
17
  }
8
18
  interface IpcSuccessResponse<TResult = unknown> {
9
19
  ok: true;
@@ -27,10 +37,16 @@ interface IpcHealthResponse {
27
37
  protocolVersion: number;
28
38
  port: number;
29
39
  }
40
+ type IpcNamespaceStatus = "initializing" | "ready" | "error";
41
+ interface IpcNamespaceErrorInfo {
42
+ code: string;
43
+ message: string;
44
+ }
30
45
  interface IpcNamespaceSummary {
31
46
  name: string;
32
47
  displayName: string;
33
48
  version: string;
49
+ status: IpcNamespaceStatus;
34
50
  }
35
51
  interface IpcNamespacesResponse {
36
52
  namespaces: IpcNamespaceSummary[];
@@ -39,11 +55,15 @@ interface IpcNamespaceDetail {
39
55
  namespace: string;
40
56
  displayName: string;
41
57
  version: string;
58
+ status: IpcNamespaceStatus;
59
+ error?: IpcNamespaceErrorInfo | null;
42
60
  methods: string[];
43
61
  }
44
62
  interface AdofaiIpcClientOptions {
45
63
  baseUrl?: string;
46
64
  fetch?: typeof fetch;
65
+ requestTimeoutMs?: number;
66
+ /** @deprecated Use requestTimeoutMs instead. */
47
67
  timeoutMs?: number;
48
68
  }
49
69
  interface TryConnectOptions {
@@ -51,18 +71,22 @@ interface TryConnectOptions {
51
71
  startPort?: number;
52
72
  endPort?: number;
53
73
  fetch?: typeof fetch;
74
+ probeTimeoutMs?: number;
75
+ requestTimeoutMs?: number;
76
+ /** @deprecated Use probeTimeoutMs and requestTimeoutMs instead. */
54
77
  timeoutMs?: number;
55
78
  }
56
79
 
57
80
  declare class AdofaiIpcClient {
58
81
  readonly baseUrl: string;
59
82
  private readonly fetchImpl;
60
- private readonly timeoutMs;
83
+ private readonly requestTimeoutMs;
61
84
  constructor(options?: AdofaiIpcClientOptions);
62
85
  static connect(options?: TryConnectOptions): Promise<AdofaiIpcClient>;
63
- health(): Promise<IpcHealthResponse>;
64
- listNamespaces(): Promise<IpcNamespacesResponse>;
65
- getNamespace(namespace: string): Promise<IpcNamespaceDetail>;
86
+ health(options?: IpcRequestOptions): Promise<IpcHealthResponse>;
87
+ listNamespaces(options?: IpcRequestOptions): Promise<IpcNamespacesResponse>;
88
+ getNamespace(namespace: string, options?: IpcRequestOptions): Promise<IpcNamespaceDetail>;
89
+ waitForNamespace(namespace: string, options?: WaitForNamespaceOptions): Promise<IpcNamespaceDetail>;
66
90
  namespace(namespace: string): AdofaiIpcNamespaceClient;
67
91
  call<TResult = unknown, TParams = unknown>(options: IpcCallOptions<TParams>): Promise<TResult>;
68
92
  private get;
@@ -73,16 +97,28 @@ declare class AdofaiIpcNamespaceClient {
73
97
  private readonly client;
74
98
  readonly namespace: string;
75
99
  constructor(client: AdofaiIpcClient, namespace: string);
76
- call<TResult = unknown, TParams = unknown>(method: string, params?: TParams, id?: string): Promise<TResult>;
100
+ call<TResult = unknown, TParams = unknown>(method: string, params?: TParams, idOrOptions?: IpcRequestId | IpcRequestOptions, options?: IpcRequestOptions): Promise<TResult>;
77
101
  }
78
102
  declare function tryConnect(options?: TryConnectOptions): Promise<AdofaiIpcClient>;
79
103
 
104
+ type IpcConnectionErrorCode = "UNAVAILABLE" | "TIMEOUT";
105
+ interface IpcConnectionErrorOptions {
106
+ code?: IpcConnectionErrorCode;
107
+ cause?: unknown;
108
+ }
80
109
  declare class AdofaiIpcError extends Error {
81
110
  constructor(message: string);
82
111
  }
83
112
  declare class IpcConnectionError extends AdofaiIpcError {
84
- constructor(message?: string);
113
+ readonly code: IpcConnectionErrorCode;
114
+ readonly cause?: unknown;
115
+ constructor(message?: string, options?: IpcConnectionErrorOptions);
116
+ }
117
+ declare class IpcTimeoutError extends IpcConnectionError {
118
+ readonly timeoutMs: number;
119
+ constructor(timeoutMs: number, options?: Pick<IpcConnectionErrorOptions, "cause">);
85
120
  }
121
+ declare function isIpcUnavailable(error: unknown): error is IpcConnectionError;
86
122
  declare class IpcHttpError extends AdofaiIpcError {
87
123
  readonly status: number;
88
124
  constructor(status: number, message: string);
@@ -93,4 +129,4 @@ declare class IpcResponseError extends AdofaiIpcError {
93
129
  constructor(error: IpcErrorInfo);
94
130
  }
95
131
 
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 };
132
+ export { AdofaiIpcClient, type AdofaiIpcClientOptions, AdofaiIpcError, AdofaiIpcNamespaceClient, 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 TryConnectOptions, type WaitForNamespaceOptions, isIpcUnavailable, tryConnect };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,16 @@ interface IpcCallOptions<TParams = unknown> {
4
4
  method: string;
5
5
  params?: TParams;
6
6
  id?: IpcRequestId;
7
+ timeoutMs?: number;
8
+ }
9
+ interface IpcRequestOptions {
10
+ timeoutMs?: number;
11
+ }
12
+ interface WaitForNamespaceOptions {
13
+ timeoutMs?: number;
14
+ pollIntervalMs?: number;
15
+ requestTimeoutMs?: number;
16
+ status?: "registered" | "ready";
7
17
  }
8
18
  interface IpcSuccessResponse<TResult = unknown> {
9
19
  ok: true;
@@ -27,10 +37,16 @@ interface IpcHealthResponse {
27
37
  protocolVersion: number;
28
38
  port: number;
29
39
  }
40
+ type IpcNamespaceStatus = "initializing" | "ready" | "error";
41
+ interface IpcNamespaceErrorInfo {
42
+ code: string;
43
+ message: string;
44
+ }
30
45
  interface IpcNamespaceSummary {
31
46
  name: string;
32
47
  displayName: string;
33
48
  version: string;
49
+ status: IpcNamespaceStatus;
34
50
  }
35
51
  interface IpcNamespacesResponse {
36
52
  namespaces: IpcNamespaceSummary[];
@@ -39,11 +55,15 @@ interface IpcNamespaceDetail {
39
55
  namespace: string;
40
56
  displayName: string;
41
57
  version: string;
58
+ status: IpcNamespaceStatus;
59
+ error?: IpcNamespaceErrorInfo | null;
42
60
  methods: string[];
43
61
  }
44
62
  interface AdofaiIpcClientOptions {
45
63
  baseUrl?: string;
46
64
  fetch?: typeof fetch;
65
+ requestTimeoutMs?: number;
66
+ /** @deprecated Use requestTimeoutMs instead. */
47
67
  timeoutMs?: number;
48
68
  }
49
69
  interface TryConnectOptions {
@@ -51,18 +71,22 @@ interface TryConnectOptions {
51
71
  startPort?: number;
52
72
  endPort?: number;
53
73
  fetch?: typeof fetch;
74
+ probeTimeoutMs?: number;
75
+ requestTimeoutMs?: number;
76
+ /** @deprecated Use probeTimeoutMs and requestTimeoutMs instead. */
54
77
  timeoutMs?: number;
55
78
  }
56
79
 
57
80
  declare class AdofaiIpcClient {
58
81
  readonly baseUrl: string;
59
82
  private readonly fetchImpl;
60
- private readonly timeoutMs;
83
+ private readonly requestTimeoutMs;
61
84
  constructor(options?: AdofaiIpcClientOptions);
62
85
  static connect(options?: TryConnectOptions): Promise<AdofaiIpcClient>;
63
- health(): Promise<IpcHealthResponse>;
64
- listNamespaces(): Promise<IpcNamespacesResponse>;
65
- getNamespace(namespace: string): Promise<IpcNamespaceDetail>;
86
+ health(options?: IpcRequestOptions): Promise<IpcHealthResponse>;
87
+ listNamespaces(options?: IpcRequestOptions): Promise<IpcNamespacesResponse>;
88
+ getNamespace(namespace: string, options?: IpcRequestOptions): Promise<IpcNamespaceDetail>;
89
+ waitForNamespace(namespace: string, options?: WaitForNamespaceOptions): Promise<IpcNamespaceDetail>;
66
90
  namespace(namespace: string): AdofaiIpcNamespaceClient;
67
91
  call<TResult = unknown, TParams = unknown>(options: IpcCallOptions<TParams>): Promise<TResult>;
68
92
  private get;
@@ -73,16 +97,28 @@ declare class AdofaiIpcNamespaceClient {
73
97
  private readonly client;
74
98
  readonly namespace: string;
75
99
  constructor(client: AdofaiIpcClient, namespace: string);
76
- call<TResult = unknown, TParams = unknown>(method: string, params?: TParams, id?: string): Promise<TResult>;
100
+ call<TResult = unknown, TParams = unknown>(method: string, params?: TParams, idOrOptions?: IpcRequestId | IpcRequestOptions, options?: IpcRequestOptions): Promise<TResult>;
77
101
  }
78
102
  declare function tryConnect(options?: TryConnectOptions): Promise<AdofaiIpcClient>;
79
103
 
104
+ type IpcConnectionErrorCode = "UNAVAILABLE" | "TIMEOUT";
105
+ interface IpcConnectionErrorOptions {
106
+ code?: IpcConnectionErrorCode;
107
+ cause?: unknown;
108
+ }
80
109
  declare class AdofaiIpcError extends Error {
81
110
  constructor(message: string);
82
111
  }
83
112
  declare class IpcConnectionError extends AdofaiIpcError {
84
- constructor(message?: string);
113
+ readonly code: IpcConnectionErrorCode;
114
+ readonly cause?: unknown;
115
+ constructor(message?: string, options?: IpcConnectionErrorOptions);
116
+ }
117
+ declare class IpcTimeoutError extends IpcConnectionError {
118
+ readonly timeoutMs: number;
119
+ constructor(timeoutMs: number, options?: Pick<IpcConnectionErrorOptions, "cause">);
85
120
  }
121
+ declare function isIpcUnavailable(error: unknown): error is IpcConnectionError;
86
122
  declare class IpcHttpError extends AdofaiIpcError {
87
123
  readonly status: number;
88
124
  constructor(status: number, message: string);
@@ -93,4 +129,4 @@ declare class IpcResponseError extends AdofaiIpcError {
93
129
  constructor(error: IpcErrorInfo);
94
130
  }
95
131
 
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 };
132
+ export { AdofaiIpcClient, type AdofaiIpcClientOptions, AdofaiIpcError, AdofaiIpcNamespaceClient, 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 TryConnectOptions, type WaitForNamespaceOptions, isIpcUnavailable, tryConnect };
package/dist/index.js CHANGED
@@ -6,11 +6,26 @@ 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
+ function isIpcUnavailable(error) {
27
+ return error instanceof IpcConnectionError && error.code === "UNAVAILABLE";
28
+ }
14
29
  var IpcHttpError = class extends AdofaiIpcError {
15
30
  constructor(status, message) {
16
31
  super(message);
@@ -31,12 +46,15 @@ var IpcResponseError = class extends AdofaiIpcError {
31
46
  var DEFAULT_HOST = "127.0.0.1";
32
47
  var DEFAULT_START_PORT = 32145;
33
48
  var DEFAULT_END_PORT = 32155;
34
- var DEFAULT_TIMEOUT_MS = 500;
49
+ var DEFAULT_PROBE_TIMEOUT_MS = 500;
50
+ var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
51
+ var DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS = 1e4;
52
+ var DEFAULT_NAMESPACE_POLL_INTERVAL_MS = 100;
35
53
  var AdofaiIpcClient = class {
36
54
  constructor(options = {}) {
37
55
  this.baseUrl = normalizeBaseUrl(options.baseUrl ?? `http://${DEFAULT_HOST}:${DEFAULT_START_PORT}`);
38
56
  this.fetchImpl = options.fetch ?? globalThis.fetch;
39
- this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
57
+ this.requestTimeoutMs = options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
40
58
  if (!this.fetchImpl) {
41
59
  throw new IpcConnectionError("A fetch implementation is required.");
42
60
  }
@@ -44,47 +62,102 @@ var AdofaiIpcClient = class {
44
62
  static async connect(options = {}) {
45
63
  return tryConnect(options);
46
64
  }
47
- async health() {
48
- return this.get("/ipc/health");
65
+ async health(options = {}) {
66
+ return this.get("/ipc/health", options);
67
+ }
68
+ async listNamespaces(options = {}) {
69
+ return this.get("/ipc/namespaces", options);
49
70
  }
50
- async listNamespaces() {
51
- return this.get("/ipc/namespaces");
71
+ async getNamespace(namespace, options = {}) {
72
+ return this.get(
73
+ `/ipc/namespaces/${encodeURIComponent(namespace)}`,
74
+ options
75
+ );
52
76
  }
53
- async getNamespace(namespace) {
54
- return this.get(`/ipc/namespaces/${encodeURIComponent(namespace)}`);
77
+ async waitForNamespace(namespace, options = {}) {
78
+ const timeoutMs = options.timeoutMs ?? DEFAULT_NAMESPACE_WAIT_TIMEOUT_MS;
79
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_NAMESPACE_POLL_INTERVAL_MS;
80
+ const requiredStatus = options.status ?? "registered";
81
+ const deadline = Date.now() + timeoutMs;
82
+ let stateError;
83
+ while (true) {
84
+ try {
85
+ const detail = await this.getNamespace(namespace, {
86
+ timeoutMs: options.requestTimeoutMs
87
+ });
88
+ if (requiredStatus === "registered" || detail.status === "ready") {
89
+ return detail;
90
+ }
91
+ if (detail.status === "error") {
92
+ throw new IpcResponseError({
93
+ code: "namespace_error",
94
+ message: detail.error?.message ?? `Namespace initialization failed: ${namespace}`
95
+ });
96
+ }
97
+ if (detail.status !== "initializing") {
98
+ throw new IpcResponseError({
99
+ code: "namespace_status_unavailable",
100
+ message: `Namespace status is unavailable: ${namespace}`
101
+ });
102
+ }
103
+ stateError = new IpcResponseError({
104
+ code: "namespace_initializing",
105
+ message: `Namespace is initializing: ${namespace}`
106
+ });
107
+ } catch (error) {
108
+ if (!(error instanceof IpcResponseError) || error.code !== "namespace_not_found" && error.code !== "namespace_initializing") {
109
+ throw error;
110
+ }
111
+ stateError = error;
112
+ }
113
+ const remainingMs = deadline - Date.now();
114
+ if (remainingMs <= 0) {
115
+ throw stateError ?? new IpcResponseError({
116
+ code: "namespace_initializing",
117
+ message: `Namespace is initializing: ${namespace}`
118
+ });
119
+ }
120
+ await delay(Math.min(pollIntervalMs, remainingMs));
121
+ }
55
122
  }
56
123
  namespace(namespace) {
57
124
  return new AdofaiIpcNamespaceClient(this, namespace);
58
125
  }
59
126
  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
- });
127
+ const response = await this.post(
128
+ "/ipc",
129
+ {
130
+ namespace: options.namespace,
131
+ method: options.method,
132
+ params: options.params ?? {},
133
+ id: options.id ?? createRequestId()
134
+ },
135
+ { timeoutMs: options.timeoutMs }
136
+ );
66
137
  if (!response.ok) {
67
138
  throw new IpcResponseError(response.error);
68
139
  }
69
140
  return response.result;
70
141
  }
71
- async get(path) {
142
+ async get(path, options = {}) {
72
143
  return this.request(path, {
73
144
  method: "GET"
74
- });
145
+ }, options);
75
146
  }
76
- async post(path, body) {
147
+ async post(path, body, options = {}) {
77
148
  return this.request(path, {
78
149
  method: "POST",
79
150
  headers: {
80
151
  "Content-Type": "application/json"
81
152
  },
82
153
  body: JSON.stringify(body)
83
- });
154
+ }, options);
84
155
  }
85
- async request(path, init) {
156
+ async request(path, init, options) {
157
+ const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
86
158
  const controller = new AbortController();
87
- const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
159
+ const timeoutError = new IpcTimeoutError(timeoutMs);
160
+ const timeout = setTimeout(() => controller.abort(timeoutError), timeoutMs);
88
161
  try {
89
162
  const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
90
163
  ...init,
@@ -92,13 +165,18 @@ var AdofaiIpcClient = class {
92
165
  });
93
166
  if (!response.ok) {
94
167
  const text = await response.text();
168
+ const responseError = parseIpcResponseError(text);
169
+ if (responseError) throw new IpcResponseError(responseError);
95
170
  throw new IpcHttpError(response.status, text || response.statusText);
96
171
  }
97
172
  return await response.json();
98
173
  } catch (error) {
99
- if (error instanceof IpcHttpError) throw error;
100
- if (error instanceof Error) throw new IpcConnectionError(error.message);
101
- throw new IpcConnectionError();
174
+ if (error instanceof IpcHttpError || error instanceof IpcResponseError) throw error;
175
+ if (controller.signal.aborted) {
176
+ if (error === timeoutError) throw timeoutError;
177
+ throw new IpcTimeoutError(timeoutMs, { cause: error });
178
+ }
179
+ throw new IpcConnectionError(getErrorMessage(error), { cause: error });
102
180
  } finally {
103
181
  clearTimeout(timeout);
104
182
  }
@@ -109,12 +187,15 @@ var AdofaiIpcNamespaceClient = class {
109
187
  this.client = client;
110
188
  this.namespace = namespace;
111
189
  }
112
- async call(method, params, id) {
190
+ async call(method, params, idOrOptions, options = {}) {
191
+ const requestOptions = isIpcRequestOptions(idOrOptions) ? idOrOptions : options;
192
+ const id = isIpcRequestOptions(idOrOptions) ? void 0 : idOrOptions;
113
193
  return this.client.call({
114
194
  namespace: this.namespace,
115
195
  method,
116
196
  params,
117
- id
197
+ id,
198
+ timeoutMs: requestOptions.timeoutMs
118
199
  });
119
200
  }
120
201
  };
@@ -122,16 +203,22 @@ async function tryConnect(options = {}) {
122
203
  const host = options.host ?? DEFAULT_HOST;
123
204
  const startPort = options.startPort ?? DEFAULT_START_PORT;
124
205
  const endPort = options.endPort ?? DEFAULT_END_PORT;
206
+ const probeTimeoutMs = options.probeTimeoutMs ?? options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
207
+ const requestTimeoutMs = options.requestTimeoutMs ?? options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
125
208
  for (let port = startPort; port <= endPort; port++) {
126
209
  const client = new AdofaiIpcClient({
127
210
  baseUrl: `http://${host}:${port}`,
128
211
  fetch: options.fetch,
129
- timeoutMs: options.timeoutMs
212
+ requestTimeoutMs: probeTimeoutMs
130
213
  });
131
214
  try {
132
215
  const health = await client.health();
133
216
  if (health.ok && health.server === "AdofaiIpc") {
134
- return client;
217
+ return new AdofaiIpcClient({
218
+ baseUrl: client.baseUrl,
219
+ fetch: options.fetch,
220
+ requestTimeoutMs
221
+ });
135
222
  }
136
223
  } catch {
137
224
  }
@@ -143,6 +230,29 @@ async function tryConnect(options = {}) {
143
230
  function normalizeBaseUrl(value) {
144
231
  return value.replace(/\/+$/, "");
145
232
  }
233
+ function getErrorMessage(error) {
234
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
235
+ return error.message;
236
+ }
237
+ return void 0;
238
+ }
239
+ function parseIpcResponseError(text) {
240
+ try {
241
+ const value = JSON.parse(text);
242
+ const error = value?.error;
243
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && "message" in error && typeof error.message === "string") {
244
+ return { code: error.code, message: error.message };
245
+ }
246
+ } catch {
247
+ }
248
+ return void 0;
249
+ }
250
+ function isIpcRequestOptions(value) {
251
+ return typeof value === "object" && value !== null;
252
+ }
253
+ function delay(timeoutMs) {
254
+ return new Promise((resolve) => setTimeout(resolve, timeoutMs));
255
+ }
146
256
  function createRequestId() {
147
257
  return `adofai-ipc-${Date.now()}-${Math.random().toString(36).slice(2)}`;
148
258
  }
@@ -153,6 +263,8 @@ export {
153
263
  IpcConnectionError,
154
264
  IpcHttpError,
155
265
  IpcResponseError,
266
+ IpcTimeoutError,
267
+ isIpcUnavailable,
156
268
  tryConnect
157
269
  };
158
270
  //# 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":";AAEO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,qBAAN,cAAiC,eAAe;AAAA,EACrD,YAAY,UAAU,mCAAmC;AACvD,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,eAAN,cAA2B,eAAe;AAAA,EAG/C,YAAY,QAAgB,SAAiB;AAC3C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAEO,IAAM,mBAAN,cAA+B,eAAe;AAAA,EAInD,YAAY,OAAqB;AAC/B,UAAM,MAAM,OAAO;AACnB,SAAK,OAAO;AACZ,SAAK,OAAO,MAAM;AAClB,SAAK,QAAQ;AAAA,EACf;AACF;;;ACzBA,IAAM,eAAe;AACrB,IAAM,qBAAqB;AAC3B,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAEpB,IAAM,kBAAN,MAAsB;AAAA,EAM3B,YAAY,UAAkC,CAAC,GAAG;AAChD,SAAK,UAAU,iBAAiB,QAAQ,WAAW,UAAU,YAAY,IAAI,kBAAkB,EAAE;AACjG,SAAK,YAAY,QAAQ,SAAS,WAAW;AAC7C,SAAK,YAAY,QAAQ,aAAa;AAEtC,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,mBAAmB,qCAAqC;AAAA,IACpE;AAAA,EACF;AAAA,EAEA,aAAa,QAAQ,UAA6B,CAAC,GAA6B;AAC9E,WAAO,WAAW,OAAO;AAAA,EAC3B;AAAA,EAEA,MAAM,SAAqC;AACzC,WAAO,KAAK,IAAuB,aAAa;AAAA,EAClD;AAAA,EAEA,MAAM,iBAAiD;AACrD,WAAO,KAAK,IAA2B,iBAAiB;AAAA,EAC1D;AAAA,EAEA,MAAM,aAAa,WAAgD;AACjE,WAAO,KAAK,IAAwB,mBAAmB,mBAAmB,SAAS,CAAC,EAAE;AAAA,EACxF;AAAA,EAEA,UAAU,WAA6C;AACrD,WAAO,IAAI,yBAAyB,MAAM,SAAS;AAAA,EACrD;AAAA,EAEA,MAAM,KACJ,SACkB;AAClB,UAAM,WAAW,MAAM,KAAK,KAA2B,QAAQ;AAAA,MAC7D,WAAW,QAAQ;AAAA,MACnB,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ,UAAU,CAAC;AAAA,MAC3B,IAAI,QAAQ,MAAM,gBAAgB;AAAA,IACpC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,iBAAiB,SAAS,KAAK;AAAA,IAC3C;AAEA,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAc,IAAa,MAAgC;AACzD,WAAO,KAAK,QAAiB,MAAM;AAAA,MACjC,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,KAAc,MAAc,MAAiC;AACzE,WAAO,KAAK,QAAiB,MAAM;AAAA,MACjC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,QAAiB,MAAc,MAAqC;AAChF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AAEnE,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,QAC9D,GAAG;AAAA,QACH,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,cAAM,IAAI,aAAa,SAAS,QAAQ,QAAQ,SAAS,UAAU;AAAA,MACrE;AAEA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,SAAS,OAAO;AACd,UAAI,iBAAiB,aAAc,OAAM;AACzC,UAAI,iBAAiB,MAAO,OAAM,IAAI,mBAAmB,MAAM,OAAO;AACtE,YAAM,IAAI,mBAAmB;AAAA,IAC/B,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AACF;AAEO,IAAM,2BAAN,MAA+B;AAAA,EACpC,YACmB,QACR,WACT;AAFiB;AACR;AAAA,EAEX;AAAA,EAEA,MAAM,KACJ,QACA,QACA,IACkB;AAClB,WAAO,KAAK,OAAO,KAAuB;AAAA,MACxC,WAAW,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,WAAW,UAA6B,CAAC,GAA6B;AAC1F,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,QAAQ,WAAW;AAEnC,WAAS,OAAO,WAAW,QAAQ,SAAS,QAAQ;AAClD,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,SAAS,UAAU,IAAI,IAAI,IAAI;AAAA,MAC/B,OAAO,QAAQ;AAAA,MACf,WAAW,QAAQ;AAAA,IACrB,CAAC;AAED,QAAI;AACF,YAAM,SAAS,MAAM,OAAO,OAAO;AAEnC,UAAI,OAAO,MAAM,OAAO,WAAW,aAAa;AAC9C,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IACR;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,qCAAqC,IAAI,IAAI,SAAS,IAAI,OAAO;AAAA,EACnE;AACF;AAEA,SAAS,iBAAiB,OAAuB;AAC/C,SAAO,MAAM,QAAQ,QAAQ,EAAE;AACjC;AAEA,SAAS,kBAA0B;AACjC,SAAO,cAAc,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACxE;","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["import type { IpcErrorInfo } from \"./types\";\n\nexport type IpcConnectionErrorCode = \"UNAVAILABLE\" | \"TIMEOUT\";\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 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","import {\n IpcConnectionError,\n IpcHttpError,\n IpcResponseError,\n IpcTimeoutError\n} from \"./errors\";\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\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 return new AdofaiIpcClient({\n baseUrl: client.baseUrl,\n fetch: options.fetch,\n requestTimeoutMs\n });\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 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.2.0",
4
4
  "description": "TypeScript client for the AdofaiIpc local HTTP IPC gateway.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,6 +35,13 @@
35
35
  "files": [
36
36
  "dist"
37
37
  ],
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "check": "tsc --noEmit",
41
+ "clean": "rm -rf dist",
42
+ "test": "pnpm run build && node --test test/*.test.mjs",
43
+ "prepack": "pnpm run build"
44
+ },
38
45
  "engines": {
39
46
  "node": ">=18"
40
47
  },
@@ -44,10 +51,5 @@
44
51
  "devDependencies": {
45
52
  "tsup": "^8.3.5",
46
53
  "typescript": "^5.6.3"
47
- },
48
- "scripts": {
49
- "build": "tsup",
50
- "check": "tsc --noEmit",
51
- "clean": "rm -rf dist"
52
54
  }
53
- }
55
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 KGH1113
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.