@kevludwig/lexware-office 0.4.0 → 0.4.1

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
@@ -90,7 +90,7 @@ The ledger needs somewhere to keep one small JSON record per invoice. `JsonStore
90
90
 
91
91
  ## Tests
92
92
 
93
- `npm test` runs 38 tests against a fake API — no account, no network. `test/fake-api.ts` shows the pattern: routes answer by method and path, and every call is recorded.
93
+ `npm test` runs 56 tests against a fake API — no account, no network. `test/fake-api.ts` shows the pattern: routes answer by method and path, and every call is recorded.
94
94
 
95
95
  ## Not affiliated with Lexware
96
96
 
package/dist/client.js CHANGED
@@ -55,6 +55,24 @@ export function query(params) {
55
55
  return rendered ? `?${rendered}` : "";
56
56
  }
57
57
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
58
+ /** Waits `ms`, or rejects with the signal's reason as soon as it aborts. */
59
+ function abortableSleep(ms, signal) {
60
+ if (!signal)
61
+ return sleep(ms).then(() => undefined);
62
+ if (signal.aborted)
63
+ return Promise.reject(signal.reason);
64
+ return new Promise((resolve, reject) => {
65
+ const onAbort = () => {
66
+ clearTimeout(timer);
67
+ reject(signal.reason);
68
+ };
69
+ const timer = setTimeout(() => {
70
+ signal.removeEventListener("abort", onAbort);
71
+ resolve();
72
+ }, ms);
73
+ signal.addEventListener("abort", onAbort, { once: true });
74
+ });
75
+ }
58
76
  /** The server's Retry-After in ms (seconds or an HTTP date), capped at 10 s; null without one. */
59
77
  function retryAfterMs(response) {
60
78
  const header = response.headers.get("retry-after")?.trim();
@@ -109,7 +127,8 @@ export function createLexwareClient(options) {
109
127
  let response = await fire();
110
128
  for (let attempt = 1; response.status === 429 && attempt <= retries; attempt++) {
111
129
  await response.body?.cancel().catch(() => undefined);
112
- await sleep(retryAfterMs(response) ?? backoffMs * attempt);
130
+ // The caller's cancellation also ends the wait between retries.
131
+ await abortableSleep(retryAfterMs(response) ?? backoffMs * attempt, init.signal);
113
132
  response = await fire();
114
133
  }
115
134
  if (!response.ok) {
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Contacts — customers and vendors alike.
3
3
  *
4
- * Search is fuzzy on the API side: "Nordlicht" finds "Nordlicht Consulting
5
- * GmbH", "Müller" may find three. Functions here return candidates and leave
4
+ * Search is fuzzy on the API side: "Beispiel" finds "Beispiel GmbH",
5
+ * "Müller" may find three. Functions here return candidates and leave
6
6
  * the choice to the caller; guessing one of several is the worse mistake.
7
7
  */
8
8
  import { type LexwareClient } from "./client.ts";
package/dist/contacts.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Contacts — customers and vendors alike.
3
3
  *
4
- * Search is fuzzy on the API side: "Nordlicht" finds "Nordlicht Consulting
5
- * GmbH", "Müller" may find three. Functions here return candidates and leave
4
+ * Search is fuzzy on the API side: "Beispiel" finds "Beispiel GmbH",
5
+ * "Müller" may find three. Functions here return candidates and leave
6
6
  * the choice to the caller; guessing one of several is the worse mistake.
7
7
  */
8
8
  import { query } from "./client.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevludwig/lexware-office",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Typed client and domain logic for the Lexware Office Public API: contacts, receivables, payment reminder rules.",
5
5
  "type": "module",
6
6
  "license": "MIT",