@go-avro/avro-js 0.0.47 → 0.0.49

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.
@@ -9,10 +9,9 @@ import { CacheData } from '../types/cache';
9
9
  import { Waiver } from '../types/api/Waiver';
10
10
  import type { EmailSucceededPayload, EmailFailedPayload, EmailType } from '../types/api/EmailNotification';
11
11
  import type { BulkDeleteBillsResponse, BulkEmailBillsResponse } from '../client/hooks/bills';
12
- /** Callbacks for a tracked email request. */
13
12
  export interface TrackEmailOptions {
14
13
  emailType?: EmailType;
15
- /** How long to wait before firing onTimeout (ms). Default 30 000. */
14
+ requestId?: string;
16
15
  timeout?: number;
17
16
  onSuccess?: (data: EmailSucceededPayload) => void;
18
17
  onFailure?: (data: EmailFailedPayload) => void;
@@ -750,20 +749,7 @@ export declare class AvroQueryClient {
750
749
  query?: string;
751
750
  offset?: number;
752
751
  }, cancelToken?: CancelToken, headers?: Record<string, string>): Promise<any>;
753
- /**
754
- * Lazily register socket listeners for email_succeeded / email_failed.
755
- * Listeners live on the socket (not in a React effect) so they survive
756
- * component unmounts.
757
- */
758
752
  private _initEmailListeners;
759
- /**
760
- * Track an outbound email request.
761
- *
762
- * Generates a `request_id`, registers socket listeners (once), and returns
763
- * the ID so callers can pass it in the HTTP body. The backend emits
764
- * `email_succeeded` / `email_failed` to the user's GUID room; this method
765
- * correlates the event by `request_id` and fires the appropriate callback.
766
- */
767
753
  trackEmail(options?: TrackEmailOptions): string;
768
754
  sendEmail(emailId: string, formData: FormData, progressUpdateCallback?: (loaded: number, total: number) => void): Promise<void>;
769
755
  sendBillEmail(billId: string, body?: {
@@ -1100,12 +1100,6 @@ export class AvroQueryClient {
1100
1100
  throw new StandardError(500, 'Failed to fetch sessions');
1101
1101
  });
1102
1102
  }
1103
- /* ── Email delivery tracking ──────────────────────────────────────── */
1104
- /**
1105
- * Lazily register socket listeners for email_succeeded / email_failed.
1106
- * Listeners live on the socket (not in a React effect) so they survive
1107
- * component unmounts.
1108
- */
1109
1103
  _initEmailListeners() {
1110
1104
  if (this._emailListenersInit)
1111
1105
  return;
@@ -1114,7 +1108,6 @@ export class AvroQueryClient {
1114
1108
  const entry = this._emailTracking.get(data.request_id);
1115
1109
  if (!entry)
1116
1110
  return;
1117
- // Reset timeout — more recipients may follow for the same request_id
1118
1111
  clearTimeout(entry.timerId);
1119
1112
  entry.timerId = setTimeout(() => {
1120
1113
  this._emailTracking.delete(data.request_id);
@@ -1132,18 +1125,14 @@ export class AvroQueryClient {
1132
1125
  entry.onFailure?.(data);
1133
1126
  });
1134
1127
  }
1135
- /**
1136
- * Track an outbound email request.
1137
- *
1138
- * Generates a `request_id`, registers socket listeners (once), and returns
1139
- * the ID so callers can pass it in the HTTP body. The backend emits
1140
- * `email_succeeded` / `email_failed` to the user's GUID room; this method
1141
- * correlates the event by `request_id` and fires the appropriate callback.
1142
- */
1143
1128
  trackEmail(options = {}) {
1144
1129
  this._initEmailListeners();
1145
- const requestId = uuidv4();
1130
+ const requestId = options.requestId ?? uuidv4();
1146
1131
  const { timeout = 30000 } = options;
1132
+ const existing = this._emailTracking.get(requestId);
1133
+ if (existing) {
1134
+ clearTimeout(existing.timerId);
1135
+ }
1147
1136
  const timerId = setTimeout(() => {
1148
1137
  this._emailTracking.delete(requestId);
1149
1138
  options.onTimeout?.(requestId);
@@ -5,45 +5,20 @@ export interface EmailResult {
5
5
  status: EmailResultStatus;
6
6
  emailType?: EmailType;
7
7
  recipient?: string;
8
- /**
9
- * Bill GUID when the event came from a bill email — single or bulk.
10
- * Use this to correlate per-bill outcomes when one `requestId`
11
- * covers many bills (`POST /company/<id>/bills/bulk`).
12
- */
13
8
  billGuid?: string;
14
9
  error?: EmailFailedPayload['error'];
15
10
  }
16
11
  export interface UseEmailStatusOptions {
17
- /** How long to wait for a socket event before firing `onTimeout` (ms). Default 30 000. */
18
12
  timeout?: number;
19
- /** Called when the backend reports success. */
20
13
  onSuccess?: (result: EmailResult) => void;
21
- /** Called when the backend reports failure. */
22
14
  onFailure?: (result: EmailResult) => void;
23
- /** Called when neither success nor failure arrives within the timeout window. */
24
15
  onTimeout?: (requestId: string) => void;
25
16
  }
26
- /**
27
- * Subscribe to backend email delivery notifications via Socket.IO.
28
- *
29
- * Delegates to `AvroQueryClient.trackEmail()` which registers socket
30
- * listeners on the client itself (not in a React effect), so
31
- * notifications survive component unmounts.
32
- *
33
- * Usage:
34
- * ```ts
35
- * const { trackEmail, pending, results } = useEmailStatus({
36
- * onSuccess: (r) => toast.success(`Email sent to ${r.recipient}`),
37
- * onFailure: (r) => toast.error(`Email failed: ${r.error?.message}`),
38
- * onTimeout: (id) => toast.warn("Email status unknown"),
39
- * });
40
- *
41
- * const requestId = trackEmail("bill");
42
- * await avroQueryClient.sendBillingEmail({ billId, request_id: requestId });
43
- * ```
44
- */
45
17
  export declare function useEmailStatus(options?: UseEmailStatusOptions): {
46
- readonly trackEmail: (emailType?: EmailType) => string;
18
+ readonly trackEmail: (emailType?: EmailType, opts?: {
19
+ requestId?: string;
20
+ timeout?: number;
21
+ }) => string;
47
22
  readonly pending: Map<string, {
48
23
  emailType?: EmailType;
49
24
  trackedAt: number;
@@ -1,45 +1,21 @@
1
1
  import { useCallback, useRef, useState } from 'react';
2
2
  import { useAvroQueryClient } from '../../client/AvroQueryClientProvider';
3
- /* ──────────────────────────────────────────────────────────────────────── */
4
- /* Hook */
5
- /* ──────────────────────────────────────────────────────────────────────── */
6
- /**
7
- * Subscribe to backend email delivery notifications via Socket.IO.
8
- *
9
- * Delegates to `AvroQueryClient.trackEmail()` which registers socket
10
- * listeners on the client itself (not in a React effect), so
11
- * notifications survive component unmounts.
12
- *
13
- * Usage:
14
- * ```ts
15
- * const { trackEmail, pending, results } = useEmailStatus({
16
- * onSuccess: (r) => toast.success(`Email sent to ${r.recipient}`),
17
- * onFailure: (r) => toast.error(`Email failed: ${r.error?.message}`),
18
- * onTimeout: (id) => toast.warn("Email status unknown"),
19
- * });
20
- *
21
- * const requestId = trackEmail("bill");
22
- * await avroQueryClient.sendBillingEmail({ billId, request_id: requestId });
23
- * ```
24
- */
25
3
  export function useEmailStatus(options = {}) {
26
4
  const { timeout = 30000 } = options;
27
5
  const client = useAvroQueryClient();
28
- // Use refs for callbacks so the client-level handlers always see the latest
29
6
  const onSuccessRef = useRef(options.onSuccess);
30
7
  const onFailureRef = useRef(options.onFailure);
31
8
  const onTimeoutRef = useRef(options.onTimeout);
32
9
  onSuccessRef.current = options.onSuccess;
33
10
  onFailureRef.current = options.onFailure;
34
11
  onTimeoutRef.current = options.onTimeout;
35
- // Expose reactive state so the UI can render pending / completed
36
12
  const [pending, setPending] = useState(new Map());
37
13
  const [results, setResults] = useState(new Map());
38
- /* ── trackEmail delegates to client.trackEmail() ──────────────── */
39
- const trackEmail = useCallback((emailType) => {
14
+ const trackEmail = useCallback((emailType, opts = {}) => {
40
15
  const requestId = client.trackEmail({
41
16
  emailType,
42
- timeout,
17
+ requestId: opts.requestId,
18
+ timeout: opts.timeout ?? timeout,
43
19
  onSuccess: (data) => {
44
20
  const result = {
45
21
  requestId: data.request_id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-avro/avro-js",
3
- "version": "0.0.47",
3
+ "version": "0.0.49",
4
4
  "description": "JS client for Avro backend integration.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",