@go-avro/avro-js 0.0.37 → 0.0.38

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.
Files changed (59) hide show
  1. package/dist/auth/AuthManager.d.ts +2 -2
  2. package/dist/auth/AuthManager.js +37 -32
  3. package/dist/auth/storage.d.ts +1 -1
  4. package/dist/auth/storage.js +6 -6
  5. package/dist/client/AvroQueryClientProvider.js +1 -1
  6. package/dist/client/QueryClient.d.ts +35 -17
  7. package/dist/client/QueryClient.js +486 -389
  8. package/dist/client/core/fetch.js +16 -11
  9. package/dist/client/core/utils.js +5 -5
  10. package/dist/client/core/xhr.js +28 -23
  11. package/dist/client/hooks/analytics.js +14 -14
  12. package/dist/client/hooks/avro.js +2 -2
  13. package/dist/client/hooks/bills.js +66 -30
  14. package/dist/client/hooks/catalog_items.js +57 -22
  15. package/dist/client/hooks/chats.js +4 -4
  16. package/dist/client/hooks/companies.js +96 -39
  17. package/dist/client/hooks/email.js +1 -1
  18. package/dist/client/hooks/events.js +174 -63
  19. package/dist/client/hooks/groups.js +37 -22
  20. package/dist/client/hooks/jobs.js +69 -18
  21. package/dist/client/hooks/labels.js +36 -21
  22. package/dist/client/hooks/messages.js +9 -6
  23. package/dist/client/hooks/months.js +42 -22
  24. package/dist/client/hooks/plans.js +2 -2
  25. package/dist/client/hooks/prepayments.js +42 -22
  26. package/dist/client/hooks/proposal.js +21 -5
  27. package/dist/client/hooks/root.js +4 -4
  28. package/dist/client/hooks/routes.js +77 -32
  29. package/dist/client/hooks/sessions.js +66 -34
  30. package/dist/client/hooks/skills.js +33 -18
  31. package/dist/client/hooks/teams.js +36 -21
  32. package/dist/client/hooks/timecards.js +6 -0
  33. package/dist/client/hooks/users.js +61 -29
  34. package/dist/client/hooks/waivers.js +41 -19
  35. package/dist/index.d.ts +38 -38
  36. package/dist/index.js +37 -37
  37. package/dist/types/api/Bill.d.ts +1 -1
  38. package/dist/types/api/Bill.js +1 -1
  39. package/dist/types/api/Job.d.ts +1 -1
  40. package/dist/types/api/Job.js +14 -14
  41. package/dist/types/api/LineItem.d.ts +3 -3
  42. package/dist/types/api/LineItem.js +5 -2
  43. package/dist/types/api/PaymentType.d.ts +1 -1
  44. package/dist/types/api/Prepayment.d.ts +1 -1
  45. package/dist/types/api/Route.d.ts +3 -3
  46. package/dist/types/api/Route.js +4 -2
  47. package/dist/types/api/RouteJob.d.ts +1 -1
  48. package/dist/types/api/Task.d.ts +2 -2
  49. package/dist/types/api/Task.js +12 -7
  50. package/dist/types/api/Timecard.d.ts +1 -1
  51. package/dist/types/api/TimecardAction.d.ts +1 -1
  52. package/dist/types/api/UserCompanyAssociation.d.ts +2 -2
  53. package/dist/types/api/UserCompanyAssociation.js +1 -1
  54. package/dist/types/api/_Event.d.ts +1 -1
  55. package/dist/types/api/_Event.js +1 -1
  56. package/dist/types/api.d.ts +1 -1
  57. package/dist/types/auth.d.ts +1 -1
  58. package/dist/types/client.d.ts +1 -1
  59. package/package.json +3 -2
@@ -1,11 +1,11 @@
1
- import io from 'socket.io-client';
2
- import { useMutation, useQueryClient } from '@tanstack/react-query';
1
+ import io from "socket.io-client";
2
+ import { useMutation, useQueryClient, } from "@tanstack/react-query";
3
3
  import { v4 as uuidv4 } from "uuid";
4
- import { Job, LoginResponse } from '../types/api';
5
- import { AuthState } from '../types/auth';
6
- import { StandardError } from '../types/error';
4
+ import { Job, LoginResponse, } from "../types/api";
5
+ import { AuthState } from "../types/auth";
6
+ import { StandardError } from "../types/error";
7
7
  function isBulkEvent(c) {
8
- return 'invalidateKeys' in c;
8
+ return "invalidateKeys" in c;
9
9
  }
10
10
  /**
11
11
  * Maps socket event names to cache-update strategies.
@@ -18,75 +18,185 @@ function isBulkEvent(c) {
18
18
  */
19
19
  const SOCKET_EVENT_CONFIG = {
20
20
  // ── Company ──
21
- create_company: { entityKey: 'companies', action: 'create', fetchPath: (id) => `/company/${id}` },
22
- update_company: { entityKey: 'companies', action: 'update', fetchPath: (id) => `/company/${id}` },
23
- delete_company: { entityKey: 'companies', action: 'delete', fetchPath: null },
21
+ create_company: {
22
+ entityKey: "companies",
23
+ action: "create",
24
+ fetchPath: (id) => `/company/${id}`,
25
+ },
26
+ update_company: {
27
+ entityKey: "companies",
28
+ action: "update",
29
+ fetchPath: (id) => `/company/${id}`,
30
+ },
31
+ delete_company: { entityKey: "companies", action: "delete", fetchPath: null },
24
32
  // ── Users (no single-entity socket events) ──
25
- user_updated: { invalidateKeys: [['users'], ['user']] },
26
- update_users: { invalidateKeys: [['users'], ['user']] },
33
+ user_updated: { invalidateKeys: [["users"], ["user"]] },
34
+ update_users: { invalidateKeys: [["users"], ["user"]] },
27
35
  // ── Jobs ──
28
- create_job: { entityKey: 'jobs', action: 'create', fetchPath: (id) => `/job/${id}`, construct: (d) => new Job(d) },
29
- update_job: { entityKey: 'jobs', action: 'update', fetchPath: (id) => `/job/${id}`, construct: (d) => new Job(d) },
30
- delete_job: { entityKey: 'jobs', action: 'delete', fetchPath: null },
31
- update_jobs: { invalidateKeys: [['jobs'], ['infinite', 'jobs']] },
32
- delete_jobs: { invalidateKeys: [['jobs'], ['infinite', 'jobs']] },
36
+ create_job: {
37
+ entityKey: "jobs",
38
+ action: "create",
39
+ fetchPath: (id) => `/job/${id}`,
40
+ construct: (d) => new Job(d),
41
+ },
42
+ update_job: {
43
+ entityKey: "jobs",
44
+ action: "update",
45
+ fetchPath: (id) => `/job/${id}`,
46
+ construct: (d) => new Job(d),
47
+ },
48
+ delete_job: { entityKey: "jobs", action: "delete", fetchPath: null },
49
+ update_jobs: { invalidateKeys: [["jobs"], ["infinite", "jobs"]] },
50
+ delete_jobs: { invalidateKeys: [["jobs"], ["infinite", "jobs"]] },
33
51
  // ── Routes ──
34
- create_route: { entityKey: 'routes', action: 'create', fetchPath: (id) => `/route/${id}` },
35
- update_route: { entityKey: 'routes', action: 'update', fetchPath: (id) => `/route/${id}` },
36
- delete_route: { entityKey: 'routes', action: 'delete', fetchPath: null },
52
+ create_route: {
53
+ entityKey: "routes",
54
+ action: "create",
55
+ fetchPath: (id) => `/route/${id}`,
56
+ },
57
+ update_route: {
58
+ entityKey: "routes",
59
+ action: "update",
60
+ fetchPath: (id) => `/route/${id}`,
61
+ },
62
+ delete_route: { entityKey: "routes", action: "delete", fetchPath: null },
37
63
  // ── Events (also refetch parent job — overdueness, last_event, etc.) ──
38
- create_event: { entityKey: 'events', action: 'create', fetchPath: (id) => `/event/${id}`, relatedRefetch: [{ entityKey: 'jobs', idField: 'job_id', fetchPath: (id) => `/job/${id}`, construct: (d) => new Job(d) }] },
39
- update_event: { entityKey: 'events', action: 'update', fetchPath: (id) => `/event/${id}`, relatedRefetch: [{ entityKey: 'jobs', idField: 'job_id', fetchPath: (id) => `/job/${id}`, construct: (d) => new Job(d) }] },
40
- delete_event: { entityKey: 'events', action: 'delete', fetchPath: null, relatedRefetch: [{ entityKey: 'jobs', idField: 'job_id', fetchPath: (id) => `/job/${id}`, construct: (d) => new Job(d) }] },
41
- update_events: { invalidateKeys: [['events']] },
64
+ create_event: {
65
+ entityKey: "events",
66
+ action: "create",
67
+ fetchPath: (id) => `/event/${id}`,
68
+ relatedRefetch: [
69
+ {
70
+ entityKey: "jobs",
71
+ idField: "job_id",
72
+ fetchPath: (id) => `/job/${id}`,
73
+ construct: (d) => new Job(d),
74
+ },
75
+ ],
76
+ },
77
+ update_event: {
78
+ entityKey: "events",
79
+ action: "update",
80
+ fetchPath: (id) => `/event/${id}`,
81
+ relatedRefetch: [
82
+ {
83
+ entityKey: "jobs",
84
+ idField: "job_id",
85
+ fetchPath: (id) => `/job/${id}`,
86
+ construct: (d) => new Job(d),
87
+ },
88
+ ],
89
+ },
90
+ delete_event: {
91
+ entityKey: "events",
92
+ action: "delete",
93
+ fetchPath: null,
94
+ relatedRefetch: [
95
+ {
96
+ entityKey: "jobs",
97
+ idField: "job_id",
98
+ fetchPath: (id) => `/job/${id}`,
99
+ construct: (d) => new Job(d),
100
+ },
101
+ ],
102
+ },
103
+ update_events: { invalidateKeys: [["events"]] },
42
104
  // ── Teams ──
43
- create_team: { entityKey: 'teams', action: 'create', fetchPath: null },
44
- update_team: { entityKey: 'teams', action: 'update', fetchPath: null },
45
- delete_team: { entityKey: 'teams', action: 'delete', fetchPath: null },
46
- update_teams: { invalidateKeys: [['teams']] },
105
+ create_team: { entityKey: "teams", action: "create", fetchPath: null },
106
+ update_team: { entityKey: "teams", action: "update", fetchPath: null },
107
+ delete_team: { entityKey: "teams", action: "delete", fetchPath: null },
108
+ update_teams: { invalidateKeys: [["teams"]] },
47
109
  // ── Bills ──
48
- create_bill: { entityKey: 'bills', action: 'create', fetchPath: (id) => `/bill/${id}` },
49
- delete_bill: { entityKey: 'bills', action: 'delete', fetchPath: null },
50
- update_bills: { invalidateKeys: [['bills']] },
110
+ create_bill: {
111
+ entityKey: "bills",
112
+ action: "create",
113
+ fetchPath: (id) => `/bill/${id}`,
114
+ },
115
+ delete_bill: { entityKey: "bills", action: "delete", fetchPath: null },
116
+ update_bills: { invalidateKeys: [["bills"]] },
51
117
  // ── Sessions ──
52
- create_session: { entityKey: 'sessions', action: 'create', fetchPath: null },
53
- update_session: { entityKey: 'sessions', action: 'update', fetchPath: null },
118
+ create_session: { entityKey: "sessions", action: "create", fetchPath: null },
119
+ update_session: { entityKey: "sessions", action: "update", fetchPath: null },
54
120
  // ── Catalog Items ──
55
- create_catalog_item: { entityKey: 'catalog_items', action: 'create', fetchPath: (id) => `/catalog_item/${id}` },
56
- update_catalog_item: { entityKey: 'catalog_items', action: 'update', fetchPath: (id) => `/catalog_item/${id}` },
57
- delete_catalog_item: { entityKey: 'catalog_items', action: 'delete', fetchPath: null },
121
+ create_catalog_item: {
122
+ entityKey: "catalog_items",
123
+ action: "create",
124
+ fetchPath: (id) => `/catalog_item/${id}`,
125
+ },
126
+ update_catalog_item: {
127
+ entityKey: "catalog_items",
128
+ action: "update",
129
+ fetchPath: (id) => `/catalog_item/${id}`,
130
+ },
131
+ delete_catalog_item: {
132
+ entityKey: "catalog_items",
133
+ action: "delete",
134
+ fetchPath: null,
135
+ },
58
136
  // ── Groups ──
59
- create_group: { entityKey: 'groups', action: 'create', fetchPath: null },
60
- update_group: { entityKey: 'groups', action: 'update', fetchPath: null },
61
- delete_group: { entityKey: 'groups', action: 'delete', fetchPath: null },
137
+ create_group: { entityKey: "groups", action: "create", fetchPath: null },
138
+ update_group: { entityKey: "groups", action: "update", fetchPath: null },
139
+ delete_group: { entityKey: "groups", action: "delete", fetchPath: null },
62
140
  // ── Labels ──
63
- create_label: { entityKey: 'labels', action: 'create', fetchPath: null },
64
- update_label: { entityKey: 'labels', action: 'update', fetchPath: null },
65
- delete_label: { entityKey: 'labels', action: 'delete', fetchPath: null },
141
+ create_label: { entityKey: "labels", action: "create", fetchPath: null },
142
+ update_label: { entityKey: "labels", action: "update", fetchPath: null },
143
+ delete_label: { entityKey: "labels", action: "delete", fetchPath: null },
66
144
  // ── Skills ──
67
- create_skill: { entityKey: 'skills', action: 'create', fetchPath: null },
68
- update_skill: { entityKey: 'skills', action: 'update', fetchPath: null },
69
- delete_skill: { entityKey: 'skills', action: 'delete', fetchPath: null },
145
+ create_skill: { entityKey: "skills", action: "create", fetchPath: null },
146
+ update_skill: { entityKey: "skills", action: "update", fetchPath: null },
147
+ delete_skill: { entityKey: "skills", action: "delete", fetchPath: null },
70
148
  // ── Proposals ──
71
- create_proposal: { entityKey: 'proposals', action: 'create', fetchPath: (id) => `/proposal/${id}` },
72
- update_proposal: { entityKey: 'proposals', action: 'update', fetchPath: (id) => `/proposal/${id}` },
73
- delete_proposal: { entityKey: 'proposals', action: 'delete', fetchPath: null },
149
+ create_proposal: {
150
+ entityKey: "proposals",
151
+ action: "create",
152
+ fetchPath: (id) => `/proposal/${id}`,
153
+ },
154
+ update_proposal: {
155
+ entityKey: "proposals",
156
+ action: "update",
157
+ fetchPath: (id) => `/proposal/${id}`,
158
+ },
159
+ delete_proposal: {
160
+ entityKey: "proposals",
161
+ action: "delete",
162
+ fetchPath: null,
163
+ },
74
164
  // ── Service Months ──
75
- create_month: { entityKey: 'months', action: 'create', fetchPath: null },
76
- update_months: { invalidateKeys: [['months']] },
77
- delete_months: { invalidateKeys: [['months']] },
165
+ create_month: { entityKey: "months", action: "create", fetchPath: null },
166
+ update_months: { invalidateKeys: [["months"]] },
167
+ delete_months: { invalidateKeys: [["months"]] },
78
168
  // ── Tasks (nested under jobs — target the parent job) ──
79
- create_task: { entityKey: 'jobs', action: 'update', fetchPath: (id) => `/job/${id}`, idField: 'job_id', construct: (d) => new Job(d) },
80
- update_task: { entityKey: 'jobs', action: 'update', fetchPath: (id) => `/job/${id}`, idField: 'job_id', construct: (d) => new Job(d) },
81
- delete_task: { entityKey: 'jobs', action: 'update', fetchPath: (id) => `/job/${id}`, idField: 'job_id', construct: (d) => new Job(d) },
169
+ create_task: {
170
+ entityKey: "jobs",
171
+ action: "update",
172
+ fetchPath: (id) => `/job/${id}`,
173
+ idField: "job_id",
174
+ construct: (d) => new Job(d),
175
+ },
176
+ update_task: {
177
+ entityKey: "jobs",
178
+ action: "update",
179
+ fetchPath: (id) => `/job/${id}`,
180
+ idField: "job_id",
181
+ construct: (d) => new Job(d),
182
+ },
183
+ delete_task: {
184
+ entityKey: "jobs",
185
+ action: "update",
186
+ fetchPath: (id) => `/job/${id}`,
187
+ idField: "job_id",
188
+ construct: (d) => new Job(d),
189
+ },
82
190
  // ── Scheduling ──
83
- schedule_complete: { invalidateKeys: [['routes'], ['jobs'], ['infinite', 'jobs']] },
191
+ schedule_complete: {
192
+ invalidateKeys: [["routes"], ["jobs"], ["infinite", "jobs"]],
193
+ },
84
194
  // ── Location ──
85
- location_update: { invalidateKeys: [['teams']] },
195
+ location_update: { invalidateKeys: [["teams"]] },
86
196
  // ── Prepayments ──
87
- update_prepayments: { invalidateKeys: [['prepayments']] },
197
+ update_prepayments: { invalidateKeys: [["prepayments"]] },
88
198
  // ── Chats ──
89
- new_message: { invalidateKeys: [['chats'], ['messages']] },
199
+ new_message: { invalidateKeys: [["chats"], ["messages"]] },
90
200
  };
91
201
  /**
92
202
  * Returns true when `query.queryKey` belongs to the given entity,
@@ -94,7 +204,7 @@ const SOCKET_EVENT_CONFIG = {
94
204
  */
95
205
  function matchesEntityKey(query, entityKey) {
96
206
  const k = query.queryKey;
97
- return k[0] === entityKey || (k[0] === 'infinite' && k[1] === entityKey);
207
+ return k[0] === entityKey || (k[0] === "infinite" && k[1] === entityKey);
98
208
  }
99
209
  export class AvroQueryClient {
100
210
  constructor(config) {
@@ -110,39 +220,45 @@ export class AvroQueryClient {
110
220
  baseUrl: config.baseUrl,
111
221
  authManager: config.authManager,
112
222
  maxRetries: config.maxRetries ?? 3,
113
- retryStrategy: config.retryStrategy ?? 'fixed',
223
+ retryStrategy: config.retryStrategy ?? "fixed",
114
224
  timeout: config.timeout ?? 0,
115
225
  };
116
- this.socket = io(config.baseUrl, { autoConnect: false, transports: ["websocket"], });
117
- config.authManager.isAuthenticated().then(isAuth => {
226
+ this.socket = io(config.baseUrl, {
227
+ autoConnect: false,
228
+ transports: ["websocket"],
229
+ });
230
+ config.authManager.isAuthenticated().then((isAuth) => {
118
231
  this.setAuthState(isAuth);
119
- this.getCompanyId().then(id => {
232
+ this.getCompanyId().then((id) => {
120
233
  this.companyId = id;
121
234
  });
122
235
  if (!this.socket.connected && isAuth === AuthState.AUTHENTICATED) {
123
- this.config.authManager.accessToken().then(token => {
124
- console.log('Initializing socket connection with token:', token);
236
+ this.config.authManager
237
+ .accessToken()
238
+ .then((token) => {
239
+ console.log("Initializing socket connection with token:", token);
125
240
  this.socket.auth = { token: token };
126
241
  this.socket.connect();
127
- }).catch(err => {
128
- console.error('Not logged in:', err);
242
+ })
243
+ .catch((err) => {
244
+ console.error("Not logged in:", err);
129
245
  });
130
246
  }
131
247
  });
132
- this.socket.on('connect', () => {
248
+ this.socket.on("connect", () => {
133
249
  this.setAuthState(AuthState.AUTHENTICATED);
134
250
  console.log(`Socket connected with ID: ${this.socket?.id}`);
135
251
  });
136
- this.socket.on('disconnect', (reason) => {
252
+ this.socket.on("disconnect", (reason) => {
137
253
  console.log(`Socket disconnected: ${reason}`);
138
254
  });
139
- this.socket.on('connect_error', (err) => {
255
+ this.socket.on("connect_error", (err) => {
140
256
  console.error(`Socket connection error: ${err.message}`);
141
257
  });
142
258
  this.config.authManager.onTokenRefreshed((newAccessToken) => {
143
259
  if (this.socket && newAccessToken) {
144
260
  this.setAuthState(AuthState.AUTHENTICATED);
145
- console.log('Access token refreshed, updating socket auth...');
261
+ console.log("Access token refreshed, updating socket auth...");
146
262
  this.socket.auth = { token: newAccessToken };
147
263
  this.socket.disconnect().connect();
148
264
  }
@@ -157,7 +273,7 @@ export class AvroQueryClient {
157
273
  }
158
274
  emit(eventName, data) {
159
275
  if (!this.socket?.connected) {
160
- console.error('Socket is not connected. Cannot emit event.');
276
+ console.error("Socket is not connected. Cannot emit event.");
161
277
  return;
162
278
  }
163
279
  this.socket.emit(eventName, data);
@@ -191,7 +307,9 @@ export class AvroQueryClient {
191
307
  const client = this; // stable reference for async closures
192
308
  /** Full-invalidate every key that matches the entity (including 'infinite' prefix). */
193
309
  const invalidateEntity = (entityKey) => {
194
- queryClient.invalidateQueries({ predicate: (q) => matchesEntityKey(q, entityKey) });
310
+ queryClient.invalidateQueries({
311
+ predicate: (q) => matchesEntityKey(q, entityKey),
312
+ });
195
313
  };
196
314
  for (const [event, config] of Object.entries(SOCKET_EVENT_CONFIG)) {
197
315
  if (isBulkEvent(config)) {
@@ -206,160 +324,39 @@ export class AvroQueryClient {
206
324
  }
207
325
  else {
208
326
  // ── Targeted: surgical cache update ──────────────────
209
- const { entityKey, action, fetchPath, idField, alsoInvalidate, relatedRefetch } = config;
327
+ const { entityKey, action, fetchPath, idField, alsoInvalidate, relatedRefetch, } = config;
210
328
  const handler = async (data) => {
211
- const id = data?.[idField ?? 'id'];
329
+ const id = data?.[idField ?? "id"];
212
330
  // No id → old backend or malformed payload → full invalidation
213
- if (!id || typeof id !== 'string') {
331
+ if (!id || typeof id !== "string") {
214
332
  invalidateEntity(entityKey);
215
333
  alsoInvalidate?.forEach((k) => queryClient.invalidateQueries({ queryKey: k }));
216
334
  return;
217
335
  }
218
- const entityPredicate = (q) => matchesEntityKey(q, entityKey);
219
336
  let fetchedItem;
220
- switch (action) {
221
- // ─── CREATE ─────────────────────────────────
222
- case 'create': {
223
- if (fetchPath) {
224
- try {
225
- const item = await queryClient.fetchQuery({
226
- queryKey: [entityKey, id],
227
- queryFn: () => client.get({ path: fetchPath(id) }),
228
- staleTime: 0,
229
- });
230
- fetchedItem = config.construct ? config.construct(item) : item;
231
- queryClient.setQueryData([entityKey, id], fetchedItem);
232
- queryClient.setQueriesData({ predicate: entityPredicate, type: 'active' }, (old) => {
233
- if (!old)
234
- return old;
235
- if (old.pages && Array.isArray(old.pages)) {
236
- // Skip if already present (avoids duplicates
237
- // when the creating user also gets the event)
238
- if (old.pages.some((p) => p.some((x) => x?.id === id)))
239
- return old;
240
- return {
241
- ...old,
242
- pages: [
243
- [fetchedItem, ...(old.pages[0] || [])],
244
- ...old.pages.slice(1),
245
- ],
246
- };
247
- }
248
- if (Array.isArray(old)) {
249
- if (old.some((x) => x?.id === id))
250
- return old;
251
- return [...old, fetchedItem];
252
- }
253
- return old;
254
- });
255
- }
256
- catch {
257
- invalidateEntity(entityKey);
258
- }
259
- }
260
- else {
261
- // No individual GET endpoint → invalidate lists
262
- invalidateEntity(entityKey);
263
- }
264
- break;
265
- }
266
- // ─── UPDATE ─────────────────────────────────
267
- case 'update': {
268
- if (fetchPath) {
269
- try {
270
- // Fetch fresh copy (also updates the [entity, id] cache)
271
- const item = await queryClient.fetchQuery({
272
- queryKey: [entityKey, id],
273
- queryFn: () => client.get({ path: fetchPath(id) }),
274
- staleTime: 0,
275
- });
276
- fetchedItem = config.construct ? config.construct(item) : item;
277
- queryClient.setQueryData([entityKey, id], fetchedItem);
278
- // Patch it into every active list / infinite-query cache
279
- queryClient.setQueriesData({ predicate: entityPredicate, type: 'active' }, (old) => {
280
- if (!old)
281
- return old;
282
- if (old.pages && Array.isArray(old.pages)) {
283
- return {
284
- ...old,
285
- pages: old.pages.map((page) => page.map((x) => (x?.id === id ? fetchedItem : x))),
286
- };
287
- }
288
- if (Array.isArray(old)) {
289
- return old.map((x) => (x?.id === id ? fetchedItem : x));
290
- }
291
- return old;
292
- });
293
- }
294
- catch {
295
- invalidateEntity(entityKey);
296
- }
297
- }
298
- else {
299
- // No individual GET → invalidate everything for this entity
300
- invalidateEntity(entityKey);
301
- }
302
- break;
303
- }
304
- // ─── DELETE ─────────────────────────────────
305
- case 'delete': {
306
- // Remove the individual-item cache entry
307
- queryClient.removeQueries({ queryKey: [entityKey, id], exact: true });
308
- // Remove from every active list / infinite-query cache
309
- queryClient.setQueriesData({ predicate: entityPredicate, type: 'active' }, (old) => {
310
- if (!old)
311
- return old;
312
- if (old.pages && Array.isArray(old.pages)) {
313
- return {
314
- ...old,
315
- pages: old.pages.map((page) => page.filter((x) => x?.id !== id)),
316
- };
317
- }
318
- if (Array.isArray(old)) {
319
- return old.filter((x) => x?.id !== id);
320
- }
321
- return old;
322
- });
323
- break;
324
- }
325
- }
337
+ // ─── CREATE / UPDATE / DELETE via shared _syncEntity ───
338
+ fetchedItem = await client._syncEntity(queryClient, {
339
+ action,
340
+ entityKey,
341
+ id,
342
+ fetchPath: fetchPath ? fetchPath(id) : undefined,
343
+ construct: config.construct,
344
+ });
326
345
  // Invalidate any additional keys (e.g. companies → /company/list)
327
346
  alsoInvalidate?.forEach((k) => queryClient.invalidateQueries({ queryKey: k }));
328
347
  // Refetch & patch related entities (e.g. event → parent job)
329
348
  if (relatedRefetch) {
330
349
  for (const related of relatedRefetch) {
331
350
  const relatedId = data?.[related.idField] ?? fetchedItem?.[related.idField];
332
- if (!relatedId || typeof relatedId !== 'string')
351
+ if (!relatedId || typeof relatedId !== "string")
333
352
  continue;
334
- try {
335
- let relatedItem = await queryClient.fetchQuery({
336
- queryKey: [related.entityKey, relatedId],
337
- queryFn: () => client.get({ path: related.fetchPath(relatedId) }),
338
- staleTime: 0,
339
- });
340
- if (related.construct)
341
- relatedItem = related.construct(relatedItem);
342
- queryClient.setQueryData([related.entityKey, relatedId], relatedItem);
343
- const relatedPredicate = (q) => matchesEntityKey(q, related.entityKey);
344
- queryClient.setQueriesData({ predicate: relatedPredicate, type: 'active' }, (old) => {
345
- if (!old)
346
- return old;
347
- if (old.pages && Array.isArray(old.pages)) {
348
- return {
349
- ...old,
350
- pages: old.pages.map((page) => page.map((x) => (x?.id === relatedId ? relatedItem : x))),
351
- };
352
- }
353
- if (Array.isArray(old)) {
354
- return old.map((x) => (x?.id === relatedId ? relatedItem : x));
355
- }
356
- return old;
357
- });
358
- }
359
- catch {
360
- // Fetch failed → invalidate related entity lists
361
- invalidateEntity(related.entityKey);
362
- }
353
+ await client._syncEntity(queryClient, {
354
+ action: "update",
355
+ entityKey: related.entityKey,
356
+ id: relatedId,
357
+ fetchPath: related.fetchPath(relatedId),
358
+ construct: related.construct,
359
+ });
363
360
  }
364
361
  }
365
362
  };
@@ -370,11 +367,11 @@ export class AvroQueryClient {
370
367
  // Auto join/leave company room
371
368
  const joinCompanyRoom = () => {
372
369
  if (this.companyId) {
373
- this.socket.emit('join_company', { company_id: this.companyId });
370
+ this.socket.emit("join_company", { company_id: this.companyId });
374
371
  }
375
372
  };
376
- this.socket.on('connect', joinCompanyRoom);
377
- handlers.push({ event: 'connect', handler: joinCompanyRoom });
373
+ this.socket.on("connect", joinCompanyRoom);
374
+ handlers.push({ event: "connect", handler: joinCompanyRoom });
378
375
  // If already connected, join immediately
379
376
  if (this.socket.connected && this.companyId) {
380
377
  joinCompanyRoom();
@@ -385,7 +382,7 @@ export class AvroQueryClient {
385
382
  }
386
383
  // Leave company room on teardown
387
384
  if (this.socket.connected && this.companyId) {
388
- this.socket.emit('leave_company', { company_id: this.companyId });
385
+ this.socket.emit("leave_company", { company_id: this.companyId });
389
386
  }
390
387
  this._queryClient = null;
391
388
  };
@@ -397,17 +394,17 @@ export class AvroQueryClient {
397
394
  this._socketInvalidationCleanup?.();
398
395
  this._socketInvalidationCleanup = null;
399
396
  }
400
- get({ path, cancelToken, headers, progressUpdateCallback }) {
401
- return this._xhr('GET', path, null, cancelToken, headers, true, this.config.maxRetries, progressUpdateCallback);
397
+ get({ path, cancelToken, headers, progressUpdateCallback, }) {
398
+ return this._xhr("GET", path, null, cancelToken, headers, true, this.config.maxRetries, progressUpdateCallback);
402
399
  }
403
- post({ path, data, cancelToken, headers, progressUpdateCallback }) {
404
- return this._xhr('POST', path, data, cancelToken, headers, false, this.config.maxRetries, progressUpdateCallback);
400
+ post({ path, data, cancelToken, headers, progressUpdateCallback, }) {
401
+ return this._xhr("POST", path, data, cancelToken, headers, false, this.config.maxRetries, progressUpdateCallback);
405
402
  }
406
- put({ path, data, cancelToken, headers, progressUpdateCallback }) {
407
- return this._xhr('PUT', path, data, cancelToken, headers, true, this.config.maxRetries, progressUpdateCallback);
403
+ put({ path, data, cancelToken, headers, progressUpdateCallback, }) {
404
+ return this._xhr("PUT", path, data, cancelToken, headers, true, this.config.maxRetries, progressUpdateCallback);
408
405
  }
409
- delete({ path, cancelToken, headers, progressUpdateCallback }) {
410
- return this._xhr('DELETE', path, null, cancelToken, headers, false, this.config.maxRetries, progressUpdateCallback);
406
+ delete({ path, cancelToken, headers, progressUpdateCallback, }) {
407
+ return this._xhr("DELETE", path, null, cancelToken, headers, false, this.config.maxRetries, progressUpdateCallback);
411
408
  }
412
409
  loginSuccess(tokens) {
413
410
  this.setAuthState(AuthState.AUTHENTICATED);
@@ -420,20 +417,23 @@ export class AvroQueryClient {
420
417
  useLogin() {
421
418
  const queryClient = this.getQueryClient();
422
419
  return useMutation({
423
- mutationFn: async ({ username, password, code, cancelToken }) => {
420
+ mutationFn: async ({ username, password, code, cancelToken, }) => {
424
421
  const resp = await this.post({
425
- path: '/login',
422
+ path: "/login",
426
423
  data: JSON.stringify({ username, password, code }),
427
424
  cancelToken,
428
- headers: { 'Content-Type': 'application/json' }
425
+ headers: { "Content-Type": "application/json" },
429
426
  });
430
- if (!resp || !('access_token' in resp)) {
427
+ if (!resp || !("access_token" in resp)) {
431
428
  if (resp.msg === "TOTP required") {
432
429
  return LoginResponse.NEEDS_TOTP;
433
430
  }
434
- throw new StandardError(401, 'Invalid login response');
431
+ throw new StandardError(401, "Invalid login response");
435
432
  }
436
- await this.loginSuccess({ access_token: resp.access_token, refresh_token: resp.refresh_token });
433
+ await this.loginSuccess({
434
+ access_token: resp.access_token,
435
+ refresh_token: resp.refresh_token,
436
+ });
437
437
  return LoginResponse.SUCCESS;
438
438
  },
439
439
  onSettled: () => {
@@ -441,19 +441,19 @@ export class AvroQueryClient {
441
441
  },
442
442
  onError: (err) => {
443
443
  this.config.authManager.clearCache();
444
- throw new StandardError(401, err.message || 'Login failed');
445
- }
444
+ throw new StandardError(401, err.message || "Login failed");
445
+ },
446
446
  });
447
447
  }
448
448
  useRequestCode() {
449
449
  const queryClient = this.getQueryClient();
450
450
  return useMutation({
451
- mutationFn: async ({ username, email, cancelToken }) => {
451
+ mutationFn: async ({ username, email, cancelToken, }) => {
452
452
  const resp = await this.post({
453
- path: '/code',
453
+ path: "/code",
454
454
  data: JSON.stringify({ username, email }),
455
455
  cancelToken,
456
- headers: { 'Content-Type': 'application/json' }
456
+ headers: { "Content-Type": "application/json" },
457
457
  });
458
458
  return resp;
459
459
  },
@@ -461,46 +461,49 @@ export class AvroQueryClient {
461
461
  queryClient.invalidateQueries();
462
462
  },
463
463
  onError: (err) => {
464
- throw new StandardError(err.status, err.message || 'Request code failed');
465
- }
464
+ throw new StandardError(err.status, err.message || "Request code failed");
465
+ },
466
466
  });
467
467
  }
468
468
  useUpdatePassword() {
469
469
  const queryClient = this.getQueryClient();
470
470
  return useMutation({
471
- mutationFn: async ({ username, email, code, newPassword, cancelToken }) => {
471
+ mutationFn: async ({ username, email, code, newPassword, cancelToken, }) => {
472
472
  await this.post({
473
473
  path: `/user/${username ?? email}/password`,
474
474
  data: JSON.stringify({ code, password: newPassword }),
475
475
  cancelToken,
476
- headers: { 'Content-Type': 'application/json' }
476
+ headers: { "Content-Type": "application/json" },
477
477
  });
478
478
  },
479
479
  onSettled: () => {
480
480
  queryClient.invalidateQueries();
481
481
  },
482
482
  onError: (err) => {
483
- throw new StandardError(err.status, err.message || 'Update password failed');
484
- }
483
+ throw new StandardError(err.status, err.message || "Update password failed");
484
+ },
485
485
  });
486
486
  }
487
487
  useGoogleLogin() {
488
488
  const queryClient = this.getQueryClient();
489
489
  return useMutation({
490
- mutationFn: async ({ token, cancelToken }) => {
491
- const resp = await this._xhr('POST', `/google/authorize?token=${token}`, {}, cancelToken, { 'Content-Type': 'application/json' });
492
- if (!resp || !('access_token' in resp)) {
490
+ mutationFn: async ({ token, cancelToken, }) => {
491
+ const resp = await this._xhr("POST", `/google/authorize?token=${token}`, {}, cancelToken, { "Content-Type": "application/json" });
492
+ if (!resp || !("access_token" in resp)) {
493
493
  if (resp.msg === "TOTP required") {
494
494
  return LoginResponse.NEEDS_TOTP;
495
495
  }
496
- throw new StandardError(401, 'Invalid Google login response');
496
+ throw new StandardError(401, "Invalid Google login response");
497
497
  }
498
498
  this.setAuthState(AuthState.AUTHENTICATED);
499
499
  this.socket.auth = { token: resp.access_token };
500
500
  if (!this.socket.connected) {
501
501
  this.socket.connect();
502
502
  }
503
- await this.config.authManager.setTokens({ access_token: resp.access_token, refresh_token: resp.refresh_token });
503
+ await this.config.authManager.setTokens({
504
+ access_token: resp.access_token,
505
+ refresh_token: resp.refresh_token,
506
+ });
504
507
  return LoginResponse.SUCCESS;
505
508
  },
506
509
  onSettled: () => {
@@ -508,27 +511,30 @@ export class AvroQueryClient {
508
511
  },
509
512
  onError: (err) => {
510
513
  this.config.authManager.clearCache();
511
- throw new StandardError(err.status, err.message || 'Google Login failed');
512
- }
514
+ throw new StandardError(err.status, err.message || "Google Login failed");
515
+ },
513
516
  });
514
517
  }
515
518
  useAppleLogin() {
516
519
  const queryClient = this.getQueryClient();
517
520
  return useMutation({
518
- mutationFn: async ({ token, cancelToken }) => {
519
- const resp = await this._xhr('POST', `/apple/authorize?token=${encodeURIComponent(token)}`, {}, cancelToken, { 'Content-Type': 'application/json' });
520
- if (!resp || !('access_token' in resp)) {
521
+ mutationFn: async ({ token, cancelToken, }) => {
522
+ const resp = await this._xhr("POST", `/apple/authorize?token=${encodeURIComponent(token)}`, {}, cancelToken, { "Content-Type": "application/json" });
523
+ if (!resp || !("access_token" in resp)) {
521
524
  if (resp.msg === "TOTP required") {
522
525
  return LoginResponse.NEEDS_TOTP;
523
526
  }
524
- throw new StandardError(401, 'Invalid Apple login response');
527
+ throw new StandardError(401, "Invalid Apple login response");
525
528
  }
526
529
  this.setAuthState(AuthState.AUTHENTICATED);
527
530
  this.socket.auth = { token: resp.access_token };
528
531
  if (!this.socket.connected) {
529
532
  this.socket.connect();
530
533
  }
531
- await this.config.authManager.setTokens({ access_token: resp.access_token, refresh_token: resp.refresh_token });
534
+ await this.config.authManager.setTokens({
535
+ access_token: resp.access_token,
536
+ refresh_token: resp.refresh_token,
537
+ });
532
538
  return LoginResponse.SUCCESS;
533
539
  },
534
540
  onSettled: () => {
@@ -536,8 +542,8 @@ export class AvroQueryClient {
536
542
  },
537
543
  onError: (err) => {
538
544
  this.config.authManager.clearCache();
539
- throw new StandardError(err.status, err.message || 'Apple Login failed');
540
- }
545
+ throw new StandardError(err.status, err.message || "Apple Login failed");
546
+ },
541
547
  });
542
548
  }
543
549
  setTokens(tokens) {
@@ -576,10 +582,10 @@ export class AvroQueryClient {
576
582
  return () => this.offAuthStateChange(cb);
577
583
  }
578
584
  offAuthStateChange(cb) {
579
- this.authStateListeners = this.authStateListeners.filter(c => c !== cb);
585
+ this.authStateListeners = this.authStateListeners.filter((c) => c !== cb);
580
586
  }
581
587
  setAuthState(state) {
582
- this.authStateListeners.forEach(cb => cb(state));
588
+ this.authStateListeners.forEach((cb) => cb(state));
583
589
  this._authState = state;
584
590
  }
585
591
  getAuthState() {
@@ -591,13 +597,104 @@ export class AvroQueryClient {
591
597
  getQueryClient() {
592
598
  return useQueryClient();
593
599
  }
600
+ /**
601
+ * Fetch an entity from the API, optionally construct it, and surgically
602
+ * update all matching React-Query caches (individual + list + infinite).
603
+ *
604
+ * Shared by socket handlers and mutation `onSuccess` callbacks so the
605
+ * sender gets an immediate cache sync and everyone else gets the socket
606
+ * update — both use the identical code path.
607
+ *
608
+ * @returns The fetched (and optionally constructed) item, or `undefined`
609
+ * for deletes / entities without a fetchPath.
610
+ */
611
+ async _syncEntity(queryClient, params) {
612
+ const { action, entityKey, id, fetchPath, construct } = params;
613
+ const predicate = (q) => matchesEntityKey(q, entityKey);
614
+ const invalidate = () => queryClient.invalidateQueries({ predicate });
615
+ // ─── DELETE ─────────────────────────────────────────
616
+ if (action === "delete") {
617
+ queryClient.removeQueries({ queryKey: [entityKey, id], exact: true });
618
+ queryClient.setQueriesData({ predicate, type: "active" }, (old) => {
619
+ if (!old)
620
+ return old;
621
+ if (old.pages && Array.isArray(old.pages)) {
622
+ return {
623
+ ...old,
624
+ pages: old.pages.map((p) => p.filter((x) => x?.id !== id)),
625
+ };
626
+ }
627
+ if (Array.isArray(old)) {
628
+ return old.filter((x) => x?.id !== id);
629
+ }
630
+ return old;
631
+ });
632
+ return undefined;
633
+ }
634
+ // ─── CREATE / UPDATE ────────────────────────────────
635
+ if (!fetchPath) {
636
+ invalidate();
637
+ return undefined;
638
+ }
639
+ try {
640
+ const raw = await queryClient.fetchQuery({
641
+ queryKey: [entityKey, id],
642
+ queryFn: () => this.get({ path: fetchPath }),
643
+ staleTime: 0,
644
+ });
645
+ const item = construct ? construct(raw) : raw;
646
+ queryClient.setQueryData([entityKey, id], item);
647
+ if (action === "create") {
648
+ queryClient.setQueriesData({ predicate, type: "active" }, (old) => {
649
+ if (!old)
650
+ return old;
651
+ if (old.pages && Array.isArray(old.pages)) {
652
+ if (old.pages.some((p) => p.some((x) => x?.id === id)))
653
+ return old;
654
+ return {
655
+ ...old,
656
+ pages: [[item, ...(old.pages[0] || [])], ...old.pages.slice(1)],
657
+ };
658
+ }
659
+ if (Array.isArray(old)) {
660
+ if (old.some((x) => x?.id === id))
661
+ return old;
662
+ return [...old, item];
663
+ }
664
+ return old;
665
+ });
666
+ }
667
+ else {
668
+ // UPDATE — replace in every active list / infinite-query cache
669
+ queryClient.setQueriesData({ predicate, type: "active" }, (old) => {
670
+ if (!old)
671
+ return old;
672
+ if (old.pages && Array.isArray(old.pages)) {
673
+ return {
674
+ ...old,
675
+ pages: old.pages.map((page) => page.map((x) => (x?.id === id ? item : x))),
676
+ };
677
+ }
678
+ if (Array.isArray(old)) {
679
+ return old.map((x) => (x?.id === id ? item : x));
680
+ }
681
+ return old;
682
+ });
683
+ }
684
+ return item;
685
+ }
686
+ catch {
687
+ invalidate();
688
+ return undefined;
689
+ }
690
+ }
594
691
  useLogout() {
595
692
  const queryClient = this.getQueryClient();
596
693
  return useMutation({
597
694
  mutationFn: async (cancelToken) => {
598
695
  await this.post({
599
- path: '/logout',
600
- cancelToken
696
+ path: "/logout",
697
+ cancelToken,
601
698
  });
602
699
  await this.config.authManager.clearCache();
603
700
  if (this.socket && this.socket.connected) {
@@ -611,276 +708,276 @@ export class AvroQueryClient {
611
708
  },
612
709
  onError: (err) => {
613
710
  this.clearCache();
614
- console.error('Logout failed:', err);
615
- throw new StandardError(500, 'Logout failed');
616
- }
711
+ console.error("Logout failed:", err);
712
+ throw new StandardError(500, "Logout failed");
713
+ },
617
714
  });
618
715
  }
619
716
  fetchJobs(body = {}, companyId, cancelToken, headers = {}) {
620
717
  const companyIdToUse = companyId ?? this.companyId;
621
- if (!companyIdToUse || companyIdToUse.trim() === '') {
622
- throw new StandardError(400, 'Company ID is required');
718
+ if (!companyIdToUse || companyIdToUse.trim() === "") {
719
+ throw new StandardError(400, "Company ID is required");
623
720
  }
624
- return this._fetch('POST', `/company/${companyIdToUse}/jobs`, JSON.stringify(body), cancelToken, {
721
+ return this._fetch("POST", `/company/${companyIdToUse}/jobs`, JSON.stringify(body), cancelToken, {
625
722
  ...headers,
626
- 'Content-Type': 'application/json',
723
+ "Content-Type": "application/json",
627
724
  })
628
- .then(response => {
725
+ .then((response) => {
629
726
  if (!response || !Array.isArray(response)) {
630
- throw new StandardError(400, 'Invalid jobs response');
727
+ throw new StandardError(400, "Invalid jobs response");
631
728
  }
632
729
  return response;
633
730
  })
634
- .catch(err => {
635
- console.error('Failed to fetch jobs:', err);
731
+ .catch((err) => {
732
+ console.error("Failed to fetch jobs:", err);
636
733
  throw new StandardError(500, `Failed to fetch jobs: ${err.message ?? err}`);
637
734
  });
638
735
  }
639
736
  fetchChats(body = {}, cancelToken, headers = {}) {
640
- if (!this.companyId || this.companyId.trim() === '') {
641
- throw new StandardError(400, 'Company ID is required');
737
+ if (!this.companyId || this.companyId.trim() === "") {
738
+ throw new StandardError(400, "Company ID is required");
642
739
  }
643
- return this._fetch('POST', `/company/${this.companyId}/chats`, JSON.stringify(body), cancelToken, {
740
+ return this._fetch("POST", `/company/${this.companyId}/chats`, JSON.stringify(body), cancelToken, {
644
741
  ...headers,
645
- 'Content-Type': 'application/json',
742
+ "Content-Type": "application/json",
646
743
  })
647
- .then(response => {
744
+ .then((response) => {
648
745
  if (!response || !Array.isArray(response)) {
649
- throw new StandardError(400, 'Invalid chats response');
746
+ throw new StandardError(400, "Invalid chats response");
650
747
  }
651
748
  return response;
652
749
  })
653
- .catch(err => {
654
- console.error('Failed to fetch chats:', err);
655
- throw new StandardError(500, 'Failed to fetch chats');
750
+ .catch((err) => {
751
+ console.error("Failed to fetch chats:", err);
752
+ throw new StandardError(500, "Failed to fetch chats");
656
753
  });
657
754
  }
658
755
  fetchMessages(chatId, body = {}, cancelToken, headers = {}) {
659
- if (!chatId || chatId.trim() === '') {
660
- throw new StandardError(400, 'Chat ID is required');
756
+ if (!chatId || chatId.trim() === "") {
757
+ throw new StandardError(400, "Chat ID is required");
661
758
  }
662
- return this._fetch('POST', `/chat/${chatId}/messages`, JSON.stringify(body), cancelToken, {
759
+ return this._fetch("POST", `/chat/${chatId}/messages`, JSON.stringify(body), cancelToken, {
663
760
  ...headers,
664
- 'Content-Type': 'application/json',
761
+ "Content-Type": "application/json",
665
762
  })
666
- .then(response => {
763
+ .then((response) => {
667
764
  if (!response || !Array.isArray(response)) {
668
- throw new StandardError(400, 'Invalid messages response');
765
+ throw new StandardError(400, "Invalid messages response");
669
766
  }
670
767
  return response;
671
768
  })
672
- .catch(err => {
673
- console.error('Failed to fetch messages:', err);
674
- throw new StandardError(500, 'Failed to fetch messages');
769
+ .catch((err) => {
770
+ console.error("Failed to fetch messages:", err);
771
+ throw new StandardError(500, "Failed to fetch messages");
675
772
  });
676
773
  }
677
774
  async fetchPrepayments(body = {}, cancelToken, headers = {}) {
678
- if (!this.companyId || this.companyId.trim() === '') {
679
- throw new StandardError(400, 'Company ID is required');
775
+ if (!this.companyId || this.companyId.trim() === "") {
776
+ throw new StandardError(400, "Company ID is required");
680
777
  }
681
- return this._fetch('POST', `/company/${this.companyId}/prepayments`, JSON.stringify(body), cancelToken, {
778
+ return this._fetch("POST", `/company/${this.companyId}/prepayments`, JSON.stringify(body), cancelToken, {
682
779
  ...headers,
683
- 'Content-Type': 'application/json',
780
+ "Content-Type": "application/json",
684
781
  })
685
- .then(response => {
782
+ .then((response) => {
686
783
  if (!response || !Array.isArray(response)) {
687
- throw new StandardError(400, 'Invalid prepayments response');
784
+ throw new StandardError(400, "Invalid prepayments response");
688
785
  }
689
786
  return response;
690
787
  })
691
- .catch(err => {
692
- console.error('Failed to fetch prepayments:', err);
693
- throw new StandardError(500, 'Failed to fetch prepayments');
788
+ .catch((err) => {
789
+ console.error("Failed to fetch prepayments:", err);
790
+ throw new StandardError(500, "Failed to fetch prepayments");
694
791
  });
695
792
  }
696
793
  async fetchWaivers(body = {}, cancelToken, headers = {}) {
697
- if (!this.companyId || this.companyId.trim() === '') {
698
- throw new StandardError(400, 'Company ID is required');
794
+ if (!this.companyId || this.companyId.trim() === "") {
795
+ throw new StandardError(400, "Company ID is required");
699
796
  }
700
- return this._fetch('POST', `/company/${this.companyId}/waivers`, JSON.stringify(body), cancelToken, {
797
+ return this._fetch("POST", `/company/${this.companyId}/waivers`, JSON.stringify(body), cancelToken, {
701
798
  ...headers,
702
- 'Content-Type': 'application/json',
799
+ "Content-Type": "application/json",
703
800
  })
704
- .then(response => {
801
+ .then((response) => {
705
802
  if (!response || !Array.isArray(response)) {
706
- throw new StandardError(400, 'Invalid waivers response');
803
+ throw new StandardError(400, "Invalid waivers response");
707
804
  }
708
805
  return response;
709
806
  })
710
- .catch(err => {
711
- console.error('Failed to fetch waivers:', err);
712
- throw new StandardError(500, 'Failed to fetch waivers');
807
+ .catch((err) => {
808
+ console.error("Failed to fetch waivers:", err);
809
+ throw new StandardError(500, "Failed to fetch waivers");
713
810
  });
714
811
  }
715
812
  async fetchEvents(body = {}, cancelToken, headers = {}) {
716
- if (!this.companyId || this.companyId.trim() === '') {
717
- throw new StandardError(400, 'Company ID is required');
813
+ if (!this.companyId || this.companyId.trim() === "") {
814
+ throw new StandardError(400, "Company ID is required");
718
815
  }
719
- return this._fetch('POST', `/company/${this.companyId}/events`, JSON.stringify(body), cancelToken, {
816
+ return this._fetch("POST", `/company/${this.companyId}/events`, JSON.stringify(body), cancelToken, {
720
817
  ...headers,
721
- 'Content-Type': 'application/json',
818
+ "Content-Type": "application/json",
722
819
  })
723
- .then(response => {
820
+ .then((response) => {
724
821
  if (!response || !Array.isArray(response)) {
725
- throw new StandardError(400, 'Invalid events response');
822
+ throw new StandardError(400, "Invalid events response");
726
823
  }
727
824
  return response;
728
825
  })
729
- .catch(err => {
730
- console.error('Failed to fetch events:', err);
731
- throw new StandardError(500, 'Failed to fetch events');
826
+ .catch((err) => {
827
+ console.error("Failed to fetch events:", err);
828
+ throw new StandardError(500, "Failed to fetch events");
732
829
  });
733
830
  }
734
831
  fetchMonths(body = {}, cancelToken, headers = {}) {
735
- if (!this.companyId || this.companyId.trim() === '') {
736
- throw new StandardError(400, 'Company ID is required');
832
+ if (!this.companyId || this.companyId.trim() === "") {
833
+ throw new StandardError(400, "Company ID is required");
737
834
  }
738
- return this._fetch('POST', `/company/${this.companyId}/months`, JSON.stringify(body), cancelToken, {
835
+ return this._fetch("POST", `/company/${this.companyId}/months`, JSON.stringify(body), cancelToken, {
739
836
  ...headers,
740
- 'Content-Type': 'application/json',
837
+ "Content-Type": "application/json",
741
838
  })
742
- .then(response => {
839
+ .then((response) => {
743
840
  if (!response || !Array.isArray(response)) {
744
- throw new StandardError(400, 'Invalid months response');
841
+ throw new StandardError(400, "Invalid months response");
745
842
  }
746
843
  return response;
747
844
  })
748
- .catch(err => {
749
- console.error('Failed to fetch months:', err);
750
- throw new StandardError(500, 'Failed to fetch months');
845
+ .catch((err) => {
846
+ console.error("Failed to fetch months:", err);
847
+ throw new StandardError(500, "Failed to fetch months");
751
848
  });
752
849
  }
753
850
  fetchBills(body = {}, cancelToken, headers = {}) {
754
- if (!this.companyId || this.companyId.trim() === '') {
755
- throw new StandardError(400, 'Company ID is required');
851
+ if (!this.companyId || this.companyId.trim() === "") {
852
+ throw new StandardError(400, "Company ID is required");
756
853
  }
757
- return this._fetch('POST', `/company/${this.companyId}/bills`, JSON.stringify(body), cancelToken, {
854
+ return this._fetch("POST", `/company/${this.companyId}/bills`, JSON.stringify(body), cancelToken, {
758
855
  ...headers,
759
- 'Content-Type': 'application/json',
856
+ "Content-Type": "application/json",
760
857
  })
761
- .then(response => {
858
+ .then((response) => {
762
859
  if (!response || !Array.isArray(response)) {
763
- throw new StandardError(400, 'Invalid bills response');
860
+ throw new StandardError(400, "Invalid bills response");
764
861
  }
765
862
  return response;
766
863
  })
767
- .catch(err => {
768
- console.error('Failed to fetch bills:', err);
769
- throw new StandardError(500, 'Failed to fetch bills');
864
+ .catch((err) => {
865
+ console.error("Failed to fetch bills:", err);
866
+ throw new StandardError(500, "Failed to fetch bills");
770
867
  });
771
868
  }
772
869
  fetchRoutes(body = {}, cancelToken, headers = {}) {
773
- if (!this.companyId || this.companyId.trim() === '') {
774
- throw new StandardError(400, 'Company ID is required');
870
+ if (!this.companyId || this.companyId.trim() === "") {
871
+ throw new StandardError(400, "Company ID is required");
775
872
  }
776
- return this._fetch('POST', `/company/${this.companyId}/routes`, JSON.stringify(body), cancelToken, {
873
+ return this._fetch("POST", `/company/${this.companyId}/routes`, JSON.stringify(body), cancelToken, {
777
874
  ...headers,
778
- 'Content-Type': 'application/json',
875
+ "Content-Type": "application/json",
779
876
  })
780
- .then(response => {
877
+ .then((response) => {
781
878
  if (!response || !Array.isArray(response)) {
782
- throw new StandardError(400, 'Invalid routes response');
879
+ throw new StandardError(400, "Invalid routes response");
783
880
  }
784
881
  return response;
785
882
  })
786
- .catch(err => {
787
- console.error('Failed to fetch routes:', err);
788
- throw new StandardError(500, 'Failed to fetch routes');
883
+ .catch((err) => {
884
+ console.error("Failed to fetch routes:", err);
885
+ throw new StandardError(500, "Failed to fetch routes");
789
886
  });
790
887
  }
791
888
  fetchTeams(body = {}, cancelToken, headers = {}) {
792
- if (!this.companyId || this.companyId.trim() === '') {
793
- throw new StandardError(400, 'Company ID is required');
889
+ if (!this.companyId || this.companyId.trim() === "") {
890
+ throw new StandardError(400, "Company ID is required");
794
891
  }
795
- return this._fetch('POST', `/company/${this.companyId}/teams`, JSON.stringify(body), cancelToken, {
892
+ return this._fetch("POST", `/company/${this.companyId}/teams`, JSON.stringify(body), cancelToken, {
796
893
  ...headers,
797
- 'Content-Type': 'application/json',
894
+ "Content-Type": "application/json",
798
895
  })
799
- .then(response => {
896
+ .then((response) => {
800
897
  if (!response || !Array.isArray(response)) {
801
- throw new StandardError(400, 'Invalid teams response');
898
+ throw new StandardError(400, "Invalid teams response");
802
899
  }
803
900
  return response;
804
901
  })
805
- .catch(err => {
806
- console.error('Failed to fetch teams:', err);
807
- throw new StandardError(500, 'Failed to fetch teams');
902
+ .catch((err) => {
903
+ console.error("Failed to fetch teams:", err);
904
+ throw new StandardError(500, "Failed to fetch teams");
808
905
  });
809
906
  }
810
907
  fetchLabels(body = {}, cancelToken, headers = {}) {
811
- if (!this.companyId || this.companyId.trim() === '') {
812
- throw new StandardError(400, 'Company ID is required');
908
+ if (!this.companyId || this.companyId.trim() === "") {
909
+ throw new StandardError(400, "Company ID is required");
813
910
  }
814
- return this._fetch('POST', `/company/${this.companyId}/labels`, JSON.stringify(body), cancelToken, {
911
+ return this._fetch("POST", `/company/${this.companyId}/labels`, JSON.stringify(body), cancelToken, {
815
912
  ...headers,
816
- 'Content-Type': 'application/json',
913
+ "Content-Type": "application/json",
817
914
  })
818
- .then(response => {
915
+ .then((response) => {
819
916
  if (!response || !Array.isArray(response)) {
820
- throw new StandardError(400, 'Invalid labels response');
917
+ throw new StandardError(400, "Invalid labels response");
821
918
  }
822
919
  return response;
823
920
  })
824
- .catch(err => {
825
- console.error('Failed to fetch labels:', err);
826
- throw new StandardError(500, 'Failed to fetch labels');
921
+ .catch((err) => {
922
+ console.error("Failed to fetch labels:", err);
923
+ throw new StandardError(500, "Failed to fetch labels");
827
924
  });
828
925
  }
829
926
  fetchGroups(body = {}, cancelToken, headers = {}) {
830
- if (!this.companyId || this.companyId.trim() === '') {
831
- throw new StandardError(400, 'Company ID is required');
927
+ if (!this.companyId || this.companyId.trim() === "") {
928
+ throw new StandardError(400, "Company ID is required");
832
929
  }
833
- return this._fetch('POST', `/company/${this.companyId}/groups`, JSON.stringify(body), cancelToken, {
930
+ return this._fetch("POST", `/company/${this.companyId}/groups`, JSON.stringify(body), cancelToken, {
834
931
  ...headers,
835
- 'Content-Type': 'application/json',
932
+ "Content-Type": "application/json",
836
933
  })
837
- .then(response => {
934
+ .then((response) => {
838
935
  if (!response || !Array.isArray(response)) {
839
- throw new StandardError(400, 'Invalid groups response');
936
+ throw new StandardError(400, "Invalid groups response");
840
937
  }
841
938
  return response;
842
939
  })
843
- .catch(err => {
844
- console.error('Failed to fetch groups:', err);
845
- throw new StandardError(500, 'Failed to fetch groups');
940
+ .catch((err) => {
941
+ console.error("Failed to fetch groups:", err);
942
+ throw new StandardError(500, "Failed to fetch groups");
846
943
  });
847
944
  }
848
945
  fetchSkills(body = {}, cancelToken, headers = {}) {
849
- if (!this.companyId || this.companyId.trim() === '') {
850
- throw new StandardError(400, 'Company ID is required');
946
+ if (!this.companyId || this.companyId.trim() === "") {
947
+ throw new StandardError(400, "Company ID is required");
851
948
  }
852
- return this._fetch('POST', `/company/${this.companyId}/skills`, JSON.stringify(body), cancelToken, {
949
+ return this._fetch("POST", `/company/${this.companyId}/skills`, JSON.stringify(body), cancelToken, {
853
950
  ...headers,
854
- 'Content-Type': 'application/json',
951
+ "Content-Type": "application/json",
855
952
  })
856
- .then(response => {
953
+ .then((response) => {
857
954
  if (!response || !Array.isArray(response)) {
858
- throw new StandardError(400, 'Invalid skills response');
955
+ throw new StandardError(400, "Invalid skills response");
859
956
  }
860
957
  return response;
861
958
  })
862
- .catch(err => {
863
- console.error('Failed to fetch skills:', err);
864
- throw new StandardError(500, 'Failed to fetch skills');
959
+ .catch((err) => {
960
+ console.error("Failed to fetch skills:", err);
961
+ throw new StandardError(500, "Failed to fetch skills");
865
962
  });
866
963
  }
867
964
  fetchSessions(body = {}, cancelToken, headers = {}) {
868
- if (!this.companyId || this.companyId.trim() === '') {
869
- throw new StandardError(400, 'Company ID is required');
965
+ if (!this.companyId || this.companyId.trim() === "") {
966
+ throw new StandardError(400, "Company ID is required");
870
967
  }
871
- return this._fetch('POST', `/company/${this.companyId}/sessions`, JSON.stringify(body), cancelToken, {
968
+ return this._fetch("POST", `/company/${this.companyId}/sessions`, JSON.stringify(body), cancelToken, {
872
969
  ...headers,
873
- 'Content-Type': 'application/json',
970
+ "Content-Type": "application/json",
874
971
  })
875
- .then(response => {
972
+ .then((response) => {
876
973
  if (!response || !Array.isArray(response)) {
877
- throw new StandardError(400, 'Invalid sessions response');
974
+ throw new StandardError(400, "Invalid sessions response");
878
975
  }
879
976
  return response;
880
977
  })
881
- .catch(err => {
882
- console.error('Failed to fetch sessions:', err);
883
- throw new StandardError(500, 'Failed to fetch sessions');
978
+ .catch((err) => {
979
+ console.error("Failed to fetch sessions:", err);
980
+ throw new StandardError(500, "Failed to fetch sessions");
884
981
  });
885
982
  }
886
983
  /* ── Email delivery tracking ──────────────────────────────────────── */
@@ -945,7 +1042,7 @@ export class AvroQueryClient {
945
1042
  return this.post({
946
1043
  path: `/email/${emailId}`,
947
1044
  data: formData,
948
- progressUpdateCallback
1045
+ progressUpdateCallback,
949
1046
  });
950
1047
  }
951
1048
  catch (error) {
@@ -957,7 +1054,7 @@ export class AvroQueryClient {
957
1054
  return this.post({
958
1055
  path: `/bill/${billId}/email`,
959
1056
  data: JSON.stringify(body),
960
- headers: { 'Content-Type': 'application/json' }
1057
+ headers: { "Content-Type": "application/json" },
961
1058
  });
962
1059
  }
963
1060
  catch (error) {