@shapesos/clay 0.7.0 → 0.8.1

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.
@@ -0,0 +1,71 @@
1
+ // src/utils/parse-markdown-table.ts
2
+ function parseCells(line) {
3
+ const trimmed = line.trim();
4
+ const stripped = trimmed.startsWith("|") ? trimmed.slice(1) : trimmed;
5
+ const withoutTrailing = stripped.endsWith("|") ? stripped.slice(0, -1) : stripped;
6
+ return withoutTrailing.split("|").map((cell) => cell.trim());
7
+ }
8
+ function isSeparatorRow(line) {
9
+ const cells = parseCells(line);
10
+ return cells.length > 0 && cells.every((cell) => /^:?-{1,}:?$/.test(cell));
11
+ }
12
+ function isTableRow(line) {
13
+ return line.includes("|");
14
+ }
15
+ var TABLE_TITLE_PREFIX = "<!-- table-title:";
16
+ var TABLE_TITLE_SUFFIX = "-->";
17
+ function extractTableTitle(line) {
18
+ const trimmed = line.trim();
19
+ if (!trimmed.startsWith(TABLE_TITLE_PREFIX) || !trimmed.endsWith(TABLE_TITLE_SUFFIX)) {
20
+ return void 0;
21
+ }
22
+ return trimmed.slice(TABLE_TITLE_PREFIX.length, -TABLE_TITLE_SUFFIX.length).trim() || void 0;
23
+ }
24
+ function parseMarkdownTable(markdown, tableIndex = 0) {
25
+ const lines = markdown.split("\n");
26
+ let tableCount = 0;
27
+ for (let i = 0; i < lines.length - 1; i++) {
28
+ const headerLine = lines[i];
29
+ const separatorLine = lines[i + 1];
30
+ if (!isTableRow(headerLine) || !isSeparatorRow(separatorLine)) {
31
+ continue;
32
+ }
33
+ if (tableCount === tableIndex) {
34
+ const columns = parseCells(headerLine);
35
+ const rows = [];
36
+ const title = i > 0 ? extractTableTitle(lines[i - 1]) : void 0;
37
+ for (let j = i + 2; j < lines.length; j++) {
38
+ if (!isTableRow(lines[j]) || isSeparatorRow(lines[j])) {
39
+ break;
40
+ }
41
+ const trimmedLine = lines[j].trim();
42
+ if (trimmedLine === "") {
43
+ break;
44
+ }
45
+ rows.push(parseCells(lines[j]));
46
+ }
47
+ return { columns, rows, ...title ? { title } : {} };
48
+ }
49
+ tableCount++;
50
+ }
51
+ return null;
52
+ }
53
+
54
+ // src/utils/convert-table-to-csv.ts
55
+ function escapeField(field) {
56
+ if (field.includes(",") || field.includes('"') || field.includes("\n") || field.includes("\r")) {
57
+ return `"${field.replace(/"/g, '""')}"`;
58
+ }
59
+ return field;
60
+ }
61
+ function convertTableToCSV(table) {
62
+ const headerRow = table.columns.map(escapeField).join(",");
63
+ const dataRows = table.rows.map((row) => row.map(escapeField).join(","));
64
+ return [headerRow, ...dataRows].join("\r\n");
65
+ }
66
+
67
+ export {
68
+ parseMarkdownTable,
69
+ convertTableToCSV
70
+ };
71
+ //# sourceMappingURL=chunk-N4FMZU56.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/parse-markdown-table.ts","../src/utils/convert-table-to-csv.ts"],"sourcesContent":["/** Structured representation of a parsed markdown table. */\nexport interface MarkdownTable {\n /** Column header labels. */\n columns: string[];\n /** Data rows, each an array of cell values matching the column order. */\n rows: string[][];\n /** Optional title extracted from a `<!-- table-title: ... -->` comment above the table. */\n title?: string;\n}\n\n/**\n * Parses cells from a markdown table row, trimming whitespace and stripping leading/trailing pipes.\n * @param line - A single line of a markdown table.\n * @returns An array of trimmed cell values.\n */\nfunction parseCells(line: string): string[] {\n const trimmed = line.trim();\n const stripped = trimmed.startsWith(\"|\") ? trimmed.slice(1) : trimmed;\n const withoutTrailing = stripped.endsWith(\"|\") ? stripped.slice(0, -1) : stripped;\n return withoutTrailing.split(\"|\").map((cell) => cell.trim());\n}\n\n/**\n * Tests whether a line is a GFM table separator row (e.g. `| --- | --- |`).\n * @param line - A single line to test.\n * @returns `true` if the line matches the separator pattern.\n */\nfunction isSeparatorRow(line: string): boolean {\n const cells = parseCells(line);\n return cells.length > 0 && cells.every((cell) => /^:?-{1,}:?$/.test(cell));\n}\n\n/**\n * Tests whether a line looks like a markdown table row (contains at least one pipe).\n * @param line - A single line to test.\n * @returns `true` if the line contains a pipe character.\n */\nfunction isTableRow(line: string): boolean {\n return line.includes(\"|\");\n}\n\nconst TABLE_TITLE_PREFIX = \"<!-- table-title:\";\nconst TABLE_TITLE_SUFFIX = \"-->\";\n\n/**\n * Extracts a table title from a `<!-- table-title: ... -->` HTML comment.\n * Uses string methods instead of regex to avoid ReDoS on untrusted input.\n * @param line - A single line to test.\n * @returns The title string, or `undefined` if the line is not a title comment.\n */\nfunction extractTableTitle(line: string): string | undefined {\n const trimmed = line.trim();\n if (!trimmed.startsWith(TABLE_TITLE_PREFIX) || !trimmed.endsWith(TABLE_TITLE_SUFFIX)) {\n return undefined;\n }\n return trimmed.slice(TABLE_TITLE_PREFIX.length, -TABLE_TITLE_SUFFIX.length).trim() || undefined;\n}\n\n/**\n * Extracts the Nth GFM markdown table from a string.\n * @param markdown - The markdown content to parse.\n * @param tableIndex - Zero-based index of the table to extract.\n * @returns The parsed table, or `null` if no table exists at the given index.\n */\nexport function parseMarkdownTable(markdown: string, tableIndex = 0): MarkdownTable | null {\n const lines = markdown.split(\"\\n\");\n let tableCount = 0;\n\n for (let i = 0; i < lines.length - 1; i++) {\n const headerLine = lines[i];\n const separatorLine = lines[i + 1];\n\n if (!isTableRow(headerLine) || !isSeparatorRow(separatorLine)) {\n continue;\n }\n\n if (tableCount === tableIndex) {\n const columns = parseCells(headerLine);\n const rows: string[][] = [];\n const title = i > 0 ? extractTableTitle(lines[i - 1]) : undefined;\n\n for (let j = i + 2; j < lines.length; j++) {\n if (!isTableRow(lines[j]) || isSeparatorRow(lines[j])) {\n break;\n }\n const trimmedLine = lines[j].trim();\n if (trimmedLine === \"\") {\n break;\n }\n rows.push(parseCells(lines[j]));\n }\n\n return { columns, rows, ...(title ? { title } : {}) };\n }\n\n tableCount++;\n }\n\n return null;\n}\n","import type { MarkdownTable } from \"./parse-markdown-table\";\n\n/**\n * Escapes and optionally quotes a CSV field per RFC 4180.\n * Fields containing commas, double quotes, or newlines are wrapped in double quotes.\n * Existing double quotes are escaped by doubling them.\n * @param field - The raw field value.\n * @returns The escaped/quoted field string.\n */\nfunction escapeField(field: string): string {\n if (field.includes(\",\") || field.includes('\"') || field.includes(\"\\n\") || field.includes(\"\\r\")) {\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n }\n return field;\n}\n\n/**\n * Converts a parsed markdown table to a CSV string (RFC 4180).\n * Fields containing commas, double quotes, or newlines are quoted.\n * @param table - The structured table data to convert.\n * @returns A CSV-formatted string with CRLF line endings.\n */\nexport function convertTableToCSV(table: MarkdownTable): string {\n const headerRow = table.columns.map(escapeField).join(\",\");\n const dataRows = table.rows.map((row) => row.map(escapeField).join(\",\"));\n return [headerRow, ...dataRows].join(\"\\r\\n\");\n}\n"],"mappings":";AAeA,SAAS,WAAW,MAAwB;AAC1C,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,WAAW,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAC9D,QAAM,kBAAkB,SAAS,SAAS,GAAG,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI;AACzE,SAAO,gBAAgB,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AAC7D;AAOA,SAAS,eAAe,MAAuB;AAC7C,QAAM,QAAQ,WAAW,IAAI;AAC7B,SAAO,MAAM,SAAS,KAAK,MAAM,MAAM,CAAC,SAAS,cAAc,KAAK,IAAI,CAAC;AAC3E;AAOA,SAAS,WAAW,MAAuB;AACzC,SAAO,KAAK,SAAS,GAAG;AAC1B;AAEA,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;AAQ3B,SAAS,kBAAkB,MAAkC;AAC3D,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAQ,WAAW,kBAAkB,KAAK,CAAC,QAAQ,SAAS,kBAAkB,GAAG;AACpF,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,MAAM,mBAAmB,QAAQ,CAAC,mBAAmB,MAAM,EAAE,KAAK,KAAK;AACxF;AAQO,SAAS,mBAAmB,UAAkB,aAAa,GAAyB;AACzF,QAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,MAAI,aAAa;AAEjB,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,UAAM,aAAa,MAAM,CAAC;AAC1B,UAAM,gBAAgB,MAAM,IAAI,CAAC;AAEjC,QAAI,CAAC,WAAW,UAAU,KAAK,CAAC,eAAe,aAAa,GAAG;AAC7D;AAAA,IACF;AAEA,QAAI,eAAe,YAAY;AAC7B,YAAM,UAAU,WAAW,UAAU;AACrC,YAAM,OAAmB,CAAC;AAC1B,YAAM,QAAQ,IAAI,IAAI,kBAAkB,MAAM,IAAI,CAAC,CAAC,IAAI;AAExD,eAAS,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACzC,YAAI,CAAC,WAAW,MAAM,CAAC,CAAC,KAAK,eAAe,MAAM,CAAC,CAAC,GAAG;AACrD;AAAA,QACF;AACA,cAAM,cAAc,MAAM,CAAC,EAAE,KAAK;AAClC,YAAI,gBAAgB,IAAI;AACtB;AAAA,QACF;AACA,aAAK,KAAK,WAAW,MAAM,CAAC,CAAC,CAAC;AAAA,MAChC;AAEA,aAAO,EAAE,SAAS,MAAM,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG;AAAA,IACtD;AAEA;AAAA,EACF;AAEA,SAAO;AACT;;;AC1FA,SAAS,YAAY,OAAuB;AAC1C,MAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI,GAAG;AAC9F,WAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACtC;AACA,SAAO;AACT;AAQO,SAAS,kBAAkB,OAA8B;AAC9D,QAAM,YAAY,MAAM,QAAQ,IAAI,WAAW,EAAE,KAAK,GAAG;AACzD,QAAM,WAAW,MAAM,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,WAAW,EAAE,KAAK,GAAG,CAAC;AACvE,SAAO,CAAC,WAAW,GAAG,QAAQ,EAAE,KAAK,MAAM;AAC7C;","names":[]}
@@ -5,7 +5,7 @@ var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUF
5
5
  function markdownToPlainText(markdown) {
6
6
  if (!markdown) return "";
7
7
  const inlineCodeSnippets = [];
8
- let result = markdown.replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
8
+ let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
9
9
  inlineCodeSnippets.push(code);
10
10
  return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
11
11
  }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "").replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1").replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
@@ -16,4 +16,4 @@ function markdownToPlainText(markdown) {
16
16
  export {
17
17
  markdownToPlainText
18
18
  };
19
- //# sourceMappingURL=chunk-WYGH3FKT.js.map
19
+ //# sourceMappingURL=chunk-QUJYTIGD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/markdown-to-plain-text.ts"],"sourcesContent":["const PLACEHOLDER_PREFIX = \"\\u200B\\u200BIC\";\nconst PLACEHOLDER_SUFFIX = \"\\u200B\\u200B\";\nconst PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\\\d+)${PLACEHOLDER_SUFFIX}`, \"g\");\n\n/**\n * Converts a Markdown string to plain text by stripping all formatting syntax.\n *\n * Handles headings, bold/italic/strikethrough, links, images, blockquotes, code blocks,\n * inline code, tables, and horizontal rules. Inline code content is preserved literally\n * (formatting inside backticks is not stripped).\n *\n * @param markdown - The Markdown source string.\n * @returns A plain-text string with all Markdown syntax removed.\n */\nexport function markdownToPlainText(markdown: string): string {\n if (!markdown) return \"\";\n\n // Protect inline code content from bold/italic stripping by replacing with placeholders\n const inlineCodeSnippets: string[] = [];\n let result = markdown\n // HTML comments (e.g. <!-- table-title: ... -->), including multiline and --!> variants\n .replace(/^<!--[\\s\\S]*?--!?>\\n?/gm, \"\")\n // Code blocks (must come first)\n .replace(/```[\\s\\S]*?```/g, \"\")\n // Inline code → placeholder\n .replace(/`([^`]+)`/g, (_match, code: string) => {\n inlineCodeSnippets.push(code);\n return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;\n })\n // Remove headings markers, keep text\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^(?:---|\\*\\*\\*|___)\\s*$/gm, \"\")\n // Bold + italic\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n // Bold\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n // Italic\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Strikethrough\n .replace(/~~(.+?)~~/g, \"$1\")\n // Images: ![alt](url) → alt (must come before links)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Blockquote markers\n .replace(/^>\\s?/gm, \"\")\n // Table separator rows (|---|---|)\n .replace(/^\\|[-:\\s|]+\\|\\s*$/gm, \"\")\n // Table rows: | a | b | → a | b\n .replace(/^\\|\\s?/gm, \"\")\n .replace(/\\s?\\|$/gm, \"\")\n // Collapse multiple blank lines\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n\n // Restore inline code content\n result = result.replace(PLACEHOLDER_REGEX, (_match, index: string) => inlineCodeSnippets[Number(index)]);\n\n return result;\n}\n"],"mappings":";AAAA,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;AAC3B,IAAM,oBAAoB,IAAI,OAAO,GAAG,kBAAkB,SAAS,kBAAkB,IAAI,GAAG;AAYrF,SAAS,oBAAoB,UAA0B;AAC5D,MAAI,CAAC,SAAU,QAAO;AAGtB,QAAM,qBAA+B,CAAC;AACtC,MAAI,SAAS,SAEV,QAAQ,2BAA2B,EAAE,EAErC,QAAQ,mBAAmB,EAAE,EAE7B,QAAQ,cAAc,CAAC,QAAQ,SAAiB;AAC/C,uBAAmB,KAAK,IAAI;AAC5B,WAAO,GAAG,kBAAkB,GAAG,mBAAmB,SAAS,CAAC,GAAG,kBAAkB;AAAA,EACnF,CAAC,EAEA,QAAQ,gBAAgB,EAAE,EAE1B,QAAQ,6BAA6B,EAAE,EAEvC,QAAQ,oBAAoB,IAAI,EAEhC,QAAQ,oBAAoB,IAAI,EAChC,QAAQ,kBAAkB,IAAI,EAE9B,QAAQ,cAAc,IAAI,EAC1B,QAAQ,YAAY,IAAI,EAExB,QAAQ,cAAc,IAAI,EAE1B,QAAQ,2BAA2B,IAAI,EAEvC,QAAQ,0BAA0B,IAAI,EAEtC,QAAQ,WAAW,EAAE,EAErB,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,YAAY,EAAE,EACtB,QAAQ,YAAY,EAAE,EAEtB,QAAQ,WAAW,MAAM,EACzB,KAAK;AAGR,WAAS,OAAO,QAAQ,mBAAmB,CAAC,QAAQ,UAAkB,mBAAmB,OAAO,KAAK,CAAC,CAAC;AAEvG,SAAO;AACT;","names":[]}