@billkit-eu/sdk 0.5.0 → 0.7.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/src/transport.ts CHANGED
@@ -26,10 +26,15 @@ export type QueryValue = string | number | boolean | null | undefined;
26
26
  export interface RequestOptions {
27
27
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
28
28
  path: string;
29
- // Loosened from ``Record<string, QueryValue>`` so resource methods
30
- // can pass a structurally-typed ``ListParams``-style object without
31
- // a cast, since TS demands an index signature otherwise.
32
- query?: { readonly [key: string]: QueryValue };
29
+ /**
30
+ * Query parameters, as a plain object. Typed this way rather than with
31
+ * an index signature because TypeScript only gives an implicit index
32
+ * signature to type aliases, so a closed `*ListParams` interface would
33
+ * need a cast at every call site. {@link buildUrl} does the pruning:
34
+ * `undefined` and `null` are dropped, an array is joined with commas
35
+ * (the API's `expand=a,b` shape), everything else is stringified.
36
+ */
37
+ query?: object;
33
38
  body?: Record<string, unknown> | undefined;
34
39
  idempotencyKey?: string | undefined;
35
40
  extraHeaders?: Record<string, string>;
@@ -104,10 +109,9 @@ function buildUrl(baseUrl: string, path: string, query: RequestOptions["query"])
104
109
  const normalised = path.startsWith("/") ? path : `/${path}`;
105
110
  const url = new URL(baseUrl.replace(/\/$/, "") + normalised);
106
111
  if (query) {
107
- for (const [k, v] of Object.entries(query)) {
108
- if (v !== null && v !== undefined) {
109
- url.searchParams.set(k, String(v));
110
- }
112
+ for (const [k, v] of Object.entries(query as Record<string, unknown>)) {
113
+ if (v === null || v === undefined) continue;
114
+ url.searchParams.set(k, Array.isArray(v) ? v.join(",") : String(v));
111
115
  }
112
116
  }
113
117
  return url.toString();
@@ -195,13 +199,20 @@ function retryDelayMs(
195
199
  * ``AbortSignal.timeout`` aborts with a ``TimeoutError`` (some runtimes
196
200
  * surface ``AbortError``); we translate that into an explicit, greppable
197
201
  * timeout message instead of the runtime's terse default.
202
+ *
203
+ * The original error is attached as ``cause`` either way. Node's fetch
204
+ * reports every transport failure as the same "fetch failed" message and
205
+ * puts the real reason (``ECONNREFUSED``, ``ENOTFOUND``, a TLS error) in
206
+ * its own cause, so dropping it left the caller with nothing to diagnose.
198
207
  */
199
208
  function connectionError(err: unknown, timeoutMs: number): APIConnectionError {
200
209
  const e = err as { name?: string; message?: string } | undefined;
201
210
  if (e?.name === "TimeoutError" || e?.name === "AbortError") {
202
- return new APIConnectionError(`BillKit request timed out after ${timeoutMs}ms.`);
211
+ return new APIConnectionError(`BillKit request timed out after ${timeoutMs}ms.`, {
212
+ cause: err,
213
+ });
203
214
  }
204
- return new APIConnectionError(e?.message ?? "Network request failed.");
215
+ return new APIConnectionError(e?.message ?? "Network request failed.", { cause: err });
205
216
  }
206
217
 
207
218
  export class Transport {
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.5.0";
1
+ export const VERSION = "0.7.0";