@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.
- package/dist/cli.js +404 -335
- 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,
|
|
194299
|
-
|
|
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
|
-
|
|
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
|
|
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 } =
|
|
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 =
|
|
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 =
|
|
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 } =
|
|
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 } =
|
|
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
|
|
238566
|
-
|
|
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 ?
|
|
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
|
|
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.
|
|
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.
|
|
33
|
+
"@patterkit/ops": "0.4.0",
|
|
34
34
|
"@wildwinter/simple-vc-lib": "^0.4.1"
|
|
35
35
|
}
|
|
36
36
|
}
|