@rowsncolumns/y-spreadsheet 14.0.32

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 (53) hide show
  1. package/LICENSE.md +100 -0
  2. package/Readme.md +77 -0
  3. package/dist/cjs/helpers.d.ts +179 -0
  4. package/dist/cjs/helpers.d.ts.map +1 -0
  5. package/dist/cjs/helpers.js +1635 -0
  6. package/dist/cjs/index.d.ts +6 -0
  7. package/dist/cjs/index.d.ts.map +1 -0
  8. package/dist/cjs/index.js +8 -0
  9. package/dist/cjs/observables.d.ts +10 -0
  10. package/dist/cjs/observables.d.ts.map +1 -0
  11. package/dist/cjs/observables.js +90 -0
  12. package/dist/cjs/profiling/batch-profile.spec.d.ts +2 -0
  13. package/dist/cjs/profiling/batch-profile.spec.d.ts.map +1 -0
  14. package/dist/cjs/profiling/batch-profile.spec.js +175 -0
  15. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  16. package/dist/cjs/types.d.ts +62 -0
  17. package/dist/cjs/types.d.ts.map +1 -0
  18. package/dist/cjs/types.js +2 -0
  19. package/dist/cjs/use-yspreadsheet-v2.d.ts +152 -0
  20. package/dist/cjs/use-yspreadsheet-v2.d.ts.map +1 -0
  21. package/dist/cjs/use-yspreadsheet-v2.js +980 -0
  22. package/dist/cjs/use-yspreadsheet.d.ts +39 -0
  23. package/dist/cjs/use-yspreadsheet.d.ts.map +1 -0
  24. package/dist/cjs/use-yspreadsheet.js +430 -0
  25. package/dist/cjs/vitest.config.d.ts +3 -0
  26. package/dist/cjs/vitest.config.d.ts.map +1 -0
  27. package/dist/cjs/vitest.config.js +23 -0
  28. package/dist/esm/helpers.d.ts +179 -0
  29. package/dist/esm/helpers.d.ts.map +1 -0
  30. package/dist/esm/helpers.js +1346 -0
  31. package/dist/esm/index.d.ts +6 -0
  32. package/dist/esm/index.d.ts.map +1 -0
  33. package/dist/esm/index.js +5 -0
  34. package/dist/esm/observables.d.ts +10 -0
  35. package/dist/esm/observables.d.ts.map +1 -0
  36. package/dist/esm/observables.js +51 -0
  37. package/dist/esm/profiling/batch-profile.spec.d.ts +2 -0
  38. package/dist/esm/profiling/batch-profile.spec.d.ts.map +1 -0
  39. package/dist/esm/profiling/batch-profile.spec.js +131 -0
  40. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  41. package/dist/esm/types.d.ts +62 -0
  42. package/dist/esm/types.d.ts.map +1 -0
  43. package/dist/esm/types.js +1 -0
  44. package/dist/esm/use-yspreadsheet-v2.d.ts +152 -0
  45. package/dist/esm/use-yspreadsheet-v2.d.ts.map +1 -0
  46. package/dist/esm/use-yspreadsheet-v2.js +814 -0
  47. package/dist/esm/use-yspreadsheet.d.ts +39 -0
  48. package/dist/esm/use-yspreadsheet.d.ts.map +1 -0
  49. package/dist/esm/use-yspreadsheet.js +304 -0
  50. package/dist/esm/vitest.config.d.ts +3 -0
  51. package/dist/esm/vitest.config.d.ts.map +1 -0
  52. package/dist/esm/vitest.config.js +21 -0
  53. package/package.json +54 -0
@@ -0,0 +1,980 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useYSpreadsheetV2 = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var spreadsheet_1 = require("@rowsncolumns/spreadsheet");
6
+ var utils_1 = require("@rowsncolumns/utils");
7
+ var ui_1 = require("@rowsncolumns/ui");
8
+ var react_1 = require("react");
9
+ var react_dom_1 = require("react-dom");
10
+ var Y = tslib_1.__importStar(require("yjs"));
11
+ var helpers_1 = require("./helpers");
12
+ /**
13
+ * Convert Y.js events to SheetDataEvent format for incremental processing
14
+ */
15
+ var convertToSheetDataEvents = function (events) {
16
+ return events.map(function (event) {
17
+ var isYArrayEvent = event instanceof Y.YArrayEvent;
18
+ return {
19
+ path: event.path,
20
+ delta: isYArrayEvent
21
+ ? event.changes.delta
22
+ : undefined,
23
+ isYArrayEvent: isYArrayEvent,
24
+ };
25
+ });
26
+ };
27
+ /**
28
+ * Read user entered value directly from Y.js data to avoid race conditions
29
+ * with React state updates. This is used when recalcCells observer fires
30
+ * before sheetData observer has updated React state.
31
+ */
32
+ var getUserEnteredValueFromYDoc = function (ySheetData, sheetId, rowIndex, columnIndex) {
33
+ var sheet = ySheetData.get(String(sheetId));
34
+ if (!sheet || rowIndex >= sheet.length)
35
+ return undefined;
36
+ var row = sheet.get(rowIndex);
37
+ if (!row)
38
+ return undefined;
39
+ var values = row.get("values");
40
+ if (!values || columnIndex >= values.length)
41
+ return undefined;
42
+ var cell = values.get(columnIndex);
43
+ // Extract value from ExtendedValue, matching SheetCell.getUserEnteredValue() logic
44
+ // ue is the abbreviated form of userEnteredValue
45
+ var userEnteredValue = (0, utils_1.getCellUserEnteredValue)(cell);
46
+ return (0, utils_1.getExtendedValueFormula)(userEnteredValue);
47
+ };
48
+ /**
49
+ * Read user entered value from V3 Y.js data structure
50
+ */
51
+ var getUserEnteredValueFromYDocV3 = function (ySheetDataV3, sheetId, rowIndex, columnIndex) {
52
+ var cellKey = (0, helpers_1.createCellKeyV3)(sheetId, rowIndex, columnIndex);
53
+ var cellDataV3 = ySheetDataV3.get(cellKey);
54
+ return (0, utils_1.getExtendedValueFormula)((0, utils_1.getCellUserEnteredValue)(cellDataV3 === null || cellDataV3 === void 0 ? void 0 : cellDataV3.value));
55
+ };
56
+ var observables_1 = require("./observables");
57
+ // Add a single null entry to the YSheetData
58
+ var origin = "local";
59
+ var useYSpreadsheetV2 = function (_a) {
60
+ var onChangeSheetData = _a.onChangeSheetData, onChangeSheets = _a.onChangeSheets, onChangeTables = _a.onChangeTables, onChangeCharts = _a.onChangeCharts, onChangeEmbeds = _a.onChangeEmbeds, onChangeConditionalFormats = _a.onChangeConditionalFormats, onChangeDataValidations = _a.onChangeDataValidations, onChangeCitations = _a.onChangeCitations, onAfterBroadcastPatch = _a.onAfterBroadcastPatch, recalcCellsMaxEntries = _a.recalcCellsMaxEntries, onChangeNamedRanges = _a.onChangeNamedRanges, onChangeProtectedRanges = _a.onChangeProtectedRanges, onChangePivotTables = _a.onChangePivotTables, enqueueCalculation = _a.enqueueCalculation, enqueueGraphOperation = _a.enqueueGraphOperation, reevaluateDerivationsForCells = _a.reevaluateDerivationsForCells, onChangeCellXfs = _a.onChangeCellXfs, onChangeSharedStrings = _a.onChangeSharedStrings, calculateNow = _a.calculateNow, onChangeActiveSheet = _a.onChangeActiveSheet, clientOnlyFunctionPattern = _a.clientOnlyFunctionPattern, initialSheets = _a.initialSheets, awarenessScopeId = _a.awarenessScopeId, userId = _a.userId, title = _a.title, sheetId = _a.sheetId, activeCell = _a.activeCell, kind = _a.kind, agentInitiator = _a.agentInitiator, selections = _a.selections, doc = _a.doc, provider = _a.provider, _b = _a.useV3, useV3 = _b === void 0 ? false : _b, _c = _a.sheetDataVersion, sheetDataVersion = _c === void 0 ? "v2" : _c, onMigrationComplete = _a.onMigrationComplete, _d = _a.deleteV2AfterMigration, deleteV2AfterMigration = _d === void 0 ? false : _d, _e = _a.skipInitialSync, skipInitialSync = _e === void 0 ? false : _e, _f = _a.supportLegacySharedStringsArray, supportLegacySharedStringsArray = _f === void 0 ? false : _f;
61
+ var _g = tslib_1.__read((0, react_1.useState)(false), 2), synced = _g[0], setSynced = _g[1];
62
+ var _h = tslib_1.__read((0, react_1.useState)([]), 2), users = _h[0], setUsers = _h[1];
63
+ var _j = tslib_1.__read((0, react_1.useState)(false), 2), isLeader = _j[0], setIsLeader = _j[1];
64
+ var isLeaderRef = (0, react_1.useRef)(false);
65
+ var resolvedAwarenessScopeId = awarenessScopeId;
66
+ // Listen to awareness
67
+ var awareness = provider === null || provider === void 0 ? void 0 : provider.awareness;
68
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
69
+ if (!awareness)
70
+ return;
71
+ var updateLeader = function () {
72
+ var entries = Array.from(awareness.getStates().entries());
73
+ var docStates = resolvedAwarenessScopeId
74
+ ? entries.filter(function (_a) {
75
+ var _b = tslib_1.__read(_a, 2), _ = _b[0], state = _b[1];
76
+ return (state === null || state === void 0 ? void 0 : state.docGuid) === resolvedAwarenessScopeId;
77
+ })
78
+ : entries;
79
+ if (!docStates.length) {
80
+ setIsLeader(false);
81
+ return;
82
+ }
83
+ var states = docStates.map(function (_a) {
84
+ var _b = tslib_1.__read(_a, 1), clientId = _b[0];
85
+ return clientId;
86
+ });
87
+ var minId = Math.min.apply(Math, tslib_1.__spreadArray([], tslib_1.__read(states), false));
88
+ // Always include self in the elec
89
+ setIsLeader(doc.clientID === minId);
90
+ };
91
+ awareness.on("change", updateLeader);
92
+ updateLeader(); // Initial check
93
+ return function () { return awareness.off("change", updateLeader); };
94
+ }, [awareness, doc, resolvedAwarenessScopeId]);
95
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
96
+ isLeaderRef.current = isLeader;
97
+ }, [isLeader]);
98
+ var ySheetDataV2 = (0, react_1.useMemo)(function () { return doc.getMap("sheetDataV2"); }, [doc]);
99
+ // V3: Flat map structure - Map<cellKey, CellDataV3>
100
+ // cellKey format: `${sheetId}!${A1Address}` (e.g., "1!A1", "2!B5")
101
+ var ySheetDataV3 = (0, react_1.useMemo)(function () { return doc.getMap("sheetDataV3"); }, [doc]);
102
+ // Doc metadata - used as fallback for version when prop is undefined
103
+ var yDocMeta = (0, react_1.useMemo)(function () { return doc.getMap("docMeta"); }, [doc]);
104
+ var yDisableRecalc = (0, react_1.useMemo)(function () { return doc.getMap("disableRecalc"); }, [doc]);
105
+ // Recalc cells
106
+ var yRecalcCells = (0, react_1.useMemo)(function () { return doc.getArray("recalcCells"); }, [doc]);
107
+ // Make sure initialSheets is always filled
108
+ var getInitialSheets = (0, react_1.useCallback)(function () {
109
+ if (initialSheets.length) {
110
+ return initialSheets;
111
+ }
112
+ return [(0, spreadsheet_1.createNewSheet)(1, "Sheet1")];
113
+ }, [initialSheets]);
114
+ // cellxfs
115
+ var yCellXfs = (0, react_1.useMemo)(function () { return doc.getMap("cellXfs"); }, [doc]);
116
+ // Shared strings - always use Map for new storage, Array kept for migration
117
+ var ySharedStringsArray = (0, react_1.useMemo)(function () { return doc.getArray("sharedStrings"); }, [doc]);
118
+ var ySharedStringsMap = (0, react_1.useMemo)(function () { return doc.getMap("sharedStringsMap"); }, [doc]);
119
+ // Track if initial sync has changed active sheet
120
+ var isActiveSheetChanged = (0, react_1.useRef)(false);
121
+ /**
122
+ * Handle shared strings updates from the Y.Map (new format).
123
+ * Merges incoming Map entries into the current shared strings state.
124
+ * Used for documents that store shared strings in the Map format.
125
+ */
126
+ var handleSharedStringsMapChange = (0, react_1.useCallback)(function (value) {
127
+ onChangeSharedStrings === null || onChangeSharedStrings === void 0 ? void 0 : onChangeSharedStrings(function (currentMap) {
128
+ var e_1, _a;
129
+ var newMap = new Map(currentMap);
130
+ try {
131
+ for (var value_1 = tslib_1.__values(value), value_1_1 = value_1.next(); !value_1_1.done; value_1_1 = value_1.next()) {
132
+ var _b = tslib_1.__read(value_1_1.value, 2), key = _b[0], val = _b[1];
133
+ newMap.set(key, val);
134
+ }
135
+ }
136
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
137
+ finally {
138
+ try {
139
+ if (value_1_1 && !value_1_1.done && (_a = value_1.return)) _a.call(value_1);
140
+ }
141
+ finally { if (e_1) throw e_1.error; }
142
+ }
143
+ return newMap;
144
+ });
145
+ }, [onChangeSharedStrings]);
146
+ /**
147
+ * Handle shared strings updates from the Y.Array (legacy format).
148
+ * Converts array indices to string keys and merges into current state.
149
+ * Provides backwards compatibility for older documents using array storage.
150
+ */
151
+ var handleSharedStringsArrayChange = (0, react_1.useCallback)(function (value) {
152
+ onChangeSharedStrings === null || onChangeSharedStrings === void 0 ? void 0 : onChangeSharedStrings(function (currentMap) {
153
+ var newMap = new Map(currentMap);
154
+ value.forEach(function (v, index) {
155
+ newMap.set(String(index), v);
156
+ });
157
+ return newMap;
158
+ });
159
+ }, [onChangeSharedStrings]);
160
+ var documentMap = (0, react_1.useMemo)(function () {
161
+ var entries = tslib_1.__spreadArray([
162
+ ["sheets", [doc.getArray("sheets"), onChangeSheets]],
163
+ ["tables", [doc.getArray("tables"), onChangeTables]],
164
+ [
165
+ "embeds",
166
+ [doc.getArray("embeds"), onChangeEmbeds],
167
+ ],
168
+ [
169
+ "charts",
170
+ [doc.getArray("charts"), onChangeCharts],
171
+ ],
172
+ [
173
+ "conditionalFormats",
174
+ [
175
+ doc.getArray("conditionalFormats"),
176
+ onChangeConditionalFormats,
177
+ ],
178
+ ],
179
+ [
180
+ "dataValidations",
181
+ [
182
+ doc.getArray("dataValidations"),
183
+ onChangeDataValidations,
184
+ ],
185
+ ],
186
+ [
187
+ "pivotTables",
188
+ [doc.getArray("pivotTables"), onChangePivotTables],
189
+ ],
190
+ [
191
+ "namedRanges",
192
+ [doc.getArray("namedRanges"), onChangeNamedRanges],
193
+ ],
194
+ [
195
+ "protectedRanges",
196
+ [
197
+ doc.getArray("protectedRanges"),
198
+ onChangeProtectedRanges,
199
+ ],
200
+ ],
201
+ [
202
+ "citations",
203
+ [doc.getArray("citations"), onChangeCitations],
204
+ ],
205
+ [
206
+ "sharedStrings",
207
+ [ySharedStringsMap, handleSharedStringsMapChange],
208
+ ]
209
+ ], tslib_1.__read((supportLegacySharedStringsArray
210
+ ? [
211
+ [
212
+ "sharedStringsArray",
213
+ [ySharedStringsArray, handleSharedStringsArrayChange],
214
+ ],
215
+ ]
216
+ : [])), false);
217
+ return new Map(entries);
218
+ }, [
219
+ doc,
220
+ onChangeSheets,
221
+ onChangeTables,
222
+ onChangeEmbeds,
223
+ onChangeCharts,
224
+ onChangeConditionalFormats,
225
+ onChangeDataValidations,
226
+ onChangePivotTables,
227
+ onChangeNamedRanges,
228
+ onChangeProtectedRanges,
229
+ onChangeCitations,
230
+ ySharedStringsMap,
231
+ handleSharedStringsMapChange,
232
+ supportLegacySharedStringsArray,
233
+ ySharedStringsArray,
234
+ handleSharedStringsArrayChange,
235
+ ]);
236
+ // Get backlog of recalc cells from backend
237
+ var getBacklog = (0, react_1.useCallback)(function () {
238
+ return yRecalcCells
239
+ .toArray()
240
+ .flatMap(function (entry) { return entry.patches; })
241
+ .filter(function (_a) {
242
+ var _b = tslib_1.__read(_a, 3), source = _b[2];
243
+ return source === "backend" || source === "agent";
244
+ });
245
+ }, [yRecalcCells]);
246
+ // Trigger cell recalcs
247
+ var syncRecalcCells = (0, react_1.useCallback)(function (recalcCells, origin) {
248
+ var e_2, _a;
249
+ try {
250
+ for (var recalcCells_1 = tslib_1.__values(recalcCells), recalcCells_1_1 = recalcCells_1.next(); !recalcCells_1_1.done; recalcCells_1_1 = recalcCells_1.next()) {
251
+ var _b = tslib_1.__read(recalcCells_1_1.value, 3), _1 = _b[0], position = _b[1], source = _b[2];
252
+ // backend will calculate results
253
+ var isAgent = source === "agent"; // Agent already does the calculation
254
+ // Read directly from Y.js to avoid race condition where recalcCells
255
+ // observer fires before sheetData observer has updated React state
256
+ var formulaValue = useV3
257
+ ? getUserEnteredValueFromYDocV3(ySheetDataV3, position.sheetId, position.rowIndex, position.columnIndex)
258
+ : getUserEnteredValueFromYDoc(ySheetDataV2, position.sheetId, position.rowIndex, position.columnIndex);
259
+ // Was this from a changeBatch event in the back-end?
260
+ // To support client functions to run only
261
+ var isChangeBatch = position.force && isAgent;
262
+ var isClientOnly = !(0, utils_1.isNil)(formulaValue) &&
263
+ !(0, utils_1.isNil)(clientOnlyFunctionPattern) &&
264
+ (0, utils_1.isAFormula)(formulaValue)
265
+ ? clientOnlyFunctionPattern.test(String(formulaValue))
266
+ : false;
267
+ /**
268
+ * Determines whether to force immediate execution of the operation.
269
+ *
270
+ * Force execution is allowed only for leaders and under these conditions:
271
+ * - No source specified (backwards compatibility - treats as force exec)
272
+ * - Source is "backend" (backend-initiated changes)
273
+ * - Client-only mode with change batch (batch operations need immediate execution)
274
+ *
275
+ * Note: Agent sources never force execution since they've pre-calculated results.
276
+ */
277
+ var forceExecution = isLeaderRef.current &&
278
+ (!source || // backwards compatible: no source means force exec for leader
279
+ source === "backend" ||
280
+ (isClientOnly && isChangeBatch));
281
+ var enqueue = forceExecution
282
+ ? enqueueCalculation
283
+ : enqueueGraphOperation;
284
+ /**
285
+ * Selects the appropriate queue function for the operation.
286
+ *
287
+ * - enqueueCalculation: Used when forcing execution (immediate formula evaluation)
288
+ * - enqueueGraphOperation: Used for normal graph operations (no formula evaluation)
289
+ */
290
+ enqueue === null || enqueue === void 0 ? void 0 : enqueue({
291
+ type: "add",
292
+ position: position,
293
+ source: source,
294
+ force: forceExecution,
295
+ });
296
+ }
297
+ }
298
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
299
+ finally {
300
+ try {
301
+ if (recalcCells_1_1 && !recalcCells_1_1.done && (_a = recalcCells_1.return)) _a.call(recalcCells_1);
302
+ }
303
+ finally { if (e_2) throw e_2.error; }
304
+ }
305
+ // The agent/backend pre-computes cell VALUES (hence the graph-only
306
+ // enqueues above), but conditional-format / data-validation RESULTS
307
+ // are derived on the client — re-evaluate them for the changed cells
308
+ // so their paint tracks the new values.
309
+ if (recalcCells.length) {
310
+ reevaluateDerivationsForCells === null || reevaluateDerivationsForCells === void 0 ? void 0 : reevaluateDerivationsForCells(recalcCells.map(function (_a) {
311
+ var _b = tslib_1.__read(_a, 2), position = _b[1];
312
+ return ({
313
+ sheetId: position.sheetId,
314
+ rowIndex: position.rowIndex,
315
+ columnIndex: position.columnIndex,
316
+ });
317
+ }));
318
+ }
319
+ }, [
320
+ synced,
321
+ clientOnlyFunctionPattern,
322
+ ySheetDataV2,
323
+ ySheetDataV3,
324
+ useV3,
325
+ reevaluateDerivationsForCells,
326
+ ]);
327
+ // Setup initial sync
328
+ var setupInitialSync = (0, react_1.useCallback)(function () {
329
+ var e_3, _a;
330
+ var _b;
331
+ // Migration from sharedStrings (Array) to sharedStringsMap (Map) is disabled.
332
+ // Both formats are now supported simultaneously via handleSharedStringsMapChange
333
+ // and handleSharedStringsArrayChange callbacks.
334
+ var arrayData = ySharedStringsArray.toArray();
335
+ var mapData = ySharedStringsMap.toJSON();
336
+ var hasArrayData = arrayData.length > 0;
337
+ var isMapEmpty = Object.keys(mapData).length === 0;
338
+ // Migrate if array has data and map is empty
339
+ if (!supportLegacySharedStringsArray && hasArrayData && isMapEmpty) {
340
+ doc.transact(function () {
341
+ // Convert array to map with string keys
342
+ arrayData.forEach(function (value, index) {
343
+ ySharedStringsMap.set(String(index), value);
344
+ });
345
+ // Optional: Clear the old array to save space
346
+ // Uncomment if you want to remove old data after migration
347
+ // ySharedStringsArray.delete(0, ySharedStringsArray.length);
348
+ }, "migration");
349
+ }
350
+ try {
351
+ for (var _c = tslib_1.__values(documentMap.entries()), _d = _c.next(); !_d.done; _d = _c.next()) {
352
+ var _e = tslib_1.__read(_d.value, 2), mappingKey = _e[0], _f = tslib_1.__read(_e[1], 2), ydoc = _f[0], callback = _f[1];
353
+ var value = ydoc.toJSON();
354
+ if (mappingKey === "sheets") {
355
+ var sheets = (Array.isArray(value) && value.length
356
+ ? (0, helpers_1.validateSheets)(value)
357
+ : getInitialSheets());
358
+ callback === null || callback === void 0 ? void 0 : callback(sheets);
359
+ // Set active sheet id
360
+ if (sheets.length && !isActiveSheetChanged.current) {
361
+ onChangeActiveSheet === null || onChangeActiveSheet === void 0 ? void 0 : onChangeActiveSheet((0, utils_1.sortSheetsByIndex)(sheets)[0].sheetId);
362
+ isActiveSheetChanged.current = true;
363
+ }
364
+ }
365
+ else if (mappingKey === "sharedStrings") {
366
+ // Convert Map object to Map instance for shared strings
367
+ var sharedStringsMap = new Map(Object.entries(value !== null && value !== void 0 ? value : {}));
368
+ callback === null || callback === void 0 ? void 0 : callback(sharedStringsMap);
369
+ }
370
+ else {
371
+ callback === null || callback === void 0 ? void 0 : callback(value);
372
+ }
373
+ }
374
+ }
375
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
376
+ finally {
377
+ try {
378
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
379
+ }
380
+ finally { if (e_3) throw e_3.error; }
381
+ }
382
+ // Load sheet data based on V2 or V3 mode
383
+ if (useV3) {
384
+ // Check if we need to migrate from V2 to V3
385
+ // Use prop if provided, otherwise fall back to yDocMeta for backwards compatibility
386
+ var resolvedVersion = sheetDataVersion !== null && sheetDataVersion !== void 0 ? sheetDataVersion : yDocMeta.get("sheetDataVersion");
387
+ if (resolvedVersion !== "v3") {
388
+ var v2Data_1 = ySheetDataV2.toJSON();
389
+ var hasV2Data = Object.keys(v2Data_1).length > 0;
390
+ if (hasV2Data) {
391
+ // Migrate V2 data to V3
392
+ doc.transact(function () {
393
+ var e_4, _a, e_5, _b;
394
+ var v3Data = (0, helpers_1.sheetDataV2ToV3)(v2Data_1);
395
+ try {
396
+ // Add all V3 entries to the Y.Map
397
+ for (var _c = tslib_1.__values(v3Data.entries()), _d = _c.next(); !_d.done; _d = _c.next()) {
398
+ var _e = tslib_1.__read(_d.value, 2), key = _e[0], cellData = _e[1];
399
+ ySheetDataV3.set(key, cellData);
400
+ }
401
+ }
402
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
403
+ finally {
404
+ try {
405
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
406
+ }
407
+ finally { if (e_4) throw e_4.error; }
408
+ }
409
+ // Optionally clear V2 data after migration
410
+ if (deleteV2AfterMigration) {
411
+ try {
412
+ for (var _f = tslib_1.__values(ySheetDataV2.keys()), _g = _f.next(); !_g.done; _g = _f.next()) {
413
+ var sheetId_1 = _g.value;
414
+ ySheetDataV2.delete(sheetId_1);
415
+ }
416
+ }
417
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
418
+ finally {
419
+ try {
420
+ if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
421
+ }
422
+ finally { if (e_5) throw e_5.error; }
423
+ }
424
+ }
425
+ }, "migrate-v2-to-v3");
426
+ // Notify backend that migration completed
427
+ onMigrationComplete === null || onMigrationComplete === void 0 ? void 0 : onMigrationComplete("v3");
428
+ }
429
+ else {
430
+ // No V2 data, notify backend of version
431
+ onMigrationComplete === null || onMigrationComplete === void 0 ? void 0 : onMigrationComplete("v3");
432
+ }
433
+ }
434
+ // V3: Convert flat map to V2 format for React state
435
+ var v3Data = ySheetDataV3.toJSON();
436
+ var v3Map = new Map(Object.entries(v3Data));
437
+ onChangeSheetData === null || onChangeSheetData === void 0 ? void 0 : onChangeSheetData((0, helpers_1.sheetDataV3ToV2)(v3Map));
438
+ }
439
+ else {
440
+ onChangeSheetData === null || onChangeSheetData === void 0 ? void 0 : onChangeSheetData(ySheetDataV2.toJSON());
441
+ }
442
+ // Cellxfs
443
+ var cellXfs = new Map(Object.entries((_b = yCellXfs.toJSON()) !== null && _b !== void 0 ? _b : {}));
444
+ onChangeCellXfs === null || onChangeCellXfs === void 0 ? void 0 : onChangeCellXfs(cellXfs);
445
+ }, [
446
+ documentMap,
447
+ onChangeCellXfs,
448
+ onChangeSheetData,
449
+ ySheetDataV2,
450
+ ySheetDataV3,
451
+ yDocMeta,
452
+ yCellXfs,
453
+ getInitialSheets,
454
+ onChangeActiveSheet,
455
+ ySharedStringsArray,
456
+ ySharedStringsMap,
457
+ doc,
458
+ useV3,
459
+ sheetDataVersion,
460
+ onMigrationComplete,
461
+ deleteV2AfterMigration,
462
+ ]);
463
+ // Synced
464
+ var onSynced = (0, react_1.useCallback)(function () {
465
+ if (!synced) {
466
+ if (!skipInitialSync) {
467
+ // Use flushSync to ensure React state is updated before continuing
468
+ // This guarantees sheetData is available when syncRecalcCells runs
469
+ (0, react_dom_1.flushSync)(function () {
470
+ setupInitialSync();
471
+ });
472
+ // Build dependency graph, then sync backlog cells
473
+ requestAnimationFrame(function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
474
+ var backlog;
475
+ return tslib_1.__generator(this, function (_a) {
476
+ switch (_a.label) {
477
+ case 0: return [4 /*yield*/, calculateNow({
478
+ disableEvaluation: true,
479
+ shouldResetCellDependencyGraph: true,
480
+ })];
481
+ case 1:
482
+ _a.sent();
483
+ backlog = getBacklog();
484
+ if (backlog.length) {
485
+ syncRecalcCells(backlog, "backend");
486
+ // Clear processed backlog entries from yRecalcCells
487
+ doc.transact(function () {
488
+ yRecalcCells.delete(0, yRecalcCells.length);
489
+ }, origin);
490
+ }
491
+ return [2 /*return*/];
492
+ }
493
+ });
494
+ }); });
495
+ }
496
+ setSynced(true);
497
+ }
498
+ }, [
499
+ calculateNow,
500
+ setupInitialSync,
501
+ synced,
502
+ syncRecalcCells,
503
+ getBacklog,
504
+ doc,
505
+ yRecalcCells,
506
+ skipInitialSync,
507
+ ]);
508
+ // Sync state
509
+ if ((provider === null || provider === void 0 ? void 0 : provider.synced) && !synced) {
510
+ onSynced();
511
+ }
512
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
513
+ provider === null || provider === void 0 ? void 0 : provider.on("synced", onSynced);
514
+ return function () {
515
+ provider === null || provider === void 0 ? void 0 : provider.off("synced", onSynced);
516
+ };
517
+ }, [provider]);
518
+ // Only for sheet Data (V2 mode)
519
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
520
+ if (useV3)
521
+ return; // Skip V2 observer in V3 mode
522
+ // Subscribe to sheet data
523
+ var subscription = (0, observables_1.createSheetDataObservable)(ySheetDataV2).subscribe(function (events) {
524
+ var _a, _b;
525
+ /**
526
+ * Disable recalc is a flag that is set ONLY locally
527
+ * when a calculation completes
528
+ *
529
+ * This is to prevent yjs from observing sheetdata
530
+ * and updating it, which is not necessary
531
+ */
532
+ if (events.length) {
533
+ var origin_1 = (_b = (_a = events === null || events === void 0 ? void 0 : events[0]) === null || _a === void 0 ? void 0 : _a.transaction) === null || _b === void 0 ? void 0 : _b.origin;
534
+ if (origin_1 !== "local") {
535
+ // Convert Y.js events to SheetDataEvent format and apply incremental updates
536
+ var sheetDataEvents_1 = convertToSheetDataEvents(events);
537
+ onChangeSheetData(function (prevSheetData) {
538
+ var result = (0, helpers_1.processSheetDataEvents)(sheetDataEvents_1, prevSheetData, ySheetDataV2);
539
+ // Fall back to full serialization only if incremental update fails
540
+ return result !== null && result !== void 0 ? result : ySheetDataV2.toJSON();
541
+ });
542
+ }
543
+ }
544
+ });
545
+ return function () {
546
+ subscription.unsubscribe();
547
+ };
548
+ }, [onChangeSheetData, ySheetDataV2, useV3]);
549
+ // Only for sheet Data (V3 mode)
550
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
551
+ if (!useV3)
552
+ return; // Skip V3 observer in V2 mode
553
+ // Subscribe to V3 sheet data (flat Map)
554
+ var callback = function (event) {
555
+ if (event.transaction.origin !== "local") {
556
+ // Extract changes synchronously - Yjs requires accessing event.changes
557
+ // within the event handler, not in a deferred callback like setState
558
+ var changedKeys_1 = new Map();
559
+ event.changes.keys.forEach(function (change, key) {
560
+ changedKeys_1.set(key, { action: change.action });
561
+ });
562
+ // Now we can safely use the extracted data in the async setState callback
563
+ onChangeSheetData(function (prevSheetData) {
564
+ var result = (0, helpers_1.processSheetDataEventsV3)(changedKeys_1, prevSheetData, ySheetDataV3);
565
+ if (result) {
566
+ return result;
567
+ }
568
+ // Fall back to full conversion
569
+ var v3Data = ySheetDataV3.toJSON();
570
+ var v3Map = new Map(Object.entries(v3Data));
571
+ return (0, helpers_1.sheetDataV3ToV2)(v3Map);
572
+ });
573
+ }
574
+ };
575
+ ySheetDataV3.observe(callback);
576
+ return function () {
577
+ ySheetDataV3.unobserve(callback);
578
+ };
579
+ }, [onChangeSheetData, ySheetDataV3, useV3]);
580
+ // CellXfs
581
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
582
+ // Subscribe to sheet data
583
+ var callback = function (event) {
584
+ var _a;
585
+ if (event.transaction.origin !== "local") {
586
+ var cellXfs = new Map(Object.entries((_a = yCellXfs.toJSON()) !== null && _a !== void 0 ? _a : {}));
587
+ onChangeCellXfs === null || onChangeCellXfs === void 0 ? void 0 : onChangeCellXfs(cellXfs);
588
+ }
589
+ };
590
+ yCellXfs.observe(callback);
591
+ return function () {
592
+ yCellXfs.unobserve(callback);
593
+ };
594
+ }, [onChangeCellXfs, yCellXfs]);
595
+ // All others
596
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
597
+ var e_6, _a;
598
+ var subs = [];
599
+ var _loop_1 = function (mappingKey, ydoc, callback) {
600
+ // Only observe Arrays (shared strings Map is handled separately if needed)
601
+ if (ydoc instanceof Y.Array) {
602
+ var subscription = (0, observables_1.createDeepObservable)(ydoc).subscribe(function (events) {
603
+ var e_7, _a;
604
+ var _b;
605
+ try {
606
+ for (var events_1 = (e_7 = void 0, tslib_1.__values(events)), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) {
607
+ var event_1 = events_1_1.value;
608
+ if (event_1.transaction.origin !== "local") {
609
+ var json = ydoc.toJSON();
610
+ if (Array.isArray(json) &&
611
+ json.length &&
612
+ ((_b = json[0]) === null || _b === void 0 ? void 0 : _b.userId)) {
613
+ continue;
614
+ }
615
+ callback === null || callback === void 0 ? void 0 : callback(json);
616
+ }
617
+ }
618
+ }
619
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
620
+ finally {
621
+ try {
622
+ if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1);
623
+ }
624
+ finally { if (e_7) throw e_7.error; }
625
+ }
626
+ });
627
+ subs.push(subscription);
628
+ }
629
+ else if (ydoc instanceof Y.Map && mappingKey === "sharedStrings") {
630
+ // Handle shared strings Map observation
631
+ var callback_map_1 = function (event) {
632
+ if (event.transaction.origin !== "local") {
633
+ var json = ydoc.toJSON();
634
+ var sharedStringsMap = new Map(Object.entries(json !== null && json !== void 0 ? json : {}));
635
+ callback === null || callback === void 0 ? void 0 : callback(sharedStringsMap);
636
+ }
637
+ };
638
+ ydoc.observe(callback_map_1);
639
+ subs.push({
640
+ unsubscribe: function () { return ydoc.unobserve(callback_map_1); },
641
+ });
642
+ }
643
+ };
644
+ try {
645
+ for (var _b = tslib_1.__values(documentMap.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
646
+ var _d = tslib_1.__read(_c.value, 2), mappingKey = _d[0], _e = tslib_1.__read(_d[1], 2), ydoc = _e[0], callback = _e[1];
647
+ _loop_1(mappingKey, ydoc, callback);
648
+ }
649
+ }
650
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
651
+ finally {
652
+ try {
653
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
654
+ }
655
+ finally { if (e_6) throw e_6.error; }
656
+ }
657
+ return function () {
658
+ subs.forEach(function (sub) { return sub.unsubscribe(); });
659
+ };
660
+ }, [documentMap]);
661
+ // Only for recalc cells
662
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
663
+ if (!synced) {
664
+ return;
665
+ }
666
+ // Sync all cells that are updated in the backend
667
+ // // Observe recalc cells separat
668
+ var onRecalcCellsChange = function (event) {
669
+ var patches = (0, observables_1.getPatchDeltasFromEvent)(event);
670
+ var origin = event.transaction.origin;
671
+ if (origin !== "local") {
672
+ requestAnimationFrame(function () {
673
+ syncRecalcCells(patches, origin);
674
+ });
675
+ }
676
+ };
677
+ // Skip the first recalc cells change
678
+ var sub = (0, observables_1.createObservable)(yRecalcCells).subscribe(onRecalcCellsChange);
679
+ return function () { return sub.unsubscribe(); };
680
+ }, [syncRecalcCells, recalcCellsMaxEntries, synced, yRecalcCells]);
681
+ // Broadcast patch
682
+ var onBroadcastPatch = (0, react_1.useCallback)(function (patches) {
683
+ var e_8, _a, e_9, _b;
684
+ var _c;
685
+ // Ignore push flag
686
+ var disableRecalc = Boolean(patches.find(function (_a) {
687
+ var _b = tslib_1.__read(_a, 1), patch = _b[0];
688
+ return patch === null || patch === void 0 ? void 0 : patch.disableRecalc;
689
+ }));
690
+ // Check if this is a full recalc
691
+ var isFullRecalc = Boolean(patches.find(function (_a) {
692
+ var _b = tslib_1.__read(_a, 1), patch = _b[0];
693
+ return patch === null || patch === void 0 ? void 0 : patch.isFullRecalc;
694
+ }));
695
+ var disableSync = isFullRecalc ? false : disableRecalc && !isLeader;
696
+ // Only leader can broadcast recalc patches. Error effective values are
697
+ // the exception: async custom-function failures (#VALUE!, #NAME?, …)
698
+ // only exist as disableRecalc calc-result patches, and the leader's
699
+ // write is deferred/lossy — if non-leaders drop them too, the error
700
+ // paints during the live session but the cell is blank after reload.
701
+ // Sync just the error-bearing cell patches; ordinary recalc results
702
+ // stay leader-gated so N clients don't all write identical results.
703
+ if (disableSync) {
704
+ var errorCellPatches = [];
705
+ try {
706
+ for (var patches_1 = tslib_1.__values(patches), patches_1_1 = patches_1.next(); !patches_1_1.done; patches_1_1 = patches_1.next()) {
707
+ var _d = tslib_1.__read(patches_1_1.value, 2), patch = _d[0], _e = _d[1], type = _e === void 0 ? "redo" : _e;
708
+ if (!(patch === null || patch === void 0 ? void 0 : patch.sheetData)) {
709
+ continue;
710
+ }
711
+ type = type !== null && type !== void 0 ? type : "redo";
712
+ var patchKey = type === "redo" ? "patches" : "inversePatches";
713
+ var sheetDataPatches = patch.sheetData[patchKey];
714
+ if (sheetDataPatches === null || sheetDataPatches === void 0 ? void 0 : sheetDataPatches.length) {
715
+ errorCellPatches.push.apply(errorCellPatches, tslib_1.__spreadArray([], tslib_1.__read((0, helpers_1.filterErrorCellPatches)(sheetDataPatches)), false));
716
+ }
717
+ }
718
+ }
719
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
720
+ finally {
721
+ try {
722
+ if (patches_1_1 && !patches_1_1.done && (_a = patches_1.return)) _a.call(patches_1);
723
+ }
724
+ finally { if (e_8) throw e_8.error; }
725
+ }
726
+ // Containment for this non-leader write path: an error may only
727
+ // fill a cell with no effective value (the blank-after-reload case)
728
+ // or replace an existing error — never a non-error result; a cell
729
+ // whose formula (ue) changed since the calculation started is
730
+ // skipped; identical persisted errors are skipped so concurrent
731
+ // non-leaders don't append redundant Y.js updates; and a proceeding
732
+ // write merges ONLY the error effective value into the existing doc
733
+ // cell, preserving every other persisted field.
734
+ var persistableErrorCellPatches_1 = [];
735
+ try {
736
+ for (var errorCellPatches_1 = tslib_1.__values(errorCellPatches), errorCellPatches_1_1 = errorCellPatches_1.next(); !errorCellPatches_1_1.done; errorCellPatches_1_1 = errorCellPatches_1.next()) {
737
+ var errorPatch = errorCellPatches_1_1.value;
738
+ var _f = tslib_1.__read(errorPatch.path, 4), patchSheetId = _f[0], rowIndex = _f[1], columnIndex = _f[3];
739
+ var currentCellData = useV3
740
+ ? (_c = ySheetDataV3.get((0, helpers_1.createCellKeyV3)(Number(patchSheetId), Number(rowIndex), Number(columnIndex)))) === null || _c === void 0 ? void 0 : _c.value
741
+ : (0, helpers_1.getCellDataFromYSheetData)(ySheetDataV2, patchSheetId, Number(rowIndex), Number(columnIndex));
742
+ var incomingCellData = errorPatch.value;
743
+ if (!(0, helpers_1.shouldPersistErrorCellPatch)(currentCellData, incomingCellData)) {
744
+ continue;
745
+ }
746
+ persistableErrorCellPatches_1.push(tslib_1.__assign(tslib_1.__assign({}, errorPatch), { value: (0, helpers_1.mergeErrorEffectiveValueIntoCellData)(currentCellData, incomingCellData) }));
747
+ }
748
+ }
749
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
750
+ finally {
751
+ try {
752
+ if (errorCellPatches_1_1 && !errorCellPatches_1_1.done && (_b = errorCellPatches_1.return)) _b.call(errorCellPatches_1);
753
+ }
754
+ finally { if (e_9) throw e_9.error; }
755
+ }
756
+ if (persistableErrorCellPatches_1.length) {
757
+ doc.transact(function () {
758
+ if (useV3) {
759
+ (0, helpers_1.addSheetDataPatchesV3)(ySheetDataV3, persistableErrorCellPatches_1);
760
+ }
761
+ else {
762
+ (0, helpers_1.addSheetDataPatches)(ySheetDataV2, persistableErrorCellPatches_1);
763
+ }
764
+ }, origin);
765
+ }
766
+ return;
767
+ }
768
+ doc.transact(function () {
769
+ var e_10, _a, e_11, _b;
770
+ var _c, _d, _e, _f;
771
+ try {
772
+ for (var patches_2 = tslib_1.__values(patches), patches_2_1 = patches_2.next(); !patches_2_1.done; patches_2_1 = patches_2.next()) {
773
+ var _g = tslib_1.__read(patches_2_1.value, 2), patch = _g[0], _h = _g[1], type = _h === void 0 ? "redo" : _h;
774
+ // Check for existence of patch
775
+ // Investigate when when importing csv, it create a [undefined, undefined] patch object
776
+ // todo @Vinay
777
+ if (!patch) {
778
+ continue;
779
+ }
780
+ type = type !== null && type !== void 0 ? type : "redo";
781
+ var patchKey = type === "redo" ? "patches" : "inversePatches";
782
+ if (patch.sheetData) {
783
+ var patches_3 = (_c = patch.sheetData) === null || _c === void 0 ? void 0 : _c[patchKey];
784
+ if (useV3) {
785
+ (0, helpers_1.addSheetDataPatchesV3)(ySheetDataV3, patches_3);
786
+ }
787
+ else {
788
+ (0, helpers_1.addSheetDataPatches)(ySheetDataV2, patches_3);
789
+ }
790
+ }
791
+ if (patch.cellXfs) {
792
+ var patches_4 = (_d = patch.cellXfs) === null || _d === void 0 ? void 0 : _d[patchKey];
793
+ (0, helpers_1.applyMapPatches)(yCellXfs, patches_4);
794
+ }
795
+ try {
796
+ for (var _j = (e_11 = void 0, tslib_1.__values(documentMap.entries())), _k = _j.next(); !_k.done; _k = _j.next()) {
797
+ var _l = tslib_1.__read(_k.value, 2), mappingKey = _l[0], _m = tslib_1.__read(_l[1], 1), ydoc = _m[0];
798
+ if (patch === null || patch === void 0 ? void 0 : patch[mappingKey]) {
799
+ var patches_5 = (_e = patch[mappingKey]) === null || _e === void 0 ? void 0 : _e[patchKey];
800
+ // Handle shared strings Map (always use Map now)
801
+ if (mappingKey === "sharedStrings" && ydoc instanceof Y.Map) {
802
+ (0, helpers_1.applyMapPatches)(ydoc, patches_5);
803
+ }
804
+ else if (ydoc instanceof Y.Array) {
805
+ // Check if we are updating sheets
806
+ if (mappingKey === "sheets" && ydoc.length === 0) {
807
+ // Add initial sheets
808
+ ydoc.insert(0, getInitialSheets());
809
+ }
810
+ (0, helpers_1.updateArrayType)(ydoc, patches_5);
811
+ }
812
+ }
813
+ }
814
+ }
815
+ catch (e_11_1) { e_11 = { error: e_11_1 }; }
816
+ finally {
817
+ try {
818
+ if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
819
+ }
820
+ finally { if (e_11) throw e_11.error; }
821
+ }
822
+ // Recalc cells
823
+ if ((_f = patch.recalcCells) === null || _f === void 0 ? void 0 : _f[type]) {
824
+ if (isLeader) {
825
+ // Remove old cells
826
+ yRecalcCells.delete(0, yRecalcCells.length);
827
+ }
828
+ yRecalcCells.push([
829
+ {
830
+ userId: userId,
831
+ patches: Array.from(patch.recalcCells[type]),
832
+ },
833
+ ]);
834
+ }
835
+ }
836
+ }
837
+ catch (e_10_1) { e_10 = { error: e_10_1 }; }
838
+ finally {
839
+ try {
840
+ if (patches_2_1 && !patches_2_1.done && (_a = patches_2.return)) _a.call(patches_2);
841
+ }
842
+ finally { if (e_10) throw e_10.error; }
843
+ }
844
+ }, origin);
845
+ onAfterBroadcastPatch === null || onAfterBroadcastPatch === void 0 ? void 0 : onAfterBroadcastPatch(patches, doc);
846
+ }, [
847
+ onAfterBroadcastPatch,
848
+ documentMap,
849
+ yRecalcCells,
850
+ doc,
851
+ userId,
852
+ yDisableRecalc,
853
+ ySheetDataV2,
854
+ ySheetDataV3,
855
+ isLeader,
856
+ recalcCellsMaxEntries,
857
+ useV3,
858
+ ]);
859
+ // Listen to active cell change
860
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
861
+ if (activeCell) {
862
+ awareness === null || awareness === void 0 ? void 0 : awareness.setLocalStateField("activeCell", activeCell);
863
+ }
864
+ }, [awareness, activeCell]);
865
+ // Listen to sheet change
866
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
867
+ awareness === null || awareness === void 0 ? void 0 : awareness.setLocalStateField("sheetId", sheetId);
868
+ }, [awareness, sheetId]);
869
+ // Listen to selection range change
870
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
871
+ if (selections && selections.length > 0) {
872
+ awareness === null || awareness === void 0 ? void 0 : awareness.setLocalStateField("selections", selections);
873
+ }
874
+ }, [awareness, selections]);
875
+ // Awareness
876
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
877
+ var onChangeAwareness = function () {
878
+ var e_12, _a;
879
+ var _b, _c, _d, _e, _f;
880
+ if (awareness) {
881
+ var collaborators = [];
882
+ try {
883
+ for (var _g = tslib_1.__values(awareness.getStates().entries()), _h = _g.next(); !_h.done; _h = _g.next()) {
884
+ var _j = tslib_1.__read(_h.value, 2), _2 = _j[0], value = _j[1];
885
+ if (!value)
886
+ continue;
887
+ // States with no docGuid are accepted as a wildcard so REST
888
+ // writers or pre-scoped clients still appear as collaborators.
889
+ var docMatches = !resolvedAwarenessScopeId ||
890
+ !value.docGuid ||
891
+ value.docGuid === resolvedAwarenessScopeId;
892
+ if (!docMatches)
893
+ continue;
894
+ // Canonical shape is the nested `state.user = { kind, name, userId,
895
+ // agentInitiator? }` used by Olympus, pptx-studio, and the keryx
896
+ // agentTask helper. Fall back to legacy flat fields for one
897
+ // release so callers can migrate at their own pace.
898
+ var userBlock = ((_b = value.user) !== null && _b !== void 0 ? _b : null);
899
+ var resolvedUserId = (_c = userBlock === null || userBlock === void 0 ? void 0 : userBlock.userId) !== null && _c !== void 0 ? _c : value.userId;
900
+ var resolvedTitle = (_d = userBlock === null || userBlock === void 0 ? void 0 : userBlock.name) !== null && _d !== void 0 ? _d : value.title;
901
+ var resolvedKind = (_e = userBlock === null || userBlock === void 0 ? void 0 : userBlock.kind) !== null && _e !== void 0 ? _e : value.kind;
902
+ var resolvedAgentInitiator = (_f = userBlock === null || userBlock === void 0 ? void 0 : userBlock.agentInitiator) !== null && _f !== void 0 ? _f : value.agentInitiator;
903
+ if (!resolvedUserId)
904
+ continue;
905
+ if (!value.activeCell && !value.selections)
906
+ continue;
907
+ collaborators.push({
908
+ activeCell: value.activeCell,
909
+ title: (resolvedTitle !== null && resolvedTitle !== void 0 ? resolvedTitle : ""),
910
+ userId: resolvedUserId,
911
+ sheetId: value.sheetId,
912
+ kind: resolvedKind,
913
+ agentInitiator: resolvedAgentInitiator,
914
+ selections: value.selections,
915
+ });
916
+ }
917
+ }
918
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
919
+ finally {
920
+ try {
921
+ if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
922
+ }
923
+ finally { if (e_12) throw e_12.error; }
924
+ }
925
+ setUsers(collaborators);
926
+ }
927
+ };
928
+ // Listen to awareness
929
+ awareness === null || awareness === void 0 ? void 0 : awareness.on("change", onChangeAwareness);
930
+ return function () {
931
+ awareness === null || awareness === void 0 ? void 0 : awareness.off("change", onChangeAwareness);
932
+ };
933
+ }, [awareness, resolvedAwarenessScopeId]);
934
+ // Set local awareness state.
935
+ //
936
+ // Use setLocalStateField for each field individually so sibling consumers
937
+ // (Olympus's unified presence layer, agent-task helpers, etc.) can compose
938
+ // their own fields on the same awareness state without us clobbering them.
939
+ (0, ui_1.useIsomorphicLayoutEffect)(function () {
940
+ if (!awareness)
941
+ return;
942
+ awareness.setLocalStateField("userId", userId);
943
+ awareness.setLocalStateField("title", title);
944
+ awareness.setLocalStateField("sheetId", sheetId);
945
+ if (resolvedAwarenessScopeId) {
946
+ awareness.setLocalStateField("docGuid", resolvedAwarenessScopeId);
947
+ }
948
+ // Mirror the canonical nested shape so other Athena editors can read
949
+ // state.user.* without falling back to the legacy flat fields.
950
+ var userBlock = {
951
+ userId: userId,
952
+ name: title,
953
+ };
954
+ if (kind)
955
+ userBlock.kind = kind;
956
+ if (agentInitiator)
957
+ userBlock.agentInitiator = agentInitiator;
958
+ awareness.setLocalStateField("user", userBlock);
959
+ if (kind)
960
+ awareness.setLocalStateField("kind", kind);
961
+ if (agentInitiator)
962
+ awareness.setLocalStateField("agentInitiator", agentInitiator);
963
+ }, [
964
+ awareness,
965
+ resolvedAwarenessScopeId,
966
+ userId,
967
+ title,
968
+ sheetId,
969
+ kind,
970
+ agentInitiator,
971
+ ]);
972
+ return {
973
+ synced: synced,
974
+ users: users,
975
+ onBroadcastPatch: onBroadcastPatch,
976
+ isLeader: isLeader,
977
+ setupInitialSync: setupInitialSync,
978
+ };
979
+ };
980
+ exports.useYSpreadsheetV2 = useYSpreadsheetV2;