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