@rowsncolumns/y-spreadsheet 15.0.2 → 15.1.0

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.
@@ -13,7 +13,7 @@ import unset from "lodash/unset";
13
13
  * @param source
14
14
  * @param sheetDataVersion - Version of sheet data format ("v2" or "v3"). Falls back to docMeta if undefined.
15
15
  */
16
- export const applyPatchesToYDoc = (doc, userId, patches, source, sheetDataVersion) => {
16
+ export const applyPatchesToYDoc = (doc, userId, patches, source, sheetDataVersion, yjs = Y) => {
17
17
  const ySheetDataV2 = doc.getMap("sheetDataV2");
18
18
  const ySheetDataV3 = doc.getMap("sheetDataV3");
19
19
  const yDocMeta = doc.getMap("docMeta");
@@ -42,7 +42,7 @@ export const applyPatchesToYDoc = (doc, userId, patches, source, sheetDataVersio
42
42
  addSheetDataPatchesV3(ySheetDataV3, patches);
43
43
  }
44
44
  else {
45
- addSheetDataPatches(ySheetDataV2, patches);
45
+ addSheetDataPatches(ySheetDataV2, patches, yjs);
46
46
  }
47
47
  }
48
48
  if (patch.sheets) {
@@ -114,7 +114,7 @@ export const applyPatchesToYDoc = (doc, userId, patches, source, sheetDataVersio
114
114
  * 1. Deduplicating patches targeting the same cell (later patches win)
115
115
  * 2. Grouping updates by sheet/row for efficient batch application
116
116
  */
117
- export const addSheetDataPatches = (ySheetData, patches) => {
117
+ export const addSheetDataPatches = (ySheetData, patches, yjs = Y) => {
118
118
  // Optimization: consecutive cell-level patches are consolidated (later
119
119
  // patches override earlier ones for the same cell) and applied together,
120
120
  // grouped by sheet and row so bulk operations like paste (10k+ rows) cost
@@ -138,9 +138,9 @@ export const addSheetDataPatches = (ySheetData, patches) => {
138
138
  rowUpdates.get(rowIdx).set(colIdx, value);
139
139
  }
140
140
  for (const [sheetIdStr, rowUpdates] of sheetRowUpdates) {
141
- const ysheet = getSheetFromYSheetData(ySheetData, sheetIdStr);
141
+ const ysheet = getSheetFromYSheetData(ySheetData, sheetIdStr, yjs);
142
142
  for (const [rowIdx, colUpdates] of rowUpdates) {
143
- const rowData = getRowDataFromSheet(ysheet, rowIdx);
143
+ const rowData = getRowDataFromSheet(ysheet, rowIdx, yjs);
144
144
  for (const [colIdx, value] of colUpdates) {
145
145
  setCellDataInRowData(rowData, colIdx, value);
146
146
  }
@@ -169,12 +169,12 @@ export const addSheetDataPatches = (ySheetData, patches) => {
169
169
  return;
170
170
  }
171
171
  if (op === "replace" || op === "add") {
172
- const ysheet = getSheetFromYSheetData(ySheetData, sheetId);
172
+ const ysheet = getSheetFromYSheetData(ySheetData, sheetId, yjs);
173
173
  // Cell-level patches (path.length === 4) are already handled above via consolidation
174
174
  // [sheetId, rowIndex, 'values'] - Replace values array directly
175
175
  if (path.length === 3 && path[2] === "values") {
176
176
  const [, rowIndex] = path;
177
- const rowData = getRowDataFromSheet(ysheet, rowIndex);
177
+ const rowData = getRowDataFromSheet(ysheet, rowIndex, yjs);
178
178
  // Clear existing row data
179
179
  rowData.delete(0, rowData.length);
180
180
  if (Array.isArray(value)) {
@@ -192,7 +192,7 @@ export const addSheetDataPatches = (ySheetData, patches) => {
192
192
  const [, rowIndex] = path;
193
193
  if (value?.values) {
194
194
  // Replace entire row with new values
195
- const rowData = getRowDataFromSheet(ysheet, rowIndex);
195
+ const rowData = getRowDataFromSheet(ysheet, rowIndex, yjs);
196
196
  // Clear existing row data
197
197
  rowData.delete(0, rowData.length);
198
198
  // Add new cell values
@@ -218,7 +218,7 @@ export const addSheetDataPatches = (ySheetData, patches) => {
218
218
  .sort(([a], [b]) => Number(a) - Number(b))
219
219
  .forEach(([rowIndex, rowData]) => {
220
220
  if (rowData?.values) {
221
- const rowDataArray = getRowDataFromSheet(ysheet, Number(rowIndex));
221
+ const rowDataArray = getRowDataFromSheet(ysheet, Number(rowIndex), yjs);
222
222
  Object.entries(rowData.values)
223
223
  .sort(([a], [b]) => Number(a) - Number(b))
224
224
  .forEach(([columnIndex, cellData]) => {
@@ -234,7 +234,7 @@ export const addSheetDataPatches = (ySheetData, patches) => {
234
234
  // [sheetId, rowIndex] - Remove entire row
235
235
  if (path.length === 2) {
236
236
  const [, rowIndex] = path;
237
- const ysheet = getSheetFromYSheetData(ySheetData, sheetId);
237
+ const ysheet = getSheetFromYSheetData(ySheetData, sheetId, yjs);
238
238
  removeRowData(ysheet, rowIndex);
239
239
  }
240
240
  // [sheetId] - Remove entire sheet
@@ -268,11 +268,11 @@ export const addSheetDataPatches = (ySheetData, patches) => {
268
268
  }
269
269
  flushPendingCellUpdates();
270
270
  };
271
- export const getSheetFromYSheetData = (ySheetData, sheetId) => {
271
+ export const getSheetFromYSheetData = (ySheetData, sheetId, yjs = Y) => {
272
272
  const id = String(sheetId);
273
273
  let sheet = ySheetData.get(id);
274
274
  if (!sheet) {
275
- sheet = new Y.Array();
275
+ sheet = new yjs.Array();
276
276
  ySheetData.set(id, sheet);
277
277
  }
278
278
  return sheet;
@@ -281,7 +281,7 @@ export const deleteSheetFromYSheetData = (ySheetData, sheetId) => {
281
281
  const id = String(sheetId);
282
282
  ySheetData.delete(id);
283
283
  };
284
- export const getRowDataFromSheet = (ysheet, rowIndex) => {
284
+ export const getRowDataFromSheet = (ysheet, rowIndex, yjs = Y) => {
285
285
  const index = Number(rowIndex);
286
286
  if (index < 0) {
287
287
  throw new Error("Row index cannot be negative");
@@ -292,8 +292,8 @@ export const getRowDataFromSheet = (ysheet, rowIndex) => {
292
292
  ysheet.insert(ysheet.length, padding);
293
293
  ysheet.insert(index, [null]); // Insert null first
294
294
  // Create new row data
295
- const newRowData = new Y.Map();
296
- newRowData.set("values", new Y.Array());
295
+ const newRowData = new yjs.Map();
296
+ newRowData.set("values", new yjs.Array());
297
297
  // Replace the null
298
298
  ysheet.insert(index, [newRowData]);
299
299
  ysheet.delete(index + 1, 1);
@@ -311,8 +311,8 @@ export const getRowDataFromSheet = (ysheet, rowIndex) => {
311
311
  let rowData = ysheet.get(index);
312
312
  if (!rowData || !rowData.get("values")) {
313
313
  // Create new row data if it doesn't exist or is incomplete
314
- const newRowData = new Y.Map();
315
- newRowData.set("values", new Y.Array());
314
+ const newRowData = new yjs.Map();
315
+ newRowData.set("values", new yjs.Array());
316
316
  // Replace the existing item
317
317
  ysheet.insert(index, [newRowData]);
318
318
  ysheet.delete(index + 1, 1);
@@ -1,6 +1,42 @@
1
1
  import type { Patch } from "immer";
2
2
  import type { RecalcCellCoordinate } from "@rowsncolumns/spreadsheet-state";
3
3
  import type { CellData } from "@rowsncolumns/spreadsheet";
4
+ import type * as Y from "yjs";
5
+ /**
6
+ * The Yjs members y-spreadsheet needs as runtime values: the shared-type
7
+ * constructors it instantiates and `instanceof`-checks, and the update
8
+ * functions `useYSpreadsheet` calls on the document. Defaults to the `yjs`
9
+ * (13) module; pass the namespace of the Yjs implementation that created
10
+ * `doc` (for example a yjs-13-compatible layer over `@y/y` 14) so the types
11
+ * this hook creates belong to the same implementation as the document and
12
+ * its `instanceof` checks see that implementation's classes. The members are
13
+ * typed against yjs 13, so another implementation is passed with a cast.
14
+ */
15
+ export type YjsRuntime = {
16
+ Map: typeof Y.Map;
17
+ Array: typeof Y.Array;
18
+ YArrayEvent: typeof Y.YArrayEvent;
19
+ Doc: typeof Y.Doc;
20
+ applyUpdate: typeof Y.applyUpdate;
21
+ diffUpdate: typeof Y.diffUpdate;
22
+ encodeStateAsUpdate: typeof Y.encodeStateAsUpdate;
23
+ encodeStateVector: typeof Y.encodeStateVector;
24
+ };
25
+ /** The awareness surface the hooks use (y-protocols and @y/protocols both satisfy it). */
26
+ export type AwarenessLike = {
27
+ getStates(): Map<number, Record<string, any>>;
28
+ on(event: "change", callback: (...args: any[]) => void): void;
29
+ off(event: "change", callback: (...args: any[]) => void): void;
30
+ setLocalState(state: Record<string, any> | null): void;
31
+ setLocalStateField(field: string, value: any): void;
32
+ };
33
+ /** A collaboration provider as the hooks see it: an optional awareness plus sync/status events. */
34
+ export type CollabProviderLike = {
35
+ awareness?: AwarenessLike | null;
36
+ synced?: boolean;
37
+ on?(event: string, callback: (...args: any[]) => void): void;
38
+ off?(event: string, callback: (...args: any[]) => void): void;
39
+ };
4
40
  export type PatchDocument<T = Patch> = {
5
41
  userId: string | number;
6
42
  patches: T[];
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,KAAK,IAAI;IACrC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,OAAO,EAAE,CAAC,EAAE,CAAC;CACd,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;AAC9D,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM;IACX,QAAQ,EAAE,oBAAoB;IAC9B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;CACtC,CAAC;AAEF;;;;;;;;;;;GAWG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,IAAI;IACtD,KAAK,EAAE,CAAC,CAAC;IACT,eAAe;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,6BAA6B;IAC7B,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../types.ts"],"names":[],"mappings":"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"}
@@ -4,8 +4,8 @@ import type { CellInterface, SelectionArea } from "@rowsncolumns/grid";
4
4
  import { PivotTable, type CellData, type Collaborator, type ConditionalFormatRule, type DataValidationRuleRecord, type EmbeddedChart, type EmbeddedObject, type NamedRange, type ProtectedRange, type Sheet, type TableView } from "@rowsncolumns/spreadsheet";
5
5
  import { type CalculateNowOptions, type CalculationOperation, type CellXfs, type HistoryType, type SheetData, type SharedStrings, type SpreadsheetPatch } from "@rowsncolumns/spreadsheet-state";
6
6
  import type { Patch } from "immer";
7
- import type { WebsocketProvider } from "y-websocket";
8
7
  import * as Y from "yjs";
8
+ import type { CollabProviderLike, YjsRuntime } from "./types";
9
9
  export type UseYSpreadsheetPropsV2<T extends CellData, S extends Sheet, C extends EmbeddedChart, O extends EmbeddedObject> = {
10
10
  /**
11
11
  * Scopes awareness updates to a single document.
@@ -23,6 +23,18 @@ export type UseYSpreadsheetPropsV2<T extends CellData, S extends Sheet, C extend
23
23
  * convention to remain wire-compatible.
24
24
  */
25
25
  awarenessScopeId?: string;
26
+ /**
27
+ * Whether this client may persist to the shared document (the host's
28
+ * edit permission; `false` for view-only sessions). Published on awareness
29
+ * as `canWrite` and consulted by the leader election: the leader is the
30
+ * peer that evaluates backend/agent recalc backlogs and writes the computed
31
+ * values back, so a client whose writes the server refuses must never win —
32
+ * every value it computed would be dropped. Peers whose awareness state has
33
+ * no `canWrite` field (older clients, REST writers) count as writers. When
34
+ * no writer is present there is no leader: nothing could persist the values
35
+ * anyway. Default: `true`.
36
+ */
37
+ canWrite?: boolean;
26
38
  userId: string | number;
27
39
  title: string;
28
40
  sheetId: number;
@@ -44,8 +56,14 @@ export type UseYSpreadsheetPropsV2<T extends CellData, S extends Sheet, C extend
44
56
  * needed so agents touching A1:E10 don't appear to be sitting on A1.
45
57
  */
46
58
  selections?: SelectionArea[];
47
- provider?: HocuspocusProvider | WebsocketProvider | any;
59
+ provider?: HocuspocusProvider | CollabProviderLike | any;
48
60
  doc: Y.Doc;
61
+ /**
62
+ * The Yjs implementation `doc` belongs to (its shared-type constructors and
63
+ * event classes). Defaults to `yjs`; pass the namespace that created `doc`
64
+ * when it is not the `yjs` module (see `YjsRuntime`).
65
+ */
66
+ yjs?: YjsRuntime;
49
67
  calculateNow(options?: CalculateNowOptions): Promise<void>;
50
68
  initialSheets: S[];
51
69
  /**
@@ -142,7 +160,7 @@ export type UseYSpreadsheetPropsV2<T extends CellData, S extends Sheet, C extend
142
160
  };
143
161
  export type YRowData = Y.Map<Y.Array<CellData | null>>;
144
162
  export type YSheetData = Y.Array<YRowData | null>;
145
- export declare const useYSpreadsheetV2: <T extends CellData, S extends Sheet, C extends EmbeddedChart, O extends EmbeddedObject>({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onAfterBroadcastPatch, recalcCellsMaxEntries, onChangeNamedRanges, onChangeProtectedRanges, onChangePivotTables, enqueueCalculation, enqueueGraphOperation, reevaluateDerivationsForCells, onChangeCellXfs, onChangeSharedStrings, calculateNow, onChangeActiveSheet, clientOnlyFunctionPattern, initialSheets, awarenessScopeId, userId, title, sheetId, activeCell, kind, agentInitiator, selections, doc, provider, useV3, sheetDataVersion, onMigrationComplete, deleteV2AfterMigration, skipInitialSync, supportLegacySharedStringsArray, }: UseYSpreadsheetPropsV2<T, S, C, O>) => {
163
+ export declare const useYSpreadsheetV2: <T extends CellData, S extends Sheet, C extends EmbeddedChart, O extends EmbeddedObject>({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onAfterBroadcastPatch, recalcCellsMaxEntries, onChangeNamedRanges, onChangeProtectedRanges, onChangePivotTables, enqueueCalculation, enqueueGraphOperation, reevaluateDerivationsForCells, onChangeCellXfs, onChangeSharedStrings, calculateNow, onChangeActiveSheet, clientOnlyFunctionPattern, initialSheets, awarenessScopeId, canWrite, userId, title, sheetId, activeCell, kind, agentInitiator, selections, doc, provider, yjs, useV3, sheetDataVersion, onMigrationComplete, deleteV2AfterMigration, skipInitialSync, supportLegacySharedStringsArray, }: UseYSpreadsheetPropsV2<T, S, C, O>) => {
146
164
  synced: boolean;
147
165
  users: Collaborator[];
148
166
  onBroadcastPatch: (patches: [patch: SpreadsheetPatch<Patch>, type?: HistoryType | undefined | null][]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"use-yspreadsheet-v2.d.ts","sourceRoot":"","sources":["../../use-yspreadsheet-v2.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-yspreadsheet-v2.d.ts","sourceRoot":"","sources":["../../use-yspreadsheet-v2.ts"],"names":[],"mappings":"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"}
@@ -8,9 +8,9 @@ import { addSheetDataPatches, addSheetDataPatchesV3, applyMapPatches, createCell
8
8
  /**
9
9
  * Convert Y.js events to SheetDataEvent format for incremental processing
10
10
  */
11
- const convertToSheetDataEvents = (events) => {
11
+ const convertToSheetDataEvents = (events, yjs) => {
12
12
  return events.map((event) => {
13
- const isYArrayEvent = event instanceof Y.YArrayEvent;
13
+ const isYArrayEvent = event instanceof yjs.YArrayEvent;
14
14
  return {
15
15
  path: event.path,
16
16
  delta: isYArrayEvent
@@ -52,7 +52,7 @@ const getUserEnteredValueFromYDocV3 = (ySheetDataV3, sheetId, rowIndex, columnIn
52
52
  import { createDeepObservable, createObservable, createSheetDataObservable, getPatchDeltasFromEvent, } from "./observables";
53
53
  // Add a single null entry to the YSheetData
54
54
  const origin = "local";
55
- export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onAfterBroadcastPatch, recalcCellsMaxEntries, onChangeNamedRanges, onChangeProtectedRanges, onChangePivotTables, enqueueCalculation, enqueueGraphOperation, reevaluateDerivationsForCells, onChangeCellXfs, onChangeSharedStrings, calculateNow, onChangeActiveSheet, clientOnlyFunctionPattern, initialSheets, awarenessScopeId, userId, title, sheetId, activeCell, kind, agentInitiator, selections, doc, provider, useV3 = false, sheetDataVersion = "v2", onMigrationComplete, deleteV2AfterMigration = false, skipInitialSync = false, supportLegacySharedStringsArray = false, }) => {
55
+ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onAfterBroadcastPatch, recalcCellsMaxEntries, onChangeNamedRanges, onChangeProtectedRanges, onChangePivotTables, enqueueCalculation, enqueueGraphOperation, reevaluateDerivationsForCells, onChangeCellXfs, onChangeSharedStrings, calculateNow, onChangeActiveSheet, clientOnlyFunctionPattern, initialSheets, awarenessScopeId, canWrite = true, userId, title, sheetId, activeCell, kind, agentInitiator, selections, doc, provider, yjs = Y, useV3 = false, sheetDataVersion = "v2", onMigrationComplete, deleteV2AfterMigration = false, skipInitialSync = false, supportLegacySharedStringsArray = false, }) => {
56
56
  const [synced, setSynced] = useState(false);
57
57
  const [users, setUsers] = useState([]);
58
58
  const [isLeader, setIsLeader] = useState(false);
@@ -68,19 +68,24 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
68
68
  const docStates = resolvedAwarenessScopeId
69
69
  ? entries.filter(([_, state]) => state?.docGuid === resolvedAwarenessScopeId)
70
70
  : entries;
71
- if (!docStates.length) {
71
+ // Only peers that can persist may lead (see `canWrite`). A view-only
72
+ // client's recalc write-backs are refused by the server, so electing it
73
+ // silently drops every computed value. A missing field is a writer.
74
+ const writers = docStates
75
+ .filter(([_, state]) => state?.canWrite !== false)
76
+ .map(([clientId]) => clientId);
77
+ if (!canWrite || !writers.length) {
72
78
  setIsLeader(false);
73
79
  return;
74
80
  }
75
- const states = docStates.map(([clientId]) => clientId);
76
- const minId = Math.min(...states);
81
+ const minId = Math.min(...writers);
77
82
  // Always include self in the elec
78
83
  setIsLeader(doc.clientID === minId);
79
84
  };
80
85
  awareness.on("change", updateLeader);
81
86
  updateLeader(); // Initial check
82
87
  return () => awareness.off("change", updateLeader);
83
- }, [awareness, doc, resolvedAwarenessScopeId]);
88
+ }, [awareness, doc, resolvedAwarenessScopeId, canWrite]);
84
89
  useIsomorphicLayoutEffect(() => {
85
90
  isLeaderRef.current = isLeader;
86
91
  }, [isLeader]);
@@ -219,6 +224,12 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
219
224
  .flatMap((entry) => entry.patches)
220
225
  .filter(([, , source]) => source === "backend" || source === "agent");
221
226
  }, [yRecalcCells]);
227
+ /**
228
+ * Set once the initial sync has built the dependency graph and replayed the
229
+ * backlog, so a later leadership hand-over can replay it again.
230
+ */
231
+ const graphReadyRef = useRef(false);
232
+ const initialSyncTimerRef = useRef(null);
222
233
  // Trigger cell recalcs
223
234
  const syncRecalcCells = useCallback((recalcCells, origin) => {
224
235
  for (const [_, position, source] of recalcCells) {
@@ -389,6 +400,24 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
389
400
  onMigrationComplete,
390
401
  deleteV2AfterMigration,
391
402
  ]);
403
+ /**
404
+ * Replay the backend/agent recalc backlog into the local graph — and, on
405
+ * the leader, evaluate it. Only the leader clears the entries, and only once
406
+ * they sit in its calculation queue: a non-leader merely graph-enqueues
407
+ * (no evaluation), so if it deleted them the leader would never recompute
408
+ * the backlog at all.
409
+ */
410
+ const drainBacklog = useCallback(() => {
411
+ const backlog = getBacklog();
412
+ if (!backlog.length)
413
+ return;
414
+ syncRecalcCells(backlog, "backend");
415
+ if (isLeaderRef.current) {
416
+ doc.transact(() => {
417
+ yRecalcCells.delete(0, yRecalcCells.length);
418
+ }, origin);
419
+ }
420
+ }, [getBacklog, syncRecalcCells, doc, yRecalcCells]);
392
421
  // Synced
393
422
  const onSynced = useCallback(() => {
394
423
  if (!synced) {
@@ -398,35 +427,40 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
398
427
  flushSync(() => {
399
428
  setupInitialSync();
400
429
  });
401
- // Build dependency graph, then sync backlog cells
402
- requestAnimationFrame(async () => {
430
+ // Build dependency graph, then sync backlog cells. Deferred through a
431
+ // timer, not `requestAnimationFrame`: rAF is paused in background tabs,
432
+ // which left a backgrounded leader's backlog unevaluated indefinitely.
433
+ initialSyncTimerRef.current = setTimeout(async () => {
434
+ initialSyncTimerRef.current = null;
403
435
  await calculateNow({
404
436
  disableEvaluation: true,
405
437
  shouldResetCellDependencyGraph: true,
406
438
  });
407
439
  // Sync backlog after graph is initialized
408
- const backlog = getBacklog();
409
- if (backlog.length) {
410
- syncRecalcCells(backlog, "backend");
411
- // Clear processed backlog entries from yRecalcCells
412
- doc.transact(() => {
413
- yRecalcCells.delete(0, yRecalcCells.length);
414
- }, origin);
415
- }
416
- });
440
+ drainBacklog();
441
+ graphReadyRef.current = true;
442
+ }, 0);
417
443
  }
418
444
  setSynced(true);
419
445
  }
420
- }, [
421
- calculateNow,
422
- setupInitialSync,
423
- synced,
424
- syncRecalcCells,
425
- getBacklog,
426
- doc,
427
- yRecalcCells,
428
- skipInitialSync,
429
- ]);
446
+ }, [calculateNow, setupInitialSync, synced, drainBacklog, skipInitialSync]);
447
+ // A client that becomes leader after its initial sync (the previous leader
448
+ // left, or the election settled late) takes over whatever backlog is still
449
+ // in the document: as a non-leader it only mirrored those cells into its
450
+ // graph, so it evaluates them now — and only now clears them.
451
+ useIsomorphicLayoutEffect(() => {
452
+ if (!isLeader || !synced || !graphReadyRef.current)
453
+ return;
454
+ drainBacklog();
455
+ }, [isLeader, synced, drainBacklog]);
456
+ useIsomorphicLayoutEffect(() => {
457
+ return () => {
458
+ if (initialSyncTimerRef.current !== null) {
459
+ clearTimeout(initialSyncTimerRef.current);
460
+ initialSyncTimerRef.current = null;
461
+ }
462
+ };
463
+ }, []);
430
464
  // Sync state
431
465
  if (provider?.synced && !synced) {
432
466
  onSynced();
@@ -454,7 +488,7 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
454
488
  const origin = events?.[0]?.transaction?.origin;
455
489
  if (origin !== "local") {
456
490
  // Convert Y.js events to SheetDataEvent format and apply incremental updates
457
- const sheetDataEvents = convertToSheetDataEvents(events);
491
+ const sheetDataEvents = convertToSheetDataEvents(events, yjs);
458
492
  onChangeSheetData((prevSheetData) => {
459
493
  const result = processSheetDataEvents(sheetDataEvents, prevSheetData, ySheetDataV2);
460
494
  // Fall back to full serialization only if incremental update fails
@@ -517,7 +551,7 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
517
551
  const subs = [];
518
552
  for (const [mappingKey, [ydoc, callback]] of documentMap.entries()) {
519
553
  // Only observe Arrays (shared strings Map is handled separately if needed)
520
- if (ydoc instanceof Y.Array) {
554
+ if (ydoc instanceof yjs.Array) {
521
555
  const subscription = createDeepObservable(ydoc).subscribe((events) => {
522
556
  for (const event of events) {
523
557
  if (event.transaction.origin !== "local") {
@@ -533,7 +567,7 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
533
567
  });
534
568
  subs.push(subscription);
535
569
  }
536
- else if (ydoc instanceof Y.Map && mappingKey === "sharedStrings") {
570
+ else if (ydoc instanceof yjs.Map && mappingKey === "sharedStrings") {
537
571
  // Handle shared strings Map observation
538
572
  const callback_map = (event) => {
539
573
  if (event.transaction.origin !== "local") {
@@ -557,20 +591,29 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
557
591
  if (!synced) {
558
592
  return;
559
593
  }
594
+ const pending = new Set();
560
595
  // Sync all cells that are updated in the backend
561
596
  // // Observe recalc cells separat
562
597
  const onRecalcCellsChange = (event) => {
563
598
  const patches = getPatchDeltasFromEvent(event);
564
599
  const origin = event.transaction.origin;
565
600
  if (origin !== "local") {
566
- requestAnimationFrame(() => {
601
+ // A timer, not `requestAnimationFrame`: rAF is paused in background
602
+ // tabs, so a backgrounded leader would stop evaluating remote patches.
603
+ const timer = setTimeout(() => {
604
+ pending.delete(timer);
567
605
  syncRecalcCells(patches, origin);
568
- });
606
+ }, 0);
607
+ pending.add(timer);
569
608
  }
570
609
  };
571
610
  // Skip the first recalc cells change
572
611
  const sub = createObservable(yRecalcCells).subscribe(onRecalcCellsChange);
573
- return () => sub.unsubscribe();
612
+ return () => {
613
+ sub.unsubscribe();
614
+ for (const timer of pending)
615
+ clearTimeout(timer);
616
+ };
574
617
  }, [syncRecalcCells, recalcCellsMaxEntries, synced, yRecalcCells]);
575
618
  // Broadcast patch
576
619
  const onBroadcastPatch = useCallback((patches) => {
@@ -628,7 +671,7 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
628
671
  addSheetDataPatchesV3(ySheetDataV3, persistableErrorCellPatches);
629
672
  }
630
673
  else {
631
- addSheetDataPatches(ySheetDataV2, persistableErrorCellPatches);
674
+ addSheetDataPatches(ySheetDataV2, persistableErrorCellPatches, yjs);
632
675
  }
633
676
  }, origin);
634
677
  }
@@ -650,7 +693,7 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
650
693
  addSheetDataPatchesV3(ySheetDataV3, patches);
651
694
  }
652
695
  else {
653
- addSheetDataPatches(ySheetDataV2, patches);
696
+ addSheetDataPatches(ySheetDataV2, patches, yjs);
654
697
  }
655
698
  }
656
699
  if (patch.cellXfs) {
@@ -661,10 +704,10 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
661
704
  if (patch?.[mappingKey]) {
662
705
  const patches = patch[mappingKey]?.[patchKey];
663
706
  // Handle shared strings Map (always use Map now)
664
- if (mappingKey === "sharedStrings" && ydoc instanceof Y.Map) {
707
+ if (mappingKey === "sharedStrings" && ydoc instanceof yjs.Map) {
665
708
  applyMapPatches(ydoc, patches);
666
709
  }
667
- else if (ydoc instanceof Y.Array) {
710
+ else if (ydoc instanceof yjs.Array) {
668
711
  // Check if we are updating sheets
669
712
  if (mappingKey === "sheets" && ydoc.length === 0) {
670
713
  // Add initial sheets
@@ -694,6 +737,7 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
694
737
  onAfterBroadcastPatch,
695
738
  documentMap,
696
739
  yRecalcCells,
740
+ yjs,
697
741
  doc,
698
742
  userId,
699
743
  yDisableRecalc,
@@ -777,6 +821,7 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
777
821
  awareness.setLocalStateField("userId", userId);
778
822
  awareness.setLocalStateField("title", title);
779
823
  awareness.setLocalStateField("sheetId", sheetId);
824
+ awareness.setLocalStateField("canWrite", canWrite);
780
825
  if (resolvedAwarenessScopeId) {
781
826
  awareness.setLocalStateField("docGuid", resolvedAwarenessScopeId);
782
827
  }
@@ -798,6 +843,7 @@ export const useYSpreadsheetV2 = ({ onChangeSheetData, onChangeSheets, onChangeT
798
843
  }, [
799
844
  awareness,
800
845
  resolvedAwarenessScopeId,
846
+ canWrite,
801
847
  userId,
802
848
  title,
803
849
  sheetId,
@@ -3,15 +3,17 @@ import type { CellInterface } from "@rowsncolumns/grid";
3
3
  import type { CellData, Collaborator, ConditionalFormatRule, DataValidationRuleRecord, EmbeddedChart, EmbeddedObject, NamedRange, ProtectedRange, Sheet, TableView } from "@rowsncolumns/spreadsheet";
4
4
  import type { CalculateNowOptions, CalculationOperation, HistoryType, SheetData, SpreadsheetPatch } from "@rowsncolumns/spreadsheet-state";
5
5
  import type { Patch } from "immer";
6
- import type { WebsocketProvider } from "y-websocket";
7
- import type * as Y from "yjs";
6
+ import * as Y from "yjs";
7
+ import type { CollabProviderLike, YjsRuntime } from "./types";
8
8
  export type useYSpreadsheetProps<T extends CellData, S extends Sheet, C extends EmbeddedChart, O extends EmbeddedObject> = {
9
9
  userId: string | number;
10
10
  title: string;
11
11
  sheetId: number;
12
12
  activeCell?: CellInterface;
13
- provider?: HocuspocusProvider | WebsocketProvider | any;
13
+ provider?: HocuspocusProvider | CollabProviderLike | any;
14
14
  doc: Y.Doc;
15
+ /** The Yjs implementation `doc` belongs to; defaults to `yjs` (see `YjsRuntime`). */
16
+ yjs?: YjsRuntime;
15
17
  calculateNow(options?: CalculateNowOptions): void;
16
18
  onChangeSheetData: React.Dispatch<React.SetStateAction<SheetData<T>>>;
17
19
  onChangeTables: React.Dispatch<React.SetStateAction<TableView[]>>;
@@ -31,7 +33,7 @@ export type useYSpreadsheetProps<T extends CellData, S extends Sheet, C extends
31
33
  * Does not load initial data
32
34
  * @returns
33
35
  */
34
- export declare const useYSpreadsheet: <T extends CellData, S extends Sheet, C extends EmbeddedChart, O extends EmbeddedObject>({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeNamedRanges, onChangeProtectedRanges, onError, enqueueCalculation, calculateNow, userId, title, sheetId, activeCell, doc, provider, }: useYSpreadsheetProps<T, S, C, O>) => {
36
+ export declare const useYSpreadsheet: <T extends CellData, S extends Sheet, C extends EmbeddedChart, O extends EmbeddedObject>({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeNamedRanges, onChangeProtectedRanges, onError, enqueueCalculation, calculateNow, userId, title, sheetId, activeCell, doc, provider, yjs, }: useYSpreadsheetProps<T, S, C, O>) => {
35
37
  onBroadcastPatch: (patches: [patch: SpreadsheetPatch<Patch>, type?: HistoryType | undefined][]) => void;
36
38
  users: Collaborator[];
37
39
  synced: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"use-yspreadsheet.d.ts","sourceRoot":"","sources":["../../use-yspreadsheet.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-yspreadsheet.d.ts","sourceRoot":"","sources":["../../use-yspreadsheet.ts"],"names":[],"mappings":"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"}
@@ -2,20 +2,20 @@ import { scheduleNormalPriorityCallback, useIsomorphicLayoutEffect, } from "@row
2
2
  import { applyPatches } from "immer";
3
3
  import uniqBy from "lodash/uniqBy";
4
4
  import { useCallback, useMemo, useRef, useState } from "react";
5
- import { applyUpdate, diffUpdate, Doc, encodeStateAsUpdate, encodeStateVector, } from "yjs";
6
- const getStateVector = (doc) => encodeStateVector(doc);
5
+ import * as Y from "yjs";
6
+ const getStateVector = (doc, yjs) => yjs.encodeStateVector(doc);
7
7
  // Function to get updates between two state vectors
8
- const getUpdatesBetween = (doc, stateVector1, stateVector2) => {
9
- const diff = encodeStateAsUpdate(doc, stateVector1);
10
- const newDiff = encodeStateAsUpdate(doc, stateVector2);
11
- return diffUpdate(diff, newDiff);
8
+ const getUpdatesBetween = (doc, stateVector1, stateVector2, yjs) => {
9
+ const diff = yjs.encodeStateAsUpdate(doc, stateVector1);
10
+ const newDiff = yjs.encodeStateAsUpdate(doc, stateVector2);
11
+ return yjs.diffUpdate(diff, newDiff);
12
12
  };
13
13
  /**
14
14
  * Connects to a Y server and picks up the most recent change
15
15
  * Does not load initial data
16
16
  * @returns
17
17
  */
18
- export const useYSpreadsheet = ({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeNamedRanges, onChangeProtectedRanges, onError, enqueueCalculation, calculateNow, userId, title, sheetId, activeCell, doc, provider, }) => {
18
+ export const useYSpreadsheet = ({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeNamedRanges, onChangeProtectedRanges, onError, enqueueCalculation, calculateNow, userId, title, sheetId, activeCell, doc, provider, yjs = Y, }) => {
19
19
  const [users, setUsers] = useState([]);
20
20
  const [synced, setSynced] = useState(false);
21
21
  const previousStateVectorRef = useRef(undefined);
@@ -89,10 +89,10 @@ export const useYSpreadsheet = ({ onChangeSheetData, onChangeSheets, onChangeTab
89
89
  let syncedDoc = doc;
90
90
  if (previousStateVectorRef.current) {
91
91
  // Capture the current state vector on subsequent syncs
92
- currentStateVectorRef.current = getStateVector(doc);
93
- const updates = getUpdatesBetween(doc, previousStateVectorRef.current, currentStateVectorRef.current);
94
- const newDoc = new Doc({ gc: true });
95
- applyUpdate(newDoc, updates);
92
+ currentStateVectorRef.current = getStateVector(doc, yjs);
93
+ const updates = getUpdatesBetween(doc, previousStateVectorRef.current, currentStateVectorRef.current, yjs);
94
+ const newDoc = new yjs.Doc({ gc: true });
95
+ yjs.applyUpdate(newDoc, updates);
96
96
  syncedDoc = newDoc;
97
97
  previousStateVectorRef.current = currentStateVectorRef.current;
98
98
  }
@@ -137,7 +137,7 @@ export const useYSpreadsheet = ({ onChangeSheetData, onChangeSheets, onChangeTab
137
137
  });
138
138
  // Set synced
139
139
  setSynced(true);
140
- }, [calculateNow, docMap, onError, doc]);
140
+ }, [calculateNow, docMap, onError, doc, yjs]);
141
141
  // Sync state
142
142
  if (provider?.synced && !synced) {
143
143
  onSynced();
@@ -147,7 +147,7 @@ export const useYSpreadsheet = ({ onChangeSheetData, onChangeSheets, onChangeTab
147
147
  provider?.on("synced", onSynced);
148
148
  const onConnectionStatusChange = ({ status }) => {
149
149
  if (status === "disconnected") {
150
- previousStateVectorRef.current = getStateVector(doc);
150
+ previousStateVectorRef.current = getStateVector(doc, yjs);
151
151
  }
152
152
  };
153
153
  provider?.on("status", onConnectionStatusChange);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowsncolumns/y-spreadsheet",
3
- "version": "15.0.2",
3
+ "version": "15.1.0",
4
4
  "main": "./dist/cjs/index.js",
5
5
  "module": "./dist/esm/index.js",
6
6
  "exports": {
@@ -56,7 +56,6 @@
56
56
  "@rowsncolumns/utils": ">=9.3.0",
57
57
  "react": ">=18.0.0",
58
58
  "react-dom": ">=18.0.0",
59
- "y-websocket": ">=2.0.0",
60
59
  "yjs": ">=13.0.0"
61
60
  },
62
61
  "dependencies": {