@helping-ai-workflow/md2doc 3.4.2 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,7 +41,6 @@
41
41
  * about it — rewrite the block, cancel, edit by hand — belongs to the
42
42
  * layer that can ask them, and re-serialising the block behind their back
43
43
  * is the one answer this file may never give.
44
- " PLACEHOLDER
45
44
  *
46
45
  * A throw inside an `apply` callback is NOT caught. The store's own state is
47
46
  * untouched when one happens (nothing is pushed until the new document is in
@@ -63,6 +62,17 @@ const C = require('./wave-codec.js');
63
62
  */
64
63
  const MAX_DEPTH = C.MAX_DEPTH;
65
64
 
65
+ /**
66
+ * The undo stack's cap, in entries.
67
+ *
68
+ * Every entry holds a whole frozen document, so an unbounded stack grows with
69
+ * the length of the editing session rather than with anything about the
70
+ * document. 200 is a ruling, not a measurement of any observed cost — there is
71
+ * no profiling behind the number, just a decision that a session's undo depth
72
+ * stops mattering well before 200 steps back.
73
+ */
74
+ const HISTORY_LIMIT = 200;
75
+
66
76
  /**
67
77
  * Open a store over one wavedrom block's source text.
68
78
  *
@@ -141,6 +151,23 @@ function createStore(sourceText) {
141
151
  origins: origins,
142
152
  });
143
153
  at++;
154
+ // Cap the stack, not just its growth: apply always leaves `at` on the
155
+ // entry it just pushed (the truncation above already dropped any redo
156
+ // tail), so history.length is always at + 1 here. Dropping the oldest
157
+ // entry when the cap is exceeded means `at` has to move down with it, or
158
+ // every canUndo/undoName/redo answer after the drop would be off by one —
159
+ // pointing one entry short of the one actually current.
160
+ // `while`, not `if`: apply today only ever pushes one entry per call, so
161
+ // the stack is never more than one over the cap and `if` is provably
162
+ // enough — but that "one entry per call" fact lives in this function, not
163
+ // in the invariant. A future batch-push (or a cap lowered at runtime)
164
+ // would leave history silently one-or-more over HISTORY_LIMIT forever,
165
+ // since nothing after this point ever re-checks. `while` costs nothing
166
+ // today and stops correctness depending on a fact this loop cannot see.
167
+ while (history.length > HISTORY_LIMIT) {
168
+ history.shift();
169
+ at--;
170
+ }
144
171
  return true;
145
172
  }
146
173
 
@@ -882,9 +909,44 @@ function diffMembers(ctx, path, was, now, skipKey, edits) {
882
909
  const r = diffValue(ctx, path.concat([key]), was[key], now[key], edits);
883
910
  if (r.ok !== true) return r;
884
911
  }
912
+ // v3.6.0 Task 13 fix round 1 (Critical 1): every new key on THIS object
913
+ // goes into ONE edit, not one `insert` per key.
914
+ //
915
+ // `toPatch()` (this file, `store.toPatch`) always diffs the WHOLE session
916
+ // against the document as it stood when the editor opened — not
917
+ // incrementally, gesture by gesture — so two new sibling fields set at any
918
+ // two points in the same session (the Lane panel's period then phase; two
919
+ // of the six ruler fields) both show up as "new" here in the SAME call.
920
+ // The old code pushed one independent `{op: 'insert', path: […key]}` per
921
+ // key, and `wave-codec.js`'s resolver turns EVERY one of those into a
922
+ // patch anchored at the object's same last EXISTING member — because
923
+ // neither edit can see the other, both compute the identical insertion
924
+ // point, and `patchSource`'s "two edits land at the same place" guard
925
+ // (which exists for exactly this ambiguity) refused the batch. Every
926
+ // session that set two new fields on one object failed, always, not
927
+ // intermittently.
928
+ //
929
+ // Folding them into one `insert-members` edit — resolved by
930
+ // `wave-codec.js`'s `resolveInsertMembers` — gives the object ONE new
931
+ // anchor for however many new keys it picked up this session. Key order
932
+ // inside it is `Object.keys(now)` order (insertion order for a plain
933
+ // object, so it matches the order the fields were actually set in); it
934
+ // does not have to be THE order, because `toPatch()`'s own read-back
935
+ // check just below in this file verifies the combined edit reproduces the
936
+ // exact document regardless of key order (`sameValue` does not compare
937
+ // key order either).
938
+ const newKeys = [];
885
939
  for (const key of Object.keys(now)) {
886
940
  if (key === skipKey) continue;
887
- if (!has(was, key)) edits.push({ op: 'insert', path: path.concat([key]), value: now[key] });
941
+ if (!has(was, key)) newKeys.push(key);
942
+ }
943
+ if (newKeys.length === 1) {
944
+ edits.push({ op: 'insert', path: path.concat([newKeys[0]]), value: now[newKeys[0]] });
945
+ } else if (newKeys.length > 1) {
946
+ edits.push({
947
+ op: 'insert-members', path: path,
948
+ entries: newKeys.map(function (key) { return [key, now[key]]; }),
949
+ });
888
950
  }
889
951
  return OK;
890
952
  }