@patterkit/cli 0.2.6 → 0.2.7

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.
Files changed (2) hide show
  1. package/dist/cli.js +404 -335
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -192615,6 +192615,7 @@ function readDirSafe(dir2) {
192615
192615
  function walkFiles(dir2, ext) {
192616
192616
  const out = [];
192617
192617
  for (const e of readDirSafe(dir2)) {
192618
+ if (e.name.startsWith(".")) continue;
192618
192619
  const p = join(dir2, e.name);
192619
192620
  if (e.isDirectory()) out.push(...walkFiles(p, ext));
192620
192621
  else if (e.isFile() && e.name.endsWith(ext)) out.push(p);
@@ -192748,6 +192749,347 @@ import { dirname as dirname2 } from "path";
192748
192749
  // ../ops/src/validate.ts
192749
192750
  import { readFileSync as readFileSync2, statSync as statSync2 } from "fs";
192750
192751
 
192752
+ // ../ops/src/merge.ts
192753
+ var UnsupportedMergeError = class extends Error {
192754
+ };
192755
+ var eq = (a, b) => canonicalStringify(a) === canonicalStringify(b);
192756
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
192757
+ var CONFLICT_SIDECAR = ".patterconflict";
192758
+ function sidecarIssues(sidecarPaths) {
192759
+ return sidecarPaths.map((file) => ({
192760
+ file,
192761
+ message: "unresolved merge conflict - resolve it and delete the .patterconflict sidecar before committing"
192762
+ }));
192763
+ }
192764
+ function detectMergeType(file) {
192765
+ const s = typeof file.schema === "string" ? file.schema : "";
192766
+ if (s.startsWith("patter/flow")) return "flow";
192767
+ if (s.startsWith("patter/strings")) return "loc";
192768
+ if (s.startsWith("patter/authoring")) return "authoring";
192769
+ if (s.startsWith("patter/project")) return "project";
192770
+ throw new UnsupportedMergeError(`cannot detect a Patter merge type from schema '${s}'`);
192771
+ }
192772
+ function runMerge(base, ours, theirs, opts) {
192773
+ const type = opts?.type ?? detectMergeType(ours);
192774
+ switch (type) {
192775
+ case "loc":
192776
+ return mergeLoc(base, ours, theirs);
192777
+ case "authoring":
192778
+ return mergeAuthoring(base, ours, theirs);
192779
+ case "flow":
192780
+ return mergeFlow(base, ours, theirs);
192781
+ case "project":
192782
+ return mergeProject(base, ours, theirs);
192783
+ }
192784
+ }
192785
+ function deletedKind(base, ours, theirs) {
192786
+ const deleted = base !== void 0 && (ours === void 0 || theirs === void 0);
192787
+ return deleted ? "delete-vs-edit" : "both-changed";
192788
+ }
192789
+ function merge3(base, ours, theirs, id, path, conflicts) {
192790
+ if (eq(ours, theirs)) return ours;
192791
+ if (eq(base, ours)) return theirs;
192792
+ if (eq(base, theirs)) return ours;
192793
+ conflicts.push({ id, path, base, ours, theirs, kind: deletedKind(base, ours, theirs) });
192794
+ return ours;
192795
+ }
192796
+ function mergeMap(base, ours, theirs, prefix, conflicts, resolve6) {
192797
+ const out = {};
192798
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
192799
+ const b = base[k], o = ours[k], t = theirs[k];
192800
+ let v;
192801
+ if (eq(o, t)) v = o;
192802
+ else if (eq(b, o)) v = t;
192803
+ else if (eq(b, t)) v = o;
192804
+ else {
192805
+ const r = resolve6?.(k, b, o, t);
192806
+ if (r && r.resolved) v = r.value;
192807
+ else {
192808
+ conflicts.push({ id: k, path: `${prefix}.${k}`, base: b, ours: o, theirs: t, kind: r ? r.kind : deletedKind(b, o, t) });
192809
+ v = o;
192810
+ }
192811
+ }
192812
+ if (v !== void 0) out[k] = v;
192813
+ }
192814
+ return out;
192815
+ }
192816
+ var asMap = (v) => isObj(v) ? v : {};
192817
+ var asArr = (v) => Array.isArray(v) ? v : [];
192818
+ function setIf(obj2, key2, val) {
192819
+ if (Array.isArray(val) ? val.length > 0 : Object.keys(val).length > 0) obj2[key2] = val;
192820
+ }
192821
+ function mergeLoc(base, ours, theirs) {
192822
+ const conflicts = [];
192823
+ const merged = {};
192824
+ for (const f of ["schema", "scene", "locale", "default"]) {
192825
+ const v = merge3(base[f], ours[f], theirs[f], "", f, conflicts);
192826
+ if (v !== void 0) merged[f] = v;
192827
+ }
192828
+ merged.strings = mergeMap(asMap(base.strings), asMap(ours.strings), asMap(theirs.strings), "strings", conflicts);
192829
+ return { type: "loc", merged, conflicts, warnings: [] };
192830
+ }
192831
+ function mergeAuthoring(base, ours, theirs) {
192832
+ const conflicts = [];
192833
+ const merged = {};
192834
+ merged.schema = merge3(base.schema, ours.schema, theirs.schema, "", "schema", conflicts) ?? ours.schema;
192835
+ setIf(merged, "comments", mergeComments(asArr(base.comments), asArr(ours.comments), asArr(theirs.comments)));
192836
+ const edits = mergeEdits(asMap(base.edits), asMap(ours.edits), asMap(theirs.edits));
192837
+ setIf(merged, "edits", edits);
192838
+ const lww = lastWriterWins(asMap(ours.edits), asMap(theirs.edits));
192839
+ for (const f of ["writing", "recording", "audio"]) {
192840
+ setIf(merged, f, mergeMap(asMap(base[f]), asMap(ours[f]), asMap(theirs[f]), f, conflicts, lww));
192841
+ }
192842
+ setIf(merged, "documentation", mergeMap(asMap(base.documentation), asMap(ours.documentation), asMap(theirs.documentation), "documentation", conflicts));
192843
+ setIf(merged, "cut", mergeMap(asMap(base.cut), asMap(ours.cut), asMap(theirs.cut), "cut", conflicts, void 0));
192844
+ setIf(merged, "suggestions", mergeById(asArr(base.suggestions), asArr(ours.suggestions), asArr(theirs.suggestions), "suggestions", conflicts));
192845
+ setIf(merged, "rerecord", mergeMap(asMap(base.rerecord), asMap(ours.rerecord), asMap(theirs.rerecord), "rerecord", conflicts, void 0));
192846
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
192847
+ if (k in merged || AUTHORING_HANDLED.has(k)) continue;
192848
+ const v = merge3(base[k], ours[k], theirs[k], "", k, conflicts);
192849
+ if (v !== void 0) merged[k] = v;
192850
+ }
192851
+ return { type: "authoring", merged, conflicts, warnings: [] };
192852
+ }
192853
+ var AUTHORING_HANDLED = /* @__PURE__ */ new Set(["schema", "comments", "edits", "writing", "recording", "audio", "documentation", "cut", "suggestions", "rerecord"]);
192854
+ function mergeById(base, ours, theirs, path, conflicts) {
192855
+ const index2 = (arr) => {
192856
+ const m = {};
192857
+ for (const r of arr) if (isObj(r) && typeof r.id === "string") m[r.id] = r;
192858
+ return m;
192859
+ };
192860
+ const merged = mergeMap(index2(base), index2(ours), index2(theirs), path, conflicts);
192861
+ return Object.values(merged).sort((a, b) => String(asMap(a).ts ?? "").localeCompare(String(asMap(b).ts ?? "")));
192862
+ }
192863
+ function mergeComments(base, ours, theirs) {
192864
+ const byId = /* @__PURE__ */ new Map();
192865
+ for (const c2 of [...base, ...ours, ...theirs]) {
192866
+ if (isObj(c2) && typeof c2.id === "string" && !byId.has(c2.id)) byId.set(c2.id, c2);
192867
+ }
192868
+ return [...byId.values()].sort((a, b) => String(a.ts ?? "").localeCompare(String(b.ts ?? "")));
192869
+ }
192870
+ function lastWriterWins(oursEdits, theirsEdits) {
192871
+ return (id, _b, o, t) => {
192872
+ const om = asMap(oursEdits[id]).modifiedAt, tm = asMap(theirsEdits[id]).modifiedAt;
192873
+ if (typeof om === "string" && typeof tm === "string") return { resolved: true, value: om >= tm ? o : t };
192874
+ return { resolved: false, kind: "no-timestamp" };
192875
+ };
192876
+ }
192877
+ function mergeEdits(base, ours, theirs) {
192878
+ const out = {};
192879
+ for (const id of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
192880
+ const o = ours[id], t = theirs[id];
192881
+ if (eq(o, t)) {
192882
+ if (o !== void 0) out[id] = o;
192883
+ continue;
192884
+ }
192885
+ if (o === void 0) {
192886
+ if (t !== void 0) out[id] = t;
192887
+ continue;
192888
+ }
192889
+ if (t === void 0) {
192890
+ out[id] = o;
192891
+ continue;
192892
+ }
192893
+ out[id] = mergeEditRecord(asMap(o), asMap(t));
192894
+ }
192895
+ return out;
192896
+ }
192897
+ function mergeEditRecord(o, t) {
192898
+ const om = typeof o.modifiedAt === "string" ? o.modifiedAt : "";
192899
+ const tm = typeof t.modifiedAt === "string" ? t.modifiedAt : "";
192900
+ const newer = om >= tm ? o : t;
192901
+ const ol = asMap(o.localisedAt), tl = asMap(t.localisedAt);
192902
+ const loc = {};
192903
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(ol), ...Object.keys(tl)])) {
192904
+ const a = typeof ol[k] === "string" ? ol[k] : "";
192905
+ const b = typeof tl[k] === "string" ? tl[k] : "";
192906
+ loc[k] = a >= b ? ol[k] ?? tl[k] : tl[k] ?? ol[k];
192907
+ }
192908
+ const merged = { ...newer };
192909
+ if (Object.keys(loc).length > 0) merged.localisedAt = loc;
192910
+ return merged;
192911
+ }
192912
+ function mergeKeyedByName(base, ours, theirs, path, conflicts) {
192913
+ const byName = (arr) => {
192914
+ const m = {};
192915
+ for (const p of arr) if (isObj(p) && typeof p.name === "string") m[p.name] = p;
192916
+ return m;
192917
+ };
192918
+ const merged = mergeMap(byName(base), byName(ours), byName(theirs), path, conflicts);
192919
+ return Object.keys(merged).sort().map((name) => merged[name]);
192920
+ }
192921
+ function mergeProject(base, ours, theirs) {
192922
+ const conflicts = [];
192923
+ const merged = {};
192924
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
192925
+ if (k === "properties" || k === "cast") {
192926
+ const v = mergeKeyedByName(asArr(base[k]), asArr(ours[k]), asArr(theirs[k]), k, conflicts);
192927
+ if (v.length > 0) merged[k] = v;
192928
+ } else if (k === "gameDataFields") {
192929
+ const v = mergeGameDataFields(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
192930
+ if (Object.keys(v).length > 0) merged[k] = v;
192931
+ } else if (k === "locales") {
192932
+ merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
192933
+ } else {
192934
+ const v = merge3(base[k], ours[k], theirs[k], "", k, conflicts);
192935
+ if (v !== void 0) merged[k] = v;
192936
+ }
192937
+ }
192938
+ return { type: "project", merged, conflicts, warnings: [] };
192939
+ }
192940
+ function mergeGameDataFields(b, o, t, conflicts) {
192941
+ const out = {};
192942
+ for (const kind of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
192943
+ const v = mergeKeyedByName(asArr(b[kind]), asArr(o[kind]), asArr(t[kind]), `gameDataFields.${kind}`, conflicts);
192944
+ if (v.length > 0) out[kind] = v;
192945
+ }
192946
+ return out;
192947
+ }
192948
+ function mergeLocales(b, o, t, conflicts) {
192949
+ const out = {};
192950
+ const def = merge3(b.default, o.default, t.default, "", "locales.default", conflicts);
192951
+ if (def !== void 0) out.default = def;
192952
+ const all = [];
192953
+ for (const arr of [asArr(b.all), asArr(o.all), asArr(t.all)]) {
192954
+ for (const x of arr) if (typeof x === "string" && !all.includes(x)) all.push(x);
192955
+ }
192956
+ out.all = all;
192957
+ return out;
192958
+ }
192959
+ var CHILD_KEYS = ["blocks", "children", "beats"];
192960
+ function toTree(raw) {
192961
+ const childKey = CHILD_KEYS.find((k) => Array.isArray(raw[k]));
192962
+ const fields = {};
192963
+ for (const [k, v] of Object.entries(raw)) if (k !== childKey) fields[k] = v;
192964
+ const children = childKey ? raw[childKey].filter(isObj).map(toTree) : [];
192965
+ return { id: typeof raw.id === "string" ? raw.id : "", fields, childKey, children };
192966
+ }
192967
+ function fromTree(n) {
192968
+ const out = { ...n.fields };
192969
+ if (n.childKey) out[n.childKey] = n.children.map(fromTree);
192970
+ return out;
192971
+ }
192972
+ function mergeFlow(base, ours, theirs) {
192973
+ const conflicts = [];
192974
+ const warnings = [];
192975
+ const merged = {};
192976
+ merged.schema = merge3(base.schema, ours.schema, theirs.schema, "", "schema", conflicts) ?? ours.schema;
192977
+ const bScene = isObj(base.scene) ? toTree(base.scene) : void 0;
192978
+ const oScene = toTree(asMap(ours.scene));
192979
+ const tScene = isObj(theirs.scene) ? toTree(theirs.scene) : void 0;
192980
+ const mergedScene = mergeNode(bScene, oScene, tScene, "scene", conflicts, warnings);
192981
+ merged.scene = fromTree(mergedScene);
192982
+ checkDuplicateIds(mergedScene, conflicts);
192983
+ return { type: "flow", merged, conflicts, warnings };
192984
+ }
192985
+ function mergeNode(b, o, t, path, conflicts, warnings) {
192986
+ const fields = mergeFields(b?.fields ?? {}, o.fields, t?.fields ?? {}, o.id, path, conflicts);
192987
+ const childKey = o.childKey ?? t?.childKey ?? b?.childKey;
192988
+ const children = childKey ? mergeChildren(b?.children ?? [], o.children, t?.children ?? [], `${path}.${childKey}`, conflicts, warnings) : [];
192989
+ return { id: o.id, fields, childKey, children };
192990
+ }
192991
+ function mergeFields(b, o, t, id, path, conflicts) {
192992
+ const out = {};
192993
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
192994
+ const v = merge3(b[k], o[k], t[k], id, `${path}.${k}`, conflicts);
192995
+ if (v !== void 0) out[k] = v;
192996
+ }
192997
+ return out;
192998
+ }
192999
+ function mergeChildren(B, O, T, path, conflicts, warnings) {
193000
+ const Bm = idMap(B), Om = idMap(O), Tm = idMap(T);
193001
+ const Bset = new Set(Bm.keys()), Oset = new Set(Om.keys()), Tset = new Set(Tm.keys());
193002
+ const merged = /* @__PURE__ */ new Map();
193003
+ const here = (id) => `${path}[${id}]`;
193004
+ for (const id of /* @__PURE__ */ new Set([...Bm.keys(), ...Om.keys(), ...Tm.keys()])) {
193005
+ const b = Bm.get(id), o = Om.get(id), t = Tm.get(id);
193006
+ const inB = Bset.has(id), inO = Oset.has(id), inT = Tset.has(id);
193007
+ if (inO && inT) {
193008
+ if (!inB && !eq(fromTree(o), fromTree(t))) {
193009
+ conflicts.push({ id, path: here(id), base: void 0, ours: fromTree(o), theirs: fromTree(t), kind: "added-both" });
193010
+ merged.set(id, o);
193011
+ } else {
193012
+ merged.set(id, mergeNode(b, o, t, here(id), conflicts, warnings));
193013
+ }
193014
+ } else if (inO && !inT) {
193015
+ if (inB && !eq(fromTree(o), fromTree(b))) {
193016
+ conflicts.push({ id, path: here(id), base: fromTree(b), ours: fromTree(o), theirs: void 0, kind: "delete-vs-edit" });
193017
+ merged.set(id, o);
193018
+ } else if (!inB) {
193019
+ merged.set(id, o);
193020
+ }
193021
+ } else if (!inO && inT) {
193022
+ if (inB && !eq(fromTree(t), fromTree(b))) {
193023
+ conflicts.push({ id, path: here(id), base: fromTree(b), ours: void 0, theirs: fromTree(t), kind: "delete-vs-edit" });
193024
+ } else if (!inB) {
193025
+ merged.set(id, t);
193026
+ }
193027
+ }
193028
+ }
193029
+ return orderChildren(merged, B, O, T, path, conflicts, warnings);
193030
+ }
193031
+ function orderChildren(merged, B, O, T, path, conflicts, warnings) {
193032
+ const Bids = B.map((n) => n.id), Oids = O.map((n) => n.id), Tids = T.map((n) => n.id);
193033
+ const Oidset = new Set(Oids);
193034
+ const common = new Set(Bids.filter((id) => merged.has(id)));
193035
+ const restrict = (ids) => ids.filter((id) => common.has(id));
193036
+ const relB = restrict(Bids), relO = restrict(Oids), relT = restrict(Tids);
193037
+ const fullO = relO.length === common.size, fullT = relT.length === common.size;
193038
+ let commonOrder;
193039
+ if (!fullO && !fullT) commonOrder = relB;
193040
+ else if (!fullO) commonOrder = relT;
193041
+ else if (!fullT) commonOrder = relO;
193042
+ else if (arrEq(relO, relB)) commonOrder = relT;
193043
+ else if (arrEq(relT, relB)) commonOrder = relO;
193044
+ else if (arrEq(relO, relT)) commonOrder = relO;
193045
+ else {
193046
+ conflicts.push({ id: "", path, base: relB, ours: relO, theirs: relT, kind: "moved" });
193047
+ commonOrder = relO;
193048
+ }
193049
+ const anchorIn = (ids, id) => {
193050
+ for (let i = ids.indexOf(id) - 1; i >= 0; i--) if (common.has(ids[i])) return ids[i];
193051
+ return null;
193052
+ };
193053
+ const byAnchor = /* @__PURE__ */ new Map();
193054
+ const group = (a) => {
193055
+ let g = byAnchor.get(a);
193056
+ if (!g) {
193057
+ g = { ours: [], theirs: [] };
193058
+ byAnchor.set(a, g);
193059
+ }
193060
+ return g;
193061
+ };
193062
+ for (const n of O) if (merged.has(n.id) && !common.has(n.id)) group(anchorIn(Oids, n.id)).ours.push(n.id);
193063
+ for (const n of T) if (merged.has(n.id) && !common.has(n.id) && !Oidset.has(n.id)) group(anchorIn(Tids, n.id)).theirs.push(n.id);
193064
+ const out = [];
193065
+ const emit = (anchor) => {
193066
+ const g = byAnchor.get(anchor);
193067
+ if (!g) return;
193068
+ if (g.ours.length > 0 && g.theirs.length > 0) warnings.push({ id: anchor ?? "", path, message: "both sides inserted here; kept ours-then-theirs - review the order" });
193069
+ out.push(...g.ours, ...g.theirs);
193070
+ };
193071
+ emit(null);
193072
+ for (const id of commonOrder) {
193073
+ out.push(id);
193074
+ emit(id);
193075
+ }
193076
+ return out.map((id) => merged.get(id));
193077
+ }
193078
+ function checkDuplicateIds(root2, conflicts) {
193079
+ const seen = /* @__PURE__ */ new Set(), dup = /* @__PURE__ */ new Set();
193080
+ const walk2 = (n) => {
193081
+ if (n.id) {
193082
+ if (seen.has(n.id)) dup.add(n.id);
193083
+ else seen.add(n.id);
193084
+ }
193085
+ n.children.forEach(walk2);
193086
+ };
193087
+ walk2(root2);
193088
+ for (const id of dup) conflicts.push({ id, path: "scene", base: void 0, ours: void 0, theirs: void 0, kind: "structural" });
193089
+ }
193090
+ var idMap = (nodes) => new Map(nodes.filter((n) => n.id).map((n) => [n.id, n]));
193091
+ var arrEq = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
193092
+
192751
193093
  // ../../../expr/packages/expr/src/parser.ts
192752
193094
  var ParseError = class extends Error {
192753
193095
  constructor(message, pos2, source2) {
@@ -194295,10 +194637,8 @@ function runValidate(loaded) {
194295
194637
  const interpolation = validateInterpolation({ project, scenes, locales }, { foreignScopes });
194296
194638
  const hygiene = checkHygiene([loaded.projectFile, ...Object.values(loaded.sceneFiles), ...loaded.localeFiles, ...loaded.authoringFiles]);
194297
194639
  const staleBundles = checkBundles(loaded);
194298
- const unresolvedMerges = walkFiles(loaded.root, ".patterconflict").map((file) => ({
194299
- file,
194300
- message: "unresolved merge conflict - resolve it and delete the .patterconflict sidecar before committing"
194301
- }));
194640
+ const unresolvedMerges = sidecarIssues(walkFiles(loaded.root, CONFLICT_SIDECAR));
194641
+ const orphans = orphanShards(loaded);
194302
194642
  return {
194303
194643
  structural,
194304
194644
  conditions,
@@ -194306,9 +194646,32 @@ function runValidate(loaded) {
194306
194646
  hygiene,
194307
194647
  staleBundles,
194308
194648
  unresolvedMerges,
194309
- ok: structural.length === 0 && conditions.length === 0 && interpolation.length === 0 && hygiene.length === 0 && staleBundles.length === 0 && unresolvedMerges.length === 0
194649
+ orphans,
194650
+ ok: structural.length === 0 && conditions.length === 0 && interpolation.length === 0 && hygiene.length === 0 && staleBundles.length === 0 && unresolvedMerges.length === 0 && orphans.length === 0
194310
194651
  };
194311
194652
  }
194653
+ function orphanShards(loaded) {
194654
+ const collected = /* @__PURE__ */ new Set([
194655
+ loaded.projectFile,
194656
+ ...Object.values(loaded.sceneFiles),
194657
+ ...loaded.localeFiles,
194658
+ ...loaded.authoringFiles
194659
+ ]);
194660
+ const kind = {
194661
+ ".patterflow": "scene",
194662
+ ".patterloc": "locale",
194663
+ ".patterx": "authoring",
194664
+ ".patterproj": "project"
194665
+ };
194666
+ const out = [];
194667
+ for (const [ext, what] of Object.entries(kind)) {
194668
+ for (const file of walkFiles(loaded.root, ext)) {
194669
+ if (collected.has(file)) continue;
194670
+ out.push({ file, message: `a ${what} shard outside the project's layout - nothing loads it, so its content is not in the project (move it under the configured folder, or delete it)` });
194671
+ }
194672
+ }
194673
+ return out;
194674
+ }
194312
194675
  function checkBundles(loaded) {
194313
194676
  const issues = [];
194314
194677
  const bundles = walkFiles(loaded.root, ".patterc");
@@ -194388,7 +194751,16 @@ function compileScenes(loaded) {
194388
194751
  blocks: s.blocks.map((b) => ({ ...b, children: b.children.map(clean3) }))
194389
194752
  }));
194390
194753
  }
194754
+ function refuseUnresolvedMerge(loaded) {
194755
+ const issues = sidecarIssues(walkFiles(loaded.root, CONFLICT_SIDECAR));
194756
+ if (!issues.length) return;
194757
+ throw new Error(
194758
+ `${issues.length} unresolved merge conflict(s) - resolve them and delete the .patterconflict sidecar(s) first:
194759
+ ` + issues.map((i) => ` ${i.file}`).join("\n")
194760
+ );
194761
+ }
194391
194762
  function runExport(loaded) {
194763
+ refuseUnresolvedMerge(loaded);
194392
194764
  const { project, locales } = loaded;
194393
194765
  const full = exportBundle({ project, scenes: compileScenes(loaded), locales });
194394
194766
  const loc = project.export?.localisation;
@@ -194397,6 +194769,7 @@ function runExport(loaded) {
194397
194769
  return { ...full, strings, localisation: { mode: "ids", ...loc.sourceDebug ? { sourceDebug: true } : {} } };
194398
194770
  }
194399
194771
  function runExportFull(loaded) {
194772
+ refuseUnresolvedMerge(loaded);
194400
194773
  const { project, locales } = loaded;
194401
194774
  return exportBundle({ project, scenes: compileScenes(loaded), locales });
194402
194775
  }
@@ -196456,7 +196829,7 @@ function stringsByLocale(loaded) {
196456
196829
  }
196457
196830
  return byLocale;
196458
196831
  }
196459
- function mergeAuthoring(loaded) {
196832
+ function mergeAuthoring2(loaded) {
196460
196833
  const writing = /* @__PURE__ */ new Map();
196461
196834
  const recording = /* @__PURE__ */ new Map();
196462
196835
  const cut = /* @__PURE__ */ new Set();
@@ -196875,7 +197248,7 @@ function runReport(loaded, recordingOverride) {
196875
197248
  const thresholdIdx = estimatingOn ? writingIndex.get(est?.thresholdStatus ?? "") ?? 0 : -1;
196876
197249
  const defaultLines = est?.defaultLines ?? 0;
196877
197250
  const tagMap = new Map((est?.tagEstimates ?? []).map((t) => [t.tag, t.lines]));
196878
- const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, edits: editsOf } = mergeAuthoring(loaded);
197251
+ const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, edits: editsOf } = mergeAuthoring2(loaded);
196879
197252
  const recordingBase = recordingOverride ?? manualRecordingOf;
196880
197253
  const recordingOf = (id) => effectiveRecording(id, recordingBase, rerecordSet, recordingLadder[0]);
196881
197254
  const byLocale = stringsByLocale(loaded);
@@ -197232,7 +197605,7 @@ function classesForChannel(classes, channel) {
197232
197605
  function resolveDocumentation(loaded, channel) {
197233
197606
  const classes = loaded.project.documentationClasses ?? DEFAULT_DOCUMENTATION_CLASSES;
197234
197607
  const allowed = classesForChannel(classes, channel);
197235
- const docsOf = mergeAuthoring(loaded).documentation;
197608
+ const docsOf = mergeAuthoring2(loaded).documentation;
197236
197609
  const own = (id) => (docsOf.get(id) ?? []).filter((l2) => l2.type !== void 0 && allowed.has(l2.type));
197237
197610
  const out = /* @__PURE__ */ new Map();
197238
197611
  const visit = (id, inherited) => {
@@ -197268,7 +197641,7 @@ function extractLoc(loaded, opts = {}) {
197268
197641
  const targetLocale = isTemplate ? void 0 : opts.locale;
197269
197642
  const source2 = tableFor(loaded, defaultLocale);
197270
197643
  const target = targetLocale ? tableFor(loaded, targetLocale) : {};
197271
- const edits = mergeAuthoring(loaded).edits;
197644
+ const edits = mergeAuthoring2(loaded).edits;
197272
197645
  const docs = resolveDocumentation(loaded, "loc");
197273
197646
  const commentsOf = (id) => (docs.get(id) ?? []).map((d) => d.text);
197274
197647
  const staleFor = (id, translation) => {
@@ -197608,7 +197981,7 @@ function runVoiceScript(loaded, opts = {}) {
197608
197981
  const stub = writingLadder[0];
197609
197982
  const recordThreshold = ladderDecls.findIndex((s) => s.readyToRecord);
197610
197983
  const writingIndex = new Map(writingLadder.map((name, i) => [name, i]));
197611
- const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, documentation: docsOf } = mergeAuthoring(loaded);
197984
+ const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, documentation: docsOf } = mergeAuthoring2(loaded);
197612
197985
  const recordingBase = opts.recordingOverride ?? manualRecordingOf;
197613
197986
  const recordingOf = (id) => effectiveRecording(id, recordingBase, rerecordSet, recordingLadder[0]);
197614
197987
  const source2 = sourceStrings(loaded);
@@ -197804,7 +198177,7 @@ function sequenceLabel(node) {
197804
198177
  function runScriptDoc(loaded) {
197805
198178
  const { project } = loaded;
197806
198179
  const source2 = sourceStrings(loaded);
197807
- const { cut } = mergeAuthoring(loaded);
198180
+ const { cut } = mergeAuthoring2(loaded);
197808
198181
  const sceneOf = /* @__PURE__ */ new Map();
197809
198182
  const blockTrail = /* @__PURE__ */ new Map();
197810
198183
  const blockName = /* @__PURE__ */ new Map();
@@ -238165,6 +238538,13 @@ async function runPack(startPath) {
238165
238538
  const projectFile = findProjectFile(startPath);
238166
238539
  const root2 = dirname4(projectFile);
238167
238540
  const project = parseSource(readFileSync4(projectFile, "utf8"));
238541
+ const unresolved = sidecarIssues(walkFiles(root2, CONFLICT_SIDECAR));
238542
+ if (unresolved.length) {
238543
+ throw new Error(
238544
+ `${unresolved.length} unresolved merge conflict(s) - resolve them before packing, or the recipient gets your side of a disagreement with no sign of it:
238545
+ ` + unresolved.map((i) => ` ${i.file}`).join("\n")
238546
+ );
238547
+ }
238168
238548
  const files = SHARD_EXTENSIONS.flatMap((ext) => walkFiles(root2, ext)).map((abs) => ({ abs, rel: relative(root2, abs).split(sep2).join("/") })).sort((a, b) => a.rel.localeCompare(b.rel));
238169
238549
  const manifest = {
238170
238550
  schema: "patter/document@0",
@@ -238181,325 +238561,6 @@ async function runPack(startPath) {
238181
238561
  var import_jszip2 = __toESM(require_lib3(), 1);
238182
238562
  import { join as join5, normalize, isAbsolute as isAbsolute2, resolve as resolve4, sep as sep3 } from "path";
238183
238563
  import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
238184
-
238185
- // ../ops/src/merge.ts
238186
- var UnsupportedMergeError = class extends Error {
238187
- };
238188
- var eq = (a, b) => canonicalStringify(a) === canonicalStringify(b);
238189
- var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
238190
- function detectMergeType(file) {
238191
- const s = typeof file.schema === "string" ? file.schema : "";
238192
- if (s.startsWith("patter/flow")) return "flow";
238193
- if (s.startsWith("patter/strings")) return "loc";
238194
- if (s.startsWith("patter/authoring")) return "authoring";
238195
- if (s.startsWith("patter/project")) return "project";
238196
- throw new UnsupportedMergeError(`cannot detect a Patter merge type from schema '${s}'`);
238197
- }
238198
- function runMerge(base, ours, theirs, opts) {
238199
- const type = opts?.type ?? detectMergeType(ours);
238200
- switch (type) {
238201
- case "loc":
238202
- return mergeLoc(base, ours, theirs);
238203
- case "authoring":
238204
- return mergeAuthoring2(base, ours, theirs);
238205
- case "flow":
238206
- return mergeFlow(base, ours, theirs);
238207
- case "project":
238208
- return mergeProject(base, ours, theirs);
238209
- }
238210
- }
238211
- function deletedKind(base, ours, theirs) {
238212
- const deleted = base !== void 0 && (ours === void 0 || theirs === void 0);
238213
- return deleted ? "delete-vs-edit" : "both-changed";
238214
- }
238215
- function merge3(base, ours, theirs, id, path, conflicts) {
238216
- if (eq(ours, theirs)) return ours;
238217
- if (eq(base, ours)) return theirs;
238218
- if (eq(base, theirs)) return ours;
238219
- conflicts.push({ id, path, base, ours, theirs, kind: deletedKind(base, ours, theirs) });
238220
- return ours;
238221
- }
238222
- function mergeMap(base, ours, theirs, prefix, conflicts, resolve6) {
238223
- const out = {};
238224
- for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
238225
- const b = base[k], o = ours[k], t = theirs[k];
238226
- let v;
238227
- if (eq(o, t)) v = o;
238228
- else if (eq(b, o)) v = t;
238229
- else if (eq(b, t)) v = o;
238230
- else {
238231
- const r = resolve6?.(k, b, o, t);
238232
- if (r && r.resolved) v = r.value;
238233
- else {
238234
- conflicts.push({ id: k, path: `${prefix}.${k}`, base: b, ours: o, theirs: t, kind: r ? r.kind : deletedKind(b, o, t) });
238235
- v = o;
238236
- }
238237
- }
238238
- if (v !== void 0) out[k] = v;
238239
- }
238240
- return out;
238241
- }
238242
- var asMap = (v) => isObj(v) ? v : {};
238243
- var asArr = (v) => Array.isArray(v) ? v : [];
238244
- function setIf(obj2, key2, val) {
238245
- if (Array.isArray(val) ? val.length > 0 : Object.keys(val).length > 0) obj2[key2] = val;
238246
- }
238247
- function mergeLoc(base, ours, theirs) {
238248
- const conflicts = [];
238249
- const merged = {};
238250
- for (const f of ["schema", "scene", "locale", "default"]) {
238251
- const v = merge3(base[f], ours[f], theirs[f], "", f, conflicts);
238252
- if (v !== void 0) merged[f] = v;
238253
- }
238254
- merged.strings = mergeMap(asMap(base.strings), asMap(ours.strings), asMap(theirs.strings), "strings", conflicts);
238255
- return { type: "loc", merged, conflicts, warnings: [] };
238256
- }
238257
- function mergeAuthoring2(base, ours, theirs) {
238258
- const conflicts = [];
238259
- const merged = {};
238260
- merged.schema = merge3(base.schema, ours.schema, theirs.schema, "", "schema", conflicts) ?? ours.schema;
238261
- setIf(merged, "comments", mergeComments(asArr(base.comments), asArr(ours.comments), asArr(theirs.comments)));
238262
- const edits = mergeEdits(asMap(base.edits), asMap(ours.edits), asMap(theirs.edits));
238263
- setIf(merged, "edits", edits);
238264
- const lww = lastWriterWins(asMap(ours.edits), asMap(theirs.edits));
238265
- for (const f of ["writing", "recording", "audio"]) {
238266
- setIf(merged, f, mergeMap(asMap(base[f]), asMap(ours[f]), asMap(theirs[f]), f, conflicts, lww));
238267
- }
238268
- setIf(merged, "documentation", mergeMap(asMap(base.documentation), asMap(ours.documentation), asMap(theirs.documentation), "documentation", conflicts));
238269
- setIf(merged, "cut", mergeMap(asMap(base.cut), asMap(ours.cut), asMap(theirs.cut), "cut", conflicts, void 0));
238270
- return { type: "authoring", merged, conflicts, warnings: [] };
238271
- }
238272
- function mergeComments(base, ours, theirs) {
238273
- const byId = /* @__PURE__ */ new Map();
238274
- for (const c2 of [...base, ...ours, ...theirs]) {
238275
- if (isObj(c2) && typeof c2.id === "string" && !byId.has(c2.id)) byId.set(c2.id, c2);
238276
- }
238277
- return [...byId.values()].sort((a, b) => String(a.ts ?? "").localeCompare(String(b.ts ?? "")));
238278
- }
238279
- function lastWriterWins(oursEdits, theirsEdits) {
238280
- return (id, _b, o, t) => {
238281
- const om = asMap(oursEdits[id]).modifiedAt, tm = asMap(theirsEdits[id]).modifiedAt;
238282
- if (typeof om === "string" && typeof tm === "string") return { resolved: true, value: om >= tm ? o : t };
238283
- return { resolved: false, kind: "no-timestamp" };
238284
- };
238285
- }
238286
- function mergeEdits(base, ours, theirs) {
238287
- const out = {};
238288
- for (const id of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
238289
- const o = ours[id], t = theirs[id];
238290
- if (eq(o, t)) {
238291
- if (o !== void 0) out[id] = o;
238292
- continue;
238293
- }
238294
- if (o === void 0) {
238295
- if (t !== void 0) out[id] = t;
238296
- continue;
238297
- }
238298
- if (t === void 0) {
238299
- out[id] = o;
238300
- continue;
238301
- }
238302
- out[id] = mergeEditRecord(asMap(o), asMap(t));
238303
- }
238304
- return out;
238305
- }
238306
- function mergeEditRecord(o, t) {
238307
- const om = typeof o.modifiedAt === "string" ? o.modifiedAt : "";
238308
- const tm = typeof t.modifiedAt === "string" ? t.modifiedAt : "";
238309
- const newer = om >= tm ? o : t;
238310
- const ol = asMap(o.localisedAt), tl = asMap(t.localisedAt);
238311
- const loc = {};
238312
- for (const k of /* @__PURE__ */ new Set([...Object.keys(ol), ...Object.keys(tl)])) {
238313
- const a = typeof ol[k] === "string" ? ol[k] : "";
238314
- const b = typeof tl[k] === "string" ? tl[k] : "";
238315
- loc[k] = a >= b ? ol[k] ?? tl[k] : tl[k] ?? ol[k];
238316
- }
238317
- const merged = { ...newer };
238318
- if (Object.keys(loc).length > 0) merged.localisedAt = loc;
238319
- return merged;
238320
- }
238321
- function mergeKeyedByName(base, ours, theirs, path, conflicts) {
238322
- const byName = (arr) => {
238323
- const m = {};
238324
- for (const p of arr) if (isObj(p) && typeof p.name === "string") m[p.name] = p;
238325
- return m;
238326
- };
238327
- const merged = mergeMap(byName(base), byName(ours), byName(theirs), path, conflicts);
238328
- return Object.keys(merged).sort().map((name) => merged[name]);
238329
- }
238330
- function mergeProject(base, ours, theirs) {
238331
- const conflicts = [];
238332
- const merged = {};
238333
- for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
238334
- if (k === "properties" || k === "cast") {
238335
- const v = mergeKeyedByName(asArr(base[k]), asArr(ours[k]), asArr(theirs[k]), k, conflicts);
238336
- if (v.length > 0) merged[k] = v;
238337
- } else if (k === "gameDataFields") {
238338
- const v = mergeGameDataFields(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
238339
- if (Object.keys(v).length > 0) merged[k] = v;
238340
- } else if (k === "locales") {
238341
- merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
238342
- } else {
238343
- const v = merge3(base[k], ours[k], theirs[k], "", k, conflicts);
238344
- if (v !== void 0) merged[k] = v;
238345
- }
238346
- }
238347
- return { type: "project", merged, conflicts, warnings: [] };
238348
- }
238349
- function mergeGameDataFields(b, o, t, conflicts) {
238350
- const out = {};
238351
- for (const kind of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
238352
- const v = mergeKeyedByName(asArr(b[kind]), asArr(o[kind]), asArr(t[kind]), `gameDataFields.${kind}`, conflicts);
238353
- if (v.length > 0) out[kind] = v;
238354
- }
238355
- return out;
238356
- }
238357
- function mergeLocales(b, o, t, conflicts) {
238358
- const out = {};
238359
- const def = merge3(b.default, o.default, t.default, "", "locales.default", conflicts);
238360
- if (def !== void 0) out.default = def;
238361
- const all = [];
238362
- for (const arr of [asArr(b.all), asArr(o.all), asArr(t.all)]) {
238363
- for (const x of arr) if (typeof x === "string" && !all.includes(x)) all.push(x);
238364
- }
238365
- out.all = all;
238366
- return out;
238367
- }
238368
- var CHILD_KEYS = ["blocks", "children", "beats"];
238369
- function toTree(raw) {
238370
- const childKey = CHILD_KEYS.find((k) => Array.isArray(raw[k]));
238371
- const fields = {};
238372
- for (const [k, v] of Object.entries(raw)) if (k !== childKey) fields[k] = v;
238373
- const children = childKey ? raw[childKey].filter(isObj).map(toTree) : [];
238374
- return { id: typeof raw.id === "string" ? raw.id : "", fields, childKey, children };
238375
- }
238376
- function fromTree(n) {
238377
- const out = { ...n.fields };
238378
- if (n.childKey) out[n.childKey] = n.children.map(fromTree);
238379
- return out;
238380
- }
238381
- function mergeFlow(base, ours, theirs) {
238382
- const conflicts = [];
238383
- const warnings = [];
238384
- const merged = {};
238385
- merged.schema = merge3(base.schema, ours.schema, theirs.schema, "", "schema", conflicts) ?? ours.schema;
238386
- const bScene = isObj(base.scene) ? toTree(base.scene) : void 0;
238387
- const oScene = toTree(asMap(ours.scene));
238388
- const tScene = isObj(theirs.scene) ? toTree(theirs.scene) : void 0;
238389
- const mergedScene = mergeNode(bScene, oScene, tScene, "scene", conflicts, warnings);
238390
- merged.scene = fromTree(mergedScene);
238391
- checkDuplicateIds(mergedScene, conflicts);
238392
- return { type: "flow", merged, conflicts, warnings };
238393
- }
238394
- function mergeNode(b, o, t, path, conflicts, warnings) {
238395
- const fields = mergeFields(b?.fields ?? {}, o.fields, t?.fields ?? {}, o.id, path, conflicts);
238396
- const childKey = o.childKey ?? t?.childKey ?? b?.childKey;
238397
- const children = childKey ? mergeChildren(b?.children ?? [], o.children, t?.children ?? [], `${path}.${childKey}`, conflicts, warnings) : [];
238398
- return { id: o.id, fields, childKey, children };
238399
- }
238400
- function mergeFields(b, o, t, id, path, conflicts) {
238401
- const out = {};
238402
- for (const k of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
238403
- const v = merge3(b[k], o[k], t[k], id, `${path}.${k}`, conflicts);
238404
- if (v !== void 0) out[k] = v;
238405
- }
238406
- return out;
238407
- }
238408
- function mergeChildren(B, O, T, path, conflicts, warnings) {
238409
- const Bm = idMap(B), Om = idMap(O), Tm = idMap(T);
238410
- const Bset = new Set(Bm.keys()), Oset = new Set(Om.keys()), Tset = new Set(Tm.keys());
238411
- const merged = /* @__PURE__ */ new Map();
238412
- const here = (id) => `${path}[${id}]`;
238413
- for (const id of /* @__PURE__ */ new Set([...Bm.keys(), ...Om.keys(), ...Tm.keys()])) {
238414
- const b = Bm.get(id), o = Om.get(id), t = Tm.get(id);
238415
- const inB = Bset.has(id), inO = Oset.has(id), inT = Tset.has(id);
238416
- if (inO && inT) {
238417
- if (!inB && !eq(fromTree(o), fromTree(t))) {
238418
- conflicts.push({ id, path: here(id), base: void 0, ours: fromTree(o), theirs: fromTree(t), kind: "added-both" });
238419
- merged.set(id, o);
238420
- } else {
238421
- merged.set(id, mergeNode(b, o, t, here(id), conflicts, warnings));
238422
- }
238423
- } else if (inO && !inT) {
238424
- if (inB && !eq(fromTree(o), fromTree(b))) {
238425
- conflicts.push({ id, path: here(id), base: fromTree(b), ours: fromTree(o), theirs: void 0, kind: "delete-vs-edit" });
238426
- merged.set(id, o);
238427
- } else if (!inB) {
238428
- merged.set(id, o);
238429
- }
238430
- } else if (!inO && inT) {
238431
- if (inB && !eq(fromTree(t), fromTree(b))) {
238432
- conflicts.push({ id, path: here(id), base: fromTree(b), ours: void 0, theirs: fromTree(t), kind: "delete-vs-edit" });
238433
- } else if (!inB) {
238434
- merged.set(id, t);
238435
- }
238436
- }
238437
- }
238438
- return orderChildren(merged, B, O, T, path, conflicts, warnings);
238439
- }
238440
- function orderChildren(merged, B, O, T, path, conflicts, warnings) {
238441
- const Bids = B.map((n) => n.id), Oids = O.map((n) => n.id), Tids = T.map((n) => n.id);
238442
- const Oidset = new Set(Oids);
238443
- const common = new Set(Bids.filter((id) => merged.has(id)));
238444
- const restrict = (ids) => ids.filter((id) => common.has(id));
238445
- const relB = restrict(Bids), relO = restrict(Oids), relT = restrict(Tids);
238446
- const fullO = relO.length === common.size, fullT = relT.length === common.size;
238447
- let commonOrder;
238448
- if (!fullO && !fullT) commonOrder = relB;
238449
- else if (!fullO) commonOrder = relT;
238450
- else if (!fullT) commonOrder = relO;
238451
- else if (arrEq(relO, relB)) commonOrder = relT;
238452
- else if (arrEq(relT, relB)) commonOrder = relO;
238453
- else if (arrEq(relO, relT)) commonOrder = relO;
238454
- else {
238455
- conflicts.push({ id: "", path, base: relB, ours: relO, theirs: relT, kind: "moved" });
238456
- commonOrder = relO;
238457
- }
238458
- const anchorIn = (ids, id) => {
238459
- for (let i = ids.indexOf(id) - 1; i >= 0; i--) if (common.has(ids[i])) return ids[i];
238460
- return null;
238461
- };
238462
- const byAnchor = /* @__PURE__ */ new Map();
238463
- const group = (a) => {
238464
- let g = byAnchor.get(a);
238465
- if (!g) {
238466
- g = { ours: [], theirs: [] };
238467
- byAnchor.set(a, g);
238468
- }
238469
- return g;
238470
- };
238471
- for (const n of O) if (merged.has(n.id) && !common.has(n.id)) group(anchorIn(Oids, n.id)).ours.push(n.id);
238472
- for (const n of T) if (merged.has(n.id) && !common.has(n.id) && !Oidset.has(n.id)) group(anchorIn(Tids, n.id)).theirs.push(n.id);
238473
- const out = [];
238474
- const emit = (anchor) => {
238475
- const g = byAnchor.get(anchor);
238476
- if (!g) return;
238477
- if (g.ours.length > 0 && g.theirs.length > 0) warnings.push({ id: anchor ?? "", path, message: "both sides inserted here; kept ours-then-theirs - review the order" });
238478
- out.push(...g.ours, ...g.theirs);
238479
- };
238480
- emit(null);
238481
- for (const id of commonOrder) {
238482
- out.push(id);
238483
- emit(id);
238484
- }
238485
- return out.map((id) => merged.get(id));
238486
- }
238487
- function checkDuplicateIds(root2, conflicts) {
238488
- const seen = /* @__PURE__ */ new Set(), dup = /* @__PURE__ */ new Set();
238489
- const walk2 = (n) => {
238490
- if (n.id) {
238491
- if (seen.has(n.id)) dup.add(n.id);
238492
- else seen.add(n.id);
238493
- }
238494
- n.children.forEach(walk2);
238495
- };
238496
- walk2(root2);
238497
- for (const id of dup) conflicts.push({ id, path: "scene", base: void 0, ours: void 0, theirs: void 0, kind: "structural" });
238498
- }
238499
- var idMap = (nodes) => new Map(nodes.filter((n) => n.id).map((n) => [n.id, n]));
238500
- var arrEq = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
238501
-
238502
- // ../ops/src/unpack.ts
238503
238564
  var MANIFEST = "patter.manifest.json";
238504
238565
  var UnsafeEntryError = class extends Error {
238505
238566
  };
@@ -238562,10 +238623,17 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
238562
238623
  shards.push({ path: rel, added: true });
238563
238624
  continue;
238564
238625
  }
238565
- const oursObj = parseSource(readFileSync5(outPath, "utf8"));
238566
- const theirsObj = parseSource(theirText);
238626
+ const readSide = (side, text) => {
238627
+ try {
238628
+ return parseSource(text);
238629
+ } catch (e) {
238630
+ throw new Error(`${rel}: the ${side} copy is not readable Patter source - ${e instanceof Error ? e.message : String(e)}`);
238631
+ }
238632
+ };
238633
+ const oursObj = readSide("ours", readFileSync5(outPath, "utf8"));
238634
+ const theirsObj = readSide("theirs", theirText);
238567
238635
  const baseText = base.get(rel);
238568
- const baseObj = baseText !== void 0 ? parseSource(baseText) : {};
238636
+ const baseObj = baseText !== void 0 ? readSide("base", baseText) : {};
238569
238637
  const result = runMerge(baseObj, oursObj, theirsObj);
238570
238638
  writes.push({ path: outPath, content: canonicalStringify(result.merged) });
238571
238639
  if (result.conflicts.length > 0) {
@@ -240689,14 +240757,15 @@ async function run(cmd, positionals, flags) {
240689
240757
  }
240690
240758
  case "validate": {
240691
240759
  const loaded = loadProject(positionals[0] ?? ".");
240692
- const { structural, conditions, interpolation, hygiene, staleBundles, unresolvedMerges, ok } = runValidate(loaded);
240760
+ const { structural, conditions, interpolation, hygiene, staleBundles, unresolvedMerges, orphans, ok } = runValidate(loaded);
240693
240761
  for (const i of structural) console.error(` [${i.code}] ${i.message}`);
240694
240762
  for (const i of conditions) console.error(` [${i.field}] ${i.nodeId}: ${i.message} (${i.src})`);
240695
240763
  for (const i of interpolation) console.error(` [${i.field}] ${i.nodeId}: ${i.message} (${i.src})`);
240696
240764
  for (const i of hygiene) console.error(` [hygiene] ${i.file}: ${i.message}`);
240697
240765
  for (const i of staleBundles) console.error(` [stale-bundle] ${i.file}: ${i.message}`);
240698
240766
  for (const i of unresolvedMerges) console.error(` [unresolved-merge] ${i.file}: ${i.message}`);
240699
- const count = structural.length + conditions.length + interpolation.length + hygiene.length + staleBundles.length + unresolvedMerges.length;
240767
+ for (const i of orphans) console.error(` [not-in-project] ${i.file}: ${i.message}`);
240768
+ const count = structural.length + conditions.length + interpolation.length + hygiene.length + staleBundles.length + unresolvedMerges.length + orphans.length;
240700
240769
  if (ok) console.log(`ok - ${loaded.scenes.length} scene(s), no issues`);
240701
240770
  else console.error(`
240702
240771
  ${count} issue(s)`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/cli",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "The `patter` CLI: validate, format, export, and play a Patter project.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@patterkit/core": "0.2.1",
33
- "@patterkit/ops": "0.3.6",
33
+ "@patterkit/ops": "0.4.0",
34
34
  "@wildwinter/simple-vc-lib": "^0.4.1"
35
35
  }
36
36
  }