@taurgis/bonsai 3.1.0 → 3.2.2
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 +21 -4
- package/agents/agents/salesforce-docs-researcher.agent.md +14 -1
- package/agents/instructions/salesforce-research.instructions.md +2 -1
- package/bin/cli.mjs +9 -17
- package/dist/base-command.d.ts +37 -10
- package/dist/base-command.js +113 -46
- package/dist/commands/config/base.js +11 -2
- package/dist/commands/config/get.js +8 -12
- package/dist/commands/config/index.js +2 -2
- package/dist/commands/config/list.js +10 -16
- package/dist/commands/config/set.d.ts +2 -1
- package/dist/commands/config/set.js +34 -27
- package/dist/commands/config/unset.d.ts +2 -1
- package/dist/commands/config/unset.js +25 -24
- package/dist/commands/fetch.d.ts +7 -15
- package/dist/commands/fetch.js +58 -401
- package/dist/commands/import.d.ts +4 -9
- package/dist/commands/import.js +56 -280
- package/dist/commands/inspect.d.ts +7 -1
- package/dist/commands/inspect.js +103 -52
- package/dist/commands/list.d.ts +13 -1
- package/dist/commands/list.js +79 -32
- package/dist/commands/prune.d.ts +4 -1
- package/dist/commands/prune.js +56 -65
- package/dist/commands/status.d.ts +3 -2
- package/dist/commands/status.js +52 -79
- package/dist/hooks/command-not-found/suggest.d.ts +1 -0
- package/dist/hooks/command-not-found/suggest.js +66 -39
- package/dist/lib/argv.d.ts +24 -3
- package/dist/lib/argv.js +124 -53
- package/dist/lib/batch.d.ts +41 -0
- package/dist/lib/batch.js +39 -0
- package/dist/lib/cache-view.d.ts +21 -0
- package/dist/lib/cache-view.js +30 -0
- package/dist/lib/cli-emit.d.ts +28 -0
- package/dist/lib/cli-emit.js +44 -0
- package/dist/lib/cli-error-policy.d.ts +57 -0
- package/dist/lib/cli-error-policy.js +74 -0
- package/dist/lib/cli-flag-manifest.d.ts +5 -0
- package/dist/lib/cli-flag-manifest.js +8 -0
- package/dist/lib/cli-presentation.d.ts +39 -0
- package/dist/lib/cli-presentation.js +50 -0
- package/dist/lib/cli-result-types.d.ts +88 -0
- package/dist/lib/cli-result-types.js +2 -0
- package/dist/lib/config/index.d.ts +22 -4
- package/dist/lib/config/index.js +59 -4
- package/dist/lib/config/resolve.d.ts +21 -0
- package/dist/lib/config/resolve.js +55 -6
- package/dist/lib/config-persist.d.ts +28 -0
- package/dist/lib/config-persist.js +29 -0
- package/dist/lib/envelope.d.ts +41 -3
- package/dist/lib/envelope.js +147 -4
- package/dist/lib/help-preflight.d.ts +5 -8
- package/dist/lib/help-preflight.js +13 -32
- package/dist/lib/json-meta.d.ts +0 -2
- package/dist/lib/json-meta.js +21 -25
- package/dist/lib/limit-flag.d.ts +11 -1
- package/dist/lib/limit-flag.js +19 -6
- package/dist/lib/parse-error-ux.d.ts +5 -0
- package/dist/lib/parse-error-ux.js +73 -0
- package/dist/lib/prune-flags.d.ts +28 -0
- package/dist/lib/prune-flags.js +74 -0
- package/dist/lib/research/artifact-index.d.ts +11 -2
- package/dist/lib/research/artifact-index.js +7 -4
- package/dist/lib/research/browser.d.ts +41 -2
- package/dist/lib/research/browser.js +139 -50
- package/dist/lib/research/capture.d.ts +24 -2
- package/dist/lib/research/capture.js +24 -4
- package/dist/lib/research/cli-io.d.ts +33 -0
- package/dist/lib/research/cli-io.js +27 -0
- package/dist/lib/research/compress.d.ts +7 -0
- package/dist/lib/research/compress.js +7 -3
- package/dist/lib/research/docs/capabilities.d.ts +15 -0
- package/dist/lib/research/docs/capabilities.js +5 -0
- package/dist/lib/research/docs/clean-dom.d.ts +2 -0
- package/dist/lib/research/docs/clean-dom.js +2 -0
- package/dist/lib/research/docs/code-blocks.d.ts +2 -0
- package/dist/lib/research/docs/code-blocks.js +2 -0
- package/dist/lib/research/docs/detect.d.ts +4 -0
- package/dist/lib/research/docs/detect.js +4 -0
- package/dist/lib/research/docs/machine-readable.d.ts +18 -0
- package/dist/lib/research/docs/machine-readable.js +10 -0
- package/dist/lib/research/docs/markdown-source.d.ts +20 -0
- package/dist/lib/research/docs/markdown-source.js +19 -2
- package/dist/lib/research/docs/quality-gates.d.ts +5 -0
- package/dist/lib/research/docs/quality-gates.js +4 -0
- package/dist/lib/research/docs/rsc.d.ts +9 -2
- package/dist/lib/research/docs/rsc.js +5 -2
- package/dist/lib/research/docs/section-artifacts.d.ts +12 -0
- package/dist/lib/research/docs/section-artifacts.js +12 -0
- package/dist/lib/research/docs/sections.d.ts +18 -0
- package/dist/lib/research/docs/sections.js +17 -1
- package/dist/lib/research/docs/source-map.d.ts +28 -0
- package/dist/lib/research/docs/source-map.js +27 -0
- package/dist/lib/research/docs/validate.d.ts +35 -2
- package/dist/lib/research/docs/validate.js +47 -2
- package/dist/lib/research/extract.d.ts +16 -0
- package/dist/lib/research/extract.js +12 -1
- package/dist/lib/research/fetch-command-service.d.ts +36 -0
- package/dist/lib/research/fetch-command-service.js +310 -0
- package/dist/lib/research/fetch-result.d.ts +114 -0
- package/dist/lib/research/fetch-result.js +212 -0
- package/dist/lib/research/fetcher.d.ts +46 -8
- package/dist/lib/research/fetcher.js +58 -30
- package/dist/lib/research/freshness.d.ts +54 -11
- package/dist/lib/research/freshness.js +91 -31
- package/dist/lib/research/import-command-service.d.ts +56 -0
- package/dist/lib/research/import-command-service.js +381 -0
- package/dist/lib/research/keywords.js +5 -1
- package/dist/lib/research/markdown.d.ts +12 -1
- package/dist/lib/research/markdown.js +13 -5
- package/dist/lib/research/persist-artifact.d.ts +23 -0
- package/dist/lib/research/persist-artifact.js +27 -0
- package/dist/lib/research/prompt-injection.d.ts +8 -0
- package/dist/lib/research/prompt-injection.js +42 -13
- package/dist/lib/research/proxy.d.ts +14 -8
- package/dist/lib/research/proxy.js +14 -10
- package/dist/lib/research/revalidate.d.ts +49 -20
- package/dist/lib/research/revalidate.js +80 -15
- package/dist/lib/research/schema.d.ts +7 -0
- package/dist/lib/research/schema.js +2 -0
- package/dist/lib/research/secret-scan.d.ts +6 -0
- package/dist/lib/research/secret-scan.js +8 -0
- package/dist/lib/research/secure-write.d.ts +11 -1
- package/dist/lib/research/secure-write.js +38 -7
- package/dist/lib/research/storage.d.ts +5 -1
- package/dist/lib/research/storage.js +8 -3
- package/dist/lib/research/summarize.d.ts +15 -0
- package/dist/lib/research/summarize.js +11 -0
- package/dist/lib/research/url.d.ts +9 -6
- package/dist/lib/research/url.js +33 -21
- package/dist/lib/text.d.ts +7 -2
- package/dist/lib/text.js +13 -2
- package/dist/lib/value-flags.d.ts +14 -0
- package/dist/lib/value-flags.js +28 -0
- package/dist/sites/coveo.d.ts +10 -0
- package/dist/sites/coveo.js +10 -0
- package/dist/sites/index.d.ts +16 -0
- package/dist/sites/index.js +16 -4
- package/dist/sites/markdown-twin.d.ts +31 -0
- package/dist/sites/markdown-twin.js +56 -0
- package/dist/sites/salesforce/fetch-page.d.ts +8 -2
- package/dist/sites/salesforce/fetch-page.js +14 -3
- package/dist/sites/salesforce/help-tooling-markdown.d.ts +30 -0
- package/dist/sites/salesforce/help-tooling-markdown.js +74 -0
- package/dist/sites/salesforce/index.d.ts +1 -0
- package/dist/sites/salesforce/index.js +1 -0
- package/dist/sites/salesforce-developer/fetch-page.d.ts +6 -1
- package/dist/sites/salesforce-developer/fetch-page.js +6 -1
- package/dist/sites/salesforce-developer/index.d.ts +1 -0
- package/dist/sites/salesforce-developer/index.js +1 -0
- package/dist/sites/salesforce-developer/route-markdown.d.ts +18 -6
- package/dist/sites/salesforce-developer/route-markdown.js +29 -67
- package/dist/sites/salesforce-doc-fetch.d.ts +39 -5
- package/dist/sites/salesforce-doc-fetch.js +52 -7
- package/dist/sites/salesforce-dom-probe.d.ts +20 -2
- package/dist/sites/salesforce-dom-probe.js +20 -2
- package/dist/sites/types.d.ts +14 -8
- package/dist/sites/types.js +3 -0
- package/oclif.manifest.json +192 -87
- package/package.json +19 -7
- package/agents/agents/official-docs-researcher.agent.md +0 -87
- package/agents/instructions/web-research.instructions.md +0 -44
- package/agents/skills/web-research/SKILL.md +0 -118
|
@@ -37,7 +37,8 @@ export function getArtifactPath(dataDir, key) {
|
|
|
37
37
|
return join(dataDir, 'research', `${key}.md`);
|
|
38
38
|
}
|
|
39
39
|
function processFileForLookup(file, dir, key) {
|
|
40
|
-
|
|
40
|
+
// Same file filter as scanCacheDir — superseded/corrupt/tmp archives must not resolve as active.
|
|
41
|
+
if (!isResearchFile(file)) {
|
|
41
42
|
return null;
|
|
42
43
|
}
|
|
43
44
|
const filePath = join(dir, file);
|
|
@@ -162,12 +163,16 @@ export function locateArtifact(dataDirs, key) {
|
|
|
162
163
|
* order and deduplicates by cache key so an entry present in an earlier root (project) shadows
|
|
163
164
|
* the same key in a later root (global) — even when the callback filters it out. Composes
|
|
164
165
|
* scanCacheDir so the file-format and read/parse rules stay defined in exactly one place.
|
|
166
|
+
*
|
|
167
|
+
* Pass `persistIndex: false` under read-only/plan mode so listing never creates `.search-index.json`.
|
|
165
168
|
*/
|
|
166
|
-
export function scanCacheDirs(dataDirs, fn) {
|
|
169
|
+
export function scanCacheDirs(dataDirs, fn, options = {}) {
|
|
167
170
|
const seen = new Set();
|
|
168
171
|
return dataDirs.flatMap((dataDir) => {
|
|
169
172
|
const researchDir = join(dataDir, 'research');
|
|
170
|
-
const searchable = loadIndexedArtifactsForDir(researchDir
|
|
173
|
+
const searchable = loadIndexedArtifactsForDir(researchDir, {
|
|
174
|
+
persist: options.persistIndex,
|
|
175
|
+
});
|
|
171
176
|
return searchable
|
|
172
177
|
.map(({ artifact, filePath }) => {
|
|
173
178
|
const key = artifact.metadata.cache_key;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { SummaryLevel } from '../config/schema.js';
|
|
2
|
+
/** Re-export of the configured summary aggressiveness level (`conservative` | `balanced` | `aggressive`). */
|
|
2
3
|
export type { SummaryLevel };
|
|
4
|
+
/**
|
|
5
|
+
* Options for `summarizeMarkdown`. All fields are optional; omitted values use the documented defaults.
|
|
6
|
+
*/
|
|
3
7
|
export interface SummarizeOptions {
|
|
4
8
|
/** Aggressiveness of prose reduction. Default 'conservative'. */
|
|
5
9
|
level?: SummaryLevel;
|
|
@@ -8,10 +12,21 @@ export interface SummarizeOptions {
|
|
|
8
12
|
/** Prose paragraphs with fewer sentences than this are kept whole. Default 2. */
|
|
9
13
|
minSentences?: number;
|
|
10
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Splits a prose paragraph into individual sentences. Avoids false boundaries inside inline code,
|
|
17
|
+
* after known abbreviations, and before digits.
|
|
18
|
+
*
|
|
19
|
+
* @param paragraph - A single prose paragraph (no Markdown structure like headings or lists).
|
|
20
|
+
* @returns Array of sentence strings in document order.
|
|
21
|
+
*/
|
|
11
22
|
export declare function splitSentences(paragraph: string): string[];
|
|
12
23
|
/**
|
|
13
24
|
* Extractively shortens the prose paragraphs of a Markdown document while preserving headings,
|
|
14
25
|
* fenced code blocks, tables, and lists verbatim. Returns the input unchanged when it is below
|
|
15
26
|
* `minTokens` or contains no prose to condense. Deterministic for identical input + options.
|
|
27
|
+
*
|
|
28
|
+
* @param markdown - Markdown document to summarize.
|
|
29
|
+
* @param options - Summarization options (level, minTokens, minSentences).
|
|
30
|
+
* @returns Summarized Markdown, or the original when no summarization is needed.
|
|
16
31
|
*/
|
|
17
32
|
export declare function summarizeMarkdown(markdown: string, options?: SummarizeOptions): string;
|
|
@@ -166,6 +166,13 @@ function shouldSkipBoundary(masked, i, end) {
|
|
|
166
166
|
// Splits a single prose paragraph into sentences, avoiding splits inside inline code, after known
|
|
167
167
|
// abbreviations or single-letter initials, and before digits. Exported for unit testing because
|
|
168
168
|
// sentence boundaries are not observable once kept sentences are rejoined.
|
|
169
|
+
/**
|
|
170
|
+
* Splits a prose paragraph into individual sentences. Avoids false boundaries inside inline code,
|
|
171
|
+
* after known abbreviations, and before digits.
|
|
172
|
+
*
|
|
173
|
+
* @param paragraph - A single prose paragraph (no Markdown structure like headings or lists).
|
|
174
|
+
* @returns Array of sentence strings in document order.
|
|
175
|
+
*/
|
|
169
176
|
export function splitSentences(paragraph) {
|
|
170
177
|
const { masked, restore } = maskInlineCode(paragraph);
|
|
171
178
|
const sentences = [];
|
|
@@ -381,6 +388,10 @@ function reassemble(segments, sentences, kept) {
|
|
|
381
388
|
* Extractively shortens the prose paragraphs of a Markdown document while preserving headings,
|
|
382
389
|
* fenced code blocks, tables, and lists verbatim. Returns the input unchanged when it is below
|
|
383
390
|
* `minTokens` or contains no prose to condense. Deterministic for identical input + options.
|
|
391
|
+
*
|
|
392
|
+
* @param markdown - Markdown document to summarize.
|
|
393
|
+
* @param options - Summarization options (level, minTokens, minSentences).
|
|
394
|
+
* @returns Summarized Markdown, or the original when no summarization is needed.
|
|
384
395
|
*/
|
|
385
396
|
export function summarizeMarkdown(markdown, options = {}) {
|
|
386
397
|
const level = options.level ?? 'conservative';
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Checks if a literal IPv4 address is in a safe range.
|
|
3
|
-
* Rejects loopback, private RFC1918, and link-local
|
|
3
|
+
* Rejects "this network", loopback, private RFC1918, shared address space (CGNAT), and link-local
|
|
4
|
+
* ranges — the non-globally-reachable blocks in the IANA IPv4 special-purpose registry
|
|
5
|
+
* (https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml).
|
|
4
6
|
*/
|
|
5
7
|
export declare function isSafeIpv4(ip: string): boolean;
|
|
6
8
|
/**
|
|
7
9
|
* Checks if a literal IPv6 address is in a safe range.
|
|
8
|
-
* Rejects loopback, unspecified, link-local, and mapped unsafe IPv4 ranges.
|
|
10
|
+
* Rejects loopback, unspecified, link-local, unique-local, and mapped unsafe IPv4 ranges.
|
|
9
11
|
*/
|
|
10
12
|
export declare function isSafeIpv6(ip: string): boolean;
|
|
11
13
|
/**
|
|
@@ -13,10 +15,6 @@ export declare function isSafeIpv6(ip: string): boolean;
|
|
|
13
15
|
* Returns false for loopback, private, link-local, or unspecified ranges.
|
|
14
16
|
*/
|
|
15
17
|
export declare function isSafeIp(ip: string): boolean;
|
|
16
|
-
/**
|
|
17
|
-
* Validates a hostname to ensure it is not blocked (e.g. localhost or literal private IP).
|
|
18
|
-
*/
|
|
19
|
-
export declare function validateHostname(hostname: string): void;
|
|
20
18
|
/**
|
|
21
19
|
* Whether the input looks like a URL that simply forgot its `http(s)://` scheme
|
|
22
20
|
* (`example.com`, `docs.nestjs.com/guide`, `192.168.1.1`) rather than unparseable junk.
|
|
@@ -40,6 +38,11 @@ export declare function artifactMatchesUrlFilter(meta: {
|
|
|
40
38
|
source_urls?: string[];
|
|
41
39
|
source_url?: string;
|
|
42
40
|
}, filterUrl: string | undefined): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Reject empty/whitespace `--url` globs on list/prune. Almost always a shell-quoting mistake;
|
|
43
|
+
* matching the empty string silently would look like "no results" instead of bad input.
|
|
44
|
+
*/
|
|
45
|
+
export declare function emptyUrlFilterError(url: string | undefined): string | undefined;
|
|
43
46
|
/**
|
|
44
47
|
* Checks if a filename matches the standard research note naming pattern.
|
|
45
48
|
*/
|
package/dist/lib/research/url.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { isIP } from 'node:net';
|
|
2
2
|
/**
|
|
3
3
|
* Checks if a literal IPv4 address is in a safe range.
|
|
4
|
-
* Rejects loopback, private RFC1918, and link-local
|
|
4
|
+
* Rejects "this network", loopback, private RFC1918, shared address space (CGNAT), and link-local
|
|
5
|
+
* ranges — the non-globally-reachable blocks in the IANA IPv4 special-purpose registry
|
|
6
|
+
* (https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml).
|
|
5
7
|
*/
|
|
6
8
|
export function isSafeIpv4(ip) {
|
|
7
9
|
const parts = ip.split('.').map(Number);
|
|
@@ -10,6 +12,10 @@ export function isSafeIpv4(ip) {
|
|
|
10
12
|
const [a, b, c] = parts;
|
|
11
13
|
if (a === undefined || b === undefined || c === undefined)
|
|
12
14
|
return false;
|
|
15
|
+
// "This network" / unspecified: 0.0.0.0/8 (includes 0.0.0.0 itself, a well-known SSRF-filter
|
|
16
|
+
// bypass — many stacks route it to the loopback interface, same as 127.0.0.1).
|
|
17
|
+
if (a === 0)
|
|
18
|
+
return false;
|
|
13
19
|
// Loopback: 127.0.0.0/8
|
|
14
20
|
if (a === 127)
|
|
15
21
|
return false;
|
|
@@ -20,6 +26,9 @@ export function isSafeIpv4(ip) {
|
|
|
20
26
|
return false;
|
|
21
27
|
if (a === 192 && b === 168)
|
|
22
28
|
return false;
|
|
29
|
+
// Shared Address Space / CGNAT (RFC6598): 100.64.0.0/10
|
|
30
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
31
|
+
return false;
|
|
23
32
|
// Link-local: 169.254.0.0/16
|
|
24
33
|
if (a === 169 && b === 254)
|
|
25
34
|
return false;
|
|
@@ -27,7 +36,7 @@ export function isSafeIpv4(ip) {
|
|
|
27
36
|
}
|
|
28
37
|
/**
|
|
29
38
|
* Checks if a literal IPv6 address is in a safe range.
|
|
30
|
-
* Rejects loopback, unspecified, link-local, and mapped unsafe IPv4 ranges.
|
|
39
|
+
* Rejects loopback, unspecified, link-local, unique-local, and mapped unsafe IPv4 ranges.
|
|
31
40
|
*/
|
|
32
41
|
export function isSafeIpv6(ip) {
|
|
33
42
|
const cleanIp = ip.toLowerCase().trim();
|
|
@@ -44,6 +53,13 @@ export function isSafeIpv6(ip) {
|
|
|
44
53
|
cleanIp.startsWith('feb')) {
|
|
45
54
|
return false;
|
|
46
55
|
}
|
|
56
|
+
// Unique Local Address (RFC4193): fc00::/7 (fc00 to fdff) — IPv6's RFC1918 equivalent, commonly
|
|
57
|
+
// used for internal/container networks. Only a full 4-digit first hextet can reach 0xfc00; a
|
|
58
|
+
// shorter one (leading zeros suppressed) is always < 0x1000 and safely out of range.
|
|
59
|
+
const firstGroup = cleanIp.split(':')[0] ?? '';
|
|
60
|
+
if (firstGroup.length === 4 && (firstGroup.startsWith('fc') || firstGroup.startsWith('fd'))) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
47
63
|
// IPv4-mapped IPv6: ::ffff:127.0.0.1
|
|
48
64
|
if (cleanIp.startsWith('::ffff:')) {
|
|
49
65
|
const ipv4Part = ip.slice(7);
|
|
@@ -65,24 +81,6 @@ export function isSafeIp(ip) {
|
|
|
65
81
|
return isSafeIpv6(ip);
|
|
66
82
|
return false;
|
|
67
83
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Validates a hostname to ensure it is not blocked (e.g. localhost or literal private IP).
|
|
70
|
-
*/
|
|
71
|
-
export function validateHostname(hostname) {
|
|
72
|
-
const lower = hostname.toLowerCase().trim();
|
|
73
|
-
if (lower === 'localhost' || lower.endsWith('.localhost')) {
|
|
74
|
-
throw new Error(`Hostname "${hostname}" is a blocked local target.`);
|
|
75
|
-
}
|
|
76
|
-
let ipToCheck = lower;
|
|
77
|
-
if (ipToCheck.startsWith('[') && ipToCheck.endsWith(']')) {
|
|
78
|
-
ipToCheck = ipToCheck.slice(1, -1);
|
|
79
|
-
}
|
|
80
|
-
if (isIP(ipToCheck) !== 0) {
|
|
81
|
-
if (!isSafeIp(ipToCheck)) {
|
|
82
|
-
throw new Error(`IP address "${ipToCheck}" is a blocked local or private target.`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
84
|
// Dot-separated, non-empty labels: a real domain (`docs.nestjs.com`) or IPv4 (`192.168.1.1`). The
|
|
87
85
|
// URL parser also accepts a leading-dot filename (`.env`) or relative path (`./x`) as a "host", whose
|
|
88
86
|
// suggestion would be a nonsense `https://.env`; requiring well-formed labels rejects those. Anchored
|
|
@@ -133,7 +131,9 @@ export function normalizeUrl(input) {
|
|
|
133
131
|
if (url.username || url.password) {
|
|
134
132
|
throw new Error('URLs containing usernames or passwords are rejected.');
|
|
135
133
|
}
|
|
136
|
-
|
|
134
|
+
// Localhost / private hosts are valid cache keys (import + cache-hit lookup). SSRF protection
|
|
135
|
+
// lives in the network layer (fetcher/browser DNS checks), so a later `bonsai http://localhost/…`
|
|
136
|
+
// can serve an imported note without ever opening a socket — while a cache miss still fails safely.
|
|
137
137
|
// Clear default ports
|
|
138
138
|
if (url.protocol === 'https:' && url.port === '443') {
|
|
139
139
|
url.port = '';
|
|
@@ -175,6 +175,18 @@ export function artifactMatchesUrlFilter(meta, filterUrl) {
|
|
|
175
175
|
const urls = meta.source_urls || (meta.source_url ? [meta.source_url] : []);
|
|
176
176
|
return urls.some((u) => matchesUrlGlob(u, filterUrl));
|
|
177
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Reject empty/whitespace `--url` globs on list/prune. Almost always a shell-quoting mistake;
|
|
180
|
+
* matching the empty string silently would look like "no results" instead of bad input.
|
|
181
|
+
*/
|
|
182
|
+
export function emptyUrlFilterError(url) {
|
|
183
|
+
if (url === undefined)
|
|
184
|
+
return undefined;
|
|
185
|
+
if (url.trim() === '') {
|
|
186
|
+
return '--url must be a non-empty glob pattern (e.g. "https://react.dev/*").';
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
178
190
|
/**
|
|
179
191
|
* Checks if a filename matches the standard research note naming pattern.
|
|
180
192
|
*/
|
package/dist/lib/text.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ export interface ResultListLabels {
|
|
|
18
18
|
/** Heading for a result listing; notes truncation inline when more matched than are shown. */
|
|
19
19
|
export declare function resultListHeading(total: number, shown: number, labels: ResultListLabels): string;
|
|
20
20
|
/**
|
|
21
|
-
* One-line truncation notice for stderr, or null when nothing was cut.
|
|
22
|
-
*
|
|
21
|
+
* One-line truncation notice for stderr, or null when nothing was cut. Available for human-mode
|
|
22
|
+
* tips; `list --json` surfaces truncation via the envelope `truncation` field instead (#91).
|
|
23
23
|
*/
|
|
24
24
|
export declare function truncationNotice(total: number, shown: number, labels: ResultListLabels): string | null;
|
|
25
25
|
/** Max edit distance for a plausible typo; scales with input length. */
|
|
@@ -35,3 +35,8 @@ export declare function levenshtein(s1: string, s2: string): number;
|
|
|
35
35
|
* gets no correction rather than an absurd one.
|
|
36
36
|
*/
|
|
37
37
|
export declare function closestMatch(input: string, candidates: readonly string[], maxDistance: number): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Plausible option-value corrections: edit-distance first, then prefix matches for truncated enums
|
|
40
|
+
* (`stale` → `stale_grace` / `stale_expired`) where Levenshtein alone is too far.
|
|
41
|
+
*/
|
|
42
|
+
export declare function closestOptionValues(input: string, options: readonly string[]): string[];
|
package/dist/lib/text.js
CHANGED
|
@@ -16,8 +16,8 @@ export function resultListHeading(total, shown, labels) {
|
|
|
16
16
|
: `Found ${total} ${noun}:`;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* One-line truncation notice for stderr, or null when nothing was cut.
|
|
20
|
-
*
|
|
19
|
+
* One-line truncation notice for stderr, or null when nothing was cut. Available for human-mode
|
|
20
|
+
* tips; `list --json` surfaces truncation via the envelope `truncation` field instead (#91).
|
|
21
21
|
*/
|
|
22
22
|
export function truncationNotice(total, shown, labels) {
|
|
23
23
|
if (total <= shown)
|
|
@@ -71,3 +71,14 @@ export function closestMatch(input, candidates, maxDistance) {
|
|
|
71
71
|
}
|
|
72
72
|
return bestDistance <= maxDistance ? best : null;
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Plausible option-value corrections: edit-distance first, then prefix matches for truncated enums
|
|
76
|
+
* (`stale` → `stale_grace` / `stale_expired`) where Levenshtein alone is too far.
|
|
77
|
+
*/
|
|
78
|
+
export function closestOptionValues(input, options) {
|
|
79
|
+
const edit = closestMatch(input, options, maxFuzzyDistance(input));
|
|
80
|
+
if (edit)
|
|
81
|
+
return [edit];
|
|
82
|
+
// Exact equals already won via closestMatch; prefix covers truncated enums (stale → stale_*).
|
|
83
|
+
return options.filter((option) => option.startsWith(`${input}_`));
|
|
84
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Command } from '@oclif/core';
|
|
2
|
+
type FlagDef = {
|
|
3
|
+
type?: string;
|
|
4
|
+
char?: string;
|
|
5
|
+
aliases?: string[] | string;
|
|
6
|
+
charAliases?: string[];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Derive every CLI token that consumes a following argv value from command class metadata
|
|
10
|
+
* (long names, short chars, aliases). Used by argv normalization so VALUE_TAKING_FLAG_TOKENS
|
|
11
|
+
* cannot drift from the real command surface.
|
|
12
|
+
*/
|
|
13
|
+
export declare function valueTakingFlagTokensFromCommands(commands: Record<string, typeof Command>, extraBaseFlags?: Record<string, FlagDef>): Set<string>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Every argv token that selects this flag: long name, short char, and all aliases. */
|
|
2
|
+
function flagTokens(name, flag) {
|
|
3
|
+
const tokens = [`--${name}`];
|
|
4
|
+
if (flag.char)
|
|
5
|
+
tokens.push(`-${flag.char}`);
|
|
6
|
+
const aliases = typeof flag.aliases === 'string' ? [flag.aliases] : (flag.aliases ?? []);
|
|
7
|
+
tokens.push(...aliases.map((alias) => `--${alias}`));
|
|
8
|
+
tokens.push(...(flag.charAliases ?? []).map((charAlias) => `-${charAlias}`));
|
|
9
|
+
return tokens;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Derive every CLI token that consumes a following argv value from command class metadata
|
|
13
|
+
* (long names, short chars, aliases). Used by argv normalization so VALUE_TAKING_FLAG_TOKENS
|
|
14
|
+
* cannot drift from the real command surface.
|
|
15
|
+
*/
|
|
16
|
+
export function valueTakingFlagTokensFromCommands(commands, extraBaseFlags = {}) {
|
|
17
|
+
const tokens = new Set();
|
|
18
|
+
for (const command of Object.values(commands)) {
|
|
19
|
+
const flags = { ...extraBaseFlags, ...command.baseFlags, ...command.flags };
|
|
20
|
+
for (const [name, flag] of Object.entries(flags)) {
|
|
21
|
+
if (!flag || flag.type === 'boolean')
|
|
22
|
+
continue;
|
|
23
|
+
for (const token of flagTokens(name, flag))
|
|
24
|
+
tokens.add(token);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return tokens;
|
|
28
|
+
}
|
package/dist/sites/coveo.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
/** Hostnames served by Salesforce-operated Lightning Web Runtime (LWR) doc sites with shared browser-capture logic. */
|
|
1
2
|
export declare const ALLOWED_DOC_HOSTS: Set<string>;
|
|
3
|
+
/**
|
|
4
|
+
* Returns true when `hostname` is one of the allowed Salesforce doc hosts.
|
|
5
|
+
*
|
|
6
|
+
* @param hostname - Hostname to check (case-insensitive).
|
|
7
|
+
* @returns True when the hostname is in `ALLOWED_DOC_HOSTS`.
|
|
8
|
+
*/
|
|
2
9
|
export declare function isAllowedDocHost(hostname: string): boolean;
|
|
3
10
|
/**
|
|
4
11
|
* Coveo indexes Help articles under an internal `/help_doccontent` URL; rewrite it to the
|
|
5
12
|
* canonical `/s/articleView` page a human (or our fetcher) can actually open.
|
|
13
|
+
*
|
|
14
|
+
* @param rawUrl - Any URL string; non-matching URLs are returned unchanged.
|
|
15
|
+
* @returns The canonical articleView URL, or `rawUrl` unchanged if no rewrite applies.
|
|
6
16
|
*/
|
|
7
17
|
export declare function normalizeHelpDocContentUrl(rawUrl: string): string;
|
package/dist/sites/coveo.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
|
+
/** Hostnames served by Salesforce-operated Lightning Web Runtime (LWR) doc sites with shared browser-capture logic. */
|
|
1
2
|
export const ALLOWED_DOC_HOSTS = new Set(['help.salesforce.com', 'developer.salesforce.com']);
|
|
3
|
+
/**
|
|
4
|
+
* Returns true when `hostname` is one of the allowed Salesforce doc hosts.
|
|
5
|
+
*
|
|
6
|
+
* @param hostname - Hostname to check (case-insensitive).
|
|
7
|
+
* @returns True when the hostname is in `ALLOWED_DOC_HOSTS`.
|
|
8
|
+
*/
|
|
2
9
|
export function isAllowedDocHost(hostname) {
|
|
3
10
|
return ALLOWED_DOC_HOSTS.has(hostname.toLowerCase());
|
|
4
11
|
}
|
|
5
12
|
/**
|
|
6
13
|
* Coveo indexes Help articles under an internal `/help_doccontent` URL; rewrite it to the
|
|
7
14
|
* canonical `/s/articleView` page a human (or our fetcher) can actually open.
|
|
15
|
+
*
|
|
16
|
+
* @param rawUrl - Any URL string; non-matching URLs are returned unchanged.
|
|
17
|
+
* @returns The canonical articleView URL, or `rawUrl` unchanged if no rewrite applies.
|
|
8
18
|
*/
|
|
9
19
|
export function normalizeHelpDocContentUrl(rawUrl) {
|
|
10
20
|
let url;
|
package/dist/sites/index.d.ts
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { SiteModule } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Built-in documentation site modules shipped with the CLI (not a runtime registry).
|
|
4
|
+
* Salesforce Help and Salesforce Developer are separate entries — different domains and extractors.
|
|
5
|
+
*/
|
|
2
6
|
export declare const SITES: SiteModule[];
|
|
7
|
+
/**
|
|
8
|
+
* Matches a URL to its registered site module by hostname. Returns undefined for unrecognized hosts.
|
|
9
|
+
*
|
|
10
|
+
* @param url - Any URL string; invalid URLs return undefined rather than throwing.
|
|
11
|
+
* @returns The matching SiteModule, or undefined when no registered site owns the URL's hostname.
|
|
12
|
+
*/
|
|
3
13
|
export declare function detectSite(url: string): SiteModule | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Looks up a registered site module by its stable identifier (e.g. `'salesforce'`).
|
|
16
|
+
*
|
|
17
|
+
* @param id - The site module's `id` field.
|
|
18
|
+
* @returns The matching SiteModule, or undefined when no module with that id is registered.
|
|
19
|
+
*/
|
|
4
20
|
export declare function getSiteModuleById(id: string): SiteModule | undefined;
|
package/dist/sites/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { salesforce } from './salesforce/index.js';
|
|
2
2
|
import { salesforceDeveloper } from './salesforce-developer/index.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Built-in documentation site modules shipped with the CLI (not a runtime registry).
|
|
5
|
+
* Salesforce Help and Salesforce Developer are separate entries — different domains and extractors.
|
|
6
|
+
*/
|
|
7
7
|
export const SITES = [
|
|
8
8
|
salesforce,
|
|
9
9
|
salesforceDeveloper,
|
|
@@ -12,6 +12,12 @@ export const SITES = [
|
|
|
12
12
|
// Forcing rendered here bypassed that and dropped fenced code samples (e.g. useQueries).
|
|
13
13
|
{ id: 'tanstack', name: 'TanStack', domains: ['tanstack.com'] },
|
|
14
14
|
];
|
|
15
|
+
/**
|
|
16
|
+
* Matches a URL to its registered site module by hostname. Returns undefined for unrecognized hosts.
|
|
17
|
+
*
|
|
18
|
+
* @param url - Any URL string; invalid URLs return undefined rather than throwing.
|
|
19
|
+
* @returns The matching SiteModule, or undefined when no registered site owns the URL's hostname.
|
|
20
|
+
*/
|
|
15
21
|
export function detectSite(url) {
|
|
16
22
|
let hostname;
|
|
17
23
|
try {
|
|
@@ -22,6 +28,12 @@ export function detectSite(url) {
|
|
|
22
28
|
}
|
|
23
29
|
return SITES.find((site) => site.domains.includes(hostname));
|
|
24
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Looks up a registered site module by its stable identifier (e.g. `'salesforce'`).
|
|
33
|
+
*
|
|
34
|
+
* @param id - The site module's `id` field.
|
|
35
|
+
* @returns The matching SiteModule, or undefined when no module with that id is registered.
|
|
36
|
+
*/
|
|
25
37
|
export function getSiteModuleById(id) {
|
|
26
38
|
return SITES.find((site) => site.id === id);
|
|
27
39
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { fetchText } from '../lib/research/fetcher.js';
|
|
2
|
+
import type { SiteFetchResult } from './types.js';
|
|
3
|
+
/** Result of a per-site body transform applied before extraction. */
|
|
4
|
+
export interface TwinBodyTransform {
|
|
5
|
+
body: string;
|
|
6
|
+
/** Appended to the extraction's qualityNotes when set (e.g. "N directive(s) removed"). */
|
|
7
|
+
qualityNote?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Options for {@link probeMarkdownTwin}. */
|
|
10
|
+
export interface MarkdownTwinOptions {
|
|
11
|
+
/** Hostname the redirect chain must land on (see `isValidatedMarkdownTwin`). */
|
|
12
|
+
allowedHost: string;
|
|
13
|
+
/** Injectable fetcher (tests supply a fixture; production defaults to `fetchText`). */
|
|
14
|
+
fetcher?: typeof fetchText;
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
/** Extra request headers (e.g. a WAF-appeasing User-Agent). */
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
/** Applied to the raw body before extraction. Omit when the twin needs no preprocessing. */
|
|
19
|
+
transformBody?: (body: string) => TwinBodyTransform;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Probes candidate `.md`-twin URLs in order and returns the first that validates as a usable
|
|
23
|
+
* article, packaged as a SiteFetchResult with `route_markdown` provenance. Returns null when
|
|
24
|
+
* every candidate 404s, fails validation, or extracts thinner than the minimum viable article —
|
|
25
|
+
* callers fall back to their browser capture in that case. Never throws.
|
|
26
|
+
*
|
|
27
|
+
* @param candidates - Candidate `.md` URLs to try, in order.
|
|
28
|
+
* @param options - Allowed host, injectable fetcher/timeout, and an optional body transform.
|
|
29
|
+
* @returns SiteFetchResult with `route_markdown` provenance, or null when no candidate validates.
|
|
30
|
+
*/
|
|
31
|
+
export declare function probeMarkdownTwin(candidates: string[], { allowedHost, fetcher, timeoutMs, headers, transformBody, }: MarkdownTwinOptions): Promise<SiteFetchResult | null>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { fetchText } from '../lib/research/fetcher.js';
|
|
2
|
+
import { isValidatedMarkdownTwin } from '../lib/research/docs/validate.js';
|
|
3
|
+
import { extractFromSource } from '../lib/research/docs/markdown-source.js';
|
|
4
|
+
// Shared shape behind every site module's ".md twin" probe: try candidate URLs, validate the
|
|
5
|
+
// response against the expected host, and — once the body clears the minimum-viable-article
|
|
6
|
+
// length — package it into the SiteFetchResult shape createArtifactFromFetch expects. Two site
|
|
7
|
+
// modules use this: developer.salesforce.com (one derived URL) and help.salesforce.com (two
|
|
8
|
+
// category candidates on the b2c-developer-tooling mirror). `transformBody` covers the one place
|
|
9
|
+
// they differ — Developer strips unresolvable `::include{…}` directives before extraction.
|
|
10
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 4_000;
|
|
11
|
+
// A twin this thin reads as a rollout stub/placeholder rather than a real article.
|
|
12
|
+
const MIN_TWIN_CHARS = 100;
|
|
13
|
+
/**
|
|
14
|
+
* Probes candidate `.md`-twin URLs in order and returns the first that validates as a usable
|
|
15
|
+
* article, packaged as a SiteFetchResult with `route_markdown` provenance. Returns null when
|
|
16
|
+
* every candidate 404s, fails validation, or extracts thinner than the minimum viable article —
|
|
17
|
+
* callers fall back to their browser capture in that case. Never throws.
|
|
18
|
+
*
|
|
19
|
+
* @param candidates - Candidate `.md` URLs to try, in order.
|
|
20
|
+
* @param options - Allowed host, injectable fetcher/timeout, and an optional body transform.
|
|
21
|
+
* @returns SiteFetchResult with `route_markdown` provenance, or null when no candidate validates.
|
|
22
|
+
*/
|
|
23
|
+
export async function probeMarkdownTwin(candidates, { allowedHost, fetcher = fetchText, timeoutMs = DEFAULT_PROBE_TIMEOUT_MS, headers, transformBody, }) {
|
|
24
|
+
for (const candidate of candidates) {
|
|
25
|
+
let res;
|
|
26
|
+
try {
|
|
27
|
+
res = await fetcher(candidate, { timeoutMs, headers });
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!isValidatedMarkdownTwin(res, allowedHost))
|
|
33
|
+
continue;
|
|
34
|
+
const transformed = transformBody?.(res.content);
|
|
35
|
+
const extraction = extractFromSource(transformed?.body ?? res.content, res.finalUrl);
|
|
36
|
+
if (extraction.detailedMarkdown.length < MIN_TWIN_CHARS)
|
|
37
|
+
continue;
|
|
38
|
+
if (transformed?.qualityNote) {
|
|
39
|
+
extraction.qualityNotes = [...(extraction.qualityNotes ?? []), transformed.qualityNote];
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
fetchResult: {
|
|
43
|
+
contentType: res.contentType,
|
|
44
|
+
etag: res.etag,
|
|
45
|
+
lastModified: res.lastModified,
|
|
46
|
+
finalUrl: res.finalUrl,
|
|
47
|
+
responseSize: res.responseSize,
|
|
48
|
+
content: res.content,
|
|
49
|
+
},
|
|
50
|
+
extraction,
|
|
51
|
+
captureMethod: 'route_markdown',
|
|
52
|
+
sourceDocUrl: res.finalUrl,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { SiteFetchResult } from '../types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Fetches a help.salesforce.com article. Coveo's internal /help_doccontent URLs are rewritten
|
|
4
|
-
*
|
|
3
|
+
* Fetches a help.salesforce.com article. Coveo's internal /help_doccontent URLs are rewritten to
|
|
4
|
+
* the canonical /s/articleView page first. The Salesforce B2C Developer Tooling project mirrors a
|
|
5
|
+
* curated set of B2C Commerce Help articles as static Markdown; that twin is probed next, and used
|
|
6
|
+
* when it validates. Otherwise the shared Lightning Web Runtime (LWR) fetcher renders the page.
|
|
7
|
+
*
|
|
8
|
+
* @param url - The help.salesforce.com article URL (or a Coveo /help_doccontent URL).
|
|
9
|
+
* @returns Extracted Markdown, fetch metadata, and capture provenance (route_markdown or browser_fallback).
|
|
10
|
+
* @throws {Error} When the browser fallback path fails (same modes as `fetchSalesforceDoc`).
|
|
5
11
|
*/
|
|
6
12
|
export declare function fetchSalesforcePage(url: string): Promise<SiteFetchResult>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fetchSalesforceDoc } from '../salesforce-doc-fetch.js';
|
|
2
2
|
import { normalizeHelpDocContentUrl } from '../coveo.js';
|
|
3
|
+
import { fetchHelpToolingMarkdown } from './help-tooling-markdown.js';
|
|
3
4
|
// Containers a Help article renders into (inside shadow DOM); the shared fetcher pierces
|
|
4
5
|
// shadow roots to find them, falling back to the page body when none match.
|
|
5
6
|
const CONTENT_SELECTORS = [
|
|
@@ -13,11 +14,21 @@ const CONTENT_SELECTORS = [
|
|
|
13
14
|
'.cHCPortalTheme',
|
|
14
15
|
];
|
|
15
16
|
/**
|
|
16
|
-
* Fetches a help.salesforce.com article. Coveo's internal /help_doccontent URLs are rewritten
|
|
17
|
-
*
|
|
17
|
+
* Fetches a help.salesforce.com article. Coveo's internal /help_doccontent URLs are rewritten to
|
|
18
|
+
* the canonical /s/articleView page first. The Salesforce B2C Developer Tooling project mirrors a
|
|
19
|
+
* curated set of B2C Commerce Help articles as static Markdown; that twin is probed next, and used
|
|
20
|
+
* when it validates. Otherwise the shared Lightning Web Runtime (LWR) fetcher renders the page.
|
|
21
|
+
*
|
|
22
|
+
* @param url - The help.salesforce.com article URL (or a Coveo /help_doccontent URL).
|
|
23
|
+
* @returns Extracted Markdown, fetch metadata, and capture provenance (route_markdown or browser_fallback).
|
|
24
|
+
* @throws {Error} When the browser fallback path fails (same modes as `fetchSalesforceDoc`).
|
|
18
25
|
*/
|
|
19
26
|
export async function fetchSalesforcePage(url) {
|
|
20
|
-
|
|
27
|
+
const normalizedUrl = normalizeHelpDocContentUrl(url);
|
|
28
|
+
const fromToolingMirror = await fetchHelpToolingMarkdown(normalizedUrl);
|
|
29
|
+
if (fromToolingMirror)
|
|
30
|
+
return fromToolingMirror;
|
|
31
|
+
return fetchSalesforceDoc(normalizedUrl, {
|
|
21
32
|
allowedHost: 'help.salesforce.com',
|
|
22
33
|
contentSelectors: CONTENT_SELECTORS,
|
|
23
34
|
removeSelectors: [
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { fetchText } from '../../lib/research/fetcher.js';
|
|
2
|
+
import type { SiteFetchResult } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts the b2c-developer-tooling slug from a help.salesforce.com article URL, or null when the
|
|
5
|
+
* URL isn't a `cc.<slug>.htm` B2C Commerce Help article (wrong host, missing `id`, or an `id` that
|
|
6
|
+
* doesn't follow that pattern — e.g. numeric knowledge-article ids, or other clouds' Help content).
|
|
7
|
+
*
|
|
8
|
+
* @param url - A help.salesforce.com article URL (or any URL; non-matching returns null).
|
|
9
|
+
* @returns The derived slug (e.g. `b2c_inventory_list_object_import_export`), or null.
|
|
10
|
+
*/
|
|
11
|
+
export declare function deriveHelpToolingSlug(url: string): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Builds the candidate mirror URLs for a slug, one per known category. Category membership isn't
|
|
14
|
+
* derivable from the URL, so callers must probe each until one validates.
|
|
15
|
+
*
|
|
16
|
+
* @param slug - Slug derived by {@link deriveHelpToolingSlug}.
|
|
17
|
+
* @returns Candidate `.md` URLs on the b2c-developer-tooling mirror, one per category.
|
|
18
|
+
*/
|
|
19
|
+
export declare function candidateHelpToolingUrls(slug: string): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Probes the b2c-developer-tooling Markdown mirror for a help.salesforce.com article. Returns a
|
|
22
|
+
* complete SiteFetchResult (with route_markdown provenance) for the first category whose twin
|
|
23
|
+
* validates, or null so the caller falls back to the browser capture. Never throws: any
|
|
24
|
+
* network/validation failure just means "no twin in this category," or "no twin at all."
|
|
25
|
+
*
|
|
26
|
+
* @param url - The help.salesforce.com article URL to probe.
|
|
27
|
+
* @param fetcher - HTTP fetcher (injectable for tests; defaults to `fetchText`).
|
|
28
|
+
* @returns SiteFetchResult with route_markdown provenance, or null when no valid twin is found.
|
|
29
|
+
*/
|
|
30
|
+
export declare function fetchHelpToolingMarkdown(url: string, fetcher?: typeof fetchText): Promise<SiteFetchResult | null>;
|