@bitcobblers/wod-wiki-core 0.35.2116 → 0.35.2119
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 +217 -360
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -69
- package/dist/index.d.ts +109 -69
- package/dist/index.mjs +213 -359
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -485,6 +485,7 @@ var bySpelling = (() => {
|
|
|
485
485
|
return map;
|
|
486
486
|
})();
|
|
487
487
|
function lookupUnit(spelling) {
|
|
488
|
+
if (typeof spelling !== "string") return void 0;
|
|
488
489
|
return bySpelling.get(spelling.trim().toLowerCase());
|
|
489
490
|
}
|
|
490
491
|
function dimensionKeyOf(spelling) {
|
|
@@ -978,393 +979,246 @@ var toNotebookTag = (notebookId) => `${NOTEBOOK_TAG_PREFIX}${notebookId}`;
|
|
|
978
979
|
var fromNotebookTag = (tag) => tag.startsWith(NOTEBOOK_TAG_PREFIX) ? tag.slice(NOTEBOOK_TAG_PREFIX.length) : null;
|
|
979
980
|
var isNotebookTag = (tag) => tag.startsWith(NOTEBOOK_TAG_PREFIX);
|
|
980
981
|
|
|
981
|
-
// src/sections/
|
|
982
|
+
// src/sections/sectionParser.ts
|
|
983
|
+
var KNOWN_SECTION_WIDGETS = ["playground-run-tip", "attention", "code-example", "syntax-group"];
|
|
982
984
|
function matchDialectFence(trimmedLine) {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
if (VALID_FENCE_DIALECTS.includes(base)) {
|
|
987
|
-
return { dialect: base, sport: sport || void 0 };
|
|
988
|
-
}
|
|
989
|
-
return null;
|
|
985
|
+
const match = trimmedLine.match(/^`{3,}(time|log)(?::([\w-]+))?\s*$/i);
|
|
986
|
+
if (!match) return null;
|
|
987
|
+
return { dialect: match[1].toLowerCase() === "time" ? "time" : "log", ...match[2] ? { sport: match[2] } : {} };
|
|
990
988
|
}
|
|
991
|
-
function
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
989
|
+
function frontmatterSubtype(properties) {
|
|
990
|
+
const type = properties.type?.toLowerCase();
|
|
991
|
+
if (type === "youtube" || type === "strava" || type === "amazon" || type === "file" || type === "effort") return type;
|
|
992
|
+
const url = properties.url || properties.link || "";
|
|
993
|
+
const host = url.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "").split(/[/?#:]/, 1)[0].toLowerCase();
|
|
994
|
+
if (host === "youtu.be" || host === "youtube.com" || host.endsWith(".youtube.com")) return "youtube";
|
|
995
|
+
if (host === "strava.com" || host.endsWith(".strava.com")) return "strava";
|
|
996
|
+
if (host === "amzn.to" || host === "amazon.com" || host.endsWith(".amazon.com")) return "amazon";
|
|
997
|
+
if (["baseAttributes", "discipline", "effortType", "targetRPE", "equipment", "aliases", "derivation"].some((key) => key in properties)) return "effort";
|
|
998
|
+
return "default";
|
|
999
|
+
}
|
|
1000
|
+
function markdownSubtype(text) {
|
|
1001
|
+
if (/^#{1,6}\s/.test(text)) return "heading";
|
|
1002
|
+
if (/^[-*+]\s|^\d+\.\s/.test(text)) return "list";
|
|
1003
|
+
if (/^>\s?/.test(text)) return "blockquote";
|
|
1004
|
+
if (text.startsWith("|") && /^\|[\s:]*-+/m.test(text)) return "table";
|
|
1005
|
+
return "paragraph";
|
|
1006
|
+
}
|
|
1007
|
+
function parseDocumentSections(content, options = {}) {
|
|
1008
|
+
if (!content) return [];
|
|
1009
|
+
const lines = content.split(/\r\n|\n|\r/);
|
|
1010
|
+
const offsets = [];
|
|
1011
|
+
const separators = [];
|
|
1012
|
+
let offset = 0;
|
|
1013
|
+
for (const text of lines) {
|
|
1014
|
+
offsets.push(offset);
|
|
1015
|
+
offset += text.length;
|
|
1016
|
+
const separator = content.startsWith("\r\n", offset) ? "\r\n" : /[\r\n]/.test(content[offset] ?? "") ? content[offset] : "";
|
|
1017
|
+
separators.push(separator);
|
|
1018
|
+
offset += separator.length;
|
|
1019
|
+
}
|
|
1020
|
+
const sections = [];
|
|
1021
|
+
const createdAt = Date.now();
|
|
1022
|
+
let firstText = true;
|
|
1023
|
+
let line = 0;
|
|
1024
|
+
function make(type, start, end) {
|
|
1025
|
+
const from = offsets[start];
|
|
1026
|
+
const to = offsets[end] + lines[end].length;
|
|
1027
|
+
return {
|
|
1028
|
+
id: crypto.randomUUID(),
|
|
1029
|
+
type,
|
|
1030
|
+
from,
|
|
1031
|
+
to,
|
|
1032
|
+
rawContent: content.slice(from, to),
|
|
1033
|
+
displayContent: content.slice(from, to),
|
|
1034
|
+
startLine: start,
|
|
1035
|
+
endLine: end,
|
|
1036
|
+
lineCount: end - start + 1,
|
|
1037
|
+
version: 1,
|
|
1038
|
+
createdAt,
|
|
1039
|
+
separatorAfter: separators[end]
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
while (line < lines.length) {
|
|
1043
|
+
const text = lines[line].trim();
|
|
1044
|
+
const fence = text.match(/^(`{3,}|~{3,})(.*)$/);
|
|
1045
|
+
if (fence) {
|
|
1046
|
+
const marker = fence[1];
|
|
1047
|
+
let close = line + 1;
|
|
1048
|
+
const closing = new RegExp(`^${marker[0]}{${marker.length},}\\s*$`);
|
|
1049
|
+
while (close < lines.length && !closing.test(lines[close].trim())) close++;
|
|
1050
|
+
const closed = close < lines.length;
|
|
1051
|
+
const end2 = closed ? close : lines.length - 1;
|
|
1052
|
+
const tag = fence[2].trim();
|
|
1053
|
+
const dialect = marker[0] === "`" ? matchDialectFence(`\`\`\`${tag}`) : null;
|
|
1054
|
+
const widget = marker[0] === "`" ? tag.match(/^widget:([\w-]+)$/) : null;
|
|
1055
|
+
const query = marker[0] === "`" ? tag.match(/^query(?:[:\s](.*))?$/i) : null;
|
|
1056
|
+
const names = options.registeredWidgets ?? KNOWN_SECTION_WIDGETS;
|
|
1057
|
+
const knownWidget = widget && ("has" in names ? names.has(widget[1]) : names.includes(widget[1]));
|
|
1058
|
+
const type = closed && dialect ? dialect.dialect : closed && knownWidget ? "widget" : closed && query ? "query" : "code";
|
|
1059
|
+
const section2 = make(type, line, end2);
|
|
1060
|
+
section2.contentFrom = offsets[line] + lines[line].length + separators[line].length;
|
|
1061
|
+
section2.contentTo = closed ? offsets[close] : section2.to;
|
|
1062
|
+
section2.displayContent = content.slice(section2.contentFrom, section2.contentTo);
|
|
1063
|
+
if (dialect && closed) {
|
|
1064
|
+
section2.sport = dialect.sport;
|
|
1065
|
+
section2.contentId = blockContentId(section2.displayContent);
|
|
1066
|
+
section2.scriptBlock = {
|
|
1067
|
+
id: section2.id,
|
|
1068
|
+
contentId: section2.contentId,
|
|
1069
|
+
dialect: dialect.dialect,
|
|
1070
|
+
sport: dialect.sport,
|
|
1071
|
+
startLine: line,
|
|
1072
|
+
endLine: end2,
|
|
1073
|
+
content: section2.displayContent,
|
|
1009
1074
|
state: "idle",
|
|
1010
1075
|
version: 1,
|
|
1011
|
-
createdAt
|
|
1076
|
+
createdAt
|
|
1012
1077
|
};
|
|
1013
|
-
blockContent = [];
|
|
1014
|
-
return;
|
|
1015
1078
|
}
|
|
1079
|
+
if (type === "widget" && widget) section2.widgetName = widget[1];
|
|
1080
|
+
if (type === "query" && query) section2.querySuffix = (query[1] ?? "").trim();
|
|
1081
|
+
sections.push(section2);
|
|
1082
|
+
line = end2 + 1;
|
|
1083
|
+
continue;
|
|
1016
1084
|
}
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1085
|
+
if (text === "---" && lines[line + 1]?.trim()) {
|
|
1086
|
+
let close = line + 1;
|
|
1087
|
+
while (close < lines.length && lines[close].trim() !== "---" && !/^(`{3,}|~{3,})/.test(lines[close].trim())) close++;
|
|
1088
|
+
const inner = lines.slice(line + 1, close);
|
|
1089
|
+
if (lines[close]?.trim() === "---" && inner.some((value) => /^[A-Za-z_][\w-]*\s*:/.test(value.trim()))) {
|
|
1090
|
+
const section2 = make("frontmatter", line, close);
|
|
1091
|
+
section2.contentFrom = offsets[line + 1];
|
|
1092
|
+
section2.contentTo = offsets[close];
|
|
1093
|
+
section2.displayContent = content.slice(section2.contentFrom, section2.contentTo);
|
|
1094
|
+
section2.properties = {};
|
|
1095
|
+
for (const value of inner) {
|
|
1096
|
+
const pair = value.match(/^([^:]+):\s*(.*)$/);
|
|
1097
|
+
if (pair) section2.properties[pair[1].trim()] = pair[2].trim();
|
|
1098
|
+
}
|
|
1099
|
+
section2.frontmatterType = frontmatterSubtype(section2.properties);
|
|
1100
|
+
sections.push(section2);
|
|
1101
|
+
line = close + 1;
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1026
1104
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
(
|
|
1038
|
-
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
// src/sections/sectionParser.ts
|
|
1046
|
-
var METADATA_REGEX = /<!--\s*section-metadata\s+id:(\S+)\s+version:(\d+)\s+created:(\d+)\s*-->/i;
|
|
1047
|
-
function extractMetadata(text) {
|
|
1048
|
-
const match = text.match(METADATA_REGEX);
|
|
1049
|
-
if (!match) return { metadata: null, cleanText: text };
|
|
1050
|
-
return {
|
|
1051
|
-
metadata: {
|
|
1052
|
-
id: match[1],
|
|
1053
|
-
version: parseInt(match[2], 10),
|
|
1054
|
-
createdAt: parseInt(match[3], 10)
|
|
1055
|
-
},
|
|
1056
|
-
cleanText: text.replace(METADATA_REGEX, "").trim()
|
|
1057
|
-
};
|
|
1058
|
-
}
|
|
1059
|
-
function generateSectionId(type, startLine, content) {
|
|
1060
|
-
let hash = 0;
|
|
1061
|
-
for (let i = 0; i < content.length && i < 64; i++) {
|
|
1062
|
-
hash = (hash << 5) - hash + content.charCodeAt(i) | 0;
|
|
1105
|
+
const embed = text.match(/^(!)?\[([^\]]*)\]\((https?:\/\/[^)\s]+|\/[^)\s]+)\)$/);
|
|
1106
|
+
if (embed) {
|
|
1107
|
+
const section2 = make("embed", line, line);
|
|
1108
|
+
const isImage = !!embed[1];
|
|
1109
|
+
section2.embed = { type: isImage ? "image" : /(?:youtube\.com|youtu\.be)/i.test(embed[3]) ? "youtube" : "link", label: embed[2], url: embed[3], isImage };
|
|
1110
|
+
sections.push(section2);
|
|
1111
|
+
line++;
|
|
1112
|
+
continue;
|
|
1113
|
+
}
|
|
1114
|
+
let end = line;
|
|
1115
|
+
if (text) {
|
|
1116
|
+
while (end + 1 < lines.length && lines[end + 1].trim() && !/^(?:`{3,}|~{3,}|---\s*$|!?\[[^\]]*\]\()/.test(lines[end + 1].trim())) end++;
|
|
1117
|
+
}
|
|
1118
|
+
const section = make(text && firstText ? "title" : "markdown", line, end);
|
|
1119
|
+
section.subtype = markdownSubtype(section.displayContent.trim());
|
|
1120
|
+
if (text) firstText = false;
|
|
1121
|
+
sections.push(section);
|
|
1122
|
+
line = end + 1;
|
|
1063
1123
|
}
|
|
1064
|
-
|
|
1065
|
-
return `${type}-${startLine}-${hashHex}`;
|
|
1124
|
+
return sections;
|
|
1066
1125
|
}
|
|
1067
|
-
function
|
|
1068
|
-
if (
|
|
1069
|
-
|
|
1070
|
-
const host = withoutScheme.split(/[/?#:]/, 1)[0].toLowerCase();
|
|
1071
|
-
if (host === "youtube.com" || host.endsWith(".youtube.com") || host === "youtu.be") return "youtube";
|
|
1072
|
-
if (host === "strava.com" || host.endsWith(".strava.com")) return "strava";
|
|
1073
|
-
if (host === "amazon.com" || host.endsWith(".amazon.com") || host === "amzn.to") return "amazon";
|
|
1126
|
+
function actionableSectionKind(section) {
|
|
1127
|
+
if (section.type === "time" || section.type === "log") return "wod";
|
|
1128
|
+
if (section.type === "query" || section.type === "widget" || section.type === "embed") return section.type;
|
|
1074
1129
|
return null;
|
|
1075
1130
|
}
|
|
1076
|
-
function
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1131
|
+
function reconcileNoteBlocks(rawContent, options) {
|
|
1132
|
+
if (!Number.isSafeInteger(options.sourceRevision) || options.sourceRevision < 1) throw new Error("Invalid source revision");
|
|
1133
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
1134
|
+
const lineage = Object.values(options.blocks ?? {}).filter((block) => block && typeof block === "object");
|
|
1135
|
+
const createId = options.createId ?? (() => crypto.randomUUID());
|
|
1136
|
+
for (const section of parseDocumentSections(rawContent, options)) {
|
|
1137
|
+
const kind = actionableSectionKind(section);
|
|
1138
|
+
if (!kind) continue;
|
|
1139
|
+
const matches = lineage.filter((block) => block.from === section.from && block.to === section.to && block.kind === kind);
|
|
1140
|
+
const prior = matches.length === 1 ? matches[0] : void 0;
|
|
1141
|
+
const id = prior?.id ?? createId();
|
|
1142
|
+
if (!id || Object.hasOwn(result, id)) throw new Error("Duplicate block occurrence ID");
|
|
1143
|
+
result[id] = {
|
|
1144
|
+
id,
|
|
1145
|
+
kind,
|
|
1146
|
+
from: section.from,
|
|
1147
|
+
to: section.to,
|
|
1148
|
+
sourceRevision: options.sourceRevision,
|
|
1149
|
+
version: prior?.version ?? 1,
|
|
1150
|
+
...kind === "wod" && (section.type === "time" || section.type === "log") ? { dialect: section.type, ...section.sport ? { sport: section.sport } : {} } : {}
|
|
1151
|
+
};
|
|
1090
1152
|
}
|
|
1091
|
-
return
|
|
1153
|
+
return result;
|
|
1092
1154
|
}
|
|
1093
|
-
function
|
|
1094
|
-
|
|
1095
|
-
for (const
|
|
1096
|
-
|
|
1097
|
-
if (match) {
|
|
1098
|
-
props[match[1].trim()] = match[2].trim();
|
|
1099
|
-
}
|
|
1155
|
+
function validateNoteBlocks(rawContent, blocks, sourceRevision, options = {}) {
|
|
1156
|
+
if (typeof rawContent !== "string" || !blocks || typeof blocks !== "object" || Array.isArray(blocks)) throw new Error("Invalid Note source or blocks");
|
|
1157
|
+
for (const [id, block] of Object.entries(blocks)) {
|
|
1158
|
+
if (!block || block.id !== id || !id || !Number.isSafeInteger(block.version) || block.version < 1 || block.sourceRevision !== sourceRevision || !Number.isSafeInteger(block.from) || !Number.isSafeInteger(block.to)) throw new Error("Invalid block identity, version or source revision");
|
|
1100
1159
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
const isImage = !!match[1];
|
|
1107
|
-
const label = match[2];
|
|
1108
|
-
const url = match[3];
|
|
1109
|
-
let type = isImage ? "image" : "link";
|
|
1110
|
-
if (/youtube\.com|youtu\.be/i.test(url)) {
|
|
1111
|
-
type = "youtube";
|
|
1112
|
-
}
|
|
1113
|
-
return { type, label, url, isImage };
|
|
1114
|
-
}
|
|
1115
|
-
function detectFrontMatterBlocks(lines, scriptBlocks) {
|
|
1116
|
-
const ranges = [];
|
|
1117
|
-
let i = 0;
|
|
1118
|
-
while (i < lines.length) {
|
|
1119
|
-
const inScript = scriptBlocks.some((b) => i >= b.startLine && i <= b.endLine);
|
|
1120
|
-
if (inScript) {
|
|
1121
|
-
i++;
|
|
1122
|
-
continue;
|
|
1123
|
-
}
|
|
1124
|
-
if (lines[i].trim() === "---") {
|
|
1125
|
-
const openLine = i;
|
|
1126
|
-
let j = i + 1;
|
|
1127
|
-
while (j < lines.length) {
|
|
1128
|
-
const inScriptInner = scriptBlocks.some((b) => j >= b.startLine && j <= b.endLine);
|
|
1129
|
-
if (inScriptInner) {
|
|
1130
|
-
j++;
|
|
1131
|
-
continue;
|
|
1132
|
-
}
|
|
1133
|
-
if (lines[j].trim() === "---") {
|
|
1134
|
-
ranges.push({ startLine: openLine, endLine: j });
|
|
1135
|
-
i = j + 1;
|
|
1136
|
-
break;
|
|
1137
|
-
}
|
|
1138
|
-
j++;
|
|
1139
|
-
}
|
|
1140
|
-
if (j >= lines.length) {
|
|
1141
|
-
i++;
|
|
1142
|
-
}
|
|
1143
|
-
} else {
|
|
1144
|
-
i++;
|
|
1145
|
-
}
|
|
1160
|
+
const expected = reconcileNoteBlocks(rawContent, { ...options, sourceRevision, blocks });
|
|
1161
|
+
if (Object.keys(expected).length !== Object.keys(blocks).length) throw new Error("Incomplete actionable block map");
|
|
1162
|
+
for (const [id, block] of Object.entries(blocks)) {
|
|
1163
|
+
const recognized = expected[id];
|
|
1164
|
+
if (!recognized || recognized.from !== block.from || recognized.to !== block.to || recognized.kind !== block.kind || recognized.dialect !== block.dialect || recognized.sport !== block.sport) throw new Error("Block does not match authored source");
|
|
1146
1165
|
}
|
|
1147
|
-
return ranges;
|
|
1148
1166
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
const { metadata, cleanText } = extractMetadata(raw);
|
|
1166
|
-
const trimmed = cleanText.trim();
|
|
1167
|
-
const embed = currentGroup.length === 1 ? matchMarkdownEmbed(trimmed) : null;
|
|
1168
|
-
if (embed) {
|
|
1169
|
-
sections.push({
|
|
1170
|
-
id: metadata?.id || generateSectionId("embed", groupStartLine, trimmed),
|
|
1171
|
-
type: "embed",
|
|
1172
|
-
rawContent: cleanText,
|
|
1173
|
-
displayContent: cleanText,
|
|
1174
|
-
startLine: groupStartLine,
|
|
1175
|
-
endLine: groupStartLine,
|
|
1176
|
-
lineCount: 1,
|
|
1177
|
-
embed,
|
|
1178
|
-
version: metadata?.version || 1,
|
|
1179
|
-
createdAt: metadata?.createdAt || now
|
|
1180
|
-
});
|
|
1181
|
-
} else if (isFirstTextBlock) {
|
|
1182
|
-
isFirstTextBlock = false;
|
|
1183
|
-
sections.push({
|
|
1184
|
-
id: metadata?.id || generateSectionId("title", groupStartLine, cleanText),
|
|
1185
|
-
type: "title",
|
|
1186
|
-
rawContent: cleanText,
|
|
1187
|
-
displayContent: cleanText,
|
|
1188
|
-
startLine: groupStartLine,
|
|
1189
|
-
endLine: groupStartLine + currentGroup.length - 1,
|
|
1190
|
-
lineCount: currentGroup.length,
|
|
1191
|
-
version: metadata?.version || 1,
|
|
1192
|
-
createdAt: metadata?.createdAt || now
|
|
1193
|
-
});
|
|
1194
|
-
} else {
|
|
1195
|
-
sections.push({
|
|
1196
|
-
id: metadata?.id || generateSectionId("markdown", groupStartLine, cleanText),
|
|
1197
|
-
type: "markdown",
|
|
1198
|
-
rawContent: cleanText,
|
|
1199
|
-
displayContent: cleanText,
|
|
1200
|
-
startLine: groupStartLine,
|
|
1201
|
-
endLine: groupStartLine + currentGroup.length - 1,
|
|
1202
|
-
lineCount: currentGroup.length,
|
|
1203
|
-
version: metadata?.version || 1,
|
|
1204
|
-
createdAt: metadata?.createdAt || now
|
|
1205
|
-
});
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
currentGroup = [];
|
|
1209
|
-
};
|
|
1210
|
-
for (let i = 0; i < mdLines.length; i++) {
|
|
1211
|
-
const line = mdLines[i];
|
|
1212
|
-
if (line.trim().length === 0) {
|
|
1213
|
-
flushGroup();
|
|
1214
|
-
sections.push({
|
|
1215
|
-
id: generateSectionId("markdown", startLine + i, ""),
|
|
1216
|
-
type: "markdown",
|
|
1217
|
-
rawContent: "",
|
|
1218
|
-
displayContent: "",
|
|
1219
|
-
startLine: startLine + i,
|
|
1220
|
-
endLine: startLine + i,
|
|
1221
|
-
lineCount: 1,
|
|
1222
|
-
version: 1,
|
|
1223
|
-
createdAt: now
|
|
1224
|
-
});
|
|
1225
|
-
groupStartLine = startLine + i + 1;
|
|
1226
|
-
} else {
|
|
1227
|
-
currentGroup.push(line);
|
|
1167
|
+
function mapNoteBlockChanges(blocks, changes) {
|
|
1168
|
+
let previousEnd = 0;
|
|
1169
|
+
for (const change of changes) {
|
|
1170
|
+
if (!Number.isSafeInteger(change.from) || !Number.isSafeInteger(change.to) || change.from < previousEnd || change.to < change.from || typeof change.insert !== "string") throw new Error("Invalid source changes");
|
|
1171
|
+
previousEnd = change.to;
|
|
1172
|
+
}
|
|
1173
|
+
const result = {};
|
|
1174
|
+
for (const block of Object.values(blocks)) {
|
|
1175
|
+
let from = block.from;
|
|
1176
|
+
let to = block.to;
|
|
1177
|
+
let removed = false;
|
|
1178
|
+
for (const change of changes) {
|
|
1179
|
+
const delta = change.insert.length - (change.to - change.from);
|
|
1180
|
+
if (change.from <= block.from && change.to >= block.to || change.from < block.from && change.to > block.from || change.from < block.to && change.to > block.to) {
|
|
1181
|
+
removed = true;
|
|
1182
|
+
break;
|
|
1228
1183
|
}
|
|
1184
|
+
if (change.to <= block.from) {
|
|
1185
|
+
from += delta;
|
|
1186
|
+
to += delta;
|
|
1187
|
+
} else if (change.from < block.to) to += delta;
|
|
1229
1188
|
}
|
|
1230
|
-
|
|
1231
|
-
}
|
|
1232
|
-
let mdBuffer = [];
|
|
1233
|
-
let mdBufferStart = 0;
|
|
1234
|
-
while (currentLine < lines.length) {
|
|
1235
|
-
const scriptBlock = blocks.find((b) => b.startLine === currentLine);
|
|
1236
|
-
if (scriptBlock) {
|
|
1237
|
-
flushMarkdownLines(mdBuffer, mdBufferStart);
|
|
1238
|
-
mdBuffer = [];
|
|
1239
|
-
const dialect = scriptBlock.dialect ?? "time";
|
|
1240
|
-
const { metadata, cleanText } = extractMetadata(scriptBlock.content);
|
|
1241
|
-
const fenceTag = scriptBlock.sport ? `${dialect}:${scriptBlock.sport}` : dialect;
|
|
1242
|
-
const cleanRawContent = `\`\`\`${fenceTag}
|
|
1243
|
-
${cleanText}
|
|
1244
|
-
\`\`\``;
|
|
1245
|
-
const cleanLineCount = cleanRawContent.split("\n").length;
|
|
1246
|
-
const sectionId = metadata?.id || generateSectionId(dialect, scriptBlock.startLine, cleanText);
|
|
1247
|
-
const contentId = blockContentId(cleanText);
|
|
1248
|
-
sections.push({
|
|
1249
|
-
id: sectionId,
|
|
1250
|
-
contentId,
|
|
1251
|
-
type: dialect,
|
|
1252
|
-
sport: scriptBlock.sport,
|
|
1253
|
-
rawContent: cleanRawContent,
|
|
1254
|
-
displayContent: cleanText,
|
|
1255
|
-
startLine: scriptBlock.startLine,
|
|
1256
|
-
endLine: scriptBlock.startLine + cleanLineCount - 1,
|
|
1257
|
-
lineCount: cleanLineCount,
|
|
1258
|
-
scriptBlock: {
|
|
1259
|
-
...scriptBlock,
|
|
1260
|
-
id: sectionId,
|
|
1261
|
-
contentId,
|
|
1262
|
-
content: cleanText,
|
|
1263
|
-
dialect,
|
|
1264
|
-
version: metadata?.version || 1,
|
|
1265
|
-
createdAt: metadata?.createdAt || now
|
|
1266
|
-
},
|
|
1267
|
-
version: metadata?.version || 1,
|
|
1268
|
-
createdAt: metadata?.createdAt || now
|
|
1269
|
-
});
|
|
1270
|
-
currentLine = scriptBlock.endLine + 1;
|
|
1271
|
-
mdBufferStart = currentLine;
|
|
1272
|
-
continue;
|
|
1273
|
-
}
|
|
1274
|
-
const fmRange = fmRanges.find((r) => r.startLine === currentLine);
|
|
1275
|
-
if (fmRange) {
|
|
1276
|
-
flushMarkdownLines(mdBuffer, mdBufferStart);
|
|
1277
|
-
mdBuffer = [];
|
|
1278
|
-
const rawLines = lines.slice(fmRange.startLine, fmRange.endLine + 1);
|
|
1279
|
-
const raw = rawLines.join("\n");
|
|
1280
|
-
const innerLines = lines.slice(fmRange.startLine + 1, fmRange.endLine);
|
|
1281
|
-
const { metadata, cleanText } = extractMetadata(innerLines.join("\n"));
|
|
1282
|
-
const properties = parseFrontMatterProperties(cleanText.split("\n"));
|
|
1283
|
-
const subtype = resolveFrontMatterSubtype(properties);
|
|
1284
|
-
const lineCount = fmRange.endLine - fmRange.startLine + 1;
|
|
1285
|
-
sections.push({
|
|
1286
|
-
id: metadata?.id || generateSectionId("frontmatter", fmRange.startLine, raw),
|
|
1287
|
-
type: "frontmatter",
|
|
1288
|
-
rawContent: raw,
|
|
1289
|
-
displayContent: cleanText,
|
|
1290
|
-
startLine: fmRange.startLine,
|
|
1291
|
-
endLine: fmRange.endLine,
|
|
1292
|
-
lineCount,
|
|
1293
|
-
properties,
|
|
1294
|
-
frontmatterType: subtype,
|
|
1295
|
-
version: metadata?.version || 1,
|
|
1296
|
-
createdAt: metadata?.createdAt || now
|
|
1297
|
-
});
|
|
1298
|
-
currentLine = fmRange.endLine + 1;
|
|
1299
|
-
mdBufferStart = currentLine;
|
|
1300
|
-
continue;
|
|
1301
|
-
}
|
|
1302
|
-
if (mdBuffer.length === 0) {
|
|
1303
|
-
mdBufferStart = currentLine;
|
|
1304
|
-
}
|
|
1305
|
-
mdBuffer.push(lines[currentLine]);
|
|
1306
|
-
currentLine++;
|
|
1189
|
+
if (!removed) result[block.id] = { ...block, from, to };
|
|
1307
1190
|
}
|
|
1308
|
-
|
|
1309
|
-
return sections;
|
|
1191
|
+
return result;
|
|
1310
1192
|
}
|
|
1311
1193
|
function buildRawContent(sections) {
|
|
1312
|
-
return sections.filter((
|
|
1194
|
+
return sections.filter((section) => !section.deleted).map((section) => section.rawContent + (section.separatorAfter ?? "")).join("");
|
|
1313
1195
|
}
|
|
1314
1196
|
function calculateTotalLines(sections) {
|
|
1315
|
-
const visible = sections.filter((
|
|
1316
|
-
|
|
1317
|
-
const last = visible[visible.length - 1];
|
|
1318
|
-
return last.endLine + 1;
|
|
1197
|
+
const visible = sections.filter((section) => !section.deleted);
|
|
1198
|
+
return visible.length ? visible[visible.length - 1].endLine + 1 : 0;
|
|
1319
1199
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
);
|
|
1333
|
-
if (exactMatch) {
|
|
1334
|
-
const updated = { ...newSec, id: exactMatch.id, version: exactMatch.version, createdAt: exactMatch.createdAt };
|
|
1335
|
-
if (updated.scriptBlock) {
|
|
1336
|
-
updated.scriptBlock = { ...updated.scriptBlock, id: exactMatch.id };
|
|
1337
|
-
}
|
|
1338
|
-
return updated;
|
|
1339
|
-
}
|
|
1340
|
-
const positionMatch = oldSections.find(
|
|
1341
|
-
(old) => old.type === newSec.type && old.startLine === newSec.startLine
|
|
1342
|
-
);
|
|
1343
|
-
if (positionMatch) {
|
|
1344
|
-
const newVersion = (positionMatch.version || 1) + 1;
|
|
1345
|
-
const updated = {
|
|
1346
|
-
...newSec,
|
|
1347
|
-
id: positionMatch.id,
|
|
1348
|
-
version: newVersion,
|
|
1349
|
-
createdAt: positionMatch.createdAt
|
|
1350
|
-
};
|
|
1351
|
-
if (updated.scriptBlock) {
|
|
1352
|
-
updated.scriptBlock = { ...updated.scriptBlock, id: positionMatch.id, version: newVersion };
|
|
1353
|
-
}
|
|
1354
|
-
return updated;
|
|
1355
|
-
}
|
|
1356
|
-
return newSec;
|
|
1357
|
-
});
|
|
1200
|
+
|
|
1201
|
+
// src/sections/blockDetection.ts
|
|
1202
|
+
function detectScriptBlocks(content) {
|
|
1203
|
+
return parseDocumentSections(content).flatMap((section) => section.scriptBlock ? [section.scriptBlock] : []);
|
|
1204
|
+
}
|
|
1205
|
+
function findBlockAtLine(blocks, lineNumber) {
|
|
1206
|
+
return blocks.find(
|
|
1207
|
+
(block) => lineNumber >= block.startLine && lineNumber <= block.endLine
|
|
1208
|
+
) ?? null;
|
|
1209
|
+
}
|
|
1210
|
+
function extractBlockContent(content, startLine, endLine) {
|
|
1211
|
+
return parseDocumentSections(content).find((section) => section.startLine === startLine && section.endLine === endLine && section.scriptBlock)?.scriptBlock?.content ?? "";
|
|
1358
1212
|
}
|
|
1359
1213
|
|
|
1360
1214
|
// src/version.ts
|
|
1361
|
-
var VERSION = "0.35.
|
|
1362
|
-
var GIT_SHA = "
|
|
1363
|
-
var BUILD_TIME = "2026-10-
|
|
1215
|
+
var VERSION = "0.35.2119";
|
|
1216
|
+
var GIT_SHA = "e0eb840";
|
|
1217
|
+
var BUILD_TIME = "2026-10-13T06:24:52.520Z";
|
|
1364
1218
|
var SEMVER = {
|
|
1365
|
-
version: "0.35.
|
|
1366
|
-
gitSha: "
|
|
1367
|
-
buildTime: "2026-10-
|
|
1219
|
+
version: "0.35.2119",
|
|
1220
|
+
gitSha: "e0eb840",
|
|
1221
|
+
buildTime: "2026-10-13T06:24:52.520Z"
|
|
1368
1222
|
};
|
|
1369
1223
|
|
|
1370
1224
|
exports.BUILD_TIME = BUILD_TIME;
|
|
@@ -1373,6 +1227,7 @@ exports.CodeMetadata = CodeMetadata;
|
|
|
1373
1227
|
exports.CodeStatement = CodeStatement;
|
|
1374
1228
|
exports.GIT_SHA = GIT_SHA;
|
|
1375
1229
|
exports.KNOWN_OUTPUT_TYPES = KNOWN_OUTPUT_TYPES;
|
|
1230
|
+
exports.KNOWN_SECTION_WIDGETS = KNOWN_SECTION_WIDGETS;
|
|
1376
1231
|
exports.LEGACY_ORIGIN_TO_OWNERSHIP_LAYER = LEGACY_ORIGIN_TO_OWNERSHIP_LAYER;
|
|
1377
1232
|
exports.METRIC_OWNERSHIP_LAYER_CHAIN = METRIC_OWNERSHIP_LAYER_CHAIN;
|
|
1378
1233
|
exports.Metric = Metric;
|
|
@@ -1388,6 +1243,7 @@ exports.UNIT_CATALOG = UNIT_CATALOG;
|
|
|
1388
1243
|
exports.UnitConversionError = UnitConversionError;
|
|
1389
1244
|
exports.VALID_FENCE_DIALECTS = VALID_FENCE_DIALECTS;
|
|
1390
1245
|
exports.VERSION = VERSION;
|
|
1246
|
+
exports.actionableSectionKind = actionableSectionKind;
|
|
1391
1247
|
exports.blockContentId = blockContentId;
|
|
1392
1248
|
exports.buildRawContent = buildRawContent;
|
|
1393
1249
|
exports.calculateTotalLines = calculateTotalLines;
|
|
@@ -1401,21 +1257,22 @@ exports.fieldRefKey = fieldRefKey;
|
|
|
1401
1257
|
exports.findBlockAtLine = findBlockAtLine;
|
|
1402
1258
|
exports.flattenFieldLeaves = flattenFieldLeaves;
|
|
1403
1259
|
exports.fromNotebookTag = fromNotebookTag;
|
|
1404
|
-
exports.generateSectionId = generateSectionId;
|
|
1405
1260
|
exports.getMetricOwnershipLayer = getMetricOwnershipLayer;
|
|
1406
1261
|
exports.isNotebookTag = isNotebookTag;
|
|
1407
1262
|
exports.isWorkoutSectionType = isWorkoutSectionType;
|
|
1408
1263
|
exports.lookupUnit = lookupUnit;
|
|
1264
|
+
exports.mapNoteBlockChanges = mapNoteBlockChanges;
|
|
1409
1265
|
exports.matchDialectFence = matchDialectFence;
|
|
1410
|
-
exports.matchSectionIds = matchSectionIds;
|
|
1411
1266
|
exports.normalizeFieldComponent = normalizeFieldComponent;
|
|
1412
1267
|
exports.normalizeFieldPath = normalizeFieldPath;
|
|
1413
1268
|
exports.ownershipRank = ownershipRank;
|
|
1414
1269
|
exports.parseDocumentSections = parseDocumentSections;
|
|
1270
|
+
exports.reconcileNoteBlocks = reconcileNoteBlocks;
|
|
1415
1271
|
exports.resolveLayer = resolveLayer;
|
|
1416
1272
|
exports.runAffordance = runAffordance;
|
|
1417
1273
|
exports.systemDefaultUnit = systemDefaultUnit;
|
|
1418
1274
|
exports.toNotebookTag = toNotebookTag;
|
|
1275
|
+
exports.validateNoteBlocks = validateNoteBlocks;
|
|
1419
1276
|
exports.valueKindOf = valueKindOf;
|
|
1420
1277
|
//# sourceMappingURL=index.cjs.map
|
|
1421
1278
|
//# sourceMappingURL=index.cjs.map
|