@rindle/remote 0.10.1 → 0.10.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.
@@ -13,7 +13,9 @@ export interface QueuedMutationSenderOptions {
13
13
  /** A policy rejection for one envelope (never retried; the prediction snaps back via the
14
14
  * lmid release — this callback is where the reason reaches the app). */
15
15
  onRejected?: (envelope: MutationEnvelope, reason: string) => void;
16
- /** A failed flush attempt, before its retry (observability). */
16
+ /** A failed flush attempt, before its retry. The ONLY surface for a transport/authority
17
+ * failure: `send` throwing is otherwise absorbed by the retry loop, so a queue built without
18
+ * this hook fails perfectly silently while head-of-line blocking every later mutation. */
17
19
  onError?: (err: unknown, attempt: number) => void;
18
20
  /** Backoff before retry `attempt` (1-based). Default: 200ms · 2^(attempt-1), capped at 5s. */
19
21
  retryDelayMs?: (attempt: number) => number;
@@ -1 +1 @@
1
- {"version":3,"file":"mutation-queue.d.ts","sourceRoot":"","sources":["../src/mutation-queue.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7D,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C;;uFAEmF;IACnF,IAAI,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAAC;IACvE;6EACyE;IACzE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAClE,gEAAgE;IAChE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,8FAA8F;IAC9F,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAaD;yEACyE;AACzE,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,2BAA2B,GAAG,sBAAsB,CA2CpG"}
1
+ {"version":3,"file":"mutation-queue.d.ts","sourceRoot":"","sources":["../src/mutation-queue.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7D,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C;;uFAEmF;IACnF,IAAI,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAAC;IACvE;6EACyE;IACzE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAClE;;+FAE2F;IAC3F,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,8FAA8F;IAC9F,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAwBD;yEACyE;AACzE,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,2BAA2B,GAAG,sBAAsB,CA4CpG"}
@@ -12,6 +12,17 @@
12
12
  // surfaces here through `onRejected` (the wire itself carries no rejection signal).
13
13
  const defaultDelay = (attempt) => Math.min(200 * 2 ** (attempt - 1), 5_000);
14
14
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
15
+ /** Run an app-supplied observer without letting it break the queue. A throw escaping `flush`
16
+ * would abandon the loop with envelopes still queued and nothing scheduled to drain them —
17
+ * the notification hooks must not be able to wedge the write path they report on. */
18
+ const notify = (what, fn) => {
19
+ try {
20
+ fn();
21
+ }
22
+ catch (err) {
23
+ console.error(`[rindle] mutation queue ${what} handler threw:`, err);
24
+ }
25
+ };
15
26
  /** A `MutationEnvelopeSender` (the `pushMutation` option of `RemoteOptimisticSource`) that
16
27
  * queues envelopes and delivers them as confirmed, in-order batches. */
17
28
  export function createQueuedMutationSender(opts) {
@@ -33,7 +44,7 @@ export function createQueuedMutationSender(opts) {
33
44
  break;
34
45
  }
35
46
  catch (err) {
36
- opts.onError?.(err, attempt);
47
+ notify("onError", () => opts.onError?.(err, attempt));
37
48
  await sleep(delay(attempt));
38
49
  }
39
50
  }
@@ -41,7 +52,8 @@ export function createQueuedMutationSender(opts) {
41
52
  batch.forEach((pending, i) => {
42
53
  const outcome = Array.isArray(outcomes) ? outcomes[i] : undefined;
43
54
  if (outcome && !outcome.accepted) {
44
- opts.onRejected?.(pending.envelope, outcome.reason ?? "mutation rejected");
55
+ const reason = outcome.reason ?? "mutation rejected";
56
+ notify("onRejected", () => opts.onRejected?.(pending.envelope, reason));
45
57
  }
46
58
  pending.resolve();
47
59
  });
@@ -1 +1 @@
1
- {"version":3,"file":"mutation-queue.js","sourceRoot":"","sources":["../src/mutation-queue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mutation-queue.js","sourceRoot":"","sources":["../src/mutation-queue.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rindle/remote",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "README.md"
26
26
  ],
27
27
  "dependencies": {
28
- "@rindle/client": "0.10.1"
28
+ "@rindle/client": "0.10.2"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^22.10.0",
@@ -29,7 +29,9 @@ export interface QueuedMutationSenderOptions {
29
29
  /** A policy rejection for one envelope (never retried; the prediction snaps back via the
30
30
  * lmid release — this callback is where the reason reaches the app). */
31
31
  onRejected?: (envelope: MutationEnvelope, reason: string) => void;
32
- /** A failed flush attempt, before its retry (observability). */
32
+ /** A failed flush attempt, before its retry. The ONLY surface for a transport/authority
33
+ * failure: `send` throwing is otherwise absorbed by the retry loop, so a queue built without
34
+ * this hook fails perfectly silently while head-of-line blocking every later mutation. */
33
35
  onError?: (err: unknown, attempt: number) => void;
34
36
  /** Backoff before retry `attempt` (1-based). Default: 200ms · 2^(attempt-1), capped at 5s. */
35
37
  retryDelayMs?: (attempt: number) => number;
@@ -48,6 +50,17 @@ const defaultDelay = (attempt: number): number => Math.min(200 * 2 ** (attempt -
48
50
 
49
51
  const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
50
52
 
53
+ /** Run an app-supplied observer without letting it break the queue. A throw escaping `flush`
54
+ * would abandon the loop with envelopes still queued and nothing scheduled to drain them —
55
+ * the notification hooks must not be able to wedge the write path they report on. */
56
+ const notify = (what: string, fn: () => void): void => {
57
+ try {
58
+ fn();
59
+ } catch (err) {
60
+ console.error(`[rindle] mutation queue ${what} handler threw:`, err);
61
+ }
62
+ };
63
+
51
64
  /** A `MutationEnvelopeSender` (the `pushMutation` option of `RemoteOptimisticSource`) that
52
65
  * queues envelopes and delivers them as confirmed, in-order batches. */
53
66
  export function createQueuedMutationSender(opts: QueuedMutationSenderOptions): MutationEnvelopeSender {
@@ -68,7 +81,7 @@ export function createQueuedMutationSender(opts: QueuedMutationSenderOptions): M
68
81
  outcomes = await opts.send(batch.map((p) => p.envelope));
69
82
  break;
70
83
  } catch (err) {
71
- opts.onError?.(err, attempt);
84
+ notify("onError", () => opts.onError?.(err, attempt));
72
85
  await sleep(delay(attempt));
73
86
  }
74
87
  }
@@ -76,7 +89,8 @@ export function createQueuedMutationSender(opts: QueuedMutationSenderOptions): M
76
89
  batch.forEach((pending, i) => {
77
90
  const outcome = Array.isArray(outcomes) ? outcomes[i] : undefined;
78
91
  if (outcome && !outcome.accepted) {
79
- opts.onRejected?.(pending.envelope, outcome.reason ?? "mutation rejected");
92
+ const reason = outcome.reason ?? "mutation rejected";
93
+ notify("onRejected", () => opts.onRejected?.(pending.envelope, reason));
80
94
  }
81
95
  pending.resolve();
82
96
  });