@bendyline/squisq 2.11.12 → 2.11.14

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.
@@ -6,7 +6,9 @@ import {
6
6
  import {
7
7
  formatFrontmatterYaml,
8
8
  getChildren,
9
- splitFrontmatterBlock
9
+ splitFrontmatterBlock,
10
+ stringifyHtmlNodes,
11
+ walkMarkdownTree
10
12
  } from "./chunk-X7BNDUNI.js";
11
13
 
12
14
  // src/markdown/stringify.ts
@@ -391,6 +393,103 @@ function tooLarge(maxBytes) {
391
393
  );
392
394
  }
393
395
 
396
+ // src/markdown/condenseMarkdown.ts
397
+ function cellSource(source) {
398
+ let text = trimPadding(source);
399
+ if (text.startsWith("|")) text = text.slice(1);
400
+ if (text.endsWith("|")) {
401
+ let backslashes = 0;
402
+ for (let i = text.length - 2; i >= 0 && text[i] === "\\"; i--) backslashes++;
403
+ if (backslashes % 2 === 0) text = text.slice(0, -1);
404
+ }
405
+ return trimPadding(text);
406
+ }
407
+ function trimPadding(source) {
408
+ let start = 0;
409
+ let end = source.length;
410
+ while (start < end && (source[start] === " " || source[start] === " ")) start++;
411
+ while (end > start && (source[end - 1] === " " || source[end - 1] === " ")) end--;
412
+ return source.slice(start, end);
413
+ }
414
+ function tableEdits(source, table) {
415
+ const edits = [];
416
+ for (const row of table.children) {
417
+ const start2 = row.position?.start.offset;
418
+ const end2 = row.position?.end.offset;
419
+ if (start2 === void 0 || end2 === void 0) throw new Error("Table row has no source span");
420
+ let boundary = start2;
421
+ const cells2 = [];
422
+ for (const cell of row.children) {
423
+ const a = cell.position?.start.offset;
424
+ const b = cell.position?.end.offset;
425
+ if (a !== boundary || b === void 0 || b < a) {
426
+ throw new Error("Table cell spans do not cover their row");
427
+ }
428
+ cells2.push(cellSource(source.slice(a, b)));
429
+ boundary = b;
430
+ }
431
+ if (boundary !== end2) throw new Error("Table cell spans do not cover their row");
432
+ edits.push({ start: start2, end: end2, text: `| ${cells2.join(" | ")} |` });
433
+ }
434
+ const headerEnd = table.children[0]?.position?.end.offset;
435
+ if (headerEnd === void 0) throw new Error("Table has no header span");
436
+ const start = source.indexOf("\n", headerEnd) + 1;
437
+ const newline = source.indexOf("\n", start);
438
+ let end = newline === -1 ? source.length : newline;
439
+ if (source[end - 1] === "\r") end--;
440
+ const divider = source.slice(start, end);
441
+ const prefix = /^[ \t>]*/.exec(divider)?.[0] ?? "";
442
+ if (start === 0 || !/^[|:\- \t]+$/.test(divider.slice(prefix.length))) {
443
+ throw new Error("Table has no recognizable delimiter row");
444
+ }
445
+ const columns = table.children[0]?.children.length ?? 0;
446
+ const cells = Array.from({ length: columns }, (_, i) => {
447
+ const align = table.align?.[i];
448
+ return `${align === "left" || align === "center" ? ":" : ""}---${align === "right" || align === "center" ? ":" : ""}`;
449
+ });
450
+ edits.push({ start: start + prefix.length, end, text: `| ${cells.join(" | ")} |` });
451
+ return edits;
452
+ }
453
+ function condenseMarkdownSource(source) {
454
+ const { frontmatter, body } = splitFrontmatterBlock(source);
455
+ const options = { frontmatter: false, parseHtml: false };
456
+ const document = parseMarkdown(body, options);
457
+ const edits = [];
458
+ const visit = (node) => {
459
+ if (node.type === "table") {
460
+ edits.push(...tableEdits(body, node));
461
+ return;
462
+ }
463
+ if (node.type === "thematicBreak") {
464
+ const start = node.position?.start.offset;
465
+ const end = node.position?.end.offset;
466
+ if (start !== void 0 && end !== void 0) {
467
+ const marker = body.slice(start, end).match(/[-*_]/)?.[0];
468
+ if (marker) edits.push({ start, end, text: marker.repeat(3) });
469
+ }
470
+ }
471
+ for (const child of getChildren(node)) visit(child);
472
+ };
473
+ visit(document);
474
+ const parts = [];
475
+ let boundary = 0;
476
+ let changed = false;
477
+ for (const edit of edits.sort((a, b) => a.start - b.start)) {
478
+ if (edit.start < boundary) throw new Error("Overlapping Markdown condensation edits");
479
+ parts.push(body.slice(boundary, edit.start), edit.text);
480
+ changed || (changed = body.slice(edit.start, edit.end) !== edit.text);
481
+ boundary = edit.end;
482
+ }
483
+ if (!changed) return source;
484
+ parts.push(body.slice(boundary));
485
+ const output = parts.join("");
486
+ const withoutPositions = (key, value) => key === "position" ? void 0 : value;
487
+ if (JSON.stringify(document, withoutPositions) !== JSON.stringify(parseMarkdown(output, options), withoutPositions)) {
488
+ throw new Error("Condensed Markdown no longer parses to an equivalent document");
489
+ }
490
+ return (frontmatter ?? "") + output;
491
+ }
492
+
394
493
  // src/markdown/sourceTransforms.ts
395
494
  var DEFAULT_WRAP_WIDTH = 80;
396
495
  var MIN_WRAP_WIDTH = 20;
@@ -859,6 +958,25 @@ ${cleanedBody}` : fm;
859
958
  edits: [{ start: 0, end: source.length, text: output }]
860
959
  };
861
960
  }
961
+ function condenseTransform(source, options) {
962
+ try {
963
+ const output = condenseMarkdownSource(source);
964
+ if (output === source) return noopResult(source, false);
965
+ return {
966
+ output,
967
+ changed: true,
968
+ degraded: false,
969
+ edits: [{ start: 0, end: source.length, text: output }]
970
+ };
971
+ } catch (err) {
972
+ return degrade(
973
+ "condense",
974
+ source,
975
+ options?.strict,
976
+ err instanceof Error ? err.message : String(err)
977
+ );
978
+ }
979
+ }
862
980
  var MARKDOWN_SOURCE_TRANSFORMS = Object.freeze([
863
981
  {
864
982
  id: "unwrap",
@@ -877,6 +995,12 @@ var MARKDOWN_SOURCE_TRANSFORMS = Object.freeze([
877
995
  label: "Clean up formatting",
878
996
  description: "Re-serialize through the canonical house style: bullets, emphasis, headings, table padding, spacing.",
879
997
  apply: (source, options) => cleanupTransform(source, options)
998
+ },
999
+ {
1000
+ id: "condense",
1001
+ label: "Condense formatting",
1002
+ description: "Compact table padding and decorative dividers while preserving all document content.",
1003
+ apply: (source, options) => condenseTransform(source, options)
880
1004
  }
881
1005
  ]);
882
1006
  function applyMarkdownSourceTransform(id, source, options) {
@@ -964,6 +1088,175 @@ function detectMarkdownWrapState(source) {
964
1088
  };
965
1089
  }
966
1090
 
1091
+ // src/markdown/contentReferences.ts
1092
+ function rewriteMarkdownReferences(source, options = {}) {
1093
+ const { frontmatter, body } = splitFrontmatterBlock(source);
1094
+ const document = parseMarkdown(body, { frontmatter: false });
1095
+ const html = (nodes, insidePicture = false) => {
1096
+ let changed = false;
1097
+ const output2 = nodes.flatMap((node) => {
1098
+ if (node.type !== "htmlElement") return [node];
1099
+ if (options.images === "omit" && node.tagName === "picture") {
1100
+ changed = true;
1101
+ return html(node.children, true).nodes;
1102
+ }
1103
+ if (options.images === "omit" && (node.tagName === "img" || node.tagName === "source" && insidePicture)) {
1104
+ changed = true;
1105
+ const alt = node.tagName === "img" ? node.attributes.alt : void 0;
1106
+ return alt ? [
1107
+ {
1108
+ type: "htmlText",
1109
+ value: alt.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
1110
+ }
1111
+ ] : [];
1112
+ }
1113
+ const child = html(node.children);
1114
+ changed || (changed = child.changed);
1115
+ const copy = { ...node, attributes: { ...node.attributes }, children: child.nodes };
1116
+ const key = node.tagName === "a" ? "href" : node.tagName === "img" ? "src" : void 0;
1117
+ if (key && node.attributes[key]) {
1118
+ const value = options.rewriteUrl?.(node.attributes[key], key === "href" ? "link" : "image");
1119
+ if (value !== void 0 && value !== node.attributes[key]) {
1120
+ copy.attributes[key] = value;
1121
+ changed = true;
1122
+ }
1123
+ }
1124
+ return [copy];
1125
+ });
1126
+ return { nodes: output2, changed };
1127
+ };
1128
+ const blocks = /* @__PURE__ */ new Set([
1129
+ "heading",
1130
+ "paragraph",
1131
+ "blockquote",
1132
+ "code",
1133
+ "thematicBreak",
1134
+ "list",
1135
+ "table",
1136
+ "htmlBlock",
1137
+ "mathBlock",
1138
+ "definition",
1139
+ "footnoteDefinition",
1140
+ "containerDirective",
1141
+ "leafDirective",
1142
+ "definitionList"
1143
+ ]);
1144
+ const structural = /* @__PURE__ */ new Set([
1145
+ "listItem",
1146
+ "tableRow",
1147
+ "tableCell",
1148
+ "definitionTerm",
1149
+ "definitionDescription"
1150
+ ]);
1151
+ const render = (node) => {
1152
+ if (node.type === "document") return stringifyMarkdown(node).trimEnd();
1153
+ return stringifyMarkdown({
1154
+ type: "document",
1155
+ children: blocks.has(node.type) ? [node] : [{ type: "paragraph", children: [node] }]
1156
+ }).trimEnd();
1157
+ };
1158
+ const visit = (node) => {
1159
+ const children = getChildren(node).map(visit);
1160
+ const childChanged = children.some((child) => child.changed);
1161
+ let clone = childChanged && "children" in node ? { ...node, children: children.map((child) => child.node) } : node;
1162
+ let ownChange = false;
1163
+ if ((node.type === "image" || node.type === "imageReference") && options.images === "omit") {
1164
+ clone = { type: "text", value: node.alt ?? "" };
1165
+ ownChange = true;
1166
+ } else if (node.type === "htmlBlock" || node.type === "htmlInline") {
1167
+ const transformed = html(node.htmlChildren);
1168
+ if (transformed.changed) {
1169
+ clone = {
1170
+ ...node,
1171
+ htmlChildren: transformed.nodes,
1172
+ rawHtml: stringifyHtmlNodes(transformed.nodes)
1173
+ };
1174
+ ownChange = true;
1175
+ }
1176
+ } else if (clone.type === "link" || clone.type === "image" || clone.type === "definition") {
1177
+ const url = options.rewriteUrl?.(clone.url, clone.type === "image" ? "image" : "link");
1178
+ if (url !== void 0 && url !== clone.url) {
1179
+ clone = { ...clone, url };
1180
+ ownChange = true;
1181
+ }
1182
+ }
1183
+ const changed = ownChange || childChanged;
1184
+ const pending = ownChange || children.some((child) => child.pending);
1185
+ const start = node.position?.start.offset;
1186
+ const end = node.position?.end.offset;
1187
+ if (pending && start !== void 0 && end !== void 0 && !structural.has(node.type)) {
1188
+ return { node: clone, changed, pending: false, edits: [{ start, end, text: render(clone) }] };
1189
+ }
1190
+ return { node: clone, changed, pending, edits: children.flatMap((child) => child.edits) };
1191
+ };
1192
+ const result = visit(document);
1193
+ if (result.pending) throw new Error("Reference edit has no positioned ancestor");
1194
+ const edits = result.edits;
1195
+ let output = body;
1196
+ let boundary = body.length;
1197
+ for (const e of edits.sort((a, b) => b.start - a.start)) {
1198
+ if (e.end > boundary) throw new Error("Overlapping reference edits");
1199
+ output = output.slice(0, e.start) + e.text + output.slice(e.end);
1200
+ boundary = e.start;
1201
+ }
1202
+ return (frontmatter ?? "") + output;
1203
+ }
1204
+
1205
+ // src/markdown/docfxMarkdown.ts
1206
+ function normalizeDocfxMarkdown(source, options = {}) {
1207
+ const { frontmatter, body } = splitFrontmatterBlock(source);
1208
+ const protectedSpans = [];
1209
+ for (const comment of body.matchAll(/<!--[\s\S]*?-->/g))
1210
+ protectedSpans.push([comment.index, comment.index + comment[0].length]);
1211
+ walkMarkdownTree(parseMarkdown(body, { frontmatter: false }), (node) => {
1212
+ if ((node.type === "code" || node.type === "inlineCode") && node.position?.start.offset !== void 0 && node.position.end.offset !== void 0)
1213
+ protectedSpans.push([node.position.start.offset, node.position.end.offset]);
1214
+ });
1215
+ const escape = (text) => stringifyMarkdown({
1216
+ type: "document",
1217
+ children: [{ type: "paragraph", children: [{ type: "text", value: text }] }]
1218
+ }).trimEnd();
1219
+ const output = body.replace(
1220
+ /(^[^\S\n]*(?:>\s*)?):::\s*([\w-]+)([^\n]*?)(?=\r?$)|(^[^\S\n]*>\s*)\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|div)\b([^\]]*)\]|:::\s*(image)\b([^\n]*?):::/gim,
1221
+ (match, prefix, name, raw, quote, admonition, _extra, inlineName, inlineRaw, offset) => {
1222
+ if (protectedSpans.some(([start, end]) => offset >= start && offset < end)) return match;
1223
+ if (admonition)
1224
+ return admonition.toLowerCase() === "div" ? quote ?? "" : `${quote}**${admonition[0] + admonition.slice(1).toLowerCase()}:**`;
1225
+ name ?? (name = inlineName);
1226
+ raw ?? (raw = inlineRaw);
1227
+ const attributes = /* @__PURE__ */ new Map();
1228
+ for (const attr of (raw ?? "").matchAll(/([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g))
1229
+ attributes.set(attr[1], attr[2] ?? attr[3]);
1230
+ switch (name?.toLowerCase()) {
1231
+ case "image": {
1232
+ const alt = attributes.get("alt-text") ?? "";
1233
+ const url = attributes.get("source");
1234
+ if (options.images === "omit") return `${prefix ?? ""}${escape(alt)}`;
1235
+ if (!url) throw new Error("DocFX image has no source");
1236
+ const image = stringifyMarkdown({
1237
+ type: "document",
1238
+ children: [{ type: "paragraph", children: [{ type: "image", url, alt }] }]
1239
+ }).trimEnd();
1240
+ return `${prefix ?? ""}${image}`;
1241
+ }
1242
+ case "zone":
1243
+ return attributes.has("pivot") ? `${prefix ?? ""}**Applies to: ${escape(attributes.get("pivot"))}**
1244
+ ` : "";
1245
+ case "zone-end":
1246
+ case "image-end":
1247
+ case "row":
1248
+ case "row-end":
1249
+ case "column":
1250
+ case "column-end":
1251
+ return "";
1252
+ default:
1253
+ throw new Error(`Unsupported DocFX directive: ${name}`);
1254
+ }
1255
+ }
1256
+ );
1257
+ return (frontmatter ?? "") + output;
1258
+ }
1259
+
967
1260
  export {
968
1261
  stringifyMarkdown,
969
1262
  DEFAULT_RESOURCE_MAX_BYTES,
@@ -973,11 +1266,14 @@ export {
973
1266
  ResourcePolicyError,
974
1267
  isResourceUrlAllowed,
975
1268
  fetchResourceBytes,
1269
+ condenseMarkdownSource,
976
1270
  DEFAULT_WRAP_WIDTH,
977
1271
  MARKDOWN_SOURCE_TRANSFORMS,
978
1272
  applyMarkdownSourceTransform,
979
1273
  unwrapMarkdownSource,
980
1274
  wrapMarkdownSource,
981
1275
  cleanupMarkdownSource,
982
- detectMarkdownWrapState
1276
+ detectMarkdownWrapState,
1277
+ rewriteMarkdownReferences,
1278
+ normalizeDocfxMarkdown
983
1279
  };
@@ -348,6 +348,8 @@ function createMediaProviderFromContainer(container) {
348
348
  const generation = generations.get(relativePath) ?? 0;
349
349
  const work = (async () => {
350
350
  const data = await container.readFile(relativePath);
351
+ if (disposed) return relativePath;
352
+ if ((generations.get(relativePath) ?? 0) !== generation) return resolveUrl(relativePath);
351
353
  const entries = data ? await container.listFiles() : [];
352
354
  if (disposed) return relativePath;
353
355
  if ((generations.get(relativePath) ?? 0) !== generation) return resolveUrl(relativePath);
@@ -376,6 +378,7 @@ function createMediaProviderFromContainer(container) {
376
378
  },
377
379
  async addMedia(name, data, mimeType) {
378
380
  invalidate(name);
381
+ const generation = generations.get(name);
379
382
  let buffer;
380
383
  if (data instanceof Blob) {
381
384
  buffer = await data.arrayBuffer();
@@ -383,7 +386,12 @@ function createMediaProviderFromContainer(container) {
383
386
  buffer = data;
384
387
  }
385
388
  await container.writeFile(name, buffer, mimeType);
389
+ const canCacheUpload = generations.get(name) === generation;
386
390
  invalidate(name);
391
+ if (!disposed && canCacheUpload) {
392
+ const bytes = ArrayBuffer.isView(buffer) ? new Uint8Array(buffer).slice().buffer : buffer;
393
+ blobUrlCache.set(name, URL.createObjectURL(new Blob([bytes], { type: mimeType })));
394
+ }
387
395
  return name;
388
396
  },
389
397
  async removeMedia(relativePath) {