@bitcobblers/wod-wiki-engine 0.37.2137 → 0.37.2140
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/index.cjs +203 -203
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -23
- package/dist/index.d.ts +28 -23
- package/dist/index.mjs +177 -205
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -245,43 +245,6 @@ function extractTypedTags(markdown, typedKeys = wodWikiWql.WQL_TYPED_TAG_KEYS) {
|
|
|
245
245
|
}
|
|
246
246
|
return results.filter((t) => t.label.length > 0);
|
|
247
247
|
}
|
|
248
|
-
function markdownToEffort(raw) {
|
|
249
|
-
const match = FRONTMATTER_BLOCK_RE.exec(raw);
|
|
250
|
-
if (!match) return null;
|
|
251
|
-
const meta = readFrontmatterMeta(raw);
|
|
252
|
-
const body = raw.slice(match[0].length).trim();
|
|
253
|
-
const id = scalarOf(meta["id"]);
|
|
254
|
-
const slug = scalarOf(meta["slug"]);
|
|
255
|
-
const label = scalarOf(meta["label"]);
|
|
256
|
-
if (!id || !slug || !label) return null;
|
|
257
|
-
const baseAttributes = {};
|
|
258
|
-
const blockRe = /^\s{2,}([^:]+):\s*(.+)$/;
|
|
259
|
-
let inAttrs = false;
|
|
260
|
-
for (const line of match[1].split(/\r?\n/)) {
|
|
261
|
-
if (/^baseAttributes:/.test(line)) {
|
|
262
|
-
inAttrs = true;
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
if (inAttrs) {
|
|
266
|
-
const attr = blockRe.exec(line);
|
|
267
|
-
if (attr && /^ {2,}/.test(line)) {
|
|
268
|
-
const v = attr[2].trim();
|
|
269
|
-
baseAttributes[attr[1].trim()] = /^\d+(\.\d+)?$/.test(v) ? Number(v) : v;
|
|
270
|
-
} else if (/^\S/.test(line)) {
|
|
271
|
-
inAttrs = false;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (typeof baseAttributes["met"] !== "number") return null;
|
|
276
|
-
return {
|
|
277
|
-
id,
|
|
278
|
-
slug,
|
|
279
|
-
label,
|
|
280
|
-
aliases: asList(meta["aliases"]),
|
|
281
|
-
baseAttributes,
|
|
282
|
-
...body ? { body } : {}
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
248
|
function effortToMarkdown(effort) {
|
|
286
249
|
const lines = ["---", `id: ${effort.id}`, `slug: ${effort.slug}`, `label: ${effort.label}`];
|
|
287
250
|
if (effort.aliases && effort.aliases.length > 0) {
|
|
@@ -297,8 +260,15 @@ function effortToMarkdown(effort) {
|
|
|
297
260
|
return `${lines.join("\n")}
|
|
298
261
|
${(effort.body ?? "").trim()}`.trimEnd() + "\n";
|
|
299
262
|
}
|
|
300
|
-
function
|
|
301
|
-
|
|
263
|
+
function equipmentToMarkdown(equipment) {
|
|
264
|
+
const lines = ["---", "type: equipment", `id: ${equipment.id}`, `slug: ${equipment.slug}`, `label: ${equipment.label}`];
|
|
265
|
+
if (equipment.aliases && equipment.aliases.length > 0) {
|
|
266
|
+
lines.push("aliases:");
|
|
267
|
+
for (const alias of equipment.aliases) lines.push(` - ${alias}`);
|
|
268
|
+
}
|
|
269
|
+
lines.push("---", "");
|
|
270
|
+
return `${lines.join("\n")}
|
|
271
|
+
${(equipment.body ?? "").trim()}`.trimEnd() + "\n";
|
|
302
272
|
}
|
|
303
273
|
function equipmentToCsv(labels) {
|
|
304
274
|
return ["label", ...labels].map((l) => escapeCsvField(l)).join("\n") + "\n";
|
|
@@ -647,18 +617,37 @@ function csvToSessionRows(csv) {
|
|
|
647
617
|
}
|
|
648
618
|
|
|
649
619
|
// src/version.ts
|
|
650
|
-
var VERSION = "0.37.
|
|
651
|
-
var GIT_SHA = "
|
|
652
|
-
var BUILD_TIME = "2026-10-
|
|
620
|
+
var VERSION = "0.37.2140";
|
|
621
|
+
var GIT_SHA = "bcf7960";
|
|
622
|
+
var BUILD_TIME = "2026-10-18T21:43:29.081Z";
|
|
653
623
|
var SEMVER = {
|
|
654
|
-
version: "0.37.
|
|
655
|
-
gitSha: "
|
|
656
|
-
buildTime: "2026-10-
|
|
624
|
+
version: "0.37.2140",
|
|
625
|
+
gitSha: "bcf7960",
|
|
626
|
+
buildTime: "2026-10-18T21:43:29.081Z"
|
|
657
627
|
};
|
|
658
|
-
var SEED_SCHEMA =
|
|
628
|
+
var SEED_SCHEMA = 9;
|
|
659
629
|
var SEED_REGISTERED_WIDGETS = wodWikiCore.KNOWN_SECTION_WIDGETS;
|
|
630
|
+
var SEED_PRECEDENCE_RANK = {
|
|
631
|
+
core: 0,
|
|
632
|
+
community: 1,
|
|
633
|
+
imported: 2,
|
|
634
|
+
user: 3
|
|
635
|
+
};
|
|
636
|
+
function seedSourceTier(sourceIdOrMetaKey, explicitTier) {
|
|
637
|
+
if (explicitTier) return explicitTier;
|
|
638
|
+
if (!sourceIdOrMetaKey) return "core";
|
|
639
|
+
if (sourceIdOrMetaKey === "user") return "user";
|
|
640
|
+
const id = sourceIdOrMetaKey.startsWith("seed:") ? sourceIdOrMetaKey.slice(5) : sourceIdOrMetaKey;
|
|
641
|
+
if (id === "core" || id === "efforts" || id === "equipment") return "core";
|
|
642
|
+
if (id === "community" || id.startsWith("community-") || id === "common") return "community";
|
|
643
|
+
return "imported";
|
|
644
|
+
}
|
|
645
|
+
function seedPrecedenceRank(sourceIdOrMetaKey, explicitTier) {
|
|
646
|
+
return SEED_PRECEDENCE_RANK[seedSourceTier(sourceIdOrMetaKey, explicitTier)];
|
|
647
|
+
}
|
|
660
648
|
var SEED_BROADCAST_CHANNEL = "wodwiki.seed";
|
|
661
649
|
var EFFORTS_CHUNK_ID = "efforts";
|
|
650
|
+
var EQUIPMENT_CHUNK_ID = "equipment";
|
|
662
651
|
var CANVAS_CHUNK_ID = "canvas";
|
|
663
652
|
var DASHBOARDS_CHUNK_ID = "dashboards";
|
|
664
653
|
var TEMPLATE_CHUNK_ID = "template";
|
|
@@ -684,6 +673,7 @@ function seedNoteSourceId(path, content, chunkId) {
|
|
|
684
673
|
if (chunkId === CANVAS_CHUNK_ID) return `guides:${canvasRouteSlug(content) ?? under}`;
|
|
685
674
|
if (chunkId === DASHBOARDS_CHUNK_ID) return "dashboards";
|
|
686
675
|
if (chunkId === EFFORTS_CHUNK_ID) return "efforts";
|
|
676
|
+
if (chunkId === EQUIPMENT_CHUNK_ID) return "equipment";
|
|
687
677
|
if (chunkId === TEMPLATE_CHUNK_ID) return "playground";
|
|
688
678
|
if (chunkId.startsWith("collection.")) {
|
|
689
679
|
const catalog = catalogForChunkId(chunkId);
|
|
@@ -692,141 +682,6 @@ function seedNoteSourceId(path, content, chunkId) {
|
|
|
692
682
|
if (chunkId.startsWith("feed.")) return `feed:feeds/${under}`;
|
|
693
683
|
return void 0;
|
|
694
684
|
}
|
|
695
|
-
|
|
696
|
-
// src/seed/frontmatter.ts
|
|
697
|
-
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
698
|
-
function unquote(value) {
|
|
699
|
-
return value.replace(
|
|
700
|
-
/^(['"])(.*)\1$/,
|
|
701
|
-
(_match, quote, inner) => quote === '"' ? inner.replace(/\\(["\\])/g, "$1") : inner
|
|
702
|
-
);
|
|
703
|
-
}
|
|
704
|
-
function parseFrontmatter(raw) {
|
|
705
|
-
const match = raw.match(FRONTMATTER_RE);
|
|
706
|
-
if (!match) return { meta: {}, body: raw };
|
|
707
|
-
return { meta: parseMetaLines(match[1].split(/\r?\n/)), body: raw.slice(match[0].length) };
|
|
708
|
-
}
|
|
709
|
-
function parseMetaLines(lines) {
|
|
710
|
-
const meta = {};
|
|
711
|
-
for (let i = 0; i < lines.length; i++) {
|
|
712
|
-
const keyMatch = lines[i].match(/^([A-Za-z][\w.-]*)\s*:(.*)$/);
|
|
713
|
-
if (!keyMatch) continue;
|
|
714
|
-
const key = keyMatch[1];
|
|
715
|
-
const rawVal = keyMatch[2].trim();
|
|
716
|
-
if (rawVal === "") {
|
|
717
|
-
const items = [];
|
|
718
|
-
while (i + 1 < lines.length) {
|
|
719
|
-
const itemMatch = lines[i + 1].match(/^\s+-\s+(.+)$/);
|
|
720
|
-
if (!itemMatch) break;
|
|
721
|
-
items.push(itemMatch[1].trim());
|
|
722
|
-
i++;
|
|
723
|
-
}
|
|
724
|
-
meta[key] = items.length > 0 ? items : "";
|
|
725
|
-
} else {
|
|
726
|
-
const wasQuoted = /^(['"])(.*)\1$/.test(rawVal);
|
|
727
|
-
const value = unquote(rawVal);
|
|
728
|
-
const num2 = Number(value);
|
|
729
|
-
meta[key] = !wasQuoted && value !== "" && !isNaN(num2) ? num2 : value;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
return meta;
|
|
733
|
-
}
|
|
734
|
-
function getScalar(meta, key) {
|
|
735
|
-
const value = meta[key];
|
|
736
|
-
return typeof value === "string" || typeof value === "number" ? value : void 0;
|
|
737
|
-
}
|
|
738
|
-
function getList(meta, key) {
|
|
739
|
-
const value = meta[key];
|
|
740
|
-
return Array.isArray(value) ? value : [];
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// src/seed/effort-file.ts
|
|
744
|
-
function parseDiscipline(val) {
|
|
745
|
-
if (!val) return void 0;
|
|
746
|
-
const normalized = unquoteYaml(val.trim()).toLowerCase();
|
|
747
|
-
if (wodWikiLang.isEffortDiscipline(normalized)) return normalized;
|
|
748
|
-
console.warn(`[effort-file] unknown discipline '${val}' \u2014 treating as unset`);
|
|
749
|
-
return void 0;
|
|
750
|
-
}
|
|
751
|
-
function unquoteYaml(val) {
|
|
752
|
-
if (val.startsWith('"') && val.endsWith('"')) {
|
|
753
|
-
return val.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
754
|
-
}
|
|
755
|
-
if (val.startsWith("'") && val.endsWith("'")) {
|
|
756
|
-
return val.slice(1, -1).replace(/\\'/g, "'");
|
|
757
|
-
}
|
|
758
|
-
return val;
|
|
759
|
-
}
|
|
760
|
-
function coerceNestedScalar(raw) {
|
|
761
|
-
if (raw === "true") return true;
|
|
762
|
-
if (raw === "false") return false;
|
|
763
|
-
if (raw !== "" && !Number.isNaN(Number(raw))) return Number(raw);
|
|
764
|
-
return raw;
|
|
765
|
-
}
|
|
766
|
-
function readNestedSection(lines, key) {
|
|
767
|
-
const start = lines.findIndex((l) => new RegExp(`^${key}:\\s*$`).test(l));
|
|
768
|
-
if (start === -1) return void 0;
|
|
769
|
-
const result = {};
|
|
770
|
-
let subKey = null;
|
|
771
|
-
for (let i = start + 1; i < lines.length; i++) {
|
|
772
|
-
const line = lines[i];
|
|
773
|
-
if (/^\S/.test(line)) break;
|
|
774
|
-
const trimmed = line.trim();
|
|
775
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
776
|
-
const indent = line.length - line.trimStart().length;
|
|
777
|
-
const m = trimmed.match(/^([\w.-]+):\s*(.*)$/);
|
|
778
|
-
if (!m) continue;
|
|
779
|
-
const [, k, rawVal] = m;
|
|
780
|
-
if (indent === 2) {
|
|
781
|
-
if (rawVal === "") {
|
|
782
|
-
subKey = k;
|
|
783
|
-
result[k] = {};
|
|
784
|
-
} else {
|
|
785
|
-
subKey = null;
|
|
786
|
-
result[k] = coerceNestedScalar(rawVal);
|
|
787
|
-
}
|
|
788
|
-
} else if (indent >= 4 && subKey !== null) {
|
|
789
|
-
result[subKey][k] = coerceNestedScalar(rawVal);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
793
|
-
}
|
|
794
|
-
function parseEffortFile(raw) {
|
|
795
|
-
const blockMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
796
|
-
if (!blockMatch) return null;
|
|
797
|
-
const { meta, body } = parseFrontmatter(raw);
|
|
798
|
-
const innerLines = blockMatch[1].split(/\r?\n/);
|
|
799
|
-
const id = getScalar(meta, "id");
|
|
800
|
-
const slug = getScalar(meta, "slug");
|
|
801
|
-
const label = getScalar(meta, "label");
|
|
802
|
-
const aliases = getList(meta, "aliases");
|
|
803
|
-
const baseAttrs = readNestedSection(innerLines, "baseAttributes");
|
|
804
|
-
const hints = readNestedSection(innerLines, "hints");
|
|
805
|
-
const derivation = readNestedSection(innerLines, "derivation");
|
|
806
|
-
const metRaw = baseAttrs?.met;
|
|
807
|
-
const met = typeof metRaw === "number" ? metRaw : Number(metRaw);
|
|
808
|
-
if (!id || !slug || !label || metRaw === void 0 || Number.isNaN(met)) return null;
|
|
809
|
-
const discipline = parseDiscipline(
|
|
810
|
-
typeof baseAttrs?.discipline === "string" ? baseAttrs.discipline : void 0
|
|
811
|
-
);
|
|
812
|
-
const intensityTier = baseAttrs?.intensityTier;
|
|
813
|
-
const trimmedBody = body.trim();
|
|
814
|
-
return {
|
|
815
|
-
id: String(id),
|
|
816
|
-
slug: String(slug),
|
|
817
|
-
label: String(label),
|
|
818
|
-
aliases,
|
|
819
|
-
baseAttributes: {
|
|
820
|
-
met,
|
|
821
|
-
...discipline ? { discipline } : {},
|
|
822
|
-
...intensityTier ? { intensityTier } : {}
|
|
823
|
-
},
|
|
824
|
-
registrySource: "bundled",
|
|
825
|
-
...trimmedBody ? { body: trimmedBody } : {},
|
|
826
|
-
...hints ? { hints } : {},
|
|
827
|
-
...derivation ? { derivation } : {}
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
685
|
function object(value) {
|
|
831
686
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
832
687
|
}
|
|
@@ -854,7 +709,7 @@ function assertSeedRows(value) {
|
|
|
854
709
|
if (!Array.isArray(value)) throw new Error("seed chunk is not an array");
|
|
855
710
|
const paths = /* @__PURE__ */ new Set();
|
|
856
711
|
for (const row of value) {
|
|
857
|
-
if (!object(row) || typeof row.path !== "string" || !row.path || row.path.startsWith("/") || row.path.split("/").includes("..") || paths.has(row.path) || typeof row.content !== "string" || !object(row.blocks) || !Array.isArray(row.blockIndex) || !Array.isArray(row.blockEfforts)) throw new Error("seed chunk row failed shape validation");
|
|
712
|
+
if (!object(row) || typeof row.path !== "string" || !row.path || row.path.startsWith("/") || row.path.split("/").includes("..") || paths.has(row.path) || typeof row.content !== "string" || !object(row.blocks) || !Array.isArray(row.blockIndex) || !Array.isArray(row.blockEfforts) || !Array.isArray(row.blockEquipment)) throw new Error("seed chunk row failed shape validation");
|
|
858
713
|
paths.add(row.path);
|
|
859
714
|
for (const [id, block] of Object.entries(row.blocks)) {
|
|
860
715
|
if (!object(block) || block.id !== id || !id || block.version !== 1 || block.sourceRevision !== 1 || !nonnegative(block.from) || !positive(block.to) || block.from >= block.to || block.to > row.content.length || !["wod", "query", "widget", "embed"].includes(String(block.kind)) || (block.kind === "wod" ? !["time", "log"].includes(String(block.dialect)) : block.dialect !== void 0) || block.sport !== void 0 && typeof block.sport !== "string") throw new Error("invalid seed block range or revision");
|
|
@@ -885,6 +740,15 @@ function assertSeedRows(value) {
|
|
|
885
740
|
if (!object(projection) || link.noteId !== projection.noteId || link.blockContentId !== projection.blockContentId || !Number.isFinite(link.createdAt)) throw new Error("invalid seed effort metadata");
|
|
886
741
|
links.add(String(link.id));
|
|
887
742
|
}
|
|
743
|
+
const equipmentLinks = /* @__PURE__ */ new Set();
|
|
744
|
+
for (const link of row.blockEquipment) {
|
|
745
|
+
if (!object(link) || typeof link.blockId !== "string" || typeof link.noteId !== "string" || typeof link.equipmentSlug !== "string" || !link.equipmentSlug) throw new Error("invalid seed equipment identity");
|
|
746
|
+
const block = row.blocks[link.blockId];
|
|
747
|
+
if (link.id !== `${link.noteId}:${link.blockId}:${link.equipmentSlug}` || equipmentLinks.has(String(link.id)) || !object(block) || block.kind !== "wod" || !indexed.has(link.blockId) || link.sourcePath !== row.path || link.isStatic !== true || typeof link.createdAt !== "number") throw new Error("orphan or incompatible seed equipment link");
|
|
748
|
+
const projection = row.blockIndex.find((p) => object(p) && p.blockId === link.blockId);
|
|
749
|
+
if (!object(projection) || link.noteId !== projection.noteId || link.blockContentId !== projection.blockContentId || !Number.isFinite(link.createdAt)) throw new Error("invalid seed equipment metadata");
|
|
750
|
+
equipmentLinks.add(String(link.id));
|
|
751
|
+
}
|
|
888
752
|
}
|
|
889
753
|
return value;
|
|
890
754
|
}
|
|
@@ -922,6 +786,83 @@ function refreshedBlocks(existing, rawContent, sourceRevision) {
|
|
|
922
786
|
let ordinal = 0;
|
|
923
787
|
return wodWikiCore.reconcileNoteBlocks(rawContent, { sourceRevision, blocks: mapped, registeredWidgets: SEED_REGISTERED_WIDGETS, createId: () => `seed:${existing.id}:r${sourceRevision}:${ordinal++}` });
|
|
924
788
|
}
|
|
789
|
+
async function applyDefinitionChunk(defStore, noteStore, parse, family, seedSource, incomingRank, otherCheckpoints, priorIds, rows, ids, owned, existingById, allIds) {
|
|
790
|
+
const priorIdSet = new Set(priorIds);
|
|
791
|
+
const maxOtherRankForNote = (noteId) => {
|
|
792
|
+
let max = -1;
|
|
793
|
+
for (const cp of otherCheckpoints) {
|
|
794
|
+
if (cp.noteIds.has(noteId) && cp.rank > max) max = cp.rank;
|
|
795
|
+
}
|
|
796
|
+
return max;
|
|
797
|
+
};
|
|
798
|
+
const projections = [];
|
|
799
|
+
for (const [i, row] of rows.entries()) {
|
|
800
|
+
if (!owned.has(ids[i])) continue;
|
|
801
|
+
const parsed = parse(row.content);
|
|
802
|
+
if (!parsed) continue;
|
|
803
|
+
projections.push({ ...parsed, registrySource: "bundled", ownerNoteId: ids[i], sourceId: seedSource, sourcePath: row.path });
|
|
804
|
+
}
|
|
805
|
+
const prior = await defStore.getMany(projections.map((p) => p.slug));
|
|
806
|
+
const changed = projections.filter((p, i) => {
|
|
807
|
+
const before = prior[i];
|
|
808
|
+
if (!before) return true;
|
|
809
|
+
if (before.registrySource !== "bundled") return false;
|
|
810
|
+
let beforeRank;
|
|
811
|
+
if (before.ownerNoteId === p.ownerNoteId || before.ownerNoteId && priorIdSet.has(before.ownerNoteId) && maxOtherRankForNote(before.ownerNoteId) < incomingRank) {
|
|
812
|
+
beforeRank = incomingRank;
|
|
813
|
+
} else {
|
|
814
|
+
const cpRank = before.ownerNoteId ? maxOtherRankForNote(before.ownerNoteId) : -1;
|
|
815
|
+
beforeRank = cpRank >= 0 ? cpRank : seedPrecedenceRank(before.sourceId);
|
|
816
|
+
}
|
|
817
|
+
return incomingRank >= beforeRank && !equal(p, before);
|
|
818
|
+
});
|
|
819
|
+
if (changed.length) await defStore.putMany(changed);
|
|
820
|
+
const wantedSlugs = new Set(projections.map((p) => p.slug));
|
|
821
|
+
const removedBySlug = /* @__PURE__ */ new Map();
|
|
822
|
+
for (const id of allIds) {
|
|
823
|
+
const note = existingById.get(id);
|
|
824
|
+
if (!note || note.seedOrigin !== "seed") continue;
|
|
825
|
+
const slug = parse(note.rawContent)?.slug;
|
|
826
|
+
if (slug && !wantedSlugs.has(slug)) removedBySlug.set(slug, id);
|
|
827
|
+
}
|
|
828
|
+
const removedSlugs = [...removedBySlug.keys()];
|
|
829
|
+
if (removedSlugs.length) {
|
|
830
|
+
const old = await defStore.getMany(removedSlugs);
|
|
831
|
+
for (const [i, row] of old.entries()) {
|
|
832
|
+
const slug = removedSlugs[i];
|
|
833
|
+
if (!row || row.registrySource !== "bundled") continue;
|
|
834
|
+
const removedNoteId = removedBySlug.get(slug);
|
|
835
|
+
const isOwnedByThisSource = row.ownerNoteId === removedNoteId && (!row.sourceId || row.sourceId === seedSource || row.sourceId === family);
|
|
836
|
+
if (!isOwnedByThisSource) continue;
|
|
837
|
+
let restored = false;
|
|
838
|
+
for (const cp of otherCheckpoints) {
|
|
839
|
+
const candidateIds = cp.meta.chunks?.[family]?.noteIds ?? [];
|
|
840
|
+
if (!candidateIds.length) continue;
|
|
841
|
+
const candidateNotes = await noteStore.getMany(candidateIds);
|
|
842
|
+
for (const candidateNote of candidateNotes) {
|
|
843
|
+
if (!candidateNote || candidateNote.seedOrigin !== "seed") continue;
|
|
844
|
+
const parsed = parse(candidateNote.rawContent);
|
|
845
|
+
if (parsed && parsed.slug === slug) {
|
|
846
|
+
await defStore.put({
|
|
847
|
+
...parsed,
|
|
848
|
+
registrySource: "bundled",
|
|
849
|
+
ownerNoteId: candidateNote.id,
|
|
850
|
+
sourceId: cp.sourceId,
|
|
851
|
+
sourcePath: candidateNote.sourcePath ?? ""
|
|
852
|
+
});
|
|
853
|
+
restored = true;
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (restored) break;
|
|
858
|
+
}
|
|
859
|
+
if (!restored) {
|
|
860
|
+
await defStore.delete(slug);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return changed.length;
|
|
865
|
+
}
|
|
925
866
|
async function putChanged(store, values, keys) {
|
|
926
867
|
if (!values.length) return;
|
|
927
868
|
const prior = await store.getMany(keys);
|
|
@@ -931,7 +872,7 @@ async function putChanged(store, values, keys) {
|
|
|
931
872
|
async function applySeedChunk(storage, write) {
|
|
932
873
|
assertSeedManifest(write.manifest);
|
|
933
874
|
assertSeedRows(write.rows);
|
|
934
|
-
const result = { totalNotes: 0, totalEfforts: 0, totalBlocks: 0, skippedUserOwned: 0, deleted: 0, addedNotes: 0, updatedNotes: 0 };
|
|
875
|
+
const result = { totalNotes: 0, totalEfforts: 0, totalEquipment: 0, totalBlocks: 0, skippedUserOwned: 0, deleted: 0, addedNotes: 0, updatedNotes: 0 };
|
|
935
876
|
const ids = await Promise.all(write.rows.map((row) => seedNoteId(row.path)));
|
|
936
877
|
const pagesBySlug = /* @__PURE__ */ new Map();
|
|
937
878
|
const linkIds = /* @__PURE__ */ new Map();
|
|
@@ -946,7 +887,7 @@ async function applySeedChunk(storage, write) {
|
|
|
946
887
|
if (!tagIds.has(label)) tagIds.set(label, await seedNoteId(`tag:${label}`));
|
|
947
888
|
}
|
|
948
889
|
}
|
|
949
|
-
return storage.transaction(["notes", "block_revisions", "block_index", "block_efforts", "page", "page_notes", "tags", "note_tags", "efforts", "meta"], "readwrite", async (tx) => {
|
|
890
|
+
return storage.transaction(["notes", "block_revisions", "block_index", "block_efforts", "block_equipment", "page", "page_notes", "tags", "note_tags", "efforts", "equipment", "meta"], "readwrite", async (tx) => {
|
|
950
891
|
const metaStore = tx.readwrite("meta");
|
|
951
892
|
const actualMeta = await metaStore.get(write.meta.key);
|
|
952
893
|
if (!equal(actualMeta, write.expectedMeta)) throw new Error("Seed checkpoint changed during import");
|
|
@@ -958,19 +899,50 @@ async function applySeedChunk(storage, write) {
|
|
|
958
899
|
const allIds = [.../* @__PURE__ */ new Set([...ids, ...goneIds])];
|
|
959
900
|
const stored = await noteStore.getMany(allIds);
|
|
960
901
|
const existingById = new Map(allIds.map((id, i) => [id, stored[i]]));
|
|
902
|
+
const seedSource = write.meta.key.startsWith("seed:") ? write.meta.key.slice(5) : write.meta.key;
|
|
903
|
+
const incomingRank = seedPrecedenceRank(write.meta.key, write.meta.tier);
|
|
904
|
+
const otherCheckpoints = [];
|
|
905
|
+
for (const row of await metaStore.getAll()) {
|
|
906
|
+
const meta = row;
|
|
907
|
+
if (!meta || typeof meta.key !== "string" || !meta.key.startsWith("seed:") || meta.key === write.meta.key) continue;
|
|
908
|
+
const noteIds = /* @__PURE__ */ new Set();
|
|
909
|
+
for (const chunk of Object.values(meta.chunks ?? {})) {
|
|
910
|
+
for (const noteId of chunk.noteIds ?? []) noteIds.add(noteId);
|
|
911
|
+
}
|
|
912
|
+
otherCheckpoints.push({
|
|
913
|
+
key: meta.key,
|
|
914
|
+
sourceId: meta.key.slice(5),
|
|
915
|
+
rank: seedPrecedenceRank(meta.key, meta.tier),
|
|
916
|
+
meta,
|
|
917
|
+
noteIds
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
otherCheckpoints.sort((a, b) => b.rank - a.rank);
|
|
921
|
+
const maxOtherRankForNote = (noteId) => {
|
|
922
|
+
let max = -1;
|
|
923
|
+
for (const cp of otherCheckpoints) {
|
|
924
|
+
if (cp.noteIds.has(noteId) && cp.rank > max) max = cp.rank;
|
|
925
|
+
}
|
|
926
|
+
return max;
|
|
927
|
+
};
|
|
961
928
|
const ownedIds = allIds.filter((id) => {
|
|
962
929
|
const note = existingById.get(id);
|
|
963
|
-
|
|
930
|
+
if (!note) return declared.has(id);
|
|
931
|
+
if (note.seedOrigin !== "seed") return false;
|
|
932
|
+
const otherRank = maxOtherRankForNote(id);
|
|
933
|
+
if (declared.has(id)) return incomingRank >= otherRank;
|
|
934
|
+
return otherRank < 0 && note.seedChunkId === write.chunkId;
|
|
964
935
|
});
|
|
965
936
|
const owned = new Set(ownedIds);
|
|
966
937
|
const revisionStore = tx.readwrite("block_revisions");
|
|
967
938
|
const historyIds = ids.filter((id, i) => owned.has(id) && existingById.get(id)?.rawContent !== write.rows[i].content);
|
|
968
939
|
const histories = await revisionStore.getAllFromIndexMany("by-note", historyIds);
|
|
969
940
|
const history = new Map(historyIds.map((id, i) => [id, histories[i]]));
|
|
970
|
-
const indexStore = tx.readwrite("block_index"), effortLinks = tx.readwrite("block_efforts");
|
|
941
|
+
const indexStore = tx.readwrite("block_index"), effortLinks = tx.readwrite("block_efforts"), equipmentLinks = tx.readwrite("block_equipment");
|
|
971
942
|
const tagLinks = tx.readwrite("note_tags"), pageLinks = tx.readwrite("page_notes");
|
|
972
943
|
const currentIndexes = await indexStore.getAllFromIndexMany("by-note", ownedIds);
|
|
973
944
|
const currentEfforts = await effortLinks.getAllFromIndexMany("by-note", ownedIds);
|
|
945
|
+
const currentEquipment = await equipmentLinks.getAllFromIndexMany("by-note", ownedIds);
|
|
974
946
|
const currentTags = await tagLinks.getAllFromIndexMany("by-note", ownedIds);
|
|
975
947
|
const currentPages = await pageLinks.getAllFromIndexMany("by-note", ownedIds);
|
|
976
948
|
const slot = new Map(ownedIds.map((id, i) => [id, i]));
|
|
@@ -979,11 +951,11 @@ async function applySeedChunk(storage, write) {
|
|
|
979
951
|
const matches = await tagsStore.getAllFromIndexMany("by-label", labels);
|
|
980
952
|
const tagsByLabel = new Map(labels.map((label, i) => [label, matches[i][0]]));
|
|
981
953
|
const changedTags = [], changedNotes = [], revisions = [], pages = [], pageNotes = [], noteTags = [];
|
|
982
|
-
const blocks = [], blockEfforts = [];
|
|
954
|
+
const blocks = [], blockEfforts = [], blockEquipment = [];
|
|
983
955
|
for (const [i, row] of write.rows.entries()) {
|
|
984
956
|
const id = ids[i], existing = existingById.get(id);
|
|
985
957
|
if (!owned.has(id)) {
|
|
986
|
-
result.skippedUserOwned++;
|
|
958
|
+
if (existing && existing.seedOrigin !== "seed") result.skippedUserOwned++;
|
|
987
959
|
continue;
|
|
988
960
|
}
|
|
989
961
|
if (!existing) result.addedNotes++;
|
|
@@ -1035,6 +1007,13 @@ async function applySeedChunk(storage, write) {
|
|
|
1035
1007
|
const prior = currentEfforts[slot.get(id)].find((b) => b.id === link.id);
|
|
1036
1008
|
if (!prior || prior.isStatic === true) blockEfforts.push(prior ? { ...link, createdAt: prior.createdAt } : link);
|
|
1037
1009
|
}
|
|
1010
|
+
for (const compiled of row.blockEquipment) {
|
|
1011
|
+
const blockId = remap.get(compiled.blockId);
|
|
1012
|
+
if (!blockId || compiledById.get(compiled.blockId)?.dataType !== "wod") throw new Error("Orphan seed equipment link");
|
|
1013
|
+
const link = { ...compiled, id: `${id}:${blockId}:${compiled.equipmentSlug}`, noteId: id, blockId, sourcePath: row.path };
|
|
1014
|
+
const prior = currentEquipment[slot.get(id)].find((b) => b.id === link.id);
|
|
1015
|
+
if (!prior || prior.isStatic === true) blockEquipment.push(prior ? { ...link, createdAt: prior.createdAt } : link);
|
|
1016
|
+
}
|
|
1038
1017
|
const n = slot.get(id);
|
|
1039
1018
|
const wantedTags = /* @__PURE__ */ new Set();
|
|
1040
1019
|
for (const item of extractTypedTags(row.content)) {
|
|
@@ -1063,9 +1042,10 @@ async function applySeedChunk(storage, write) {
|
|
|
1063
1042
|
result.totalNotes++;
|
|
1064
1043
|
result.totalBlocks += Object.keys(map).length;
|
|
1065
1044
|
}
|
|
1066
|
-
const wantedBlocks = new Set(blocks.map((b) => b.id)), wantedEfforts = new Set(blockEfforts.map((b) => b.id));
|
|
1045
|
+
const wantedBlocks = new Set(blocks.map((b) => b.id)), wantedEfforts = new Set(blockEfforts.map((b) => b.id)), wantedEquipment = new Set(blockEquipment.map((b) => b.id));
|
|
1067
1046
|
for (const rows of currentIndexes) for (const row of rows) if (row.isStatic === true && !wantedBlocks.has(row.id)) await indexStore.delete(row.id);
|
|
1068
1047
|
for (const rows of currentEfforts) for (const row of rows) if (row.isStatic === true && !wantedEfforts.has(row.id)) await effortLinks.delete(row.id);
|
|
1048
|
+
for (const rows of currentEquipment) for (const row of rows) if (row.isStatic === true && !wantedEquipment.has(row.id)) await equipmentLinks.delete(row.id);
|
|
1069
1049
|
for (const id of goneIds) {
|
|
1070
1050
|
if (!owned.has(id) || !existingById.get(id)) continue;
|
|
1071
1051
|
const n = slot.get(id);
|
|
@@ -1074,25 +1054,17 @@ async function applySeedChunk(storage, write) {
|
|
|
1074
1054
|
for (const link of currentPages[n]) await pageLinks.delete(link.id);
|
|
1075
1055
|
result.deleted++;
|
|
1076
1056
|
}
|
|
1077
|
-
const effortStore = tx.readwrite("efforts");
|
|
1078
1057
|
if (write.chunkId === EFFORTS_CHUNK_ID) {
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
result.totalEfforts = changed.length;
|
|
1084
|
-
const wantedSlugs = new Set(efforts.map((effort) => effort.slug));
|
|
1085
|
-
const removed = allIds.map((id) => {
|
|
1086
|
-
const note = existingById.get(id);
|
|
1087
|
-
return owned.has(id) && note ? parseEffortFile(note.rawContent)?.slug : void 0;
|
|
1088
|
-
}).filter((slug) => !!slug && !wantedSlugs.has(slug));
|
|
1089
|
-
const old = await effortStore.getMany(removed);
|
|
1090
|
-
for (const [i, effort] of old.entries()) if (effort?.registrySource === "bundled") await effortStore.delete(removed[i]);
|
|
1058
|
+
result.totalEfforts = await applyDefinitionChunk(tx.readwrite("efforts"), noteStore, wodWikiLang.parseEffortFile, "efforts", seedSource, incomingRank, otherCheckpoints, priorIds, write.rows, ids, owned, existingById, allIds);
|
|
1059
|
+
}
|
|
1060
|
+
if (write.chunkId === EQUIPMENT_CHUNK_ID) {
|
|
1061
|
+
result.totalEquipment = await applyDefinitionChunk(tx.readwrite("equipment"), noteStore, wodWikiLang.parseEquipmentFile, "equipment", seedSource, incomingRank, otherCheckpoints, priorIds, write.rows, ids, owned, existingById, allIds);
|
|
1091
1062
|
}
|
|
1092
1063
|
if (changedNotes.length) await noteStore.putMany(changedNotes);
|
|
1093
1064
|
if (revisions.length) await revisionStore.putMany(revisions);
|
|
1094
1065
|
await putChanged(indexStore, blocks, blocks.map((b) => b.id));
|
|
1095
1066
|
await putChanged(effortLinks, blockEfforts, blockEfforts.map((b) => b.id));
|
|
1067
|
+
await putChanged(equipmentLinks, blockEquipment, blockEquipment.map((b) => b.id));
|
|
1096
1068
|
if (changedTags.length) await tagsStore.putMany([...new Map(changedTags.map((t) => [t.id, t])).values()]);
|
|
1097
1069
|
if (noteTags.length) await tagLinks.putMany(noteTags);
|
|
1098
1070
|
await putChanged(tx.readwrite("page"), pages, pages.map((p) => p.id));
|
|
@@ -1142,6 +1114,10 @@ Object.defineProperty(exports, "ButtonBehavior", {
|
|
|
1142
1114
|
enumerable: true,
|
|
1143
1115
|
get: function () { return wodWikiLang.ButtonBehavior; }
|
|
1144
1116
|
});
|
|
1117
|
+
Object.defineProperty(exports, "CATALOG_PRECEDENCE_RANK", {
|
|
1118
|
+
enumerable: true,
|
|
1119
|
+
get: function () { return wodWikiLang.CATALOG_PRECEDENCE_RANK; }
|
|
1120
|
+
});
|
|
1145
1121
|
Object.defineProperty(exports, "CONSUMED_HINTS", {
|
|
1146
1122
|
enumerable: true,
|
|
1147
1123
|
get: function () { return wodWikiLang.CONSUMED_HINTS; }
|
|
@@ -1542,6 +1518,10 @@ Object.defineProperty(exports, "createParser", {
|
|
|
1542
1518
|
enumerable: true,
|
|
1543
1519
|
get: function () { return wodWikiLang.createParser; }
|
|
1544
1520
|
});
|
|
1521
|
+
Object.defineProperty(exports, "dedupeEntitiesByPrecedence", {
|
|
1522
|
+
enumerable: true,
|
|
1523
|
+
get: function () { return wodWikiLang.dedupeEntitiesByPrecedence; }
|
|
1524
|
+
});
|
|
1545
1525
|
Object.defineProperty(exports, "dialectRegistry", {
|
|
1546
1526
|
enumerable: true,
|
|
1547
1527
|
get: function () { return wodWikiLang.dialectRegistry; }
|
|
@@ -1674,6 +1654,22 @@ Object.defineProperty(exports, "renderMetric", {
|
|
|
1674
1654
|
enumerable: true,
|
|
1675
1655
|
get: function () { return wodWikiLang.renderMetric; }
|
|
1676
1656
|
});
|
|
1657
|
+
Object.defineProperty(exports, "resolveEntityRank", {
|
|
1658
|
+
enumerable: true,
|
|
1659
|
+
get: function () { return wodWikiLang.resolveEntityRank; }
|
|
1660
|
+
});
|
|
1661
|
+
Object.defineProperty(exports, "resolveEntityTier", {
|
|
1662
|
+
enumerable: true,
|
|
1663
|
+
get: function () { return wodWikiLang.resolveEntityTier; }
|
|
1664
|
+
});
|
|
1665
|
+
Object.defineProperty(exports, "resolveSourceRank", {
|
|
1666
|
+
enumerable: true,
|
|
1667
|
+
get: function () { return wodWikiLang.resolveSourceRank; }
|
|
1668
|
+
});
|
|
1669
|
+
Object.defineProperty(exports, "resolveSourceTier", {
|
|
1670
|
+
enumerable: true,
|
|
1671
|
+
get: function () { return wodWikiLang.resolveSourceTier; }
|
|
1672
|
+
});
|
|
1677
1673
|
Object.defineProperty(exports, "strategyRegistry", {
|
|
1678
1674
|
enumerable: true,
|
|
1679
1675
|
get: function () { return wodWikiLang.strategyRegistry; }
|
|
@@ -1994,9 +1990,11 @@ exports.BUILD_TIME = BUILD_TIME;
|
|
|
1994
1990
|
exports.CANVAS_CHUNK_ID = CANVAS_CHUNK_ID;
|
|
1995
1991
|
exports.DASHBOARDS_CHUNK_ID = DASHBOARDS_CHUNK_ID;
|
|
1996
1992
|
exports.EFFORTS_CHUNK_ID = EFFORTS_CHUNK_ID;
|
|
1993
|
+
exports.EQUIPMENT_CHUNK_ID = EQUIPMENT_CHUNK_ID;
|
|
1997
1994
|
exports.GIT_SHA = GIT_SHA;
|
|
1998
1995
|
exports.NOTE_ID_FRONTMATTER_KEY = NOTE_ID_FRONTMATTER_KEY;
|
|
1999
1996
|
exports.SEED_BROADCAST_CHANNEL = SEED_BROADCAST_CHANNEL;
|
|
1997
|
+
exports.SEED_PRECEDENCE_RANK = SEED_PRECEDENCE_RANK;
|
|
2000
1998
|
exports.SEED_REGISTERED_WIDGETS = SEED_REGISTERED_WIDGETS;
|
|
2001
1999
|
exports.SEED_SCHEMA = SEED_SCHEMA;
|
|
2002
2000
|
exports.SEMVER = SEMVER;
|
|
@@ -2017,6 +2015,7 @@ exports.defineLanguagePack = defineLanguagePack;
|
|
|
2017
2015
|
exports.effortToMarkdown = effortToMarkdown;
|
|
2018
2016
|
exports.emptySeedMeta = emptySeedMeta;
|
|
2019
2017
|
exports.equipmentToCsv = equipmentToCsv;
|
|
2018
|
+
exports.equipmentToMarkdown = equipmentToMarkdown;
|
|
2020
2019
|
exports.extractNoteIdentity = extractNoteIdentity;
|
|
2021
2020
|
exports.extractTypedTags = extractTypedTags;
|
|
2022
2021
|
exports.getRegisteredLanguagePacks = getRegisteredLanguagePacks;
|
|
@@ -2024,13 +2023,14 @@ exports.inMemoryEventStore = inMemoryEventStore;
|
|
|
2024
2023
|
exports.injectIdentityFrontmatter = injectIdentityFrontmatter;
|
|
2025
2024
|
exports.isIRFile = isIRFile;
|
|
2026
2025
|
exports.listLanguagePacks = listLanguagePacks;
|
|
2027
|
-
exports.markdownToEffort = markdownToEffort;
|
|
2028
2026
|
exports.parseCsvLine = parseCsvLine;
|
|
2029
2027
|
exports.readFrontmatterMeta = readFrontmatterMeta;
|
|
2030
2028
|
exports.registerLanguagePack = registerLanguagePack;
|
|
2031
2029
|
exports.seedMetaKey = seedMetaKey;
|
|
2032
2030
|
exports.seedNoteId = seedNoteId;
|
|
2033
2031
|
exports.seedNoteSourceId = seedNoteSourceId;
|
|
2032
|
+
exports.seedPrecedenceRank = seedPrecedenceRank;
|
|
2033
|
+
exports.seedSourceTier = seedSourceTier;
|
|
2034
2034
|
exports.sessionToCsv = sessionToCsv;
|
|
2035
2035
|
exports.splitCsvRecords = splitCsvRecords;
|
|
2036
2036
|
exports.statementToNode = statementToNode;
|