synapse-react-client 4.0.43 → 4.0.44

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 (63) hide show
  1. package/dist/SWC.index.js +1 -1
  2. package/dist/components/DataGrid/DataGridWebSocket.d.ts +29 -1
  3. package/dist/components/DataGrid/DataGridWebSocket.d.ts.map +1 -1
  4. package/dist/components/DataGrid/DataGridWebSocket.js +48 -16
  5. package/dist/components/DataGrid/DataGridWebSocket.js.map +1 -1
  6. package/dist/components/DataGrid/components/GridLoadingState.d.ts +2 -2
  7. package/dist/components/DataGrid/components/GridLoadingState.d.ts.map +1 -1
  8. package/dist/components/DataGrid/components/GridLoadingState.js +2 -2
  9. package/dist/components/DataGrid/components/GridLoadingState.js.map +1 -1
  10. package/dist/components/DataGrid/useDataGridWebsocket.d.ts.map +1 -1
  11. package/dist/components/DataGrid/useDataGridWebsocket.js +2 -1
  12. package/dist/components/DataGrid/useDataGridWebsocket.js.map +1 -1
  13. package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.d.ts.map +1 -1
  14. package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.js +11 -10
  15. package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.js.map +1 -1
  16. package/dist/components/SetManagedAccessRequirementFields/DataUseCertificateFields.d.ts +10 -0
  17. package/dist/components/SetManagedAccessRequirementFields/DataUseCertificateFields.d.ts.map +1 -0
  18. package/dist/components/SetManagedAccessRequirementFields/DataUseCertificateFields.js +81 -0
  19. package/dist/components/SetManagedAccessRequirementFields/DataUseCertificateFields.js.map +1 -0
  20. package/dist/components/SetManagedAccessRequirementFields/ExpirationPeriodField.d.ts +9 -0
  21. package/dist/components/SetManagedAccessRequirementFields/ExpirationPeriodField.d.ts.map +1 -0
  22. package/dist/components/SetManagedAccessRequirementFields/ExpirationPeriodField.js +32 -0
  23. package/dist/components/SetManagedAccessRequirementFields/ExpirationPeriodField.js.map +1 -0
  24. package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.d.ts +0 -2
  25. package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.d.ts.map +1 -1
  26. package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js +74 -151
  27. package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js.map +1 -1
  28. package/dist/components/TanStackTable/TanStackTableUtils.d.ts.map +1 -1
  29. package/dist/components/TanStackTable/TanStackTableUtils.js +8 -5
  30. package/dist/components/TanStackTable/TanStackTableUtils.js.map +1 -1
  31. package/dist/components/dataaccess/SubmissionPage/DataAccessSubmissionFileHandleLink.d.ts +5 -0
  32. package/dist/components/dataaccess/SubmissionPage/DataAccessSubmissionFileHandleLink.d.ts.map +1 -0
  33. package/dist/components/dataaccess/SubmissionPage/DataAccessSubmissionFileHandleLink.js +20 -0
  34. package/dist/components/dataaccess/SubmissionPage/DataAccessSubmissionFileHandleLink.js.map +1 -0
  35. package/dist/components/dataaccess/SubmissionPage/SubmissionPage.d.ts.map +1 -1
  36. package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js +183 -188
  37. package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js.map +1 -1
  38. package/dist/components/dataaccess/SubmissionPage/SubmissionSchemaAnswers.d.ts +25 -0
  39. package/dist/components/dataaccess/SubmissionPage/SubmissionSchemaAnswers.d.ts.map +1 -0
  40. package/dist/components/dataaccess/SubmissionPage/SubmissionSchemaAnswers.js +125 -0
  41. package/dist/components/dataaccess/SubmissionPage/SubmissionSchemaAnswers.js.map +1 -0
  42. package/dist/mocks/dataaccess/MockSubmission.d.ts +76 -0
  43. package/dist/mocks/dataaccess/MockSubmission.d.ts.map +1 -1
  44. package/dist/mocks/dataaccess/MockSubmission.js +108 -63
  45. package/dist/mocks/dataaccess/MockSubmission.js.map +1 -1
  46. package/dist/style/components/_submission-page.scss +0 -4
  47. package/dist/style/main.css +1 -1
  48. package/dist/synapse-queries/dataaccess/useGeneratedSubmissionForm.d.ts +37 -0
  49. package/dist/synapse-queries/dataaccess/useGeneratedSubmissionForm.d.ts.map +1 -0
  50. package/dist/synapse-queries/dataaccess/useGeneratedSubmissionForm.js +35 -0
  51. package/dist/synapse-queries/dataaccess/useGeneratedSubmissionForm.js.map +1 -0
  52. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts +2 -0
  53. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts.map +1 -1
  54. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.js.map +1 -1
  55. package/dist/synapse-queries/termsOfService/useTermsOfService.d.ts.map +1 -1
  56. package/dist/synapse-queries/termsOfService/useTermsOfService.js +21 -19
  57. package/dist/synapse-queries/termsOfService/useTermsOfService.js.map +1 -1
  58. package/dist/tsconfig.build.tsbuildinfo +1 -1
  59. package/dist/utils/functions/EntityTypeUtils.d.ts +2 -2
  60. package/dist/utils/functions/EntityTypeUtils.d.ts.map +1 -1
  61. package/dist/utils/functions/EntityTypeUtils.js +135 -135
  62. package/dist/utils/functions/EntityTypeUtils.js.map +1 -1
  63. package/package.json +2 -2
package/dist/SWC.index.js CHANGED
@@ -117,7 +117,7 @@ var x = { BackendDestinationEnum: r }, S = {
117
117
  sendSearchResultsReturnedEvent: y,
118
118
  sendSearchResultReturnedEvent: v,
119
119
  sendSearchResultClickedEvent: _
120
- }, T = "4.0.43";
120
+ }, T = "4.0.44";
121
121
  //#endregion
122
122
  export { w as Analytics, a as HttpClient, d as Palettes, o as SynapseClient, C as SynapseComponents, n as SynapseConstants, S as SynapseContext, x as SynapseEnums, m as SynapseQueries, T as SynapseReactClientVersion, p as SynapseSessionManager, i as xssOptions };
123
123
 
@@ -1,4 +1,5 @@
1
1
  import { GridModel } from '@/components/DataGrid/DataGridTypes';
2
+ import { CompactCodecPatch } from 'json-joy/lib/json-crdt-patch/codec/compact';
2
3
  type DataGridWebSocketConstructorArgs = {
3
4
  replicaId: number;
4
5
  url: string;
@@ -14,6 +15,9 @@ type DataGridWebSocketConstructorArgs = {
14
15
  socket?: WebSocket;
15
16
  model?: GridModel | null;
16
17
  patchThrottleMs?: number;
18
+ /** Patches the previous connection never saw acknowledged, resent in order ahead of new ones */
19
+ pendingPatches?: CompactCodecPatch[];
20
+ patchAckTimeoutMs?: number;
17
21
  };
18
22
  export declare class DataGridWebSocket {
19
23
  private socket;
@@ -24,6 +28,12 @@ export declare class DataGridWebSocket {
24
28
  private maxPayloadSizeBytes;
25
29
  private throttledSendPatch;
26
30
  private snapshotDecoder;
31
+ private unacknowledgedPatches;
32
+ private inFlightPatchRequestId;
33
+ private patchAckTimeoutMs;
34
+ private patchAckTimer;
35
+ private isClosing;
36
+ private isSyncExchangeOpen;
27
37
  private onModelCreate;
28
38
  private onGridReady;
29
39
  private onStatusChange;
@@ -34,7 +44,17 @@ export declare class DataGridWebSocket {
34
44
  private onError;
35
45
  constructor(args: DataGridWebSocketConstructorArgs);
36
46
  private attachSocketHandlers;
47
+ /**
48
+ * Closes the connection once every unacknowledged patch has been acknowledged, so edits made
49
+ * just before leaving are not lost. The owner has moved on, so no further callbacks fire.
50
+ */
37
51
  disconnect(): void;
52
+ private closeSocket;
53
+ /**
54
+ * @returns the patches the server has not acknowledged, oldest first. A replacement connection
55
+ * created with these as `pendingPatches` resends them in order.
56
+ */
57
+ getPendingPatches(): CompactCodecPatch[];
38
58
  /**
39
59
  * Flushes the local model clock and sends the resulting patch (or a clock-sync
40
60
  * message) to the server so that both sides converge.
@@ -45,13 +65,21 @@ export declare class DataGridWebSocket {
45
65
  private handlePatchPayload;
46
66
  private handleSnapshotPayload;
47
67
  private handleResponseComplete;
68
+ private handlePatchAcknowledged;
48
69
  private handleNotification;
49
70
  private sendMessage;
50
71
  sendPatch(): void;
51
72
  /**
52
- * @returns true if one or more patches were sent
73
+ * @returns true if the model had local changes, which are now queued to be sent
53
74
  */
54
75
  private sendPatchImmediate;
76
+ private sendNextPatch;
77
+ private stopWaitingForPatchAck;
78
+ /**
79
+ * Resending on the same connection could reorder patches, so the connection is dropped and
80
+ * the owner reconnects with getPendingPatches(). The server ignores patches it already stored.
81
+ */
82
+ private handlePatchAckTimeout;
55
83
  private sendSyncMessage;
56
84
  private fetchAndDecodeSnapshot;
57
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DataGridWebSocket.d.ts","sourceRoot":"","sources":["../../../src/components/DataGrid/DataGridWebSocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AAyC/D,KAAK,gCAAgC,GAAG;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACvE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;IAC1C,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC/B,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,CAAA;AAQD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,mBAAmB,CAAQ;IACnC,OAAO,CAAC,kBAAkB,CAA2B;IACrD,OAAO,CAAC,eAAe,CAAwB;IAE/C,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,cAAc,CAAqD;IAC3E,OAAO,CAAC,kBAAkB,CAAY;IACtC,OAAO,CAAC,qBAAqB,CAAY;IACzC,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAA0B;gBAE7B,IAAI,EAAE,gCAAgC;IA+ClD,OAAO,CAAC,oBAAoB;IAwBrB,UAAU;IAWjB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,kBAAkB;YAiBZ,qBAAqB;IAYnC,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,kBAAkB;IAmD1B,OAAO,CAAC,WAAW;IAWZ,SAAS;IAIhB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA0B1B,OAAO,CAAC,eAAe;YAUT,sBAAsB;CAYrC"}
1
+ {"version":3,"file":"DataGridWebSocket.d.ts","sourceRoot":"","sources":["../../../src/components/DataGrid/DataGridWebSocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AAU/D,OAAO,EACL,iBAAiB,EAElB,MAAM,4CAA4C,CAAA;AA4BnD,KAAK,gCAAgC,GAAG;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACvE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;IAC1C,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC/B,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gGAAgG;IAChG,cAAc,CAAC,EAAE,iBAAiB,EAAE,CAAA;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAUD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,mBAAmB,CAAQ;IACnC,OAAO,CAAC,kBAAkB,CAA2B;IACrD,OAAO,CAAC,eAAe,CAAwB;IAG/C,OAAO,CAAC,qBAAqB,CAAqB;IAClD,OAAO,CAAC,sBAAsB,CAAsB;IACpD,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,aAAa,CAAsB;IAE3C,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,kBAAkB,CAAQ;IAElC,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,cAAc,CAAqD;IAC3E,OAAO,CAAC,kBAAkB,CAAY;IACtC,OAAO,CAAC,qBAAqB,CAAY;IACzC,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAA0B;gBAE7B,IAAI,EAAE,gCAAgC;IAmDlD,OAAO,CAAC,oBAAoB;IA2B5B;;;OAGG;IACI,UAAU;IAwBjB,OAAO,CAAC,WAAW;IAKnB;;;OAGG;IACI,iBAAiB,IAAI,iBAAiB,EAAE;IAI/C;;;OAGG;IACH,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,kBAAkB;YAiBZ,qBAAqB;IAYnC,OAAO,CAAC,sBAAsB;IAc9B,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,kBAAkB;IAqD1B,OAAO,CAAC,WAAW;IAWZ,SAAS;IAIhB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,sBAAsB;IAQ9B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,eAAe;YAWT,sBAAsB;CAYrC"}
@@ -14,7 +14,7 @@ import f from "lodash-es/throttle";
14
14
  import { Decoder as p } from "json-joy/lib/json-crdt/codec/indexed/binary/Decoder";
15
15
  import { decode as m } from "cbor2";
16
16
  //#region src/components/DataGrid/DataGridWebSocket.ts
17
- var h = 30 * 1024, g = 250, _ = class {
17
+ var h = 30 * 1024, g = 250, _ = 15 * 1e3, v = class {
18
18
  socket;
19
19
  model = null;
20
20
  messageCounter;
@@ -23,6 +23,12 @@ var h = 30 * 1024, g = 250, _ = class {
23
23
  maxPayloadSizeBytes;
24
24
  throttledSendPatch;
25
25
  snapshotDecoder = new p();
26
+ unacknowledgedPatches;
27
+ inFlightPatchRequestId = null;
28
+ patchAckTimeoutMs;
29
+ patchAckTimer = null;
30
+ isClosing = !1;
31
+ isSyncExchangeOpen = !1;
26
32
  onModelCreate;
27
33
  onGridReady;
28
34
  onStatusChange;
@@ -32,8 +38,8 @@ var h = 30 * 1024, g = 250, _ = class {
32
38
  onSyncEnd;
33
39
  onError;
34
40
  constructor(e) {
35
- let { replicaId: t, url: n, onGridReady: r, onStatusChange: i, onModelCreate: o, onReplicaConnected: s, onReplicaDisconnected: l, onSyncStart: u, onSyncEnd: d, onError: p, maxPayloadSizeBytes: m, socket: _, model: v, patchThrottleMs: y } = e;
36
- this.messageCounter = new a(), this.replicaId = t, this.maxPayloadSizeBytes = m ?? h, this.socket = _ ?? new WebSocket(n), this.onModelCreate = o ?? c, this.onGridReady = r ?? c, this.onStatusChange = i ?? c, this.onReplicaConnected = s ?? c, this.onReplicaDisconnected = l ?? c, this.onSyncStart = u ?? c, this.onSyncEnd = d ?? c, this.onError = p ?? c, v && (this.model = v), this.attachSocketHandlers(), this.throttledSendPatch = f(() => this.sendPatchImmediate(), y ?? g, {
41
+ let { replicaId: t, url: n, onGridReady: r, onStatusChange: i, onModelCreate: o, onReplicaConnected: s, onReplicaDisconnected: l, onSyncStart: u, onSyncEnd: d, onError: p, maxPayloadSizeBytes: m, socket: v, model: y, patchThrottleMs: b, pendingPatches: x, patchAckTimeoutMs: S } = e;
42
+ this.messageCounter = new a(), this.replicaId = t, this.maxPayloadSizeBytes = m ?? h, this.socket = v ?? new WebSocket(n), this.unacknowledgedPatches = [...x ?? []], this.patchAckTimeoutMs = S ?? _, this.onModelCreate = o ?? c, this.onGridReady = r ?? c, this.onStatusChange = i ?? c, this.onReplicaConnected = s ?? c, this.onReplicaDisconnected = l ?? c, this.onSyncStart = u ?? c, this.onSyncEnd = d ?? c, this.onError = p ?? c, y && (this.model = y), this.attachSocketHandlers(), this.throttledSendPatch = f(() => this.sendPatchImmediate(), b ?? g, {
37
43
  leading: !1,
38
44
  trailing: !0
39
45
  });
@@ -44,13 +50,23 @@ var h = 30 * 1024, g = 250, _ = class {
44
50
  }, this.socket.onmessage = (e) => {
45
51
  typeof e.data == "string" ? this.handleMessage(e.data) : console.error("Received non-string message data:", e.data);
46
52
  }, this.socket.onclose = () => {
47
- console.debug("Disconnected from the WebSocket server"), this.onStatusChange(!1, this);
53
+ console.debug("Disconnected from the WebSocket server"), this.stopWaitingForPatchAck(), this.throttledSendPatch.cancel(), this.onStatusChange(!1, this);
48
54
  }, this.socket.onerror = (e) => {
49
55
  console.error("WebSocket error:", e);
50
56
  };
51
57
  }
52
58
  disconnect() {
53
- this.throttledSendPatch.flush(), this.throttledSendPatch.cancel(), this.socket.readyState === WebSocket.OPEN ? (this.socket.close(), console.debug("WebSocket connection closed")) : console.warn("WebSocket is not open. No action taken.");
59
+ if (this.socket.readyState !== WebSocket.OPEN) {
60
+ this.throttledSendPatch.cancel(), console.warn("WebSocket is not open. No action taken.");
61
+ return;
62
+ }
63
+ this.throttledSendPatch.flush(), this.throttledSendPatch.cancel(), this.isClosing = !0, this.onModelCreate = c, this.onGridReady = c, this.onStatusChange = c, this.onReplicaConnected = c, this.onReplicaDisconnected = c, this.onSyncStart = c, this.onSyncEnd = c, this.onError = c, this.unacknowledgedPatches.length === 0 && this.closeSocket();
64
+ }
65
+ closeSocket() {
66
+ this.socket.close(), console.debug("WebSocket connection closed");
67
+ }
68
+ getPendingPatches() {
69
+ return [...this.unacknowledgedPatches];
54
70
  }
55
71
  sendClockSync() {
56
72
  if (!this.model) {
@@ -64,7 +80,7 @@ var h = 30 * 1024, g = 250, _ = class {
64
80
  }
65
81
  handleMessage(r) {
66
82
  let a = i.fromJson(JSON.parse(r));
67
- a instanceof t ? this.handleResponse(a) : a instanceof n ? this.handleResponseComplete() : a instanceof e ? this.handleNotification(a) : console.warn("Unexpected WebSocket message format:", a);
83
+ this.isClosing && !(a instanceof n) || (a instanceof t ? this.handleResponse(a) : a instanceof n ? this.handleResponseComplete(a) : a instanceof e ? this.handleNotification(a) : console.warn("Unexpected WebSocket message format:", a));
68
84
  }
69
85
  handleResponse(e) {
70
86
  let t = e.getPayload();
@@ -100,8 +116,15 @@ var h = 30 * 1024, g = 250, _ = class {
100
116
  console.error("Failed to fetch or decode snapshot", e);
101
117
  }
102
118
  }
103
- handleResponseComplete() {
104
- this.onGridReady(), this.onSyncEnd(), console.debug("Clocks synchronized with server. Incrementing sequence number.");
119
+ handleResponseComplete(e) {
120
+ if (e.getSubscriptionId() === this.inFlightPatchRequestId) {
121
+ this.handlePatchAcknowledged();
122
+ return;
123
+ }
124
+ this.isSyncExchangeOpen = !1, this.onGridReady(), this.onSyncEnd(), console.debug("Clocks synchronized with server. Incrementing sequence number.");
125
+ }
126
+ handlePatchAcknowledged() {
127
+ this.unacknowledgedPatches.shift(), this.stopWaitingForPatchAck(), this.unacknowledgedPatches.length > 0 ? this.sendNextPatch() : this.isClosing ? this.closeSocket() : this.isSyncExchangeOpen && this.model && this.sendSyncMessage(this.verboseEncoder.encode(this.model).time);
105
128
  }
106
129
  handleNotification(e) {
107
130
  let t = e.getMethodName();
@@ -110,7 +133,7 @@ var h = 30 * 1024, g = 250, _ = class {
110
133
  console.debug("Received ping from server");
111
134
  break;
112
135
  case "connected":
113
- console.debug("Server ready to receive patches"), this.sendSyncMessage(this.model ? this.verboseEncoder.encode(this.model).time : void 0);
136
+ console.debug("Server ready to receive patches"), this.sendSyncMessage(this.model ? this.verboseEncoder.encode(this.model).time : void 0), this.sendNextPatch();
114
137
  break;
115
138
  case "error":
116
139
  console.warn("Error from server:", e.getPayload()), this.onError(e.getPayload());
@@ -145,15 +168,24 @@ var h = 30 * 1024, g = 250, _ = class {
145
168
  sendPatchImmediate() {
146
169
  if (!this.model) return console.warn("Model is not initialized. Cannot send patch."), !1;
147
170
  let e = this.model.api.flush(), t = e.ops.length > 0;
148
- return t && o(e, this.maxPayloadSizeBytes).forEach((e) => {
149
- console.debug("Sending patch to server:", e);
150
- let t = new r(this.messageCounter.getNext(), "patch", e);
151
- this.sendMessage(t);
152
- }), t;
171
+ return t && (this.unacknowledgedPatches.push(...o(e, this.maxPayloadSizeBytes)), this.sendNextPatch()), t;
172
+ }
173
+ sendNextPatch() {
174
+ let e = this.unacknowledgedPatches[0];
175
+ if (e === void 0 || this.inFlightPatchRequestId !== null || this.socket.readyState !== WebSocket.OPEN) return;
176
+ console.debug("Sending patch to server:", e);
177
+ let t = this.messageCounter.getNext();
178
+ this.inFlightPatchRequestId = t, this.patchAckTimer = window.setTimeout(() => this.handlePatchAckTimeout(), this.patchAckTimeoutMs), this.sendMessage(new r(t, "patch", e));
179
+ }
180
+ stopWaitingForPatchAck() {
181
+ this.patchAckTimer !== null && (window.clearTimeout(this.patchAckTimer), this.patchAckTimer = null), this.inFlightPatchRequestId = null;
182
+ }
183
+ handlePatchAckTimeout() {
184
+ console.warn("Timed out waiting for a patch acknowledgement. Dropping the connection so unacknowledged patches are resent."), this.socket.onopen = null, this.socket.onmessage = null, this.socket.onclose = null, this.socket.onerror = null, this.throttledSendPatch.cancel(), this.stopWaitingForPatchAck(), this.socket.close(), this.onStatusChange(!1, this);
153
185
  }
154
186
  sendSyncMessage(e) {
155
187
  let t = new r(this.messageCounter.getNext(), "synchronize-clock", e ?? []);
156
- this.onSyncStart(), this.sendMessage(t);
188
+ this.isSyncExchangeOpen = !0, this.onSyncStart(), this.sendMessage(t);
157
189
  }
158
190
  async fetchAndDecodeSnapshot(e) {
159
191
  let t = await (await (await s(e)).blob()).arrayBuffer(), n = m(new Uint8Array(t));
@@ -161,6 +193,6 @@ var h = 30 * 1024, g = 250, _ = class {
161
193
  }
162
194
  };
163
195
  //#endregion
164
- export { _ as DataGridWebSocket };
196
+ export { v as DataGridWebSocket };
165
197
 
166
198
  //# sourceMappingURL=DataGridWebSocket.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DataGridWebSocket.js","names":["GridModel","JsonRx","JsonRxMessage","JsonRxNotification","JsonRxRequestComplete","JsonRxResponse","JsonRxResponseComplete","MessageCounter","splitPatch","Model","CompactCodecPatch","decode","Encoder","VerboseEncoder","JsonCrdtVerboseLogicalTimestamp","noop","throttle","DebouncedFunc","Decoder","SnapshotDecoder","decodeCbor","IndexedFields","fetchWithExponentialTimeout","SynapseGridWebSocketMessagePayloadType","PATCH","SNAPSHOT","PATCHES","SynapseGridWebSocketMessagePayload","type","body","DataGridWebSocketConstructorArgs","replicaId","url","onGridReady","onStatusChange","isOpen","instance","DataGridWebSocket","onModelCreate","model","onReplicaConnected","onReplicaDisconnected","onSyncStart","onSyncEnd","onError","error","maxPayloadSizeBytes","socket","WebSocket","patchThrottleMs","DEFAULT_MAX_PAYLOAD_SIZE_BYTES","DEFAULT_PATCH_THROTTLE_MS","messageCounter","verboseEncoder","throttledSendPatch","snapshotDecoder","_this","constructor","args","attachSocketHandlers","sendPatchImmediate","leading","trailing","onopen","console","debug","onmessage","event","MessageEvent","data","handleMessage","onclose","onerror","Event","disconnect","flush","cancel","readyState","OPEN","close","warn","sendClockSync","patchesWereSent","verbModel","encode","sendSyncMessage","time","rawMessage","message","fromJson","JSON","parse","handleResponse","handleResponseComplete","handleNotification","payload","getPayload","handlePatchPayload","handleSnapshotPayload","encodedPatch","patches","map","fromPatches","fork","applyBatch","err","snapshotUrl","decodedModel","fetchAndDecodeSnapshot","methodName","getMethodName","undefined","sendMessage","send","stringify","getJson","sendPatch","patch","api","hasOperationsToDispatch","ops","length","forEach","compactEncodedPatch","msg","getNext","clock","Promise","response","blob","buffer","arrayBuffer","decodedFromCbor","Uint8Array"],"sources":["../../../src/components/DataGrid/DataGridWebSocket.ts"],"sourcesContent":["import { GridModel } from '@/components/DataGrid/DataGridTypes'\nimport JsonRx from '@/components/DataGrid/utils/json-rx/JsonRx'\nimport JsonRxMessage from '@/components/DataGrid/utils/json-rx/JsonRxMessage'\nimport JsonRxNotification from '@/components/DataGrid/utils/json-rx/JsonRxNotification'\nimport JsonRxRequestComplete from '@/components/DataGrid/utils/json-rx/JsonRxRequestComplete'\nimport JsonRxResponse from '@/components/DataGrid/utils/json-rx/JsonRxResponse'\nimport JsonRxResponseComplete from '@/components/DataGrid/utils/json-rx/JsonRxResponseComplete'\nimport MessageCounter from '@/components/DataGrid/utils/MessageCounter'\nimport { splitPatch } from '@/components/DataGrid/utils/splitPatch'\nimport { Model } from 'json-joy/lib/json-crdt'\nimport {\n CompactCodecPatch,\n decode,\n} from 'json-joy/lib/json-crdt-patch/codec/compact'\nimport { Encoder as VerboseEncoder } from 'json-joy/lib/json-crdt/codec/structural/verbose/Encoder'\nimport { JsonCrdtVerboseLogicalTimestamp } from 'json-joy/lib/json-crdt/codec/structural/verbose/types'\nimport noop from 'lodash-es/noop'\nimport throttle from 'lodash-es/throttle'\nimport { type DebouncedFunc } from 'lodash-es'\nimport { Decoder as SnapshotDecoder } from 'json-joy/lib/json-crdt/codec/indexed/binary/Decoder'\nimport { decode as decodeCbor } from 'cbor2'\nimport type { IndexedFields } from 'json-joy/lib/json-crdt/codec/indexed/binary'\nimport { fetchWithExponentialTimeout } from '@sage-bionetworks/synapse-client'\n\nenum SynapseGridWebSocketMessagePayloadType {\n PATCH = 'patch',\n SNAPSHOT = 'snapshot',\n PATCHES = 'patches',\n}\n\ntype SynapseGridWebSocketMessagePayload =\n | {\n type: SynapseGridWebSocketMessagePayloadType.PATCH\n body: CompactCodecPatch\n }\n | {\n type: SynapseGridWebSocketMessagePayloadType.PATCHES\n body: CompactCodecPatch[]\n }\n | { type: SynapseGridWebSocketMessagePayloadType.SNAPSHOT; body: string }\n\ntype DataGridWebSocketConstructorArgs = {\n replicaId: number\n url: string\n onGridReady?: () => void\n onStatusChange?: (isOpen: boolean, instance: DataGridWebSocket) => void\n onModelCreate?: (model: GridModel) => void\n onReplicaConnected?: () => void\n onReplicaDisconnected?: () => void\n onSyncStart?: () => void\n onSyncEnd?: () => void\n onError?: (error: unknown) => void\n maxPayloadSizeBytes?: number\n socket?: WebSocket\n model?: GridModel | null\n patchThrottleMs?: number\n}\n\n// API Gateway WebSocket payload size limit is 32 KB per message\n// There is some overhead we aren't computing in our utility, namely the size of the patch header and the communication protocol itself\n// So add some buffer\nconst DEFAULT_MAX_PAYLOAD_SIZE_BYTES = 30 * 1024 // 30 KB\nconst DEFAULT_PATCH_THROTTLE_MS = 250\n\nexport class DataGridWebSocket {\n private socket: WebSocket\n private model: GridModel | null = null\n private messageCounter: MessageCounter\n private replicaId: number\n private verboseEncoder = new VerboseEncoder()\n private maxPayloadSizeBytes: number\n private throttledSendPatch: DebouncedFunc<() => void>\n private snapshotDecoder = new SnapshotDecoder()\n\n private onModelCreate: (model: GridModel) => void\n private onGridReady: () => void\n private onStatusChange: (isOpen: boolean, _this: DataGridWebSocket) => void\n private onReplicaConnected: () => void\n private onReplicaDisconnected: () => void\n private onSyncStart: () => void\n private onSyncEnd: () => void\n private onError: (error: unknown) => void\n\n constructor(args: DataGridWebSocketConstructorArgs) {\n const {\n replicaId,\n url,\n onGridReady,\n onStatusChange,\n onModelCreate,\n onReplicaConnected,\n onReplicaDisconnected,\n onSyncStart,\n onSyncEnd,\n onError,\n maxPayloadSizeBytes,\n socket,\n model,\n patchThrottleMs,\n } = args\n this.messageCounter = new MessageCounter()\n this.replicaId = replicaId\n this.maxPayloadSizeBytes =\n maxPayloadSizeBytes ?? DEFAULT_MAX_PAYLOAD_SIZE_BYTES\n this.socket = socket ?? new WebSocket(url)\n\n // Assign callbacks, falling back to no-ops\n this.onModelCreate = onModelCreate ?? noop\n this.onGridReady = onGridReady ?? noop\n this.onStatusChange = onStatusChange ?? noop\n this.onReplicaConnected = onReplicaConnected ?? noop\n this.onReplicaDisconnected = onReplicaDisconnected ?? noop\n this.onSyncStart = onSyncStart ?? noop\n this.onSyncEnd = onSyncEnd ?? noop\n this.onError = onError ?? noop\n\n // Restore existing model if provided\n if (model) {\n this.model = model\n }\n\n this.attachSocketHandlers()\n\n this.throttledSendPatch = throttle(\n () => this.sendPatchImmediate(),\n patchThrottleMs ?? DEFAULT_PATCH_THROTTLE_MS,\n { leading: false, trailing: true },\n )\n }\n\n private attachSocketHandlers() {\n this.socket.onopen = () => {\n console.debug('Connected to the WebSocket server')\n this.onStatusChange(true, this)\n }\n\n this.socket.onmessage = (event: MessageEvent) => {\n if (typeof event.data === 'string') {\n this.handleMessage(event.data)\n } else {\n console.error('Received non-string message data:', event.data)\n }\n }\n\n this.socket.onclose = () => {\n console.debug('Disconnected from the WebSocket server')\n this.onStatusChange(false, this)\n }\n\n this.socket.onerror = (error: Event) => {\n console.error('WebSocket error:', error)\n }\n }\n\n public disconnect() {\n this.throttledSendPatch.flush()\n this.throttledSendPatch.cancel()\n if (this.socket.readyState === WebSocket.OPEN) {\n this.socket.close()\n console.debug('WebSocket connection closed')\n } else {\n console.warn('WebSocket is not open. No action taken.')\n }\n }\n\n /**\n * Flushes the local model clock and sends the resulting patch (or a clock-sync\n * message) to the server so that both sides converge.\n */\n private sendClockSync() {\n if (!this.model) {\n console.error('Model is not initialized. Cannot sync model.')\n return\n }\n\n const patchesWereSent = this.sendPatchImmediate()\n if (!patchesWereSent) {\n // We've flushed all of our changes, so now we can receive changes from the server\n const verbModel = this.verboseEncoder.encode(this.model)\n this.sendSyncMessage(verbModel.time)\n }\n }\n\n private handleMessage(rawMessage: string) {\n const message = JsonRx.fromJson(JSON.parse(rawMessage))\n if (message instanceof JsonRxResponse) {\n this.handleResponse(\n message as JsonRxResponse<SynapseGridWebSocketMessagePayload>,\n )\n } else if (message instanceof JsonRxResponseComplete) {\n this.handleResponseComplete()\n } else if (message instanceof JsonRxNotification) {\n this.handleNotification(message)\n } else {\n console.warn('Unexpected WebSocket message format:', message)\n }\n }\n\n private handleResponse(\n message: JsonRxResponse<SynapseGridWebSocketMessagePayload>,\n ) {\n const payload = message.getPayload()\n\n switch (payload.type) {\n case SynapseGridWebSocketMessagePayloadType.PATCH:\n this.handlePatchPayload([payload.body])\n break\n\n case SynapseGridWebSocketMessagePayloadType.PATCHES:\n void this.handlePatchPayload(payload.body)\n break\n\n case SynapseGridWebSocketMessagePayloadType.SNAPSHOT:\n void this.handleSnapshotPayload(payload.body)\n break\n\n default:\n console.warn('Unknown payload type:', payload)\n break\n }\n }\n\n private handlePatchPayload(encodedPatch: CompactCodecPatch[]) {\n try {\n const patches = encodedPatch.map(decode)\n if (!this.model) {\n this.model = Model.fromPatches(patches).fork(\n this.replicaId,\n ) as unknown as GridModel\n this.onModelCreate(this.model)\n } else {\n this.model.applyBatch(patches)\n }\n this.sendClockSync()\n } catch (err) {\n console.error('Failed to apply patches or send clock:', err)\n }\n }\n\n private async handleSnapshotPayload(snapshotUrl: string) {\n console.debug('Received snapshot URL from server:', snapshotUrl)\n try {\n const decodedModel = await this.fetchAndDecodeSnapshot(snapshotUrl)\n this.model = decodedModel.fork(this.replicaId) as unknown as GridModel\n this.onModelCreate(this.model)\n this.sendClockSync()\n } catch (err) {\n console.error('Failed to fetch or decode snapshot', err)\n }\n }\n\n private handleResponseComplete() {\n // Clocks are in sync, no further action needed\n this.onGridReady()\n this.onSyncEnd()\n console.debug(\n 'Clocks synchronized with server. Incrementing sequence number.',\n )\n }\n\n private handleNotification(message: JsonRxNotification) {\n const methodName = message.getMethodName()\n console.debug('Notification received from server:', methodName)\n\n switch (methodName) {\n case 'ping':\n console.debug('Received ping from server')\n break\n\n case 'connected':\n console.debug('Server ready to receive patches')\n this.sendSyncMessage(\n this.model ? this.verboseEncoder.encode(this.model).time : undefined,\n )\n break\n\n case 'error':\n console.warn('Error from server:', message.getPayload())\n this.onError(message.getPayload())\n break\n\n case 'new-patch':\n if (!this.model) {\n console.warn(\n \"Model is not initialized. Cannot handle 'new-patch' message.\",\n )\n break\n }\n {\n const verbModel = this.verboseEncoder.encode(this.model)\n console.debug('New patch received, syncing data:', verbModel.time)\n this.sendSyncMessage(verbModel.time)\n }\n break\n\n case 'replica-connected':\n console.debug('A replica connected to the grid session')\n this.onReplicaConnected()\n break\n\n case 'replica-disconnected':\n console.debug('A replica disconnected from the grid session')\n this.onReplicaDisconnected()\n break\n\n default:\n console.warn('Unknown notification method:', methodName)\n break\n }\n }\n\n private sendMessage(message: JsonRxMessage) {\n if (this.socket.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify(message.getJson()))\n } else {\n console.error(\n 'WebSocket is not open. Unable to send message. Current state:',\n this.socket.readyState,\n )\n }\n }\n\n public sendPatch() {\n this.throttledSendPatch()\n }\n\n /**\n * @returns true if one or more patches were sent\n */\n private sendPatchImmediate(): boolean {\n if (!this.model) {\n console.warn('Model is not initialized. Cannot send patch.')\n return false\n }\n\n const patch = this.model.api.flush()\n const hasOperationsToDispatch = patch.ops.length > 0\n\n if (hasOperationsToDispatch) {\n // Split the patch if it exceeds the maximum size we can send in a single frame\n const patches = splitPatch(patch, this.maxPayloadSizeBytes)\n patches.forEach(compactEncodedPatch => {\n console.debug('Sending patch to server:', compactEncodedPatch)\n const msg = new JsonRxRequestComplete(\n this.messageCounter.getNext(),\n 'patch',\n compactEncodedPatch,\n )\n this.sendMessage(msg)\n })\n }\n\n return hasOperationsToDispatch\n }\n\n private sendSyncMessage(clock?: number | JsonCrdtVerboseLogicalTimestamp[]) {\n const message = new JsonRxRequestComplete(\n this.messageCounter.getNext(),\n 'synchronize-clock',\n clock ?? [],\n )\n this.onSyncStart()\n this.sendMessage(message)\n }\n\n private async fetchAndDecodeSnapshot(\n snapshotUrl: string,\n ): Promise<GridModel> {\n const response = await fetchWithExponentialTimeout(snapshotUrl)\n const blob = await response.blob()\n const buffer = await blob.arrayBuffer()\n const decodedFromCbor = decodeCbor(new Uint8Array(buffer))\n const decodedModel = this.snapshotDecoder.decode(\n decodedFromCbor as IndexedFields,\n ) as unknown as GridModel\n return decodedModel\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataGridWebSocket.js","names":["GridModel","JsonRx","JsonRxMessage","JsonRxNotification","JsonRxRequestComplete","JsonRxResponse","JsonRxResponseComplete","MessageCounter","splitPatch","Model","CompactCodecPatch","decode","Encoder","VerboseEncoder","JsonCrdtVerboseLogicalTimestamp","noop","throttle","DebouncedFunc","Decoder","SnapshotDecoder","decodeCbor","IndexedFields","fetchWithExponentialTimeout","SynapseGridWebSocketMessagePayloadType","PATCH","SNAPSHOT","PATCHES","SynapseGridWebSocketMessagePayload","type","body","DataGridWebSocketConstructorArgs","replicaId","url","onGridReady","onStatusChange","isOpen","instance","DataGridWebSocket","onModelCreate","model","onReplicaConnected","onReplicaDisconnected","onSyncStart","onSyncEnd","onError","error","maxPayloadSizeBytes","socket","WebSocket","patchThrottleMs","pendingPatches","patchAckTimeoutMs","DEFAULT_MAX_PAYLOAD_SIZE_BYTES","DEFAULT_PATCH_THROTTLE_MS","DEFAULT_PATCH_ACK_TIMEOUT_MS","messageCounter","verboseEncoder","throttledSendPatch","snapshotDecoder","unacknowledgedPatches","inFlightPatchRequestId","patchAckTimer","isClosing","isSyncExchangeOpen","_this","constructor","args","attachSocketHandlers","sendPatchImmediate","leading","trailing","onopen","console","debug","onmessage","event","MessageEvent","data","handleMessage","onclose","stopWaitingForPatchAck","cancel","onerror","Event","disconnect","readyState","OPEN","warn","flush","length","closeSocket","close","getPendingPatches","sendClockSync","patchesWereSent","verbModel","encode","sendSyncMessage","time","rawMessage","message","fromJson","JSON","parse","handleResponse","handleResponseComplete","handleNotification","payload","getPayload","handlePatchPayload","handleSnapshotPayload","encodedPatch","patches","map","fromPatches","fork","applyBatch","err","snapshotUrl","decodedModel","fetchAndDecodeSnapshot","getSubscriptionId","handlePatchAcknowledged","shift","sendNextPatch","methodName","getMethodName","undefined","sendMessage","send","stringify","getJson","sendPatch","patch","api","hasOperationsToDispatch","ops","push","nextPatch","requestId","getNext","window","setTimeout","handlePatchAckTimeout","clearTimeout","clock","Promise","response","blob","buffer","arrayBuffer","decodedFromCbor","Uint8Array"],"sources":["../../../src/components/DataGrid/DataGridWebSocket.ts"],"sourcesContent":["import { GridModel } from '@/components/DataGrid/DataGridTypes'\nimport JsonRx from '@/components/DataGrid/utils/json-rx/JsonRx'\nimport JsonRxMessage from '@/components/DataGrid/utils/json-rx/JsonRxMessage'\nimport JsonRxNotification from '@/components/DataGrid/utils/json-rx/JsonRxNotification'\nimport JsonRxRequestComplete from '@/components/DataGrid/utils/json-rx/JsonRxRequestComplete'\nimport JsonRxResponse from '@/components/DataGrid/utils/json-rx/JsonRxResponse'\nimport JsonRxResponseComplete from '@/components/DataGrid/utils/json-rx/JsonRxResponseComplete'\nimport MessageCounter from '@/components/DataGrid/utils/MessageCounter'\nimport { splitPatch } from '@/components/DataGrid/utils/splitPatch'\nimport { Model } from 'json-joy/lib/json-crdt'\nimport {\n CompactCodecPatch,\n decode,\n} from 'json-joy/lib/json-crdt-patch/codec/compact'\nimport { Encoder as VerboseEncoder } from 'json-joy/lib/json-crdt/codec/structural/verbose/Encoder'\nimport { JsonCrdtVerboseLogicalTimestamp } from 'json-joy/lib/json-crdt/codec/structural/verbose/types'\nimport noop from 'lodash-es/noop'\nimport throttle from 'lodash-es/throttle'\nimport { type DebouncedFunc } from 'lodash-es'\nimport { Decoder as SnapshotDecoder } from 'json-joy/lib/json-crdt/codec/indexed/binary/Decoder'\nimport { decode as decodeCbor } from 'cbor2'\nimport type { IndexedFields } from 'json-joy/lib/json-crdt/codec/indexed/binary'\nimport { fetchWithExponentialTimeout } from '@sage-bionetworks/synapse-client'\n\nenum SynapseGridWebSocketMessagePayloadType {\n PATCH = 'patch',\n SNAPSHOT = 'snapshot',\n PATCHES = 'patches',\n}\n\ntype SynapseGridWebSocketMessagePayload =\n | {\n type: SynapseGridWebSocketMessagePayloadType.PATCH\n body: CompactCodecPatch\n }\n | {\n type: SynapseGridWebSocketMessagePayloadType.PATCHES\n body: CompactCodecPatch[]\n }\n | { type: SynapseGridWebSocketMessagePayloadType.SNAPSHOT; body: string }\n\ntype DataGridWebSocketConstructorArgs = {\n replicaId: number\n url: string\n onGridReady?: () => void\n onStatusChange?: (isOpen: boolean, instance: DataGridWebSocket) => void\n onModelCreate?: (model: GridModel) => void\n onReplicaConnected?: () => void\n onReplicaDisconnected?: () => void\n onSyncStart?: () => void\n onSyncEnd?: () => void\n onError?: (error: unknown) => void\n maxPayloadSizeBytes?: number\n socket?: WebSocket\n model?: GridModel | null\n patchThrottleMs?: number\n /** Patches the previous connection never saw acknowledged, resent in order ahead of new ones */\n pendingPatches?: CompactCodecPatch[]\n patchAckTimeoutMs?: number\n}\n\n// API Gateway WebSocket payload size limit is 32 KB per message\n// There is some overhead we aren't computing in our utility, namely the size of the patch header and the communication protocol itself\n// So add some buffer\nconst DEFAULT_MAX_PAYLOAD_SIZE_BYTES = 30 * 1024 // 30 KB\nconst DEFAULT_PATCH_THROTTLE_MS = 250\n// A patch is acknowledged only after the server has stored it, so a missing acknowledgement means the connection is unusable\nconst DEFAULT_PATCH_ACK_TIMEOUT_MS = 15 * 1000\n\nexport class DataGridWebSocket {\n private socket: WebSocket\n private model: GridModel | null = null\n private messageCounter: MessageCounter\n private replicaId: number\n private verboseEncoder = new VerboseEncoder()\n private maxPayloadSizeBytes: number\n private throttledSendPatch: DebouncedFunc<() => void>\n private snapshotDecoder = new SnapshotDecoder()\n // Oldest first. The server must receive patches in order, so only the head is ever on the wire\n // (identified by inFlightPatchRequestId); the rest wait for its acknowledgement.\n private unacknowledgedPatches: CompactCodecPatch[]\n private inFlightPatchRequestId: number | null = null\n private patchAckTimeoutMs: number\n private patchAckTimer: number | null = null\n // Set once disconnect() has been requested: the socket closes when the last patch is acknowledged\n private isClosing = false\n private isSyncExchangeOpen = false\n\n private onModelCreate: (model: GridModel) => void\n private onGridReady: () => void\n private onStatusChange: (isOpen: boolean, _this: DataGridWebSocket) => void\n private onReplicaConnected: () => void\n private onReplicaDisconnected: () => void\n private onSyncStart: () => void\n private onSyncEnd: () => void\n private onError: (error: unknown) => void\n\n constructor(args: DataGridWebSocketConstructorArgs) {\n const {\n replicaId,\n url,\n onGridReady,\n onStatusChange,\n onModelCreate,\n onReplicaConnected,\n onReplicaDisconnected,\n onSyncStart,\n onSyncEnd,\n onError,\n maxPayloadSizeBytes,\n socket,\n model,\n patchThrottleMs,\n pendingPatches,\n patchAckTimeoutMs,\n } = args\n this.messageCounter = new MessageCounter()\n this.replicaId = replicaId\n this.maxPayloadSizeBytes =\n maxPayloadSizeBytes ?? DEFAULT_MAX_PAYLOAD_SIZE_BYTES\n this.socket = socket ?? new WebSocket(url)\n this.unacknowledgedPatches = [...(pendingPatches ?? [])]\n this.patchAckTimeoutMs = patchAckTimeoutMs ?? DEFAULT_PATCH_ACK_TIMEOUT_MS\n\n // Assign callbacks, falling back to no-ops\n this.onModelCreate = onModelCreate ?? noop\n this.onGridReady = onGridReady ?? noop\n this.onStatusChange = onStatusChange ?? noop\n this.onReplicaConnected = onReplicaConnected ?? noop\n this.onReplicaDisconnected = onReplicaDisconnected ?? noop\n this.onSyncStart = onSyncStart ?? noop\n this.onSyncEnd = onSyncEnd ?? noop\n this.onError = onError ?? noop\n\n // Restore existing model if provided\n if (model) {\n this.model = model\n }\n\n this.attachSocketHandlers()\n\n this.throttledSendPatch = throttle(\n () => this.sendPatchImmediate(),\n patchThrottleMs ?? DEFAULT_PATCH_THROTTLE_MS,\n { leading: false, trailing: true },\n )\n }\n\n private attachSocketHandlers() {\n this.socket.onopen = () => {\n console.debug('Connected to the WebSocket server')\n this.onStatusChange(true, this)\n }\n\n this.socket.onmessage = (event: MessageEvent) => {\n if (typeof event.data === 'string') {\n this.handleMessage(event.data)\n } else {\n console.error('Received non-string message data:', event.data)\n }\n }\n\n this.socket.onclose = () => {\n console.debug('Disconnected from the WebSocket server')\n this.stopWaitingForPatchAck()\n // Edits not yet flushed stay in the model for the replacement connection to send\n this.throttledSendPatch.cancel()\n this.onStatusChange(false, this)\n }\n\n this.socket.onerror = (error: Event) => {\n console.error('WebSocket error:', error)\n }\n }\n\n /**\n * Closes the connection once every unacknowledged patch has been acknowledged, so edits made\n * just before leaving are not lost. The owner has moved on, so no further callbacks fire.\n */\n public disconnect() {\n if (this.socket.readyState !== WebSocket.OPEN) {\n // Edits that were never flushed stay in the model for the next connection to send\n this.throttledSendPatch.cancel()\n console.warn('WebSocket is not open. No action taken.')\n return\n }\n\n this.throttledSendPatch.flush()\n this.throttledSendPatch.cancel()\n this.isClosing = true\n this.onModelCreate = noop\n this.onGridReady = noop\n this.onStatusChange = noop\n this.onReplicaConnected = noop\n this.onReplicaDisconnected = noop\n this.onSyncStart = noop\n this.onSyncEnd = noop\n this.onError = noop\n if (this.unacknowledgedPatches.length === 0) {\n this.closeSocket()\n }\n }\n\n private closeSocket() {\n this.socket.close()\n console.debug('WebSocket connection closed')\n }\n\n /**\n * @returns the patches the server has not acknowledged, oldest first. A replacement connection\n * created with these as `pendingPatches` resends them in order.\n */\n public getPendingPatches(): CompactCodecPatch[] {\n return [...this.unacknowledgedPatches]\n }\n\n /**\n * Flushes the local model clock and sends the resulting patch (or a clock-sync\n * message) to the server so that both sides converge.\n */\n private sendClockSync() {\n if (!this.model) {\n console.error('Model is not initialized. Cannot sync model.')\n return\n }\n\n const patchesWereSent = this.sendPatchImmediate()\n if (!patchesWereSent) {\n // We've flushed all of our changes, so now we can receive changes from the server\n const verbModel = this.verboseEncoder.encode(this.model)\n this.sendSyncMessage(verbModel.time)\n }\n }\n\n private handleMessage(rawMessage: string) {\n const message = JsonRx.fromJson(JSON.parse(rawMessage))\n if (this.isClosing && !(message instanceof JsonRxResponseComplete)) {\n return\n }\n if (message instanceof JsonRxResponse) {\n this.handleResponse(\n message as JsonRxResponse<SynapseGridWebSocketMessagePayload>,\n )\n } else if (message instanceof JsonRxResponseComplete) {\n this.handleResponseComplete(message)\n } else if (message instanceof JsonRxNotification) {\n this.handleNotification(message)\n } else {\n console.warn('Unexpected WebSocket message format:', message)\n }\n }\n\n private handleResponse(\n message: JsonRxResponse<SynapseGridWebSocketMessagePayload>,\n ) {\n const payload = message.getPayload()\n\n switch (payload.type) {\n case SynapseGridWebSocketMessagePayloadType.PATCH:\n this.handlePatchPayload([payload.body])\n break\n\n case SynapseGridWebSocketMessagePayloadType.PATCHES:\n void this.handlePatchPayload(payload.body)\n break\n\n case SynapseGridWebSocketMessagePayloadType.SNAPSHOT:\n void this.handleSnapshotPayload(payload.body)\n break\n\n default:\n console.warn('Unknown payload type:', payload)\n break\n }\n }\n\n private handlePatchPayload(encodedPatch: CompactCodecPatch[]) {\n try {\n const patches = encodedPatch.map(decode)\n if (!this.model) {\n this.model = Model.fromPatches(patches).fork(\n this.replicaId,\n ) as unknown as GridModel\n this.onModelCreate(this.model)\n } else {\n this.model.applyBatch(patches)\n }\n this.sendClockSync()\n } catch (err) {\n console.error('Failed to apply patches or send clock:', err)\n }\n }\n\n private async handleSnapshotPayload(snapshotUrl: string) {\n console.debug('Received snapshot URL from server:', snapshotUrl)\n try {\n const decodedModel = await this.fetchAndDecodeSnapshot(snapshotUrl)\n this.model = decodedModel.fork(this.replicaId) as unknown as GridModel\n this.onModelCreate(this.model)\n this.sendClockSync()\n } catch (err) {\n console.error('Failed to fetch or decode snapshot', err)\n }\n }\n\n private handleResponseComplete(message: JsonRxResponseComplete) {\n if (message.getSubscriptionId() === this.inFlightPatchRequestId) {\n this.handlePatchAcknowledged()\n return\n }\n this.isSyncExchangeOpen = false\n // Clocks are in sync, no further action needed\n this.onGridReady()\n this.onSyncEnd()\n console.debug(\n 'Clocks synchronized with server. Incrementing sequence number.',\n )\n }\n\n private handlePatchAcknowledged() {\n this.unacknowledgedPatches.shift()\n this.stopWaitingForPatchAck()\n if (this.unacknowledgedPatches.length > 0) {\n this.sendNextPatch()\n } else if (this.isClosing) {\n this.closeSocket()\n } else if (this.isSyncExchangeOpen && this.model) {\n // The sync was answered with local patches instead of a clock. Now that they are stored,\n // confirm the clock so the server can complete the exchange.\n this.sendSyncMessage(this.verboseEncoder.encode(this.model).time)\n }\n }\n\n private handleNotification(message: JsonRxNotification) {\n const methodName = message.getMethodName()\n console.debug('Notification received from server:', methodName)\n\n switch (methodName) {\n case 'ping':\n console.debug('Received ping from server')\n break\n\n case 'connected':\n console.debug('Server ready to receive patches')\n this.sendSyncMessage(\n this.model ? this.verboseEncoder.encode(this.model).time : undefined,\n )\n // Resend patches an earlier connection never saw acknowledged\n this.sendNextPatch()\n break\n\n case 'error':\n console.warn('Error from server:', message.getPayload())\n this.onError(message.getPayload())\n break\n\n case 'new-patch':\n if (!this.model) {\n console.warn(\n \"Model is not initialized. Cannot handle 'new-patch' message.\",\n )\n break\n }\n {\n const verbModel = this.verboseEncoder.encode(this.model)\n console.debug('New patch received, syncing data:', verbModel.time)\n this.sendSyncMessage(verbModel.time)\n }\n break\n\n case 'replica-connected':\n console.debug('A replica connected to the grid session')\n this.onReplicaConnected()\n break\n\n case 'replica-disconnected':\n console.debug('A replica disconnected from the grid session')\n this.onReplicaDisconnected()\n break\n\n default:\n console.warn('Unknown notification method:', methodName)\n break\n }\n }\n\n private sendMessage(message: JsonRxMessage) {\n if (this.socket.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify(message.getJson()))\n } else {\n console.error(\n 'WebSocket is not open. Unable to send message. Current state:',\n this.socket.readyState,\n )\n }\n }\n\n public sendPatch() {\n this.throttledSendPatch()\n }\n\n /**\n * @returns true if the model had local changes, which are now queued to be sent\n */\n private sendPatchImmediate(): boolean {\n if (!this.model) {\n console.warn('Model is not initialized. Cannot send patch.')\n return false\n }\n\n const patch = this.model.api.flush()\n const hasOperationsToDispatch = patch.ops.length > 0\n\n if (hasOperationsToDispatch) {\n // Split the patch if it exceeds the maximum size we can send in a single frame\n this.unacknowledgedPatches.push(\n ...splitPatch(patch, this.maxPayloadSizeBytes),\n )\n this.sendNextPatch()\n }\n\n return hasOperationsToDispatch\n }\n\n private sendNextPatch() {\n const nextPatch = this.unacknowledgedPatches[0]\n if (\n nextPatch === undefined ||\n this.inFlightPatchRequestId !== null ||\n this.socket.readyState !== WebSocket.OPEN\n ) {\n return\n }\n console.debug('Sending patch to server:', nextPatch)\n const requestId = this.messageCounter.getNext()\n this.inFlightPatchRequestId = requestId\n this.patchAckTimer = window.setTimeout(\n () => this.handlePatchAckTimeout(),\n this.patchAckTimeoutMs,\n )\n this.sendMessage(new JsonRxRequestComplete(requestId, 'patch', nextPatch))\n }\n\n private stopWaitingForPatchAck() {\n if (this.patchAckTimer !== null) {\n window.clearTimeout(this.patchAckTimer)\n this.patchAckTimer = null\n }\n this.inFlightPatchRequestId = null\n }\n\n /**\n * Resending on the same connection could reorder patches, so the connection is dropped and\n * the owner reconnects with getPendingPatches(). The server ignores patches it already stored.\n */\n private handlePatchAckTimeout() {\n console.warn(\n 'Timed out waiting for a patch acknowledgement. Dropping the connection so unacknowledged patches are resent.',\n )\n // Detach handlers so late events from the abandoned socket can't interleave with its replacement\n this.socket.onopen = null\n this.socket.onmessage = null\n this.socket.onclose = null\n this.socket.onerror = null\n this.throttledSendPatch.cancel()\n this.stopWaitingForPatchAck()\n this.socket.close()\n this.onStatusChange(false, this)\n }\n\n private sendSyncMessage(clock?: number | JsonCrdtVerboseLogicalTimestamp[]) {\n const message = new JsonRxRequestComplete(\n this.messageCounter.getNext(),\n 'synchronize-clock',\n clock ?? [],\n )\n this.isSyncExchangeOpen = true\n this.onSyncStart()\n this.sendMessage(message)\n }\n\n private async fetchAndDecodeSnapshot(\n snapshotUrl: string,\n ): Promise<GridModel> {\n const response = await fetchWithExponentialTimeout(snapshotUrl)\n const blob = await response.blob()\n const buffer = await blob.arrayBuffer()\n const decodedFromCbor = decodeCbor(new Uint8Array(buffer))\n const decodedModel = this.snapshotDecoder.decode(\n decodedFromCbor as IndexedFields,\n ) as unknown as GridModel\n return decodedModel\n }\n}\n"],"mappings":"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"}
@@ -4,11 +4,11 @@ export type GridLoadingStateProps = {
4
4
  isConnected: boolean;
5
5
  };
6
6
  export declare const GRID_LOADING_MESSAGES: {
7
- readonly heading: "Setting up grid";
7
+ readonly heading: "Preparing your grid";
8
8
  readonly awaitingReplica: "Setting up real-time sync…";
9
9
  readonly awaitingPresignedUrl: "Establishing secure connection…";
10
10
  readonly awaitingConnection: "Connecting to server…";
11
- readonly awaitingData: "Loading table data…";
11
+ readonly awaitingData: "Loading metadata and validation results. Large datasets may take up to 5 minutes to load. Please keep this page open.";
12
12
  };
13
13
  /**
14
14
  * Describes how far along the grid is in establishing its first connection.
@@ -1 +1 @@
1
- {"version":3,"file":"GridLoadingState.d.ts","sourceRoot":"","sources":["../../../../src/components/DataGrid/components/GridLoadingState.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,EAAE,OAAO,CAAA;IACrB,eAAe,EAAE,OAAO,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,eAAO,MAAM,qBAAqB;;;;;;CAMxB,CAAA;AAEV;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,YAAY,EACZ,eAAe,EACf,WAAW,GACZ,EAAE,qBAAqB,GAAG,MAAM,CAWhC;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,2CAepE"}
1
+ {"version":3,"file":"GridLoadingState.d.ts","sourceRoot":"","sources":["../../../../src/components/DataGrid/components/GridLoadingState.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,EAAE,OAAO,CAAA;IACrB,eAAe,EAAE,OAAO,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,eAAO,MAAM,qBAAqB;;;;;;CAOxB,CAAA;AAEV;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,YAAY,EACZ,eAAe,EACf,WAAW,GACZ,EAAE,qBAAqB,GAAG,MAAM,CAWhC;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,2CAepE"}
@@ -5,11 +5,11 @@ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
5
  import { c as o } from "react/compiler-runtime";
6
6
  //#region src/components/DataGrid/components/GridLoadingState.tsx
7
7
  var s = {
8
- heading: "Setting up grid",
8
+ heading: "Preparing your grid",
9
9
  awaitingReplica: "Setting up real-time sync…",
10
10
  awaitingPresignedUrl: "Establishing secure connection…",
11
11
  awaitingConnection: "Connecting to server…",
12
- awaitingData: "Loading table data…"
12
+ awaitingData: "Loading metadata and validation results. Large datasets may take up to 5 minutes to load. Please keep this page open."
13
13
  };
14
14
  function c({ hasReplicaId: e, hasPresignedUrl: t, isConnected: n }) {
15
15
  return e ? t ? n ? s.awaitingData : s.awaitingConnection : s.awaitingPresignedUrl : s.awaitingReplica;
@@ -1 +1 @@
1
- {"version":3,"file":"GridLoadingState.js","names":["SkeletonTable","Stack","Typography","SynapseSpinner","GridLoadingStateProps","hasReplicaId","hasPresignedUrl","isConnected","GRID_LOADING_MESSAGES","heading","awaitingReplica","awaitingPresignedUrl","awaitingConnection","awaitingData","const","getGridLoadingMessage","GridLoadingState","props","$","_c","t0","t1","Symbol","for","mb","t2","t3","t4","t5","t6"],"sources":["../../../../src/components/DataGrid/components/GridLoadingState.tsx"],"sourcesContent":["import { SkeletonTable } from '@/components/Skeleton/SkeletonTable'\nimport { Stack, Typography } from '@mui/material'\nimport { SynapseSpinner } from '../../LoadingScreen/LoadingScreen'\n\nexport type GridLoadingStateProps = {\n hasReplicaId: boolean\n hasPresignedUrl: boolean\n isConnected: boolean\n}\n\nexport const GRID_LOADING_MESSAGES = {\n heading: 'Setting up grid',\n awaitingReplica: 'Setting up real-time sync…',\n awaitingPresignedUrl: 'Establishing secure connection…',\n awaitingConnection: 'Connecting to server…',\n awaitingData: 'Loading table data…',\n} as const\n\n/**\n * Describes how far along the grid is in establishing its first connection.\n */\nexport function getGridLoadingMessage({\n hasReplicaId,\n hasPresignedUrl,\n isConnected,\n}: GridLoadingStateProps): string {\n if (!hasReplicaId) {\n return GRID_LOADING_MESSAGES.awaitingReplica\n }\n if (!hasPresignedUrl) {\n return GRID_LOADING_MESSAGES.awaitingPresignedUrl\n }\n if (!isConnected) {\n return GRID_LOADING_MESSAGES.awaitingConnection\n }\n return GRID_LOADING_MESSAGES.awaitingData\n}\n\n/**\n * Placeholder shown in place of the grid until its first connection has finished\n * loading data. Rendering this instead of the grid keeps users from editing cells\n * or watching rows stream in while the initial data is still being replayed.\n */\nexport default function GridLoadingState(props: GridLoadingStateProps) {\n return (\n <Stack spacing={1} aria-busy=\"true\" sx={{ mb: 1.25 }}>\n <Typography variant=\"sectionTitle\" component=\"h2\">\n {GRID_LOADING_MESSAGES.heading}\n </Typography>\n <Stack direction=\"row\" spacing={1} role=\"status\" alignItems=\"center\">\n <SynapseSpinner size={16} margin=\"0\" />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {getGridLoadingMessage(props)}\n </Typography>\n </Stack>\n <SkeletonTable aria-hidden numRows={4} numCols={1} />\n </Stack>\n )\n}\n"],"mappings":";;;;;;AAUA,IAAaQ,IAAwB;CACnCC,SAAS;CACTC,iBAAiB;CACjBC,sBAAsB;CACtBC,oBAAoB;CACpBC,cAAc;AAChB;AAKA,SAAgBE,EAAsB,EACpCV,iBACAC,oBACAC,kBACgC;CAUhC,OATKF,IAGAC,IAGAC,IAGEC,EAAsBK,eAFpBL,EAAsBI,qBAHtBJ,EAAsBG,uBAHtBH,EAAsBE;AASjC;AAOA,SAAeM,EAAAC,GAAA;CAAA,IAAAC,IAAAC,EAAA,EAAA,GAAAC,GAAAC;CAAA,AAAAH,EAAA,OAAAI,OAAAC,IAAA,2BAAA,KAE6BH,IAAA,EAAAI,IAAM,KAAK,GACjDH,IAAA,kBAAC,GAAD;EAAoB,SAAA;EAAyB,WAAA;YAC1Cb,EAAqBC;CADb,CAAA,GAEES,EAAA,KAAAE,GAAAF,EAAA,KAAAG,MAAAD,IAAAF,EAAA,IAAAG,IAAAH,EAAA;CAAA,IAAAO;CAAA,AAAAP,EAAA,OAAAI,OAAAC,IAAA,2BAAA,KAEXE,IAAA,kBAAC,GAAD;EAAsB,MAAA;EAAW,QAAA;CAAG,CAAA,GAAGP,EAAA,KAAAO,KAAAA,IAAAP,EAAA;CAAA,IAAAQ;CAAA,AAAAR,EAAA,OAAAD,IAERS,IAAAR,EAAA,MAA5BQ,IAAAX,EAAsBE,CAAK,GAACC,EAAA,KAAAD,GAAAC,EAAA,KAAAQ;CAAA,IAAAC;CAAA,AAAAT,EAAA,OAAAQ,IAEzBC,IAAAT,EAAA,MALRS,IAAA,kBAAC,GAAD;EAAiB,WAAA;EAAe,SAAA;EAAQ,MAAA;EAAoB,YAAA;YAA5D,CACEF,GACA,kBAAC,GAAD;GAAoB,SAAA;GAAgB,OAAA;aACjCC;EADQ,CAAA,CAFP;KAKER,EAAA,KAAAQ,GAAAR,EAAA,KAAAS;CAAA,IAAAC;CAAA,AAAAV,EAAA,OAAAI,OAAAC,IAAA,2BAAA,KACRK,IAAA,kBAAC,GAAD;EAAe,eAAA;EAAqB,SAAA;EAAY,SAAA;CAAC,CAAA,GAAIV,EAAA,KAAAU,KAAAA,IAAAV,EAAA;CAAA,IAAAW;CAC/C,OAD+CX,EAAA,OAAAS,IAC/CE,IAAAX,EAAA,MAXRW,IAAA,kBAAC,GAAD;EAAgB,SAAA;EAAa,aAAA;EAAW,IAAAT;YAAxC;GACEC;GAGAM;GAMAC;EAVI;KAWEV,EAAA,KAAAS,GAAAT,EAAA,KAAAW,IAXRA;AAWQ"}
1
+ {"version":3,"file":"GridLoadingState.js","names":["SkeletonTable","Stack","Typography","SynapseSpinner","GridLoadingStateProps","hasReplicaId","hasPresignedUrl","isConnected","GRID_LOADING_MESSAGES","heading","awaitingReplica","awaitingPresignedUrl","awaitingConnection","awaitingData","const","getGridLoadingMessage","GridLoadingState","props","$","_c","t0","t1","Symbol","for","mb","t2","t3","t4","t5","t6"],"sources":["../../../../src/components/DataGrid/components/GridLoadingState.tsx"],"sourcesContent":["import { SkeletonTable } from '@/components/Skeleton/SkeletonTable'\nimport { Stack, Typography } from '@mui/material'\nimport { SynapseSpinner } from '../../LoadingScreen/LoadingScreen'\n\nexport type GridLoadingStateProps = {\n hasReplicaId: boolean\n hasPresignedUrl: boolean\n isConnected: boolean\n}\n\nexport const GRID_LOADING_MESSAGES = {\n heading: 'Preparing your grid',\n awaitingReplica: 'Setting up real-time sync…',\n awaitingPresignedUrl: 'Establishing secure connection…',\n awaitingConnection: 'Connecting to server…',\n awaitingData:\n 'Loading metadata and validation results. Large datasets may take up to 5 minutes to load. Please keep this page open.',\n} as const\n\n/**\n * Describes how far along the grid is in establishing its first connection.\n */\nexport function getGridLoadingMessage({\n hasReplicaId,\n hasPresignedUrl,\n isConnected,\n}: GridLoadingStateProps): string {\n if (!hasReplicaId) {\n return GRID_LOADING_MESSAGES.awaitingReplica\n }\n if (!hasPresignedUrl) {\n return GRID_LOADING_MESSAGES.awaitingPresignedUrl\n }\n if (!isConnected) {\n return GRID_LOADING_MESSAGES.awaitingConnection\n }\n return GRID_LOADING_MESSAGES.awaitingData\n}\n\n/**\n * Placeholder shown in place of the grid until its first connection has finished\n * loading data. Rendering this instead of the grid keeps users from editing cells\n * or watching rows stream in while the initial data is still being replayed.\n */\nexport default function GridLoadingState(props: GridLoadingStateProps) {\n return (\n <Stack spacing={1} aria-busy=\"true\" sx={{ mb: 1.25 }}>\n <Typography variant=\"sectionTitle\" component=\"h2\">\n {GRID_LOADING_MESSAGES.heading}\n </Typography>\n <Stack direction=\"row\" spacing={1} role=\"status\" alignItems=\"center\">\n <SynapseSpinner size={16} margin=\"0\" />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {getGridLoadingMessage(props)}\n </Typography>\n </Stack>\n <SkeletonTable aria-hidden numRows={4} numCols={1} />\n </Stack>\n )\n}\n"],"mappings":";;;;;;AAUA,IAAaQ,IAAwB;CACnCC,SAAS;CACTC,iBAAiB;CACjBC,sBAAsB;CACtBC,oBAAoB;CACpBC,cACE;AACJ;AAKA,SAAgBE,EAAsB,EACpCV,iBACAC,oBACAC,kBACgC;CAUhC,OATKF,IAGAC,IAGAC,IAGEC,EAAsBK,eAFpBL,EAAsBI,qBAHtBJ,EAAsBG,uBAHtBH,EAAsBE;AASjC;AAOA,SAAeM,EAAAC,GAAA;CAAA,IAAAC,IAAAC,EAAA,EAAA,GAAAC,GAAAC;CAAA,AAAAH,EAAA,OAAAI,OAAAC,IAAA,2BAAA,KAE6BH,IAAA,EAAAI,IAAM,KAAK,GACjDH,IAAA,kBAAC,GAAD;EAAoB,SAAA;EAAyB,WAAA;YAC1Cb,EAAqBC;CADb,CAAA,GAEES,EAAA,KAAAE,GAAAF,EAAA,KAAAG,MAAAD,IAAAF,EAAA,IAAAG,IAAAH,EAAA;CAAA,IAAAO;CAAA,AAAAP,EAAA,OAAAI,OAAAC,IAAA,2BAAA,KAEXE,IAAA,kBAAC,GAAD;EAAsB,MAAA;EAAW,QAAA;CAAG,CAAA,GAAGP,EAAA,KAAAO,KAAAA,IAAAP,EAAA;CAAA,IAAAQ;CAAA,AAAAR,EAAA,OAAAD,IAERS,IAAAR,EAAA,MAA5BQ,IAAAX,EAAsBE,CAAK,GAACC,EAAA,KAAAD,GAAAC,EAAA,KAAAQ;CAAA,IAAAC;CAAA,AAAAT,EAAA,OAAAQ,IAEzBC,IAAAT,EAAA,MALRS,IAAA,kBAAC,GAAD;EAAiB,WAAA;EAAe,SAAA;EAAQ,MAAA;EAAoB,YAAA;YAA5D,CACEF,GACA,kBAAC,GAAD;GAAoB,SAAA;GAAgB,OAAA;aACjCC;EADQ,CAAA,CAFP;KAKER,EAAA,KAAAQ,GAAAR,EAAA,KAAAS;CAAA,IAAAC;CAAA,AAAAV,EAAA,OAAAI,OAAAC,IAAA,2BAAA,KACRK,IAAA,kBAAC,GAAD;EAAe,eAAA;EAAqB,SAAA;EAAY,SAAA;CAAC,CAAA,GAAIV,EAAA,KAAAU,KAAAA,IAAAV,EAAA;CAAA,IAAAW;CAC/C,OAD+CX,EAAA,OAAAS,IAC/CE,IAAAX,EAAA,MAXRW,IAAA,kBAAC,GAAD;EAAgB,SAAA;EAAa,aAAA;EAAW,IAAAT;YAAxC;GACEC;GAGAM;GAMAC;EAVI;KAWEV,EAAA,KAAAS,GAAAT,EAAA,KAAAW,IAXRA;AAWQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDataGridWebsocket.d.ts","sourceRoot":"","sources":["../../../src/components/DataGrid/useDataGridWebsocket.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,iBAAiB,EAClB,MAAM,qCAAqC,CAAA;AAI5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAKvD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACvB;;;;;OAKG;IACH,uBAAuB,EAAE,OAAO,CAAA;IAChC;;;;;OAKG;IACH,uBAAuB,EAAE,OAAO,CAAA;IAChC;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,OAAO,CAAA;IACrB,gBAAgB,EAAE;QAChB,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;KAClB,GAAG,IAAI,CAAA;IACR,iBAAiB,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC3C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,EAAE,OAAO,CAAA;CACxB;AAGD,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,mBAAmB,CAAA;IACzB,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CACrE,GACD;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,uBAAuB,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,SAAS,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAGjD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,eAAe,GACtB,cAAc,CAyGhB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAYnC,CAAA;AAmBD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,EACrB,aAAa,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,GAClD,OAAO,CAOT;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,2BAA2B;IAC1C,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC/B,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAA;CACnC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAyD1D,MAAM,aAAa,MAAM;;;;EAuIxC"}
1
+ {"version":3,"file":"useDataGridWebsocket.d.ts","sourceRoot":"","sources":["../../../src/components/DataGrid/useDataGridWebsocket.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,iBAAiB,EAClB,MAAM,qCAAqC,CAAA;AAI5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAKvD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACvB;;;;;OAKG;IACH,uBAAuB,EAAE,OAAO,CAAA;IAChC;;;;;OAKG;IACH,uBAAuB,EAAE,OAAO,CAAA;IAChC;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,OAAO,CAAA;IACrB,gBAAgB,EAAE;QAChB,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;KAClB,GAAG,IAAI,CAAA;IACR,iBAAiB,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC3C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,EAAE,OAAO,CAAA;CACxB;AAGD,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,mBAAmB,CAAA;IACzB,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CACrE,GACD;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,uBAAuB,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,SAAS,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAGjD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,eAAe,GACtB,cAAc,CAyGhB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAYnC,CAAA;AAmBD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,EACrB,aAAa,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,GAClD,OAAO,CAOT;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,2BAA2B;IAC1C,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC/B,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAA;CACnC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAyD1D,MAAM,aAAa,MAAM;;;;EA2IxC"}
@@ -144,7 +144,8 @@ function p(d) {
144
144
  sessionId: e.sessionId,
145
145
  websocketOptions: {
146
146
  ...E,
147
- model: p.model
147
+ model: p.model,
148
+ pendingPatches: p.model ? p.websocketInstance?.getPendingPatches() : void 0
148
149
  }
149
150
  }).then((e) => {
150
151
  if (g.current !== t) {
@@ -1 +1 @@
1
- {"version":3,"file":"useDataGridWebsocket.js","names":["GridModel","GridModelSnapshot","useCRDTModelView","normalizeWebsocketError","useCallback","useEffect","useMemo","useReducer","useRef","DataGridWebSocket","useEstablishWebsocketConnection","useDocumentVisibility","WebSocketState","model","hasCompletedInitialSync","hasCompletedInitialLoad","isSyncing","isConnected","isConnecting","connectionParams","replicaId","sessionId","websocketInstance","connectionAttemptId","connectionError","websocketError","WebSocketAction","type","payload","attemptId","websocketReducer","state","action","isSameConnection","initialWebSocketState","isModelRenderable","modelSnapshot","api","getSnapshot","columnNames","columnOrder","rows","columnsReady","length","orderReady","rowsReady","isInitialLoadReady","UseDataGridWebSocketOptions","onGridReady","onReplicaConnected","onReplicaDisconnected","useDataGridWebSocket","options","dispatch","connectionAttemptCounter","activeConnectionAttemptIdRef","isDocumentVisible","mutateAsync","establishWebsocketConnection","isPending","isEstablishingWebsocketConnection","error","errorEstablishingWebsocketConnection","presignedUrl","reset","resetEstablishWebsocketConnection","clearPresignedUrl","handleModelCreate","newModel","websocketOptionsWithoutModel","onStatusChange","open","onModelCreate","onSyncStart","onSyncEnd","onError","connect","isDifferentConnection","current","websocketOptions","then","ws","disconnect","catch","err","console","previousConnectionParams","previous"],"sources":["../../../src/components/DataGrid/useDataGridWebsocket.ts"],"sourcesContent":["import {\n GridModel,\n GridModelSnapshot,\n} from '@/components/DataGrid/DataGridTypes'\nimport { useCRDTModelView } from '@/components/DataGrid/useCRDTModelView'\nimport { normalizeWebsocketError } from '@/components/DataGrid/utils/normalizeWebsocketError'\nimport { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'\nimport { DataGridWebSocket } from './DataGridWebSocket'\nimport { useEstablishWebsocketConnection } from '@/synapse-queries/grid/useEstablishWebsocketConnection'\nimport { useDocumentVisibility } from '@react-hookz/web'\n\n// State type\nexport interface WebSocketState {\n model: GridModel | null\n /**\n * True if the WebSocket has finished a sync exchange on the current connection\n * and can process CRDT updates. Set by `GRID_READY`; cleared on disconnect until\n * `hasCompletedInitialLoad` is set, since a drop mid-replay leaves the model\n * holding only part of the server's data.\n */\n hasCompletedInitialSync: boolean\n /**\n * True once the first connection for the current session has finished replaying\n * the server's data into the model. A one-way transition: later sync activity\n * never takes it back to false, and only connecting to a different\n * session/replica resets it.\n */\n hasCompletedInitialLoad: boolean\n /**\n * True while a clock-sync exchange is in progress. Set on every outgoing\n * `synchronize-clock` and cleared on `ResponseComplete`, which the server\n * sends once per exchange to signal \"we're synchronized\" — not per request.\n */\n isSyncing: boolean\n isConnected: boolean\n isConnecting: boolean\n connectionParams: {\n replicaId: number\n sessionId: string\n } | null\n websocketInstance: DataGridWebSocket | null\n connectionAttemptId: number | null\n connectionError: unknown\n websocketError: unknown\n}\n\n// Action types\nexport type WebSocketAction =\n | {\n type: 'CONNECT_REQUESTED'\n payload: { replicaId: number; sessionId: string; attemptId: number }\n }\n | { type: 'CONNECTION_ESTABLISHED'; payload: DataGridWebSocket }\n | { type: 'CONNECTION_OPENED' }\n | { type: 'CONNECTION_CLOSED' }\n | { type: 'GRID_READY' }\n | { type: 'INITIAL_LOAD_COMPLETE' }\n | { type: 'SYNC_STARTED' }\n | { type: 'SYNC_ENDED' }\n | { type: 'MODEL_CREATED'; payload: GridModel }\n | { type: 'CONNECTION_ERROR'; payload: unknown }\n | { type: 'WEBSOCKET_ERROR'; payload: unknown }\n\n// Reducer function\nexport function websocketReducer(\n state: WebSocketState,\n action: WebSocketAction,\n): WebSocketState {\n switch (action.type) {\n case 'CONNECT_REQUESTED': {\n const isSameConnection =\n state.connectionParams &&\n state.connectionParams.replicaId === action.payload.replicaId &&\n state.connectionParams.sessionId === action.payload.sessionId\n\n return {\n ...state,\n connectionParams: {\n replicaId: action.payload.replicaId,\n sessionId: action.payload.sessionId,\n },\n hasCompletedInitialSync: isSameConnection\n ? state.hasCompletedInitialSync\n : false,\n hasCompletedInitialLoad: isSameConnection\n ? state.hasCompletedInitialLoad\n : false,\n isSyncing: false,\n model: isSameConnection ? state.model : null,\n isConnected: false,\n isConnecting: false,\n connectionAttemptId: action.payload.attemptId,\n connectionError: null,\n }\n }\n\n case 'CONNECTION_ESTABLISHED':\n return {\n ...state,\n websocketInstance: action.payload,\n isConnecting: true,\n }\n\n case 'CONNECTION_OPENED':\n return {\n ...state,\n isConnected: true,\n isConnecting: false,\n }\n\n case 'CONNECTION_CLOSED':\n return {\n ...state,\n isConnected: false,\n isConnecting: false,\n isSyncing: false,\n // A drop before the initial load finishes invalidates the completed-exchange\n // signal: the model may hold only the part of the data that arrived so far.\n // Clearing it makes the readiness gate wait for the reconnect's replay\n // instead of latching onto a partially loaded model. Once the load is done,\n // drops are ordinary reconnects and the signal must survive them.\n hasCompletedInitialSync: state.hasCompletedInitialLoad\n ? state.hasCompletedInitialSync\n : false,\n }\n\n case 'GRID_READY':\n return {\n ...state,\n hasCompletedInitialSync: true,\n }\n\n case 'INITIAL_LOAD_COMPLETE':\n return {\n ...state,\n hasCompletedInitialLoad: true,\n }\n\n case 'SYNC_STARTED':\n return {\n ...state,\n isSyncing: true,\n }\n\n case 'SYNC_ENDED':\n return {\n ...state,\n isSyncing: false,\n }\n\n case 'MODEL_CREATED':\n return {\n ...state,\n model: action.payload,\n }\n\n case 'CONNECTION_ERROR':\n return {\n ...state,\n connectionError: action.payload,\n isConnecting: false,\n }\n\n case 'WEBSOCKET_ERROR':\n return {\n ...state,\n websocketError: action.payload,\n }\n\n default:\n return state\n }\n}\n\nexport const initialWebSocketState: WebSocketState = {\n model: null,\n hasCompletedInitialSync: false,\n hasCompletedInitialLoad: false,\n isSyncing: false,\n isConnected: false,\n isConnecting: false,\n connectionParams: null,\n websocketInstance: null,\n connectionAttemptId: null,\n connectionError: null,\n websocketError: null,\n}\n\n/**\n * Checks if the model snapshot contains the minimum data required for rendering (columns and rows).\n */\nfunction isModelRenderable(\n model: GridModel | null,\n modelSnapshot: GridModelSnapshot | null | undefined,\n) {\n if (!model?.api.getSnapshot() || !modelSnapshot) {\n return false\n }\n const { columnNames, columnOrder, rows } = modelSnapshot\n const columnsReady = columnNames.length >= 1\n const orderReady = columnOrder.length >= 1\n const rowsReady = rows.length >= 0\n return columnsReady && orderReady && rowsReady\n}\n\n/**\n * Whether the server has finished replaying its data into the model, so the grid\n * can be shown and edited. Drives the one-way `INITIAL_LOAD_COMPLETE` transition.\n *\n * Returns false once `hasCompletedInitialLoad` is set, so callers can dispatch\n * unconditionally on a true result without re-entering the transition.\n *\n * `hasCompletedInitialSync` alone is not sufficient: the server may complete the\n * snapshot request before the client has fetched and decoded the snapshot, which\n * leaves that flag true while the rows are still arriving. Requiring an idle sync\n * exchange holds the transition until the replay drains.\n */\nexport function isInitialLoadReady(\n state: WebSocketState,\n modelSnapshot: GridModelSnapshot | null | undefined,\n): boolean {\n return (\n !state.hasCompletedInitialLoad &&\n state.hasCompletedInitialSync &&\n !state.isSyncing &&\n isModelRenderable(state.model, modelSnapshot)\n )\n}\n\n/**\n * Custom hook to manage a DataGrid WebSocket connection.\n * Handles:\n * - Fetching presigned URLs via a mutation hook\n * - Instantiating the DataGridWebSocket\n * - Connection status tracking\n * - Reconnection on disconnect\n * - Grid model updates and snapshot tracking\n */\nexport interface UseDataGridWebSocketOptions {\n onGridReady?: () => void\n onReplicaConnected?: () => void\n onReplicaDisconnected?: () => void\n}\n\nexport function useDataGridWebSocket(options?: UseDataGridWebSocketOptions) {\n const [state, dispatch] = useReducer(websocketReducer, initialWebSocketState)\n\n const connectionAttemptCounter = useRef(0)\n const activeConnectionAttemptIdRef = useRef<number | null>(null)\n\n const modelSnapshot = useCRDTModelView(state.model)\n\n const isDocumentVisible = useDocumentVisibility()\n\n const {\n mutateAsync: establishWebsocketConnection,\n isPending: isEstablishingWebsocketConnection,\n error: errorEstablishingWebsocketConnection,\n presignedUrl,\n reset: resetEstablishWebsocketConnection,\n clearPresignedUrl,\n } = useEstablishWebsocketConnection()\n\n // Update model creation handler - only set model, don't reset to null on disconnect\n const handleModelCreate = useCallback(\n (newModel: GridModel) => {\n dispatch({ type: 'MODEL_CREATED', payload: newModel })\n },\n [dispatch],\n )\n\n // Memoize websocket options to prevent unnecessary re-renders (excluding model to avoid circular deps)\n const websocketOptionsWithoutModel = useMemo(\n () => ({\n onGridReady: () => {\n dispatch({ type: 'GRID_READY' })\n options?.onGridReady?.()\n },\n onStatusChange: (open: boolean) =>\n dispatch({ type: open ? 'CONNECTION_OPENED' : 'CONNECTION_CLOSED' }),\n onModelCreate: handleModelCreate,\n onReplicaConnected: options?.onReplicaConnected,\n onReplicaDisconnected: options?.onReplicaDisconnected,\n onSyncStart: () => dispatch({ type: 'SYNC_STARTED' }),\n onSyncEnd: () => dispatch({ type: 'SYNC_ENDED' }),\n onError: (error: unknown) =>\n dispatch({\n type: 'WEBSOCKET_ERROR',\n payload: normalizeWebsocketError(error),\n }),\n }),\n [\n handleModelCreate,\n options?.onGridReady,\n options?.onReplicaConnected,\n options?.onReplicaDisconnected,\n ],\n )\n\n // Initiate (or re-initiate) a connection\n const connect = useCallback(\n (replicaId: number, sessionId: string) => {\n resetEstablishWebsocketConnection()\n\n const isDifferentConnection =\n !state.connectionParams ||\n state.connectionParams.replicaId !== replicaId ||\n state.connectionParams.sessionId !== sessionId\n\n if (isDifferentConnection) {\n clearPresignedUrl()\n }\n\n connectionAttemptCounter.current += 1\n const attemptId = connectionAttemptCounter.current\n\n dispatch({\n type: 'CONNECT_REQUESTED',\n payload: { replicaId, sessionId, attemptId },\n })\n },\n [\n dispatch,\n state.connectionParams,\n resetEstablishWebsocketConnection,\n clearPresignedUrl,\n connectionAttemptCounter,\n ],\n )\n\n /**\n * Establish the WebSocket connection when conditions are met.\n * Uses the current model state at connection time for reconnections to the same session.\n * Note: Changes to `model` alone do NOT trigger reconnection - only changes to connection\n * params, visibility, or connection status will trigger a new connection attempt.\n */\n useEffect(() => {\n // Don't attempt connection if conditions aren't met\n const { connectionParams, connectionAttemptId } = state\n\n if (\n !connectionParams ||\n connectionAttemptId === null ||\n state.isConnected ||\n state.isConnecting ||\n isEstablishingWebsocketConnection ||\n errorEstablishingWebsocketConnection ||\n state.connectionError ||\n !isDocumentVisible\n ) {\n return\n }\n\n activeConnectionAttemptIdRef.current = connectionAttemptId\n\n // Use current model state for reconnections to same session\n // (model may have been created by a previous connection)\n establishWebsocketConnection({\n replicaId: connectionParams.replicaId,\n sessionId: connectionParams.sessionId,\n websocketOptions: {\n ...websocketOptionsWithoutModel,\n model: state.model, // Current model at connection time\n },\n })\n .then(ws => {\n if (activeConnectionAttemptIdRef.current !== connectionAttemptId) {\n ws.disconnect()\n return\n }\n\n dispatch({ type: 'CONNECTION_ESTABLISHED', payload: ws })\n })\n .catch(err => {\n dispatch({ type: 'CONNECTION_ERROR', payload: err })\n console.error('Failed to establish WebSocket', err)\n })\n }, [\n state,\n state.connectionParams,\n state.connectionAttemptId,\n state.isConnected,\n state.isConnecting,\n isEstablishingWebsocketConnection,\n errorEstablishingWebsocketConnection,\n isDocumentVisible,\n establishWebsocketConnection,\n websocketOptionsWithoutModel,\n ])\n\n const previousConnectionParams =\n useRef<WebSocketState['connectionParams']>(null)\n\n useEffect(() => {\n const previous = previousConnectionParams.current\n const current = state.connectionParams\n\n if (\n previous &&\n current &&\n (previous.replicaId !== current.replicaId ||\n previous.sessionId !== current.sessionId)\n ) {\n state.websocketInstance?.disconnect()\n }\n\n previousConnectionParams.current = current\n }, [state.connectionParams, state.websocketInstance])\n\n useEffect(() => {\n return () => {\n state.websocketInstance?.disconnect()\n activeConnectionAttemptIdRef.current = null\n }\n }, [state.websocketInstance])\n\n useEffect(() => {\n if (isInitialLoadReady(state, modelSnapshot)) {\n dispatch({ type: 'INITIAL_LOAD_COMPLETE' })\n }\n }, [state, modelSnapshot])\n\n return {\n isConnected: state.isConnected,\n websocketInstance: state.websocketInstance,\n hasCompletedInitialSync: state.hasCompletedInitialSync,\n hasCompletedInitialLoad: state.hasCompletedInitialLoad,\n isSyncing: state.isSyncing,\n model: state.model,\n modelSnapshot,\n connect,\n presignedUrl,\n errorEstablishingWebsocketConnection:\n state.connectionError ?? errorEstablishingWebsocketConnection,\n websocketError: state.websocketError,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useDataGridWebsocket.js","names":["GridModel","GridModelSnapshot","useCRDTModelView","normalizeWebsocketError","useCallback","useEffect","useMemo","useReducer","useRef","DataGridWebSocket","useEstablishWebsocketConnection","useDocumentVisibility","WebSocketState","model","hasCompletedInitialSync","hasCompletedInitialLoad","isSyncing","isConnected","isConnecting","connectionParams","replicaId","sessionId","websocketInstance","connectionAttemptId","connectionError","websocketError","WebSocketAction","type","payload","attemptId","websocketReducer","state","action","isSameConnection","initialWebSocketState","isModelRenderable","modelSnapshot","api","getSnapshot","columnNames","columnOrder","rows","columnsReady","length","orderReady","rowsReady","isInitialLoadReady","UseDataGridWebSocketOptions","onGridReady","onReplicaConnected","onReplicaDisconnected","useDataGridWebSocket","options","dispatch","connectionAttemptCounter","activeConnectionAttemptIdRef","isDocumentVisible","mutateAsync","establishWebsocketConnection","isPending","isEstablishingWebsocketConnection","error","errorEstablishingWebsocketConnection","presignedUrl","reset","resetEstablishWebsocketConnection","clearPresignedUrl","handleModelCreate","newModel","websocketOptionsWithoutModel","onStatusChange","open","onModelCreate","onSyncStart","onSyncEnd","onError","connect","isDifferentConnection","current","websocketOptions","pendingPatches","getPendingPatches","undefined","then","ws","disconnect","catch","err","console","previousConnectionParams","previous"],"sources":["../../../src/components/DataGrid/useDataGridWebsocket.ts"],"sourcesContent":["import {\n GridModel,\n GridModelSnapshot,\n} from '@/components/DataGrid/DataGridTypes'\nimport { useCRDTModelView } from '@/components/DataGrid/useCRDTModelView'\nimport { normalizeWebsocketError } from '@/components/DataGrid/utils/normalizeWebsocketError'\nimport { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'\nimport { DataGridWebSocket } from './DataGridWebSocket'\nimport { useEstablishWebsocketConnection } from '@/synapse-queries/grid/useEstablishWebsocketConnection'\nimport { useDocumentVisibility } from '@react-hookz/web'\n\n// State type\nexport interface WebSocketState {\n model: GridModel | null\n /**\n * True if the WebSocket has finished a sync exchange on the current connection\n * and can process CRDT updates. Set by `GRID_READY`; cleared on disconnect until\n * `hasCompletedInitialLoad` is set, since a drop mid-replay leaves the model\n * holding only part of the server's data.\n */\n hasCompletedInitialSync: boolean\n /**\n * True once the first connection for the current session has finished replaying\n * the server's data into the model. A one-way transition: later sync activity\n * never takes it back to false, and only connecting to a different\n * session/replica resets it.\n */\n hasCompletedInitialLoad: boolean\n /**\n * True while a clock-sync exchange is in progress. Set on every outgoing\n * `synchronize-clock` and cleared on `ResponseComplete`, which the server\n * sends once per exchange to signal \"we're synchronized\" — not per request.\n */\n isSyncing: boolean\n isConnected: boolean\n isConnecting: boolean\n connectionParams: {\n replicaId: number\n sessionId: string\n } | null\n websocketInstance: DataGridWebSocket | null\n connectionAttemptId: number | null\n connectionError: unknown\n websocketError: unknown\n}\n\n// Action types\nexport type WebSocketAction =\n | {\n type: 'CONNECT_REQUESTED'\n payload: { replicaId: number; sessionId: string; attemptId: number }\n }\n | { type: 'CONNECTION_ESTABLISHED'; payload: DataGridWebSocket }\n | { type: 'CONNECTION_OPENED' }\n | { type: 'CONNECTION_CLOSED' }\n | { type: 'GRID_READY' }\n | { type: 'INITIAL_LOAD_COMPLETE' }\n | { type: 'SYNC_STARTED' }\n | { type: 'SYNC_ENDED' }\n | { type: 'MODEL_CREATED'; payload: GridModel }\n | { type: 'CONNECTION_ERROR'; payload: unknown }\n | { type: 'WEBSOCKET_ERROR'; payload: unknown }\n\n// Reducer function\nexport function websocketReducer(\n state: WebSocketState,\n action: WebSocketAction,\n): WebSocketState {\n switch (action.type) {\n case 'CONNECT_REQUESTED': {\n const isSameConnection =\n state.connectionParams &&\n state.connectionParams.replicaId === action.payload.replicaId &&\n state.connectionParams.sessionId === action.payload.sessionId\n\n return {\n ...state,\n connectionParams: {\n replicaId: action.payload.replicaId,\n sessionId: action.payload.sessionId,\n },\n hasCompletedInitialSync: isSameConnection\n ? state.hasCompletedInitialSync\n : false,\n hasCompletedInitialLoad: isSameConnection\n ? state.hasCompletedInitialLoad\n : false,\n isSyncing: false,\n model: isSameConnection ? state.model : null,\n isConnected: false,\n isConnecting: false,\n connectionAttemptId: action.payload.attemptId,\n connectionError: null,\n }\n }\n\n case 'CONNECTION_ESTABLISHED':\n return {\n ...state,\n websocketInstance: action.payload,\n isConnecting: true,\n }\n\n case 'CONNECTION_OPENED':\n return {\n ...state,\n isConnected: true,\n isConnecting: false,\n }\n\n case 'CONNECTION_CLOSED':\n return {\n ...state,\n isConnected: false,\n isConnecting: false,\n isSyncing: false,\n // A drop before the initial load finishes invalidates the completed-exchange\n // signal: the model may hold only the part of the data that arrived so far.\n // Clearing it makes the readiness gate wait for the reconnect's replay\n // instead of latching onto a partially loaded model. Once the load is done,\n // drops are ordinary reconnects and the signal must survive them.\n hasCompletedInitialSync: state.hasCompletedInitialLoad\n ? state.hasCompletedInitialSync\n : false,\n }\n\n case 'GRID_READY':\n return {\n ...state,\n hasCompletedInitialSync: true,\n }\n\n case 'INITIAL_LOAD_COMPLETE':\n return {\n ...state,\n hasCompletedInitialLoad: true,\n }\n\n case 'SYNC_STARTED':\n return {\n ...state,\n isSyncing: true,\n }\n\n case 'SYNC_ENDED':\n return {\n ...state,\n isSyncing: false,\n }\n\n case 'MODEL_CREATED':\n return {\n ...state,\n model: action.payload,\n }\n\n case 'CONNECTION_ERROR':\n return {\n ...state,\n connectionError: action.payload,\n isConnecting: false,\n }\n\n case 'WEBSOCKET_ERROR':\n return {\n ...state,\n websocketError: action.payload,\n }\n\n default:\n return state\n }\n}\n\nexport const initialWebSocketState: WebSocketState = {\n model: null,\n hasCompletedInitialSync: false,\n hasCompletedInitialLoad: false,\n isSyncing: false,\n isConnected: false,\n isConnecting: false,\n connectionParams: null,\n websocketInstance: null,\n connectionAttemptId: null,\n connectionError: null,\n websocketError: null,\n}\n\n/**\n * Checks if the model snapshot contains the minimum data required for rendering (columns and rows).\n */\nfunction isModelRenderable(\n model: GridModel | null,\n modelSnapshot: GridModelSnapshot | null | undefined,\n) {\n if (!model?.api.getSnapshot() || !modelSnapshot) {\n return false\n }\n const { columnNames, columnOrder, rows } = modelSnapshot\n const columnsReady = columnNames.length >= 1\n const orderReady = columnOrder.length >= 1\n const rowsReady = rows.length >= 0\n return columnsReady && orderReady && rowsReady\n}\n\n/**\n * Whether the server has finished replaying its data into the model, so the grid\n * can be shown and edited. Drives the one-way `INITIAL_LOAD_COMPLETE` transition.\n *\n * Returns false once `hasCompletedInitialLoad` is set, so callers can dispatch\n * unconditionally on a true result without re-entering the transition.\n *\n * `hasCompletedInitialSync` alone is not sufficient: the server may complete the\n * snapshot request before the client has fetched and decoded the snapshot, which\n * leaves that flag true while the rows are still arriving. Requiring an idle sync\n * exchange holds the transition until the replay drains.\n */\nexport function isInitialLoadReady(\n state: WebSocketState,\n modelSnapshot: GridModelSnapshot | null | undefined,\n): boolean {\n return (\n !state.hasCompletedInitialLoad &&\n state.hasCompletedInitialSync &&\n !state.isSyncing &&\n isModelRenderable(state.model, modelSnapshot)\n )\n}\n\n/**\n * Custom hook to manage a DataGrid WebSocket connection.\n * Handles:\n * - Fetching presigned URLs via a mutation hook\n * - Instantiating the DataGridWebSocket\n * - Connection status tracking\n * - Reconnection on disconnect\n * - Grid model updates and snapshot tracking\n */\nexport interface UseDataGridWebSocketOptions {\n onGridReady?: () => void\n onReplicaConnected?: () => void\n onReplicaDisconnected?: () => void\n}\n\nexport function useDataGridWebSocket(options?: UseDataGridWebSocketOptions) {\n const [state, dispatch] = useReducer(websocketReducer, initialWebSocketState)\n\n const connectionAttemptCounter = useRef(0)\n const activeConnectionAttemptIdRef = useRef<number | null>(null)\n\n const modelSnapshot = useCRDTModelView(state.model)\n\n const isDocumentVisible = useDocumentVisibility()\n\n const {\n mutateAsync: establishWebsocketConnection,\n isPending: isEstablishingWebsocketConnection,\n error: errorEstablishingWebsocketConnection,\n presignedUrl,\n reset: resetEstablishWebsocketConnection,\n clearPresignedUrl,\n } = useEstablishWebsocketConnection()\n\n // Update model creation handler - only set model, don't reset to null on disconnect\n const handleModelCreate = useCallback(\n (newModel: GridModel) => {\n dispatch({ type: 'MODEL_CREATED', payload: newModel })\n },\n [dispatch],\n )\n\n // Memoize websocket options to prevent unnecessary re-renders (excluding model to avoid circular deps)\n const websocketOptionsWithoutModel = useMemo(\n () => ({\n onGridReady: () => {\n dispatch({ type: 'GRID_READY' })\n options?.onGridReady?.()\n },\n onStatusChange: (open: boolean) =>\n dispatch({ type: open ? 'CONNECTION_OPENED' : 'CONNECTION_CLOSED' }),\n onModelCreate: handleModelCreate,\n onReplicaConnected: options?.onReplicaConnected,\n onReplicaDisconnected: options?.onReplicaDisconnected,\n onSyncStart: () => dispatch({ type: 'SYNC_STARTED' }),\n onSyncEnd: () => dispatch({ type: 'SYNC_ENDED' }),\n onError: (error: unknown) =>\n dispatch({\n type: 'WEBSOCKET_ERROR',\n payload: normalizeWebsocketError(error),\n }),\n }),\n [\n handleModelCreate,\n options?.onGridReady,\n options?.onReplicaConnected,\n options?.onReplicaDisconnected,\n ],\n )\n\n // Initiate (or re-initiate) a connection\n const connect = useCallback(\n (replicaId: number, sessionId: string) => {\n resetEstablishWebsocketConnection()\n\n const isDifferentConnection =\n !state.connectionParams ||\n state.connectionParams.replicaId !== replicaId ||\n state.connectionParams.sessionId !== sessionId\n\n if (isDifferentConnection) {\n clearPresignedUrl()\n }\n\n connectionAttemptCounter.current += 1\n const attemptId = connectionAttemptCounter.current\n\n dispatch({\n type: 'CONNECT_REQUESTED',\n payload: { replicaId, sessionId, attemptId },\n })\n },\n [\n dispatch,\n state.connectionParams,\n resetEstablishWebsocketConnection,\n clearPresignedUrl,\n connectionAttemptCounter,\n ],\n )\n\n /**\n * Establish the WebSocket connection when conditions are met.\n * Uses the current model state at connection time for reconnections to the same session.\n * Note: Changes to `model` alone do NOT trigger reconnection - only changes to connection\n * params, visibility, or connection status will trigger a new connection attempt.\n */\n useEffect(() => {\n // Don't attempt connection if conditions aren't met\n const { connectionParams, connectionAttemptId } = state\n\n if (\n !connectionParams ||\n connectionAttemptId === null ||\n state.isConnected ||\n state.isConnecting ||\n isEstablishingWebsocketConnection ||\n errorEstablishingWebsocketConnection ||\n state.connectionError ||\n !isDocumentVisible\n ) {\n return\n }\n\n activeConnectionAttemptIdRef.current = connectionAttemptId\n\n // Use current model state for reconnections to same session\n // (model may have been created by a previous connection)\n establishWebsocketConnection({\n replicaId: connectionParams.replicaId,\n sessionId: connectionParams.sessionId,\n websocketOptions: {\n ...websocketOptionsWithoutModel,\n model: state.model, // Current model at connection time\n // A model only survives for the same session, so patches only carry over with it\n pendingPatches: state.model\n ? state.websocketInstance?.getPendingPatches()\n : undefined,\n },\n })\n .then(ws => {\n if (activeConnectionAttemptIdRef.current !== connectionAttemptId) {\n ws.disconnect()\n return\n }\n\n dispatch({ type: 'CONNECTION_ESTABLISHED', payload: ws })\n })\n .catch(err => {\n dispatch({ type: 'CONNECTION_ERROR', payload: err })\n console.error('Failed to establish WebSocket', err)\n })\n }, [\n state,\n state.connectionParams,\n state.connectionAttemptId,\n state.isConnected,\n state.isConnecting,\n isEstablishingWebsocketConnection,\n errorEstablishingWebsocketConnection,\n isDocumentVisible,\n establishWebsocketConnection,\n websocketOptionsWithoutModel,\n ])\n\n const previousConnectionParams =\n useRef<WebSocketState['connectionParams']>(null)\n\n useEffect(() => {\n const previous = previousConnectionParams.current\n const current = state.connectionParams\n\n if (\n previous &&\n current &&\n (previous.replicaId !== current.replicaId ||\n previous.sessionId !== current.sessionId)\n ) {\n state.websocketInstance?.disconnect()\n }\n\n previousConnectionParams.current = current\n }, [state.connectionParams, state.websocketInstance])\n\n useEffect(() => {\n return () => {\n state.websocketInstance?.disconnect()\n activeConnectionAttemptIdRef.current = null\n }\n }, [state.websocketInstance])\n\n useEffect(() => {\n if (isInitialLoadReady(state, modelSnapshot)) {\n dispatch({ type: 'INITIAL_LOAD_COMPLETE' })\n }\n }, [state, modelSnapshot])\n\n return {\n isConnected: state.isConnected,\n websocketInstance: state.websocketInstance,\n hasCompletedInitialSync: state.hasCompletedInitialSync,\n hasCompletedInitialLoad: state.hasCompletedInitialLoad,\n isSyncing: state.isSyncing,\n model: state.model,\n modelSnapshot,\n connect,\n presignedUrl,\n errorEstablishingWebsocketConnection:\n state.connectionError ?? errorEstablishingWebsocketConnection,\n websocketError: state.websocketError,\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"EntityViewMaskEditor.d.ts","sourceRoot":"","sources":["../../../src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx"],"names":[],"mappings":"AAeA,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAUD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,WAShD;AAED,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,2CA0C5E"}
1
+ {"version":3,"file":"EntityViewMaskEditor.d.ts","sourceRoot":"","sources":["../../../src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx"],"names":[],"mappings":"AAgBA,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAWD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,WAShD;AAED,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,2CA0C5E"}
@@ -1,21 +1,22 @@
1
1
  import { Alert as e, Checkbox as t, FormControlLabel as n, FormGroup as r, Typography as i } from "@mui/material";
2
2
  import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
3
- import { ENTITY_VIEW_TYPE_MASK_DATASET as c, ENTITY_VIEW_TYPE_MASK_DOCKER as l, ENTITY_VIEW_TYPE_MASK_FILE as u, ENTITY_VIEW_TYPE_MASK_FOLDER as d, ENTITY_VIEW_TYPE_MASK_TABLE as f } from "@sage-bionetworks/synapse-types";
3
+ import { ENTITY_VIEW_TYPE_MASK_DATASET as c, ENTITY_VIEW_TYPE_MASK_DOCKER as l, ENTITY_VIEW_TYPE_MASK_FILE as u, ENTITY_VIEW_TYPE_MASK_FOLDER as d, ENTITY_VIEW_TYPE_MASK_RECORDSET as f, ENTITY_VIEW_TYPE_MASK_TABLE as p } from "@sage-bionetworks/synapse-types";
4
4
  //#region src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx
5
- var p = [
5
+ var m = [
6
6
  ["Files", u],
7
7
  ["Folders", d],
8
- ["Tables", f],
8
+ ["Tables", p],
9
9
  ["Datasets", c],
10
- ["Docker Repositories", l]
10
+ ["Docker Repositories", l],
11
+ ["Record Sets", f]
11
12
  ];
12
- function m(e) {
13
- return p.forEach(([, t]) => {
13
+ function h(e) {
14
+ return m.forEach(([, t]) => {
14
15
  (e & t) !== 0 && (e -= t);
15
16
  }), e === 0;
16
17
  }
17
- function h(c) {
18
- let { value: l, onChange: u, disabled: d = !1 } = c, f = !m(l);
18
+ function g(c) {
19
+ let { value: l, onChange: u, disabled: d = !1 } = c, f = !h(l);
19
20
  return /* @__PURE__ */ s(a, { children: [
20
21
  /* @__PURE__ */ o(i, {
21
22
  variant: "body1",
@@ -28,7 +29,7 @@ function h(c) {
28
29
  }),
29
30
  /* @__PURE__ */ o(r, {
30
31
  sx: { gap: 1 },
31
- children: p.map(([e, r]) => /* @__PURE__ */ o(n, {
32
+ children: m.map(([e, r]) => /* @__PURE__ */ o(n, {
32
33
  control: /* @__PURE__ */ o(t, {}),
33
34
  label: e,
34
35
  checked: (l & r) > 0,
@@ -46,6 +47,6 @@ function h(c) {
46
47
  ] });
47
48
  }
48
49
  //#endregion
49
- export { h as default, m as isMaskSupportedInUI };
50
+ export { g as default, h as isMaskSupportedInUI };
50
51
 
51
52
  //# sourceMappingURL=EntityViewMaskEditor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EntityViewMaskEditor.js","names":[],"sources":["../../../src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx"],"sourcesContent":["import {\n Alert,\n Checkbox,\n FormControlLabel,\n FormGroup,\n Typography,\n} from '@mui/material'\nimport {\n ENTITY_VIEW_TYPE_MASK_DATASET,\n ENTITY_VIEW_TYPE_MASK_FILE,\n ENTITY_VIEW_TYPE_MASK_FOLDER,\n ENTITY_VIEW_TYPE_MASK_TABLE,\n ENTITY_VIEW_TYPE_MASK_DOCKER,\n} from '@sage-bionetworks/synapse-types'\n\nexport type EntityViewMaskEditorProps = {\n value: number\n onChange: (mask: number) => void\n disabled?: boolean\n}\n\nconst maskLabelPairs: [string, number][] = [\n ['Files', ENTITY_VIEW_TYPE_MASK_FILE],\n ['Folders', ENTITY_VIEW_TYPE_MASK_FOLDER],\n ['Tables', ENTITY_VIEW_TYPE_MASK_TABLE],\n ['Datasets', ENTITY_VIEW_TYPE_MASK_DATASET],\n ['Docker Repositories', ENTITY_VIEW_TYPE_MASK_DOCKER],\n]\n\nexport function isMaskSupportedInUI(value: number) {\n maskLabelPairs.forEach(([, mask]) => {\n // Remove each supported bit\n if ((value & mask) !== 0) {\n value = value - mask\n }\n })\n // If value is not zero, then some unsupported bit remains\n return value === 0\n}\n\nexport default function EntityViewMaskEditor(props: EntityViewMaskEditorProps) {\n const { value, onChange, disabled = false } = props\n\n const customMaskInUse = !isMaskSupportedInUI(value)\n\n return (\n <>\n <Typography\n variant={'body1'}\n sx={{\n mt: 2.5,\n mb: 1.25,\n fontWeight: 700,\n }}\n >\n Include in View\n </Typography>\n <FormGroup sx={{ gap: 1 }}>\n {maskLabelPairs.map(([label, mask]) => (\n <FormControlLabel\n control={<Checkbox />}\n key={label}\n label={label}\n checked={\n // Checked if the mask bit is set\n (value & mask) > 0\n }\n disabled={customMaskInUse || disabled}\n onChange={() => {\n // Toggle the clicked mask value with XOR\n onChange(value ^ mask)\n }}\n />\n ))}\n </FormGroup>\n {customMaskInUse && (\n <Alert severity={'warning'} sx={{ my: 2.25 }}>\n A custom mask is in use. Changes cannot be made in the UI.\n </Alert>\n )}\n </>\n )\n}\n"],"mappings":";;;;AAqBA,IAAM,IAAqC;CACzC,CAAC,SAAS,CAA0B;CACpC,CAAC,WAAW,CAA4B;CACxC,CAAC,UAAU,CAA2B;CACtC,CAAC,YAAY,CAA6B;CAC1C,CAAC,uBAAuB,CAA4B;AACtD;AAEA,SAAgB,EAAoB,GAAe;CAQjD,OAPA,EAAe,SAAS,GAAG,OAAU;EAEnC,CAAK,IAAQ,OAAU,MACrB,KAAgB;CAEpB,CAAC,GAEM,MAAU;AACnB;AAEA,SAAwB,EAAqB,GAAkC;CAC7E,IAAM,EAAE,UAAO,aAAU,cAAW,OAAU,GAExC,IAAkB,CAAC,EAAoB,CAAK;CAElD,OACE,kBAAA,GAAA,EAAA,UAAA;EACE,kBAAC,GAAD;GACE,SAAS;GACT,IAAI;IACF,IAAI;IACJ,IAAI;IACJ,YAAY;GACd;aACD;EAEW,CAAA;EACZ,kBAAC,GAAD;GAAW,IAAI,EAAE,KAAK,EAAE;aACrB,EAAe,KAAK,CAAC,GAAO,OAC3B,kBAAC,GAAD;IACE,SAAS,kBAAC,GAAD,CAAW,CAAA;IAEb;IACP,UAEG,IAAQ,KAAQ;IAEnB,UAAU,KAAmB;IAC7B,gBAAgB;KAEd,EAAS,IAAQ,CAAI;IACvB;GACD,GAXM,CAWN,CACF;EACQ,CAAA;EACV,KACC,kBAAC,GAAD;GAAO,UAAU;GAAW,IAAI,EAAE,IAAI,KAAK;aAAG;EAEvC,CAAA;CAET,EAAA,CAAA;AAEN"}
1
+ {"version":3,"file":"EntityViewMaskEditor.js","names":[],"sources":["../../../src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx"],"sourcesContent":["import {\n Alert,\n Checkbox,\n FormControlLabel,\n FormGroup,\n Typography,\n} from '@mui/material'\nimport {\n ENTITY_VIEW_TYPE_MASK_DATASET,\n ENTITY_VIEW_TYPE_MASK_FILE,\n ENTITY_VIEW_TYPE_MASK_FOLDER,\n ENTITY_VIEW_TYPE_MASK_TABLE,\n ENTITY_VIEW_TYPE_MASK_DOCKER,\n ENTITY_VIEW_TYPE_MASK_RECORDSET,\n} from '@sage-bionetworks/synapse-types'\n\nexport type EntityViewMaskEditorProps = {\n value: number\n onChange: (mask: number) => void\n disabled?: boolean\n}\n\nconst maskLabelPairs: [string, number][] = [\n ['Files', ENTITY_VIEW_TYPE_MASK_FILE],\n ['Folders', ENTITY_VIEW_TYPE_MASK_FOLDER],\n ['Tables', ENTITY_VIEW_TYPE_MASK_TABLE],\n ['Datasets', ENTITY_VIEW_TYPE_MASK_DATASET],\n ['Docker Repositories', ENTITY_VIEW_TYPE_MASK_DOCKER],\n ['Record Sets', ENTITY_VIEW_TYPE_MASK_RECORDSET],\n]\n\nexport function isMaskSupportedInUI(value: number) {\n maskLabelPairs.forEach(([, mask]) => {\n // Remove each supported bit\n if ((value & mask) !== 0) {\n value = value - mask\n }\n })\n // If value is not zero, then some unsupported bit remains\n return value === 0\n}\n\nexport default function EntityViewMaskEditor(props: EntityViewMaskEditorProps) {\n const { value, onChange, disabled = false } = props\n\n const customMaskInUse = !isMaskSupportedInUI(value)\n\n return (\n <>\n <Typography\n variant={'body1'}\n sx={{\n mt: 2.5,\n mb: 1.25,\n fontWeight: 700,\n }}\n >\n Include in View\n </Typography>\n <FormGroup sx={{ gap: 1 }}>\n {maskLabelPairs.map(([label, mask]) => (\n <FormControlLabel\n control={<Checkbox />}\n key={label}\n label={label}\n checked={\n // Checked if the mask bit is set\n (value & mask) > 0\n }\n disabled={customMaskInUse || disabled}\n onChange={() => {\n // Toggle the clicked mask value with XOR\n onChange(value ^ mask)\n }}\n />\n ))}\n </FormGroup>\n {customMaskInUse && (\n <Alert severity={'warning'} sx={{ my: 2.25 }}>\n A custom mask is in use. Changes cannot be made in the UI.\n </Alert>\n )}\n </>\n )\n}\n"],"mappings":";;;;AAsBA,IAAM,IAAqC;CACzC,CAAC,SAAS,CAA0B;CACpC,CAAC,WAAW,CAA4B;CACxC,CAAC,UAAU,CAA2B;CACtC,CAAC,YAAY,CAA6B;CAC1C,CAAC,uBAAuB,CAA4B;CACpD,CAAC,eAAe,CAA+B;AACjD;AAEA,SAAgB,EAAoB,GAAe;CAQjD,OAPA,EAAe,SAAS,GAAG,OAAU;EAEnC,CAAK,IAAQ,OAAU,MACrB,KAAgB;CAEpB,CAAC,GAEM,MAAU;AACnB;AAEA,SAAwB,EAAqB,GAAkC;CAC7E,IAAM,EAAE,UAAO,aAAU,cAAW,OAAU,GAExC,IAAkB,CAAC,EAAoB,CAAK;CAElD,OACE,kBAAA,GAAA,EAAA,UAAA;EACE,kBAAC,GAAD;GACE,SAAS;GACT,IAAI;IACF,IAAI;IACJ,IAAI;IACJ,YAAY;GACd;aACD;EAEW,CAAA;EACZ,kBAAC,GAAD;GAAW,IAAI,EAAE,KAAK,EAAE;aACrB,EAAe,KAAK,CAAC,GAAO,OAC3B,kBAAC,GAAD;IACE,SAAS,kBAAC,GAAD,CAAW,CAAA;IAEb;IACP,UAEG,IAAQ,KAAQ;IAEnB,UAAU,KAAmB;IAC7B,gBAAgB;KAEd,EAAS,IAAQ,CAAI;IACvB;GACD,GAXM,CAWN,CACF;EACQ,CAAA;EACV,KACC,kBAAC,GAAD;GAAO,UAAU;GAAW,IAAI,EAAE,IAAI,KAAK;aAAG;EAEvC,CAAA;CAET,EAAA,CAAA;AAEN"}