@bpinternal/site-scout 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/README.md +92 -0
- package/dist/index.cjs +1797 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +211 -0
- package/dist/index.d.ts +211 -0
- package/dist/index.js +1762 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { z } from '@bpinternal/zui';
|
|
2
|
+
|
|
3
|
+
/** A source a URL was discovered from. Trust/recall differ per source. */
|
|
4
|
+
type DiscoverySource = 'llms' | 'sitemap' | 'robots' | 'discoverUrls' | 'search';
|
|
5
|
+
/** Per-URL metadata gathered during discovery (sparse; depends on source). */
|
|
6
|
+
type UrlMeta = {
|
|
7
|
+
sources: DiscoverySource[];
|
|
8
|
+
lastmod?: string;
|
|
9
|
+
priority?: number;
|
|
10
|
+
changefreq?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
searchRank?: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A node in the prioritized tree. Each node corresponds to ONE path segment
|
|
16
|
+
* (the root is the host). A node may be:
|
|
17
|
+
* - an internal "folder" (has children; `url` is the canonical folder URL if
|
|
18
|
+
* that path itself resolves), and/or
|
|
19
|
+
* - a "leaf" page (`url` set, usually no children).
|
|
20
|
+
* Folders carry `count` (# of leaf URLs anywhere beneath them) and an aggregate
|
|
21
|
+
* `score` so select() can reason about cardinality (10K products vs 670 docs)
|
|
22
|
+
* WITHOUT walking every leaf.
|
|
23
|
+
*/
|
|
24
|
+
type TreeNode = {
|
|
25
|
+
/** Path segment label for this node ('' for root; e.g. 'docs', 'api'). */
|
|
26
|
+
segment: string;
|
|
27
|
+
/** Full URL when this node is itself a real page (leaf or folder-with-page). */
|
|
28
|
+
url?: string;
|
|
29
|
+
/** Per-URL metadata, present when `url` is set. */
|
|
30
|
+
meta?: UrlMeta;
|
|
31
|
+
/** Child nodes keyed by their segment, sorted by score desc at read time. */
|
|
32
|
+
children: TreeNode[];
|
|
33
|
+
/** Deterministic priority score (aggregate for folders; own for leaves). */
|
|
34
|
+
score: number;
|
|
35
|
+
/** Number of leaf URLs at-or-below this node (own page counts as 1). */
|
|
36
|
+
count: number;
|
|
37
|
+
/** Human-readable signals contributing to `score` (explainable assertions). */
|
|
38
|
+
reasons?: string[];
|
|
39
|
+
};
|
|
40
|
+
/** One thing discovery did — surfaced via the onActivity callback + collected. */
|
|
41
|
+
type DiscoveryStep = {
|
|
42
|
+
kind: 'robots' | 'sitemap' | 'sitemap-index' | 'llms' | 'discoverUrls' | 'search' | 'redirect' | 'site-type' | 'note';
|
|
43
|
+
detail: string;
|
|
44
|
+
/** URLs/items yielded by this step, when meaningful. */
|
|
45
|
+
count?: number;
|
|
46
|
+
};
|
|
47
|
+
type DetectedSiteType = {
|
|
48
|
+
type: 'shopify' | 'unknown';
|
|
49
|
+
excludePatterns: string[];
|
|
50
|
+
includeHints: string[];
|
|
51
|
+
};
|
|
52
|
+
/** What discover() returns — the contract handed to select(). */
|
|
53
|
+
/** Why the pipeline stopped / how it resolved. */
|
|
54
|
+
type StopReason = 'ok' | 'unsupported_site' | 'no_sources' | 'time_limit_reached';
|
|
55
|
+
type MasterMap = {
|
|
56
|
+
/** Root of the prioritized tree (segment === ''). */
|
|
57
|
+
tree: TreeNode;
|
|
58
|
+
/** Total distinct leaf URLs in the tree. */
|
|
59
|
+
total: number;
|
|
60
|
+
/** Ordered log of what discovery did (also streamed via onActivity). */
|
|
61
|
+
steps: DiscoveryStep[];
|
|
62
|
+
/** True when discovery was aborted/timed-out and the tree is partial. */
|
|
63
|
+
aborted: boolean;
|
|
64
|
+
siteType: DetectedSiteType['type'];
|
|
65
|
+
/** Set when the site was refused by the guardrail (no discovery happened). */
|
|
66
|
+
unsupported?: boolean;
|
|
67
|
+
};
|
|
68
|
+
type DiscoverOptions = {
|
|
69
|
+
/** Topics/use-case hints — seed the site: search (find help/docs subdomains). */
|
|
70
|
+
topics?: string[];
|
|
71
|
+
/** Abort discovery; the tree built so far is returned (aborted: true). */
|
|
72
|
+
signal?: AbortSignal;
|
|
73
|
+
/** Soft wall-clock budget (ms) — discovery self-aborts at the deadline. */
|
|
74
|
+
budgetMs?: number;
|
|
75
|
+
/** Max distinct URLs to retain in the tree. */
|
|
76
|
+
maxUrls?: number;
|
|
77
|
+
/** Activity callback for live logging (searches, sitemaps, counts). */
|
|
78
|
+
onActivity?: (step: DiscoveryStep) => void;
|
|
79
|
+
/** `Date.parse`-able "now" for deterministic recency scoring in tests. */
|
|
80
|
+
now?: number;
|
|
81
|
+
};
|
|
82
|
+
type SelectOptions = {
|
|
83
|
+
/** Max URLs to return (default 100). */
|
|
84
|
+
limit?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Optional natural-language refinement of what to prioritize for this use
|
|
87
|
+
* case (e.g. "focus on developer docs and API reference"), passed to the LLM
|
|
88
|
+
* pick call as extra guidance.
|
|
89
|
+
*/
|
|
90
|
+
prompt?: string;
|
|
91
|
+
/** Company context passed to the LLM pick call. */
|
|
92
|
+
context?: {
|
|
93
|
+
company?: string;
|
|
94
|
+
website?: string;
|
|
95
|
+
overview?: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
type Selection = {
|
|
99
|
+
/** Prioritized URLs to scrape, best first, ≤ limit. */
|
|
100
|
+
urls: string[];
|
|
101
|
+
/** Distinct useful URLs before the limit was applied. */
|
|
102
|
+
useful: number;
|
|
103
|
+
};
|
|
104
|
+
/** Tunable weights for the deterministic scorer. Defaults target a support KB. */
|
|
105
|
+
type ScoreWeights = {
|
|
106
|
+
sectionWeights: Record<string, number>;
|
|
107
|
+
defaultSectionWeight: number;
|
|
108
|
+
depthPenalty: number;
|
|
109
|
+
depthWaivedSections: string[];
|
|
110
|
+
recencyBonus: number;
|
|
111
|
+
priorityBonus: number;
|
|
112
|
+
multiSourceBonus: number;
|
|
113
|
+
llmsBonus: number;
|
|
114
|
+
queryParamPenalty: number;
|
|
115
|
+
};
|
|
116
|
+
/** A discovered candidate page: a URL plus whatever metadata discovery gathered. */
|
|
117
|
+
type Candidate = {
|
|
118
|
+
url: string;
|
|
119
|
+
meta: UrlMeta;
|
|
120
|
+
};
|
|
121
|
+
/** A flat scored URL — produced by score.ts, consumed by the tree builder. */
|
|
122
|
+
type ScoredUrl = {
|
|
123
|
+
url: string;
|
|
124
|
+
score: number;
|
|
125
|
+
reasons: string[];
|
|
126
|
+
section: string;
|
|
127
|
+
meta: UrlMeta;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** Fetch a URL's body as text, SSRF-guarded against `baseHost`. Null on any failure. */
|
|
131
|
+
declare function fetchText(url: string, baseHost: string, timeoutMs?: number): Promise<string | null>;
|
|
132
|
+
/**
|
|
133
|
+
* Fetch + parse a JSON endpoint, SSRF-guarded against `baseHost`. Tolerates
|
|
134
|
+
* stores that serve JSON with a non-JSON content-type (e.g. `text/javascript`
|
|
135
|
+
* on Shopify's `/products.json`) by parsing the raw text. Null on any failure
|
|
136
|
+
* (network, non-2xx, blocked, unparseable).
|
|
137
|
+
*/
|
|
138
|
+
declare function fetchJson<T = unknown>(url: string, baseHost: string, timeoutMs?: number): Promise<T | null>;
|
|
139
|
+
/** The origin (scheme + host) of a base URL, tolerating a bare host. */
|
|
140
|
+
declare function siteOrigin(baseUrl: string): string | null;
|
|
141
|
+
/** Lower-cased hostname of a URL, tolerating a bare host. */
|
|
142
|
+
declare function hostOf(url: string): string | null;
|
|
143
|
+
|
|
144
|
+
type SearchResult = {
|
|
145
|
+
name: string;
|
|
146
|
+
url: string;
|
|
147
|
+
snippet?: string;
|
|
148
|
+
};
|
|
149
|
+
type DiscoverDeps = {
|
|
150
|
+
fetchText: typeof fetchText;
|
|
151
|
+
fetchJson: typeof fetchJson;
|
|
152
|
+
discoverUrls: (args: {
|
|
153
|
+
url: string;
|
|
154
|
+
count: number;
|
|
155
|
+
onlyHttps: boolean;
|
|
156
|
+
}) => Promise<{
|
|
157
|
+
urls: string[];
|
|
158
|
+
stopReason: string;
|
|
159
|
+
}>;
|
|
160
|
+
webSearch: (args: {
|
|
161
|
+
query: string;
|
|
162
|
+
includeSites?: string[];
|
|
163
|
+
count: number;
|
|
164
|
+
}) => Promise<{
|
|
165
|
+
results: SearchResult[];
|
|
166
|
+
}>;
|
|
167
|
+
};
|
|
168
|
+
declare function discover(website: string, opts: DiscoverOptions | undefined, deps: DiscoverDeps): Promise<MasterMap>;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The one LLM touchpoint, host-provided. Mirrors zai's `extract` shape:
|
|
172
|
+
* feed text + a schema + instructions, get validated structured output back.
|
|
173
|
+
*/
|
|
174
|
+
type ExtractFn = <S extends z.ZodType>(input: string, schema: S, opts: {
|
|
175
|
+
instructions: string;
|
|
176
|
+
}) => Promise<z.infer<S>>;
|
|
177
|
+
/**
|
|
178
|
+
* Optional record/replay cache around side-effecting calls (the analogue of
|
|
179
|
+
* viber's cognitive cache). Defaults to passthrough — production hosts don't
|
|
180
|
+
* need to provide it.
|
|
181
|
+
*/
|
|
182
|
+
type CacheFn = <T>(kind: string, keyParts: unknown, fn: () => Promise<T>) => Promise<T>;
|
|
183
|
+
type SelectDeps = {
|
|
184
|
+
extract: ExtractFn;
|
|
185
|
+
cache?: CacheFn;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
declare function select(map: MasterMap, opts: SelectOptions | undefined, deps: SelectDeps): Promise<Selection>;
|
|
189
|
+
|
|
190
|
+
declare function cachedDeps(group: string, real: SiteScoutDeps): SiteScoutDeps;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Everything the pipeline needs from its host: discovery I/O (fetch, URL
|
|
194
|
+
* discovery, web search) plus the one LLM touchpoint and its optional cache.
|
|
195
|
+
*/
|
|
196
|
+
type SiteScoutDeps = DiscoverDeps & SelectDeps;
|
|
197
|
+
type FindUrlsInput = {
|
|
198
|
+
website: string;
|
|
199
|
+
limit?: number;
|
|
200
|
+
} & Pick<SelectOptions, 'prompt' | 'context'> & Pick<DiscoverOptions, 'topics' | 'signal' | 'budgetMs' | 'onActivity'>;
|
|
201
|
+
/** Convenience: guard → discover → select in one call. */
|
|
202
|
+
declare function findKnowledgeUrls(input: FindUrlsInput, deps: SiteScoutDeps, extra?: {
|
|
203
|
+
now?: number;
|
|
204
|
+
}): Promise<Selection & {
|
|
205
|
+
discovered: number;
|
|
206
|
+
aborted: boolean;
|
|
207
|
+
siteType: string;
|
|
208
|
+
stopReason: StopReason;
|
|
209
|
+
}>;
|
|
210
|
+
|
|
211
|
+
export { type CacheFn, type Candidate, type DetectedSiteType, type DiscoverDeps, type DiscoverOptions, type DiscoverySource, type DiscoveryStep, type ExtractFn, type FindUrlsInput, type MasterMap, type ScoreWeights, type ScoredUrl, type SearchResult, type SelectDeps, type SelectOptions, type Selection, type SiteScoutDeps, type StopReason, type TreeNode, type UrlMeta, cachedDeps, discover, fetchJson, fetchText, findKnowledgeUrls, hostOf, select, siteOrigin };
|