@steipete/summarize-core 0.7.0

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.
Files changed (182) hide show
  1. package/README.md +7 -0
  2. package/dist/esm/content/index.js +5 -0
  3. package/dist/esm/content/index.js.map +1 -0
  4. package/dist/esm/content/link-preview/client.js +28 -0
  5. package/dist/esm/content/link-preview/client.js.map +1 -0
  6. package/dist/esm/content/link-preview/content/article.js +155 -0
  7. package/dist/esm/content/link-preview/content/article.js.map +1 -0
  8. package/dist/esm/content/link-preview/content/cleaner.js +55 -0
  9. package/dist/esm/content/link-preview/content/cleaner.js.map +1 -0
  10. package/dist/esm/content/link-preview/content/constants.js +7 -0
  11. package/dist/esm/content/link-preview/content/constants.js.map +1 -0
  12. package/dist/esm/content/link-preview/content/fetcher.js +124 -0
  13. package/dist/esm/content/link-preview/content/fetcher.js.map +1 -0
  14. package/dist/esm/content/link-preview/content/firecrawl.js +86 -0
  15. package/dist/esm/content/link-preview/content/firecrawl.js.map +1 -0
  16. package/dist/esm/content/link-preview/content/html.js +162 -0
  17. package/dist/esm/content/link-preview/content/html.js.map +1 -0
  18. package/dist/esm/content/link-preview/content/index.js +345 -0
  19. package/dist/esm/content/link-preview/content/index.js.map +1 -0
  20. package/dist/esm/content/link-preview/content/jsonld.js +77 -0
  21. package/dist/esm/content/link-preview/content/jsonld.js.map +1 -0
  22. package/dist/esm/content/link-preview/content/parsers.js +77 -0
  23. package/dist/esm/content/link-preview/content/parsers.js.map +1 -0
  24. package/dist/esm/content/link-preview/content/podcast-utils.js +79 -0
  25. package/dist/esm/content/link-preview/content/podcast-utils.js.map +1 -0
  26. package/dist/esm/content/link-preview/content/readability.js +53 -0
  27. package/dist/esm/content/link-preview/content/readability.js.map +1 -0
  28. package/dist/esm/content/link-preview/content/twitter-utils.js +68 -0
  29. package/dist/esm/content/link-preview/content/twitter-utils.js.map +1 -0
  30. package/dist/esm/content/link-preview/content/types.js +4 -0
  31. package/dist/esm/content/link-preview/content/types.js.map +1 -0
  32. package/dist/esm/content/link-preview/content/utils.js +164 -0
  33. package/dist/esm/content/link-preview/content/utils.js.map +1 -0
  34. package/dist/esm/content/link-preview/content/video.js +96 -0
  35. package/dist/esm/content/link-preview/content/video.js.map +1 -0
  36. package/dist/esm/content/link-preview/content/youtube.js +82 -0
  37. package/dist/esm/content/link-preview/content/youtube.js.map +1 -0
  38. package/dist/esm/content/link-preview/deps.js +20 -0
  39. package/dist/esm/content/link-preview/deps.js.map +1 -0
  40. package/dist/esm/content/link-preview/fetch-with-timeout.js +35 -0
  41. package/dist/esm/content/link-preview/fetch-with-timeout.js.map +1 -0
  42. package/dist/esm/content/link-preview/types.js +2 -0
  43. package/dist/esm/content/link-preview/types.js.map +1 -0
  44. package/dist/esm/content/transcript/cache.js +79 -0
  45. package/dist/esm/content/transcript/cache.js.map +1 -0
  46. package/dist/esm/content/transcript/index.js +130 -0
  47. package/dist/esm/content/transcript/index.js.map +1 -0
  48. package/dist/esm/content/transcript/normalize.js +43 -0
  49. package/dist/esm/content/transcript/normalize.js.map +1 -0
  50. package/dist/esm/content/transcript/providers/generic.js +11 -0
  51. package/dist/esm/content/transcript/providers/generic.js.map +1 -0
  52. package/dist/esm/content/transcript/providers/podcast/apple-flow.js +222 -0
  53. package/dist/esm/content/transcript/providers/podcast/apple-flow.js.map +1 -0
  54. package/dist/esm/content/transcript/providers/podcast/apple.js +38 -0
  55. package/dist/esm/content/transcript/providers/podcast/apple.js.map +1 -0
  56. package/dist/esm/content/transcript/providers/podcast/constants.js +8 -0
  57. package/dist/esm/content/transcript/providers/podcast/constants.js.map +1 -0
  58. package/dist/esm/content/transcript/providers/podcast/flow-context.js +2 -0
  59. package/dist/esm/content/transcript/providers/podcast/flow-context.js.map +1 -0
  60. package/dist/esm/content/transcript/providers/podcast/itunes.js +134 -0
  61. package/dist/esm/content/transcript/providers/podcast/itunes.js.map +1 -0
  62. package/dist/esm/content/transcript/providers/podcast/json.js +34 -0
  63. package/dist/esm/content/transcript/providers/podcast/json.js.map +1 -0
  64. package/dist/esm/content/transcript/providers/podcast/media.js +345 -0
  65. package/dist/esm/content/transcript/providers/podcast/media.js.map +1 -0
  66. package/dist/esm/content/transcript/providers/podcast/results.js +28 -0
  67. package/dist/esm/content/transcript/providers/podcast/results.js.map +1 -0
  68. package/dist/esm/content/transcript/providers/podcast/rss.js +253 -0
  69. package/dist/esm/content/transcript/providers/podcast/rss.js.map +1 -0
  70. package/dist/esm/content/transcript/providers/podcast/spotify-flow.js +218 -0
  71. package/dist/esm/content/transcript/providers/podcast/spotify-flow.js.map +1 -0
  72. package/dist/esm/content/transcript/providers/podcast/spotify.js +113 -0
  73. package/dist/esm/content/transcript/providers/podcast/spotify.js.map +1 -0
  74. package/dist/esm/content/transcript/providers/podcast.js +222 -0
  75. package/dist/esm/content/transcript/providers/podcast.js.map +1 -0
  76. package/dist/esm/content/transcript/providers/youtube/api.js +257 -0
  77. package/dist/esm/content/transcript/providers/youtube/api.js.map +1 -0
  78. package/dist/esm/content/transcript/providers/youtube/apify.js +55 -0
  79. package/dist/esm/content/transcript/providers/youtube/apify.js.map +1 -0
  80. package/dist/esm/content/transcript/providers/youtube/captions.js +409 -0
  81. package/dist/esm/content/transcript/providers/youtube/captions.js.map +1 -0
  82. package/dist/esm/content/transcript/providers/youtube/yt-dlp.js +166 -0
  83. package/dist/esm/content/transcript/providers/youtube/yt-dlp.js.map +1 -0
  84. package/dist/esm/content/transcript/providers/youtube.js +173 -0
  85. package/dist/esm/content/transcript/providers/youtube.js.map +1 -0
  86. package/dist/esm/content/transcript/types.js +2 -0
  87. package/dist/esm/content/transcript/types.js.map +1 -0
  88. package/dist/esm/content/transcript/utils.js +259 -0
  89. package/dist/esm/content/transcript/utils.js.map +1 -0
  90. package/dist/esm/index.js +4 -0
  91. package/dist/esm/index.js.map +1 -0
  92. package/dist/esm/language.js +126 -0
  93. package/dist/esm/language.js.map +1 -0
  94. package/dist/esm/prompts/cli.js +20 -0
  95. package/dist/esm/prompts/cli.js.map +1 -0
  96. package/dist/esm/prompts/file.js +48 -0
  97. package/dist/esm/prompts/file.js.map +1 -0
  98. package/dist/esm/prompts/index.js +4 -0
  99. package/dist/esm/prompts/index.js.map +1 -0
  100. package/dist/esm/prompts/link-summary.js +116 -0
  101. package/dist/esm/prompts/link-summary.js.map +1 -0
  102. package/dist/esm/shared/contracts.js +2 -0
  103. package/dist/esm/shared/contracts.js.map +1 -0
  104. package/dist/esm/transcription/whisper/constants.js +8 -0
  105. package/dist/esm/transcription/whisper/constants.js.map +1 -0
  106. package/dist/esm/transcription/whisper/core.js +303 -0
  107. package/dist/esm/transcription/whisper/core.js.map +1 -0
  108. package/dist/esm/transcription/whisper/fal.js +41 -0
  109. package/dist/esm/transcription/whisper/fal.js.map +1 -0
  110. package/dist/esm/transcription/whisper/ffmpeg.js +179 -0
  111. package/dist/esm/transcription/whisper/ffmpeg.js.map +1 -0
  112. package/dist/esm/transcription/whisper/openai.js +47 -0
  113. package/dist/esm/transcription/whisper/openai.js.map +1 -0
  114. package/dist/esm/transcription/whisper/types.js +2 -0
  115. package/dist/esm/transcription/whisper/types.js.map +1 -0
  116. package/dist/esm/transcription/whisper/utils.js +63 -0
  117. package/dist/esm/transcription/whisper/utils.js.map +1 -0
  118. package/dist/esm/transcription/whisper/whisper-cpp.js +227 -0
  119. package/dist/esm/transcription/whisper/whisper-cpp.js.map +1 -0
  120. package/dist/esm/transcription/whisper.js +5 -0
  121. package/dist/esm/transcription/whisper.js.map +1 -0
  122. package/dist/types/content/index.d.ts +5 -0
  123. package/dist/types/content/link-preview/client.d.ts +18 -0
  124. package/dist/types/content/link-preview/content/article.d.ts +4 -0
  125. package/dist/types/content/link-preview/content/cleaner.d.ts +12 -0
  126. package/dist/types/content/link-preview/content/constants.d.ts +6 -0
  127. package/dist/types/content/link-preview/content/fetcher.d.ts +16 -0
  128. package/dist/types/content/link-preview/content/firecrawl.d.ts +14 -0
  129. package/dist/types/content/link-preview/content/html.d.ts +17 -0
  130. package/dist/types/content/link-preview/content/index.d.ts +4 -0
  131. package/dist/types/content/link-preview/content/jsonld.d.ts +6 -0
  132. package/dist/types/content/link-preview/content/parsers.d.ts +7 -0
  133. package/dist/types/content/link-preview/content/podcast-utils.d.ts +7 -0
  134. package/dist/types/content/link-preview/content/readability.d.ts +8 -0
  135. package/dist/types/content/link-preview/content/twitter-utils.d.ts +4 -0
  136. package/dist/types/content/link-preview/content/types.d.ts +61 -0
  137. package/dist/types/content/link-preview/content/utils.d.ts +17 -0
  138. package/dist/types/content/link-preview/content/video.d.ts +5 -0
  139. package/dist/types/content/link-preview/content/youtube.d.ts +1 -0
  140. package/dist/types/content/link-preview/deps.d.ts +167 -0
  141. package/dist/types/content/link-preview/fetch-with-timeout.d.ts +4 -0
  142. package/dist/types/content/link-preview/types.d.ts +37 -0
  143. package/dist/types/content/transcript/cache.d.ts +29 -0
  144. package/dist/types/content/transcript/index.d.ts +9 -0
  145. package/dist/types/content/transcript/normalize.d.ts +3 -0
  146. package/dist/types/content/transcript/providers/generic.d.ts +3 -0
  147. package/dist/types/content/transcript/providers/podcast/apple-flow.d.ts +4 -0
  148. package/dist/types/content/transcript/providers/podcast/apple.d.ts +6 -0
  149. package/dist/types/content/transcript/providers/podcast/constants.d.ts +7 -0
  150. package/dist/types/content/transcript/providers/podcast/flow-context.d.ts +11 -0
  151. package/dist/types/content/transcript/providers/podcast/itunes.d.ts +17 -0
  152. package/dist/types/content/transcript/providers/podcast/json.d.ts +8 -0
  153. package/dist/types/content/transcript/providers/podcast/media.d.ts +42 -0
  154. package/dist/types/content/transcript/providers/podcast/results.d.ts +10 -0
  155. package/dist/types/content/transcript/providers/podcast/rss.d.ts +22 -0
  156. package/dist/types/content/transcript/providers/podcast/spotify-flow.d.ts +3 -0
  157. package/dist/types/content/transcript/providers/podcast/spotify.d.ts +24 -0
  158. package/dist/types/content/transcript/providers/podcast.d.ts +20 -0
  159. package/dist/types/content/transcript/providers/youtube/api.d.ts +26 -0
  160. package/dist/types/content/transcript/providers/youtube/apify.d.ts +1 -0
  161. package/dist/types/content/transcript/providers/youtube/captions.d.ts +7 -0
  162. package/dist/types/content/transcript/providers/youtube/yt-dlp.d.ts +17 -0
  163. package/dist/types/content/transcript/providers/youtube.d.ts +3 -0
  164. package/dist/types/content/transcript/types.d.ts +30 -0
  165. package/dist/types/content/transcript/utils.d.ts +8 -0
  166. package/dist/types/index.d.ts +4 -0
  167. package/dist/types/language.d.ts +25 -0
  168. package/dist/types/prompts/cli.d.ts +10 -0
  169. package/dist/types/prompts/file.d.ts +17 -0
  170. package/dist/types/prompts/index.d.ts +4 -0
  171. package/dist/types/prompts/link-summary.d.ts +29 -0
  172. package/dist/types/shared/contracts.d.ts +2 -0
  173. package/dist/types/transcription/whisper/constants.d.ts +7 -0
  174. package/dist/types/transcription/whisper/core.d.ts +20 -0
  175. package/dist/types/transcription/whisper/fal.d.ts +1 -0
  176. package/dist/types/transcription/whisper/ffmpeg.d.ts +16 -0
  177. package/dist/types/transcription/whisper/openai.d.ts +2 -0
  178. package/dist/types/transcription/whisper/types.d.ts +17 -0
  179. package/dist/types/transcription/whisper/utils.d.ts +5 -0
  180. package/dist/types/transcription/whisper/whisper-cpp.d.ts +9 -0
  181. package/dist/types/transcription/whisper.d.ts +5 -0
  182. package/package.json +54 -0
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @steipete/summarize-core
2
+
3
+ Core library for Summarize (content extraction + prompt builders).
4
+
5
+ - CLI package: `@steipete/summarize`
6
+ - Recommended imports (library use): `@steipete/summarize-core/content`, `@steipete/summarize-core/prompts`
7
+
@@ -0,0 +1,5 @@
1
+ export { createLinkPreviewClient, } from './link-preview/client.js';
2
+ export { DEFAULT_CACHE_MODE, DEFAULT_MAX_CONTENT_CHARACTERS, DEFAULT_TIMEOUT_MS, } from './link-preview/content/types.js';
3
+ export { ProgressKind } from './link-preview/deps.js';
4
+ export { CACHE_MODES, } from './link-preview/types.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/content/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,GAGxB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,kBAAkB,EAClB,8BAA8B,EAC9B,kBAAkB,GAGnB,MAAM,iCAAiC,CAAA;AAUxC,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EACL,WAAW,GAIZ,MAAM,yBAAyB,CAAA"}
@@ -0,0 +1,28 @@
1
+ import { fetchLinkContent } from './content/index.js';
2
+ export function createLinkPreviewClient(options = {}) {
3
+ const fetchImpl = options.fetch ?? ((...args) => globalThis.fetch(...args));
4
+ const scrape = options.scrapeWithFirecrawl ?? null;
5
+ const apifyApiToken = typeof options.apifyApiToken === 'string' ? options.apifyApiToken : null;
6
+ const ytDlpPath = typeof options.ytDlpPath === 'string' ? options.ytDlpPath : null;
7
+ const falApiKey = typeof options.falApiKey === 'string' ? options.falApiKey : null;
8
+ const openaiApiKey = typeof options.openaiApiKey === 'string' ? options.openaiApiKey : null;
9
+ const convertHtmlToMarkdown = options.convertHtmlToMarkdown ?? null;
10
+ const transcriptCache = options.transcriptCache ?? null;
11
+ const readTweetWithBird = typeof options.readTweetWithBird === 'function' ? options.readTweetWithBird : null;
12
+ const onProgress = typeof options.onProgress === 'function' ? options.onProgress : null;
13
+ return {
14
+ fetchLinkContent: (url, contentOptions) => fetchLinkContent(url, contentOptions, {
15
+ fetch: fetchImpl,
16
+ scrapeWithFirecrawl: scrape,
17
+ apifyApiToken,
18
+ ytDlpPath,
19
+ falApiKey,
20
+ openaiApiKey,
21
+ convertHtmlToMarkdown,
22
+ transcriptCache,
23
+ readTweetWithBird,
24
+ onProgress,
25
+ }),
26
+ };
27
+ }
28
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../../src/content/link-preview/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AA2BrD,MAAM,UAAU,uBAAuB,CAAC,UAAoC,EAAE;IAC5E,MAAM,SAAS,GACb,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAA8B,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;IACrF,MAAM,MAAM,GAA+B,OAAO,CAAC,mBAAmB,IAAI,IAAI,CAAA;IAC9E,MAAM,aAAa,GAAG,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAA;IAC9F,MAAM,SAAS,GAAG,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;IAClF,MAAM,SAAS,GAAG,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;IAClF,MAAM,YAAY,GAAG,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAA;IAC3F,MAAM,qBAAqB,GAAiC,OAAO,CAAC,qBAAqB,IAAI,IAAI,CAAA;IACjG,MAAM,eAAe,GAA2B,OAAO,CAAC,eAAe,IAAI,IAAI,CAAA;IAC/E,MAAM,iBAAiB,GACrB,OAAO,OAAO,CAAC,iBAAiB,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAA;IACpF,MAAM,UAAU,GAAG,OAAO,OAAO,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAA;IAEvF,OAAO;QACL,gBAAgB,EAAE,CAAC,GAAW,EAAE,cAAwC,EAAE,EAAE,CAC1E,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE;YACpC,KAAK,EAAE,SAAS;YAChB,mBAAmB,EAAE,MAAM;YAC3B,aAAa;YACb,SAAS;YACT,SAAS;YACT,YAAY;YACZ,qBAAqB;YACrB,eAAe;YACf,iBAAiB;YACjB,UAAU;SACX,CAAC;KACL,CAAA;AACH,CAAC"}
@@ -0,0 +1,155 @@
1
+ import { load } from 'cheerio';
2
+ import sanitizeHtml from 'sanitize-html';
3
+ import { decodeHtmlEntities, normalizeWhitespace } from './cleaner.js';
4
+ const MIN_SEGMENT_LENGTH = 30;
5
+ export function sanitizeHtmlForMarkdownConversion(html) {
6
+ return sanitizeHtml(html, {
7
+ allowedTags: [
8
+ 'article',
9
+ 'section',
10
+ 'div',
11
+ 'p',
12
+ 'h1',
13
+ 'h2',
14
+ 'h3',
15
+ 'h4',
16
+ 'h5',
17
+ 'h6',
18
+ 'ol',
19
+ 'ul',
20
+ 'li',
21
+ 'blockquote',
22
+ 'pre',
23
+ 'code',
24
+ 'span',
25
+ 'strong',
26
+ 'em',
27
+ 'br',
28
+ 'a',
29
+ ],
30
+ allowedAttributes: {
31
+ a: ['href'],
32
+ },
33
+ nonTextTags: [
34
+ 'style',
35
+ 'script',
36
+ 'noscript',
37
+ 'template',
38
+ 'svg',
39
+ 'canvas',
40
+ 'iframe',
41
+ 'object',
42
+ 'embed',
43
+ ],
44
+ textFilter(text) {
45
+ return decodeHtmlEntities(text);
46
+ },
47
+ });
48
+ }
49
+ export function extractArticleContent(html) {
50
+ const segments = collectSegmentsFromHtml(html);
51
+ if (segments.length > 0) {
52
+ return segments.join('\n');
53
+ }
54
+ const fallback = normalizeWhitespace(extractPlainText(html));
55
+ return fallback ?? '';
56
+ }
57
+ export function collectSegmentsFromHtml(html) {
58
+ const sanitized = sanitizeHtml(html, {
59
+ allowedTags: [
60
+ 'article',
61
+ 'section',
62
+ 'div',
63
+ 'p',
64
+ 'h1',
65
+ 'h2',
66
+ 'h3',
67
+ 'h4',
68
+ 'h5',
69
+ 'h6',
70
+ 'ol',
71
+ 'ul',
72
+ 'li',
73
+ 'blockquote',
74
+ 'pre',
75
+ 'code',
76
+ 'span',
77
+ 'strong',
78
+ 'em',
79
+ 'br',
80
+ ],
81
+ allowedAttributes: {},
82
+ nonTextTags: [
83
+ 'style',
84
+ 'script',
85
+ 'noscript',
86
+ 'template',
87
+ 'svg',
88
+ 'canvas',
89
+ 'iframe',
90
+ 'object',
91
+ 'embed',
92
+ ],
93
+ textFilter(text) {
94
+ return decodeHtmlEntities(text);
95
+ },
96
+ });
97
+ const $ = load(sanitized);
98
+ const segments = [];
99
+ $('h1,h2,h3,h4,h5,h6,li,p,blockquote,pre').each((_, element) => {
100
+ if (!('tagName' in element) || typeof element.tagName !== 'string') {
101
+ return;
102
+ }
103
+ const tag = element.tagName.toLowerCase();
104
+ const raw = $(element).text();
105
+ const text = normalizeWhitespace(raw).replaceAll(/\n+/g, ' ');
106
+ if (!text || text.length === 0) {
107
+ return;
108
+ }
109
+ if (tag.startsWith('h')) {
110
+ if (text.length >= 10) {
111
+ segments.push(text);
112
+ }
113
+ return;
114
+ }
115
+ if (tag === 'li') {
116
+ if (text.length >= 20) {
117
+ segments.push(`• ${text}`);
118
+ }
119
+ return;
120
+ }
121
+ if (text.length < MIN_SEGMENT_LENGTH) {
122
+ return;
123
+ }
124
+ segments.push(text);
125
+ });
126
+ if (segments.length === 0) {
127
+ const fallback = normalizeWhitespace($('body').text() || sanitized);
128
+ return fallback ? [fallback] : [];
129
+ }
130
+ return mergeConsecutiveSegments(segments);
131
+ }
132
+ export function extractPlainText(html) {
133
+ const stripped = sanitizeHtml(html, {
134
+ allowedTags: [],
135
+ allowedAttributes: {},
136
+ nonTextTags: [
137
+ 'style',
138
+ 'script',
139
+ 'noscript',
140
+ 'template',
141
+ 'svg',
142
+ 'canvas',
143
+ 'iframe',
144
+ 'object',
145
+ 'embed',
146
+ ],
147
+ });
148
+ return decodeHtmlEntities(stripped);
149
+ }
150
+ function mergeConsecutiveSegments(segments) {
151
+ // Keep headings as separate segments; merging short segments mostly collapses headings into the
152
+ // previous paragraph ("... Conclusion"), which reads worse than a standalone heading line.
153
+ return segments.filter(Boolean);
154
+ }
155
+ //# sourceMappingURL=article.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article.js","sourceRoot":"","sources":["../../../../../src/content/link-preview/content/article.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { compact } from 'es-toolkit';
2
+ const WORD_SPLIT_PATTERN = /\s+/g;
3
+ export function normalizeForPrompt(input) {
4
+ return input
5
+ .replaceAll('\u00A0', ' ')
6
+ .replaceAll(/[\t ]+/g, ' ')
7
+ .replaceAll(/\s*\n\s*/g, '\n')
8
+ .replaceAll(/\n{3,}/g, '\n\n')
9
+ .trim();
10
+ }
11
+ export function normalizeWhitespace(input) {
12
+ return input
13
+ .replaceAll('\u00A0', ' ')
14
+ .replaceAll(/[\t ]+/g, ' ')
15
+ .replaceAll(/\s*\n\s*/g, '\n')
16
+ .trim();
17
+ }
18
+ export function decodeHtmlEntities(input) {
19
+ return input
20
+ .replaceAll('&amp;', '&')
21
+ .replaceAll('&lt;', '<')
22
+ .replaceAll('&gt;', '>')
23
+ .replaceAll('&quot;', '"')
24
+ .replaceAll('&#39;', "'")
25
+ .replaceAll('&#x27;', "'")
26
+ .replaceAll('&#x2F;', '/')
27
+ .replaceAll('&nbsp;', ' ');
28
+ }
29
+ export function normalizeCandidate(value) {
30
+ if (!value) {
31
+ return null;
32
+ }
33
+ const trimmed = value.replaceAll(/\s+/g, ' ').trim();
34
+ return trimmed.length > 0 ? trimmed : null;
35
+ }
36
+ export function clipAtSentenceBoundary(input, maxLength) {
37
+ if (input.length <= maxLength) {
38
+ return input;
39
+ }
40
+ const slice = input.slice(0, maxLength);
41
+ const lastSentenceBreak = Math.max(slice.lastIndexOf('. '), slice.lastIndexOf('! '), slice.lastIndexOf('? '), slice.lastIndexOf('\n\n'));
42
+ if (lastSentenceBreak > maxLength * 0.5) {
43
+ return slice.slice(0, lastSentenceBreak + 1);
44
+ }
45
+ return slice;
46
+ }
47
+ export function applyContentBudget(baseContent, maxCharacters) {
48
+ const totalCharacters = baseContent.length;
49
+ const truncated = totalCharacters > maxCharacters;
50
+ const clipped = truncated ? clipAtSentenceBoundary(baseContent, maxCharacters) : baseContent;
51
+ const content = clipped.trim();
52
+ const wordCount = content.length > 0 ? compact(content.split(WORD_SPLIT_PATTERN)).length : 0;
53
+ return { content, truncated, totalCharacters, wordCount };
54
+ }
55
+ //# sourceMappingURL=cleaner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleaner.js","sourceRoot":"","sources":["../../../../../src/content/link-preview/content/cleaner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export const BLOCKED_HTML_HINT_PATTERN = /access denied|attention required|captcha|cloudflare|enable javascript|forbidden|please turn javascript on|verify you are human/i;
2
+ export const MIN_HTML_CONTENT_CHARACTERS = 200;
3
+ export const MIN_READABILITY_CONTENT_CHARACTERS = 200;
4
+ export const MIN_METADATA_DESCRIPTION_CHARACTERS = 120;
5
+ export const READABILITY_RELATIVE_THRESHOLD = 0.6;
6
+ export const MIN_HTML_DOCUMENT_CHARACTERS_FOR_FALLBACK = 5000;
7
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../src/content/link-preview/content/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GACpC,iIAAiI,CAAA;AACnI,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAA;AAC9C,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAG,CAAA;AACrD,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,CAAA;AACtD,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAA;AACjD,MAAM,CAAC,MAAM,yCAAyC,GAAG,IAAI,CAAA"}
@@ -0,0 +1,124 @@
1
+ import { isYouTubeUrl } from '../../transcript/utils.js';
2
+ import { appendNote } from './utils.js';
3
+ const REQUEST_HEADERS = {
4
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
5
+ Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
6
+ 'Accept-Language': 'en-US,en;q=0.9',
7
+ 'Cache-Control': 'no-cache',
8
+ Pragma: 'no-cache',
9
+ };
10
+ const DEFAULT_REQUEST_TIMEOUT_MS = 5000;
11
+ export async function fetchHtmlDocument(fetchImpl, url, { timeoutMs, onProgress, } = {}) {
12
+ onProgress?.({ kind: 'fetch-html-start', url });
13
+ const controller = new AbortController();
14
+ const effectiveTimeoutMs = typeof timeoutMs === 'number' && Number.isFinite(timeoutMs)
15
+ ? timeoutMs
16
+ : DEFAULT_REQUEST_TIMEOUT_MS;
17
+ const timeout = setTimeout(() => {
18
+ controller.abort();
19
+ }, effectiveTimeoutMs);
20
+ try {
21
+ const response = await fetchImpl(url, {
22
+ headers: REQUEST_HEADERS,
23
+ redirect: 'follow',
24
+ signal: controller.signal,
25
+ });
26
+ if (!response.ok) {
27
+ throw new Error(`Failed to fetch HTML document (status ${response.status})`);
28
+ }
29
+ const contentType = response.headers.get('content-type')?.toLowerCase() ?? null;
30
+ if (contentType &&
31
+ !contentType.includes('text/html') &&
32
+ !contentType.includes('application/xhtml+xml') &&
33
+ !contentType.includes('application/xml') &&
34
+ !contentType.includes('text/xml') &&
35
+ !contentType.includes('application/rss+xml') &&
36
+ !contentType.includes('application/atom+xml') &&
37
+ !contentType.startsWith('text/')) {
38
+ throw new Error(`Unsupported content-type for HTML document fetch: ${contentType}`);
39
+ }
40
+ const totalBytes = (() => {
41
+ const raw = response.headers.get('content-length');
42
+ if (!raw)
43
+ return null;
44
+ const parsed = Number(raw);
45
+ return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : null;
46
+ })();
47
+ const body = response.body;
48
+ if (!body) {
49
+ const text = await response.text();
50
+ const bytes = new TextEncoder().encode(text).byteLength;
51
+ onProgress?.({ kind: 'fetch-html-done', url, downloadedBytes: bytes, totalBytes });
52
+ return text;
53
+ }
54
+ const reader = body.getReader();
55
+ const decoder = new TextDecoder();
56
+ let downloadedBytes = 0;
57
+ let text = '';
58
+ onProgress?.({ kind: 'fetch-html-progress', url, downloadedBytes: 0, totalBytes });
59
+ while (true) {
60
+ const { value, done } = await reader.read();
61
+ if (done)
62
+ break;
63
+ if (!value)
64
+ continue;
65
+ downloadedBytes += value.byteLength;
66
+ text += decoder.decode(value, { stream: true });
67
+ onProgress?.({ kind: 'fetch-html-progress', url, downloadedBytes, totalBytes });
68
+ }
69
+ text += decoder.decode();
70
+ onProgress?.({ kind: 'fetch-html-done', url, downloadedBytes, totalBytes });
71
+ return text;
72
+ }
73
+ catch (error) {
74
+ if (error instanceof DOMException && error.name === 'AbortError') {
75
+ throw new Error('Fetching HTML document timed out');
76
+ }
77
+ throw error;
78
+ }
79
+ finally {
80
+ clearTimeout(timeout);
81
+ }
82
+ }
83
+ export async function fetchWithFirecrawl(url, scrapeWithFirecrawl, options = {}) {
84
+ const timeoutMs = options.timeoutMs;
85
+ const cacheMode = options.cacheMode ?? 'default';
86
+ const onProgress = typeof options.onProgress === 'function' ? options.onProgress : null;
87
+ const reason = typeof options.reason === 'string' ? options.reason : null;
88
+ const diagnostics = {
89
+ attempted: false,
90
+ used: false,
91
+ cacheMode,
92
+ cacheStatus: cacheMode === 'bypass' ? 'bypassed' : 'unknown',
93
+ notes: null,
94
+ };
95
+ if (isYouTubeUrl(url)) {
96
+ diagnostics.notes = appendNote(diagnostics.notes, 'Skipped Firecrawl for YouTube URL');
97
+ return { payload: null, diagnostics };
98
+ }
99
+ if (!scrapeWithFirecrawl) {
100
+ diagnostics.notes = appendNote(diagnostics.notes, 'Firecrawl is not configured');
101
+ return { payload: null, diagnostics };
102
+ }
103
+ diagnostics.attempted = true;
104
+ onProgress?.({ kind: 'firecrawl-start', url, reason: reason ?? 'firecrawl' });
105
+ try {
106
+ const payload = await scrapeWithFirecrawl(url, { timeoutMs, cacheMode });
107
+ if (!payload) {
108
+ diagnostics.notes = appendNote(diagnostics.notes, 'Firecrawl returned no content payload');
109
+ onProgress?.({ kind: 'firecrawl-done', url, ok: false, markdownBytes: null, htmlBytes: null });
110
+ return { payload: null, diagnostics };
111
+ }
112
+ const encoder = new TextEncoder();
113
+ const markdownBytes = typeof payload.markdown === 'string' ? encoder.encode(payload.markdown).byteLength : null;
114
+ const htmlBytes = typeof payload.html === 'string' ? encoder.encode(payload.html).byteLength : null;
115
+ onProgress?.({ kind: 'firecrawl-done', url, ok: true, markdownBytes, htmlBytes });
116
+ return { payload, diagnostics };
117
+ }
118
+ catch (error) {
119
+ diagnostics.notes = appendNote(diagnostics.notes, `Firecrawl error: ${error instanceof Error ? error.message : 'unknown error'}`);
120
+ onProgress?.({ kind: 'firecrawl-done', url, ok: false, markdownBytes: null, htmlBytes: null });
121
+ return { payload: null, diagnostics };
122
+ }
123
+ }
124
+ //# sourceMappingURL=fetcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../../../../../src/content/link-preview/content/fetcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { resolveTranscriptForLink } from '../../transcript/index.js';
2
+ import { extractArticleContent, extractPlainText } from './article.js';
3
+ import { normalizeForPrompt } from './cleaner.js';
4
+ import { BLOCKED_HTML_HINT_PATTERN, MIN_HTML_CONTENT_CHARACTERS, MIN_HTML_DOCUMENT_CHARACTERS_FOR_FALLBACK, MIN_METADATA_DESCRIPTION_CHARACTERS, READABILITY_RELATIVE_THRESHOLD, } from './constants.js';
5
+ import { extractJsonLdContent } from './jsonld.js';
6
+ import { extractMetadataFromFirecrawl, extractMetadataFromHtml } from './parsers.js';
7
+ import { isPodcastHost, isPodcastLikeJsonLdType } from './podcast-utils.js';
8
+ import { appendNote, ensureTranscriptDiagnostics, finalizeExtractedLinkContent, pickFirstText, safeHostname, selectBaseContent, } from './utils.js';
9
+ import { detectPrimaryVideoFromHtml } from './video.js';
10
+ export function shouldFallbackToFirecrawl(html) {
11
+ const plainText = normalizeForPrompt(extractPlainText(html));
12
+ if (BLOCKED_HTML_HINT_PATTERN.test(plainText))
13
+ return true;
14
+ const normalized = normalizeForPrompt(extractArticleContent(html));
15
+ if (normalized.length >= MIN_HTML_CONTENT_CHARACTERS) {
16
+ return false;
17
+ }
18
+ // Avoid spending Firecrawl on truly small/simple pages where the extracted HTML content is short but
19
+ // likely complete (e.g. https://example.com). Only treat "thin" content as a Firecrawl signal when
20
+ // the HTML document itself is large (SSR/app-shell pages, blocked pages without a match, etc.).
21
+ return html.length >= MIN_HTML_DOCUMENT_CHARACTERS_FOR_FALLBACK;
22
+ }
23
+ export async function buildResultFromFirecrawl({ url, payload, cacheMode, maxCharacters, youtubeTranscriptMode, firecrawlDiagnostics, markdownRequested, deps, }) {
24
+ const normalizedMarkdown = normalizeForPrompt(payload.markdown ?? '');
25
+ if (normalizedMarkdown.length === 0) {
26
+ firecrawlDiagnostics.notes = appendNote(firecrawlDiagnostics.notes, 'Firecrawl markdown normalization yielded empty text');
27
+ return null;
28
+ }
29
+ const jsonLd = payload.html ? extractJsonLdContent(payload.html) : null;
30
+ const isPodcastJsonLd = isPodcastLikeJsonLdType(jsonLd?.type);
31
+ const transcriptResolution = await resolveTranscriptForLink(url, payload.html ?? null, deps, {
32
+ youtubeTranscriptMode,
33
+ cacheMode,
34
+ });
35
+ const htmlMetadata = payload.html
36
+ ? extractMetadataFromHtml(payload.html, url)
37
+ : { title: null, description: null, siteName: null };
38
+ const metadata = extractMetadataFromFirecrawl(payload.metadata ?? null);
39
+ const title = pickFirstText([jsonLd?.title, metadata.title, htmlMetadata.title]);
40
+ const description = pickFirstText([
41
+ jsonLd?.description,
42
+ metadata.description,
43
+ htmlMetadata.description,
44
+ ]);
45
+ const siteName = pickFirstText([metadata.siteName, htmlMetadata.siteName, safeHostname(url)]);
46
+ const descriptionCandidate = description ? normalizeForPrompt(description) : '';
47
+ const preferDescription = descriptionCandidate.length >= MIN_METADATA_DESCRIPTION_CHARACTERS &&
48
+ (isPodcastJsonLd ||
49
+ isPodcastHost(url) ||
50
+ normalizedMarkdown.length < MIN_HTML_CONTENT_CHARACTERS ||
51
+ descriptionCandidate.length >= normalizedMarkdown.length * READABILITY_RELATIVE_THRESHOLD);
52
+ const baseCandidate = preferDescription ? descriptionCandidate : normalizedMarkdown;
53
+ const baseContent = selectBaseContent(baseCandidate, transcriptResolution.text);
54
+ if (baseContent.length === 0) {
55
+ firecrawlDiagnostics.notes = appendNote(firecrawlDiagnostics.notes, 'Firecrawl produced content that normalized to an empty string');
56
+ return null;
57
+ }
58
+ firecrawlDiagnostics.used = true;
59
+ const transcriptDiagnostics = ensureTranscriptDiagnostics(transcriptResolution, cacheMode ?? 'default');
60
+ const video = payload.html ? detectPrimaryVideoFromHtml(payload.html, url) : null;
61
+ const isVideoOnly = !transcriptResolution.text &&
62
+ normalizedMarkdown.length < MIN_HTML_CONTENT_CHARACTERS &&
63
+ video !== null;
64
+ return finalizeExtractedLinkContent({
65
+ url,
66
+ baseContent,
67
+ maxCharacters,
68
+ title,
69
+ description,
70
+ siteName,
71
+ transcriptResolution,
72
+ video,
73
+ isVideoOnly,
74
+ diagnostics: {
75
+ strategy: 'firecrawl',
76
+ firecrawl: firecrawlDiagnostics,
77
+ markdown: {
78
+ requested: markdownRequested,
79
+ used: true,
80
+ provider: 'firecrawl',
81
+ },
82
+ transcript: transcriptDiagnostics,
83
+ },
84
+ });
85
+ }
86
+ //# sourceMappingURL=firecrawl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firecrawl.js","sourceRoot":"","sources":["../../../../../src/content/link-preview/content/firecrawl.ts"],"names":[],"mappings":"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"}