strom-research 1.4.0 → 1.5.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.
package/dist/core/tree.js CHANGED
@@ -23,7 +23,7 @@ import * as git from "./git.js";
23
23
  import { commitSeal, createKey, fingerprint, loadKey, sha256, signOp } from "./seal.js";
24
24
  import { stringifyCanonical } from "./json.js";
25
25
  export const TREE_FILE = "strom.json";
26
- export const VERSION = "1.4.0";
26
+ export const VERSION = "1.5.0";
27
27
  const GITIGNORE = `# Strom Research — local state and large files are not versioned
28
28
  .strom/
29
29
  /media/
@@ -210,6 +210,10 @@ function checkResearch(r, out) {
210
210
  out.push({ path: "question", message: "is required for direction question" });
211
211
  if (r.review !== undefined && (!isObj(r.review) || !REVIEW_SCOPES.includes(r.review.scope)))
212
212
  out.push({ path: "review.scope", message: `must be one of ${REVIEW_SCOPES.join(", ")}` });
213
+ if (isObj(r.review) && r.review.people !== undefined && (!Array.isArray(r.review.people) || !r.review.people.every((x) => str(x))))
214
+ out.push({ path: "review.people", message: "must be a list of person IDs" });
215
+ if (isObj(r.review) && r.review.unproven !== undefined && typeof r.review.unproven !== "boolean")
216
+ out.push({ path: "review.unproven", message: "must be true or false" });
213
217
  checkNotes(r.notes, "notes", out);
214
218
  }
215
219
  export function validateRecord(value) {
@@ -34,6 +34,7 @@ import { quay } from "../core/evidence.js";
34
34
  import { VERSION } from "../core/tree.js";
35
35
  import { dataUrl } from "../core/excerpt.js";
36
36
  import { mainPerson } from "../core/kin.js";
37
+ import * as git from "../core/git.js";
37
38
  export const GED_PROFILES = ["standard", "strom"];
38
39
  /** The first Strom version that reads AGE, CAUS, ADDR, _FREL/_MREL and more source notes itself (not released yet). */
39
40
  export const STROM_READS_TAGS = undefined;
@@ -56,6 +57,8 @@ export function stromReadsTags(version, from = STROM_READS_TAGS, unknown = false
56
57
  return (a[i] ?? 0) > (b[i] ?? 0);
57
58
  return true;
58
59
  }
60
+ /** Whose IDs a person's REFN carries: a tree coming back is matched by them (strom sync, the Strom app). */
61
+ export const REFN_TYPE = "strom-research";
59
62
  /** Tags whose value IS the fact and rides on the tag line. */
60
63
  const VALUE_TAGS = new Set(["OCCU", "RELI", "TITL", "NATI"]);
61
64
  /** Tags the Strom importer reads on INDI (contract, "Events" + note tags). */
@@ -127,8 +130,13 @@ export function exportGedcom(tree, opts = {}) {
127
130
  w.line(2, "NAME", "Strom Research");
128
131
  w.line(1, "DATE", gedDate(new Date()));
129
132
  // Which research this is: the Strom app updates the same tree when it is opened again.
130
- if (opts.for === "strom")
133
+ if (opts.for === "strom") {
131
134
  w.line(1, "_STROM_TREE", tree.config.id);
135
+ // …and of which state of it: a tree coming back is compared with what it was given (strom sync)
136
+ const head = opts.head ?? git.head(tree.root);
137
+ if (head)
138
+ w.line(1, "_STROM_HEAD", head);
139
+ }
132
140
  w.line(1, "SUBM", "@U1@");
133
141
  w.line(1, "GEDC");
134
142
  w.line(2, "VERS", "5.5.1");
@@ -162,6 +170,7 @@ export function exportGedcom(tree, opts = {}) {
162
170
  }
163
171
  w.line(1, "SEX", p.sex);
164
172
  w.line(1, "REFN", p.id);
173
+ w.line(2, "TYPE", REFN_TYPE);
165
174
  const deferred = [];
166
175
  const assos = [];
167
176
  for (const e of preferredOrder(p.events))
@@ -331,6 +340,8 @@ export function exportGedcom(tree, opts = {}) {
331
340
  w.line(1, tag, tag === "EVEN" && !typeLabel ? e.value : empty && Y_TAGS.has(tag) ? "Y" : undefined);
332
341
  if (tag === "EVEN" && typeLabel)
333
342
  w.line(2, "TYPE", typeLabel);
343
+ if (opts.ids)
344
+ w.line(2, "_EID", e.id);
334
345
  if (e.date)
335
346
  w.line(2, "DATE", e.date);
336
347
  if (e.place) {
@@ -14,7 +14,7 @@ const INDI_EVENTS = [
14
14
  const FAM_EVENTS = ["MARR", "DIV", "MARB", "MARC", "MARL", "MARS", "ANUL", "DIVF", "ENGA", "CENS", "EVEN", "NCHI"];
15
15
  /** Allowed child tags by parent context ("INDI", "INDI.BIRT", …). CONC/CONT are allowed under anything with text. */
16
16
  const CHILDREN = {
17
- HEAD: ["SOUR", "DEST", "DATE", "SUBM", "SUBN", "FILE", "COPR", "GEDC", "CHAR", "LANG", "PLAC", "NOTE"],
17
+ HEAD: ["SOUR", "DEST", "DATE", "SUBM", "SUBN", "FILE", "COPR", "GEDC", "CHAR", "LANG", "PLAC", "NOTE", "_STROM_TREE", "_STROM_HEAD"],
18
18
  "HEAD.SOUR": ["VERS", "NAME", "CORP", "DATA"],
19
19
  "HEAD.GEDC": ["VERS", "FORM"],
20
20
  "HEAD.DATE": ["TIME"],
@@ -43,7 +43,7 @@ const CHILDREN = {
43
43
  ADDR: ["CONT", "ADR1", "ADR2", "ADR3", "CITY", "STAE", "POST", "CTRY"],
44
44
  };
45
45
  const TEXT_TAGS = new Set(["NOTE", "TEXT", "TITL", "PAGE", "AUTH", "PUBL", "_STORY", "DATA", "COPR", "ADDR", "CAUS", "FILE", "_URL"]);
46
- const EXTENSIONS = new Set(["_STORY", "_WITN", "_FREL", "_MREL", "_STROM_KIND", "_URL"]);
46
+ const EXTENSIONS = new Set(["_STORY", "_WITN", "_FREL", "_MREL", "_STROM_KIND", "_URL", "_STROM_TREE", "_STROM_HEAD"]);
47
47
  /** Context of the children of the last tag in `anc` (the ancestors of a line). */
48
48
  function contextOf(anc) {
49
49
  const record = anc[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strom-research",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Genealogical research toolkit for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {