@chatcops/ingest 0.0.0-stage → 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CoderCops
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,37 @@
1
- # Temporary Holding Version
1
+ # @chatcops/ingest
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Turns a website into a knowledge index a ChatCops bot can answer from, with citations. It discovers pages (sitemaps, URL lists, folders of Markdown or HTML), fetches and extracts the readable content, splits it into chunks along headings, embeds them, and writes them to a vector store.
4
+
5
+ ```ts
6
+ import { memoryStorage, memoryVectors, openaiEmbedder } from '@chatcops/core';
7
+ import { ingest } from '@chatcops/ingest';
8
+
9
+ const embedder = openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY! });
10
+ const stats = await ingest({
11
+ sources: [{ type: 'sitemap', url: 'https://example.com/sitemap.xml' }],
12
+ embedder,
13
+ store: memoryVectors({ dimensions: embedder.dimensions }),
14
+ manifest: memoryStorage().kv!,
15
+ });
16
+ console.log(stats);
17
+ ```
18
+
19
+ Use a persistent vector store (`@chatcops/store-postgres`, `store-sqlite`, `store-cloudflare` or `store-upstash-vector`) and the same storage's key-value store as the manifest, so later runs only embed what changed.
20
+
21
+ - Re-running is cheap: unchanged pages (same ETag, or the same extracted text) aren't fetched again or embedded again.
22
+ - Hidden elements, scripts, styles and HTML comments are removed before extraction, since that's where injected instructions usually hide.
23
+ - `prune: true` removes pages that are no longer discovered, but never after a sitemap failed to load.
24
+ - An index remembers its embedder and refuses another one; build a new index with a different `index` name instead.
25
+ - `dryRun: true` discovers, extracts and chunks without embedding or writing anything.
26
+
27
+ Node 22.13 or later.
28
+
29
+ ## robots.txt
30
+
31
+ Network sources respect `robots.txt` by default. Each run fetches it once per origin, selecting `ChatCopsBot` rules (or `*`), with the longest matching rule winning and `allow` winning ties. Wildcards and `$` end anchors are supported. Disallowed pages are skipped with the reason `blocked by robots.txt`.
32
+
33
+ A 4xx response allows crawling; a 5xx response or an unreachable robots server blocks it. Up to five redirects are followed. `crawl-delay` (seconds) sets a minimum interval between requests to an origin, including concurrent workers. Directory sources read local files and never check robots. Set `respectRobots: false` in the ingest config to opt out.
34
+
35
+ `robotsFor(origin, fetch)` also exposes `Sitemap:` entries as `sitemaps`; ingestion still uses the sources you configure.
36
+
37
+ When robots.txt is unavailable, affected pages are skipped with a reason such as `robots.txt unavailable (HTTP 503)`. The run reports one error per origin and skips pruning, preserving existing indexed pages until the rules can be read again.
@@ -0,0 +1,21 @@
1
+ import type { Chunk, ExtractedDoc } from './types.js';
2
+ export interface ChunkOptions {
3
+ /** About this many tokens per chunk. Default 500. */
4
+ targetTokens?: number;
5
+ /** Tokens repeated from the end of the previous chunk in the same section. Default 60. */
6
+ overlapTokens?: number;
7
+ }
8
+ /**
9
+ * A rough token count: about four characters per token, except CJK, where
10
+ * each character is about one. Good enough to size chunks; never billed on.
11
+ */
12
+ export declare function estimateTokens(text: string): number;
13
+ /**
14
+ * Splits a page into chunks: first at h1 to h3, then by packing blocks
15
+ * (paragraphs, lists, code blocks, tables) up to `targetTokens`. Code blocks
16
+ * and tables stay whole. Each chunk starts with its heading path
17
+ * (`Page title > Section > Subsection`), so a chunk read alone still says
18
+ * what it's about.
19
+ */
20
+ export declare function chunkDoc(doc: ExtractedDoc, options?: ChunkOptions): Chunk[];
21
+ //# sourceMappingURL=chunk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../src/chunk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAeD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,GAAE,YAAiB,GAAG,KAAK,EAAE,CAkB/E"}
package/dist/chunk.js ADDED
@@ -0,0 +1,162 @@
1
+ const CJK = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu;
2
+ /**
3
+ * A rough token count: about four characters per token, except CJK, where
4
+ * each character is about one. Good enough to size chunks; never billed on.
5
+ */
6
+ export function estimateTokens(text) {
7
+ const cjk = text.match(CJK)?.length ?? 0;
8
+ return cjk + Math.ceil((text.length - cjk) / 4);
9
+ }
10
+ /**
11
+ * Splits a page into chunks: first at h1 to h3, then by packing blocks
12
+ * (paragraphs, lists, code blocks, tables) up to `targetTokens`. Code blocks
13
+ * and tables stay whole. Each chunk starts with its heading path
14
+ * (`Page title > Section > Subsection`), so a chunk read alone still says
15
+ * what it's about.
16
+ */
17
+ export function chunkDoc(doc, options = {}) {
18
+ const target = Math.max(50, options.targetTokens ?? 500);
19
+ const overlap = Math.max(0, Math.min(options.overlapTokens ?? 60, Math.floor(target / 2)));
20
+ const chunks = [];
21
+ for (const section of sections(doc.markdown)) {
22
+ const crumbs = [doc.title, ...section.path].filter((part) => !!part);
23
+ const breadcrumb = dedupe(crumbs).join(' > ');
24
+ const section_ = section.path.length ? section.path.join(' > ') : undefined;
25
+ for (const body of pack(section.blocks.flatMap((block) => split(block, target)), target, overlap)) {
26
+ chunks.push({
27
+ index: chunks.length,
28
+ text: breadcrumb ? `${breadcrumb}\n\n${body}` : body,
29
+ ...(section_ ? { section: section_ } : {}),
30
+ });
31
+ }
32
+ }
33
+ return chunks;
34
+ }
35
+ function sections(markdown) {
36
+ const out = [];
37
+ // The current heading at each level (1 to 3); a heading clears the ones below it.
38
+ const levels = [];
39
+ let current = { path: [], blocks: [] };
40
+ const lines = markdown.split('\n');
41
+ const flush = () => {
42
+ if (current.blocks.length)
43
+ out.push(current);
44
+ };
45
+ for (let i = 0; i < lines.length;) {
46
+ const line = lines[i];
47
+ const heading = /^(#{1,3})\s+(.+?)\s*#*\s*$/.exec(line);
48
+ if (heading) {
49
+ flush();
50
+ const level = heading[1].length;
51
+ levels[level] = heading[2];
52
+ levels.length = level + 1;
53
+ current = { path: levels.filter((text) => !!text), blocks: [] };
54
+ i++;
55
+ continue;
56
+ }
57
+ if (/^\s*(```|~~~)/.test(line)) {
58
+ const fence = line.trim().slice(0, 3);
59
+ const start = i;
60
+ i++;
61
+ while (i < lines.length && !lines[i].trim().startsWith(fence))
62
+ i++;
63
+ i = Math.min(i + 1, lines.length);
64
+ current.blocks.push({ text: lines.slice(start, i).join('\n'), atomic: true });
65
+ continue;
66
+ }
67
+ if (/^\s*\|/.test(line)) {
68
+ const start = i;
69
+ while (i < lines.length && /^\s*\|/.test(lines[i]))
70
+ i++;
71
+ current.blocks.push({ text: lines.slice(start, i).join('\n'), atomic: true });
72
+ continue;
73
+ }
74
+ if (!line.trim()) {
75
+ i++;
76
+ continue;
77
+ }
78
+ const start = i;
79
+ while (i < lines.length && lines[i].trim() && !/^(#{1,3})\s/.test(lines[i]) && !/^\s*(```|~~~|\|)/.test(lines[i]))
80
+ i++;
81
+ current.blocks.push({ text: lines.slice(start, i).join('\n'), atomic: false });
82
+ }
83
+ flush();
84
+ return out;
85
+ }
86
+ /** A paragraph longer than a chunk, split at sentence ends (or, failing that, words). */
87
+ function split(block, target) {
88
+ if (block.atomic || estimateTokens(block.text) <= target)
89
+ return [block];
90
+ const sentences = block.text.match(/[^.!?。!?\n]+(?:[.!?。!?]+|\n|$)\s*/gu) ?? [block.text];
91
+ const pieces = [];
92
+ let piece = '';
93
+ for (const sentence of sentences) {
94
+ if (piece && estimateTokens(piece + sentence) > target) {
95
+ pieces.push({ text: piece.trim(), atomic: false });
96
+ piece = '';
97
+ }
98
+ if (estimateTokens(sentence) > target) {
99
+ for (const part of byLength(sentence, target))
100
+ pieces.push({ text: part, atomic: false });
101
+ }
102
+ else {
103
+ piece += sentence;
104
+ }
105
+ }
106
+ if (piece.trim())
107
+ pieces.push({ text: piece.trim(), atomic: false });
108
+ return pieces;
109
+ }
110
+ function byLength(text, target) {
111
+ const parts = [];
112
+ let part = '';
113
+ for (const char of text) {
114
+ part += char;
115
+ if (estimateTokens(part) >= target) {
116
+ parts.push(part.trim());
117
+ part = '';
118
+ }
119
+ }
120
+ if (part.trim())
121
+ parts.push(part.trim());
122
+ return parts;
123
+ }
124
+ /** Blocks packed into chunk bodies, each starting with the previous chunk's last blocks up to `overlap`. */
125
+ function pack(blocks, target, overlap) {
126
+ const bodies = [];
127
+ let current = [];
128
+ let tokens = 0;
129
+ let fresh = false;
130
+ const close = () => {
131
+ if (fresh)
132
+ bodies.push(current.map((block) => block.text).join('\n\n'));
133
+ const tail = [];
134
+ let tailTokens = 0;
135
+ for (let i = current.length - 1; i >= 0; i--) {
136
+ const block = current[i];
137
+ const size = estimateTokens(block.text);
138
+ if (block.atomic || tailTokens + size > overlap)
139
+ break;
140
+ tail.unshift(block);
141
+ tailTokens += size;
142
+ }
143
+ current = tail;
144
+ tokens = tailTokens;
145
+ fresh = false;
146
+ };
147
+ for (const block of blocks) {
148
+ const size = estimateTokens(block.text);
149
+ if (fresh && tokens + size > target)
150
+ close();
151
+ current.push(block);
152
+ tokens += size;
153
+ fresh = true;
154
+ }
155
+ if (fresh)
156
+ bodies.push(current.map((block) => block.text).join('\n\n'));
157
+ return bodies;
158
+ }
159
+ function dedupe(parts) {
160
+ return parts.filter((part, i) => i === 0 || part !== parts[i - 1]);
161
+ }
162
+ //# sourceMappingURL=chunk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk.js","sourceRoot":"","sources":["../src/chunk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { DiscoveredPage, IngestSource } from './types.js';
2
+ export interface DiscoverOptions {
3
+ fetch: typeof fetch;
4
+ userAgent: string;
5
+ timeoutMs: number;
6
+ include?: string[];
7
+ exclude?: string[];
8
+ }
9
+ export interface Discovery {
10
+ pages: DiscoveredPage[];
11
+ /** One line per problem. Any entry means pruning isn't safe. */
12
+ errors: string[];
13
+ }
14
+ /** Every page the sources list, once each, filtered by `include` and `exclude`. */
15
+ export declare function discover(sources: IngestSource[], options: DiscoverOptions): Promise<Discovery>;
16
+ /** The URL without its fragment, or undefined when it isn't http(s). */
17
+ export declare function normalizeUrl(url: string): string | undefined;
18
+ /**
19
+ * `*` matches within a path segment, `**` across segments, `?` one character.
20
+ * A double star followed by a slash also matches no segments at all, so
21
+ * "docs, any folders, *.md" matches docs/a.md as well as docs/x/a.md.
22
+ */
23
+ export declare function globToRegExp(glob: string): RegExp;
24
+ export declare function matches(url: string, include?: string[], exclude?: string[]): boolean;
25
+ //# sourceMappingURL=discover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,gEAAgE;IAChE,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,mFAAmF;AACnF,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAsBpG;AA2DD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAS5D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmBjD;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAGpF"}
@@ -0,0 +1,148 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import { join, relative, sep } from 'node:path';
3
+ import { gunzipSync } from 'node:zlib';
4
+ /** Sitemap indexes can point at more indexes; this many levels is plenty. */
5
+ const MAX_SITEMAP_DEPTH = 3;
6
+ const PAGE_FILES = /\.(md|mdx|html?)$/i;
7
+ /** Every page the sources list, once each, filtered by `include` and `exclude`. */
8
+ export async function discover(sources, options) {
9
+ const pages = new Map();
10
+ const errors = [];
11
+ const add = (page) => {
12
+ const url = normalizeUrl(page.url);
13
+ if (url && !pages.has(url) && matches(url, options.include, options.exclude))
14
+ pages.set(url, { ...page, url });
15
+ };
16
+ for (const source of sources) {
17
+ if (source.type === 'urls') {
18
+ for (const url of source.urls)
19
+ add({ url });
20
+ }
21
+ else if (source.type === 'sitemap') {
22
+ await readSitemap(source.url, options, 0, new Set(), add, errors);
23
+ }
24
+ else {
25
+ try {
26
+ for (const page of await readDirectory(source.path, source.baseUrl))
27
+ add(page);
28
+ }
29
+ catch (error) {
30
+ errors.push(`${source.path}: ${message(error)}`);
31
+ }
32
+ }
33
+ }
34
+ return { pages: [...pages.values()], errors };
35
+ }
36
+ async function readSitemap(url, options, depth, seen, add, errors) {
37
+ if (seen.has(url))
38
+ return;
39
+ seen.add(url);
40
+ if (depth > MAX_SITEMAP_DEPTH) {
41
+ errors.push(`${url}: sitemap indexes nested more than ${MAX_SITEMAP_DEPTH} deep`);
42
+ return;
43
+ }
44
+ let xml;
45
+ try {
46
+ const response = await options.fetch(url, {
47
+ headers: { 'user-agent': options.userAgent },
48
+ signal: AbortSignal.timeout(options.timeoutMs),
49
+ });
50
+ if (!response.ok)
51
+ throw new Error(`HTTP ${response.status}`);
52
+ const bytes = new Uint8Array(await response.arrayBuffer());
53
+ // gzip's magic number; some servers send .xml.gz without saying so.
54
+ xml = (bytes[0] === 0x1f && bytes[1] === 0x8b ? gunzipSync(bytes) : Buffer.from(bytes)).toString('utf8');
55
+ }
56
+ catch (error) {
57
+ errors.push(`${url}: ${message(error)}`);
58
+ return;
59
+ }
60
+ const locations = [...xml.matchAll(/<loc>\s*([^<]+?)\s*<\/loc>/gi)].map((match) => decodeXml(match[1]));
61
+ if (/<sitemapindex[\s>]/i.test(xml)) {
62
+ for (const child of locations)
63
+ await readSitemap(child, options, depth + 1, seen, add, errors);
64
+ }
65
+ else if (/<urlset[\s>]/i.test(xml)) {
66
+ for (const location of locations)
67
+ add({ url: location });
68
+ }
69
+ else {
70
+ errors.push(`${url}: not a sitemap (no <urlset> or <sitemapindex>)`);
71
+ }
72
+ }
73
+ /** Markdown and HTML files under `path`, with URLs under `baseUrl` (`docs/intro.md` is `<baseUrl>/docs/intro`). */
74
+ async function readDirectory(path, baseUrl) {
75
+ const entries = await readdir(path, { recursive: true, withFileTypes: true });
76
+ const base = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
77
+ return entries
78
+ .filter((entry) => entry.isFile() && PAGE_FILES.test(entry.name))
79
+ .map((entry) => {
80
+ const file = join(entry.parentPath, entry.name);
81
+ const route = relative(path, file)
82
+ .split(sep)
83
+ .join('/')
84
+ .replace(PAGE_FILES, '')
85
+ .replace(/(^|\/)index$/, '$1');
86
+ return { url: new URL(route, base).href, file };
87
+ })
88
+ .sort((a, b) => a.url.localeCompare(b.url));
89
+ }
90
+ /** The URL without its fragment, or undefined when it isn't http(s). */
91
+ export function normalizeUrl(url) {
92
+ try {
93
+ const parsed = new URL(url);
94
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
95
+ return undefined;
96
+ parsed.hash = '';
97
+ return parsed.href;
98
+ }
99
+ catch {
100
+ return undefined;
101
+ }
102
+ }
103
+ /**
104
+ * `*` matches within a path segment, `**` across segments, `?` one character.
105
+ * A double star followed by a slash also matches no segments at all, so
106
+ * "docs, any folders, *.md" matches docs/a.md as well as docs/x/a.md.
107
+ */
108
+ export function globToRegExp(glob) {
109
+ let source = '';
110
+ for (let i = 0; i < glob.length; i++) {
111
+ const char = glob[i];
112
+ if (char === '*' && glob[i + 1] === '*' && glob[i + 2] === '/') {
113
+ source += '(?:.*/)?';
114
+ i += 2;
115
+ }
116
+ else if (char === '*' && glob[i + 1] === '*') {
117
+ source += '.*';
118
+ i++;
119
+ }
120
+ else if (char === '*') {
121
+ source += '[^/]*';
122
+ }
123
+ else if (char === '?') {
124
+ source += '[^/]';
125
+ }
126
+ else {
127
+ source += char.replace(/[.+^${}()|[\]\\]/g, '\\$&');
128
+ }
129
+ }
130
+ return new RegExp(`^${source}$`);
131
+ }
132
+ export function matches(url, include, exclude) {
133
+ if (include?.length && !include.some((glob) => globToRegExp(glob).test(url)))
134
+ return false;
135
+ return !exclude?.some((glob) => globToRegExp(glob).test(url));
136
+ }
137
+ function decodeXml(text) {
138
+ return text
139
+ .replace(/&lt;/g, '<')
140
+ .replace(/&gt;/g, '>')
141
+ .replace(/&quot;/g, '"')
142
+ .replace(/&apos;/g, "'")
143
+ .replace(/&amp;/g, '&');
144
+ }
145
+ function message(error) {
146
+ return error instanceof Error ? error.message : String(error);
147
+ }
148
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { ExtractedDoc } from './types.js';
2
+ /**
3
+ * The readable content of an HTML page as Markdown. Hidden elements and
4
+ * comments go first. Then `selector`, when given, picks the content;
5
+ * otherwise Readability does, falling back to `<main>`, `<article>` or the
6
+ * body without its navigation, header, footer and sidebars.
7
+ */
8
+ export declare function extractHtml(html: string, url: string, selector?: string): ExtractedDoc;
9
+ /**
10
+ * A Markdown or MDX file as a document: front matter gives the title and
11
+ * description, and MDX `import`/`export` lines and component tags are
12
+ * removed, keeping the text between tags.
13
+ */
14
+ export declare function extractMarkdown(source: string, url: string): ExtractedDoc;
15
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAkC/C;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,CAqCtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAqBzE"}
@@ -0,0 +1,138 @@
1
+ import { Readability, isProbablyReaderable } from '@mozilla/readability';
2
+ import { parseHTML } from 'linkedom';
3
+ import TurndownService from 'turndown';
4
+ import { gfm } from 'turndown-plugin-gfm';
5
+ /**
6
+ * Where text that a visitor never sees sits in a page. Prompt injection
7
+ * usually hides in exactly these places, so they're removed before anything
8
+ * is read.
9
+ */
10
+ const INVISIBLE = [
11
+ 'script',
12
+ 'style',
13
+ 'noscript',
14
+ 'template',
15
+ 'iframe',
16
+ 'svg',
17
+ 'canvas',
18
+ 'object',
19
+ 'embed',
20
+ '[hidden]',
21
+ '[aria-hidden="true"]',
22
+ '[style*="display:none"]',
23
+ '[style*="display: none"]',
24
+ '[style*="visibility:hidden"]',
25
+ '[style*="visibility: hidden"]',
26
+ ].join(',');
27
+ /** Page chrome, dropped when Readability can't find the article. */
28
+ const CHROME = 'nav, header, footer, aside, form, [role="navigation"], [role="banner"], [role="contentinfo"]';
29
+ const turndown = new TurndownService({ headingStyle: 'atx', codeBlockStyle: 'fenced', bulletListMarker: '-' });
30
+ turndown.use(gfm);
31
+ // Images and link targets are noise for search; the link text stays.
32
+ turndown.addRule('no-images', { filter: 'img', replacement: () => '' });
33
+ turndown.addRule('link-text', { filter: 'a', replacement: (content) => content });
34
+ /**
35
+ * The readable content of an HTML page as Markdown. Hidden elements and
36
+ * comments go first. Then `selector`, when given, picks the content;
37
+ * otherwise Readability does, falling back to `<main>`, `<article>` or the
38
+ * body without its navigation, header, footer and sidebars.
39
+ */
40
+ export function extractHtml(html, url, selector) {
41
+ const { document } = parseHTML(html);
42
+ const lang = document.documentElement?.getAttribute('lang')?.trim() || undefined;
43
+ const description = document.querySelector('meta[name="description"]')?.getAttribute('content')?.trim() || undefined;
44
+ const pageTitle = document.querySelector('meta[property="og:title"]')?.getAttribute('content')?.trim() ||
45
+ document.querySelector('title')?.textContent?.trim() ||
46
+ undefined;
47
+ for (const element of [...document.querySelectorAll(INVISIBLE)])
48
+ element.remove();
49
+ removeComments(document);
50
+ let contentHtml;
51
+ let title = pageTitle;
52
+ if (selector) {
53
+ contentHtml = document.querySelector(selector)?.outerHTML;
54
+ }
55
+ if (contentHtml === undefined && isProbablyReaderable(document)) {
56
+ const article = new Readability(document.cloneNode(true)).parse();
57
+ if (article?.content) {
58
+ contentHtml = article.content;
59
+ title = article.title?.trim() || title;
60
+ }
61
+ }
62
+ if (contentHtml === undefined) {
63
+ const root = document.querySelector('main') ?? document.querySelector('article') ?? document.body;
64
+ for (const element of [...(root?.querySelectorAll(CHROME) ?? [])])
65
+ element.remove();
66
+ contentHtml = root?.innerHTML ?? '';
67
+ }
68
+ return {
69
+ url,
70
+ ...(title ? { title } : {}),
71
+ ...(lang ? { lang } : {}),
72
+ ...(description ? { description } : {}),
73
+ markdown: tidy(turndown.turndown(contentHtml)),
74
+ };
75
+ }
76
+ /**
77
+ * A Markdown or MDX file as a document: front matter gives the title and
78
+ * description, and MDX `import`/`export` lines and component tags are
79
+ * removed, keeping the text between tags.
80
+ */
81
+ export function extractMarkdown(source, url) {
82
+ let body = source.replace(/^\uFEFF/, '');
83
+ const frontMatter = {};
84
+ const front = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(body);
85
+ if (front) {
86
+ for (const line of front[1].split(/\r?\n/)) {
87
+ const field = /^(\w+):\s*(.*)$/.exec(line);
88
+ if (field)
89
+ frontMatter[field[1]] = field[2].trim().replace(/^(['"])(.*)\1$/, '$2');
90
+ }
91
+ body = body.slice(front[0].length);
92
+ }
93
+ body = withoutMdx(body);
94
+ const heading = /^#\s+(.+)$/m.exec(body)?.[1]?.trim();
95
+ const title = frontMatter.title || heading;
96
+ return {
97
+ url,
98
+ ...(title ? { title } : {}),
99
+ ...(frontMatter.description ? { description: frontMatter.description } : {}),
100
+ ...(frontMatter.lang ? { lang: frontMatter.lang } : {}),
101
+ markdown: tidy(body),
102
+ };
103
+ }
104
+ /** MDX syntax that isn't content, outside code fences. */
105
+ function withoutMdx(markdown) {
106
+ const out = [];
107
+ let inFence = false;
108
+ for (const line of markdown.split('\n')) {
109
+ const fence = /^\s*(```|~~~)/.test(line);
110
+ if (inFence || fence) {
111
+ out.push(line);
112
+ if (fence)
113
+ inFence = !inFence;
114
+ continue;
115
+ }
116
+ if (/^\s*(import|export)\s/.test(line))
117
+ continue;
118
+ out.push(line.replace(/<\/?[A-Z][\w.]*[^>]*>/g, ''));
119
+ }
120
+ return out.join('\n');
121
+ }
122
+ function removeComments(document) {
123
+ const all = [document, ...document.querySelectorAll('*')];
124
+ for (const node of all) {
125
+ for (const child of [...Array.from(node.childNodes ?? [])]) {
126
+ if (child.nodeType === 8)
127
+ child.remove();
128
+ }
129
+ }
130
+ }
131
+ /** At most one blank line in a row, no trailing spaces. */
132
+ function tidy(markdown) {
133
+ return markdown
134
+ .replace(/[ \t]+$/gm, '')
135
+ .replace(/\n{3,}/g, '\n\n')
136
+ .trim();
137
+ }
138
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { ingest, chunkId, manifestKeys, DEFAULT_USER_AGENT } from './ingest.js';
2
+ export { discover, globToRegExp, normalizeUrl, type Discovery, type DiscoverOptions } from './discover.js';
3
+ export { extractHtml, extractMarkdown } from './extract.js';
4
+ export { chunkDoc, estimateTokens, type ChunkOptions } from './chunk.js';
5
+ export { robotsFor, parseRobots, type Robots } from './robots.js';
6
+ export type { IngestConfig, IngestSource, IngestEvent, IngestStats, ExtractedDoc, Chunk, DiscoveredPage } from './types.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC3G,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { ingest, chunkId, manifestKeys, DEFAULT_USER_AGENT } from './ingest.js';
2
+ export { discover, globToRegExp, normalizeUrl } from './discover.js';
3
+ export { extractHtml, extractMarkdown } from './extract.js';
4
+ export { chunkDoc, estimateTokens } from './chunk.js';
5
+ export { robotsFor, parseRobots } from './robots.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAwC,MAAM,eAAe,CAAC;AAC3G,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAqB,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,aAAa,CAAC"}