lh-api-client 0.0.1 → 0.0.2

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/dist/client.d.ts CHANGED
@@ -22,4 +22,5 @@ export declare class Client {
22
22
  private readonly opts;
23
23
  constructor(opts: ClientOptions);
24
24
  request<T>(path: string, options?: RequestOptions): Promise<T>;
25
+ private handleResponse;
25
26
  }
package/dist/client.js CHANGED
@@ -35,11 +35,37 @@ class Client {
35
35
  headers.authorization = `Bearer ${this.opts.token}`;
36
36
  if (options.headers)
37
37
  Object.assign(headers, options.headers);
38
- const res = await fetch(url, {
39
- method: options.method ?? 'GET',
40
- headers,
41
- body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
42
- });
38
+ const method = options.method ?? 'GET';
39
+ const isIdempotentGet = method === 'GET';
40
+ // #616 — undici (Node fetch) 偶发 DNS/keep-alive 断 against launchhill.com
41
+ // (curl 同网络稳定 client 侧缺 timeout/retry)。GET 幂等 → 网络层错误重试
42
+ // 2 次(指数退避 200ms/600ms)。POST/PUT/PATCH/DELETE 不自动重试:
43
+ // pricebook publish version-unique 重试会 409;非幂等写不能自动重放。HTTP
44
+ // 4xx/5xx 不重试(那是服务端逻辑,重试无意义,由 caller 处理 ApiError)。
45
+ const MAX_RETRIES = isIdempotentGet ? 2 : 0;
46
+ let attempt = 0;
47
+ // eslint-disable-next-line no-constant-condition
48
+ while (true) {
49
+ try {
50
+ const res = await fetch(url, {
51
+ method,
52
+ headers,
53
+ body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
54
+ });
55
+ return await this.handleResponse(res, method, url);
56
+ }
57
+ catch (err) {
58
+ if (attempt < MAX_RETRIES && isTransientNetworkError(err)) {
59
+ const backoff = 200 * (attempt === 0 ? 1 : 3);
60
+ await sleep(backoff);
61
+ attempt++;
62
+ continue;
63
+ }
64
+ throw err;
65
+ }
66
+ }
67
+ }
68
+ async handleResponse(res, method, url) {
43
69
  const text = await res.text();
44
70
  let parsed = text;
45
71
  if (text.length > 0) {
@@ -51,7 +77,7 @@ class Client {
51
77
  }
52
78
  }
53
79
  if (!res.ok) {
54
- throw new ApiError(res.status, parsed, `HTTP ${res.status} ${res.statusText} from ${options.method ?? 'GET'} ${url.pathname}`);
80
+ throw new ApiError(res.status, parsed, `HTTP ${res.status} ${res.statusText} from ${method} ${url.pathname}`);
55
81
  }
56
82
  // LH backend wraps successful responses in a TransformInterceptor envelope:
57
83
  // `{ data: <actual payload>, timestamp, ... }`. Unwrap for caller convenience.
@@ -65,4 +91,30 @@ class Client {
65
91
  }
66
92
  }
67
93
  exports.Client = Client;
94
+ // #616 — transient = network-layer (fetch rejected before a response), NOT
95
+ // HTTP errors. undici throws these as Error with .code like ECONNRESET,
96
+ // ENOTFOUND, EAI_AGAIN, UND_ERR_SOCKET, or a bare "fetch failed" wrapping
97
+ // a cause. Non-transient (a real HTTP 5xx) reaches handleResponse → ApiError.
98
+ function isTransientNetworkError(err) {
99
+ if (!(err instanceof Error))
100
+ return false;
101
+ const code = err.code;
102
+ if (code === 'ECONNRESET' ||
103
+ code === 'ENOTFOUND' ||
104
+ code === 'EAI_AGAIN' ||
105
+ code === 'ETIMEDOUT' ||
106
+ code === 'UND_ERR_SOCKET' ||
107
+ code === 'ECONNREFUSED') {
108
+ return true;
109
+ }
110
+ // undici often throws a generic "fetch failed" wrapping a cause; treat a
111
+ // fetch-rejected error (no HTTP response) with a network-y cause as transient.
112
+ const cause = err.cause;
113
+ if (cause instanceof Error)
114
+ return isTransientNetworkError(cause);
115
+ return false;
116
+ }
117
+ function sleep(ms) {
118
+ return new Promise((resolve) => setTimeout(resolve, ms));
119
+ }
68
120
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;;;AAmB9E,MAAa,QAAS,SAAQ,KAAK;IAEf;IACA;IAFlB,YACkB,MAAc,EACd,IAAa,EAC7B,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAS;QAI7B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AATD,4BASC;AAED,MAAa,MAAM;IACY;IAA7B,YAA6B,IAAmB;QAAnB,SAAI,GAAJ,IAAI,CAAe;IAAG,CAAC;IAEpD,KAAK,CAAC,OAAO,CAAI,IAAY,EAAE,UAA0B,EAAE;QACzD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,KAAK,SAAS;oBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;SACnC,CAAC;QACF,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,aAAa,GAAG,UAAU,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACzE,IAAI,OAAO,CAAC,OAAO;YAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAE7D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;YAC/B,OAAO;YACP,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SAC5E,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,MAAM,GAAY,IAAI,CAAC;QAC3B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,gBAAgB;YAClB,CAAC;QACH,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,QAAQ,CAChB,GAAG,CAAC,MAAM,EACV,MAAM,EACN,QAAQ,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,SAAS,OAAO,CAAC,MAAM,IAAI,KAAK,IAAI,GAAG,CAAC,QAAQ,EAAE,CACvF,CAAC;QACJ,CAAC;QAED,4EAA4E;QAC5E,+EAA+E;QAC/E,IACE,MAAM;YACN,OAAO,MAAM,KAAK,QAAQ;YAC1B,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YACtB,MAAM,IAAK,MAAkC,EAC7C,CAAC;YACD,OAAQ,MAAsB,CAAC,IAAI,CAAC;QACtC,CAAC;QACD,OAAO,MAAW,CAAC;IACrB,CAAC;CACF;AArDD,wBAqDC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lh-api-client",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "private": false,
5
5
  "description": "Shared HTTP client + types + pure-logic helpers for talking to LH Enterprise backend. Consumed by lh-ops (CLI) and lh-ops-skill (MCP).",
6
6
  "main": "./dist/index.js",