@hawkeyexl/manni 2.8.1 → 2.8.3

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.
package/README.md CHANGED
@@ -98,7 +98,7 @@ actually reads rather than hand-written:
98
98
  ```yaml
99
99
  repos:
100
100
  - repo: https://github.com/hawkeyexl/manni
101
- rev: v2.8.1
101
+ rev: v2.8.3
102
102
  hooks:
103
103
  - id: manni-meta
104
104
  ```
@@ -35,6 +35,7 @@ import {
35
35
  hasFrontmatterFence,
36
36
  hashLines,
37
37
  hashRange,
38
+ heldManifest,
38
39
  isEncryptedValue,
39
40
  isErrorSeverity,
40
41
  isIgnoredByGit,
@@ -55,11 +56,14 @@ import {
55
56
  pinOfLines,
56
57
  readConfigFile,
57
58
  readFamilyConfigFile,
59
+ readManifestValue,
60
+ rebaseable,
58
61
  removeManifestKey,
59
62
  resolveBaselineRequest,
60
63
  resolveEncryptionKey,
61
64
  resolveTargetSet,
62
65
  selectCollections,
66
+ settle,
63
67
  settleBaseline,
64
68
  sliceLines,
65
69
  spellLines,
@@ -71,7 +75,7 @@ import {
71
75
  withValueAt,
72
76
  writeEncryptionKey,
73
77
  writeFileAtomic
74
- } from "./chunk-RLHN765L.js";
78
+ } from "./chunk-KGI7B6DD.js";
75
79
 
76
80
  // src/meta/core/reencrypt.ts
77
81
  import { extname } from "node:path";
@@ -151,15 +155,15 @@ function reencryptWith(extractor, page, opts) {
151
155
  }
152
156
  };
153
157
  const data = read(page.content);
154
- const apply2 = extractor.apply;
155
- const plan = reencryptData(data, { ...opts, writable: apply2 !== void 0 });
158
+ const apply3 = extractor.apply;
159
+ const plan = reencryptData(data, { ...opts, writable: apply3 !== void 0 });
156
160
  const { rewritten, skipped } = plan;
157
- if (rewritten.length === 0 || apply2 === void 0) {
161
+ if (rewritten.length === 0 || apply3 === void 0) {
158
162
  return { content: page.content, rewritten: [], skipped };
159
163
  }
160
164
  const patch = {};
161
165
  for (const top of plan.keys) patch[top] = plan.data[top];
162
- const content = apply2(page.content, patch, { filePath: page.file });
166
+ const content = apply3(page.content, patch, { filePath: page.file });
163
167
  const reread = tokensIn(read(content));
164
168
  for (const r of rewritten) {
165
169
  if (!reread.some((f) => f.pointer === r.pointer && f.token === r.to)) {
@@ -2662,6 +2666,7 @@ function unifiedDiff(label, before, after) {
2662
2666
  }
2663
2667
 
2664
2668
  // src/cite/core/manifest.ts
2669
+ var codec = { apply, read: readManifestValue };
2665
2670
  var ManifestSet = class {
2666
2671
  held = /* @__PURE__ */ new Map();
2667
2672
  /** Read a manifest, or hand back the copy this run has already rewritten. */
@@ -2675,7 +2680,7 @@ var ManifestSet = class {
2675
2680
  const reason = error instanceof Error ? error.message : String(error);
2676
2681
  throw new CiteError(`Manifest ${manifest.file} could not be read: ${reason}`);
2677
2682
  }
2678
- const fresh = { path: manifest.path, file: manifest.file, before, text: before };
2683
+ const fresh = heldManifest(manifest.path, manifest.file, before);
2679
2684
  this.held.set(manifest.path, fresh);
2680
2685
  return fresh;
2681
2686
  }
@@ -2686,16 +2691,37 @@ var ManifestSet = class {
2686
2691
  */
2687
2692
  async write(manifest, entry, citations, index) {
2688
2693
  const held = await this.hold(manifest);
2694
+ const op = {
2695
+ kind: "splice",
2696
+ entry,
2697
+ key: CITATIONS_KEY2,
2698
+ join: manifest.join,
2699
+ file: manifest.file,
2700
+ value: [...citations]
2701
+ };
2702
+ rebaseable(held, op, codec);
2689
2703
  const spliced = splice(held.text, {
2690
2704
  entry,
2691
2705
  key: CITATIONS_KEY2,
2692
- value: [...citations],
2706
+ value: op.value,
2693
2707
  join: manifest.join,
2694
2708
  file: manifest.file
2695
2709
  });
2696
2710
  held.text = spliced.text;
2711
+ held.ops.push(op);
2697
2712
  return { file: manifest.file, line: itemLine(spliced.text, entry, index, manifest.join) ?? spliced.line };
2698
2713
  }
2714
+ /**
2715
+ * Where one item of an entry's `citations` sits in the manifest as this run
2716
+ * will write it. `add` names a line in its report, and `commit` may have
2717
+ * replayed the entry onto bytes that moved it, so the line is read back from
2718
+ * the settled text rather than kept from the splice.
2719
+ */
2720
+ lineOf(manifest, entry, index) {
2721
+ const held = this.held.get(manifest.path);
2722
+ if (held === void 0) return void 0;
2723
+ return itemLine(held.text, entry, index, manifest.join);
2724
+ }
2699
2725
  /**
2700
2726
  * Take a page's `citations` key out of the manifest, for the removal that
2701
2727
  * leaves it with no entries. The entry itself goes when that key was all it
@@ -2703,23 +2729,66 @@ var ManifestSet = class {
2703
2729
  */
2704
2730
  async remove(manifest, entry) {
2705
2731
  const held = await this.hold(manifest);
2706
- try {
2707
- held.text = removeManifestKey(held.text, {
2708
- entry,
2709
- key: CITATIONS_KEY2,
2710
- join: manifest.join,
2711
- file: manifest.file
2712
- }).text;
2713
- } catch (error) {
2714
- if (error instanceof DocmetaError) throw new CiteError(error.message);
2715
- throw error;
2716
- }
2732
+ const op = {
2733
+ kind: "remove",
2734
+ entry,
2735
+ key: CITATIONS_KEY2,
2736
+ join: manifest.join,
2737
+ file: manifest.file
2738
+ };
2739
+ rebaseable(held, op, codec);
2740
+ held.text = apply(held.text, op);
2741
+ held.ops.push(op);
2717
2742
  }
2718
2743
  /** The manifests this run rewrote, with the diff of each. */
2719
2744
  changed() {
2720
2745
  return [...this.held.values()].filter((m) => m.text !== m.before).map((m) => ({ ...m, diff: unifiedDiff(m.file, m.before, m.text) }));
2721
2746
  }
2747
+ /**
2748
+ * Write every manifest this run rewrote, and report what landed.
2749
+ *
2750
+ * The write is the last thing a run does, so this is the one place that
2751
+ * knows a manifest is about to be replaced by bytes derived from a read
2752
+ * that may be seconds old.
2753
+ */
2754
+ async commit(io = {}) {
2755
+ const write = io.write ?? writeFileAtomic;
2756
+ const read = io.read ?? ((at2) => readFile2(at2, "utf8"));
2757
+ const landed = [];
2758
+ for (const held of this.held.values()) {
2759
+ if (held.text === held.before) continue;
2760
+ try {
2761
+ await settle(held, { codec, write, read, toError: (message) => new CiteError(message) });
2762
+ } catch (error) {
2763
+ if (io.onError !== void 0) await io.onError(changeOf(held), error);
2764
+ throw error;
2765
+ }
2766
+ const change = changeOf(held);
2767
+ landed.push(change);
2768
+ io.after?.(change);
2769
+ }
2770
+ return landed;
2771
+ }
2722
2772
  };
2773
+ function changeOf(held) {
2774
+ return {
2775
+ path: held.path,
2776
+ file: held.file,
2777
+ before: held.before,
2778
+ text: held.text,
2779
+ diff: unifiedDiff(held.file, held.before, held.text)
2780
+ };
2781
+ }
2782
+ function apply(text, op) {
2783
+ const where = { entry: op.entry, key: op.key, join: op.join, file: op.file };
2784
+ if (op.kind === "splice") return splice(text, { ...where, value: op.value }).text;
2785
+ try {
2786
+ return removeManifestKey(text, where).text;
2787
+ } catch (error) {
2788
+ if (error instanceof DocmetaError) throw new CiteError(error.message);
2789
+ throw error;
2790
+ }
2791
+ }
2723
2792
  function splice(text, options) {
2724
2793
  try {
2725
2794
  return spliceManifestValue(text, options);
@@ -3846,8 +3915,8 @@ async function runAdd(opts) {
3846
3915
  if (unit === void 0 || pin === void 0) {
3847
3916
  throw new CiteError(`${at2} has no paragraph or block for a marker to anchor.`);
3848
3917
  }
3849
- for (const { index, lines: moved } of shifted.frontmatter) {
3850
- body = spliceEntryField(body, format, index, ["claim", "lines"], moved);
3918
+ for (const { index: index2, lines: moved } of shifted.frontmatter) {
3919
+ body = spliceEntryField(body, format, index2, ["claim", "lines"], moved);
3851
3920
  }
3852
3921
  claimSpan = unit;
3853
3922
  claim = { integrity: pin };
@@ -3903,18 +3972,20 @@ async function runAdd(opts) {
3903
3972
  }
3904
3973
  const manifests = new ManifestSet();
3905
3974
  let placed;
3975
+ let index = 0;
3906
3976
  if (owner !== void 0) {
3907
3977
  if (sidecar?.entry === void 0) {
3908
3978
  throw new CiteError(
3909
3979
  `${label} carries no ${owner.join}: value, so its citations cannot be keyed in ${owner.file}.`
3910
3980
  );
3911
3981
  }
3912
- const existing = (sidecar.citations ?? []).map((input, index) => {
3913
- const moved = shifted.manifest.get(index);
3982
+ const existing = (sidecar.citations ?? []).map((input, index2) => {
3983
+ const moved = shifted.manifest.get(index2);
3914
3984
  return moved === void 0 ? input.entry : withClaimLines(input.entry, moved);
3915
3985
  });
3916
3986
  const list = [...existing, entryObject(citation)];
3917
- const at3 = await manifests.write(owner, sidecar.entry, list, list.length - 1);
3987
+ index = list.length - 1;
3988
+ const at3 = await manifests.write(owner, sidecar.entry, list, index);
3918
3989
  const [changed] = manifests.changed();
3919
3990
  placed = {
3920
3991
  file: at3.file,
@@ -3943,7 +4014,14 @@ async function runAdd(opts) {
3943
4014
  result.written = path !== void 0 && opts.dryRun !== true && after !== content;
3944
4015
  if (result.written && path !== void 0) await writeFileAtomic(path, after);
3945
4016
  if (result.manifest !== void 0 && opts.dryRun !== true) {
3946
- for (const changed of manifests.changed()) await writeFileAtomic(changed.path, changed.text);
4017
+ const [settled] = await manifests.commit();
4018
+ if (owner !== void 0 && sidecar?.entry !== void 0) {
4019
+ result.manifest.line = manifests.lineOf(owner, sidecar.entry, index) ?? result.manifest.line;
4020
+ }
4021
+ if (settled !== void 0) {
4022
+ result.manifest.content = settled.text;
4023
+ result.manifest.diff = settled.diff;
4024
+ }
3947
4025
  result.manifest.written = true;
3948
4026
  }
3949
4027
  if (elsewhere.length > 0) {
@@ -4114,10 +4192,11 @@ async function runRemove(opts) {
4114
4192
  item.page.written = write;
4115
4193
  }
4116
4194
  const rewritten = [];
4117
- for (const changed of manifests.changed()) {
4118
- if (write) await writeFileAtomic(changed.path, changed.text);
4195
+ const record = (changed) => {
4119
4196
  rewritten.push({ file: changed.file, diff: changed.diff, written: write });
4120
- }
4197
+ };
4198
+ if (write) await manifests.commit({ after: record });
4199
+ else for (const changed of manifests.changed()) record(changed);
4121
4200
  const pages = planned.map((item) => item.page);
4122
4201
  return {
4123
4202
  pages,
@@ -4171,7 +4250,7 @@ function settles(plan) {
4171
4250
  return "";
4172
4251
  }
4173
4252
  }
4174
- function apply(content, format, plan) {
4253
+ function apply2(content, format, plan) {
4175
4254
  const index = plan.result.origin.index;
4176
4255
  switch (plan.kind) {
4177
4256
  case "claim-moved":
@@ -4825,7 +4904,7 @@ async function runUpdate(opts) {
4825
4904
  if (!applyToEntry(held, plan)) continue;
4826
4905
  manifestDirty = true;
4827
4906
  } else {
4828
- after = apply(after, format, plan);
4907
+ after = apply2(after, format, plan);
4829
4908
  }
4830
4909
  rewritten2.push(rewriteOf(plan));
4831
4910
  settled.add(`${String(result.origin.index)}\0${settles(plan)}`);
@@ -4879,20 +4958,25 @@ async function runUpdate(opts) {
4879
4958
  reports.push(await updateOne(file, await readTarget(run3, file), resolve6(run3.base, file)));
4880
4959
  }
4881
4960
  assertNoOrphanJoins(prepared, hits);
4882
- const changedManifests = manifests.changed();
4961
+ const write = opts.dryRun !== true;
4883
4962
  const rewrittenManifests = [];
4884
- for (const changed of changedManifests) {
4885
- const write = opts.dryRun !== true;
4886
- if (write) {
4887
- try {
4888
- await writeFileAtomic(changed.path, changed.text);
4889
- } catch (error) {
4963
+ const record = (changed) => {
4964
+ rewrittenManifests.push({ file: changed.file, diff: changed.diff, written: write });
4965
+ };
4966
+ if (write) {
4967
+ await manifests.commit({
4968
+ // A manifest joins the run's written files the moment it lands, so a
4969
+ // later manifest's failure puts this one back with the pages. Without
4970
+ // it the tree would keep pins no page content matches.
4971
+ after: (changed) => {
4972
+ writes.record(changed.path, changed.file, changed.before);
4973
+ record(changed);
4974
+ },
4975
+ onError: async (changed, error) => {
4890
4976
  throw await writes.restore(changed.file, error);
4891
4977
  }
4892
- writes.record(changed.path, changed.file, changed.before);
4893
- }
4894
- rewrittenManifests.push({ file: changed.file, diff: changed.diff, written: write });
4895
- }
4978
+ });
4979
+ } else for (const changed of manifests.changed()) record(changed);
4896
4980
  const reminted = pages.some(
4897
4981
  (page) => page.rewritten.some((r) => r.reason === "accepted" && r.end === "source")
4898
4982
  );
@@ -5973,4 +6057,4 @@ export {
5973
6057
  runKeyRotate,
5974
6058
  runKeySet
5975
6059
  };
5976
- //# sourceMappingURL=chunk-FWB6LGBB.js.map
6060
+ //# sourceMappingURL=chunk-DGC4CCK3.js.map