@rowsncolumns/y-spreadsheet 15.0.0 → 15.0.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../helpers.ts"],"names":[],"mappings":"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"}
@@ -132,127 +132,81 @@ exports.applyPatchesToYDoc = applyPatchesToYDoc;
132
132
  * 2. Grouping updates by sheet/row for efficient batch application
133
133
  */
134
134
  var addSheetDataPatches = function (ySheetData, patches) {
135
- var e_2, _a, e_3, _b, e_4, _c, e_5, _d, e_6, _e;
136
- // Optimization: Pre-process and consolidate cell-level patches
137
- // For bulk operations like paste (10k+ rows), this dramatically reduces overhead
135
+ var e_2, _a;
136
+ // Optimization: consecutive cell-level patches are consolidated (later
137
+ // patches override earlier ones for the same cell) and applied together,
138
+ // grouped by sheet and row so bulk operations like paste (10k+ rows) cost
139
+ // one row lookup per row. They are never reordered ACROSS a row- or
140
+ // sheet-level patch (see `addSheetDataPatchesV3`).
138
141
  // Key format: "sheetId:rowIndex:columnIndex"
139
- var cellUpdates = new Map();
140
- var otherPatches = [];
141
- try {
142
- // First pass: categorize and consolidate patches
143
- for (var patches_13 = tslib_1.__values(patches), patches_13_1 = patches_13.next(); !patches_13_1.done; patches_13_1 = patches_13.next()) {
144
- var patch = patches_13_1.value;
145
- var op = patch.op, path = patch.path, value = patch.value;
146
- var _f = tslib_1.__read(path, 1), sheetIdRaw = _f[0];
147
- // Handle undefined sheetId separately (full replace)
148
- if (typeof sheetIdRaw === "undefined") {
149
- otherPatches.push(patch);
150
- continue;
151
- }
152
- // Use string representation for sheet ID to handle both string and numeric IDs
153
- var sheetIdStr = String(sheetIdRaw);
154
- if (op === "replace" || op === "add") {
155
- // [sheetId, rowIndex, 'values', columnIndex] - Single cell update (most common in paste)
156
- if (path.length === 4) {
157
- var _g = tslib_1.__read(path, 4), rowIndex = _g[1], columnIndex = _g[3];
158
- var rowIdx = Number(rowIndex);
159
- var colIdx = Number(columnIndex);
160
- var key = "".concat(sheetIdStr, ":").concat(rowIdx, ":").concat(colIdx);
161
- // Later patches override earlier ones for the same cell
162
- cellUpdates.set(key, { sheetIdStr: sheetIdStr, rowIdx: rowIdx, colIdx: colIdx, value: value });
163
- }
164
- else {
165
- // Row-level or sheet-level updates
166
- otherPatches.push(patch);
167
- }
168
- }
169
- else if (op === "remove") {
170
- if (path.length === 4) {
171
- var _h = tslib_1.__read(path, 4), rowIndex = _h[1], columnIndex = _h[3];
172
- var rowIdx = Number(rowIndex);
173
- var colIdx = Number(columnIndex);
174
- var key = "".concat(sheetIdStr, ":").concat(rowIdx, ":").concat(colIdx);
175
- cellUpdates.set(key, { sheetIdStr: sheetIdStr, rowIdx: rowIdx, colIdx: colIdx, value: null });
142
+ var pendingCellUpdates = new Map();
143
+ var flushPendingCellUpdates = function () {
144
+ var e_3, _a, e_4, _b, e_5, _c, e_6, _d;
145
+ if (pendingCellUpdates.size === 0) {
146
+ return;
147
+ }
148
+ var sheetRowUpdates = new Map();
149
+ try {
150
+ for (var pendingCellUpdates_1 = tslib_1.__values(pendingCellUpdates), pendingCellUpdates_1_1 = pendingCellUpdates_1.next(); !pendingCellUpdates_1_1.done; pendingCellUpdates_1_1 = pendingCellUpdates_1.next()) {
151
+ var _e = tslib_1.__read(pendingCellUpdates_1_1.value, 2), _f = _e[1], sheetIdStr = _f.sheetIdStr, rowIdx = _f.rowIdx, colIdx = _f.colIdx, value = _f.value;
152
+ if (!sheetRowUpdates.has(sheetIdStr)) {
153
+ sheetRowUpdates.set(sheetIdStr, new Map());
176
154
  }
177
- else {
178
- otherPatches.push(patch);
155
+ var rowUpdates = sheetRowUpdates.get(sheetIdStr);
156
+ if (!rowUpdates.has(rowIdx)) {
157
+ rowUpdates.set(rowIdx, new Map());
179
158
  }
159
+ rowUpdates.get(rowIdx).set(colIdx, value);
180
160
  }
181
- else {
182
- otherPatches.push(patch);
183
- }
184
- }
185
- }
186
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
187
- finally {
188
- try {
189
- if (patches_13_1 && !patches_13_1.done && (_a = patches_13.return)) _a.call(patches_13);
190
161
  }
191
- finally { if (e_2) throw e_2.error; }
192
- }
193
- // Second pass: Group cell updates by sheet and row for efficient batch application
194
- // This minimizes Y.js operations by batching updates to the same row
195
- var sheetRowUpdates = new Map();
196
- try {
197
- for (var cellUpdates_1 = tslib_1.__values(cellUpdates), cellUpdates_1_1 = cellUpdates_1.next(); !cellUpdates_1_1.done; cellUpdates_1_1 = cellUpdates_1.next()) {
198
- var _j = tslib_1.__read(cellUpdates_1_1.value, 2), _k = _j[1], sheetIdStr = _k.sheetIdStr, rowIdx = _k.rowIdx, colIdx = _k.colIdx, value = _k.value;
199
- if (!sheetRowUpdates.has(sheetIdStr)) {
200
- sheetRowUpdates.set(sheetIdStr, new Map());
201
- }
202
- var rowUpdates = sheetRowUpdates.get(sheetIdStr);
203
- if (!rowUpdates.has(rowIdx)) {
204
- rowUpdates.set(rowIdx, new Map());
162
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
163
+ finally {
164
+ try {
165
+ if (pendingCellUpdates_1_1 && !pendingCellUpdates_1_1.done && (_a = pendingCellUpdates_1.return)) _a.call(pendingCellUpdates_1);
205
166
  }
206
- rowUpdates.get(rowIdx).set(colIdx, value);
167
+ finally { if (e_3) throw e_3.error; }
207
168
  }
208
- }
209
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
210
- finally {
211
169
  try {
212
- if (cellUpdates_1_1 && !cellUpdates_1_1.done && (_b = cellUpdates_1.return)) _b.call(cellUpdates_1);
213
- }
214
- finally { if (e_3) throw e_3.error; }
215
- }
216
- try {
217
- // Apply batched cell updates
218
- for (var sheetRowUpdates_1 = tslib_1.__values(sheetRowUpdates), sheetRowUpdates_1_1 = sheetRowUpdates_1.next(); !sheetRowUpdates_1_1.done; sheetRowUpdates_1_1 = sheetRowUpdates_1.next()) {
219
- var _l = tslib_1.__read(sheetRowUpdates_1_1.value, 2), sheetIdStr = _l[0], rowUpdates = _l[1];
220
- var ysheet = (0, exports.getSheetFromYSheetData)(ySheetData, sheetIdStr);
221
- try {
222
- for (var rowUpdates_1 = (e_5 = void 0, tslib_1.__values(rowUpdates)), rowUpdates_1_1 = rowUpdates_1.next(); !rowUpdates_1_1.done; rowUpdates_1_1 = rowUpdates_1.next()) {
223
- var _m = tslib_1.__read(rowUpdates_1_1.value, 2), rowIdx = _m[0], colUpdates = _m[1];
224
- var rowData = (0, exports.getRowDataFromSheet)(ysheet, rowIdx);
225
- try {
226
- for (var colUpdates_1 = (e_6 = void 0, tslib_1.__values(colUpdates)), colUpdates_1_1 = colUpdates_1.next(); !colUpdates_1_1.done; colUpdates_1_1 = colUpdates_1.next()) {
227
- var _o = tslib_1.__read(colUpdates_1_1.value, 2), colIdx = _o[0], value = _o[1];
228
- (0, exports.setCellDataInRowData)(rowData, colIdx, value);
229
- }
230
- }
231
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
232
- finally {
170
+ for (var sheetRowUpdates_1 = tslib_1.__values(sheetRowUpdates), sheetRowUpdates_1_1 = sheetRowUpdates_1.next(); !sheetRowUpdates_1_1.done; sheetRowUpdates_1_1 = sheetRowUpdates_1.next()) {
171
+ var _g = tslib_1.__read(sheetRowUpdates_1_1.value, 2), sheetIdStr = _g[0], rowUpdates = _g[1];
172
+ var ysheet = (0, exports.getSheetFromYSheetData)(ySheetData, sheetIdStr);
173
+ try {
174
+ for (var rowUpdates_1 = (e_5 = void 0, tslib_1.__values(rowUpdates)), rowUpdates_1_1 = rowUpdates_1.next(); !rowUpdates_1_1.done; rowUpdates_1_1 = rowUpdates_1.next()) {
175
+ var _h = tslib_1.__read(rowUpdates_1_1.value, 2), rowIdx = _h[0], colUpdates = _h[1];
176
+ var rowData = (0, exports.getRowDataFromSheet)(ysheet, rowIdx);
233
177
  try {
234
- if (colUpdates_1_1 && !colUpdates_1_1.done && (_e = colUpdates_1.return)) _e.call(colUpdates_1);
178
+ for (var colUpdates_1 = (e_6 = void 0, tslib_1.__values(colUpdates)), colUpdates_1_1 = colUpdates_1.next(); !colUpdates_1_1.done; colUpdates_1_1 = colUpdates_1.next()) {
179
+ var _j = tslib_1.__read(colUpdates_1_1.value, 2), colIdx = _j[0], value = _j[1];
180
+ (0, exports.setCellDataInRowData)(rowData, colIdx, value);
181
+ }
182
+ }
183
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
184
+ finally {
185
+ try {
186
+ if (colUpdates_1_1 && !colUpdates_1_1.done && (_d = colUpdates_1.return)) _d.call(colUpdates_1);
187
+ }
188
+ finally { if (e_6) throw e_6.error; }
235
189
  }
236
- finally { if (e_6) throw e_6.error; }
237
190
  }
238
191
  }
239
- }
240
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
241
- finally {
242
- try {
243
- if (rowUpdates_1_1 && !rowUpdates_1_1.done && (_d = rowUpdates_1.return)) _d.call(rowUpdates_1);
192
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
193
+ finally {
194
+ try {
195
+ if (rowUpdates_1_1 && !rowUpdates_1_1.done && (_c = rowUpdates_1.return)) _c.call(rowUpdates_1);
196
+ }
197
+ finally { if (e_5) throw e_5.error; }
244
198
  }
245
- finally { if (e_5) throw e_5.error; }
246
199
  }
247
200
  }
248
- }
249
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
250
- finally {
251
- try {
252
- if (sheetRowUpdates_1_1 && !sheetRowUpdates_1_1.done && (_c = sheetRowUpdates_1.return)) _c.call(sheetRowUpdates_1);
201
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
202
+ finally {
203
+ try {
204
+ if (sheetRowUpdates_1_1 && !sheetRowUpdates_1_1.done && (_b = sheetRowUpdates_1.return)) _b.call(sheetRowUpdates_1);
205
+ }
206
+ finally { if (e_4) throw e_4.error; }
253
207
  }
254
- finally { if (e_4) throw e_4.error; }
255
- }
208
+ pendingCellUpdates.clear();
209
+ };
256
210
  // Process non-cell-level patches using original logic
257
211
  var processPatch = function (_a) {
258
212
  var op = _a.op, path = _a.path, value = _a.value;
@@ -369,8 +323,40 @@ var addSheetDataPatches = function (ySheetData, patches) {
369
323
  }
370
324
  }
371
325
  };
372
- // Only process non-cell-level patches (cell-level already handled via consolidation)
373
- otherPatches.forEach(processPatch);
326
+ try {
327
+ for (var patches_13 = tslib_1.__values(patches), patches_13_1 = patches_13.next(); !patches_13_1.done; patches_13_1 = patches_13.next()) {
328
+ var patch = patches_13_1.value;
329
+ var op = patch.op, path = patch.path, value = patch.value;
330
+ var _b = tslib_1.__read(path, 1), sheetIdRaw = _b[0];
331
+ if (typeof sheetIdRaw !== "undefined" &&
332
+ path.length === 4 &&
333
+ (op === "replace" || op === "add" || op === "remove")) {
334
+ // [sheetId, rowIndex, 'values', columnIndex] - single cell update (most common in paste)
335
+ var _c = tslib_1.__read(path, 4), rowIndex = _c[1], columnIndex = _c[3];
336
+ // Use string representation for sheet ID to handle both string and numeric IDs
337
+ var sheetIdStr = String(sheetIdRaw);
338
+ var rowIdx = Number(rowIndex);
339
+ var colIdx = Number(columnIndex);
340
+ pendingCellUpdates.set("".concat(sheetIdStr, ":").concat(rowIdx, ":").concat(colIdx), {
341
+ sheetIdStr: sheetIdStr,
342
+ rowIdx: rowIdx,
343
+ colIdx: colIdx,
344
+ value: op === "remove" ? null : value,
345
+ });
346
+ continue;
347
+ }
348
+ flushPendingCellUpdates();
349
+ processPatch(patch);
350
+ }
351
+ }
352
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
353
+ finally {
354
+ try {
355
+ if (patches_13_1 && !patches_13_1.done && (_a = patches_13.return)) _a.call(patches_13);
356
+ }
357
+ finally { if (e_2) throw e_2.error; }
358
+ }
359
+ flushPendingCellUpdates();
374
360
  };
375
361
  exports.addSheetDataPatches = addSheetDataPatches;
376
362
  var getSheetFromYSheetData = function (ySheetData, sheetId) {
@@ -657,7 +643,12 @@ var updateArrayType = function (yarray, patches) {
657
643
  updatedItem = (0, cloneDeep_1.default)(baseItem);
658
644
  pending = { index: index, item: updatedItem };
659
645
  }
660
- (0, set_1.default)(updatedItem, nestedPath, value);
646
+ // Clone the value too: immer auto-freezes the state its patches point
647
+ // into, so a later patch of the same batch that writes INSIDE an object
648
+ // this one placed (`rowMetadata/4` = `{ outlineLevel: 1 }`, then
649
+ // `rowMetadata/4/outlineLevel` = 2 — a row grouped twice by one
650
+ // Subtotal) would otherwise hit a frozen object and be dropped.
651
+ (0, set_1.default)(updatedItem, nestedPath, (0, cloneDeep_1.default)(value));
661
652
  }
662
653
  }
663
654
  else if (op === "remove") {
@@ -1193,82 +1184,7 @@ exports.getSheetDataV3 = getSheetDataV3;
1193
1184
  * 2. Processing all cell updates in a single pass
1194
1185
  */
1195
1186
  var addSheetDataPatchesV3 = function (ySheetDataV3, patches) {
1196
- var e_13, _a, e_14, _b;
1197
- // Optimization: Pre-process and consolidate cell-level patches
1198
- // For bulk operations like paste (10k+ rows), this dramatically reduces overhead
1199
- var cellUpdates = new Map();
1200
- var otherPatches = [];
1201
- try {
1202
- // First pass: categorize and consolidate patches
1203
- for (var patches_15 = tslib_1.__values(patches), patches_15_1 = patches_15.next(); !patches_15_1.done; patches_15_1 = patches_15.next()) {
1204
- var patch = patches_15_1.value;
1205
- var op = patch.op, path = patch.path, value = patch.value;
1206
- var _c = tslib_1.__read(path, 1), sheetIdRaw = _c[0];
1207
- // Handle undefined sheetId separately (full replace)
1208
- if (typeof sheetIdRaw === "undefined") {
1209
- otherPatches.push(patch);
1210
- continue;
1211
- }
1212
- var sheetId = Number(sheetIdRaw);
1213
- if (op === "replace" || op === "add") {
1214
- // [sheetId, rowIndex, 'values', columnIndex] - Single cell update (most common in paste)
1215
- if (path.length === 4) {
1216
- var _d = tslib_1.__read(path, 4), rowIndex = _d[1], columnIndex = _d[3];
1217
- var rowIdx = Number(rowIndex);
1218
- var colIdx = Number(columnIndex);
1219
- var key = (0, exports.createCellKeyV3)(sheetId, rowIdx, colIdx);
1220
- // Later patches override earlier ones for the same cell
1221
- cellUpdates.set(key, { sheetId: sheetId, rowIdx: rowIdx, colIdx: colIdx, value: value });
1222
- }
1223
- else {
1224
- // Row-level or sheet-level updates
1225
- otherPatches.push(patch);
1226
- }
1227
- }
1228
- else if (op === "remove") {
1229
- if (path.length === 4) {
1230
- var _e = tslib_1.__read(path, 4), rowIndex = _e[1], columnIndex = _e[3];
1231
- var rowIdx = Number(rowIndex);
1232
- var colIdx = Number(columnIndex);
1233
- var key = (0, exports.createCellKeyV3)(sheetId, rowIdx, colIdx);
1234
- cellUpdates.set(key, { sheetId: sheetId, rowIdx: rowIdx, colIdx: colIdx, value: null });
1235
- }
1236
- else {
1237
- otherPatches.push(patch);
1238
- }
1239
- }
1240
- else {
1241
- otherPatches.push(patch);
1242
- }
1243
- }
1244
- }
1245
- catch (e_13_1) { e_13 = { error: e_13_1 }; }
1246
- finally {
1247
- try {
1248
- if (patches_15_1 && !patches_15_1.done && (_a = patches_15.return)) _a.call(patches_15);
1249
- }
1250
- finally { if (e_13) throw e_13.error; }
1251
- }
1252
- try {
1253
- // Second pass: Apply consolidated cell updates in batch
1254
- // This is much faster than processing each patch individually
1255
- for (var cellUpdates_2 = tslib_1.__values(cellUpdates), cellUpdates_2_1 = cellUpdates_2.next(); !cellUpdates_2_1.done; cellUpdates_2_1 = cellUpdates_2.next()) {
1256
- var _f = tslib_1.__read(cellUpdates_2_1.value, 2), key = _f[0], _g = _f[1], sheetId = _g.sheetId, rowIdx = _g.rowIdx, colIdx = _g.colIdx, value = _g.value;
1257
- if (value) {
1258
- ySheetDataV3.set(key, (0, exports.createCellDataV3)(value, sheetId, rowIdx, colIdx));
1259
- }
1260
- else {
1261
- ySheetDataV3.delete(key);
1262
- }
1263
- }
1264
- }
1265
- catch (e_14_1) { e_14 = { error: e_14_1 }; }
1266
- finally {
1267
- try {
1268
- if (cellUpdates_2_1 && !cellUpdates_2_1.done && (_b = cellUpdates_2.return)) _b.call(cellUpdates_2);
1269
- }
1270
- finally { if (e_14) throw e_14.error; }
1271
- }
1187
+ var e_13, _a;
1272
1188
  // Row-targeted patches (e.g. the per-shifted-row patches an immer array
1273
1189
  // splice emits for a row insert/delete) previously scanned the entire flat
1274
1190
  // cell map once per patch — O(patches × totalCells), which freezes the tab
@@ -1280,7 +1196,7 @@ var addSheetDataPatchesV3 = function (ySheetDataV3, patches) {
1280
1196
  return "".concat(sheetId, ":").concat(rowIdx);
1281
1197
  };
1282
1198
  var ensureRowCellKeyIndex = function () {
1283
- var e_15, _a;
1199
+ var e_14, _a;
1284
1200
  if (rowCellKeyIndex) {
1285
1201
  return rowCellKeyIndex;
1286
1202
  }
@@ -1297,12 +1213,12 @@ var addSheetDataPatchesV3 = function (ySheetDataV3, patches) {
1297
1213
  keys.add(key);
1298
1214
  }
1299
1215
  }
1300
- catch (e_15_1) { e_15 = { error: e_15_1 }; }
1216
+ catch (e_14_1) { e_14 = { error: e_14_1 }; }
1301
1217
  finally {
1302
1218
  try {
1303
1219
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1304
1220
  }
1305
- finally { if (e_15) throw e_15.error; }
1221
+ finally { if (e_14) throw e_14.error; }
1306
1222
  }
1307
1223
  return rowCellKeyIndex;
1308
1224
  };
@@ -1318,8 +1234,12 @@ var addSheetDataPatchesV3 = function (ySheetDataV3, patches) {
1318
1234
  }
1319
1235
  keys.add(key);
1320
1236
  };
1237
+ var untrackCellKey = function (sheetId, rowIdx, key) {
1238
+ var _a;
1239
+ (_a = rowCellKeyIndex === null || rowCellKeyIndex === void 0 ? void 0 : rowCellKeyIndex.get(rowIndexKey(sheetId, rowIdx))) === null || _a === void 0 ? void 0 : _a.delete(key);
1240
+ };
1321
1241
  var deleteRowCells = function (sheetId, rowIdx) {
1322
- var e_16, _a;
1242
+ var e_15, _a;
1323
1243
  var keys = ensureRowCellKeyIndex().get(rowIndexKey(sheetId, rowIdx));
1324
1244
  if (!keys) {
1325
1245
  return;
@@ -1330,17 +1250,17 @@ var addSheetDataPatchesV3 = function (ySheetDataV3, patches) {
1330
1250
  ySheetDataV3.delete(key);
1331
1251
  }
1332
1252
  }
1333
- catch (e_16_1) { e_16 = { error: e_16_1 }; }
1253
+ catch (e_15_1) { e_15 = { error: e_15_1 }; }
1334
1254
  finally {
1335
1255
  try {
1336
1256
  if (keys_1_1 && !keys_1_1.done && (_a = keys_1.return)) _a.call(keys_1);
1337
1257
  }
1338
- finally { if (e_16) throw e_16.error; }
1258
+ finally { if (e_15) throw e_15.error; }
1339
1259
  }
1340
1260
  rowCellKeyIndex.delete(rowIndexKey(sheetId, rowIdx));
1341
1261
  };
1342
1262
  var dropSheetFromIndex = function (sheetId) {
1343
- var e_17, _a;
1263
+ var e_16, _a;
1344
1264
  if (!rowCellKeyIndex) {
1345
1265
  return;
1346
1266
  }
@@ -1353,13 +1273,46 @@ var addSheetDataPatchesV3 = function (ySheetDataV3, patches) {
1353
1273
  }
1354
1274
  }
1355
1275
  }
1356
- catch (e_17_1) { e_17 = { error: e_17_1 }; }
1276
+ catch (e_16_1) { e_16 = { error: e_16_1 }; }
1357
1277
  finally {
1358
1278
  try {
1359
1279
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1360
1280
  }
1281
+ finally { if (e_16) throw e_16.error; }
1282
+ }
1283
+ };
1284
+ // Optimization: consecutive cell-level patches are consolidated (later
1285
+ // patches override earlier ones for the same cell) and applied together —
1286
+ // for bulk operations like paste (10k+ rows) this dramatically reduces
1287
+ // overhead. They are never reordered ACROSS a row-, sheet- or document-level
1288
+ // patch, though: a history entry that composes a row insert with writes into
1289
+ // the inserted row (Data → Subtotal) carries the insert's per-row replaces
1290
+ // and the writes' cell patches in that order, and a row replace applied
1291
+ // after the writes it precedes would delete them.
1292
+ var pendingCellUpdates = new Map();
1293
+ var flushPendingCellUpdates = function () {
1294
+ var e_17, _a;
1295
+ try {
1296
+ for (var pendingCellUpdates_2 = tslib_1.__values(pendingCellUpdates), pendingCellUpdates_2_1 = pendingCellUpdates_2.next(); !pendingCellUpdates_2_1.done; pendingCellUpdates_2_1 = pendingCellUpdates_2.next()) {
1297
+ var _b = tslib_1.__read(pendingCellUpdates_2_1.value, 2), key = _b[0], _c = _b[1], sheetId = _c.sheetId, rowIdx = _c.rowIdx, colIdx = _c.colIdx, value = _c.value;
1298
+ if (value) {
1299
+ ySheetDataV3.set(key, (0, exports.createCellDataV3)(value, sheetId, rowIdx, colIdx));
1300
+ trackCellKeySet(sheetId, rowIdx, key);
1301
+ }
1302
+ else {
1303
+ ySheetDataV3.delete(key);
1304
+ untrackCellKey(sheetId, rowIdx, key);
1305
+ }
1306
+ }
1307
+ }
1308
+ catch (e_17_1) { e_17 = { error: e_17_1 }; }
1309
+ finally {
1310
+ try {
1311
+ if (pendingCellUpdates_2_1 && !pendingCellUpdates_2_1.done && (_a = pendingCellUpdates_2.return)) _a.call(pendingCellUpdates_2);
1312
+ }
1361
1313
  finally { if (e_17) throw e_17.error; }
1362
1314
  }
1315
+ pendingCellUpdates.clear();
1363
1316
  };
1364
1317
  // Process non-cell-level patches using original logic
1365
1318
  var processPatch = function (_a) {
@@ -1551,8 +1504,39 @@ var addSheetDataPatchesV3 = function (ySheetDataV3, patches) {
1551
1504
  }
1552
1505
  }
1553
1506
  };
1554
- // Only process non-cell-level patches (cell-level already handled via consolidation)
1555
- otherPatches.forEach(processPatch);
1507
+ try {
1508
+ for (var patches_15 = tslib_1.__values(patches), patches_15_1 = patches_15.next(); !patches_15_1.done; patches_15_1 = patches_15.next()) {
1509
+ var patch = patches_15_1.value;
1510
+ var op = patch.op, path = patch.path, value = patch.value;
1511
+ var _b = tslib_1.__read(path, 1), sheetIdRaw = _b[0];
1512
+ if (typeof sheetIdRaw !== "undefined" &&
1513
+ path.length === 4 &&
1514
+ (op === "replace" || op === "add" || op === "remove")) {
1515
+ // [sheetId, rowIndex, 'values', columnIndex] - single cell update (most common in paste)
1516
+ var _c = tslib_1.__read(path, 4), rowIndex = _c[1], columnIndex = _c[3];
1517
+ var sheetId = Number(sheetIdRaw);
1518
+ var rowIdx = Number(rowIndex);
1519
+ var colIdx = Number(columnIndex);
1520
+ pendingCellUpdates.set((0, exports.createCellKeyV3)(sheetId, rowIdx, colIdx), {
1521
+ sheetId: sheetId,
1522
+ rowIdx: rowIdx,
1523
+ colIdx: colIdx,
1524
+ value: op === "remove" ? null : value,
1525
+ });
1526
+ continue;
1527
+ }
1528
+ flushPendingCellUpdates();
1529
+ processPatch(patch);
1530
+ }
1531
+ }
1532
+ catch (e_13_1) { e_13 = { error: e_13_1 }; }
1533
+ finally {
1534
+ try {
1535
+ if (patches_15_1 && !patches_15_1.done && (_a = patches_15.return)) _a.call(patches_15);
1536
+ }
1537
+ finally { if (e_13) throw e_13.error; }
1538
+ }
1539
+ flushPendingCellUpdates();
1556
1540
  };
1557
1541
  exports.addSheetDataPatchesV3 = addSheetDataPatchesV3;
1558
1542
  /**
@@ -1 +1 @@
1
- {"root":["../../helpers.ts","../../index.ts","../../observables.ts","../../types.ts","../../use-yspreadsheet-v2.ts","../../use-yspreadsheet.ts","../../vitest.config.ts","../../profiling/batch-profile.spec.ts"],"version":"5.8.3"}
1
+ {"root":["../../helpers.ts","../../index.ts","../../observables.ts","../../types.ts","../../use-yspreadsheet-v2.ts","../../use-yspreadsheet.ts","../../vitest.config.ts"],"version":"5.8.3"}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,KAAK,EAEX,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACtB,MAAM,iCAAiC,CAAC;AAWzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAOzB,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAEV,SAAS,EACT,UAAU,EACV,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC;QACjC,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;IACJ,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAC7B,KAAK,CAAC,CAAC,GAAG,EACV,QAAQ,MAAM,GAAG,MAAM,EACvB,SAAS,CACP,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAC9B,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CACtC,EAAE,EACH,SAAS,OAAO,GAAG,MAAM,GAAG,SAAS,EACrC,mBAAmB,IAAI,GAAG,IAAI,SAyG/B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,GAC9B,YAAY,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAC7B,SAAS,KAAK,EAAE,SAiMjB,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,YAAY,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAC7B,SAAS,MAAM,GAAG,MAAM,KACvB,UAUF,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,YAAY,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAC7B,SAAS,MAAM,GAAG,MAAM,KACvB,IAGF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,EAChC,UAAU,MAAM,GAAG,MAAM,KACxB,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CA+CzB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,SAAS,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,EACjC,aAAa,MAAM,GAAG,MAAM,EAC5B,WAAU,QAAQ,GAAG,IAAW,KAC/B,IAkBF,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,EAChC,UAAU,MAAM,GAAG,MAAM,KACxB,IAQF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GACpC,YAAY,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAC7B,SAAS,MAAM,GAAG,MAAM,EACxB,UAAU,MAAM,GAAG,MAAM,EACzB,aAAa,MAAM,GAAG,MAAM,KAC3B,QAAQ,GAAG,IAAI,GAAG,SAapB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,WAAW,QAAQ,GAAG,IAAI,KACzB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAExB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,KAAK,EAAE,KAAG,KAAK,EAQ9D,CAAC;AAqCF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,2BAA2B,GACtC,iBAAiB,QAAQ,GAAG,IAAI,GAAG,SAAS,EAC5C,kBAAkB,QAAQ,GAAG,IAAI,GAAG,SAAS,KAC5C,OA0BF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,oCAAoC,GAC/C,iBAAiB,QAAQ,GAAG,IAAI,GAAG,SAAS,EAC5C,kBAAkB,QAAQ,KACzB,QAcF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAC1B,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EACpB,SAAS,KAAK,EAAE,KACf,IA6GF,CAAC;AAsCF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,KAAK,EAAE,QAAQ,CAAC,EAAE,KAAG,CAAC,EAM9D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,GAAG,UAAU,EAC5C,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EACd,SAAS,KAAK,EAAE,SA4BjB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GACjC,QAAQ,cAAc,EAAE,EACxB,kBAAkB,SAAS,CAAC,GAAG,CAAC,EAChC,cAAc,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,KAC9B,SAAS,CAAC,GAAG,CAAC,GAAG,IA8KnB,CAAC;AAiBF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EACf,UAAU,MAAM,EAChB,aAAa,MAAM,KAClB,SAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,SAAS,KAAG,eAAe,GAAG,IAWrE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,QAAQ,EACjD,OAAO,CAAC,EACR,SAAS,MAAM,EACf,UAAU,MAAM,EAChB,aAAa,MAAM,KAClB,UAAU,CAAC,CAAC,CAKb,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,QAAQ,EAChD,QAAQ,SAAS,CAAC,CAAC,CAAC,KACnB,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAuB3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,QAAQ,EAChD,QAAQ,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,KACjC,SAAS,CAAC,CAAC,CAgCb,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,GACpB,cAAc,YAAY,EAC1B,SAAS,MAAM,EACf,UAAU,MAAM,EAChB,aAAa,MAAM,KAClB,UAAU,GAAG,SAGf,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,GACpB,cAAc,YAAY,EAC1B,SAAS,MAAM,EACf,UAAU,MAAM,EAChB,aAAa,MAAM,EACnB,OAAO,QAAQ,KACd,IAMF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,cAAc,YAAY,EAC1B,SAAS,MAAM,EACf,UAAU,MAAM,EAChB,aAAa,MAAM,KAClB,IAGF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GACxB,cAAc,YAAY,EAC1B,SAAS,MAAM,KACd,IAOF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,CAAC,CAAC,GAAG,KAAG,YAE3C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,cAAc,YAAY,EAC1B,SAAS,KAAK,EAAE,SAgUjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GAAI,CAAC,SAAS,QAAQ,EACzD,aAAa,GAAG,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;CAAE,CAAC,EACjE,kBAAkB,SAAS,CAAC,CAAC,CAAC,EAC9B,cAAc,YAAY,KACzB,SAAS,CAAC,CAAC,CAAC,GAAG,IA4EjB,CAAC"}
@@ -115,76 +115,39 @@ export const applyPatchesToYDoc = (doc, userId, patches, source, sheetDataVersio
115
115
  * 2. Grouping updates by sheet/row for efficient batch application
116
116
  */
117
117
  export const addSheetDataPatches = (ySheetData, patches) => {
118
- // Optimization: Pre-process and consolidate cell-level patches
119
- // For bulk operations like paste (10k+ rows), this dramatically reduces overhead
118
+ // Optimization: consecutive cell-level patches are consolidated (later
119
+ // patches override earlier ones for the same cell) and applied together,
120
+ // grouped by sheet and row so bulk operations like paste (10k+ rows) cost
121
+ // one row lookup per row. They are never reordered ACROSS a row- or
122
+ // sheet-level patch (see `addSheetDataPatchesV3`).
120
123
  // Key format: "sheetId:rowIndex:columnIndex"
121
- const cellUpdates = new Map();
122
- const otherPatches = [];
123
- // First pass: categorize and consolidate patches
124
- for (const patch of patches) {
125
- const { op, path, value } = patch;
126
- const [sheetIdRaw] = path;
127
- // Handle undefined sheetId separately (full replace)
128
- if (typeof sheetIdRaw === "undefined") {
129
- otherPatches.push(patch);
130
- continue;
131
- }
132
- // Use string representation for sheet ID to handle both string and numeric IDs
133
- const sheetIdStr = String(sheetIdRaw);
134
- if (op === "replace" || op === "add") {
135
- // [sheetId, rowIndex, 'values', columnIndex] - Single cell update (most common in paste)
136
- if (path.length === 4) {
137
- const [, rowIndex, , columnIndex] = path;
138
- const rowIdx = Number(rowIndex);
139
- const colIdx = Number(columnIndex);
140
- const key = `${sheetIdStr}:${rowIdx}:${colIdx}`;
141
- // Later patches override earlier ones for the same cell
142
- cellUpdates.set(key, { sheetIdStr, rowIdx, colIdx, value });
143
- }
144
- else {
145
- // Row-level or sheet-level updates
146
- otherPatches.push(patch);
147
- }
124
+ const pendingCellUpdates = new Map();
125
+ const flushPendingCellUpdates = () => {
126
+ if (pendingCellUpdates.size === 0) {
127
+ return;
148
128
  }
149
- else if (op === "remove") {
150
- if (path.length === 4) {
151
- const [, rowIndex, , columnIndex] = path;
152
- const rowIdx = Number(rowIndex);
153
- const colIdx = Number(columnIndex);
154
- const key = `${sheetIdStr}:${rowIdx}:${colIdx}`;
155
- cellUpdates.set(key, { sheetIdStr, rowIdx, colIdx, value: null });
129
+ const sheetRowUpdates = new Map();
130
+ for (const [, { sheetIdStr, rowIdx, colIdx, value }] of pendingCellUpdates) {
131
+ if (!sheetRowUpdates.has(sheetIdStr)) {
132
+ sheetRowUpdates.set(sheetIdStr, new Map());
156
133
  }
157
- else {
158
- otherPatches.push(patch);
134
+ const rowUpdates = sheetRowUpdates.get(sheetIdStr);
135
+ if (!rowUpdates.has(rowIdx)) {
136
+ rowUpdates.set(rowIdx, new Map());
159
137
  }
138
+ rowUpdates.get(rowIdx).set(colIdx, value);
160
139
  }
161
- else {
162
- otherPatches.push(patch);
163
- }
164
- }
165
- // Second pass: Group cell updates by sheet and row for efficient batch application
166
- // This minimizes Y.js operations by batching updates to the same row
167
- const sheetRowUpdates = new Map();
168
- for (const [, { sheetIdStr, rowIdx, colIdx, value }] of cellUpdates) {
169
- if (!sheetRowUpdates.has(sheetIdStr)) {
170
- sheetRowUpdates.set(sheetIdStr, new Map());
171
- }
172
- const rowUpdates = sheetRowUpdates.get(sheetIdStr);
173
- if (!rowUpdates.has(rowIdx)) {
174
- rowUpdates.set(rowIdx, new Map());
175
- }
176
- rowUpdates.get(rowIdx).set(colIdx, value);
177
- }
178
- // Apply batched cell updates
179
- for (const [sheetIdStr, rowUpdates] of sheetRowUpdates) {
180
- const ysheet = getSheetFromYSheetData(ySheetData, sheetIdStr);
181
- for (const [rowIdx, colUpdates] of rowUpdates) {
182
- const rowData = getRowDataFromSheet(ysheet, rowIdx);
183
- for (const [colIdx, value] of colUpdates) {
184
- setCellDataInRowData(rowData, colIdx, value);
140
+ for (const [sheetIdStr, rowUpdates] of sheetRowUpdates) {
141
+ const ysheet = getSheetFromYSheetData(ySheetData, sheetIdStr);
142
+ for (const [rowIdx, colUpdates] of rowUpdates) {
143
+ const rowData = getRowDataFromSheet(ysheet, rowIdx);
144
+ for (const [colIdx, value] of colUpdates) {
145
+ setCellDataInRowData(rowData, colIdx, value);
146
+ }
185
147
  }
186
148
  }
187
- }
149
+ pendingCellUpdates.clear();
150
+ };
188
151
  // Process non-cell-level patches using original logic
189
152
  const processPatch = ({ op, path, value }) => {
190
153
  let [sheetId] = path;
@@ -280,8 +243,30 @@ export const addSheetDataPatches = (ySheetData, patches) => {
280
243
  }
281
244
  }
282
245
  };
283
- // Only process non-cell-level patches (cell-level already handled via consolidation)
284
- otherPatches.forEach(processPatch);
246
+ for (const patch of patches) {
247
+ const { op, path, value } = patch;
248
+ const [sheetIdRaw] = path;
249
+ if (typeof sheetIdRaw !== "undefined" &&
250
+ path.length === 4 &&
251
+ (op === "replace" || op === "add" || op === "remove")) {
252
+ // [sheetId, rowIndex, 'values', columnIndex] - single cell update (most common in paste)
253
+ const [, rowIndex, , columnIndex] = path;
254
+ // Use string representation for sheet ID to handle both string and numeric IDs
255
+ const sheetIdStr = String(sheetIdRaw);
256
+ const rowIdx = Number(rowIndex);
257
+ const colIdx = Number(columnIndex);
258
+ pendingCellUpdates.set(`${sheetIdStr}:${rowIdx}:${colIdx}`, {
259
+ sheetIdStr,
260
+ rowIdx,
261
+ colIdx,
262
+ value: op === "remove" ? null : value,
263
+ });
264
+ continue;
265
+ }
266
+ flushPendingCellUpdates();
267
+ processPatch(patch);
268
+ }
269
+ flushPendingCellUpdates();
285
270
  };
286
271
  export const getSheetFromYSheetData = (ySheetData, sheetId) => {
287
272
  const id = String(sheetId);
@@ -553,7 +538,12 @@ export const updateArrayType = (yarray, patches) => {
553
538
  updatedItem = cloneDeep(baseItem);
554
539
  pending = { index, item: updatedItem };
555
540
  }
556
- set(updatedItem, nestedPath, value);
541
+ // Clone the value too: immer auto-freezes the state its patches point
542
+ // into, so a later patch of the same batch that writes INSIDE an object
543
+ // this one placed (`rowMetadata/4` = `{ outlineLevel: 1 }`, then
544
+ // `rowMetadata/4/outlineLevel` = 2 — a row grouped twice by one
545
+ // Subtotal) would otherwise hit a frozen object and be dropped.
546
+ set(updatedItem, nestedPath, cloneDeep(value));
557
547
  }
558
548
  }
559
549
  else if (op === "remove") {
@@ -1002,61 +992,6 @@ export const getSheetDataV3 = (doc) => {
1002
992
  * 2. Processing all cell updates in a single pass
1003
993
  */
1004
994
  export const addSheetDataPatchesV3 = (ySheetDataV3, patches) => {
1005
- // Optimization: Pre-process and consolidate cell-level patches
1006
- // For bulk operations like paste (10k+ rows), this dramatically reduces overhead
1007
- const cellUpdates = new Map();
1008
- const otherPatches = [];
1009
- // First pass: categorize and consolidate patches
1010
- for (const patch of patches) {
1011
- const { op, path, value } = patch;
1012
- const [sheetIdRaw] = path;
1013
- // Handle undefined sheetId separately (full replace)
1014
- if (typeof sheetIdRaw === "undefined") {
1015
- otherPatches.push(patch);
1016
- continue;
1017
- }
1018
- const sheetId = Number(sheetIdRaw);
1019
- if (op === "replace" || op === "add") {
1020
- // [sheetId, rowIndex, 'values', columnIndex] - Single cell update (most common in paste)
1021
- if (path.length === 4) {
1022
- const [, rowIndex, , columnIndex] = path;
1023
- const rowIdx = Number(rowIndex);
1024
- const colIdx = Number(columnIndex);
1025
- const key = createCellKeyV3(sheetId, rowIdx, colIdx);
1026
- // Later patches override earlier ones for the same cell
1027
- cellUpdates.set(key, { sheetId, rowIdx, colIdx, value });
1028
- }
1029
- else {
1030
- // Row-level or sheet-level updates
1031
- otherPatches.push(patch);
1032
- }
1033
- }
1034
- else if (op === "remove") {
1035
- if (path.length === 4) {
1036
- const [, rowIndex, , columnIndex] = path;
1037
- const rowIdx = Number(rowIndex);
1038
- const colIdx = Number(columnIndex);
1039
- const key = createCellKeyV3(sheetId, rowIdx, colIdx);
1040
- cellUpdates.set(key, { sheetId, rowIdx, colIdx, value: null });
1041
- }
1042
- else {
1043
- otherPatches.push(patch);
1044
- }
1045
- }
1046
- else {
1047
- otherPatches.push(patch);
1048
- }
1049
- }
1050
- // Second pass: Apply consolidated cell updates in batch
1051
- // This is much faster than processing each patch individually
1052
- for (const [key, { sheetId, rowIdx, colIdx, value }] of cellUpdates) {
1053
- if (value) {
1054
- ySheetDataV3.set(key, createCellDataV3(value, sheetId, rowIdx, colIdx));
1055
- }
1056
- else {
1057
- ySheetDataV3.delete(key);
1058
- }
1059
- }
1060
995
  // Row-targeted patches (e.g. the per-shifted-row patches an immer array
1061
996
  // splice emits for a row insert/delete) previously scanned the entire flat
1062
997
  // cell map once per patch — O(patches × totalCells), which freezes the tab
@@ -1093,6 +1028,9 @@ export const addSheetDataPatchesV3 = (ySheetDataV3, patches) => {
1093
1028
  }
1094
1029
  keys.add(key);
1095
1030
  };
1031
+ const untrackCellKey = (sheetId, rowIdx, key) => {
1032
+ rowCellKeyIndex?.get(rowIndexKey(sheetId, rowIdx))?.delete(key);
1033
+ };
1096
1034
  const deleteRowCells = (sheetId, rowIdx) => {
1097
1035
  const keys = ensureRowCellKeyIndex().get(rowIndexKey(sheetId, rowIdx));
1098
1036
  if (!keys) {
@@ -1114,6 +1052,28 @@ export const addSheetDataPatchesV3 = (ySheetDataV3, patches) => {
1114
1052
  }
1115
1053
  }
1116
1054
  };
1055
+ // Optimization: consecutive cell-level patches are consolidated (later
1056
+ // patches override earlier ones for the same cell) and applied together —
1057
+ // for bulk operations like paste (10k+ rows) this dramatically reduces
1058
+ // overhead. They are never reordered ACROSS a row-, sheet- or document-level
1059
+ // patch, though: a history entry that composes a row insert with writes into
1060
+ // the inserted row (Data → Subtotal) carries the insert's per-row replaces
1061
+ // and the writes' cell patches in that order, and a row replace applied
1062
+ // after the writes it precedes would delete them.
1063
+ const pendingCellUpdates = new Map();
1064
+ const flushPendingCellUpdates = () => {
1065
+ for (const [key, { sheetId, rowIdx, colIdx, value }] of pendingCellUpdates) {
1066
+ if (value) {
1067
+ ySheetDataV3.set(key, createCellDataV3(value, sheetId, rowIdx, colIdx));
1068
+ trackCellKeySet(sheetId, rowIdx, key);
1069
+ }
1070
+ else {
1071
+ ySheetDataV3.delete(key);
1072
+ untrackCellKey(sheetId, rowIdx, key);
1073
+ }
1074
+ }
1075
+ pendingCellUpdates.clear();
1076
+ };
1117
1077
  // Process non-cell-level patches using original logic
1118
1078
  const processPatch = ({ op, path, value }) => {
1119
1079
  const [sheetIdRaw] = path;
@@ -1264,8 +1224,29 @@ export const addSheetDataPatchesV3 = (ySheetDataV3, patches) => {
1264
1224
  }
1265
1225
  }
1266
1226
  };
1267
- // Only process non-cell-level patches (cell-level already handled via consolidation)
1268
- otherPatches.forEach(processPatch);
1227
+ for (const patch of patches) {
1228
+ const { op, path, value } = patch;
1229
+ const [sheetIdRaw] = path;
1230
+ if (typeof sheetIdRaw !== "undefined" &&
1231
+ path.length === 4 &&
1232
+ (op === "replace" || op === "add" || op === "remove")) {
1233
+ // [sheetId, rowIndex, 'values', columnIndex] - single cell update (most common in paste)
1234
+ const [, rowIndex, , columnIndex] = path;
1235
+ const sheetId = Number(sheetIdRaw);
1236
+ const rowIdx = Number(rowIndex);
1237
+ const colIdx = Number(columnIndex);
1238
+ pendingCellUpdates.set(createCellKeyV3(sheetId, rowIdx, colIdx), {
1239
+ sheetId,
1240
+ rowIdx,
1241
+ colIdx,
1242
+ value: op === "remove" ? null : value,
1243
+ });
1244
+ continue;
1245
+ }
1246
+ flushPendingCellUpdates();
1247
+ processPatch(patch);
1248
+ }
1249
+ flushPendingCellUpdates();
1269
1250
  };
1270
1251
  /**
1271
1252
  * Process V3 events and return updated sheet data
@@ -1 +1 @@
1
- {"root":["../../helpers.ts","../../index.ts","../../observables.ts","../../types.ts","../../use-yspreadsheet-v2.ts","../../use-yspreadsheet.ts","../../vitest.config.ts","../../profiling/batch-profile.spec.ts"],"version":"5.8.3"}
1
+ {"root":["../../helpers.ts","../../index.ts","../../observables.ts","../../types.ts","../../use-yspreadsheet-v2.ts","../../use-yspreadsheet.ts","../../vitest.config.ts"],"version":"5.8.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowsncolumns/y-spreadsheet",
3
- "version": "15.0.0",
3
+ "version": "15.0.2",
4
4
  "main": "./dist/cjs/index.js",
5
5
  "module": "./dist/esm/index.js",
6
6
  "exports": {
@@ -17,7 +17,9 @@
17
17
  "build:watch": "tsc -p ./tsconfig.esm.json --watch",
18
18
  "lint": "eslint . --ext .ts,.tsx",
19
19
  "test": "vitest --run",
20
- "types": "tsc -p . --noEmit"
20
+ "types": "tsc -p . --noEmit",
21
+ "types:specs": "node ../../scripts/typecheck-specs.mjs",
22
+ "types:specs:update": "node ../../scripts/typecheck-specs.mjs --update"
21
23
  },
22
24
  "files": [
23
25
  "dist"
@@ -32,16 +34,17 @@
32
34
  "sideEffects": false,
33
35
  "devDependencies": {
34
36
  "@hocuspocus/provider": "^3.1.1",
35
- "@rowsncolumns/common-types": ">=5.2.33",
36
- "@rowsncolumns/grid": ">=13.2.33",
37
- "@rowsncolumns/spreadsheet": ">=10.3.0",
38
- "@rowsncolumns/spreadsheet-state": ">=29.0.0",
39
- "@rowsncolumns/ui": ">=16.0.0",
40
- "@rowsncolumns/utils": ">=9.3.0",
37
+ "@rowsncolumns/common-types": ">=5.3.1",
38
+ "@rowsncolumns/fast-formula-parser": ">=6.2.41",
39
+ "@rowsncolumns/grid": ">=13.2.35",
40
+ "@rowsncolumns/spreadsheet": ">=10.4.1",
41
+ "@rowsncolumns/spreadsheet-state": ">=29.1.4",
42
+ "@rowsncolumns/ui": ">=16.0.3",
43
+ "@rowsncolumns/utils": ">=9.5.1",
44
+ "@types/lodash": "^4.17.17",
41
45
  "react": "^19.1.0",
42
46
  "react-dom": "^19.1.0",
43
47
  "y-websocket": "^3.0.0",
44
- "@types/lodash": "^4.17.17",
45
48
  "yjs": "^13.6.27"
46
49
  },
47
50
  "peerDependencies": {
@@ -57,7 +60,7 @@
57
60
  "yjs": ">=13.0.0"
58
61
  },
59
62
  "dependencies": {
60
- "rxjs": "^7.8.2",
61
- "lodash": "^4.17.21"
63
+ "lodash": "^4.17.21",
64
+ "rxjs": "^7.8.2"
62
65
  }
63
66
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=batch-profile.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"batch-profile.spec.d.ts","sourceRoot":"","sources":["../../../profiling/batch-profile.spec.ts"],"names":[],"mappings":""}
@@ -1,175 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- var tslib_1 = require("tslib");
4
- /* @vitest-environment jsdom */
5
- var react_1 = require("@testing-library/react");
6
- var vitest_1 = require("vitest");
7
- var Y = tslib_1.__importStar(require("yjs"));
8
- var use_yspreadsheet_v2_1 = require("../use-yspreadsheet-v2");
9
- vitest_1.vi.mock("@rowsncolumns/utils", function (importOriginal) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
10
- var actual;
11
- return tslib_1.__generator(this, function (_a) {
12
- switch (_a.label) {
13
- case 0: return [4 /*yield*/, importOriginal()];
14
- case 1:
15
- actual = _a.sent();
16
- return [2 /*return*/, tslib_1.__assign(tslib_1.__assign({}, actual), { scheduleAfterPaint: function (callback) {
17
- if (typeof requestAnimationFrame === "function") {
18
- var frameId_1 = requestAnimationFrame(function () { return callback(); });
19
- return function () {
20
- if (typeof cancelAnimationFrame === "function") {
21
- cancelAnimationFrame(frameId_1);
22
- }
23
- };
24
- }
25
- var timeoutId = setTimeout(callback, 0);
26
- return function () { return clearTimeout(timeoutId); };
27
- } })];
28
- }
29
- });
30
- }); });
31
- var parseEnvNumber = function (key, fallback) {
32
- var value = Number(process.env[key]);
33
- return Number.isFinite(value) ? value : fallback;
34
- };
35
- var testTimeoutMs = parseEnvNumber("PROFILE_TEST_TIMEOUT_MS", 30000);
36
- var now = function () {
37
- return typeof performance !== "undefined" && typeof performance.now === "function"
38
- ? performance.now()
39
- : Date.now();
40
- };
41
- var createSheetDataPatchBatch = function (sheetId, startRow, rows, columns) {
42
- var patches = [];
43
- var _loop_1 = function (rowIndex) {
44
- var absoluteRow = startRow + rowIndex;
45
- var values = Array.from({ length: columns }, function (_, columnIndex) { return ({
46
- value: "R".concat(absoluteRow, "C").concat(columnIndex),
47
- }); });
48
- patches.push([
49
- {
50
- sheetData: {
51
- patches: [
52
- {
53
- op: "add",
54
- path: [sheetId, absoluteRow],
55
- value: { values: values },
56
- },
57
- ],
58
- inversePatches: [],
59
- },
60
- },
61
- undefined,
62
- ]);
63
- };
64
- for (var rowIndex = 0; rowIndex < rows; rowIndex++) {
65
- _loop_1(rowIndex);
66
- }
67
- return patches;
68
- };
69
- var waitForCondition = function (condition_1, timeoutMs_1) {
70
- var args_1 = [];
71
- for (var _i = 2; _i < arguments.length; _i++) {
72
- args_1[_i - 2] = arguments[_i];
73
- }
74
- return tslib_1.__awaiter(void 0, tslib_1.__spreadArray([condition_1, timeoutMs_1], tslib_1.__read(args_1), false), void 0, function (condition, timeoutMs, intervalMs) {
75
- var start;
76
- if (intervalMs === void 0) { intervalMs = 25; }
77
- return tslib_1.__generator(this, function (_a) {
78
- switch (_a.label) {
79
- case 0:
80
- start = Date.now();
81
- _a.label = 1;
82
- case 1:
83
- if (!(Date.now() - start < timeoutMs)) return [3 /*break*/, 3];
84
- if (condition()) {
85
- return [2 /*return*/];
86
- }
87
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, intervalMs); })];
88
- case 2:
89
- _a.sent();
90
- return [3 /*break*/, 1];
91
- case 3: throw new Error("Profiling timed out while waiting for condition.");
92
- }
93
- });
94
- });
95
- };
96
- (0, vitest_1.describe)("profiling: staggered broadcast patches", function () {
97
- (0, vitest_1.it)("profiles large batch updates without data loss", function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
98
- var sheetId, rowsPerCall, calls, columns, timeoutMs, totalRows, totalPatches, doc, transactSpy, result, memStart, enqueueStart, _loop_2, i, enqueueMs, applyStart, applyMs, sheetData, lastRow, lastValues, lastCell, memEnd, report;
99
- var _a, _b;
100
- return tslib_1.__generator(this, function (_c) {
101
- switch (_c.label) {
102
- case 0:
103
- vitest_1.vi.useRealTimers();
104
- sheetId = parseEnvNumber("PROFILE_SHEET_ID", 1);
105
- rowsPerCall = parseEnvNumber("PROFILE_ROWS_PER_CALL", 150);
106
- calls = parseEnvNumber("PROFILE_CALLS", 10);
107
- columns = parseEnvNumber("PROFILE_COLUMNS", 20);
108
- timeoutMs = parseEnvNumber("PROFILE_TIMEOUT_MS", 20000);
109
- totalRows = rowsPerCall * calls;
110
- totalPatches = totalRows;
111
- doc = new Y.Doc();
112
- transactSpy = vitest_1.vi.spyOn(doc, "transact");
113
- result = (0, react_1.renderHook)(function () {
114
- return (0, use_yspreadsheet_v2_1.useYSpreadsheetV2)({
115
- userId: "user-1",
116
- title: "Profiler",
117
- sheetId: sheetId,
118
- doc: doc,
119
- calculateNow: vitest_1.vi.fn(),
120
- initialSheets: [],
121
- onChangeSheetData: vitest_1.vi.fn(),
122
- onChangeSheets: vitest_1.vi.fn(),
123
- onChangeTables: vitest_1.vi.fn(),
124
- enqueueCalculation: vitest_1.vi.fn(),
125
- enqueueGraphOperation: vitest_1.vi.fn(),
126
- });
127
- }).result;
128
- memStart = (_a = process === null || process === void 0 ? void 0 : process.memoryUsage) === null || _a === void 0 ? void 0 : _a.call(process).heapUsed;
129
- enqueueStart = now();
130
- _loop_2 = function (i) {
131
- var batch = createSheetDataPatchBatch(sheetId, i * rowsPerCall, rowsPerCall, columns);
132
- (0, react_1.act)(function () {
133
- result.current.onBroadcastPatch(batch);
134
- });
135
- };
136
- for (i = 0; i < calls; i++) {
137
- _loop_2(i);
138
- }
139
- enqueueMs = now() - enqueueStart;
140
- applyStart = now();
141
- return [4 /*yield*/, waitForCondition(function () {
142
- var sheetData = doc.getMap("sheetDataV2").get(String(sheetId));
143
- return (sheetData === null || sheetData === void 0 ? void 0 : sheetData.length) === totalRows;
144
- }, timeoutMs)];
145
- case 1:
146
- _c.sent();
147
- applyMs = now() - applyStart;
148
- sheetData = doc.getMap("sheetDataV2").get(String(sheetId));
149
- lastRow = sheetData === null || sheetData === void 0 ? void 0 : sheetData.get(totalRows - 1);
150
- lastValues = lastRow === null || lastRow === void 0 ? void 0 : lastRow.get("values");
151
- lastCell = lastValues === null || lastValues === void 0 ? void 0 : lastValues.get(columns - 1);
152
- if (!lastCell || lastCell.value !== "R".concat(totalRows - 1, "C").concat(columns - 1)) {
153
- throw new Error("Data loss detected: last cell value mismatch.");
154
- }
155
- memEnd = (_b = process === null || process === void 0 ? void 0 : process.memoryUsage) === null || _b === void 0 ? void 0 : _b.call(process).heapUsed;
156
- report = {
157
- sheetId: sheetId,
158
- calls: calls,
159
- rowsPerCall: rowsPerCall,
160
- columns: columns,
161
- totalRows: totalRows,
162
- totalPatches: totalPatches,
163
- enqueueMs: Math.round(enqueueMs),
164
- applyMs: Math.round(applyMs),
165
- transactBatches: transactSpy.mock.calls.length,
166
- heapDeltaMB: typeof memStart === "number" && typeof memEnd === "number"
167
- ? Math.round(((memEnd - memStart) / 1024 / 1024) * 100) / 100
168
- : "n/a",
169
- };
170
- console.table(report);
171
- return [2 /*return*/];
172
- }
173
- });
174
- }); }, testTimeoutMs);
175
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=batch-profile.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"batch-profile.spec.d.ts","sourceRoot":"","sources":["../../../profiling/batch-profile.spec.ts"],"names":[],"mappings":""}
@@ -1,131 +0,0 @@
1
- /* @vitest-environment jsdom */
2
- import { act, renderHook } from "@testing-library/react";
3
- import { describe, it, vi } from "vitest";
4
- import * as Y from "yjs";
5
- import { useYSpreadsheetV2 } from "../use-yspreadsheet-v2";
6
- vi.mock("@rowsncolumns/utils", async (importOriginal) => {
7
- const actual = await importOriginal();
8
- return {
9
- ...actual,
10
- scheduleAfterPaint: (callback) => {
11
- if (typeof requestAnimationFrame === "function") {
12
- const frameId = requestAnimationFrame(() => callback());
13
- return () => {
14
- if (typeof cancelAnimationFrame === "function") {
15
- cancelAnimationFrame(frameId);
16
- }
17
- };
18
- }
19
- const timeoutId = setTimeout(callback, 0);
20
- return () => clearTimeout(timeoutId);
21
- },
22
- };
23
- });
24
- const parseEnvNumber = (key, fallback) => {
25
- const value = Number(process.env[key]);
26
- return Number.isFinite(value) ? value : fallback;
27
- };
28
- const testTimeoutMs = parseEnvNumber("PROFILE_TEST_TIMEOUT_MS", 30000);
29
- const now = () => typeof performance !== "undefined" && typeof performance.now === "function"
30
- ? performance.now()
31
- : Date.now();
32
- const createSheetDataPatchBatch = (sheetId, startRow, rows, columns) => {
33
- const patches = [];
34
- for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
35
- const absoluteRow = startRow + rowIndex;
36
- const values = Array.from({ length: columns }, (_, columnIndex) => ({
37
- value: `R${absoluteRow}C${columnIndex}`,
38
- }));
39
- patches.push([
40
- {
41
- sheetData: {
42
- patches: [
43
- {
44
- op: "add",
45
- path: [sheetId, absoluteRow],
46
- value: { values },
47
- },
48
- ],
49
- inversePatches: [],
50
- },
51
- },
52
- undefined,
53
- ]);
54
- }
55
- return patches;
56
- };
57
- const waitForCondition = async (condition, timeoutMs, intervalMs = 25) => {
58
- const start = Date.now();
59
- while (Date.now() - start < timeoutMs) {
60
- if (condition()) {
61
- return;
62
- }
63
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
64
- }
65
- throw new Error("Profiling timed out while waiting for condition.");
66
- };
67
- describe("profiling: staggered broadcast patches", () => {
68
- it("profiles large batch updates without data loss", async () => {
69
- vi.useRealTimers();
70
- const sheetId = parseEnvNumber("PROFILE_SHEET_ID", 1);
71
- const rowsPerCall = parseEnvNumber("PROFILE_ROWS_PER_CALL", 150);
72
- const calls = parseEnvNumber("PROFILE_CALLS", 10);
73
- const columns = parseEnvNumber("PROFILE_COLUMNS", 20);
74
- const timeoutMs = parseEnvNumber("PROFILE_TIMEOUT_MS", 20000);
75
- const totalRows = rowsPerCall * calls;
76
- const totalPatches = totalRows;
77
- const doc = new Y.Doc();
78
- const transactSpy = vi.spyOn(doc, "transact");
79
- const { result } = renderHook(() => useYSpreadsheetV2({
80
- userId: "user-1",
81
- title: "Profiler",
82
- sheetId,
83
- doc,
84
- calculateNow: vi.fn(),
85
- initialSheets: [],
86
- onChangeSheetData: vi.fn(),
87
- onChangeSheets: vi.fn(),
88
- onChangeTables: vi.fn(),
89
- enqueueCalculation: vi.fn(),
90
- enqueueGraphOperation: vi.fn(),
91
- }));
92
- const memStart = process?.memoryUsage?.().heapUsed;
93
- const enqueueStart = now();
94
- for (let i = 0; i < calls; i++) {
95
- const batch = createSheetDataPatchBatch(sheetId, i * rowsPerCall, rowsPerCall, columns);
96
- act(() => {
97
- result.current.onBroadcastPatch(batch);
98
- });
99
- }
100
- const enqueueMs = now() - enqueueStart;
101
- const applyStart = now();
102
- await waitForCondition(() => {
103
- const sheetData = doc.getMap("sheetDataV2").get(String(sheetId));
104
- return sheetData?.length === totalRows;
105
- }, timeoutMs);
106
- const applyMs = now() - applyStart;
107
- const sheetData = doc.getMap("sheetDataV2").get(String(sheetId));
108
- const lastRow = sheetData?.get(totalRows - 1);
109
- const lastValues = lastRow?.get("values");
110
- const lastCell = lastValues?.get(columns - 1);
111
- if (!lastCell || lastCell.value !== `R${totalRows - 1}C${columns - 1}`) {
112
- throw new Error("Data loss detected: last cell value mismatch.");
113
- }
114
- const memEnd = process?.memoryUsage?.().heapUsed;
115
- const report = {
116
- sheetId,
117
- calls,
118
- rowsPerCall,
119
- columns,
120
- totalRows,
121
- totalPatches,
122
- enqueueMs: Math.round(enqueueMs),
123
- applyMs: Math.round(applyMs),
124
- transactBatches: transactSpy.mock.calls.length,
125
- heapDeltaMB: typeof memStart === "number" && typeof memEnd === "number"
126
- ? Math.round(((memEnd - memStart) / 1024 / 1024) * 100) / 100
127
- : "n/a",
128
- };
129
- console.table(report);
130
- }, testTimeoutMs);
131
- });