@bitcobblers/wod-wiki-engine 0.37.2137 → 0.37.2139

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 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 scalarOf(value) {
301
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
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,19 @@ function csvToSessionRows(csv) {
647
617
  }
648
618
 
649
619
  // src/version.ts
650
- var VERSION = "0.37.2137";
651
- var GIT_SHA = "b82a721";
652
- var BUILD_TIME = "2026-10-16T20:56:02.597Z";
620
+ var VERSION = "0.37.2139";
621
+ var GIT_SHA = "491ec71";
622
+ var BUILD_TIME = "2026-10-17T15:36:01.834Z";
653
623
  var SEMVER = {
654
- version: "0.37.2137",
655
- gitSha: "b82a721",
656
- buildTime: "2026-10-16T20:56:02.597Z"
624
+ version: "0.37.2139",
625
+ gitSha: "491ec71",
626
+ buildTime: "2026-10-17T15:36:01.834Z"
657
627
  };
658
- var SEED_SCHEMA = 8;
628
+ var SEED_SCHEMA = 9;
659
629
  var SEED_REGISTERED_WIDGETS = wodWikiCore.KNOWN_SECTION_WIDGETS;
660
630
  var SEED_BROADCAST_CHANNEL = "wodwiki.seed";
661
631
  var EFFORTS_CHUNK_ID = "efforts";
632
+ var EQUIPMENT_CHUNK_ID = "equipment";
662
633
  var CANVAS_CHUNK_ID = "canvas";
663
634
  var DASHBOARDS_CHUNK_ID = "dashboards";
664
635
  var TEMPLATE_CHUNK_ID = "template";
@@ -684,6 +655,7 @@ function seedNoteSourceId(path, content, chunkId) {
684
655
  if (chunkId === CANVAS_CHUNK_ID) return `guides:${canvasRouteSlug(content) ?? under}`;
685
656
  if (chunkId === DASHBOARDS_CHUNK_ID) return "dashboards";
686
657
  if (chunkId === EFFORTS_CHUNK_ID) return "efforts";
658
+ if (chunkId === EQUIPMENT_CHUNK_ID) return "equipment";
687
659
  if (chunkId === TEMPLATE_CHUNK_ID) return "playground";
688
660
  if (chunkId.startsWith("collection.")) {
689
661
  const catalog = catalogForChunkId(chunkId);
@@ -692,141 +664,6 @@ function seedNoteSourceId(path, content, chunkId) {
692
664
  if (chunkId.startsWith("feed.")) return `feed:feeds/${under}`;
693
665
  return void 0;
694
666
  }
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
667
  function object(value) {
831
668
  return typeof value === "object" && value !== null && !Array.isArray(value);
832
669
  }
@@ -854,7 +691,7 @@ function assertSeedRows(value) {
854
691
  if (!Array.isArray(value)) throw new Error("seed chunk is not an array");
855
692
  const paths = /* @__PURE__ */ new Set();
856
693
  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");
694
+ 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
695
  paths.add(row.path);
859
696
  for (const [id, block] of Object.entries(row.blocks)) {
860
697
  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 +722,15 @@ function assertSeedRows(value) {
885
722
  if (!object(projection) || link.noteId !== projection.noteId || link.blockContentId !== projection.blockContentId || !Number.isFinite(link.createdAt)) throw new Error("invalid seed effort metadata");
886
723
  links.add(String(link.id));
887
724
  }
725
+ const equipmentLinks = /* @__PURE__ */ new Set();
726
+ for (const link of row.blockEquipment) {
727
+ if (!object(link) || typeof link.blockId !== "string" || typeof link.noteId !== "string" || typeof link.equipmentSlug !== "string" || !link.equipmentSlug) throw new Error("invalid seed equipment identity");
728
+ const block = row.blocks[link.blockId];
729
+ 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");
730
+ const projection = row.blockIndex.find((p) => object(p) && p.blockId === link.blockId);
731
+ if (!object(projection) || link.noteId !== projection.noteId || link.blockContentId !== projection.blockContentId || !Number.isFinite(link.createdAt)) throw new Error("invalid seed equipment metadata");
732
+ equipmentLinks.add(String(link.id));
733
+ }
888
734
  }
889
735
  return value;
890
736
  }
@@ -922,6 +768,40 @@ function refreshedBlocks(existing, rawContent, sourceRevision) {
922
768
  let ordinal = 0;
923
769
  return wodWikiCore.reconcileNoteBlocks(rawContent, { sourceRevision, blocks: mapped, registeredWidgets: SEED_REGISTERED_WIDGETS, createId: () => `seed:${existing.id}:r${sourceRevision}:${ordinal++}` });
924
770
  }
771
+ async function applyDefinitionChunk(defStore, parse, family, rows, ids, owned, existingById, allIds) {
772
+ const projections = [];
773
+ for (const [i, row] of rows.entries()) {
774
+ if (!owned.has(ids[i])) continue;
775
+ const parsed = parse(row.content);
776
+ if (!parsed) continue;
777
+ projections.push({ ...parsed, registrySource: "bundled", ownerNoteId: ids[i], sourceId: family, sourcePath: row.path });
778
+ }
779
+ const prior = await defStore.getMany(projections.map((p) => p.slug));
780
+ for (const [i, p] of projections.entries()) {
781
+ const before = prior[i];
782
+ if (before?.registrySource === "bundled" && before.ownerNoteId && before.ownerNoteId !== p.ownerNoteId) {
783
+ throw new Error(`Seed definition collision: '${p.slug}' is already owned by another seed source (note ${before.ownerNoteId})`);
784
+ }
785
+ }
786
+ const changed = projections.filter((p, i) => (!prior[i] || prior[i]?.registrySource === "bundled") && !equal(p, prior[i]));
787
+ if (changed.length) await defStore.putMany(changed);
788
+ const wantedSlugs = new Set(projections.map((p) => p.slug));
789
+ const removedBySlug = /* @__PURE__ */ new Map();
790
+ for (const id of allIds) {
791
+ if (!owned.has(id)) continue;
792
+ const note = existingById.get(id);
793
+ const slug = note ? parse(note.rawContent)?.slug : void 0;
794
+ if (slug && !wantedSlugs.has(slug)) removedBySlug.set(slug, id);
795
+ }
796
+ const removedSlugs = [...removedBySlug.keys()];
797
+ const old = await defStore.getMany(removedSlugs);
798
+ for (const [i, row] of old.entries()) {
799
+ if (row?.registrySource === "bundled" && removedBySlug.get(removedSlugs[i]) === row.ownerNoteId) {
800
+ await defStore.delete(removedSlugs[i]);
801
+ }
802
+ }
803
+ return changed.length;
804
+ }
925
805
  async function putChanged(store, values, keys) {
926
806
  if (!values.length) return;
927
807
  const prior = await store.getMany(keys);
@@ -931,7 +811,7 @@ async function putChanged(store, values, keys) {
931
811
  async function applySeedChunk(storage, write) {
932
812
  assertSeedManifest(write.manifest);
933
813
  assertSeedRows(write.rows);
934
- const result = { totalNotes: 0, totalEfforts: 0, totalBlocks: 0, skippedUserOwned: 0, deleted: 0, addedNotes: 0, updatedNotes: 0 };
814
+ const result = { totalNotes: 0, totalEfforts: 0, totalEquipment: 0, totalBlocks: 0, skippedUserOwned: 0, deleted: 0, addedNotes: 0, updatedNotes: 0 };
935
815
  const ids = await Promise.all(write.rows.map((row) => seedNoteId(row.path)));
936
816
  const pagesBySlug = /* @__PURE__ */ new Map();
937
817
  const linkIds = /* @__PURE__ */ new Map();
@@ -946,7 +826,7 @@ async function applySeedChunk(storage, write) {
946
826
  if (!tagIds.has(label)) tagIds.set(label, await seedNoteId(`tag:${label}`));
947
827
  }
948
828
  }
949
- return storage.transaction(["notes", "block_revisions", "block_index", "block_efforts", "page", "page_notes", "tags", "note_tags", "efforts", "meta"], "readwrite", async (tx) => {
829
+ 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
830
  const metaStore = tx.readwrite("meta");
951
831
  const actualMeta = await metaStore.get(write.meta.key);
952
832
  if (!equal(actualMeta, write.expectedMeta)) throw new Error("Seed checkpoint changed during import");
@@ -963,14 +843,34 @@ async function applySeedChunk(storage, write) {
963
843
  return !note || note.seedOrigin === "seed" && (declared.has(id) || note.seedChunkId === write.chunkId);
964
844
  });
965
845
  const owned = new Set(ownedIds);
846
+ {
847
+ let checkpoints;
848
+ for (const [i, id] of ids.entries()) {
849
+ const existing = existingById.get(id);
850
+ if (!existing || existing.seedOrigin !== "seed") continue;
851
+ if (checkpoints === void 0) {
852
+ checkpoints = [];
853
+ for (const row of await metaStore.getAll()) {
854
+ const meta = row;
855
+ if (!meta || typeof meta.key !== "string" || !meta.key.startsWith("seed:") || meta.key === write.meta.key) continue;
856
+ const noteIds = /* @__PURE__ */ new Set();
857
+ for (const chunk of Object.values(meta.chunks ?? {})) for (const noteId of chunk.noteIds ?? []) noteIds.add(noteId);
858
+ checkpoints.push({ key: meta.key, noteIds });
859
+ }
860
+ }
861
+ const owner = checkpoints.find((checkpoint) => checkpoint.noteIds.has(id));
862
+ if (owner) throw new Error(`Seed note collision: '${write.rows[i].path}' is already owned by ${owner.key}`);
863
+ }
864
+ }
966
865
  const revisionStore = tx.readwrite("block_revisions");
967
866
  const historyIds = ids.filter((id, i) => owned.has(id) && existingById.get(id)?.rawContent !== write.rows[i].content);
968
867
  const histories = await revisionStore.getAllFromIndexMany("by-note", historyIds);
969
868
  const history = new Map(historyIds.map((id, i) => [id, histories[i]]));
970
- const indexStore = tx.readwrite("block_index"), effortLinks = tx.readwrite("block_efforts");
869
+ const indexStore = tx.readwrite("block_index"), effortLinks = tx.readwrite("block_efforts"), equipmentLinks = tx.readwrite("block_equipment");
971
870
  const tagLinks = tx.readwrite("note_tags"), pageLinks = tx.readwrite("page_notes");
972
871
  const currentIndexes = await indexStore.getAllFromIndexMany("by-note", ownedIds);
973
872
  const currentEfforts = await effortLinks.getAllFromIndexMany("by-note", ownedIds);
873
+ const currentEquipment = await equipmentLinks.getAllFromIndexMany("by-note", ownedIds);
974
874
  const currentTags = await tagLinks.getAllFromIndexMany("by-note", ownedIds);
975
875
  const currentPages = await pageLinks.getAllFromIndexMany("by-note", ownedIds);
976
876
  const slot = new Map(ownedIds.map((id, i) => [id, i]));
@@ -979,7 +879,7 @@ async function applySeedChunk(storage, write) {
979
879
  const matches = await tagsStore.getAllFromIndexMany("by-label", labels);
980
880
  const tagsByLabel = new Map(labels.map((label, i) => [label, matches[i][0]]));
981
881
  const changedTags = [], changedNotes = [], revisions = [], pages = [], pageNotes = [], noteTags = [];
982
- const blocks = [], blockEfforts = [];
882
+ const blocks = [], blockEfforts = [], blockEquipment = [];
983
883
  for (const [i, row] of write.rows.entries()) {
984
884
  const id = ids[i], existing = existingById.get(id);
985
885
  if (!owned.has(id)) {
@@ -1035,6 +935,13 @@ async function applySeedChunk(storage, write) {
1035
935
  const prior = currentEfforts[slot.get(id)].find((b) => b.id === link.id);
1036
936
  if (!prior || prior.isStatic === true) blockEfforts.push(prior ? { ...link, createdAt: prior.createdAt } : link);
1037
937
  }
938
+ for (const compiled of row.blockEquipment) {
939
+ const blockId = remap.get(compiled.blockId);
940
+ if (!blockId || compiledById.get(compiled.blockId)?.dataType !== "wod") throw new Error("Orphan seed equipment link");
941
+ const link = { ...compiled, id: `${id}:${blockId}:${compiled.equipmentSlug}`, noteId: id, blockId, sourcePath: row.path };
942
+ const prior = currentEquipment[slot.get(id)].find((b) => b.id === link.id);
943
+ if (!prior || prior.isStatic === true) blockEquipment.push(prior ? { ...link, createdAt: prior.createdAt } : link);
944
+ }
1038
945
  const n = slot.get(id);
1039
946
  const wantedTags = /* @__PURE__ */ new Set();
1040
947
  for (const item of extractTypedTags(row.content)) {
@@ -1063,9 +970,10 @@ async function applySeedChunk(storage, write) {
1063
970
  result.totalNotes++;
1064
971
  result.totalBlocks += Object.keys(map).length;
1065
972
  }
1066
- const wantedBlocks = new Set(blocks.map((b) => b.id)), wantedEfforts = new Set(blockEfforts.map((b) => b.id));
973
+ 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
974
  for (const rows of currentIndexes) for (const row of rows) if (row.isStatic === true && !wantedBlocks.has(row.id)) await indexStore.delete(row.id);
1068
975
  for (const rows of currentEfforts) for (const row of rows) if (row.isStatic === true && !wantedEfforts.has(row.id)) await effortLinks.delete(row.id);
976
+ for (const rows of currentEquipment) for (const row of rows) if (row.isStatic === true && !wantedEquipment.has(row.id)) await equipmentLinks.delete(row.id);
1069
977
  for (const id of goneIds) {
1070
978
  if (!owned.has(id) || !existingById.get(id)) continue;
1071
979
  const n = slot.get(id);
@@ -1074,25 +982,17 @@ async function applySeedChunk(storage, write) {
1074
982
  for (const link of currentPages[n]) await pageLinks.delete(link.id);
1075
983
  result.deleted++;
1076
984
  }
1077
- const effortStore = tx.readwrite("efforts");
1078
985
  if (write.chunkId === EFFORTS_CHUNK_ID) {
1079
- const efforts = write.rows.filter((_row, i) => owned.has(ids[i])).map((r) => parseEffortFile(r.content)).filter((e) => e !== null).map((e) => ({ ...e, registrySource: "bundled" }));
1080
- const prior = await effortStore.getMany(efforts.map((e) => e.slug));
1081
- const changed = efforts.filter((e, i) => (!prior[i] || prior[i]?.registrySource === "bundled") && !equal(e, prior[i]));
1082
- if (changed.length) await effortStore.putMany(changed);
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]);
986
+ result.totalEfforts = await applyDefinitionChunk(tx.readwrite("efforts"), wodWikiLang.parseEffortFile, "efforts", write.rows, ids, owned, existingById, allIds);
987
+ }
988
+ if (write.chunkId === EQUIPMENT_CHUNK_ID) {
989
+ result.totalEquipment = await applyDefinitionChunk(tx.readwrite("equipment"), wodWikiLang.parseEquipmentFile, "equipment", write.rows, ids, owned, existingById, allIds);
1091
990
  }
1092
991
  if (changedNotes.length) await noteStore.putMany(changedNotes);
1093
992
  if (revisions.length) await revisionStore.putMany(revisions);
1094
993
  await putChanged(indexStore, blocks, blocks.map((b) => b.id));
1095
994
  await putChanged(effortLinks, blockEfforts, blockEfforts.map((b) => b.id));
995
+ await putChanged(equipmentLinks, blockEquipment, blockEquipment.map((b) => b.id));
1096
996
  if (changedTags.length) await tagsStore.putMany([...new Map(changedTags.map((t) => [t.id, t])).values()]);
1097
997
  if (noteTags.length) await tagLinks.putMany(noteTags);
1098
998
  await putChanged(tx.readwrite("page"), pages, pages.map((p) => p.id));
@@ -1994,6 +1894,7 @@ exports.BUILD_TIME = BUILD_TIME;
1994
1894
  exports.CANVAS_CHUNK_ID = CANVAS_CHUNK_ID;
1995
1895
  exports.DASHBOARDS_CHUNK_ID = DASHBOARDS_CHUNK_ID;
1996
1896
  exports.EFFORTS_CHUNK_ID = EFFORTS_CHUNK_ID;
1897
+ exports.EQUIPMENT_CHUNK_ID = EQUIPMENT_CHUNK_ID;
1997
1898
  exports.GIT_SHA = GIT_SHA;
1998
1899
  exports.NOTE_ID_FRONTMATTER_KEY = NOTE_ID_FRONTMATTER_KEY;
1999
1900
  exports.SEED_BROADCAST_CHANNEL = SEED_BROADCAST_CHANNEL;
@@ -2017,6 +1918,7 @@ exports.defineLanguagePack = defineLanguagePack;
2017
1918
  exports.effortToMarkdown = effortToMarkdown;
2018
1919
  exports.emptySeedMeta = emptySeedMeta;
2019
1920
  exports.equipmentToCsv = equipmentToCsv;
1921
+ exports.equipmentToMarkdown = equipmentToMarkdown;
2020
1922
  exports.extractNoteIdentity = extractNoteIdentity;
2021
1923
  exports.extractTypedTags = extractTypedTags;
2022
1924
  exports.getRegisteredLanguagePacks = getRegisteredLanguagePacks;
@@ -2024,7 +1926,6 @@ exports.inMemoryEventStore = inMemoryEventStore;
2024
1926
  exports.injectIdentityFrontmatter = injectIdentityFrontmatter;
2025
1927
  exports.isIRFile = isIRFile;
2026
1928
  exports.listLanguagePacks = listLanguagePacks;
2027
- exports.markdownToEffort = markdownToEffort;
2028
1929
  exports.parseCsvLine = parseCsvLine;
2029
1930
  exports.readFrontmatterMeta = readFrontmatterMeta;
2030
1931
  exports.registerLanguagePack = registerLanguagePack;