@go-avro/avro-js 0.0.38 → 0.0.42

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.
@@ -17,7 +17,10 @@ export const AvroQueryClientProvider = ({ baseUrl, authManager, queryClient, con
17
17
  return () => {
18
18
  try {
19
19
  client.teardownSocketInvalidation();
20
- client.socket?.disconnect();
20
+ // Socket is NOT disconnected here. Its lifecycle is managed by the
21
+ // AvroQueryClient constructor and token refresh callbacks.
22
+ // Disconnecting here breaks React StrictMode — the constructor's
23
+ // async connect flow has already completed, so the socket stays dead.
21
24
  }
22
25
  catch (e) {
23
26
  // ignore
@@ -1,7 +1,7 @@
1
1
  import io from "socket.io-client";
2
2
  import { useMutation, useQueryClient, } from "@tanstack/react-query";
3
3
  import { v4 as uuidv4 } from "uuid";
4
- import { Job, LoginResponse, } from "../types/api";
4
+ import { _Event, Job, LoginResponse, Route, } from "../types/api";
5
5
  import { AuthState } from "../types/auth";
6
6
  import { StandardError } from "../types/error";
7
7
  function isBulkEvent(c) {
@@ -53,11 +53,13 @@ const SOCKET_EVENT_CONFIG = {
53
53
  entityKey: "routes",
54
54
  action: "create",
55
55
  fetchPath: (id) => `/route/${id}`,
56
+ construct: (d) => new Route(d),
56
57
  },
57
58
  update_route: {
58
59
  entityKey: "routes",
59
60
  action: "update",
60
61
  fetchPath: (id) => `/route/${id}`,
62
+ construct: (d) => new Route(d),
61
63
  },
62
64
  delete_route: { entityKey: "routes", action: "delete", fetchPath: null },
63
65
  // ── Events (also refetch parent job — overdueness, last_event, etc.) ──
@@ -65,6 +67,7 @@ const SOCKET_EVENT_CONFIG = {
65
67
  entityKey: "events",
66
68
  action: "create",
67
69
  fetchPath: (id) => `/event/${id}`,
70
+ construct: (d) => new _Event(d),
68
71
  relatedRefetch: [
69
72
  {
70
73
  entityKey: "jobs",
@@ -78,6 +81,7 @@ const SOCKET_EVENT_CONFIG = {
78
81
  entityKey: "events",
79
82
  action: "update",
80
83
  fetchPath: (id) => `/event/${id}`,
84
+ construct: (d) => new _Event(d),
81
85
  relatedRefetch: [
82
86
  {
83
87
  entityKey: "jobs",
@@ -229,13 +233,16 @@ export class AvroQueryClient {
229
233
  });
230
234
  config.authManager.isAuthenticated().then((isAuth) => {
231
235
  this.setAuthState(isAuth);
232
- this.getCompanyId().then((id) => {
233
- this.companyId = id;
234
- });
235
236
  if (!this.socket.connected && isAuth === AuthState.AUTHENTICATED) {
236
- this.config.authManager
237
- .accessToken()
238
- .then((token) => {
237
+ // Resolve both companyId and access token before connecting so
238
+ // the 'connect' handler in setupSocketInvalidation can immediately
239
+ // join the company room.
240
+ Promise.all([
241
+ this.getCompanyId(),
242
+ this.config.authManager.accessToken(),
243
+ ])
244
+ .then(([id, token]) => {
245
+ this.companyId = id;
239
246
  console.log("Initializing socket connection with token:", token);
240
247
  this.socket.auth = { token: token };
241
248
  this.socket.connect();
@@ -244,6 +251,12 @@ export class AvroQueryClient {
244
251
  console.error("Not logged in:", err);
245
252
  });
246
253
  }
254
+ else {
255
+ // Not authenticated – still cache the companyId for later.
256
+ this.getCompanyId().then((id) => {
257
+ this.companyId = id;
258
+ });
259
+ }
247
260
  });
248
261
  this.socket.on("connect", () => {
249
262
  this.setAuthState(AuthState.AUTHENTICATED);
@@ -315,6 +328,7 @@ export class AvroQueryClient {
315
328
  if (isBulkEvent(config)) {
316
329
  // ── Bulk: invalidate all specified keys ──────────────
317
330
  const handler = () => {
331
+ console.log(`[socket-debug] bulk event: ${event}`, config.invalidateKeys);
318
332
  for (const key of config.invalidateKeys) {
319
333
  queryClient.invalidateQueries({ queryKey: key });
320
334
  }
@@ -326,9 +340,11 @@ export class AvroQueryClient {
326
340
  // ── Targeted: surgical cache update ──────────────────
327
341
  const { entityKey, action, fetchPath, idField, alsoInvalidate, relatedRefetch, } = config;
328
342
  const handler = async (data) => {
343
+ console.log(`[socket-debug] targeted event: ${event}`, data);
329
344
  const id = data?.[idField ?? "id"];
330
345
  // No id → old backend or malformed payload → full invalidation
331
346
  if (!id || typeof id !== "string") {
347
+ console.log(`[socket-debug] no id found, falling back to full invalidation for ${entityKey}`);
332
348
  invalidateEntity(entityKey);
333
349
  alsoInvalidate?.forEach((k) => queryClient.invalidateQueries({ queryKey: k }));
334
350
  return;
@@ -366,8 +382,13 @@ export class AvroQueryClient {
366
382
  }
367
383
  // Auto join/leave company room
368
384
  const joinCompanyRoom = () => {
385
+ console.log(`[socket-debug] joinCompanyRoom called, companyId=${this.companyId}, connected=${this.socket.connected}`);
369
386
  if (this.companyId) {
370
387
  this.socket.emit("join_company", { company_id: this.companyId });
388
+ console.log(`[socket-debug] emitted join_company for ${this.companyId}`);
389
+ }
390
+ else {
391
+ console.warn(`[socket-debug] joinCompanyRoom skipped — companyId is undefined`);
371
392
  }
372
393
  };
373
394
  this.socket.on("connect", joinCompanyRoom);
@@ -376,6 +397,7 @@ export class AvroQueryClient {
376
397
  if (this.socket.connected && this.companyId) {
377
398
  joinCompanyRoom();
378
399
  }
400
+ console.log(`[socket-debug] setupSocketInvalidation complete, socket.connected=${this.socket.connected}, companyId=${this.companyId}`);
379
401
  this._socketInvalidationCleanup = () => {
380
402
  for (const { event, handler } of handlers) {
381
403
  this.socket.off(event, handler);
@@ -556,7 +578,20 @@ export class AvroQueryClient {
556
578
  return this.config.authManager.getCache(key);
557
579
  }
558
580
  setCompanyId(companyId) {
581
+ const previousId = this.companyId;
559
582
  this.companyId = companyId;
583
+ console.log(`[socket-debug] setCompanyId called: ${companyId}, socket.connected=${this.socket.connected}, prev=${previousId}`);
584
+ // If the socket is already connected, leave the old room and join the new one.
585
+ if (this.socket.connected) {
586
+ if (previousId && previousId !== companyId) {
587
+ this.socket.emit("leave_company", { company_id: previousId });
588
+ }
589
+ this.socket.emit("join_company", { company_id: companyId });
590
+ console.log(`[socket-debug] setCompanyId emitted join_company for ${companyId}`);
591
+ }
592
+ else {
593
+ console.warn(`[socket-debug] setCompanyId: socket not connected, will join on next connect`);
594
+ }
560
595
  return this.config.authManager.setCompanyId(companyId);
561
596
  }
562
597
  getCompanyId() {
@@ -610,6 +645,7 @@ export class AvroQueryClient {
610
645
  */
611
646
  async _syncEntity(queryClient, params) {
612
647
  const { action, entityKey, id, fetchPath, construct } = params;
648
+ console.log(`[socket-debug] _syncEntity: action=${action}, entity=${entityKey}, id=${id}, fetchPath=${fetchPath}`);
613
649
  const predicate = (q) => matchesEntityKey(q, entityKey);
614
650
  const invalidate = () => queryClient.invalidateQueries({ predicate });
615
651
  // ─── DELETE ─────────────────────────────────────────
@@ -643,6 +679,7 @@ export class AvroQueryClient {
643
679
  staleTime: 0,
644
680
  });
645
681
  const item = construct ? construct(raw) : raw;
682
+ console.log(`[socket-debug] _syncEntity fetched ${entityKey}/${id}:`, item);
646
683
  queryClient.setQueryData([entityKey, id], item);
647
684
  if (action === "create") {
648
685
  queryClient.setQueriesData({ predicate, type: "active" }, (old) => {
@@ -681,9 +718,11 @@ export class AvroQueryClient {
681
718
  return old;
682
719
  });
683
720
  }
721
+ console.log(`[socket-debug] _syncEntity completed ${action} for ${entityKey}/${id}`);
684
722
  return item;
685
723
  }
686
- catch {
724
+ catch (err) {
725
+ console.error(`[socket-debug] _syncEntity fetch FAILED for ${entityKey}/${id}:`, err);
687
726
  invalidate();
688
727
  return undefined;
689
728
  }
@@ -32,14 +32,17 @@ AvroQueryClient.prototype.useGetCurrentCompany = function () {
32
32
  queryKey: ["companies", "current"],
33
33
  queryFn: async () => {
34
34
  if (!this.companyId) {
35
- this.companyId = await this.config.authManager.getCompanyId();
35
+ const storedId = await this.config.authManager.getCompanyId();
36
+ if (storedId) {
37
+ await this.setCompanyId(storedId);
38
+ }
36
39
  }
37
40
  if (!this.companyId) {
38
41
  const companyList = await this.get({
39
42
  path: `/company/list`,
40
43
  });
41
44
  if (companyList.length > 0) {
42
- this.companyId = companyList[0].id;
45
+ await this.setCompanyId(companyList[0].id);
43
46
  }
44
47
  else {
45
48
  throw new Error("No company ID set and no companies available");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-avro/avro-js",
3
- "version": "0.0.38",
3
+ "version": "0.0.42",
4
4
  "description": "JS client for Avro backend integration.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",