@taurgis/bonsai 3.2.0 → 4.0.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 +9 -6
- package/agents/agents/salesforce-docs-researcher.agent.md +17 -4
- package/agents/instructions/salesforce-research.instructions.md +2 -1
- package/bin/cli.mjs +13 -18
- package/dist/base-command.d.ts +59 -12
- package/dist/base-command.js +179 -51
- package/dist/commands/config/base.js +12 -2
- package/dist/commands/config/get.js +8 -12
- package/dist/commands/config/index.js +2 -2
- package/dist/commands/config/list.js +11 -17
- package/dist/commands/config/set.d.ts +2 -1
- package/dist/commands/config/set.js +35 -28
- package/dist/commands/config/unset.d.ts +2 -1
- package/dist/commands/config/unset.js +26 -25
- package/dist/commands/fetch.d.ts +7 -15
- package/dist/commands/fetch.js +61 -401
- package/dist/commands/import.d.ts +4 -9
- package/dist/commands/import.js +56 -284
- package/dist/commands/inspect.d.ts +7 -1
- package/dist/commands/inspect.js +112 -52
- package/dist/commands/list.d.ts +18 -3
- package/dist/commands/list.js +121 -46
- package/dist/commands/prune.d.ts +6 -1
- package/dist/commands/prune.js +89 -78
- package/dist/commands/status.d.ts +3 -2
- package/dist/commands/status.js +62 -79
- package/dist/hooks/command-not-found/suggest.d.ts +2 -1
- package/dist/hooks/command-not-found/suggest.js +78 -41
- package/dist/lib/argv.d.ts +29 -3
- package/dist/lib/argv.js +147 -54
- 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 +12 -0
- package/dist/lib/cli-flag-manifest.js +15 -0
- package/dist/lib/cli-presentation.d.ts +41 -0
- package/dist/lib/cli-presentation.js +53 -0
- package/dist/lib/cli-result-types.d.ts +112 -0
- package/dist/lib/cli-result-types.js +2 -0
- package/dist/lib/config/index.d.ts +23 -5
- package/dist/lib/config/index.js +60 -5
- package/dist/lib/config/io.d.ts +9 -0
- package/dist/lib/config/io.js +71 -21
- package/dist/lib/config/schema.d.ts +2 -0
- package/dist/lib/config/schema.js +7 -0
- 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 +154 -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 +30 -0
- package/dist/lib/prune-flags.js +86 -0
- package/dist/lib/research/artifact-index.d.ts +11 -2
- package/dist/lib/research/artifact-index.js +14 -5
- package/dist/lib/research/artifact.js +20 -2
- package/dist/lib/research/browser.d.ts +53 -2
- package/dist/lib/research/browser.js +202 -58
- 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 +40 -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 +367 -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 +61 -31
- 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 +402 -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/metadata-filters.d.ts +18 -0
- package/dist/lib/research/metadata-filters.js +37 -0
- package/dist/lib/research/metadata-flags.d.ts +34 -0
- package/dist/lib/research/metadata-flags.js +45 -0
- 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 +9 -0
- package/dist/lib/research/prompt-injection.js +59 -14
- package/dist/lib/research/proxy.d.ts +14 -8
- package/dist/lib/research/proxy.js +14 -10
- package/dist/lib/research/resolve-target.d.ts +2 -0
- package/dist/lib/research/resolve-target.js +1 -1
- 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 +4 -0
- package/dist/lib/research/secure-write.js +31 -2
- package/dist/lib/research/storage.d.ts +9 -8
- package/dist/lib/research/storage.js +34 -28
- 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 +19 -8
- package/dist/lib/text.js +32 -9
- 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 +216 -102
- package/package.json +20 -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
package/dist/lib/text.d.ts
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
/** Display label when an artifact has no topic — not a filterable topic value. */
|
|
2
2
|
export declare const NO_TOPIC_LABEL = "(no topic)";
|
|
3
|
+
export declare const CONTROL_CHAR_PATTERN: RegExp;
|
|
4
|
+
/**
|
|
5
|
+
* Strip ANSI escape codes and other control characters from a cached metadata string before it
|
|
6
|
+
* reaches a human-readable terminal line. Fields like `topic` and section headings can originate
|
|
7
|
+
* from fetched web content or agent-supplied notes — both untrusted per the repo's trust-boundary
|
|
8
|
+
* rules — so a value containing raw escape bytes must never be echoed as-is to a TTY. Newlines and
|
|
9
|
+
* tabs collapse to a single space rather than being stripped outright, so injected content can't
|
|
10
|
+
* fake extra output lines while still being removed. JSON output is unaffected: `JSON.stringify`
|
|
11
|
+
* already escapes control characters, so this only needs to run on the human-readable path.
|
|
12
|
+
*/
|
|
13
|
+
export declare function sanitizeForTerminal(value: string): string;
|
|
3
14
|
/**
|
|
4
15
|
* Pick the singular or plural noun for a count so human-readable output stays grammatical
|
|
5
16
|
* ("1 entry" / "2 entries"). English has irregular plurals, so the plural is passed explicitly
|
|
6
17
|
* rather than derived.
|
|
7
18
|
*/
|
|
8
19
|
export declare function pluralize(count: number, singular: string, plural: string): string;
|
|
9
|
-
/**
|
|
20
|
+
/** Shared "Tip: " prefix for contextual next-step suggestions in human-mode output. */
|
|
21
|
+
export declare function formatTip(message: string): string;
|
|
22
|
+
/** How a command labels a result listing, so its heading stays consistent. */
|
|
10
23
|
export interface ResultListLabels {
|
|
11
24
|
/** Noun phrase after the count, e.g. "cached research"; the "entry/entries" plural is appended. */
|
|
12
25
|
noun: string;
|
|
13
26
|
/** Ordering word shown when truncated: "first" (recency order) or "top" (ranked order). */
|
|
14
27
|
order: 'first' | 'top';
|
|
15
|
-
/** The command's --limit ceiling, surfaced in the truncation hint. */
|
|
16
|
-
maxLimit: number;
|
|
17
28
|
}
|
|
18
29
|
/** Heading for a result listing; notes truncation inline when more matched than are shown. */
|
|
19
30
|
export declare function resultListHeading(total: number, shown: number, labels: ResultListLabels): string;
|
|
20
|
-
/**
|
|
21
|
-
* One-line truncation notice for stderr, or null when nothing was cut. Used under --json, where the
|
|
22
|
-
* stdout heading is suppressed and an agent would otherwise get no signal that results were capped.
|
|
23
|
-
*/
|
|
24
|
-
export declare function truncationNotice(total: number, shown: number, labels: ResultListLabels): string | null;
|
|
25
31
|
/** Max edit distance for a plausible typo; scales with input length. */
|
|
26
32
|
export declare function maxFuzzyDistance(input: string): number;
|
|
27
33
|
/**
|
|
@@ -35,3 +41,8 @@ export declare function levenshtein(s1: string, s2: string): number;
|
|
|
35
41
|
* gets no correction rather than an absurd one.
|
|
36
42
|
*/
|
|
37
43
|
export declare function closestMatch(input: string, candidates: readonly string[], maxDistance: number): string | null;
|
|
44
|
+
/**
|
|
45
|
+
* Plausible option-value corrections: edit-distance first, then prefix matches for truncated enums
|
|
46
|
+
* (`stale` → `stale_grace` / `stale_expired`) where Levenshtein alone is too far.
|
|
47
|
+
*/
|
|
48
|
+
export declare function closestOptionValues(input: string, options: readonly string[]): string[];
|
package/dist/lib/text.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
/** Display label when an artifact has no topic — not a filterable topic value. */
|
|
2
2
|
export const NO_TOPIC_LABEL = '(no topic)';
|
|
3
|
+
// Unicode "Control" category (C0 + C1) covers ANSI/terminal escape sequences: the ESC control
|
|
4
|
+
// character that starts them is a member, so stripping this category is what keeps injected
|
|
5
|
+
// control bytes from reaching a real terminal. Exported so `prompt-injection.ts`'s
|
|
6
|
+
// multi-line-content variant (which preserves tab/newline/CR) shares this one definition.
|
|
7
|
+
export const CONTROL_CHAR_PATTERN = /\p{Cc}/gu;
|
|
8
|
+
/**
|
|
9
|
+
* Strip ANSI escape codes and other control characters from a cached metadata string before it
|
|
10
|
+
* reaches a human-readable terminal line. Fields like `topic` and section headings can originate
|
|
11
|
+
* from fetched web content or agent-supplied notes — both untrusted per the repo's trust-boundary
|
|
12
|
+
* rules — so a value containing raw escape bytes must never be echoed as-is to a TTY. Newlines and
|
|
13
|
+
* tabs collapse to a single space rather than being stripped outright, so injected content can't
|
|
14
|
+
* fake extra output lines while still being removed. JSON output is unaffected: `JSON.stringify`
|
|
15
|
+
* already escapes control characters, so this only needs to run on the human-readable path.
|
|
16
|
+
*/
|
|
17
|
+
export function sanitizeForTerminal(value) {
|
|
18
|
+
return value.replace(/[\t\n\r]/g, ' ').replace(CONTROL_CHAR_PATTERN, '');
|
|
19
|
+
}
|
|
3
20
|
/**
|
|
4
21
|
* Pick the singular or plural noun for a count so human-readable output stays grammatical
|
|
5
22
|
* ("1 entry" / "2 entries"). English has irregular plurals, so the plural is passed explicitly
|
|
@@ -8,6 +25,10 @@ export const NO_TOPIC_LABEL = '(no topic)';
|
|
|
8
25
|
export function pluralize(count, singular, plural) {
|
|
9
26
|
return count === 1 ? singular : plural;
|
|
10
27
|
}
|
|
28
|
+
/** Shared "Tip: " prefix for contextual next-step suggestions in human-mode output. */
|
|
29
|
+
export function formatTip(message) {
|
|
30
|
+
return `Tip: ${message}`;
|
|
31
|
+
}
|
|
11
32
|
/** Heading for a result listing; notes truncation inline when more matched than are shown. */
|
|
12
33
|
export function resultListHeading(total, shown, labels) {
|
|
13
34
|
const noun = `${labels.noun} ${pluralize(total, 'entry', 'entries')}`;
|
|
@@ -15,15 +36,6 @@ export function resultListHeading(total, shown, labels) {
|
|
|
15
36
|
? `Found ${total} ${noun} (showing ${labels.order} ${shown}; raise --limit to see more):`
|
|
16
37
|
: `Found ${total} ${noun}:`;
|
|
17
38
|
}
|
|
18
|
-
/**
|
|
19
|
-
* One-line truncation notice for stderr, or null when nothing was cut. Used under --json, where the
|
|
20
|
-
* stdout heading is suppressed and an agent would otherwise get no signal that results were capped.
|
|
21
|
-
*/
|
|
22
|
-
export function truncationNotice(total, shown, labels) {
|
|
23
|
-
if (total <= shown)
|
|
24
|
-
return null;
|
|
25
|
-
return `${total} entries matched; returning the ${labels.order} ${shown}. Raise --limit (max ${labels.maxLimit}) to see more.`;
|
|
26
|
-
}
|
|
27
39
|
/** Max edit distance for a plausible typo; scales with input length. */
|
|
28
40
|
export function maxFuzzyDistance(input) {
|
|
29
41
|
if (input.length <= 3)
|
|
@@ -71,3 +83,14 @@ export function closestMatch(input, candidates, maxDistance) {
|
|
|
71
83
|
}
|
|
72
84
|
return bestDistance <= maxDistance ? best : null;
|
|
73
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Plausible option-value corrections: edit-distance first, then prefix matches for truncated enums
|
|
88
|
+
* (`stale` → `stale_grace` / `stale_expired`) where Levenshtein alone is too far.
|
|
89
|
+
*/
|
|
90
|
+
export function closestOptionValues(input, options) {
|
|
91
|
+
const edit = closestMatch(input, options, maxFuzzyDistance(input));
|
|
92
|
+
if (edit)
|
|
93
|
+
return [edit];
|
|
94
|
+
// Exact equals already won via closestMatch; prefix covers truncated enums (stale → stale_*).
|
|
95
|
+
return options.filter((option) => option.startsWith(`${input}_`));
|
|
96
|
+
}
|
|
@@ -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>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { fetchText } from '../../lib/research/fetcher.js';
|
|
2
|
+
import { probeMarkdownTwin } from '../markdown-twin.js';
|
|
3
|
+
// Salesforce's official "B2C Developer Tooling" project (GA, SalesforceCommerceCloud org) mirrors
|
|
4
|
+
// a curated subset of help.salesforce.com's B2C Commerce articles as static Markdown for its own
|
|
5
|
+
// CLI/MCP `docs` tool: /help/<category>/<slug>.md on its GitHub Pages site. Two categories cover
|
|
6
|
+
// admin- and merchant-facing content. Fetching that twin replaces Help's ~45s shadow-DOM render
|
|
7
|
+
// with one static request — the same win developer.salesforce.com's route.md twin gives us.
|
|
8
|
+
//
|
|
9
|
+
// Unlike the developer.salesforce.com twin, the mirror lives on a DIFFERENT host than the article
|
|
10
|
+
// (help.salesforce.com -> salesforcecommercecloud.github.io), and there is no public index mapping
|
|
11
|
+
// a help.salesforce.com article to its slug/category: the mapping ships inside the b2c-developer-
|
|
12
|
+
// tooling CLI's bundled package, not as a fetchable file. But B2C Commerce Help articles' `id` query
|
|
13
|
+
// param follows a stable `cc.<slug>.htm` pattern that matches the mirror's slug exactly (verified
|
|
14
|
+
// against several live articles spanning both categories), so the slug is derivable from the URL;
|
|
15
|
+
// which of the two categories holds it is not, so both are probed (see probeMarkdownTwin).
|
|
16
|
+
const HELP_HOST = 'help.salesforce.com';
|
|
17
|
+
const TOOLING_HOST = 'salesforcecommercecloud.github.io';
|
|
18
|
+
const TOOLING_HELP_BASE = `https://${TOOLING_HOST}/b2c-developer-tooling/help`;
|
|
19
|
+
// Order doesn't reflect prevalence, just the two categories b2c-developer-tooling publishes.
|
|
20
|
+
const HELP_TOOLING_CATEGORIES = ['help-admin', 'help-merchant'];
|
|
21
|
+
// Every real B2C Commerce Help slug is lowercase word characters/underscores (e.g.
|
|
22
|
+
// b2c_inventory_list_object_import_export); restricting the capture group to that charset (rather
|
|
23
|
+
// than `.+`) keeps a crafted `id` query param from steering the mirror probe at an arbitrary path
|
|
24
|
+
// on the trusted host.
|
|
25
|
+
const HELP_ARTICLE_ID_PATTERN = /^cc\.([\w-]+)\.htm$/i;
|
|
26
|
+
/**
|
|
27
|
+
* Extracts the b2c-developer-tooling slug from a help.salesforce.com article URL, or null when the
|
|
28
|
+
* URL isn't a `cc.<slug>.htm` B2C Commerce Help article (wrong host, missing `id`, or an `id` that
|
|
29
|
+
* doesn't follow that pattern — e.g. numeric knowledge-article ids, or other clouds' Help content).
|
|
30
|
+
*
|
|
31
|
+
* @param url - A help.salesforce.com article URL (or any URL; non-matching returns null).
|
|
32
|
+
* @returns The derived slug (e.g. `b2c_inventory_list_object_import_export`), or null.
|
|
33
|
+
*/
|
|
34
|
+
export function deriveHelpToolingSlug(url) {
|
|
35
|
+
let parsed;
|
|
36
|
+
try {
|
|
37
|
+
parsed = new URL(url);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (parsed.hostname.toLowerCase() !== HELP_HOST)
|
|
43
|
+
return null;
|
|
44
|
+
const id = parsed.searchParams.get('id');
|
|
45
|
+
if (!id)
|
|
46
|
+
return null;
|
|
47
|
+
return HELP_ARTICLE_ID_PATTERN.exec(id)?.[1] ?? null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Builds the candidate mirror URLs for a slug, one per known category. Category membership isn't
|
|
51
|
+
* derivable from the URL, so callers must probe each until one validates.
|
|
52
|
+
*
|
|
53
|
+
* @param slug - Slug derived by {@link deriveHelpToolingSlug}.
|
|
54
|
+
* @returns Candidate `.md` URLs on the b2c-developer-tooling mirror, one per category.
|
|
55
|
+
*/
|
|
56
|
+
export function candidateHelpToolingUrls(slug) {
|
|
57
|
+
return HELP_TOOLING_CATEGORIES.map((category) => `${TOOLING_HELP_BASE}/${category}/${slug}.md`);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Probes the b2c-developer-tooling Markdown mirror for a help.salesforce.com article. Returns a
|
|
61
|
+
* complete SiteFetchResult (with route_markdown provenance) for the first category whose twin
|
|
62
|
+
* validates, or null so the caller falls back to the browser capture. Never throws: any
|
|
63
|
+
* network/validation failure just means "no twin in this category," or "no twin at all."
|
|
64
|
+
*
|
|
65
|
+
* @param url - The help.salesforce.com article URL to probe.
|
|
66
|
+
* @param fetcher - HTTP fetcher (injectable for tests; defaults to `fetchText`).
|
|
67
|
+
* @returns SiteFetchResult with route_markdown provenance, or null when no valid twin is found.
|
|
68
|
+
*/
|
|
69
|
+
export async function fetchHelpToolingMarkdown(url, fetcher = fetchText) {
|
|
70
|
+
const slug = deriveHelpToolingSlug(url);
|
|
71
|
+
if (!slug)
|
|
72
|
+
return null;
|
|
73
|
+
return probeMarkdownTwin(candidateHelpToolingUrls(slug), { allowedHost: TOOLING_HOST, fetcher });
|
|
74
|
+
}
|
|
@@ -2,6 +2,7 @@ import { fetchSalesforcePage } from './fetch-page.js';
|
|
|
2
2
|
// Scoped to help.salesforce.com — an LWR Experience site with light-DOM article content.
|
|
3
3
|
// developer.salesforce.com is intentionally NOT routed here: its API-reference docs render
|
|
4
4
|
// inside shadow DOM with tabbed/code content that needs a different extraction strategy.
|
|
5
|
+
/** Site module for help.salesforce.com: forces rendered capture and applies the Help-specific Lightning Web Runtime (LWR) fetcher. */
|
|
5
6
|
export const salesforce = {
|
|
6
7
|
id: 'salesforce',
|
|
7
8
|
name: 'Salesforce Help',
|
|
@@ -3,6 +3,11 @@ import type { SiteFetchResult } from '../types.js';
|
|
|
3
3
|
* Fetches a developer.salesforce.com doc page. Supported articles publish a Markdown twin at the
|
|
4
4
|
* derived `.md` route ("View as Markdown"), which is preferred: one static request instead of a
|
|
5
5
|
* browser render, and it returns the article's source. Pages without a validated twin (API
|
|
6
|
-
* reference, atlas.* books, landing pages) fall back to the shared
|
|
6
|
+
* reference, atlas.* books, landing pages) fall back to the shared Lightning Web Runtime (LWR)
|
|
7
|
+
* shadow-DOM fetcher.
|
|
8
|
+
*
|
|
9
|
+
* @param url - The developer.salesforce.com doc page URL.
|
|
10
|
+
* @returns Extracted Markdown, fetch metadata, and capture provenance (route_markdown or browser_fallback).
|
|
11
|
+
* @throws {Error} When the browser fallback path fails (same modes as `fetchSalesforceDoc`).
|
|
7
12
|
*/
|
|
8
13
|
export declare function fetchDeveloperPage(url: string): Promise<SiteFetchResult>;
|
|
@@ -16,7 +16,12 @@ const CONTENT_SELECTORS = [
|
|
|
16
16
|
* Fetches a developer.salesforce.com doc page. Supported articles publish a Markdown twin at the
|
|
17
17
|
* derived `.md` route ("View as Markdown"), which is preferred: one static request instead of a
|
|
18
18
|
* browser render, and it returns the article's source. Pages without a validated twin (API
|
|
19
|
-
* reference, atlas.* books, landing pages) fall back to the shared
|
|
19
|
+
* reference, atlas.* books, landing pages) fall back to the shared Lightning Web Runtime (LWR)
|
|
20
|
+
* shadow-DOM fetcher.
|
|
21
|
+
*
|
|
22
|
+
* @param url - The developer.salesforce.com doc page 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`).
|
|
20
25
|
*/
|
|
21
26
|
export async function fetchDeveloperPage(url) {
|
|
22
27
|
const fromMarkdownRoute = await fetchDeveloperRouteMarkdown(url);
|
|
@@ -2,6 +2,7 @@ import { fetchDeveloperPage } from './fetch-page.js';
|
|
|
2
2
|
// developer.salesforce.com docs (guides + API reference) render client-side inside web
|
|
3
3
|
// components, so content lives in shadow DOM — a fundamentally different extraction strategy
|
|
4
4
|
// than help.salesforce.com (see ../salesforce).
|
|
5
|
+
/** Site module for developer.salesforce.com: forces rendered capture and applies the shadow-DOM Lightning Web Runtime (LWR) fetcher. */
|
|
5
6
|
export const salesforceDeveloper = {
|
|
6
7
|
id: 'salesforce-developer',
|
|
7
8
|
name: 'Salesforce Developer',
|
|
@@ -1,23 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fetchText } from '../../lib/research/fetcher.js';
|
|
2
2
|
import type { SiteFetchResult } from '../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Derives the candidate `.md` route for a developer.salesforce.com doc URL, or null when the URL
|
|
5
5
|
* can't serve one (wrong host, not under /docs/, or an atlas.* book — verified to answer `.md`
|
|
6
6
|
* with an HTML shell, never Markdown). Query/hash are view state and are dropped.
|
|
7
|
+
*
|
|
8
|
+
* @param url - A developer.salesforce.com doc URL (or any URL; non-matching returns null).
|
|
9
|
+
* @returns The derived `.md` URL, or null when no `.md` route exists for this URL.
|
|
7
10
|
*/
|
|
8
11
|
export declare function deriveMarkdownUrl(url: string): string | null;
|
|
9
|
-
/** Removes unresolvable `::include{…}` directive lines, reporting how many were dropped.
|
|
12
|
+
/** Removes unresolvable `::include{…}` directive lines, reporting how many were dropped.
|
|
13
|
+
*
|
|
14
|
+
* @param md - Markdown body from the `.md` route response.
|
|
15
|
+
* @returns Cleaned body string and a count of removed directive lines.
|
|
16
|
+
*/
|
|
10
17
|
export declare function stripIncludeDirectives(md: string): {
|
|
11
18
|
body: string;
|
|
12
19
|
dropped: number;
|
|
13
20
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Injectable HTTP fetcher used by `fetchDeveloperRouteMarkdown`. The default is `fetchText`;
|
|
23
|
+
* tests inject a fixture fetcher to avoid real network calls.
|
|
24
|
+
*/
|
|
25
|
+
export type RouteMarkdownFetcher = typeof fetchText;
|
|
18
26
|
/**
|
|
19
27
|
* Probes the derived `.md` route for a developer doc URL. Returns a complete SiteFetchResult
|
|
20
28
|
* (with route_markdown provenance) when the twin validates, or null so the caller falls back to
|
|
21
29
|
* the browser capture. Never throws: any network/validation failure is just "no twin".
|
|
30
|
+
*
|
|
31
|
+
* @param url - The developer.salesforce.com doc page URL to probe.
|
|
32
|
+
* @param fetcher - HTTP fetcher (injectable for tests; defaults to `fetchText`).
|
|
33
|
+
* @returns SiteFetchResult with route_markdown provenance, or null when no valid twin is found.
|
|
22
34
|
*/
|
|
23
35
|
export declare function fetchDeveloperRouteMarkdown(url: string, fetcher?: RouteMarkdownFetcher): Promise<SiteFetchResult | null>;
|