strom-research 1.13.2-beta.1 → 1.13.2-beta.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.
package/dist/core/sync.js CHANGED
@@ -668,6 +668,8 @@ const APP_COUPLE_EVENTS = {
668
668
  };
669
669
  /** Facts whose value is what they are (the GEDCOM tag's value). */
670
670
  const VALUE_KINDS = new Set(["OCCU", "RELI", "TITL", "NATI"]);
671
+ /** A fact that is its value (an occupation, a religion) without one: no fact — its value taken off in the app (B-1). */
672
+ const bare = (f) => VALUE_KINDS.has(kindOf(f.kind)) && !f.value?.trim();
671
673
  /** A tree of the Strom app (its JSON) as a snapshot. */
672
674
  export function readStromJson(data) {
673
675
  const d = data;
@@ -1918,8 +1920,20 @@ export function planSync(tree, incoming, edits, opts = {}) {
1918
1920
  }
1919
1921
  const less = our.find((o) => knowsMore(o, f));
1920
1922
  if (less) {
1921
- // the file knows less of it (the date lost on the way)
1923
+ // the file knows less of it (the date lost on the way) — or, against what the app was given, the user took a
1924
+ // part of it off, the fact itself kept (B-1, found with the app's 3.11.0-beta.2: a proven death's date taken off,
1925
+ // "not written 1", no conflict): its date, place or value as given, the research's still. A record's fact: the
1926
+ // user's to decide (empty where nothing is left); a lead corrected. Without a base nothing is taken off (an
1927
+ // older copy, another program: what it lacks it may never have had)
1922
1928
  partsDiff(f, less);
1929
+ const given = less.id && !used.has(factKey(less)) ? was?.find((b) => factKey(b) === factKey(less) && (!b.id || b.id === less.id)) : undefined;
1930
+ const off = given ? ["date", "place", "value"].filter((k) => !!exact(given[k]) && !exact(f[k])) : [];
1931
+ if (off.length) {
1932
+ used.add(factKey(less));
1933
+ const action = recordBacked(tree, eventById(tree, less.id)) ? edits : "correct";
1934
+ if (action !== "conflict" || !openConflict(tree, owner.person ?? owner.family, f))
1935
+ push({ kind: "fact.changed", action, ...owner, fact: f, was: less, off });
1936
+ }
1923
1937
  continue;
1924
1938
  }
1925
1939
  if (was && baseKeys.has(k)) {
@@ -2858,7 +2872,7 @@ function addsTo(ours, file) {
2858
2872
  }
2859
2873
  /** An open conflict whose claims hold this value for the person or family already. */
2860
2874
  function openConflict(tree, owner, f) {
2861
- return openClaim(tree, owner, describe(f));
2875
+ return openClaim(tree, owner, claimValue(f));
2862
2876
  }
2863
2877
  function openClaim(tree, owner, value) {
2864
2878
  if (!owner)
@@ -3182,7 +3196,13 @@ export function applySync(tree, plan, incoming, source, only) {
3182
3196
  toVerify(owner, given);
3183
3197
  }
3184
3198
  else if (c.action === "correct" || (c.kind === "fact.differs" && c.action === "pick" && !recordBacked(tree, mine))) {
3185
- editEvent(tree, mine.id, { date: c.fact.date ?? "", place: c.fact.place ?? "", ...(c.fact.value !== undefined ? { value: c.fact.value } : {}) }, `corrected in ${reason}`);
3199
+ // a value the user took off goes (B-1); a fact that is its value (an occupation) goes with it
3200
+ if (bare(c.fact)) {
3201
+ retractEvent(tree, mine.id, `corrected in ${reason}`);
3202
+ applied.push({ do: "event.retract", id: mine.id, before: mine.status });
3203
+ break;
3204
+ }
3205
+ editEvent(tree, mine.id, { date: c.fact.date ?? "", place: c.fact.place ?? "", ...(c.fact.value !== undefined ? { value: c.fact.value } : c.off?.includes("value") ? { value: "" } : {}) }, `corrected in ${reason}`);
3186
3206
  // cited: the sources the user gave it in the app, else the sync — a family tree that gave the old value (a file
3187
3207
  // taken in, an earlier send) says it no more: its citation goes, kept for an undo (found on Mac: "S0001:
3188
3208
  // kovář" in a conflict, the file saying mlynář)
@@ -3200,6 +3220,8 @@ export function applySync(tree, plan, incoming, source, only) {
3200
3220
  else if (c.action === "user") {
3201
3221
  retractEvent(tree, mine.id, `the user's edit wins: ${reason}`);
3202
3222
  applied.push({ do: "event.retract", id: mine.id, before: mine.status });
3223
+ if (bare(c.fact))
3224
+ break;
3203
3225
  // the user's word: no record makes it probable — unless it is their reading of one, which counts
3204
3226
  const citations = theirs.length ? theirs : [cite(c.fact.kind)];
3205
3227
  const { event } = addEvent(tree, owner, { ...fields(c.fact), status: citations.some((x) => reading(x.source)) ? "probable" : "possible", citations });
@@ -3210,7 +3232,7 @@ export function applySync(tree, plan, incoming, source, only) {
3210
3232
  const title = editTitle(c.name ?? owner, eventName(c.fact.kind, tree.lang, c.fact.label), factWords(tree, incoming, mine), factWords(tree, incoming, c.fact));
3211
3233
  conflictOfEdit(tree, applied, owner, c.fact.kind, title, [
3212
3234
  { ...(mine.citations[0] ? { source: mine.citations[0].source } : {}), value: describe(mine), note: `the research: ${mine.id}`, text: factWords(tree, incoming, mine) },
3213
- { source: source.id, value: describe(c.fact), note: "the user's edit", text: factWords(tree, incoming, c.fact) },
3235
+ { source: source.id, value: claimValue(c.fact), note: "the user's edit", text: factWords(tree, incoming, c.fact) },
3214
3236
  ], { event: mine.id, ...(c.fact.date ? { date: c.fact.date } : {}), ...(c.fact.place ? { place: c.fact.place } : {}), ...(c.fact.value ? { value: c.fact.value } : {}), ...(theirs.length ? { cites: theirs } : {}) });
3215
3237
  }
3216
3238
  break;
@@ -3754,6 +3776,11 @@ function describe(f) {
3754
3776
  const ages = Object.entries(f.ages ?? {}).map(([who, a]) => `${who} aged ${a}`);
3755
3777
  return [f.value, f.date, f.place, f.house && `house ${f.house}`, f.age && `aged ${f.age}`, ...ages, f.cause && `cause ${f.cause}`].filter(Boolean).join(", ") || "—";
3756
3778
  }
3779
+ /** A fact as a claim of a conflict of the user's edit holds it: nothing left of it (B-1: its date and place taken off) empty. */
3780
+ function claimValue(f) {
3781
+ const said = describe(f);
3782
+ return said === "—" ? "" : said;
3783
+ }
3757
3784
  /**
3758
3785
  * The title of a conflict of the user's edit, in the research's language — the Strom app shows it in what the research
3759
3786
  * knows (found on Mac: "Petr Svoboda: SEX — U × F" in a Czech research): "Petr Svoboda: Pohlaví — neznámé × žena".
@@ -3840,6 +3867,11 @@ export function takeSide(tree, x, side, reason) {
3840
3867
  const user = x.claims.find((c) => c.note === "the user's edit");
3841
3868
  // where the user's value came from: the sources they gave it in the app (where in them), and their edits' source
3842
3869
  const cited = [...(x.edit.cites ?? []), ...(user?.source && tree.get(user.source)?.type === "source" ? [{ source: user.source, locator: mine.kind }] : [])].filter((c, i, all) => !!tree.get(c.source) && all.findIndex((y) => y.source === c.source) === i);
3870
+ // a fact that is its value (an occupation) with the value taken off is no fact: withdrawn, nothing in its place (B-1)
3871
+ if (bare({ kind: mine.kind, ...(x.edit.value ? { value: x.edit.value } : {}) })) {
3872
+ retractEvent(tree, mine.id, `the user's edit wins (${x.id}): ${reason}`);
3873
+ return `${mine.id} withdrawn`;
3874
+ }
3843
3875
  if (recordBacked(tree, mine)) {
3844
3876
  retractEvent(tree, mine.id, `the user's edit wins (${x.id}): ${reason}`);
3845
3877
  const { event } = addEvent(tree, owner, { kind: mine.kind, ...(x.edit.date ? { date: x.edit.date } : {}), ...(x.edit.place ? { place: x.edit.place } : {}), ...(x.edit.value ? { value: x.edit.value } : {}), ...(mine.label ? { label: mine.label } : {}), status: "possible", citations: cited });
package/dist/core/tree.js CHANGED
@@ -27,7 +27,7 @@ import { lastBackup } from "./backup.js";
27
27
  export const TREE_FILE = "strom.json";
28
28
  // between releases the candidate being made (a bridge run from the sources says what it is: the Strom app goes by it);
29
29
  // at a release the release's own (package.json)
30
- export const VERSION = "1.13.2-beta.1";
30
+ export const VERSION = "1.13.2-beta.2";
31
31
  const GITIGNORE = `# Strom Research — local state and large files are not versioned
32
32
  .strom/
33
33
  /media/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strom-research",
3
- "version": "1.13.2-beta.1",
3
+ "version": "1.13.2-beta.2",
4
4
  "description": "Genealogical research toolkit for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {