@remit/web-client 0.0.283 → 0.0.285

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/web-client",
3
- "version": "0.0.283",
3
+ "version": "0.0.285",
4
4
  "type": "module",
5
5
  "description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
6
6
  "exports": {
@@ -17,7 +17,7 @@ import { QuarantinePanelView } from "./QuarantinePanel";
17
17
  const render = (props: {
18
18
  entries: readonly QuarantineEntry[];
19
19
  isPending: boolean;
20
- error: Error | null;
20
+ error: unknown;
21
21
  }): string =>
22
22
  renderToString(createElement(QuarantinePanelView, props) as never);
23
23
 
@@ -44,7 +44,7 @@ function issueUrlFor(entry: QuarantineEntry): string {
44
44
  );
45
45
  }
46
46
 
47
- function QuarantineReadFailure({ error }: { error: Error }) {
47
+ function QuarantineReadFailure({ error }: { error: unknown }) {
48
48
  return (
49
49
  <section role="alert" className="space-y-3">
50
50
  <h2 className="text-sm font-semibold text-fg">Messages set aside</h2>
@@ -77,7 +77,7 @@ function QuarantineReadFailure({ error }: { error: Error }) {
77
77
  export interface QuarantinePanelViewProps {
78
78
  entries: readonly QuarantineEntry[];
79
79
  isPending: boolean;
80
- error: Error | null;
80
+ error: unknown;
81
81
  }
82
82
 
83
83
  export function QuarantinePanelView({
@@ -25,13 +25,17 @@ type MailboxItems = RemitImapMailboxResponse[];
25
25
  interface MailboxListsState {
26
26
  items: MailboxItems[];
27
27
  isPending: boolean;
28
- error: Error | null;
28
+ // Typed as the generated wire body by TanStack now that the operation
29
+ // declares its failures, but always an `ApiError` or a `NetworkError` by the
30
+ // time it arrives — `client.ts` re-wraps every HTTP failure.
31
+ // `formatErrorMessage` reads a message off any of them.
32
+ error: unknown;
29
33
  }
30
34
 
31
35
  export interface ResultFolderIndexState {
32
36
  index: ResultFolderIndex;
33
37
  isPending: boolean;
34
- error: Error | null;
38
+ error: unknown;
35
39
  }
36
40
 
37
41
  /**
@@ -44,7 +48,7 @@ const combineMailboxLists = (
44
48
  results: {
45
49
  data?: { items: MailboxItems };
46
50
  isPending: boolean;
47
- error: Error | null;
51
+ error: unknown;
48
52
  }[],
49
53
  ): MailboxListsState => ({
50
54
  items: results.map((result) => result.data?.items ?? []),
@@ -78,6 +78,7 @@ export const useRulePreview = (
78
78
  setState({
79
79
  count: response.matchedCount,
80
80
  matchedIds: response.messageIds,
81
+ indexEmpty: response.semanticIndexEmpty === true,
81
82
  previewedSignature: signature,
82
83
  });
83
84
  })
@@ -9,7 +9,13 @@ interface UseTriggerSyncResult {
9
9
  trigger: () => void;
10
10
  triggerAsync: () => Promise<unknown>;
11
11
  isPending: boolean;
12
- error: Error | null;
12
+ // TanStack types this as the generated wire body now that the operation
13
+ // declares its failures; by the time it arrives, `client.ts`'s error
14
+ // interceptor has re-wrapped it as an `ApiError` and `taggedFetch` a
15
+ // transport failure as a `NetworkError`. `unknown` is what the declared type
16
+ // and the real one agree on, and every reader goes through
17
+ // `formatErrorMessage`.
18
+ error: unknown;
13
19
  reset: () => void;
14
20
  }
15
21
 
@@ -276,6 +276,36 @@ describe("derivePreview", () => {
276
276
  );
277
277
  });
278
278
 
279
+ it("carries the empty-index answer through, so a zero can say why (#452)", () => {
280
+ assert.deepEqual(
281
+ derivePreview(
282
+ { count: 0, indexEmpty: true, previewedSignature: signature },
283
+ signature,
284
+ ),
285
+ { status: "ready", count: 0, indexEmpty: true },
286
+ );
287
+ });
288
+
289
+ it("keeps the empty-index answer on a stale count", () => {
290
+ assert.deepEqual(
291
+ derivePreview(
292
+ { count: 0, indexEmpty: true, previewedSignature: "sig-old" },
293
+ signature,
294
+ ),
295
+ { status: "ready", count: 0, stale: true, indexEmpty: true },
296
+ );
297
+ });
298
+
299
+ it("leaves the flag off when the index answered", () => {
300
+ assert.deepEqual(
301
+ derivePreview(
302
+ { count: 0, indexEmpty: false, previewedSignature: signature },
303
+ signature,
304
+ ),
305
+ { status: "ready", count: 0 },
306
+ );
307
+ });
308
+
279
309
  it("surfaces an error raised for the current predicate", () => {
280
310
  assert.deepEqual(
281
311
  derivePreview(
@@ -253,6 +253,8 @@ export interface PreviewState {
253
253
  count?: number;
254
254
  /** The ids that count was counted over, as the server bounded them. */
255
255
  matchedIds?: readonly string[];
256
+ /** The server said the semantic index had nothing to answer with (#452). */
257
+ indexEmpty?: boolean;
256
258
  /** The signature the {@link count} was counted for. */
257
259
  previewedSignature?: string;
258
260
  /** The signature the last error was raised for. */
@@ -274,10 +276,11 @@ export const derivePreview = (
274
276
  return { status: "error", reason: state.error };
275
277
  }
276
278
  if (state.count === undefined) return { status: "loading" };
279
+ const indexEmpty = state.indexEmpty === true ? { indexEmpty: true } : {};
277
280
  if (state.previewedSignature === currentSignature) {
278
- return { status: "ready", count: state.count };
281
+ return { status: "ready", count: state.count, ...indexEmpty };
279
282
  }
280
- return { status: "ready", count: state.count, stale: true };
283
+ return { status: "ready", count: state.count, stale: true, ...indexEmpty };
281
284
  };
282
285
 
283
286
  /** How long a rule change settles before the next preview fires. */